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 +#include + + +void null_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) { + + matrix_diag_set_scalar( X , 1.0 ); + +} + + +long null_enkf_get_options( void * arg , long flag ) { + return 0L; +} + + + +/** + gcc -fpic -c -I?? + gcc -shared -o +*/ + + + +#ifdef INTERNAL_LINK +#define LINK_NAME NULL_ENKF +#else +#define LINK_NAME EXTERNAL_MODULE_SYMBOL +#endif + + + +analysis_table_type LINK_NAME = { + .name = "NULL_ENKF", + .updateA = NULL, + .initX = null_enkf_initX , + .init_update = NULL, + .complete_update = NULL, + + .freef = NULL , + .alloc = NULL , + + .set_int = NULL , + .set_double = NULL , + .set_bool = NULL , + .set_string = NULL , + .get_options = null_enkf_get_options, + + .has_var = NULL, + .get_int = NULL, + .get_double = NULL, + .get_bool = NULL, + .get_ptr = NULL, +}; diff --git a/libres/lib/analysis/sqrt_enkf.cpp b/libres/lib/analysis/sqrt_enkf.cpp new file mode 100644 index 00000000000..d60af2b6d24 --- /dev/null +++ b/libres/lib/analysis/sqrt_enkf.cpp @@ -0,0 +1,224 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'sqrt_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 + + +/* + The sqrt_enkf module performs a EnKF update based on the square root + scheme. Observe that this module shares quite a lot of + implementation with the std_enkf module. +*/ + +#define SQRT_ENKF_TYPE_ID 268823 + +typedef struct { + UTIL_TYPE_ID_DECLARATION; + std_enkf_data_type * std_data; + matrix_type * randrot; + long options; +} sqrt_enkf_data_type; + + +static UTIL_SAFE_CAST_FUNCTION( sqrt_enkf_data , SQRT_ENKF_TYPE_ID ) +static UTIL_SAFE_CAST_FUNCTION_CONST( sqrt_enkf_data , SQRT_ENKF_TYPE_ID ) + + +void * sqrt_enkf_data_alloc( ) { + sqrt_enkf_data_type * data = (sqrt_enkf_data_type*)util_malloc( sizeof * data ); + UTIL_TYPE_ID_INIT( data , SQRT_ENKF_TYPE_ID ); + + data->std_data = (std_enkf_data_type*)std_enkf_data_alloc( ); + data->randrot = NULL; + data->options = ANALYSIS_SCALE_DATA; + + return data; +} + + + +void sqrt_enkf_data_free( void * data ) { + sqrt_enkf_data_type * module_data = sqrt_enkf_data_safe_cast( data ); + { + std_enkf_data_free( module_data->std_data ); + free( module_data ); + } +} + + + +bool sqrt_enkf_set_double( void * arg , const char * var_name , double value) { + sqrt_enkf_data_type * module_data = sqrt_enkf_data_safe_cast( arg ); + { + if (std_enkf_set_double( module_data->std_data , var_name , value )) + return true; + else { + /* Could in principle set sqrt specific variables here. */ + return false; + } + } +} + + +bool sqrt_enkf_set_int( void * arg , const char * var_name , int value) { + sqrt_enkf_data_type * module_data = sqrt_enkf_data_safe_cast( arg ); + { + if (std_enkf_set_int( module_data->std_data , var_name , value )) + return true; + else { + /* Could in principle set sqrt specific variables here. */ + return false; + } + } +} + + + + + +void sqrt_enkf_initX(void * module_data , + matrix_type * X , + const matrix_type * A , + const matrix_type * S0 , + const matrix_type * R , + const matrix_type * dObs , + const matrix_type * E , + const matrix_type *D, + rng_type * rng) { + + sqrt_enkf_data_type * data = sqrt_enkf_data_safe_cast( module_data ); + { + matrix_type * S = matrix_alloc_copy(S0); + int ncomp = std_enkf_get_subspace_dimension( data->std_data ); + double truncation = std_enkf_get_truncation( data->std_data ); + int nrobs = matrix_get_rows( S ); + int ens_size = matrix_get_columns( S ); + int nrmin = util_int_min( ens_size , nrobs); + matrix_type * W = matrix_alloc(nrobs , nrmin); + double * eig = (double*)util_calloc( nrmin , sizeof * eig ); + + matrix_subtract_row_mean( S ); /* Shift away the mean */ + enkf_linalg_lowrankCinv( S , R , W , eig , truncation , ncomp); + enkf_linalg_init_sqrtX( X , S , data->randrot , dObs , W , eig , false); + matrix_free( W ); + free( eig ); + + enkf_linalg_checkX( X , false ); + matrix_free(S); + } +} + + +long sqrt_enkf_get_options( void * arg , long flag ) { + sqrt_enkf_data_type * module_data = sqrt_enkf_data_safe_cast( arg ); + { + return module_data->options; + } +} + + + +// Called from analysis_module.c: analysis_module_init_update() +void sqrt_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 ) { + + sqrt_enkf_data_type * sqrt_data = sqrt_enkf_data_safe_cast( arg ); + { + int ens_size = matrix_get_columns( S ); + sqrt_data->randrot = enkf_linalg_alloc_mp_randrot( ens_size , rng ); + } +} + + +void sqrt_enkf_complete_update( void * arg ) { + sqrt_enkf_data_type * sqrt_data = sqrt_enkf_data_safe_cast( arg ); + { + matrix_free( sqrt_data->randrot ); + sqrt_data->randrot = NULL; + } +} + +bool sqrt_enkf_has_var( const void * arg, const char * var_name) { + const sqrt_enkf_data_type * module_data = sqrt_enkf_data_safe_cast_const( arg ); + { + return std_enkf_has_var(module_data->std_data, var_name); + } +} + +double sqrt_enkf_get_double( const void * arg, const char * var_name) { + const sqrt_enkf_data_type * module_data = sqrt_enkf_data_safe_cast_const( arg ); + { + return std_enkf_get_double( module_data->std_data , var_name); + } +} + +int sqrt_enkf_get_int( const void * arg, const char * var_name) { + const sqrt_enkf_data_type * module_data = sqrt_enkf_data_safe_cast_const( arg ); + { + return std_enkf_get_int( module_data->std_data , var_name); + } +} + + + +/*****************************************************************/ + +#ifdef INTERNAL_LINK +#define LINK_NAME SQRT_ENKF +#else +#define LINK_NAME EXTERNAL_MODULE_SYMBOL +#endif + + +analysis_table_type LINK_NAME = { + .name = "SQRT_ENKF", + .updateA = NULL, + .initX = sqrt_enkf_initX , + .init_update = sqrt_enkf_init_update, + .complete_update = sqrt_enkf_complete_update, + + .freef = sqrt_enkf_data_free, + .alloc = sqrt_enkf_data_alloc, + + .set_int = sqrt_enkf_set_int , + .set_double = sqrt_enkf_set_double , + .set_bool = NULL , + .set_string = NULL , + .get_options = sqrt_enkf_get_options, + + .has_var = sqrt_enkf_has_var, + .get_int = sqrt_enkf_get_int, + .get_double = sqrt_enkf_get_double, + .get_bool = NULL, + .get_ptr = NULL +}; diff --git a/libres/lib/analysis/std_enkf.cpp b/libres/lib/analysis/std_enkf.cpp new file mode 100644 index 00000000000..bf84b46cf2c --- /dev/null +++ b/libres/lib/analysis/std_enkf.cpp @@ -0,0 +1,372 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'std_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 + + +/* + A random 'magic' integer id which is used for run-time type checking + of the input data. +*/ +#define STD_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 +#define DEFAULT_USE_EE false +#define DEFAULT_USE_GE false +#define DEFAULT_ANALYSIS_SCALE_DATA true + + + + + +/* + The configuration data used by the std_enkf module is contained in a + std_enkf_data_struct instance. The data type used for the std_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 std_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 std_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; + bool use_EE; + bool use_GE; + bool analysis_scale_data; +}; + +static UTIL_SAFE_CAST_FUNCTION_CONST( std_enkf_data , STD_ENKF_TYPE_ID ) + + +/* + This is a macro which will expand to generate a function: + + std_enkf_data_type * std_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( std_enkf_data , STD_ENKF_TYPE_ID ) + + +double std_enkf_get_truncation( std_enkf_data_type * data ) { + return data->truncation; +} + +int std_enkf_get_subspace_dimension( std_enkf_data_type * data ) { + return data->subspace_dimension; +} + +void std_enkf_set_truncation( std_enkf_data_type * data , double truncation ) { + data->truncation = truncation; + if (truncation > 0.0) + data->subspace_dimension = INVALID_SUBSPACE_DIMENSION; +} + +void std_enkf_set_subspace_dimension( std_enkf_data_type * data , int subspace_dimension) { + data->subspace_dimension = subspace_dimension; + if (subspace_dimension > 0) + data->truncation = INVALID_TRUNCATION; +} + + + +void * std_enkf_data_alloc( ) { + std_enkf_data_type * data = (std_enkf_data_type*)util_malloc( sizeof * data ); + UTIL_TYPE_ID_INIT( data , STD_ENKF_TYPE_ID ); + + std_enkf_set_truncation( data , DEFAULT_ENKF_TRUNCATION_ ); + std_enkf_set_subspace_dimension( data , DEFAULT_SUBSPACE_DIMENSION ); + data->option_flags = ANALYSIS_NEED_ED; + data->use_EE = DEFAULT_USE_EE; + data->use_GE = DEFAULT_USE_GE; + data->analysis_scale_data = DEFAULT_ANALYSIS_SCALE_DATA; + return data; +} + + +void std_enkf_data_free( void * data ) { + free( data ); +} + + + + +static void std_enkf_initX__( matrix_type * X , + const matrix_type * S0 , + const matrix_type * R , + const matrix_type * E , + const matrix_type * D , + double truncation, + int ncomp, + bool bootstrap , + bool use_EE , + bool use_GE) { + + matrix_type * S = matrix_alloc_copy(S0); + int nrobs = matrix_get_rows( S ); + int ens_size = matrix_get_columns( S ); + int nrmin = util_int_min( ens_size , nrobs); + + matrix_type * W = matrix_alloc(nrobs , nrmin); + double * eig = (double*)util_calloc( nrmin , sizeof * eig); + + matrix_subtract_row_mean( S ); /* Shift away the mean */ + + if (use_EE) { + if (use_GE) { + enkf_linalg_lowrankE( S , E , W , eig , truncation , ncomp); + } + else { + matrix_type * Et = matrix_alloc_transpose( E ); + matrix_type * Cee = matrix_alloc_matmul( E , Et ); + matrix_scale( Cee , 1.0 / (ens_size - 1)); + + enkf_linalg_lowrankCinv( S , Cee , W , eig , truncation , ncomp); + + matrix_free( Et ); + matrix_free( Cee ); + } + + } + else { + enkf_linalg_lowrankCinv( S , R , W , eig , truncation , ncomp); + } + + enkf_linalg_init_stdX( X , S , D , W , eig , bootstrap); + + matrix_free( W ); + matrix_free( S ); + free( eig ); + enkf_linalg_checkX( X , bootstrap ); +} + + + + + +void std_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) { + + + std_enkf_data_type * data = std_enkf_data_safe_cast( module_data ); + { + int ncomp = data->subspace_dimension; + double truncation = data->truncation; + + std_enkf_initX__(X,S,R,E,D,truncation,ncomp,false,data->use_EE,data->use_GE); + } +} + + + + + + + +bool std_enkf_set_double( void * arg , const char * var_name , double value) { + std_enkf_data_type * module_data = std_enkf_data_safe_cast( arg ); + { + bool name_recognized = true; + + if (strcmp( var_name , ENKF_TRUNCATION_KEY_) == 0) + std_enkf_set_truncation( module_data , value ); + else + name_recognized = false; + + return name_recognized; + } +} + + +bool std_enkf_set_int( void * arg , const char * var_name , int value) { + std_enkf_data_type * module_data = std_enkf_data_safe_cast( arg ); + { + bool name_recognized = true; + + if (strcmp( var_name , ENKF_NCOMP_KEY_) == 0) + std_enkf_set_subspace_dimension( module_data , value ); + else + name_recognized = false; + + return name_recognized; + } +} + + +bool std_enkf_set_bool( void * arg , const char * var_name , bool value) { + std_enkf_data_type * module_data = std_enkf_data_safe_cast( arg ); + { + bool name_recognized = true; + + if (strcmp( var_name , USE_EE_KEY_) == 0) + module_data->use_EE = value; + else if (strcmp( var_name , USE_GE_KEY_) == 0) + module_data->use_GE = value; + else if (strcmp( var_name , ANALYSIS_SCALE_DATA_KEY_) == 0) + module_data->analysis_scale_data = value; + else + name_recognized = false; + + return name_recognized; + } +} + + + +long std_enkf_get_options( void * arg , long flag ) { + std_enkf_data_type * module_data = std_enkf_data_safe_cast( arg ); + int scale_option = (module_data->analysis_scale_data) ? ANALYSIS_SCALE_DATA : 0; + return module_data->option_flags + scale_option; +} + +bool std_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 , USE_EE_KEY_) == 0) + return true; + else if (strcmp(var_name , USE_GE_KEY_) == 0) + return true; + else if (strcmp(var_name , ANALYSIS_SCALE_DATA_KEY_) == 0) + return true; + else + return false; + } +} + +double std_enkf_get_double( const void * arg, const char * var_name) { + const std_enkf_data_type * module_data = std_enkf_data_safe_cast_const( arg ); + { + if (strcmp(var_name , ENKF_TRUNCATION_KEY_) == 0) + return module_data->truncation; + else + return -1; + } +} + +int std_enkf_get_int( const void * arg, const char * var_name) { + const std_enkf_data_type * module_data = std_enkf_data_safe_cast_const( arg ); + { + if (strcmp(var_name , ENKF_NCOMP_KEY_) == 0) + return module_data->subspace_dimension; + else + return -1; + } +} + + +bool std_enkf_get_bool( const void * arg, const char * var_name) { + const std_enkf_data_type * module_data = std_enkf_data_safe_cast_const( arg ); + { + if (strcmp(var_name , USE_EE_KEY_) == 0) + return module_data->use_EE; + else if (strcmp(var_name , USE_GE_KEY_) == 0) + return module_data->use_GE; + else if (strcmp(var_name , ANALYSIS_SCALE_DATA_KEY_) == 0) + return module_data->analysis_scale_data; + else + return false; + } +} + + +/** + gcc -fpic -c -I?? + gcc -shared -o +*/ + + +#ifdef INTERNAL_LINK +#define LINK_NAME STD_ENKF +#else +#define LINK_NAME EXTERNAL_MODULE_SYMBOL +#endif + + +analysis_table_type LINK_NAME = { + .name = "STD_ENKF", + .updateA = NULL, + .initX = std_enkf_initX , + .init_update = NULL, + .complete_update = NULL, + + .freef = std_enkf_data_free, + .alloc = std_enkf_data_alloc, + + .set_int = std_enkf_set_int , + .set_double = std_enkf_set_double , + .set_bool = std_enkf_set_bool, + .set_string = NULL , + .get_options = std_enkf_get_options , + + .has_var = std_enkf_has_var, + .get_int = std_enkf_get_int, + .get_double = std_enkf_get_double, + .get_bool = std_enkf_get_bool, + .get_ptr = NULL, +}; + diff --git a/libres/lib/analysis/stepwise.cpp b/libres/lib/analysis/stepwise.cpp new file mode 100644 index 00000000000..b5d1fbd7293 --- /dev/null +++ b/libres/lib/analysis/stepwise.cpp @@ -0,0 +1,377 @@ +#include +#include + +#include +#include + +#include + +#include + + +struct stepwise_struct { + UTIL_TYPE_ID_DECLARATION; + + matrix_type * X0; // Externally supplied data. + matrix_type * E0; // Externally supplied data. + matrix_type * Y0; + + matrix_type * beta; // Quantities estimated by the stepwise algorithm + matrix_type * X_mean; + matrix_type * X_norm; + bool_vector_type * active_set; + rng_type * rng; // Needed in the cross-validation +}; + + + +static double stepwise_estimate__( stepwise_type * stepwise , bool_vector_type * active_rows) { + matrix_type * X; + matrix_type * E; + matrix_type * Y; + + double y_mean = 0; + int ncols = matrix_get_columns( stepwise->X0 ); + int nrows = matrix_get_rows( stepwise->X0 ); + + int nsample = bool_vector_count_equal( active_rows , true ); + int nvar = bool_vector_count_equal( stepwise->active_set , true ); + + + matrix_set( stepwise->beta , 0 ); // It is essential to make sure that old finite values in the beta0 vector do not hang around. + + + /* + Extracting the data used for regression, and storing them in the + temporary local matrices X and Y. Selecting data is based both on + which varibles are active (stepwise->active_set) and which rows + should be used for regression, versus which should be used for + validation (@active_rows). + */ + if ((nsample < nrows) || (nvar < ncols)) { + X = matrix_alloc( nsample , nvar ); + E = matrix_alloc( nsample , nvar ); + Y = matrix_alloc( nsample , 1); + + { + int icol,irow; // Running over all values. + int arow,acol; // Running over active values. + arow = 0; + for (irow = 0; irow < nrows; irow++) { + if (bool_vector_iget( active_rows , irow )) { + acol = 0; + for (icol = 0; icol < ncols; icol++) { + if (bool_vector_iget( stepwise->active_set , icol )) { + matrix_iset( X , arow , acol , matrix_iget( stepwise->X0 , irow , icol )); + matrix_iset( E , arow , acol , matrix_iget( stepwise->E0 , irow , icol )); + acol++; + } + } + + matrix_iset( Y , arow , 0 , matrix_iget( stepwise->Y0 , irow , 0 )); + arow++; + } + } + } + } else { + X = matrix_alloc_copy( stepwise->X0 ); + E = matrix_alloc_copy( stepwise->E0 ); + Y = matrix_alloc_copy( stepwise->Y0 ); + } + + + { + + if (stepwise->X_mean != NULL) + matrix_free( stepwise->X_mean); + + stepwise->X_mean = matrix_alloc( 1 , nvar ); + + if (stepwise->X_norm != NULL) + matrix_free( stepwise->X_norm); + + stepwise->X_norm = matrix_alloc( 1 , nvar ); + + matrix_type * beta = matrix_alloc( nvar , 1); /* This is the beta vector as estimated from the OLS estimator. */ + + regression_augmented_OLS( X , Y , E, beta ); + + + /* + In this code block the beta/tmp_beta vector which is dense with + fewer elements than the full model is scattered into the beta0 + vector which has full size and @nvar elements. + */ + { + int ivar,avar; + avar = 0; + for (ivar = 0; ivar < ncols; ivar++) { + if (bool_vector_iget( stepwise->active_set , ivar )) { + matrix_iset( stepwise->beta , ivar , 0 , matrix_iget( beta , avar , 0)); + avar++; + } + } + } + + + matrix_free( beta ); + } + + matrix_free( X ); + matrix_free( E ); + matrix_free( Y ); + return y_mean; +} + + + + +static double stepwise_eval__( const stepwise_type * stepwise , const matrix_type * x ) { + return matrix_row_column_dot_product( x , 0 , stepwise->beta , 0 ); +} + + + +static double stepwise_test_var( stepwise_type * stepwise , int test_var , int blocks) { + double prediction_error = 0; + + bool_vector_iset( stepwise->active_set , test_var , true ); // Temporarily activate this variable + { + + int nvar = matrix_get_columns( stepwise->X0 ); + int nsample = matrix_get_rows( stepwise->X0 ); + int block_size = nsample / blocks; + bool_vector_type * active_rows = bool_vector_alloc( nsample, true ); + + + + + + /*True Cross-Validation: */ + int * randperms = (int*)util_calloc( nsample , sizeof * randperms ); + for (int i=0; i < nsample; i++) + randperms[i] = i; + + /* Randomly perturb ensemble indices */ + rng_shuffle_int( stepwise->rng , randperms , nsample ); + + + for (int iblock = 0; iblock < blocks; iblock++) { + + int validation_start = iblock * block_size; + int validation_end = validation_start + block_size - 1; + + if (iblock == (blocks - 1)) + validation_end = nsample - 1; + + /* + Ensure that the active_rows vector has a block consisting of + the interval [validation_start : validation_end] which is set to + false, and the remaining part of the vector is set to true. + */ + { + bool_vector_set_all(active_rows, true); + /* + If blocks == 1 that means all datapoint are used in the + regression, and then subsequently reused in the R2 + calculation. + */ + if (blocks > 1) { + for (int i = validation_start; i <= validation_end; i++) { + bool_vector_iset( active_rows , randperms[i] , false ); + } + } + } + + + /* + Evaluate the prediction error on the validation part of the + dataset. + */ + { + stepwise_estimate__( stepwise , active_rows ); + { + int irow; + matrix_type * x_vector = matrix_alloc( 1 , nvar ); + //matrix_type * e_vector = matrix_alloc( 1 , nvar ); + for (irow=validation_start; irow <= validation_end; irow++) { + matrix_copy_row( x_vector , stepwise->X0 , 0 , randperms[irow]); + //matrix_copy_row( e_vector , stepwise->E0 , 0 , randperms[irow]); + { + double true_value = matrix_iget( stepwise->Y0 , randperms[irow] , 0 ); + double estimated_value = stepwise_eval__( stepwise , x_vector ); + prediction_error += (true_value - estimated_value) * (true_value - estimated_value); + //double e_estimated_value = stepwise_eval__( stepwise , e_vector ); + //prediction_error += e_estimated_value*e_estimated_value; + } + + } + matrix_free( x_vector ); + } + } + } + + free( randperms ); + bool_vector_free( active_rows ); + } + + /*inactivate the test_var-variable after completion*/ + bool_vector_iset( stepwise->active_set , test_var , false ); + return prediction_error; +} + + +void stepwise_estimate( stepwise_type * stepwise , double deltaR2_limit , int CV_blocks) { + int nvar = matrix_get_columns( stepwise->X0 ); + int nsample = matrix_get_rows( stepwise->X0 ); + double currentR2 = -1; + bool_vector_type * active_rows = bool_vector_alloc( nsample , true ); + + + /*Reset beta*/ + for (int i = 0; i < nvar; i++) { + matrix_iset(stepwise->beta, i , 0 , 0.0); + } + + + + bool_vector_set_all( stepwise->active_set , false ); + + double MSE_min = 10000000; + double Prev_MSE_min = MSE_min; + double minR2 = -1; + + while (true) { + int best_var = 0; + Prev_MSE_min = MSE_min; + + /* + Go through all the inactive variables, and calculate the + resulting prediction error IF this particular variable is added; + keep track of the variable which gives the lowest prediction error. + */ + for (int ivar = 0; ivar < nvar; ivar++) { + if (!bool_vector_iget( stepwise->active_set , ivar)) { + double newR2 = stepwise_test_var(stepwise , ivar , CV_blocks); + if ((minR2 < 0) || (newR2 < minR2)) { + minR2 = newR2; + best_var = ivar; + } + } + } + + /* + If the best relative improvement in prediction error is better + than @deltaR2_limit, the corresponding variable is added to the + active set, and we return to repeat the loop one more + time. Otherwise we just exit. + */ + + { + MSE_min = minR2; + double deltaR2 = MSE_min / Prev_MSE_min; + + if (( currentR2 < 0) || deltaR2 < deltaR2_limit) { + bool_vector_iset( stepwise->active_set , best_var , true ); + currentR2 = minR2; + bool_vector_set_all(active_rows, true); + stepwise_estimate__( stepwise , active_rows ); + } else { + /* The gain in prediction error is so small that we just leave the building. */ + /* NB! Need one final compuation of beta (since the test_var function does not reset the last tested beta value !) */ + bool_vector_set_all(active_rows, true); + stepwise_estimate__( stepwise , active_rows ); + break; + } + + if (bool_vector_count_equal( stepwise->active_set , true) == matrix_get_columns( stepwise->X0 )) { + stepwise_estimate__( stepwise , active_rows ); + break; /* All variables are active. */ + } + } + } + + bool_vector_free( active_rows ); +} + + + +double stepwise_eval( const stepwise_type * stepwise , const matrix_type * x ) { + double yHat = stepwise_eval__(stepwise, x ); + return yHat; +} + + + +static stepwise_type * stepwise_alloc__( int nsample , int nvar , rng_type * rng) { + stepwise_type * stepwise = (stepwise_type*)util_malloc( sizeof * stepwise ); + + stepwise->X_mean = NULL; + stepwise->X_norm = NULL; + stepwise->rng = rng; + stepwise->X0 = NULL; + stepwise->E0 = NULL; + stepwise->Y0 = NULL; + stepwise->active_set = bool_vector_alloc( nvar , true ); + stepwise->beta = matrix_alloc( nvar , 1 ); + + return stepwise; +} + + +stepwise_type * stepwise_alloc1( int nsample , int nvar, rng_type * rng, const matrix_type* St, const matrix_type* Et) { + stepwise_type * stepwise = stepwise_alloc__( nsample , nvar , rng); + + stepwise->rng = rng; + stepwise->X0 = matrix_alloc_copy(St); // It would be nice to get rid of these copies, but due to data race it is not possible at the moment + stepwise->E0 = matrix_alloc_copy(Et); + stepwise->Y0 = NULL; //matrix_alloc( nsample , 1 ); + + return stepwise; +} + + +void stepwise_set_Y0( stepwise_type * stepwise , matrix_type * Y) { + stepwise->Y0 = Y; +} + +int stepwise_get_n_active( stepwise_type * stepwise ) { + return bool_vector_count_equal( stepwise->active_set , true); +} + +bool_vector_type * stepwise_get_active_set( stepwise_type * stepwise ) { + return stepwise->active_set; +} + +double stepwise_iget_beta(const stepwise_type * stepwise, const int index ) { + return matrix_iget( stepwise->beta, index, 0); +} + +double stepwise_get_sum_beta(const stepwise_type * stepwise ) { + return matrix_get_column_abssum( stepwise->beta, 0); +} + +void stepwise_free( stepwise_type * stepwise ) { + if (stepwise->active_set != NULL) { + bool_vector_free( stepwise->active_set ); + } + + + if (stepwise->beta != NULL) + matrix_free( stepwise->beta ); + + + if (stepwise->X_mean != NULL) + matrix_free( stepwise->X_mean ); + + + if (stepwise->X_norm != NULL) + matrix_free( stepwise->X_norm ); + + + matrix_free( stepwise->X0 ); + matrix_free( stepwise->E0 ); + matrix_free( stepwise->Y0 ); + + free( stepwise ); +} diff --git a/libres/lib/analysis/tests/analysis_module_test.cpp b/libres/lib/analysis/tests/analysis_module_test.cpp new file mode 100644 index 00000000000..2d4c7d9652e --- /dev/null +++ b/libres/lib/analysis/tests/analysis_module_test.cpp @@ -0,0 +1,87 @@ +/* + Copyright (C) 2019 Equinor ASA, Norway. + + The file 'analysis_test_external_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 + + + +void test_invalid_mask_size() { + analysis_module_type * module = analysis_module_alloc_internal("STD_ENKF"); + int active_ens_size = 10; + int active_obs_size = 5; + rng_type * rng = NULL; + + matrix_type * S = matrix_alloc(active_obs_size, active_ens_size); + matrix_type * R = matrix_alloc(active_obs_size, active_obs_size); + matrix_type * dObs = matrix_alloc(active_obs_size, 2); + matrix_type * E = matrix_alloc(active_obs_size, active_obs_size); + matrix_type * D = matrix_alloc(active_obs_size, active_obs_size); + + bool_vector_type * ens_mask = bool_vector_alloc(active_ens_size + 1, true ); + bool_vector_type * obs_mask = bool_vector_alloc(active_obs_size + 1, true ); + + test_assert_throw( analysis_module_init_update(module, + ens_mask, + obs_mask, + S, + R, + dObs, + E, + D, + rng), + std::invalid_argument); + + bool_vector_iset(ens_mask, 0, false); + bool_vector_iset(obs_mask, 0, false); + + analysis_module_init_update(module, + ens_mask, + obs_mask, + S, + R, + dObs, + E, + D, + rng); + + bool_vector_free(ens_mask); + bool_vector_free(obs_mask); + + matrix_free(S); + matrix_free(R); + matrix_free(dObs); + matrix_free(E); + matrix_free(D); + + analysis_module_free( module ); +} + + + +int main(int argc, char **argv) { + test_invalid_mask_size(); +} diff --git a/libres/lib/analysis/tests/analysis_test_external_module.c b/libres/lib/analysis/tests/analysis_test_external_module.c new file mode 100644 index 00000000000..53a898276de --- /dev/null +++ b/libres/lib/analysis/tests/analysis_test_external_module.c @@ -0,0 +1,106 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'analysis_test_external_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 + + + +void test_set_get(analysis_module_type * module , const char * var_value) { + char * var , *string_value; + util_binary_split_string( var_value , ":" , false , &var , &string_value); + + if (var && string_value) { + printf("Testing variable:%s \n",var); + while (true) { + int int_value; + double double_value; + bool bool_value; + test_assert_true(analysis_module_has_var( module , var )); + + if (util_sscanf_int( string_value , &int_value)) { + test_assert_true(analysis_module_set_var( module , var , string_value )); + test_assert_int_equal( int_value , analysis_module_get_int( module , var )); + break; + } + + if (util_sscanf_double( string_value , &double_value)) { + test_assert_true(analysis_module_set_var( module , var , string_value )); + test_assert_double_equal( double_value , analysis_module_get_double( module , var )); + break; + } + + if (util_sscanf_bool( string_value , &bool_value)) { + test_assert_true(analysis_module_set_var( module , var , string_value )); + test_assert_bool_equal( bool_value , analysis_module_get_bool( module , var )); + break; + } + + + test_assert_true(analysis_module_set_var( module , var , string_value )); + test_assert_string_equal( string_value , (const char *) analysis_module_get_ptr( module , var )); + break; + } + } else { + fprintf(stderr,"Invalid test input data: %s -> could not split in var:value\n" , var_value); + exit(1); + } +} + + + +void load_module( rng_type * rng , const char * user_name , const char * lib_name, const char * options_str , int nvar , const char ** var_list) { + long flags = strtol(options_str , NULL , 10); + analysis_module_type * analysis_module = analysis_module_alloc_external(lib_name); + + printf("Loading:%s \n" , lib_name); + test_assert_string_equal( EXTERNAL_MODULE_NAME , analysis_module_get_table_name(analysis_module)); + if (util_is_abs_path(lib_name)) + test_assert_string_equal( lib_name , analysis_module_get_lib_name(analysis_module)); + + test_assert_true( analysis_module_is_instance( analysis_module)); + { + for (int i=0; i < nvar; i++) + test_set_get( analysis_module , var_list[i] ); + } + test_assert_false( analysis_module_has_var(analysis_module , "DoesNotHaveThisVariable")); + + test_assert_true( analysis_module_check_option( analysis_module , flags)); + flags += 1; + test_assert_false( analysis_module_check_option( analysis_module , flags)); + analysis_module_free( analysis_module); +} + + +int main(int argc , char ** argv) { + const char * user_name = argv[1]; + const char * lib_name = argv[2]; + const char * options_str = argv[3]; + int nvar = argc - 4; + rng_type * rng = rng_alloc( MZRAN , INIT_DEFAULT); + + load_module(rng , user_name , lib_name , options_str , nvar , (const char **) &argv[4]); + rng_free( rng ); + + exit(0); +} diff --git a/libres/lib/analysis/tests/analysis_test_external_module.cpp b/libres/lib/analysis/tests/analysis_test_external_module.cpp new file mode 100644 index 00000000000..53a898276de --- /dev/null +++ b/libres/lib/analysis/tests/analysis_test_external_module.cpp @@ -0,0 +1,106 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'analysis_test_external_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 + + + +void test_set_get(analysis_module_type * module , const char * var_value) { + char * var , *string_value; + util_binary_split_string( var_value , ":" , false , &var , &string_value); + + if (var && string_value) { + printf("Testing variable:%s \n",var); + while (true) { + int int_value; + double double_value; + bool bool_value; + test_assert_true(analysis_module_has_var( module , var )); + + if (util_sscanf_int( string_value , &int_value)) { + test_assert_true(analysis_module_set_var( module , var , string_value )); + test_assert_int_equal( int_value , analysis_module_get_int( module , var )); + break; + } + + if (util_sscanf_double( string_value , &double_value)) { + test_assert_true(analysis_module_set_var( module , var , string_value )); + test_assert_double_equal( double_value , analysis_module_get_double( module , var )); + break; + } + + if (util_sscanf_bool( string_value , &bool_value)) { + test_assert_true(analysis_module_set_var( module , var , string_value )); + test_assert_bool_equal( bool_value , analysis_module_get_bool( module , var )); + break; + } + + + test_assert_true(analysis_module_set_var( module , var , string_value )); + test_assert_string_equal( string_value , (const char *) analysis_module_get_ptr( module , var )); + break; + } + } else { + fprintf(stderr,"Invalid test input data: %s -> could not split in var:value\n" , var_value); + exit(1); + } +} + + + +void load_module( rng_type * rng , const char * user_name , const char * lib_name, const char * options_str , int nvar , const char ** var_list) { + long flags = strtol(options_str , NULL , 10); + analysis_module_type * analysis_module = analysis_module_alloc_external(lib_name); + + printf("Loading:%s \n" , lib_name); + test_assert_string_equal( EXTERNAL_MODULE_NAME , analysis_module_get_table_name(analysis_module)); + if (util_is_abs_path(lib_name)) + test_assert_string_equal( lib_name , analysis_module_get_lib_name(analysis_module)); + + test_assert_true( analysis_module_is_instance( analysis_module)); + { + for (int i=0; i < nvar; i++) + test_set_get( analysis_module , var_list[i] ); + } + test_assert_false( analysis_module_has_var(analysis_module , "DoesNotHaveThisVariable")); + + test_assert_true( analysis_module_check_option( analysis_module , flags)); + flags += 1; + test_assert_false( analysis_module_check_option( analysis_module , flags)); + analysis_module_free( analysis_module); +} + + +int main(int argc , char ** argv) { + const char * user_name = argv[1]; + const char * lib_name = argv[2]; + const char * options_str = argv[3]; + int nvar = argc - 4; + rng_type * rng = rng_alloc( MZRAN , INIT_DEFAULT); + + load_module(rng , user_name , lib_name , options_str , nvar , (const char **) &argv[4]); + rng_free( rng ); + + exit(0); +} diff --git a/libres/lib/analysis/tests/analysis_test_module_info.cpp b/libres/lib/analysis/tests/analysis_test_module_info.cpp new file mode 100644 index 00000000000..4f5353ffe37 --- /dev/null +++ b/libres/lib/analysis/tests/analysis_test_module_info.cpp @@ -0,0 +1,50 @@ +/* + Copyright (C) 2016 Equinor ASA, Norway. + + The file 'analysis_test_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 + + +int main(int argc , char ** argv) { + + const char* ministep_name = "SOME MINISTEP"; + module_info_type * module_info = module_info_alloc( ministep_name ); + test_assert_true( module_info_is_instance( module_info )); + + module_data_block_vector_type * module_data_block_vector = module_info_get_data_block_vector(module_info); + test_assert_true( module_data_block_vector_is_instance( module_data_block_vector )); + + module_obs_block_vector_type * module_obs_block_vector = module_info_get_obs_block_vector(module_info); + test_assert_true( module_obs_block_vector_is_instance( module_obs_block_vector )); + + int index_list[1] = { 1 }; + module_data_block_type * module_data_block = module_data_block_alloc( "PARAMETER", &index_list[0], 0, 1 ); + test_assert_true( module_data_block_is_instance( module_data_block )); + module_data_block_vector_add_data_block(module_data_block_vector, module_data_block); + + + module_obs_block_type * module_obs_block = module_obs_block_alloc( "OBS", &index_list[0], 0, 1 ); + test_assert_true( module_obs_block_is_instance( module_obs_block )); + module_obs_block_vector_add_obs_block(module_obs_block_vector, module_obs_block); + + module_info_free( module_info ); + exit(0); +} + + diff --git a/libres/lib/config/conf.cpp b/libres/lib/config/conf.cpp new file mode 100644 index 00000000000..b749a5c14db --- /dev/null +++ b/libres/lib/config/conf.cpp @@ -0,0 +1,1564 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'conf.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 + + + +/** S T R U C T D E F I N I T I O N S */ + + + +struct conf_class_struct +{ + const conf_class_type * super_class; /** Can be NULL. */ + char * class_name; + char * help; /** Can be NULL if not given. */ + bool require_instance; + bool singleton; + + hash_type * sub_classes; /** conf_class_types */ + hash_type * item_specs; /** conf_item_spec_types */ + vector_type * item_mutexes; /** item_mutex_types */ +}; + + + +struct conf_instance_struct +{ + const conf_class_type * conf_class; + char * name; + + hash_type * sub_instances; /** conf_instance_types */ + hash_type * items; /** conf_item_types */ +}; + + + +struct conf_item_spec_struct +{ + const conf_class_type * super_class; /** NULL if not inserted into a class. */ + char * name; + bool required_set; /** Require the item to take a valid value. */ + char * default_value; /** Can be NULL if not given. */ + dt_enum dt; /** Data type. See conf_data.* */ + std::set * restriction; + char * help; /** Can be NULL if not given. */ +}; + + + +struct conf_item_struct +{ + const conf_item_spec_type * conf_item_spec; + char * value; +}; + + + +struct conf_item_mutex_struct +{ + const conf_class_type * super_class; + bool require_one; + bool inverse; /* if inverse == true the 'mutex' implements: if A then ALSO B, C and D. */ + hash_type * item_spec_refs; +}; + + + +/** D E F A U L T A L L O C / F R E E F U N C T I O N S */ + + + +conf_class_type * conf_class_alloc_empty( + const char * class_name, + bool require_instance, + bool singleton, + const char * help) +{ + assert(class_name != NULL); + + conf_class_type * conf_class = (conf_class_type*)util_malloc(sizeof *conf_class); + + conf_class->super_class = NULL; + conf_class->class_name = util_alloc_string_copy(class_name); + conf_class->help = NULL; + conf_class->require_instance = require_instance; + conf_class->singleton = singleton; + conf_class->sub_classes = hash_alloc(); + conf_class->item_specs = hash_alloc(); + conf_class->item_mutexes = vector_alloc_new(); + + + conf_class_set_help( conf_class , help ); + return conf_class; +} + + + +void conf_class_free( + conf_class_type * conf_class) +{ + free(conf_class->class_name); + free(conf_class->help); + hash_free(conf_class->sub_classes); + hash_free(conf_class->item_specs); + vector_free(conf_class->item_mutexes); + free(conf_class); +} + + + +void conf_class_free__( + void * conf_class) +{ + conf_class_free( (conf_class_type *) conf_class); +} + + + +static conf_instance_type * conf_instance_alloc_default( + const conf_class_type * conf_class, + const char * name) +{ + assert(conf_class != NULL); + assert(name != NULL); + + conf_instance_type * conf_instance = (conf_instance_type*)util_malloc(sizeof * conf_instance); + + conf_instance->conf_class = conf_class; + conf_instance->name = util_alloc_string_copy(name); + conf_instance->sub_instances = hash_alloc(); + conf_instance->items = hash_alloc(); + { + /** Insert items that have a default value in their specs. */ + int num_item_specs = hash_get_size(conf_class->item_specs); + char ** item_spec_keys = hash_alloc_keylist(conf_class->item_specs); + + for(int item_spec_nr = 0; item_spec_nr < num_item_specs; item_spec_nr++) + { + const char * item_spec_name = item_spec_keys[item_spec_nr]; + const conf_item_spec_type * conf_item_spec = (const conf_item_spec_type*)hash_get(conf_class->item_specs, item_spec_name); + if(conf_item_spec->default_value != NULL) + { + conf_item_type * conf_item = conf_item_alloc(conf_item_spec, conf_item_spec->default_value); + conf_instance_insert_owned_item(conf_instance, conf_item); + } + } + + util_free_stringlist(item_spec_keys, num_item_specs); + } + + return conf_instance; +} + + + +static +void conf_instance_copy_items( + conf_instance_type * conf_instance_target, + const conf_instance_type * conf_instance_source) +{ + int num_items = hash_get_size(conf_instance_source->items); + char ** item_keys = hash_alloc_keylist(conf_instance_source->items); + + for(int item_nr = 0; item_nr < num_items; item_nr++) + { + const char * item_name = item_keys[item_nr]; + const conf_item_type * conf_item = (const conf_item_type*)hash_get(conf_instance_source->items, item_name); + conf_item_type * conf_item_copy = conf_item_copyc(conf_item); + conf_instance_insert_owned_item(conf_instance_target, conf_item_copy); + } + + util_free_stringlist(item_keys, num_items); +} + + + +static +void conf_instance_copy_sub_instances( + conf_instance_type * conf_instance_target, + const conf_instance_type * conf_instance_source) +{ + int num_sub_instances = hash_get_size(conf_instance_source->sub_instances); + char ** sub_instance_keys = hash_alloc_keylist(conf_instance_source->sub_instances); + + for(int sub_nr = 0; sub_nr < num_sub_instances; sub_nr++) + { + const char * sub_name = sub_instance_keys[sub_nr]; + const conf_instance_type * sub_conf_instance = (const conf_instance_type*)hash_get(conf_instance_source->sub_instances, sub_name); + conf_instance_type * sub_conf_instance_copy = conf_instance_copyc(sub_conf_instance); + conf_instance_insert_owned_sub_instance(conf_instance_target, sub_conf_instance_copy); + } + + util_free_stringlist(sub_instance_keys, num_sub_instances); +} + + + +conf_instance_type * conf_instance_copyc( + const conf_instance_type * conf_instance) +{ + conf_instance_type * conf_instance_copy = (conf_instance_type*)util_malloc(sizeof * conf_instance_copy); + + conf_instance_copy->conf_class = conf_instance->conf_class; + conf_instance_copy->name = util_alloc_string_copy(conf_instance->name); + conf_instance_copy->sub_instances = hash_alloc(); + conf_instance_copy->items = hash_alloc(); + + conf_instance_copy_items( conf_instance_copy, conf_instance); + conf_instance_copy_sub_instances(conf_instance_copy, conf_instance); + + return conf_instance_copy; +} + + + +void conf_instance_free( + conf_instance_type * conf_instance) +{ + free(conf_instance->name); + hash_free(conf_instance->sub_instances); + hash_free(conf_instance->items); + free(conf_instance); +} + + + +void conf_instance_free__( + void * conf_instance) +{ + conf_instance_free( (conf_instance_type *) conf_instance); +} + + + +conf_item_spec_type * conf_item_spec_alloc( + const char * name, + bool required_set, + dt_enum dt, + const char * help) +{ + assert(name != NULL); + + conf_item_spec_type * conf_item_spec = (conf_item_spec_type*)util_malloc(sizeof * conf_item_spec); + + conf_item_spec->super_class = NULL; + conf_item_spec->name = util_alloc_string_copy(name); + conf_item_spec->required_set = required_set; + conf_item_spec->dt = dt; + conf_item_spec->default_value = NULL; + conf_item_spec->help = NULL; + conf_item_spec_set_help( conf_item_spec , help ); + + conf_item_spec->restriction = new std::set(); + return conf_item_spec; +} + + + +void conf_item_spec_free( + conf_item_spec_type * conf_item_spec) +{ + free(conf_item_spec->name); + free(conf_item_spec->default_value); + free(conf_item_spec->help); + delete conf_item_spec->restriction; + free(conf_item_spec); +} + + + +void conf_item_spec_free__( + void * conf_item_spec) +{ + conf_item_spec_free( (conf_item_spec_type *) conf_item_spec); +} + + + +conf_item_type * conf_item_alloc( + const conf_item_spec_type * conf_item_spec, + const char * value) +{ + conf_item_type * conf_item = (conf_item_type*)util_malloc(sizeof * conf_item); + + assert(conf_item_spec != NULL); + assert(value != NULL); + + conf_item->conf_item_spec = conf_item_spec; + if (conf_item_spec->dt == DT_FILE) + conf_item->value = util_alloc_abs_path( value ); + else + conf_item->value = util_alloc_string_copy(value); + + return conf_item; +} + + + +conf_item_type * conf_item_copyc( + const conf_item_type * conf_item) +{ + return conf_item_alloc(conf_item->conf_item_spec, conf_item->value); +} + + + +void conf_item_free( + conf_item_type * conf_item) +{ + free(conf_item->value); + free(conf_item); +} + + + +void conf_item_free__( + void * conf_item) +{ + conf_item_free( (conf_item_type *) conf_item); +} + + + +static +conf_item_mutex_type * conf_item_mutex_alloc( + const conf_class_type * super_class, + bool require_one, + bool inverse) +{ + conf_item_mutex_type * conf_item_mutex = (conf_item_mutex_type*)util_malloc(sizeof * conf_item_mutex); + + conf_item_mutex->super_class = super_class; + conf_item_mutex->require_one = require_one; + conf_item_mutex->inverse = inverse; + conf_item_mutex->item_spec_refs = hash_alloc(); + + return conf_item_mutex; +} + + + +void conf_item_mutex_free( + conf_item_mutex_type * conf_item_mutex) +{ + hash_free(conf_item_mutex->item_spec_refs); + free(conf_item_mutex); +} + + + +void conf_item_mutex_free__( + void * conf_item_mutex) +{ + conf_item_mutex_free( (conf_item_mutex_type *) conf_item_mutex); +} + + + + + + +/** M A N I P U L A T O R S , I N S E R T I O N */ + + + +static +bool conf_class_has_super_class( + const conf_class_type * conf_class, + const conf_class_type * super_class) +{ + assert(conf_class != NULL); + assert(super_class != NULL); + + const conf_class_type * parent = conf_class->super_class; + + while(parent != NULL) + { + if(parent == super_class) + return true; + else + parent = parent->super_class; + } + return false; +} + + + +void conf_class_insert_owned_sub_class( + conf_class_type * conf_class, + conf_class_type * sub_conf_class) +{ + assert(conf_class != NULL); + assert(sub_conf_class != NULL); + + /** Abort if conf_class already has an item with the same name. */ + if(hash_has_key(conf_class->item_specs, sub_conf_class->class_name)) + util_abort("%s: Internal error. conf class already has an item with name \"%s\".\n", + __func__, sub_conf_class->class_name); + + /** Abort if sub_conf_class is equal to conf_class. */ + if(sub_conf_class == conf_class) + util_abort("%s: Internal error. Trying to make a class it's own super class.\n", __func__); + + /** Abort if sub_conf_class is a super class to conf_class. */ + if(conf_class_has_super_class(conf_class, sub_conf_class)) + util_abort("%s: Internal error. Trying to make a class it's own super class .\n", __func__); + + /** Abort if sub_conf_class already has a super class. */ + if(sub_conf_class->super_class != NULL) + util_abort("%s: Internal error. Inserted class already has a super class.\n", __func__); + + hash_insert_hash_owned_ref(conf_class->sub_classes, sub_conf_class->class_name, + sub_conf_class, conf_class_free__); + + sub_conf_class->super_class = conf_class; +} + + + +void conf_class_insert_owned_item_spec( + conf_class_type * conf_class, + conf_item_spec_type * item_spec) +{ + assert(conf_class != NULL); + assert(item_spec != NULL); + + + /** Abort if item_spec already has a super class. */ + if(item_spec->super_class != NULL) + util_abort("%s: Internal error: item is already assigned to another class.\n", __func__); + + + /** Abort if the class has a sub class with the same name.. */ + if(hash_has_key(conf_class->sub_classes, item_spec->name)) + util_abort("%s: Internal error. conf class already has a sub class with name \"%s\".\n", __func__, item_spec->name); + + + hash_insert_hash_owned_ref(conf_class->item_specs, item_spec->name, + item_spec, conf_item_spec_free__); + + item_spec->super_class = conf_class; +} + + + +conf_item_mutex_type * conf_class_new_item_mutex(conf_class_type * conf_class , bool require_one , bool inverse) +{ + assert(conf_class != NULL); + conf_item_mutex_type * mutex = conf_item_mutex_alloc( conf_class , require_one , inverse); + vector_append_owned_ref(conf_class->item_mutexes, mutex, conf_item_mutex_free__); + return mutex; +} + + + +void conf_instance_insert_owned_sub_instance( + conf_instance_type * conf_instance, + conf_instance_type * sub_conf_instance) +{ + assert(conf_instance != NULL); + assert(sub_conf_instance != NULL); + + /** Abort if the instance is of unknown type. */ + if(sub_conf_instance->conf_class->super_class != conf_instance->conf_class) + util_abort("%s: Internal error. Trying to insert instance of unknown type.\n", __func__); + + + /** Check if the instance's class is singleton. If so, remove the old instance. */ + if(sub_conf_instance->conf_class->singleton) + { + stringlist_type * instances = conf_instance_alloc_list_of_sub_instances_of_class(conf_instance, + sub_conf_instance->conf_class); + int num_instances = stringlist_get_size(instances); + + for(int i = 0; i < num_instances; i++) + { + const char * key = stringlist_iget(instances, i); + printf("WARNING: Class \"%s\" is of singleton type. Overwriting instance \"%s\" with \"%s\".\n", + sub_conf_instance->conf_class->class_name, key, sub_conf_instance->name); + hash_del(conf_instance->sub_instances, key); + } + + stringlist_free(instances); + } + + + /** Warn if the sub_instance already exists and is overwritten. */ + if(hash_has_key(conf_instance->sub_instances, sub_conf_instance->name)) + { + printf("WARNING: Overwriting instance \"%s\" of class \"%s\" in instance \"%s\" of class \"%s\"\n", + sub_conf_instance->name, conf_instance_get_class_name_ref(sub_conf_instance), + conf_instance->name, conf_instance_get_class_name_ref(conf_instance)); + } + + hash_insert_hash_owned_ref(conf_instance->sub_instances, + sub_conf_instance->name, + sub_conf_instance, + conf_instance_free__); +} + + + +void conf_instance_insert_owned_item( + conf_instance_type * conf_instance, + conf_item_type * conf_item) +{ + assert(conf_instance != NULL); + assert(conf_item != NULL); + + const char * item_name = conf_item->conf_item_spec->name; + + /** Check that the inserted item is of known type. */ + { + const hash_type * item_spec_hash = conf_instance->conf_class->item_specs; + const conf_item_spec_type * conf_item_spec = (const conf_item_spec_type*)hash_get(item_spec_hash, item_name); + if(conf_item_spec != conf_item->conf_item_spec) + util_abort("%s: Internal error.\n", __func__); + } + + hash_insert_hash_owned_ref(conf_instance->items, item_name, conf_item, conf_item_free__); +} + + + +void conf_instance_insert_item( + conf_instance_type * conf_instance, + const char * item_name, + const char * value) +{ + assert(conf_instance != NULL); + assert(item_name != NULL); + assert(value != NULL); + + conf_item_type * conf_item; + const conf_class_type * conf_class = conf_instance->conf_class; + const conf_item_spec_type * conf_item_spec; + + if(!conf_class_has_item_spec(conf_class, item_name)) + util_abort("%s: Internal error. Unkown item \"%s\" in class \"%s\".\n", + __func__, item_name, conf_instance->conf_class->class_name); + + conf_item_spec = conf_class_get_item_spec_ref(conf_class, item_name); + + conf_item = conf_item_alloc(conf_item_spec, value); + conf_instance_insert_owned_item(conf_instance, conf_item); +} + + +void conf_item_mutex_add_item_spec( + conf_item_mutex_type * conf_item_mutex, + const conf_item_spec_type * conf_item_spec) +{ + + if(conf_item_mutex->super_class != NULL) + { + const conf_class_type * conf_class = conf_item_mutex->super_class; + const char * item_key = conf_item_spec->name; + + if(!hash_has_key(conf_class->item_specs, item_key)) + { + util_abort("%s: Internal error. Trying to insert a mutex on item \"%s\", which class \"%s\" does not have.\n", + __func__, item_key, conf_class->class_name); + } + else + { + const conf_item_spec_type * conf_item_spec_class = (const conf_item_spec_type*)hash_get(conf_class->item_specs, item_key); + if(conf_item_spec_class != conf_item_spec) + { + util_abort("Internal error. Trying to insert a mutex on item \"%s\", which class \"%s\" has a different implementation of.\n", + __func__, item_key, conf_class->class_name); + } + } + } + + if(conf_item_mutex->require_one && conf_item_spec->required_set) + util_abort("%s: Trying to add item \"%s\" to a mutex, but it is required set!\n", + __func__, conf_item_spec->name); + + hash_insert_ref(conf_item_mutex->item_spec_refs, conf_item_spec->name, conf_item_spec); +} + + + +/** M A N I P U L A T O R S , C L A S S A N D I T E M S P E C I F I C A T I O N */ + + + +void conf_class_set_help( + conf_class_type * conf_class, + const char * help) +{ + conf_class->help = util_realloc_string_copy(conf_class->help , help); +} + + + +void conf_item_spec_add_restriction( + conf_item_spec_type * conf_item_spec, + const char * restriction) +{ + conf_item_spec->restriction->insert(restriction); +} + + + +void conf_item_spec_set_default_value( + conf_item_spec_type * conf_item_spec, + const char * default_value) +{ + if(conf_item_spec->default_value != NULL) + free(conf_item_spec->default_value); + if(default_value != NULL) + conf_item_spec->default_value = util_alloc_string_copy(default_value); + else + conf_item_spec->default_value = NULL; +} + + + +void conf_item_spec_set_help( + conf_item_spec_type * conf_item_spec, + const char * help) +{ + conf_item_spec->help = util_realloc_string_copy( conf_item_spec->help , help); +} + + + + +/** A C C E S S O R S */ + + + +bool conf_class_has_item_spec( + const conf_class_type * conf_class, + const char * item_name) +{ + if(!hash_has_key(conf_class->item_specs, item_name)) + return false; + else + return true; +} + + + +bool conf_class_has_sub_class( + const conf_class_type * conf_class, + const char * sub_class_name) +{ + if(!hash_has_key(conf_class->sub_classes, sub_class_name)) + return false; + else + return true; +} + + + +const conf_item_spec_type * conf_class_get_item_spec_ref( + const conf_class_type * conf_class, + const char * item_name) +{ + if(!hash_has_key(conf_class->item_specs, item_name)) + util_abort("%s: Internal error.\n", __func__); + + return (const conf_item_spec_type*)hash_get(conf_class->item_specs, item_name); +} + + + +const conf_class_type * conf_class_get_sub_class_ref( + const conf_class_type * conf_class, + const char * sub_class_name) +{ + if(!hash_has_key(conf_class->sub_classes, sub_class_name)) + util_abort("%s: Internal error.\n", __func__); + + return (const conf_class_type*)hash_get(conf_class->sub_classes, sub_class_name); +} + + + +const char * conf_instance_get_name_ref( + const conf_instance_type * conf_instance) +{ + return conf_instance->name; +} + + + +bool conf_instance_is_of_class( + const conf_instance_type * conf_instance, + const char * class_name) +{ + if(strcmp(conf_instance->conf_class->class_name, class_name) == 0) + return true; + else + return false; +} + + + +bool conf_instance_has_item( + const conf_instance_type * conf_instance, + const char * item_name) +{ + if(!hash_has_key(conf_instance->items, item_name)) + return false; + else + return true; +} + + +const conf_instance_type * conf_instance_get_sub_instance_ref( + const conf_instance_type * conf_instance, + const char * sub_instance_name) +{ + if(!hash_has_key(conf_instance->sub_instances, sub_instance_name)) + { + util_abort("%s: Instance %s of type %s has no sub instance named %s.\n", + __func__, conf_instance->name, + conf_instance->conf_class->class_name, + sub_instance_name); + } + return (const conf_instance_type*)hash_get(conf_instance->sub_instances, sub_instance_name); +} + + + +stringlist_type * conf_instance_alloc_list_of_sub_instances_of_class( + const conf_instance_type * conf_instance, + const conf_class_type * conf_class) +{ + stringlist_type * instances = stringlist_alloc_new(); + int num_sub_instances = hash_get_size(conf_instance->sub_instances); + char ** sub_instance_keys = hash_alloc_keylist(conf_instance->sub_instances); + + for(int key_nr = 0; key_nr < num_sub_instances; key_nr++) + { + const conf_instance_type * sub_instance = (const conf_instance_type*)hash_get(conf_instance->sub_instances, + sub_instance_keys[key_nr]); + + const conf_class_type * sub_instance_class = sub_instance->conf_class; + + if(sub_instance_class == conf_class) + stringlist_append_copy(instances, sub_instance_keys[key_nr]); + } + + util_free_stringlist(sub_instance_keys, num_sub_instances); + + return instances; +} + + + +stringlist_type * conf_instance_alloc_list_of_sub_instances_of_class_by_name( + const conf_instance_type * conf_instance, + const char * sub_class_name) +{ + if(!conf_class_has_sub_class(conf_instance->conf_class, sub_class_name)) + util_abort("%s: Instance \"%s\" is of class \"%s\" which has no sub class with name \"%s\"\n", + conf_instance->name, conf_instance->conf_class->class_name, sub_class_name); + + const conf_class_type * conf_class = conf_class_get_sub_class_ref(conf_instance->conf_class, sub_class_name); + + return conf_instance_alloc_list_of_sub_instances_of_class(conf_instance, conf_class); +} + + +const char * conf_instance_get_class_name_ref( + const conf_instance_type * conf_instance) +{ + return conf_instance->conf_class->class_name; +} + + + +const char * conf_instance_get_item_value_ref( + const conf_instance_type * conf_instance, + const char * item_name) +{ + if(!hash_has_key(conf_instance->items, item_name)) + { + util_abort("%s: Instance %s of type %s has no item %s.\n", + __func__, conf_instance->name, + conf_instance->conf_class->class_name, + item_name); + } + const conf_item_type * conf_item = (const conf_item_type*)hash_get(conf_instance->items, item_name); + return conf_item->value; +} + + + +/** If the dt supports it, this function shall return the item value as an int. + If the dt does not support it, the function will abort. +*/ +int conf_instance_get_item_value_int( + const conf_instance_type * conf_instance, + const char * item_name) +{ + if(!hash_has_key(conf_instance->items, item_name)) + util_abort("%s: Instance %s of type %s has no item %s.\n", + __func__, conf_instance->name, + conf_instance->conf_class->class_name, + item_name); + + const conf_item_type * conf_item = (const conf_item_type*)hash_get(conf_instance->items, item_name); + const conf_item_spec_type * conf_item_spec = conf_item->conf_item_spec; + + return conf_data_get_int_from_string(conf_item_spec->dt, conf_item->value); +} + + + +/** If the dt supports it, this function shall return the item value as a double. + If the dt does not support it, the function will abort. +*/ +double conf_instance_get_item_value_double( + const conf_instance_type * conf_instance, + const char * item_name) +{ + if(!hash_has_key(conf_instance->items, item_name)) + util_abort("%s: Instance %s of type %s has no item %s.\n", + __func__, conf_instance->name, + conf_instance->conf_class->class_name, + item_name); + + const conf_item_type * conf_item = (const conf_item_type*)hash_get(conf_instance->items, item_name); + const conf_item_spec_type * conf_item_spec = (const conf_item_spec_type*)conf_item->conf_item_spec; + + return conf_data_get_double_from_string(conf_item_spec->dt, conf_item->value); +} + + + +/** If the dt supports it, this function shall return the item value as a time_t. + If the dt does not support it, the function will abort. +*/ +time_t conf_instance_get_item_value_time_t( + const conf_instance_type * conf_instance, + const char * item_name) +{ + if(!hash_has_key(conf_instance->items, item_name)) + util_abort("%s: Instance %s of type %s has no item %s.\n", + __func__, conf_instance->name, + conf_instance->conf_class->class_name, + item_name); + + const conf_item_type * conf_item = (const conf_item_type*)hash_get(conf_instance->items, item_name); + const conf_item_spec_type * conf_item_spec = conf_item->conf_item_spec; + + return conf_data_get_time_t_from_string(conf_item_spec->dt, conf_item->value); +} + + + + + +/** V A L I D A T O R S */ + + + +static +void conf_item_spec_printf_help( + const conf_item_spec_type * conf_item_spec) +{ + assert(conf_item_spec->super_class != NULL); + int num_restrictions = conf_item_spec->restriction->size(); + + printf("\n Help on item \"%s\" in class \"%s\":\n\n", + conf_item_spec->name, conf_item_spec->super_class->class_name); + printf(" - Data type : %s\n\n", conf_data_get_dt_name_ref(conf_item_spec->dt)); + if(conf_item_spec->default_value != NULL) + printf(" - Default value: %s\n\n", conf_item_spec->default_value); + if(conf_item_spec->help != NULL) + printf(" - %s\n", conf_item_spec->help); + + if(num_restrictions > 0) + { + printf("\n The item \"%s\" is restricted to the following values:\n\n", + conf_item_spec->name); + int i=0; + for (auto iter=conf_item_spec->restriction->begin(); iter != conf_item_spec->restriction->end(); ++iter, ++i) + printf(" %i. %s\n", i+1, iter->c_str()); + } + printf("\n"); +} + + + +static +void conf_class_printf_help( + const conf_class_type * conf_class) +{ + /** TODO Should print info on the required sub classes and items. */ + + if(conf_class->help != NULL) + { + if(conf_class->super_class != NULL) + printf("\n Help on class \"%s\" with super class \"%s\":\n\n", + conf_class->class_name, conf_class->super_class->class_name); + else + printf("\n Help on class \"%s\":\n\n", + conf_class->class_name); + + printf(" %s\n", conf_class->help); + } + printf("\n"); +} + + + +static +bool conf_item_validate( + const conf_item_type * conf_item) +{ + assert(conf_item != NULL); + + bool ok = true; + const conf_item_spec_type * conf_item_spec = conf_item->conf_item_spec; + int num_restrictions = conf_item_spec->restriction->size();; + + if(!conf_data_validate_string_as_dt_value(conf_item_spec->dt, conf_item->value)) + { + ok = false; + printf("ERROR: Failed to validate \"%s\" as a %s for item \"%s\".\n", + conf_item->value, conf_data_get_dt_name_ref(conf_item_spec->dt), + conf_item_spec->name); + } + + if(num_restrictions > 0 && ok) + { + std::vector restriction_keys; + for (auto iter = conf_item_spec->restriction->begin(); iter != conf_item_spec->restriction->end(); ++iter) + restriction_keys.push_back(*iter); + + /** Legacy work-around when removing the vector supprt. */ + const int num_tokens = 1; + const char ** tokens = (const char **) &conf_item->value; + + for(int token_nr = 0; token_nr < num_tokens; token_nr++) + { + bool valid = false; + + for(int key_nr = 0; key_nr < num_restrictions; key_nr++) + { + if(strcmp(tokens[token_nr], restriction_keys[key_nr].c_str()) == 0) + valid = true; + } + + if(valid == false) + { + ok = false; + printf("ERROR: Failed to validate \"%s\" as a valid value for item \"%s\".\n", + conf_item->value, conf_item_spec->name); + } + } + } + + if(!ok) + conf_item_spec_printf_help(conf_item_spec); + + + return ok; +} + + + +static +bool conf_instance_has_required_items( + const conf_instance_type * conf_instance) +{ + bool ok = true; + const conf_class_type * conf_class = conf_instance->conf_class; + + int num_item_specs = hash_get_size(conf_class->item_specs); + char ** item_spec_keys = hash_alloc_keylist(conf_class->item_specs); + + for(int item_spec_nr = 0; item_spec_nr < num_item_specs; item_spec_nr++) + { + const char * item_spec_name = item_spec_keys[item_spec_nr]; + const conf_item_spec_type * conf_item_spec = (const conf_item_spec_type*)hash_get(conf_class->item_specs, item_spec_name); + if(conf_item_spec->required_set) + { + if(!hash_has_key(conf_instance->items, item_spec_name)) + { + ok = false; + printf("ERROR: Missing item \"%s\" in instance \"%s\" of class \"%s\"\n", + item_spec_name, conf_instance->name, conf_instance->conf_class->class_name); + conf_item_spec_printf_help(conf_item_spec); + } + } + } + + util_free_stringlist(item_spec_keys, num_item_specs); + + return ok; +} + + + +static +bool conf_instance_has_valid_items( + const conf_instance_type * conf_instance) +{ + bool ok = true; + + int num_items = hash_get_size(conf_instance->items); + char ** item_keys = hash_alloc_keylist(conf_instance->items); + + for(int item_nr = 0; item_nr < num_items; item_nr++) + { + const conf_item_type * conf_item = (const conf_item_type*)hash_get(conf_instance->items, item_keys[item_nr]); + if(!conf_item_validate(conf_item)) + ok = false; + } + + util_free_stringlist(item_keys, num_items); + + return ok; +} + + +static +bool conf_instance_check_item_mutex( + const conf_instance_type * conf_instance, + const conf_item_mutex_type * conf_item_mutex) +{ + std::set items_set; + bool ok = true; + int num_items_set = 0; + int num_items = hash_get_size(conf_item_mutex->item_spec_refs); + char ** item_keys = hash_alloc_keylist(conf_item_mutex->item_spec_refs); + + for(int item_nr = 0; item_nr < num_items; item_nr++) + { + const char * item_key = item_keys[item_nr]; + if(conf_instance_has_item(conf_instance, item_key)) + { + items_set.insert( item_key ); + } + } + + num_items_set = items_set.size(); + + if (conf_item_mutex->inverse) + { + /** This is an inverse mutex - all (or none) items should be set. */ + if (!((num_items_set == 0) || (num_items_set == num_items))) + { + std::vector items_set_keys; + ok = false; + for (const auto& key : items_set) + items_set_keys.push_back(key); + + printf("ERROR: Failed to validate mutal inclusion in instance \"%s\" of class \"%s\".\n\n", + conf_instance->name, conf_instance->conf_class->class_name); + printf(" When using one or more of the following items, all must be set:\n"); + + for(int item_nr = 0; item_nr < num_items; item_nr++) + printf(" %i : %s\n", item_nr, item_keys[item_nr]); + + printf("\n"); + printf(" However, only the following items were set:\n"); + + for(int item_nr = 0; item_nr < num_items_set; item_nr++) + printf(" %i : %s\n", item_nr, items_set_keys[item_nr].c_str()); + + printf("\n"); + } + } + else + { + if(num_items_set > 1) + { + std::vector items_set_keys; + ok = false; + for (const auto& key : items_set) + items_set_keys.push_back(key); + + printf("ERROR: Failed to validate mutal inclusion in instance \"%s\" of class \"%s\".\n\n", + conf_instance->name, conf_instance->conf_class->class_name); + printf("ERROR: Failed to validate mutex in instance \"%s\" of class \"%s\".\n\n", + conf_instance->name, conf_instance->conf_class->class_name); + printf(" Only one of the following items may be set:\n"); + for(int item_nr = 0; item_nr < num_items; item_nr++) + printf(" %i : %s\n", item_nr, item_keys[item_nr]); + + printf("\n"); + printf(" However, all the following items were set:\n"); + for(int item_nr = 0; item_nr < num_items_set; item_nr++) + printf(" %i : %s\n", item_nr, items_set_keys[item_nr].c_str()); + printf("\n"); + } + } + + if(num_items_set == 0 && conf_item_mutex->require_one && num_items > 0) + { + ok = false; + printf("ERROR: Failed to validate mutex in instance \"%s\" of class \"%s\".\n\n", + conf_instance->name, conf_instance->conf_class->class_name); + printf(" One of the following items MUST be set:\n"); + for(int item_nr = 0; item_nr < num_items; item_nr++) + printf(" %i : %s\n", item_nr, item_keys[item_nr]); + printf("\n"); + } + + util_free_stringlist(item_keys, num_items); + return ok; +} + + + +static +bool conf_instance_has_valid_mutexes( + const conf_instance_type * conf_instance) +{ + bool ok = true; + const conf_class_type * conf_class = conf_instance->conf_class; + const vector_type * item_mutexes = conf_class->item_mutexes; + int num_mutexes = vector_get_size(item_mutexes); + + for(int mutex_nr = 0; mutex_nr < num_mutexes; mutex_nr++) + { + const conf_item_mutex_type * conf_item_mutex = (const conf_item_mutex_type*)vector_iget( item_mutexes, mutex_nr ); + if(!conf_instance_check_item_mutex(conf_instance, conf_item_mutex)) + ok = false; + } + + return ok; +} + +static bool __instance_has_sub_instance_of_type( + const conf_class_type * __conf_class, int num_sub_instances, conf_class_type ** class_signatures) +{ + for(int sub_instance_nr = 0; sub_instance_nr < num_sub_instances; sub_instance_nr++) + { + if(class_signatures[sub_instance_nr] == __conf_class) + return true; + } + return false; +} + +static +bool conf_instance_has_required_sub_instances( + const conf_instance_type * conf_instance) +{ + /** U G L Y B U G L Y U G L Y B U G LY U G L Y B U G LY U G L Y B U G LY */ + /** This function is really ugly. It could be smoother if set_type supported size_t's. */ + /** U G L Y B U G L Y U G L Y B U G LY U G L Y B U G LY U G L Y B U G LY */ + + bool ok = true; + + + /** This first part is just concerned with creating the function __instance_has_sub_instance_of_type. */ + int num_sub_instances = hash_get_size(conf_instance->sub_instances); + conf_class_type ** class_signatures = (conf_class_type**)util_calloc(num_sub_instances , sizeof * class_signatures); + { + char ** sub_instance_keys = hash_alloc_keylist(conf_instance->sub_instances); + for(int sub_instance_nr = 0; sub_instance_nr < num_sub_instances; sub_instance_nr++) + { + const char * sub_instance_name = sub_instance_keys[sub_instance_nr]; + const conf_instance_type * sub_conf_instance = (const conf_instance_type*)hash_get(conf_instance->sub_instances, sub_instance_name); + class_signatures[sub_instance_nr] = (conf_class_type *) sub_conf_instance->conf_class; + } + util_free_stringlist(sub_instance_keys, num_sub_instances); + } + + + + + + + /** OK, we now check that the sub classes that have require_instance true have at least one instance. */ + { + const conf_class_type * conf_class = conf_instance->conf_class; + int num_sub_classes = hash_get_size(conf_class->sub_classes); + char ** sub_class_keys = hash_alloc_keylist(conf_class->sub_classes); + + for(int sub_class_nr = 0; sub_class_nr < num_sub_classes; sub_class_nr ++) + { + const char * sub_class_name = sub_class_keys[sub_class_nr]; + const conf_class_type * sub_conf_class = (const conf_class_type*)hash_get(conf_class->sub_classes, sub_class_name); + if(sub_conf_class->require_instance) + { + if(!__instance_has_sub_instance_of_type(sub_conf_class, num_sub_instances, class_signatures)) + { + printf("ERROR: Missing required instance of sub class \"%s\" in instance \"%s\" of class \"%s\".\n", + sub_conf_class->class_name, conf_instance->name, conf_instance->conf_class->class_name); + conf_class_printf_help(sub_conf_class); + ok = false; + } + } + } + + util_free_stringlist(sub_class_keys, num_sub_classes); + } + + free(class_signatures); + + return ok; +} + + + +static +bool conf_instance_validate_sub_instances( + const conf_instance_type * conf_instance) +{ + bool ok = true; + + int num_sub_instances = hash_get_size(conf_instance->sub_instances); + char ** sub_instance_keys = hash_alloc_keylist(conf_instance->sub_instances); + + for(int sub_instance_nr = 0; sub_instance_nr < num_sub_instances; sub_instance_nr++) + { + const char * sub_instances_key = sub_instance_keys[sub_instance_nr]; + const conf_instance_type * sub_conf_instance = (const conf_instance_type*)hash_get(conf_instance->sub_instances, sub_instances_key); + if(!conf_instance_validate(sub_conf_instance)) + ok = false; + } + + util_free_stringlist(sub_instance_keys, num_sub_instances); + + return ok; +} + + +bool conf_instance_get_path_error(const conf_instance_type * conf_instance) { + bool path_errors = false; + { + int num_items = hash_get_size(conf_instance->items); + char ** item_keys = hash_alloc_keylist(conf_instance->items); + + for(int item_nr = 0; item_nr < num_items; item_nr++) { + const conf_item_type * conf_item = (const conf_item_type*)hash_get(conf_instance->items, item_keys[item_nr]); + const conf_item_spec_type * conf_item_spec = conf_item->conf_item_spec; + if (conf_item_spec->dt == DT_FILE) { + if (!util_file_exists(conf_item->value)) + path_errors = true; + } + } + util_free_stringlist(item_keys, num_items); + } + + { + int num_sub_instances = hash_get_size(conf_instance->sub_instances); + char ** sub_instance_keys = hash_alloc_keylist(conf_instance->sub_instances); + + for(int sub_instance_nr = 0; sub_instance_nr < num_sub_instances; sub_instance_nr++) + { + const char * sub_instances_key = sub_instance_keys[sub_instance_nr]; + const conf_instance_type * sub_conf_instance = (const conf_instance_type*)hash_get(conf_instance->sub_instances, sub_instances_key); + if (conf_instance_get_path_error(sub_conf_instance)) + path_errors = true; + } + + util_free_stringlist(sub_instance_keys, num_sub_instances); + } + + return path_errors; +} + + +bool conf_instance_validate( + const conf_instance_type * conf_instance) { + return conf_instance + && conf_instance_has_required_items(conf_instance) + && conf_instance_has_valid_mutexes(conf_instance) + && conf_instance_has_valid_items(conf_instance) + && conf_instance_has_required_sub_instances(conf_instance) + && conf_instance_validate_sub_instances(conf_instance); +} + + + +/** A L L O C F R O M F I L E */ + + + +static +void conf_instance_parser_add_item( + conf_instance_type * conf_instance, + const char * item_name, + char ** buffer_pos) +{ + char * token_assign; + char * token_value; + char * token_end; + + char * buffer_pos_loc = *buffer_pos; + + token_assign = conf_util_alloc_next_token(&buffer_pos_loc); + if(token_assign == NULL) + { + /** This will fail. Give up. */ + printf("WARNING: Unexpected EOF after \"%s\". Giving up on this item.\n\n", item_name); + return; + } + else if(strcmp(token_assign, "=") != 0) + { + /** This will fail. Give up. */ + printf("WARNING: Unexpected \"%s\" after \"%s\". Giving up on this item.\n\n", token_assign, item_name); + free(token_assign); + *buffer_pos = buffer_pos_loc; + return; + } + + token_value = conf_util_alloc_next_token(&buffer_pos_loc); + if(token_value == NULL) + { + /** This will fail. Give up. */ + printf("WARNING: Unexpected EOF after \"%s = \". Giving up on this item.\n\n", item_name); + free(token_assign); + return; + } + else + conf_instance_insert_item(conf_instance, item_name, token_value); + + *buffer_pos = buffer_pos_loc; + + token_end = conf_util_alloc_next_token(&buffer_pos_loc); + if(token_end == NULL) + { + /** We've already alloc'd the token. Print a warning to the user. */ + printf("WARNING: Unexpected EOF after \"%s = %s \".\n\n", item_name, token_value); + free(token_assign); + free(token_value); + return; + } + else if(strcmp(token_end, ";") != 0) + { + printf("WARNING: Unexpected \"%s\" after \"%s = %s \". Probably a missing \";\".\n\n", + token_end, item_name, token_value); + } + else + { + *buffer_pos = buffer_pos_loc; + } + + free(token_assign); + free(token_value); + free(token_end); +} + + + +static +void conf_instance_parser_skip_unknown_class( + char ** buffer_pos) +{ + int depth_in_unkown_class = 1; + char * token = conf_util_alloc_next_token(buffer_pos); + + while(token != NULL) + { + if(strcmp(token, "{") == 0) + depth_in_unkown_class++; + else if(strcmp(token, "}") == 0) + depth_in_unkown_class--; + + printf("WARNING: Skipping token \"%s\" in unknown class.\n", token); + free(token); + if(depth_in_unkown_class == 0) + break; + else + token = conf_util_alloc_next_token(buffer_pos); + } +} + + + +static +void conf_instance_add_data_from_token_buffer( + conf_instance_type * conf_instance, + char ** buffer_pos, + bool allow_inclusion, + bool is_root) +{ + const conf_class_type * conf_class = conf_instance->conf_class; + char * token = conf_util_alloc_next_token(buffer_pos); + + bool scope_start_set = false; + bool scope_end_set = false; + + while(token != NULL) + { + if(conf_class_has_item_spec(conf_class, token) && (scope_start_set || is_root)) + conf_instance_parser_add_item(conf_instance, token, buffer_pos); + else if(conf_class_has_sub_class(conf_class, token) && (scope_start_set || is_root)) + { + char * name = conf_util_alloc_next_token(buffer_pos); + const conf_class_type * sub_conf_class = conf_class_get_sub_class_ref(conf_class, token); + if(name != NULL) + { + conf_instance_type * sub_conf_instance = conf_instance_alloc_default(sub_conf_class, name); + free(name); + conf_instance_insert_owned_sub_instance(conf_instance, sub_conf_instance); + conf_instance_add_data_from_token_buffer(sub_conf_instance, buffer_pos, allow_inclusion, false); + } + else + printf("WARNING: Unexpected EOF after \"%s\".\n\n", token); + } + else if(strcmp(token, "}") == 0) + { + if(scope_start_set) + { + scope_end_set = true; + free(token); + break; + } + else + printf("WARNING: Skipping unexpected token \"%s\".\n\n", token); + } + else if(strcmp(token, "{") == 0) + { + if(!scope_start_set && !is_root) + scope_start_set = true; + else + conf_instance_parser_skip_unknown_class(buffer_pos); + } + else if(strcmp(token, ";") == 0) + { + if(!scope_start_set) + { + free(token); + break; + } + else + printf("WARNING: Skipping unexpected token \"%s\".\n\n", token); + } + else if(strcmp(token, "include") == 0) + { + char * file_name = util_alloc_abs_path( conf_util_alloc_next_token(buffer_pos) ); + char * buffer_pos_lookahead = *buffer_pos; + char * token_end; + + if(file_name == NULL) + { + printf("WARNING: Unexpected EOF after \"%s\".\n\n", token); + free(token); + break; + } + else if(!allow_inclusion) + { + printf("WARNING: No support for nested inclusion. Skipping file \"%s\".\n\n", file_name); + } + else + { + path_stack_type * path_stack = path_stack_alloc( ); + path_stack_push_cwd(path_stack ); + util_chdir_file( file_name ); + { + char * buffer_new = conf_util_fscanf_alloc_token_buffer(file_name); + char * buffer_pos_new = buffer_new; + + conf_instance_add_data_from_token_buffer(conf_instance, &buffer_pos_new, false, true); + + free(buffer_new); + } + path_stack_pop( path_stack ); + path_stack_free( path_stack ); + } + + /** Check that the filename is followed by a ; */ + token_end = conf_util_alloc_next_token(&buffer_pos_lookahead); + if(token_end == NULL) + { + printf("WARNING: Unexpected EOF after inclusion of file \"%s\".\n\n", file_name); + free(token); + free(file_name); + break; + } + else if(strcmp(token_end, ";") != 0) + { + printf("WARNING: Unexpected \"%s\" after inclusion of file \"%s\". Probably a missing \";\".\n\n", + token_end, file_name); + } + else + { + *buffer_pos = buffer_pos_lookahead; + } + free(token_end); + free(file_name); + } + else + { + printf("WARNING: Skipping unexpected token \"%s\".\n\n", token); + } + + free(token); + token = conf_util_alloc_next_token(buffer_pos); + } + + + if(scope_end_set) + { + token = conf_util_alloc_next_token(buffer_pos); + if(token == NULL) + { + printf("WARNING: Unexpected EOF. Missing terminating \";\".\n"); + } + else if(strcmp(token, ";") != 0) + { + printf("WARNING: Missing terminating \";\" at the end of \"%s\".\n", conf_instance->name); + free(token); + } + else + free(token); + } +} + + + +conf_instance_type * conf_instance_alloc_from_file( + const conf_class_type * conf_class, + const char * name, + const char * file_name) +{ + conf_instance_type * conf_instance = conf_instance_alloc_default(conf_class, name); + path_stack_type * path_stack = path_stack_alloc( ); + char * file_arg = util_split_alloc_filename( file_name ); + path_stack_push_cwd( path_stack ); + util_chdir_file( file_name ); + { + char * buffer = conf_util_fscanf_alloc_token_buffer(file_arg); + char * buffer_pos = buffer; + + conf_instance_add_data_from_token_buffer(conf_instance, &buffer_pos, true, true); + + free(buffer); + } + free( file_arg ); + path_stack_pop( path_stack ); + path_stack_free( path_stack ); + return conf_instance; +} diff --git a/libres/lib/config/conf_data.cpp b/libres/lib/config/conf_data.cpp new file mode 100644 index 00000000000..b2550bbbec3 --- /dev/null +++ b/libres/lib/config/conf_data.cpp @@ -0,0 +1,178 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'conf_data.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 DT_STR_STRING "string" +#define DT_INT_STRING "integer" +#define DT_POSINT_STRING "positive integer" +#define DT_FLOAT_STRING "floating point number" +#define DT_POSFLOAT_STRING "positive floating foint number" +#define DT_FILE_STRING "file" +#define DT_DATE_STRING "date" + +const char *conf_data_get_dt_name_ref(dt_enum dt) { + switch(dt) + { + case(DT_STR): + return DT_STR_STRING; + case(DT_INT): + return DT_INT_STRING; + case(DT_POSINT): + return DT_POSINT_STRING; + case(DT_FLOAT): + return DT_FLOAT_STRING; + case(DT_POSFLOAT): + return DT_POSFLOAT_STRING; + case(DT_FILE): + return DT_FILE_STRING; + case(DT_DATE): + return DT_DATE_STRING; + default: + util_abort("%s: Internal error.\n", __func__); + return ""; + } +} + + +bool conf_data_validate_string_as_dt_value(dt_enum dt, const char *str) { + if(str == NULL) + return false; + + switch(dt) + { + case(DT_STR): + return true; + case(DT_INT): + return util_sscanf_int(str, NULL); + case(DT_POSINT): + { + int val; + bool ok = util_sscanf_int(str, &val); + if(!ok) + return false; + else + return val > 0; + } + case(DT_FLOAT): + return util_sscanf_double(str, NULL); + case(DT_POSFLOAT): + { + double val; + bool ok = util_sscanf_double(str, &val); + if(!ok) + return false; + else + return val >= 0.0; + } + case(DT_FILE): + { + return util_file_exists(str); + } + case(DT_DATE): + { + time_t date; + return util_sscanf_date_utc(str, &date); + } + default: + util_abort("%s: Internal error.\n", __func__); + } + return true; +} + + +int conf_data_get_int_from_string(dt_enum dt, const char *str) { + int value = 0; + bool ok = true; + + switch(dt) + { + case(DT_INT): + ok = util_sscanf_int(str, &value); + break; + case(DT_POSINT): + ok = util_sscanf_int(str, &value); + break; + default: + ok = false; + } + + if(!ok) + util_abort("%s: Can not get an int from \"%s\".\n", + __func__, str); + + return value; +} + + +double conf_data_get_double_from_string(dt_enum dt, const char *str) { + double value = 0; + bool ok = true; + + switch(dt) + { + case(DT_INT): + ok = util_sscanf_double(str, &value); + break; + case(DT_POSINT): + ok = util_sscanf_double(str, &value); + break; + case(DT_FLOAT): + ok = util_sscanf_double(str, &value); + break; + case(DT_POSFLOAT): + ok = util_sscanf_double(str, &value); + break; + default: + ok = false; + } + + if(!ok) + util_abort("%s: Can not get a double from \"%s\".\n", + __func__, str); + + return value; +} + + +time_t conf_data_get_time_t_from_string(dt_enum dt, const char *str) { + time_t value = 0; + bool ok = true; + + switch(dt) + { + case(DT_DATE): + ok = util_sscanf_date_utc(str, &value); + break; + default: + ok = false; + } + + if(!ok) + util_abort("%s: Can not get a time_t from \"%s\".\n", + __func__, str); + return value; +} + + + diff --git a/libres/lib/config/conf_util.cpp b/libres/lib/config/conf_util.cpp new file mode 100644 index 00000000000..c77d08ff9b9 --- /dev/null +++ b/libres/lib/config/conf_util.cpp @@ -0,0 +1,162 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'conf_util.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 + + +/* + This function creates a string buffer from a file. Furthermore, if the strings in pad_keys are found in the buffer, + they are padded with a space before and after. + + I.e., if the file contains + + key=value + + and "=" is in pad_keys, then the buffer will read + + key = value +*/ +static +char * __conf_util_fscanf_alloc_token_buffer( + const char * file, + const char * comment, + int num_pad_keys, + const char ** pad_keys) +{ + char * buffer_wrk = basic_parser_fread_alloc_file_content( file , NULL /* quote_set */ , NULL /* delete_set */ , "--" /* Comment start*/ , "\n" /* Comment end */); + char ** padded_keys = (char**)util_calloc(num_pad_keys , sizeof * padded_keys); + for(int key_nr = 0; key_nr < num_pad_keys; key_nr++) + { + assert(pad_keys[key_nr] != NULL); + + int key_len = strlen(pad_keys[key_nr]); + padded_keys[key_nr] = (char*)util_calloc((key_len + 3) , sizeof * padded_keys[key_nr]); + padded_keys[key_nr][0] = ' '; + for(int i=0; i 0) + found = true; + else if(len_token == 0 && !quoted) + return NULL; + else if(len_token == 0 && quoted) + *buff_pos += 1; + } + + char * token = (char*)util_calloc( (len_token + 1) , sizeof * token); + memmove(token, *buff_pos, len_token); + token[len_token] = '\0'; + *buff_pos += len_token; + + if(quoted) + *buff_pos += 1; + + return token; +} diff --git a/libres/lib/config/config_content.cpp b/libres/lib/config/config_content.cpp new file mode 100644 index 00000000000..c04378eed5d --- /dev/null +++ b/libres/lib/config/config_content.cpp @@ -0,0 +1,415 @@ +/* + Copyright (C) 2015 Equinor ASA, Norway. + + The file 'config_content.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 + + +#define CONFIG_CONTENT_TYPE_ID 6612520 + +struct config_content_struct { + UTIL_TYPE_ID_DECLARATION; + std::set * parsed_files; /* A set of config files whcih have been parsed - to protect against circular includes. */ + + vector_type * nodes; + hash_type * items; + config_error_type * parse_errors; + stringlist_type * warnings; + subst_list_type * define_list; + char * config_file; + char * abs_path; + char * config_path; + + config_path_stack_type * path_stack; + config_root_path_type * invoke_path; + bool valid; +}; + + +UTIL_IS_INSTANCE_FUNCTION( config_content , CONFIG_CONTENT_TYPE_ID ) + +config_content_type * config_content_alloc(const char * filename) { + config_content_type * content = (config_content_type*)util_malloc( sizeof * content ); + UTIL_TYPE_ID_INIT( content , CONFIG_CONTENT_TYPE_ID ); + content->parsed_files = new std::set(); + + content->valid = false; + content->items = hash_alloc(); + content->nodes = vector_alloc_new(); + content->parse_errors = config_error_alloc(); + content->define_list = subst_list_alloc( NULL ); + content->warnings = stringlist_alloc_new(); + + content->path_stack = config_path_stack_alloc( ); + content->config_file = util_alloc_string_copy( filename ); + content->abs_path = util_alloc_abs_path( filename ); + content->config_path = util_split_alloc_dirname( content->abs_path ); + content->invoke_path = config_root_path_alloc( NULL ); + + return content; +} + + +bool config_content_has_item( const config_content_type * content , const char * key) { + return hash_has_key( content->items , key ); +} + + +config_content_item_type * config_content_get_item( const config_content_type * content , const char * key) { + return (config_content_item_type*)hash_get( content->items , key ); +} + + +void config_content_add_item( config_content_type * content , const config_schema_item_type * schema_item , const config_path_elm_type * path_elm) { + + const char * kw = config_schema_item_get_kw( schema_item ); + config_content_item_type * content_item = config_content_item_alloc( schema_item , path_elm ); + hash_insert_hash_owned_ref( content->items , kw , content_item , config_content_item_free__ ); + + if (config_schema_item_is_deprecated(schema_item)) + stringlist_append_copy( content->warnings , config_schema_item_get_deprecate_msg(schema_item)); +} + +void config_content_add_node( config_content_type * content , config_content_node_type * content_node ) { + vector_append_ref( content->nodes , content_node ); +} + + +void config_content_set_valid( config_content_type * content) { + content->valid = true; +} + +bool config_content_is_valid( const config_content_type * content ) { + return content->valid; +} + + +config_error_type * config_content_get_errors( const config_content_type * content) { + return content->parse_errors; +} + + +const stringlist_type * config_content_get_warnings( const config_content_type * content) { + return content->warnings; +} + + + +void config_content_free( config_content_type * content ) { + if(!content) + return; + + delete content->parsed_files; + + stringlist_free( content->warnings ); + vector_free( content->nodes ); + hash_free( content->items ); + config_error_free( content->parse_errors ); + subst_list_free( content->define_list ); + free( content->config_file ); + free( content->abs_path ); + free( content->config_path ); + if (content->invoke_path != NULL) + config_root_path_free( content->invoke_path ); + + config_path_stack_free( content->path_stack ); + free( content ); +} + +bool config_content_add_file( config_content_type * content , const char * config_file) { + const auto iter = content->parsed_files->find( config_file ); + if (iter == content->parsed_files->end()) { + content->parsed_files->insert( config_file ); + return true; + } + return false; +} + +config_root_path_type * config_content_get_invoke_path( config_content_type * content ) { + return content->invoke_path; +} + + + + + +/*****************************************************************/ + +/* + Here comes some xxx_get() functions - many of them will fail if + the item has not been added in the right way (this is to ensure that + the xxx_get() request is unambigous. +*/ + + +/** + This function can be used to get the value of a config + parameter. But to ensure that the get is unambigous we set the + following requirements to the item corresponding to 'kw': + + * argc_minmax has been set to 1,1 + + If this is not the case - we die. +*/ + +/** + Assume we installed a key 'KEY' which occurs three times in the final + config file: + + KEY 1 2 3 + KEY 11 22 33 + KEY 111 222 333 + + + Now when accessing these values the occurence variable will + correspond to the linenumber, and the index will index along a line: + + config_iget_as_int( config , "KEY" , 0 , 2) => 3 + config_iget_as_int( config , "KEY" , 2 , 1) => 222 +*/ + +const char * config_content_iget( const config_content_type * content , const char * key , int occurence , int index) { + config_content_item_type * item = config_content_get_item(content , key); + return config_content_item_iget(item , occurence , index); +} + + +int config_content_iget_as_int( const config_content_type * content , const char * key , int occurence , int index) { + config_content_item_type * item = config_content_get_item(content , key); + return config_content_item_iget_as_int(item , occurence , index); +} + + +bool config_content_iget_as_bool( const config_content_type * content , const char * key , int occurence , int index) { + config_content_item_type * item = config_content_get_item(content , key); + return config_content_item_iget_as_bool(item , occurence , index); +} + + +double config_content_iget_as_double( const config_content_type * content , const char * key , int occurence , int index) { + config_content_item_type * item = config_content_get_item(content , key); + return config_content_item_iget_as_double(item , occurence , index); +} + +/** + This function will return NULL is the item has not been set, + however it must be installed with config_add_schema_item(). +*/ + +const char * config_content_safe_iget(const config_content_type * content , const char *kw, int occurence , int index) { + const char * value = NULL; + + if (config_content_has_item( content , kw )) { + config_content_item_type * item = config_content_get_item(content , kw); + if (occurence < config_content_item_get_size( item )) { + config_content_node_type * node = config_content_item_iget_node( item , occurence ); + value = config_content_node_safe_iget( node , index ); + } + } + return value; +} + + +/** + Return the number of times a keyword has been set - dies on unknown + 'kw'. If the append_arg attribute has been set to false the + function will return 0 or 1 irrespective of how many times the item + has been set in the config file. +*/ + + +int config_content_get_occurences(const config_content_type * content, const char * kw) { + if (config_content_has_item( content , kw )) + return config_content_item_get_size( config_content_get_item(content , kw) ); + else + return 0; +} + + + +const config_content_node_type * config_content_iget_node( const config_content_type * content , int index) { + const config_content_node_type * node = (const config_content_node_type*)vector_iget_const(content->nodes , index ); + return node; +} + + +int config_content_get_size(const config_content_type * content) { + return vector_get_size( content->nodes ); +} + +/*****************************************************************/ +/* All the get_value functions will operate on the last item which has + been set with a particular key value. So assuming the config file + looks like: + + KEY VALUE1 + KEY VALUE2 OPTIONAL + KEY 100 VALUE3 OPTIONAL ERROR + + these functions will all operate on the last line in the config file: + + KEY 100 VALUE3 OPTIONAL ERROR +*/ + + + +static config_content_node_type * config_content_get_value_node__( const config_content_type * config , const char * kw) { + config_content_node_type * node = config_content_get_value_node( config , kw ); + if (node == NULL) + util_abort("Tried to get value node from unset kw:%s \n",__func__ , kw ); + + return node; +} + +config_content_node_type * config_content_get_value_node( const config_content_type * content , const char * kw) { + config_content_item_type * item = config_content_get_item(content , kw); + config_content_node_type * node = config_content_item_get_last_node( item ); + config_content_node_assert_key_value( node ); + return node; +} + + +bool config_content_get_value_as_bool(const config_content_type * config , const char * kw) { + config_content_node_type * node = config_content_get_value_node__( config , kw ); + return config_content_node_iget_as_bool(node , 0); +} + +int config_content_get_value_as_int(const config_content_type * config , const char * kw) { + config_content_node_type * node = config_content_get_value_node__( config , kw ); + return config_content_node_iget_as_int(node , 0); +} + +double config_content_get_value_as_double(const config_content_type * config , const char * kw) { + config_content_node_type * node = config_content_get_value_node__( config , kw ); + return config_content_node_iget_as_double(node , 0); +} + +const char * config_content_get_value_as_path( const config_content_type * config , const char * kw) { + config_content_node_type * node = config_content_get_value_node__( config , kw ); + return config_content_node_iget_as_path(node , 0); +} + +const char * config_content_get_value_as_abspath( const config_content_type * config , const char * kw) { + config_content_node_type * node = config_content_get_value_node__( config , kw ); + return config_content_node_iget_as_abspath(node , 0); +} + +const char * config_content_get_value_as_relpath( const config_content_type * config , const char * kw) { + config_content_node_type * node = config_content_get_value_node__( config , kw ); + return config_content_node_iget_as_relpath(node , 0); +} + +const char * config_content_get_value_as_executable( const config_content_type * config , const char * kw) { + config_content_node_type * node = config_content_get_value_node__( config , kw ); + return config_content_node_iget_as_executable(node , 0); +} + +const char * config_content_get_value(const config_content_type * config , const char * kw) { + config_content_node_type * node = config_content_get_value_node__( config , kw ); + return config_content_node_iget(node , 0); +} + +/*****************************************************************/ + +const stringlist_type * config_content_iget_stringlist_ref(const config_content_type * content , const char * kw, int occurence) { + config_content_item_type * item = config_content_get_item(content , kw); + + return config_content_item_iget_stringlist_ref(item , occurence); +} + +void config_content_add_define( config_content_type * content , const char * key , const char * value ) { + char * filtered_value = subst_list_alloc_filtered_string(content->define_list, value); + subst_list_append_copy(content->define_list, key, filtered_value, NULL); + free(filtered_value); +} + +subst_list_type * config_content_get_define_list( config_content_type * content ) { + return content->define_list; +} + +const subst_list_type * config_content_get_const_define_list(const config_content_type * content) { + return content->define_list; +} + +/*****************************************************************/ + + + + +const char * config_content_get_config_file( const config_content_type * content , bool abs_path ) { + if (abs_path) + return content->abs_path; + else + return content->config_file; +} + + +config_path_elm_type * config_content_add_path_elm( config_content_type * content , const char * path ) { + const config_path_elm_type * current_path_elm; + + if (config_path_stack_size( content->path_stack ) == 0) + current_path_elm = NULL; + else + current_path_elm = config_path_stack_get_last( content->path_stack ); + + { + config_path_elm_type * new_path_elm; + + { + char * rel_path = NULL; + config_root_path_type * invoke_path = config_content_get_invoke_path( content ); + if (path != NULL) { + if (current_path_elm == NULL) + rel_path = util_alloc_rel_path( config_root_path_get_abs_path(invoke_path) , path); + else + rel_path = config_path_elm_alloc_relpath( current_path_elm , path ); + } + new_path_elm = config_path_elm_alloc( invoke_path , rel_path ); + free( rel_path ); + } + config_path_stack_append( content->path_stack , new_path_elm ); + return new_path_elm; + } +} + + + +const char * config_content_get_config_path( const config_content_type * content ) { + return content->config_path; +} + +void config_content_pop_path_stack( config_content_type * content ) { + config_path_stack_pop( content->path_stack ); +} + + + +stringlist_type * config_content_alloc_keys(const config_content_type * content) { + return hash_alloc_stringlist(content->items); +} diff --git a/libres/lib/config/config_content_item.cpp b/libres/lib/config/config_content_item.cpp new file mode 100644 index 00000000000..c921bada7b0 --- /dev/null +++ b/libres/lib/config/config_content_item.cpp @@ -0,0 +1,206 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'config_content_item.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 CONFIG_CONTENT_ITEM_ID 8876752 +struct config_content_item_struct { + UTIL_TYPE_ID_DECLARATION; + const config_schema_item_type * schema; + vector_type * nodes; + const config_path_elm_type * path_elm; +}; + + + + + + + + +/*****************************************************************/ + +/** + This function counts the number of times a config item has been + set. Referring again to the example at the top: + + config_content_item_get_occurences( "KEY1" ) + + will return 2. +*/ + + + +int config_content_item_get_size(const config_content_item_type * item) { + return vector_get_size( item->nodes ); +} + + +config_content_node_type * config_content_item_get_last_node(const config_content_item_type * item) { + return (config_content_node_type*)vector_get_last( item->nodes ); +} + +config_content_node_type * config_content_item_iget_node(const config_content_item_type * item , int index) { + return (config_content_node_type*)vector_iget( item->nodes , index ); +} + + +const stringlist_type * config_content_item_iget_stringlist_ref(const config_content_item_type * item, int occurence) { + const config_content_node_type * node = config_content_item_iget_node(item , occurence); + return config_content_node_get_stringlist( node ); +} + +/** + If copy == false - the hash will break down when/if the + config object is freed - your call. +*/ + +hash_type * config_content_item_alloc_hash(const config_content_item_type * item , bool copy) { + hash_type * hash = hash_alloc(); + if (item != NULL) { + int inode; + for (inode = 0; inode < vector_get_size( item->nodes ); inode++) { + const config_content_node_type * node = config_content_item_iget_node(item , inode); + const stringlist_type * src_list = config_content_node_get_stringlist( node ); + const char * key = stringlist_iget(src_list , 0); + const char * value = stringlist_iget(src_list , 1); + + if (copy) { + hash_insert_hash_owned_ref(hash , + key , + util_alloc_string_copy(value) , + free); + } else + hash_insert_ref(hash , key , value ); + + } + } + return hash; +} + + +/******************************************************************/ + + + + +const char * config_content_item_iget(const config_content_item_type * item , int occurence , int index) { + const config_content_node_type * node = config_content_item_iget_node(item , occurence); + const stringlist_type * src_list = config_content_node_get_stringlist( node ); + return stringlist_iget( src_list , index ); +} + +bool config_content_item_iget_as_bool(const config_content_item_type * item, int occurence , int index) { + bool value; + config_schema_item_assure_type(item->schema , index , CONFIG_BOOL); + util_sscanf_bool( config_content_item_iget(item , occurence ,index) , &value ); + return value; +} + + + + +int config_content_item_iget_as_int(const config_content_item_type * item, int occurence , int index) { + int value; + config_schema_item_assure_type(item->schema , index , CONFIG_INT); + util_sscanf_int( config_content_item_iget(item , occurence , index) , &value ); + return value; +} + + +double config_content_item_iget_as_double(const config_content_item_type * item, int occurence , int index) { + double value; + config_schema_item_assure_type(item->schema , index , CONFIG_FLOAT); + util_sscanf_double( config_content_item_iget(item , occurence , index) , &value ); + return value; +} + + +/** + Used to reset an item is the special string 'CLEAR_STRING' + is found as the only argument: + + OPTION V1 + OPTION V2 V3 V4 + OPTION __RESET__ + OPTION V6 + + In this case OPTION will get the value 'V6'. The example given + above is a bit contrived; this option is designed for situations + where several config files are parsed serially; and the user can + not/will not update the first. +*/ + +void config_content_item_clear( config_content_item_type * item ) { + vector_clear( item->nodes ); +} + + + +void config_content_item_free( config_content_item_type * item ) { + vector_free( item->nodes ); + free(item); +} + + + +UTIL_SAFE_CAST_FUNCTION( config_content_item , CONFIG_CONTENT_ITEM_ID) +UTIL_IS_INSTANCE_FUNCTION( config_content_item , CONFIG_CONTENT_ITEM_ID) + + +void config_content_item_free__( void * arg ) { + config_content_item_type * content_item = config_content_item_safe_cast( arg ); + config_content_item_free( content_item ); +} + + +config_content_item_type * config_content_item_alloc( const config_schema_item_type * schema , const config_path_elm_type * path_elm) { + config_content_item_type * content_item = (config_content_item_type*)util_malloc( sizeof * content_item ); + UTIL_TYPE_ID_INIT( content_item , CONFIG_CONTENT_ITEM_ID ); + content_item->schema = schema; + content_item->nodes = vector_alloc_new(); + content_item->path_elm = path_elm; + return content_item; +} + + + + + +config_content_node_type * config_content_item_alloc_node( const config_content_item_type * item , const config_path_elm_type * path_elm) { + config_content_node_type * node = config_content_node_alloc( item->schema , path_elm ); + vector_append_owned_ref( item->nodes , node , config_content_node_free__); + return node; +} + + + +const config_schema_item_type * config_content_item_get_schema( const config_content_item_type * item ) { + return item->schema; +} + + +const config_path_elm_type * config_content_item_get_path_elm( const config_content_item_type * item ) { + return item->path_elm; +} diff --git a/libres/lib/config/config_content_node.cpp b/libres/lib/config/config_content_node.cpp new file mode 100644 index 00000000000..32cb7bbb0a1 --- /dev/null +++ b/libres/lib/config/config_content_node.cpp @@ -0,0 +1,255 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'config_content_node.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 + + +#define CONFIG_CONTENT_NODE_ID 6752887 +struct config_content_node_struct { + UTIL_TYPE_ID_DECLARATION; + const config_schema_item_type * schema; + stringlist_type * stringlist; /* The values which have been set. */ + const config_path_elm_type * cwd; + stringlist_type * string_storage; +}; + + +static UTIL_SAFE_CAST_FUNCTION( config_content_node , CONFIG_CONTENT_NODE_ID ) + + + +config_content_node_type * config_content_node_alloc( const config_schema_item_type * schema , const config_path_elm_type * cwd) { + config_content_node_type * node = (config_content_node_type*)util_malloc(sizeof * node ); + UTIL_TYPE_ID_INIT( node , CONFIG_CONTENT_NODE_ID ); + node->stringlist = stringlist_alloc_new(); + node->cwd = cwd; + node->schema = schema; + node->string_storage = NULL; + return node; +} + + +void config_content_node_add_value(config_content_node_type * node , const char * value) { + stringlist_append_copy( node->stringlist , value); +} + + +void config_content_node_set(config_content_node_type * node , const stringlist_type * token_list) { + int argc = stringlist_get_size( token_list ) - 1; + for (int iarg=0; iarg < argc; iarg++) + config_content_node_add_value( node , stringlist_iget( token_list , iarg + 1)); +} + + + +char * config_content_node_alloc_joined_string(const config_content_node_type * node, const char * sep) { + return stringlist_alloc_joined_string(node->stringlist , sep); +} + + + +void config_content_node_free(config_content_node_type * node) { + stringlist_free(node->stringlist); + if (node->string_storage != NULL) + stringlist_free( node->string_storage ); + free(node); +} + + + +void config_content_node_free__(void * arg) { + config_content_node_type * node = config_content_node_safe_cast( arg ); + config_content_node_free( node ); +} + +static void config_content_node_push_string( config_content_node_type * node , char * string) { + if (node->string_storage == NULL) + node->string_storage = stringlist_alloc_new( ); + + stringlist_append_copy( node->string_storage , string ); +} + +const char * config_content_node_get_full_string(const config_content_node_type * node, + const char * sep) { + char * full_string = stringlist_alloc_joined_string(node->stringlist , sep); + + config_content_node_push_string((config_content_node_type *)node, full_string ); + free(full_string); + + return stringlist_get_last(node->string_storage); +} + +const char * config_content_node_iget(const config_content_node_type * node , int index) { + return stringlist_iget( node->stringlist , index ); +} + + +const char * config_content_node_safe_iget(const config_content_node_type * node , int index) { + if (index >= stringlist_get_size( node->stringlist )) + return NULL; + else + return stringlist_iget( node->stringlist , index ); +} + + +config_item_types config_content_node_iget_type( const config_content_node_type * node , int index) { + return config_schema_item_iget_type( node->schema , index ); +} + + +time_t config_content_node_iget_as_isodate(const config_content_node_type * node , int index) { + time_t value; + config_schema_item_assure_type(node->schema , index , CONFIG_ISODATE); + util_sscanf_isodate( config_content_node_iget(node , index) , &value ); + return value; +} + + +bool config_content_node_iget_as_bool(const config_content_node_type * node , int index) { + bool value; + config_schema_item_assure_type(node->schema , index , CONFIG_BOOL); + util_sscanf_bool( config_content_node_iget(node , index) , &value ); + return value; +} + + +int config_content_node_iget_as_int(const config_content_node_type * node , int index) { + int value; + config_schema_item_assure_type(node->schema , index , CONFIG_INT); + util_sscanf_int( config_content_node_iget(node , index) , &value ); + return value; +} + + + +double config_content_node_iget_as_double(const config_content_node_type * node , int index) { + double value; + config_schema_item_assure_type(node->schema , index , CONFIG_FLOAT + CONFIG_INT); + util_sscanf_double( config_content_node_iget(node , index) , &value ); + return value; +} + + + + +const char * config_content_node_iget_as_path(config_content_node_type * node , int index) { + config_schema_item_assure_type(node->schema , index , CONFIG_PATH + CONFIG_EXISTING_PATH); + { + const char * config_value = config_content_node_iget(node , index); + char * path_value = config_path_elm_alloc_path( node->cwd , config_value ); + config_content_node_push_string( node , path_value ); + + return path_value; + } +} + + +const char * config_content_node_iget_as_abspath( config_content_node_type * node , int index) { + config_schema_item_assure_type(node->schema , index , CONFIG_PATH + CONFIG_EXISTING_PATH); + { + const char * config_value = config_content_node_iget(node , index); + char * path_value = config_path_elm_alloc_abspath( node->cwd , config_value ); + config_content_node_push_string( node , path_value ); + + return path_value; + } +} + + +const char * config_content_node_iget_as_relpath( config_content_node_type * node , int index) { + config_schema_item_assure_type(node->schema , index , CONFIG_PATH + CONFIG_EXISTING_PATH); + { + const char * config_value = config_content_node_iget(node , index); + char * path_value = config_path_elm_alloc_relpath( node->cwd , config_value ); + config_content_node_push_string( node , path_value ); + + return path_value; + } +} + +const char * config_content_node_iget_as_executable( config_content_node_type * node, int index ) { + config_schema_item_assure_type(node->schema, index, + CONFIG_PATH + CONFIG_EXISTING_PATH + CONFIG_EXECUTABLE ); + { + const char * config_value = config_content_node_iget(node , index); + char* path_value = config_path_elm_alloc_abspath( node->cwd , config_value ); + + if( !strstr( config_value, UTIL_PATH_SEP_STRING ) + && !util_file_exists( path_value ) ) { + char* tmp = res_env_alloc_PATH_executable( config_value ); + if( tmp ) { + free( path_value ); + path_value = tmp; + } + } + + config_content_node_push_string( node , path_value ); + return path_value; + } +} + + +const stringlist_type * config_content_node_get_stringlist( const config_content_node_type * node ) { + return node->stringlist; +} + + +const char * config_content_node_get_kw( const config_content_node_type * node ) { + return config_schema_item_get_kw( node->schema ); +} + + + + +int config_content_node_get_size( const config_content_node_type * node ) { + return stringlist_get_size( node->stringlist ); +} + + +void config_content_node_assert_key_value( const config_content_node_type * node ) { + int argc_min , argc_max; + config_schema_item_get_argc( node->schema , &argc_min , &argc_max); + + if (!((argc_min == 1) && (argc_min == 1))) + util_abort("%s: item:%s before calling config_get_value() functions *without* index you must set argc_min == argc_max = 1 \n",__func__ , config_schema_item_get_kw( node->schema )); +} + +/** + The node should contain elements of the type: + + KEY1:VALUE1 KEY2:Value2 XX Key3:Val3 Ignored + + Which will be inserted in the opt_hash dictionary as : {"KEY1" : + "VALUE1" , ... } Elements which do not conform to this syntax are + ignored. +*/ + + +void config_content_node_init_opt_hash( const config_content_node_type * node , hash_type * opt_hash , int elm_offset) { + int i; + for (i = elm_offset; i < config_content_node_get_size( node ); i++) + hash_add_option( opt_hash , config_content_node_iget( node , i )); +} diff --git a/libres/lib/config/config_error.cpp b/libres/lib/config/config_error.cpp new file mode 100644 index 00000000000..65b6d3f285a --- /dev/null +++ b/libres/lib/config/config_error.cpp @@ -0,0 +1,81 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'config_error.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 + +struct config_error_struct { + stringlist_type * error_list; +}; + + + +config_error_type * config_error_alloc() { + config_error_type * error = (config_error_type*)util_malloc( sizeof * error ); + error->error_list = stringlist_alloc_new(); + return error; +} + + +config_error_type * config_error_alloc_copy( const config_error_type * src_error) { + config_error_type * config_error = config_error_alloc(); + stringlist_deep_copy( config_error->error_list , src_error->error_list ); + return config_error; +} + + +bool config_error_equal( const config_error_type * error1 , const config_error_type * error2) { + return stringlist_equal( error1->error_list , error2->error_list ); +} + +void config_error_free( config_error_type * error ) { + stringlist_free( error->error_list ); + free( error ); +} + + + +void config_error_add( config_error_type * error , char * new_error) { + stringlist_append_copy( error->error_list , new_error ); +} + + +int config_error_count( const config_error_type * error ) { + return stringlist_get_size( error->error_list ); +} + + +const char * config_error_iget( const config_error_type * error , int index) { + return stringlist_iget( error->error_list , index ); +} + + +void config_error_fprintf( const config_error_type * error , bool add_count , FILE * stream ) { + int error_nr; + + for (error_nr = 0; error_nr < stringlist_get_size( error->error_list ); error_nr++) { + if (add_count) + fprintf(stream , " %02d: " , error_nr); + + fprintf( stream , "%s\n" , stringlist_iget( error->error_list , error_nr)); + } +} diff --git a/libres/lib/config/config_parser.cpp b/libres/lib/config/config_parser.cpp new file mode 100644 index 00000000000..ef1ef09e810 --- /dev/null +++ b/libres/lib/config/config_parser.cpp @@ -0,0 +1,732 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file '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 +#include +#include +#include +#include +#include + +#include + +#define CLEAR_STRING "__RESET__" + + + +/** +Structure to parse configuration files of this type: + +KEYWORD1 ARG2 ARG2 ARG3 +KEYWORD2 ARG1-2 +.... +KEYWORDN ARG1 ARG2 + +A keyword can occure many times. + +*/ + + + +/** + + + ============================= + | config_type object | + | | + | Contains 'all' the | + | configuration information.| + | | + ============================= + | | + | \________________________ + | \ + KEY1 KEY2 + | | + \|/ \|/ + ========================= ========================= + | config_item object | | config_item object | + | | | | + | Indexed by a keyword | | Indexed by a keyword | + | which is the first | | which is the first | + | string in the | | string in the | + | config file. | | config file. | + | | | | + ========================= ========================= + | | | + | | | + \|/ \|/ \|/ +============================ ============================ ============================ +| config_item_node object | | config_item_node object | | config_item_node object | +| | | | | | +| Only containing the | | Only containing the | | Only containing the | +| stringlist object | | stringlist object | | stringlist object | +| directly parsed from the | | directly parsed from the | | directly parsed from the | +| file. | | file. | | file. | +|--------------------------| |--------------------------| |--------------------------| +| ARG1 ARG2 ARG3 | | VERBOSE | | DEBUG | +============================ ============================ ============================ + + +The example illustrated above would correspond to the following config +file (invariant under line-permutations): + +KEY1 ARG1 ARG2 ARG3 +KEY1 VERBOSE +KEY2 DEBUG + + +Example config file(2): + +OUTFILE filename +INPUT filename +OPTIONS store +OPTIONS verbose +OPTIONS optimize cache=1 + +In this case the whole config object will contain three items, +corresponding to the keywords OUTFILE, INPUT and OPTIONS. The two +first will again only contain one node each, whereas the OPTIONS item +will contain three nodes, corresponding to the three times the keyword +"OPTIONS" appear in the config file. +*/ + + + + + + + + + + + +struct config_parser_struct { + hash_type * schema_items; + hash_type * messages; /* Can print a (warning) message when a keyword is encountered. */ +}; + +int config_get_schema_size( const config_parser_type * config ) { + return hash_get_size( config->schema_items ); +} + + + +/* + The last argument (config_file) is only used for printing + informative error messages, and can be NULL. The config_cwd is + essential if we are looking up a filename, otherwise it can be NULL. + + Returns a string with an error description, or NULL if the supplied + arguments were OK. The string is allocated here, but is assumed that + calling scope will free it. +*/ + +static config_content_node_type * config_content_item_set_arg__(subst_list_type * define_list , + config_error_type * parse_errors , + config_content_item_type * item , + stringlist_type * token_list , + const config_path_elm_type * path_elm , + const char * config_file ) { + + config_content_node_type * new_node = NULL; + int argc = stringlist_get_size( token_list ) - 1; + + if (argc == 1 && (strcmp(stringlist_iget(token_list , 1) , CLEAR_STRING) == 0)) { + config_content_item_clear(item); + } else { + const config_schema_item_type * schema_item = config_content_item_get_schema( item ); + + /* Filtering based on DEFINE statements */ + if (subst_list_get_size( define_list ) > 0) { + int iarg; + for (iarg = 0; iarg < argc; iarg++) { + char * filtered_copy = subst_list_alloc_filtered_string( define_list , stringlist_iget(token_list , iarg + 1)); + stringlist_iset_owned_ref( token_list , iarg + 1 , filtered_copy); + } + } + + + /* Filtering based on environment variables */ + if (config_schema_item_expand_envvar( schema_item )) { + int iarg; + for (iarg = 0; iarg < argc; iarg++) { + int env_offset = 0; + while (true) { + char * env_var = res_env_isscanf_alloc_envvar( stringlist_iget(token_list , iarg + 1) , env_offset ); + if (env_var == NULL) + break; + + { + const char * env_value = getenv( &env_var[1] ); + if (env_value != NULL) { + char * new_value = util_string_replace_alloc( stringlist_iget( token_list , iarg + 1 ) , env_var , env_value ); + stringlist_iset_owned_ref( token_list , iarg + 1 , new_value ); + } else { + env_offset += 1; + res_log_fwarning("Environment variable: %s is not defined", + env_var); + } + } + + free( env_var ); + } + } + } + + { + if (config_schema_item_validate_set(schema_item , token_list , config_file, path_elm , parse_errors)) { + new_node = config_content_item_alloc_node( item , config_content_item_get_path_elm( item )); + config_content_node_set(new_node , token_list); + } + } + } + return new_node; +} + + + + + +/*****************************************************************/ + + + +config_parser_type * config_alloc() { + config_parser_type *config = (config_parser_type*)util_malloc(sizeof * config ); + config->schema_items = hash_alloc(); + config->messages = hash_alloc(); + return config; +} + + + + + + + + + + +void config_free(config_parser_type * config) { + + hash_free(config->schema_items); + hash_free(config->messages); + + free(config); +} + + + +static void config_insert_schema_item(config_parser_type * config , const char * kw , const config_schema_item_type * item , bool ref) { + if (ref) + hash_insert_ref(config->schema_items , kw , item); + else + hash_insert_hash_owned_ref(config->schema_items , kw , item , config_schema_item_free__); +} + + +/** + This function allocates a simple item with all values + defaulted. The item is added to the config object, and a pointer is + returned to the calling scope. If you want to change the properties + of the item you can do that with config_schema_item_set_xxxx() functions + from the calling scope. +*/ + + +config_schema_item_type * config_add_schema_item(config_parser_type * config , + const char * kw, + bool required) { + + config_schema_item_type * item = config_schema_item_alloc( kw , required ); + config_insert_schema_item(config , kw , item , false); + return item; +} + + + +/** + This is a minor wrapper for adding an item with the properties. + + 1. It has argc_minmax = {1,1} + + The value can than be extracted with config_get_value() and + config_get_value_as_xxxx functions. +*/ + +config_schema_item_type * config_add_key_value( config_parser_type * config , const char * key , bool required , config_item_types item_type) { + config_schema_item_type * item = config_add_schema_item( config , key , required ); + config_schema_item_set_argc_minmax( item , 1 , 1 ); + config_schema_item_iset_type( item , 0 , item_type ); + return item; +} + + + +bool config_has_schema_item(const config_parser_type * config , const char * kw) { + return hash_has_key(config->schema_items , kw); +} + + +config_schema_item_type * config_get_schema_item(const config_parser_type * config , const char * kw) { + return (config_schema_item_type*)hash_get(config->schema_items , kw); +} + +/* + Due to the possibility of aliases we must go through the canonical + keyword which is internalized in the schema_item. +*/ + + + + + + + + + +static void config_validate_content_item(const config_parser_type * config , config_content_type * content , const config_content_item_type * item) { + const config_schema_item_type * schema_item = config_content_item_get_schema( item ); + const char * schema_kw = config_schema_item_get_kw( schema_item ); + + { + int i; + for (i = 0; i < config_schema_item_num_required_children(schema_item); i++) { + const char * required_child = config_schema_item_iget_required_child( schema_item , i ); + if (!config_content_has_item(content , required_child)) { + char * error_message = (char*)util_alloc_sprintf("When:%s is set - you also must set:%s.",schema_kw , required_child); + config_error_add( config_content_get_errors( content ) , error_message ); + free( error_message ); + } + } + + if (config_schema_item_has_required_children_value( schema_item )) { + int inode; + for (inode = 0; inode < config_content_item_get_size(item); inode++) { + config_content_node_type * node = config_content_item_iget_node(item , inode); + const stringlist_type * values = config_content_node_get_stringlist( node ); + int is; + + for (is = 0; is < stringlist_get_size(values); is++) { + const char * value = stringlist_iget(values , is); + stringlist_type * required_children = config_schema_item_get_required_children_value( schema_item , value ); + + if (required_children != NULL) { + int ic; + for (ic = 0; ic < stringlist_get_size( required_children ); ic++) { + const char * req_child = stringlist_iget( required_children , ic ); + if (!config_content_has_item(content , req_child )) { + char * error_message = (char*)util_alloc_sprintf("When:%s is set to:%s - you also must set:%s.",schema_kw , value , req_child ); + config_error_add( config_content_get_errors( content ) , error_message ); + free( error_message ); + } + } + } + } + } + } + } +} + + + +void config_validate(config_parser_type * config, config_content_type * content) { + int size = hash_get_size(config->schema_items); + char ** key_list = hash_alloc_keylist(config->schema_items); + int ikey; + for (ikey = 0; ikey < size; ikey++) { + const config_schema_item_type * schema_item = config_get_schema_item( config , key_list[ikey]); + const char * content_key = config_schema_item_get_kw( schema_item ); + if (config_content_has_item( content , content_key)) { + const config_content_item_type * item = config_content_get_item(content , content_key); + config_validate_content_item(config , content , item ); + } else { + if (config_schema_item_required( schema_item)) { /* The item is not set ... */ + char * error_message = (char*)util_alloc_sprintf("Item:%s must be set - parsing:%s",content_key , config_content_get_config_file( content , true )); + config_error_add( config_content_get_errors( content ) , error_message ); + free( error_message ); + } + } + } + util_free_stringlist(key_list , size); +} + + + + + +static void assert_no_circular_includes(config_content_type * config_content, + const char * config_filename) +{ + char * abs_filename = (char*)util_alloc_realpath(config_filename); + if (!config_content_add_file(config_content, abs_filename)) + util_exit( + "%s: file (%s) already parsed - circular include?", + __func__, + abs_filename + ); + + free(abs_filename); +} + + +static void alloc_config_filename_components(const char * config_filename, + char ** config_path, + char ** config_file) +{ + char * config_base; + char * config_ext; + util_alloc_file_components(config_filename, config_path, &config_base, &config_ext); + + *config_file = util_alloc_filename(NULL, config_base, config_ext); + + free(config_base); + free(config_ext); +} + + +static config_path_elm_type * config_relocate(const char * config_path, + config_content_type * config_content, + path_stack_type * path_stack) +{ + config_path_elm_type * current_path_elm = config_content_add_path_elm(config_content, config_path); + path_stack_push_cwd(path_stack); + + if (config_path) + util_chdir(config_path); + + return current_path_elm; +} + + +bool config_parser_add_key_values(config_parser_type * config, + config_content_type * content, + const char * kw, + stringlist_type * values, + const config_path_elm_type * current_path_elm, + const char * config_filename, + config_schema_unrecognized_enum unrecognized) +{ + if (!config_has_schema_item(config, kw)) { + + if (unrecognized == CONFIG_UNRECOGNIZED_IGNORE) + return false; + + if (unrecognized == CONFIG_UNRECOGNIZED_WARN) { + fprintf(stderr, "** Warning keyword: %s not recognized when parsing: %s --- \n", + kw, + config_filename); + return false; + } + + if (unrecognized == CONFIG_UNRECOGNIZED_ERROR) { + char * error_message = (char*)util_alloc_sprintf("Keyword:%s is not recognized", kw); + config_error_add(config_content_get_errors(content), error_message); + free(error_message); + return false; + } + + /* + We allow unrecognized keywords - they are automatically added to the + current parser class. + */ + if (unrecognized == CONFIG_UNRECOGNIZED_ADD) { + config_schema_item_type * item = config_add_schema_item(config, kw ,false); + config_schema_item_set_argc_minmax(item , 1, CONFIG_DEFAULT_ARG_MAX); + } + } + config_schema_item_type * schema_item = config_get_schema_item(config, kw); + + + if (hash_has_key(config->messages, kw)) + printf("%s \n", (const char *) hash_get(config->messages, kw)); + + if (!config_content_has_item(content, kw)) + config_content_add_item(content, schema_item, current_path_elm); + + + config_content_item_type * content_item = config_content_get_item(content, + config_schema_item_get_kw(schema_item)); + + config_content_node_type * new_node = config_content_item_set_arg__(config_content_get_define_list(content), + config_content_get_errors(content), + content_item, + values, + current_path_elm, + config_filename); + + if(new_node) + config_content_add_node(content, new_node); + + return new_node != NULL; +} + + +/** + This function parses the config file 'filename', and updated the + internal state of the config object as parsing proceeds. If + comment_string != NULL everything following 'comment_string' on a + line is discarded. + + include_kw is a string identifier for an include functionality, if + an include is encountered, the included file is parsed immediately + (through a recursive call to config_parse__). if include_kw == NULL, + include files are not supported. + + Observe that the use of include and relative paths is + quite tricky. The following is currently implemented: + + 1. The front_end function will split the path to the config file + in a path_name component and a file component. + + 2. Recursive calls to config_parse__() will keep control of the + parsers notion of cwd (note that the real OS'wise cwd never + changes), and every item is tagged with the config_cwd + currently active. + + 3. When an item has been entered with type CONFIG_FILE / + CONFIG_DIRECTORY / CONFIG_EXECUTABLE - the item is updated to + reflect to be relative (iff it is relative in the first place) + to the path of the root config file. + + These are not strict rules - it is possible to get other things to + work as well, but the problem is that it very quickly becomes + dependent on 'arbitrariness' in the parsing configuration. + + validate: whether we should validate when complete, that should + typically only be done at the last parsing. + + + define_kw: This a string which can serve as a "#define" for the + parsing. The define_kw keyword should have two arguments - a key + and a value. If the define_kw is present all __subsequent__ + occurences of 'key' are replaced with 'value'. alloc_new_key + is an optinal function (can be NULL) which is used to alloc a new + key, i.e. add leading and trailing 'magic' characters. + + + Example: + -------- + + char * add_angular_brackets(const char * key) { + char * new_key = (char*)util_alloc_sprintf("<%s>" , key); + } + + + + config_parse(... , "myDEF" , add_angular_brackets , ...) + + + Config file: + ------------- + myDEF Name BJARNE + myDEF pet Dog + ... + ... + PERSON 28 + ... + ------------ + + After parsing we will have an entry: "NAME" , "Bjarne" , "28" , "Dog". + + The key-value pairs internalized during the config parsing are NOT + returned to the calling scope in any way. +*/ + + +static void config_parse__(config_parser_type * config, + config_content_type * content, + path_stack_type * path_stack, + const char * config_filename, + const char * comment_string, + const char * include_kw, + const char * define_kw, + config_schema_unrecognized_enum unrecognized, + bool validate) +{ + assert_no_circular_includes(content, config_filename); + + // Relocate + char * config_path; + char * config_file; + alloc_config_filename_components(config_filename, &config_path, &config_file); + + config_path_elm_type * current_path_elm = config_relocate(config_path, content, path_stack); + free(config_path); + + // Setup config parsing + const char * comment_end = comment_string ? "\n" : NULL; + basic_parser_type * parser = basic_parser_alloc(" \t", "\"", NULL, NULL, comment_string, comment_end); + + FILE * stream = util_fopen(config_file, "r"); + bool at_eof = false; + + while (!at_eof) { + char * line_buffer = util_fscanf_alloc_line(stream , &at_eof); + if (!line_buffer) + continue; + + stringlist_type * token_list = basic_parser_tokenize_buffer(parser, line_buffer, true); + int active_tokens = stringlist_get_size(token_list); + + if (active_tokens > 0) { + const char * kw = stringlist_iget(token_list, 0); + + // Include config file + if (include_kw && (strcmp(include_kw, kw) == 0)) { + if (active_tokens != 2) + util_abort("%s: keyword:%s must have exactly one argument. \n", __func__, include_kw); + + const char * include_file = stringlist_iget(token_list, 1); + + if (!util_file_exists(include_file)) { + char * error_message = (char*)util_alloc_sprintf("%s file:%s not found", include_kw, include_file); + config_error_add(config_content_get_errors(content), error_message); + free(error_message); + } + + config_parse__(config, + content, + path_stack, + include_file, + comment_string, + include_kw, + define_kw, + unrecognized, + false); + } + + // Add define + else if (define_kw && (strcmp(define_kw , kw) == 0)) { + if (active_tokens < 3) + util_abort("%s: keyword:%s must have exactly one (or more) arguments. \n", __func__, define_kw); + + char * key = (char*)util_alloc_string_copy(stringlist_iget(token_list, 1)); + char * value = stringlist_alloc_joined_substring(token_list, 2, active_tokens, " "); + + config_content_add_define(content, key, value); + + free(key); + free(value); + } + + // Add keyword + else + config_parser_add_key_values(config, content, kw, token_list, current_path_elm, config_file, unrecognized); + } + + stringlist_free(token_list); + free(line_buffer); + } + + fclose(stream); + basic_parser_free(parser); + + if (validate) + config_validate(config, content); + + free(config_file); + path_stack_pop(path_stack); + config_content_pop_path_stack(content); +} + + +config_content_type * config_parse(config_parser_type * config , + const char * filename, + const char * comment_string , + const char * include_kw , + const char * define_kw , + const hash_type * pre_defined_kw_map, + config_schema_unrecognized_enum unrecognized_behaviour, + bool validate) { + + config_content_type * content = config_content_alloc( filename ); + + if(pre_defined_kw_map != NULL) { + hash_iter_type * keys = hash_iter_alloc(pre_defined_kw_map); + + while(!hash_iter_is_complete(keys)) { + const char * key = hash_iter_get_next_key(keys); + const char * value = (const char*)hash_get(pre_defined_kw_map, key); + config_content_add_define( content , key , value ); + } + + hash_iter_free(keys); + } + + + if (util_file_readable( filename )) { + path_stack_type * path_stack = path_stack_alloc(); + config_parse__(config , content , path_stack , filename , comment_string , include_kw , define_kw , unrecognized_behaviour , validate); + path_stack_free( path_stack ); + } else { + char * error_message = (char*)util_alloc_sprintf("Could not open file:%s for parsing" , filename); + config_error_add( config_content_get_errors( content ) , error_message ); + free( error_message ); + } + + if (config_error_count( config_content_get_errors( content ) ) == 0) + config_content_set_valid( content ); + + return content; +} + + + + +/*****************************************************************/ + + + + +/** + This function adds an alias to an existing item; so that the + value+++ of an item can be referred to by two different names. +*/ + + +void config_add_alias(config_parser_type * config , const char * src , const char * alias) { + if (config_has_schema_item(config , src)) { + config_schema_item_type * item = config_get_schema_item(config , src); + config_insert_schema_item(config , alias , item , true); + } else + util_abort("%s: item:%s not recognized \n",__func__ , src); +} + + + +void config_install_message(config_parser_type * config , const char * kw, const char * message) { + hash_insert_hash_owned_ref(config->messages , kw , util_alloc_string_copy(message) , free); +} + + +void config_parser_deprecate(config_parser_type * config , const char * kw, const char * msg) { + if (config_has_schema_item(config , kw)) { + config_schema_item_type * item = config_get_schema_item(config , kw); + config_schema_item_set_deprecated(item , msg); + } else + util_abort("%s: item:%s not recognized \n",__func__ , kw); +} + + diff --git a/libres/lib/config/config_path_elm.cpp b/libres/lib/config/config_path_elm.cpp new file mode 100644 index 00000000000..36ebee12fb7 --- /dev/null +++ b/libres/lib/config/config_path_elm.cpp @@ -0,0 +1,140 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'config_path_elm.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 CONFIG_PATH_ELM_TYPE_ID 7100063 + +struct config_path_elm_struct { + UTIL_TYPE_ID_DECLARATION; + char * abs_path; // This will always be absolute + char * rel_path; // This will always be relative to the root path. + const config_root_path_type * root_path; +}; + +static UTIL_SAFE_CAST_FUNCTION( config_path_elm , CONFIG_PATH_ELM_TYPE_ID ) + + +config_path_elm_type * config_path_elm_alloc( const config_root_path_type * root_path , const char * path) { + if (root_path != NULL) { + config_path_elm_type * path_elm = (config_path_elm_type*)util_malloc( sizeof * path_elm ); + UTIL_TYPE_ID_INIT(path_elm , CONFIG_PATH_ELM_TYPE_ID); + path_elm->root_path = root_path; + if (path == NULL) { + path_elm->rel_path = NULL; + path_elm->abs_path = util_alloc_string_copy( config_root_path_get_abs_path(root_path) ); + } else { + if (util_is_abs_path( path )) { + path_elm->abs_path = util_alloc_string_copy( path ); + path_elm->rel_path = util_alloc_rel_path( config_root_path_get_abs_path(root_path) , path ); + } else { + { + char * tmp_abs_path = (char*)util_alloc_filename( config_root_path_get_abs_path(root_path) , path , NULL ); + path_elm->abs_path = util_alloc_abs_path( tmp_abs_path ); + free( tmp_abs_path ); + } + path_elm->rel_path = util_alloc_string_copy( path ); + } + } + return path_elm; + } else { + util_abort("%s: root_path input argument == NULL - invalid \n",__func__); + return NULL; + } +} + + + + +void config_path_elm_free( config_path_elm_type * path_elm ) { + free( path_elm->rel_path ); + free( path_elm->abs_path ); + free( path_elm ); +} + + + +void config_path_elm_free__( void * arg ) { + config_path_elm_type * path_elm = config_path_elm_safe_cast( arg ); + config_path_elm_free( path_elm ); +} + +const char * config_path_elm_get_relpath( const config_path_elm_type * path_elm ) { + return path_elm->rel_path; +} + +const char * config_path_elm_get_abspath( const config_path_elm_type * path_elm ) { + return path_elm->abs_path; +} + + +/*****************************************************************/ + + +char * config_path_elm_alloc_path(const config_path_elm_type * path_elm , const char * path) { + if (util_is_abs_path( path )) + return util_alloc_string_copy( path ); + else { + /* This will be relative or absolute depending on the relative/absolute + status of the root_path. */ + const char * input_root = config_root_path_get_input_path( path_elm->root_path ); + char * tmp_path; + char * return_path; + if (input_root == NULL) + tmp_path = util_alloc_filename( path_elm->rel_path , path , NULL); + else { + const char * str_list[3] = { input_root , path_elm->rel_path , path }; + tmp_path = util_alloc_joined_string( str_list , 3 , UTIL_PATH_SEP_STRING ); + } + + return_path = util_alloc_normal_path( tmp_path ); + free( tmp_path ); + + return return_path; + } +} + + +char * config_path_elm_alloc_relpath(const config_path_elm_type * path_elm , const char * input_path) { + if (util_is_abs_path( input_path )) + return util_alloc_rel_path( config_root_path_get_rel_path( path_elm->root_path ) , input_path); + else { + char * abs_path = config_path_elm_alloc_abspath( path_elm , input_path ); + char * rel_path = (char*)util_alloc_rel_path( config_root_path_get_abs_path( path_elm->root_path ) , abs_path ); + free( abs_path ); + return rel_path; + } +} + + +char * config_path_elm_alloc_abspath(const config_path_elm_type * path_elm , const char * input_path) { + if (util_is_abs_path( input_path )) + return util_alloc_string_copy( input_path ); + else { + char * abs_path1 = (char*)util_alloc_filename( path_elm->abs_path , input_path , NULL ); + char * abs_path = (char*)util_alloc_realpath__( abs_path1 ); // The util_alloc_realpath__() will work also for nonexsting paths + free( abs_path1 ); + return abs_path; + } +} diff --git a/libres/lib/config/config_path_stack.cpp b/libres/lib/config/config_path_stack.cpp new file mode 100644 index 00000000000..f31911c31af --- /dev/null +++ b/libres/lib/config/config_path_stack.cpp @@ -0,0 +1,68 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'config_path_stack.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 CONFIG_PATH_STACK_TYPE_ID 86751520 + +struct config_path_stack_struct { + UTIL_TYPE_ID_DECLARATION; + vector_type * storage; + vector_type * stack; +}; + + + +config_path_stack_type * config_path_stack_alloc( ) { + config_path_stack_type * path_stack = (config_path_stack_type*)util_malloc( sizeof * path_stack); + UTIL_TYPE_ID_INIT( path_stack , CONFIG_PATH_STACK_TYPE_ID ); + path_stack->storage = vector_alloc_new(); + path_stack->stack = vector_alloc_new(); + return path_stack; +} + + +void config_path_stack_free( config_path_stack_type * path_stack ) { + vector_free( path_stack->storage ); + vector_free( path_stack->stack ); +} + + +void config_path_stack_append( config_path_stack_type * path_stack , config_path_elm_type * path_elm) { + vector_append_owned_ref( path_stack->storage , path_elm , config_path_elm_free__); + vector_append_ref( path_stack->stack , path_elm ); +} + + +int config_path_stack_size( const config_path_stack_type * path_stack ) { + return vector_get_size( path_stack->stack ); +} + +const config_path_elm_type * config_path_stack_get_last( const config_path_stack_type * path_stack ) { + return (const config_path_elm_type*)vector_get_last_const(path_stack->stack); +} + + +void config_path_stack_pop( config_path_stack_type * path_stack ) { + vector_pop_back( path_stack->stack ); +} diff --git a/libres/lib/config/config_root_path.cpp b/libres/lib/config/config_root_path.cpp new file mode 100644 index 00000000000..23d6ea398c3 --- /dev/null +++ b/libres/lib/config/config_root_path.cpp @@ -0,0 +1,89 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'config_root_path.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 + +struct config_root_path_struct { + char * input_path; + char * abs_path; + char * rel_path; +}; + + +/** + Input must be an existing directory, which will be used as the + root; or NULL in which case cwd will be used as root. The input + directory can be both realtive or absolute. +*/ + +config_root_path_type * config_root_path_alloc( const char * input_path ) { + if (input_path == NULL || util_is_directory( input_path )) { + config_root_path_type * root_path = (config_root_path_type*)util_malloc( sizeof * root_path ); + { + char * cwd = (char*)util_alloc_cwd(); + + root_path->input_path = util_alloc_string_copy( input_path ); + if (input_path == NULL) { + root_path->rel_path = NULL; + root_path->abs_path = util_alloc_string_copy( cwd ); + } else { + if (util_is_abs_path( input_path )) { + root_path->abs_path = util_alloc_string_copy( input_path ); + root_path->rel_path = util_alloc_rel_path( cwd , root_path->abs_path); + } else { + root_path->rel_path = util_alloc_string_copy( input_path ); + { + char * abs_path = (char*)util_alloc_filename( cwd , input_path , NULL ); + root_path->abs_path = util_alloc_realpath( abs_path ); + free( abs_path ); + } + } + } + free( cwd ); + } + return root_path; + } else + return NULL; +} + + +void config_root_path_free( config_root_path_type * root_path ) { + free( root_path->rel_path ); + free( root_path->abs_path ); + free( root_path->input_path ); + free( root_path ); +} + +const char * config_root_path_get_input_path( const config_root_path_type * root_path ) { + return root_path->input_path; +} + + +const char * config_root_path_get_rel_path( const config_root_path_type * root_path ) { + return root_path->rel_path; +} + + +const char * config_root_path_get_abs_path( const config_root_path_type * root_path ) { + return root_path->abs_path; +} diff --git a/libres/lib/config/config_schema_item.cpp b/libres/lib/config/config_schema_item.cpp new file mode 100644 index 00000000000..1bb2b09cc60 --- /dev/null +++ b/libres/lib/config/config_schema_item.cpp @@ -0,0 +1,584 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'config_schema_item.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 + +typedef struct validate_struct validate_type; + +/** + This is a 'support-struct' holding various pieces of information + needed during the validation process. Observe the following about + validation: + + 1. It is atomic, in the sense that if you try to an item like this: + + KW ARG1 ARG2 ARG3 + + where ARG1 and ARG2 are valid, whereas there is something wrong + with ARG3, NOTHING IS SET. + + 2. Validation is a two-step process, the first step is run when an + item is parsed. This includes checking: + + o The number of argument. + o That the arguments have the right type. + o That the values match the selection set. + + The second validation step is done when the pasing is complete, + in this pass we check dependencies - i.e. required_children and + required_children_on_value. + + + Observe that nothing has-to be set in this struct. There are some dependencies: + + 1. Only _one_ of common_selection_set and indexed_selection_set + can be set. + + 2. If setting indexed_selection_set or type_map, you MUST set + argc_max first. +*/ + + +struct validate_struct { + std::set common_selection_set; /* A selection set which will apply uniformly to all the arguments. */ + std::vector> indexed_selection_set; + + int argc_min; /* The minimum number of arguments: -1 means no lower limit. */ + int argc_max; /* The maximum number of arguments: -1 means no upper limit. */ + int_vector_type * type_map; /* A list of types for the items. Set along with argc_minmax(); */ + stringlist_type * required_children; /* A list of item's which must also be set (if this item is set). (can be NULL) */ + hash_type * required_children_value; /* A list of item's which must also be set - depending on the value of this item. (can be NULL) (This one is complex). */ +}; + + + + + +#define CONFIG_SCHEMA_ITEM_ID 6751 +struct config_schema_item_struct { + UTIL_TYPE_ID_DECLARATION; + char * kw; /* The kw which identifies this item */ + + + bool required_set; + stringlist_type * required_children; /* A list of item's which must also be set (if this item is set). (can be NULL) */ + hash_type * required_children_value; /* A list of item's which must also be set - depending on the value of this item. (can be NULL) */ + validate_type * validate; /* Information need during validation. */ + bool expand_envvar; /* Should environment variables like $HOME be expanded?*/ + bool deprecated; + char * deprecate_msg; +}; + + +/*****************************************************************/ + + +/*****************************************************************/ +static void validate_set_default_type( validate_type * validate , config_item_types item_type) { + int_vector_set_default(validate->type_map , item_type); +} + +static validate_type * validate_alloc() { + validate_type * validate = new validate_type(); + validate->argc_min = CONFIG_DEFAULT_ARG_MIN; + validate->argc_max = CONFIG_DEFAULT_ARG_MAX; + validate->required_children = NULL; + validate->required_children_value = NULL; + validate->type_map = int_vector_alloc(0 , 0); + validate_set_default_type( validate , CONFIG_STRING ); + return validate; +} + + +static void validate_free(validate_type * validate) { + int_vector_free( validate->type_map ); + if (validate->required_children != NULL) stringlist_free(validate->required_children); + if (validate->required_children_value != NULL) hash_free(validate->required_children_value); + delete validate; +} + + +static void validate_iset_type( validate_type * validate , int index , config_item_types type) { + int_vector_iset( validate->type_map , index , type); +} + + +static config_item_types validate_iget_type( const validate_type * validate , int index) { + return (config_item_types)int_vector_safe_iget( validate->type_map , index ); +} + + +static void validate_set_argc_minmax(validate_type * validate , int argc_min , int argc_max) { + if (validate->argc_min != CONFIG_DEFAULT_ARG_MIN) + util_abort("%s: sorry - current implementation does not allow repeated calls to: %s \n",__func__ , __func__); + + if (argc_min == CONFIG_DEFAULT_ARG_MIN) + argc_min = 0; + + validate->argc_min = argc_min; + validate->argc_max = argc_max; + + if ((argc_max != CONFIG_DEFAULT_ARG_MAX) && (argc_max < argc_min)) + util_abort("%s invalid arg min/max values. argc_min:%d argc_max:%d \n",__func__ , argc_min , argc_max); +} + + + +static void validate_set_common_selection_set(validate_type * validate , const stringlist_type * argv) { + validate->common_selection_set.clear(); + for (int i=0; i < stringlist_get_size(argv); i++) + validate->common_selection_set.insert( stringlist_iget(argv,i)); +} + + +static std::set* validate_iget_selection_set( validate_type * validate , int index) { + if (index >= validate->indexed_selection_set.size()) + return nullptr; + + return &validate->indexed_selection_set[index]; +} + +static void validate_add_indexed_alternative(validate_type * validate , int index , const char * value) { + if (index >= validate->indexed_selection_set.size()) + validate->indexed_selection_set.resize(index+1); + + auto& set = validate->indexed_selection_set[index]; + set.insert( value ); +} + + + +static void validate_set_indexed_selection_set(validate_type * validate , int index , const stringlist_type * argv) { + if (index >= validate->argc_min) + util_abort("%s: When not not setting argc_max selection set can only be applied to indices up to argc_min\n",__func__); + + if (index >= validate->indexed_selection_set.size()) + validate->indexed_selection_set.resize(index+1); + + auto& set = validate->indexed_selection_set[index]; + for (int i=0; i < stringlist_get_size(argv); i++) + set.insert( stringlist_iget(argv, i) ); +} + + +/*****************************************************************/ + + +static UTIL_SAFE_CAST_FUNCTION( config_schema_item , CONFIG_SCHEMA_ITEM_ID) + +void config_schema_item_assure_type(const config_schema_item_type * item , int index , int type_mask) { + bool OK = false; + + if (int_vector_safe_iget( item->validate->type_map , index) & type_mask) + OK = true; + + if (!OK) + util_abort("%s: failed - wrong installed type \n" , __func__); +} + + +config_schema_item_type * config_schema_item_alloc(const char * kw , bool required) { + config_schema_item_type * item = (config_schema_item_type*)util_malloc(sizeof * item ); + UTIL_TYPE_ID_INIT( item , CONFIG_SCHEMA_ITEM_ID); + item->kw = util_alloc_string_copy(kw); + + item->required_set = required; + item->deprecated = false; + item->deprecate_msg = NULL; + item->required_children = NULL; + item->required_children_value = NULL; + item->expand_envvar = true; /* Default is to expand $VAR expressions; can be turned off with + config_schema_item_set_envvar_expansion( item , false ); */ + item->validate = validate_alloc(); + return item; +} + +bool config_schema_item_valid_string(config_item_types value_type , const char * value, bool runtime) +{ + switch(value_type) { + case(CONFIG_ISODATE): + return util_sscanf_isodate( value , NULL ); + break; + case(CONFIG_INT): + return util_sscanf_int( value , NULL ); + break; + case(CONFIG_FLOAT): + return util_sscanf_double( value , NULL ); + break; + case(CONFIG_BOOL): + return util_sscanf_bool( value , NULL ); + break; + case(CONFIG_BYTESIZE): + return util_sscanf_bytesize( value , NULL); + break; + case(CONFIG_RUNTIME_INT): + if (runtime) + return util_sscanf_int( value , NULL ); + else + return true; + break; + case(CONFIG_RUNTIME_FILE): + if(runtime) + return util_file_exists(value); + else + return true; + break; + default: + return true; + } +} + + +static char * alloc_relocated__(const config_path_elm_type * path_elm , const char * value) { + if ( util_is_abs_path(value) ) + return util_alloc_string_copy( value ); + + return util_alloc_filename( config_path_elm_get_abspath( path_elm ) , value , NULL); +} + +bool config_schema_item_validate_set(const config_schema_item_type * item , stringlist_type * token_list , const char * config_file, const config_path_elm_type * path_elm , config_error_type * error_list) { + bool OK = true; + int argc = stringlist_get_size( token_list ) - 1; + if (item->validate->argc_min >= 0) { + if (argc < item->validate->argc_min) { + OK = false; + { + char * error_message; + if (config_file != NULL) + error_message = util_alloc_sprintf("Error when parsing config_file:\"%s\" Keyword:%s must have at least %d arguments.",config_file , item->kw , item->validate->argc_min); + else + error_message = util_alloc_sprintf("Error:: Keyword:%s must have at least %d arguments.",item->kw , item->validate->argc_min); + + config_error_add( error_list , error_message ); + } + } + } + + if (item->validate->argc_max >= 0) { + if (argc > item->validate->argc_max) { + OK = false; + { + char * error_message; + + if (config_file != NULL) + error_message = util_alloc_sprintf("Error when parsing config_file:\"%s\" Keyword:%s must have maximum %d arguments.",config_file , item->kw , item->validate->argc_max); + else + error_message = util_alloc_sprintf("Error:: Keyword:%s must have maximum %d arguments.",item->kw , item->validate->argc_max); + + config_error_add( error_list , error_message ); + } + } + } + + /* + OK - now we have verified that the number of arguments is correct. Then + we start actually looking at the values. + */ + if (OK) { + /* Validating selection set - first common, then indexed */ + if (item->validate->common_selection_set.size()) { + for (int iarg = 0; iarg < argc; iarg++) { + if (!item->validate->common_selection_set.count(stringlist_iget(token_list, iarg + 1))) { + config_error_add( error_list , util_alloc_sprintf("%s: is not a valid value for: %s.",stringlist_iget( token_list , iarg + 1) , item->kw)); + OK = false; + } + } + } else if (item->validate->indexed_selection_set.size()) { + for (int iarg = 0; iarg < argc; iarg++) { + if ((item->validate->argc_max > 0) || (iarg < item->validate->argc_min)) { /* Without this test we might go out of range on the indexed selection set. */ + const auto * selection_set = validate_iget_selection_set( item->validate , iarg); + if (selection_set && selection_set->size()) { + if (!selection_set->count( stringlist_iget(token_list, iarg + 1))) { + config_error_add( error_list , util_alloc_sprintf("%s: is not a valid value for item %d of \'%s\'.",stringlist_iget( token_list , iarg + 1) , iarg + 1 , item->kw)); + OK = false; + } + } + } + } + } + + /* + Observe that the following code might rewrite the content of + argv for arguments referring to path locations. + */ + + + /* Validate the TYPE of the various argumnents */ + { + for (int iarg = 0; iarg < argc; iarg++) { + const char * value = stringlist_iget(token_list , iarg + 1); + switch (validate_iget_type( item->validate , iarg)) { + case(CONFIG_STRING): /* This never fails ... */ + break; + case(CONFIG_RUNTIME_INT): + break; + case(CONFIG_RUNTIME_FILE): + break; + case(CONFIG_ISODATE): + if (!util_sscanf_isodate( value , NULL )) + config_error_add( error_list , util_alloc_sprintf("Failed to parse:%s as an ISO date: YYYY-MM-DD.",value)); + break; + case(CONFIG_INT): + if (!util_sscanf_int( value , NULL )) + config_error_add( error_list , util_alloc_sprintf("Failed to parse:%s as an integer.",value)); + break; + case(CONFIG_FLOAT): + if (!util_sscanf_double( value , NULL )) { + config_error_add( error_list , util_alloc_sprintf("Failed to parse:%s as a floating point number.", value)); + OK = false; + } + break; + case(CONFIG_PATH): + // As long as we do not reuqire the path to exist it is just a string. + break; + case(CONFIG_EXISTING_PATH): + { + char * path = config_path_elm_alloc_abspath( path_elm , value ); + if (!util_entry_exists(path)) { + config_error_add( error_list , util_alloc_sprintf("Can not find entry %s in %s ",value , config_path_elm_get_relpath( path_elm) )); + OK = false; + } + free( path ); + } + break; + case(CONFIG_EXECUTABLE): + { + if (!util_is_abs_path( value )) { + + char * relocated = alloc_relocated__( path_elm , value ); + if (util_file_exists(relocated)) { + if (util_is_executable(relocated)) + stringlist_iset_copy( token_list , iarg , relocated); + + free( relocated ); + break; + } + + free( relocated ); + + if( strstr( value, "/" ) ) { + /* + * value not naked name, which means it was relative path + * that wasn't found + */ + + config_error_add( error_list , util_alloc_sprintf("Could not locate executable:%s ", value)); + break; + } + + /* + * util_alloc_PATH_executable aborts if some parts of the path is + * not an existing dir, so call it only when its an absolute path + */ + char * path_exe = res_env_alloc_PATH_executable( value ); + if (path_exe != NULL) + stringlist_iset_copy( token_list , iarg , path_exe); + else + config_error_add( error_list , util_alloc_sprintf("Could not locate executable:%s ", value)); + + free(path_exe); + } else { + if (!util_is_executable( value )) + config_error_add( error_list , util_alloc_sprintf("Could not locate executable:%s ", value)); + } + } + break; + case(CONFIG_BOOL): + if (!util_sscanf_bool( value , NULL )) { + config_error_add( error_list , util_alloc_sprintf("Failed to parse:%s as a boolean.", value)); + OK = false; + } + break; + case(CONFIG_BYTESIZE): + if (!util_sscanf_bytesize( value , NULL)) { + config_error_add( error_list , util_alloc_sprintf("Failed to parse:\"%s\" as number of bytes." , value)); + OK = false; + } + break; + default: + util_abort("%s: config_item_type:%d not recognized \n",__func__ , validate_iget_type(item->validate , iarg)); + } + } + } + } + return OK; +} + + +void config_schema_item_free( config_schema_item_type * item) { + free(item->kw); + free( item->deprecate_msg ); + if (item->required_children != NULL) stringlist_free(item->required_children); + if (item->required_children_value != NULL) hash_free(item->required_children_value); + validate_free(item->validate); + free(item); +} + + +void config_schema_item_free__ (void * void_item) { + config_schema_item_type * item = config_schema_item_safe_cast( void_item ); + config_schema_item_free( item ); +} + + + +void config_schema_item_set_required_children_on_value(config_schema_item_type * item , const char * value , stringlist_type * child_list) { + if (item->required_children_value == NULL) + item->required_children_value = hash_alloc(); + hash_insert_hash_owned_ref( item->required_children_value , value , stringlist_alloc_deep_copy(child_list) , stringlist_free__); +} + + + +/** + This function is used to set the minimum and maximum number of + arguments for an item. In addition you can pass in a pointer to an + array of config_schema_item_types values which will be used for validation + of the input. This vector must be argc_max elements long; it can be + NULL. +*/ + + +void config_schema_item_set_argc_minmax(config_schema_item_type * item , + int argc_min , + int argc_max) { + + validate_set_argc_minmax(item->validate , argc_min , argc_max); + +} + +void config_schema_item_iset_type( config_schema_item_type * item , int index , config_item_types type) { + validate_iset_type( item->validate , index , type ); +} + +void config_schema_item_set_default_type( config_schema_item_type * item , config_item_types type) { + validate_set_default_type( item->validate , type ); +} + + +config_item_types config_schema_item_iget_type(const config_schema_item_type * item , int index ) { + return validate_iget_type( item->validate , index ); +} + + + + +void config_schema_item_set_envvar_expansion( config_schema_item_type * item , bool expand_envvar ) { + item->expand_envvar = expand_envvar; +} + + + +void config_schema_item_set_common_selection_set(config_schema_item_type * item , const stringlist_type * argv) { + validate_set_common_selection_set(item->validate , argv); +} + +void config_schema_item_set_indexed_selection_set(config_schema_item_type * item , int index , const stringlist_type * argv) { + validate_set_indexed_selection_set(item->validate , index , argv); +} + +void config_schema_item_add_indexed_alternative(config_schema_item_type * item , int index , const char * value) { + validate_add_indexed_alternative(item->validate , index , value); +} + + +void config_schema_item_add_required_children(config_schema_item_type * item , const char * child_key) { + if (item->required_children == NULL) + item->required_children = stringlist_alloc_new(); + + stringlist_append_copy( item->required_children , child_key ); +} + + +int config_schema_item_num_required_children(const config_schema_item_type * item) { + if (item->required_children == NULL) + return 0; + else + return stringlist_get_size( item->required_children ); +} + + +const char * config_schema_item_iget_required_child( const config_schema_item_type * item , int index) { + return stringlist_iget( item->required_children , index ); +} + + +const char * config_schema_item_get_kw( const config_schema_item_type * item ) { + return item->kw; +} + + +bool config_schema_item_required( const config_schema_item_type * item ) { + return item->required_set; +} + + +bool config_schema_item_expand_envvar( const config_schema_item_type * item ) { + return item->expand_envvar; +} + + +void config_schema_item_get_argc( const config_schema_item_type * item , int *argc_min , int *argc_max) { + *argc_min = item->validate->argc_min; + *argc_max = item->validate->argc_max; +} + + + +bool config_schema_item_has_required_children_value( const config_schema_item_type * item ) { + if (item->required_children_value == NULL) + return false; + else + return true; +} + + + +stringlist_type * config_schema_item_get_required_children_value(const config_schema_item_type * item , const char * value) { + return (stringlist_type*)hash_safe_get( item->required_children_value , value ); +} + +bool config_schema_item_is_deprecated( const config_schema_item_type * item) { + return item->deprecated; +} + +const char * config_schema_item_get_deprecate_msg( const config_schema_item_type * item) { + return item->deprecate_msg; +} + +void config_schema_item_set_deprecated( config_schema_item_type * item , const char * msg) { + item->deprecated = true; + item->deprecate_msg = util_realloc_string_copy(item->deprecate_msg, msg); +} + + +/*****************************************************************/ +/* Small functions to support enum introspection. */ diff --git a/libres/lib/config/config_settings.cpp b/libres/lib/config/config_settings.cpp new file mode 100644 index 00000000000..afe4faaa15e --- /dev/null +++ b/libres/lib/config/config_settings.cpp @@ -0,0 +1,320 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'config_settings.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 SETTING_NODE_TYPE_ID 76254096 + + +struct config_settings_struct { + UTIL_TYPE_ID_DECLARATION; + char * root_key; + hash_type * settings; +}; + +/*****************************************************************/ + +typedef struct setting_node_struct setting_node_type; + +struct setting_node_struct { + UTIL_TYPE_ID_DECLARATION; + config_item_types value_type; + char * key; + char * string_value; +}; + +static void setting_node_assert_type( const setting_node_type * node , config_item_types expected_type) { + if (node->value_type != expected_type) + util_abort("%s: internal error. Asked for type:%d is of type:%d \n",__func__ , expected_type , node->value_type); +} + +UTIL_SAFE_CAST_FUNCTION( setting_node , SETTING_NODE_TYPE_ID ) + +static setting_node_type * setting_node_alloc( const char * key, config_item_types value_type, const char * initial_value) { + if (!config_schema_item_valid_string( value_type , initial_value, false)) + return NULL; + + { + setting_node_type * node = (setting_node_type*)util_malloc( sizeof * node ); + UTIL_TYPE_ID_INIT( node , SETTING_NODE_TYPE_ID ); + node->value_type = value_type; + node->string_value = util_alloc_string_copy( initial_value ); + node->key = util_alloc_string_copy( key ); + return node; + } +} + + +static void setting_node_free( setting_node_type * node ) { + free( node->key ); + free( node->string_value ); + free( node ); +} + + +static void setting_node_free__( void * arg ) { + setting_node_type * node = setting_node_safe_cast( arg ); + setting_node_free( node ); +} + + +static bool setting_node_set_value( setting_node_type * node, const char * value) { + if (config_schema_item_valid_string(node->value_type , value, false)) { + node->string_value = util_realloc_string_copy( node->string_value , value ); + return true; + } else + return false; +} + + +static void setting_node_set_string_value( setting_node_type * node, const char * value) { + setting_node_assert_type( node , CONFIG_STRING ); + setting_node_set_value( node , value ); +} + + +static void setting_node_set_int_value( setting_node_type * node, int value) { + setting_node_assert_type( node , CONFIG_INT ); + { + char * string_value = (char*)util_alloc_sprintf("%d" , value); + setting_node_set_value( node , string_value ); + free( string_value ); + } +} + +static void setting_node_set_double_value( setting_node_type * node, double value) { + setting_node_assert_type( node , CONFIG_FLOAT ); + { + char * string_value = (char*)util_alloc_sprintf("%g" , value); + setting_node_set_value( node , string_value ); + free( string_value ); + } +} + + +static void setting_node_set_bool_value( setting_node_type * node, bool value) { + setting_node_assert_type( node , CONFIG_BOOL ); + if (value) + setting_node_set_value( node , "True"); + else + setting_node_set_value( node , "False"); +} + + +static const char * setting_node_get_value( const setting_node_type * node) { + return node->string_value; +} + + +static const char * setting_node_get_string_value( const setting_node_type * node) { + setting_node_assert_type( node , CONFIG_STRING ); + return node->string_value; +} + +static int setting_node_get_int_value( const setting_node_type * node) { + setting_node_assert_type( node , CONFIG_INT ); + return strtol( node->string_value , NULL , 10 ); +} + +static double setting_node_get_double_value( const setting_node_type * node) { + setting_node_assert_type( node , CONFIG_FLOAT ); + return strtod( node->string_value , NULL ); +} + + +static bool setting_node_get_bool_value( const setting_node_type * node) { + bool bool_value; + setting_node_assert_type( node , CONFIG_BOOL ); + util_sscanf_bool( node->string_value , &bool_value ); + return bool_value; +} + + + + +/*****************************************************************/ + +config_settings_type * config_settings_alloc( const char * root_key ) { + config_settings_type * settings = (config_settings_type*)util_malloc( sizeof * settings ); + settings->root_key = util_alloc_string_copy( root_key ); + settings->settings = hash_alloc(); + return settings; +} + + +void config_settings_free( config_settings_type * settings) { + free( settings->root_key ); + hash_free( settings->settings ); + free( settings ); +} + + + +bool config_settings_add_setting(config_settings_type * settings , const char* key, config_item_types value_type , const char* initial_value) { + setting_node_type * node = setting_node_alloc( key , value_type , initial_value); + if (node) { + hash_insert_hash_owned_ref( settings->settings , key , node , setting_node_free__ ); + return true; + } else + return false; +} + + +void config_settings_add_bool_setting(config_settings_type * settings , const char* key, bool initial_value) { + if (initial_value) + config_settings_add_setting( settings , key , CONFIG_BOOL , "True"); + else + config_settings_add_setting( settings , key , CONFIG_BOOL , "False"); +} + + +void config_settings_add_int_setting(config_settings_type * settings , const char* key, int initial_value) { + char * string_value = (char*)util_alloc_sprintf("%d" , initial_value); + config_settings_add_setting( settings , key , CONFIG_INT , string_value); + free( string_value ); +} + + +void config_settings_add_double_setting(config_settings_type * settings , const char* key, double initial_value) { + char * string_value = (char*)util_alloc_sprintf("%g" , initial_value); + config_settings_add_setting( settings , key , CONFIG_FLOAT , string_value); + free( string_value ); +} + + +void config_settings_add_string_setting(config_settings_type * settings , const char* key, const char * initial_value) { + config_settings_add_setting( settings , key , CONFIG_STRING , initial_value); +} + + + + +bool config_settings_has_key( const config_settings_type * settings , const char * key) { + return hash_has_key( settings->settings , key ); +} + +static setting_node_type * config_settings_get_node( const config_settings_type * config_settings, const char * key){ + return (setting_node_type*)hash_get( config_settings->settings , key ); +} + + +const char * config_settings_get_value( const config_settings_type * config_settings , const char * key) { + setting_node_type * node = config_settings_get_node( config_settings , key ); + return setting_node_get_value( node ); +} + +double config_settings_get_double_value( const config_settings_type * config_settings , const char * key) { + setting_node_type * node = config_settings_get_node( config_settings , key ); + return setting_node_get_double_value( node ); +} + +int config_settings_get_int_value( const config_settings_type * config_settings , const char * key) { + setting_node_type * node = config_settings_get_node( config_settings , key ); + return setting_node_get_int_value( node ); +} + + +bool config_settings_get_bool_value( const config_settings_type * config_settings , const char * key) { + setting_node_type * node = config_settings_get_node( config_settings , key ); + return setting_node_get_bool_value( node ); +} + +config_item_types config_settings_get_value_type( const config_settings_type * config_settings , const char * key) { + setting_node_type * node = config_settings_get_node( config_settings , key ); + return node->value_type; +} + + +bool config_settings_set_value( const config_settings_type * config_settings , const char * key, const char * value) { + if (config_settings_has_key( config_settings , key )) { + setting_node_type * node = config_settings_get_node( config_settings , key ); + return setting_node_set_value( node, value ); + } + + return false; +} + + +bool config_settings_set_int_value( const config_settings_type * config_settings , const char * key, int value) { + if (config_settings_has_key( config_settings , key )) { + setting_node_type * node = config_settings_get_node( config_settings , key ); + setting_node_set_int_value( node, value ); + return true; + } + + return false; +} + + +bool config_settings_set_double_value( const config_settings_type * config_settings , const char * key, double value) { + if (config_settings_has_key( config_settings , key )) { + setting_node_type * node = config_settings_get_node( config_settings , key ); + setting_node_set_double_value( node, value ); + return true; + } + + return false; +} + + +bool config_settings_set_bool_value( const config_settings_type * config_settings , const char * key, bool value) { + if (config_settings_has_key( config_settings , key )) { + setting_node_type * node = config_settings_get_node( config_settings , key ); + setting_node_set_bool_value( node, value ); + return true; + } + + return false; +} + + +void config_settings_init_parser__( const char * root_key , config_parser_type * config , bool required) { + config_schema_item_type * item = config_add_schema_item(config, root_key , required); + config_schema_item_set_argc_minmax(item, 2, 2); +} + + + +void config_settings_init_parser( const config_settings_type * config_settings, config_parser_type * config , bool required) { + config_settings_init_parser__( config_settings->root_key , config , required ); +} + + +void config_settings_apply(config_settings_type * config_settings , const config_content_type * config ) { + for (int i = 0; i < config_content_get_occurences(config, config_settings->root_key); i++) { + const stringlist_type * tokens = config_content_iget_stringlist_ref(config, config_settings->root_key , i); + const char * setting = stringlist_iget(tokens, 0); + const char * value = stringlist_iget(tokens, 1); + + bool set_ok = config_settings_set_value( config_settings , setting , value ); + if (!set_ok) + fprintf(stderr," ** Warning: failed to apply CONFIG_SETTING %s=%s \n",setting,value); + } +} + + +stringlist_type * config_settings_alloc_keys( const config_settings_type * config_settings ) { + return hash_alloc_stringlist(config_settings->settings); +} diff --git a/libres/lib/config/testcase/test.txt b/libres/lib/config/testcase/test.txt new file mode 100644 index 00000000000..30461dad04a --- /dev/null +++ b/libres/lib/config/testcase/test.txt @@ -0,0 +1,76 @@ +-- Dette er en OK kommentar. + + + + + +" " +HISTORY_OBSERVATION C-17:WOPR +{ + ERROR = 0.10; + ERROR_MODE = "rel"; + ERROR_MIN = 0.10; +}; + + + +HISTORY_OBSERVATION C-17:WGOR; +HISTORY_OBSERVATION C-17:WWCT; + + + + +SUMMARY_OBSERVATION SEP_TEST_2006 +{ + VALUE = 1; + ERROR = 0.1; +-- DATE = 22/10/2006; +-- DAYS = 100; + RESTART = 10; + KEY = GOPR:G4; +}; + + + +SUMMARY_OBSERVATION SEP_TEST_2007 +{ + VALUE = 1; + ERROR = 0.1; + RESTART = 10; + KEY = GOPR:G4; +}; + + + +BLOCK_OBSERVATION RFT_2005 +{ + DATE = 22/10/2005; + + OBS P1 { I=1; J=1; K=1; VALUE=10; ERROR=1;}; + OBS P2 { I=2; J=1; K=1; VALUE=10; ERROR=1;}; + OBS P3 { I=3; J=1; K=1; VALUE=10; ERROR=1;}; + OBS P4 { I=4; J=1; K=1; VALUE=10; ERROR=1;}; + OBS P5 { I=5; J=1; K=1; VALUE=10; ERROR=1;}; + OBS P6 { I=6; J=1; K=1; VALUE=10; ERROR=1;}; + OBS P7 { I=7; J=1; K=1; VALUE=10; ERROR=1;}; + OBS P8 { I=8; J=1; K=1; VALUE=10; ERROR=1;}; + OBS P9 { I=9; J=1; K=1; VALUE=10; ERROR=1;}; + OBS P10 { I=10; J=1; K=1; VALUE=10; ERROR=1;}; + OBS P11 { I=11; J=1; K=1; VALUE=10; ERROR=1;}; + OBS P12 { I=12; J=1; K=1; VALUE=10; ERROR=1;}; + + + FIELD = SWAT; +}; + +INTVEC = +[ +0 +1 +2 +3 +]; + +DOUBLEVEC = [0.1, 0.2, 10.1, 3.14]; + +SINGLETON svada; diff --git a/libres/lib/config/testcase/test2.txt b/libres/lib/config/testcase/test2.txt new file mode 100644 index 00000000000..b0f1e1a6680 --- /dev/null +++ b/libres/lib/config/testcase/test2.txt @@ -0,0 +1,9 @@ +-- Dette er en OK kommentar. + + +HISTORY_OBSERVATION C-18:WOPR +{ + ERROR = 0.10; + ERROR_MODE = "rel"; + ERROR_MIN = 0.10; +}; diff --git a/libres/lib/config/tests/config_append_test.cpp b/libres/lib/config/tests/config_append_test.cpp new file mode 100644 index 00000000000..fa30c3e9670 --- /dev/null +++ b/libres/lib/config/tests/config_append_test.cpp @@ -0,0 +1,51 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'config_append_test.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 + + +int main(int argc , char ** argv) { + const char * config_file = argv[1]; + config_parser_type * config = config_alloc(); + config_schema_item_type * item = config_add_schema_item(config , "APPEND" , false ); + config_schema_item_set_argc_minmax( item , 1 , 1); + + { + config_content_type * content = config_parse(config , config_file , "--" , NULL , NULL , NULL , CONFIG_UNRECOGNIZED_IGNORE, true ); + test_assert_true(config_content_is_instance( content )); + test_assert_true(config_content_is_valid( content )); + test_assert_int_equal( config_content_get_occurences( content , "APPEND" ) , 3); + + { + const char * value = config_content_get_value( content , "APPEND"); + test_assert_string_equal( value , "VALUE3"); + } + + config_content_free( content ); + } + + { + config_content_type * content = config_parse( config , "DoesNotExist" , "--" , NULL , NULL , NULL , CONFIG_UNRECOGNIZED_IGNORE, true); + test_assert_false( config_content_is_valid( content )); + config_content_free( content ); + } + exit(0); +} diff --git a/libres/lib/config/tests/config_argc.cpp b/libres/lib/config/tests/config_argc.cpp new file mode 100644 index 00000000000..97be56edb1f --- /dev/null +++ b/libres/lib/config/tests/config_argc.cpp @@ -0,0 +1,85 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'config_argc.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 + +void install_SIGNALS(void) { + signal(SIGSEGV , util_abort_signal); /* Segmentation violation, i.e. overwriting memory ... */ + signal(SIGINT , util_abort_signal); /* Control C */ + signal(SIGTERM , util_abort_signal); /* If killing the program with SIGTERM (the default kill signal) you will get a backtrace. + Killing with SIGKILL (-9) will not give a backtrace.*/ +} + + + +int main(int argc , char ** argv) { + install_SIGNALS(); + { + const char * argc_OK = argv[1]; + const char * argc_less = argv[2]; + const char * argc_more = argv[3]; + + config_parser_type * config = config_alloc(); + config_schema_item_type * schema_item = config_add_schema_item( config , "ITEM" , false ); + config_schema_item_set_argc_minmax( schema_item , 2 , 2 ); + + { + config_content_type * content = config_parse( config , argc_OK , "--" , NULL , NULL , NULL , CONFIG_UNRECOGNIZED_ERROR , true); + test_assert_true( config_content_is_instance( content )); + test_assert_true(config_content_is_valid( content )); + config_content_free( content ); + } + + { + config_content_type * content = config_parse( config , argc_less , "--" , NULL , NULL , NULL , CONFIG_UNRECOGNIZED_ERROR , true); + test_assert_true( config_content_is_instance( content )); + test_assert_false( config_content_is_valid( content )); + + { + const config_error_type * config_error = config_content_get_errors( content ); + const char * error_msg = "Error when parsing config_file:\"argc_less\" Keyword:ITEM must have at least 2 arguments."; + + test_assert_int_equal( config_error_count( config_error ) , 1); + test_assert_string_equal( config_error_iget( config_error , 0 ) , error_msg); + } + config_content_free( content ); + } + + { + config_content_type * content = config_parse( config , argc_more , "--" , NULL , NULL , NULL , CONFIG_UNRECOGNIZED_ERROR , true); + test_assert_true( config_content_is_instance( content )); + test_assert_false( config_content_is_valid( content )); + { + const config_error_type * config_error = config_content_get_errors( content ); + const char * error_msg = "Error when parsing config_file:\"argc_more\" Keyword:ITEM must have maximum 2 arguments."; + + test_assert_int_equal( config_error_count( config_error ) , 1); + test_assert_string_equal( config_error_iget( config_error , 0 ) , error_msg); + } + config_content_free( content ); + } + + config_free( config ); + exit(0); + } +} diff --git a/libres/lib/config/tests/config_config.cpp b/libres/lib/config/tests/config_config.cpp new file mode 100644 index 00000000000..91af5026108 --- /dev/null +++ b/libres/lib/config/tests/config_config.cpp @@ -0,0 +1,29 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'config_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 + + +int main(int argc , char ** argv) { + config_parser_type * config = config_alloc(); + config_add_schema_item( config , "KEYWORD" , false ); + config_free( config ); + exit(0); +} + diff --git a/libres/lib/config/tests/config_content.cpp b/libres/lib/config/tests/config_content.cpp new file mode 100644 index 00000000000..84fc343fd04 --- /dev/null +++ b/libres/lib/config/tests/config_content.cpp @@ -0,0 +1,37 @@ +/* + Copyright (C) 2015 Equinor ASA, Norway. + + The file 'config_content.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 + + +void test_create() { + config_content_type * content = config_content_alloc( "filename" ); + test_assert_true( config_content_is_instance( content ) ); + config_content_free( content ); +} + + + + + + +int main( int argc , char ** argv) { + test_create(); +} diff --git a/libres/lib/config/tests/config_content_item.cpp b/libres/lib/config/tests/config_content_item.cpp new file mode 100644 index 00000000000..f03ec5e202d --- /dev/null +++ b/libres/lib/config/tests/config_content_item.cpp @@ -0,0 +1,51 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'config_content_item.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 + + +int main(int argc , char ** argv) { + const char * config_file = argv[1]; + config_parser_type * config = config_alloc(); + + config_add_schema_item( config , "SET" , true ); + config_add_schema_item( config , "NOTSET" , false ); + + { + config_content_type * content = config_parse( config , config_file , "--" , "INCLUDE" , NULL , NULL , CONFIG_UNRECOGNIZED_IGNORE , true ); + test_assert_true( config_content_is_instance( content )); + test_assert_true(config_content_is_valid( content )); + + test_assert_true( config_content_has_item( content , "SET" )); + test_assert_false( config_content_has_item( content , "NOTSET" ) ); + test_assert_false( config_content_has_item( content , "UNKNOWN" ) ); + + test_assert_true( config_has_schema_item( config , "SET" )); + test_assert_true( config_has_schema_item( config , "NOTSET" )); + test_assert_false( config_has_schema_item( config , "UNKNOWN" )); + + config_content_free( content ); + } + + exit(0); +} + diff --git a/libres/lib/config/tests/config_content_node.cpp b/libres/lib/config/tests/config_content_node.cpp new file mode 100644 index 00000000000..ba034d24d4d --- /dev/null +++ b/libres/lib/config/tests/config_content_node.cpp @@ -0,0 +1,71 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'config_content_node.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 + + +int main(int argc , char ** argv) { + config_schema_item_type * schema = config_schema_item_alloc("TEST" , true); + config_root_path_type * root_path = config_root_path_alloc( NULL ); + config_path_elm_type * cwd = config_path_elm_alloc( root_path , NULL ); + { + config_content_node_type * node = config_content_node_alloc( schema , cwd ); + config_content_node_add_value( node , "KEY1:VALUE1" ); + config_content_node_add_value( node , "KEY2:VALUE2" ); + config_content_node_add_value( node , "KEY3:VALUE3" ); + config_content_node_add_value( node , "KEYVALUE" ); + + test_assert_int_equal( config_content_node_get_size( node ) , 4 ); + test_assert_string_equal( config_content_node_iget( node , 0 ) , "KEY1:VALUE1" ); + test_assert_string_equal( config_content_node_iget( node , 2 ) , "KEY3:VALUE3" ); + + test_assert_string_equal( config_content_node_get_full_string( node , ",") , "KEY1:VALUE1,KEY2:VALUE2,KEY3:VALUE3,KEYVALUE"); + + { + hash_type * opt_hash = hash_alloc( ); + { + config_content_node_init_opt_hash( node , opt_hash , 0 ); + test_assert_int_equal( hash_get_size( opt_hash ) , 3 ); + test_assert_string_equal( (const char *) hash_get( opt_hash , "KEY1" ) , "VALUE1" ); + test_assert_string_equal( (const char *) hash_get( opt_hash , "KEY3" ) , "VALUE3" ); + } + + hash_clear( opt_hash ); + test_assert_int_equal( hash_get_size( opt_hash ) , 0 ); + config_content_node_init_opt_hash( node , opt_hash , 1 ); + test_assert_int_equal( hash_get_size( opt_hash ) , 2 ); + test_assert_string_equal( (const char *) hash_get( opt_hash , "KEY2" ) , "VALUE2" ); + test_assert_string_equal( (const char *) hash_get( opt_hash , "KEY3" ) , "VALUE3" ); + test_assert_false( hash_has_key( opt_hash , "KEY1" ) ); + test_assert_false( hash_has_key( opt_hash , "KEYVALUE" ) ); + hash_free( opt_hash ); + } + + + config_content_node_free( node ); + } + config_path_elm_free( cwd ); + config_root_path_free( root_path ); + config_schema_item_free( schema ); + exit(0); +} + diff --git a/libres/lib/config/tests/config_define.cpp b/libres/lib/config/tests/config_define.cpp new file mode 100644 index 00000000000..36a6079013a --- /dev/null +++ b/libres/lib/config/tests/config_define.cpp @@ -0,0 +1,74 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'config_define.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 + +void test_define(config_parser_type * config , const char * config_file) { + hash_type * pre_defined_kw_map = hash_alloc(); + hash_insert_string(pre_defined_kw_map, "", "TEST_VALUE"); + config_content_type * content = config_parse( config , config_file , NULL , NULL , "DEFINE" , pre_defined_kw_map , CONFIG_UNRECOGNIZED_IGNORE , true ); + hash_free(pre_defined_kw_map); + test_assert_true( config_content_is_instance( content )); + test_assert_true(config_content_is_valid( content )); + { + const subst_list_type * define_list = config_content_get_define_list( content ); + test_assert_true( subst_list_has_key( define_list , "VAR1")); + test_assert_true( subst_list_has_key( define_list , "VAR2")); + test_assert_true( subst_list_has_key( define_list , "VARX")); + test_assert_true( subst_list_has_key( define_list , "")); + test_assert_false( subst_list_has_key( define_list , "VARY")); + + + test_assert_string_equal( subst_list_get_value( define_list , "VAR1") , "100"); + test_assert_string_equal( subst_list_get_value( define_list , "VAR2") , "10"); + test_assert_string_equal( subst_list_get_value( define_list , "VARX") , "1"); + test_assert_string_equal( subst_list_get_value( define_list , "") , "TEST_VALUE"); + } + + config_content_free( content ); +} + + + +config_parser_type * config_create_schema() { + config_parser_type * config = config_alloc(); + + config_add_schema_item( config , "SET" , true ); + config_add_schema_item( config , "NOTSET" , false ); + + return config; +} + + +int main(int argc , char ** argv) { + util_install_signals(); + { + const char * config_file = argv[1]; + config_parser_type * config = config_create_schema(); + + test_define( config , config_file ); + + config_free( config ); + exit(0); + } +} + diff --git a/libres/lib/config/tests/config_error.cpp b/libres/lib/config/tests/config_error.cpp new file mode 100644 index 00000000000..509fe325828 --- /dev/null +++ b/libres/lib/config/tests/config_error.cpp @@ -0,0 +1,39 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'config_error.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 + +int main(int argc , char ** argv) { + config_error_type * config_error = config_error_alloc(); + + { + config_error_type * error_copy = config_error_alloc_copy( config_error ); + + test_assert_true( config_error_equal( config_error , error_copy )); + test_assert_ptr_not_equal( config_error , error_copy ); + + config_error_free( error_copy ); + } + + config_error_free( config_error ); + exit(0); +} + diff --git a/libres/lib/config/tests/config_include_test.cpp b/libres/lib/config/tests/config_include_test.cpp new file mode 100644 index 00000000000..a89c85d529b --- /dev/null +++ b/libres/lib/config/tests/config_include_test.cpp @@ -0,0 +1,128 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'config_include_test.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 parse_test(config_parser_type * config , + const char * root_path , // The new working directory - the test will start by chdir() here. + const char * config_file ) { // The config_file, either as an absolute path - or relative from root_path + + const char * path0 = "PATH0"; + const char * path1 = "path/PATH1"; + const char * path2 = "path/PATH2"; + const char * path3 = "path/subpath/PATH3"; + const char * path4 = "path/subpath/subsubpath/PATH4"; + + char * config_path, *config_rel_path,*config_abs_path; + path_stack_type * path_stack = path_stack_alloc(); + + util_alloc_file_components( config_file , &config_path , NULL , NULL); + path_stack_push( path_stack , NULL ); + if (root_path != NULL) + util_chdir( root_path ); + + config_abs_path = util_alloc_abs_path( config_path ); + config_rel_path = util_alloc_rel_path( NULL , config_abs_path); + + { + config_content_type * content = config_parse( config , config_file , "--" , "INCLUDE" , NULL , NULL , CONFIG_UNRECOGNIZED_IGNORE , true ); + if (config_content_is_valid( content )) { + + char * relpath0 = util_alloc_filename( config_rel_path , path0, NULL); + char * relpath1 = util_alloc_filename( config_rel_path , path1, NULL); + char * relpath2 = util_alloc_filename( config_rel_path , path2, NULL); + char * relpath3 = util_alloc_filename( config_rel_path , path3, NULL); + char * relpath4 = util_alloc_filename( config_rel_path , path4, NULL); + + char * abspath0 = util_alloc_filename( config_abs_path , path0, NULL); + char * abspath1 = util_alloc_filename( config_abs_path , path1, NULL); + char * abspath2 = util_alloc_filename( config_abs_path , path2, NULL); + char * abspath3 = util_alloc_filename( config_abs_path , path3, NULL); + char * abspath4 = util_alloc_filename( config_abs_path , path4, NULL); + + test_assert_string_equal(config_content_get_value_as_relpath(content , "PATH0") , relpath0 ); + test_assert_string_equal(config_content_get_value_as_relpath(content , "PATH1") , relpath1 ); + test_assert_string_equal(config_content_get_value_as_relpath(content , "PATH2") , relpath2 ); + test_assert_string_equal(config_content_get_value_as_relpath(content , "PATH3") , relpath3 ); + test_assert_string_equal(config_content_get_value_as_relpath(content , "PATH4") , relpath4 ); + + test_assert_string_equal(config_content_get_value_as_abspath(content , "PATH0") , abspath0 ); + test_assert_string_equal(config_content_get_value_as_abspath(content , "PATH1") , abspath1 ); + test_assert_string_equal(config_content_get_value_as_abspath(content , "PATH2") , abspath2 ); + test_assert_string_equal(config_content_get_value_as_abspath(content , "PATH3") , abspath3 ); + test_assert_string_equal(config_content_get_value_as_abspath(content , "PATH4") , abspath4 ); + + } else { + const config_error_type * error = config_content_get_errors( content ); + config_error_fprintf( error , true , stdout ); + test_error_exit("Hmm - parsing %s failed \n", config_file ); + } + config_content_free( content ); + } + path_stack_pop( path_stack ); +} + + +int main(int argc , char ** argv) { + const char * abs_path = argv[1]; + const char * config_file = argv[2]; + char * abs_config_file = util_alloc_filename( abs_path , config_file , NULL); + config_parser_type * config = config_alloc(); + + { + config_schema_item_type * schema_item; + + schema_item = config_add_schema_item( config , "PATH0" , true ); + config_schema_item_set_argc_minmax( schema_item , 1 , 1 ); + config_schema_item_iset_type( schema_item , 0 , CONFIG_PATH ); + + schema_item = config_add_schema_item( config , "PATH1" , true ); + config_schema_item_set_argc_minmax( schema_item , 1 , 1 ); + config_schema_item_iset_type( schema_item , 0 , CONFIG_PATH ); + + schema_item = config_add_schema_item( config , "PATH2" , true ); + config_schema_item_set_argc_minmax( schema_item , 1 , 1 ); + config_schema_item_iset_type( schema_item , 0 , CONFIG_PATH ); + + schema_item = config_add_schema_item( config , "PATH3" , true ); + config_schema_item_set_argc_minmax( schema_item , 1 , 1 ); + config_schema_item_iset_type( schema_item , 0 , CONFIG_PATH ); + + schema_item = config_add_schema_item( config , "PATH4" , true ); + config_schema_item_set_argc_minmax( schema_item , 1 , 1 ); + config_schema_item_iset_type( schema_item , 0 , CONFIG_PATH ); + } + + parse_test( config , abs_path , config_file ); + parse_test( config , abs_path , abs_config_file ); + parse_test( config , NULL , abs_config_file ); + parse_test( config , "../../" , abs_config_file ); + + config_free( config ); + exit(0); +} + + + + diff --git a/libres/lib/config/tests/config_node_test.cpp b/libres/lib/config/tests/config_node_test.cpp new file mode 100644 index 00000000000..7dd65de59ce --- /dev/null +++ b/libres/lib/config/tests/config_node_test.cpp @@ -0,0 +1,54 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'config_node_test.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 + + +int main(int argc , char ** argv) { + const char * config_file = argv[1]; + config_parser_type * config = config_alloc(); + { + config_schema_item_type * item = config_add_schema_item(config , "APPEND" , false ); + config_schema_item_set_argc_minmax( item , 1 , 1); + } + config_add_schema_item(config , "NEXT" , false ); + + config_content_type * content = config_parse(config , config_file , "--" , NULL , NULL , NULL , CONFIG_UNRECOGNIZED_IGNORE, true ); + + if (config_content_is_valid( content )) { + if (config_content_get_size( content ) == 4) { + const config_content_node_type * node0 = config_content_iget_node( content , 0 ); + if (strcmp( config_content_node_get_kw( node0 ) , "APPEND") == 0) { + if (config_content_node_get_size(node0) == 1) { + const config_content_node_type * node3 = config_content_iget_node( content , 3 ); + if (strcmp( config_content_node_get_kw( node3 ) , "NEXT") == 0) { + if (config_content_node_get_size(node3) == 2) { + config_content_free( content ); + exit(0); + } else printf("Size error node3\n"); + } else printf("kw error node3 \n"); + } else printf("Size error node0\n"); + } else printf("kw error node0 kw:%s \n", config_content_node_get_kw( node0 )); + } else printf("Size error \n"); + } else printf("Parse error"); + + config_content_free( content ); + exit(1); +} + diff --git a/libres/lib/config/tests/config_path_elm.cpp b/libres/lib/config/tests/config_path_elm.cpp new file mode 100644 index 00000000000..c6802a53495 --- /dev/null +++ b/libres/lib/config/tests/config_path_elm.cpp @@ -0,0 +1,84 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'config_path_elm.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 + +int main(int argc , char ** argv) { + const char * rel_path = "rel/path"; + const char * rel_true = "rel/path/XXX"; + const char * path_true1 = "rel/path/XXX"; + + ecl::util::TestArea ta("config_path"); + const char * root = ta.test_cwd().c_str(); + char * abs_path = util_alloc_filename( root , "rel/path" , NULL); + char * abs_true = util_alloc_filename( root , "rel/path/XXX" , NULL); + char * path_true2 = util_alloc_filename( root , "rel/path/XXX" , NULL); + + util_chdir( ta.original_cwd().c_str() ); + config_root_path_type * root_path = config_root_path_alloc( root ); + { + config_path_elm_type * path_elm = config_path_elm_alloc( root_path , rel_path ); + + test_assert_string_equal( config_path_elm_get_relpath( path_elm ) , rel_path ); + test_assert_string_equal( config_path_elm_get_abspath( path_elm ) , abs_path ); + + test_assert_string_equal( config_path_elm_alloc_relpath( path_elm , "XXX" ) , rel_true); + test_assert_string_equal( config_path_elm_alloc_abspath( path_elm , "XXX" ) , abs_true); + test_assert_string_equal( config_path_elm_alloc_path( path_elm , "XXX" ) , path_true2 ); + + + config_path_elm_free( path_elm ); + } + { + config_path_elm_type * path_elm = config_path_elm_alloc( root_path , abs_path ); + + test_assert_string_equal( config_path_elm_get_relpath( path_elm ) , rel_path ); + test_assert_string_equal( config_path_elm_get_abspath( path_elm ) , abs_path ); + + test_assert_string_equal( config_path_elm_alloc_relpath( path_elm , "XXX" ) , rel_true); + test_assert_string_equal( config_path_elm_alloc_abspath( path_elm , "XXX" ) , abs_true); + test_assert_string_equal( config_path_elm_alloc_path( path_elm , "XXX" ) , path_true2 ); + + config_path_elm_free( path_elm ); + } + config_root_path_free( root_path ); + + util_chdir( root ); + root_path = config_root_path_alloc( NULL ); + { + config_path_elm_type * path_elm = config_path_elm_alloc( root_path , rel_path ); + + test_assert_string_equal( config_path_elm_get_relpath( path_elm ) , rel_path ); + test_assert_string_equal( config_path_elm_get_abspath( path_elm ) , abs_path ); + + test_assert_string_equal( config_path_elm_alloc_relpath( path_elm , "XXX" ) , rel_true); + test_assert_string_equal( config_path_elm_alloc_abspath( path_elm , "XXX" ) , abs_true); + test_assert_string_equal( config_path_elm_alloc_path( path_elm , "XXX" ) , path_true1 ); + + + config_path_elm_free( path_elm ); + } + + exit(0); +} + diff --git a/libres/lib/config/tests/config_root_path.cpp b/libres/lib/config/tests/config_root_path.cpp new file mode 100644 index 00000000000..d173344dbe0 --- /dev/null +++ b/libres/lib/config/tests/config_root_path.cpp @@ -0,0 +1,75 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'config_root_path.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 + + +int main(int argc , char ** argv) { + char * cwd = util_alloc_cwd(); + + { + config_root_path_type * root_path = config_root_path_alloc( NULL ); + + if (!test_check_string_equal( config_root_path_get_abs_path( root_path ) , cwd )) + test_error_exit("abs:path:%s expeceted:%s \n",config_root_path_get_abs_path( root_path ) , cwd ); + + if (!test_check_string_equal( config_root_path_get_input_path( root_path ) , NULL )) + test_error_exit("input:path:%s expeceted:%s \n",config_root_path_get_input_path( root_path ) , NULL ); + + if (!test_check_string_equal( config_root_path_get_rel_path( root_path ) , NULL )) + test_error_exit("rel:path:%s expeceted:%s \n",config_root_path_get_rel_path( root_path ) , NULL ); + + + config_root_path_free( root_path ); + } + + + { + config_root_path_type * root_path = config_root_path_alloc( "/does/not/exist" ); + if (root_path != NULL) + test_error_exit("Created root_path instance for not-existing input \n"); + } + + + + { + const char * input_path = argv[1]; + char * cwd = util_alloc_cwd(); + char * rel_path = util_alloc_rel_path( cwd , input_path ); + + config_root_path_type * root_path1 = config_root_path_alloc( input_path ); + config_root_path_type * root_path2 = config_root_path_alloc( rel_path ); + + if (!test_check_string_equal( config_root_path_get_rel_path( root_path1 ) , config_root_path_get_rel_path( root_path2 ))) + test_error_exit("Rel: %s != %s \n",config_root_path_get_rel_path( root_path1 ) , config_root_path_get_rel_path( root_path2)); + + if (!test_check_string_equal( config_root_path_get_abs_path( root_path1 ) , config_root_path_get_abs_path( root_path2 ))) + test_error_exit("Abs: %s != %s \n",config_root_path_get_abs_path( root_path1 ) , config_root_path_get_abs_path( root_path2 )); + + config_root_path_free( root_path1 ); + config_root_path_free( root_path2 ); + } + + + exit(0); +} + diff --git a/libres/lib/config/tests/config_schema_item.cpp b/libres/lib/config/tests/config_schema_item.cpp new file mode 100644 index 00000000000..b0aeb742f85 --- /dev/null +++ b/libres/lib/config/tests/config_schema_item.cpp @@ -0,0 +1,47 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'config_schema_item.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 + + + +int main(int argc , char ** argv) { + config_schema_item_type * schema_item = config_schema_item_alloc( "KW" , false ); + + test_assert_int_equal( config_schema_item_iget_type( schema_item , 1 ) , CONFIG_STRING ); + test_assert_int_equal( config_schema_item_iget_type( schema_item , 2 ) , CONFIG_STRING ); + + config_schema_item_iset_type( schema_item , 0 , CONFIG_INT ); + config_schema_item_iset_type( schema_item , 5 , CONFIG_BOOL ); + + + test_assert_int_equal( config_schema_item_iget_type( schema_item , 0 ) , CONFIG_INT ); + test_assert_int_equal( config_schema_item_iget_type( schema_item , 1 ) , CONFIG_STRING ); + test_assert_int_equal( config_schema_item_iget_type( schema_item , 2 ) , CONFIG_STRING ); + test_assert_int_equal( config_schema_item_iget_type( schema_item , 5 ) , CONFIG_BOOL ); + + config_schema_item_set_default_type( schema_item , CONFIG_FLOAT ); + test_assert_int_equal( config_schema_item_iget_type( schema_item , 7 ) , CONFIG_FLOAT ); + + config_schema_item_free( schema_item ); + exit(0); +} + diff --git a/libres/lib/config/tests/config_typeFail.cpp b/libres/lib/config/tests/config_typeFail.cpp new file mode 100644 index 00000000000..b76e06b067f --- /dev/null +++ b/libres/lib/config/tests/config_typeFail.cpp @@ -0,0 +1,69 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'config_parser_typeFail.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 + + +void error(const char * msg) { + fprintf(stderr , "%s" , msg); + exit(1); +} + + +int main(int argc , char ** argv) { + const char * config_file = argv[1]; + config_parser_type * config = config_alloc(); + { + config_schema_item_type * item = config_add_schema_item(config , "TYPES_KEY" , false ); + config_schema_item_set_argc_minmax( item , 4 , 4 ); + config_schema_item_iset_type( item , 0 , CONFIG_INT ); + config_schema_item_iset_type( item , 1 , CONFIG_FLOAT ); + config_schema_item_iset_type( item , 2 , CONFIG_BOOL ); + + item = config_add_schema_item( config , "SHORT_KEY" , false ); + config_schema_item_set_argc_minmax( item , 1 , 1 ); + + item = config_add_schema_item( config , "LONG_KEY" , false ); + config_schema_item_set_argc_minmax( item , 3 , CONFIG_DEFAULT_ARG_MAX); + } + + { + config_content_type * content = config_parse(config , config_file , "--" , NULL , NULL , NULL , CONFIG_UNRECOGNIZED_IGNORE, true ); + + if (config_content_is_valid( content )) { + error("Parse error\n"); + } else { + const config_error_type * cerror = config_content_get_errors( content ); + if (config_error_count( cerror ) > 0) { + int i; + for (i=0; i < config_error_count( cerror ); i++) { + printf("Error %d: %s \n",i , config_error_iget( cerror , i )); + } + } + test_assert_int_equal( 5 , config_error_count( cerror )); + } + config_content_free( content ); + } + printf("OK \n"); + exit(0); +} + diff --git a/libres/lib/config/tests/config_typeOK.cpp b/libres/lib/config/tests/config_typeOK.cpp new file mode 100644 index 00000000000..033d40acb03 --- /dev/null +++ b/libres/lib/config/tests/config_typeOK.cpp @@ -0,0 +1,49 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'config_parser_typeOK.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 + + +int main(int argc , char ** argv) { + const char * config_file = argv[1]; + config_parser_type * config = config_alloc(); + { + config_schema_item_type * item = config_add_schema_item(config , "TYPE_KEY" , false ); + config_schema_item_set_argc_minmax( item , 4 , 4 ); + config_schema_item_iset_type( item , 0 , CONFIG_INT ); + config_schema_item_iset_type( item , 1 , CONFIG_FLOAT ); + config_schema_item_iset_type( item , 2 , CONFIG_BOOL ); + + item = config_add_schema_item( config , "SHORT_KEY" , false ); + config_schema_item_set_argc_minmax( item , 1 , 1 ); + + item = config_add_schema_item( config , "LONG_KEY" , false ); + config_schema_item_set_argc_minmax( item , 3 , CONFIG_DEFAULT_ARG_MAX ); + } + { + config_content_type * content = config_parse(config , config_file , "--" , NULL , NULL , NULL , CONFIG_UNRECOGNIZED_IGNORE, true ); + test_assert_true( config_content_is_valid( content )); + config_content_free( content ); + } + + + exit(0); +} + diff --git a/libres/lib/config/tests/data/append_test b/libres/lib/config/tests/data/append_test new file mode 100644 index 00000000000..ca1e6ff90df --- /dev/null +++ b/libres/lib/config/tests/data/append_test @@ -0,0 +1,4 @@ +APPEND VALUE1 +APPEND VALUE2 +APPEND VALUE3 +NEXT VALUE4 VALUE5 diff --git a/libres/lib/config/tests/data/argc_OK b/libres/lib/config/tests/data/argc_OK new file mode 100644 index 00000000000..771501c380a --- /dev/null +++ b/libres/lib/config/tests/data/argc_OK @@ -0,0 +1 @@ +ITEM Arg1 Arg2 diff --git a/libres/lib/config/tests/data/argc_less b/libres/lib/config/tests/data/argc_less new file mode 100644 index 00000000000..de3b1d11eb7 --- /dev/null +++ b/libres/lib/config/tests/data/argc_less @@ -0,0 +1 @@ +ITEM Arg1 diff --git a/libres/lib/config/tests/data/argc_more b/libres/lib/config/tests/data/argc_more new file mode 100644 index 00000000000..bd026bbdc44 --- /dev/null +++ b/libres/lib/config/tests/data/argc_more @@ -0,0 +1,2 @@ +ITEM Arg1 ARG2 Arg3 + diff --git a/libres/lib/config/tests/data/content_item_test b/libres/lib/config/tests/data/content_item_test new file mode 100644 index 00000000000..403ef6ae3e9 --- /dev/null +++ b/libres/lib/config/tests/data/content_item_test @@ -0,0 +1 @@ +SET SetValue diff --git a/libres/lib/config/tests/data/define_test b/libres/lib/config/tests/data/define_test new file mode 100644 index 00000000000..6976c4853f4 --- /dev/null +++ b/libres/lib/config/tests/data/define_test @@ -0,0 +1,5 @@ +DEFINE VAR1 100 +DEFINE VAR2 10 +DEFINE VARX 1 + +SET VAR1 100 diff --git a/libres/lib/config/tests/data/include_test b/libres/lib/config/tests/data/include_test new file mode 100644 index 00000000000..e0d7c873661 --- /dev/null +++ b/libres/lib/config/tests/data/include_test @@ -0,0 +1,3 @@ +PATH0 PATH0 +INCLUDE path/include1 +INCLUDE path/subpath/include2 diff --git a/libres/lib/config/tests/data/path/include1 b/libres/lib/config/tests/data/path/include1 new file mode 100644 index 00000000000..10281317323 --- /dev/null +++ b/libres/lib/config/tests/data/path/include1 @@ -0,0 +1,5 @@ +PATH1 PATH1 +PATH2 PATH2 + +INCLUDE subpath/include3 +INCLUDE subpath/subsubpath/include4 diff --git a/libres/lib/config/tests/data/path/subpath/include2 b/libres/lib/config/tests/data/path/subpath/include2 new file mode 100644 index 00000000000..695a4bc32ff --- /dev/null +++ b/libres/lib/config/tests/data/path/subpath/include2 @@ -0,0 +1 @@ +PATH2 PATH2 diff --git a/libres/lib/config/tests/data/path/subpath/include3 b/libres/lib/config/tests/data/path/subpath/include3 new file mode 100644 index 00000000000..d749fcbbe31 --- /dev/null +++ b/libres/lib/config/tests/data/path/subpath/include3 @@ -0,0 +1 @@ +PATH3 PATH3 diff --git a/libres/lib/config/tests/data/path/subpath/subsubpath/include4 b/libres/lib/config/tests/data/path/subpath/subsubpath/include4 new file mode 100644 index 00000000000..21aadfac54f --- /dev/null +++ b/libres/lib/config/tests/data/path/subpath/subsubpath/include4 @@ -0,0 +1 @@ +PATH4 PATH4 diff --git a/libres/lib/config/tests/data/type_testFail b/libres/lib/config/tests/data/type_testFail new file mode 100644 index 00000000000..47a358dc0b1 --- /dev/null +++ b/libres/lib/config/tests/data/type_testFail @@ -0,0 +1,6 @@ +TYPES_KEY 100 0.75 rue String +TYPES_KEY 100 0.75 True String +TYPES_KEY 100 0.75X True String +TYPES_KEY 100X 0.75 True String +SHORT_KEY 100 100 +LONG_KEY 100 100 diff --git a/libres/lib/config/tests/data/type_testOK b/libres/lib/config/tests/data/type_testOK new file mode 100644 index 00000000000..7c76291c363 --- /dev/null +++ b/libres/lib/config/tests/data/type_testOK @@ -0,0 +1,4 @@ +TYPES_KEY 100 0.75 True String +SHORT_KEY 100 +LONG_KEY 100 100 100 +LONG_KEY asc asc asc asc asc asc as asc sac asc sac sca sca sa sac diff --git a/libres/lib/enkf/README.new_config_draft b/libres/lib/enkf/README.new_config_draft new file mode 100644 index 00000000000..9aa8b8d0fd7 --- /dev/null +++ b/libres/lib/enkf/README.new_config_draft @@ -0,0 +1,251 @@ +-- QUEUE_SYSTEM prevails, but becomes a class. +-- +-- This is a singleton class. I.e., it can only have +-- one instance. +-- +-- Example: +QUEUE_SYSTEM xHydro_LSF +{ + TYPE = LSF; + MAX_JOBS = 10; +}; + + + + +-- SUBSTITUTION takes over for DATA_KW. +-- Adding support for arbitrary files +-- and keys. Adding support for sourced +-- file. +-- +-- Example A: +SUBSTITUTION my_own_hack +{ + FILENAME = "my_magic_file.txt"; + KEY = ""; + VALUE = "SVADAFRANZ_"; +}; + +-- Example B: +SUBSTITUTION your_hack +{ + FILENAME = ; + KEY = "__SVADAFRANZ__"; + VALUE = ; +}; + +-- Example C: +SUBSTITUTION our_hack +{ + FILENAME = "our_file.txt"; + KEY = "__INSERT_HERE__"; + SOURCE_FILE = "the_data.txt"; + +}; + + + +-- JOB takes over for INSTALL_JOB. +-- +-- Example: +JOB STEINFYSIKK +{ + PLATFORM_EXE i386 + { + EXE = "/bin/old/bein.exe"; + }; + + + PLATFORM_EXE x86_64 + { + EXE = "/bin/x86_64/bein.exe"; + }; + + + PLATFORM_EXE ia64 + { + EXE = "/bin/ia64/bein.exe"; + + -- Special arguments and env for this platform. + ARGUMENTS = "-finn -olje -some funky special stuff"; + SETENV F_UMFTENDIAN { VALUE = "special"; }; + }; + + + ARGUMENTS = "-finn -olje"; + + STDIN = ""; + STDOUT = "steinfysikk.stdout"; + + LSF_RESOURCES = "det skal gÃ¥ snabbt!"; + +}; + + + +-- SIMULATION_WORKFLOW takes over for FORWARD_MODEL +-- +-- "Simulation workflow" might be more instructive? +-- Not 100% sure though... +-- +-- Example: +SIMULATION_WORKFLOW = "RELPERM ECLIPSE100 STEINFYSIKK"; + + + +-- SIMULATION_SCHEDULE takes over for ENKF_SCHED_FILE. +-- +-- This is a singleton class. I.e., it can only have +-- one instance. +-- +-- Example: +SIMULATION_SCHEDULE my_schedule +{ + SEGMENT A + { + START = 0; + END = 10; + STRIDE = 5; + UPDATE = ENKF; + SIMULATION_WORKFLOW = "ECLIPSE100 ROCKPHYSICS SEISMIC"; + }; + + + SEGMENT B + { + START = 10; + END = 100; + + -- Use smoother for this interval + UPDATE = ENKS; + }; + + + SEGMENT C + { + START = 100; + END = 200; + UPDATE = NO; + }; + +}; + + + + +-- PARAMETER_COLLECTION takes over for GEN_KW. +-- +-- Example: +PARAMETER_COLLECTION my_big_collection +{ + -- All the parameters in the collection + -- subsitutes into the TEMPLATE_FILE. + TEMPLATE_FILE = "templates/my_template.txt"; + + + -- File produced in the simulation folder. + TARGET_FILE = "my_output.txt"; + + + SCALAR foo + { + -- The key in my_template to subsitute. + -- This shold probably default to + -- __INSTANCE_NAME__ or . + TEMPLATE_KEY = ""; + + + -- Prior distribution can not be used + -- with MAX, MIN, TRANSFER_FUNCTION + -- and PRIOR_SAMPLES. + PRIOR_DISTRIBUTION = "LOGUNIF 0 1"; + }; + + + -- This parameter behaves like the old GEN_KW, + -- but reads input from a user provided file + -- and subsitutes a key different from name. + SCALAR bar + { + TEMPLATE_KEY = "__MAGIC__"; + + -- The file user_provided_realizations.txt shall + -- contain ascii doubles, one per line. + PRIOR_SAMPLES = "user_provided_realizations.txt"; + TRANSFER_FUNCTION = LOG10; + MAX = 100; + MIN = 1; + }; + + -- User provided covariance matrix. + -- + -- Note that we cannot use this if not all + -- parameters are sampled internally! + -- COVARIANCE_MATRIX = "my_covmat.txt"; +}; + + +-- PARAMETER takes over for GEN_PARAM and +-- FIELD * PARAMETER. +-- +-- Example A, subsituting FIELD * PARAMETER. +PARAMETER poro +{ + -- Need to have either: + -- 1. ECL_OUTPUT and KEYWORD (for FIELD * PARAMETER) + -- or + -- 2. TEMPLATE_FILE and KEY (for GEN_PARAM). + ECL_OUTPUT = GRDECL; + KEYWORD = "PORO"; + TARGET_FILE = "PORO.GRDECL"; + PRIOR_SAMPLES = "poro-%d.grdecl"; +}; +-- Example B, subsituting FIELD * PARAMETER +PARAMETER permx +{ + ECL_OUTPUT = "ECLBIN"; + KEYWORD = "PERMX"; + TARGET_FILE = "PERMX.BIN"; + PRIOR_SAMPLES = "permx-%d.roff"; +}; +-- Example C, subsituting GEN_PARAM. +PARAMETER surf +{ + TEMPLATE_FILE = "my_surf_template.txt"; + KEY = "__REPLACE_THIS__"; + TARGET_FILE = "surface.roff"; + PRIOR_SAMPLES = "surf-%d.txt"; +}; + + +-- STATE takes over for FIELD * DYNAMIC +-- and SUMMARY. +-- +-- Example A, replacing FIELD * DYNAMIC. +STATE pres +{ + ECL_SOURCE = RESTART; + KEYWORD = PRESSURE; + MIN = 100; + -- Specify UPDATE_SPACE instead of + -- input and output transform. + UPDATE_SPACE = LOG10; +}; +-- Example B, replacing SUMMARY. +STATE region_pressure; +{ + ECL_SOURCE = SUMMARY; + KEYWORD = RPR:10; + -- Need to think about how this affects observations.. + UPDATE_SPACE = LOG10; +}; + + + +SUMMARY_OBSERVATION foobar +{ + KEY = WOPR:P1; + RESTARTS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + VALUES = [0, 0, 0, 1, 1, 1, 1, 1, 1, 1]; + ERRORS = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; +} diff --git a/libres/lib/enkf/README.new_type.cpp b/libres/lib/enkf/README.new_type.cpp new file mode 100644 index 00000000000..97fd571bde7 --- /dev/null +++ b/libres/lib/enkf/README.new_type.cpp @@ -0,0 +1,343 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'README.new_type.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. +*/ + +/** +The two files README.new_type.c and README.new_type_config.c (along +with the corresponding header files) are meant to serve as a +documentation and reference on how to add new object types to the enkf +system. + +The enkf object system is based on the enkf_node_type as a sort of +abstract class, this object has a void pointer which will point to the +actual data instance (i.e. for instance an instance of the type +"new_type" implemented here), along with several function pointers to +manipulate the data object. + + _______________________________________ _______________________________ + | enkf_node_type instance | | | + | ------------------------------------- | | Actual data , e.g. a field or | + | * void * data |-------------->| a multiplier. | + | * Function pointers to manipulate | |_______________________________| + | data | + | * Something more - not relevant here | + |_______________________________________| + + +The enkf_node object can contain pointers to all types of objects. + + + + + + + +new_type.c (should read README.new_type.h first). +========== +For all the enkf objects we have split the member spesific data, and +the configuration information in two different files (objects): + + ______________ + | | + _________________| Config |_______________ + | | information | | + | /|______________|\ | + | / | \ | + | / | \ | + | / | | | + | | | | | + _|__ __|_ _|__ _|__ _|__ + | | | | | | | | | | + | 01 | | 02 | | 03 | | 04 | | 05 | + |____| |____| |____| |____| |____| + + +The figure shows an ensemble of 5 members (of some type); they all +have a pointer to a common configuration object. The thought behind +this is that all members should share configuration information, +i.e. for instance all the members in a permeability field should have +the same active/inactive cells, all the relperm instances should use +the same relperm model e.t.c. This file is about implementing the data +members, i.e. the small boxes. The implementation of the config +object, is discussed in README.new_type_config.c. + + +1. alloc_ftype: new_type * new_type_alloc(const new_type_config *); + -------------------------------------------- + + This function takes a pointer to a config object of the right type + (i.e. new_type_config_type in this case), reads the required + configuration information from this object, and returns a new new_type + instance. + + +2. ecl_write_ftype: void new_type_ecl_write(const new_type * , const char *); + -------------------------------------------------------------------------- + This function takes a const pointer to a new_type instance, along + with a filename. The function should write eclipse data to the + filename specified in the second argument. + + + +3. fread_ftype: void new_type_fread(new_type * , FILE * ); + ------------------------------------------------ + This function should take a pointer to a new_type object, and a + stream opened for reading as input, and then read in data for the + object from disk. + + +4. fwrite_ftype: void new_type_fwrite(new_type * , FILE * ); + ------------------------------------------------ + This function should take a pointer to a new_type object, and a + stream opened for writing as input, and then write the data from + the object to disk. The two functions instances fread_ftype - and + fwrite_ftype must of course match, but apart from that they are + quite free. + + +5. copyc_ftype: new_type * new_type_copyc(const new_type *); + --------------------------------------------------------------- + This function takes a const pointer to a new_type instance. A new + new_type copy instance with identical data is created. The two + instance share config object. + + +6. initialize_ftype: void new_type_initialize(new_type * , int); + ------------------------------------------------------------ + This function takes a pointer to a new_type instance, and a an + ensemble number as input; it should then initialize the data of + the new_type object - this can either be done by sampling a random + number according to some distribution, by reading in input from an + external program or by calling an external program (i.e. RMS). + + +7. serialize_ftype: int (const new_type * , int , size_t , double * , size_t , size_t , bool *); + -------------------------------------------------------------------------------------------- + This one is a bit tricky ... The enkf_state object, holding among + others things instances of new_type * can be illustrated as this: + + + -------------- + | enkf_state | + ============== ---------------- + | PRESSURE |-------->| Pressure data| + |------------| ---------------- + | New_type |---| + -------------- | ----------------- + | multz | |-------->| new_type data | + -------------- ----------------- + | + | -------------- + |------>| multz data | + -------------- + + The key point of this figure is that the various data we want to + update with enkf are at random locations in memory. To be able to + do the + A' = AX + + matrix multiplication we must assemble this data in a long + vector. That is what is meant by serializing. The serialize + routine is so complicated for (at least) two reasons: + + o We can allocate a certain amount of memory, and then the + serialize routines should continue until the available memory + is exhausted, update and the resume. + + o For efficiency reasons a "funny" stride should be used. + + + +8. deserialize_ftype: int new_type_deserialize(new_type * , int , size_t , const double * , size_t , size_t); + --------------------------------------------------------------------------------------------------------- + This is "just" the opposite of serialize, take data back after the + update (matrix multiplication). + + +9. free_ftype: void new_type_free(new_type *); + ------------------------------------------- + This function should free all the memory used by the new_type + instance. Observe that the config pointer should be left alone, + that will be collected elsewhere. + +10. free_data_ftype: void new_type_free_data(new_type *); + ----------------------------------------------------- + This function should free the data of the new_type instance, but + retain the new_type holding structure. This function is called + after an instance has swapped to disk. + +11. realloc_data_ftype: void (new_type *); + -------------------------------------------------------------- + This function is the "opposite" of function number 10, i.e. it + is to reallocate memory to hold the actual data. + + +12. ensemble_fprintf_results_ftype * fprintf_results + -------------------------------------------------------------- + This function is used to print the reuslts in a formatted nice way. Only + applicable for variables with small amounts of data, like gen_kw. + +13. ecl_load_ftype: void (new_type * , const char * run_path , const char * eclbase, const ecl_sum_type , int report_step) + ----------------------------------------------------------------- + This function is used to load ECLIPSE results from a complete + forward simulation. Observe that the restart data are not loaded + through this interface, and that the summary datka get an + ecl_sum_type * instance for convenience. If you want to add a new + which should be loaded from a eclipse directory, you must manage + with the run_path ankd ecl_base input. + + +Now - since the enkf_node objects can point to arbitrary types of +object the data pointer is a void pointer, and the function pointer +expect (void *) as input, instead of pointers to e.g. new_type +instances. To cast from the typed functions listed above, to functions +accepting (void *) there are several utility functions, i.e. the macro +VOID_ECL_WRITE() will create a void * version of the XXX_ecl_write() +function: + +The macro call: VOID_ECL_WRITE(new_type) will generate the following code: + + void new_type_ecl_write__(void *__new_type) { + new_type_ecl_write( (new_type *) __new_type); + } + +And the macro VOID_ECL_WRITE_HEADER(new_type) will generate the +corresponding header. These (void) functions are the ones used when +initializing the function pointers in the enkf_node object. For +instance for registering the new_type object in the enkf_node +implementation (function: enkf_node_alloc_empty) + +.... +.... +case(NEW_TYPE): + node->alloc = new_type_alloc__; + node->fwrite_f = new_type_fwrite__; + ... + +*/ + + + +/* +OK - here comes the implementation: +*/ + +/* + These are standard header files needed by almost any C-program: +*/ +#include +#include +#include +#include + +/* + Lots of small utility functions for: memory handling, string + handling and file handling. Implemented in the libutil library. +*/ +#include + + +/* + Lots of (ugly) C-macros to generate anonymous versions of the various + functions listed above. +*/ +#include + + +/* + This file contains several enum typedefs, the most important one in + this context is the ert_impl_type; the NEW_TYPE should be added to + this (at the end). +*/ +#include + + +/* + The header file for the actual new_type object. +*/ +#include + + + +/* + These two #define statements, along with the macros found in + enkf_debug.h allow for some simple run_time checks of the various + casts. Observe that the header file enkf_debug.h is included from + the current directory; it is *not* installed in an include/ + directory. +*/ +#define TARGET_TYPE NEW_TYPE /* The variable type in this file - according to the ert_impl_type classification */ +#define DEBUG /* We want debugging */ +#include "enkf_debug.h" + + + +/* + Here comes the new_type_struct defintion: +*/ + +struct new_type_struct { + DEBUG_DECLARE + const new_type_config_type *config; + + 1: scalar_type *scalar; + 2: double *data; +}; + +/* + Observe the following properties of the struct: + + 1. The statement DEBUG_DECLARE inserts a variable __impl_type as the + first element of the struct (if debugging is enabled) with + #define DBEUG. This should be the first element in the struct. + + 2. The struct contains a pointer to a new_type_config_type object; + this pointer is const, as the new_type instances are not allowed + to write the configuration information (it is shared among many + instances), only read it. + + 3. The actual data is stored in either a scalar instance, or just as + double * data. (1: and 2: is *NOT* some new funny C-syntax). + + The scalar_type object is described further down. If use of + scalar_type is not appropriate, you must store the data in some + other way. There are no formal rules to this, but if you stick to + the suggested "double *data;" convention suggested above, you + will get several macros for algebraic manipulation (adding, + scaling, squaring +++) of new_type instances for free. + + Hence - it is *strongly* recommended to use either: + + scalar_type * scalar; + + or + + double * data; + + to hold the actual data. +*/ + + +/* +scalar_type +=========== +This type is implemented in scalar.c / scalar_config.c. +*/ + + + + diff --git a/libres/lib/enkf/README.obs b/libres/lib/enkf/README.obs new file mode 100644 index 00000000000..473f65b21da --- /dev/null +++ b/libres/lib/enkf/README.obs @@ -0,0 +1,52 @@ +/** + +Observations/measurements are handled by three different (toplevel) +objects: + + +obs_node_type +------------- +This object contians qualitative information about the observations, +i.e. when they are active, what is the error of the observations, what +type they are, and how to measure them. The obs_node_type object +contains the two function pointers: + + get_obs : void (const void* , int , * obs_data_type) + measure : void (const void * , const voide * , meas_vector_type) + +The first of these, get_obs, is used to load/get the actual +observation, that can typically be to get an historical rate from the +history; the data is pushed onto an instance of obs_data_type. measure +on the other is used to extract information from the ensemble state, +i.e. measure for instance a simulated rate or a pressure, and push the +data onto a meas_vector_type instance. + +Several obs_node instances are collected in the enkf_obs type, which +is mostly a hash table of obs_node instances. + +The obs_node_type lives through the whole simulation, this in contrast +to the two other data types involved in the process. + + +obs_data_type +------------- +This data type is used to store all the observation AT ONE +TIMESTEP. New observations are just added with a obs_data_add(), and +the structure grows as necessary. Before the next timestep, the +obs_data_type instance is reset. + + +meas_vector_type / meas_matrix_type +----------------------------------- +This data type is used in the same way as the obs_data_type, for +measurements. When an eclipse integration is complete the +obs_node->measure function is called, and the measurements are pushed +onto the meas_vector. (The whole ensemble corresponds to meas_matrix_type). + + +Analysis +-------- +Observe that the analysis step ONLY involves a meas_matrix_type +instance and an obs_data instance. + +----------------------------------------------------------------- diff --git a/libres/lib/enkf/README.paths b/libres/lib/enkf/README.paths new file mode 100644 index 00000000000..a9dd538a059 --- /dev/null +++ b/libres/lib/enkf/README.paths @@ -0,0 +1,25 @@ +The enkf program uses an object call path_fmt_type to store/configure +various paths and filenames (The path_fmt_type is implemented in the +libutil library). For instance the main configuration file must +contain a line like this: + + RUNPATH /tmp/Eclipse/tmpdir_%04d + +This is actually a format string, so when we want to get/make a +directory for an eclipse simulation, the %04d is replaced with the +actual ensemble member. Usage example: + +/* This is common to all members in the ensemble */ +path_fmt_type * run_path_fmt = path_fmt_alloc_directory_fmt("/tmp/Eclipse/tmpdir_%04d" , true); +... +... +/* For a spesific member: */ +{ + char * run_path = path_fmt_alloc_path(run_path_fmt , iens); + +} + + + + + diff --git a/libres/lib/enkf/active_list.cpp b/libres/lib/enkf/active_list.cpp new file mode 100644 index 00000000000..704d8fbf8ea --- /dev/null +++ b/libres/lib/enkf/active_list.cpp @@ -0,0 +1,222 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'active_list.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 + + +/** + This file implements a small structure used to denote which + elements of a node/observation which is active. At the lowest level + the active elements in a node is just a list of integers. This + list of integers, with som extra twists is what is implemented + here. + + All the xxx_config objects have a pointer to an active_list + instance. This pointer is passed to the enkf_serialize / + enkf_deserialize routines. + + Observe that for the (very important!!) special case that all + elements are active the (int *) pointer should not be accessed, and + the code here is free to return NULL. + + +Example +------- + +Consider a situation where faults number 0,4 and 5 should be active in +a fault object. Then the code will be like: + + + .... + active_list_add_index(multflt_config->active_list , 0); + active_list_add_index(multflt_config->active_list , 4); + active_list_add_index(multflt_config->active_list , 5); + .... + + When this fault object is serialized/deserialized only the elements + 0,4,5 are updated. +*/ + + +#define ACTIVE_LIST_TYPE_ID 66109 + +struct active_list_struct { + UTIL_TYPE_ID_DECLARATION; + active_mode_type mode; /* ALL_ACTIVE | INACTIVE | PARTLY_ACTIVE */ + int_vector_type *index_list; /* A list of active indices - if data_size == active_size this can be NULL. */ +}; + +/*****************************************************************/ + + +static UTIL_SAFE_CAST_FUNCTION(active_list , ACTIVE_LIST_TYPE_ID) +UTIL_IS_INSTANCE_FUNCTION( active_list , ACTIVE_LIST_TYPE_ID) + + + + +/** + The newly created active_list default to setting all indices actiove. +*/ +active_list_type * active_list_alloc( ) { + active_list_type * active_list = (active_list_type *)util_malloc(sizeof * active_list); + UTIL_TYPE_ID_INIT( active_list , ACTIVE_LIST_TYPE_ID ); + active_list->index_list = int_vector_alloc(0 , -1); + active_list->mode = ALL_ACTIVE; + return active_list; +} + + +active_list_type * active_list_alloc_copy( const active_list_type * src) { + active_list_type * new_list = active_list_alloc( ); + new_list->mode = src->mode; + int_vector_free( new_list->index_list ) ; + new_list->index_list = int_vector_alloc_copy( src->index_list ); // CXX_CAST_ERROR + return new_list; +} + + +void active_list_copy( active_list_type * target , const active_list_type * src) { + target->mode = src->mode; + int_vector_memcpy( target->index_list , src->index_list); +} + + +void active_list_free( active_list_type * active_list ) { + int_vector_free(active_list->index_list); + free(active_list); +} + + + +void active_list_free__( void * arg ) { + active_list_type * active_list = active_list_safe_cast ( arg ); + active_list_free(active_list); +} + + + +/** + Appends a new index to the current list of active indices, and + setting the mode to PARTLY_ACTIVE. +*/ +void active_list_add_index(active_list_type * active_list, int new_index) { + if (int_vector_contains(active_list->index_list , new_index )) + return; + active_list->mode = PARTLY_ACTIVE; + int_vector_append( active_list->index_list , new_index ); +} + + + + + + + + +/** + When mode == PARTLY_ACTIVE the active_list instance knows the size + of the active set; if the mode is INACTIVE 0 will be returned and + if the mode is ALL_ACTIVE the input parameter @total_size will be + passed back to calling scope. +*/ + + +int active_list_get_active_size(const active_list_type * active_list, int total_size) { + int active_size; + switch( active_list->mode ) { + case PARTLY_ACTIVE: + active_size = int_vector_size( active_list->index_list ); + break; + case INACTIVE: + active_size = 0; + break; + case ALL_ACTIVE: + active_size = total_size; + break; + default: + util_abort("%s: Internal inconsistency in active_list \n",__func__); + active_size = -1; + } + return active_size; +} + + +active_mode_type active_list_get_mode(const active_list_type * active_list) { + return active_list->mode; +} + + + +/** + This will return a (const int *) pointer to the active indices. IFF + (mode == INACTIVE || mode == ALL_ACTIVE) it will instead just + return NULL. In that case it is the responsability of the calling + scope to not dereference the NULL pointer. +*/ + +const int * active_list_get_active(const active_list_type * active_list) { + if (active_list->mode == PARTLY_ACTIVE) + return int_vector_get_const_ptr( active_list->index_list ); + else + return NULL; +} + + +bool active_list_iget( const active_list_type * active_list , int index ) { + if (active_list->mode == ALL_ACTIVE) + return true; + else if (active_list->mode == INACTIVE) + return false; + else + return int_vector_iget( active_list->index_list , index ); +} + +void active_list_summary_fprintf( const active_list_type * active_list , const char *dataset_key, const char *key , FILE * stream) { + int number_of_active = int_vector_size( active_list->index_list ); + if (active_list->mode == ALL_ACTIVE){ + fprintf(stream , "NUMBER OF ACTIVE:%d,STATUS:%s,", number_of_active, "ALL_ACTIVE"); + } + else if (active_list->mode == PARTLY_ACTIVE){ + fprintf(stream , "NUMBER OF ACTIVE:%d,STATUS:%s,", number_of_active, "PARTLY_ACTIVE"); + } + else + fprintf(stream , "NUMBER OF ACTIVE:%d,STATUS:%s,", number_of_active, "INACTIVE"); +} + + + +bool active_list_equal( const active_list_type * active_list1 , const active_list_type * active_list2) { + if (active_list1 == active_list2) + return true; + else { + if (active_list1->mode != active_list2->mode) + return false; + else { + if (active_list1->mode == PARTLY_ACTIVE) + return int_vector_equal( active_list1->index_list , active_list2->index_list); + else + return true; + } + } +} diff --git a/libres/lib/enkf/active_node.cpp b/libres/lib/enkf/active_node.cpp new file mode 100644 index 00000000000..7b76afd040c --- /dev/null +++ b/libres/lib/enkf/active_node.cpp @@ -0,0 +1,63 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'active_node.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 + +/** + This file implements the two most basic objects used in the mapping + of active/inactive observations and variables. One of these nodes + contains the information necessary to activate/deactivate one + variable/observation. +*/ + + +/** + This struct implements the holding information for the + activation/deactivation of one variable. +*/ + +struct active_var_struct { + const enkf_config_node_type * config_node; /* The enkf_config_node instance this is all about - pointer to *shared* resource. */ + active_mode_type active_mode; + void * active_config; /* An object (type depending on datatype of config_node) used to hold info abourt partly active variable. + Owned by this object. If active_mode == all_active or active_mode == inactive, this can be NULL. */ + active_config_destructor_ftype * free_active_config; /* Destructor for the active_config object, can be NULL if that object is NULL. */ +}; + + + +/** + Similar to active_var_struct, but for observations. +*/ +struct active_obs_struct { + const obs_vector_type * obs_vector; /* The obs_node instance this is all about - pointer to *shared* resource. */ + active_mode_type active_mode; + void * active_config; /* An object (type depending on datatype of obs_node) used to hold info abourt partly active variable. + Owned by this object. If active_mode == all_active or active_mode == inactive, this can be NULL. */ + active_config_destructor_ftype * free_active_config; /* Destructor for the active_config object, can be NULL if that object is NULL. */ +}; + + + + diff --git a/libres/lib/enkf/analysis_config.cpp b/libres/lib/enkf/analysis_config.cpp new file mode 100644 index 00000000000..e784da846c2 --- /dev/null +++ b/libres/lib/enkf/analysis_config.cpp @@ -0,0 +1,719 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'analysis_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 +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include + + +#define UPDATE_ENKF_ALPHA_KEY "ENKF_ALPHA" +#define UPDATE_STD_CUTOFF_KEY "STD_CUTOFF" + + +#define ANALYSIS_CONFIG_TYPE_ID 64431306 + +struct analysis_config_struct { + UTIL_TYPE_ID_DECLARATION; + std::unordered_map analysis_modules; + analysis_module_type * analysis_module; + char * log_path; /* Points to directory with update logs. */ + + bool merge_observations; /* When observing from time1 to time2 - should ALL observations in between be used? */ + bool rerun; /* Should we rerun the simulator when the parameters have been updated? */ + int rerun_start; /* When rerunning - from where should we start? */ + + config_settings_type * update_settings; + + char * PC_filename; + char * PC_path; + bool store_PC; + bool single_node_update; /* When creating the default ALL_ACTIVE local configuration. */ + analysis_iter_config_type * iter_config; + int min_realisations; + bool stop_long_running; + bool std_scale_correlated_obs; + int max_runtime; + double global_std_scaling; +}; + + + + +UTIL_IS_INSTANCE_FUNCTION( analysis_config , ANALYSIS_CONFIG_TYPE_ID ) + +/*****************************************************************/ +/* + +Interacting with modules +------------------------ + +The modules which are included in the build must be installed/loaded with a +hard-coded call to analysis_config_load_internal_module(). External modules +are loaded with the config statement: + + ANALYSIS_LOAD ModuleName libfile + +Where 'ModuleName is the name you want to use to refer to the module, and +libfile is the name of the library file which implements the analysis +module[1]. + +It is possible to create a copy of an analysis module under a different +name, this can be convenient when trying out the same algorithm with +different parameter settings. I.e. based on the built in module STD_ENKF we +can create two copies with high and low truncation respectively: + + ANALYSIS_COPY STD_ENKF ENKF_HIGH_TRUNCATION + ANALYSIS_COPY STD_ENKF ENKF_LOW_TRUNCATION + +The copy operation does not differentiate between external and internal +modules. When a module has been loaded you can set internal parameters for +the module with the config command: + + ANALYSIS_SET_VAR ModuleName VariableName Value + +The module will be called with a function for setting variables which gets +the VariableName and value parameters as input; if the module recognizes +VariableName and Value is of the right type the module should set the +internal variable accordingly. If the module does not recognize the +variable name a warning will be printed on stderr, but no further action. + +The actual analysis module to use is selected with the statement: + +ANALYSIS_SELECT ModuleName + +[1] The libfile argument should include the '.so' extension, and can + optionally contain a path component. The libfile will be passed directly to + the dlopen() library call, this implies that normal runtime linking + conventions apply - i.e. you have three options: + + 1. The library name is given with a full path. + 2. The library is in a standard location for shared libraries. + 3. The library is in one of the directories mentioned in the + LD_LIBRARY_PATH environment variable. + +*/ + + +/*****************************************************************/ + +bool analysis_config_have_enough_realisations( const analysis_config_type * config , int realisations, int ensemble_size) { + if (config->min_realisations > 0) { + /* A value > 0 has been set in the config; compare with this value. */ + return realisations >= std::min(config->min_realisations, ensemble_size); + } + else { + return realisations >= ensemble_size; + } +} + +void analysis_config_set_stop_long_running( analysis_config_type * config, bool stop_long_running ) { + config->stop_long_running = stop_long_running; +} + +bool analysis_config_get_stop_long_running( const analysis_config_type * config) { + return config->stop_long_running; +} + +bool analysis_config_get_std_scale_correlated_obs( const analysis_config_type * config) { + return config->std_scale_correlated_obs; +} + +void analysis_config_set_std_scale_correlated_obs( analysis_config_type * config, bool std_scale_correlated_obs) { + config->std_scale_correlated_obs = std_scale_correlated_obs; +} + +double analysis_config_get_global_std_scaling(const analysis_config_type * config) { + return config->global_std_scaling; +} + +void analysis_config_set_global_std_scaling(analysis_config_type * config, double global_std_scaling) { + config->global_std_scaling = global_std_scaling; +} + +int analysis_config_get_max_runtime( const analysis_config_type * config ) { + return config->max_runtime; +} + +void analysis_config_set_max_runtime( analysis_config_type * config, int max_runtime ) { + config->max_runtime = max_runtime; +} + +static void analysis_config_set_min_realisations( analysis_config_type * config , int min_realisations) { + config->min_realisations = min_realisations; +} + +int analysis_config_get_min_realisations( const analysis_config_type * config) { + return config->min_realisations; +} + +stringlist_type * analysis_config_alloc_module_names( const analysis_config_type * config ) { + stringlist_type * s = stringlist_alloc_new(); + + for (const auto& analysis_pair : config->analysis_modules) + stringlist_append_copy(s, analysis_pair.first.c_str()); + + return s; +} + +void analysis_config_set_store_PC( analysis_config_type * config , bool store_PC) { + config->store_PC = store_PC; +} + +bool analysis_config_get_store_PC( const analysis_config_type * config ) { + return config->store_PC; +} + +void analysis_config_set_PC_filename( analysis_config_type * config , const char * filename ) { + config->PC_filename = util_realloc_string_copy( config->PC_filename , filename ); +} + +const char * analysis_config_get_PC_filename( const analysis_config_type * config ) { + return config->PC_filename; +} + + +void analysis_config_set_PC_path( analysis_config_type * config , const char * path ) { + config->PC_path = util_realloc_string_copy( config->PC_path , path ); +} + +const char * analysis_config_get_PC_path( const analysis_config_type * config ) { + return config->PC_path; +} + + +void analysis_config_set_alpha( analysis_config_type * config , double alpha) { + config_settings_set_double_value(config->update_settings, UPDATE_ENKF_ALPHA_KEY, alpha ); +} + + +double analysis_config_get_alpha(const analysis_config_type * config) { + return config_settings_get_double_value(config->update_settings, UPDATE_ENKF_ALPHA_KEY); +} + +void analysis_config_set_std_cutoff( analysis_config_type * config , double std_cutoff ) { + config_settings_set_double_value(config->update_settings, UPDATE_STD_CUTOFF_KEY, std_cutoff ); +} + +double analysis_config_get_std_cutoff(const analysis_config_type * config) { + return config_settings_get_double_value(config->update_settings, UPDATE_STD_CUTOFF_KEY); +} + + +void analysis_config_set_log_path(analysis_config_type * config , const char * log_path ) { + config->log_path = util_realloc_string_copy(config->log_path , log_path); +} + + +/** + Will in addition create the path. +*/ +const char * analysis_config_get_log_path( const analysis_config_type * config ) { + util_make_path( config->log_path ); + return config->log_path; +} + + + +void analysis_config_set_rerun_start( analysis_config_type * config , int rerun_start ) { + config->rerun_start = rerun_start; +} + +void analysis_config_set_rerun(analysis_config_type * config , bool rerun) { + config->rerun = rerun; +} + +bool analysis_config_get_rerun(const analysis_config_type * config) { + return config->rerun; +} + +void analysis_config_set_single_node_update(analysis_config_type * config , bool single_node_update) { + config->single_node_update = single_node_update; +} + +bool analysis_config_get_single_node_update(const analysis_config_type * config) { + return config->single_node_update; +} + + +int analysis_config_get_rerun_start(const analysis_config_type * config) { + return config->rerun_start; +} + + +void analysis_config_set_merge_observations( analysis_config_type * config , bool merge_observations) { + config->merge_observations = merge_observations; +} + + + +/*****************************************************************/ + +void analysis_config_load_internal_module( analysis_config_type * config , + const char * symbol_table ) { + analysis_module_type * module = analysis_module_alloc_internal(symbol_table); + if (module) + config->analysis_modules[ analysis_module_get_name( module )] = module; + else + fprintf(stderr,"** Warning: failed to load module %s from %s.\n", analysis_module_get_name( module ) , symbol_table); +} + + +void analysis_config_load_all_external_modules_from_config ( analysis_config_type * analysis, const config_content_type * config) { + + if (config_content_has_item( config, ANALYSIS_LOAD_KEY)) { + const config_content_item_type * load_item = config_content_get_item( config , ANALYSIS_LOAD_KEY ); + for (int i=0; i < config_content_item_get_size( load_item ); i++) { + const config_content_node_type * load_node = config_content_item_iget_node( load_item , i ); + const char * user_name = config_content_node_iget( load_node , 0 ); + const char * lib_name = config_content_node_iget( load_node , 1 ); + + analysis_config_load_external_module( analysis , lib_name , user_name); + } + } +} + + +bool analysis_config_load_external_module( analysis_config_type * config , + const char * lib_name, + const char * user_name) { + analysis_module_type * module = analysis_module_alloc_external(lib_name); + if (module != NULL) { + if (user_name) + analysis_module_set_name(module, user_name); + config->analysis_modules[ analysis_module_get_name(module) ] = module; + return true; + } else { + fprintf(stderr,"** Warning: failed to load module from %s.\n",lib_name); + return false; + } +} + + +void analysis_config_add_module_copy( analysis_config_type * config , + const char * src_name , + const char * target_name) { + const analysis_module_type * src_module = analysis_config_get_module( config , src_name ); + analysis_module_type * target_module; + + if (analysis_module_internal( src_module )) { + const char * symbol_table = analysis_module_get_table_name( src_module ); + target_module = analysis_module_alloc_internal(symbol_table); + } else { + const char * lib_name = analysis_module_get_lib_name( src_module ); + target_module = analysis_module_alloc_external(lib_name); + } + + config->analysis_modules[ target_name ] = target_module; + analysis_module_set_name( target_module , target_name ); +} + + +analysis_module_type * analysis_config_get_module(const analysis_config_type * config , const char * module_name ) { + return config->analysis_modules.at(module_name); +} + +bool analysis_config_has_module(const analysis_config_type * config , const char * module_name) { + return (config->analysis_modules.count(module_name) > 0); +} + +bool analysis_config_get_module_option( const analysis_config_type * config , long flag) { + if (config->analysis_module) + return analysis_module_check_option(config->analysis_module , flag); + else + return false; +} + + +bool analysis_config_select_module( analysis_config_type * config , const char * module_name ) { + if (analysis_config_has_module( config , module_name )) { + analysis_module_type * module = analysis_config_get_module( config , module_name ); + + if (analysis_module_check_option( module , ANALYSIS_ITERABLE)) { + if (analysis_config_get_single_node_update( config )) { + fprintf(stderr," ** Warning: the module:%s requires the setting \"SINGLE_NODE_UPDATE FALSE\" in the config file.\n" , module_name); + fprintf(stderr," ** the module has NOT been selected. \n"); + return false; + } + } + + config->analysis_module = module; + return true; + } else { + if (config->analysis_module == NULL) + util_abort("%s: sorry module:%s does not exist - and no module currently selected\n",__func__ , module_name); + else + fprintf(stderr , "** Warning: analysis module:%s does not exist - current selection unchanged:%s\n", + module_name , + analysis_module_get_name( config->analysis_module )); + return false; + } +} + + +analysis_module_type * analysis_config_get_active_module(const analysis_config_type * config ) { + return config->analysis_module; +} + + +const char * analysis_config_get_active_module_name( const analysis_config_type * config ) { + if (config->analysis_module) + return analysis_module_get_name( config->analysis_module ); + else + return NULL; +} + + + +/*****************************************************************/ + + +void analysis_config_load_internal_modules( analysis_config_type * config ) { + analysis_config_load_internal_module( config , "STD_ENKF"); + analysis_config_load_internal_module( config , "NULL_ENKF"); + analysis_config_load_internal_module( config , "SQRT_ENKF"); + analysis_config_load_internal_module( config , "CV_ENKF"); + analysis_config_load_internal_module( config , "BOOTSTRAP_ENKF"); + analysis_config_load_internal_module( config , "FWD_STEP_ENKF"); + analysis_config_select_module( config , DEFAULT_ANALYSIS_MODULE); +} + +/** + The analysis_config object is instantiated with the default values + for enkf_defaults.h +*/ + +void analysis_config_init( analysis_config_type * analysis , const config_content_type * config ) { + config_settings_apply(analysis->update_settings , config); + + if (config_content_has_item( config , UPDATE_LOG_PATH_KEY )) + analysis_config_set_log_path( analysis , config_content_get_value( config , UPDATE_LOG_PATH_KEY )); + + if (config_content_has_item( config , STD_CUTOFF_KEY )) + analysis_config_set_std_cutoff( analysis , config_content_get_value_as_double( config , STD_CUTOFF_KEY )); + + if (config_content_has_item( config , ENKF_ALPHA_KEY )) + analysis_config_set_alpha( analysis , config_content_get_value_as_double( config , ENKF_ALPHA_KEY )); + + if (config_content_has_item( config , ENKF_MERGE_OBSERVATIONS_KEY )) + analysis_config_set_merge_observations( analysis , config_content_get_value_as_bool( config , ENKF_MERGE_OBSERVATIONS_KEY )); + + if (config_content_has_item( config , ENKF_RERUN_KEY )) + analysis_config_set_rerun( analysis , config_content_get_value_as_bool( config , ENKF_RERUN_KEY )); + + if (config_content_has_item( config , SINGLE_NODE_UPDATE_KEY )) + analysis_config_set_single_node_update( analysis , config_content_get_value_as_bool( config , SINGLE_NODE_UPDATE_KEY )); + + if (config_content_has_item( config , STD_SCALE_CORRELATED_OBS_KEY )) + analysis_config_set_std_scale_correlated_obs( analysis , config_content_get_value_as_bool( config , STD_SCALE_CORRELATED_OBS_KEY )); + + if (config_content_has_item( config , RERUN_START_KEY )) + analysis_config_set_rerun_start( analysis , config_content_get_value_as_int( config , RERUN_START_KEY )); + + if (config_content_has_item( config , MIN_REALIZATIONS_KEY )) { + + config_content_node_type * config_content = config_content_get_value_node(config , MIN_REALIZATIONS_KEY); + char * min_realizations_string = config_content_node_alloc_joined_string(config_content, " "); + + int num_realizations = config_content_get_value_as_int(config, NUM_REALIZATIONS_KEY); + int min_realizations = DEFAULT_ANALYSIS_MIN_REALISATIONS; + double percent = 0.0; + if (util_sscanf_percent(min_realizations_string, &percent)) { + min_realizations = ceil(num_realizations * percent/100); + } else { + bool min_realizations_int_exists = util_sscanf_int(min_realizations_string, &min_realizations); + if (!min_realizations_int_exists) + fprintf(stderr, "Method %s: failed to read integer value for MIN_REALIZATIONS_KEY\n", __func__); + } + + if (min_realizations > num_realizations) + min_realizations = num_realizations; + + analysis_config_set_min_realisations(analysis, min_realizations); + free(min_realizations_string); + } + + if (config_content_has_item( config , STOP_LONG_RUNNING_KEY )) + analysis_config_set_stop_long_running( analysis , config_content_get_value_as_bool( config , STOP_LONG_RUNNING_KEY )); + + if (config_content_has_item( config, MAX_RUNTIME_KEY)) { + analysis_config_set_max_runtime( analysis, config_content_get_value_as_int( config, MAX_RUNTIME_KEY )); + } + + + /* Loading external modules */ + analysis_config_load_all_external_modules_from_config(analysis, config); + + + /* Reload/copy modules. */ + { + if (config_content_has_item( config , ANALYSIS_COPY_KEY )) { + const config_content_item_type * copy_item = config_content_get_item( config , ANALYSIS_COPY_KEY ); + for (int i=0; i < config_content_item_get_size( copy_item ); i++) { + const config_content_node_type * copy_node = config_content_item_iget_node( copy_item , i ); + const char * src_name = config_content_node_iget( copy_node , 0 ); + const char * target_name = config_content_node_iget( copy_node , 1 ); + + analysis_config_add_module_copy( analysis , src_name , target_name); + } + } + } + + + /* Setting variables for analysis modules */ + { + if (config_content_has_item( config , ANALYSIS_SET_VAR_KEY )) { + const config_content_item_type * assign_item = config_content_get_item( config , ANALYSIS_SET_VAR_KEY ); + for (int i=0; i < config_content_item_get_size( assign_item ); i++) { + const config_content_node_type * assign_node = config_content_item_iget_node( assign_item , i ); + + const char * module_name = config_content_node_iget( assign_node , 0 ); + const char * var_name = config_content_node_iget( assign_node , 1 ); + analysis_module_type * module = analysis_config_get_module( analysis , module_name ); + { + char * value = NULL; + + for (int j=2; j < config_content_node_get_size( assign_node ); j++) { + const char * config_value = config_content_node_iget( assign_node , j ); + if (value == NULL) + value = util_alloc_string_copy( config_value ); + else { + value = util_strcat_realloc( value , " " ); + value = util_strcat_realloc( value , config_value ); + } + } + + analysis_module_set_var( module , var_name , value ); + free( value ); + } + } + } + } + + if (config_content_has_item( config, ANALYSIS_SELECT_KEY )) + analysis_config_select_module( analysis , config_content_get_value( config , ANALYSIS_SELECT_KEY )); + + analysis_iter_config_init( analysis->iter_config , config ); +} + + + +bool analysis_config_get_merge_observations(const analysis_config_type * config) { + return config->merge_observations; +} + + +analysis_iter_config_type * analysis_config_get_iter_config( const analysis_config_type * config ) { + return config->iter_config; +} + + +void analysis_config_free(analysis_config_type * config) { + analysis_iter_config_free( config->iter_config ); + for (auto& module_pair : config->analysis_modules) + analysis_module_free( module_pair.second ); + + config_settings_free( config->update_settings ); + free( config->log_path ); + free( config->PC_filename ); + free( config->PC_path ); + + delete config; +} + +analysis_config_type * analysis_config_alloc_full( + double alpha, + bool merge_observations, + bool rerun, + int rerun_start, + const char * log_path, + double std_cutoff, + bool stop_long_running, + bool single_node_update, + bool std_scale_correlated_obs, + double global_std_scaling, + int max_runtime, + int min_realisations +) { + analysis_config_type * config = new analysis_config_type(); + UTIL_TYPE_ID_INIT( config , ANALYSIS_CONFIG_TYPE_ID ); + + config->update_settings = config_settings_alloc( UPDATE_SETTING_KEY ); + config_settings_add_double_setting(config->update_settings, UPDATE_ENKF_ALPHA_KEY , alpha ); + config_settings_add_double_setting(config->update_settings, UPDATE_STD_CUTOFF_KEY, std_cutoff ); + + config->merge_observations = merge_observations; + config->rerun = rerun; + config->rerun_start = rerun_start; + config->log_path = util_realloc_string_copy(config->log_path , log_path); + config->PC_filename = util_realloc_string_copy( config->PC_filename , DEFAULT_PC_FILENAME ); + config->PC_path = util_realloc_string_copy( config->PC_path , DEFAULT_PC_PATH ); + config->single_node_update = single_node_update; + config->store_PC = DEFAULT_STORE_PC; + config->min_realisations = min_realisations; + config->stop_long_running = stop_long_running; + config->max_runtime = max_runtime; + + config->analysis_module = NULL; + config->iter_config = analysis_iter_config_alloc(); + config->std_scale_correlated_obs = std_scale_correlated_obs; + config->global_std_scaling = global_std_scaling; + + analysis_config_load_internal_modules(config); + + return config; +} + + +analysis_config_type * analysis_config_alloc_default(void) { + analysis_config_type * config = new analysis_config_type(); + UTIL_TYPE_ID_INIT( config , ANALYSIS_CONFIG_TYPE_ID ); + + config->log_path = NULL; + config->PC_filename = NULL; + config->PC_path = NULL; + config->update_settings = config_settings_alloc( UPDATE_SETTING_KEY ); + config_settings_add_double_setting(config->update_settings, UPDATE_ENKF_ALPHA_KEY , DEFAULT_ENKF_ALPHA); + config_settings_add_double_setting(config->update_settings, UPDATE_STD_CUTOFF_KEY, DEFAULT_ENKF_STD_CUTOFF ); + + analysis_config_set_merge_observations( config , DEFAULT_MERGE_OBSERVATIONS ); + analysis_config_set_rerun( config , DEFAULT_RERUN ); + analysis_config_set_rerun_start( config , DEFAULT_RERUN_START ); + analysis_config_set_single_node_update( config , DEFAULT_SINGLE_NODE_UPDATE ); + analysis_config_set_log_path( config , DEFAULT_UPDATE_LOG_PATH); + + analysis_config_set_store_PC( config , DEFAULT_STORE_PC ); + analysis_config_set_PC_filename( config , DEFAULT_PC_FILENAME ); + analysis_config_set_PC_path( config , DEFAULT_PC_PATH ); + analysis_config_set_min_realisations( config , DEFAULT_ANALYSIS_MIN_REALISATIONS ); + analysis_config_set_stop_long_running( config , DEFAULT_ANALYSIS_STOP_LONG_RUNNING ); + analysis_config_set_max_runtime( config , DEFAULT_MAX_RUNTIME ); + + config->analysis_module = NULL; + config->iter_config = analysis_iter_config_alloc(); + config->std_scale_correlated_obs = false; + config->global_std_scaling = 1.0; + return config; +} + +static analysis_config_type * analysis_config_alloc_load_site_config() { + analysis_config_type * analysis_config = analysis_config_alloc_default(); + + config_parser_type * config = config_alloc(); + config_content_type * content = site_config_alloc_content(config); + + analysis_config_load_all_external_modules_from_config(analysis_config, content); + + config_free(config); + config_content_free(content); + + return analysis_config; +} + +analysis_config_type * analysis_config_alloc_load(const char * user_config_file) { + config_parser_type * config_parser = config_alloc(); + config_content_type * config_content = NULL; + if(user_config_file) + config_content = model_config_alloc_content(user_config_file, config_parser); + + analysis_config_type * analysis_config = analysis_config_alloc(config_content); + + config_content_free(config_content); + config_free(config_parser); + + return analysis_config; +} + +analysis_config_type * analysis_config_alloc(const config_content_type * config_content) { + analysis_config_type * analysis_config = analysis_config_alloc_load_site_config(); + + if(config_content) { + analysis_config_load_internal_modules(analysis_config); + analysis_config_init(analysis_config, config_content); + } + + return analysis_config; + +} +/*****************************************************************/ +/* + Keywords for the analysis - all optional. The analysis_config object + is instantiated with defaults from enkf_defaults.h +*/ + +void analysis_config_add_config_items( config_parser_type * config ) { + config_schema_item_type * item; + + config_add_key_value( config , ENKF_ALPHA_KEY , false , CONFIG_FLOAT); + config_add_key_value( config , STD_CUTOFF_KEY , false , CONFIG_FLOAT); + config_settings_init_parser__( UPDATE_SETTING_KEY , config , false ); + + config_add_key_value( config , ENKF_MERGE_OBSERVATIONS_KEY , false , CONFIG_BOOL); + config_add_key_value( config , SINGLE_NODE_UPDATE_KEY , false , CONFIG_BOOL); + config_add_key_value( config , ENKF_CROSS_VALIDATION_KEY , false , CONFIG_BOOL); + + config_add_key_value( config , ENKF_RERUN_KEY , false , CONFIG_BOOL); + config_add_key_value( config , RERUN_START_KEY , false , CONFIG_INT); + config_add_key_value( config , UPDATE_LOG_PATH_KEY , false , CONFIG_STRING); + config_add_key_value( config , MIN_REALIZATIONS_KEY , false , CONFIG_STRING ); + config_add_key_value( config , MAX_RUNTIME_KEY , false , CONFIG_INT ); + + config_add_key_value(config, STD_SCALE_CORRELATED_OBS_KEY, false, CONFIG_BOOL); + config_parser_deprecate( + config, + STD_SCALE_CORRELATED_OBS_KEY, + "STD_SCALE_CORRELATED_OBS is deprecated. " + "Use the 'auto_scale' option in the MISFIT_PREPROCESSOR workflow instead." + ); + + item = config_add_key_value( config , STOP_LONG_RUNNING_KEY, false, CONFIG_BOOL ); + stringlist_type * child_list = stringlist_alloc_new(); + stringlist_append_copy(child_list, MIN_REALIZATIONS_KEY); + config_schema_item_set_required_children_on_value(item , "TRUE" , child_list); + stringlist_free(child_list); + + config_add_key_value( config , ANALYSIS_SELECT_KEY , false , CONFIG_STRING); + + item = config_add_schema_item( config , ANALYSIS_LOAD_KEY , false ); + config_schema_item_set_argc_minmax( item , 2 , 2); + + item = config_add_schema_item( config , ANALYSIS_COPY_KEY , false ); + config_schema_item_set_argc_minmax( item , 2 , 2); + + + item = config_add_schema_item( config , ANALYSIS_SET_VAR_KEY , false ); + config_schema_item_set_argc_minmax( item , 3 , CONFIG_DEFAULT_ARG_MAX); + analysis_iter_config_add_config_items( config ); +} diff --git a/libres/lib/enkf/analysis_iter_config.cpp b/libres/lib/enkf/analysis_iter_config.cpp new file mode 100644 index 00000000000..bc3684ec94a --- /dev/null +++ b/libres/lib/enkf/analysis_iter_config.cpp @@ -0,0 +1,144 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'analysis_iter_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 +#include +#include + + +struct analysis_iter_config_struct { + char * case_fmt; + stringlist_type * storage; + int num_iterations; + int num_iter_tries; + bool case_set; + bool num_iterations_set; +}; + + +void analysis_iter_config_set_num_iterations( analysis_iter_config_type * config , int num_iterations) { + config->num_iterations = num_iterations; + config->num_iterations_set = true; +} + +int analysis_iter_config_get_num_iterations( const analysis_iter_config_type * config ) { + return config->num_iterations; +} + +bool analysis_iter_config_num_iterations_set( const analysis_iter_config_type * config ) { + return config->num_iterations_set; +} + + +void analysis_iter_config_set_num_retries_per_iteration( analysis_iter_config_type * config , int num_iter_tries) { + config->num_iter_tries = num_iter_tries; +} + +int analysis_iter_config_get_num_retries_per_iteration( const analysis_iter_config_type * config ) { + return config->num_iter_tries; +} + + +void analysis_iter_config_set_case_fmt( analysis_iter_config_type * config , const char * case_fmt) { + config->case_fmt = util_realloc_string_copy( config->case_fmt , case_fmt ); + config->case_set = true; +} + + +bool analysis_iter_config_case_fmt_set( const analysis_iter_config_type * config ) { + return config->case_set; +} + + +char * analysis_iter_config_get_case_fmt( analysis_iter_config_type * config) { + return config->case_fmt; +} + + +analysis_iter_config_type * analysis_iter_config_alloc() { + analysis_iter_config_type * config = (analysis_iter_config_type *)util_malloc( sizeof * config ); + config->case_fmt = NULL; + analysis_iter_config_set_case_fmt( config, DEFAULT_ANALYSIS_ITER_CASE); + config->storage = stringlist_alloc_new(); + analysis_iter_config_set_num_iterations( config , DEFAULT_ANALYSIS_NUM_ITERATIONS ); + analysis_iter_config_set_num_retries_per_iteration(config, DEFAULT_ITER_RETRY_COUNT); + + config->num_iterations_set = false; + config->case_set = false; + return config; +} + + +analysis_iter_config_type * analysis_iter_config_alloc_full(const char * case_fmt, int num_iterations, int num_iter_tries) { + analysis_iter_config_type * config = (analysis_iter_config_type *)util_malloc( sizeof * config ); + config->case_fmt = NULL; + analysis_iter_config_set_case_fmt( config, case_fmt); + config->storage = stringlist_alloc_new(); + analysis_iter_config_set_num_iterations( config , num_iterations ); + analysis_iter_config_set_num_retries_per_iteration(config, num_iter_tries); + + config->num_iterations_set = false; + config->case_set = false; + return config; +} + +void analysis_iter_config_free( analysis_iter_config_type * config ) { + free( config->case_fmt ); + stringlist_free( config->storage ); + free( config ); +} + + +const char * analysis_iter_config_iget_case( analysis_iter_config_type * config , int iter) { + if (config->case_fmt != NULL) { + char * fs_case = util_alloc_sprintf( config->case_fmt , iter ); + stringlist_append_copy( config->storage , fs_case); + free(fs_case); + return stringlist_get_last( config->storage ); + } else + return NULL; +} + + +void analysis_iter_config_add_config_items( config_parser_type * config ) { + config_add_key_value( config , ITER_CASE_KEY , false , CONFIG_STRING); + config_add_key_value( config , ITER_COUNT_KEY , false , CONFIG_INT); + config_add_key_value( config , ITER_RETRY_COUNT_KEY , false , CONFIG_INT); +} + + +void analysis_iter_config_init(analysis_iter_config_type * iter_config , const config_content_type * config) { + if (config_content_has_item( config , ITER_CASE_KEY )) + analysis_iter_config_set_case_fmt( iter_config , config_content_get_value( config , ITER_CASE_KEY )); + + if (config_content_has_item( config , ITER_COUNT_KEY )) + analysis_iter_config_set_num_iterations( iter_config , config_content_get_value_as_int( config , ITER_COUNT_KEY )); + + if (config_content_has_item( config , ITER_RETRY_COUNT_KEY )) + analysis_iter_config_set_num_retries_per_iteration( iter_config , config_content_get_value_as_int( config , ITER_RETRY_COUNT_KEY )); +} + + diff --git a/libres/lib/enkf/block_fs_driver.cpp b/libres/lib/enkf/block_fs_driver.cpp new file mode 100644 index 00000000000..6a4ec773ae5 --- /dev/null +++ b/libres/lib/enkf/block_fs_driver.cpp @@ -0,0 +1,506 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'block_fs_driver.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 + + +typedef struct bfs_struct bfs_type; +typedef struct bfs_config_struct bfs_config_type; + +struct bfs_config_struct { + int fsync_interval; + double fragmentation_limit; + bool read_only; + bool preload; + int block_size; + int max_cache_size; + bool bfs_lock; +}; + + +#define BFS_TYPE_ID 5510643 + +struct bfs_struct { + UTIL_TYPE_ID_DECLARATION; + /*-----------------------------------------------------------------*/ + /* New variables */ + block_fs_type * block_fs; + char * mountfile; // The full path to the file mounted by the block_fs layer - including extension. + + const bfs_config_type * config; +}; + + + +struct block_fs_driver_struct { + FS_DRIVER_FIELDS; + int __id; + int num_fs; + bfs_config_type * config; + + // New variables + bfs_type ** fs_list; +}; + +/*****************************************************************/ + +bfs_config_type * bfs_config_alloc( fs_driver_enum driver_type , bool read_only, bool bfs_lock) { + const int PARAMETER_blocksize = 64; + const int DYNAMIC_blocksize = 64; + const int DEFAULT_blocksize = 64; + + const bool PARAMETER_preload = false; + const bool DYNAMIC_preload = true; + const bool DEFAULT_preload = false; + + const int max_cache_size = 512; + const int fsync_interval = 10; /* An fsync() call is issued for every 10'th write. */ + const double fragmentation_limit = 1.0; /* 1.0 => NO defrag is run. */ + + { + bfs_config_type * config = (bfs_config_type *)util_malloc( sizeof * config ); + config->max_cache_size = max_cache_size; + config->fsync_interval = fsync_interval; + config->fragmentation_limit = fragmentation_limit; + config->read_only = read_only; + config->bfs_lock = bfs_lock; + + switch (driver_type) { + case( DRIVER_PARAMETER ): + config->block_size = PARAMETER_blocksize; + config->preload = PARAMETER_preload; + break; + case(DRIVER_DYNAMIC_FORECAST): + config->block_size = DYNAMIC_blocksize; + config->preload = DYNAMIC_preload; + break; + default: + config->block_size = DEFAULT_blocksize; + config->preload = DEFAULT_preload; + } + return config; + } +} + +void bfs_config_free( bfs_config_type * config ) { + free( config ); +} + +/*****************************************************************/ + +static UTIL_SAFE_CAST_FUNCTION(bfs , BFS_TYPE_ID); + +static void bfs_close( bfs_type * bfs ) { + if (bfs->block_fs != NULL) + block_fs_close( bfs->block_fs , false); + free( bfs->mountfile ); + free( bfs ); +} + + +static void * bfs_close__( void * arg ) { + bfs_type * bfs = ( bfs_type * ) arg; + bfs_close( bfs ); + + return NULL; +} + +static bfs_type * bfs_alloc( const bfs_config_type * config ) { + bfs_type * fs = (bfs_type *)util_malloc( sizeof * fs ); + UTIL_TYPE_ID_INIT( fs , BFS_TYPE_ID ); + fs->config = config; + + // New init + fs->mountfile = NULL; + + return fs; +} + + +static bfs_type * bfs_alloc_new( const bfs_config_type * config , char * mountfile) { + bfs_type * bfs = bfs_alloc( config ); + + bfs->mountfile = mountfile; // Warning pattern break: This is allocated in external scope; and the bfs takes ownership. + return bfs; +} + + +static void bfs_mount( bfs_type * bfs) { + const bfs_config_type * config = bfs->config; + bfs->block_fs = block_fs_mount( bfs->mountfile , + config->block_size , + config->max_cache_size , + config->fragmentation_limit , + config->fsync_interval , + config->preload , + config->read_only, + config->bfs_lock); +} + + +static void * bfs_mount__( void * arg ) { + bfs_type * bfs = bfs_safe_cast( arg ); + bfs_mount( bfs ); + return NULL; +} + + + + +static void bfs_fsync( bfs_type * bfs ) { + block_fs_fsync( bfs->block_fs ); +} + + + +/*****************************************************************/ + + +static void block_fs_driver_assert_cast(block_fs_driver_type * block_fs_driver) { + if (block_fs_driver->__id != BLOCK_FS_DRIVER_ID) + util_abort("%s: internal error - cast failed - aborting \n",__func__); +} + + +static block_fs_driver_type * block_fs_driver_safe_cast( void * __driver) { + block_fs_driver_type * driver = (block_fs_driver_type *) __driver; + block_fs_driver_assert_cast(driver); + return driver; +} + +static char * block_fs_driver_alloc_node_key( const block_fs_driver_type * driver , const char * node_key , int report_step , int iens) { + char * key = util_alloc_sprintf("%s.%d.%d" , node_key , report_step , iens); + return key; +} + + +static char * block_fs_driver_alloc_vector_key( const block_fs_driver_type * driver , const char * node_key , int iens) { + char * key = util_alloc_sprintf("%s.%d" , node_key , iens); + return key; +} + +/** + This function will take an input string, and try to to parse it as + string.int.int, where string is the normal enkf key, and the two + integers are report_step and ensemble number respectively. The + storage for the enkf_key is allocated here in this function, and + must be freed by the calling scope. + + If the parsing fails the function will return false, and *config_key + will be set to NULL; in this case the report_step and iens poinyers + will not be touched. +*/ + +bool block_fs_sscanf_key(const char * key , char ** config_key , int * __report_step , int * __iens) { + char ** tmp; + int num_items; + + *config_key = NULL; + util_split_string(key , "." , &num_items , &tmp); /* The key can contain additional '.' - can not use sscanf(). */ + if (num_items >= 3) { + int report_step , iens; + if (util_sscanf_int(tmp[num_items - 2] , &report_step) && util_sscanf_int(tmp[num_items - 1] , &iens)) { + /* OK - all is hunkadory */ + *__report_step = report_step; + *__iens = iens; + *config_key = util_alloc_joined_string((const char **) tmp , num_items - 2 , "."); /* This must bee freed by the calling scope */ + util_free_stringlist( tmp , num_items ); + return true; + } else + /* Failed to parse the two last items as integers. */ + return false; + } else + /* Did not have at least three items. */ + return false; +} + + + + +static bfs_type * block_fs_driver_get_fs( block_fs_driver_type * driver , int iens ) { + int phase = (iens % driver->num_fs); + + return driver->fs_list[phase]; +} + + + +static void block_fs_driver_load_node(void * _driver , const char * node_key , int report_step , int iens , buffer_type * buffer) { + block_fs_driver_type * driver = block_fs_driver_safe_cast( _driver ); + { + char * key = block_fs_driver_alloc_node_key( driver , node_key , report_step , iens ); + bfs_type * bfs = block_fs_driver_get_fs( driver , iens ); + + block_fs_fread_realloc_buffer( bfs->block_fs , key , buffer); + + free( key ); + } +} + + +static void block_fs_driver_load_vector(void * _driver , const char * node_key , int iens , buffer_type * buffer) { + block_fs_driver_type * driver = block_fs_driver_safe_cast( _driver ); + { + char * key = block_fs_driver_alloc_vector_key( driver , node_key , iens ); + bfs_type * bfs = block_fs_driver_get_fs( driver , iens ); + + block_fs_fread_realloc_buffer( bfs->block_fs , key , buffer); + free( key ); + } +} + +/*****************************************************************/ + +static void block_fs_driver_save_node(void * _driver , const char * node_key , int report_step , int iens , buffer_type * buffer) { + block_fs_driver_type * driver = (block_fs_driver_type *) _driver; + block_fs_driver_assert_cast(driver); + { + char * key = block_fs_driver_alloc_node_key( driver , node_key , report_step , iens ); + bfs_type * bfs = block_fs_driver_get_fs( driver , iens ); + block_fs_fwrite_buffer( bfs->block_fs , key , buffer); + free( key ); + } +} + + +static void block_fs_driver_save_vector(void * _driver , const char * node_key , int iens , buffer_type * buffer) { + block_fs_driver_type * driver = (block_fs_driver_type *) _driver; + block_fs_driver_assert_cast(driver); + { + char * key = block_fs_driver_alloc_vector_key( driver , node_key , iens ); + bfs_type * bfs = block_fs_driver_get_fs( driver , iens ); + block_fs_fwrite_buffer( bfs->block_fs , key , buffer); + free( key ); + } +} + +/*****************************************************************/ + +void block_fs_driver_unlink_node(void * _driver , const char * node_key , int report_step , int iens ) { + block_fs_driver_type * driver = (block_fs_driver_type *) _driver; + block_fs_driver_assert_cast(driver); + { + char * key = block_fs_driver_alloc_node_key( driver , node_key , report_step , iens ); + bfs_type * bfs = block_fs_driver_get_fs( driver , iens ); + block_fs_unlink_file( bfs->block_fs , key ); + free( key ); + } +} + +void block_fs_driver_unlink_vector(void * _driver , const char * node_key , int iens ) { + block_fs_driver_type * driver = (block_fs_driver_type *) _driver; + block_fs_driver_assert_cast(driver); + { + char * key = block_fs_driver_alloc_vector_key( driver , node_key , iens ); + bfs_type * bfs = block_fs_driver_get_fs( driver , iens ); + block_fs_unlink_file( bfs->block_fs , key ); + free( key ); + } +} + + +/*****************************************************************/ + +bool block_fs_driver_has_node(void * _driver , const char * node_key , int report_step , int iens ) { + block_fs_driver_type * driver = (block_fs_driver_type *) _driver; + block_fs_driver_assert_cast(driver); + { + char * key = block_fs_driver_alloc_node_key( driver , node_key , report_step , iens ); + bfs_type * bfs = block_fs_driver_get_fs( driver , iens ); + bool has_node = block_fs_has_file( bfs->block_fs , key ); + free( key ); + return has_node; + } +} + + +bool block_fs_driver_has_vector(void * _driver , const char * node_key , int iens ) { + block_fs_driver_type * driver = (block_fs_driver_type *) _driver; + block_fs_driver_assert_cast(driver); + { + char * key = block_fs_driver_alloc_vector_key( driver , node_key , iens ); + bfs_type * bfs = block_fs_driver_get_fs( driver , iens ); + bool has_node = block_fs_has_file( bfs->block_fs , key ); + free( key ); + return has_node; + } +} + +/*****************************************************************/ + + + + + + + + + +void block_fs_driver_free(void *_driver) { + block_fs_driver_type * driver = block_fs_driver_safe_cast( _driver ); + { + int driver_nr; + thread_pool_type * tp = thread_pool_alloc( 4 , true); + for (driver_nr = 0; driver_nr < driver->num_fs; driver_nr++) + thread_pool_add_job( tp , bfs_close__ , driver->fs_list[driver_nr] ); + + thread_pool_join( tp ); + thread_pool_free( tp ); + } + bfs_config_free( driver->config ); + free( driver->fs_list ); + free(driver); +} + + + +static void block_fs_driver_fsync( void * _driver ) { + block_fs_driver_type * driver = (block_fs_driver_type *) _driver; + block_fs_driver_assert_cast(driver); + + { + int driver_nr; + block_fs_driver_type * driver = block_fs_driver_safe_cast(_driver); + for (driver_nr = 0; driver_nr < driver->num_fs; driver_nr++) + bfs_fsync( driver->fs_list[driver_nr] ); + } +} + + +static block_fs_driver_type * block_fs_driver_alloc(int num_fs) { + block_fs_driver_type * driver = (block_fs_driver_type *)util_malloc(sizeof * driver ); + { + fs_driver_type * fs_driver = (fs_driver_type *) driver; + fs_driver_init(fs_driver); + } + driver->load_node = block_fs_driver_load_node; + driver->save_node = block_fs_driver_save_node; + driver->unlink_node = block_fs_driver_unlink_node; + driver->has_node = block_fs_driver_has_node; + + driver->load_vector = block_fs_driver_load_vector; + driver->save_vector = block_fs_driver_save_vector; + driver->unlink_vector = block_fs_driver_unlink_vector; + driver->has_vector = block_fs_driver_has_vector; + + driver->free_driver = block_fs_driver_free; + driver->fsync_driver = block_fs_driver_fsync; + driver->__id = BLOCK_FS_DRIVER_ID; + driver->num_fs = num_fs; + + driver->fs_list = (bfs_type **) util_calloc( driver->num_fs , sizeof * driver->fs_list ); + return driver; +} + + + + +static void * block_fs_driver_alloc_new( fs_driver_enum driver_type , bool read_only , int num_fs , const char * mountfile_fmt, bool block_level_lock ) { + block_fs_driver_type * driver = block_fs_driver_alloc( num_fs); + driver->config = bfs_config_alloc( driver_type , read_only, block_level_lock ); + { + for (int ifs = 0; ifs < driver->num_fs; ifs++) + driver->fs_list[ifs] = bfs_alloc_new( driver->config , util_alloc_sprintf( mountfile_fmt , ifs) ); + } + return driver; +} + + +static void block_fs_driver_mount( block_fs_driver_type * driver ) { + thread_pool_type * tp = thread_pool_alloc( 4 , true ); + + for (int ifs = 0; ifs < driver->num_fs; ifs++) + thread_pool_add_job( tp , bfs_mount__ , driver->fs_list[ ifs ]); + + thread_pool_join( tp ); + thread_pool_free( tp ); +} + + + + + +/*****************************************************************/ + +void block_fs_driver_create_fs( FILE * stream , + const char * mount_point , + fs_driver_enum driver_type , + int num_fs , + const char * ens_path_fmt, + const char * filename ) { + + util_fwrite_int(driver_type , stream ); + util_fwrite_int(num_fs , stream ); + { + char * mountfile_fmt = util_alloc_sprintf("%s%c%s.mnt" , ens_path_fmt , UTIL_PATH_SEP_CHAR , filename ); + util_fwrite_string( mountfile_fmt , stream ); + free( mountfile_fmt ); + } + + for (int ifs = 0; ifs < num_fs; ifs++) { + char * path_fmt = util_alloc_sprintf("%s%c%s" , mount_point , UTIL_PATH_SEP_CHAR , ens_path_fmt); + char * ens_path = util_alloc_sprintf(path_fmt , ifs); + + util_make_path( ens_path ); + + free( path_fmt ); + free( ens_path ); + } + +} + + +/* + @path should contain both elements called root_path and case_path in + the block_fs_driver_create() function. +*/ + +void * block_fs_driver_open(FILE * fstab_stream , const char * mount_point , fs_driver_enum driver_type , bool read_only) { + int num_fs = util_fread_int( fstab_stream ); + char * tmp_fmt = util_fread_alloc_string( fstab_stream ); + char * mountfile_fmt = util_alloc_sprintf("%s%c%s" , mount_point , UTIL_PATH_SEP_CHAR , tmp_fmt ); + const bool block_level_lock = false; + + block_fs_driver_type * driver = (block_fs_driver_type * ) block_fs_driver_alloc_new( driver_type , read_only , num_fs , mountfile_fmt, block_level_lock ); + + block_fs_driver_mount( driver ); + + free( tmp_fmt ); + free( mountfile_fmt ); + return driver; +} + + +void block_fs_driver_fskip(FILE * fstab_stream) { + util_fskip_int( fstab_stream ); + { + char * tmp_fmt = util_fread_alloc_string( fstab_stream ); + free( tmp_fmt ); + } +} diff --git a/libres/lib/enkf/block_obs.cpp b/libres/lib/enkf/block_obs.cpp new file mode 100644 index 00000000000..1ea85f9101c --- /dev/null +++ b/libres/lib/enkf/block_obs.cpp @@ -0,0 +1,441 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'block_obs.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. +*/ + +/** + See the overview documentation of the observation system in enkf_obs.c +*/ +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define BLOCK_OBS_TYPE_ID 661098 +#define POINT_OBS_TYPE_ID 778196 + + +typedef struct { + UTIL_TYPE_ID_DECLARATION; + block_obs_source_type source_type; + int i; + int j; + int k; + int active_index; + double value; + double std; + double std_scaling; + char * sum_key; +} point_obs_type; + +static UTIL_SAFE_CAST_FUNCTION(point_obs , POINT_OBS_TYPE_ID); + + +struct block_obs_struct { + UTIL_TYPE_ID_DECLARATION; + char * obs_key; /** A user provided label for the observation. */ + vector_type * point_list; + const ecl_grid_type * grid; + const void * data_config; + block_obs_source_type source_type; +}; + + +static UTIL_SAFE_CAST_FUNCTION_CONST(block_obs , BLOCK_OBS_TYPE_ID); +static UTIL_SAFE_CAST_FUNCTION(block_obs , BLOCK_OBS_TYPE_ID); +UTIL_IS_INSTANCE_FUNCTION(block_obs , BLOCK_OBS_TYPE_ID); + + +/*****************************************************************/ + + + + + +static point_obs_type * point_obs_alloc( block_obs_source_type source_type , int i , int j , int k , int active_index , const char * sum_key , double value , double std) { + point_obs_type * point_obs = (point_obs_type *)util_malloc( sizeof * point_obs ); + UTIL_TYPE_ID_INIT( point_obs , POINT_OBS_TYPE_ID ); + + if (source_type == SOURCE_FIELD) { + point_obs->active_index = active_index; + point_obs->sum_key = NULL; + } else { + point_obs->active_index = -1; + point_obs->sum_key = util_alloc_string_copy( sum_key ); + } + + point_obs->source_type = source_type; + point_obs->i = i; + point_obs->j = j; + point_obs->k = k; + point_obs->value = value; + point_obs->std = std; + point_obs->std_scaling = 1.0; + + return point_obs; +} + + + + + + +static void point_obs_free( point_obs_type * point_obs ) { + free( point_obs->sum_key ); + free( point_obs ); +} + +static void point_obs_free__( void * arg ) { + point_obs_type * point_obs = point_obs_safe_cast( arg ); + point_obs_free( point_obs ); +} + + + +static double point_obs_iget_data( const point_obs_type * point_obs , const void * state , int iobs , node_id_type node_id) { + if (point_obs->source_type == SOURCE_FIELD) { + const field_type * field = field_safe_cast_const( state ); + return field_iget_double(field , point_obs->active_index); + } else if (point_obs->source_type == SOURCE_SUMMARY) { + const container_type * container = container_safe_cast_const( state ); + const summary_type * summary = summary_safe_cast_const( container_iget_node( container , iobs )); + return summary_get( summary , node_id.report_step ); + } else { + util_abort("%s: unknown source type: %d \n",__func__, point_obs->source_type ); + return -1; + } +} + + +/*****************************************************************/ + + +static const point_obs_type * block_obs_iget_point_const( const block_obs_type * block_obs , int index) { + return (const point_obs_type *) vector_iget_const( block_obs->point_list , index ); +} + + +static point_obs_type * block_obs_iget_point( const block_obs_type * block_obs , int index) { + return (point_obs_type *) vector_iget( block_obs->point_list , index ); +} + + +static void block_obs_validate_ijk( const ecl_grid_type * grid , int size, const int * i , const int * j , const int * k) { + int l; + for (l = 0; l < size; l++) { + if (ecl_grid_ijk_valid(grid , i[l] , j[l] , k[l])) { + int active_index = ecl_grid_get_active_index3( grid , i[l] , j[l] , k[l]); + if (active_index < 0) + util_abort("%s: sorry: cell:(%d,%d,%d) is not active - can not observe it. \n",__func__ , i[l]+1 , j[l]+1 , k[l]+1); + + } else + util_abort("%s: sorry: cell (%d,%d,%d) is outside valid range: \n",__func__ , i[l]+1 , j[l]+1 , k[l]+1); + } +} + + +static void block_obs_append_point( block_obs_type * block_obs , point_obs_type * point) { + if (point->source_type == block_obs->source_type) + vector_append_owned_ref(block_obs->point_list , point , point_obs_free__); + else + util_abort("%s: fatal internal error - mixing points with different source type in one block_obs instance.\n",__func__); +} + + +void block_obs_append_field_obs( block_obs_type * block_obs , int i , int j , int k , double value , double std) { + int active_index = ecl_grid_get_active_index3( block_obs->grid , i , j , k ); + point_obs_type * point_obs = point_obs_alloc( SOURCE_FIELD , i , j , k , active_index , NULL , value , std); + block_obs_append_point( block_obs , point_obs ); +} + + +void block_obs_append_summary_obs( block_obs_type * block_obs , int i , int j , int k , const char * sum_key , double value , double std) { + int active_index = -1; + point_obs_type * point_obs = point_obs_alloc( SOURCE_SUMMARY , i , j , k , active_index, sum_key , value , std); + block_obs_append_point( block_obs , point_obs ); +} + + +block_obs_type * block_obs_alloc(const char * obs_key, + const void * data_config , + const ecl_grid_type * grid) { + + if (!(field_config_is_instance( data_config ) || container_config_is_instance( data_config ))) + return NULL; + + { + block_obs_type * block_obs = (block_obs_type *)util_malloc(sizeof * block_obs); + UTIL_TYPE_ID_INIT( block_obs , BLOCK_OBS_TYPE_ID ); + + block_obs->obs_key = util_alloc_string_copy(obs_key); + block_obs->data_config = data_config; + block_obs->point_list = vector_alloc_new(); + block_obs->grid = grid; + + if (field_config_is_instance( data_config )) + block_obs->source_type = SOURCE_FIELD; + else + block_obs->source_type = SOURCE_SUMMARY; + + return block_obs; + } +} + + + +/** + The input vectors i,j,k should contain offset zero values. +*/ +block_obs_type * block_obs_alloc_complete(const char * obs_key, + block_obs_source_type source_type , + const stringlist_type * summary_keys , + const void * data_config , + const ecl_grid_type * grid , + int size, + const int * i, + const int * j, + const int * k, + const double * obs_value, + const double * obs_std) +{ + if (source_type == SOURCE_FIELD) + block_obs_validate_ijk( grid , size , i,j,k); + + { + block_obs_type * block_obs = block_obs_alloc( obs_key , data_config , grid ); + if (block_obs) { + for (int l=0; l < size; l++) { + + if (source_type == SOURCE_SUMMARY) { + const char * sum_key = stringlist_iget( summary_keys , l ); + block_obs_append_summary_obs( block_obs , i[l] , j[l] , k[l] , sum_key , obs_value[l] , obs_std[l]); + } else + block_obs_append_field_obs( block_obs , i[l] , j[l] , k[l] , obs_value[l] , obs_std[l]); + + } + return block_obs; + } else { + util_abort("%s: internal error - block_obs_alloc() returned NULL \n",__func__); + return NULL; + } + } +} + + + +void block_obs_free( block_obs_type * block_obs) { + vector_free( block_obs->point_list ); + free(block_obs->obs_key); + free(block_obs); +} + + + + + + + +void block_obs_get_observations(const block_obs_type * block_obs, obs_data_type * obs_data, enkf_fs_type * fs, int report_step , const active_list_type * __active_list) { + int i; + int obs_size = block_obs_get_size( block_obs ); + int active_size = active_list_get_active_size( __active_list , obs_size); + active_mode_type active_mode = active_list_get_mode( __active_list ); + obs_block_type * obs_block = obs_data_add_block( obs_data , block_obs->obs_key , obs_size , NULL , false ); + + if (active_mode == ALL_ACTIVE) { + for (i=0; i < obs_size; i++) { + const point_obs_type * point_obs = block_obs_iget_point_const( block_obs , i ); + obs_block_iset(obs_block , i , point_obs->value , point_obs->std * point_obs->std_scaling ); + } + } else if (active_mode == PARTLY_ACTIVE) { + const int * active_list = active_list_get_active( __active_list ); + for (i =0 ; i < active_size; i++) { + int iobs = active_list[i]; + const point_obs_type * point_obs = block_obs_iget_point_const( block_obs , i ); + obs_block_iset(obs_block , iobs , point_obs->value , point_obs->std * point_obs->std_scaling ); + } + } +} + + +static void block_obs_assert_data( const block_obs_type * block_obs , const void * state ) { + if (block_obs->source_type == SOURCE_FIELD) { + if (!field_is_instance( state )) + util_abort("%s: state data is not of type FIELD - aborting \n",__func__); + } else if (block_obs->source_type == SOURCE_SUMMARY) { + if (!container_is_instance( state )) + util_abort("%s: state data is not of type CONTAINER - aborting \n",__func__); + } +} + + +double block_obs_iget_data( const block_obs_type * block_obs, const void * state , int iobs , node_id_type node_id ) { + const point_obs_type * point_obs = block_obs_iget_point_const( block_obs , iobs ); + return point_obs_iget_data( point_obs , state , iobs , node_id); +} + + + +void block_obs_measure(const block_obs_type * block_obs, const void * state , node_id_type node_id , meas_data_type * meas_data , const active_list_type * __active_list) { + block_obs_assert_data( block_obs , state ); + { + int obs_size = block_obs_get_size( block_obs ); + int active_size = active_list_get_active_size( __active_list , obs_size ); + meas_block_type * meas_block = meas_data_add_block( meas_data , block_obs->obs_key , node_id.report_step , obs_size ); + int iobs; + + active_mode_type active_mode = active_list_get_mode( __active_list ); + if (active_mode == ALL_ACTIVE) { + for (iobs=0; iobs < obs_size; iobs++) { + double value = block_obs_iget_data( block_obs , state , iobs , node_id ); + meas_block_iset( meas_block , node_id.iens , iobs , value ); + } + } else if (active_mode == PARTLY_ACTIVE) { + const int * active_list = active_list_get_active( __active_list ); + for (int i =0 ; i < active_size; i++) { + iobs = active_list[i]; + { + double value = block_obs_iget_data( block_obs , state , iobs , node_id); + meas_block_iset( meas_block , node_id.iens , i , value ); + } + } + } + } +} + +// used by the VOID_CHI2 macro +C_USED double block_obs_chi2(const block_obs_type * block_obs, const void * state, node_id_type node_id) { + double sum_chi2 = 0; + int obs_size = block_obs_get_size( block_obs ); + block_obs_assert_data(block_obs, state); + + for (int i=0; i < obs_size; i++) { + const point_obs_type * point_obs = block_obs_iget_point_const( block_obs , i ); + double sim_value = point_obs_iget_data( point_obs , state , i, node_id ); + double x = (sim_value - point_obs->value) / point_obs->std; + sum_chi2 += x*x; + } + return sum_chi2; +} + + +double block_obs_iget_value(const block_obs_type * block_obs, int index ){ + const point_obs_type * point_obs = block_obs_iget_point_const( block_obs , index ); + return point_obs->value; +} + +double block_obs_iget_std(const block_obs_type * block_obs, int index ){ + const point_obs_type * point_obs = block_obs_iget_point_const( block_obs , index ); + return point_obs->std; +} + +double block_obs_iget_std_scaling(const block_obs_type * block_obs, int index ){ + const point_obs_type * point_obs = block_obs_iget_point_const( block_obs , index ); + return point_obs->std_scaling; +} + + +void block_obs_user_get(const block_obs_type * block_obs , const char * index_key , double *value , double * std, bool * valid) { + int i,j,k; + + *valid = false; + if (field_config_parse_user_key__( index_key , &i , &j , &k)) { + int obs_size = block_obs_get_size( block_obs ); + int active_index = ecl_grid_get_active_index3(block_obs->grid , i,j,k); + int l = 0; + /* iterating through all the cells the observation is observing. */ + + while (!(*valid) && l < obs_size) { + const point_obs_type * point_obs = block_obs_iget_point_const( block_obs , l); + if (point_obs->active_index == active_index) { + *value = point_obs->value; + *std = point_obs->std; + *valid = true; + } + l++; + } + } +} + + + + +int block_obs_iget_i(const block_obs_type * block_obs, int index) { + const point_obs_type * point_obs = block_obs_iget_point_const( block_obs , index); + return point_obs->i; +} + +int block_obs_iget_j(const block_obs_type * block_obs, int index) { + const point_obs_type * point_obs = block_obs_iget_point_const( block_obs , index); + return point_obs->j; +} + +int block_obs_iget_k(const block_obs_type * block_obs, int index) { + const point_obs_type * point_obs = block_obs_iget_point_const( block_obs , index); + return point_obs->k; +} + +double block_obs_iget_depth( const block_obs_type * block_obs , int index) { + const point_obs_type * point_obs = block_obs_iget_point_const( block_obs , index); + return ecl_grid_get_cdepth3( block_obs->grid , point_obs->i , point_obs->j ,point_obs->k); +} + +int block_obs_get_size(const block_obs_type * block_obs) { + return vector_get_size( block_obs->point_list ); +} + +void block_obs_update_std_scale(block_obs_type * block_obs, double scale_factor, const active_list_type * active_list) { + int obs_size = block_obs_get_size( block_obs ); + if (active_list_get_mode( active_list ) == ALL_ACTIVE) { + for (int i = 0; i < obs_size; i++) { + point_obs_type * point_observation = block_obs_iget_point( block_obs , i ); + point_observation->std_scaling = scale_factor; + } + } else { + const int * active_index = active_list_get_active( active_list ); + int size = active_list_get_active_size( active_list , obs_size ); + for (int i=0; i < size; i++) { + int obs_index = active_index[i]; + point_obs_type * point_observation = block_obs_iget_point( block_obs , obs_index ); + point_observation->std_scaling = scale_factor; + } + } +} + + + + + + +/*****************************************************************/ + +VOID_FREE(block_obs) +VOID_GET_OBS(block_obs) +VOID_MEASURE_UNSAFE(block_obs , data) // The cast of data field is not checked - that is done in block_obs_measure(). +VOID_USER_GET_OBS(block_obs) +VOID_CHI2(block_obs , field) +VOID_UPDATE_STD_SCALE(block_obs) diff --git a/libres/lib/enkf/callback_arg.cpp b/libres/lib/enkf/callback_arg.cpp new file mode 100644 index 00000000000..06275a01595 --- /dev/null +++ b/libres/lib/enkf/callback_arg.cpp @@ -0,0 +1,40 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'callback_arg.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 + +#define CALLBACK_ARG_TYPE_ID 7814509 + + +callback_arg_type * callback_arg_alloc(const res_config_type * res_config, + run_arg_type * run_arg, + rng_type * rng) +{ + callback_arg_type * cb = (callback_arg_type *)util_malloc( sizeof * cb ); + UTIL_TYPE_ID_INIT( cb , CALLBACK_ARG_TYPE_ID); + cb->run_arg = run_arg; + cb->rng = rng; + cb->res_config = res_config; + return cb; +} + + + +UTIL_IS_INSTANCE_FUNCTION( callback_arg, CALLBACK_ARG_TYPE_ID ) +UTIL_SAFE_CAST_FUNCTION( callback_arg, CALLBACK_ARG_TYPE_ID ) diff --git a/libres/lib/enkf/cases_config.cpp b/libres/lib/enkf/cases_config.cpp new file mode 100644 index 00000000000..aadb09eb7b7 --- /dev/null +++ b/libres/lib/enkf/cases_config.cpp @@ -0,0 +1,80 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'cases_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 + + +struct cases_config_struct { + int iteration_number; +}; + + +cases_config_type * cases_config_alloc( ) { + cases_config_type * config = (cases_config_type *)util_malloc( sizeof * config ); + config->iteration_number = 0; + return config; +} + + +static void cases_config_set_iteration_number( cases_config_type * config , int num_iterations) { + config->iteration_number = num_iterations; +} + +int cases_config_get_iteration_number( const cases_config_type * config ) { + return config->iteration_number; +} + +bool cases_config_set_int( cases_config_type * cases_config , const char * var_name , int value) { + bool name_recognized = true; + if (strcmp( var_name , "iteration_number") == 0) + cases_config_set_iteration_number(cases_config, value); + else + name_recognized = false; + + return name_recognized; +} + + + +void cases_config_fwrite( cases_config_type * config , const char * filename ) { + FILE * stream = util_mkdir_fopen(filename , "w"); + int iteration_no = cases_config_get_iteration_number(config); + util_fwrite_int( iteration_no , stream ); + fclose( stream ); +} + +void cases_config_fread( cases_config_type * config , const char * filename) { + if (util_file_exists( filename )) { + FILE * stream = util_fopen( filename , "r"); + int iteration_number = util_fread_int( stream ); + cases_config_set_iteration_number(config,iteration_number); + fclose( stream ); + } +} + + +void cases_config_free( cases_config_type * config ) { + free( config ); +} + diff --git a/libres/lib/enkf/config_keys.cpp b/libres/lib/enkf/config_keys.cpp new file mode 100644 index 00000000000..5376ec6b151 --- /dev/null +++ b/libres/lib/enkf/config_keys.cpp @@ -0,0 +1,430 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'config_keys.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 + +const char * config_keys_get_config_directory_key() { + return CONFIG_DIRECTORY_KEY; +} + +const char * config_keys_get_config_file_key() { + return RES_CONFIG_FILE_KEY; +} + +const char * config_keys_get_queue_system_key() { + return QUEUE_SYSTEM_KEY; +} + +const char * config_keys_get_run_template_key() { + return RUN_TEMPLATE_KEY; +} + +const char * config_keys_get_gen_kw_key() { + return GEN_KW_KEY; +} + +const char * config_keys_get_queue_option_key() { + return QUEUE_OPTION_KEY; +} + +const char * config_keys_get_install_job_key() { + return INSTALL_JOB_KEY; +} + +const char * config_keys_get_log_file_key() { + return LOG_FILE_KEY; +} + +const char * config_keys_get_log_level_key() { + return LOG_LEVEL_KEY; +} + +const char * config_keys_get_update_log_path_key() { + return UPDATE_LOG_PATH_KEY; +} + +const char * config_keys_get_summary_key() { + return SUMMARY_KEY; +} + +const char * config_keys_get_max_runtime_key() { + return MAX_RUNTIME_KEY; +} + +const char * config_keys_get_min_realizations_key() { + return MIN_REALIZATIONS_KEY; +} + +const char * config_keys_get_max_submit_key() { + return MAX_SUBMIT_KEY; +} + +const char * config_keys_get_umask_key() { + return UMASK_KEY; +} + +const char * config_keys_get_data_kw_key() { + return DATA_KW_KEY; +} + +const char * config_keys_get_runpath_file_key() { + return RUNPATH_FILE_KEY; +} + +const char * config_keys_get_gen_data_key() { + return GEN_DATA_KEY; +} + +/* ************* ECL config ************* */ +const char * config_keys_get_eclbase_key() { + return ECLBASE_KEY; +} + +const char * config_keys_get_data_file_key() { + return DATA_FILE_KEY; +} + +const char * config_keys_get_grid_key() { + return GRID_KEY; +} + +const char * config_keys_get_refcase_key() { + return REFCASE_KEY; +} + +const char * config_keys_get_refcase_list_key() { + return REFCASE_LIST_KEY; +} + +const char * config_keys_get_end_date_key() { + return END_DATE_KEY; +} + +const char * config_keys_get_schedule_prediction_file_key() { + return SCHEDULE_PREDICTION_FILE_KEY; +} +/* ************* ECL config ************* */ + +const char * config_keys_get_result_file() { + return RESULT_FILE_KEY; +} + +const char * config_keys_get_report_steps() { + return REPORT_STEPS_KEY; +} + +const char * config_keys_get_input_format() { + return INPUT_FORMAT_KEY; +} + +const char * config_keys_get_ecl_file() { + return ECL_FILE_KEY; +} + +const char * config_keys_get_output_format() { + return OUTPUT_FORMAT_KEY; +} + +const char * config_keys_get_init_files() { + return INIT_FILES_KEY; +} + +const char * config_keys_get_random_seed() { + return RANDOM_SEED_KEY; +} +/////***** Analysis config keys*******//////////// +const char * config_keys_get_alpha() { + return ENKF_ALPHA_KEY; +} + +const char * config_keys_get_std_cutoff() { + return STD_CUTOFF_KEY; +} +const char * config_keys_get_stop_long_running() { + return STOP_LONG_RUNNING_KEY; +} + +const char * config_keys_get_single_node_update() { + return SINGLE_NODE_UPDATE_KEY; +} + +const char * config_keys_get_std_scale_correlated_obs() { + return STD_SCALE_CORRELATED_OBS_KEY; +} + +const char * config_keys_get_rerun() { + return ENKF_RERUN_KEY; +} + +const char * config_keys_get_rerun_start() { + return RERUN_START_KEY; +} + +const char * config_keys_get_merge_observations() { + return ENKF_MERGE_OBSERVATIONS_KEY; +} + +const char * config_keys_get_analysis_load() { + return ANALYSIS_LOAD_KEY; +} + +const char * config_keys_get_analysis_copy() { + return ANALYSIS_COPY_KEY; +} + +const char * config_keys_get_analysis_select() { + return ANALYSIS_SELECT_KEY; +} + +const char * config_keys_get_analysis_set_var() { + return ANALYSIS_SET_VAR_KEY; +} +/////***** Analysis config keys*******//////////// +const char * config_keys_get_install_job_directory_key() { + return INSTALL_JOB_DIRECTORY_KEY; +} + +const char * config_keys_get_license_path_key() { + return LICENSE_PATH_KEY; +} + +const char * config_keys_get_setenv_key() { + return SETENV_KEY; +} + +const char * config_keys_get_job_script_key() { + return JOB_SCRIPT_KEY; +} + +const char * config_keys_get_num_cpu_key() { + return NUM_CPU_KEY; +} + +const char * config_keys_get_define_key() { + return DEFINE_KEY; +} + +//*********analysis_iter_config keys************// +const char * config_keys_get_iter_case_key() { + return ITER_CASE_KEY; +} + +const char * config_keys_get_iter_count_key() { + return ITER_COUNT_KEY; +} + +const char * config_keys_get_iter_retry_count_key() { + return ITER_RETRY_COUNT_KEY; +} +//*********analysis_iter_config keys************// + +//*********ert workflow list_config keys************// +const char * config_keys_get_load_workflow_key() { + return LOAD_WORKFLOW_KEY; +} + +const char * config_keys_get_load_workflow_job_key() { + return LOAD_WORKFLOW_JOB_KEY; +} + +const char * config_keys_get_workflow_job_directory_key() { + return WORKFLOW_JOB_DIRECTORY_KEY; +} +//*********ert workflow list_config keys************// + +const char * config_keys_get_hook_workflow_key() { + return HOOK_WORKFLOW_KEY; +} +// hook_manager config keys + +/* ************* Model config ************* */ + +const char * config_keys_get_max_resample_key() { + return MAX_RESAMPLE_KEY; +} + +const char * config_keys_get_num_realizations_key() { + return NUM_REALIZATIONS_KEY; +} + +const char * config_keys_get_runpath_key() { + return RUNPATH_KEY; +} + +const char * config_keys_get_data_root_key() { + return DATA_ROOT_KEY; +} + +const char * config_keys_get_enspath_key() { + return ENSPATH_KEY; +} + +const char * config_keys_get_jobname_key() { + return JOBNAME_KEY; +} + +const char * config_keys_get_forward_model_key() { + return FORWARD_MODEL_KEY; +} + +const char * config_keys_get_simulation_job_key() { + return SIMULATION_JOB_KEY; +} + +const char * config_keys_get_rftpath_key() { + return RFTPATH_KEY; +} + +const char * config_keys_get_gen_kw_export_name_key() { + return GEN_KW_EXPORT_NAME_KEY; +} + +const char * config_keys_get_history_source_key() { + return HISTORY_SOURCE_KEY; +} + +const char * config_keys_get_obs_config_key() { + return OBS_CONFIG_KEY; +} + +const char * config_keys_get_time_map_key() { + return TIME_MAP_KEY; +} + +/* ************* Model config ************* */ + +/* ************* Ensemble config ************* */ + const char * config_keys_get_gen_param_key() { + return GEN_PARAM_KEY; + } + + const char * config_keys_get_forward_init_key() { + return FORWARD_INIT_KEY; + } + + const char * config_keys_get_min_std_key() { + return MIN_STD_KEY; + } + + const char * config_keys_get_template_key() { + return TEMPLATE_KEY; + } + + const char * config_keys_get_key_key() { + return KEY_KEY; + } + + const char * config_keys_get_kw_tag_format_key() { + return GEN_KW_TAG_FORMAT_KEY; + } + + const char * config_keys_get_surface_key() { + return SURFACE_KEY; + } + + const char * config_keys_get_base_surface_key() { + return BASE_SURFACE_KEY; + } + + const char * config_keys_get_field_key() { + return FIELD_KEY; + } + + const char * config_keys_get_init_transform_key() { + return INIT_TRANSFORM_KEY; + } + + const char * config_keys_get_input_transform_key() { + return INPUT_TRANSFORM_KEY; + } + + const char * config_keys_get_output_transform_key() { + return OUTPUT_TRANSFORM_KEY; + } + + const char * config_keys_get_min_key() { + return MIN_KEY; + } + + const char * config_keys_get_max_key() { + return MAX_KEY; + } + + const char * config_keys_get_parameter_key() { + return PARAMETER_KEY; + } + + const char * config_keys_get_general_key() { + return GENERAL_KEY; + } + + const char * config_keys_get_pred_key() { + return PRED_KEY; + } + + const char * config_keys_get_container_key() { + return CONTAINER_KEY; + } + + const char * config_keys_get_slurm_sbatch_option() { + return SLURM_SBATCH_OPTION; + } + + const char * config_keys_get_slurm_scancel_option() { + return SLURM_SCANCEL_OPTION; + } + + const char * config_keys_get_slurm_scontrol_option() { + return SLURM_SCONTROL_OPTION; + } + + const char * config_keys_get_slurm_squeue_option() { + return SLURM_SQUEUE_OPTION; + } + + const char * config_keys_get_slurm_partition_option() { + return SLURM_PARTITION_OPTION; + } + + const char * config_keys_get_slurm_squeue_timeout_option() { + return SLURM_SQUEUE_TIMEOUT_OPTION; + } + + const char * config_keys_get_slurm_max_runtime_option() { + return SLURM_MAX_RUNTIME_OPTION; + } + + const char * config_keys_get_slurm_memory_option() { + return SLURM_MEMORY_OPTION; + } + + const char * config_keys_get_slurm_memory_per_cpu_option() { + return SLURM_MEMORY_PER_CPU_OPTION; + } + + const char * config_keys_get_slurm_exclude_host_option() { + return SLURM_EXCLUDE_HOST_OPTION; + } + + const char * config_keys_get_slurm_include_host_option() { + return SLURM_INCLUDE_HOST_OPTION; + } + + + +/* ************* Ensemble config ************* */ diff --git a/libres/lib/enkf/container.cpp b/libres/lib/enkf/container.cpp new file mode 100644 index 00000000000..aa1842f92af --- /dev/null +++ b/libres/lib/enkf/container.cpp @@ -0,0 +1,80 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'container.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 + +struct container_struct { + int __type_id; + const container_config_type * config; + vector_type * nodes; +}; + + +// Used by the VOID_ALLOC macro +C_USED container_type * container_alloc( const container_config_type * config ) { + container_type * container = (container_type *)util_malloc( sizeof * container ); + UTIL_TYPE_ID_INIT( container , CONTAINER ); + container->config = config; + container->nodes = vector_alloc_new(); + return container; +} + + +void container_free( container_type * container ) { + vector_free( container->nodes ); + free( container ); +} + +void container_add_node(container_type * container , void * child_node ) { + vector_append_ref( container->nodes , child_node ); +} + +const void * container_iget_node(const container_type * container , int index) { + return vector_iget_const( container->nodes , index ); // CXX_CAST_ERROR +} + +int container_get_size( const container_type * container ) { + return vector_get_size( container->nodes ); +} + +void container_assert_size( const container_type * container ) { + if (vector_get_size( container->nodes ) != container_config_get_size( container->config )) + util_abort("%s: container size mismatch. Current:%d Config:%d \n",__func__ , container_get_size( container ) , container_config_get_size( container->config )); +} + + +/******************************************************************/ +/* Anonumously generated functions used by the enkf_node object */ +/******************************************************************/ + +UTIL_IS_INSTANCE_FUNCTION(container , CONTAINER) +UTIL_SAFE_CAST_FUNCTION(container , CONTAINER) +UTIL_SAFE_CAST_FUNCTION_CONST(container , CONTAINER) +VOID_ALLOC(container) +VOID_FREE(container) + + + + + + + diff --git a/libres/lib/enkf/container_config.cpp b/libres/lib/enkf/container_config.cpp new file mode 100644 index 00000000000..b1c638358fa --- /dev/null +++ b/libres/lib/enkf/container_config.cpp @@ -0,0 +1,69 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'container_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 + +#define CONTAINER_CONFIG_TYPE_ID 51330852 + +struct container_config_struct { + UTIL_TYPE_ID_DECLARATION; + vector_type * nodes; +}; + +UTIL_IS_INSTANCE_FUNCTION(container_config , CONTAINER_CONFIG_TYPE_ID); + +container_config_type * container_config_alloc( const char * key ) { + container_config_type * container = (container_config_type *)util_malloc( sizeof * container ); + UTIL_TYPE_ID_INIT( container , CONTAINER_CONFIG_TYPE_ID ); + container->nodes = vector_alloc_new(); + return container; +} + + + +void container_config_free( container_config_type * container_config ) { + vector_free( container_config->nodes ); + free( container_config ); +} + + +void container_config_add_node( container_config_type * container_config , const enkf_config_node_type * config_node) { + vector_append_ref( container_config->nodes , config_node ); +} + +int container_config_get_size( const container_config_type * container_config ) { + return vector_get_size( container_config->nodes ); +} + + +int container_config_get_data_size( const container_config_type * container_config ) { + util_exit("%s: not implemented \n",__func__); + return 0; +} + + +/*****************************************************************/ + +UTIL_SAFE_CAST_FUNCTION(container_config , CONTAINER_CONFIG_TYPE_ID) +UTIL_SAFE_CAST_FUNCTION_CONST(container_config , CONTAINER_CONFIG_TYPE_ID) +VOID_GET_DATA_SIZE(container) +VOID_CONFIG_FREE(container) diff --git a/libres/lib/enkf/data_ranking.cpp b/libres/lib/enkf/data_ranking.cpp new file mode 100644 index 00000000000..464d14bef99 --- /dev/null +++ b/libres/lib/enkf/data_ranking.cpp @@ -0,0 +1,142 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'data_ranking.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 + + +#define DATA_RANKING_TYPE_ID 71420672 + +struct data_ranking_struct { + UTIL_TYPE_ID_DECLARATION; + int ens_size; + double_vector_type * data_ensemble; + perm_vector_type * sort_permutation; + bool_vector_type * valid; + char * user_key; + bool sort_increasing; +}; + + +UTIL_SAFE_CAST_FUNCTION( data_ranking , DATA_RANKING_TYPE_ID ) +UTIL_IS_INSTANCE_FUNCTION( data_ranking , DATA_RANKING_TYPE_ID ); + + +void data_ranking_free( data_ranking_type * ranking ) { + double_vector_free( ranking->data_ensemble ); + bool_vector_free( ranking->valid ); + + if (ranking->sort_permutation) + perm_vector_free( ranking->sort_permutation ); + + free( ranking->user_key ); + free( ranking ); +} + + + + + +static void data_ranking_init(data_ranking_type * ranking , + enkf_fs_type * fs , + const enkf_config_node_type * config_node, + const char * key_index , + int step) { + + enkf_node_type * enkf_node = enkf_node_alloc( config_node ); + int iens; + for (iens = 0; iens < ranking->ens_size; iens++) { + + double value; + node_id_type node_id = {.report_step = step , + .iens = iens }; + + if (enkf_node_user_get( enkf_node , fs , key_index , node_id , &value)) { + double_vector_iset( ranking->data_ensemble , iens , value ); + bool_vector_iset( ranking->valid , iens , true ); + } + } + + if (ranking->sort_increasing) + ranking->sort_permutation = double_vector_alloc_sort_perm( ranking->data_ensemble ); + else + ranking->sort_permutation = double_vector_alloc_rsort_perm( ranking->data_ensemble ); + + enkf_node_free( enkf_node ); +} + + + +data_ranking_type * data_ranking_alloc( bool sort_increasing , int ens_size , const char * user_key , const char * key_index , enkf_fs_type * fs , const enkf_config_node_type * config_node , int step) { + data_ranking_type * ranking = (data_ranking_type *)util_malloc( sizeof * ranking ); + UTIL_TYPE_ID_INIT( ranking , DATA_RANKING_TYPE_ID ); + ranking->ens_size = ens_size; + ranking->sort_increasing = sort_increasing; + + if (ranking->sort_increasing) + ranking->data_ensemble = double_vector_alloc( ens_size , INFINITY); // To ensure it comes last when sorting + else + ranking->data_ensemble = double_vector_alloc( ens_size , -INFINITY); // To ensure it comes last when sorting + + ranking->valid = bool_vector_alloc( ens_size , false ); + ranking->sort_permutation = NULL; + ranking->user_key = util_alloc_string_copy( user_key ); + + data_ranking_init( ranking , fs , config_node , key_index , step ); + return ranking; +} + + + +void data_ranking_free__( void * arg) { + data_ranking_type * ranking = data_ranking_safe_cast( arg ); + data_ranking_free( ranking ); +} + + +const perm_vector_type * data_ranking_get_permutation( const data_ranking_type * data_ranking ) { + return data_ranking->sort_permutation; +} + + +void data_ranking_display( const data_ranking_type * data_ranking , FILE * stream) { + const int ens_size = data_ranking->ens_size; + const perm_vector_type * permutations = data_ranking->sort_permutation; + + { + int i; + fprintf(stream,"\n\n"); + fprintf(stream," # Realization %12s\n" , data_ranking->user_key); + fprintf(stream,"----------------------------------\n"); + for (i = 0; i < ens_size; i++) { + int iens = perm_vector_iget( permutations , i ); + if (bool_vector_iget( data_ranking->valid , iens)) + fprintf(stream,"%3d %3d %14.3f\n",i,iens,double_vector_iget(data_ranking->data_ensemble , iens)); + + } + fprintf(stream,"----------------------------------\n"); + } +} diff --git a/libres/lib/enkf/dependencies b/libres/lib/enkf/dependencies new file mode 100644 index 00000000000..6849904ceca --- /dev/null +++ b/libres/lib/enkf/dependencies @@ -0,0 +1,71 @@ +enkf_tui_fs.o : enkf_tui_fs.c enkf_tui_fs.h enkf_tui_util.o enkf_tui_init.o enkf_main.o enkf_types.o enkf_fs.o +summary_obs.o : summary_obs.c summary_obs.h obs_data.o meas_matrix.o summary.o active_list.o +enkf_fs.o : enkf_fs.c enkf_fs.h enkf_defaults.o enkf_node.o basic_driver.o fs_types.o ecl_static_kw.o plain_driver_index.o plain_driver.o sqlite3_driver.o block_fs_driver.o block_fs_driver_index.o +local_updatestep.o : local_updatestep.c local_updatestep.h local_ministep.o enkf_macros.h +enkf_config_node.o : enkf_config_node.c enkf_config_node.h enkf_types.o enkf_macros.h field_config.o gen_data_config.o gen_kw_config.o summary_config.o +enkf_tui_util.o : enkf_tui_util.c enkf_tui_util.h enkf_node.o field.o field_config.o enkf_state.o ensemble_config.o enkf_types.o +enkf_defaults.o : enkf_defaults.c enkf_defaults.h +gen_data.o : gen_data.c gen_data.h enkf_serialize.o enkf_types.o enkf_macros.h enkf_util.o gen_data_config.o gen_data_common.h gen_common.o +block_fs_driver_index.o : block_fs_driver_index.c block_fs_driver_index.h fs_types.o basic_driver.o +field_obs.o : field_obs.c field_obs.h enkf_util.o field_config.o obs_data.o meas_vector.o field_config.o field.o active_list.o +enkf_tui_init.o : enkf_tui_init.c enkf_tui_init.h enkf_main.o enkf_sched.o enkf_types.o enkf_tui_util.o enkf_state.o enkf_node.o enkf_fs.o ensemble_config.o +block_fs_driver.o : block_fs_driver.c block_fs_driver.h fs_types.o basic_driver.o enkf_types.o +enkf_tui_ranking.o : enkf_tui_ranking.c enkf_tui_ranking.h enkf_main.o enkf_obs.o enkf_tui_util.o +enkf_main.o : enkf_main.c enkf_main.h enkf_config_node.o enkf_types.o obs_data.o meas_matrix.o enkf_state.o enkf_obs.o enkf_fs.o enkf_serialize.o enkf_sched.o ecl_config.o ensemble_config.o model_config.o site_config.o active_config.o enkf_analysis.o local_ministep.o local_updatestep.o local_config.o misfit_table.o plot_config.o ert_template.o enkf_defaults.o +plain_driver_common.o : plain_driver_common.c plain_driver_common.h enkf_types.o enkf_node.o +gen_data_active.o : gen_data_active.c gen_data_active.h enkf_macros.h +active_node.o : active_node.c active_node.h enkf_types.o enkf_config_node.o ensemble_config.o obs_vector.o +enkf_sched.o : enkf_sched.c enkf_sched.h enkf_types.o enkf_defaults.o +summary.o : summary.c summary.h enkf_macros.h enkf_util.o summary_config.o enkf_types.o enkf_util.o enkf_serialize.o +enkf_types.o : enkf_types.c enkf_types.h +plain_driver.o : plain_driver.c plain_driver.h fs_types.o basic_driver.o plain_driver_index.o plain_driver_common.o enkf_node.o +model_config.o : model_config.c model_config.h enkf_sched.o enkf_types.o plain_driver.o fs_types.o enkf_defaults.o +fs_types.o : fs_types.c fs_types.h +site_config.o : site_config.c site_config.h +ert_template.o : ert_template.c ert_template.h +misfit_table.o : misfit_table.c misfit_table.h enkf_obs.o enkf_fs.o +field_active.o : field_active.c field_active.h enkf_macros.h active_list.o +enkf_obs.o : enkf_obs.c enkf_obs.h summary_obs.o field_obs.o enkf_fs.o obs_vector.o enkf_state.o local_ministep.o local_config.o +enkf_state.o : enkf_state.c enkf_state.h enkf_node.o enkf_types.o ecl_static_kw.o field.o field_config.o gen_kw.o summary.o enkf_fs.o basic_driver.o ensemble_config.o model_config.o site_config.o ecl_config.o ert_template.o +active_config.o : active_config.c active_config.h enkf_types.o ensemble_config.o enkf_obs.o active_node.o +ensemble_config.o : ensemble_config.c ensemble_config.h enkf_config_node.o enkf_types.o field_config.o gen_data_config.o meas_matrix.o enkf_types.o enkf_fs.o gen_kw_config.o summary.o summary_config.o gen_data.o pilot_point_config.o gen_data_config.o gen_data_config.o field_trans.o +field_trans.o : field_trans.c field_trans.h +sqlite3_driver.o : sqlite3_driver.c sqlite3_driver.h sqlite3.o basic_driver.o fs_types.o enkf_config_node.o +field_config.o : field_config.c field_config.h enkf_types.o enkf_macros.h field_active.o active_list.o field_trans.o field_common.h +obs_vector.o : obs_vector.c obs_vector.h enkf_fs.o summary_obs.o field_obs.o gen_obs.o ensemble_config.o active_list.o +enkf_tui_export.o : enkf_tui_export.c enkf_tui_export.h enkf_main.o field.o field_config.o enkf_state.o enkf_fs.o enkf_tui_util.o field_config.o gen_data.o +enkf_tui_table.o : enkf_tui_table.c enkf_tui_table.h enkf_main.o enkf_tui_plot.o enkf_tui_fs.o enkf_obs.o field_obs.o field_config.o obs_vector.o enkf_tui_util.o ensemble_config.o gen_kw_config.o +enkf_tui_main.o : enkf_tui_main.c enkf_tui_main.h enkf_tui_run.o enkf_tui_export.o enkf_tui_plot.o enkf_tui_table.o enkf_tui_fs.o enkf_tui_ranking.o enkf_tui_misc.o enkf_main.o enkf_sched.o +enkf_util.o : enkf_util.c enkf_util.h enkf_defaults.o +main.o : main.c enkf_fs.o enkf_main.o enkf_types.o enkf_sched.o enkf_tui_main.o ERT.h +scalar.o : scalar.c scalar.h enkf_types.o scalar_config.o enkf_util.o enkf_serialize.o +pilot_point_config.o : pilot_point_config.c pilot_point_config.h enkf_types.o enkf_macros.h scalar_config.o +enkf_tui_plot.o : enkf_tui_plot.c enkf_tui_plot.h enkf_main.o enkf_tui_fs.o enkf_obs.o field_obs.o gen_obs.o field_config.o obs_vector.o enkf_tui_util.o ensemble_config.o enkf_state.o gen_kw_config.o enkf_defaults.o plot_config.o +summary_config.o : summary_config.c summary_config.h enkf_types.o enkf_util.o enkf_macros.h active_list.o +meas_matrix.o : meas_matrix.c meas_matrix.h meas_vector.o +gen_data_config.o : gen_data_config.c gen_data_config.h enkf_macros.h enkf_types.o gen_data_common.h gen_data_active.o active_list.o +active_list.o : active_list.c active_list.h enkf_macros.h +gen_kw.o : gen_kw.c gen_kw.h enkf_types.o enkf_util.o scalar.o enkf_macros.h gen_kw_common.h gen_kw_config.o +gen_obs.o : gen_obs.c gen_obs.h enkf_util.o enkf_types.o enkf_macros.h meas_vector.o obs_data.o gen_data.o gen_common.o gen_obs_active.o active_list.o +meas_vector.o : meas_vector.c meas_vector.h enkf_util.o +scalar_config.o : scalar_config.c scalar_config.h enkf_util.o enkf_macros.h trans_func.o active_list.o +enkf_tui_run.o : enkf_tui_run.c enkf_tui_run.h enkf_main.o enkf_fs.o enkf_sched.o ensemble_config.o enkf_analysis.o enkf_tui_util.o enkf_tui_fs.o +enkf_serialize.o : enkf_serialize.c enkf_serialize.h enkf_types.o active_list.o +sqlite3.o : sqlite3.c sqlite3.h +gen_obs_active.o : gen_obs_active.c gen_obs_active.h enkf_macros.h +basic_driver.o : basic_driver.c basic_driver.h fs_types.o +enkf_node.o : enkf_node.c enkf_node.h enkf_config_node.o field.o summary.o ecl_static_kw.o gen_kw.o gen_data.o enkf_serialize.o +ecl_config.o : ecl_config.c ecl_config.h enkf_util.o enkf_defaults.o +trans_func.o : trans_func.c trans_func.h +gen_common.o : gen_common.c gen_common.h gen_data_config.o +local_config.o : local_config.c local_config.h local_ministep.o local_updatestep.o +obs_data.o : obs_data.c obs_data.h enkf_util.o meas_matrix.o +plain_driver_index.o : plain_driver_index.c plain_driver_index.h fs_types.o plain_driver_common.o basic_driver.o +field.o : field.c field.h field_config.o enkf_serialize.o +plot_config.o : plot_config.c plot_config.h enkf_defaults.o +ecl_static_kw.o : ecl_static_kw.c ecl_static_kw.h enkf_util.o enkf_macros.h +local_ministep.o : local_ministep.c local_ministep.h active_list.o enkf_macros.h +enkf_tui_misc.o : enkf_tui_misc.c enkf_tui_misc.h enkf_types.o enkf_main.o enkf_state.o +enkf_analysis.o : enkf_analysis.c enkf_analysis.h meas_matrix.o obs_data.o analysis_config.o enkf_util.o +gen_kw_config.o : gen_kw_config.c gen_kw_config.h enkf_util.o enkf_macros.h trans_func.o scalar_config.o enkf_defaults.o gen_kw_common.h +analysis_config.o : analysis_config.c analysis_config.h enkf_types.o enkf_defaults.o diff --git a/libres/lib/enkf/ecl_config.cpp b/libres/lib/enkf/ecl_config.cpp new file mode 100644 index 00000000000..011e5115d62 --- /dev/null +++ b/libres/lib/enkf/ecl_config.cpp @@ -0,0 +1,608 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'ecl_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 +#include + + +#include + +#include +#include +#include + +#include +#include +#include + +/** + This file implements a struct which holds configuration information + needed to run ECLIPSE. + + Pointers to the fields in this structure are passed on to e.g. the + enkf_state->shared_info object, but this struct is the *OWNER* of + this information, and hence responsible for booting and deleting + these objects. + + Observe that the distinction of what goes in model_config, and what + goes in ecl_config is not entirely clear. + */ + +struct ecl_config_struct +{ + ecl_io_config_type * io_config; /* This struct contains information of whether the eclipse files should be formatted|unified|endian_fliped */ + char * data_file; /* Eclipse data file. */ + time_t start_date; /* The start date of the ECLIPSE simulation - parsed from the data_file. */ + time_t end_date; /* An optional date value which can be used to check if the ECLIPSE simulation has been 'long enough'. */ + ecl_refcase_list_type * refcase_list; + ecl_grid_type * grid; /* The grid which is active for this model. */ + char * schedule_prediction_file; /* Name of schedule prediction file - observe that this is internally handled as a gen_kw node. */ + int last_history_restart; + bool can_restart; /* Have we found the tag in the data file? */ + bool have_eclbase; + int num_cpu; /* We should parse the ECLIPSE data file and determine how many cpus this eclipse file needs. */ + ert_ecl_unit_enum unit_system; /* Either metric, field or lab */ +}; + +/*****************************************************************/ + +/** + With this function we try to determine whether ECLIPSE is active + for this case, i.e. if ECLIPSE is part of the forward model. This + should ideally be inferred from the FORWARD model, but what we do + here is just to check if the core field ->eclbase or ->data_file + have been set. If they are both equal to NULL we assume that + ECLIPSE is not active and return false, otherwise we return true. + */ + +bool ecl_config_active(const ecl_config_type * config) +{ + if (config->have_eclbase) + return true; + + if (config->data_file) + return true; + + return false; +} + + /** + Could look up the sched_file instance directly - because the + ecl_config will never be the owner of a file with predictions. + */ + +int ecl_config_get_last_history_restart(const ecl_config_type * ecl_config) +{ + return ecl_config->last_history_restart; +} + +bool ecl_config_can_restart(const ecl_config_type * ecl_config) +{ + return ecl_config->can_restart; +} + +void ecl_config_assert_restart(const ecl_config_type * ecl_config) +{ + if (!ecl_config_can_restart(ecl_config)) + { + fprintf(stderr, "** Warning - tried to restart case which is not properly set up for restart.\n"); + fprintf(stderr, "** Need in datafile and INIT_SECTION keyword in config file.\n"); + util_exit("%s: exiting \n", __func__); + } +} + +ui_return_type * ecl_config_validate_data_file(const ecl_config_type * ecl_config, const char * data_file) { + if (util_file_exists(data_file)) + return ui_return_alloc(UI_RETURN_OK); + else { + ui_return_type * ui_return = ui_return_alloc(UI_RETURN_FAIL); + char * error_msg = util_alloc_sprintf("File not found:%s" , data_file); + ui_return_add_error(ui_return , error_msg); + free( error_msg ); + return ui_return; + } +} + +void ecl_config_set_data_file(ecl_config_type * ecl_config, const char * data_file) { + ecl_config->data_file = util_realloc_string_copy(ecl_config->data_file, data_file); + { + FILE * stream = util_fopen(ecl_config->data_file, "r"); + basic_parser_type * parser = basic_parser_alloc(NULL, NULL, NULL, NULL, "--", "\n"); + const char * init_tag = DEFAULT_START_TAG "INIT" DEFAULT_END_TAG; + + ecl_config->can_restart = basic_parser_fseek_string(parser, stream, init_tag, false, true); + + basic_parser_free(parser); + fclose(stream); + } + ecl_config->start_date = ecl_util_get_start_date(ecl_config->data_file); + ecl_config->num_cpu = ecl_util_get_num_cpu(ecl_config->data_file); + ecl_config->unit_system = ecl_util_get_unit_set(ecl_config->data_file); +} + + +const char * ecl_config_get_data_file(const ecl_config_type * ecl_config) +{ + return ecl_config->data_file; +} + +time_t ecl_config_get_start_date(const ecl_config_type * ecl_config) +{ + return ecl_config->start_date; +} + +time_t ecl_config_get_end_date(const ecl_config_type * ecl_config) +{ + return ecl_config->end_date; +} + +static void ecl_config_set_end_date(ecl_config_type * ecl_config, time_t end_date) +{ + ecl_config->end_date = end_date; +} + +int ecl_config_get_num_cpu(const ecl_config_type * ecl_config) +{ + return ecl_config->num_cpu; +} + +const char * ecl_config_get_schedule_prediction_file(const ecl_config_type * ecl_config) +{ + return ecl_config->schedule_prediction_file; +} + +/** + Observe: The real schedule prediction functionality is implemented + as a special GEN_KW node in ensemble_config. + */ + +void ecl_config_set_schedule_prediction_file(ecl_config_type * ecl_config, const char * schedule_prediction_file) +{ + ecl_config->schedule_prediction_file = util_realloc_string_copy(ecl_config->schedule_prediction_file, schedule_prediction_file); +} + +ui_return_type * ecl_config_validate_eclbase(const ecl_config_type * ecl_config, const char * eclbase_fmt) { + if (ecl_util_valid_basename_fmt(eclbase_fmt)) + return ui_return_alloc(UI_RETURN_OK); + else { + ui_return_type * ui_return = ui_return_alloc(UI_RETURN_FAIL); + { + char * error_msg = util_alloc_sprintf("The format string: %s was invalid as ECLBASE format", eclbase_fmt); + ui_return_add_error(ui_return, error_msg); + free(error_msg); + } + ui_return_add_help(ui_return , "The eclbase format must have all characters in the same case,"); + ui_return_add_help(ui_return , "in addition it can contain a %d specifier which will be"); + ui_return_add_help(ui_return , "with the realization number."); + + return ui_return; + } +} + + +/** + Can be called with @refcase == NULL - which amounts to clearing the + current refcase. +*/ +bool ecl_config_load_refcase(ecl_config_type * ecl_config, const char * refcase) +{ + return ecl_refcase_list_set_default(ecl_config->refcase_list, refcase); +} + + +ui_return_type * ecl_config_validate_refcase( const ecl_config_type * ecl_config , const char * refcase) { + if (ecl_sum_case_exists( refcase )) + return ui_return_alloc( UI_RETURN_OK ); + else { + ui_return_type * ui_return = ui_return_alloc( UI_RETURN_FAIL ); + char * error_msg = util_alloc_sprintf( "Could not load summary case from:%s \n",refcase); + ui_return_add_error( ui_return , error_msg ); + free( error_msg ); + return ui_return; + } +} + + +/** + Will return NULL if no refcase is set. + */ +const char * ecl_config_get_refcase_name(const ecl_config_type * ecl_config) +{ + const ecl_sum_type * refcase = ecl_refcase_list_get_default(ecl_config->refcase_list); + if (refcase == NULL ) + return NULL ; + else + return ecl_sum_get_case(refcase); + +} + +static ecl_config_type * ecl_config_alloc_empty(void) +{ + ecl_config_type * ecl_config = new ecl_config_type(); + + ecl_config->io_config = ecl_io_config_alloc(DEFAULT_FORMATTED, DEFAULT_UNIFIED, DEFAULT_UNIFIED); + ecl_config->have_eclbase = false; + ecl_config->num_cpu = 1; /* This must get a valid default in case no ECLIPSE datafile is provided. */ + ecl_config->unit_system = ECL_METRIC_UNITS; + ecl_config->data_file = NULL; + ecl_config->grid = NULL; + ecl_config->can_restart = false; + ecl_config->start_date = -1; + ecl_config->end_date = -1; + ecl_config->schedule_prediction_file = NULL; + ecl_config->refcase_list = ecl_refcase_list_alloc(); + + return ecl_config; +} + +ecl_config_type * ecl_config_alloc(const config_content_type * config_content) { + ecl_config_type * ecl_config = ecl_config_alloc_empty(); + + if(config_content) + ecl_config_init(ecl_config, config_content); + + return ecl_config; +} + +ecl_config_type * ecl_config_alloc_full(bool have_eclbase, + char * data_file, + ecl_grid_type * grid, + char * refcase_default, + stringlist_type * ref_case_list, + time_t end_date, + char * sched_prediction_file + ) { + ecl_config_type * ecl_config = ecl_config_alloc_empty(); + ecl_config->have_eclbase = have_eclbase; + ecl_config->grid = grid; + if (data_file != NULL) { + ecl_config_set_data_file(ecl_config, data_file); + } + + for (int i = 0; i < stringlist_get_size(ref_case_list); i++) + { + ecl_refcase_list_add_matching(ecl_config->refcase_list, stringlist_safe_iget(ref_case_list, i)); + } + if (refcase_default) + ecl_refcase_list_set_default(ecl_config->refcase_list, refcase_default); + + ecl_config->end_date = end_date; + if (sched_prediction_file) + ecl_config->schedule_prediction_file = util_alloc_string_copy(sched_prediction_file); + + return ecl_config; +} + +static void handle_has_eclbase_key(ecl_config_type * ecl_config, + const config_content_type * config) { + /* + The eclbase is not internalized here; here we only flag that the + ECLBASE keyword has been present in the configuration. The + actualt value is internalized as a job_name in the model_config. + */ + + if (config_content_has_item(config, ECLBASE_KEY)) { + ui_return_type * ui_return = ecl_config_validate_eclbase(ecl_config, config_content_iget(config, ECLBASE_KEY, 0, 0)); + if (ui_return_get_status(ui_return) == UI_RETURN_OK) + ecl_config->have_eclbase = true; + else + util_abort("%s: failed to set eclbase format. Error:%s\n", __func__ , ui_return_get_last_error(ui_return)); + ui_return_free(ui_return); + } +} + +static void handle_has_data_file_key(ecl_config_type * ecl_config, + const config_content_type * config) { + const char * data_file = config_content_get_value_as_abspath(config, + DATA_FILE_KEY); + ui_return_type * ui_return = ecl_config_validate_data_file(ecl_config, + data_file); + if (ui_return_get_status( ui_return ) == UI_RETURN_OK) + ecl_config_set_data_file(ecl_config, data_file); + else + util_abort("%s: problem setting ECLIPSE data file (%s)\n", + __func__, ui_return_get_last_error(ui_return)); + ui_return_free(ui_return); +} + +static void handle_has_grid_key(ecl_config_type * ecl_config, + const config_content_type * config) { + const char * grid_file = config_content_get_value_as_abspath(config, GRID_KEY); + + ui_return_type * ui_return = ecl_config_validate_grid(ecl_config, grid_file); + if (ui_return_get_status(ui_return) == UI_RETURN_OK) + ecl_config_set_grid(ecl_config, grid_file ); + else + util_abort("%s: failed to set grid file:%s Error:%s \n", + __func__, + grid_file, + ui_return_get_last_error(ui_return)); + + ui_return_free(ui_return); +} + + +static void handle_has_refcase_key(ecl_config_type * ecl_config, + const config_content_type * config) { + const char * refcase_path = config_content_get_value_as_abspath(config, + REFCASE_KEY); + + if (!ecl_config_load_refcase(ecl_config, refcase_path)) + fprintf(stderr, "** Warning: loading refcase:%s failed \n", refcase_path); +} + +static void handle_has_refcase_list_key(ecl_config_type * ecl_config, + const config_content_type * config) { + config_content_item_type * item = config_content_get_item(config, REFCASE_LIST_KEY); + for (int i = 0; i < config_content_item_get_size(item); i++) { + config_content_node_type * node = config_content_item_iget_node(item, i); + for (int j = 0; j < config_content_node_get_size(node); j++) { + const char * refcase_list_path = config_content_node_iget_as_abspath(node, j); + ecl_refcase_list_add_matching(ecl_config->refcase_list, refcase_list_path); + } + } +} + +static void handle_has_end_date_key(ecl_config_type * ecl_config, + const config_content_type * config) { + const char * date_string = config_content_get_value(config, END_DATE_KEY); + time_t end_date; + if (util_sscanf_date_utc(date_string, &end_date)) + ecl_config_set_end_date(ecl_config, end_date); + else + fprintf(stderr, "** WARNING **: Failed to parse %s as a date - should be in format dd/mm/yyyy \n", date_string); +} + +static void handle_has_schedule_prediction_file_key(ecl_config_type * ecl_config, + const config_content_type * config) { + const config_content_item_type * pred_item = config_content_get_item( + config, + SCHEDULE_PREDICTION_FILE_KEY + ); + + config_content_node_type * pred_node = config_content_item_get_last_node(pred_item); + const char * template_file = config_content_node_iget_as_path(pred_node, 0); + ecl_config_set_schedule_prediction_file(ecl_config, template_file); +} + + + + + +void ecl_config_init(ecl_config_type * ecl_config, const config_content_type * config) +{ + if (config_content_has_item(config, ECLBASE_KEY)) + handle_has_eclbase_key(ecl_config, config); + + if (config_content_has_item(config, DATA_FILE_KEY)) + handle_has_data_file_key(ecl_config, config); + + if (config_content_has_item(config, GRID_KEY)) + handle_has_grid_key(ecl_config, config); + + if (config_content_has_item(config, REFCASE_KEY)) + handle_has_refcase_key(ecl_config, config); + + if (config_content_has_item(config, REFCASE_LIST_KEY)) + handle_has_refcase_list_key(ecl_config, config); + + + if (ecl_config->can_restart) + fprintf(stderr, + "** Warning: The ECLIPSE data file contains a section, the support\n" + " for this functionality has been removed. libres will not\n" + " be able to properly initialize the ECLIPSE MODEL.\n" + ); + /** + This is a hard error - the datafile contains , however + the config file does NOT contain INIT_SECTION, i.e. we have + no information to fill in for the section. This case + will not be able to initialize an ECLIPSE model, and that is + broken behaviour. + */ + + /* + The user has not supplied a INIT_SECTION keyword whatsoever, + this essentially means that we can not restart - because: + + 1. The EQUIL section must be inlined in the DATAFILE without any + special markup. + + 2. ECLIPSE will fail hard if the datafile contains both an EQUIL + section and a restart statement, and when we have not marked + the EQUIL section specially with the INIT_SECTION keyword it + is impossible for ERT to dynamically change between a + datafile with initialisation and a datafile for restart. + + IFF the user has no intentitions of any form of restart, this is + perfectly legitemate. + */ + if (config_content_has_item(config, END_DATE_KEY)) + handle_has_end_date_key(ecl_config, config); + + if (config_content_has_item(config, SCHEDULE_PREDICTION_FILE_KEY)) + handle_has_schedule_prediction_file_key(ecl_config, config); +} + + + +void ecl_config_free(ecl_config_type * ecl_config) +{ + ecl_io_config_free(ecl_config->io_config); + free(ecl_config->data_file); + free(ecl_config->schedule_prediction_file); + + if (ecl_config->grid != NULL ) + ecl_grid_free(ecl_config->grid); + + ecl_refcase_list_free(ecl_config->refcase_list); + + delete ecl_config; +} + + +ecl_grid_type * ecl_config_get_grid(const ecl_config_type * ecl_config) +{ + return ecl_config->grid; +} + +const char * ecl_config_get_gridfile(const ecl_config_type * ecl_config) +{ + if (ecl_config->grid == NULL ) + return NULL ; + else + return ecl_grid_get_name(ecl_config->grid); +} + +/** + The ecl_config object isolated supports run-time changing of the + grid, however this does not (in general) apply to the system as a + whole. Other objects which internalize pointers (i.e. field_config + objects) to an ecl_grid_type instance will be left with dangling + pointers; and things will probably die an ugly death. So - changing + grid runtime should be done with extreme care. +*/ + +void ecl_config_set_grid(ecl_config_type * ecl_config, const char * grid_file) +{ + if (ecl_config->grid != NULL ) + ecl_grid_free(ecl_config->grid); + ecl_config->grid = ecl_grid_alloc(grid_file); +} + +ui_return_type * ecl_config_validate_grid( const ecl_config_type * ecl_config , const char * grid_file ) { + ui_return_type * ui_return; + if (util_file_exists( grid_file )) { + ecl_file_enum file_type = ecl_util_get_file_type( grid_file , NULL , NULL ); + if ((file_type == ECL_EGRID_FILE) || (file_type == ECL_GRID_FILE)) + ui_return = ui_return_alloc( UI_RETURN_OK ); + else { + ui_return = ui_return_alloc( UI_RETURN_FAIL ); + ui_return_add_error( ui_return , "Input argument is not a GRID/EGRID file"); + } + } else { + ui_return = ui_return_alloc( UI_RETURN_FAIL ); + ui_return_add_error( ui_return , "Input argument does not exist."); + } + return ui_return; +} + + + +ecl_refcase_list_type * ecl_config_get_refcase_list(const ecl_config_type * ecl_config) +{ + return ecl_config->refcase_list; +} + +const ecl_sum_type * ecl_config_get_refcase(const ecl_config_type * ecl_config) +{ + return ecl_refcase_list_get_default(ecl_config->refcase_list); +} + +bool ecl_config_has_refcase(const ecl_config_type * ecl_config) +{ + const ecl_sum_type * refcase = ecl_config_get_refcase(ecl_config); + if (refcase) + return true; + else + return false; +} + +bool ecl_config_get_formatted(const ecl_config_type * ecl_config) +{ + return ecl_io_config_get_formatted(ecl_config->io_config); +} +bool ecl_config_get_unified_restart(const ecl_config_type * ecl_config) +{ + return ecl_io_config_get_unified_restart(ecl_config->io_config); +} + +bool ecl_config_have_eclbase(const ecl_config_type * ecl_config) { + return ecl_config->have_eclbase; +} + + +void ecl_config_add_config_items(config_parser_type * config) +{ + config_schema_item_type * item; + + /* + Observe that SCHEDULE_PREDICTION_FILE - which is implemented as a + GEN_KW is added in ensemble_config.c + */ + + item = config_add_schema_item(config, ECLBASE_KEY, false); + config_schema_item_set_argc_minmax(item, 1, 1); + + item = config_add_schema_item(config, DATA_FILE_KEY, false); + config_schema_item_set_argc_minmax(item, 1, 1); + config_schema_item_iset_type(item, 0, CONFIG_EXISTING_PATH); + + item = config_add_schema_item(config, REFCASE_KEY, false); + config_schema_item_set_argc_minmax(item, 1, 1); + config_schema_item_iset_type(item, 0, CONFIG_PATH); + + item = config_add_schema_item(config, REFCASE_LIST_KEY, false); + config_schema_item_set_default_type(item, CONFIG_PATH); + + item = config_add_schema_item(config, GRID_KEY, false); + config_schema_item_set_argc_minmax(item, 1, 1); + config_schema_item_iset_type(item, 0, CONFIG_EXISTING_PATH); + + item = config_add_schema_item(config, END_DATE_KEY, false); + config_schema_item_set_argc_minmax(item, 1, 1); +} + + +/* Units as specified in the ECLIPSE technical manual */ +const char * ecl_config_get_depth_unit(const ecl_config_type * ecl_config) +{ + switch(ecl_config->unit_system) { + case ECL_METRIC_UNITS: + return "M"; + case ECL_FIELD_UNITS: + return "FT"; + case ECL_LAB_UNITS: + return "CM"; + default: + util_abort("%s: unit system enum value:%d not recognized \n",__func__ , ecl_config->unit_system); + return NULL; + } +} + + +const char * ecl_config_get_pressure_unit(const ecl_config_type * ecl_config) +{ + switch(ecl_config->unit_system) { + case ECL_METRIC_UNITS: + return "BARSA"; + case ECL_FIELD_UNITS: + return "PSIA"; + case ECL_LAB_UNITS: + return "ATMA"; + default: + util_abort("%s: unit system enum value:%d not recognized \n",__func__ , ecl_config->unit_system); + return NULL; + } +} diff --git a/libres/lib/enkf/ecl_refcase_list.cpp b/libres/lib/enkf/ecl_refcase_list.cpp new file mode 100644 index 00000000000..9eb97c8d0d9 --- /dev/null +++ b/libres/lib/enkf/ecl_refcase_list.cpp @@ -0,0 +1,394 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'ecl_refcase_list.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 + + +/* + This file implements a quite simple collection of ecl_sum instances, + with the following twists: + + o The default case is special in several ways: + + - It is added with the ecl_refcase_list_set_default() function. + + - The default case is always the first case, i.e. iget(0) will + return the default case. + + - When setting the default we verify that it can be loaded, and + the thing fails if not. That is in contrast to the other cases + where the actual summary loading is lazy and on demand. + + o The collection has an internal dictionary which ensures that the + same case is not added twice. + + o When calling iget( ) the default case will come as index 0, and + the remaining cases will come in util_strcmp_int() ordering. + + o Observe that the functions in this module return NULL quite + liberally: + + - If you have added a case which does not exist you will get + NULL when you (at a later stage) try to access the ecl_sum + instance. + + - If you ask for the default case before one has been set you + will get NULL. + */ + + +#define SUM_PAIR_TYPE_ID 665109971 + +typedef struct sum_pair_struct { + UTIL_TYPE_ID_DECLARATION; + char * case_name; // This should be (path)/basename - no extension + ecl_sum_type * ecl_sum; +} sum_pair_type; + + + +struct ecl_refcase_list_struct { + sum_pair_type * default_case; + hash_type * case_dict; + vector_type * case_list; /* This is created, and sorted, on demand when we do indexed lookup of cases. */ + bool sorted; +}; + +/*****************************************************************/ + + + +static sum_pair_type * sum_pair_alloc( const char * case_name , bool strict_load) { + ecl_sum_type * ecl_sum = NULL; + char * path = NULL; + char * basename = NULL; + + util_alloc_file_components( case_name , &path , &basename , NULL); + if (basename != NULL) { + char * use_case = util_alloc_filename( path , basename , NULL); + if (strict_load) + ecl_sum = ecl_sum_fread_alloc_case( use_case , ":"); + + free( path ); + free( basename ); + if (strict_load && (ecl_sum == NULL)) { + free( use_case ); + return NULL; + } + + + { + sum_pair_type * pair = (sum_pair_type *)util_malloc( sizeof * pair ); + UTIL_TYPE_ID_INIT( pair , SUM_PAIR_TYPE_ID ); + pair->case_name = use_case; + pair->ecl_sum = ecl_sum; + return pair; + } + } else { + free( path ); + free( basename ); + return NULL; + } +} + + +static UTIL_SAFE_CAST_FUNCTION( sum_pair , SUM_PAIR_TYPE_ID ); +static UTIL_SAFE_CAST_FUNCTION_CONST( sum_pair , SUM_PAIR_TYPE_ID ); + +const ecl_sum_type * sum_pair_get_ecl_sum( sum_pair_type * sum_pair ) { + if (sum_pair->ecl_sum == NULL) + sum_pair->ecl_sum = ecl_sum_fread_alloc_case( sum_pair->case_name , ":"); + return sum_pair->ecl_sum; +} + + +static void sum_pair_free( sum_pair_type * sum_pair ) { + free( sum_pair->case_name ); + if (sum_pair->ecl_sum != NULL) + ecl_sum_free( sum_pair->ecl_sum ); + free( sum_pair ); +} + + +static void sum_pair_free__( void * arg ) { + sum_pair_type * pair = sum_pair_safe_cast( arg ); + sum_pair_free( pair ); +} + + + + +static int sum_pair_cmp( const void * arg1 , const void * arg2) { + const sum_pair_type * pair1 = sum_pair_safe_cast_const( arg1 ); + const sum_pair_type * pair2 = sum_pair_safe_cast_const( arg2 ); + + return util_strcmp_int( pair1->case_name , pair2->case_name ); +} + +/*****************************************************************/ + + +ecl_refcase_list_type * ecl_refcase_list_alloc( ) { + ecl_refcase_list_type * refcase_list = (ecl_refcase_list_type *)util_malloc( sizeof * refcase_list ); + refcase_list->default_case = NULL; + refcase_list->case_list = vector_alloc_new(); + refcase_list->case_dict = hash_alloc(); + refcase_list->sorted = false; + return refcase_list; +} + + + +void ecl_refcase_list_free( ecl_refcase_list_type * refcase_list ) { + vector_free( refcase_list->case_list ); + hash_free( refcase_list->case_dict ); + free( refcase_list ); +} + + + +const ecl_sum_type * ecl_refcase_list_get_default( ecl_refcase_list_type * refcase_list ) { + const ecl_sum_type * return_value = NULL; + if (refcase_list->default_case) + return_value = sum_pair_get_ecl_sum( refcase_list->default_case ); + + return return_value; +} + + +bool ecl_refcase_list_has_default( ecl_refcase_list_type * refcase_list ) { + if (refcase_list->default_case) + return true; + else + return false; +} + + +static void ecl_refcase_list_del_default( ecl_refcase_list_type * refcase_list ) { + sum_pair_type * default_pair = refcase_list->default_case; + if (default_pair) { + hash_del( refcase_list->case_dict , default_pair->case_name ); + refcase_list->default_case = NULL; + } +} + + +/* + If a valid refcase has been set already, and this fails, nothing happens. +*/ + +bool ecl_refcase_list_set_default( ecl_refcase_list_type * refcase_list , const char * default_case) { + + if (default_case) { + sum_pair_type * default_pair = sum_pair_alloc( default_case , true ); + if (default_pair) { + ecl_refcase_list_del_default( refcase_list ); + refcase_list->default_case = default_pair; + + hash_insert_hash_owned_ref( refcase_list->case_dict , default_pair->case_name , default_pair , sum_pair_free__); + refcase_list->sorted = false; + return true; + } else + return false; + } else { + ecl_refcase_list_del_default( refcase_list ); + return true; + } +} + + + + +/** + Will sort the list and remove all elements which can not be loaded. +*/ + +static void ecl_refcase_list_assert_clean( ecl_refcase_list_type * refcase_list ) { + if (!refcase_list->sorted) { + vector_free( refcase_list->case_list ); + refcase_list->case_list = vector_alloc_new(); + + { + stringlist_type * tmp_list = hash_alloc_stringlist( refcase_list->case_dict ); + sum_pair_type * default_case = refcase_list->default_case; + int i; + + for (i =0; i < stringlist_get_size( tmp_list ); i++) { + const char * casename = stringlist_iget( tmp_list , i ); + bool normal_case = true; + + if (default_case && util_string_equal( casename , default_case->case_name)) + normal_case = false; + + if (normal_case) { + sum_pair_type * pair = (sum_pair_type *)hash_get( refcase_list->case_dict , casename); + const ecl_sum_type * ecl_sum = sum_pair_get_ecl_sum( pair ); + + if (ecl_sum) + vector_append_ref( refcase_list->case_list , pair); + else + hash_del( refcase_list->case_dict , casename ); + + } + } + stringlist_free( tmp_list ); + } + + vector_sort( refcase_list->case_list , sum_pair_cmp ); + refcase_list->sorted = true; + } +} + + +static sum_pair_type * ecl_refcase_list_get_pair( ecl_refcase_list_type * refcase_list , const char * case_name) { + ecl_refcase_list_assert_clean( refcase_list ); + { + if (hash_has_key( refcase_list->case_dict , case_name)) + return (sum_pair_type *) hash_get( refcase_list->case_dict , case_name ); + else + return NULL; + } +} + + +static sum_pair_type * ecl_refcase_list_iget_pair( ecl_refcase_list_type * refcase_list , int index) { + ecl_refcase_list_assert_clean( refcase_list ); + { + int index_offset = refcase_list->default_case ? 1 : 0; + index -= index_offset; + + if (index < 0) + return refcase_list->default_case; + else + return (sum_pair_type *) vector_safe_iget( refcase_list->case_list , index); + } +} + + +int ecl_refcase_list_get_size(ecl_refcase_list_type * refcase_list ) { + ecl_refcase_list_assert_clean( refcase_list ); + { + int size = hash_get_size( refcase_list->case_dict ); + return size; + } +} + + + +const ecl_sum_type * ecl_refcase_list_iget_case( ecl_refcase_list_type * refcase_list , int index) { + sum_pair_type * pair = ecl_refcase_list_iget_pair( refcase_list , index ); + if (pair) + return sum_pair_get_ecl_sum( pair ); + else + return NULL; +} + + +const ecl_sum_type * ecl_refcase_list_get_case( ecl_refcase_list_type * refcase_list , const char * case_name) { + sum_pair_type * pair = ecl_refcase_list_get_pair( refcase_list , case_name ); + if (pair) + return sum_pair_get_ecl_sum( pair ); + else + return NULL; +} + + +bool ecl_refcase_list_has_case( ecl_refcase_list_type * refcase_list , const char * case_name) { + const sum_pair_type * pair = ecl_refcase_list_get_pair( refcase_list , case_name ); + if (pair) + return true; + else + return false; +} + + + +const char * ecl_refcase_list_iget_pathcase( ecl_refcase_list_type * refcase_list , int index) { + const ecl_sum_type * ecl_sum = ecl_refcase_list_iget_case( refcase_list , index ); + if (ecl_sum) + return ecl_sum_get_case( ecl_sum ); + else + return NULL; +} + + + +int ecl_refcase_list_add_case( ecl_refcase_list_type * refcase_list , const char * case_name) { + sum_pair_type * pair = sum_pair_alloc( case_name , false ); + if (pair) { + if (hash_has_key( refcase_list->case_dict , pair->case_name)) { + sum_pair_free( pair ); + return 0; + } else { + hash_insert_hash_owned_ref( refcase_list->case_dict , pair->case_name , pair , sum_pair_free__); + refcase_list->sorted = false; + return 1; + } + } else + return 0; +} + +/* + The glob_string pattern must (for all practical purposes) end in an + explicit extension: + + - If it ends without an extension there will be zero mathces. It + - it ends with a '*' there will be a hell-of-a-lot of duplicate + matches. + + If the input glob string does not have an explicit extension we add + .*SMSPEC for the matching purpose. +*/ + + +int ecl_refcase_list_add_matching( ecl_refcase_list_type * refcase_list , const char * __glob_string) { + int count = 0; + char * glob_string; + + { + char * glob_ext; + util_alloc_file_components( __glob_string , NULL , NULL , &glob_ext); + if (glob_ext == NULL) + glob_string = util_alloc_filename(NULL , __glob_string , "*SMSPEC"); + else { + glob_string = util_alloc_string_copy( __glob_string ); + free( glob_ext ); + } + } + + { + stringlist_type * case_list = stringlist_alloc_new(); + int i; + stringlist_select_matching( case_list , glob_string ); + for (i=0; i < stringlist_get_size( case_list ); i++) { + count += ecl_refcase_list_add_case( refcase_list , stringlist_iget( case_list , i ) ); + } + stringlist_free( case_list ); + } + + free( glob_string ); + return count; +} + diff --git a/libres/lib/enkf/enkf_analysis.cpp b/libres/lib/enkf/enkf_analysis.cpp new file mode 100644 index 00000000000..913135d81b3 --- /dev/null +++ b/libres/lib/enkf/enkf_analysis.cpp @@ -0,0 +1,190 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'enkf_analysis.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 + + + + + + +/*****************************************************************/ + +void enkf_analysis_fprintf_obs_summary(const obs_data_type * obs_data , const meas_data_type * meas_data , const int_vector_type * step_list , const char * ministep_name , FILE * stream ) { + const char * float_fmt = "%15.3f"; + bool local_inactive_obs; + local_inactive_obs = false; + + fprintf(stream , "===============================================================================================================================\n"); + fprintf(stream , "Report step...: %04d",int_vector_iget( step_list , 0)); + if (int_vector_size( step_list ) == 1) + fprintf(stream , "\n"); + else + fprintf(stream , " - %04d \n",int_vector_get_last( step_list )); + + + fprintf(stream , "Ministep......: %s \n",ministep_name); + fprintf(stream , "-------------------------------------------------------------------------------------------------------------------------------\n"); + { + char * obs_fmt = util_alloc_sprintf(" %%-3d : %%-32s %s +/- %s" , float_fmt , float_fmt); + char * sim_fmt = util_alloc_sprintf(" %s +/- %s \n" , float_fmt , float_fmt); + + fprintf(stream , " Observed history | Simulated data \n"); + fprintf(stream , "-------------------------------------------------------------------------------------------------------------------------------\n"); + + { + int block_nr; + int obs_count = 1; /* Only for printing */ + for (block_nr =0; block_nr < obs_data_get_num_blocks( obs_data ); block_nr++) { + const obs_block_type * obs_block = obs_data_iget_block_const( obs_data , block_nr); + meas_block_type * meas_block = meas_data_iget_block( meas_data , block_nr ); + const char * obs_key = obs_block_get_key( obs_block ); + + for (int iobs = 0; iobs < obs_block_get_size( obs_block ); iobs++) { + active_type active_mode = obs_block_iget_active_mode( obs_block , iobs ); + const char * print_key; + if (iobs == 0) + print_key = obs_key; + else + print_key = " ..."; + + fprintf(stream , obs_fmt , obs_count , print_key , obs_block_iget_value( obs_block , iobs ) , obs_block_iget_std( obs_block , iobs )); + + if (active_mode == ACTIVE) + fprintf(stream , " Active |"); + else if (active_mode == DEACTIVATED) + fprintf(stream , " Inactive |"); + else if (active_mode == LOCAL_INACTIVE) { + fprintf(stream , " Inactive* |"); + local_inactive_obs = true; + } + else if (active_mode == MISSING) + fprintf(stream , " Missing |"); + else + util_abort("%s: enum_value:%d not handled - internal error\n" , __func__ , active_mode); + + double simulated_value; + double simulated_std; + if ((active_mode == MISSING) || (active_mode == LOCAL_INACTIVE)) { + simulated_value = NAN; + simulated_std = NAN; + } + else { + simulated_value = meas_block_iget_ens_mean( meas_block , iobs ); + simulated_std = meas_block_iget_ens_std( meas_block , iobs ); + } + fprintf(stream , sim_fmt, simulated_value, simulated_std); + obs_count++; + } + } + } + + free( obs_fmt ); + free( sim_fmt ); + } + fprintf(stream , "===============================================================================================================================\n"); + if (local_inactive_obs == true) + fprintf(stream , "* Local inactive\n"); + fprintf(stream , "\n\n\n"); +} + + + + +void enkf_analysis_deactivate_outliers(obs_data_type * obs_data , meas_data_type * meas_data , double std_cutoff , double alpha, bool verbose) { + for (int block_nr =0; block_nr < obs_data_get_num_blocks( obs_data ); block_nr++) { + obs_block_type * obs_block = obs_data_iget_block( obs_data , block_nr); + meas_block_type * meas_block = meas_data_iget_block( meas_data , block_nr ); + + { + int iobs; + for (iobs =0; iobs < meas_block_get_total_obs_size( meas_block ); iobs++) { + if (meas_block_iget_active( meas_block , iobs )) { + double ens_std = meas_block_iget_ens_std( meas_block , iobs ); + if (ens_std <= std_cutoff) { + /* + De activated because the ensemble has to small + variation for this particular measurement. + */ + obs_block_deactivate( obs_block , iobs , verbose , "No ensemble variation"); + meas_block_deactivate( meas_block , iobs ); + } else { + double ens_mean = meas_block_iget_ens_mean( meas_block , iobs ); + double obs_std = obs_block_iget_std( obs_block , iobs ); + double obs_value = obs_block_iget_value( obs_block , iobs ); + double innov = obs_value - ens_mean; + + /* + Deactivated because the distance between the observed data + and the ensemble prediction is to large. Keeping these + outliers will lead to numerical problems. + */ + + if (std::abs( innov ) > alpha * (ens_std + obs_std)) { + obs_block_deactivate(obs_block , iobs , verbose , "No overlap"); + meas_block_deactivate(meas_block , iobs); + } + } + } + } + } + } +} + +void enkf_analysis_deactivate_std_zero(obs_data_type * obs_data , meas_data_type * meas_data , bool verbose) { + + for (int block_nr =0; block_nr < obs_data_get_num_blocks( obs_data ); block_nr++) { + obs_block_type * obs_block = obs_data_iget_block( obs_data , block_nr); + meas_block_type * meas_block = meas_data_iget_block( meas_data , block_nr ); + + { + int iobs; + for (iobs =0; iobs < meas_block_get_total_obs_size( meas_block ); iobs++) { + if (meas_block_iget_active( meas_block , iobs )) { + double ens_std = meas_block_iget_ens_std( meas_block , iobs ); + if (ens_std <= 0.0) { + /* + De activated because the ensemble has to small + variation for this particular measurement. + */ + obs_block_deactivate( obs_block , iobs , verbose , "No ensemble variation"); + meas_block_deactivate( meas_block , iobs ); + } + } + } + } + } +} + + + + + + + + + diff --git a/libres/lib/enkf/enkf_config_node.cpp b/libres/lib/enkf/enkf_config_node.cpp new file mode 100644 index 00000000000..e23e41dce9e --- /dev/null +++ b/libres/lib/enkf/enkf_config_node.cpp @@ -0,0 +1,1044 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'enkf_config_node.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 +#include + +#define ENKF_CONFIG_NODE_TYPE_ID 776104 + +struct enkf_config_node_struct { + UTIL_TYPE_ID_DECLARATION; + ert_impl_type impl_type; + enkf_var_type var_type; + bool vector_storage; + bool forward_init; /* Should the (parameter) node be initialized by loading results from the Forward model? */ + + bool_vector_type * internalize; /* Should this node be internalized - observe that question of what to internalize is MOSTLY handled at a higher level - without consulting this variable. Can be NULL. */ + stringlist_type * obs_keys; /* Keys of observations which observe this node. */ + char * key; + char * init_file_abs_path; + path_fmt_type * init_file_fmt; /* Format used to create files for initialization. */ + path_fmt_type * enkf_infile_fmt; /* Format used to load in file from forward model - one %d (if present) is replaced with report_step. */ + path_fmt_type * enkf_outfile_fmt; /* Name of file which is written by EnKF, and read by the forward model. */ + void * data; /* This points to the config object of the actual implementation. */ + enkf_node_type * min_std; + char * min_std_file; + + vector_type * container_nodes; + /*****************************************************************/ + /* Function pointers to methods working on the underlying config object. */ + get_data_size_ftype * get_data_size; /* Function pointer to ask the underlying config object of the size - i.e. number of elements. */ + config_free_ftype * freef; +}; + + +UTIL_IS_INSTANCE_FUNCTION( enkf_config_node , ENKF_CONFIG_NODE_TYPE_ID ) + + +static bool enkf_config_node_has_container(const enkf_config_node_type * node , enkf_fs_type * fs , node_id_type node_id) { + bool has_container = true; + for (int inode=0; inode < vector_get_size( node->container_nodes ); inode++) { + enkf_config_node_type * child_node = (enkf_config_node_type *)vector_iget( node->container_nodes , inode ); + bool has_child; + if (child_node->vector_storage) + has_child = enkf_config_node_has_vector( child_node , fs , node_id.iens ); + else + has_child = enkf_config_node_has_node( child_node , fs , node_id ); + + if (!has_child) { + has_container = false; + break; + } + } + return has_container; +} + + + +bool enkf_config_node_has_node( const enkf_config_node_type * node , enkf_fs_type * fs , node_id_type node_id) { + if (node->impl_type == CONTAINER) + return enkf_config_node_has_container( node , fs , node_id ); + else + return enkf_fs_has_node( fs , node->key , node->var_type , node_id.report_step , node_id.iens ); +} + + +bool enkf_config_node_has_vector( const enkf_config_node_type * node , enkf_fs_type * fs , int iens) { + bool has_vector = enkf_fs_has_vector( fs , node->key , node->var_type , iens ); + return has_vector; +} + + + +static enkf_config_node_type * enkf_config_node_alloc__(enkf_var_type var_type, ert_impl_type impl_type, const char * key, bool forward_init) { + enkf_config_node_type * node = (enkf_config_node_type *)util_malloc( sizeof *node ); + UTIL_TYPE_ID_INIT( node , ENKF_CONFIG_NODE_TYPE_ID ); + node->forward_init = forward_init; + node->var_type = var_type; + node->impl_type = impl_type; + node->key = util_alloc_string_copy( key ); + node->container_nodes = vector_alloc_new(); + node->vector_storage = false; + + node->init_file_abs_path = NULL, + node->init_file_fmt = NULL; + node->enkf_infile_fmt = NULL; + node->enkf_outfile_fmt = NULL; + node->internalize = NULL; + node->data = NULL; + node->obs_keys = stringlist_alloc_new(); + node->min_std = NULL; + node->min_std_file = NULL; + + node->get_data_size = NULL; + node->freef = NULL; + + switch(impl_type) { + case(FIELD): + node->freef = field_config_free__; + node->get_data_size = field_config_get_data_size__; + break; + case(GEN_KW): + node->freef = gen_kw_config_free__; + node->get_data_size = gen_kw_config_get_data_size__; + break; + case(SUMMARY): + node->vector_storage = true; + node->freef = summary_config_free__; + node->get_data_size = summary_config_get_data_size__; + break; + case(GEN_DATA): + node->freef = gen_data_config_free__; + node->get_data_size = NULL; + break; + case(SURFACE): + node->freef = surface_config_free__; + node->get_data_size = surface_config_get_data_size__; + break; + case(CONTAINER): + node->freef = container_config_free__; + node->get_data_size = container_config_get_data_size__; + break; + case(EXT_PARAM): + node->freef = ext_param_config_free__; + node->get_data_size = ext_param_config_get_data_size__; + break; + default: + util_abort("%s : invalid implementation type: %d - aborting \n",__func__ , impl_type); + } + return node; +} + + +bool enkf_config_node_vector_storage( const enkf_config_node_type * config_node) { + return config_node->vector_storage; +} + +static bool enkf_config_node_is_valid_FIELD( const enkf_config_node_type * config_node ) { + bool valid = false; + if ( config_node->var_type != INVALID_VAR ) + valid = field_config_is_valid( (const field_config_type * ) config_node->data ); + + + return valid; +} + + +static bool enkf_config_node_is_valid_GEN_DATA( const enkf_config_node_type * config_node ) { + bool valid = gen_kw_config_is_valid( (const gen_kw_config_type * ) config_node->data ); + valid = (valid && (config_node->enkf_outfile_fmt != NULL)); + + return valid; +} + + +void enkf_config_node_update_min_std( enkf_config_node_type * config_node , const char * min_std_file ) { + if (!util_string_equal( config_node->min_std_file , min_std_file )) { + /* The current min_std_file and the new input are different, and + the min_std node must be cleared. */ + if (config_node->min_std != NULL) { + enkf_node_free( config_node->min_std ); + config_node->min_std = NULL; + free( config_node->min_std_file ); + } + } + config_node->min_std_file = util_realloc_string_copy( config_node->min_std_file , min_std_file ); + if (config_node->min_std_file != NULL) { + config_node->min_std = enkf_node_alloc( config_node ); + enkf_node_fload( config_node->min_std , min_std_file ); + } +} + + +static void enkf_config_node_update( enkf_config_node_type * config_node , + const char * initfile_fmt , + const char * enkf_outfile_fmt , + const char * enkf_infile_fmt , + const char * min_std_file ) { + + config_node->init_file_fmt = path_fmt_realloc_path_fmt( config_node->init_file_fmt , initfile_fmt ); + config_node->enkf_infile_fmt = path_fmt_realloc_path_fmt( config_node->enkf_infile_fmt , enkf_infile_fmt ); + config_node->enkf_outfile_fmt = path_fmt_realloc_path_fmt( config_node->enkf_outfile_fmt , enkf_outfile_fmt ); + enkf_config_node_update_min_std( config_node , min_std_file ); +} + + + + +enkf_config_node_type * enkf_config_node_alloc(enkf_var_type var_type, + ert_impl_type impl_type, + bool forward_init , + const char * key , + const char * init_file_fmt , + const char * enkf_outfile_fmt , + const char * enkf_infile_fmt , + void * data) { + + enkf_config_node_type * node = enkf_config_node_alloc__( var_type , impl_type , key , forward_init); + enkf_config_node_update( node , init_file_fmt, enkf_outfile_fmt , enkf_infile_fmt , NULL ); + node->data = data; + return node; +} + + + + + +void enkf_config_node_update_gen_kw( enkf_config_node_type * config_node , + const char * enkf_outfile_fmt , /* The include file created by ERT for the forward model. */ + const char * template_file , + const char * parameter_file , + const char * min_std_file , + const char * init_file_fmt ) { + + /* 1: Update the low level gen_kw_config stuff. */ + gen_kw_config_update( (gen_kw_config_type * ) config_node->data , template_file , parameter_file ); + + /* 2: Update the stuff which is owned by the upper-level enkf_config_node instance. */ + enkf_config_node_update( config_node , init_file_fmt , enkf_outfile_fmt , NULL , min_std_file); +} + + +/** + This will create a new gen_kw_config instance which is NOT yet + valid. +*/ +enkf_config_node_type * enkf_config_node_new_gen_kw( const char * key , const char * tag_fmt , bool forward_init) { + enkf_config_node_type * config_node = enkf_config_node_alloc__( PARAMETER , GEN_KW , key , forward_init); + config_node->data = gen_kw_config_alloc_empty( key , tag_fmt ); + return config_node; +} + +enkf_config_node_type * enkf_config_node_new_surface( const char * key , bool forward_init) { + enkf_config_node_type * config_node = enkf_config_node_alloc__( PARAMETER , SURFACE , key , forward_init); + config_node->data = surface_config_alloc_empty( ); + return config_node; +} + + +void enkf_config_node_update_surface( enkf_config_node_type * config_node , const char * base_surface, const char * init_file_fmt , const char * output_file , const char * min_std_file ) { + + /* 1: Update the data owned by the surface node. */ + surface_config_set_base_surface( (surface_config_type * ) config_node->data , base_surface ); + + /* 2: Update the stuff which is owned by the upper-level enkf_config_node instance. */ + enkf_config_node_update( config_node , init_file_fmt , output_file , NULL , min_std_file); +} + + +/*****************************************************************/ + +enkf_config_node_type * enkf_config_node_alloc_summary( const char * key , load_fail_type load_fail) { + enkf_config_node_type * config_node = enkf_config_node_alloc__( DYNAMIC_RESULT , SUMMARY , key , false); + config_node->data = summary_config_alloc( key , load_fail ); + return config_node; +} + + +enkf_config_node_type * enkf_config_node_alloc_GEN_PARAM( const char * node_key , + bool forward_init , + gen_data_file_format_type input_format , + gen_data_file_format_type output_format , + const char * init_file_fmt , + const char * ert_outfile_fmt) { + + + enkf_config_node_type * config_node = enkf_config_node_alloc__( PARAMETER , GEN_DATA , node_key , forward_init ); + config_node->data = gen_data_config_alloc_GEN_PARAM( node_key , output_format , input_format); + + enkf_config_node_update( config_node , /* Generic update - needs the format settings from the special.*/ + init_file_fmt , + ert_outfile_fmt , + NULL , + NULL ); + + return config_node; +} + + +enkf_config_node_type * enkf_config_node_alloc_GEN_DATA_everest( const char * key , + const char * result_file_fmt, + const int_vector_type * report_steps) { + + if (!gen_data_config_valid_result_format( result_file_fmt )) + return NULL; + + enkf_config_node_type * config_node = enkf_config_node_alloc_GEN_DATA_result( key, ASCII , result_file_fmt ); + gen_data_config_type * gen_data_config = (gen_data_config_type *)enkf_config_node_get_ref( config_node ); + + for (int i=0; i < int_vector_size( report_steps ); i++) { + int report_step = int_vector_iget( report_steps , i ); + gen_data_config_add_report_step( gen_data_config , report_step); + enkf_config_node_set_internalize( config_node , report_step ); + } + + return config_node; +} + + +enkf_config_node_type * enkf_config_node_alloc_GEN_DATA_result( const char * key , + gen_data_file_format_type input_format, + const char * enkf_infile_fmt ) { + + enkf_config_node_type * config_node = enkf_config_node_alloc__( DYNAMIC_RESULT , GEN_DATA , key , false); + config_node->data = gen_data_config_alloc_GEN_DATA_result( key , input_format ); + + enkf_config_node_update( config_node , /* Generic update - needs the format settings from the special.*/ + NULL , + NULL , + enkf_infile_fmt , + NULL ); + + return config_node; +} + + + + + +/*****************************************************************/ + +enkf_config_node_type * enkf_config_node_new_container( const char * key ) { + enkf_config_node_type * config_node = enkf_config_node_alloc__( INVALID_VAR , CONTAINER , key , false); + config_node->data = container_config_alloc( key ); + return config_node; +} + +void enkf_config_node_update_container( enkf_config_node_type * config_node , const enkf_config_node_type * child_node) { + vector_append_ref( config_node->container_nodes , child_node ); + container_config_add_node( (container_config_type * ) config_node->data , child_node ); +} + +const char * enkf_config_node_iget_container_key( const enkf_config_node_type * config_node , int index) { + const enkf_config_node_type * child_node = (const enkf_config_node_type *)vector_iget_const( config_node->container_nodes , index ); + return child_node->key; +} + + +/*****************************************************************/ + +/** + This will create a new gen_kw_config instance which is NOT yet + valid. Mainly support code for the GUI. +*/ +enkf_config_node_type * enkf_config_node_alloc_field( const char * key , ecl_grid_type * ecl_grid, field_trans_table_type * trans_table, bool forward_init) { + enkf_config_node_type * config_node = enkf_config_node_alloc__( INVALID_VAR , FIELD , key , forward_init); + config_node->data = field_config_alloc_empty( key , ecl_grid , trans_table, false ); + return config_node; +} + + + + + +void enkf_config_node_update_parameter_field( enkf_config_node_type * config_node , + const char * enkf_outfile_fmt , + const char * init_file_fmt , + const char * min_std_file , + int truncation , double value_min , double value_max , + const char * init_transform , + const char * output_transform ) { + + field_file_format_type export_format = field_config_default_export_format( enkf_outfile_fmt ); /* Purely based on extension, recognizes ROFF and GRDECL, the rest will be ecl_kw format. */ + field_config_update_parameter_field( (field_config_type * ) config_node->data , truncation , value_min , value_max , + export_format , + init_transform , + output_transform ); + config_node->var_type = PARAMETER; + enkf_config_node_update( config_node , init_file_fmt , enkf_outfile_fmt , NULL , min_std_file); +} + + + + + +/*****************************************************************/ + + +void enkf_config_node_update_general_field( enkf_config_node_type * config_node , + const char * enkf_outfile_fmt , + const char * enkf_infile_fmt , + const char * init_file_fmt , + const char * min_std_file , + int truncation , + double value_min , + double value_max , + const char * init_transform , + const char * input_transform , + const char * output_transform ) { + + + field_file_format_type export_format = field_config_default_export_format( enkf_outfile_fmt ); /* Purely based on extension, recognizes ROFF and GRDECL, the rest will be ecl_kw format. */ + { + enkf_var_type var_type = INVALID_VAR; + if (enkf_infile_fmt == NULL) + var_type = PARAMETER; + else { + if (enkf_outfile_fmt == NULL) + var_type = DYNAMIC_RESULT; /* Probably not very realistic */ + else + util_abort("%s: this used to be DYNAMIC_STATE ?? \n",__func__); + } + config_node->var_type = var_type; + } + field_config_update_general_field( (field_config_type * ) config_node->data , + truncation , value_min , value_max , + export_format , + init_transform , + input_transform , + output_transform ); + + enkf_config_node_update( config_node , init_file_fmt , enkf_outfile_fmt , enkf_infile_fmt, min_std_file); +} + + + + + + +/*****************************************************************/ + + +enkf_config_node_type * enkf_config_node_container_iget( const enkf_config_node_type * node , int index) { + return (enkf_config_node_type *) vector_iget( node->container_nodes , index ); +} + +int enkf_config_node_container_size( const enkf_config_node_type * node ) { + return vector_get_size( node->container_nodes ); +} + + +/*****************************************************************/ + +/** + Invokes the get_data_size() function of the underlying node object. +*/ + +int enkf_config_node_get_data_size( const enkf_config_node_type * node , int report_step) { + if (node->impl_type == GEN_DATA) + return gen_data_config_get_data_size( (const gen_data_config_type * ) node->data , report_step); + else + return node->get_data_size( node->data ); +} + +void enkf_config_node_free(enkf_config_node_type * node) { + /* Freeing the underlying node object. */ + if (node->freef != NULL) node->freef(node->data); + free(node->key); + stringlist_free(node->obs_keys); + + free(node->init_file_abs_path); + + if (node->enkf_infile_fmt != NULL) + path_fmt_free( node->enkf_infile_fmt ); + + if (node->enkf_outfile_fmt != NULL) + path_fmt_free( node->enkf_outfile_fmt ); + + if (node->init_file_fmt != NULL) + path_fmt_free( node->init_file_fmt ); + + if (node->internalize != NULL) + bool_vector_free( node->internalize ); + + if (node->min_std != NULL) + enkf_node_free( node->min_std ); + + vector_free( node->container_nodes ); + free(node); +} + + + +const enkf_node_type * enkf_config_node_get_min_std( const enkf_config_node_type * config_node ) { + return config_node->min_std; +} + +const char * enkf_config_node_get_min_std_file( const enkf_config_node_type * config_node ) { + return config_node->min_std_file; +} + + +const char * enkf_config_node_get_enkf_outfile( const enkf_config_node_type * config_node ) { + return path_fmt_get_fmt( config_node->enkf_outfile_fmt ); +} + +const char * enkf_config_node_get_enkf_infile( const enkf_config_node_type * config_node ) { + return path_fmt_get_fmt( config_node->enkf_infile_fmt ); +} + + +const char * enkf_config_node_get_FIELD_fill_file(enkf_config_node_type * config_node, const path_fmt_type * runpath_fmt) { + if (config_node->init_file_abs_path) + return config_node->init_file_abs_path; + + char * runpath = NULL; + bool forward_init = enkf_config_node_use_forward_init(config_node); + + if (forward_init && runpath_fmt) { + runpath = path_fmt_alloc_path(runpath_fmt , false , 0, 0); /* Replace first %d with iens, if a second %d replace with iter */ + config_node->init_file_abs_path = enkf_config_node_alloc_initfile(config_node, runpath, 0); + } else + config_node->init_file_abs_path = enkf_config_node_alloc_initfile(config_node, NULL, 0); + + if (config_node->init_file_abs_path) { + config_node->init_file_abs_path = util_alloc_abs_path(config_node->init_file_abs_path); + if (!util_file_exists(config_node->init_file_abs_path)) { + free(config_node->init_file_abs_path); + config_node->init_file_abs_path = NULL; + } + } + + free(runpath); + + return config_node->init_file_abs_path; +} + + + +const char * enkf_config_node_get_init_file_fmt( const enkf_config_node_type * config_node) +{ + return path_fmt_get_fmt( config_node->init_file_fmt ); +} + + +void enkf_config_node_set_internalize(enkf_config_node_type * node, int report_step) { + ert_impl_type impl_type = enkf_config_node_get_impl_type( node ); + if (impl_type == CONTAINER) { + int inode; + int container_size = enkf_config_node_container_size( node ); + for (inode = 0; inode < container_size; inode++) { + enkf_config_node_type * child_node = enkf_config_node_container_iget( node , inode ); + enkf_config_node_set_internalize( child_node , report_step ); + } + } else { + if (node->internalize == NULL) + node->internalize = bool_vector_alloc( 0 , false ); + bool_vector_iset( node->internalize , report_step , true); + } +} + + +/* Query function: */ +bool enkf_config_node_internalize(const enkf_config_node_type * node, int report_step) { + if (node->internalize == NULL) + return false; + else + return bool_vector_safe_iget( node->internalize , report_step); /* Will return default value if report_step is beyond size. */ +} + + + +/** + This is the filename used when loading from a completed forward + model. +*/ + +char * enkf_config_node_alloc_infile(const enkf_config_node_type * node , int report_step) { + if (node->enkf_infile_fmt != NULL) + return path_fmt_alloc_path(node->enkf_infile_fmt , false , report_step); + else + return NULL; +} + + +char * enkf_config_node_alloc_outfile(const enkf_config_node_type * node , int report_step) { + if (node->enkf_outfile_fmt != NULL) + return path_fmt_alloc_path(node->enkf_outfile_fmt , false , report_step); + else + return NULL; +} + +/* + The path argument is used when the function is during forward_model + based initialisation. +*/ + +char * enkf_config_node_alloc_initfile( const enkf_config_node_type * node , const char * path , int iens) { + if (node->init_file_fmt == NULL) + return NULL; + else { + char * file = path_fmt_alloc_file( node->init_file_fmt , false , iens ); + if (util_is_abs_path( file )) + return file; + else { + char * full_path = util_alloc_filename( path , file , NULL ); + free( file ); + return full_path; + } + } +} + + + + +void * enkf_config_node_get_ref(const enkf_config_node_type * node) { // CXX_CAST_ERROR + return node->data; +} + + + +bool enkf_config_node_include_type(const enkf_config_node_type * config_node , int mask) { + + enkf_var_type var_type = config_node->var_type; + if (var_type & mask) + return true; + else + return false; + +} + + +bool enkf_config_node_use_forward_init(const enkf_config_node_type * config_node) { + return config_node->forward_init; +} + + +ert_impl_type enkf_config_node_get_impl_type(const enkf_config_node_type *config_node) { + return config_node->impl_type; +} + + +enkf_var_type enkf_config_node_get_var_type(const enkf_config_node_type *config_node) { + return config_node->var_type; +} + + +const char * enkf_config_node_get_key(const enkf_config_node_type * config_node) { return config_node->key; } + + +const stringlist_type * enkf_config_node_get_obs_keys(const enkf_config_node_type *config_node) { + return config_node->obs_keys; +} + + +int enkf_config_node_get_num_obs( const enkf_config_node_type * config_node ) { + return stringlist_get_size( config_node->obs_keys ); +} + + +/** + This checks the index_key - and sums up over all the time points of the observation. +*/ + +int enkf_config_node_load_obs( const enkf_config_node_type * config_node , enkf_obs_type * enkf_obs ,const char * key_index , int obs_count , time_t * _sim_time , double * _y , double * _std) { + ert_impl_type impl_type = enkf_config_node_get_impl_type(config_node); + int num_obs = 0; + int iobs; + + for (iobs = 0; iobs < stringlist_get_size( config_node->obs_keys ); iobs++) { + obs_vector_type * obs_vector = enkf_obs_get_vector( enkf_obs , stringlist_iget( config_node->obs_keys , iobs)); + + int report_step = -1; + while (true) { + report_step = obs_vector_get_next_active_step( obs_vector , report_step); + if (report_step == -1) break; + { + bool valid; + double value , std1; + + /** + The user index used when calling the user_get function on the + gen_obs data type is different depending on whether is called with a + data context user_key (as here) or with a observation context + user_key (as when plotting an observation plot). See more + documentation of the function gen_obs_user_get_data_index(). + */ + + if (impl_type == GEN_DATA) + gen_obs_user_get_with_data_index( (gen_obs_type *) obs_vector_iget_node( obs_vector , report_step ) , key_index , &value , &std1 , &valid); + else + obs_vector_user_get( obs_vector , key_index , report_step , &value , &std1 , &valid); + + if (valid) { + if (obs_count > 0) { + _sim_time[num_obs] = enkf_obs_iget_obs_time( enkf_obs , report_step ); + _y[num_obs] = value; + _std[num_obs] = std1; + } + num_obs++; + } + } + } + } + + /* Sorting the observations in time order. */ + if (obs_count > 0) { + double_vector_type * y = double_vector_alloc_shared_wrapper( 0 , 0 , _y , obs_count ); + double_vector_type * std = double_vector_alloc_shared_wrapper( 0 , 0 , _std , obs_count ); + time_t_vector_type * sim_time = time_t_vector_alloc_shared_wrapper( 0 , 0 , _sim_time , obs_count ); + perm_vector_type * sort_perm = time_t_vector_alloc_sort_perm( sim_time ); + + time_t_vector_permute( sim_time , sort_perm ); + double_vector_permute( y , sort_perm ); + double_vector_permute( std , sort_perm ); + + free( sort_perm ); + double_vector_free( y ); + double_vector_free( std ); + time_t_vector_free( sim_time ); + } + return num_obs; +} + + + +void enkf_config_node_add_obs_key(enkf_config_node_type * config_node , const char * obs_key) { + if (!stringlist_contains(config_node->obs_keys , obs_key)) + stringlist_append_copy(config_node->obs_keys , obs_key); +} + + +void enkf_config_node_clear_obs_keys(enkf_config_node_type * config_node) { + stringlist_clear( config_node->obs_keys ); +} + +/*****************************************************************/ + + + +void enkf_config_node_fprintf_config( const enkf_config_node_type * config_node , FILE * stream ) { + switch( config_node->impl_type) { + case(GEN_KW): + fprintf( stream , CONFIG_KEY_FORMAT , GEN_KW_KEY ); + fprintf( stream , CONFIG_VALUE_FORMAT , config_node->key ); + gen_kw_config_fprintf_config( (const gen_kw_config_type * ) config_node->data , path_fmt_get_fmt( config_node->enkf_outfile_fmt ) , config_node->min_std_file , stream ); + break; + case(FIELD): + fprintf( stream , CONFIG_KEY_FORMAT , FIELD_KEY ); + fprintf( stream , CONFIG_VALUE_FORMAT , config_node->key ); + field_config_fprintf_config( (field_config_type * ) config_node->data , + config_node->var_type , + path_fmt_get_fmt( config_node->enkf_outfile_fmt ) , + path_fmt_get_fmt( config_node->enkf_infile_fmt ) , + config_node->min_std_file , + stream ); + break; + case(GEN_DATA): + + if (config_node->var_type == PARAMETER) + fprintf( stream , CONFIG_KEY_FORMAT , GEN_PARAM_KEY ); + else + fprintf( stream , CONFIG_KEY_FORMAT , GEN_DATA_KEY ); + + gen_data_config_fprintf_config( (const gen_data_config_type * )config_node->data , + config_node->var_type , + path_fmt_get_fmt( config_node->enkf_outfile_fmt ) , + path_fmt_get_fmt( config_node->enkf_infile_fmt ) , + config_node->min_std_file , + stream ); + break; + default: + util_abort("%s: internal error - function can not store configuration for: %s variables. \n",__func__ , enkf_types_get_impl_name( config_node->impl_type) ); + } + fprintf( stream , "\n"); +} +/*****************************************************************/ + +void enkf_config_node_add_GEN_PARAM_config_schema( config_parser_type * config ) { + config_schema_item_type * item; + item = config_add_schema_item(config , GEN_PARAM_KEY , false ); + config_schema_item_set_argc_minmax(item , 2 , CONFIG_DEFAULT_ARG_MAX); +} + + +void enkf_config_node_add_GEN_DATA_config_schema( config_parser_type * config ) { + config_schema_item_type * item; + item = config_add_schema_item(config , GEN_DATA_KEY , false ); + config_schema_item_set_argc_minmax(item , 1 , CONFIG_DEFAULT_ARG_MAX); +} + +enkf_config_node_type * enkf_config_node_alloc_GEN_DATA_from_config( const config_content_node_type * node ) { + enkf_config_node_type * config_node = NULL; + const char * node_key = config_content_node_iget( node , 0 ); + { + hash_type * options = hash_alloc(); + + config_content_node_init_opt_hash( node , options , 1 ); + { + gen_data_file_format_type input_format = gen_data_config_check_format( (const char *) hash_safe_get( options , INPUT_FORMAT_KEY)); + const char * init_file_fmt = (const char *) hash_safe_get( options , INIT_FILES_KEY); + const char * ecl_file = (const char *) hash_safe_get( options , ECL_FILE_KEY); + const char * template_file = (const char *) hash_safe_get( options , TEMPLATE_KEY); + const char * data_key = (const char *) hash_safe_get( options , KEY_KEY); + const char * result_file = (const char *) hash_safe_get( options , RESULT_FILE_KEY); + const char * forward_string = (const char *) hash_safe_get( options , FORWARD_INIT_KEY ); + const char * report_steps_string = (const char *) hash_safe_get( options , REPORT_STEPS_KEY ); + int_vector_type * report_steps = int_vector_alloc(0,0); + bool forward_init = false; + bool valid_input = true; + + if (input_format == GEN_DATA_UNDEFINED) + valid_input = false; + + if (!gen_data_config_valid_result_format( result_file )) { + fprintf(stderr, "** ERROR: The RESULT_FILE:%s setting for %s is invalid - must have an embedded %%d - and be a relative path.\n" , result_file , node_key ); + valid_input = false; + } + + if (report_steps_string) { + if (!string_util_update_active_list( report_steps_string , report_steps )) { + valid_input = false; + fprintf(stderr,"** ERROR: The REPORT_STEPS:%s attribute was not valid.\n",report_steps_string); + } + } else { + fprintf(stderr,"** ERROR: As of July 2014 the GEN_DATA keywords must have a REPORT_STEPS:xxxx \n"); + fprintf(stderr," attribute to indicate which report step(s) you want to load data \n"); + fprintf(stderr," from. By requiring the user to enter this information in advance\n"); + fprintf(stderr," it is easier for ERT for to check that the results are valid, and\n"); + fprintf(stderr," handle errors with the GEN_DATA results gracefully.\n"); + fprintf(stderr," \n"); + fprintf(stderr," You can list several report steps separated with ',' and ranges with '-' \n"); + fprintf(stderr," but observe that spaces is NOT ALLOWED. \n"); + fprintf(stderr," \n"); + fprintf(stderr," - load from report step 100: REPORT_STEPS:100 \n"); + fprintf(stderr," - load from report steps 10, 20 and 30-40 REPORT_STEPS:10,20,30-40 \n"); + fprintf(stderr," \n"); + fprintf(stderr," The GEN_DATA keyword: %s will be ignored\n",node_key); + valid_input = false; + } + + if (valid_input) { + + if (forward_string) { + if (!util_sscanf_bool( forward_string , &forward_init)) + fprintf(stderr,"** Warning: parsing %s as bool failed - using FALSE \n",forward_string); + } + + if ((init_file_fmt == NULL) && + (ecl_file == NULL) && + (result_file != NULL)) + config_node = enkf_config_node_alloc_GEN_DATA_result( node_key , input_format , result_file); + else if ((init_file_fmt != NULL) && + (ecl_file != NULL) && + (result_file != NULL)) + util_abort("%s: This used to call the removed enkf_config_node_alloc_GEN_DATA_state() function \n",__func__); + { + gen_data_config_type * gen_data_config = (gen_data_config_type *)enkf_config_node_get_ref( config_node ); + + if (template_file) + gen_data_config_set_template( gen_data_config , template_file , data_key); + + for (int i=0; i < int_vector_size( report_steps ); i++) { + int report_step = int_vector_iget( report_steps , i ); + gen_data_config_add_report_step( gen_data_config , report_step); + enkf_config_node_set_internalize( config_node , report_step ); + } + } + } + + int_vector_free( report_steps ); + } + hash_free( options ); + } + + return config_node; +} + + +enkf_config_node_type * enkf_config_node_alloc_GEN_PARAM_from_config( const config_content_node_type * node ) { + enkf_config_node_type * config_node = NULL; + const char * node_key = config_content_node_iget( node , 0 ); + const char * ecl_file = config_content_node_iget( node , 1 ); + { + hash_type * options = hash_alloc(); + + config_content_node_init_opt_hash( node , options , 2 ); + { + gen_data_file_format_type input_format = gen_data_config_check_format( (const char *) hash_safe_get( options , INPUT_FORMAT_KEY)); + gen_data_file_format_type output_format = gen_data_config_check_format( (const char *) hash_safe_get( options , OUTPUT_FORMAT_KEY)); + const char * init_file_fmt = (const char *) hash_safe_get( options , INIT_FILES_KEY); + const char * template_file = (const char *) hash_safe_get( options , TEMPLATE_KEY); + const char * data_key = (const char *) hash_safe_get( options , KEY_KEY); + const char * min_std_file = (const char *) hash_safe_get( options , MIN_STD_KEY); + const char * forward_string = (const char *) hash_safe_get( options , FORWARD_INIT_KEY ); + bool forward_init = false; + bool valid_input = true; + + + if (input_format == GEN_DATA_UNDEFINED) + valid_input = false; + + if (input_format == ASCII_TEMPLATE) + valid_input = false; + + if (output_format == GEN_DATA_UNDEFINED) + valid_input = false; + + if (init_file_fmt == NULL) + valid_input = false; + + if (valid_input) { + + if (forward_string) { + if (!util_sscanf_bool( forward_string , &forward_init)) + fprintf(stderr,"** Warning: parsing %s as bool failed - using FALSE \n",forward_string); + } + + config_node = enkf_config_node_alloc_GEN_PARAM( node_key , forward_init , input_format , output_format , init_file_fmt , ecl_file); + + if (template_file) { + bool template_set_ok = gen_data_config_set_template( (gen_data_config_type *) enkf_config_node_get_ref( config_node ) , template_file , data_key); + if (!template_set_ok) + fprintf(stderr,"** Warning: the template settings were not applied correctly - ignored\n"); + } + + if (min_std_file) + enkf_config_node_update_min_std( config_node , min_std_file ); + + } + } + hash_free( options ); + } + return config_node; +} + +/* ************* FULL ALLOCS ************* */ +enkf_config_node_type * enkf_config_node_alloc_GEN_PARAM_full( const char * node_key , + bool forward_init , + gen_data_file_format_type input_format , + gen_data_file_format_type output_format , + const char * init_file_fmt , + const char * ecl_file, + const char * min_std_file, + const char * template_file, + const char * data_key) { + enkf_config_node_type * config_node = NULL; + config_node = enkf_config_node_alloc_GEN_PARAM( node_key , forward_init , input_format , output_format , init_file_fmt , ecl_file); + + if (template_file) { + bool template_set_ok = gen_data_config_set_template( (gen_data_config_type *) enkf_config_node_get_ref( config_node ) , template_file , data_key); + if (!template_set_ok) + fprintf(stderr,"** Warning: the template settings were not applied correctly - ignored\n"); + } + + if (min_std_file) + enkf_config_node_update_min_std( config_node , min_std_file ); + + return config_node; +} + +enkf_config_node_type * enkf_config_node_alloc_GEN_DATA_full( const char * node_key , + const char * result_file, + gen_data_file_format_type input_format , + const int_vector_type * report_steps, + const char * ecl_file, + const char * init_file_fmt , + const char * template_file, + const char * data_key) { + enkf_config_node_type * config_node = NULL; + + if ((init_file_fmt == NULL) && (ecl_file == NULL) && (result_file != NULL)) { + config_node = enkf_config_node_alloc_GEN_DATA_result( node_key , input_format , result_file); + } + else if ((init_file_fmt != NULL) && (ecl_file != NULL) && (result_file != NULL)) { + util_abort("%s: This used to call the removed enkf_config_node_alloc_GEN_DATA_state() function \n",__func__); + } + gen_data_config_type * gen_data_config = (gen_data_config_type *)enkf_config_node_get_ref( config_node ); + + if (template_file) + gen_data_config_set_template( gen_data_config , template_file , data_key); + + for (int i=0; i < int_vector_size( report_steps ); i++) { + int report_step = int_vector_iget( report_steps , i ); + gen_data_config_add_report_step( gen_data_config , report_step); + enkf_config_node_set_internalize( config_node , report_step ); + } + + return config_node; +} + +enkf_config_node_type * enkf_config_node_alloc_GEN_KW_full(const char * node_key, + bool forward_init, + const char * gen_kw_format, + const char * template_file, + const char * enkf_outfile, + const char * parameter_file, + const char * min_std_file, + const char * init_file_fmt) { + enkf_config_node_type * config_node = NULL; + config_node = enkf_config_node_new_gen_kw(node_key, gen_kw_format, forward_init); + + + enkf_config_node_update_gen_kw(config_node, + enkf_outfile, + template_file, + parameter_file, + min_std_file, + init_file_fmt); + + return config_node; +} + +enkf_config_node_type *enkf_config_node_alloc_SURFACE_full(const char * node_key, + bool forward_init, + const char * output_file, + const char * base_surface, + const char * min_std_file, + const char * init_file_fmt) { + + enkf_config_node_type * config_node = enkf_config_node_new_surface( node_key , forward_init ); + enkf_config_node_update_surface( config_node , base_surface , init_file_fmt , output_file , min_std_file ); + + return config_node; +} +/* ************* FULL ALLOCS ************* */ +/*****************************************************************/ +UTIL_SAFE_CAST_FUNCTION( enkf_config_node , ENKF_CONFIG_NODE_TYPE_ID) +VOID_FREE(enkf_config_node) diff --git a/libres/lib/enkf/enkf_defaults.cpp b/libres/lib/enkf/enkf_defaults.cpp new file mode 100644 index 00000000000..7a94f963757 --- /dev/null +++ b/libres/lib/enkf/enkf_defaults.cpp @@ -0,0 +1,31 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'enkf_defaults.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 + +const char * enkf_defaults_get_default_gen_kw_export_name() +{ + return DEFAULT_GEN_KW_EXPORT_NAME; +} + + +/** + Currently no code here - but the way DEFAULT_STATIC_KW is implemented + is dang ugly. Should be compiled. +*/ + diff --git a/libres/lib/enkf/enkf_fs.cpp b/libres/lib/enkf/enkf_fs.cpp new file mode 100644 index 00000000000..795c7478b4f --- /dev/null +++ b/libres/lib/enkf/enkf_fs.cpp @@ -0,0 +1,916 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'enkf_fs.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 + +/** + + About storage in the EnKF system + ================================ + + The system for storage in the EnKF system is quite complicated, maybe too + complicated. The reason the system is so complex is (at least) twofold: + + 1. It is a goal that it should be relatively easy to write new + systems (i.e. drivers) for storage. The current suite of + drivers (plain_driver_xx) are based on normal (compressed) + fread() and fwrite() system calls. But with the current + implementation one could write e.g. a MySQL based driver for + storage without touching(??) the rest of the EnKF code. + + 2. The parameters / static restart data / dynamic restart data + have very different storage characteristics. By splitting the + storage up in different drivers we can write drivers which are + specialized for the different types of data. + + + The interface + ------------- + + The unit of storage in the enkf_fs system is one enkf_node instance. The + interface between the storage system and the rest of the EnKF system is + through the enkf_fs functions: + + enkf_fs_fread_node() + enkf_fs_has_node() + enkf_fs_fwrite_node() + enkf_fs_unlink_node() - not implemented yet. + + + So all these functions (partly except enkf_fs_has_node()) work on a enkf_node + instance, and in addition they take the following input: + + - iens : ensemble member number + - report_step : the report_step number we are interested in + - state : whether we are considering an analyzed node or a forecast. + + In addition to the functions enkf_fs_fread_node() and enkf_fs_fwrite_node() there + are higher level functions enkf_fs_fread_alloc_ensemble() to load an ensemble of + nodes and enkf_fs_fread_alloc_ts() to load a time series of nodes. The driver can + implement these functions (to get a performance gain when loading many nodes). It + is not necessary to implement these functions, the enkf_fs layer has simple + functions doing repeated calls to the enkf_fs_fread_node() function. + + + The drivers + ----------- + + The enkf_fs layer does not self implement the functions to read and write + nodes. Instead what happens is: + + 1. We determine the type of the node (static/dynamic/parameter), and select + the appropriate driver. + + 2. The appropriate driver is called to implement e.g. the fread_node + functions. + + The different types of data have different characteristics, which the driver is + implemented to support. The characteristics the drivers support are the + following: + + + dynamic driver + -------------- + This is the simplest driver, all data is stored both in a forecast version and + an analyzed version. + + + parameter driver + ---------------- + This driver utilizes that parameters do not change during the forward model, + i.e. (analyzed , t) = (forecast , t - 1). So, only one version of the data is + actually stored; if you ask for the forecast you just get the data from the + previous report_step. + To support spin-ups and such the driver will actually go backwards in + report_time all the way until a node is found on disk. + + + static driver + ------------- + Like the parameter driver this also only stores one version of the data, + however in addition it has to query the node for an ID to support multiple + occurring keywords in ECLIPSE restart files. + + Currently only the plain_driver_xxx family has been implemented. Observe that + there is no dependencies between the drivers, it is perfectly possible to + implement a new driver for storage of static data only. (There is probably a + large amount of static data which is common both between members and for + several consecutive report steps; utilizing that one could write a static + driver which was admittedly slower, but leaner on the storage.) + + The drivers are allocated prior to allocating the enkf_fs instance, and + pointers are passed in when allocating the enkf_fs instance. + + + Mounting the filesystem + ----------------------- + + Mounting the filesystem - cool ehh?? Anyway, the important point is + that the moment ensemble information has hit the filesystem later + versions of the enkf program must support exactly that lay-out, + those drivers+++. To ensure this I see two possibilities: + + 1. We can freeze the filesystem drivers, and the layout on disk + indefinitely. + + 2. We can store the information needed to bootstrap the drivers, + according to the current layout on disk, in the + filesystem. I.e. something like a '/etc/fstab' file. + + We have chosen the second alternative. Currently this implemented as + follows: + + 1. In main() we query for the file {root-path}/enkf_mount_info. If + that file does not exists it is created by calls to the + selected drivers xxxx_fwrite_mount_info() functions. + + 2. enkf_fs_mount() is called with the enkf_mount_info as input. + + The enkf_mount_info file (BINARY) consists of four records (one for + each driver, including the index). The format of each record is: + + DRIVER_CATEGORY DRIVER_ID INFO + int int void * + + The driver category should be one of the four integer values in + fs_driver_type (fs_types.h) and DRIVER_ID is one of the integer + values in fs_driver_impl. The last void * data is whatever + (serialized) info the driver needs to bootstrap. This info is + written by the drivers xxxx_fwrite_mount_info() function, and it is + used when the driver is allocated with xxxx_fread_alloc(). + + The different drivers can be in arbitrary order in the + enkf_mount_info file, but when four records are read it checks that + all drivers have been initialized, and aborts if that is not the + case. + + If the enkf_mount_info file is deleted that can cause problems. + It is currently 'protected' with chomd a-w - but that is of course not + foolprof. +*/ + + + + +/** + Observe the following convention: the initial ensemble at report + step 0 is supposed to be analyzed. If we ask for the forecast at + report_step 0, we should get the analyzed value. +*/ + + +#define ENKF_FS_TYPE_ID 1089763 +#define ENKF_MOUNT_MAP "enkf_mount_info" +#define SUMMARY_KEY_SET_FILE "summary-key-set" +#define TIME_MAP_FILE "time-map" +#define STATE_MAP_FILE "state-map" +#define MISFIT_ENSEMBLE_FILE "misfit-ensemble" +#define CASE_CONFIG_FILE "case_config" + +struct enkf_fs_struct { + UTIL_TYPE_ID_DECLARATION; + char * case_name; + char * root_path; + char * mount_point; // mount_point = root_path / case_name; the mount_point is the fundamental INPUT. + + char * lock_file; + int lock_fd; + + fs_driver_type * dynamic_forecast; + fs_driver_type * parameter; + fs_driver_type * index ; + + bool read_only; /* Whether this filesystem has been mounted read-only. */ + time_map_type * time_map; + cases_config_type * cases_config; + state_map_type * state_map; + summary_key_set_type * summary_key_set; + misfit_ensemble_type * misfit_ensemble; + /* + The variables below here are for storing arbitrary files within + the enkf_fs storage directory, but not as serialized enkf_nodes. + */ + path_fmt_type * case_fmt; + path_fmt_type * case_member_fmt; + path_fmt_type * case_tstep_fmt; + path_fmt_type * case_tstep_member_fmt; + + int refcount; + int runcount; // Counts the number of simulations currently writing to this enkf_fs; the purpose is to + // be able to answer the question: Is this case currently 'running'? +}; + + +/*****************************************************************/ + + +UTIL_SAFE_CAST_FUNCTION( enkf_fs , ENKF_FS_TYPE_ID) +UTIL_IS_INSTANCE_FUNCTION( enkf_fs , ENKF_FS_TYPE_ID) + +static void enkf_fs_umount( enkf_fs_type * fs ); + +int enkf_fs_incref( enkf_fs_type * fs ) { + fs->refcount++; + + res_log_fdebug("Calling incref on: %s . Refcount after incref:%d", fs->mount_point, fs->refcount); + + return fs->refcount; +} + + +int enkf_fs_decref( enkf_fs_type * fs ) { + int refcount; + fs->refcount--; + refcount = fs->refcount; + + if (fs->refcount < 0) + util_abort("%s: Internal inconsistency in file system. The filesystem refcount:%d is < 0 \n",__func__ , fs->refcount); + + res_log_fdebug("Calling decref on: %s . Refcount after decref:%d", fs->mount_point, fs->refcount); + if (refcount == 0) + enkf_fs_umount( fs ); + + return refcount; +} + + +int enkf_fs_get_refcount( const enkf_fs_type * fs ) { + return fs->refcount; +} + + +enkf_fs_type * enkf_fs_get_ref( enkf_fs_type * fs ) { + enkf_fs_incref( fs ); + return fs; +} + + +static enkf_fs_type * enkf_fs_alloc_empty( const char * mount_point ) { + enkf_fs_type * fs = (enkf_fs_type *)util_malloc(sizeof * fs ); + UTIL_TYPE_ID_INIT( fs , ENKF_FS_TYPE_ID ); + fs->time_map = time_map_alloc( ); + fs->cases_config = cases_config_alloc(); + fs->state_map = state_map_alloc(); + fs->summary_key_set = summary_key_set_alloc(); + fs->misfit_ensemble = misfit_ensemble_alloc(); + fs->index = NULL; + fs->parameter = NULL; + fs->dynamic_forecast = NULL; + fs->read_only = true; + fs->mount_point = util_alloc_string_copy( mount_point ); + fs->refcount = 0; + fs->runcount = 0; + fs->lock_fd = 0; + + if (mount_point == NULL) + util_abort("%s: fatal internal error: mount_point == NULL \n",__func__); + { + char ** path_tmp; + int path_len; + + util_path_split( fs->mount_point , &path_len , &path_tmp); + fs->case_name = util_alloc_string_copy( path_tmp[path_len - 1]); + fs->root_path = util_alloc_joined_string( (const char **) path_tmp , path_len , UTIL_PATH_SEP_STRING); + fs->lock_file = util_alloc_filename( fs->mount_point , fs->case_name , "lock"); + + if (util_try_lockf( fs->lock_file , S_IWUSR + S_IWGRP , &fs->lock_fd)) { + fs->read_only = false; + } else { + fprintf(stderr," Another program has already opened filesystem read-write - this instance will be UNSYNCRONIZED read-only. Cross your fingers ....\n"); + fs->read_only = true; + } + + util_free_stringlist( path_tmp , path_len ); + } + return fs; +} + + + + +static int enkf_fs_fread_fs_version__(FILE * stream) { + int version; + long fs_tag = util_fread_long( stream ); + if (fs_tag == FS_MAGIC_ID) + version = util_fread_int(stream); + else + version = 0; + return version; +} + + +/** + -1 : No mount map found. + 0 : Old mount map without version info. + x : Actual version info. +*/ + +static int enkf_fs_get_fs_version__(const char * config_file) { + int version = -1; + if (util_file_exists(config_file)) { + FILE * stream = util_fopen(config_file , "r"); + version = enkf_fs_fread_fs_version__(stream); + fclose(stream); + } + return version; +} + +/** + Function written to look for old (version <= 104) mount info maps. +*/ + +int enkf_fs_get_version104( const char * path ) { + char * config_file = util_alloc_filename( path , ENKF_MOUNT_MAP, NULL); + int version = enkf_fs_get_fs_version__( config_file ); + free( config_file ); + return version; +} + + + + + + + +/*****************************************************************/ + + +static void enkf_fs_init_path_fmt( enkf_fs_type * fs) { + /* + Installing the path_fmt instances for the storage of arbitrary files. + */ + fs->case_fmt = path_fmt_alloc_directory_fmt( DEFAULT_CASE_PATH ); + fs->case_member_fmt = path_fmt_alloc_directory_fmt( DEFAULT_CASE_MEMBER_PATH ); + fs->case_tstep_fmt = path_fmt_alloc_directory_fmt( DEFAULT_CASE_TSTEP_PATH ); + fs->case_tstep_member_fmt = path_fmt_alloc_directory_fmt( DEFAULT_CASE_TSTEP_MEMBER_PATH ); + +} + +static void enkf_fs_create_block_fs( FILE * stream , int num_drivers , const char * mount_point , void * arg) { + + block_fs_driver_create_fs( stream , mount_point , DRIVER_PARAMETER , num_drivers , "Ensemble/mod_%d" , "PARAMETER"); + block_fs_driver_create_fs( stream , mount_point , DRIVER_DYNAMIC_FORECAST , num_drivers , "Ensemble/mod_%d" , "FORECAST"); + block_fs_driver_create_fs( stream , mount_point , DRIVER_INDEX , 1 , "Index" , "INDEX"); + +} + + +static void enkf_fs_assign_driver( enkf_fs_type * fs , fs_driver_type * driver , fs_driver_enum driver_type ) { + switch(driver_type) { + case(DRIVER_PARAMETER): + fs->parameter = driver; + break; + case(DRIVER_DYNAMIC_FORECAST): + fs->dynamic_forecast = driver; + break; + case(DRIVER_INDEX): + fs->index = driver; + break; + case(DRIVER_STATIC): + util_abort("%s: internal error - should not assign a STATIC driver \n",__func__); + break; + case(DRIVER_DYNAMIC_ANALYZED): + util_abort("%s: internal error - should not assign a DYNAMIC_ANALYZED driver \n",__func__); + break; + } +} + + +static enkf_fs_type * enkf_fs_mount_block_fs( FILE * fstab_stream , const char * mount_point ) { + enkf_fs_type * fs = enkf_fs_alloc_empty( mount_point ); + + { + while (true) { + fs_driver_enum driver_type; + if (fread( &driver_type , sizeof driver_type , 1 , fstab_stream) == 1) { + if (fs_types_valid( driver_type )) { + fs_driver_type * driver = (fs_driver_type * ) block_fs_driver_open( fstab_stream , mount_point , driver_type , fs->read_only); + enkf_fs_assign_driver( fs , driver , driver_type ); + } else + block_fs_driver_fskip( fstab_stream ); + } else + break; + } + } + + return fs; +} + +enkf_fs_type * enkf_fs_create_fs( const char * mount_point, fs_driver_impl driver_id , void * arg , bool mount) { + const int num_drivers = 32; + FILE * stream = fs_driver_open_fstab( mount_point , true ); + if (stream != NULL) { + fs_driver_init_fstab( stream, driver_id); + { + switch( driver_id ) { + case( BLOCK_FS_DRIVER_ID ): + enkf_fs_create_block_fs( stream , num_drivers , mount_point , arg ); + break; + default: + util_abort("%s: Invalid driver_id value:%d \n",__func__ , driver_id ); + } + } + fclose( stream ); + } + + if (mount) + return enkf_fs_mount( mount_point ); + else + return NULL; +} + +static void enkf_fs_fsync_time_map( enkf_fs_type * fs ) { + char * filename = enkf_fs_alloc_case_filename( fs , TIME_MAP_FILE ); + time_map_fwrite( fs->time_map , filename ); + free( filename ); +} + + +static void enkf_fs_fread_time_map( enkf_fs_type * fs ) { + char * filename = enkf_fs_alloc_case_filename( fs , TIME_MAP_FILE ); + time_map_fread( fs->time_map , filename ); + free( filename ); +} + + +static void enkf_fs_fsync_cases_config( enkf_fs_type * fs ) { + char * filename = enkf_fs_alloc_case_filename( fs , CASE_CONFIG_FILE ); + cases_config_fwrite( fs->cases_config , filename ); + free( filename ); +} + +static void enkf_fs_fsync_state_map( enkf_fs_type * fs ) { + char * filename = enkf_fs_alloc_case_filename( fs , STATE_MAP_FILE ); + state_map_fwrite( fs->state_map , filename ); + free( filename ); +} + +static void enkf_fs_fsync_summary_key_set( enkf_fs_type * fs ) { + char * filename = enkf_fs_alloc_case_filename( fs , SUMMARY_KEY_SET_FILE ); + summary_key_set_fwrite( fs->summary_key_set , filename ); + free( filename ); +} + +static void enkf_fs_fread_cases_config( enkf_fs_type * fs ) { + char * filename = enkf_fs_alloc_case_filename( fs , CASE_CONFIG_FILE ); + cases_config_fread( fs->cases_config , filename ); + free( filename ); +} + + +static void enkf_fs_fread_state_map( enkf_fs_type * fs ) { + char * filename = enkf_fs_alloc_case_filename( fs , STATE_MAP_FILE ); + state_map_fread( fs->state_map , filename ); + free( filename ); +} + +static void enkf_fs_fread_summary_key_set( enkf_fs_type * fs ) { + char * filename = enkf_fs_alloc_case_filename( fs , SUMMARY_KEY_SET_FILE ); + summary_key_set_fread( fs->summary_key_set , filename ); + free( filename ); +} + +state_map_type * enkf_fs_alloc_readonly_state_map( const char * mount_point ) { + path_fmt_type * path_fmt = path_fmt_alloc_directory_fmt( DEFAULT_CASE_PATH ); + char * filename = path_fmt_alloc_file( path_fmt , false , mount_point , STATE_MAP_FILE); + + state_map_type * state_map = state_map_fread_alloc_readonly( filename ); + + path_fmt_free( path_fmt ); + free( filename ); + return state_map; +} + +time_map_type * enkf_fs_alloc_readonly_time_map( const char * mount_point ) { + path_fmt_type * path_fmt = path_fmt_alloc_directory_fmt( DEFAULT_CASE_PATH ); + char * filename = path_fmt_alloc_file( path_fmt , false , mount_point , TIME_MAP_FILE); + + time_map_type * time_map = time_map_fread_alloc_readonly( filename ); + + path_fmt_free( path_fmt ); + free( filename ); + return time_map; +} + + + +static void enkf_fs_fread_misfit( enkf_fs_type * fs ) { + FILE * stream = enkf_fs_open_excase_file( fs , MISFIT_ENSEMBLE_FILE ); + if (stream != NULL) { + misfit_ensemble_fread( fs->misfit_ensemble , stream ); + fclose( stream ); + } +} + + +static void enkf_fs_fwrite_misfit( enkf_fs_type * fs ) { + if (misfit_ensemble_initialized( fs->misfit_ensemble )) { + FILE * stream = enkf_fs_open_case_file( fs , MISFIT_ENSEMBLE_FILE , "w"); + misfit_ensemble_fwrite( fs->misfit_ensemble , stream ); + fclose( stream ); + } +} + + + +int enkf_fs_disk_version(const char * mount_point ) { + int disk_version = -1; + FILE * stream = fs_driver_open_fstab( mount_point , false ); + if (stream) { + disk_version = fs_driver_fread_version( stream ); + fclose( stream ); + } + return disk_version; +} + + +bool enkf_fs_update_disk_version(const char * mount_point , int src_version , int target_version) { + if (enkf_fs_disk_version( mount_point ) == src_version) { + char * fstab_file = fs_driver_alloc_fstab_file( mount_point ); + FILE * stream = util_fopen( fstab_file , "r+"); + + fseek( stream , 0L, SEEK_SET ); + fs_driver_assert_magic( stream ); + util_fwrite_int( target_version , stream ); + + fclose( stream ); + free( fstab_file ); + + return true; + } else + return false; +} + + +enkf_fs_type * enkf_fs_mount(const char * mount_point) { + FILE * stream = fs_driver_open_fstab(mount_point, false); + + if (!stream) + return NULL; + + enkf_fs_type * fs = NULL; + fs_driver_assert_magic(stream); + fs_driver_assert_version(stream, mount_point); + + fs_driver_impl driver_id = (fs_driver_impl) util_fread_int(stream); + + switch(driver_id) { + case(BLOCK_FS_DRIVER_ID): + fs = enkf_fs_mount_block_fs(stream, mount_point); + res_log_fdebug("Mounting (block_fs) point %s.", mount_point); + break; + default: + util_abort("%s: unrecognized driver_id:%d \n", __func__, driver_id); + } + + fclose(stream); + enkf_fs_init_path_fmt(fs); + enkf_fs_fread_time_map(fs); + enkf_fs_fread_cases_config(fs); + enkf_fs_fread_state_map(fs); + enkf_fs_fread_summary_key_set(fs); + enkf_fs_fread_misfit(fs); + + enkf_fs_get_ref(fs); + return fs; +} + + +bool enkf_fs_exists( const char * mount_point ) { + bool exists = false; + + FILE * stream = fs_driver_open_fstab( mount_point , false ); + if (stream != NULL) { + exists = true; + fclose( stream ); + } + + return exists; +} + + + + +/*****************************************************************/ + + + +static void enkf_fs_free_driver(fs_driver_type * driver) { + driver->free_driver(driver); +} + + +static void enkf_fs_umount(enkf_fs_type * fs) { + if (!fs->read_only) { + enkf_fs_fsync(fs); + enkf_fs_fwrite_misfit(fs); + } + + int refcount = fs->refcount; + if (refcount > 0) + util_abort("%s: Internal inconsistency - " + "tried to umount a filesystem with refcount:%d\n", + __func__, refcount); + + res_log_fdebug("%s umount filesystem %s", __func__, fs->mount_point); + + enkf_fs_free_driver(fs->dynamic_forecast); + enkf_fs_free_driver(fs->parameter); + enkf_fs_free_driver(fs->index); + + if (fs->lock_fd > 0) { + close(fs->lock_fd); // Closing the lock_file file descriptor - and releasing the lock. + util_unlink_existing(fs->lock_file); + } + + free(fs->case_name); + free(fs->root_path); + free(fs->lock_file); + free(fs->mount_point); + path_fmt_free(fs->case_fmt); + path_fmt_free(fs->case_member_fmt); + path_fmt_free(fs->case_tstep_fmt); + path_fmt_free(fs->case_tstep_member_fmt); + + state_map_free(fs->state_map); + summary_key_set_free(fs->summary_key_set); + time_map_free(fs->time_map); + cases_config_free(fs->cases_config); + misfit_ensemble_free(fs->misfit_ensemble); + free(fs); +} + + + + + +static void * enkf_fs_select_driver(enkf_fs_type * fs , enkf_var_type var_type, const char * key) { + void * driver = NULL; + switch (var_type) { + case(DYNAMIC_RESULT): + driver = fs->dynamic_forecast; + break; + case(EXT_PARAMETER): + driver = fs->parameter; + break; + case(PARAMETER): + driver = fs->parameter; + break; + default: + util_abort("%s: fatal internal error - could not determine enkf_fs driver for object:%s[integer type:%d] - aborting.\n",__func__, key , var_type); + } + return driver; +} + + + +/*****************************************************************/ +/* Exported functions for enkf_node instances . */ + + +static void enkf_fs_fsync_driver( fs_driver_type * driver ) { + if (driver->fsync_driver != NULL) + driver->fsync_driver( driver ); +} + + + +void enkf_fs_fsync( enkf_fs_type * fs ) { + enkf_fs_fsync_driver( fs->parameter ); + enkf_fs_fsync_driver( fs->dynamic_forecast ); + enkf_fs_fsync_driver( fs->index ); + + enkf_fs_fsync_time_map( fs ); + enkf_fs_fsync_cases_config( fs) ; + enkf_fs_fsync_state_map( fs ); + enkf_fs_fsync_summary_key_set( fs ); +} + + +void enkf_fs_fread_node(enkf_fs_type * enkf_fs , buffer_type * buffer , + const char * node_key , + enkf_var_type var_type , + int report_step, + int iens) { + + fs_driver_type * driver = (fs_driver_type * ) enkf_fs_select_driver(enkf_fs , var_type , node_key ); + if (var_type == PARAMETER) + /* Parameters are *ONLY* stored at report_step == 0 */ + report_step = 0; + + buffer_rewind( buffer ); + driver->load_node(driver , node_key , report_step , iens , buffer); +} + + +void enkf_fs_fread_vector(enkf_fs_type * enkf_fs , buffer_type * buffer , + const char * node_key , + enkf_var_type var_type , + int iens) { + + fs_driver_type * driver = (fs_driver_type * ) enkf_fs_select_driver(enkf_fs , var_type , node_key ); + + buffer_rewind( buffer ); + driver->load_vector(driver , node_key , iens , buffer); +} + + + +bool enkf_fs_has_node(enkf_fs_type * enkf_fs , const char * node_key , enkf_var_type var_type , int report_step , int iens) { + fs_driver_type * driver = fs_driver_safe_cast(enkf_fs_select_driver(enkf_fs , var_type , node_key)); + return driver->has_node(driver , node_key , report_step , iens ); +} + + +bool enkf_fs_has_vector(enkf_fs_type * enkf_fs , const char * node_key , enkf_var_type var_type , int iens ) { + fs_driver_type * driver = fs_driver_safe_cast(enkf_fs_select_driver(enkf_fs , var_type , node_key)); + return driver->has_vector(driver , node_key , iens ); +} + +void enkf_fs_fwrite_node(enkf_fs_type * enkf_fs , buffer_type * buffer , const char * node_key, enkf_var_type var_type, + int report_step , int iens ) { + if (enkf_fs->read_only) + util_abort("%s: attempt to write to read_only filesystem mounted at:%s - aborting. \n",__func__ , enkf_fs->mount_point); + + + if ((var_type == PARAMETER) && (report_step > 0)) + util_abort("%s: Parameters can only be saved for report_step = 0 %s:%d\n", __func__ , node_key , report_step); + { + void * _driver = enkf_fs_select_driver(enkf_fs , var_type , node_key); + { + fs_driver_type * driver = fs_driver_safe_cast(_driver); + driver->save_node(driver , node_key , report_step , iens , buffer); + } + } +} + + +void enkf_fs_fwrite_vector(enkf_fs_type * enkf_fs , buffer_type * buffer , const char * node_key, enkf_var_type var_type, + int iens ) { + if (enkf_fs->read_only) + util_abort("%s: attempt to write to read_only filesystem mounted at:%s - aborting. \n",__func__ , enkf_fs->mount_point); + { + void * _driver = enkf_fs_select_driver(enkf_fs , var_type , node_key); + { + fs_driver_type * driver = fs_driver_safe_cast(_driver); + driver->save_vector(driver , node_key , iens , buffer); + } + } +} + + + + +/*****************************************************************/ + + + + + +/*****************************************************************/ + +const char * enkf_fs_get_mount_point( const enkf_fs_type * fs ) { + return fs->mount_point; +} + +const char * enkf_fs_get_case_name( const enkf_fs_type * fs ) { + return fs->case_name; +} + + +bool enkf_fs_is_read_only(const enkf_fs_type * fs) { + return fs->read_only; +} + +/*****************************************************************/ +/* write_dir / read_dir confusion. */ + +char * enkf_fs_alloc_case_filename( const enkf_fs_type * fs , const char * input_name) { + char * filename = path_fmt_alloc_file( fs->case_fmt , false , fs->mount_point , input_name); + return filename; +} + +char * enkf_fs_alloc_case_tstep_filename( const enkf_fs_type * fs , int tstep , const char * input_name) { + char * filename = path_fmt_alloc_file( fs->case_tstep_fmt , false , fs->mount_point , tstep , input_name); + return filename; +} + +char * enkf_fs_alloc_case_tstep_member_filename( const enkf_fs_type * fs , int tstep , int iens , const char * input_name) { + char * filename = path_fmt_alloc_file( fs->case_tstep_member_fmt , false , fs->mount_point , tstep , iens , input_name); + return filename; +} + + + +FILE * enkf_fs_open_case_file( const enkf_fs_type * fs , const char * input_name , const char * mode) { + char * filename = enkf_fs_alloc_case_filename( fs , input_name ); + FILE * stream = util_mkdir_fopen( filename , mode ); + free( filename ); + return stream; +} + + +FILE * enkf_fs_open_case_tstep_file( const enkf_fs_type * fs , const char * input_name , int tstep , const char * mode) { + char * filename = enkf_fs_alloc_case_tstep_filename( fs , tstep , input_name ); + FILE * stream = util_mkdir_fopen( filename , mode ); + free( filename ); + return stream; +} + + +/*****************************************************************/ +/* + The open_exXXX functions will return NULL if the file does not + already exist. These functions can only be used to open with 'r' + mode. +*/ + + + +static FILE * enkf_fs_open_exfile( const char * filename ) { + if (util_file_exists(filename)) + return util_fopen( filename , "r"); + else + return NULL; +} + +FILE * enkf_fs_open_excase_file( const enkf_fs_type * fs , const char * input_name ) { + char * filename = enkf_fs_alloc_case_filename( fs , input_name ); + FILE * stream = enkf_fs_open_exfile( filename ); + free( filename ); + return stream; +} + + +FILE * enkf_fs_open_excase_tstep_file( const enkf_fs_type * fs , const char * input_name , int tstep ) { + char * filename = enkf_fs_alloc_case_tstep_filename( fs , tstep , input_name ); + FILE * stream = enkf_fs_open_exfile( filename ); + free( filename ); + return stream; +} + + +/*****************************************************************/ + +time_map_type * enkf_fs_get_time_map( const enkf_fs_type * fs ) { + return fs->time_map; +} + +cases_config_type * enkf_fs_get_cases_config( const enkf_fs_type * fs) { + return fs->cases_config; +} + +state_map_type * enkf_fs_get_state_map( const enkf_fs_type * fs ) { + return fs->state_map; +} + +summary_key_set_type * enkf_fs_get_summary_key_set( const enkf_fs_type * fs ) { + return fs->summary_key_set; +} + +misfit_ensemble_type * enkf_fs_get_misfit_ensemble( const enkf_fs_type * fs ) { + return fs->misfit_ensemble; +} + +void enkf_fs_increase_run_count(enkf_fs_type * fs) { + fs->runcount = fs->runcount + 1; +} + +void enkf_fs_decrease_run_count(enkf_fs_type * fs) { + fs->runcount = fs->runcount - 1; +} + +bool enkf_fs_is_running(const enkf_fs_type * fs) { + return (fs->runcount > 0); +} diff --git a/libres/lib/enkf/enkf_main.cpp b/libres/lib/enkf/enkf_main.cpp new file mode 100644 index 00000000000..330a373a7df --- /dev/null +++ b/libres/lib/enkf/enkf_main.cpp @@ -0,0 +1,2159 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + The file 'enkf_main.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 HAVE_THREAD_POOL 1 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/**/ + +/** + This object should contain **everything** needed to run a enkf + simulation. A way to wrap up all available information/state and + pass it around. An attempt has been made to collect various pieces + of related information together in a couple of objects + (model_config, ecl_config, site_config and ensemble_config). When + it comes to these holding objects the following should be observed: + + 1. It not always obvious where a piece of information should be + stored, i.e. the grid is a property of the model, however it is + an eclipse grid, and hence also belongs to eclipse + configuration?? [In this case ecl_config wins out.] + + 2. The information stored in these objects is typically passed on + to the enkf_state object, where it is used. + + 3. At enkf_state level it is not really consequent - in some cases + the enkf_state object takes a scalar copy , + and in other cases only a pointer down to the underlying + enkf_main object is taken. In the former case it is no way to + change global behaviour by modifying the enkf_main objects. + + In the enkf_state object the fields of the member_config, + ecl_config, site_config and ensemble_config objects are mixed + and matched into other small holding objects defined in + enkf_state.c. + +*/ + +#define ENKF_MAIN_ID 8301 + +struct enkf_main_struct { + UTIL_TYPE_ID_DECLARATION; + enkf_fs_type * dbase; /* The internalized information. */ + + const res_config_type * res_config; + local_config_type * local_config; /* Holding all the information about local analysis. */ + rng_manager_type * rng_manager; + rng_type * shared_rng; + ranking_table_type * ranking_table; + + enkf_obs_type * obs; + + enkf_state_type ** ensemble; /* The ensemble ... */ + int ens_size; /* The size of the ensemble */ + bool verbose; +}; + + + + +/*****************************************************************/ + +void enkf_main_init_internalization( enkf_main_type * , run_mode_type ); +void enkf_main_update_local_updates( enkf_main_type * enkf_main); +static void enkf_main_close_fs( enkf_main_type * enkf_main ); +static void enkf_main_init_fs( enkf_main_type * enkf_main ); +static void enkf_main_user_select_initial_fs(enkf_main_type * enkf_main ); +static void enkf_main_free_ensemble( enkf_main_type * enkf_main ); +static void enkf_main_analysis_update( enkf_main_type * enkf_main , + enkf_fs_type * target_fs , + const bool_vector_type * ens_mask , + int target_step , + hash_type * use_count, + run_mode_type run_mode , + int step1 , + int step2 , + const local_ministep_type * ministep , + const meas_data_type * forecast , + obs_data_type * obs_data); +/*****************************************************************/ + +UTIL_SAFE_CAST_FUNCTION(enkf_main , ENKF_MAIN_ID) +UTIL_IS_INSTANCE_FUNCTION(enkf_main , ENKF_MAIN_ID) + +const analysis_config_type * enkf_main_get_analysis_config(const enkf_main_type * enkf_main) { + return res_config_get_analysis_config(enkf_main->res_config); +} + +const char * enkf_main_get_user_config_file( const enkf_main_type * enkf_main ) { + return res_config_get_user_config_file(enkf_main->res_config); +} + +const char * enkf_main_get_site_config_file( const enkf_main_type * enkf_main ) { + return site_config_get_config_file( + enkf_main_get_site_config(enkf_main) + ); +} + +ensemble_config_type * enkf_main_get_ensemble_config(const enkf_main_type * enkf_main) { + return res_config_get_ensemble_config(enkf_main->res_config); +} + +const site_config_type * enkf_main_get_site_config( const enkf_main_type * enkf_main ) { + return res_config_get_site_config(enkf_main->res_config); +} + +const res_config_type * enkf_main_get_res_config(const enkf_main_type * enkf_main) { + return enkf_main->res_config; +} + +const log_config_type * enkf_main_get_log_config(const enkf_main_type * enkf_main) { + return res_config_get_log_config(enkf_main->res_config); +} + +subst_config_type * enkf_main_get_subst_config(const enkf_main_type * enkf_main) { + return res_config_get_subst_config(enkf_main->res_config); +} + +subst_list_type * enkf_main_get_data_kw( const enkf_main_type * enkf_main ) { + return subst_config_get_subst_list(enkf_main_get_subst_config(enkf_main)); +} + + +local_config_type * enkf_main_get_local_config( const enkf_main_type * enkf_main ) { + return enkf_main->local_config; +} + +model_config_type * enkf_main_get_model_config( const enkf_main_type * enkf_main ) { + return res_config_get_model_config(enkf_main->res_config); +} + +ranking_table_type * enkf_main_get_ranking_table( const enkf_main_type * enkf_main ) { + return enkf_main->ranking_table; +} + +const ecl_config_type * enkf_main_get_ecl_config(const enkf_main_type * enkf_main) { + return res_config_get_ecl_config(enkf_main->res_config); +} + +int enkf_main_get_history_length( const enkf_main_type * enkf_main) { + return model_config_get_last_history_restart(enkf_main_get_model_config(enkf_main)); +} + +enkf_obs_type * enkf_main_get_obs(const enkf_main_type * enkf_main) { + return enkf_main->obs; +} + + +bool enkf_main_have_obs( const enkf_main_type * enkf_main ) { + return enkf_obs_have_obs( enkf_main->obs ); +} + + + +const hook_manager_type * enkf_main_get_hook_manager( const enkf_main_type * enkf_main ) { + return res_config_get_hook_manager(enkf_main->res_config); +} + + + +void enkf_main_alloc_obs( enkf_main_type * enkf_main ) { + const ecl_config_type * ecl_config = enkf_main_get_ecl_config(enkf_main); + model_config_type * model_config = enkf_main_get_model_config(enkf_main); + enkf_main->obs = enkf_obs_alloc( model_config_get_history(model_config), + model_config_get_external_time_map(model_config), + ecl_config_get_grid(ecl_config), + ecl_config_get_refcase(ecl_config) , + enkf_main_get_ensemble_config(enkf_main)); +} + +bool enkf_main_load_obs(enkf_main_type * enkf_main, + const char * obs_config_file, + bool clear_existing) { + if (clear_existing) + enkf_obs_clear( enkf_main->obs ); + + if (!enkf_obs_is_valid(enkf_main->obs)) { + fprintf(stderr, + "** Warning: failed to load observation data from: %s \n", + obs_config_file); + return false; + } + + enkf_obs_load(enkf_main->obs , + obs_config_file , + analysis_config_get_std_cutoff(enkf_main_get_analysis_config(enkf_main))); + enkf_main_update_local_updates( enkf_main ); + return true; +} + + +static void enkf_main_add_internal_subst_kw( enkf_main_type * enkf_main , const char * key , const char * value, const char * help_text) { + subst_config_add_internal_subst_kw(enkf_main_get_subst_config(enkf_main), key, value, help_text); +} + +void enkf_main_free(enkf_main_type * enkf_main){ + if (enkf_main->rng_manager) + rng_manager_free( enkf_main->rng_manager ); + + if (enkf_main->shared_rng) + rng_free( enkf_main->shared_rng ); + + if (enkf_main->obs) + enkf_obs_free(enkf_main->obs); + + ranking_table_free( enkf_main->ranking_table ); + enkf_main_free_ensemble( enkf_main ); + enkf_main_close_fs( enkf_main ); + res_log_close(); + + local_config_free( enkf_main->local_config ); + + free(enkf_main); +} + + + +void enkf_main_exit(enkf_main_type * enkf_main) { + enkf_main_free( enkf_main ); + exit(0); +} + + +/*****************************************************************/ + + + +/** + This function returns a (enkf_node_type ** ) pointer, which points + to all the instances with the same keyword, i.e. + + enkf_main_alloc_node_ensemble(enkf_main , "PRESSURE"); + + Will return an ensemble of pressure nodes. The enkf_node instances + must be free'd with enkf_node_free( ) afterwards. + + Example: + + vector_type * pressure_nodes = enkf_main_alloc_node_ensemble(enkf_main , "PRESSURE"); + + // Do something with the pressure nodes ... + + free(pressure_nodes); + +*/ + +static vector_type * enkf_main_alloc_node_ensemble(const enkf_main_type * enkf_main , enkf_fs_type * src_fs, const char * key , int report_step) { + const int ens_size = enkf_main_get_ensemble_size( enkf_main ); + vector_type * node_ensemble = vector_alloc_new( ); + const enkf_config_node_type * config_node = ensemble_config_get_node(enkf_main_get_ensemble_config(enkf_main), key ); + node_id_type node_id = {.report_step = report_step , + .iens = -1 }; + int iens; + + + for (iens = 0; iens < ens_size; iens++) { + enkf_node_type * node = enkf_node_alloc( config_node ); + node_id.iens = iens; + enkf_node_load( node , src_fs , node_id); + vector_append_owned_ref( node_ensemble , node , enkf_node_free__ ); + } + + return node_ensemble; +} + +/*****************************************************************/ + + + + + + +static void enkf_main_node_mean( const vector_type * ensemble , enkf_node_type * mean ) { + if (vector_get_size( ensemble ) == 0) + util_abort("%s: internal error - calculation average of empty list\n",__func__); + { + int iens; + enkf_node_clear( mean ); + for (iens = 0; iens < vector_get_size( ensemble ); iens++) + enkf_node_iadd( mean , (const enkf_node_type * ) vector_iget_const( ensemble , iens) ); + + enkf_node_scale( mean , 1.0 / vector_get_size( ensemble ) ); + } +} + + +/** + This function calculates the node standard deviation from the + ensemble. The mean can be NULL, in which case it is assumed that + the mean has already been shifted away from the ensemble. +*/ + + +static void enkf_main_node_std( const vector_type * ensemble , const enkf_node_type * mean , enkf_node_type * std) { + if (vector_get_size( ensemble ) == 0) + util_abort("%s: internal error - calculation std of empty list\n",__func__); + { + int iens; + enkf_node_clear( std ); + for (iens = 0; iens < vector_get_size( ensemble ); iens++) + enkf_node_iaddsqr( std , (const enkf_node_type * ) vector_iget_const( ensemble, iens) ); + enkf_node_scale(std , 1.0 / vector_get_size( ensemble )); + + if (mean != NULL) { + enkf_node_scale( std , -1 ); + enkf_node_iaddsqr( std , mean ); + enkf_node_scale( std , -1 ); + } + + enkf_node_sqrt( std ); + } +} + + +void enkf_main_inflate_node(enkf_main_type * enkf_main , enkf_fs_type * src_fs , enkf_fs_type * target_fs , int report_step , const char * key , const enkf_node_type * min_std) { + int ens_size = enkf_main_get_ensemble_size(enkf_main); + vector_type * ensemble = enkf_main_alloc_node_ensemble( enkf_main , src_fs , key , report_step ); // Was ANALYZED + enkf_node_type * mean = enkf_node_copyc( (const enkf_node_type * ) vector_iget_const( ensemble, 0) ); + enkf_node_type * std = enkf_node_copyc( mean ); + int iens; + + /* Shifting away the mean */ + enkf_main_node_mean( ensemble , mean ); + enkf_main_node_std( ensemble , mean , std); + enkf_node_scale( mean , -1 ); + for (iens = 0; iens < ens_size; iens++) + enkf_node_iadd( (enkf_node_type * ) vector_iget(ensemble,iens), mean ); + + enkf_node_scale( mean , -1 ); + + /*****************************************************************/ + /* + Now we have the ensemble represented as a mean and an ensemble of + deviations from the mean. This is the form suitable for actually + doing the inflation. + */ + { + enkf_node_type * inflation = enkf_node_copyc( mean ); + enkf_node_set_inflation( inflation , std , min_std ); + + for (iens = 0; iens < vector_get_size( ensemble ); iens++) + enkf_node_imul( (enkf_node_type * ) vector_iget( ensemble, iens) , inflation ); + + enkf_node_free( inflation ); + } + + + /* Add the mean back in - and store the updated node to disk.*/ + for (iens = 0; iens < ens_size; iens++) { + node_id_type node_id = {.report_step = report_step , .iens = iens }; + enkf_node_iadd( (enkf_node_type * ) vector_iget( ensemble, iens) , mean ); + enkf_node_store( (enkf_node_type * ) vector_iget( ensemble, iens) , target_fs , node_id); + } + + enkf_node_free( mean ); + enkf_node_free( std ); + vector_free( ensemble ); +} + + + +/** + Denne burde istedet loope gjennom noklene fra use_count + direkte. +*/ + +void enkf_main_inflate(enkf_main_type * enkf_main , enkf_fs_type * src_fs , enkf_fs_type * target_fs , int report_step , hash_type * use_count) { + stringlist_type * keys = ensemble_config_alloc_keylist_from_var_type(enkf_main_get_ensemble_config(enkf_main), PARAMETER ); + + for (int ikey = 0; ikey < stringlist_get_size( keys ); ikey++) { + const char * key = stringlist_iget( keys , ikey ); + if (hash_get_counter(use_count , key) > 0) { + const enkf_config_node_type * config_node = ensemble_config_get_node(enkf_main_get_ensemble_config(enkf_main), key ); + const enkf_node_type * min_std = enkf_config_node_get_min_std( config_node ); + + if (min_std != NULL) + enkf_main_inflate_node(enkf_main , src_fs , target_fs , report_step , key , min_std ); + + } + } + stringlist_free( keys ); +} + + + + +static int __get_active_size(const ensemble_config_type * ensemble_config , enkf_fs_type * fs , const char * key, int report_step , const active_list_type * active_list) { + const enkf_config_node_type * config_node = ensemble_config_get_node( ensemble_config , key ); + /** + This is very awkward; the problem is that for the GEN_DATA + type the config object does not really own the size. Instead + the size is pushed (on load time) from gen_data instances to + the gen_data_config instance. Therefor we have to assert + that at least one gen_data instance has been loaded (and + consequently updated the gen_data_config instance) before we + query for the size. + */ + { + if (enkf_config_node_get_impl_type( config_node ) == GEN_DATA) { + enkf_node_type * node = enkf_node_alloc( config_node ); + node_id_type node_id = {.report_step = report_step , + .iens = 0 }; + + enkf_node_load( node , fs , node_id ); + enkf_node_free( node ); + } + } + + { + active_mode_type active_mode = active_list_get_mode( active_list ); + int active_size; + if (active_mode == INACTIVE) + active_size = 0; + else if (active_mode == ALL_ACTIVE) + active_size = enkf_config_node_get_data_size( config_node , report_step ); + else if (active_mode == PARTLY_ACTIVE) + active_size = active_list_get_active_size( active_list , -1 ); + else { + util_abort("%s: internal error .. \n",__func__); + active_size = -1; /* Compiler shut up */ + } + return active_size; + } +} + + +/*****************************************************************/ +/** + Helper structs used to pass information to the multithreaded serialize and + deserialize routines. A serialize_info structure is used at three levels, and + this also reflects on the degree of change of the various members. + + One ministep: This corresponds to one full execution of the function + enkf_main_analysis_update(). + + One dataset: Each ministep can consist of several local dataset. + + One node: Each local dataset will consist of several nodes. + + The members explicitly marked with a mutable: comment will vary in the + lifetime of the serialization_info, the other members will be constant. +*/ + +typedef struct { + int row_offset; + const active_list_type * active_list; + const char * key; +} serialize_node_info_type; + + +typedef struct { + enkf_fs_type * src_fs; + enkf_fs_type * target_fs; + const ensemble_config_type * ensemble_config; + int iens1; /* Inclusive lower limit. */ + int iens2; /* NOT inclusive upper limit. */ + int report_step; + int target_step; + run_mode_type run_mode; + matrix_type * A; + const int_vector_type * iens_active_index; + + std::vector active_size; /* mutable: For the serialization of one dataset - many nodes */ + std::vector row_offset; /* mutable: For the serialization of one dataset - many nodes */ + serialize_node_info_type * node_info; /* mutable: For the serialization of one node */ +} serialize_info_type; + + +static void serialize_node( enkf_fs_type * fs , + const enkf_config_node_type * config_node, + int iens , + int report_step , + int row_offset , + int column, + const active_list_type * active_list, + matrix_type * A) { + + enkf_node_type * node = enkf_node_alloc( config_node ); + node_id_type node_id = {.report_step = report_step, .iens = iens }; + enkf_node_serialize( node , fs , node_id , active_list , A , row_offset , column); + enkf_node_free( node ); +} + + +static void * serialize_nodes_mt( void * arg ) { + serialize_info_type * info = (serialize_info_type *) arg; + const auto * node_info = info->node_info; + const enkf_config_node_type * config_node = ensemble_config_get_node( info->ensemble_config , node_info->key ); + for (int iens = info->iens1; iens < info->iens2; iens++) { + int column = int_vector_iget( info->iens_active_index , iens); + if (column >= 0) { + serialize_node( info->src_fs , + config_node, + iens , + info->report_step , + node_info->row_offset , + column, + node_info->active_list , + info->A ); + } + } + return NULL; +} + + +static void enkf_main_serialize_node( const char * node_key , + const active_list_type * active_list , + int row_offset , + thread_pool_type * work_pool , + serialize_info_type * serialize_info) { + + /* Multithreaded serializing*/ + const int num_cpu_threads = thread_pool_get_max_running( work_pool ); + serialize_node_info_type node_info[num_cpu_threads]; + int icpu; + + thread_pool_restart( work_pool ); + for (icpu = 0; icpu < num_cpu_threads; icpu++) { + node_info[icpu].key = node_key; + node_info[icpu].active_list = active_list; + node_info[icpu].row_offset = row_offset; + serialize_info[icpu].node_info = &node_info[icpu]; + + thread_pool_add_job( work_pool , serialize_nodes_mt , &serialize_info[icpu]); + } + thread_pool_join( work_pool ); + + for (icpu = 0; icpu < num_cpu_threads; icpu++) + serialize_info[icpu].node_info = nullptr; +} + + + +/** + The return value is the number of rows in the serialized + A matrix. +*/ + +static int enkf_main_serialize_dataset( const ensemble_config_type * ens_config , + const local_dataset_type * dataset , + int report_step, + hash_type * use_count , + thread_pool_type * work_pool, + serialize_info_type * serialize_info) { + + matrix_type * A = serialize_info->A; + int ens_size = matrix_get_columns( A ); + int current_row = 0; + + const auto& unscaled_keys = local_dataset_unscaled_keys(dataset); + serialize_info->active_size.resize( unscaled_keys.size() ); + serialize_info->row_offset.resize( unscaled_keys.size() ); + for (int ikw = 0; ikw < unscaled_keys.size(); ikw++) { + const auto& key = unscaled_keys[ikw]; + const active_list_type * active_list = local_dataset_get_node_active_list( dataset , key.c_str() ); + enkf_fs_type * src_fs = serialize_info->src_fs; + serialize_info->active_size[ikw] = __get_active_size( ens_config , src_fs , key.c_str() , report_step , active_list ); + serialize_info->row_offset[ikw] = current_row; + + { + int matrix_rows = matrix_get_rows( A ); + if ((serialize_info->active_size[ikw] + current_row) > matrix_rows) + matrix_resize( A , matrix_rows + 2 * serialize_info->active_size[ikw] , ens_size , true ); + } + + if (serialize_info->active_size[ikw] > 0) { + enkf_main_serialize_node( key.c_str() , active_list , serialize_info->row_offset[ikw] , work_pool , serialize_info ); + current_row += serialize_info->active_size[ikw]; + } + } + matrix_shrink_header( A , current_row , ens_size ); + return matrix_get_rows( A ); +} + +static void deserialize_node( enkf_fs_type * target_fs, + enkf_fs_type * src_fs, + const enkf_config_node_type * config_node, + int iens, + int target_step , + int row_offset , + int column, + const active_list_type * active_list, + matrix_type * A) { + + node_id_type node_id = {.report_step = target_step, .iens = iens }; + enkf_node_type * node = enkf_node_alloc( config_node ); + + // If partly active, init node from source fs (deserialize will fill it only in part) + enkf_node_load( node , src_fs , node_id); + + // deserialize the matrix into the node (and writes it to the target fs) + enkf_node_deserialize(node , target_fs , node_id , active_list , A , row_offset , column); + state_map_update_undefined(enkf_fs_get_state_map(target_fs) , iens , STATE_INITIALIZED); + enkf_node_free( node ); +} + + + +static void * deserialize_nodes_mt( void * arg ) { + serialize_info_type * info = (serialize_info_type *) arg; + const auto * node_info = info->node_info; + const enkf_config_node_type * config_node = ensemble_config_get_node(info->ensemble_config, node_info->key); + for (int iens = info->iens1; iens < info->iens2; iens++) { + int column = int_vector_iget( info->iens_active_index , iens ); + if (column >= 0) + deserialize_node( info->target_fs , info->src_fs, config_node, iens , info->target_step , node_info->row_offset , column, node_info->active_list , info->A ); + } + return NULL; +} + + +static void enkf_main_deserialize_dataset( ensemble_config_type * ensemble_config , + const local_dataset_type * dataset , + serialize_info_type * serialize_info , + thread_pool_type * work_pool ) { + + const int num_cpu_threads = thread_pool_get_max_running( work_pool ); + const auto& unscaled_keys = local_dataset_unscaled_keys(dataset); + for (int ikw=0; ikw < unscaled_keys.size(); ikw++) { + const auto& key = unscaled_keys[ikw]; + if (serialize_info->active_size[ikw] > 0) { + const active_list_type * active_list = local_dataset_get_node_active_list( dataset , key.c_str() ); + + { + /* Multithreaded */ + int icpu; + serialize_node_info_type node_info[num_cpu_threads]; + thread_pool_restart( work_pool ); + for (icpu = 0; icpu < num_cpu_threads; icpu++) { + node_info[icpu].key = key.c_str(); + node_info[icpu].active_list = active_list; + node_info[icpu].row_offset = serialize_info->row_offset[ikw]; + serialize_info[icpu].node_info = &node_info[icpu]; + + thread_pool_add_job( work_pool , deserialize_nodes_mt , &serialize_info[icpu]); + } + thread_pool_join( work_pool ); + } + } + } +} + + +static void serialize_info_free( serialize_info_type * serialize_info ) { + delete[] serialize_info; +} + +static serialize_info_type * serialize_info_alloc( enkf_fs_type * src_fs, + enkf_fs_type * target_fs , + const ensemble_config_type * ensemble_config, + const int_vector_type * iens_active_index , + int target_step , + enkf_state_type ** ensemble , + run_mode_type run_mode , + int report_step , + matrix_type * A , + int num_cpu_threads ) { + + serialize_info_type * serialize_info = new serialize_info_type[num_cpu_threads]; + int ens_size = int_vector_size(iens_active_index); + int icpu; + int iens_offset = 0; + for (icpu = 0; icpu < num_cpu_threads; icpu++) { + serialize_info[icpu].ensemble_config = ensemble_config; + serialize_info[icpu].iens_active_index = iens_active_index; + serialize_info[icpu].run_mode = run_mode; + serialize_info[icpu].src_fs = src_fs; + serialize_info[icpu].target_fs = target_fs; + serialize_info[icpu].target_step = target_step; + serialize_info[icpu].report_step = report_step; + serialize_info[icpu].A = A; + serialize_info[icpu].iens1 = iens_offset; + serialize_info[icpu].iens2 = iens_offset + (ens_size - iens_offset) / (num_cpu_threads - icpu); + serialize_info[icpu].node_info = nullptr; + iens_offset = serialize_info[icpu].iens2; + } + serialize_info[num_cpu_threads - 1].iens2 = ens_size; + return serialize_info; +} + +static module_info_type * enkf_main_module_info_alloc( const local_ministep_type* ministep, + const obs_data_type * obs_data, + const local_dataset_type * dataset , + const local_obsdata_type * local_obsdata , + int * active_size , + int * row_offset) +{ + // Create and initialize the module_info instance. + module_info_type * module_info = module_info_alloc(local_ministep_get_name(ministep)); + + { /* Init data blocks in module_info */ + stringlist_type * update_keys = local_dataset_alloc_keys( dataset ); + const int num_kw = stringlist_get_size( update_keys ); + module_data_block_vector_type * module_data_block_vector = module_info_get_data_block_vector(module_info); + + for (int ikw=0; ikw < num_kw; ikw++) { + const char * key = stringlist_iget(update_keys , ikw); + const active_list_type * active_list = local_dataset_get_node_active_list( dataset , key ); + const module_data_block_type * data_block = module_data_block_alloc( key, active_list_get_active(active_list), row_offset[ikw], active_size[ikw] ); + module_data_block_vector_add_data_block(module_data_block_vector, data_block); + } + stringlist_free( update_keys ); + } + + { /* Init obs blocks in module_info */ + module_obs_block_vector_type * module_obs_block_vector = module_info_get_obs_block_vector ( module_info ); + int current_row = 0; + for (int block_nr = 0; block_nr < local_obsdata_get_size( local_obsdata ); block_nr++) { + const obs_block_type * obs_block = obs_data_iget_block_const( obs_data , block_nr); + int total_size = obs_block_get_size(obs_block); + local_obsdata_node_type * node = local_obsdata_iget ( local_obsdata, block_nr ); + const char * key = local_obsdata_node_get_key ( node ); + const active_list_type * active_list = local_obsdata_node_get_active_list( node ); + int n_active = active_list_get_active_size(active_list, total_size); + module_obs_block_type * module_obs_block = module_obs_block_alloc(key, active_list_get_active(active_list), current_row, n_active); + module_obs_block_vector_add_obs_block ( module_obs_block_vector, module_obs_block ); + current_row += n_active; + } + } + + return module_info; +} + +static void enkf_main_module_info_free( module_info_type * module_info ) { + free( module_info ); +} + +void enkf_main_fprintf_PC(const char * filename , + matrix_type * PC , + matrix_type * PC_obs) { + + FILE * stream = util_mkdir_fopen(filename , "w"); + const int num_PC = matrix_get_rows( PC ); + const int ens_size = matrix_get_columns( PC ); + int ipc,iens; + + for (ipc = 0; ipc < num_PC; ipc++) + fprintf(stream , "%10.6f " , matrix_iget( PC_obs , ipc , 0)); + fprintf(stream , "\n"); + + for (iens = 0; iens < ens_size; iens++) { + for (ipc = 0; ipc < num_PC; ipc++) + fprintf(stream ,"%10.6f " , matrix_iget( PC , ipc, iens )); + fprintf(stream , "\n"); + } + fclose( stream ); +} + + +void enkf_main_get_PC( const matrix_type * S, + const matrix_type * dObs, + double truncation , + int ncomp , + matrix_type * PC , + matrix_type * PC_obs , + double_vector_type * singular_values) { + + enkf_linalg_get_PC( S , dObs , truncation , ncomp , PC , PC_obs , singular_values); +} + + + + + + + +static void assert_matrix_size(const matrix_type * m , const char * name , int rows , int columns) { + if (m) { + if (!matrix_check_dims(m , rows , columns)) + util_abort("%s: matrix mismatch %s:[%d,%d] - expected:[%d, %d]", __func__ , name , matrix_get_rows(m) , matrix_get_columns(m) , rows , columns); + } else + util_abort("%s: matrix:%s is NULL \n",__func__ , name); +} + +static void assert_size_equal(int ens_size , const bool_vector_type * ens_mask) { + if (bool_vector_size( ens_mask ) != ens_size) + util_abort("%s: fundamental inconsistency detected. Total ens_size:%d mask_size:%d \n",__func__ , ens_size , bool_vector_size( ens_mask )); +} + + + + + +// Opens and returns a log file. A subroutine of enkf_main_UPDATE. +static FILE * enkf_main_log_step_list(enkf_main_type * enkf_main, const int_vector_type * step_list) { + const char * log_path = analysis_config_get_log_path(enkf_main_get_analysis_config(enkf_main)); + char * log_file; + if (int_vector_size(step_list) == 1) + log_file = util_alloc_sprintf("%s%c%04d", log_path, UTIL_PATH_SEP_CHAR, int_vector_iget(step_list, 0)); + else + log_file = util_alloc_sprintf("%s%c%04d-%04d", log_path, UTIL_PATH_SEP_CHAR, int_vector_iget(step_list, 0), + int_vector_get_last(step_list)); + FILE * log_stream = util_fopen(log_file, "w"); + + free(log_file); + return log_stream; +} + + +/** + * This is THE ENKF update function. It should only be called from enkf_main_UPDATE. + */ +static void enkf_main_update__(enkf_main_type * enkf_main, const int_vector_type * step_list, enkf_fs_type * source_fs, + enkf_fs_type * target_fs, int target_step, run_mode_type run_mode, + const analysis_config_type * analysis_config, const local_updatestep_type * updatestep, + const int total_ens_size) +{ + /* + Observations and measurements are collected in these temporary + structures. obs_data is a precursor for the 'd' vector, and + meas_data is a precursor for the 'S' matrix'. + + The reason for going via these temporary structures is to support + deactivating observations which should not be used in the update + process. + */ + bool_vector_type * ens_mask = bool_vector_alloc(total_ens_size, false); + state_map_type * source_state_map = enkf_fs_get_state_map( source_fs ); + + state_map_select_matching(source_state_map, ens_mask, STATE_HAS_DATA); + { + FILE * log_stream = enkf_main_log_step_list(enkf_main, step_list); + double global_std_scaling = analysis_config_get_global_std_scaling(analysis_config); + meas_data_type * meas_data = meas_data_alloc(ens_mask); + int_vector_type * ens_active_list = bool_vector_alloc_active_list(ens_mask); + + /* + Copy all the parameter nodes from source case to target case; + nodes which are updated will be fetched from the new target + case, and nodes which are not updated will be manually copied + over there. + */ + if (target_fs != source_fs) { + const ensemble_config_type * ensemble_config = enkf_main_get_ensemble_config(enkf_main); + stringlist_type * param_keys = ensemble_config_alloc_keylist_from_var_type(ensemble_config, PARAMETER); + for (int i = 0; i < stringlist_get_size(param_keys); i++) { + const char * key = stringlist_iget(param_keys, i); + enkf_config_node_type * config_node = ensemble_config_get_node(ensemble_config, key); + enkf_node_type * data_node = enkf_node_alloc(config_node); + for (int j = 0; j < int_vector_size(ens_active_list); j++) { + node_id_type node_id; + node_id.iens = int_vector_iget(ens_active_list, j); + node_id.report_step = 0; + + enkf_node_load(data_node, source_fs, node_id); + enkf_node_store(data_node, target_fs, node_id); + } + enkf_node_free(data_node); + } + stringlist_free(param_keys); + } + + { + hash_type * use_count = hash_alloc(); + int current_step = int_vector_get_last(step_list); + + + /* Looping over local analysis ministep */ + for (int ministep_nr = 0; ministep_nr < local_updatestep_get_num_ministep(updatestep); ministep_nr++) { + local_ministep_type * ministep = local_updatestep_iget_ministep(updatestep, ministep_nr); + local_obsdata_type * obsdata = local_ministep_get_obsdata(ministep); + + obs_data_type * obs_data = obs_data_alloc(global_std_scaling); + obs_data_reset(obs_data); + meas_data_reset(meas_data); + + /* + Temporarily we will just force the timestep from the input + argument onto the obsdata instance; in the future the + obsdata should hold it's own here. + */ + local_obsdata_reset_tstep_list(obsdata, step_list); + + const analysis_config_type * analysis_config = enkf_main_get_analysis_config(enkf_main); + double alpha = analysis_config_get_alpha(analysis_config); + double std_cutoff = analysis_config_get_std_cutoff(analysis_config); + + if (analysis_config_get_std_scale_correlated_obs(analysis_config)) { + double scale_factor = enkf_obs_scale_correlated_std(enkf_main->obs, source_fs, + ens_active_list, obsdata, alpha, std_cutoff, false); + res_log_finfo("Scaling standard deviation in obdsata set:%s with %g", + local_obsdata_get_name(obsdata), scale_factor); + } + enkf_obs_get_obs_and_measure_data(enkf_main->obs, source_fs, obsdata, + ens_active_list, meas_data, obs_data); + + enkf_analysis_deactivate_outliers(obs_data, meas_data, + std_cutoff, alpha, enkf_main->verbose); + local_ministep_add_obs_data(ministep, obs_data); + + if (enkf_main->verbose) + enkf_analysis_fprintf_obs_summary(obs_data, meas_data, step_list, local_ministep_get_name(ministep), stdout); + enkf_analysis_fprintf_obs_summary(obs_data, meas_data, step_list, local_ministep_get_name(ministep), log_stream); + + if ((obs_data_get_active_size(obs_data) > 0) && (meas_data_get_active_obs_size(meas_data) > 0)) + enkf_main_analysis_update(enkf_main, + target_fs, + ens_mask, + target_step, + use_count, + run_mode, + int_vector_get_first(step_list), + current_step, + ministep, + meas_data, + obs_data); + else if (target_fs != source_fs) + res_log_ferror("No active observations/parameters for MINISTEP: %s.", + local_ministep_get_name(ministep)); + } + enkf_main_inflate(enkf_main, source_fs, target_fs, current_step, use_count); + hash_free(use_count); + } + + + { + state_map_type * target_state_map = enkf_fs_get_state_map(target_fs); + + if (target_state_map != source_state_map) { + state_map_set_from_inverted_mask(target_state_map, ens_mask, STATE_PARENT_FAILURE); + state_map_set_from_mask(target_state_map, ens_mask, STATE_INITIALIZED); + enkf_fs_fsync(target_fs); + } + } + + int_vector_free(ens_active_list); + meas_data_free(meas_data); + fclose(log_stream); + } + bool_vector_free( ens_mask); +} + +static void enkf_main_store_PC(const analysis_config_type * analysis_config, + int step1, + int step2, + int active_ens_size, + const local_ministep_type * ministep, + const matrix_type * dObs, + const matrix_type * S) { + double truncation = -1; + int ncomp = active_ens_size - 1; + matrix_type * PC = matrix_alloc(1,1); + matrix_type * PC_obs = matrix_alloc(1,1); + double_vector_type * singular_values = double_vector_alloc(0,0); + local_obsdata_type * obsdata = local_ministep_get_obsdata( ministep ); + const char * obsdata_name = local_obsdata_get_name( obsdata ); + + enkf_main_get_PC( S , dObs , truncation , ncomp , PC , PC_obs , singular_values); + { + char * filename = util_alloc_sprintf(analysis_config_get_PC_filename(analysis_config) , step1 , step2 , obsdata_name); + char * full_path = util_alloc_filename( analysis_config_get_PC_path(analysis_config) , filename , NULL ); + + enkf_main_fprintf_PC( full_path , PC , PC_obs); + + free( full_path ); + free( filename ); + } + matrix_free( PC ); + matrix_free( PC_obs ); + double_vector_free( singular_values ); +} + + + + +static void enkf_main_analysis_update( enkf_main_type * enkf_main , + enkf_fs_type * target_fs , + const bool_vector_type * ens_mask , + int target_step , + hash_type * use_count, + run_mode_type run_mode , + int step1 , + int step2 , + const local_ministep_type * ministep , + const meas_data_type * forecast , + obs_data_type * obs_data) { + + const int cpu_threads = 4; + const int matrix_start_size = 250000; + thread_pool_type * tp = thread_pool_alloc( cpu_threads , false ); + int active_ens_size = meas_data_get_active_ens_size( forecast ); + int active_size = obs_data_get_active_size( obs_data ); + matrix_type * X = matrix_alloc( active_ens_size , active_ens_size ); + matrix_type * S = meas_data_allocS( forecast ); + matrix_type * R = obs_data_allocR( obs_data ); + matrix_type * dObs = obs_data_allocdObs( obs_data ); + matrix_type * A = matrix_alloc( matrix_start_size , active_ens_size ); + matrix_type * E = NULL; + matrix_type * D = NULL; + matrix_type * localA = NULL; + int_vector_type * iens_active_index = bool_vector_alloc_active_index_list(ens_mask , -1); + const bool_vector_type * obs_mask = obs_data_get_active_mask(obs_data); + + const analysis_config_type * analysis_config = enkf_main_get_analysis_config(enkf_main); + analysis_module_type * module = analysis_config_get_active_module(analysis_config); + if ( local_ministep_has_analysis_module (ministep)) + module = local_ministep_get_analysis_module (ministep); + + assert_matrix_size(X , "X" , active_ens_size , active_ens_size); + assert_matrix_size(S , "S" , active_size , active_ens_size); + assert_matrix_size(R , "R" , active_size , active_size); + assert_size_equal( enkf_main_get_ensemble_size( enkf_main ) , ens_mask ); + + if (analysis_module_check_option( module , ANALYSIS_NEED_ED)) { + E = obs_data_allocE( obs_data , enkf_main->shared_rng , active_ens_size ); + D = obs_data_allocD( obs_data , E , S ); + + assert_matrix_size( E , "E" , active_size , active_ens_size); + assert_matrix_size( D , "D" , active_size , active_ens_size); + } + + if (analysis_module_check_option( module , ANALYSIS_SCALE_DATA)) + obs_data_scale( obs_data , S , E , D , R , dObs ); + + if (analysis_module_check_option( module , ANALYSIS_USE_A) || analysis_module_check_option(module , ANALYSIS_UPDATE_A)) + localA = A; + + /*****************************************************************/ + + analysis_module_init_update( module , ens_mask , obs_mask, S , R , dObs , E , D, enkf_main->shared_rng); + { + hash_iter_type * dataset_iter = local_ministep_alloc_dataset_iter( ministep ); + serialize_info_type * serialize_info = serialize_info_alloc( target_fs, //src_fs - we have already copied the parameters from the src_fs to the target_fs + target_fs , + enkf_main_get_ensemble_config(enkf_main), + iens_active_index, + target_step , + enkf_main->ensemble, + run_mode , + step2 , + A , + cpu_threads); + + + if (analysis_config_get_store_PC(analysis_config)) + enkf_main_store_PC(analysis_config, step1, step2, active_ens_size, ministep, dObs, S); + + if (localA == NULL) + analysis_module_initX( module , X , NULL , S , R , dObs , E , D, enkf_main->shared_rng); + + while (!hash_iter_is_complete( dataset_iter )) { + const char * dataset_name = hash_iter_get_next_key( dataset_iter ); + const local_dataset_type * dataset = local_ministep_get_dataset( ministep , dataset_name ); + if (local_dataset_get_size( dataset )) { + local_obsdata_type * local_obsdata = local_ministep_get_obsdata( ministep ); + const auto& unscaled_keys = local_dataset_unscaled_keys(dataset); + if (unscaled_keys.size()) { + + /* + The update for one local_dataset instance consists of two main chunks: + + 1. The first chunk updates all the parameters which don't have row + scaling attached. These parameters are serialized together to the A + matrix and all the parameters are updated in one go. + + 2. The second chunk is loop over all the parameters which have row + scaling attached. These parameters are updated one at a time. + */ + + // Part 1: Parameters which do not have row scaling attached. + enkf_main_serialize_dataset(enkf_main_get_ensemble_config(enkf_main), dataset , step2 , use_count , tp , serialize_info); + module_info_type * module_info = enkf_main_module_info_alloc(ministep, obs_data, dataset, local_obsdata, serialize_info->active_size.data() , serialize_info->row_offset.data()); + + if (analysis_module_check_option( module , ANALYSIS_UPDATE_A)){ + if (analysis_module_check_option( module , ANALYSIS_ITERABLE)){ + analysis_module_updateA( module , localA , S , R , dObs , E , D , module_info, enkf_main->shared_rng); + } + else + analysis_module_updateA( module , localA , S , R , dObs , E , D , module_info, enkf_main->shared_rng); + } else { + if (analysis_module_check_option( module , ANALYSIS_USE_A)){ + analysis_module_initX( module , X , localA , S , R , dObs , E , D, enkf_main->shared_rng); + } + matrix_inplace_matmul_mt2( A , X , tp ); + } + + enkf_main_deserialize_dataset( enkf_main_get_ensemble_config( enkf_main ) , dataset , serialize_info , tp); + enkf_main_module_info_free( module_info ); + } + + // Part 2: Parameters with row scaling attached - to support distance based localization. + { + const auto& scaled_keys = local_dataset_scaled_keys(dataset); + if (scaled_keys.size()) { + + if (analysis_module_check_option( module , ANALYSIS_UPDATE_A)) + throw std::logic_error("Sorry - row scaling for distance based localization can not be combined with analysis modules which update the A matrix"); + + for (int ikw=0; ikw < scaled_keys.size(); ikw++) { + const auto& key = scaled_keys[ikw]; + const active_list_type * active_list = local_dataset_get_node_active_list(dataset, key.c_str()); + const auto matrix_rows = matrix_get_rows(A); + const auto* config_node = ensemble_config_get_node(serialize_info->ensemble_config, key.c_str()); + const auto node_size = enkf_config_node_get_data_size(config_node, serialize_info->report_step); + if (matrix_get_rows(A) < node_size) + matrix_resize( A , node_size , active_ens_size , false); + + for (int iens = 0; iens < bool_vector_size(ens_mask); iens++) { + int column = int_vector_iget( serialize_info->iens_active_index , iens); + if (column >= 0) { + serialize_node(serialize_info->src_fs, + config_node, + iens, + serialize_info->report_step, + 0, + column, + active_list, + A); + } + } + matrix_shrink_header( A , local_dataset_get_row_scaling(dataset, key.c_str())->size(), matrix_get_columns(A) ); + + if (analysis_module_check_option( module , ANALYSIS_USE_A)) + analysis_module_initX( module , X , localA , S , R , dObs , E , D, enkf_main->shared_rng); + + const row_scaling_type * row_scaling = local_dataset_get_row_scaling( dataset, key.c_str() ); + row_scaling_multiply(row_scaling, A, X); + + for (int iens = 0; iens < bool_vector_size(ens_mask); iens++) { + int column = int_vector_iget( serialize_info->iens_active_index , iens); + if (column >= 0) { + deserialize_node(serialize_info->target_fs, + serialize_info->src_fs, + ensemble_config_get_node(serialize_info->ensemble_config, key.c_str()), + iens, + serialize_info->target_step, + 0, + column, + active_list, + A); + } + } + } + } + } + } + } + hash_iter_free( dataset_iter ); + serialize_info_free( serialize_info ); + } + analysis_module_complete_update( module ); + + /*****************************************************************/ + + int_vector_free(iens_active_index); + matrix_safe_free( E ); + matrix_safe_free( D ); + matrix_free( S ); + matrix_free( R ); + matrix_free( dObs ); + matrix_free( X ); + matrix_free( A ); +} + + +/** + * This is T H E EnKF update routine. + */ +bool enkf_main_UPDATE(enkf_main_type * enkf_main , const int_vector_type * step_list, enkf_fs_type * source_fs , enkf_fs_type * target_fs , int target_step , run_mode_type run_mode) { + /* + If merge_observations is true all observations in the time + interval [step1+1,step2] will be used, otherwise only the last + observation at step2 will be used. + */ + + state_map_type * source_state_map = enkf_fs_get_state_map( source_fs ); + const analysis_config_type * analysis_config = enkf_main_get_analysis_config( enkf_main ); + const int active_ens_size = state_map_count_matching( source_state_map , STATE_HAS_DATA ); + const int total_ens_size = enkf_main_get_ensemble_size(enkf_main); + + // exit if not enough realisations + if (!analysis_config_have_enough_realisations(analysis_config , active_ens_size, total_ens_size)) { + fprintf(stderr,"** ERROR ** There are %d active realisations left, which is less than the minimum specified - stopping assimilation.\n" , + active_ens_size ); + return false; + } + + local_config_type * local_config = enkf_main->local_config; + const local_updatestep_type * updatestep = local_config_get_updatestep( local_config ); + + // exit if multi step update + if ((local_updatestep_get_num_ministep( updatestep ) > 1) && + (analysis_config_get_module_option( analysis_config , ANALYSIS_ITERABLE))) { + util_exit("** ERROR: Can not combine iterable modules with multi step updates - sorry\n"); + } + + enkf_main_update__(enkf_main, + step_list, + source_fs, + target_fs, + target_step, + run_mode, + analysis_config, + updatestep, + total_ens_size); + + return true; +} + + + + + + +static bool enkf_main_smoother_update__(enkf_main_type * enkf_main , const int_vector_type * step_list , enkf_fs_type * source_fs, enkf_fs_type * target_fs) { + return enkf_main_UPDATE( enkf_main , step_list , source_fs , target_fs , 0 , SMOOTHER_RUN ); +} + + +bool enkf_main_smoother_update(enkf_main_type * enkf_main , enkf_fs_type * source_fs, enkf_fs_type * target_fs) { + int stride = 1; + int step2; + time_map_type * time_map = enkf_fs_get_time_map( source_fs ); + int_vector_type * step_list; + bool update_done; + + step2 = time_map_get_last_step( time_map ); + if (step2 < 0) + step2 = model_config_get_last_history_restart(enkf_main_get_model_config(enkf_main)); + + step_list = enkf_main_update_alloc_step_list( enkf_main , 0 , step2 , stride); + update_done = enkf_main_smoother_update__( enkf_main , step_list , source_fs, target_fs ); + int_vector_free( step_list ); + + return update_done; +} + + +static void enkf_main_monitor_job_queue ( const enkf_main_type * enkf_main, job_queue_type * job_queue) { + const analysis_config_type * analysis_config = enkf_main_get_analysis_config( enkf_main ); + if (analysis_config_get_stop_long_running(analysis_config)) { + bool cont = true; + while (cont) { + //Check if minimum number of realizations have run, and if so, kill the rest after a certain time + if (analysis_config_have_enough_realisations(analysis_config, job_queue_get_num_complete(job_queue), enkf_main_get_ensemble_size(enkf_main))) { + job_queue_set_auto_job_stop_time(job_queue); + cont = false; + } + + //Check if all possible successes satisfies the minimum number of realizations threshold. If not so, it is time to give up + int possible_successes = job_queue_get_num_running(job_queue) + + job_queue_get_num_waiting(job_queue) + + job_queue_get_num_pending(job_queue) + + job_queue_get_num_complete(job_queue); + + + if (analysis_config_have_enough_realisations(analysis_config, possible_successes, enkf_main_get_ensemble_size(enkf_main))) { + cont = false; + } + + if (cont) { + util_usleep(10000); + } + } + + } +} + + + +void enkf_main_isubmit_job( enkf_main_type * enkf_main , run_arg_type * run_arg , job_queue_type * job_queue) { + const ecl_config_type * ecl_config = enkf_main_get_ecl_config( enkf_main ); + const queue_config_type * queue_config = enkf_main_get_queue_config(enkf_main); + const char * job_script = queue_config_get_job_script( queue_config ); + + const char * run_path = run_arg_get_runpath( run_arg ); + + /* + The job_queue_node will take ownership of this callback_arg; and destroy it when + the job_queue_node is discarded. Observe that it will be internalized in the + queue layer as (void *) and will be discarded with free( arg ). + */ + + /* + The callback_arg pointer will leak for now. + */ + callback_arg_type * callback_arg = callback_arg_alloc( enkf_main->res_config, + run_arg, + rng_manager_iget( enkf_main->rng_manager, run_arg_get_iens(run_arg))); + { + const char * argv = run_path; + int num_cpu = queue_config_get_num_cpu( queue_config ); + if (num_cpu == 0) + num_cpu = ecl_config_get_num_cpu( ecl_config ); + + int queue_index = job_queue_add_job( job_queue , + job_script , + enkf_state_complete_forward_modelOK__, + enkf_state_complete_forward_modelRETRY__, + enkf_state_complete_forward_modelEXIT__, + callback_arg , + num_cpu, + run_path , + run_arg_get_job_name( run_arg ), + 1, + &argv); + run_arg_set_queue_index( run_arg , queue_index ); + run_arg_increase_submit_count( run_arg ); + } + +} + +static void enkf_main_write_run_path( enkf_main_type * enkf_main, + const ert_run_context_type * run_context) { + runpath_list_type * runpath_list = enkf_main_get_runpath_list(enkf_main); + runpath_list_clear(runpath_list); + for (int iens = 0; iens < ert_run_context_get_size( run_context ); iens++) { + if (ert_run_context_iactive( run_context , iens)) { + run_arg_type * run_arg = ert_run_context_iget_arg( run_context , iens); + runpath_list_add( runpath_list , + run_arg_get_iens( run_arg ), + run_arg_get_iter( run_arg ), + run_arg_get_runpath( run_arg ), + run_arg_get_job_name( run_arg )); + enkf_state_init_eclipse( enkf_main->res_config, run_arg ); + } + } + runpath_list_fprintf( runpath_list ); +} + +void enkf_main_create_run_path(enkf_main_type * enkf_main , const ert_run_context_type * run_context) { + enkf_main_init_run(enkf_main, run_context); + enkf_main_write_run_path( enkf_main , run_context ); +} + + + +void * enkf_main_isubmit_job__( void * arg ) { + arg_pack_type * arg_pack = arg_pack_safe_cast( arg ); + enkf_main_type * enkf_main = enkf_main_safe_cast( arg_pack_iget_ptr( arg_pack , 0 )); + run_arg_type * run_arg = run_arg_safe_cast( arg_pack_iget_ptr( arg_pack , 1)); + job_queue_type * job_queue = job_queue_safe_cast( arg_pack_iget_ptr( arg_pack , 2)); + + enkf_main_isubmit_job( enkf_main , run_arg , job_queue); + return NULL; +} + + + + + +static void enkf_main_submit_jobs__( enkf_main_type * enkf_main , + const ert_run_context_type * run_context , + thread_pool_type * submit_threads, + arg_pack_type ** arg_pack_list, job_queue_type * job_queue) { + { + int iens; + for (iens = 0; iens < ert_run_context_get_size( run_context ); iens++) { + run_arg_type * run_arg = ert_run_context_iget_arg( run_context , iens); + if (run_arg) { + arg_pack_type * arg_pack = arg_pack_list[iens]; + + arg_pack_append_ptr( arg_pack , enkf_main ); + arg_pack_append_ptr( arg_pack , run_arg); + arg_pack_append_ptr( arg_pack , job_queue); + + run_arg_set_run_status( run_arg, JOB_SUBMITTED ); + thread_pool_add_job(submit_threads , enkf_main_isubmit_job__ , arg_pack); + } + } + } +} + + +void enkf_main_submit_jobs( enkf_main_type * enkf_main , + const ert_run_context_type * run_context, job_queue_type * job_queue) { + + int run_size = ert_run_context_get_size( run_context ); + arg_pack_type ** arg_pack_list = (arg_pack_type **) util_malloc( run_size * sizeof * arg_pack_list ); + thread_pool_type * submit_threads = thread_pool_alloc( 4 , true ); + runpath_list_type * runpath_list = enkf_main_get_runpath_list(enkf_main); + int iens; + for (iens = 0; iens < run_size; iens++) + arg_pack_list[iens] = arg_pack_alloc( ); + + runpath_list_clear( runpath_list ); + enkf_main_submit_jobs__(enkf_main , run_context , submit_threads , arg_pack_list, job_queue); + + /* + After this join all directories/files for the simulations + have been set up correctly, and all the jobs have been added + to the job_queue manager. + */ + + thread_pool_join(submit_threads); + thread_pool_free(submit_threads); + + for (iens = 0; iens < run_size; iens++) + arg_pack_free( arg_pack_list[iens] ); + free( arg_pack_list ); +} + +/** + The special value stride == 0 means to just include step2. +*/ +int_vector_type * enkf_main_update_alloc_step_list(const enkf_main_type * enkf_main, + int load_start, + int step2, + int stride) { + int_vector_type * step_list = int_vector_alloc(0, 0); + + if (step2 < load_start) + util_abort("%s: fatal internal error: Tried to make step list %d ... %d\n", + __func__, load_start, step2); + + if (stride == 0) { + int_vector_append( step_list , step2 ); + return step_list; + } + + int step = util_int_max( 0 , load_start ); + while (true) { + int_vector_append( step_list , step ); + + if (step == step2) + break; + else { + step += stride; + if (step >= step2) { + int_vector_append( step_list , step2 ); + break; + } + + } + } + return step_list; +} + + + +/** + This function will initialize the necessary enkf_main structures + before a run. Currently this means: + + 1. Set the enkf_sched instance - either by loading from file or + by using the default. + + 2. Set up the configuration of what should be internalized. + +*/ + + +void enkf_main_init_run( enkf_main_type * enkf_main, const ert_run_context_type * run_context) { + enkf_main_init_internalization(enkf_main , ert_run_context_get_mode( run_context )); + { + stringlist_type * param_list = ensemble_config_alloc_keylist_from_var_type(enkf_main_get_ensemble_config(enkf_main), PARAMETER ); + enkf_main_initialize_from_scratch(enkf_main , + param_list , + run_context); + stringlist_free( param_list ); + } +} + +static bool enkf_main_run_analysis(enkf_main_type * enkf_main, enkf_fs_type * source_fs ,const char * target_fs_name, int iteration_number) { + bool updateOK = false; + const analysis_config_type * analysis_config = enkf_main_get_analysis_config(enkf_main); + analysis_module_type * analysis_module = analysis_config_get_active_module(analysis_config); + int pre_iteration_number = analysis_module_get_int(analysis_module, "ITER"); + + if (target_fs_name == NULL){ + fprintf(stderr,"Sorry: the updated ensemble will overwrite the current case in the iterated ensemble smoother."); + printf("Running analysis on case %s, target case is %s\n", enkf_main_get_current_fs(enkf_main), enkf_main_get_current_fs(enkf_main)); + updateOK = enkf_main_smoother_update(enkf_main, source_fs, enkf_main_get_fs(enkf_main)); + } else { + enkf_fs_type * target_fs = enkf_main_mount_alt_fs(enkf_main , target_fs_name , true ); + updateOK = enkf_main_smoother_update(enkf_main, source_fs , target_fs); + enkf_fs_decref( target_fs ); + } + + int post_iteration_number = analysis_module_get_int(analysis_module, "ITER"); + + if (post_iteration_number <= pre_iteration_number) + updateOK = false; + + if (updateOK) { + enkf_fs_type * target_fs = enkf_main_mount_alt_fs(enkf_main , target_fs_name , true ); + cases_config_set_int(enkf_fs_get_cases_config(target_fs), "iteration_number", iteration_number+1); + enkf_fs_decref( target_fs ); + } + + return updateOK; +} + +ert_run_context_type * enkf_main_alloc_ert_run_context_ENSEMBLE_EXPERIMENT(const enkf_main_type * enkf_main , enkf_fs_type * fs , bool_vector_type * iactive , int iter) { + const model_config_type * model_config = enkf_main_get_model_config(enkf_main); + return ert_run_context_alloc_ENSEMBLE_EXPERIMENT( fs, + iactive, + model_config_get_runpath_fmt(model_config), + model_config_get_jobname_fmt(model_config), + enkf_main_get_data_kw(enkf_main), + iter ); +} + + + + + +/** + This function creates a local_config file corresponding to the + default 'ALL_ACTIVE' configuration. +*/ + +void enkf_main_create_all_active_config( const enkf_main_type * enkf_main) { + + + bool single_node_update = analysis_config_get_single_node_update(enkf_main_get_analysis_config(enkf_main)); + local_config_type * local_config = enkf_main->local_config; + local_config_clear( local_config ); + { + local_updatestep_type * default_step = local_config_get_updatestep(local_config); + local_obsdata_type * obsdata = local_config_alloc_obsdata(local_config, "ALL_OBS"); + local_dataset_type * all_active_dataset = local_config_alloc_dataset(local_config, "ALL_DATA"); + local_ministep_type * ministep = local_config_alloc_ministep( local_config , "ALL_ACTIVE", NULL); + if (!ministep) + throw std::logic_error("Failed to create initial ALL_ACTIVE ministep"); + + local_updatestep_add_ministep( default_step , ministep ); + + /* Adding all observation keys */ + { + hash_iter_type * obs_iter = enkf_obs_alloc_iter( enkf_main->obs ); + while ( !hash_iter_is_complete(obs_iter) ) { + const char * obs_key = hash_iter_get_next_key( obs_iter ); + local_obsdata_node_type * obsdata_node = local_obsdata_node_alloc( obs_key , true ); + local_obsdata_add_node(obsdata, obsdata_node ); + } + local_ministep_add_obsdata(ministep, obsdata); + hash_iter_free( obs_iter ); + } + + /* Adding all node which can be updated. */ + { + stringlist_type * keylist = ensemble_config_alloc_keylist_from_var_type(enkf_main_get_ensemble_config(enkf_main), PARAMETER); + int i; + for (i = 0; i < stringlist_get_size( keylist ); i++) { + const char * key = stringlist_iget( keylist , i); + bool add_node = true; + + /* + Make sure the funny GEN_KW instance masquerading as + SCHEDULE_PREDICTION_FILE is not added to the soup. + */ + if (util_string_equal(key , "PRED")) + add_node = false; + + + if (add_node) { + if (single_node_update) { + local_dataset_type * this_dataset = local_config_alloc_dataset(local_config, key); + local_dataset_add_node(this_dataset, key); + local_ministep_add_dataset(ministep, this_dataset); + } + local_dataset_add_node(all_active_dataset, key); + } + } + stringlist_free( keylist); + } + if (!single_node_update) + local_ministep_add_dataset(ministep, all_active_dataset); + + } +} + + +void enkf_main_set_verbose( enkf_main_type * enkf_main , bool verbose) { + enkf_main->verbose = verbose; +} + +/** + There is NO tagging anymore - if the user wants tags - the user + supplies the key __WITH__ tags. +*/ +void enkf_main_add_data_kw(enkf_main_type * enkf_main , const char * key , const char * value) { + subst_config_add_subst_kw(enkf_main_get_subst_config(enkf_main), key, value); +} + +void enkf_main_clear_data_kw( enkf_main_type * enkf_main ) { + subst_config_clear(enkf_main_get_subst_config(enkf_main)); +} + +static enkf_main_type * enkf_main_alloc_empty( ) { + enkf_main_type * enkf_main = (enkf_main_type *)util_malloc(sizeof * enkf_main); + UTIL_TYPE_ID_INIT(enkf_main , ENKF_MAIN_ID); + enkf_main->ensemble = NULL; + enkf_main->local_config = NULL; + enkf_main->rng_manager = NULL; + enkf_main->shared_rng = NULL; + enkf_main->ens_size = 0; + enkf_main->res_config = NULL; + enkf_main->ranking_table = ranking_table_alloc( 0 ); + enkf_main->obs = NULL; + enkf_main->local_config = local_config_alloc( ); + + enkf_main_set_verbose( enkf_main, false ); + enkf_main_init_fs( enkf_main ); + + return enkf_main; +} + + +runpath_list_type * enkf_main_get_runpath_list(const enkf_main_type * enkf_main) { + return hook_manager_get_runpath_list(enkf_main_get_hook_manager(enkf_main)); +} + + +runpath_list_type * enkf_main_alloc_runpath_list(const enkf_main_type * enkf_main) { + return runpath_list_alloc( hook_manager_get_runpath_list_file(enkf_main_get_hook_manager(enkf_main))); +} + + +void enkf_main_add_node(enkf_main_type * enkf_main, enkf_config_node_type * enkf_config_node) { + for (int iens = 0; iens < enkf_main_get_ensemble_size(enkf_main); iens++) { + + enkf_state_add_node(enkf_main->ensemble[iens], enkf_config_node_get_key(enkf_config_node), enkf_config_node); + } +} + + + + + + + + + + + + + + + + +/******************************************************************/ + +const char * enkf_main_get_schedule_prediction_file( const enkf_main_type * enkf_main ) { + return ecl_config_get_schedule_prediction_file(enkf_main_get_ecl_config(enkf_main)); +} + + +rng_config_type * enkf_main_get_rng_config( const enkf_main_type * enkf_main ) { + return res_config_get_rng_config(enkf_main->res_config); +} + +rng_type * enkf_main_get_shared_rng( enkf_main_type * enkf_main ) { + return enkf_main->shared_rng; +} + + +void enkf_main_rng_init( enkf_main_type * enkf_main) { + enkf_main->rng_manager = rng_config_alloc_rng_manager( enkf_main_get_rng_config(enkf_main) ); + enkf_main->shared_rng = rng_manager_alloc_rng( enkf_main->rng_manager ); +} + + +void enkf_main_update_local_updates( enkf_main_type * enkf_main) { + const enkf_obs_type * enkf_obs = enkf_main_get_obs( enkf_main ); + if (enkf_obs_have_obs( enkf_obs )) { + /* First create the default ALL_ACTIVE configuration. */ + enkf_main_create_all_active_config( enkf_main ); + } +} + + +static void enkf_main_init_log(const enkf_main_type * enkf_main) { + const log_config_type * log_config = enkf_main_get_log_config(enkf_main); + res_log_init_log( + log_config_get_log_level(log_config), + log_config_get_log_file(log_config), + enkf_main->verbose + ); +} + +static void enkf_main_init_obs(enkf_main_type * enkf_main) { + enkf_main_alloc_obs(enkf_main); + + const model_config_type * model_config = enkf_main_get_model_config(enkf_main); + const char * obs_config_file = model_config_get_obs_config_file(model_config); + if (obs_config_file) + enkf_main_load_obs(enkf_main, obs_config_file, true); +} + +static void enkf_main_add_ensemble_members(enkf_main_type * enkf_main) { + const model_config_type * model_config = enkf_main_get_model_config(enkf_main); + int num_realizations = model_config_get_num_realizations(model_config); + enkf_main_resize_ensemble(enkf_main, num_realizations); +} + + +/** + This function boots everything needed for running a EnKF + application from the provided res_config. + + Observe that the function will start with chdir() to the working directory + specified by res_config, so that all subsequent file + references are relative to the location of the configuration + file. This also applies if the command_line argument given is a + symlink. + + + If the parameter @strict is set to false a configuration with some + missing parameters will validate; this is to support bootstrapping + from a minimal configuration created by the GUI. The parameters + which become optional in a non-strict mode are: + + FORWARD_MODEL + DATA_FILE + SCHEDULE_FILE + ECLBASE + +*/ + +/** + It is possible to pass NULL as the model_config argument, in that + case only the site config file will be parsed. The purpose of this + is mainly to be able to test that the site config file is valid. +*/ + +enkf_main_type * enkf_main_alloc(const res_config_type * res_config, bool strict , bool verbose) { + enkf_main_type * enkf_main = enkf_main_alloc_empty(); + enkf_main->res_config = res_config; + + enkf_main_set_verbose(enkf_main, verbose); + enkf_main_init_log(enkf_main); + enkf_main_rng_init(enkf_main); + enkf_main_user_select_initial_fs(enkf_main); + enkf_main_init_obs(enkf_main); + enkf_main_add_ensemble_members(enkf_main); + + return enkf_main; +} + + +int enkf_main_get_ensemble_size( const enkf_main_type * enkf_main ) { + return enkf_main->ens_size; +} + + + + +/** + In this function we initialize the variables which control + which nodes are internalized (i.e. loaded from the forward + simulation and stored in the enkf_fs 'database'). The system is + based on two-levels: + + * Should we store the state? This is goverened by the variable + model_config->internalize_state. If this is true we will + internalize all nodes which have enkf_var_type = {dynamic_state , + static_state}. In the same way the variable + model_config->internalize_results governs whether the dynamic + results (i.e. summary variables in ECLIPSE speak) should be + internalized. + + * In addition we have fine-grained control in the enkf_config_node + objects where we can explicitly say that, altough we do not want + to internalize the full state, we want to internalize e.g. the + pressure field. + + * All decisions on internalization are based on a per report step + basis. + + The user-space API for manipulating this is (extremely) + limited. What is implemented here is the following: + + 1. We internalize the initial dynamic state. + + 2. For all the end-points in the current enkf_sched instance we + internalize the state. + + 3. store_results is set to true for all report steps irrespective + of run_mode. + + 4. We iterate over all the observations, and ensure that the + observed nodes (i.e. the pressure for an RFT) are internalized + (irrespective of whether they are of type dynamic_state or + dynamic_result). + + Observe that this cascade can result in some nodes, i.e. a rate we + are observing, to be marked for internalization several times - + that is no problem. + + ----- + + For performance reason model_config contains the bool vector + __load_eclipse_restart; if it is true the ECLIPSE restart state is + loaded from disk, otherwise no loading is performed. This implies + that if we do not want to internalize the full state but for + instance the pressure (i.e. for an RFT) we must set the + __load_state variable for the actual report step to true. +*/ + + +void enkf_main_init_internalization( enkf_main_type * enkf_main , run_mode_type run_mode ) { + /* Clearing old internalize flags. */ + model_config_init_internalization(enkf_main_get_model_config(enkf_main)); + + /* Internalizing the initial state. */ + model_config_set_internalize_state(enkf_main_get_model_config(enkf_main), 0); + + + /* Make sure we internalize at all observation times.*/ + { + hash_type * map = enkf_obs_alloc_data_map(enkf_main->obs); + hash_iter_type * iter = hash_iter_alloc(map); + const char * obs_key = hash_iter_get_next_key(iter); + + while (obs_key != NULL) { + obs_vector_type * obs_vector = enkf_obs_get_vector( enkf_main->obs , obs_key ); + enkf_config_node_type * data_node = obs_vector_get_config_node( obs_vector ); + int active_step = -1; + do { + active_step = obs_vector_get_next_active_step( obs_vector , active_step ); + if (active_step >= 0) + enkf_config_node_set_internalize( data_node , active_step ); + } while (active_step >= 0); + obs_key = hash_iter_get_next_key(iter); + } + hash_iter_free(iter); + hash_free(map); + } +} + +/*****************************************************************/ + +void enkf_main_get_observations( const enkf_main_type * enkf_main, const char * user_key , int obs_count , time_t * obs_time , double * y , double * std) { + ensemble_config_get_observations( enkf_main_get_ensemble_config(enkf_main), enkf_main->obs , user_key , obs_count , obs_time , y , std); +} + + +int enkf_main_get_observation_count( const enkf_main_type * enkf_main, const char * user_key ) { + return ensemble_config_get_observations(enkf_main_get_ensemble_config(enkf_main), enkf_main->obs , user_key , 0 , NULL , NULL , NULL); +} + + + + +void enkf_main_install_SIGNALS(void) { + util_install_signals(); +} + + + + +ert_templates_type * enkf_main_get_templates( enkf_main_type * enkf_main ) { + return res_config_get_templates(enkf_main->res_config); +} + + + +/*****************************************************************/ + + + + + +/*****************************************************************/ + +ert_workflow_list_type * enkf_main_get_workflow_list( enkf_main_type * enkf_main ) { + return res_config_get_workflow_list(enkf_main->res_config); +} + +bool enkf_main_run_workflow( enkf_main_type * enkf_main , const char * workflow ) { + ert_workflow_list_type * workflow_list = enkf_main_get_workflow_list( enkf_main ); + if (ert_workflow_list_has_workflow( workflow_list , workflow)){ + return ert_workflow_list_run_workflow_blocking( workflow_list , workflow , enkf_main); + } + else{ + return false; + } +} + +int enkf_main_load_from_forward_model_from_gui(enkf_main_type * enkf_main, int iter , bool_vector_type * iactive, enkf_fs_type * fs){ + const int ens_size = enkf_main_get_ensemble_size( enkf_main ); + stringlist_type ** realizations_msg_list = (stringlist_type **) util_calloc( ens_size , sizeof * realizations_msg_list ); // CXX_CAST_ERROR + for (int iens = 0; iens < ens_size; ++iens) + realizations_msg_list[iens] = stringlist_alloc_new(); + + int loaded = enkf_main_load_from_forward_model_with_fs(enkf_main, iter , iactive, realizations_msg_list, fs); + + for (int iens = 0; iens < ens_size; ++iens) + stringlist_free( realizations_msg_list[iens] ); + + free(realizations_msg_list); + return loaded; +} + +int enkf_main_load_from_forward_model(enkf_main_type * enkf_main, int iter , bool_vector_type * iactive, stringlist_type ** realizations_msg_list){ + enkf_fs_type * fs = enkf_main_get_fs( enkf_main ); + return enkf_main_load_from_forward_model_with_fs(enkf_main, iter, iactive, realizations_msg_list, fs); +} + + +int enkf_main_load_from_forward_model_with_fs(enkf_main_type * enkf_main, int iter , bool_vector_type * iactive, stringlist_type ** realizations_msg_list, enkf_fs_type * fs) { + model_config_type * model_config = enkf_main_get_model_config(enkf_main); + ert_run_context_type * run_context = ert_run_context_alloc_ENSEMBLE_EXPERIMENT( fs, + iactive, + model_config_get_runpath_fmt( model_config ), + model_config_get_jobname_fmt( model_config ), + enkf_main_get_data_kw(enkf_main), + iter ); + int loaded = enkf_main_load_from_run_context(enkf_main, run_context, realizations_msg_list, fs); + ert_run_context_free(run_context); + return loaded; +} + + +int enkf_main_load_from_run_context_from_gui(enkf_main_type* enkf_main, ert_run_context_type* run_context, enkf_fs_type* fs) { + auto const ens_size = enkf_main_get_ensemble_size(enkf_main); + stringlist_type ** realizations_msg_list = (stringlist_type **) util_calloc(ens_size, sizeof *realizations_msg_list); // CXX_CAST_ERROR + for(int iens = 0; iens < ens_size; ++iens) + realizations_msg_list[iens] = stringlist_alloc_new(); + + int loaded = enkf_main_load_from_run_context(enkf_main, run_context, realizations_msg_list, fs); + + for(int iens = 0; iens < ens_size; ++iens) + stringlist_free(realizations_msg_list[iens]); + free(realizations_msg_list); + return loaded; +} + +int enkf_main_load_from_run_context( + enkf_main_type * enkf_main, + ert_run_context_type * run_context, + stringlist_type ** realizations_msg_list, + enkf_fs_type * fs) { + auto const ens_size = enkf_main_get_ensemble_size( enkf_main ); + auto const * iactive = ert_run_context_get_iactive(run_context); + + int result[ens_size]; + arg_pack_type ** arg_list = (arg_pack_type **) util_calloc( ens_size , sizeof * arg_list ); // CXX_CAST_ERROR + thread_pool_type * tp = thread_pool_alloc( std::thread::hardware_concurrency() , true ); + + for (int iens = 0; iens < ens_size; ++iens) { + result[iens] = 0; + arg_pack_type * arg_pack = arg_pack_alloc(); + arg_list[iens] = arg_pack; + + if (bool_vector_iget(iactive, iens)) { + enkf_state_type * enkf_state = enkf_main_iget_state( enkf_main , iens ); + arg_pack_append_ptr( arg_pack , enkf_state); /* 0: enkf_state*/ + arg_pack_append_ptr( arg_pack , ert_run_context_iget_arg( run_context , iens )); /* 1: run_arg */ + arg_pack_append_ptr(arg_pack, realizations_msg_list[iens]); /* 2: List of interactive mode messages. */ + arg_pack_append_bool( arg_pack, true ); /* 3: Manual load */ + arg_pack_append_ptr(arg_pack, &result[iens]); /* 4: Result */ + thread_pool_add_job( tp , enkf_state_load_from_forward_model_mt , arg_pack); + } + } + + thread_pool_join( tp ); + thread_pool_free( tp ); + + int loaded = 0; + for (int iens = 0; iens < ens_size; ++iens) { + if (bool_vector_iget(iactive, iens)) { + if (result[iens] & LOAD_FAILURE) + fprintf(stderr, "** Warning: Function %s: Realization %d load failure\n", __func__, iens); + else if (result[iens] & REPORT_STEP_INCOMPATIBLE) + fprintf(stderr, "** Warning: Function %s: Realization %d report step incompatible\n", __func__, iens); + else + loaded++; + } + arg_pack_free(arg_list[iens]); + } + free( arg_list ); + return loaded; +} + + +bool enkf_main_export_field(const enkf_main_type * enkf_main, + const char * kw, + const char * path, + bool_vector_type * iactive, + field_file_format_type file_type, + int report_step) +{ + enkf_fs_type * fs = enkf_main_get_fs(enkf_main); + bool result = enkf_main_export_field_with_fs(enkf_main, kw, path, iactive, file_type, report_step, fs); + return result; +} + + + + +bool enkf_main_export_field_with_fs(const enkf_main_type * enkf_main, + const char * kw, + const char * path, + bool_vector_type * iactive, + field_file_format_type file_type, + int report_step, + enkf_fs_type * fs) { + + const ensemble_config_type * ensemble_config = enkf_main_get_ensemble_config(enkf_main); + if (!ensemble_config_has_key(ensemble_config, kw)) + return false; + + enkf_config_node_type * config_node = ensemble_config_get_node(ensemble_config, kw); + if (enkf_config_node_get_impl_type(config_node) != FIELD) + return false; + + if (util_int_format_count(path) < 1) + return false; + + + enkf_node_type * node = enkf_node_alloc(config_node); + model_config_type * mc = enkf_main_get_model_config(enkf_main); + path_fmt_type * runpath_fmt = model_config_get_runpath_fmt(mc); + const char * init_file = enkf_config_node_get_FIELD_fill_file(config_node, runpath_fmt); + if (init_file) + printf("init_file found: \"%s\", exporting initial value for inactive cells\n", init_file); + else + printf("no init_file found, exporting 0 or fill value for inactive cells\n"); + + for (int iens = 0; iens < bool_vector_size(iactive); ++iens) { + if (!bool_vector_iget(iactive, iens)) + continue; + + node_id_type node_id = {.report_step = report_step, .iens = iens }; + if (!enkf_node_try_load(node, fs, node_id)) + continue; + + path_fmt_type * export_path = path_fmt_alloc_path_fmt(path); + char * filename = path_fmt_alloc_path(export_path, false, iens); + path_fmt_free(export_path); + + char * path; + util_alloc_file_components(filename, &path, NULL, NULL); + if (path) { + util_make_path(path); + free(path); + } + + const field_type * field = (const field_type * ) enkf_node_value_ptr(node); + field_export(field, + filename, + NULL, + file_type, + true, //output_transform + init_file); + + free(filename); + } + enkf_node_free(node); + + + return true; +} + + +void enkf_main_rank_on_observations(enkf_main_type * enkf_main, + const char * ranking_key, + const stringlist_type * obs_ranking_keys, + const int_vector_type * steps) { + + enkf_fs_type * fs = enkf_main_get_fs(enkf_main); + const enkf_obs_type * enkf_obs = enkf_main_get_obs( enkf_main ); + const ensemble_config_type * ensemble_config = enkf_main_get_ensemble_config(enkf_main); + const int history_length = enkf_main_get_history_length( enkf_main ); + const int ens_size = enkf_main_get_ensemble_size( enkf_main ); + + misfit_ensemble_type * misfit_ensemble = enkf_fs_get_misfit_ensemble( fs ); + misfit_ensemble_initialize( misfit_ensemble , ensemble_config , enkf_obs , fs , ens_size , history_length, false); + + ranking_table_type * ranking_table = enkf_main_get_ranking_table( enkf_main ); + + ranking_table_add_misfit_ranking( ranking_table , misfit_ensemble , obs_ranking_keys , steps , ranking_key ); + ranking_table_display_ranking( ranking_table , ranking_key); +} + + + +void enkf_main_rank_on_data(enkf_main_type * enkf_main, + const char * ranking_key, + const char * data_key, + bool sort_increasing, + int step) { + + ranking_table_type * ranking_table = enkf_main_get_ranking_table( enkf_main ); + const ensemble_config_type * ensemble_config = enkf_main_get_ensemble_config( enkf_main ); + enkf_fs_type * fs = enkf_main_get_fs(enkf_main); + char * key_index; + + const enkf_config_node_type * config_node = ensemble_config_user_get_node( ensemble_config , data_key , &key_index); + if (config_node) { + ranking_table_add_data_ranking( ranking_table , sort_increasing , ranking_key , data_key , key_index , fs , config_node, step ); + ranking_table_display_ranking( ranking_table , ranking_key ); + } else { + fprintf(stderr,"** No data found for key %s\n", data_key); + } +} + + +void enkf_main_export_ranking(enkf_main_type * enkf_main, const char * ranking_key, const char * ranking_file) { + ranking_table_type * ranking_table = enkf_main_get_ranking_table( enkf_main ); + ranking_table_fwrite_ranking(ranking_table, ranking_key, ranking_file); +} + + +queue_config_type * enkf_main_get_queue_config(enkf_main_type * enkf_main ) { + return res_config_get_queue_config( + enkf_main_get_res_config(enkf_main) + ); +} + +rng_manager_type * enkf_main_get_rng_manager(const enkf_main_type * enkf_main ) { + return enkf_main->rng_manager; +} + +#include "enkf_main_ensemble.cpp" +#include "enkf_main_manage_fs.cpp" diff --git a/libres/lib/enkf/enkf_main_ensemble.cpp b/libres/lib/enkf/enkf_main_ensemble.cpp new file mode 100644 index 00000000000..c1831d3bc84 --- /dev/null +++ b/libres/lib/enkf/enkf_main_ensemble.cpp @@ -0,0 +1,76 @@ + + + +static void enkf_main_free_ensemble( enkf_main_type * enkf_main ) { + if (enkf_main->ensemble != NULL) { + const int ens_size = enkf_main->ens_size; + int i; + for (i=0; i < ens_size; i++) + enkf_state_free( enkf_main->ensemble[i] ); + free(enkf_main->ensemble); + enkf_main->ensemble = NULL; + } +} + + +enkf_state_type * enkf_main_iget_state(const enkf_main_type * enkf_main , int iens) { + return enkf_main->ensemble[iens]; +} + + + + +/** + This function will resize the enkf_main->ensemble vector, + allocating or freeing enkf_state instances as needed. +*/ + + +void enkf_main_resize_ensemble( enkf_main_type * enkf_main , int new_ens_size ) { + int iens; + + /* No change */ + if (new_ens_size == enkf_main->ens_size) + return ; + + ranking_table_set_ens_size( enkf_main->ranking_table , new_ens_size ); + + /* The ensemble is shrinking. */ + if (new_ens_size < enkf_main->ens_size) { + /*1: Free all ensemble members which go out of scope. */ + for (iens = new_ens_size; iens < enkf_main->ens_size; iens++) + enkf_state_free( enkf_main->ensemble[iens] ); + + /*2: Shrink the ensemble pointer. */ + enkf_main->ensemble = (enkf_state_type **) util_realloc(enkf_main->ensemble , new_ens_size * sizeof * enkf_main->ensemble ); + enkf_main->ens_size = new_ens_size; + return; + } + + + /* The ensemble is expanding */ + if (new_ens_size > enkf_main->ens_size) { + /*1: Grow the ensemble pointer. */ + enkf_main->ensemble = (enkf_state_type **) util_realloc(enkf_main->ensemble , new_ens_size * sizeof * enkf_main->ensemble ); + + /*2: Allocate the new ensemble members. */ + for (iens = enkf_main->ens_size; iens < new_ens_size; iens++) + + /* Observe that due to the initialization of the rng - this function is currently NOT thread safe. */ + enkf_main->ensemble[iens] = enkf_state_alloc(iens, + rng_manager_iget( enkf_main->rng_manager, iens), + enkf_main_get_model_config(enkf_main), + enkf_main_get_ensemble_config(enkf_main), + enkf_main_get_site_config(enkf_main), + enkf_main_get_ecl_config(enkf_main), + enkf_main_get_templates(enkf_main)); + enkf_main->ens_size = new_ens_size; + return; + } + + util_abort("%s: something is seriously broken - should NOT be here .. \n",__func__); +} + + + + diff --git a/libres/lib/enkf/enkf_main_jobs.cpp b/libres/lib/enkf/enkf_main_jobs.cpp new file mode 100644 index 00000000000..a3906059087 --- /dev/null +++ b/libres/lib/enkf/enkf_main_jobs.cpp @@ -0,0 +1,552 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'enkf_main_jobs.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 + + +extern "C" { + +static bool_vector_type * alloc_iactive_vector_from_range(const stringlist_type * range, int startindex, int endindex, int ens_size) { + bool_vector_type * iactive; + int range_list_size = stringlist_get_size(range); + if ((range_list_size > startindex) && (range_list_size >= endindex)) { + char * arg_string = stringlist_alloc_joined_substring( range, startindex, endindex, ""); + iactive = bool_vector_alloc(ens_size, false); + string_util_update_active_mask( arg_string, iactive ); + free ( arg_string ); + } else { + iactive = bool_vector_alloc(ens_size, true); + } + return iactive; +} + +// Internal workflow job +C_USED void * enkf_main_exit_JOB(void * self , const stringlist_type * args ) { + enkf_main_type * enkf_main = enkf_main_safe_cast( self ); + enkf_main_exit( enkf_main ); + return NULL; +} + +/*****************************************************************/ + +/* + Will create the new case if it does not exist. +*/ +// Internal workflow job +C_USED void * enkf_main_select_case_JOB( void * self , const stringlist_type * args) { + enkf_main_type * enkf_main = enkf_main_safe_cast( self ); + const char * new_case = stringlist_iget( args , 0 ); + enkf_main_select_fs( enkf_main , new_case ); + return NULL; +} + + +// Internal workflow job +C_USED void * enkf_main_create_case_JOB( void * self , const stringlist_type * args) { + enkf_main_type * enkf_main = enkf_main_safe_cast( self ); + const char * new_case = stringlist_iget( args , 0 ); + enkf_fs_type * fs = enkf_main_mount_alt_fs( enkf_main , new_case , true ); + enkf_fs_decref( fs ); + return NULL; +} + + + +// Internal workflow job +C_USED void * enkf_main_init_case_from_existing_JOB( void * self , const stringlist_type * args) { + enkf_main_type * enkf_main = enkf_main_safe_cast( self ); + + const char * source_case = stringlist_iget( args , 0 ); + enkf_fs_type * source_fs = enkf_main_mount_alt_fs( enkf_main , source_case , true ); + { + enkf_fs_type * target_fs; + + if (stringlist_get_size(args) > 1) { + const char * current_case = enkf_main_get_current_fs(enkf_main); + const char * target_case = stringlist_iget( args , 1 ); + if (0 != strcmp(current_case, target_case)) { + target_fs = enkf_main_mount_alt_fs( enkf_main , target_case , true ); + } else + target_fs = enkf_fs_get_ref( enkf_main_job_get_fs(enkf_main) ); // Using get_ref so that we can unconditionally call decref() further down. + } else + target_fs = enkf_fs_get_ref( enkf_main_job_get_fs(enkf_main) ); // Using get_ref so that we can unconditionally call decref() further down. + + enkf_main_init_case_from_existing(enkf_main, source_fs, 0, target_fs); // Removed ANALYZED argument + enkf_fs_decref(target_fs); + } + enkf_fs_decref(source_fs); + + return NULL; +} + + +/*****************************************************************/ + +static void * enkf_main_load_results_JOB__( enkf_main_type * enkf_main , int iter , const stringlist_type * args) { + bool_vector_type * iactive = alloc_iactive_vector_from_range(args, 0, stringlist_get_size(args), enkf_main_get_ensemble_size(enkf_main)); + int ens_size = enkf_main_get_ensemble_size(enkf_main); + stringlist_type ** realizations_msg_list = (stringlist_type **) util_calloc(ens_size, sizeof * realizations_msg_list); + for (int iens = 0; iens < ens_size; ++iens) + realizations_msg_list[iens] = stringlist_alloc_new(); + + enkf_main_load_from_forward_model(enkf_main, iter , iactive, realizations_msg_list); + + for (int iens = 0; iens < ens_size; ++iens) { + stringlist_type * msg = realizations_msg_list[iens]; + if (stringlist_get_size(msg)) { + int msg_count = 0; + for (; msg_count < stringlist_get_size(msg); ++msg_count) + fprintf(stderr, "** Warning: Function %s : Load of realization number %d returned the following warning: %s\n", __func__, iens, stringlist_iget(msg, msg_count)); + } + stringlist_free(msg); + } + + free(realizations_msg_list); + bool_vector_free(iactive); + return NULL; +} + + +// Internal workflow job +C_USED void * enkf_main_load_results_JOB( void * self , const stringlist_type * args) { + enkf_main_type * enkf_main = enkf_main_safe_cast( self ); + int iter = 0; + { + const model_config_type * model_config = enkf_main_get_model_config( enkf_main ); + if (model_config_runpath_requires_iter( model_config )) + fprintf(stderr,"**Warning: the runpath format:%s requires an iteration number - using default:0. Use the job: LOAD_RESULT_ITER instead.\n" , model_config_get_runpath_as_char( model_config )); + } + return enkf_main_load_results_JOB__(enkf_main , iter , args ); +} + +// Internal Workflow job +C_USED void * enkf_main_load_results_iter_JOB( void * self , const stringlist_type * args) { + enkf_main_type * enkf_main = enkf_main_safe_cast( self ); + stringlist_type * iens_args = stringlist_alloc_new(); + int iter; + + for (int i=1; i < stringlist_get_size(args); i++) + stringlist_append_copy( iens_args, stringlist_iget(args, i)); + + util_sscanf_int( stringlist_iget( args , 0 ) , &iter); + enkf_main_load_results_JOB__(enkf_main , iter , iens_args ); + stringlist_free( iens_args ); + + return NULL; +} + + +/*****************************************************************/ + +static void enkf_main_jobs_export_field(const enkf_main_type * enkf_main, const stringlist_type * args, field_file_format_type file_type) { + const char * field = stringlist_iget(args, 0); + const char * file_name = stringlist_iget(args, 1); + int report_step = 0; + util_sscanf_int(stringlist_iget(args,2), &report_step); + + bool_vector_type * iactive = alloc_iactive_vector_from_range(args, 4, stringlist_get_size(args), enkf_main_get_ensemble_size(enkf_main)); + enkf_main_export_field(enkf_main,field, file_name, iactive, file_type, report_step ) ; + bool_vector_free(iactive); +} + + + +// Internal workflow job +C_USED void * enkf_main_export_field_JOB(void * self, const stringlist_type * args) { + const char * file_name = stringlist_iget(args, 1); + field_file_format_type file_type = field_config_default_export_format(file_name); + + if ((RMS_ROFF_FILE == file_type) || (ECL_GRDECL_FILE == file_type)) { + enkf_main_type * enkf_main = enkf_main_safe_cast( self ); + enkf_main_jobs_export_field(enkf_main, args, file_type); + } else + printf("EXPORT_FIELD filename argument: File extension must be either .roff or .grdecl\n"); + + return NULL; +} + +// Internal workflow job +C_USED void * enkf_main_export_field_to_RMS_JOB(void * self, const stringlist_type * args) { + enkf_main_type * enkf_main = enkf_main_safe_cast( self ); + enkf_main_jobs_export_field(enkf_main, args, RMS_ROFF_FILE); + return NULL; +} + +// Internal workflow job +C_USED void * enkf_main_export_field_to_ECL_JOB(void * self, const stringlist_type * args) { + enkf_main_type * enkf_main = enkf_main_safe_cast( self ); + enkf_main_jobs_export_field(enkf_main, args, ECL_GRDECL_FILE); + return NULL; +} + + +/*****************************************************************/ + +// Internal workflow job +C_USED void * enkf_main_rank_on_observations_JOB(void * self, const stringlist_type * args) { + enkf_main_type * enkf_main = enkf_main_safe_cast( self ); + const char * ranking_name = stringlist_iget(args, 0); + + bool step_arguments = false; + bool obs_arguments = false; + int delimiter = 0; + { + delimiter = stringlist_find_first(args, "|"); + if (delimiter > -1) { + step_arguments = (delimiter > 1) ? true : false; + obs_arguments = (stringlist_get_size(args) > delimiter + 1) ? true : false; + } else if (stringlist_get_size(args) > 1) { + step_arguments = true; + delimiter = stringlist_get_size(args); + } + } + + int_vector_type * steps_vector = NULL; + { + char * report_steps = NULL; + + if (step_arguments) + report_steps = stringlist_alloc_joined_substring(args, 1, delimiter, ","); + else + report_steps = util_alloc_sprintf("0-%d", enkf_main_get_history_length(enkf_main)); + + steps_vector = string_util_alloc_value_list(report_steps); + + free(report_steps); + } + + + stringlist_type * obs_ranking_keys = NULL; + { + char * obs_key_char = NULL; + if (obs_arguments) + obs_key_char = stringlist_alloc_joined_substring( args , delimiter+1 , stringlist_get_size(args) , " "); + + enkf_obs_type * enkf_obs = enkf_main_get_obs(enkf_main); + obs_ranking_keys = enkf_obs_alloc_matching_keylist( enkf_obs , obs_key_char ); + + if ((obs_arguments) && (stringlist_get_size(obs_ranking_keys) == 0)) { + fprintf(stderr,"The input string : \"%s\" did not resolve to any valid observation keys. Job not started\n", obs_key_char); + return NULL; + } + + if (obs_arguments) + free(obs_key_char); + } + + + enkf_main_rank_on_observations(enkf_main, ranking_name, obs_ranking_keys, steps_vector); + + stringlist_free(obs_ranking_keys); + int_vector_free(steps_vector); + return NULL; +} + + +// Internal workflow job +C_USED void * enkf_main_rank_on_data_JOB(void * self, const stringlist_type * args) { + enkf_main_type * enkf_main = enkf_main_safe_cast( self ); + const char * ranking_name = stringlist_iget(args, 0); + const char * data_key = stringlist_iget(args, 1); + bool valid = true; + bool sort_increasing = stringlist_iget_as_bool(args, 2, &valid); + + if (!valid) { + fprintf(stderr,"** Third argument \"sort increasing\" not recognized as bool value, job not started\n"); + return NULL; + } + + int report_step = (stringlist_get_size(args) > 3) ? stringlist_iget_as_int(args, 3, &valid) : enkf_main_get_history_length(enkf_main) ; + if (!valid) { + fprintf(stderr,"** Fourth argument \"step\" not recognized as integer value, job not started\n"); + return NULL; + } + + if (report_step < 0) { + fprintf(stderr,"** Negative report step, job not started\n"); + return NULL; + } + + enkf_main_rank_on_data(enkf_main, ranking_name, data_key, sort_increasing, report_step); + return NULL; +} + + +// Internal workflow job +C_USED void * enkf_main_export_ranking_JOB(void * self, const stringlist_type * args) { + enkf_main_type * enkf_main = enkf_main_safe_cast( self ); + const char * ranking_name = stringlist_iget(args, 0); + const char * ranking_file = stringlist_iget(args, 1); + + enkf_main_export_ranking(enkf_main, ranking_name, ranking_file); + return NULL; +} + +// Internal workflow job +C_USED void * enkf_main_init_misfit_table_JOB(void * self, const stringlist_type * args) { + enkf_main_type * enkf_main = enkf_main_safe_cast( self ); + int history_length = enkf_main_get_history_length(enkf_main); + enkf_obs_type * enkf_obs = enkf_main_get_obs(enkf_main); + int ens_size = enkf_main_get_ensemble_size(enkf_main); + enkf_fs_type * fs = enkf_main_job_get_fs(enkf_main); + bool force_update = true; + const ensemble_config_type * ensemble_config = enkf_main_get_ensemble_config(enkf_main); + + + misfit_ensemble_type * misfit_ensemble = enkf_fs_get_misfit_ensemble( fs ); + misfit_ensemble_initialize( misfit_ensemble , ensemble_config , enkf_obs , fs , ens_size , history_length, force_update); + + return NULL; +} + + + + +static void enkf_main_export_runpath_file(enkf_main_type * enkf_main, + const int_vector_type * realizations, + const int_vector_type * iterations) { + + const model_config_type * model_config = enkf_main_get_model_config(enkf_main); + const char * basename_fmt = model_config_get_jobname_fmt( model_config ); + const char * runpath_fmt = model_config_get_runpath_as_char(model_config); + const hook_manager_type * hook_manager = enkf_main_get_hook_manager( enkf_main ); + + runpath_list_type * runpath_list = runpath_list_alloc( hook_manager_get_runpath_list_file( hook_manager )); + + for (int iter = 0; iter < int_vector_size(iterations); ++iter) { + for (int iens = 0; iens < int_vector_size(realizations); ++iens) { + int iter_value = int_vector_iget(iterations, iter); + int iens_value = int_vector_iget(realizations, iens); + char * basename; + char * runpath; + + if (basename_fmt) + basename = util_alloc_sprintf(basename_fmt, iens_value); + else + basename = util_alloc_sprintf("--%d", iens_value); + + if (model_config_runpath_requires_iter(model_config)) + runpath = util_alloc_sprintf(runpath_fmt, iens_value, iter_value); + else + runpath = util_alloc_sprintf(runpath_fmt, iens_value); + + runpath_list_add(runpath_list, iens_value, iter_value, runpath, basename); + + free(basename); + free(runpath); + } + } + runpath_list_fprintf(runpath_list); + runpath_list_free(runpath_list); +} + + + + +// Internal workflow job +C_USED void * enkf_main_export_runpath_file_JOB(void * self, const stringlist_type * args) { + enkf_main_type * enkf_main = enkf_main_safe_cast( self ); + int ensemble_size = enkf_main_get_ensemble_size(enkf_main); + const analysis_config_type * analysis_config = enkf_main_get_analysis_config(enkf_main); + analysis_iter_config_type * iter_config = analysis_config_get_iter_config(analysis_config); + int num_iterations = analysis_iter_config_get_num_iterations(iter_config); + const model_config_type * model_config = enkf_main_get_model_config(enkf_main); + int_vector_type * realizations = int_vector_alloc(1, 0); + int_vector_init_range(realizations, 0, ensemble_size, 1); + int_vector_type * iterations = int_vector_alloc(1, 0); + + + if (stringlist_get_size(args) > 0) { + int offset = 0; + while (true) { + if (offset == stringlist_get_size( args )) + break; + if (0 == strcmp("|" , stringlist_iget( args, offset ))) + break; + ++offset; + } + + if (0 != strcmp("*", stringlist_iget(args,0))) { + char * range_str = stringlist_alloc_joined_substring( args, 0, offset, ""); + string_util_init_value_list(range_str, realizations); + free(range_str); + } + + if ((offset < stringlist_get_size(args)) && model_config_runpath_requires_iter(model_config)) { + if (0 == strcmp("*", stringlist_iget(args, (offset+1)))) + int_vector_init_range(iterations, 0, num_iterations, 1); + else { + char * range_str = stringlist_alloc_joined_substring( args, offset+1, stringlist_get_size(args), ""); + string_util_init_value_list(range_str, iterations); + free(range_str); + } + } + } + + enkf_main_export_runpath_file(enkf_main, realizations, iterations); + + int_vector_free(realizations); + int_vector_free(iterations); + + return NULL; +} + + +// Internal workflow job +C_USED void * enkf_main_std_scale_correlated_obs_JOB(void * self, const stringlist_type * args) { + printf("The STD_SCALE_CORRELATED_OBS workflow is deprecated. " + "Use the 'auto_scale' option in the MISFIT_PREPROCESSOR workflow instead.\n" + ); + + if (stringlist_get_size(args) > 0) { + bool verbose = true; + enkf_main_type * enkf_main = enkf_main_safe_cast( self ); + int ensemble_size = enkf_main_get_ensemble_size(enkf_main); + enkf_fs_type * fs = enkf_main_job_get_fs( enkf_main ); + enkf_obs_type * obs = enkf_main_get_obs( enkf_main ); + local_obsdata_type * obsdata = local_obsdata_alloc( "OBS-JOB" ); + + // Find out what realizations have data + const auto* state_map = enkf_fs_get_state_map(fs); + auto* has_data_mask = bool_vector_alloc(state_map_get_size(state_map), false); + state_map_select_matching(state_map, has_data_mask, STATE_HAS_DATA); + auto* realizations = bool_vector_alloc_active_list(has_data_mask); + bool_vector_free(has_data_mask); + + + for (int iarg = 0; iarg < stringlist_get_size(args); iarg++) { + const char * arg_key = stringlist_iget( args , iarg ); + stringlist_type * key_list = enkf_obs_alloc_matching_keylist(obs, arg_key); + if (verbose) + printf("%s => ", arg_key); + + for (int iobs=0; iobs < stringlist_get_size( key_list ); iobs++) { + const char * obs_key = stringlist_iget( key_list , iobs); + const obs_vector_type * obs_vector = enkf_obs_get_vector(obs, obs_key); + local_obsdata_add_node( obsdata , obs_vector_alloc_local_node(obs_vector) ); + if (verbose) + printf("%s ", obs_key); + } + if (verbose && (stringlist_get_size(key_list) == 0)) + printf("**ERROR - no observation keys matched argument %s - ignored\n", arg_key); + + if (verbose) + puts("\n"); + + stringlist_free( key_list ); + } + if (verbose) { + printf("Realisations with data:"); + for (int i=0; i < int_vector_size(realizations); i++) { + if ((i % 20 ) == 0) + puts("\n"); + + printf(" %3d", int_vector_iget(realizations, i)); + } + puts("\n"); + } + + + if (local_obsdata_get_size(obsdata) > 0) { + const analysis_config_type * analysis_config = enkf_main_get_analysis_config( enkf_main ); + double alpha = analysis_config_get_alpha(analysis_config); + double std_cutoff = analysis_config_get_std_cutoff(analysis_config); + enkf_obs_scale_correlated_std(obs, fs, realizations, obsdata, alpha, std_cutoff, verbose ); + } + else if (verbose) + printf("**Warning: Your list of arguments did not match any observation keys - no scaling performed.\n"); + + int_vector_free(realizations); + local_obsdata_free( obsdata ); + } else + fprintf(stderr,"** Warning: When using the workflow job to scale correlated observations you must provide the observation keys as arguments\n"); + + return NULL; +} + + +// Internal workflow job +C_USED void * enkf_main_analysis_update_JOB( void * self , const stringlist_type * args) { + enkf_main_type * enkf_main = enkf_main_safe_cast( self ); + enkf_fs_type * source_fs = enkf_main_mount_alt_fs( enkf_main , stringlist_iget(args , 0 ) , false); + enkf_fs_type * target_fs = enkf_main_mount_alt_fs( enkf_main , stringlist_iget(args , 1 ) , true); + + enkf_main_smoother_update( enkf_main , source_fs , target_fs); + + enkf_fs_decref( source_fs ); + enkf_fs_decref( target_fs ); + return NULL; +} + + +// Internal workflow job +C_USED void * enkf_main_pre_simulation_copy_JOB( void * self , const stringlist_type * args) { + const char * source_path = stringlist_iget( args , 0 ); + + if (!util_entry_exists( source_path )) { + char * msg = util_alloc_sprintf("Error in workflow job PRE_SIMULATION_COPY" + "- source argument: %s not existing\n", + source_path); + res_log_error(msg); + free(msg); + return NULL; + } + + + enkf_main_type * enkf_main = enkf_main_safe_cast( self ); + model_config_type * model_config = enkf_main_get_model_config( enkf_main ); + if (!model_config_data_root_is_set( model_config )) { + res_log_error("Error in workflow job PRE_SIMULATION_COPY DATA_ROOT not set"); + return NULL; + } + + + char * target_path; + if (stringlist_get_size( args ) == 2) { + const char * arg_path = stringlist_iget(args, 1); + target_path = util_alloc_filename( model_config_get_data_root( model_config ), arg_path, NULL ); + } else + target_path = util_alloc_string_copy( model_config_get_data_root( model_config ) ); + + util_make_path( target_path ); + if (util_is_directory( source_path )) { + util_copy_directory( source_path , target_path ); + res_log_finfo("Copying directory %s -> %s", source_path, target_path); + } else { + char * base_name = util_split_alloc_filename( source_path ); + char * target_file = util_alloc_filename( target_path, base_name, NULL ); + + util_copy_file( source_path , target_file); + res_log_finfo("Copying file %s -> %s", source_path, target_path); + + free( base_name ); + free( target_file ); + } + + free( target_path ); + return NULL; +} + +} diff --git a/libres/lib/enkf/enkf_main_manage_fs.cpp b/libres/lib/enkf/enkf_main_manage_fs.cpp new file mode 100644 index 00000000000..822312485a8 --- /dev/null +++ b/libres/lib/enkf/enkf_main_manage_fs.cpp @@ -0,0 +1,663 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_main_manage_fs.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 + +/* + This small function is here only to make sure that the main + enkf_main.c file does not contain any explicit mention of the + dbase member. +*/ + +static void enkf_main_init_fs( enkf_main_type * enkf_main ) { + enkf_main->dbase = NULL; +} + + + +bool enkf_main_case_is_current(const enkf_main_type * enkf_main , const char * case_path) { + char * mount_point = enkf_main_alloc_mount_point( enkf_main , case_path ); + const char * current_mount_point = NULL; + bool is_current; + + if (enkf_main->dbase != NULL) + current_mount_point = enkf_fs_get_mount_point( enkf_main->dbase ); + + is_current = util_string_equal( mount_point , current_mount_point ); + free( mount_point ); + return is_current; +} + +static bool enkf_main_current_case_file_exists( const enkf_main_type * enkf_main) { + const char * ens_path = model_config_get_enspath(enkf_main_get_model_config(enkf_main)); + char * current_case_file = util_alloc_filename(ens_path, CURRENT_CASE_FILE, NULL); + bool exists = util_file_exists(current_case_file); + free(current_case_file); + return exists; +} + +char* enkf_main_read_alloc_current_case_name(const enkf_main_type * enkf_main) { + char * current_case = NULL; + const char * ens_path = model_config_get_enspath(enkf_main_get_model_config(enkf_main)); + char * current_case_file = util_alloc_filename(ens_path, CURRENT_CASE_FILE, NULL); + if (enkf_main_current_case_file_exists(enkf_main)) { + FILE * stream = util_fopen( current_case_file , "r"); + current_case = util_fscanf_alloc_token(stream); + fclose(stream); + } else { + util_abort("%s: File: storage/current_case not found, aborting! \n",__func__); + } + free(current_case_file); + return current_case; +} + + + + + +stringlist_type * enkf_main_alloc_caselist( const enkf_main_type * enkf_main ) { + stringlist_type * case_list = stringlist_alloc_new( ); + { + const char * ens_path = model_config_get_enspath(enkf_main_get_model_config(enkf_main)); + DIR * ens_dir = opendir( ens_path ); + if (ens_dir != NULL) { + int ens_fd = dirfd( ens_dir ); + if (ens_fd != -1) { + struct dirent * dp; + do { + dp = readdir( ens_dir ); + if (dp != NULL) { + if (!(util_string_equal( dp->d_name , ".") || util_string_equal(dp->d_name , ".."))) { + if (!util_string_equal( dp->d_name , CURRENT_CASE_FILE)) { + char * full_path = util_alloc_filename( ens_path , dp->d_name , NULL); + if (util_is_directory( full_path )) + stringlist_append_copy( case_list , dp->d_name ); + free( full_path); + } + } + } + } while (dp != NULL); + } + } + closedir( ens_dir ); + } + return case_list; +} + + + + +static void * enkf_main_initialize_from_scratch_mt(void * void_arg) { + arg_pack_type * arg_pack = arg_pack_safe_cast( void_arg ); + enkf_main_type * enkf_main = (enkf_main_type * ) arg_pack_iget_ptr( arg_pack , 0); + enkf_fs_type * init_fs = (enkf_fs_type * ) arg_pack_iget_ptr( arg_pack , 1); + const stringlist_type * param_list = (const stringlist_type * ) arg_pack_iget_const_ptr( arg_pack , 2 ); + int iens = arg_pack_iget_int( arg_pack , 3 ); + init_mode_type init_mode = (init_mode_type ) arg_pack_iget_int( arg_pack , 4 ); + enkf_state_type * state = enkf_main_iget_state( enkf_main , iens); + rng_type * rng = rng_manager_iget( enkf_main->rng_manager, iens ); + + enkf_state_initialize( state , rng, init_fs , param_list , init_mode); + return NULL; +} + +void enkf_main_initialize_from_scratch(enkf_main_type * enkf_main , + const stringlist_type * param_list , + const ert_run_context_type * run_context) { + int ens_size = enkf_main_get_ensemble_size( enkf_main ); + arg_pack_type ** arg_list = (arg_pack_type **) util_calloc( ens_size , sizeof * arg_list ); + + for (int iens = 0; iens < ens_size; iens++) { + arg_list[iens] = arg_pack_alloc(); + if (ert_run_context_iactive(run_context, iens)) { + arg_pack_append_ptr( arg_list[iens] , enkf_main ); + arg_pack_append_ptr( arg_list[iens] , ert_run_context_get_sim_fs(run_context)); + arg_pack_append_const_ptr( arg_list[iens] , param_list ); + arg_pack_append_int( arg_list[iens] , iens ); + arg_pack_append_int( arg_list[iens] ,ert_run_context_get_init_mode(run_context)); + + enkf_main_initialize_from_scratch_mt(arg_list[iens]); + } + } + for (int iens = 0; iens < ens_size; iens++){ + arg_pack_free( arg_list[iens] ); + } + free( arg_list ); +} + + + + +static void enkf_main_copy_ensemble( const enkf_main_type * enkf_main, + enkf_fs_type * source_case_fs, + int source_report_step, + enkf_fs_type * target_case_fs, + int target_report_step, + const bool_vector_type * iens_mask, + const char * ranking_key , /* It is OK to supply NULL - but if != NULL it must exist */ + const stringlist_type * node_list) { + + const int ens_size = enkf_main_get_ensemble_size( enkf_main ); + state_map_type * target_state_map = enkf_fs_get_state_map(target_case_fs); + + { + int * ranking_permutation; + int inode , src_iens; + + if (ranking_key != NULL) { + ranking_table_type * ranking_table = enkf_main_get_ranking_table( enkf_main ); + ranking_permutation = (int *) ranking_table_get_permutation( ranking_table , ranking_key ); + } else { + ranking_permutation = (int * ) util_calloc( ens_size , sizeof * ranking_permutation ); + for (src_iens = 0; src_iens < ens_size; src_iens++) + ranking_permutation[src_iens] = src_iens; + } + + for (inode =0; inode < stringlist_get_size( node_list ); inode++) { + enkf_config_node_type * config_node = ensemble_config_get_node( enkf_main_get_ensemble_config(enkf_main) , stringlist_iget( node_list , inode )); + for (src_iens = 0; src_iens < enkf_main_get_ensemble_size( enkf_main ); src_iens++) { + if (bool_vector_safe_iget(iens_mask , src_iens)) { + int target_iens = ranking_permutation[src_iens]; + node_id_type src_id = {.report_step = source_report_step , .iens = src_iens }; + node_id_type target_id = {.report_step = target_report_step , .iens = target_iens }; + + /* The copy is careful ... */ + if (enkf_config_node_has_node( config_node , source_case_fs , src_id)) + enkf_node_copy( config_node , + source_case_fs , target_case_fs , + src_id , target_id ); + + if (0 == target_report_step) + state_map_iset(target_state_map, target_iens, STATE_INITIALIZED); + } + } + } + + if (ranking_permutation == NULL) + free( ranking_permutation ); + } +} + + + +void enkf_main_init_current_case_from_existing(enkf_main_type * enkf_main, + enkf_fs_type * source_case_fs, + int source_report_step) { + + enkf_fs_type * current_fs = enkf_main_get_fs(enkf_main); + + enkf_main_init_case_from_existing(enkf_main, + source_case_fs, + source_report_step, + current_fs); + +} + + +void enkf_main_init_current_case_from_existing_custom(enkf_main_type * enkf_main, + enkf_fs_type * source_case_fs, + int source_report_step, + stringlist_type * node_list, + bool_vector_type * iactive) { + + enkf_fs_type * current_fs = enkf_main_get_fs(enkf_main); + + enkf_main_init_case_from_existing_custom(enkf_main, + source_case_fs, + source_report_step, + current_fs, + node_list, + iactive); + +} + + +void enkf_main_init_case_from_existing(const enkf_main_type * enkf_main, + enkf_fs_type * source_case_fs, + int source_report_step, + enkf_fs_type * target_case_fs ) { + + stringlist_type * param_list = ensemble_config_alloc_keylist_from_var_type( enkf_main_get_ensemble_config(enkf_main) , PARAMETER ); /* Select only paramters - will fail for GEN_DATA of type DYNAMIC_STATE. */ + int target_report_step = 0; + bool_vector_type * iactive = bool_vector_alloc( 0 , true ); + + enkf_main_copy_ensemble(enkf_main, + source_case_fs, + source_report_step, + target_case_fs, + target_report_step, + iactive, + NULL, + param_list); + + + enkf_fs_fsync(target_case_fs); + + bool_vector_free(iactive); + stringlist_free(param_list); +} + + +void enkf_main_init_case_from_existing_custom(const enkf_main_type * enkf_main, + enkf_fs_type * source_case_fs, + int source_report_step, + enkf_fs_type * target_case_fs, + stringlist_type * node_list, + bool_vector_type * iactive) { + + int target_report_step = 0; + + enkf_main_copy_ensemble(enkf_main, + source_case_fs, + source_report_step, + target_case_fs, + target_report_step, + iactive, + NULL, + node_list); + + enkf_fs_fsync(target_case_fs); +} + + + + +/** + This function will go through the filesystem and check that we have + initial data for all parameters and all realizations. If the second + argument mask is different from NULL, the function will only + consider the realizations for which mask is true (if mask == NULL + all realizations will be checked). +*/ + +static bool enkf_main_case_is_initialized__( const enkf_main_type * enkf_main , enkf_fs_type * fs , bool_vector_type * __mask) { + ensemble_config_type * ensemble_config = enkf_main_get_ensemble_config(enkf_main); + stringlist_type * parameter_keys = ensemble_config_alloc_keylist_from_var_type(ensemble_config, PARAMETER); + bool_vector_type * mask; + bool initialized = true; + int ikey = 0; + if (__mask != NULL) + mask = __mask; + else + mask = bool_vector_alloc(0 , true ); + + while ((ikey < stringlist_get_size( parameter_keys )) && (initialized)) { + const enkf_config_node_type * config_node = ensemble_config_get_node(ensemble_config , stringlist_iget( parameter_keys , ikey) ); + int iens = 0; + do { + if (bool_vector_safe_iget( mask , iens)) { + node_id_type node_id = {.report_step = 0 , .iens = iens }; + initialized = enkf_config_node_has_node( config_node , fs , node_id); + } + iens++; + } while ((iens < enkf_main->ens_size) && (initialized)); + ikey++; + } + + stringlist_free( parameter_keys ); + if (__mask == NULL) + bool_vector_free( mask ); + return initialized; +} + + + +bool enkf_main_case_is_initialized( const enkf_main_type * enkf_main , const char * case_name , bool_vector_type * __mask) { + enkf_fs_type * fs = enkf_main_mount_alt_fs(enkf_main , case_name , false ); + if (fs) { + bool initialized = enkf_main_case_is_initialized__(enkf_main , fs , __mask); + enkf_fs_decref( fs ); + return initialized; + } else + return false; +} + + + +bool enkf_main_is_initialized( const enkf_main_type * enkf_main , bool_vector_type * __mask) { + return enkf_main_case_is_initialized__(enkf_main , enkf_main->dbase , __mask); +} + + +static void update_case_log(enkf_main_type * enkf_main , const char * case_path) { + /* : Update a small text file with the name of the host currently + running ert, the pid number of the process, the active case + and when it started. + + If the previous shutdown was unclean the file will be around, + and we will need the info from the previous invocation which + is in the file. For that reason we open with mode 'a' instead + of 'w'. + */ + + const char * ens_path = model_config_get_enspath(enkf_main_get_model_config(enkf_main)); + + { + int buffer_size = 256; + char * current_host = util_alloc_filename( ens_path , CASE_LOG , NULL ); + FILE * stream = util_fopen( current_host , "a"); + + fprintf(stream , "CASE:%-16s " , case_path ); + fprintf(stream , "PID:%-8d " , getpid()); + { + char hostname[buffer_size]; + gethostname( hostname , buffer_size ); + fprintf(stream , "HOST:%-16s " , hostname ); + } + + + { + int year,month,day,hour,minute,second; + time_t now = time( NULL ); + + util_set_datetime_values_utc( now , &second , &minute , &hour , &day , &month , &year ); + + fprintf(stream , "TIME:%02d/%02d/%4d-%02d.%02d.%02d\n" , day , month , year , hour , minute , second); + } + fclose( stream ); + free( current_host ); + } +} + + + +static void enkf_main_write_current_case_file( const enkf_main_type * enkf_main, const char * case_path) { + const char * ens_path = model_config_get_enspath(enkf_main_get_model_config(enkf_main)); + const char * base = CURRENT_CASE_FILE; + char * current_case_file = util_alloc_filename(ens_path , base, NULL); + FILE * stream = util_fopen( current_case_file , "w"); + fprintf(stream, "%s", case_path); + fclose(stream); + free(current_case_file); +} + + +static void enkf_main_gen_data_special( enkf_main_type * enkf_main , enkf_fs_type * fs ) { + ensemble_config_type * ensemble_config = enkf_main_get_ensemble_config(enkf_main); + stringlist_type * gen_data_keys = ensemble_config_alloc_keylist_from_impl_type(ensemble_config, GEN_DATA); + for (int i=0; i < stringlist_get_size( gen_data_keys ); i++) { + enkf_config_node_type * config_node = ensemble_config_get_node(ensemble_config, stringlist_iget( gen_data_keys , i)); + gen_data_config_type * gen_data_config = (gen_data_config_type *)enkf_config_node_get_ref( config_node ); + + if (gen_data_config_is_dynamic( gen_data_config )) + gen_data_config_set_ens_size( gen_data_config , enkf_main->ens_size ); + + } + stringlist_free( gen_data_keys ); +} + + +static void enkf_main_update_current_case( enkf_main_type * enkf_main , const char * case_path /* Can be NULL */) { + if (!case_path) + case_path = enkf_fs_get_case_name( enkf_main_get_fs(enkf_main) ); + + enkf_main_write_current_case_file(enkf_main, case_path); + update_case_log(enkf_main , case_path); + + enkf_main_gen_data_special( enkf_main , enkf_main_get_fs( enkf_main )); + enkf_main_add_internal_subst_kw( enkf_main , "ERT-CASE" , enkf_main_get_current_fs( enkf_main ) , "Current case"); + enkf_main_add_internal_subst_kw( enkf_main , "ERTCASE" , enkf_main_get_current_fs( enkf_main ) , "Current case"); +} + + + +static void enkf_main_create_fs( const enkf_main_type * enkf_main , const char * case_path ) { + char * new_mount_point = enkf_main_alloc_mount_point( enkf_main , case_path ); + + enkf_fs_create_fs( new_mount_point, BLOCK_FS_DRIVER_ID, NULL, false ); + + free( new_mount_point ); +} + + +const char * enkf_main_get_mount_root( const enkf_main_type * enkf_main) { + return model_config_get_enspath(enkf_main_get_model_config(enkf_main)); +} + + + +char * enkf_main_alloc_mount_point( const enkf_main_type * enkf_main , const char * case_path) { + char * mount_point; + if (util_is_abs_path( case_path )) + mount_point = util_alloc_string_copy( case_path ); + else + mount_point = util_alloc_filename( model_config_get_enspath(enkf_main_get_model_config(enkf_main)) , case_path , NULL); + return mount_point; +} + +/* + Return a weak reference - i.e. the refcount is not increased. +*/ +enkf_fs_type * enkf_main_get_fs(const enkf_main_type * enkf_main) { + return enkf_main->dbase; +} + +enkf_fs_type * enkf_main_job_get_fs(const enkf_main_type * enkf_main) { + return enkf_main->dbase; +} + + +enkf_fs_type * enkf_main_get_fs_ref(const enkf_main_type * enkf_main) { + return enkf_fs_get_ref( enkf_main->dbase ); +} + + +const char * enkf_main_get_current_fs( const enkf_main_type * enkf_main ) { + return enkf_fs_get_case_name( enkf_main->dbase ); +} + + + +/* + This function will return a valid enkf_fs instance; either just a + pointer to the current enkf_main->dbase, or alternatively it will + create a brand new fs instance. Because we do not really know whether + a new instance has been created or not resource handling becomes + slightly non trivial: + + + 1. When calling scope is finished with the enkf_fs instance it + must call enkf_fs_decref(); the enkf_fs_decref() function will + close the filesystem and free all resources when the reference + count has reached zero. +*/ + + +enkf_fs_type * enkf_main_mount_alt_fs(const enkf_main_type * enkf_main , const char * case_path , bool create) { + if (enkf_main_case_is_current( enkf_main , case_path )) { + // Fast path - we just return a reference to the currently selected case; + // with increased refcount. + enkf_fs_incref( enkf_main->dbase ); + return enkf_main->dbase; + } else { + // We have asked for an alterantive fs - must mount and possibly create that first. + enkf_fs_type * new_fs = NULL; + if (case_path != NULL) { + char * new_mount_point = enkf_main_alloc_mount_point( enkf_main , case_path ); + + if (!enkf_fs_exists( new_mount_point )) { + if (create) + enkf_main_create_fs( enkf_main , case_path ); + } + + new_fs = enkf_fs_mount( new_mount_point ); + if (new_fs) { + const model_config_type * model_config = enkf_main_get_model_config( enkf_main ); + const ecl_sum_type * refcase = model_config_get_refcase( model_config ); + + if (refcase) { + time_map_type * time_map = enkf_fs_get_time_map( new_fs ); + if (time_map_attach_refcase( time_map , refcase)) + time_map_set_strict( time_map , false ); + else + res_log_ferror("Warning mismatch between refcase:%s and existing case:%s", + ecl_sum_get_case(refcase), new_mount_point); + } + } + + free( new_mount_point ); + } + return new_fs; + } +} + + +static void enkf_main_update_summary_config_from_fs__(enkf_main_type * enkf_main, enkf_fs_type * fs) { + ensemble_config_type * ensemble_config = enkf_main_get_ensemble_config(enkf_main); + summary_key_set_type * summary_key_set = enkf_fs_get_summary_key_set(fs); + stringlist_type * keys = summary_key_set_alloc_keys(summary_key_set); + + for(int i = 0; i < stringlist_get_size(keys); i++) { + const char * key = stringlist_iget(keys, i); + ensemble_config_add_summary(ensemble_config, key, LOAD_FAIL_SILENT); + } + stringlist_free( keys ); +} + + +/** + The enkf_fs instances employ a simple reference counting + scheme. The main point with this system is to avoid opening the + full timesystem more than necessary (this is quite compute + intensive). This is essentially achieved by: + + 1. Create new fs instances by using the function + enkf_main_mount_alt_fs() - depending on the input arguments + this will either create a new enkf_fs instance or it will + just return a pointer to currently open fs instance; with an + increased refcount. + + 2. When you are finished with working with filesystem pointer + call enkf_fs_unmount() - this will reduce the refcount with + one, and eventually discard the complete datastructure when + the refcount has reached zero. + + 3. By using the function enkf_main_get_fs() / + enkf_fs_get_weakref() you get a pointer to the current fs + instance WITHOUT INCREASING THE REFCOUNT. This means that + scope calling one of these functions does not get any + ownership to the enkf_fs instance. + + The enkf_main instance will take ownership of the enkf_fs instance; + this implies that the calling scope must have proper ownership of + the fs instance which is passed in. The return value from + enkf_main_get_fs() can NOT be used as input to this function; this + is not checked for in any way - but the crash will be horrible if + this is not adhered to. +*/ + +void enkf_main_set_fs( enkf_main_type * enkf_main , enkf_fs_type * fs , const char * case_path /* Can be NULL */) { + if (enkf_main->dbase != fs) { + enkf_fs_incref( fs ); + + if (enkf_main->dbase) + enkf_fs_decref(enkf_main->dbase); + + enkf_main->dbase = fs; + enkf_main_update_current_case(enkf_main, case_path); + + enkf_main_update_summary_config_from_fs__(enkf_main, fs); + } +} + + + +void enkf_main_select_fs( enkf_main_type * enkf_main , const char * case_path ) { + if (enkf_main_case_is_current( enkf_main , case_path )) + return; /* We have tried to select the currently selected case - just return. */ + else { + enkf_fs_type * new_fs = enkf_main_mount_alt_fs( enkf_main , case_path , true ); + if (enkf_main->dbase == new_fs) + util_abort("%s : return reference to current FS in situation where that should not happen.\n",__func__); + + if (new_fs != NULL) + enkf_main_set_fs( enkf_main , new_fs , case_path); + else { + const char * ens_path = model_config_get_enspath(enkf_main_get_model_config(enkf_main)); + util_exit("%s: select filesystem %s:%s failed \n",__func__ , ens_path , case_path ); + } + enkf_fs_decref( new_fs ); + } +} + + +static void enkf_main_user_select_initial_fs(enkf_main_type * enkf_main) { + const char * ens_path = model_config_get_enspath(enkf_main_get_model_config(enkf_main)); + int root_version = enkf_fs_get_version104( ens_path ); + if (root_version == -1 || root_version == 105) { + char * current_mount_point = util_alloc_filename( ens_path , CURRENT_CASE , NULL); + + if (enkf_main_current_case_file_exists(enkf_main)) { + char * current_case = enkf_main_read_alloc_current_case_name(enkf_main); + enkf_main_select_fs(enkf_main, current_case); + free (current_case); + } else if (enkf_fs_exists( current_mount_point ) && util_is_link( current_mount_point )) { + /*If the current_case file does not exists, but the 'current' symlink does we use readlink to + get hold of the actual target before calling the enkf_main_select_fs() function. We then + write the current_case file and delete the symlink.*/ + char * target_case = util_alloc_atlink_target( ens_path , CURRENT_CASE ); + enkf_main_select_fs( enkf_main , target_case ); + unlink(current_mount_point); + enkf_main_write_current_case_file(enkf_main, target_case); + free( target_case ); + } else + enkf_main_select_fs( enkf_main , DEFAULT_CASE ); // Selecting (a new) default case + + free( current_mount_point ); + } else { + fprintf(stderr,"Sorry: the filesystem located in %s must be upgraded before the current ERT version can read it.\n" , ens_path); + exit(1); + } +} + + +bool enkf_main_fs_exists(const enkf_main_type * enkf_main, const char * input_case){ + bool exists = false; + char * new_mount_point = enkf_main_alloc_mount_point( enkf_main , input_case); + if(enkf_fs_exists( new_mount_point )) + exists = true; + + free( new_mount_point ); + return exists; +} + + + +state_map_type * enkf_main_alloc_readonly_state_map( const enkf_main_type * enkf_main , const char * case_path) { + char * mount_point = enkf_main_alloc_mount_point( enkf_main , case_path ); + state_map_type * state_map = enkf_fs_alloc_readonly_state_map( mount_point ); + free( mount_point ); + return state_map; +} + + + +time_map_type * enkf_main_alloc_readonly_time_map( const enkf_main_type * enkf_main , const char * case_path ) { + char * mount_point = enkf_main_alloc_mount_point( enkf_main , case_path ); + time_map_type * time_map = enkf_fs_alloc_readonly_time_map( mount_point ); + free( mount_point ); + return time_map; +} + + +void enkf_main_close_fs( enkf_main_type * enkf_main ) { + if (enkf_main->dbase != NULL) + enkf_fs_decref( enkf_main->dbase ); +} diff --git a/libres/lib/enkf/enkf_main_update.cpp b/libres/lib/enkf/enkf_main_update.cpp new file mode 100644 index 00000000000..e35ec68f1cd --- /dev/null +++ b/libres/lib/enkf/enkf_main_update.cpp @@ -0,0 +1,39 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'enkf_main_update.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 + +/** + This file implements functions related to updating the state. The + really low level functions related to the analysis is implemented + in enkf_analysis.h. + + This file only implements code for updating, and not any data + structures. +*/ + +#include "enkf_main_struct.h" + + diff --git a/libres/lib/enkf/enkf_node.cpp b/libres/lib/enkf/enkf_node.cpp new file mode 100644 index 00000000000..85df04aaac3 --- /dev/null +++ b/libres/lib/enkf/enkf_node.cpp @@ -0,0 +1,983 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'enkf_node.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 small illustration (says more than thousand words ...) of how the + enkf_node, enkf_config_node, field[1] and field_config[1] objects + are linked. + + + ================ + | | o----------- + | ================ | ===================== + | | | o-------- | | + | | ================ |-------------> | | + | | | | | | enkf_config_node | + | | | | | | | + ===| | enkf_node | o------ | | + o | | | | | + | ===| | ===================== + | o | | o + | | ================ | + | | o | + | \ | | + | \ | | + | | | | + | | | | + | | | | + | | | | + \|/ | | | + ======|======|== \|/ + | \|/ | | o----------- + | ==========|===== | ===================== + | | \|/ | o-------- | | + | | ================ |-------------> | | + | | | | | | field_config | + | | | | | | | + ===| | field | o------ | | + | | | | | + === | | ===================== + | | + ================ + + + To summarize in words: + + * The enkf_node object is an abstract object, which again contains + a spesific enkf_object, like e.g. the field objects shown + here. In general we have an ensemble of enkf_node objects. + + * The enkf_node objects contain a pointer to an enkf_config_node + object. + + * The enkf_config_node object contains a pointer to the specific + config object, i.e. field_config in this case. + + * All the field objects contain a pointer to a field_config object. + + + [1]: field is just an example, and could be replaced with any of + the enkf object types. +*/ + +/*-----------------------------------------------------------------*/ + +/** + A note on memory + ================ + + The enkf_nodes can consume large amounts of memory, and for large + models/ensembles we have a situation where not all the + members/fields can be in memory simultaneously - such low-memory + situations are not really supported at the moment, but we have + implemented some support for such problems: + + o All enkf objects should have a xxx_realloc_data() function. This + function should be implemented in such a way that it is always + safe to call, i.e. if the object already has allocated data the + function should just return. + + o All enkf objects should implement a xxx_free_data() + function. This function free the data of the object, and set the + data pointer to NULL. + + + The following 'rules' apply to the memory treatment: + ---------------------------------------------------- + + o Functions writing to memory can always be called, and it is their + responsibility to allocate memory before actually writing on it. The + writer functions are: + + enkf_node_initialize() + enkf_node_forward_load() + + These functions should all start with a call to + enkf_node_ensure_memory(). The (re)allocation of data is done at + the enkf_node level, and **NOT** in the low level object + (altough that is where it is eventually done of course). + + o When it comes to functions reading memory it is a bit more + tricky. It could be that if the functions are called without + memory, that just means that the object is not active or + something (and the function should just return). On the other + hand trying to read a NULL pointer does indicate that program + logic is not fully up to it? And should therefor maybe be + punished? + + o The only memory operation which is exported to 'user-space' + (i.e. the enkf_state object) is enkf_node_free_data(). + +*/ + + +/** + Keeeping track of node state. + ============================= + + To keep track of the state of the node's data (actually the data of + the contained enkf_object, i.e. a field) we have three highly + internal variables __state, __modified , __iens, and + __report_step. These three variables are used/updated in the + following manner: + + + + 1. The nodes are created with (modified, report_step, state, iens) == + (true , -1 , undefined , -1). + + 2. After initialization we set: report_step -> 0 , state -> + analyzed, modified -> true, iens -> -1 + + 3. After load (both from ensemble and ECLIPSE). We set modified -> + false, and report_step, state and iens according to the load + arguments. + + 4. After deserialize (i.e. update) we set modified -> true. + + 5. After write (to ensemble) we set in the same way as after load. + + 6. After free_data we invalidate according to the newly allocated + status. + + 7. In the ens_load routine we check if modified == false and the + report_step and state arguments agree with the current + values. IN THAT CASE WE JUST RETURN WITHOUT ACTUALLY HITTING + THE FILESYSTEM. This performance gain is the main point of the + whole exercise. +*/ + + + +#define ENKF_NODE_TYPE_ID 71043086 + +struct enkf_node_struct { + UTIL_TYPE_ID_DECLARATION; + alloc_ftype * alloc; + ecl_write_ftype * ecl_write; + forward_load_ftype * forward_load; + forward_load_vector_ftype * forward_load_vector; + free_data_ftype * free_data; + user_get_ftype * user_get; + user_get_vector_ftype * user_get_vector; + set_inflation_ftype * set_inflation; + fload_ftype * fload; + has_data_ftype * has_data; + + serialize_ftype * serialize; + deserialize_ftype * deserialize; + read_from_buffer_ftype * read_from_buffer; + write_to_buffer_ftype * write_to_buffer; + initialize_ftype * initialize; + node_free_ftype * freef; + clear_ftype * clear; + node_copy_ftype * copy; + scale_ftype * scale; + iadd_ftype * iadd; + imul_ftype * imul; + isqrt_ftype * isqrt; + iaddsqr_ftype * iaddsqr; + + /******************************************************************/ + bool vector_storage; + char *node_key; /* The (hash)key this node is identified with. */ + void *data; /* A pointer to the underlying enkf_object, i.e. gen_kw_type instance, or a field_type instance or ... */ + const enkf_config_node_type *config; /* A pointer to a enkf_config_node instance (which again cointans a pointer to the config object of data). */ + /*****************************************************************/ + + vector_type *container_nodes; +}; + + +UTIL_IS_INSTANCE_FUNCTION( enkf_node, ENKF_NODE_TYPE_ID ) + +const enkf_config_node_type * enkf_node_get_config(const enkf_node_type * node) { + return node->config; +} + + +bool enkf_node_vector_storage( const enkf_node_type * node ) { + return node->vector_storage; +} + + +/*****************************************************************/ + +/* + All the function pointers REALLY should be in the config object ... +*/ + + +#define FUNC_ASSERT(func) if (func == NULL) util_abort("%s: function handler: %s not registered for node:%s - aborting\n",__func__ , #func , enkf_node->node_key); + + + + +void enkf_node_alloc_domain_object(enkf_node_type * enkf_node) { + FUNC_ASSERT(enkf_node->alloc); + { + if (enkf_node->data != NULL) + enkf_node->freef( enkf_node->data ); + enkf_node->data = enkf_node->alloc( enkf_config_node_get_ref(enkf_node->config) ); // CXX_CAST_ERROR + } +} + + + + +enkf_node_type * enkf_node_copyc(const enkf_node_type * enkf_node) { + FUNC_ASSERT(enkf_node->copy); + { + const enkf_node_type * src = enkf_node; + enkf_node_type * target; + target = enkf_node_alloc(src->config); + src->copy( src->data , target->data ); /* Calling the low level copy function */ + return target; + } +} + +ert_impl_type enkf_node_get_impl_type(const enkf_node_type * enkf_node) { + return enkf_config_node_get_impl_type(enkf_node->config); +} + +bool enkf_node_use_forward_init( const enkf_node_type * enkf_node ) { + return enkf_config_node_use_forward_init( enkf_node->config ); +} + + + +void * enkf_node_value_ptr(const enkf_node_type * enkf_node) { + return enkf_node->data; +} + + + +/** + This function calls the node spesific ecl_write function. IF the + ecl_file of the (node == NULL) *ONLY* the path is sent to the node + spesific file. +*/ + +void enkf_node_ecl_write(const enkf_node_type *enkf_node , const char *path , value_export_type * export_value , int report_step) { + if (enkf_node->ecl_write != NULL) { + char * node_eclfile = enkf_config_node_alloc_outfile(enkf_node->config , report_step); /* Will return NULL if the node does not have any outfile format. */ + /* + If the node does not have a outfile (i.e. ecl_file), the + ecl_write function will be called with file argument NULL. It + is then the responsability of the low-level implementation to + do "the right thing". + */ + enkf_node->ecl_write(enkf_node->data , path , node_eclfile , export_value); + free( node_eclfile ); + } +} + + + +/** + This function takes a string - key - as input an calls a node + specific function to look up one scalar based on that key. The key + is always a string, but the the type of content will vary for the + different objects. For a field, the key will be a string of "i,j,k" + for a cell. + + If the user has asked for something which does not exist the + function SHOULD NOT FAIL; it should return false and set the *value + to 0. +*/ + + +bool enkf_node_user_get(enkf_node_type * enkf_node , enkf_fs_type * fs , const char * key , node_id_type node_id , double * value) { + return enkf_node_user_get_no_id( enkf_node , fs , key , node_id.report_step , node_id.iens , value ); +} + +bool enkf_node_user_get_no_id(enkf_node_type * enkf_node , enkf_fs_type * fs , const char * key , int report_step, int iens, double * value) { + node_id_type node_id = {.report_step = report_step , .iens = iens}; + bool loadOK; + FUNC_ASSERT( enkf_node->user_get ); + { + loadOK = enkf_node_try_load( enkf_node , fs , node_id); + + if (loadOK) + return enkf_node->user_get(enkf_node->data , key , report_step, value); + else { + *value = 0; + return false; + } + } +} + +bool enkf_node_user_get_vector( enkf_node_type * enkf_node , enkf_fs_type * fs , const char * key , int iens , double_vector_type * values) { + if (enkf_node->vector_storage) { + if (enkf_node_try_load_vector( enkf_node , fs , iens )) { + enkf_node->user_get_vector( enkf_node->data , key , values); + return true; + } else + return false; + } else { + util_abort("%s: internal error - function should only be called by nodes with vector storage.\n",__func__); + return false; + } +} + + + +bool enkf_node_fload( enkf_node_type * enkf_node , const char * filename ) { + FUNC_ASSERT( enkf_node->fload ); + return enkf_node->fload( enkf_node->data , filename ); +} + + + +/** + This function loads (internalizes) ECLIPSE results, the ecl_file + instance with restart data, and the ecl_sum instance with summary + data must already be loaded by the calling function. + + IFF the enkf_node has registered a filename to load from, that is + passed to the specific load function, otherwise the run_path is sent + to the load function. + + If the node does not have a forward_load function, the function just + returns. +*/ + + +bool enkf_node_forward_load(enkf_node_type *enkf_node , const forward_load_context_type * load_context) { + bool loadOK; + FUNC_ASSERT(enkf_node->forward_load); + { + if (enkf_node_get_impl_type(enkf_node) == SUMMARY) + /* Fast path for loading summary data. */ + loadOK = enkf_node->forward_load(enkf_node->data , NULL , load_context); + else { + char * input_file = enkf_config_node_alloc_infile(enkf_node->config , forward_load_context_get_load_step( load_context )); + + if (input_file != NULL) { + char * file = util_alloc_filename( forward_load_context_get_run_path( load_context ) , input_file , NULL); + loadOK = enkf_node->forward_load(enkf_node->data , file , load_context); + free(file); + } else + loadOK = enkf_node->forward_load(enkf_node->data , NULL , load_context); + + free( input_file ); + } + } + return loadOK; +} + + +bool enkf_node_forward_init(enkf_node_type * enkf_node , const char * run_path , int iens) { + char * init_file = enkf_config_node_alloc_initfile( enkf_node->config , run_path , iens ); + bool init = enkf_node->initialize(enkf_node->data , iens , init_file, NULL); + free( init_file ); + return init; +} + + + +bool enkf_node_forward_load_vector(enkf_node_type *enkf_node , const forward_load_context_type * load_context , const int_vector_type * time_index) { + bool loadOK; + FUNC_ASSERT(enkf_node->forward_load_vector); + loadOK = enkf_node->forward_load_vector(enkf_node->data , NULL , load_context , time_index); + + return loadOK; +} + + + + + + +static bool enkf_node_store_buffer( enkf_node_type * enkf_node , enkf_fs_type * fs , int report_step , int iens) { + FUNC_ASSERT(enkf_node->write_to_buffer); + { + bool data_written; + buffer_type * buffer = buffer_alloc( 100 ); + const enkf_config_node_type * config_node = enkf_node_get_config( enkf_node ); + buffer_fwrite_time_t( buffer , time(NULL)); + data_written = enkf_node->write_to_buffer(enkf_node->data , buffer , report_step ); + if (data_written) { + const char * node_key = enkf_config_node_get_key( config_node ); + enkf_var_type var_type = enkf_config_node_get_var_type( config_node ); + + if (enkf_node->vector_storage) + enkf_fs_fwrite_vector( fs , buffer , node_key , var_type , iens ); + else + enkf_fs_fwrite_node( fs , buffer , node_key , var_type , report_step , iens ); + + } + buffer_free( buffer ); + return data_written; + } +} + +bool enkf_node_store_vector(enkf_node_type *enkf_node , enkf_fs_type * fs , int iens ) { + return enkf_node_store_buffer( enkf_node , fs , -1 , iens ); +} + + + +bool enkf_node_store(enkf_node_type * enkf_node , enkf_fs_type * fs , node_id_type node_id) { + if (enkf_node->vector_storage) + return enkf_node_store_vector( enkf_node , fs , node_id.iens ); + else + return enkf_node_store_buffer( enkf_node , fs , node_id.report_step , node_id.iens ); +} + + + +/** + This function will load a node from the filesystem if it is + available; if not it will just return false. + + The state argument can be 'both' - in which case it will first try + the analyzed, and then subsequently the forecast before giving up + and returning false. If the function returns true with state == + 'both' it is no way to determine which version was actually loaded. +*/ + +bool enkf_node_try_load(enkf_node_type *enkf_node , enkf_fs_type * fs , node_id_type node_id) { + if (enkf_node_has_data( enkf_node , fs , node_id)) { + enkf_node_load( enkf_node , fs , node_id); + return true; + } else + return false; +} + + +static void enkf_node_buffer_load( enkf_node_type * enkf_node , enkf_fs_type * fs , int report_step , int iens) { + FUNC_ASSERT(enkf_node->read_from_buffer); + { + buffer_type * buffer = buffer_alloc( 100 ); + const enkf_config_node_type * config_node = enkf_node_get_config( enkf_node ); + const char * node_key = enkf_config_node_get_key( config_node ); + enkf_var_type var_type = enkf_config_node_get_var_type( config_node ); + + if (enkf_node->vector_storage) + enkf_fs_fread_vector( fs , buffer , node_key , var_type , iens ); + else + enkf_fs_fread_node( fs , buffer , node_key , var_type , report_step , iens ); + + buffer_fskip_time_t( buffer ); + + enkf_node->read_from_buffer(enkf_node->data , buffer , fs , report_step ); + buffer_free( buffer ); + } +} + + + + +void enkf_node_load_vector( enkf_node_type * enkf_node , enkf_fs_type * fs , int iens ) { + enkf_node_buffer_load( enkf_node , fs , -1 , iens ); +} + + + +static void enkf_node_load_container( enkf_node_type * enkf_node , enkf_fs_type * fs , node_id_type node_id ) { + for (int inode=0; inode < vector_get_size( enkf_node->container_nodes ); inode++) { + enkf_node_type * child_node = (enkf_node_type *)vector_iget( enkf_node->container_nodes , inode ); + enkf_node_load( child_node , fs , node_id ); + } +} + + +void enkf_node_load(enkf_node_type * enkf_node , enkf_fs_type * fs , node_id_type node_id) { + if (enkf_node_get_impl_type(enkf_node) == CONTAINER) + enkf_node_load_container( enkf_node , fs , node_id ); + else { + if (enkf_node->vector_storage) + enkf_node_load_vector( enkf_node , fs , node_id.iens ); + else + /* Normal load path */ + enkf_node_buffer_load( enkf_node , fs , node_id.report_step, node_id.iens ); + } +} + + +bool enkf_node_try_load_vector(enkf_node_type *enkf_node , enkf_fs_type * fs , int iens ) { + if (enkf_config_node_has_vector( enkf_node->config , fs , iens)) { + enkf_node_load_vector( enkf_node , fs , iens ); + return true; + } else + return false; +} + + + + + +/* + In the case of nodes with vector storage this function + will load the entire vector. +*/ + +enkf_node_type * enkf_node_load_alloc( const enkf_config_node_type * config_node , enkf_fs_type * fs , node_id_type node_id) { + if (enkf_config_node_vector_storage( config_node )) { + if (enkf_config_node_has_vector( config_node , fs , node_id.iens)) { + enkf_node_type * node = enkf_node_alloc( config_node ); + enkf_node_load( node , fs , node_id ); + return node; + } else { + util_abort("%s: could not load vector:%s from iens:%d\n",__func__ , enkf_config_node_get_key( config_node ), + node_id.iens ); + return NULL; + } + } else { + if (enkf_config_node_has_node( config_node , fs , node_id)) { + enkf_node_type * node = enkf_node_alloc( config_node ); + enkf_node_load( node , fs , node_id ); + return node; + } else { + util_abort("%s: Could not load node: key:%s iens:%d report:%d \n", + __func__ , + enkf_config_node_get_key( config_node ) , + node_id.iens , node_id.report_step ); + return NULL; + } + } +} + + + +void enkf_node_copy(const enkf_config_node_type * config_node , + enkf_fs_type * src_case, + enkf_fs_type * target_case, + node_id_type src_id , + node_id_type target_id) { + + enkf_node_type * enkf_node = enkf_node_load_alloc(config_node, src_case , src_id); + + + /* Hack to ensure that size is set for the gen_data instances. + This sneeks low level stuff into a high level scope. BAD. */ + { + ert_impl_type impl_type = enkf_node_get_impl_type( enkf_node ); + if (impl_type == GEN_DATA) { + /* Read the size at report_step_from */ + gen_data_type * gen_data = (gen_data_type * ) enkf_node_value_ptr( enkf_node ); + int size = gen_data_get_size( gen_data ); + + /* Enforce the size at report_step_to */ + gen_data_assert_size( gen_data , size , target_id.report_step); + } + } + + enkf_node_store(enkf_node, target_case , target_id ); + enkf_node_free(enkf_node); +} + +bool enkf_node_has_data( enkf_node_type * enkf_node , enkf_fs_type * fs , node_id_type node_id) { + if (enkf_node->vector_storage) { + FUNC_ASSERT(enkf_node->has_data); + { + int report_step = node_id.report_step; + int iens = node_id.iens; + + // Try to load the vector. + if (enkf_config_node_has_vector( enkf_node->config , fs , iens )) { + enkf_node_load_vector( enkf_node , fs , iens); + + // The vector is loaded. Check if we have the report_step/state asked for: + return enkf_node->has_data( enkf_node->data , report_step ); + } else + return false; + } + } else + return enkf_config_node_has_node( enkf_node->config , fs , node_id ); +} + + +void enkf_node_serialize(enkf_node_type *enkf_node , enkf_fs_type * fs, node_id_type node_id , + const active_list_type * active_list , matrix_type * A , int row_offset , int column) { + + FUNC_ASSERT(enkf_node->serialize); + enkf_node_load( enkf_node , fs , node_id); + enkf_node->serialize(enkf_node->data , node_id , active_list , A , row_offset , column); + +} + + + +void enkf_node_deserialize(enkf_node_type *enkf_node , enkf_fs_type * fs , node_id_type node_id, + const active_list_type * active_list , const matrix_type * A , int row_offset , int column) { + + FUNC_ASSERT(enkf_node->deserialize); + enkf_node->deserialize(enkf_node->data , node_id , active_list , A , row_offset , column); + enkf_node_store( enkf_node , fs , node_id ); +} + + + +void enkf_node_set_inflation( enkf_node_type * inflation , const enkf_node_type * std , const enkf_node_type * min_std) { + { + enkf_node_type * enkf_node = inflation; + FUNC_ASSERT(enkf_node->set_inflation); + } + inflation->set_inflation( inflation->data , std->data , min_std->data ); +} + + +void enkf_node_sqrt(enkf_node_type *enkf_node) { + FUNC_ASSERT(enkf_node->isqrt); + enkf_node->isqrt(enkf_node->data); +} + + +void enkf_node_scale(enkf_node_type *enkf_node , double scale_factor) { + FUNC_ASSERT(enkf_node->scale); + enkf_node->scale(enkf_node->data , scale_factor); +} + + +void enkf_node_iadd(enkf_node_type *enkf_node , const enkf_node_type * delta_node) { + FUNC_ASSERT(enkf_node->iadd); + enkf_node->iadd(enkf_node->data , delta_node->data); +} + + +void enkf_node_iaddsqr(enkf_node_type *enkf_node , const enkf_node_type * delta_node) { + FUNC_ASSERT(enkf_node->iaddsqr); + enkf_node->iaddsqr(enkf_node->data , delta_node->data); +} + + +void enkf_node_imul(enkf_node_type *enkf_node , const enkf_node_type * delta_node) { + FUNC_ASSERT(enkf_node->imul); + enkf_node->imul(enkf_node->data , delta_node->data); +} + + + +/** + The return value is whether any initialization has actually taken + place. If the function returns false it is for instance not + necessary to internalize anything. +*/ + +bool enkf_node_initialize(enkf_node_type *enkf_node, int iens , rng_type * rng) { + if (enkf_node_use_forward_init( enkf_node )) + return false; // This node will be initialized by loading results from the forward model. + else { + if (enkf_node->initialize != NULL) { + char * init_file = enkf_config_node_alloc_initfile( enkf_node->config , NULL , iens ); + bool init = enkf_node->initialize(enkf_node->data , iens , init_file, rng); + free( init_file ); + return init; + } else + return false; /* No init performed */ + } +} + + + + +void enkf_node_clear(enkf_node_type *enkf_node) { + FUNC_ASSERT(enkf_node->clear); + enkf_node->clear(enkf_node->data); +} + + +void enkf_node_free(enkf_node_type *enkf_node) { + if (enkf_node->freef != NULL) + enkf_node->freef(enkf_node->data); + free(enkf_node->node_key); + vector_free(enkf_node->container_nodes); + free(enkf_node); +} + + +void enkf_node_free__(void *void_node) { + enkf_node_free((enkf_node_type *) void_node); +} + +const char * enkf_node_get_key(const enkf_node_type * enkf_node) { + return enkf_node->node_key; +} + + +#undef FUNC_ASSERT + + + +/*****************************************************************/ + + + +/* Manual inheritance - .... */ +static enkf_node_type * enkf_node_alloc_empty(const enkf_config_node_type *config ) { + const char *node_key = enkf_config_node_get_key(config); + ert_impl_type impl_type = enkf_config_node_get_impl_type(config); + enkf_node_type * node = (enkf_node_type *)util_malloc(sizeof * node ); + node->vector_storage = enkf_config_node_vector_storage( config ); + node->config = config; + node->node_key = util_alloc_string_copy(node_key); + node->data = NULL; + node->container_nodes = vector_alloc_new( ); + + /* + Start by initializing all function pointers to NULL. + */ + node->alloc = NULL; + node->ecl_write = NULL; + node->forward_load = NULL; + node->forward_load_vector = NULL; + node->copy = NULL; + node->initialize = NULL; + node->freef = NULL; + node->free_data = NULL; + node->user_get = NULL; + node->user_get_vector = NULL; + node->fload = NULL; + node->read_from_buffer = NULL; + node->write_to_buffer = NULL; + node->serialize = NULL; + node->deserialize = NULL; + node->clear = NULL; + node->set_inflation = NULL; + node->has_data = NULL; + + /* Math operations: */ + node->iadd = NULL; + node->scale = NULL; + node->isqrt = NULL; + node->iaddsqr = NULL; + node->imul = NULL; + + switch (impl_type) { + case(CONTAINER): + node->alloc = container_alloc__; + node->freef = container_free__; + break; + case(GEN_KW): + node->alloc = gen_kw_alloc__; + node->ecl_write = gen_kw_ecl_write__; + node->copy = gen_kw_copy__; + node->initialize = gen_kw_initialize__; + node->freef = gen_kw_free__; + node->user_get = gen_kw_user_get__; + node->write_to_buffer = gen_kw_write_to_buffer__; + node->read_from_buffer = gen_kw_read_from_buffer__; + node->serialize = gen_kw_serialize__; + node->deserialize = gen_kw_deserialize__; + node->clear = gen_kw_clear__; + node->iadd = gen_kw_iadd__; + node->scale = gen_kw_scale__; + node->iaddsqr = gen_kw_iaddsqr__; + node->imul = gen_kw_imul__; + node->isqrt = gen_kw_isqrt__; + node->set_inflation = gen_kw_set_inflation__; + node->fload = gen_kw_fload__; + break; + case(SUMMARY): + node->forward_load = summary_forward_load__; + node->forward_load_vector = summary_forward_load_vector__; + node->alloc = summary_alloc__; + node->copy = summary_copy__; + node->freef = summary_free__; + node->user_get = summary_user_get__; + node->user_get_vector = summary_user_get_vector__; + node->read_from_buffer = summary_read_from_buffer__; + node->write_to_buffer = summary_write_to_buffer__; + node->serialize = summary_serialize__; + node->deserialize = summary_deserialize__; + node->clear = summary_clear__; + node->has_data = summary_has_data__; + /* + node->iadd = summary_iadd__; + node->scale = summary_scale__; + node->iaddsqr = summary_iaddsqr__; + node->imul = summary_imul__; + node->isqrt = summary_isqrt__; + */ + break; + case(SURFACE): + node->initialize = surface_initialize__; + node->ecl_write = surface_ecl_write__; + node->alloc = surface_alloc__; + node->copy = surface_copy__; + node->freef = surface_free__; + node->user_get = surface_user_get__; + node->read_from_buffer = surface_read_from_buffer__; + node->write_to_buffer = surface_write_to_buffer__; + node->serialize = surface_serialize__; + node->deserialize = surface_deserialize__; + node->clear = surface_clear__; + node->iadd = surface_iadd__; + node->scale = surface_scale__; + node->iaddsqr = surface_iaddsqr__; + node->imul = surface_imul__; + node->isqrt = surface_isqrt__; + node->fload = surface_fload__; + break; + case(FIELD): + node->alloc = field_alloc__; + node->ecl_write = field_ecl_write__; + node->copy = field_copy__; + node->initialize = field_initialize__; + node->freef = field_free__; + node->user_get = field_user_get__; + node->read_from_buffer = field_read_from_buffer__; + node->write_to_buffer = field_write_to_buffer__; + node->serialize = field_serialize__; + node->deserialize = field_deserialize__; + + node->clear = field_clear__; + node->set_inflation = field_set_inflation__; + node->iadd = field_iadd__; + node->scale = field_scale__; + node->iaddsqr = field_iaddsqr__; + node->imul = field_imul__; + node->isqrt = field_isqrt__; + node->fload = field_fload__; + break; + case(GEN_DATA): + node->alloc = gen_data_alloc__; + node->initialize = gen_data_initialize__; + node->copy = gen_data_copy__; + node->freef = gen_data_free__; + node->ecl_write = gen_data_ecl_write__; + node->forward_load = gen_data_forward_load__; + node->user_get = gen_data_user_get__; + node->read_from_buffer = gen_data_read_from_buffer__; + node->write_to_buffer = gen_data_write_to_buffer__; + node->serialize = gen_data_serialize__; + node->deserialize = gen_data_deserialize__; + node->set_inflation = gen_data_set_inflation__; + + node->clear = gen_data_clear__; + node->iadd = gen_data_iadd__; + node->scale = gen_data_scale__; + node->iaddsqr = gen_data_iaddsqr__; + node->imul = gen_data_imul__; + node->isqrt = gen_data_isqrt__; + break; + case(EXT_PARAM): + node->alloc = ext_param_alloc__; + node->freef = ext_param_free__; + node->ecl_write = ext_param_ecl_write__; + node->write_to_buffer = ext_param_write_to_buffer__; + node->read_from_buffer = ext_param_read_from_buffer__; + break; + default: + util_abort("%s: implementation type: %d unknown - all hell is loose - aborting \n",__func__ , impl_type); + } + return node; +} + + + +#define CASE_SET(type , func) case(type): has_func = (func != NULL); break; +bool enkf_node_has_func(const enkf_node_type * node , node_function_type function_type) { + bool has_func = false; + switch (function_type) { + CASE_SET(alloc_func , node->alloc); + CASE_SET(ecl_write_func , node->ecl_write); + CASE_SET(forward_load_func , node->forward_load); + CASE_SET(copy_func , node->copy); + CASE_SET(initialize_func , node->initialize); + CASE_SET(free_func , node->freef); + default: + fprintf(stderr,"%s: node_function_identifier: %d not recognized - aborting \n",__func__ , function_type); + } + return has_func; +} +#undef CASE_SET + + + + +enkf_node_type * enkf_node_alloc(const enkf_config_node_type * config) { + enkf_node_type * node = enkf_node_alloc_empty(config); + UTIL_TYPE_ID_INIT( node , ENKF_NODE_TYPE_ID ); + enkf_node_alloc_domain_object(node); + return node; +} + + + + + +static void enkf_node_container_add_node( enkf_node_type * node , const enkf_node_type * child_node , bool shared) { + if (shared) + vector_append_ref( node->container_nodes , child_node ); + else + vector_append_owned_ref( node->container_nodes , child_node, enkf_node_free__ ); +} + +static enkf_node_type * enkf_node_alloc_container(const enkf_config_node_type * config, hash_type * node_hash , bool shared) { + enkf_node_type * container_node = enkf_node_alloc( config ); + { + for (int i=0; i < enkf_config_node_container_size( config ); i++) { + const enkf_config_node_type * child_config = enkf_config_node_container_iget( config , i ); + enkf_node_type * child_node; + + if (shared) + child_node = (enkf_node_type * ) hash_get( node_hash , enkf_config_node_get_key( child_config )); + else + child_node = enkf_node_alloc( child_config ); + + enkf_node_container_add_node( container_node , child_node , shared); + container_add_node( (container_type * ) enkf_node_value_ptr( container_node ) , enkf_node_value_ptr( child_node )); + } + } + return container_node; +} + +enkf_node_type * enkf_node_alloc_shared_container(const enkf_config_node_type * config, hash_type * node_hash) { + return enkf_node_alloc_container( config , node_hash , true ); +} + + +enkf_node_type * enkf_node_alloc_private_container(const enkf_config_node_type * config) { + return enkf_node_alloc_container( config , NULL , false ); +} + +enkf_node_type * enkf_node_deep_alloc(const enkf_config_node_type * config) { + if (enkf_config_node_get_impl_type( config ) == CONTAINER) { + enkf_node_type * container = enkf_node_alloc_container( config , NULL , false ); + container_assert_size( (const container_type * ) enkf_node_value_ptr( container )); + return container; + } else + return enkf_node_alloc( config ); +} + + + +bool enkf_node_internalize(const enkf_node_type * node, int report_step) { + return enkf_config_node_internalize( node->config , report_step ); +} diff --git a/libres/lib/enkf/enkf_obs.cpp b/libres/lib/enkf/enkf_obs.cpp new file mode 100644 index 00000000000..9d79b811691 --- /dev/null +++ b/libres/lib/enkf/enkf_obs.cpp @@ -0,0 +1,1255 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'enkf_obs.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 + +/* + +The observation system +---------------------- + +The observation system in the EnKF code is a three layer system. At +the the top is the enkf_obs_type. The enkf_main object contains one +enkf_obs instance which has internalized ALL the observation data. In +enkf_obs the the data is internalized in a hash table, where the keys +in the table are the keys used the observation file. + +The next level is the obs_vector type which is a vector of length +num_report_steps. Each element in this vector can either point a +spesific observation instance (which actually contains the data), or +be NULL, if the observation is not active at this report step. In +addition the obs_vector contains function pointers to manipulate the +observation data at the lowest level. + +At the lowest level we have specific observation instances, +field_obs, summary_obs and gen_obs. These instances contain the actual +data. + +To summarize we can say: + + 1. enkf_obs has ALL the observation data. + + 2. obs_vector has the full time series for one observation key, + i.e. all the watercuts in well P2. + + 3. field_obs/gen_obs/summary_obs instances contain the actual + observed data for one (logical) observation and one report step. + + +In the following example we have two observations + + WWCT:OP1 The water cut in well OP1. This is an observation which is + active for many report steps, at the lowest level it is + implemented as summary_obs. + + RFT_P2 This is an RFT test for one well. Only active at one report + step, implemented at the lowest level as a field_obs instance. + + + In the example below there are in total five report steps, hence all + the obs_vector instances have five 'slots'. If there is no active + observation for a particular report step, the corresponding pointer + in the obs_vector instance is NULL. + + + + _____________________ _____________________ + / enkf_obs \ + | | + | | + | obs_hash: {"WWCT:OP1" , "RFT_P2"} | + | | | | + | | | | + \________________|___________|________________________/ + | | + | | + | | + | \--------------------------------------------------------------\ + | | + | | + \|/ | + |--- obs_vector: WWCT:OP1 -----------------------------------------------------| | + | Function pointers: -------- -------- -------- -------- -------- | | + | Pointing to the | | | | | | | | | | | | + | underlying | NULL | | X | | X | | NULL | | X | | | + | implementation in the | | | | | | | | | | | | | | | + | summary_obs object. -------- ---|---- ---|---- -------- ---|---- | | + |---------------------------------------|---------|-------------------|--------| | + | | | | + \|/ | | | + |-- summary_obs -| | \|/ | + | Value: 0.56.. | | |-- summary_obs -| | + | std : 0.15.. | | | Value: 0.70.. | | + |----------------| | | std : 0.25.. | | + | |----------------| | + \|/ | + |-- summary_obs -| | + | Value: 0.62.. | | + | std : 0.12.. | | + |----------------| | + | + | + | + The observation WWCT:OP1 is an observation of summary type, and the | + obs_vector contains pointers to summary_obs instances; along with | + function pointers to manipulate the summary_obs instances. The | + observation is not active for report steps 0 and 3, so for these | + report steps the obse vector has a NULL pointer. | + | + | + | + | + | + | + |--- obs_vector: RFT_P2 -------------------------------------------------------| | + | Function pointers: -------- -------- -------- -------- -------- | | + | Pointing to the | | | | | | | | | | |<---------------/ + | underlying | NULL | | NULL | | NULL | | X | | NULL | | + | implementation in the | | | | | | | | | | | | + | field_obs object. -------- -------- -------- ---|---- -------- | + |-----------------------------------------------------------|------------------| + | + | + \|/ + |-- field_obs -----------------------------------| + | i = 25 , j = 16, k = 10, value = 278, std = 10 | + | i = 25 , j = 16, k = 11, value = 279, std = 10 | + | i = 25 , j = 16, k = 12, value = 279, std = 10 | + | i = 25 , j = 17, k = 12, value = 281, std = 10 | + | i = 25 , j = 18, k = 12, value = 282, std = 10 | + |------------------------------------------------| + + + The observation RFT_P2 is an RFT observation which is only active at + one report step, i.e. 4/5 pointers in the obs_vector are just NULL + pointers. The actual observation(s) are stored in a field_obs + instance. + + */ + + + + + + +////////////////////////////////////////////////////////////////////////////////////// + + +#define ENKF_OBS_TYPE_ID 637297 +struct enkf_obs_struct { + UTIL_TYPE_ID_DECLARATION; + /** A hash of obs_vector_types indexed by user provided keys. */ + vector_type * obs_vector; + hash_type * obs_hash; + time_map_type * obs_time; /* For fast lookup of report_step -> obs_time */ + + bool valid; + /* Several shared resources - can generally be NULL*/ + const history_type * history; /* A shared (not owned by enkf_obs) reference to the history object - used when + adding HISTORY observations. */ + const ecl_sum_type * refcase; + const ecl_grid_type * grid; + time_map_type * external_time_map; + ensemble_config_type * ensemble_config; +}; + + + + +////////////////////////////////////////////////////////////////////////////////////// + + +static void enkf_obs_iset_obs_time(enkf_obs_type * enkf_obs , int report_step, time_t obs_time) { + time_map_update( enkf_obs->obs_time , report_step , obs_time); +} + + +static int enkf_obs_get_last_restart( const enkf_obs_type * enkf_obs ) { + return time_map_get_size( enkf_obs->obs_time ) - 1; +} + +UTIL_IS_INSTANCE_FUNCTION( enkf_obs , ENKF_OBS_TYPE_ID ) + +enkf_obs_type * enkf_obs_alloc( const history_type * history , + time_map_type * external_time_map , + const ecl_grid_type * grid , + const ecl_sum_type * refcase, + ensemble_config_type * ensemble_config ) +{ + enkf_obs_type * enkf_obs = (enkf_obs_type *)util_malloc(sizeof * enkf_obs); + UTIL_TYPE_ID_INIT( enkf_obs , ENKF_OBS_TYPE_ID ); + enkf_obs->obs_hash = hash_alloc(); + enkf_obs->obs_vector = vector_alloc_new(); + enkf_obs->obs_time = time_map_alloc(); + + enkf_obs->history = history; + enkf_obs->refcase = refcase; + enkf_obs->grid = grid; + enkf_obs->ensemble_config = ensemble_config; + enkf_obs->external_time_map = external_time_map; + enkf_obs->valid = false; + + /* Initialize obs time: */ + { + if (enkf_obs->history) { + int last_report = history_get_last_restart( enkf_obs->history ); + int step; + for (step =0; step <= last_report; step++) { + time_t obs_time = history_get_time_t_from_restart_nr( enkf_obs->history , step ); + enkf_obs_iset_obs_time( enkf_obs , step , obs_time ); + } + enkf_obs->valid = true; + } else { + if (enkf_obs->external_time_map) { + int last_report = time_map_get_size( enkf_obs->external_time_map ) - 1; + int step; + for (step =0; step <= last_report; step++) { + time_t obs_time = time_map_iget( enkf_obs->external_time_map , step ); + enkf_obs_iset_obs_time( enkf_obs , step , obs_time ); + } + enkf_obs->valid = true; + } + } + } + + return enkf_obs; +} + + + +bool enkf_obs_have_obs( const enkf_obs_type * enkf_obs ) { + if (vector_get_size( enkf_obs->obs_vector ) > 0) + return true; + else + return false; +} + +bool enkf_obs_is_valid(const enkf_obs_type* obs) { + return obs->valid; +} + +void enkf_obs_free(enkf_obs_type * enkf_obs) { + hash_free(enkf_obs->obs_hash); + vector_free( enkf_obs->obs_vector ); + time_map_free( enkf_obs->obs_time ); + free(enkf_obs); +} + + + + +time_t enkf_obs_iget_obs_time(const enkf_obs_type * enkf_obs , int report_step) { + return time_map_iget( enkf_obs->obs_time , report_step ); +} + + + + + +/** + Observe that the obs_vector can be NULL - in which it is of course not added. +*/ +void enkf_obs_add_obs_vector(enkf_obs_type * enkf_obs, + const obs_vector_type * vector) +{ + + if (vector != NULL) { + const char * obs_key = obs_vector_get_key( vector ); + if (hash_has_key(enkf_obs->obs_hash , obs_key)) + util_abort("%s: Observation with key:%s already added.\n",__func__ , obs_key); + + hash_insert_ref(enkf_obs->obs_hash , obs_key , vector ); + vector_append_owned_ref( enkf_obs->obs_vector , vector , obs_vector_free__); + } +} + + +bool enkf_obs_has_key(const enkf_obs_type * obs , const char * key) { + return hash_has_key(obs->obs_hash , key); +} + +obs_vector_type * enkf_obs_get_vector(const enkf_obs_type * obs, const char * key) { + return (obs_vector_type * ) hash_get(obs->obs_hash , key); +} + +obs_vector_type * enkf_obs_iget_vector(const enkf_obs_type * obs, int index) { + return (obs_vector_type * ) vector_iget( obs->obs_vector , index ); +} + +int enkf_obs_get_size( const enkf_obs_type * obs ) { + return vector_get_size( obs->obs_vector ); +} + + + +static void enkf_obs_get_obs_and_measure_summary(const enkf_obs_type * enkf_obs, + obs_vector_type * obs_vector , + enkf_fs_type * fs, + const local_obsdata_node_type * obs_node , + const int_vector_type * ens_active_list , + meas_data_type * meas_data, + obs_data_type * obs_data, + double_vector_type * obs_value , + double_vector_type * obs_std) { + + const active_list_type * active_list = local_obsdata_node_get_active_list( obs_node ); + + int active_count = 0; + int last_step = -1; + int step = -1; + + /*1: Determine which report_steps have active observations; and collect the observed values. */ + double_vector_reset( obs_std ); + double_vector_reset( obs_value ); + + while (true) { + step = obs_vector_get_next_active_step( obs_vector , step ); + if (step < 0) + break; + + if (local_obsdata_node_tstep_active(obs_node, step) + && obs_vector_iget_active( obs_vector , step ) + && active_list_iget( active_list , 0 /* Index into the scalar summary observation */)) { + const summary_obs_type * summary_obs = (const summary_obs_type * ) obs_vector_iget_node( obs_vector , step ); + double_vector_iset( obs_std , active_count , summary_obs_get_std( summary_obs ) * summary_obs_get_std_scaling( summary_obs )); + double_vector_iset( obs_value , active_count , summary_obs_get_value( summary_obs )); + last_step = step; + active_count++; + } + } + + if (active_count <= 0) + return; + + /* + 3: Fill up the obs_block and meas_block structures with this + time-aggregated summary observation. + */ + + { + obs_block_type * obs_block = obs_data_add_block( obs_data , obs_vector_get_obs_key( obs_vector ) , active_count , NULL, true); + meas_block_type * meas_block = meas_data_add_block( meas_data, obs_vector_get_obs_key( obs_vector ) , last_step , active_count ); + + enkf_node_type * work_node = enkf_node_alloc( obs_vector_get_config_node( obs_vector )); + + for (int i=0; i < active_count; i++) + obs_block_iset( obs_block , i , double_vector_iget( obs_value , i) , double_vector_iget( obs_std , i )); + + int active_size = int_vector_size( ens_active_list ); + active_count = 0; + step = -1; + while (true) { + step = obs_vector_get_next_active_step( obs_vector , step ); + if (step < 0) + break; + + if (local_obsdata_node_tstep_active(obs_node, step) + && obs_vector_iget_active( obs_vector , step ) + && active_list_iget( active_list , 0 /* Index into the scalar summary observation */)) { + for (int iens_index = 0; iens_index < active_size; iens_index++) { + const int iens = int_vector_iget( ens_active_list , iens_index ); + node_id_type node_id = {.report_step = step, + .iens = iens}; + enkf_node_load( work_node , fs , node_id ); + + int smlength = summary_length( (const summary_type * ) enkf_node_value_ptr( work_node ) ); + if (step >= smlength) { + // if obs vector and sim vector have different length + // deactivate and continue to next + char * msg = util_alloc_sprintf("length of observation vector and simulated differ: %d vs. %d ", step, smlength); + meas_block_deactivate(meas_block , active_count); + obs_block_deactivate(obs_block , active_count, true, msg); + free( msg ); + break; + } else { + meas_block_iset(meas_block , iens , active_count , + summary_get( (const summary_type * ) enkf_node_value_ptr( work_node ), + node_id.report_step )); + } + } + active_count++; + } + } + enkf_node_free( work_node ); + } +} + + +void enkf_obs_get_obs_and_measure_node( const enkf_obs_type * enkf_obs, + enkf_fs_type * fs, + const local_obsdata_node_type * obs_node , + const int_vector_type * ens_active_list , + meas_data_type * meas_data, + obs_data_type * obs_data) { + + const char * obs_key = local_obsdata_node_get_key( obs_node ); + obs_vector_type * obs_vector = (obs_vector_type *)hash_get( enkf_obs->obs_hash , obs_key ); + obs_impl_type obs_type = obs_vector_get_impl_type( obs_vector ); + + if (obs_type == SUMMARY_OBS) { + // this if also used to test: (end_step >= start_step+2) + double_vector_type * work_value = double_vector_alloc( 0 , -1 ); + double_vector_type * work_std = double_vector_alloc( 0 , -1 ); + + enkf_obs_get_obs_and_measure_summary( enkf_obs , + obs_vector , + fs , + obs_node , + ens_active_list , + meas_data , + obs_data , + work_value, + work_std); + + double_vector_free( work_std ); + double_vector_free( work_value ); + return; + } + + + // obs_type is GEN_OBS or BLOCK_OBS + int report_step = -1; + while (true) { + report_step = obs_vector_get_next_active_step( obs_vector , report_step ); + if (report_step < 0) + return; + + if (local_obsdata_node_tstep_active(obs_node, report_step) + && (obs_vector_iget_active(obs_vector , report_step))) { + /* The observation is active for this report step. */ + const active_list_type * active_list = local_obsdata_node_get_active_list( obs_node ); + /* Collect the observed data in the obs_data instance. */ + obs_vector_iget_observations(obs_vector , report_step , obs_data , active_list, fs); + obs_vector_measure(obs_vector , fs , report_step , ens_active_list , meas_data , active_list); + } + } +} + + +/* + This will append observations and simulated responses from + report_step to obs_data and meas_data. + Call obs_data_reset and meas_data_reset on obs_data and meas_data + if you want to use fresh instances. +*/ + +void enkf_obs_get_obs_and_measure_data(const enkf_obs_type * enkf_obs, + enkf_fs_type * fs, + const local_obsdata_type * local_obsdata , + const int_vector_type * ens_active_list , + meas_data_type * meas_data, + obs_data_type * obs_data) { + + + int iobs; + for (iobs = 0; iobs < local_obsdata_get_size( local_obsdata ); iobs++) { + const local_obsdata_node_type * obs_node = local_obsdata_iget( local_obsdata , iobs ); + enkf_obs_get_obs_and_measure_node( enkf_obs , + fs , + obs_node , + ens_active_list , + meas_data , + obs_data); + } +} + + + + +void enkf_obs_clear( enkf_obs_type * enkf_obs ) { + hash_clear( enkf_obs->obs_hash ); + vector_clear( enkf_obs->obs_vector ); + ensemble_config_clear_obs_keys(enkf_obs->ensemble_config); +} + + + + + +/* + Adding inverse observation keys to the enkf_nodes; can be called + several times. +*/ + + +static void enkf_obs_update_keys( enkf_obs_type * enkf_obs ) { + /* First clear all existing observation keys. */ + ensemble_config_clear_obs_keys( enkf_obs->ensemble_config ); + + /* Add new observation keys. */ + { + hash_type * map = enkf_obs_alloc_data_map(enkf_obs); + hash_iter_type * iter = hash_iter_alloc(map); + const char * obs_key = hash_iter_get_next_key(iter); + while (obs_key != NULL) { + const char * state_kw = (const char *)hash_get(map , obs_key); + ensemble_config_add_obs_key(enkf_obs->ensemble_config , state_kw , obs_key); + obs_key = hash_iter_get_next_key(iter); + } + hash_iter_free(iter); + hash_free(map); + } +} + + + + +/* + * + * Here comes four helper functions for enkf_obs_load: + * + * * handle_history_observation(enkf_obs, enkf_conf, last_report, std_cutoff); + * * handle_summary_observation(enkf_obs, enkf_conf, last_report); + * * handle_block_observation(enkf_obs, enkf_conf); + * * handle_general_observation(enkf_obs, enkf_conf); + * + */ + + +/** Handle HISTORY_OBSERVATION instances. */ +static void handle_history_observation(enkf_obs_type * enkf_obs, + conf_instance_type * enkf_conf, + int last_report, + double std_cutoff) { + stringlist_type * hist_obs_keys = conf_instance_alloc_list_of_sub_instances_of_class_by_name(enkf_conf, "HISTORY_OBSERVATION"); + int num_hist_obs = stringlist_get_size(hist_obs_keys); + + for (int i = 0; i < num_hist_obs; i++) { + const char * obs_key = stringlist_iget(hist_obs_keys, i); + + if (!enkf_obs->history) { + fprintf(stderr,"** Warning: no history object registered - observation:%s is ignored\n", + obs_key); + break; + } + const conf_instance_type * hist_obs_conf = conf_instance_get_sub_instance_ref(enkf_conf, + obs_key); + obs_vector_type * obs_vector; + enkf_config_node_type * config_node; + config_node = ensemble_config_add_summary_observation(enkf_obs->ensemble_config, + obs_key, + LOAD_FAIL_WARN); + if (config_node == NULL) { + fprintf(stderr,"** Warning: summary:%s does not exist - observation:%s not added.\n", + obs_key, + obs_key); + break; + } + + obs_vector = obs_vector_alloc(SUMMARY_OBS, + obs_key, + ensemble_config_get_node(enkf_obs->ensemble_config, obs_key), + last_report); + if (obs_vector != NULL) { + if (obs_vector_load_from_HISTORY_OBSERVATION(obs_vector, + hist_obs_conf, + enkf_obs->obs_time, + enkf_obs->history, + enkf_obs->ensemble_config, + std_cutoff)) { + enkf_obs_add_obs_vector(enkf_obs, obs_vector); + } else { + fprintf(stderr,"** Could not load historical data for observation:%s - ignored\n",obs_key); + obs_vector_free(obs_vector); + } + } + } + stringlist_free(hist_obs_keys); +} + + + +/** Handle SUMMARY_OBSERVATION instances. */ +static void handle_summary_observation(enkf_obs_type * enkf_obs, + conf_instance_type * enkf_conf, + int last_report) { + stringlist_type * sum_obs_keys = conf_instance_alloc_list_of_sub_instances_of_class_by_name(enkf_conf, "SUMMARY_OBSERVATION"); + int num_sum_obs = stringlist_get_size(sum_obs_keys); + + for(int i = 0; i < num_sum_obs; i++) { + const char * obs_key = stringlist_iget(sum_obs_keys, i); + const conf_instance_type * sum_obs_conf = conf_instance_get_sub_instance_ref(enkf_conf, obs_key); + const char * sum_key = conf_instance_get_item_value_ref(sum_obs_conf, "KEY"); + + /* check if have sum_key exists */ + enkf_config_node_type * config_node = ensemble_config_add_summary_observation(enkf_obs->ensemble_config, + sum_key, + LOAD_FAIL_WARN); + if (config_node == NULL) { + fprintf(stderr,"** Warning: summary key:%s does not exist - observation key:%s not added.\n", + sum_key, obs_key); + break; + } + + /* Check if obs_vector is alloc'd */ + obs_vector_type * obs_vector = obs_vector_alloc(SUMMARY_OBS, + obs_key, + ensemble_config_get_node(enkf_obs->ensemble_config, sum_key), + last_report); + if (obs_vector == NULL) + break; + + obs_vector_load_from_SUMMARY_OBSERVATION(obs_vector, + sum_obs_conf, + enkf_obs->obs_time, + enkf_obs->ensemble_config); + enkf_obs_add_obs_vector(enkf_obs, obs_vector); + } + stringlist_free(sum_obs_keys); +} + + + +/** Handle BLOCK_OBSERVATION instances. */ +static void handle_block_observation(enkf_obs_type * enkf_obs, + conf_instance_type * enkf_conf) { + stringlist_type * block_obs_keys = conf_instance_alloc_list_of_sub_instances_of_class_by_name(enkf_conf, "BLOCK_OBSERVATION"); + int num_block_obs = stringlist_get_size(block_obs_keys); + + for(int i = 0; i < num_block_obs; i++) { + const char * obs_key = stringlist_iget(block_obs_keys, i); + const conf_instance_type * block_obs_conf = conf_instance_get_sub_instance_ref(enkf_conf, obs_key); + obs_vector_type * obs_vector = obs_vector_alloc_from_BLOCK_OBSERVATION(block_obs_conf, + enkf_obs->grid, + enkf_obs->obs_time, + enkf_obs->refcase, + enkf_obs->ensemble_config); + if (obs_vector != NULL) + enkf_obs_add_obs_vector(enkf_obs, obs_vector); + } + stringlist_free(block_obs_keys); +} + + + +/** Handle GENERAL_OBSERVATION instances. */ +static void handle_general_observation(enkf_obs_type * enkf_obs, + conf_instance_type * enkf_conf) { + stringlist_type * block_obs_keys = conf_instance_alloc_list_of_sub_instances_of_class_by_name(enkf_conf, + "GENERAL_OBSERVATION"); + int num_block_obs = stringlist_get_size(block_obs_keys); + + for(int i = 0; i < num_block_obs; i++) { + const char * obs_key = stringlist_iget(block_obs_keys, i); + const conf_instance_type * gen_obs_conf = conf_instance_get_sub_instance_ref(enkf_conf, + obs_key); + + obs_vector_type * obs_vector = obs_vector_alloc_from_GENERAL_OBSERVATION(gen_obs_conf, + enkf_obs->obs_time, + enkf_obs->ensemble_config); + if (obs_vector != NULL) + enkf_obs_add_obs_vector(enkf_obs, obs_vector); + } + stringlist_free(block_obs_keys); +} + + +static void enkf_obs_reinterpret_DT_FILE(const char * config_file) { + fprintf(stderr,"**********************************************************************\n"); + fprintf(stderr,"* In ert version 2.3 we have changed how filepaths are interpreted *\n"); + fprintf(stderr,"* in the observation file. When using the keywords OBS_FILE, *\n"); + fprintf(stderr,"* ERROR_COVAR and INDEX_FILE in the category GENERAL_OBSERVATION *\n"); + fprintf(stderr,"* the filenames will be interpreted relative to the main observation *\n"); + fprintf(stderr,"* file. *\n"); + fprintf(stderr,"* *\n"); + fprintf(stderr,"* Please update the OBS_FILE, ERROR_COVAR and INDEX_FILE keywords *\n"); + fprintf(stderr,"* by removing the path to the main observation file. *\n"); + fprintf(stderr,"**********************************************************************\n"); +} + + +/** + This function will load an observation configuration from the + observation file @config_file. + + If called several times during one invocation the function will + start by clearing the current content. +*/ +void enkf_obs_load(enkf_obs_type * enkf_obs , + const char * config_file, + double std_cutoff) { + + if (!enkf_obs_is_valid(enkf_obs)) + util_abort("%s cannot load invalid enkf observation config %s.\n", + __func__, config_file); + + int last_report = enkf_obs_get_last_restart(enkf_obs); + conf_class_type * enkf_conf_class = enkf_obs_get_obs_conf_class(); + conf_instance_type * enkf_conf = conf_instance_alloc_from_file(enkf_conf_class, + "enkf_conf", + config_file); + + if (conf_instance_get_path_error(enkf_conf)) { + enkf_obs_reinterpret_DT_FILE(config_file); + exit(1); + } + + if(!conf_instance_validate(enkf_conf)) + util_abort("%s: Can not proceed with this configuration: %s\n", + __func__, config_file); + + handle_history_observation(enkf_obs, enkf_conf, last_report, std_cutoff); + handle_summary_observation(enkf_obs, enkf_conf, last_report); + handle_block_observation( enkf_obs, enkf_conf); + handle_general_observation(enkf_obs, enkf_conf); + + conf_instance_free(enkf_conf); + conf_class_free(enkf_conf_class); + + enkf_obs_update_keys( enkf_obs ); +} + + +conf_class_type * enkf_obs_get_obs_conf_class( void ) { + const char * enkf_conf_help = "An instance of the class ENKF_CONFIG shall contain neccessary infomation to run the enkf."; + conf_class_type * enkf_conf_class = conf_class_alloc_empty("ENKF_CONFIG", true , false , enkf_conf_help); + conf_class_set_help(enkf_conf_class, enkf_conf_help); + + + + /** Create and insert HISTORY_OBSERVATION class. */ + { + const char * help_class_history_observation = "The class HISTORY_OBSERVATION is used to condition on a time series from the production history. The name of the an instance is used to define the item to condition on, and should be in summary.x syntax. E.g., creating a HISTORY_OBSERVATION instance with name GOPR:P4 conditions on GOPR for group P4."; + + conf_class_type * history_observation_class = conf_class_alloc_empty("HISTORY_OBSERVATION", false , false, help_class_history_observation); + + conf_item_spec_type * item_spec_error_mode = conf_item_spec_alloc("ERROR_MODE", true, DT_STR , "The string ERROR_MODE gives the error mode for the observation."); + conf_item_spec_add_restriction(item_spec_error_mode, "REL"); + conf_item_spec_add_restriction(item_spec_error_mode, "ABS"); + conf_item_spec_add_restriction(item_spec_error_mode, "RELMIN"); + conf_item_spec_set_default_value(item_spec_error_mode, "RELMIN"); + + conf_item_spec_type * item_spec_error = conf_item_spec_alloc("ERROR", true, DT_POSFLOAT , "The positive floating number ERROR gives the standard deviation (ABS) or the relative uncertainty (REL/RELMIN) of the observations."); + conf_item_spec_set_default_value(item_spec_error, "0.10"); + + conf_item_spec_type * item_spec_error_min = conf_item_spec_alloc("ERROR_MIN", true, DT_POSFLOAT , "The positive floating point number ERROR_MIN gives the minimum value for the standard deviation of the observation when RELMIN is used."); + conf_item_spec_set_default_value(item_spec_error_min, "0.10"); + + conf_class_insert_owned_item_spec(history_observation_class, item_spec_error_mode); + conf_class_insert_owned_item_spec(history_observation_class, item_spec_error); + conf_class_insert_owned_item_spec(history_observation_class, item_spec_error_min); + + /** Sub class segment. */ + { + const char * help_class_segment = "The class SEGMENT is used to fine tune the error model."; + conf_class_type * segment_class = conf_class_alloc_empty("SEGMENT", false , false, help_class_segment); + + conf_item_spec_type * item_spec_start_segment = conf_item_spec_alloc("START", true, DT_INT, "The first restart in the segment."); + conf_item_spec_type * item_spec_stop_segment = conf_item_spec_alloc("STOP", true, DT_INT, "The last restart in the segment."); + + conf_item_spec_type * item_spec_error_mode_segment = conf_item_spec_alloc("ERROR_MODE", true, DT_STR , "The string ERROR_MODE gives the error mode for the observation."); + conf_item_spec_add_restriction(item_spec_error_mode_segment, "REL"); + conf_item_spec_add_restriction(item_spec_error_mode_segment, "ABS"); + conf_item_spec_add_restriction(item_spec_error_mode_segment, "RELMIN"); + conf_item_spec_set_default_value(item_spec_error_mode_segment, "RELMIN"); + + conf_item_spec_type * item_spec_error_segment = conf_item_spec_alloc("ERROR", true, DT_POSFLOAT , "The positive floating number ERROR gives the standard deviation (ABS) or the relative uncertainty (REL/RELMIN) of the observations."); + conf_item_spec_set_default_value(item_spec_error_segment, "0.10"); + + conf_item_spec_type * item_spec_error_min_segment = conf_item_spec_alloc("ERROR_MIN", true, DT_POSFLOAT , "The positive floating point number ERROR_MIN gives the minimum value for the standard deviation of the observation when RELMIN is used."); + conf_item_spec_set_default_value(item_spec_error_min_segment, "0.10"); + + + conf_class_insert_owned_item_spec(segment_class, item_spec_start_segment); + conf_class_insert_owned_item_spec(segment_class, item_spec_stop_segment); + conf_class_insert_owned_item_spec(segment_class, item_spec_error_mode_segment); + conf_class_insert_owned_item_spec(segment_class, item_spec_error_segment); + conf_class_insert_owned_item_spec(segment_class, item_spec_error_min_segment); + + conf_class_insert_owned_sub_class(history_observation_class, segment_class); + } + + conf_class_insert_owned_sub_class(enkf_conf_class, history_observation_class); + } + + + + /** Create and insert SUMMARY_OBSERVATION class. */ + { + const char * help_class_summary_observation = "The class SUMMARY_OBSERVATION can be used to condition on any observation whos simulated value is written to the summary file."; + conf_class_type * summary_observation_class = conf_class_alloc_empty("SUMMARY_OBSERVATION", false , false, help_class_summary_observation); + + const char * help_item_spec_value = "The floating point number VALUE gives the observed value."; + conf_item_spec_type * item_spec_value = conf_item_spec_alloc("VALUE", true, DT_FLOAT , help_item_spec_value); + + const char * help_item_spec_error = "The positive floating point number ERROR is the standard deviation of the observed value."; + conf_item_spec_type * item_spec_error = conf_item_spec_alloc("ERROR", true, DT_POSFLOAT ,help_item_spec_error ); + + const char * help_item_spec_date = "The DATE item gives the observation time as the date date it occured. Format is dd/mm/yyyy."; + conf_item_spec_type * item_spec_date = conf_item_spec_alloc("DATE", false, DT_DATE , help_item_spec_date); + + const char * help_item_spec_days = "The DAYS item gives the observation time as days after simulation start."; + conf_item_spec_type * item_spec_days = conf_item_spec_alloc("DAYS", false, DT_POSFLOAT , help_item_spec_days); + + const char * help_item_spec_hours = "The HOURS item gives the observation time as hours after simulation start."; + conf_item_spec_type * item_spec_hours = conf_item_spec_alloc("HOURS", false, DT_POSFLOAT , help_item_spec_hours); + + const char * help_item_spec_restart = "The RESTART item gives the observation time as the ECLIPSE restart nr."; + conf_item_spec_type * item_spec_restart = conf_item_spec_alloc("RESTART", false, DT_POSINT , help_item_spec_restart); + + const char * help_item_spec_sumkey = "The string SUMMARY_KEY is used to look up the simulated value in the summary file. It has the same format as the summary.x program, e.g. WOPR:P4"; + conf_item_spec_type * item_spec_sumkey = conf_item_spec_alloc("KEY", true, DT_STR , help_item_spec_sumkey); + + conf_item_spec_type * item_spec_error_min = conf_item_spec_alloc("ERROR_MIN", true, DT_POSFLOAT , + "The positive floating point number ERROR_MIN gives the minimum value for the standard deviation of the observation when RELMIN is used."); + conf_item_spec_type * item_spec_error_mode = conf_item_spec_alloc("ERROR_MODE", true, DT_STR , "The string ERROR_MODE gives the error mode for the observation."); + + conf_item_spec_add_restriction(item_spec_error_mode, "REL"); + conf_item_spec_add_restriction(item_spec_error_mode, "ABS"); + conf_item_spec_add_restriction(item_spec_error_mode, "RELMIN"); + conf_item_spec_set_default_value(item_spec_error_mode, "ABS"); + conf_item_spec_set_default_value(item_spec_error_min, "0.10"); + + conf_class_insert_owned_item_spec(summary_observation_class, item_spec_value); + conf_class_insert_owned_item_spec(summary_observation_class, item_spec_error); + conf_class_insert_owned_item_spec(summary_observation_class, item_spec_date); + conf_class_insert_owned_item_spec(summary_observation_class, item_spec_days); + conf_class_insert_owned_item_spec(summary_observation_class, item_spec_hours); + conf_class_insert_owned_item_spec(summary_observation_class, item_spec_restart); + conf_class_insert_owned_item_spec(summary_observation_class, item_spec_sumkey); + conf_class_insert_owned_item_spec(summary_observation_class, item_spec_error_mode); + conf_class_insert_owned_item_spec(summary_observation_class, item_spec_error_min); + + /** Create a mutex on DATE, DAYS and RESTART. */ + conf_item_mutex_type * time_mutex = conf_class_new_item_mutex(summary_observation_class , true , false); + + conf_item_mutex_add_item_spec(time_mutex , item_spec_date); + conf_item_mutex_add_item_spec(time_mutex , item_spec_days); + conf_item_mutex_add_item_spec(time_mutex , item_spec_hours); + conf_item_mutex_add_item_spec(time_mutex , item_spec_restart); + conf_item_mutex_add_item_spec(time_mutex , item_spec_days ); + + conf_class_insert_owned_sub_class(enkf_conf_class, summary_observation_class); + } + + + + /** Create and insert BLOCK_OBSERVATION class. */ + { + const char * help_class_block_observation = "The class BLOCK_OBSERVATION can be used to condition on an observation whos simulated values are block/cell values of a field, e.g. RFT tests."; + conf_class_type * block_observation_class = conf_class_alloc_empty("BLOCK_OBSERVATION", false , false, help_class_block_observation); + + const char * help_item_spec_field = "The item FIELD gives the observed field. E.g., ECLIPSE fields such as PRESSURE, SGAS or any user defined fields such as PORO or PERMX."; + conf_item_spec_type * item_spec_field = conf_item_spec_alloc("FIELD", true , DT_STR , help_item_spec_field); + + const char * help_item_spec_date = "The DATE item gives the observation time as the date date it occured. Format is dd/mm/yyyy."; + conf_item_spec_type * item_spec_date = conf_item_spec_alloc("DATE", false, DT_DATE , help_item_spec_date); + + const char * help_item_spec_days = "The DAYS item gives the observation time as days after simulation start."; + conf_item_spec_type * item_spec_days = conf_item_spec_alloc("DAYS", false, DT_POSFLOAT , help_item_spec_days); + + const char * help_item_spec_hours = "The HOURS item gives the observation time as hours after simulation start."; + conf_item_spec_type * item_spec_hours = conf_item_spec_alloc("HOURS", false, DT_POSFLOAT , help_item_spec_hours); + + const char * help_item_spec_restart = "The RESTART item gives the observation time as the ECLIPSE restart nr."; + conf_item_spec_type * item_spec_restart = conf_item_spec_alloc("RESTART", false, DT_POSINT , help_item_spec_restart); + + conf_item_spec_type * item_spec_source = conf_item_spec_alloc("SOURCE", false, DT_STR , "The simulated data can be taken from the field or summary keys."); + + + conf_item_spec_add_restriction(item_spec_source, "FIELD"); + conf_item_spec_add_restriction(item_spec_source, "SUMMARY"); + conf_item_spec_set_default_value(item_spec_source, "FIELD"); + + conf_class_insert_owned_item_spec(block_observation_class, item_spec_source); + conf_class_insert_owned_item_spec(block_observation_class, item_spec_field); + conf_class_insert_owned_item_spec(block_observation_class, item_spec_date); + conf_class_insert_owned_item_spec(block_observation_class, item_spec_days); + conf_class_insert_owned_item_spec(block_observation_class, item_spec_restart); + conf_class_insert_owned_item_spec(block_observation_class, item_spec_hours); + /** Create a mutex on DATE, DAYS and RESTART. */ + { + conf_item_mutex_type * time_mutex = conf_class_new_item_mutex(block_observation_class , true , false); + conf_item_mutex_add_item_spec(time_mutex, item_spec_date); + conf_item_mutex_add_item_spec(time_mutex, item_spec_days); + conf_item_mutex_add_item_spec(time_mutex, item_spec_restart); + conf_item_mutex_add_item_spec(time_mutex, item_spec_hours); + } + + /** Create and insert the sub class OBS. */ + { + const char * help_class_obs = "The class OBS is used to specify a single observed point."; + conf_class_type * obs_class = conf_class_alloc_empty("OBS", true , false, help_class_obs); + + const char * help_item_i = "The item I gives the I index of the block observation."; + conf_item_spec_type * item_spec_i = conf_item_spec_alloc("I", true, DT_POSINT , help_item_i); + + const char * help_item_j = "The item J gives the J index of the block observation."; + conf_item_spec_type * item_spec_j = conf_item_spec_alloc("J", true, DT_POSINT, help_item_j); + + const char * help_item_k = "The item K gives the K index of the block observation."; + conf_item_spec_type * item_spec_k = conf_item_spec_alloc("K", true, DT_POSINT, help_item_k); + + const char * help_item_spec_value = "The floating point number VALUE gives the observed value."; + conf_item_spec_type * item_spec_value = conf_item_spec_alloc("VALUE", true, DT_FLOAT , help_item_spec_value); + + const char * help_item_spec_error = "The positive floating point number ERROR is the standard deviation of the observed value."; + conf_item_spec_type * item_spec_error = conf_item_spec_alloc("ERROR", true, DT_POSFLOAT , help_item_spec_error); + + conf_item_spec_type * item_spec_error_mode = conf_item_spec_alloc("ERROR_MODE", true, DT_STR , "The string ERROR_MODE gives the error mode for the observation."); + + conf_item_spec_type * item_spec_error_min = conf_item_spec_alloc("ERROR_MIN", true, DT_POSFLOAT , + "The positive floating point number ERROR_MIN gives the minimum value for the standard deviation of the observation when RELMIN is used."); + + conf_item_spec_add_restriction(item_spec_error_mode, "REL"); + conf_item_spec_add_restriction(item_spec_error_mode, "ABS"); + conf_item_spec_add_restriction(item_spec_error_mode, "RELMIN"); + conf_item_spec_set_default_value(item_spec_error_mode, "ABS"); + conf_item_spec_set_default_value(item_spec_error_min, "0.10" ); + + conf_class_insert_owned_item_spec(obs_class, item_spec_i); + conf_class_insert_owned_item_spec(obs_class, item_spec_j); + conf_class_insert_owned_item_spec(obs_class, item_spec_k); + conf_class_insert_owned_item_spec(obs_class, item_spec_value); + conf_class_insert_owned_item_spec(obs_class, item_spec_error); + conf_class_insert_owned_item_spec(obs_class, item_spec_error_mode); + conf_class_insert_owned_item_spec(obs_class, item_spec_error_min); + + conf_class_insert_owned_sub_class(block_observation_class, obs_class); + } + + conf_class_insert_owned_sub_class(enkf_conf_class, block_observation_class); + } + + /** Create and insert class for general observations. */ + { + const char * help_item_spec_restart = "The RESTART item gives the observation time as the ECLIPSE restart nr."; + const char * help_item_spec_field = "The item DATA gives the observed GEN_DATA instance."; + const char * help_item_spec_date = "The DATE item gives the observation time as the date date it occured. Format is dd/mm/yyyy."; + const char * help_item_spec_days = "The DAYS item gives the observation time as days after simulation start."; + const char * help_item_spec_hours = "The HOURS item gives the observation time as hours after simulation start."; + + conf_class_type * gen_obs_class = conf_class_alloc_empty("GENERAL_OBSERVATION" , false , false, "The class general_observation is used for general observations"); + + conf_item_spec_type * item_spec_field = conf_item_spec_alloc("DATA", true, DT_STR , help_item_spec_field); + conf_item_spec_type * item_spec_date = conf_item_spec_alloc("DATE", false, DT_DATE , help_item_spec_date); + conf_item_spec_type * item_spec_days = conf_item_spec_alloc("DAYS", false, DT_POSFLOAT , help_item_spec_days); + conf_item_spec_type * item_spec_hours = conf_item_spec_alloc("HOURS", false, DT_POSFLOAT , help_item_spec_hours); + conf_item_spec_type * item_spec_restart = conf_item_spec_alloc("RESTART", false, DT_INT , help_item_spec_restart); + conf_item_spec_type * item_spec_error_covar = conf_item_spec_alloc("ERROR_COVAR", false, DT_FILE , "Name of file containing error-covariance as formatted matrix - no header"); + + conf_class_insert_owned_item_spec(gen_obs_class, item_spec_error_covar); + conf_class_insert_owned_item_spec(gen_obs_class, item_spec_field); + conf_class_insert_owned_item_spec(gen_obs_class, item_spec_date); + conf_class_insert_owned_item_spec(gen_obs_class, item_spec_days); + conf_class_insert_owned_item_spec(gen_obs_class, item_spec_hours); + conf_class_insert_owned_item_spec(gen_obs_class, item_spec_restart); + /** Create a mutex on DATE, DAYS and RESTART. */ + { + conf_item_mutex_type * time_mutex = conf_class_new_item_mutex(gen_obs_class , true , false); + + conf_item_mutex_add_item_spec(time_mutex, item_spec_date); + conf_item_mutex_add_item_spec(time_mutex, item_spec_days); + conf_item_mutex_add_item_spec(time_mutex, item_spec_hours); + conf_item_mutex_add_item_spec(time_mutex, item_spec_restart); + } + + { + conf_item_spec_type * item_spec_obs_file = conf_item_spec_alloc("OBS_FILE" , false , DT_FILE , "The name of an (ascii) file with observation values."); + conf_item_spec_type * item_spec_value = conf_item_spec_alloc("VALUE" , false , DT_FLOAT , "One scalar observation value."); + conf_item_spec_type * item_spec_error = conf_item_spec_alloc("ERROR" , false , DT_FLOAT , "One scalar observation error."); + conf_item_mutex_type * value_mutex = conf_class_new_item_mutex( gen_obs_class , true , false); + conf_item_mutex_type * value_error_mutex = conf_class_new_item_mutex( gen_obs_class , false , true); + + conf_class_insert_owned_item_spec(gen_obs_class , item_spec_obs_file); + conf_class_insert_owned_item_spec(gen_obs_class , item_spec_value); + conf_class_insert_owned_item_spec(gen_obs_class , item_spec_error); + + + /* If the observation is in terms of VALUE - we must also have ERROR. + The conf system does not (currently ??) enforce this dependency. */ + + conf_item_mutex_add_item_spec( value_mutex , item_spec_value); + conf_item_mutex_add_item_spec( value_mutex , item_spec_obs_file); + + conf_item_mutex_add_item_spec( value_error_mutex , item_spec_value); + conf_item_mutex_add_item_spec( value_error_mutex , item_spec_error); + } + + + /* + The default is that all the elements in DATA are observed, but + we can restrict ourselves to a list of indices, with either the + INDEX_LIST or INDEX_FILE keywords. + */ + { + conf_item_spec_type * item_spec_index_list = conf_item_spec_alloc("INDEX_LIST" , false , DT_STR , "A list of indicies - possibly with ranges which should be observed in the target field."); + conf_item_spec_type * item_spec_index_file = conf_item_spec_alloc("INDEX_FILE" , false , DT_FILE , "An ASCII file containing a list of indices which should be observed in the target field."); + conf_item_mutex_type * index_mutex = conf_class_new_item_mutex( gen_obs_class , false , false); + + conf_class_insert_owned_item_spec(gen_obs_class, item_spec_index_list); + conf_class_insert_owned_item_spec(gen_obs_class, item_spec_index_file); + conf_item_mutex_add_item_spec(index_mutex , item_spec_index_list); + conf_item_mutex_add_item_spec(index_mutex , item_spec_index_file); + } + + conf_class_insert_owned_sub_class(enkf_conf_class, gen_obs_class); + } + + + return enkf_conf_class; +} + + + +/** + Allocates a stringlist of obs target keys which correspond to + summary observations, these are then added to the state vector in + enkf_main. +*/ +stringlist_type * enkf_obs_alloc_typed_keylist(enkf_obs_type * enkf_obs , obs_impl_type obs_type) { + stringlist_type * vars = stringlist_alloc_new(); + hash_iter_type * iter = hash_iter_alloc(enkf_obs->obs_hash); + const char * key = hash_iter_get_next_key(iter); + while ( key != NULL) { + obs_vector_type * obs_vector = (obs_vector_type *)hash_get( enkf_obs->obs_hash , key); + if (obs_vector_get_impl_type(obs_vector) == obs_type) + stringlist_append_copy(vars , key); + key = hash_iter_get_next_key(iter); + } + hash_iter_free(iter); + return vars; +} + + +obs_impl_type enkf_obs_get_type(const enkf_obs_type * enkf_obs , const char * key) { + obs_vector_type * obs_vector = (obs_vector_type *)hash_get( enkf_obs->obs_hash , key); + return obs_vector_get_impl_type(obs_vector); +} + +/** + */ + +stringlist_type * enkf_obs_alloc_matching_keylist(const enkf_obs_type * enkf_obs, + const char * input_string) { + + stringlist_type * obs_keys = hash_alloc_stringlist( enkf_obs->obs_hash ); + + if (!input_string) + return obs_keys; + + stringlist_type * matching_keys = stringlist_alloc_new(); + char ** input_keys; + int num_keys; + int obs_keys_count = stringlist_get_size( obs_keys ); + + util_split_string( input_string , " " , &num_keys , &input_keys); + for (int i = 0; i < num_keys; i++) { + const char * input_key = input_keys[i]; + for (int j = 0; j < obs_keys_count; j++) { + const char * obs_key = stringlist_iget( obs_keys , j); + + if (util_string_match( obs_key , input_key) + && !stringlist_contains( matching_keys , obs_key )) + stringlist_append_copy( matching_keys , obs_key); + } + } + util_free_stringlist( input_keys , num_keys ); + stringlist_free( obs_keys ); + return matching_keys; +} + +/** + This function allocates a hash table which looks like this: + + {"OBS_KEY1": "STATE_KEY1", "OBS_KEY2": "STATE_KEY2", "OBS_KEY3": "STATE_KEY3", ....} + + where "OBS_KEY" represents the keys in the enkf_obs hash, and the + value they are pointing at are the enkf_state keywords they are + measuring. For instance if we have an observation with key "RFT_1A" + the entry in the table will be: ... "RFT_1A": "PRESSURE", .. + since an RFT observation observes the pressure. + + Let us consider the watercut in a well. Then the state_kw will + typically be WWCT:P1 for a well named 'P1'. Let us assume that this + well is observed both as a normal HISTORY observation from + SCHEDULE, and from two separator tests, called S1 and S2. Then the + hash table will look like this: + + "WWCT:P1": "WWCT:P1", + "S1" : "WWCT:P1", + "S2" : "WWCT:P1" + + + I.e. there are three different observations keys, all observing the + same state_kw. +*/ + + + +hash_type * enkf_obs_alloc_data_map(enkf_obs_type * enkf_obs) +{ + hash_type * map = hash_alloc(); + hash_iter_type * iter = hash_iter_alloc(enkf_obs->obs_hash); + const char * key = hash_iter_get_next_key(iter); + while ( key != NULL) { + obs_vector_type * obs_vector = (obs_vector_type *)hash_get( enkf_obs->obs_hash , key); + hash_insert_ref( map , key , obs_vector_get_state_kw(obs_vector)); + key = hash_iter_get_next_key(iter); + } + hash_iter_free( iter ); + return map; +} + + +hash_iter_type * enkf_obs_alloc_iter( const enkf_obs_type * enkf_obs ) { + return hash_iter_alloc(enkf_obs->obs_hash); +} + + +/*****************************************************************/ + + +void enkf_obs_scale_std(enkf_obs_type * enkf_obs, double scale_factor) { + local_obsdata_type * local_obs = enkf_obs_alloc_all_active_local_obs( enkf_obs , "ALL-OBS"); + enkf_obs_local_scale_std( enkf_obs , local_obs , scale_factor ); + local_obsdata_free( local_obs ); +} + + +void enkf_obs_local_scale_std( const enkf_obs_type * enkf_obs , const local_obsdata_type * local_obsdata, double scale_factor) { + int num_nodes = local_obsdata_get_size( local_obsdata ); + int node_nr; + for (node_nr = 0; node_nr < num_nodes; node_nr++) { + const local_obsdata_node_type * node = local_obsdata_iget( local_obsdata , node_nr ); + obs_vector_type * obs_vector = enkf_obs_get_vector( enkf_obs , local_obsdata_node_get_key( node )); + obs_vector_scale_std( obs_vector , node , scale_factor ); + } +} + + +double enkf_obs_scale_correlated_std(const enkf_obs_type * enkf_obs, + enkf_fs_type * fs, + const int_vector_type * ens_active_list, + const local_obsdata_type * local_obsdata, + double alpha, + double std_cutoff, + bool verbose) { + bool_vector_type * ens_mask = int_vector_alloc_mask( ens_active_list ); + meas_data_type * meas_data = meas_data_alloc( ens_mask ); + obs_data_type * obs_data = obs_data_alloc( 1.0 ); + double scale_factor = 1.0; + + enkf_obs_get_obs_and_measure_data( enkf_obs , fs , local_obsdata , ens_active_list, + meas_data , obs_data ); + + enkf_analysis_deactivate_outliers(obs_data, meas_data, + std_cutoff, alpha, verbose); + + if (obs_data_get_active_size(obs_data) > 1) { + matrix_type * S = meas_data_allocS( meas_data ); + if (S) { + double truncation = 0.95; + int num_PC; + + obs_data_scale( obs_data , S , NULL , NULL , NULL , NULL ); + num_PC = enkf_linalg_num_PC( S , truncation ); + scale_factor = sqrt( obs_data_get_active_size( obs_data ) / num_PC ); + + matrix_free( S ); + if (verbose) { + int width = 80; + int column = 0; + const char * indent = " "; + printf("================================================================================\n"); + printf("Observations: "); + column += strlen("Observations:"); + for (int i=0; i < local_obsdata_get_size(local_obsdata); i++) { + const local_obsdata_node_type * node = local_obsdata_iget(local_obsdata, i); + const char * key = local_obsdata_node_get_key(node); + + if ((column + strlen(key) + 1) > width) { + printf("\n%s", indent); + column = strlen(indent); + } + puts(" "); + puts(key); + column += 1 + strlen(key); + } + printf("\n\n"); + printf("Standard deviation scaled with: %g \n", scale_factor); + printf("================================================================================\n"); + } + enkf_obs_local_scale_std( enkf_obs , local_obsdata , scale_factor ); + } else + if (verbose) + printf("No simulated data - S matrix is NULL. Wrong case?\n"); + } else + if (verbose) + printf("No active observations\n"); + + + meas_data_free( meas_data ); + obs_data_free( obs_data ); + bool_vector_free( ens_mask ); + return scale_factor; +} + + + +void enkf_obs_add_local_nodes_with_data(const enkf_obs_type * enkf_obs , local_obsdata_type * local_obs , enkf_fs_type *fs , const bool_vector_type * ens_mask) { + hash_iter_type * iter = hash_iter_alloc(enkf_obs->obs_hash); + while ( !hash_iter_is_complete( iter ) ) { + const char * key = hash_iter_get_next_key(iter); + obs_vector_type * obs_vector = (obs_vector_type *)hash_get( enkf_obs->obs_hash , key); + + if (obs_vector_has_data( obs_vector , ens_mask , fs )) { + local_obsdata_node_type * node = obs_vector_alloc_local_node( obs_vector ); + local_obsdata_add_node( local_obs , node ); + } + + } + hash_iter_free(iter); +} + + + +local_obsdata_type * enkf_obs_alloc_all_active_local_obs( const enkf_obs_type * enkf_obs , const char * key) { + local_obsdata_type * local_obs = local_obsdata_alloc( key ); + { + hash_iter_type * iter = hash_iter_alloc(enkf_obs->obs_hash); + while ( !hash_iter_is_complete( iter ) ) { + const char * key = hash_iter_get_next_key(iter); + obs_vector_type * obs_vector = (obs_vector_type *)hash_get( enkf_obs->obs_hash , key); + local_obsdata_node_type * node = obs_vector_alloc_local_node( obs_vector ); + local_obsdata_add_node( local_obs , node ); + } + hash_iter_free(iter); + } + return local_obs; +} diff --git a/libres/lib/enkf/enkf_plot_data.cpp b/libres/lib/enkf/enkf_plot_data.cpp new file mode 100644 index 00000000000..16fbd428007 --- /dev/null +++ b/libres/lib/enkf/enkf_plot_data.cpp @@ -0,0 +1,148 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'enkf_plot_data.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 + + +#define ENKF_PLOT_DATA_TYPE_ID 3331063 + +struct enkf_plot_data_struct { + UTIL_TYPE_ID_DECLARATION; + const enkf_config_node_type * config_node; + int size; + enkf_plot_tvector_type ** ensemble; + arg_pack_type ** work_arg; +}; + + + +static void enkf_plot_data_resize( enkf_plot_data_type * plot_data , int new_size ) { + if (new_size != plot_data->size) { + int iens; + + if (new_size < plot_data->size) { + for (iens = new_size; iens < plot_data->size; iens++) { + enkf_plot_tvector_free( plot_data->ensemble[iens] ); + arg_pack_free( plot_data->work_arg[iens] ); + } + } + + plot_data->ensemble = (enkf_plot_tvector_type **) util_realloc( plot_data->ensemble , new_size * sizeof * plot_data->ensemble); + plot_data->work_arg = (arg_pack_type **) util_realloc( plot_data->work_arg , new_size * sizeof * plot_data->work_arg); + + if (new_size > plot_data->size) { + for (iens = plot_data->size; iens < new_size; iens++) { + plot_data->ensemble[iens] = enkf_plot_tvector_alloc( plot_data->config_node , iens); + plot_data->work_arg[iens] = arg_pack_alloc(); + } + } + plot_data->size = new_size; + } +} + + +static void enkf_plot_data_reset( enkf_plot_data_type * plot_data ) { + int iens; + for (iens = 0; iens < plot_data->size; iens++) { + enkf_plot_tvector_reset( plot_data->ensemble[iens] ); + arg_pack_clear( plot_data->work_arg[iens] ); + } +} + + +void enkf_plot_data_free( enkf_plot_data_type * plot_data ) { + int iens; + for (iens = 0; iens < plot_data->size; iens++) { + enkf_plot_tvector_free( plot_data->ensemble[iens] ); + arg_pack_free( plot_data->work_arg[iens]); + } + free( plot_data->work_arg ); + free( plot_data->ensemble ); + free( plot_data ); +} + +UTIL_IS_INSTANCE_FUNCTION( enkf_plot_data , ENKF_PLOT_DATA_TYPE_ID ) + + +enkf_plot_data_type * enkf_plot_data_alloc( const enkf_config_node_type * config_node ) { + enkf_plot_data_type * plot_data = (enkf_plot_data_type *)util_malloc( sizeof * plot_data); + UTIL_TYPE_ID_INIT( plot_data , ENKF_PLOT_DATA_TYPE_ID ); + plot_data->config_node = config_node; + plot_data->size = 0; + plot_data->ensemble = NULL; + plot_data->work_arg = NULL; + return plot_data; +} + +enkf_plot_tvector_type * enkf_plot_data_iget( const enkf_plot_data_type * plot_data , int index) { + return plot_data->ensemble[index]; +} + + +int enkf_plot_data_get_size( const enkf_plot_data_type * plot_data ) { + return plot_data->size; +} + + + + +void enkf_plot_data_load( enkf_plot_data_type * plot_data , + enkf_fs_type * fs , + const char * index_key , + const bool_vector_type * input_mask) { + state_map_type * state_map = enkf_fs_get_state_map( fs ); + int ens_size = state_map_get_size( state_map ); + bool_vector_type * mask; + + if (input_mask) + mask = bool_vector_alloc_copy( input_mask ); + else + mask = bool_vector_alloc( ens_size , false ); + state_map_select_matching( state_map , mask , STATE_HAS_DATA ); + + enkf_plot_data_resize( plot_data , ens_size ); + enkf_plot_data_reset( plot_data ); + { + const int num_cpu = 4; + thread_pool_type * tp = thread_pool_alloc( num_cpu , true ); + for (int iens = 0; iens < ens_size ; iens++) { + if (bool_vector_iget( mask , iens)) { + enkf_plot_tvector_type * vector = enkf_plot_data_iget( plot_data , iens ); + arg_pack_type * work_arg = plot_data->work_arg[iens]; + + arg_pack_append_ptr( work_arg , vector ); + arg_pack_append_ptr( work_arg , fs ); + arg_pack_append_const_ptr( work_arg , index_key ); + + thread_pool_add_job( tp , enkf_plot_tvector_load__ , work_arg ); + } + } + thread_pool_join( tp ); + thread_pool_free( tp ); + } + bool_vector_free( mask ); +} + + diff --git a/libres/lib/enkf/enkf_plot_gen_kw.cpp b/libres/lib/enkf/enkf_plot_gen_kw.cpp new file mode 100644 index 00000000000..121cf06db0f --- /dev/null +++ b/libres/lib/enkf/enkf_plot_gen_kw.cpp @@ -0,0 +1,142 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'enkf_plot_gen_kw.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 + + +#define ENKF_PLOT_GEN_KW_TYPE_ID 88362063 + +struct enkf_plot_gen_kw_struct { + UTIL_TYPE_ID_DECLARATION; + const enkf_config_node_type * config_node; + int size; /* Number of ensembles. */ + enkf_plot_gen_kw_vector_type ** ensemble; /* One vector for each ensemble. */ +}; + + +UTIL_IS_INSTANCE_FUNCTION( enkf_plot_gen_kw , ENKF_PLOT_GEN_KW_TYPE_ID ) + + +enkf_plot_gen_kw_type * enkf_plot_gen_kw_alloc( const enkf_config_node_type * config_node ) { + if (enkf_config_node_get_impl_type( config_node ) == GEN_KW) { + enkf_plot_gen_kw_type * plot_gen_kw = (enkf_plot_gen_kw_type *)util_malloc( sizeof * plot_gen_kw ); + UTIL_TYPE_ID_INIT( plot_gen_kw , ENKF_PLOT_GEN_KW_TYPE_ID ); + plot_gen_kw->config_node = config_node; + plot_gen_kw->size = 0; + plot_gen_kw->ensemble = NULL; + return plot_gen_kw; + } + else { + return NULL; + } +} + + +void enkf_plot_gen_kw_free( enkf_plot_gen_kw_type * plot_gen_kw ) { + int iens; + for (iens = 0 ; iens < plot_gen_kw->size ; ++iens) { + enkf_plot_gen_kw_vector_free( plot_gen_kw->ensemble[iens] ); + } + free( plot_gen_kw ); +} + + +int enkf_plot_gen_kw_get_size( const enkf_plot_gen_kw_type * plot_gen_kw ) { + return plot_gen_kw->size; +} + +enkf_plot_gen_kw_vector_type * enkf_plot_gen_kw_iget( const enkf_plot_gen_kw_type * plot_gen_kw , int iens) { + if ((iens < 0) || (iens >= plot_gen_kw->size)) + util_abort("%s: index:%d invalid. Valid interval: [0,%d>.\n",__func__ , iens , plot_gen_kw->size); + + return plot_gen_kw->ensemble[iens]; +} + + +static void enkf_plot_gen_kw_resize( enkf_plot_gen_kw_type * plot_gen_kw , int new_size ) { + if (new_size != plot_gen_kw->size) { + int iens; + + if (new_size < plot_gen_kw->size) { + for (iens = new_size; iens < plot_gen_kw->size; iens++) { + enkf_plot_gen_kw_vector_free( plot_gen_kw->ensemble[iens] ); + } + } + + plot_gen_kw->ensemble = (enkf_plot_gen_kw_vector_type **) util_realloc( plot_gen_kw->ensemble , new_size * sizeof * plot_gen_kw->ensemble); + + if (new_size > plot_gen_kw->size) { + for (iens = plot_gen_kw->size; iens < new_size; iens++) { + plot_gen_kw->ensemble[iens] = enkf_plot_gen_kw_vector_alloc( plot_gen_kw->config_node , iens ); + } + } + plot_gen_kw->size = new_size; + } +} + + + +void enkf_plot_gen_kw_load( enkf_plot_gen_kw_type * plot_gen_kw, + enkf_fs_type * fs, + bool transform_data , + int report_step, + const bool_vector_type * input_mask ) { + + state_map_type * state_map = enkf_fs_get_state_map( fs ); + int ens_size = state_map_get_size( state_map ); + bool_vector_type * mask; + + if (input_mask) + mask = bool_vector_alloc_copy( input_mask ); + else + mask = bool_vector_alloc( ens_size , true ); + + enkf_plot_gen_kw_resize( plot_gen_kw , ens_size ); + { + int iens; + for (iens = 0; iens < ens_size; ++iens) { + if (bool_vector_iget( mask , iens)) { + enkf_plot_gen_kw_vector_type * vector = enkf_plot_gen_kw_iget( plot_gen_kw , iens ); + enkf_plot_gen_kw_vector_load( vector , fs , transform_data , report_step ); + } + } + } +} + + + +const char * enkf_plot_gen_kw_iget_key( const enkf_plot_gen_kw_type * plot_gen_kw, int index) { + const gen_kw_config_type * gen_kw_config = (const gen_kw_config_type *)enkf_config_node_get_ref( plot_gen_kw->config_node ); + return gen_kw_config_iget_name( gen_kw_config , index ); +} + +int enkf_plot_gen_kw_get_keyword_count( const enkf_plot_gen_kw_type * gen_kw ){ + const gen_kw_config_type * gen_kw_config = (const gen_kw_config_type *)enkf_config_node_get_ref( gen_kw->config_node ); + return gen_kw_config_get_data_size(gen_kw_config); +} + +bool enkf_plot_gen_kw_should_use_log_scale(const enkf_plot_gen_kw_type * gen_kw , int index) { + const gen_kw_config_type * gen_kw_config = (const gen_kw_config_type *)enkf_config_node_get_ref( gen_kw->config_node ); + return gen_kw_config_should_use_log_scale(gen_kw_config, index); +} diff --git a/libres/lib/enkf/enkf_plot_gen_kw_vector.cpp b/libres/lib/enkf/enkf_plot_gen_kw_vector.cpp new file mode 100644 index 00000000000..9e8f538c984 --- /dev/null +++ b/libres/lib/enkf/enkf_plot_gen_kw_vector.cpp @@ -0,0 +1,90 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'enkf_plot_gen_kw_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 +#include + + +#define ENKF_PLOT_GEN_KW_VECTOR_TYPE_ID 88362064 + +struct enkf_plot_gen_kw_vector_struct { + UTIL_TYPE_ID_DECLARATION; + int iens; + double_vector_type * data; + const enkf_config_node_type * config_node; +}; + + +UTIL_IS_INSTANCE_FUNCTION( enkf_plot_gen_kw_vector , ENKF_PLOT_GEN_KW_VECTOR_TYPE_ID ) + + +enkf_plot_gen_kw_vector_type * enkf_plot_gen_kw_vector_alloc( const enkf_config_node_type * config_node , int iens ) { + enkf_plot_gen_kw_vector_type * vector = (enkf_plot_gen_kw_vector_type *)util_malloc( sizeof * vector ); + UTIL_TYPE_ID_INIT( vector , ENKF_PLOT_GEN_KW_VECTOR_TYPE_ID ); + vector->config_node = config_node; + vector->data = double_vector_alloc(0,0); + vector->iens = iens; + return vector; +} + + +void enkf_plot_gen_kw_vector_free( enkf_plot_gen_kw_vector_type * vector ) { + double_vector_free( vector->data ); + free( vector ); +} + + +int enkf_plot_gen_kw_vector_get_size( const enkf_plot_gen_kw_vector_type * vector ) { + return double_vector_size( vector->data ); +} + +double enkf_plot_gen_kw_vector_iget( const enkf_plot_gen_kw_vector_type * vector , int index ) { + return double_vector_iget( vector->data , index ); +} + + +void enkf_plot_gen_kw_vector_reset( enkf_plot_gen_kw_vector_type * vector ) { + double_vector_reset( vector->data ); +} + + +void enkf_plot_gen_kw_vector_load( enkf_plot_gen_kw_vector_type * vector , enkf_fs_type * fs , bool transform_data , int report_step ) { + enkf_plot_gen_kw_vector_reset( vector ); + { + node_id_type node_id = { .report_step = report_step , + .iens = vector->iens }; + + enkf_node_type * data_node = enkf_node_alloc( vector->config_node ); + + if (enkf_node_try_load( data_node , fs , node_id )) { + const gen_kw_type * gen_kw = (const gen_kw_type * ) enkf_node_value_ptr( data_node ); + int n_kw = gen_kw_data_size( gen_kw ); + int i_kw; + + for (i_kw = 0 ; i_kw < n_kw ; ++i_kw) { + double_vector_append(vector->data , gen_kw_data_iget( gen_kw , i_kw , transform_data ) ); + } + } + + enkf_node_free( data_node ); + } +} diff --git a/libres/lib/enkf/enkf_plot_gendata.cpp b/libres/lib/enkf/enkf_plot_gendata.cpp new file mode 100644 index 00000000000..1c57b88ad8e --- /dev/null +++ b/libres/lib/enkf/enkf_plot_gendata.cpp @@ -0,0 +1,199 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'enkf_plot_gendata.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 + + +#define ENKF_PLOT_GENDATA_TYPE_ID 377626666 + +struct enkf_plot_gendata_struct { + UTIL_TYPE_ID_DECLARATION; + int size; + const enkf_config_node_type * enkf_config_node; + enkf_plot_genvector_type ** ensemble; + arg_pack_type ** work_arg; + double_vector_type * max_values; + double_vector_type * min_values; +}; + +UTIL_IS_INSTANCE_FUNCTION( enkf_plot_gendata , ENKF_PLOT_GENDATA_TYPE_ID ) + +enkf_plot_gendata_type * enkf_plot_gendata_alloc( const enkf_config_node_type * enkf_config_node ) { + if (enkf_config_node_get_impl_type(enkf_config_node) == GEN_DATA ){ + enkf_plot_gendata_type * data = (enkf_plot_gendata_type *)util_malloc(sizeof * data); + UTIL_TYPE_ID_INIT (data , ENKF_PLOT_GENDATA_TYPE_ID); + data->size = 0; + data->enkf_config_node = enkf_config_node; + data->work_arg = NULL; + data->ensemble = NULL; + + data->max_values = NULL; + data->min_values = NULL; + return data; + } else { + return NULL; + } + +} + + +enkf_plot_gendata_type * enkf_plot_gendata_alloc_from_obs_vector( const obs_vector_type * obs_vector ){ + return enkf_plot_gendata_alloc(obs_vector_get_config_node(obs_vector)); +} + +void enkf_plot_gendata_free( enkf_plot_gendata_type * data ){ + for (int iens = 0; iens < data->size; iens++) { + arg_pack_free( data->work_arg[iens] ); + enkf_plot_genvector_free( data->ensemble[iens] ); + } + + free( data->work_arg ); + free( data->ensemble ); + free( data ); + +} + +int enkf_plot_gendata_get_size( const enkf_plot_gendata_type * data ){ + return data->size; +} + +enkf_plot_genvector_type * enkf_plot_gendata_iget( const enkf_plot_gendata_type * plot_data , int index){ + if(index >=0 && index < plot_data->size){ + return plot_data->ensemble[index]; + } else { + return NULL; + } +} + +static void enkf_plot_gendata_resize( enkf_plot_gendata_type * plot_gendata , int new_size ){ + if (new_size != plot_gendata->size) { + int iens; + + if (new_size < plot_gendata->size) { + for (iens = new_size; iens < plot_gendata->size; iens++) { + enkf_plot_genvector_free( plot_gendata->ensemble[iens] ); + arg_pack_free( plot_gendata->work_arg[iens] ); + } + } + + plot_gendata->ensemble = (enkf_plot_genvector_type **) util_realloc( plot_gendata->ensemble , new_size * sizeof * plot_gendata->ensemble); + plot_gendata->work_arg = (arg_pack_type **) util_realloc( plot_gendata->work_arg , new_size * sizeof * plot_gendata->work_arg); + + if (new_size > plot_gendata->size) { + for (iens = plot_gendata->size; iens < new_size; iens++) { + plot_gendata->ensemble[iens] = enkf_plot_genvector_alloc( plot_gendata->enkf_config_node , iens ); + plot_gendata->work_arg[iens] = arg_pack_alloc(); + } + } + plot_gendata->size = new_size; + } +} + +static void enkf_plot_gendata_reset( enkf_plot_gendata_type * plot_gendata , int report_step){ + int iens; + for (iens = 0; iens < plot_gendata->size; iens++){ + arg_pack_clear( plot_gendata->work_arg[iens] ); + } +} + + + +void enkf_plot_gendata_load( enkf_plot_gendata_type * plot_data , + enkf_fs_type * fs , + int report_step , + const bool_vector_type * input_mask){ + + state_map_type * state_map = enkf_fs_get_state_map( fs ); + int ens_size = state_map_get_size( state_map ); + bool_vector_type * mask; + + if (input_mask) + mask = bool_vector_alloc_copy( input_mask ); + else + mask = bool_vector_alloc( ens_size , false ); + + state_map_select_matching( state_map , mask , STATE_HAS_DATA ); + + enkf_plot_gendata_resize( plot_data , ens_size ); + enkf_plot_gendata_reset( plot_data , report_step ); + + { + const int num_cpu = 4; + thread_pool_type * tp = thread_pool_alloc( num_cpu , true ); + for (int iens = 0; iens < ens_size ; iens++) { + if (bool_vector_iget( mask , iens)) { + enkf_plot_genvector_type * vector = enkf_plot_gendata_iget( plot_data , iens ); + arg_pack_type * work_arg = plot_data->work_arg[iens]; + + arg_pack_append_ptr( work_arg , vector ); + arg_pack_append_ptr( work_arg , fs ); + arg_pack_append_int( work_arg , report_step); + + thread_pool_add_job( tp , enkf_plot_genvector_load__ , work_arg ); + } + } + thread_pool_join( tp ); + thread_pool_free( tp ); + } + + bool_vector_free( mask ); + +} + +void enkf_plot_gendata_find_min_max_values__(enkf_plot_gendata_type * plot_data){ + for (int iens = 0; iens < plot_data->size; iens++){ + enkf_plot_genvector_type * vector = enkf_plot_gendata_iget(plot_data, iens); + int size = enkf_plot_genvector_get_size(vector); + if(iens == 0) { + plot_data->min_values = double_vector_alloc(size, DBL_MAX); + plot_data->max_values = double_vector_alloc(size, -DBL_MAX); + } + for(int index = 0; index < size; index++){ + double value = enkf_plot_genvector_iget(vector, index); + double_vector_iset(plot_data->min_values, index, util_double_min(double_vector_iget(plot_data->min_values, index), value)); + double_vector_iset(plot_data->max_values, index, util_double_max(double_vector_iget(plot_data->max_values, index), value)); + } + } +} + +double_vector_type * enkf_plot_gendata_get_min_values(enkf_plot_gendata_type * plot_data) { + if(plot_data->min_values == NULL) { + enkf_plot_gendata_find_min_max_values__(plot_data); + } + return plot_data->min_values; +} + + + +double_vector_type * enkf_plot_gendata_get_max_values(enkf_plot_gendata_type * plot_data) { + if(plot_data->max_values == NULL) { + enkf_plot_gendata_find_min_max_values__(plot_data); + } + return plot_data->max_values; +} diff --git a/libres/lib/enkf/enkf_plot_genvector.cpp b/libres/lib/enkf/enkf_plot_genvector.cpp new file mode 100644 index 00000000000..a10f79ea944 --- /dev/null +++ b/libres/lib/enkf/enkf_plot_genvector.cpp @@ -0,0 +1,88 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'enkf_plot_genvector.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 ENKF_PLOT_GENVECTOR_TYPE_ID 66862669 + +struct enkf_plot_genvector_struct { + UTIL_TYPE_ID_DECLARATION; + int iens; + double_vector_type * data; + const enkf_config_node_type * config_node; +}; + +UTIL_IS_INSTANCE_FUNCTION( enkf_plot_genvector , ENKF_PLOT_GENVECTOR_TYPE_ID ) + +enkf_plot_genvector_type * enkf_plot_genvector_alloc( const enkf_config_node_type * config_node , int iens){ + enkf_plot_genvector_type * vector = (enkf_plot_genvector_type *)util_malloc( sizeof * vector ); + UTIL_TYPE_ID_INIT( vector , ENKF_PLOT_GENVECTOR_TYPE_ID ); + vector->config_node = config_node; + vector->data = double_vector_alloc(0,0); + vector->iens = iens; + return vector; + +} + +void enkf_plot_genvector_free( enkf_plot_genvector_type * vector ){ + double_vector_free(vector->data); + free(vector); +} + +int enkf_plot_genvector_get_size( const enkf_plot_genvector_type * vector ){ + return double_vector_size( vector->data ); +} + + +double enkf_plot_genvector_iget( const enkf_plot_genvector_type * vector , int index){ + return double_vector_iget( vector->data , index ); +} + + +void enkf_plot_genvector_load( enkf_plot_genvector_type * vector , enkf_fs_type * fs , int report_step ) { + enkf_node_type * work_node = enkf_node_alloc( vector->config_node ); + + node_id_type node_id = { .report_step = report_step , + .iens = vector->iens }; + + if (enkf_node_try_load( work_node , fs , node_id )) { + const gen_data_type * node = (const gen_data_type * ) enkf_node_value_ptr( work_node ); + gen_data_copy_to_double_vector( node , vector->data ); + } + enkf_node_free( work_node ); +} + + +void * enkf_plot_genvector_load__( void * arg ){ + arg_pack_type * arg_pack = arg_pack_safe_cast( arg ); + enkf_plot_genvector_type * vector = (enkf_plot_genvector_type * ) arg_pack_iget_ptr( arg_pack , 0); + enkf_fs_type * fs = (enkf_fs_type * ) arg_pack_iget_ptr( arg_pack , 1 ); + int report_step = arg_pack_iget_int( arg_pack , 2 ); + + enkf_plot_genvector_load( vector , fs , report_step ); + return NULL; + +} diff --git a/libres/lib/enkf/enkf_plot_tvector.cpp b/libres/lib/enkf/enkf_plot_tvector.cpp new file mode 100644 index 00000000000..a8ca3eb67e8 --- /dev/null +++ b/libres/lib/enkf/enkf_plot_tvector.cpp @@ -0,0 +1,180 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'enkf_plot_tvector.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 + +#define ENKF_PLOT_TVECTOR_ID 6111861 + +struct enkf_plot_tvector_struct { + UTIL_TYPE_ID_DECLARATION; + double_vector_type * data; + double_vector_type * work; + time_t_vector_type * time; + bool_vector_type * mask; + const enkf_config_node_type * config_node; + int iens; + bool summary_mode; +}; + + + +UTIL_SAFE_CAST_FUNCTION( enkf_plot_tvector , ENKF_PLOT_TVECTOR_ID ) +UTIL_IS_INSTANCE_FUNCTION( enkf_plot_tvector , ENKF_PLOT_TVECTOR_ID ) + + + +void enkf_plot_tvector_reset( enkf_plot_tvector_type * plot_tvector ) { + double_vector_reset( plot_tvector->data ); + time_t_vector_reset( plot_tvector->time ); + bool_vector_reset( plot_tvector->mask ); +} + + +enkf_plot_tvector_type * enkf_plot_tvector_alloc( const enkf_config_node_type * config_node , int iens) { + enkf_plot_tvector_type * plot_tvector = (enkf_plot_tvector_type *)util_malloc( sizeof * plot_tvector); + UTIL_TYPE_ID_INIT( plot_tvector , ENKF_PLOT_TVECTOR_ID ); + + plot_tvector->data = double_vector_alloc( 0 , 0 ); + plot_tvector->time = time_t_vector_alloc(-1 , 0); + plot_tvector->mask = bool_vector_alloc( false , 0 ); + plot_tvector->work = double_vector_alloc(0,0); + plot_tvector->iens = iens; + + plot_tvector->config_node = config_node; + if (enkf_config_node_get_impl_type( config_node ) == SUMMARY) + plot_tvector->summary_mode = true; + else + plot_tvector->summary_mode = false; + + return plot_tvector; +} + + +void enkf_plot_tvector_free( enkf_plot_tvector_type * plot_tvector ) { + double_vector_free( plot_tvector->data ); + double_vector_free( plot_tvector->work ); + time_t_vector_free( plot_tvector->time ); + bool_vector_free( plot_tvector->mask ); +} + + +bool enkf_plot_tvector_all_active( const enkf_plot_tvector_type * plot_tvector ) { + bool all_active = true; + for (int i=0; i < bool_vector_size( plot_tvector->mask ); i++) + all_active = all_active && bool_vector_iget(plot_tvector->mask , i ); + + return all_active; +} + + +int enkf_plot_tvector_size( const enkf_plot_tvector_type * plot_tvector ) { + return bool_vector_size( plot_tvector->mask ); +} + + +void enkf_plot_tvector_iset( enkf_plot_tvector_type * plot_tvector , int index , time_t time , double value) { + time_t_vector_iset( plot_tvector->time , index , time ); + bool active_value = true; + + /* This is to handle holes in the summary vector storage. */ + if (plot_tvector->summary_mode && !summary_active_value( value )) + active_value = false; + + if (active_value) { + double_vector_iset( plot_tvector->data , index , value ); + bool_vector_iset( plot_tvector->mask , index , true ); + } else + bool_vector_iset( plot_tvector->mask , index , false ); + +} + + + +double enkf_plot_tvector_iget_value( const enkf_plot_tvector_type * plot_tvector , int index) { + return double_vector_iget( plot_tvector->data , index); +} + +time_t enkf_plot_tvector_iget_time( const enkf_plot_tvector_type * plot_tvector , int index) { + return time_t_vector_iget( plot_tvector->time , index); +} + +bool enkf_plot_tvector_iget_active( const enkf_plot_tvector_type * plot_tvector , int index) { + return bool_vector_iget( plot_tvector->mask , index ); +} + + + + + + +void enkf_plot_tvector_load( enkf_plot_tvector_type * plot_tvector , + enkf_fs_type * fs , + const char * index_key) { + + time_map_type * time_map = enkf_fs_get_time_map( fs ); + int step1 = 0; + int step2 = time_map_get_last_step( time_map ); + enkf_node_type * work_node = enkf_node_alloc( plot_tvector->config_node ); + + if (enkf_node_vector_storage( work_node )) { + bool has_data = enkf_node_user_get_vector(work_node , fs , index_key , plot_tvector->iens , plot_tvector->work); + + if(has_data) { + for (int step = 0; step < double_vector_size( plot_tvector->work ); step++) + enkf_plot_tvector_iset( plot_tvector , + step , + time_map_iget( time_map , step ) , + double_vector_iget( plot_tvector->work , step )); + } + } else { + int step; + node_id_type node_id; + node_id.iens = plot_tvector->iens; + node_id.report_step = 0; + + for (step = step1 ; step <= step2; step++) { + double value; + node_id.report_step = step; + + if (enkf_node_user_get(work_node , fs , index_key , node_id , &value)) { + enkf_plot_tvector_iset( plot_tvector , + step , + time_map_iget( time_map , step ) , + value ); + } + } + } + enkf_node_free( work_node ); +} + + +void * enkf_plot_tvector_load__( void * arg ) { + arg_pack_type * arg_pack = arg_pack_safe_cast( arg ); + enkf_plot_tvector_type * tvector = (enkf_plot_tvector_type * ) arg_pack_iget_ptr( arg_pack , 0 ); + enkf_fs_type * fs = (enkf_fs_type * ) arg_pack_iget_ptr( arg_pack , 1 ); + const char * index_key = (const char * ) arg_pack_iget_ptr( arg_pack , 2 ); + + enkf_plot_tvector_load( tvector , fs , index_key ); + return NULL; +} + diff --git a/libres/lib/enkf/enkf_serialize.cpp b/libres/lib/enkf/enkf_serialize.cpp new file mode 100644 index 00000000000..f0b0c30cfe5 --- /dev/null +++ b/libres/lib/enkf/enkf_serialize.cpp @@ -0,0 +1,265 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'enkf_serialize.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 + +/** + This file handles serialization and deserialization of the + enkf_nodes. This is at the very core of the EnKF update + algorithm. The final update step is written: + + A' = XA + + i.e. it is linear algeabra, and we need(?) to write the various + objects in the form of an ensemble matrix, this is the process we + call serialization. Then the linear algebra update is performed, + and afterwards we must read the data back from the ensemble matrix + to the enkf_node object, this is deserialization. + + + + + =============== =============== + | PORO-1 | | PORO-2 | + |-------------- Member 2 |-------------- + | | ======== | | + | P0 ... P10 | Member 1 | | P0 ... P10 | + =============== ======== | =============== + /|\ | ----- /|\ + | | | | + | \|/ \|/ | + | | + | [ P0 P0 ] | + | [ P1 P1 ] | + ------------------>[ P2 P2 ]<------------------- + [ P3 P3 ] + [ P4 P4 ] + [ ··········] ============== + [ R1 R1 ] | RELPERM-2 | + ============== ------>[ R2 R2 ]<----------->|------------| + | RELPERM-1 | | [ R3 R3 ] | | + |------------|<---- [ ··········] | R0 ... R5 | + | | [ F2 F2 ] ============== + | R0 ... R5 | ---->[ F3 F3 ] + ============== | [ F4 F4 ]<----- + | [ F6 F6 ] | + | | + | | ============== + | | | FAULT-1 | + | ----------->|------------| + ============== | | | + | FAULT-1 | | | F0 ... F6 | + |------------|<------ ============== + | | + | F0 ... F6 | + ============== + + + +This figure shows the following: + + 1. Three different nodes called PORO, RELPERM and FAULT + respectively. The PORO node consists of eleven elements (P0 + ... P10), whereas the RELPERM and FAULT nodes contain six and + seven elements. + + 2. The ensemble consists of two members (i.e. there is PORO-1 and + PORO-2.). + + 3. The members have been serialized into a a large vector where + everything comes ordered. Observe that *NOT* all elements from the + members have been inserted into the large vector, i.e. for the + PORO fields we only have elements P0 .. P4; this is because (for + some reason) not all elements were active. + + +Each of the enkf_node functions have their own xxx_serialize and +xxx_deserialize functions, however these functions SHOULD call the +enkf_serialize() and enkf_deserialize() functions in this +file. (Rolling your own serialize / deserialize functions at the +lowest level is a SERIOUS CRIME.) + +The illustration above shows three different enkf_node objects which +have been COMPLETELY serialized. One of the reasons the code is so +complex is that it is supposed to handle situations where the serial +vector is to small to hold everything, and repeated calls to serialize +& deserialize must be performed to complete the thing. + + +About stride +============ +In the enkf update the ensemble matrix A is just that - a matrix, +however in this elegant high-level language it is of course +implemented as one long linear vector. The matrix is implemented such +that the 'member-direction' is fastest running index. Consider the +following ensemble matrix, consisting of five ensemble members: + + + + + + Member 5 + Member 2 --· | + | | + | | +Member 1 ----· | | + | | | + \|/ \|/ \|/ + [ P0 P0 P0 P0 P0 ] + [ P1 P1 P1 P1 P1 ] + [ P2 P2 P2 P2 P2 ] + [ P3 P3 P3 P3 P3 ] + [ R0 R0 R0 R0 R0 ] + A = [ R1 R1 R1 R1 R1 ] + [ R2 R2 R2 R2 R2 ] + [ F0 F0 F0 F0 F0 ] + [ F1 F1 F1 F1 F1 ] + [ F2 F2 F2 F2 F2 ] + [ F3 F3 F3 F3 F3 ] + + +The in memory the matrix will look like this: + + + Member 2 + | + ______________|______________ + / | \ + | | | + ______________|______________|______________|______________ + / | | | \ + | | | | | + | | | | | + \|/ \|/ \|/ \|/ \|/ ........... + A =[ P0 P0 P0 P0 P0 P1 P1 P1 P1 P1 P2 P2 P2 P2 P2 P3 P3 P3 P3 P3 R0 R0 R0 R0 R0 R1 R1 R1 R1 R1 R2 R2 R2 R2 R2 F0 F0 F0 F0 F0 F1 F1 F1 F1 F1 F2 F2 F2 F2 F2 F3 F3 F3 F3 F3 ...] + /|\ X1 /|\ X2 /|\ /|\ /|\ ........ + | | | | | + | | | | | + \______________|______________|______________|______________/ + | | | + | | | + \______________|______________/ + | + | + Member 1 + +The stride in the serial_vector_type object is the number of elements +between consecutive elements in the same member, i.e. it is five in +the vector above. (Starting at e.g. P0 for member three (marked with +X1 in the figure), P1 for the same member is five elements down in the +vector (marked with X2 above)). Now - that was clear ehhh? + +*****************************************************************/ + + + +/* + It will be very costly to make it thread-safe if we manipulate the + shape of the A matrix from here. +*/ + + +void enkf_matrix_serialize(const void * __node_data , + int node_size , + ecl_data_type node_type , + const active_list_type * __active_list , + matrix_type * A , + int row_offset, + int column) { + int active_size; + const int * active_list = active_list_get_active( __active_list ); + active_size = active_list_get_active_size( __active_list , node_size); + + if (ecl_type_is_double(node_type)) { + const double * node_data = (const double *) __node_data; + if (active_size == node_size) /** All elements active */ + matrix_set_many_on_column( A , row_offset , node_size , node_data , column); + else { + int row_index; + int node_index; + for (row_index = 0; row_index < active_size; row_index++) { + node_index = active_list[ row_index ]; + matrix_iset( A , row_index + row_offset , column , node_data[node_index] ); + } + } + } else if (ecl_type_is_float(node_type)) { + const float * node_data = (const float *) __node_data; + int row_index; + if (active_size == node_size) {/** All elements active */ + for (row_index = 0; row_index < node_size; row_index++) + matrix_iset( A , row_index + row_offset , column , node_data[ row_index ]); /* Must have float -> double conversion; can not use memcpy() based approach */ + } else { + int row_index; + int node_index; + for (row_index = 0; row_index < active_size; row_index++) { + node_index = active_list[ row_index ]; + matrix_iset( A , row_index + row_offset , column , node_data[node_index] ); + } + } + } else + util_abort("%s: internal error: trying to serialize unserializable type:%s \n",__func__ , ecl_type_alloc_name( node_type )); +} + + +void enkf_matrix_deserialize(void * __node_data , + int node_size , + ecl_data_type node_type , + const active_list_type * __active_list , + const matrix_type * A, + int row_offset, + int column) { + int active_size; + const int * active_list = active_list_get_active( __active_list ); + active_size = active_list_get_active_size( __active_list , node_size ); + + if (ecl_type_is_double(node_type)) { + double * node_data = (double *) __node_data; + + if (active_size == node_size) { /** All elements active */ + int row_index; + for (row_index = 0; row_index < active_size; row_index++) + node_data[row_index] = matrix_iget( A , row_index + row_offset , column); + } else { + int row_index; + int node_index; + for (row_index = 0; row_index < active_size; row_index++) { + node_index = active_list[ row_index ]; + node_data[node_index] = matrix_iget( A , row_index + row_offset , column); + } + } + + } else if (ecl_type_is_float(node_type)) { + float * node_data = (float *) __node_data; + + if (active_size == node_size) { /** All elements active */ + int row_index; + for (row_index = 0; row_index < active_size; row_index++) + node_data[row_index] = matrix_iget( A , row_index + row_offset , column); + } else { + int row_index; + int node_index; + for (row_index = 0; row_index < active_size; row_index++) { + node_index = active_list[ row_index ]; + node_data[node_index] = matrix_iget( A , row_index + row_offset , column); + } + } + } else + util_abort("%s: internal error: trying to serialize unserializable type:%s \n",__func__ , ecl_type_alloc_name( node_type )); +} diff --git a/libres/lib/enkf/enkf_state.cpp b/libres/lib/enkf/enkf_state.cpp new file mode 100644 index 00000000000..d7208a3ab24 --- /dev/null +++ b/libres/lib/enkf/enkf_state.cpp @@ -0,0 +1,854 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'enkf_state.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 + +#define ENKF_STATE_TYPE_ID 78132 + + + + + +/** + This struct contains various objects which the enkf_state needs + during operation, which the enkf_state_object *DOES NOT* own. The + struct only contains pointers to objects owned by (typically) the + enkf_main object. + + If the enkf_state object writes to any of the objects in this + struct that can be considered a serious *BUG*. + + The elements in this struct should not change during the + application lifetime? +*/ + +typedef struct shared_info_struct { + model_config_type * model_config; /* .... */ + ext_joblist_type * joblist; /* The list of external jobs which are installed - and *how* they should be run (with Python code) */ + const site_config_type * site_config; + ert_templates_type * templates; + const ecl_config_type * ecl_config; +} shared_info_type; + + + + + + +/*****************************************************************/ + +struct enkf_state_struct { + UTIL_TYPE_ID_DECLARATION; + hash_type * node_hash; + ensemble_config_type * ensemble_config; /* The config nodes for the enkf_node objects contained in node_hash. */ + shared_info_type * shared_info; /* Pointers to shared objects which is needed by the enkf_state object (read only). */ + int __iens; +}; + +/*****************************************************************/ + + +static UTIL_SAFE_CAST_FUNCTION( enkf_state , ENKF_STATE_TYPE_ID ) + + +static shared_info_type * shared_info_alloc(const site_config_type * site_config , model_config_type * model_config, const ecl_config_type * ecl_config , ert_templates_type * templates) { + shared_info_type * shared_info = (shared_info_type *)util_malloc(sizeof * shared_info ); + shared_info->joblist = site_config_get_installed_jobs( site_config ); + shared_info->site_config = site_config; + shared_info->model_config = model_config; + shared_info->templates = templates; + shared_info->ecl_config = ecl_config; + return shared_info; +} + + +static void shared_info_free(shared_info_type * shared_info) { + /** + Adding something here is a BUG - this object does + not own anything. + */ + free( shared_info ); +} + + + + + +/*****************************************************************/ +/** Helper classes complete - starting on the enkf_state proper object. */ +/*****************************************************************/ + + +/* + This function does not acces the nodes of the enkf_state object. +*/ +void enkf_state_initialize(enkf_state_type * enkf_state , rng_type * rng, enkf_fs_type * fs , const stringlist_type * param_list, init_mode_type init_mode) { + if (init_mode != INIT_NONE) { + int iens = enkf_state->__iens; + state_map_type * state_map = enkf_fs_get_state_map( fs ); + realisation_state_enum current_state = state_map_iget(state_map, iens); + if ((current_state == STATE_PARENT_FAILURE) && (init_mode != INIT_FORCE)) + return; + else { + const ensemble_config_type * ensemble_config = enkf_state->ensemble_config; + for (int ip = 0; ip < stringlist_get_size(param_list); ip++) { + const enkf_config_node_type * config_node = ensemble_config_get_node( ensemble_config , stringlist_iget(param_list, ip)); + enkf_node_type * param_node = enkf_node_alloc( config_node ); + node_id_type node_id = { .report_step = 0, .iens = iens }; + bool has_data = enkf_node_has_data(param_node, fs, node_id); + + if ((init_mode == INIT_FORCE) || (has_data == false) || (current_state == STATE_LOAD_FAILURE)) { + if (enkf_node_initialize(param_node, iens, rng)) + enkf_node_store(param_node, fs, node_id); + } + + enkf_node_free( param_node ); + } + state_map_update_matching(state_map , iens , STATE_UNDEFINED | STATE_LOAD_FAILURE , STATE_INITIALIZED); + enkf_fs_fsync(fs); + } + } +} + + +static void enkf_state_add_nodes( enkf_state_type * enkf_state, const ensemble_config_type * ensemble_config) { + stringlist_type * container_keys = stringlist_alloc_new(); + stringlist_type * keylist = ensemble_config_alloc_keylist(ensemble_config); + int keys = stringlist_get_size(keylist); + + // 1: Add all regular nodes + for (int ik = 0; ik < keys; ik++) { + const char * key = stringlist_iget(keylist, ik); + const enkf_config_node_type * config_node = ensemble_config_get_node(ensemble_config , key); + if (enkf_config_node_get_impl_type( config_node ) == CONTAINER) { + stringlist_append_copy( container_keys , key ); + } else + enkf_state_add_node(enkf_state , key , config_node); + } + + // 2: Add container nodes - must ensure that all other nodes have + // been added already (this implies that containers of containers + // will be victim of hash retrieval order problems .... + + for (int ik = 0; ik < stringlist_get_size( container_keys ); ik++) { + const char * key = stringlist_iget(container_keys, ik); + const enkf_config_node_type * config_node = ensemble_config_get_node(ensemble_config , key); + enkf_state_add_node( enkf_state , key , config_node ); + } + + stringlist_free(keylist); + stringlist_free( container_keys ); +} + + +enkf_state_type * enkf_state_alloc(int iens, + rng_type * rng , + model_config_type * model_config, + ensemble_config_type * ensemble_config, + const site_config_type * site_config, + const ecl_config_type * ecl_config, + ert_templates_type * templates) { + + enkf_state_type * enkf_state = (enkf_state_type *)util_malloc(sizeof *enkf_state ); + UTIL_TYPE_ID_INIT( enkf_state , ENKF_STATE_TYPE_ID ); + + enkf_state->ensemble_config = ensemble_config; + enkf_state->shared_info = shared_info_alloc(site_config , model_config , ecl_config , templates); + enkf_state->node_hash = hash_alloc(); + + enkf_state->__iens = iens; + enkf_state_add_nodes( enkf_state , ensemble_config ); + + return enkf_state; +} + + +static void enkf_state_log_GEN_DATA_load( const enkf_node_type * enkf_node , int report_step , forward_load_context_type * load_context) { + if (forward_load_context_accept_messages(load_context)) { + char * load_file = enkf_config_node_alloc_infile(enkf_node_get_config( enkf_node ) , report_step); + int data_size = gen_data_get_size( (const gen_data_type * ) enkf_node_value_ptr( enkf_node )); + char * msg = util_alloc_sprintf("Loaded GEN_DATA:%s instance for step:%d from file:%s size:%d" , + enkf_node_get_key( enkf_node ) , + report_step , + load_file , + data_size); + + forward_load_context_add_message(load_context, msg); + + free( msg ); + free( load_file ); + } +} + + +static int_vector_type * __enkf_state_get_time_index(enkf_fs_type * sim_fs, const ecl_sum_type * summary) { + time_map_type * time_map = enkf_fs_get_time_map( sim_fs ); + time_map_summary_update( time_map , summary ); + return time_map_alloc_index_map( time_map , summary ); +} + + +/* + * Check if there are summary keys in the ensemble config that is not found in Eclipse. If this is the case, AND we + * have observations for this key, we have a problem. Otherwise, just print a message to the log. + */ +static void enkf_state_check_for_missing_eclipse_summary_data(const ensemble_config_type * ens_config, + const summary_key_matcher_type * matcher, + const ecl_smspec_type * smspec, + forward_load_context_type * load_context, + const int iens ) { + + stringlist_type * keys = summary_key_matcher_get_keys(matcher); + + for (int i = 0; i < stringlist_get_size(keys); i++) { + + const char *key = stringlist_iget(keys, i); + + if (ecl_smspec_has_general_var(smspec, key) || !summary_key_matcher_summary_key_is_required(matcher, key)) + continue; + + if (!ensemble_config_has_key(ens_config, key)) + continue; + + const enkf_config_node_type *config_node = ensemble_config_get_node(ens_config, key); + if (enkf_config_node_get_num_obs(config_node) == 0) { + res_log_finfo("[%03d:----] Unable to find Eclipse data for summary key: " + "%s, but have no observations either, so will continue.", + iens, key); + } else { + res_log_ferror("[%03d:----] Unable to find Eclipse data for summary key: " + "%s, but have observation for this, job will fail.", + iens, key); + forward_load_context_update_result(load_context, LOAD_FAILURE); + if (forward_load_context_accept_messages(load_context)) { + char *msg = util_alloc_sprintf("Failed to load vector: %s", key); + forward_load_context_add_message(load_context, msg); + free(msg); + } + } + } + + stringlist_free(keys); +} + +static bool enkf_state_internalize_dynamic_eclipse_results(ensemble_config_type * ens_config, + forward_load_context_type * load_context , + const model_config_type * model_config) { + + bool load_summary = ensemble_config_has_impl_type(ens_config, SUMMARY); + const run_arg_type * run_arg = forward_load_context_get_run_arg( load_context ); + const summary_key_matcher_type * matcher = ensemble_config_get_summary_key_matcher(ens_config); + const ecl_sum_type * summary = forward_load_context_get_ecl_sum( load_context ); + int matcher_size = summary_key_matcher_get_size(matcher); + + if (load_summary || matcher_size > 0 || summary) { + int load_start = run_arg_get_load_start( run_arg ); + + if (load_start == 0) { /* Do not attempt to load the "S0000" summary results. */ + load_start++; + } + + { + enkf_fs_type * sim_fs = run_arg_get_sim_fs( run_arg ); + /** OK - now we have actually loaded the ecl_sum instance, or ecl_sum == NULL. */ + if (summary) { + int_vector_type * time_index = __enkf_state_get_time_index(sim_fs, summary); + + /* + Now there are two related / conflicting(?) systems for + checking summary time consistency, both internally in the + time_map and also through the + model_config_report_step_compatible() function. + */ + + /*Check the loaded summary against the reference ecl_sum_type */ + if (!model_config_report_step_compatible(model_config, summary)) + forward_load_context_update_result(load_context, REPORT_STEP_INCOMPATIBLE); + + + /* The actual loading internalizing - from ecl_sum -> enkf_node. */ + const int iens = run_arg_get_iens( run_arg ); + const int step2 = ecl_sum_get_last_report_step( summary ); /* Step2 is just taken from the number of steps found in the summary file. */ + + int_vector_iset_block( time_index , 0 , load_start , -1 ); + int_vector_resize( time_index , step2 + 1, -1); + + const ecl_smspec_type * smspec = ecl_sum_get_smspec(summary); + + for(int i = 0; i < ecl_smspec_num_nodes(smspec); i++) { + const ecl::smspec_node& smspec_node = ecl_smspec_iget_node_w_node_index(smspec, i); + const char * key = smspec_node.get_gen_key1(); + + if(summary_key_matcher_match_summary_key(matcher, key)) { + summary_key_set_type * key_set = enkf_fs_get_summary_key_set(sim_fs); + summary_key_set_add_summary_key(key_set, key); + + enkf_config_node_type * config_node = ensemble_config_get_or_create_summary_node(ens_config, key); + enkf_node_type * node = enkf_node_alloc( config_node ); + + enkf_node_try_load_vector( node , sim_fs , iens ); // Ensure that what is currently on file is loaded before we update. + + enkf_node_forward_load_vector( node , load_context , time_index); + enkf_node_store_vector( node , sim_fs , iens ); + enkf_node_free( node ); + } + } + + int_vector_free( time_index ); + + /* + Check if some of the specified keys are missing from the Eclipse data, and if there are observations for them. That is a problem. + */ + enkf_state_check_for_missing_eclipse_summary_data(ens_config, matcher, smspec, load_context, iens); + + return true; + } else { + res_log_fwarning("Could not load ECLIPSE summary data from %s - " + "this will probably fail later ... ", + run_arg_get_runpath(run_arg)); + return false; + } + } + } else { + return true; + } +} + +static void enkf_state_load_gen_data_node( + forward_load_context_type * load_context, + enkf_fs_type * sim_fs, + int iens, + const enkf_config_node_type * config_node, + int start, + int stop) +{ + for (int report_step = start; report_step <= stop; report_step++) { + if (!enkf_config_node_internalize(config_node, report_step)) + continue; + + forward_load_context_select_step(load_context, report_step); + enkf_node_type * node = enkf_node_alloc(config_node); + + if (enkf_node_forward_load(node, load_context)) { + node_id_type node_id = {.report_step = report_step, + .iens = iens }; + + enkf_node_store(node, sim_fs, node_id); + enkf_state_log_GEN_DATA_load(node, report_step, load_context); + } else { + forward_load_context_update_result(load_context, LOAD_FAILURE); + res_log_ferror("[%03d:%04d] Failed load data for GEN_DATA node:%s.", + iens, report_step, enkf_node_get_key(node)); + + if (forward_load_context_accept_messages(load_context)) { + char * msg = util_alloc_sprintf("Failed to load: %s at step:%d", + enkf_node_get_key(node), report_step); + forward_load_context_add_message(load_context, msg); + free(msg); + } + } + enkf_node_free(node); + } +} + + +static void enkf_state_internalize_GEN_DATA(const ensemble_config_type * ens_config, + forward_load_context_type * load_context , + const model_config_type * model_config , + int last_report) { + + stringlist_type * keylist_GEN_DATA = ensemble_config_alloc_keylist_from_impl_type(ens_config, GEN_DATA); + + int numkeys = stringlist_get_size(keylist_GEN_DATA); + + if (numkeys > 0) + if (last_report <= 0) + res_log_fwarning("Trying to load GEN_DATA without properly " + "set last_report (was %d) - will only look for step 0 data: %s", + last_report, + stringlist_iget(keylist_GEN_DATA, 0) + ); + + const run_arg_type * run_arg = forward_load_context_get_run_arg(load_context); + enkf_fs_type * sim_fs = run_arg_get_sim_fs(run_arg); + const int iens = run_arg_get_iens(run_arg); + + for (int ikey=0; ikey < numkeys; ikey++) { + const enkf_config_node_type * config_node = ensemble_config_get_node(ens_config, + stringlist_iget(keylist_GEN_DATA, + ikey)); + + /* + This for loop should probably be changed to use the report + steps configured in the gen_data_config object, instead of + spinning through them all. + */ + int start = run_arg_get_load_start(run_arg); + int stop = util_int_max(0, last_report); // inclusive + enkf_state_load_gen_data_node(load_context, + sim_fs, + iens, + config_node, + start, + stop); + } + stringlist_free( keylist_GEN_DATA ); +} + + + + + +static forward_load_context_type * enkf_state_alloc_load_context(const ensemble_config_type * ens_config, + const ecl_config_type * ecl_config, + const run_arg_type * run_arg, + stringlist_type * messages) { + bool load_summary = false; + const summary_key_matcher_type * matcher = ensemble_config_get_summary_key_matcher(ens_config); + if (summary_key_matcher_get_size(matcher) > 0) + load_summary = true; + + if (ensemble_config_require_summary(ens_config)) + load_summary = true; + + forward_load_context_type * load_context; + + load_context = forward_load_context_alloc(run_arg, + load_summary, + ecl_config, + messages); + return load_context; + +} + + +/** + This function loads the results from a forward simulations from report_step1 + to report_step2. The details of what to load are in model_config and the + spesific nodes for special cases. + + Will mainly be called at the end of the forward model, but can also + be called manually from external scope. +*/ +static int enkf_state_internalize_results(ensemble_config_type * ens_config, + model_config_type * model_config, + const ecl_config_type * ecl_config, + const run_arg_type * run_arg, + stringlist_type * msg_list) { + + forward_load_context_type * load_context = enkf_state_alloc_load_context( ens_config, ecl_config, run_arg, msg_list); + /* + The timing information - i.e. mainly what is the last report step + in these results are inferred from the loading of summary results, + hence we must load the summary results first. + */ + + enkf_state_internalize_dynamic_eclipse_results(ens_config, + load_context , + model_config); + + enkf_fs_type * sim_fs = run_arg_get_sim_fs( run_arg ); + int last_report = time_map_get_last_step( enkf_fs_get_time_map( sim_fs )); + if (last_report < 0) + last_report = model_config_get_last_history_restart( model_config ); + + /* Ensure that the last step is internalized? */ + if (last_report > 0) + model_config_set_internalize_state( model_config , last_report); + + enkf_state_internalize_GEN_DATA(ens_config , load_context , model_config , last_report); + + int result = forward_load_context_get_result(load_context); + forward_load_context_free( load_context ); + return result; +} + + + + + +static int enkf_state_load_from_forward_model__(ensemble_config_type * ens_config, + model_config_type * model_config, + const ecl_config_type * ecl_config, + const run_arg_type * run_arg , + stringlist_type * msg_list) { + + int result = 0; + + if (ensemble_config_have_forward_init( ens_config )) + result |= ensemble_config_forward_init( ens_config , run_arg ); + + result |= enkf_state_internalize_results( ens_config, model_config, ecl_config, run_arg , msg_list ); + state_map_type * state_map = enkf_fs_get_state_map( run_arg_get_sim_fs( run_arg ) ); + int iens = run_arg_get_iens( run_arg ); + if (result & LOAD_FAILURE) + state_map_iset( state_map , iens , STATE_LOAD_FAILURE); + else + state_map_iset( state_map , iens , STATE_HAS_DATA); + + return result; +} + +int enkf_state_load_from_forward_model(enkf_state_type * enkf_state , + run_arg_type * run_arg , + stringlist_type * msg_list) { + + ensemble_config_type * ens_config = enkf_state->ensemble_config; + model_config_type * model_config = enkf_state->shared_info->model_config; + const ecl_config_type * ecl_config = enkf_state->shared_info->ecl_config; + + return enkf_state_load_from_forward_model__( ens_config, model_config, ecl_config, run_arg, msg_list); +} + + +/** + Observe that this does not return the loadOK flag; it will load as + good as it can all the data it should, and be done with it. +*/ + +void * enkf_state_load_from_forward_model_mt( void * arg ) { + arg_pack_type * arg_pack = arg_pack_safe_cast( arg ); + enkf_state_type * enkf_state = enkf_state_safe_cast((enkf_state_type * ) arg_pack_iget_ptr( arg_pack , 0 )); + run_arg_type * run_arg = (run_arg_type * ) arg_pack_iget_ptr( arg_pack , 1 ); + stringlist_type * msg_list = (stringlist_type * ) arg_pack_iget_ptr( arg_pack , 2 ); + bool manual_load = arg_pack_iget_bool( arg_pack , 3 ); + int * result = (int * ) arg_pack_iget_ptr( arg_pack , 4 ); + int iens = run_arg_get_iens( run_arg ); + + if (manual_load) + state_map_update_undefined(enkf_fs_get_state_map( run_arg_get_sim_fs(run_arg) ) , iens , STATE_INITIALIZED); + + *result = enkf_state_load_from_forward_model( enkf_state , run_arg , msg_list ); + if (*result & REPORT_STEP_INCOMPATIBLE) { + // If refcase has been used for observations: crash and burn. + fprintf(stderr,"** Warning the timesteps in refcase and current simulation are not in accordance - something wrong with schedule file?\n"); + *result -= REPORT_STEP_INCOMPATIBLE; + } + + return NULL; +} + + + + + +void enkf_state_free(enkf_state_type *enkf_state) { + hash_free(enkf_state->node_hash); + shared_info_free(enkf_state->shared_info); + free(enkf_state); +} + + + + + + + +/** + init_step : The parameters are loaded from this EnKF/report step. + report_step1 : The simulation should start from this report step; + dynamic data are loaded from this step. + report_step2 : The simulation should stop at this report step. (unless run_mode == ENSEMBLE_PREDICTION - where it just runs til end.) + + For a normal EnKF run we well have init_step == report_step1, but + in the case where we want rerun from the beginning with updated + parameters, they will be different. If init_step != report_step1, + it is required that report_step1 == 0; otherwise the dynamic data + will become completely inconsistent. We just don't allow that! +*/ + +void enkf_state_init_eclipse(const res_config_type * res_config, + const run_arg_type * run_arg ) { + + ensemble_config_type * ens_config = res_config_get_ensemble_config(res_config); + const ecl_config_type * ecl_config = res_config_get_ecl_config(res_config); + model_config_type * model_config = res_config_get_model_config(res_config); + + util_make_path(run_arg_get_runpath(run_arg)); + + ert_templates_instansiate(res_config_get_templates(res_config), + run_arg_get_runpath(run_arg), + run_arg_get_subst_list(run_arg)); + + enkf_state_ecl_write(ens_config, + model_config, + run_arg, + run_arg_get_sim_fs(run_arg)); + + /* Writing the ECLIPSE data file. */ + if (ecl_config_have_eclbase(ecl_config) && ecl_config_get_data_file(ecl_config)) { + char * data_file = ecl_util_alloc_filename(run_arg_get_runpath(run_arg), + run_arg_get_job_name(run_arg), + ECL_DATA_FILE, + true, + -1); + + subst_list_update_string(run_arg_get_subst_list(run_arg), &data_file); + subst_list_filter_file(run_arg_get_subst_list(run_arg), + ecl_config_get_data_file(ecl_config), + data_file); + + free(data_file); + } + + mode_t umask = site_config_get_umask(res_config_get_site_config( res_config )); + + /* This is where the job script is created */ + const env_varlist_type * varlist = site_config_get_env_varlist(res_config_get_site_config(res_config)); + forward_model_formatted_fprintf(model_config_get_forward_model(model_config), + run_arg_get_run_id( run_arg ), + run_arg_get_runpath(run_arg), + model_config_get_data_root(model_config), + run_arg_get_subst_list(run_arg), + umask, + varlist); +} + + + +/** + Observe that if run_arg == false, this routine will return with + job_completeOK == true, that might be a bit misleading. + + Observe that if an internal retry is performed, this function will + be called several times - MUST BE REENTRANT. +*/ + +bool enkf_state_complete_forward_modelOK(const res_config_type * res_config, + run_arg_type * run_arg) { + + ensemble_config_type * ens_config = res_config_get_ensemble_config( res_config ); + const ecl_config_type * ecl_config = res_config_get_ecl_config( res_config ); + model_config_type * model_config = res_config_get_model_config( res_config ); + const int iens = run_arg_get_iens( run_arg ); + int result; + + + /** + The queue system has reported that the run is OK, i.e. it has + completed and produced the targetfile it should. We then check + in this scope whether the results can be loaded back; if that + is OK the final status is updated, otherwise: restart. + */ + res_log_finfo("[%03d:%04d-%04d] Forward model complete - starting to load results.", + iens, run_arg_get_step1(run_arg), run_arg_get_step2(run_arg)); + + result = enkf_state_load_from_forward_model__( ens_config, + model_config, + ecl_config, + run_arg, + NULL); + + if (result & REPORT_STEP_INCOMPATIBLE) { + // If refcase has been used for observations: crash and burn. + fprintf(stderr,"** Warning the timesteps in refcase and current simulation are not in accordance - something wrong with schedule file?\n"); + result -= REPORT_STEP_INCOMPATIBLE; + } + + + if (result == 0) { + /* + The loading succeded - so this is a howling success! We set + the main status to JOB_QUEUE_ALL_OK and inform the queue layer + about the success. In addition we set the simple status + (should be avoided) to JOB_RUN_OK. + */ + run_arg_set_run_status( run_arg , JOB_RUN_OK); + res_log_finfo("[%03d:%04d-%04d] Results loaded successfully.", + iens, run_arg_get_step1(run_arg), run_arg_get_step2(run_arg)); + + } + + return result == 0; +} + + +bool enkf_state_complete_forward_modelOK__(void * arg ) { + callback_arg_type * cb_arg = callback_arg_safe_cast( arg ); + + return enkf_state_complete_forward_modelOK( cb_arg->res_config, + cb_arg->run_arg ); +} + + + +bool enkf_state_complete_forward_model_EXIT_handler__(run_arg_type * run_arg) { + const int iens = run_arg_get_iens( run_arg ); + res_log_ferror("[%03d:%04d-%04d] FAILED COMPLETELY.", + iens, run_arg_get_step1(run_arg), run_arg_get_step2(run_arg)); + + if (run_arg_get_run_status(run_arg) != JOB_LOAD_FAILURE) + run_arg_set_run_status( run_arg , JOB_RUN_FAILURE); + + state_map_type * state_map = enkf_fs_get_state_map(run_arg_get_sim_fs( run_arg )); + state_map_iset(state_map, iens, STATE_LOAD_FAILURE); + return false; +} + + +static bool enkf_state_complete_forward_model_EXIT_handler(void * arg) { + callback_arg_type * callback_arg = callback_arg_safe_cast( arg ); + run_arg_type * run_arg = callback_arg->run_arg; + return enkf_state_complete_forward_model_EXIT_handler__( run_arg); +} + + +bool enkf_state_complete_forward_modelEXIT__(void * arg ) { + return enkf_state_complete_forward_model_EXIT_handler(arg); +} + + +/** + This function is called when: + + 1. The external queue system has said that everything is OK; BUT + the ert layer failed to load all the data. + + 2. The external queue system has seen the job fail. + + The parameter and state variables will be resampled before + retrying. And all random elements in templates+++ will be + resampled. +*/ + + + +static void enkf_state_internal_retry(const res_config_type * res_config, + run_arg_type * run_arg, + rng_type * rng) { + ensemble_config_type * ens_config = res_config_get_ensemble_config( res_config ); + const int iens = run_arg_get_iens( run_arg ); + + res_log_ferror("[%03d:%04d - %04d] Forward model failed.", + iens, run_arg_get_step1(run_arg), run_arg_get_step2(run_arg)); + if (run_arg_can_retry( run_arg ) ) { + res_log_ferror("[%03d] Resampling and resubmitting realization.", iens); + + stringlist_type * init_keys = ensemble_config_alloc_keylist_from_var_type( ens_config , PARAMETER ); + for (int ikey=0; ikey < stringlist_get_size( init_keys ); ikey++) { + const enkf_config_node_type * config_node = ensemble_config_get_node( ens_config , stringlist_iget( init_keys , ikey) ); + enkf_node_type * node = enkf_node_alloc( config_node ); + if (enkf_node_initialize( node , iens , rng )) { + node_id_type node_id = { .report_step = 0, .iens = iens }; + enkf_node_store(node, run_arg_get_sim_fs( run_arg ), node_id); + } + enkf_node_free( node ); + } + stringlist_free( init_keys ); + + /* Possibly clear the directory and do a FULL rewrite of ALL the necessary files. */ + enkf_state_init_eclipse(res_config, run_arg); + run_arg_increase_submit_count( run_arg ); + } +} + + +bool enkf_state_complete_forward_modelRETRY__(void * arg ) { + callback_arg_type * cb_arg = callback_arg_safe_cast( arg ); + + if (run_arg_can_retry(cb_arg->run_arg)) { + enkf_state_internal_retry( cb_arg->res_config, + cb_arg->run_arg, + cb_arg->rng); + return true; + } + + return false; +} + + + +/*****************************************************************/ + + + + + +const ensemble_config_type * enkf_state_get_ensemble_config( const enkf_state_type * enkf_state ) { + return enkf_state->ensemble_config; +} + + +/** + This function writes out all the files needed by an ECLIPSE simulation, this + includes the restart file, and the various INCLUDE files corresponding to + parameters estimated by EnKF. + + The writing of restart file is delegated to enkf_state_write_restart_file(). +*/ + +// TODO: enkf_fs_type could be fetched from run_arg +void enkf_state_ecl_write(const ensemble_config_type * ens_config, const model_config_type * model_config, const run_arg_type * run_arg , enkf_fs_type * fs) { + /** + This iteration manipulates the hash (thorugh the enkf_state_del_node() call) + + ----------------------------------------------------------------------------------------- + T H I S W I L L D E A D L O C K I F T H E H A S H _ I T E R A P I I S U S E D. + ----------------------------------------------------------------------------------------- + */ + int iens = run_arg_get_iens( run_arg ); + const char * base_name = model_config_get_gen_kw_export_name(model_config); + value_export_type * export_value = value_export_alloc( run_arg_get_runpath( run_arg ), base_name ); + + stringlist_type * key_list = ensemble_config_alloc_keylist_from_var_type( ens_config , PARAMETER + EXT_PARAMETER); + for (int ikey = 0; ikey < stringlist_get_size( key_list ); ikey++) { + enkf_config_node_type * config_node = ensemble_config_get_node( ens_config, stringlist_iget( key_list , ikey)); + enkf_node_type * enkf_node = enkf_node_alloc( config_node ); + bool forward_init = enkf_node_use_forward_init( enkf_node ); + node_id_type node_id = {.report_step = run_arg_get_step1(run_arg), + .iens = iens }; + + if ((run_arg_get_step1(run_arg) == 0) && (forward_init)) { + + if (enkf_node_has_data( enkf_node , fs , node_id)) + enkf_node_load(enkf_node, fs, node_id); + else + continue; + } else + enkf_node_load(enkf_node, fs, node_id); + + enkf_node_ecl_write(enkf_node , run_arg_get_runpath( run_arg ) , export_value , run_arg_get_step1(run_arg)); + enkf_node_free(enkf_node); + } + value_export( export_value ); + + value_export_free( export_value ); + stringlist_free( key_list ); +} + + +#include "enkf_state_nodes.cpp" diff --git a/libres/lib/enkf/enkf_state_nodes.cpp b/libres/lib/enkf/enkf_state_nodes.cpp new file mode 100644 index 00000000000..d1f6b39ac88 --- /dev/null +++ b/libres/lib/enkf/enkf_state_nodes.cpp @@ -0,0 +1,48 @@ +static bool enkf_state_has_node(const enkf_state_type * enkf_state , const char * node_key) { + bool has_node = hash_has_key(enkf_state->node_hash , node_key); + return has_node; +} + + +static void enkf_state_del_node(enkf_state_type * enkf_state , const char * node_key) { + if (hash_has_key(enkf_state->node_hash , node_key)) + hash_del(enkf_state->node_hash , node_key); + else + fprintf(stderr,"%s: tried to remove node:%s which is not in state - internal error?? \n",__func__ , node_key); +} + + + +/** + The enkf_state inserts a reference to the node object. The + enkf_state object takes ownership of the node object, i.e. it will + free it when the game is over. + + Observe that if the node already exists the existing node will be + removed (freed and so on ... ) from the enkf_state object before + adding the new; this was previously considered a run-time error. +*/ + + +void enkf_state_add_node(enkf_state_type * enkf_state , const char * node_key , const enkf_config_node_type * config) { + if (enkf_state_has_node(enkf_state , node_key)) + enkf_state_del_node( enkf_state , node_key ); /* Deleting the old instance (if we had one). */ + { + enkf_node_type *enkf_node; + if (enkf_config_node_get_impl_type( config ) == CONTAINER) + enkf_node = enkf_node_alloc_shared_container( config , enkf_state->node_hash ); + else + enkf_node = enkf_node_alloc( config ); + + hash_insert_hash_owned_ref(enkf_state->node_hash , node_key , enkf_node, enkf_node_free__); + } +} + + + + + + + + + diff --git a/libres/lib/enkf/enkf_types.cpp b/libres/lib/enkf/enkf_types.cpp new file mode 100644 index 00000000000..9fa9c6eb97d --- /dev/null +++ b/libres/lib/enkf/enkf_types.cpp @@ -0,0 +1,47 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'enkf_types.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 + + +/*****************************************************************/ + + +const char * enkf_types_get_impl_name(ert_impl_type impl_type) { + switch(impl_type) { + case(INVALID): + return "INVALID"; + case FIELD: + return "FIELD"; + case GEN_KW: + return "GEN_KW"; + case SUMMARY: + return "SUMMARY"; + case GEN_DATA: + return "GEN_DATA"; + case EXT_PARAM: + return "EXT_PARAM"; + default: + util_abort("%s: internal error - unrecognized implementation type: %d - aborting \n",__func__ , impl_type); + return NULL; + } +} diff --git a/libres/lib/enkf/enkf_util.cpp b/libres/lib/enkf/enkf_util.cpp new file mode 100644 index 00000000000..6ae3de65b83 --- /dev/null +++ b/libres/lib/enkf/enkf_util.cpp @@ -0,0 +1,84 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'enkf_util.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 + +class generator { + rng_type *rng; + + public: + generator(rng_type *rng): rng(rng) {} + + using value_type = unsigned int; + static constexpr value_type min() {return 0;} + static constexpr value_type max() {return UINT32_MAX;} + + value_type operator()() {return rng_forward(rng); } +}; + + +double enkf_util_rand_normal(double mean , double std , rng_type * rng) { + generator gen(rng); + std::normal_distribution normdist{mean, std}; + return normdist(gen); +} + +/*****************************************************************/ + +#define TRUNCATE(type , void_data , size , min_ptr , max_ptr) \ +{ \ + type * data = (type *) void_data; \ + type min_value = *((type *) min_ptr); \ + type max_value = *((type *) max_ptr); \ + int i; \ + for (i=0; i < size; i++) { \ + if (data[i] < min_value) \ + data[i] = min_value; \ + else if (data[i] > max_value) \ + data[i] = max_value; \ + } \ +} + +void enkf_util_truncate(void * void_data , int size , ecl_data_type data_type , void * min_ptr , void *max_ptr) { + if (ecl_type_is_double(data_type)) + TRUNCATE(double , void_data , size , min_ptr , max_ptr) + else if (ecl_type_is_float(data_type)) + TRUNCATE(float , void_data , size , min_ptr , max_ptr) + else if (ecl_type_is_int(data_type)) + TRUNCATE(int , void_data , size , min_ptr , max_ptr) + else + util_abort("%s: unrecognized type - aborting \n",__func__); +} +#undef TRUNCATE + + +void enkf_util_assert_buffer_type(buffer_type * buffer, ert_impl_type target_type) { + ert_impl_type file_type = INVALID; + file_type = (ert_impl_type) buffer_fread_int(buffer); + if (file_type != target_type) + util_abort("%s: wrong target type in file (expected:%d got:%d) - aborting \n", + __func__, target_type, file_type); + +} diff --git a/libres/lib/enkf/ensemble_config.cpp b/libres/lib/enkf/ensemble_config.cpp new file mode 100644 index 00000000000..57517eadde9 --- /dev/null +++ b/libres/lib/enkf/ensemble_config.cpp @@ -0,0 +1,924 @@ +/* + copyright (c) 2011 equinor asa, norway. + + the file 'ensemble_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 /* must have rw locking on the config_nodes ... */ + +#include +#include +#include + + +#include +#include + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + + +#define ENSEMBLE_CONFIG_TYPE_ID 8825306 + +namespace { + + std::unordered_map create_opt_map(const config_content_node_type * node, int offset) { + std::unordered_map options; + for (int i = offset; i < config_content_node_get_size( node ); i++) { + const char * key_value = config_content_node_iget( node , i); + char * value = NULL; + char * key = NULL; + + util_binary_split_string( key_value , ":" , true , &key , &value); + if (value) + options[key] = value; + + free(key); + free(value); + } + + return options; + } + + + const char * get_string(const std::unordered_map& opt_map, const char* key) { + const auto& iter = opt_map.find(key); + if (iter == opt_map.end()) + return NULL; + + return iter->second.c_str(); + } + +} + + +struct ensemble_config_struct { + UTIL_TYPE_ID_DECLARATION; + pthread_mutex_t mutex; + char * gen_kw_format_string; /* format string used when creating gen_kw search/replace strings. */ + std::map config_nodes; /* a hash of enkf_config_node instances - which again conatin pointers to e.g. field_config objects. */ + field_trans_table_type * field_trans_table; /* a table of the transformations which are available to apply on fields. */ + bool have_forward_init; + summary_key_matcher_type * summary_key_matcher; +}; + + +UTIL_IS_INSTANCE_FUNCTION( ensemble_config , ENSEMBLE_CONFIG_TYPE_ID ) +UTIL_SAFE_CAST_FUNCTION( ensemble_config , ENSEMBLE_CONFIG_TYPE_ID ) + + +/** + setting the format string used to 'mangle' the string in the gen_kw + template files. consider the following example: + + parameter file + -------------- + multpv logunif 0.0001 0.10 + + + template file + ------------- + box + 1 10 1 10 1 5 / + + multpv 500*__multpv__ + + here the parameter file defines a parameter named 'multpv', and the + template file uses the marker string '__multpv__' which should be + replaced with a numerical value. for the current example the + gen_kw_format_string should have the value '__%s__'. + + there are no rules for the format string, but it _must_ contain a + '%s' placeholder which will be replaced with the parameter name + (this is not checked for). the function call creating a search + string from a parameter name is: + + tagged_string = util_alloc_sprintf( gen_kw_format_string , parameter_name ); + +*/ + +void ensemble_config_set_gen_kw_format( ensemble_config_type * ensemble_config , const char * gen_kw_format_string) { + if (!util_string_equal( gen_kw_format_string , ensemble_config->gen_kw_format_string)) { + stringlist_type * gen_kw_keys = ensemble_config_alloc_keylist_from_impl_type( ensemble_config , GEN_KW ); + int i; + ensemble_config->gen_kw_format_string = util_realloc_string_copy( ensemble_config->gen_kw_format_string , gen_kw_format_string ); + for (i=0; i < stringlist_get_size( gen_kw_keys ); i++) { + enkf_config_node_type * config_node = ensemble_config_get_node( ensemble_config , stringlist_iget( gen_kw_keys , i )); + gen_kw_config_update_tag_format( (gen_kw_config_type * ) enkf_config_node_get_ref( config_node ) , gen_kw_format_string ); // CXX_CAST_ERROR + } + stringlist_free( gen_kw_keys ); + } +} + + +field_trans_table_type * ensemble_config_get_trans_table( const ensemble_config_type * ensemble_config ) { + return ensemble_config->field_trans_table; +} + + +static ensemble_config_type * ensemble_config_alloc_empty(void) { + ensemble_config_type * ensemble_config = new ensemble_config_type(); + + UTIL_TYPE_ID_INIT( ensemble_config , ENSEMBLE_CONFIG_TYPE_ID ); + ensemble_config->field_trans_table = field_trans_table_alloc(); + ensemble_config->gen_kw_format_string = util_alloc_string_copy( DEFAULT_GEN_KW_TAG_FORMAT ); + ensemble_config->have_forward_init = false; + ensemble_config->summary_key_matcher = summary_key_matcher_alloc(); + pthread_mutex_init( &ensemble_config->mutex , NULL); + + return ensemble_config; +} + +ensemble_config_type * ensemble_config_alloc_full(const char * gen_kw_format_string) +{ + ensemble_config_type * ensemble_config = ensemble_config_alloc_empty(); + ensemble_config->field_trans_table = field_trans_table_alloc(); + ensemble_config_set_gen_kw_format( ensemble_config , gen_kw_format_string); + ensemble_config->summary_key_matcher = summary_key_matcher_alloc(); + pthread_mutex_init( &ensemble_config->mutex , NULL); + return ensemble_config; +} + +ensemble_config_type * ensemble_config_alloc_load(const char * user_config_file, ecl_grid_type * grid, const ecl_sum_type * refcase) { + config_parser_type * config_parser = config_alloc(); + config_content_type * config_content = NULL; + if(user_config_file) + config_content = model_config_alloc_content(user_config_file, config_parser); + + ensemble_config_type * ensemble_config = ensemble_config_alloc(config_content, grid, refcase); + + config_content_free(config_content); + config_free(config_parser); + + return ensemble_config; +} + +// forward declaration -> implementation further down +static void ensemble_config_init(ensemble_config_type *ensemble_config, + const config_content_type *config, ecl_grid_type *grid, + const ecl_sum_type *refcase); + +ensemble_config_type * ensemble_config_alloc( + const config_content_type * config_content, + ecl_grid_type * grid, + const ecl_sum_type * refcase) { + + ensemble_config_type * ensemble_config = ensemble_config_alloc_empty(); + + if(config_content) + ensemble_config_init(ensemble_config, config_content, grid, refcase); + + return ensemble_config; +} + +void ensemble_config_free(ensemble_config_type * ensemble_config) { + field_trans_table_free( ensemble_config->field_trans_table ); + summary_key_matcher_free(ensemble_config->summary_key_matcher); + free( ensemble_config->gen_kw_format_string ); + + for(auto& config_pair : ensemble_config->config_nodes) + enkf_config_node_free( config_pair.second ); + + delete ensemble_config; +} + + +bool ensemble_config_has_key(const ensemble_config_type * ensemble_config , const char * key) { + return ensemble_config->config_nodes.count(key) > 0; +} + + + +enkf_config_node_type * ensemble_config_get_node(const ensemble_config_type * ensemble_config, const char * key) { + const auto node_it = ensemble_config->config_nodes.find(key); + if (node_it != ensemble_config->config_nodes.end()) { + enkf_config_node_type * node = node_it->second; + return node; + } else { + util_abort("%s: ens node:\"%s\" does not exist \n",__func__ , key); + return NULL; /* compiler shut up */ + } +} + +enkf_config_node_type * ensemble_config_get_or_create_summary_node(ensemble_config_type * ensemble_config, const char * key) { + if (ensemble_config->config_nodes.count(key) == 0) + ensemble_config_add_summary(ensemble_config, key, LOAD_FAIL_SILENT); + + return ensemble_config_get_node(ensemble_config, key); +} + +bool ensemble_config_have_forward_init( const ensemble_config_type * ensemble_config ) { + return ensemble_config->have_forward_init; +} + +void ensemble_config_add_node( ensemble_config_type * ensemble_config , enkf_config_node_type * node) { + if (node) { + const char * key = enkf_config_node_get_key( node ); + if (ensemble_config_has_key(ensemble_config , key)) + util_abort("%s: a configuration object:%s has already been added - aborting \n",__func__ , key); + + ensemble_config->config_nodes[key] = node; + ensemble_config->have_forward_init |= enkf_config_node_use_forward_init( node ); + } else + util_abort("%s: internal error - tried to add NULL node to ensemble configuration \n",__func__); +} + + + + + +void ensemble_config_add_obs_key(ensemble_config_type * ensemble_config , const char * key, const char * obs_key) { + enkf_config_node_type * node = ensemble_config->config_nodes.at(key); + enkf_config_node_add_obs_key(node , obs_key); +} + + +void ensemble_config_clear_obs_keys(ensemble_config_type * ensemble_config) { + for (auto& config_pair : ensemble_config->config_nodes) { + enkf_config_node_type * config_node = config_pair.second; + enkf_config_node_clear_obs_keys( config_node ); + } +} + + + + + + +void ensemble_config_add_config_items(config_parser_type * config) { + config_schema_item_type * item; + + /** + the two fault types are just added to the config object only to + be able to print suitable messages before exiting. + */ + + item = config_add_schema_item(config , "HAVANA_FAULT" , false ); + config_schema_item_set_argc_minmax(item , 2 , 2); + + item = config_add_schema_item(config , "MULTFLT" , false ); + config_schema_item_set_argc_minmax(item , 3 , 3 ); + config_schema_item_iset_type( item , 2 , CONFIG_EXISTING_PATH ); + + + /*****************************************************************/ + + + item = config_add_schema_item(config , GEN_KW_KEY , false ); + config_schema_item_set_argc_minmax(item , 4 , 6); + config_schema_item_iset_type( item , 1 , CONFIG_EXISTING_PATH ); + config_schema_item_iset_type( item , 2 , CONFIG_PATH ); + config_schema_item_iset_type( item , 3 , CONFIG_EXISTING_PATH ); + + + + item = config_add_key_value( config , GEN_KW_TAG_FORMAT_KEY , false , CONFIG_STRING); + item = config_add_schema_item(config , SCHEDULE_PREDICTION_FILE_KEY , false ); + /* scedhule_prediction_file filename */ + config_schema_item_set_argc_minmax(item , 1 , 3 ); + config_schema_item_iset_type( item , 0 , CONFIG_EXISTING_PATH ); + + + + enkf_config_node_add_GEN_PARAM_config_schema( config ); + enkf_config_node_add_GEN_DATA_config_schema( config ); + + item = config_add_schema_item(config , SUMMARY_KEY , false ); /* can have several summary keys on each line. */ + config_schema_item_set_argc_minmax(item , 1 , CONFIG_DEFAULT_ARG_MAX); + + item = config_add_schema_item(config , CONTAINER_KEY , false ); /* can have several summary keys on each line. */ + config_schema_item_set_argc_minmax(item , 2 , CONFIG_DEFAULT_ARG_MAX); + + item = config_add_schema_item( config , SURFACE_KEY , false ); + config_schema_item_set_argc_minmax(item , 4 , 5 ); + + /* + the way config info is entered for fields is unfortunate because + it is difficult/impossible to let the config system handle run + time validation of the input. + */ + + item = config_add_schema_item(config , FIELD_KEY , false ); + config_schema_item_set_argc_minmax(item , 2 , CONFIG_DEFAULT_ARG_MAX); + config_schema_item_add_required_children(item , GRID_KEY); /* if you are using a field - you must have a grid. */ +} + + + +/* + The var type parameter is determined by inspecting the + combination of input parameters. It is possible to specify an + invalid input combination; that should be identified with a call + to gen_data_config_is_valid() in the calling scope. + + + PARAMETER: init_file_fmt != NULL + enkf_outfile_fmt != NULL + enkf_infile_fmt == NULL + + DYNAMIC_STATE: init_file_fmt != NULL + enkf_outfile_fmt != NULL + enkf_infile_fmt != NULL + + DYNAMIC_RESULT: init_file_fmt == NULL + enkf_outfile_fmt == NULL + enkf_infile_fmt != NULL + +*/ + +static void ensemble_config_init_GEN_DATA( ensemble_config_type * ensemble_config , const config_content_type * config) { + if (config_content_has_item(config , GEN_DATA_KEY)) { + const config_content_item_type * item = config_content_get_item( config , GEN_DATA_KEY ); + int i; + for (i=0; i < config_content_item_get_size(item); i++) { + const config_content_node_type * node = config_content_item_iget_node( item , i ); + enkf_config_node_type * config_node = enkf_config_node_alloc_GEN_DATA_from_config( node ); + if (config_node) + ensemble_config_add_node( ensemble_config , config_node ); + + } + } +} + + +void ensemble_config_init_GEN_PARAM( ensemble_config_type * ensemble_config , const config_content_type * config) { + /* gen_param - should be unified with the gen_data*/ + if (config_content_has_item(config , GEN_PARAM_KEY)) { + const config_content_item_type * item = config_content_get_item( config , GEN_PARAM_KEY ); + for (int i=0; i < config_content_item_get_size(item); i++) { + const config_content_node_type * node = config_content_item_iget_node( item , i ); + enkf_config_node_type * config_node = enkf_config_node_alloc_GEN_PARAM_from_config( node ); + if (config_node) + ensemble_config_add_node( ensemble_config , config_node ); + + } + } +} + + +static void ensemble_config_init_GEN_KW(ensemble_config_type * ensemble_config, const config_content_type * config) { + if (config_content_has_item(config , GEN_KW_KEY)) { + const config_content_item_type * gen_kw_item = config_content_get_item( config , GEN_KW_KEY ); + int i; + for (i=0; i < config_content_item_get_size( gen_kw_item ); i++) { + config_content_node_type * node = config_content_item_iget_node( gen_kw_item , i ); + + const char * key = config_content_node_iget(node, 0); + const char * template_file = config_content_node_iget_as_abspath(node, 1); + const char * enkf_outfile = config_content_node_iget(node, 2); + const char * parameter_file = config_content_node_iget_as_abspath(node, 3); + + auto opt_map = create_opt_map(node, 4); + { + const char * forward_string = get_string(opt_map, FORWARD_INIT_KEY); + enkf_config_node_type * config_node; + bool forward_init = false; + + if (forward_string) { + if (!util_sscanf_bool( forward_string , &forward_init)) + fprintf(stderr,"** Warning: parsing %s as bool failed - using FALSE \n",forward_string); + } + + config_node = ensemble_config_add_gen_kw( ensemble_config , key , forward_init); + enkf_config_node_update_gen_kw( config_node , + enkf_outfile , + template_file , + parameter_file , + get_string( opt_map , MIN_STD_KEY ) , + get_string( opt_map , INIT_FILES_KEY)); + } + } + } +} + +static void ensemble_config_init_SURFACE( ensemble_config_type * ensemble_config , const config_content_type * config ) { + if (config_content_has_item(config , SURFACE_KEY)) { + const config_content_item_type * item = config_content_get_item( config , SURFACE_KEY ); + int i; + for (i=0; i < config_content_item_get_size( item ); i++) { + const config_content_node_type * node = config_content_item_iget_node( item , i ); + const char * key = config_content_node_iget( node , 0 ); + { + auto opt_map = create_opt_map(node, 1); + { + const char * init_file_fmt = get_string( opt_map , INIT_FILES_KEY ); + const char * output_file = get_string( opt_map , OUTPUT_FILE_KEY); + const char * base_surface = get_string( opt_map , BASE_SURFACE_KEY); + const char * min_std_file = get_string( opt_map , MIN_STD_KEY); + const char * forward_string = get_string( opt_map , FORWARD_INIT_KEY ); + bool forward_init = false; + + if (forward_string) { + if (!util_sscanf_bool( forward_string , &forward_init)) + fprintf(stderr,"** Warning: parsing %s as bool failed - using FALSE \n",forward_string); + } + + if ((init_file_fmt == NULL) || (output_file == NULL) || (base_surface == NULL)) { + fprintf(stderr,"** error: when entering a surface you must provide arguments:\n"); + fprintf(stderr,"** %s:/path/to/input/files%%d \n",INIT_FILES_KEY); + fprintf(stderr,"** %s:name_of_output_file\n", OUTPUT_FILE_KEY); + fprintf(stderr,"** %s:base_surface_file\n",BASE_SURFACE_KEY); + exit(1); + } + + { + enkf_config_node_type * config_node = ensemble_config_add_surface( ensemble_config , key , forward_init); + enkf_config_node_update_surface( config_node , base_surface , init_file_fmt , output_file , min_std_file ); + } + } + } + } + } +} + + +static void ensemble_config_init_SUMMARY( ensemble_config_type * ensemble_config , const config_content_type * config , const ecl_sum_type * refcase) { + if (config_content_has_item(config , SUMMARY_KEY)) { + const config_content_item_type * item = config_content_get_item( config , SUMMARY_KEY ); + int i; + for (i=0; i < config_content_item_get_size( item ); i++) { + const config_content_node_type * node = config_content_item_iget_node( item , i ); + int j; + for (j= 0; j < config_content_node_get_size( node ); j++) { + const char * key = config_content_node_iget( node , j ); + summary_key_matcher_add_summary_key(ensemble_config->summary_key_matcher, key); + ensemble_config_init_SUMMARY_full(ensemble_config, key, refcase); + } + } + } +} + +void ensemble_config_init_SUMMARY_full( ensemble_config_type * ensemble_config , const char * key, const ecl_sum_type * refcase) { + summary_key_matcher_add_summary_key(ensemble_config->summary_key_matcher, key); + if (util_string_has_wildcard( key )) { + if (refcase != NULL) { + stringlist_type * keys = stringlist_alloc_new ( ); + ecl_sum_select_matching_general_var_list(refcase , key , keys ); /* expanding the wildcard notation with help of the refcase. */ + int k; + for (k=0; k < stringlist_get_size( keys ); k++) + ensemble_config_add_summary(ensemble_config , stringlist_iget(keys , k) , LOAD_FAIL_SILENT ); + + stringlist_free( keys ); + } + } else { + ensemble_config_add_summary(ensemble_config , key , LOAD_FAIL_SILENT); + } +} + +static void ensemble_config_init_FIELD( ensemble_config_type * ensemble_config , const config_content_type * config , ecl_grid_type * grid) { + if (config_content_has_item(config , FIELD_KEY)) { + const config_content_item_type * item = config_content_get_item( config , FIELD_KEY ); + int i; + for (i=0; i < config_content_item_get_size( item ); i++) { + const config_content_node_type * node = config_content_item_iget_node( item , i ); + const char * key = config_content_node_iget( node , 0 ); + const char * var_type_string = config_content_node_iget( node , 1 ); + enkf_config_node_type * config_node; + + { + int truncation = TRUNCATE_NONE; + double value_min = -1; + double value_max = -1; + + auto opt_map = create_opt_map(node, 2); + if (opt_map.count(MIN_KEY) > 0) { + truncation |= TRUNCATE_MIN; + value_min = atof(get_string( opt_map, MIN_KEY)); + } + + if (opt_map.count(MAX_KEY) > 0) { + truncation |= TRUNCATE_MAX; + value_max = atof(get_string( opt_map, MAX_KEY)); + } + + + if (strcmp(var_type_string , PARAMETER_KEY) == 0) { + const char * ecl_file = config_content_node_iget( node , 2 ); + const char * init_file_fmt = get_string( opt_map , INIT_FILES_KEY ); + const char * init_transform = get_string( opt_map , INIT_TRANSFORM_KEY ); + const char * output_transform = get_string( opt_map , OUTPUT_TRANSFORM_KEY ); + const char * min_std_file = get_string( opt_map , MIN_STD_KEY ); + const char * forward_string = get_string( opt_map , FORWARD_INIT_KEY ); + bool forward_init = false; + + if (forward_string) { + if (!util_sscanf_bool( forward_string , &forward_init)) + fprintf(stderr,"** Warning: parsing %s as bool failed - using FALSE \n",forward_string); + } + config_node = ensemble_config_add_field( ensemble_config , key , grid , forward_init); + enkf_config_node_update_parameter_field( config_node, + ecl_file , + init_file_fmt , + min_std_file , + truncation , + value_min , + value_max , + init_transform , + output_transform ); + } else if (strcmp(var_type_string , GENERAL_KEY) == 0) { + /* General - not really interesting .. */ + const char * ecl_file = config_content_node_iget( node , 2 ); + const char * enkf_infile = config_content_node_iget( node , 3 ); + const char * init_file_fmt = get_string( opt_map , INIT_FILES_KEY ); + const char * init_transform = get_string( opt_map , INIT_TRANSFORM_KEY ); + const char * output_transform = get_string( opt_map , OUTPUT_TRANSFORM_KEY ); + const char * input_transform = get_string( opt_map , INPUT_TRANSFORM_KEY ); + const char * min_std_file = get_string( opt_map , MIN_STD_KEY ); + const char * forward_string = get_string( opt_map , FORWARD_INIT_KEY ); + bool forward_init = false; + + if (forward_string) { + if (!util_sscanf_bool( forward_string , &forward_init)) + fprintf(stderr,"** Warning: parsing %s as bool failed - using FALSE \n",forward_string); + } + + config_node = ensemble_config_add_field( ensemble_config , key , grid , forward_init); + enkf_config_node_update_general_field( config_node, + ecl_file , + enkf_infile , + init_file_fmt , + min_std_file , + truncation , value_min , value_max , + init_transform , + input_transform , + output_transform); + + + } else + util_abort("%s: field type: %s is not recognized\n",__func__ , var_type_string); + } + } + } +} + +static void ensemble_config_init_PRED(ensemble_config_type * ensemble_config, const config_content_type * content) { + if (!config_content_has_item(content, SCHEDULE_PREDICTION_FILE_KEY)) + return; + + const config_content_item_type * pred_item = config_content_get_item( + content, + SCHEDULE_PREDICTION_FILE_KEY + ); + + config_content_node_type * pred_node = config_content_item_get_last_node(pred_item); + const char * template_file = config_content_node_iget_as_path(pred_node, 0); + if(!template_file) + return; + + hash_type * opt_hash = hash_alloc(); + config_content_node_init_opt_hash(pred_node, opt_hash, 1); + const char * parameters = (const char *) hash_safe_get(opt_hash, PARAMETER_KEY); + const char * min_std = (const char *) hash_safe_get(opt_hash, MIN_STD_KEY); + const char * init_files = (const char *) hash_safe_get(opt_hash, INIT_FILES_KEY); + hash_free(opt_hash); + + char * base; + char * ext; + util_alloc_file_components(template_file, NULL, &base, &ext); + char * target_file = util_alloc_filename(NULL , base, ext); + free(base); + free(ext); + + enkf_config_node_type * config_node = ensemble_config_add_gen_kw(ensemble_config, PRED_KEY, false); + enkf_config_node_update_gen_kw(config_node, target_file, template_file, parameters, min_std, init_files); + + free(target_file); +} + + +/** + observe that if the user has not given a refcase with the refcase + key the refcase pointer will be NULL. in that case it will be + impossible to use wildcards when expanding summary variables. +*/ + +static void ensemble_config_init(ensemble_config_type * ensemble_config , const config_content_type * config , ecl_grid_type * grid, const ecl_sum_type * refcase) { + + if (config_content_has_item( config , GEN_KW_TAG_FORMAT_KEY)) { + ensemble_config_set_gen_kw_format( ensemble_config , config_content_iget( config , GEN_KW_TAG_FORMAT_KEY , 0 , 0 )); + } + + ensemble_config_init_GEN_PARAM(ensemble_config, config); + ensemble_config_init_GEN_DATA(ensemble_config, config); + ensemble_config_init_GEN_KW(ensemble_config, config); + ensemble_config_init_SURFACE(ensemble_config, config); + ensemble_config_init_SUMMARY(ensemble_config, config, refcase); + ensemble_config_init_FIELD(ensemble_config, config, grid); + ensemble_config_init_PRED(ensemble_config, config); + + /* Containers - this must come last, to ensure that the other nodes have been added. */ + { + for (int i=0; i < config_content_get_occurences(config , CONTAINER_KEY ); i++) { + const stringlist_type * container_kw_list = config_content_iget_stringlist_ref(config , CONTAINER_KEY , i); + const char * container_key = stringlist_iget( container_kw_list , 0 ); + enkf_config_node_type * container_node = ensemble_config_add_container( ensemble_config , container_key ); + + for (int j= 1; j < stringlist_get_size( container_kw_list ); j++) { + const char * child_key = stringlist_iget( container_kw_list , j); + enkf_config_node_update_container( container_node , ensemble_config_get_node( ensemble_config , child_key )); + } + } + } + + /*****************************************************************/ +} + +/** + this function takes a string like this: "pressure:1,4,7" - it + splits the string on ":" and tries to lookup a config object with + that key. for the general string a:b:c:d it will try consecutively + the keys: a, a:b, a:b:c, a:b:c:d. if a config object is found it is + returned, otherwise NULL is returned. + + the last argument is the pointer to a string which will be updated + with the node-spesific part of the full key. so for instance with + the example "pressure:1,4,7", the index_key will contain + "1,4,7". if the full full_key is used to find an object index_key + will be NULL, that also applies if no object is found. +*/ + + +const enkf_config_node_type * ensemble_config_user_get_node(const ensemble_config_type * config , const char * full_key, char ** index_key ) { + const enkf_config_node_type * node = NULL; + char ** key_list; + int keys; + int key_length = 1; + int offset; + + *index_key = NULL; + util_split_string(full_key , USER_KEY_JOIN_STRING , &keys , &key_list); + while (node == NULL && key_length <= keys) { + char * current_key = util_alloc_joined_string( (const char **) key_list , key_length , USER_KEY_JOIN_STRING ); + if (ensemble_config_has_key(config , current_key)) + node = ensemble_config_get_node(config , current_key); + else + key_length++; + offset = strlen( current_key ); + free( current_key ); + } + if (node != NULL) { + if (offset < strlen( full_key )) + *index_key = util_alloc_string_copy(&full_key[offset+1]); + } + + util_free_stringlist(key_list , keys); + return node; +} + + + +stringlist_type * ensemble_config_alloc_keylist(const ensemble_config_type * config) { + stringlist_type * s = stringlist_alloc_new(); + for (const auto& config_pair : config->config_nodes) + stringlist_append_copy( s, config_pair.first.c_str()); + return s; +} + + + + +/** + observe that var_type here is an integer - naturally written as a + sum of enkf_var_type values: + + ensemble_config_alloc_keylist_from_var_type( config , parameter + dynamic_state); + +*/ + +stringlist_type * ensemble_config_alloc_keylist_from_var_type(const ensemble_config_type * config , int var_mask) { + stringlist_type * key_list = stringlist_alloc_new(); + + for (const auto& config_pair : config->config_nodes) { + const char * key = config_pair.first.c_str(); + enkf_var_type var_type = enkf_config_node_get_var_type( config_pair.second ); + + if (var_type & var_mask) + stringlist_append_copy( key_list , key ); + } + + return key_list; +} + + + +stringlist_type * ensemble_config_alloc_keylist_from_impl_type(const ensemble_config_type * config , ert_impl_type impl_type) { + stringlist_type * key_list = stringlist_alloc_new(); + + for (const auto& config_pair : config->config_nodes) { + const char * key = config_pair.first.c_str(); + if (impl_type == enkf_config_node_get_impl_type( config_pair.second )) + stringlist_append_copy( key_list , key ); + + } + + return key_list; +} + + +bool ensemble_config_has_impl_type(const ensemble_config_type * config, const ert_impl_type impl_type) { + for (const auto& config_pair : config->config_nodes) { + if (impl_type == enkf_config_node_get_impl_type( config_pair.second )) + return true; + } + + return false; +} + +bool ensemble_config_require_summary(const ensemble_config_type * ens_config) { + return ensemble_config_has_impl_type(ens_config, SUMMARY); +} + + +/** + this function will look up the user_key in the ensemble_config. if + the corresponding config_node can not be found 0 will be returned, + otherwise enkf_config_node functions will be invoked. +*/ + + +int ensemble_config_get_observations( const ensemble_config_type * config , enkf_obs_type * enkf_obs , const char * user_key , int obs_count , time_t * obs_time , double * y , double * std) { + int num_obs = 0; + char * index_key; + const enkf_config_node_type * config_node = ensemble_config_user_get_node( config , user_key , &index_key); + if (config_node != NULL) { + num_obs = enkf_config_node_load_obs( config_node , enkf_obs , index_key , obs_count , obs_time , y , std); + free( index_key ); + } + return num_obs; +} + + +/*****************************************************************/ + + +/* + the ensemble_config_add_xxx() functions below will create a new xxx + instance and add it to the ensemble_config; the return value from + the functions is the newly created config_node instances. + + the newly created enkf_config_node instances are __not__ fully + initialized, and a subsequent call to enkf_config_node_update_xxx() + is essential for proper operation. +*/ + +enkf_config_node_type * ensemble_config_add_field( ensemble_config_type * config , const char * key , ecl_grid_type * ecl_grid , bool forward_init) { + enkf_config_node_type * config_node = enkf_config_node_alloc_field( key , ecl_grid , config->field_trans_table , forward_init); + ensemble_config_add_node( config , config_node ); + return config_node; +} + + +enkf_config_node_type * ensemble_config_add_gen_kw( ensemble_config_type * config , const char * key , bool forward_init) { + enkf_config_node_type * config_node = enkf_config_node_new_gen_kw( key , config->gen_kw_format_string , forward_init); + ensemble_config_add_node( config , config_node ); + return config_node; +} + +/** + this function ensures that object contains a node with 'key' and + type == summary. + + if the @refcase pointer is different from NULL the key will be + validated. keys which do not exist in the refcase will be ignored, + a warning will be printed on stderr and the function will return + NULL. +*/ + +enkf_config_node_type * ensemble_config_add_summary(ensemble_config_type * ensemble_config , const char * key , load_fail_type load_fail) { + enkf_config_node_type * config_node = NULL; + + const auto node_it = ensemble_config->config_nodes.find(key); + if (node_it != ensemble_config->config_nodes.end()) { + config_node = node_it->second; + if (enkf_config_node_get_impl_type( config_node ) != SUMMARY) { + util_abort("%s: ensemble key:%s already exists - but it is not of summary type\n",__func__ , key); + } + + summary_config_type * summary_config = (summary_config_type *)enkf_config_node_get_ref( config_node ); + summary_config_update_load_fail_mode( summary_config , load_fail ); + + } else { + config_node = enkf_config_node_alloc_summary( key , load_fail); + ensemble_config_add_node(ensemble_config , config_node ); + } + + return config_node; +} + +enkf_config_node_type * ensemble_config_add_summary_observation(ensemble_config_type * ensemble_config , const char * key , load_fail_type load_fail) { + enkf_config_node_type * config_node = ensemble_config_add_summary(ensemble_config, key, load_fail); + + summary_key_matcher_add_summary_key(ensemble_config->summary_key_matcher, key); + + return config_node; +} + + +enkf_config_node_type * ensemble_config_add_surface( ensemble_config_type * ensemble_config , const char * key , bool forward_init) { + enkf_config_node_type * config_node = enkf_config_node_new_surface( key , forward_init ); + ensemble_config_add_node( ensemble_config , config_node ); + return config_node; +} + + +/* + If key == NULL the function will create a random key. +*/ +enkf_config_node_type * ensemble_config_add_container( ensemble_config_type * ensemble_config , const char * key) { + char * local_key = (char *) key; + bool random_key = false; + if (key == NULL) { + local_key = (char * ) util_calloc( 11 , sizeof * local_key ); + sprintf(local_key , "%ld" , random() % 10000000 ); + random_key = true; + } + + { + enkf_config_node_type * config_node = enkf_config_node_new_container( local_key ); + ensemble_config_add_node( ensemble_config , config_node ); + if (random_key) + free( local_key ); + return config_node; + } +} + + +const summary_key_matcher_type * ensemble_config_get_summary_key_matcher(const ensemble_config_type * ensemble_config) { + return ensemble_config->summary_key_matcher; +} + +/*****************************************************************/ + +int ensemble_config_get_size(const ensemble_config_type * ensemble_config ) { + return ensemble_config->config_nodes.size(); +} + + +int ensemble_config_forward_init(const ensemble_config_type * ens_config, + const run_arg_type * run_arg) { + + int result = 0; + if (run_arg_get_step1(run_arg) == 0) { + int iens = run_arg_get_iens( run_arg ); + for( auto& config_pair : ens_config->config_nodes) { + enkf_config_node_type * config_node = config_pair.second; + if (enkf_config_node_use_forward_init(config_node)) { + enkf_node_type * node = enkf_node_alloc( config_node ); + enkf_fs_type * sim_fs = run_arg_get_sim_fs( run_arg ); + node_id_type node_id = {.report_step = 0 , + .iens = iens }; + + + /* + Will not reinitialize; i.e. it is essential that the + forward model uses the state given from the stored + instance, and not from the current run of e.g. RMS. + */ + + if (!enkf_node_has_data( node , sim_fs , node_id)) { + if (enkf_node_forward_init(node , run_arg_get_runpath( run_arg ) , iens )) + enkf_node_store( node , sim_fs , node_id ); + else { + char * init_file = enkf_config_node_alloc_initfile( enkf_node_get_config( node ) , run_arg_get_runpath(run_arg) , iens ); + + if (init_file && !util_file_exists( init_file )) + fprintf(stderr,"File not found: %s - failed to initialize node: %s\n", init_file , enkf_node_get_key( node )); + else + fprintf(stderr,"Failed to initialize node: %s\n", enkf_node_get_key( node )); + + free( init_file ); + result |= LOAD_FAILURE; + } + } + enkf_node_free( node ); + } + } + } + return result; +} diff --git a/libres/lib/enkf/ert_run_context.cpp b/libres/lib/enkf/ert_run_context.cpp new file mode 100644 index 00000000000..37a042bb4bf --- /dev/null +++ b/libres/lib/enkf/ert_run_context.cpp @@ -0,0 +1,425 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'ert_run_context.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 + + +#define ERT_RUN_CONTEXT_TYPE_ID 55534132 + + +struct ert_run_context_struct { + UTIL_TYPE_ID_DECLARATION; + vector_type * run_args; + run_mode_type run_mode; + init_mode_type init_mode; + int iter; + int step1; + int step2; + int load_start; + int_vector_type * iens_map; + bool_vector_type * iactive; + enkf_fs_type * sim_fs; + enkf_fs_type * update_target_fs; + char * run_id; +}; + + + +/* + Observe that since this function uses one shared subst_list instance + for all realisations it is __NOT__ possible to get per realisation + substititions in the runpath; i.e. a element in the RUNPATH + will *not* be replaced. +*/ + + +char * ert_run_context_alloc_runpath( int iens , const path_fmt_type * runpath_fmt , const subst_list_type * subst_list , int iter) { + char * runpath; + { + char * first_pass = path_fmt_alloc_path(runpath_fmt , false , iens, iter); /* 1: Replace first %d with iens, if a second %d replace with iter */ + + if (subst_list) + runpath = subst_list_alloc_filtered_string( subst_list , first_pass ); /* 2: Filter out various magic strings like and . */ + else + runpath = util_alloc_string_copy( first_pass ); + + free( first_pass ); + } + return runpath; +} + +static char * ert_run_context_alloc_jobname( int iens , const char * jobname_fmt , const subst_list_type * subst_list) { + char * jobname; + { + char * first_pass = util_alloc_sprintf( jobname_fmt, iens ); + + if (subst_list) + jobname = subst_list_alloc_filtered_string( subst_list , first_pass ); /* 2: Filter out various magic strings like and . */ + else + jobname = util_alloc_string_copy( first_pass ); + + free( first_pass ); + } + return jobname; +} + + + +stringlist_type * ert_run_context_alloc_runpath_list(const bool_vector_type * iactive , const path_fmt_type * runpath_fmt , const subst_list_type * subst_list , int iter) { + stringlist_type * runpath_list = stringlist_alloc_new(); + for (int iens = 0; iens < bool_vector_size( iactive ); iens++) { + + if (bool_vector_iget( iactive , iens )) { + char * tmp_runpath = ert_run_context_alloc_runpath(iens, runpath_fmt, subst_list, iter); + stringlist_append_copy( runpath_list , tmp_runpath); + free(tmp_runpath); + } else + stringlist_append_copy( runpath_list , NULL ); + + } + return runpath_list; +} + + +static stringlist_type * ert_run_context_alloc_jobname_list(const bool_vector_type * iactive , const char * jobname_fmt, const subst_list_type * subst_list) { + stringlist_type * jobname_list = stringlist_alloc_new(); + for (int iens = 0; iens < bool_vector_size( iactive ); iens++) { + + if (bool_vector_iget( iactive , iens )) { + char * tmp_jobname = ert_run_context_alloc_jobname(iens, jobname_fmt, subst_list); + stringlist_append_copy( jobname_list, tmp_jobname); + free(tmp_jobname); + } else + stringlist_append_copy( jobname_list , NULL ); + + } + return jobname_list; +} + + + +char * ert_run_context_alloc_run_id( ) { + int year,month,day,hour,min,sec; + time_t now = time( NULL ); + unsigned int random = util_dev_urandom_seed( ); + util_set_datetime_values_utc( now , &sec, &min, &hour, &day, &month, &year); + return util_alloc_sprintf("%d:%d:%4d-%0d-%02d-%02d-%02d-%02d:%ud" , getpid() , getuid(), year , month , day , hour , min , sec, random); +} + +static ert_run_context_type * ert_run_context_alloc__(const bool_vector_type * iactive , run_mode_type run_mode , init_mode_type init_mode, enkf_fs_type * sim_fs , enkf_fs_type * update_target_fs , int iter) { + ert_run_context_type * context = (ert_run_context_type *)util_malloc( sizeof * context ); + UTIL_TYPE_ID_INIT( context , ERT_RUN_CONTEXT_TYPE_ID ); + + if (iactive != NULL) { + context->iactive = bool_vector_alloc_copy( iactive ); + context->iens_map = bool_vector_alloc_active_index_list( iactive , -1 ); + } + else { + context->iactive = NULL; + context->iens_map = NULL; + } + context->run_args = vector_alloc_new(); + context->run_mode = run_mode; + context->init_mode = init_mode; + context->iter = iter; + ert_run_context_set_sim_fs(context, sim_fs); + ert_run_context_set_update_target_fs(context, update_target_fs); + + context->step1 = 0; + context->step2 = 0; + context->run_id = ert_run_context_alloc_run_id( ); + return context; +} + + +ert_run_context_type * ert_run_context_alloc_ENSEMBLE_EXPERIMENT(enkf_fs_type * sim_fs, + bool_vector_type * iactive , + const path_fmt_type * runpath_fmt , + const char * jobname_fmt, + const subst_list_type * subst_list , + int iter) { + + ert_run_context_type * context = ert_run_context_alloc__( iactive , ENSEMBLE_EXPERIMENT , INIT_CONDITIONAL, sim_fs , NULL , iter); + { + stringlist_type * runpath_list = ert_run_context_alloc_runpath_list( iactive , runpath_fmt , subst_list , iter ); + stringlist_type * jobname_list = ert_run_context_alloc_jobname_list( iactive , jobname_fmt , subst_list ); + for (int iens = 0; iens < bool_vector_size( iactive ); iens++) { + if (bool_vector_iget( iactive , iens )) { + run_arg_type * arg = run_arg_alloc_ENSEMBLE_EXPERIMENT( context->run_id, + sim_fs, + iens, + iter, + stringlist_iget( runpath_list , iens), + stringlist_iget( jobname_list, iens), + subst_list); + vector_append_owned_ref( context->run_args , arg , run_arg_free__); + } else + vector_append_ref( context->run_args, NULL ); + } + stringlist_free( jobname_list ); + stringlist_free( runpath_list ); + } + return context; +} + + +ert_run_context_type * ert_run_context_alloc_INIT_ONLY(enkf_fs_type * sim_fs, + init_mode_type init_mode, + bool_vector_type * iactive , + const path_fmt_type * runpath_fmt , + const subst_list_type * subst_list , + int iter) { + ert_run_context_type * context = ert_run_context_alloc__( iactive , INIT_ONLY , init_mode, sim_fs, NULL , iter); + { + stringlist_type * runpath_list = ert_run_context_alloc_runpath_list( iactive , runpath_fmt , subst_list , iter ); + for (int iens = 0; iens < bool_vector_size( iactive ); iens++) { + if (bool_vector_iget( iactive , iens )) { + run_arg_type * arg = run_arg_alloc_INIT_ONLY(context->run_id, + sim_fs, + iens, + iter, + stringlist_iget(runpath_list, iens), + subst_list); + vector_append_owned_ref( context->run_args , arg , run_arg_free__); + } else + vector_append_ref( context->run_args, NULL ); + } + stringlist_free( runpath_list ); + } + return context; +} + + +ert_run_context_type * ert_run_context_alloc_CASE_INIT(enkf_fs_type * sim_fs, + const bool_vector_type * iactive) { + ert_run_context_type * run_context = ert_run_context_alloc__(iactive, CASE_INIT_ONLY, INIT_FORCE, sim_fs, NULL, 0); + return run_context; +} + + +ert_run_context_type * ert_run_context_alloc_SMOOTHER_RUN(enkf_fs_type * sim_fs , enkf_fs_type * target_update_fs , + bool_vector_type * iactive , + const path_fmt_type * runpath_fmt , + const char * jobname_fmt, + const subst_list_type * subst_list , + int iter) { + + ert_run_context_type * context = ert_run_context_alloc__( iactive , SMOOTHER_RUN , INIT_CONDITIONAL, sim_fs , target_update_fs , iter); + { + stringlist_type * runpath_list = ert_run_context_alloc_runpath_list( iactive , runpath_fmt , subst_list , iter ); + stringlist_type * jobname_list = ert_run_context_alloc_jobname_list( iactive , jobname_fmt , subst_list ); + for (int iens = 0; iens < bool_vector_size( iactive ); iens++) { + if (bool_vector_iget( iactive , iens )) { + run_arg_type * arg = run_arg_alloc_SMOOTHER_RUN( context->run_id, + sim_fs, + target_update_fs, + iens, + iter, + stringlist_iget( runpath_list , iens), + stringlist_iget( jobname_list , iens), + subst_list); + vector_append_owned_ref( context->run_args , arg , run_arg_free__); + } else + vector_append_ref( context->run_args, NULL ); + } + stringlist_free( jobname_list ); + stringlist_free( runpath_list ); + } + return context; +} + +ert_run_context_type * ert_run_context_alloc_SMOOTHER_UPDATE(enkf_fs_type * sim_fs , enkf_fs_type * target_update_fs ) { + return ert_run_context_alloc__( NULL , SMOOTHER_UPDATE , INIT_CONDITIONAL, sim_fs , target_update_fs , 0); +} + +ert_run_context_type * ert_run_context_alloc(run_mode_type run_mode, + init_mode_type init_mode, + enkf_fs_type * sim_fs , + enkf_fs_type * target_update_fs , + bool_vector_type * iactive , + path_fmt_type * runpath_fmt , + const char * jobname_fmt, + subst_list_type * subst_list , + int iter) { + switch (run_mode) { + + case(SMOOTHER_RUN): + return ert_run_context_alloc_SMOOTHER_RUN( sim_fs , target_update_fs, iactive, runpath_fmt , jobname_fmt, subst_list , iter ); + + case(ENSEMBLE_EXPERIMENT): + return ert_run_context_alloc_ENSEMBLE_EXPERIMENT( sim_fs , iactive , runpath_fmt , jobname_fmt, subst_list , iter); + + case(INIT_ONLY): + return ert_run_context_alloc_INIT_ONLY( sim_fs, init_mode , iactive, runpath_fmt , subst_list, iter ); + + case(SMOOTHER_UPDATE): + break; + + case(CASE_INIT_ONLY): + break; + + } + + util_abort("%s: internal error - should never be here \n",__func__); + return NULL; +} + + + + + +UTIL_IS_INSTANCE_FUNCTION( ert_run_context , ERT_RUN_CONTEXT_TYPE_ID ); + + +const char * ert_run_context_get_id( const ert_run_context_type * context ) { + return context->run_id; +} + + +void ert_run_context_free( ert_run_context_type * context ) { + if (context->sim_fs) { + enkf_fs_decrease_run_count(context->sim_fs); + enkf_fs_decref(context->sim_fs); + } + + if (context->update_target_fs) { + enkf_fs_decrease_run_count(context->update_target_fs); + enkf_fs_decref(context->update_target_fs); + } + + vector_free( context->run_args ); + if (context->iactive != NULL) { + int_vector_free( context->iens_map ); + bool_vector_free( context->iactive ); + } + free( context->run_id ); + free( context ); +} + + +int ert_run_context_get_size( const ert_run_context_type * context ) { + return vector_get_size( context->run_args ); +} + + + +run_mode_type ert_run_context_get_mode( const ert_run_context_type * context ) { + return context->run_mode; +} + + + + + +int ert_run_context_get_iter( const ert_run_context_type * context ) { + return context->iter; +} + + +init_mode_type ert_run_context_get_init_mode( const ert_run_context_type * context ) { + return context->init_mode; +} + +int ert_run_context_get_step1( const ert_run_context_type * context ) { + return context->step1; +} + +run_arg_type * ert_run_context_iget_arg( const ert_run_context_type * context , int index) { + return (run_arg_type * ) vector_iget( context->run_args , index ); +} + + +run_arg_type * ert_run_context_iens_get_arg( const ert_run_context_type * context , int iens) { + int index = int_vector_iget( context->iens_map , iens ); + if (index >= 0) + return (run_arg_type * ) vector_iget( context->run_args , index ); + else + return NULL; +} + +enkf_fs_type * ert_run_context_get_sim_fs(const ert_run_context_type * run_context) { + if (run_context->sim_fs) + return run_context->sim_fs; + else { + util_abort("%s: internal error - tried to access run_context->sim_fs when sim_fs == NULL\n",__func__); + return NULL; + } +} + + +enkf_fs_type * ert_run_context_get_update_target_fs(const ert_run_context_type * run_context) { + if (run_context->update_target_fs) + return run_context->update_target_fs; + else { + util_abort("%s: internal error - tried to access run_context->update_target_fs when update_target_fs == NULL\n",__func__); + return NULL; + } +} + +void ert_run_context_set_sim_fs(ert_run_context_type * context, enkf_fs_type * sim_fs) { + if (sim_fs) { + context->sim_fs = sim_fs; + enkf_fs_increase_run_count(sim_fs); + enkf_fs_incref(sim_fs); + } else + context->sim_fs = NULL; +} + +void ert_run_context_set_update_target_fs(ert_run_context_type * context, enkf_fs_type * update_target_fs) { + if (update_target_fs) { + context->update_target_fs = update_target_fs; + enkf_fs_increase_run_count(update_target_fs); + enkf_fs_incref(update_target_fs); + } else + context->update_target_fs = NULL; +} + + + +void ert_run_context_deactivate_realization( ert_run_context_type * context , int iens) { + bool_vector_iset( context->iactive , iens , false ); +} + + +bool ert_run_context_iactive( const ert_run_context_type * context , int iens) { + return bool_vector_safe_iget( context->iactive , iens ); +} + +int ert_run_context_get_active_size(const ert_run_context_type * context){ + return bool_vector_count_equal(context->iactive, true); +} + +bool_vector_type * ert_run_context_alloc_iactive(const ert_run_context_type * context) { + return bool_vector_alloc_copy(context->iactive); +} + +bool_vector_type const * ert_run_context_get_iactive(const ert_run_context_type * context) { + return context->iactive; +} diff --git a/libres/lib/enkf/ert_template.cpp b/libres/lib/enkf/ert_template.cpp new file mode 100644 index 00000000000..4c7fa5e2152 --- /dev/null +++ b/libres/lib/enkf/ert_template.cpp @@ -0,0 +1,229 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'ert_template.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 ERT_TEMPLATE_TYPE_ID 7731963 +#define ERT_TEMPLATES_TYPE_ID 6677330 + +/* Singular - one template. */ +struct ert_template_struct { + UTIL_TYPE_ID_DECLARATION; + template_type * tmpl; + char * target_file; +}; + + + +/* Plural - many templates. */ +struct ert_templates_struct { + UTIL_TYPE_ID_DECLARATION; + subst_list_type * parent_subst; + hash_type * templates; +}; + + + +void ert_template_set_target_file( ert_template_type * ert_template , const char * target_file ) { + ert_template->target_file = util_realloc_string_copy( ert_template->target_file , target_file ); +} + + +void ert_template_set_template_file( ert_template_type * ert_template , const char * template_file ) { + template_set_template_file( ert_template->tmpl , template_file ); +} + + +const char * ert_template_get_template_file( const ert_template_type * ert_template) { + return template_get_template_file( ert_template->tmpl ); +} + +const char * ert_template_get_target_file( const ert_template_type * ert_template) { + return ert_template->target_file; +} + +ert_template_type * ert_template_alloc( const char * template_file , const char * target_file , subst_list_type * parent_subst) { + ert_template_type * ert_template = (ert_template_type *) util_malloc( sizeof * ert_template); + UTIL_TYPE_ID_INIT(ert_template , ERT_TEMPLATE_TYPE_ID); + ert_template->tmpl = template_alloc( template_file , false , parent_subst); /* The templates are instantiated with internalize_template == false; + this means that substitutions are performed on the filename of the + template itself .*/ + + ert_template->target_file = NULL; + ert_template_set_target_file( ert_template , target_file ); + return ert_template; +} + + +void ert_template_free( ert_template_type * ert_template ) { + free( ert_template->target_file ); + template_free( ert_template->tmpl ); + free( ert_template ); +} + + +void ert_template_instantiate( ert_template_type * ert_template , const char * path , const subst_list_type * arg_list) { + char * target_file = util_alloc_filename( path , ert_template->target_file , NULL ); + template_instantiate( ert_template->tmpl , target_file , arg_list , true ); + free( target_file ); +} + + +void ert_template_add_arg( ert_template_type * ert_template , const char * key , const char * value ) { + template_add_arg( ert_template->tmpl , key , value ); +} + +subst_list_type * ert_template_get_arg_list( ert_template_type * ert_template) { + return template_get_args_list( ert_template->tmpl); +} + +void ert_template_set_args_from_string( ert_template_type * ert_template, const char * arg_string ) { + template_clear_args( ert_template->tmpl ); + template_add_args_from_string( ert_template->tmpl , arg_string ); +} + + +UTIL_SAFE_CAST_FUNCTION( ert_template , ERT_TEMPLATE_TYPE_ID ) + +void ert_template_free__(void * arg) { + ert_template_free( ert_template_safe_cast( arg )); +} + + +/*****************************************************************/ + + +ert_templates_type * ert_templates_alloc_default(subst_list_type * parent_subst) { + ert_templates_type * templates = (ert_templates_type *)util_malloc( sizeof * templates ); + UTIL_TYPE_ID_INIT( templates , ERT_TEMPLATES_TYPE_ID ); + templates->templates = hash_alloc(); + templates->parent_subst = parent_subst; + return templates; +} + +ert_templates_type * ert_templates_alloc(subst_list_type * parent_subst, + const config_content_type * config_content) { + + ert_templates_type * templates = ert_templates_alloc_default(parent_subst); + + if(config_content) + ert_templates_init(templates, config_content); + + return templates; +} + +void ert_templates_free( ert_templates_type * ert_templates ) { + hash_free( ert_templates->templates ); + free( ert_templates ); +} + + +void ert_templates_del_template( ert_templates_type * ert_templates , const char * key) { + hash_del( ert_templates->templates , key ); +} + + +ert_template_type * ert_templates_add_template( ert_templates_type * ert_templates , const char * key , const char * template_file , const char * target_file, const char * arg_string) { + ert_template_type * tmpl = ert_template_alloc( template_file , target_file , ert_templates->parent_subst); + ert_template_set_args_from_string( tmpl , arg_string ); /* Arg_string can be NULL */ + + /** + If key == NULL the function will generate a key after the following algorithm: + + 1. It tries with the basename of the template file. + 2. It tries with the basename of the template file, and a counter. + */ + + if (key == NULL) { + char * new_key = NULL; + char * base_name; + int counter = 1; + util_alloc_file_components( template_file , NULL , &base_name , NULL); + do { + if (counter == 1) + new_key = util_realloc_string_copy( new_key , base_name ); + else + new_key = util_realloc_sprintf( new_key , "%s.%d" , base_name , counter ); + counter++; + } while (hash_has_key( ert_templates->templates , new_key)); + hash_insert_hash_owned_ref( ert_templates->templates , new_key , tmpl, ert_template_free__); + free( new_key ); + free( base_name ); + } else + hash_insert_hash_owned_ref( ert_templates->templates , key , tmpl, ert_template_free__); + + return tmpl; +} + + +void ert_templates_instansiate( ert_templates_type * ert_templates , const char * path , const subst_list_type * arg_list) { + hash_iter_type * iter = hash_iter_alloc( ert_templates->templates ); + while (!hash_iter_is_complete( iter )) { + ert_template_type * ert_template = (ert_template_type * ) hash_iter_get_next_value( iter ); + ert_template_instantiate( ert_template , path , arg_list); + } + hash_iter_free( iter ); +} + + + +void ert_templates_clear( ert_templates_type * ert_templates ) { + hash_clear( ert_templates->templates ); +} + +ert_template_type * ert_templates_get_template( ert_templates_type * ert_templates , const char * key) { + return (ert_template_type * ) hash_get( ert_templates->templates , key ); +} + +stringlist_type * ert_templates_alloc_list( ert_templates_type * ert_templates) { + return hash_alloc_stringlist( ert_templates->templates ); +} + + +void ert_templates_init( ert_templates_type * templates , const config_content_type * config ) { + if (config_content_has_item( config , RUN_TEMPLATE_KEY)) { + const config_content_item_type * template_item = config_content_get_item( config , RUN_TEMPLATE_KEY ); + for (int i=0; i < config_content_item_get_size( template_item ); i++) { + config_content_node_type * template_node = config_content_item_iget_node( template_item , i ); + const char * template_file = config_content_node_iget_as_abspath(template_node , 0 ); + const char * target_file = config_content_node_iget( template_node , 1 ); + + ert_template_type * tmpl = ert_templates_add_template( templates , NULL , template_file , target_file , NULL); + + for (int iarg = 2; iarg < config_content_node_get_size( template_node ); iarg++) { + char * key , *value; + const char * key_value = config_content_node_iget( template_node , iarg ); + util_binary_split_string( key_value , "=:" , true , &key , &value); + + if (value != NULL) + ert_template_add_arg( tmpl,key , value ); + else + fprintf(stderr,"** Warning - failed to parse argument:%s as key:value - ignored \n",config_content_iget( config , "RUN_TEMPLATE" , i , iarg )); + + free( key ); + free( value ); + } + } + } +} diff --git a/libres/lib/enkf/ert_test_context.cpp b/libres/lib/enkf/ert_test_context.cpp new file mode 100644 index 00000000000..e373334e2af --- /dev/null +++ b/libres/lib/enkf/ert_test_context.cpp @@ -0,0 +1,183 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'ert_test_context.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 + + +#define ERT_TEST_CONTEXT_TYPE_ID 99671055 +struct ert_test_context_struct { + UTIL_TYPE_ID_DECLARATION; + enkf_main_type * enkf_main; + test_work_area_type * work_area; + res_config_type * res_config; + rng_type * rng; +}; + + +UTIL_IS_INSTANCE_FUNCTION( ert_test_context , ERT_TEST_CONTEXT_TYPE_ID ) + + +static ert_test_context_type * ert_test_context_alloc_internal( test_work_area_type * work_area, res_config_type * res_config, const char * ui_mode) { + ert_test_context_type * test_context = (ert_test_context_type *)util_malloc( sizeof * test_context ); + UTIL_TYPE_ID_INIT( test_context , ERT_TEST_CONTEXT_TYPE_ID ); + + /* + This environment variable is set to ensure that test context will + parse the correct files when loading site config. The ui_mode + string should be "tui" or "gui". + */ + setenv("ERT_UI_MODE" , ui_mode , 1); + test_context->res_config = res_config; + test_context->work_area = work_area; + test_context->enkf_main = enkf_main_alloc(test_context->res_config, true, false); + test_context->rng = rng_alloc( MZRAN , INIT_DEV_URANDOM ); + return test_context; +} + + +ert_test_context_type * ert_test_context_alloc__( const char * test_name , const char * model_config, bool store_area) { + if (!util_file_exists(model_config)) + return NULL; + + test_work_area_type * work_area = test_work_area_alloc__(test_name, store_area); + test_work_area_copy_parent_content(work_area , model_config ); + + char * config_file = util_split_alloc_filename( model_config ); + res_config_type * res_config = res_config_alloc_load(config_file); + free( config_file ); + + return ert_test_context_alloc_internal( work_area, res_config, "tui"); +} + +ert_test_context_type * ert_test_context_alloc( const char * test_name , const char * model_config) { + return ert_test_context_alloc__(test_name, model_config, false); +} + +ert_test_context_type * ert_test_context_alloc_python( test_work_area_type * work_area, res_config_type * res_config) { + return ert_test_context_alloc_internal( work_area, res_config , "gui"); +} + + + +enkf_main_type * ert_test_context_get_main( ert_test_context_type * test_context ) { + return test_context->enkf_main; +} + + +const char * ert_test_context_get_cwd( const ert_test_context_type * test_context ) { + return test_work_area_get_cwd( test_context->work_area ); +} + + + +void ert_test_context_free( ert_test_context_type * test_context ) { + + if (test_context->enkf_main) + enkf_main_free( test_context->enkf_main ); + + if (test_context->work_area) + test_work_area_free( test_context->work_area ); + + if (test_context->rng) + rng_free( test_context->rng ); + + if (test_context->res_config) + res_config_free(test_context->res_config); + + free( test_context ); +} + + +bool ert_test_context_install_workflow_job( ert_test_context_type * test_context , const char * job_name , const char * job_file) { + if (util_file_exists( job_file )) { + enkf_main_type * enkf_main = ert_test_context_get_main( test_context ); + ert_workflow_list_type * workflow_list = enkf_main_get_workflow_list( enkf_main ); + ert_workflow_list_add_job( workflow_list , job_name , job_file ); + return ert_workflow_list_has_job( workflow_list , job_name ); + } else + return false; +} + + + +bool ert_test_context_install_workflow( ert_test_context_type * test_context , const char * workflow_name , const char * workflow_file) { + if (util_file_exists( workflow_file )) { + enkf_main_type * enkf_main = ert_test_context_get_main( test_context ); + ert_workflow_list_type * workflow_list = enkf_main_get_workflow_list( enkf_main ); + ert_workflow_list_add_workflow( workflow_list , workflow_file , workflow_name ); + return ert_workflow_list_has_workflow( workflow_list , workflow_name); + } else + return false; +} + + +void ert_test_context_fwrite_workflow_job( FILE * stream , const char * job_name , const stringlist_type * args) { + fprintf(stream , "%s " , job_name); + stringlist_fprintf(args , " ", stream); + fprintf(stream , "\n"); +} + + +bool ert_test_context_run_worklow( ert_test_context_type * test_context , const char * workflow_name) { + enkf_main_type * enkf_main = ert_test_context_get_main( test_context ); + ert_workflow_list_type * workflow_list = enkf_main_get_workflow_list( enkf_main ); + + if (ert_workflow_list_has_workflow( workflow_list , workflow_name )){ + bool result = ert_workflow_list_run_workflow_blocking( workflow_list , workflow_name , enkf_main ); + return result; + } + else{ + return false; + } +} + + + +bool ert_test_context_run_worklow_job( ert_test_context_type * test_context , const char * job_name, const stringlist_type * args) { + enkf_main_type * enkf_main = ert_test_context_get_main( test_context ); + ert_workflow_list_type * workflow_list = enkf_main_get_workflow_list( enkf_main ); + + if (ert_workflow_list_has_job( workflow_list , job_name )) { + bool status; + { + char * workflow = util_alloc_sprintf("WORKFLOW-%06d" , rng_get_int( test_context->rng , 1000000)); + { + FILE * stream = util_fopen( workflow , "w"); + ert_test_context_fwrite_workflow_job( stream , job_name , args ); + fclose(stream); + } + ert_test_context_install_workflow( test_context , workflow , workflow); + status = ert_test_context_run_worklow( test_context , workflow ); + free(workflow); + } + return status; + } else + return false; +} + + diff --git a/libres/lib/enkf/ert_users.cpp b/libres/lib/enkf/ert_users.cpp new file mode 100644 index 00000000000..91c5e6f3606 --- /dev/null +++ b/libres/lib/enkf/ert_users.cpp @@ -0,0 +1,48 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'ert_users.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 + + +int main (int argc , char ** argv) { + char hostname[256]; + const char * executable = argv[1]; + + gethostname( hostname , 255 ); + printf("%s : " , hostname); + { + set_type * user_set = set_alloc_empty(); + enkf_main_list_users( user_set , executable ); + + if (set_get_size( user_set ) > 0) + set_fprintf(user_set , " " , stdout ); + else + printf("No users."); + + printf("\n"); + + set_free( user_set ); + } +} + + + + diff --git a/libres/lib/enkf/ert_workflow_list.cpp b/libres/lib/enkf/ert_workflow_list.cpp new file mode 100644 index 00000000000..77b6228364d --- /dev/null +++ b/libres/lib/enkf/ert_workflow_list.cpp @@ -0,0 +1,331 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'ert_workflow_list.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 + +#define ERT_WORKFLOW_LIST_TYPE_ID 8856275 + +struct ert_workflow_list_struct { + UTIL_TYPE_ID_DECLARATION; + hash_type * workflows; + hash_type * alias_map; + workflow_joblist_type * joblist; + const subst_list_type * context; + const config_error_type * last_error; + bool verbose; +}; + +static void ert_workflow_list_init(ert_workflow_list_type * workflow_list, const config_content_type * config); + +ert_workflow_list_type * ert_workflow_list_alloc_empty(const subst_list_type * context) { + ert_workflow_list_type * workflow_list = (ert_workflow_list_type *)util_malloc( sizeof * workflow_list ); + UTIL_TYPE_ID_INIT( workflow_list , ERT_WORKFLOW_LIST_TYPE_ID ); + workflow_list->workflows = hash_alloc(); + workflow_list->alias_map = hash_alloc(); + workflow_list->joblist = workflow_joblist_alloc(); + workflow_list->context = context; + workflow_list->last_error = NULL; + ert_workflow_list_set_verbose( workflow_list , DEFAULT_WORKFLOW_VERBOSE ); + return workflow_list; +} + +ert_workflow_list_type * ert_workflow_list_alloc_load_site_config(const subst_list_type * context) { + ert_workflow_list_type * workflow_list = ert_workflow_list_alloc_empty(context); + + config_parser_type * config = config_alloc(); + config_content_type * content = site_config_alloc_content(config); + + ert_workflow_list_init(workflow_list, content); + + config_free(config); + config_content_free(content); + + return workflow_list; +} + +ert_workflow_list_type * ert_workflow_list_alloc( + const subst_list_type * context, + const config_content_type * config_content) { + + ert_workflow_list_type * workflow_list = ert_workflow_list_alloc_load_site_config(context); + + if(config_content) + ert_workflow_list_init(workflow_list, config_content); + + return workflow_list; +} + +ert_workflow_list_type * ert_workflow_list_alloc_full(const subst_list_type * context, + workflow_joblist_type * workflow_joblist) { + ert_workflow_list_type * workflow_list = ert_workflow_list_alloc_empty(context); + workflow_list->joblist = workflow_joblist; + workflow_list->context = context; + + return workflow_list; + +} + +UTIL_IS_INSTANCE_FUNCTION( ert_workflow_list , ERT_WORKFLOW_LIST_TYPE_ID ) + +void ert_workflow_list_set_verbose( ert_workflow_list_type * workflow_list , bool verbose) { + workflow_list->verbose = verbose; +} + + +const subst_list_type * ert_workflow_list_get_context(const ert_workflow_list_type * workflow_list) { + return workflow_list->context; +} + +void ert_workflow_list_free( ert_workflow_list_type * workflow_list ) { + hash_free( workflow_list->workflows ); + hash_free( workflow_list->alias_map ); + workflow_joblist_free( workflow_list->joblist ); + free( workflow_list ); +} + + + +workflow_type * ert_workflow_list_add_workflow( ert_workflow_list_type * workflow_list , const char * workflow_file , const char * workflow_name) { + if (util_file_exists( workflow_file )) { + workflow_type * workflow = workflow_alloc( workflow_file , workflow_list->joblist ); + char * name; + + if (workflow_name == NULL) + util_alloc_file_components( workflow_file , NULL , &name , NULL ); + else + name = (char *) workflow_name; + + + hash_insert_hash_owned_ref( workflow_list->workflows , name , workflow , workflow_free__); + if (hash_has_key( workflow_list->alias_map , name)) + hash_del( workflow_list->alias_map , name); + + if (workflow_name == NULL) + free( name ); + + return workflow; + } else + return NULL; +} + + + +void ert_workflow_list_add_alias( ert_workflow_list_type * workflow_list , const char * real_name , const char * alias) { + if (!util_string_equal( real_name , alias)) + hash_insert_ref( workflow_list->alias_map , alias , real_name ); +} + + +void ert_workflow_list_add_job( ert_workflow_list_type * workflow_list , const char * job_name , const char * config_file ) { + char * name = (char *) job_name; + + if (job_name == NULL) + util_alloc_file_components( config_file , NULL , &name , NULL ); + + if (!workflow_joblist_add_job_from_file( workflow_list->joblist , name , config_file )) + fprintf(stderr,"** Warning: failed to add workflow job:%s from:%s \n", name , config_file ); + + if (job_name == NULL) + free(name); +} + + +bool ert_workflow_list_has_job( const ert_workflow_list_type * workflow_list , const char * job_name) { + return workflow_joblist_has_job( workflow_list->joblist , job_name ); +} + + +const workflow_job_type * ert_workflow_list_get_job( const ert_workflow_list_type * workflow_list , const char * job_name) { + return workflow_joblist_get_job(workflow_list->joblist, job_name); +} + + +static char * ert_workflow_list_alloc_name( const char * path , const char * root_name ) { + char * full_path = util_alloc_sprintf( "%s%s%s" , path , UTIL_PATH_SEP_STRING , root_name); + + if (util_is_file( full_path )) + return full_path; + else + free( full_path ); + + return NULL; +} + + +void ert_workflow_list_add_jobs_in_directory( ert_workflow_list_type * workflow_list , const char * path ) { + DIR * dirH = opendir( path ); + std::set names; + if (!dirH) { + fprintf(stderr, "** Warning: failed to open workflow/jobs directory: %s\n", path); + return; + } + while (true) { + struct dirent * entry = readdir( dirH ); + if (entry == NULL) + break; + + if ((strcmp(entry->d_name , ".") == 0) || (strcmp(entry->d_name , "..") == 0)) + continue; + + char * root_name = entry->d_name; + if (names.count(root_name)) + continue; + + char * full_path = ert_workflow_list_alloc_name( path , root_name ); + if (!full_path) + continue; + + names.insert(root_name); + res_log_finfo("Adding workflow job:%s", full_path); + ert_workflow_list_add_job( workflow_list , root_name , full_path ); + free( full_path ); + } + closedir( dirH ); +} + + +stringlist_type * ert_workflow_list_get_job_names(const ert_workflow_list_type * workflow_list) { + return workflow_joblist_get_job_names(workflow_list->joblist); +} + + +static void ert_workflow_list_init(ert_workflow_list_type * workflow_list , const config_content_type * config) { + /* Adding jobs */ + { + if (config_content_has_item( config , WORKFLOW_JOB_DIRECTORY_KEY)) { + const config_content_item_type * jobpath_item = config_content_get_item( config , WORKFLOW_JOB_DIRECTORY_KEY); + for (int i=0; i < config_content_item_get_size( jobpath_item ); i++) { + config_content_node_type * path_node = config_content_item_iget_node( jobpath_item , i ); + + for (int j=0; j < config_content_node_get_size( path_node ); j++) + ert_workflow_list_add_jobs_in_directory( workflow_list , config_content_node_iget_as_abspath( path_node , j ) ); + } + } + } + + { + if (config_content_has_item( config , LOAD_WORKFLOW_JOB_KEY)) { + const config_content_item_type * job_item = config_content_get_item( config , LOAD_WORKFLOW_JOB_KEY); + for (int i=0; i < config_content_item_get_size( job_item ); i++) { + config_content_node_type * job_node = config_content_item_iget_node( job_item , i ); + const char * config_file = config_content_node_iget_as_path( job_node , 0 ); + const char * job_name = config_content_node_safe_iget( job_node , 1 ); + ert_workflow_list_add_job( workflow_list , job_name , config_file); + } + } + } + + + /* Adding workflows */ + { + if (config_content_has_item( config , LOAD_WORKFLOW_KEY)) { + const config_content_item_type * workflow_item = config_content_get_item( config , LOAD_WORKFLOW_KEY); + for (int i=0; i < config_content_item_get_size( workflow_item ); i++) { + config_content_node_type * workflow_node = config_content_item_iget_node(workflow_item, i); + + const char * workflow_file = config_content_node_iget_as_abspath(workflow_node, 0); + const char * workflow_name = config_content_node_safe_iget( workflow_node , 1 ); + + ert_workflow_list_add_workflow( workflow_list , workflow_file , workflow_name ); + } + } + } +} + + +void ert_workflow_list_add_config_items( config_parser_type * config ) { + config_schema_item_type * item = config_add_schema_item( config , WORKFLOW_JOB_DIRECTORY_KEY , false ); + config_schema_item_set_argc_minmax(item , 1 , 1 ); + config_schema_item_iset_type( item , 0 , CONFIG_PATH ); + + item = config_add_schema_item( config , LOAD_WORKFLOW_KEY , false ); + config_schema_item_set_argc_minmax(item , 1 , 2 ); + config_schema_item_iset_type( item , 0 , CONFIG_EXISTING_PATH ); + + item = config_add_schema_item( config , LOAD_WORKFLOW_JOB_KEY , false ); + config_schema_item_set_argc_minmax(item , 1 , 2 ); + config_schema_item_iset_type( item , 0 , CONFIG_EXISTING_PATH ); +} + + + +workflow_type * ert_workflow_list_get_workflow(ert_workflow_list_type * workflow_list , const char * workflow_name ) { + const char * lookup_name = workflow_name; + + if (hash_has_key( workflow_list->alias_map , workflow_name)) + lookup_name = (const char * ) hash_get( workflow_list->alias_map , workflow_name ); + + return (workflow_type * ) hash_get( workflow_list->workflows , lookup_name ); +} + +bool ert_workflow_list_has_workflow(ert_workflow_list_type * workflow_list , const char * workflow_name ) { + return + hash_has_key( workflow_list->workflows , workflow_name ) || + hash_has_key( workflow_list->alias_map , workflow_name); +} + + +bool ert_workflow_list_run_workflow__(ert_workflow_list_type * workflow_list, workflow_type * workflow, bool verbose , void * self) { + bool runOK = workflow_run( workflow, self , verbose , workflow_list->context); + if (runOK) + workflow_list->last_error = NULL; + else + workflow_list->last_error = workflow_get_last_error( workflow ); + + return runOK; +} + + +bool ert_workflow_list_run_workflow_blocking(ert_workflow_list_type * workflow_list , const char * workflow_name , void * self) { + workflow_type * workflow = ert_workflow_list_get_workflow( workflow_list , workflow_name ); + bool result = ert_workflow_list_run_workflow__( workflow_list, workflow , workflow_list->verbose , self); + return result; +} + + +/*****************************************************************/ + +stringlist_type * ert_workflow_list_alloc_namelist( ert_workflow_list_type * workflow_list ) { + return hash_alloc_stringlist( workflow_list->workflows ); +} + + +int ert_workflow_list_get_size( const ert_workflow_list_type * workflow_list) { + return hash_get_size( workflow_list->workflows ) + hash_get_size( workflow_list->alias_map); +} diff --git a/libres/lib/enkf/ext_param.cpp b/libres/lib/enkf/ext_param.cpp new file mode 100644 index 00000000000..dec5fe6f4ac --- /dev/null +++ b/libres/lib/enkf/ext_param.cpp @@ -0,0 +1,242 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'ext_param.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 + +GET_DATA_SIZE_HEADER(ext_param); + + +struct ext_param_struct { + int __type_id; + const ext_param_config_type * config; + std::vector > data; +}; + +/*****************************************************************/ + + + +void ext_param_free(ext_param_type *ext_param) { + delete ext_param; +} + + +ext_param_type * ext_param_alloc(const ext_param_config_type * config) { + ext_param_type * ext_param = new ext_param_type(); + ext_param->__type_id = EXT_PARAM; + ext_param->config = config; + ext_param->data.resize(ext_param_config_get_data_size(config)); + for(int i = 0; i < ext_param->data.size(); i++) { + auto const suffix_count = ext_param_config_ikey_get_suffix_count(ext_param->config, i); + ext_param->data[i].resize(std::max(suffix_count, 1)); + } + return ext_param; +} + + +bool ext_param_key_set( ext_param_type * param, const char * key, double value) { + int index = ext_param_config_get_key_index( param->config, key); + if (index < 0) + return false; + + param->data[index][0] = value; + return true; +} + + +bool ext_param_key_suffix_set( ext_param_type * param, const char * key, const char * suffix, double value) { + int index = ext_param_config_get_key_index( param->config, key); + if (index < 0) + return false; + + int suffix_index = ext_param_config_ikey_get_suffix_index( param->config, index, suffix); + if (suffix_index < 0) + return false; + + + param->data[index][suffix_index] = value; + return true; +} + + +double ext_param_key_get( const ext_param_type * param, const char * key) { + int index = ext_param_config_get_key_index( param->config, key); + if (index < 0) + util_abort("%s: invalid key:%s \n",__func__ , key); + + return param->data[index].front(); +} + + +double ext_param_key_suffix_get( const ext_param_type * param, const char * key, const char * suffix) { + int ikey = ext_param_config_get_key_index( param->config, key); + if (ikey < 0) + util_abort("%s: invalid key:%s \n",__func__ , key); + + int isuffix = ext_param_config_ikey_get_suffix_index( param->config, ikey, suffix); + if (isuffix < 0) + util_abort("%s: invalid suffix:%s \n",__func__ , suffix); + + return param->data[ikey][isuffix]; +} + + +bool ext_param_iset( ext_param_type * param, int index , double value) { + if (index >= param->data.size()) + return false; + + if (index < 0) + return false; + + param->data[index].front() = value; + return true; +} + + +bool ext_param_iiset( ext_param_type * param, int ikey , int isuffix, double value) { + if (ikey >= param->data.size()) + return false; + + if (ikey < 0) + return false; + + if (isuffix >= param->data[ikey].size()) + return false; + + if (isuffix < 0) + return false; + + param->data[ikey][isuffix] = value; + return true; +} + + +double ext_param_iget(const ext_param_type * param, int index) { + if (index >= param->data.size()) + util_abort("%s: invalid index:%d - range: [0,%d) \n",__func__ , index , param->data.size()); + + if (index < 0) + util_abort("%s: invalid index:%d - range: [0,%d) \n",__func__ , index , param->data.size()); + + return param->data[index].front(); +} + + +double ext_param_iiget(const ext_param_type * param, int ikey, int isuffix) { + if (ikey >= param->data.size()) + util_abort("%s: invalid key index:%d - range: [0,%d) \n",__func__ , ikey , param->data.size()); + + if (ikey < 0) + util_abort("%s: invalid key index:%d - range: [0,%d) \n",__func__ , ikey , param->data.size()); + + if (isuffix >= param->data[ikey].size()) + util_abort("%s: invalid suffix index:%d - range: [0,%d) \n",__func__ , isuffix , param->data[ikey].size()); + + if (isuffix < 0) + util_abort("%s: invalid suffix index:%d - range: [0,%d) \n",__func__ , isuffix , param->data[ikey].size()); + + return param->data[ikey][isuffix]; +} + + +void ext_param_json_export(const ext_param_type * ext_param, const char * json_file) { + FILE * stream = util_mkdir_fopen(json_file, "w"); + fprintf(stream, "{\n"); + for(int ikey = 0; ikey < ext_param->data.size(); ikey++) { + auto const key = ext_param_config_iget_key(ext_param->config, ikey); + auto const suffix_count = ext_param_config_ikey_get_suffix_count(ext_param->config, ikey); + + fprintf(stream, "\"%s\" : ", key); // print the key + if(suffix_count == 0) { // no suffixes, just print the value + auto const value = ext_param->data[ikey].front(); + fprintf(stream, "%g", value); + } + else { // print suffixes and corresponding values + fprintf(stream, "{\n"); + for(int isuffix = 0; isuffix < suffix_count; ++isuffix) { + auto const suffix = ext_param_config_ikey_iget_suffix(ext_param->config, ikey, isuffix); + auto const value = ext_param->data[ikey][isuffix]; + fprintf(stream, " \"%s\" : %g", suffix, value); + fprintf(stream, isuffix == suffix_count-1 ? "\n" : ",\n"); + } + fprintf(stream, "}"); + } + + if(ikey< (ext_param->data.size() - 1)) + fprintf(stream, ",\n"); + else + fprintf(stream, "\n"); + + } + fprintf(stream, "}\n"); + fclose(stream); +} + +void ext_param_ecl_write(const ext_param_type * ext_param , const char * run_path , const char * base_file , value_export_type * unused) { + char * target_file; + + if (run_path) + target_file = util_alloc_filename( run_path , base_file , NULL); + else + target_file = util_alloc_string_copy( base_file ); + + ext_param_json_export( ext_param , target_file ); + free( target_file ); +} + +bool ext_param_write_to_buffer(const ext_param_type *ext_param , buffer_type * buffer, int report_step) { + buffer_fwrite_int( buffer , EXT_PARAM ); + for(auto const& d : ext_param->data) { + buffer_fwrite(buffer , d.data() , sizeof *(d.data()), d.size()); + } + return true; +} + + +void ext_param_read_from_buffer(ext_param_type * ext_param , buffer_type * buffer, enkf_fs_type * fs, int report_step) { + enkf_util_assert_buffer_type( buffer, EXT_PARAM ); + for(auto& d : ext_param->data) { + buffer_fread(buffer , d.data() , sizeof *(d.data()) , d.size()); + } +} + + +ext_param_config_type const* ext_param_get_config(const ext_param_type * param) { + return param->config; +} + +/******************************************************************/ +/* Anonumously generated functions used by the enkf_node object */ +/******************************************************************/ +UTIL_SAFE_CAST_FUNCTION(ext_param , EXT_PARAM) +UTIL_SAFE_CAST_FUNCTION_CONST(ext_param , EXT_PARAM) +VOID_ALLOC(ext_param) +VOID_FREE(ext_param) +VOID_ECL_WRITE(ext_param) +VOID_WRITE_TO_BUFFER(ext_param) +VOID_READ_FROM_BUFFER(ext_param) + diff --git a/libres/lib/enkf/ext_param_config.cpp b/libres/lib/enkf/ext_param_config.cpp new file mode 100644 index 00000000000..e0d6f4cf835 --- /dev/null +++ b/libres/lib/enkf/ext_param_config.cpp @@ -0,0 +1,110 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'ext_param_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 +#include + +#include +#include + +#define EXT_PARAM_CONFIG_ID 97124451 +struct ext_param_config_struct { + UTIL_TYPE_ID_DECLARATION; + std::string key; + std::vector keys; + std::vector > suffixes; +}; + +UTIL_SAFE_CAST_FUNCTION(ext_param_config, EXT_PARAM_CONFIG_ID) +UTIL_SAFE_CAST_FUNCTION_CONST(ext_param_config, EXT_PARAM_CONFIG_ID) + +void ext_param_config_free( ext_param_config_type * config ) { + delete config; +} + +int ext_param_config_get_data_size( const ext_param_config_type * config ) { + return config->keys.size(); +} + + +const char* ext_param_config_iget_key( const ext_param_config_type * config , int index) { + return config->keys[index].data(); +} + +int ext_param_config_get_key_index( const ext_param_config_type * config , const char * key) { + const auto it = std::find(config->keys.begin(), config->keys.end(), key); + return it == config->keys.end() ? + -1 : + std::distance(config->keys.begin(), it); +} + +bool ext_param_config_has_key( const ext_param_config_type * config , const char * key) { + return std::find(config->keys.begin(), config->keys.end(), key) != config->keys.end(); +} + + +ext_param_config_type * ext_param_config_alloc( const char * key, const stringlist_type * keys) { + if (stringlist_get_size( keys ) == 0) + return NULL; + + if (!stringlist_unique( keys )) + return NULL; + + ext_param_config_type * config = new ext_param_config_type(); + UTIL_TYPE_ID_INIT( config , EXT_PARAM_CONFIG_ID); + config->key = key; + + for(int i=0; ikeys.push_back(stringlist_iget(keys, i)); + } + config->suffixes.resize(stringlist_get_size(keys)); + return config; +} + + +void ext_param_config_ikey_set_suffixes(ext_param_config_type * config, int ikey, const stringlist_type * suffixes) { + auto tmp = std::vector(stringlist_get_size(suffixes)); + for(int isuffix = 0; isuffix < stringlist_get_size(suffixes); isuffix++) + tmp[isuffix] = stringlist_iget(suffixes, isuffix); + config->suffixes[ikey] = std::move(tmp); +} + + +int ext_param_config_ikey_get_suffix_count( const ext_param_config_type * config, int ikey) { + return config->suffixes[ikey].size(); +} + +const char* ext_param_config_ikey_iget_suffix( const ext_param_config_type * config, int ikey, int isuffix) { + return config->suffixes[ikey][isuffix].data(); +} + +int ext_param_config_ikey_get_suffix_index( const ext_param_config_type * config, int ikey, const char * suffix) { + const auto it = std::find(config->suffixes[ikey].begin(), config->suffixes[ikey].end(), suffix); + return it == config->suffixes[ikey].end() ? + -1 : + std::distance(config->suffixes[ikey].begin(), it); +} + +VOID_FREE(ext_param_config) +VOID_GET_DATA_SIZE(ext_param) diff --git a/libres/lib/enkf/field.cpp b/libres/lib/enkf/field.cpp new file mode 100644 index 00000000000..3efde8e21e3 --- /dev/null +++ b/libres/lib/enkf/field.cpp @@ -0,0 +1,1313 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'field.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 + + +GET_DATA_SIZE_HEADER(field); + + +/*****************************************************************/ + +/** + The field data type contains for "something" which is distributed + over the full grid, i.e. permeability or pressure. All configuration + information is stored in the config object, which is of type + field_config_type. Observe the following: + + * The field **only** contains the active cells - the config object + has a reference to actnum information. + + * The data is stored in a char pointer; the real underlying data can + be (at least) of the types int, float and double. +*/ + +struct field_struct { + int __type_id; + const field_config_type * config; /* The field config object - containing information of active cells++ */ + char *data; /* The actual storage for the field - suitabley casted to int/float/double on use*/ + + bool shared_data; /* If the data is shared - i.e. managed (xalloc & free) from another scope. */ + int shared_byte_size; /* The size of the shared buffer (if it is shared). */ + char *export_data; /* IFF an output transform should be applied this pointer will hold the transformed data. */ + char *__data; /* IFF an output transform, this pointer will hold the original data during the transform and export. */ +}; + + + +#define EXPORT_MACRO \ +{ \ + int nx,ny,nz; \ + field_config_get_dims(field->config , &nx , &ny , &nz); \ + int i,j,k; \ + for (k=0; k < nz; k++) { \ + for (j=0; j < ny; j++) { \ + for (i=0; i < nx; i++) { \ + bool active_cell = field_config_active_cell(config, i, j, k); \ + bool use_initial_value = false; \ + \ + if (init_file && !active_cell) \ + use_initial_value = true; \ + \ + int source_index = 0; \ + if (use_initial_value) \ + source_index = field_config_global_index(config , i , j , k); \ + else \ + source_index = field_config_active_index(config, i, j, k); \ + \ + int target_index; \ + if (rms_index_order) \ + target_index = rms_util_global_index_from_eclipse_ijk(nx,ny,nz,i,j,k); \ + else \ + target_index = i + j * nx + k* nx*ny; \ + \ + if (use_initial_value) \ + target_data[target_index] = initial_src_data[source_index]; \ + else if (active_cell) \ + target_data[target_index] = src_data[source_index]; \ + else \ + memcpy(&target_data[target_index] , fill_value , sizeof_ctype_target); \ + } \ + } \ + } \ +} \ + + +void field_export3D(const field_type * field , + void *_target_data , + bool rms_index_order , + ecl_data_type target_data_type , + void *fill_value, + const char * init_file) { + const field_config_type * config = field->config; + ecl_data_type data_type = field_config_get_ecl_data_type( config ); + int sizeof_ctype_target = ecl_type_get_sizeof_ctype(target_data_type); + + field_type * initial_field = NULL; + field_config_type * initial_field_config = NULL; + if (init_file) { + ecl_grid_type * grid = field_config_get_grid(config); + bool global_size = true; + initial_field_config = field_config_alloc_empty(field_config_get_key(config), grid, NULL, global_size); + initial_field = field_alloc(initial_field_config); + + field_fload_keep_inactive(initial_field, init_file); + } + switch(ecl_type_get_type(data_type)) { + case(ECL_DOUBLE_TYPE): + { + const double * src_data = (const double *) field->data; + const double * initial_src_data = initial_field ? (const double *) initial_field->data : NULL; + + if (ecl_type_is_float(target_data_type)) { + float *target_data = (float *) _target_data; + EXPORT_MACRO; + } else if (ecl_type_is_double(target_data_type)) { + double *target_data = (double *) _target_data; + EXPORT_MACRO; + } else { + fprintf(stderr,"%s: double field can only export to double/float\n",__func__); + abort(); + } + } + break; + case(ECL_FLOAT_TYPE): + { + const float * src_data = (const float *) field->data; + const float * initial_src_data = initial_field ? (const float *) initial_field->data : NULL; + if (ecl_type_is_float(target_data_type)) { + float *target_data = (float *) _target_data; + EXPORT_MACRO; + } else if (ecl_type_is_double(target_data_type)) { + double *target_data = (double *) _target_data; + EXPORT_MACRO; + } else { + fprintf(stderr,"%s: float field can only export to double/float\n",__func__); + abort(); + } + } + break; + case(ECL_INT_TYPE): + { + const int * src_data = (const int *) field->data; + const int * initial_src_data = initial_field ? (const int *) initial_field->data : NULL; + if (ecl_type_is_float(target_data_type)) { + float *target_data = (float *) _target_data; + EXPORT_MACRO; + } else if (ecl_type_is_double(target_data_type)) { + double *target_data = (double *) _target_data; + EXPORT_MACRO; + } else if (ecl_type_is_int(target_data_type)) { + int *target_data = (int *) _target_data; + EXPORT_MACRO; + } else { + fprintf(stderr,"%s: int field can only export to int/double/float\n",__func__); + abort(); + } + } + break; + default: + fprintf(stderr,"%s: Sorry field has unexportable type ... \n",__func__); + break; + } + + if (initial_field) { + field_config_free(initial_field_config); + field_free(initial_field); + } +} +#undef EXPORT_MACRO + + +/*****************************************************************/ +#define IMPORT_MACRO \ +{ \ + int i,j,k; \ + int nx,ny,nz; \ + field_config_get_dims(field->config , &nx , &ny , &nz); \ + for (k=0; k < nz; k++) { \ + for (j=0; j < ny; j++) { \ + for (i=0; i < nx; i++) { \ + int target_index = keep_inactive_cells ? field_config_global_index(config, i, j, k) : field_config_active_index(config, i, j, k); \ + \ + if (target_index >= 0) { \ + int source_index; \ + if (rms_index_order) \ + source_index = rms_util_global_index_from_eclipse_ijk(nx,ny,nz,i,j,k); \ + else \ + source_index = i + j * nx + k* nx*ny; \ + \ + target_data[target_index] = src_data[source_index] ; \ + } \ + } \ + } \ + } \ +} \ + + + +/** + The main function of the field_import3D and field_export3D + functions are to skip the inactive cells (field_import3D) and + distribute inactive cells (field_export3D). + When the flag keep_inactive_cells is set for field_import3D, + the values for the inactive cells are kept. The field argument + must have been allocated with flag global_size = true for this + to work. + When field_export3D is called with argument INIT_FILE, the + exported values for inactive cells are read from the INIT_FILE. + + In addition we can reorganize input/output according to the + RMS Roff index convention, and also perform float <-> double + conversions. + + Observe that these functions only import/export onto memory + buffers, the actual reading and writing of files is done in other + functions (calling these). +*/ + +static void field_import3D(field_type * field , + const void *_src_data , + bool rms_index_order , + bool keep_inactive_cells, + ecl_data_type src_type) { + const field_config_type * config = field->config; + ecl_data_type data_type = field_config_get_ecl_data_type(config); + + switch(ecl_type_get_type(data_type)) { + case(ECL_DOUBLE_TYPE): + { + double * target_data = (double *) field->data; + if (ecl_type_is_float(src_type)) { + float *src_data = (float *) _src_data; + IMPORT_MACRO; + } else if (ecl_type_is_double(src_type)) { + double *src_data = (double *) _src_data; + IMPORT_MACRO; + } else if (ecl_type_is_int(src_type)) { + int *src_data = (int *) _src_data; + IMPORT_MACRO; + } else { + fprintf(stderr,"%s: double field can only import from int/double/float\n",__func__); + abort(); + } + } + break; + case(ECL_FLOAT_TYPE): + { + float * target_data = (float *) field->data; + if (ecl_type_is_float(src_type)) { + float *src_data = (float *) _src_data; + IMPORT_MACRO; + } else if (ecl_type_is_double(src_type)) { + double *src_data = (double *) _src_data; + IMPORT_MACRO; + } else if (ecl_type_is_int(src_type)) { + int *src_data = (int *) _src_data; + IMPORT_MACRO; + } else { + fprintf(stderr,"%s: double field can only import from int/double/float\n",__func__); + abort(); + } + } + break; + case(ECL_INT_TYPE): + { + int * target_data = (int *) field->data; + if (ecl_type_is_int(src_type)) { + int *src_data = (int *) _src_data; + IMPORT_MACRO; + } else { + fprintf(stderr,"%s: int field can only import from int\n",__func__); + abort(); + } + } + break; + default: + fprintf(stderr,"%s: Sorry field has unimportable type ... \n",__func__); + break; + } +} +#undef IMPORT_MACRO + + +/*****************************************************************/ + +#define CLEAR_MACRO(d,s) { int k; for (k=0; k < (s); k++) (d)[k] = 0; } +C_USED void field_clear(field_type * field) { + const ecl_data_type data_type = field_config_get_ecl_data_type(field->config); + const int data_size = field_config_get_data_size(field->config ); + + switch (data_type.type) { + case(ECL_DOUBLE_TYPE): + { + double * data = (double *) field->data; + CLEAR_MACRO(data , data_size); + break; + } + case(ECL_FLOAT_TYPE): + { + float * data = (float *) field->data; + CLEAR_MACRO(data , data_size); + break; + } + case(ECL_INT_TYPE): + { + int * data = (int *) field->data; + CLEAR_MACRO(data , data_size); + break; + } + default: + util_abort("%s: not implemeneted for data_type: %d \n",__func__ , data_type.type); + } +} +#undef CLEAR_MACRO + + + + +static field_type * __field_alloc(const field_config_type * field_config , void * shared_data , int shared_byte_size) { + field_type * field = (field_type *)util_malloc(sizeof *field); + field->config = field_config; + if (shared_data == NULL) { + field->shared_data = false; + field->data = (char * ) util_calloc(field_config_get_byte_size(field->config) , sizeof * field->data ); + } else { + field->data = (char * ) shared_data; + field->shared_data = true; + field->shared_byte_size = shared_byte_size; + if (shared_byte_size < field_config_get_byte_size(field->config)) + util_abort("%s: the shared buffer is to small to hold the input field - aborting \n",__func__); + + } + field->export_data = NULL; /* This NULL is checked for in the revert_output_transform() */ + field->__type_id = FIELD; + return field; +} + + +int field_get_size(const field_type * field) { + return field_config_get_data_size(field->config); +} + +field_type * field_alloc(const field_config_type * field_config) { + return __field_alloc(field_config , NULL , 0); +} + + +C_USED void field_copy(const field_type *src , field_type * target ) { + if (src->config == target->config) + memcpy(target->data , src->data , field_config_get_byte_size(src->config)); + else + util_abort("%s: instances do not share config \n",__func__); +} + + + +void field_read_from_buffer(field_type * field , buffer_type * buffer, enkf_fs_type * fs, int report_step) { + int byte_size = field_config_get_byte_size(field->config); + enkf_util_assert_buffer_type(buffer, FIELD); // FIXME flaky runpath_list test + buffer_fread_compressed(buffer, + buffer_get_remaining_size(buffer), + field->data, + byte_size); +} + + + +static void * __field_alloc_3D_data(const field_type * field , + int data_size , + bool rms_index_order , + ecl_data_type data_type , + ecl_data_type target_data_type, + const char * init_file) { + void * data = (void *)util_calloc(data_size , ecl_type_get_sizeof_ctype(target_data_type) ); + if (ecl_type_is_double(data_type)) { + double fill; + if (rms_index_order) + fill = RMS_INACTIVE_DOUBLE; + else + fill = 0; + field_export3D(field , data , rms_index_order , target_data_type , &fill, init_file); + } else if (ecl_type_is_float(data_type)) { + float fill; + if (rms_index_order) + fill = RMS_INACTIVE_FLOAT; + else + fill = 0; + field_export3D(field , data , rms_index_order , target_data_type , &fill, init_file); + } else if (ecl_type_is_int(data_type)) { + int fill; + if (rms_index_order) + fill = RMS_INACTIVE_INT; + else + fill = 0; + field_export3D(field , data , rms_index_order , target_data_type , &fill, init_file); + } else + util_abort("%s: trying to export type != int/float/double - aborting \n",__func__); + return data; +} + + +/** + A general comment about writing fields to disk: + + The writing of fields to disk can be done in **MANY** different ways: + + o The native function field_fwrite() will save the field in the + format most suitable for use with enkf. This function will only + save the active cells, and compress the field if the variable + write_compressed is true. Most of the configuration information + is with the field_config object, and not saved with the field. + + o Export as ECLIPSE input. This again has three subdivisions: + + * The function field_ecl_grdecl_export() will write the field to + disk in a format suitable for ECLIPSE INCLUDE statements. This + means that both active and inactive cells are written, with a + zero fill for the inactive. If the argument init_file is set, + the value for the inactive cells are read from this file. + + * The functions field_xxxx_fortio() writes the field in the + ECLIPSE restart format. The function field_ecl_write3D_fortio() + writes all the cells - with zero filling for inactive + cells. This is suitable for IMPORT of e.g. PORO. + + The function field_ecl_write1D_fortio() will write only the + active cells in an ECLIPSE restart file. This is suitable for + e.g. the pressure. + + Observe that the function field_ecl_write() should get config + information and automatically select the right way to export to + eclipse format. + + o Export in RMS ROFF format. +*/ + + + +/** + This function exports *one* field instance to the rms_file + instance. It is the responsibility of the field_ROFF_export() + function to initialize and close down the rms_file instance. +*/ + +static void field_ROFF_export__(const field_type * field , rms_file_type * rms_file, const char * init_file) { + const int data_size = field_config_get_volume(field->config); + const ecl_data_type target_type = field_config_get_ecl_data_type(field->config); /* Could/should in principle be input */ + const ecl_data_type data_type = field_config_get_ecl_data_type(field->config); + + void *data = __field_alloc_3D_data(field , data_size , true , data_type , target_type, init_file); + rms_tagkey_type * data_key = rms_tagkey_alloc_complete("data" , data_size , rms_util_convert_ecl_type(target_type) , data , true); + rms_tag_fwrite_parameter(field_config_get_ecl_kw_name(field->config) , data_key , rms_file_get_FILE(rms_file)); + rms_tagkey_free(data_key); + free(data); +} + + +static rms_file_type * field_init_ROFF_export(const field_type * field, const char * filename) { + rms_file_type * rms_file = rms_file_alloc(filename , false); + rms_file_fopen_w(rms_file); + rms_file_init_fwrite(rms_file , "parameter"); /* Version / byteswap ++ */ + { + int nx,ny,nz; + field_config_get_dims(field->config , &nx , &ny , &nz); + rms_tag_fwrite_dimensions(nx , ny , nz , rms_file_get_FILE(rms_file)); /* Dimension header */ + } + return rms_file; +} + + +static void field_complete_ROFF_export(const field_type * field , rms_file_type * rms_file) { + rms_file_complete_fwrite(rms_file); + rms_file_fclose(rms_file); + rms_file_free(rms_file); +} + + + + +/** + This function exports the data of a field as a parameter to an RMS + roff file. The export process is divided in three parts: + + 1. The rms_file is opened, and initialized with some basic data + for dimensions++ + 2. The field is written to file. + 3. The file is completed / closed. + + The reason for doing it like this is that it should be easy to + export several fields (of the same dimension+++) with repeated + calls to 2 (i.e. field_ROFF_export__()) - that is currently not + implemented. +*/ + +void field_ROFF_export(const field_type * field , const char * export_filename, const char * init_file) { + rms_file_type * rms_file = field_init_ROFF_export(field , export_filename); + field_ROFF_export__(field , rms_file, init_file); /* Should now be possible to several calls to field_ROFF_export__() */ + field_complete_ROFF_export(field , rms_file); +} + + + +bool field_write_to_buffer(const field_type * field , buffer_type * buffer , int report_step) { + int byte_size = field_config_get_byte_size( field->config ); + buffer_fwrite_int( buffer , FIELD ); + buffer_fwrite_compressed( buffer , field->data , byte_size ); + return true; +} + + + +void field_ecl_write1D_fortio(const field_type * field , fortio_type * fortio) { + const int data_size = field_config_get_data_size(field->config ); + const ecl_data_type data_type = field_config_get_ecl_data_type(field->config); + + ecl_kw_fwrite_param_fortio(fortio , field_config_get_ecl_kw_name(field->config), data_type , data_size , field->data); +} + + +void field_ecl_write3D_fortio(const field_type * field , fortio_type * fortio, const char * init_file ) { + const int data_size = field_config_get_volume(field->config); + const ecl_data_type target_type = field_config_get_ecl_data_type(field->config); /* Could/should in principle be input */ + const ecl_data_type data_type = field_config_get_ecl_data_type(field->config); + void *data = __field_alloc_3D_data(field , data_size , false , data_type , target_type, init_file); + + ecl_kw_fwrite_param_fortio(fortio , field_config_get_ecl_kw_name(field->config), data_type , data_size , data); + free(data); +} + + +static ecl_kw_type * field_alloc_ecl_kw_wrapper__(const field_type * field, void * data) { + const int data_size = field_config_get_volume(field->config); + const ecl_data_type target_type = field_config_get_ecl_data_type(field->config); /* Could/should in principle be input */ + + ecl_kw_type * ecl_kw = ecl_kw_alloc_new_shared(field_config_get_ecl_kw_name(field->config) , data_size , target_type , data); + + return ecl_kw; +} + + +void field_ecl_grdecl_export(const field_type * field , FILE * stream, const char * init_file) { + const int data_size = field_config_get_volume(field->config); + const ecl_data_type target_type = field_config_get_ecl_data_type(field->config); /* Could/should in principle be input */ + const ecl_data_type data_type = field_config_get_ecl_data_type(field->config); + void *data = __field_alloc_3D_data(field , data_size , false , data_type , target_type, init_file ); + ecl_kw_type * ecl_kw = field_alloc_ecl_kw_wrapper__(field , data); + ecl_kw_fprintf_grdecl(ecl_kw , stream); + ecl_kw_free(ecl_kw); + free(data); +} + + +static void field_apply(field_type * field , field_func_type * func) { + field_config_assert_unary(field->config , __func__); + { + const int data_size = field_config_get_data_size( field->config ); + const ecl_data_type data_type = field_config_get_ecl_data_type(field->config); + + if (ecl_type_is_float(data_type)) { + float * data = (float *) field->data; + for (int i=0; i < data_size; i++) + data[i] = func(data[i]); + } else if (ecl_type_is_double(data_type)) { + double * data = (double *) field->data; + for (int i=0; i < data_size; i++) + data[i] = func(data[i]); + } + } +} + + +static bool field_check_finite( const field_type * field) { + const int data_size = field_config_get_data_size( field->config ); + const ecl_data_type data_type = field_config_get_ecl_data_type(field->config); + bool ok = true; + + if (ecl_type_is_float(data_type)) { + float * data = (float *) field->data; + for (int i=0; i < data_size; i++) + if (!std::isfinite( data[i] )) + ok = false; + } else if (ecl_type_is_double(data_type)) { + double * data = (double *) field->data; + for (int i=0; i < data_size; i++) + if (!std::isfinite( data[i] )) + ok = false; + } + return ok; +} + + + +void field_inplace_output_transform(field_type * field ) { + field_func_type * output_transform = field_config_get_output_transform(field->config); + if (output_transform != NULL) + field_apply(field , output_transform); +} + + + +#define TRUNCATE_MACRO(s , d , t , min , max) \ +for (int i=0; i < s; i++) { \ + if ( t & TRUNCATE_MIN ) \ + if (d[i] < min) \ + d[i] = min; \ + if ( t & TRUNCATE_MAX ) \ + if (d[i] > max) \ + d[i] = max; \ +} + + +static void field_apply_truncation(field_type * field) { + int truncation = field_config_get_truncation_mode( field->config ); + if (truncation != TRUNCATE_NONE) { + double min_value = field_config_get_truncation_min( field->config ); + double max_value = field_config_get_truncation_max( field->config ); + + const int data_size = field_config_get_data_size(field->config ); + const ecl_data_type data_type = field_config_get_ecl_data_type(field->config); + if (ecl_type_is_float(data_type)) { + float * data = (float *) field->data; + TRUNCATE_MACRO(data_size , data , truncation , min_value , max_value); + } else if (ecl_type_is_double(data_type)) { + double * data = (double *) field->data; + TRUNCATE_MACRO(data_size , data , truncation , min_value , max_value); + } else + util_abort("%s: Field type not supported for truncation \n",__func__); + } +} + + +/** + Does both the explicit output transform *AND* the truncation. +*/ + +static void field_output_transform(field_type * field) { + field_func_type * output_transform = field_config_get_output_transform(field->config); + int truncation = field_config_get_truncation_mode( field->config ); + if ((output_transform != NULL) || (truncation != TRUNCATE_NONE)) { + field->export_data = (char * ) util_alloc_copy(field->data , field_config_get_byte_size(field->config) ); + field->__data = field->data; /* Storing a pointer to the original data. */ + field->data = field->export_data; + + if (output_transform != NULL) + field_inplace_output_transform(field); + + field_apply_truncation(field); + } +} + + +static void field_revert_output_transform(field_type * field) { + if (field->export_data != NULL) { + free(field->export_data); + field->export_data = NULL; + field->data = field->__data; /* Recover the original pointer. */ + } +} + + +/** + This is the generic "export field to eclipse" function. It will + check up the config object to determine how to export the field, + and then call the appropriate function. The alternatives are: + + * Restart format - only active cells (field_ecl_write1D_fortio). + * Restart format - all cells (field_ecl_write3D_fortio). + * GRDECL format (field_ecl_grdecl_export) + + Observe that the output transform is hooked in here, that means + that if you call e.g. the ROFF export function directly, the output + transform will *NOT* be applied. +*/ + +void field_export(const field_type * __field, + const char * file , + fortio_type * restart_fortio , + field_file_format_type file_type, + bool output_transform, + const char * init_file) { + field_type * field = (field_type *) __field; /* Net effect is no change ... but */ + + if (output_transform) + field_output_transform(field); + + + /* Writes the field to in ecl_kw format to a new file. */ + if ((file_type == ECL_KW_FILE_ALL_CELLS) || (file_type == ECL_KW_FILE_ACTIVE_CELLS)) { + fortio_type * fortio; + bool fmt_file = false; /* For formats which support both formatted and unformatted output this is hardwired to unformatted. */ + + fortio = fortio_open_writer(file , fmt_file , ECL_ENDIAN_FLIP); + + if (file_type == ECL_KW_FILE_ALL_CELLS) + field_ecl_write3D_fortio(field , fortio, init_file); + else + field_ecl_write1D_fortio(field , fortio); + + fortio_fclose(fortio); + } else if (file_type == ECL_GRDECL_FILE) { + /* Writes the field to a new grdecl file. */ + FILE * stream = util_mkdir_fopen(file , "w"); + field_ecl_grdecl_export(field , stream, init_file); + fclose(stream); + } else if (file_type == RMS_ROFF_FILE) + /* Roff export */ + field_ROFF_export(field , file, init_file); + else if (file_type == ECL_FILE) + /* This entry point is used by the ecl_write() function to write to an ALREADY OPENED eclipse restart file. */ + field_ecl_write1D_fortio( field , restart_fortio); + else + util_abort("%s: internal error file_type = %d - aborting \n", + __func__, file_type); + + if (output_transform) + field_revert_output_transform(field); +} + + +/** + Observe that the output transform is hooked in here, that means + that if you call e.g. the ROFF export function directly, the output + transform will *NOT* be applied. + + Observe that the output transform is done one a copy of the data - + not in place. When the export is complete the field->data will be + unchanged. +*/ + +void field_ecl_write(const field_type * field, + const char * run_path, + const char * file, + void * filestream) { + field_file_format_type export_format = field_config_get_export_format(field->config); + + if (export_format == ECL_FILE) { + fortio_type * restart_fortio = fortio_safe_cast(filestream); + field_export(field , NULL , restart_fortio , export_format , true, NULL); + return; + } + + char * full_path = util_alloc_filename( run_path , file , NULL); + if (util_is_link(full_path)) + util_unlink_existing(full_path); + + field_export(field , full_path , NULL , export_format , true, NULL); + free( full_path ); +} + + + +bool field_initialize(field_type *field , int iens , const char * init_file , rng_type * rng) { + bool ret = false; + if (init_file) { + if (field_fload(field , init_file )) { + field_func_type * init_transform = field_config_get_init_transform(field->config); + /* + Doing the input transform - observe that this is done inplace on + the data, not as the output transform which is done on a copy of + prior to export. + */ + if (init_transform) { + field_apply(field , init_transform); + if (!field_check_finite( field )) + util_exit("Sorry: after applying the init transform field:%s contains nan/inf or similar malformed values.\n" , field_config_get_key( field->config )); + } + ret = true; + } + } + + return ret; +} + + +void field_free(field_type *field) { + if (!field->shared_data) { + free(field->data); + field->data = NULL; + } + free(field); +} + + + +void field_serialize(const field_type * field , node_id_type node_id , const active_list_type * active_list , matrix_type * A , int row_offset , int column) { + const field_config_type *config = field->config; + const int data_size = field_config_get_data_size(config ); + ecl_data_type data_type = field_config_get_ecl_data_type(config); + + enkf_matrix_serialize( field->data , data_size , data_type , active_list , A , row_offset , column); +} + + +void field_deserialize(field_type * field , node_id_type node_id , const active_list_type * active_list , const matrix_type * A , int row_offset , int column) { + const field_config_type *config = field->config; + const int data_size = field_config_get_data_size(config ); + ecl_data_type data_type = field_config_get_ecl_data_type(config); + + enkf_matrix_deserialize( field->data , data_size , data_type , active_list , A , row_offset , column); +} + +static int __get_index(const field_type * field, int i, int j, int k) { + return field_config_keep_inactive_cells(field->config) ? field_config_global_index(field->config , i , j , k) : field_config_active_index(field->config , i , j , k); +} + + +void field_ijk_get(const field_type * field , int i , int j , int k , void * value) { + int index = __get_index(field, i, j, k); + int sizeof_ctype = field_config_get_sizeof_ctype(field->config); + memcpy(value , &field->data[index * sizeof_ctype] , sizeof_ctype); +} + + + +double field_ijk_get_double(const field_type * field, int i , int j , int k) { + int index = __get_index(field, i, j, k); + return field_iget_double( field , index ); +} + + +/** + Takes an active or global index as input, and returns a double. +*/ +double field_iget_double(const field_type * field , int index) { + ecl_data_type data_type = field_config_get_ecl_data_type(field->config); + int sizeof_ctype = field_config_get_sizeof_ctype(field->config); + char __buffer[8]; + char * buffer = &__buffer[0]; + memcpy(buffer , &field->data[index * sizeof_ctype] , sizeof_ctype); + if ( ecl_type_is_double(data_type) ) + return *((double *) buffer); + else if (ecl_type_is_float(data_type) ) { + double double_value; + float float_value; + + float_value = *((float *) buffer); + double_value = float_value; + + return double_value; + } else { + util_abort("%s: failed - wrong internal type \n",__func__); + return -1; + } +} + + +/** + Takes an active or global index as input, and returns a double. +*/ +float field_iget_float(const field_type * field , int index) { + ecl_data_type data_type = field_config_get_ecl_data_type(field->config); + int sizeof_ctype = field_config_get_sizeof_ctype(field->config); + char __buffer[8]; + char * buffer = &__buffer[0]; + memcpy(buffer , &field->data[index * sizeof_ctype] , sizeof_ctype); + if ( ecl_type_is_float(data_type) ) + return *((float *) buffer); + else if (ecl_type_is_double(data_type)) { + double double_value; + float float_value; + + double_value = *((double *) buffer); + float_value = double_value; + + return float_value; + } else { + util_abort("%s: failed - wrong internal type \n",__func__); + return -1; + } +} + + +#define INDEXED_UPDATE_MACRO(t,s,n,index,add) \ +{ \ + int i; \ + if (add) \ + for (i=0; i < (n); i++) \ + (t)[index[i]] += (s)[i]; \ + else \ + for (i=0; i < (n); i++) \ + (t)[index[i]] = (s)[i]; \ +} + + + +static void field_indexed_update(field_type * field, ecl_data_type src_type , int len , const int * index_list , const void * value , bool add) { + ecl_data_type target_type = field_config_get_ecl_data_type(field->config); + + switch (target_type.type) { + case(ECL_FLOAT_TYPE): + { + float * field_data = (float *) field->data; + if (ecl_type_is_double(src_type)) { + double * src_data = (double *) value; + INDEXED_UPDATE_MACRO(field_data , src_data , len , index_list , add); + } else if (ecl_type_is_float(src_type)) { + float * src_data = (float *) value; + INDEXED_UPDATE_MACRO(field_data , src_data , len , index_list , add); + } else + util_abort("%s both existing field - and indexed values must be float / double - aborting\n",__func__); + } + break; + case(ECL_DOUBLE_TYPE): + { + double * field_data = (double *) field->data; + if (ecl_type_is_double(src_type)) { + double * src_data = (double *) value; + INDEXED_UPDATE_MACRO(field_data , src_data , len , index_list , add); + } else if (ecl_type_is_float(src_type)) { + float * src_data = (float *) value; + INDEXED_UPDATE_MACRO(field_data , src_data , len , index_list , add); + } else + util_abort("%s both existing field - and indexed values must be float / double - aborting\n",__func__); + } + break; + default: + util_abort("%s existing field must be of type float/double - aborting \n",__func__); + } +} + + +/** + Copying data from a (PACKED) ecl_kw instance down to a fields data. +*/ + +void field_copy_ecl_kw_data(field_type * field , const ecl_kw_type * ecl_kw) { + const field_config_type * config = field->config; + const int data_size = field_config_get_data_size(config ); + ecl_data_type field_type = field_config_get_ecl_data_type(field->config); + ecl_data_type kw_type = ecl_kw_get_data_type(ecl_kw); + + if (data_size != ecl_kw_get_size(ecl_kw)) { + fprintf(stderr,"\n"); + fprintf(stderr," ** Fatal error - the number of active cells has changed \n"); + fprintf(stderr," ** Grid:%s has %d active cells. \n",field_config_get_grid_name( config ) , data_size); + fprintf(stderr," ** %s loaded from file has %d active cells.\n",field_config_get_key(config), ecl_kw_get_size(ecl_kw)); + fprintf(stderr," ** MINPV / MINPVV problem?? \n"); + util_abort("%s: Aborting \n",__func__ ); + } + + ecl_util_memcpy_typed_data(field->data , ecl_kw_get_void_ptr(ecl_kw) , field_type , kw_type, ecl_kw_get_size(ecl_kw)); +} + + + +/*****************************************************************/ + +bool field_fload_rms(field_type * field , const char * filename, bool keep_inactive) { + { + FILE * stream = util_fopen__( filename , "r"); + if (!stream) + return false; + + fclose( stream ); + } + + { + const char * key = field_config_get_ecl_kw_name(field->config); + rms_file_type * rms_file = rms_file_alloc(filename , false); + rms_tagkey_type * data_tag; + if (field_config_enkf_mode(field->config)) + data_tag = rms_file_fread_alloc_data_tagkey(rms_file , "parameter" , "name" , key); + else { + /** + Setting the key - purely to support converting between + different types of files, without knowing the key. A usable + feature - but not really well defined. + */ + + rms_tag_type * rms_tag = rms_file_fread_alloc_tag(rms_file , "parameter" , NULL , NULL); + const char * parameter_name = rms_tag_get_namekey_name(rms_tag); + field_config_set_key( (field_config_type *) field->config , parameter_name ); + data_tag = rms_tagkey_copyc( rms_tag_get_key(rms_tag , "data") ); + rms_tag_free(rms_tag); + } + + ecl_data_type data_type = rms_tagkey_get_ecl_data_type(data_tag); + if (rms_tagkey_get_size(data_tag) != field_config_get_volume(field->config)) + util_abort("%s: trying to import rms_data_tag from:%s with wrong size - aborting \n",__func__ , filename); + + field_import3D(field , rms_tagkey_get_data_ref(data_tag) , true , keep_inactive, data_type); + rms_tagkey_free(data_tag); + rms_file_free(rms_file); + } + return true; +} + + + +static bool field_fload_ecl_kw(field_type * field, const char * filename, bool keep_inactive) { + const char * key = field_config_get_ecl_kw_name(field->config); + ecl_kw_type * ecl_kw = NULL; + bool fmt_file = false; + + if (!ecl_util_fmt_file(filename, &fmt_file)) + util_abort("%s: could not determine formatted/unformatted status of file:%s \n", + __func__, filename); + + fortio_type * fortio = fortio_open_reader(filename, fmt_file, ECL_ENDIAN_FLIP); + if (!fortio) + return false; + + ecl_kw_fseek_kw(key, true, true, fortio); + ecl_kw = ecl_kw_fread_alloc(fortio); + fortio_fclose(fortio); + + if (field_config_get_volume(field->config) == ecl_kw_get_size(ecl_kw)) + field_import3D(field, ecl_kw_get_void_ptr(ecl_kw), false, keep_inactive, ecl_kw_get_data_type(ecl_kw)); + else + /* Keyword is already packed - e.g. from a restart file. Size is + verified in the _copy function.*/ + field_copy_ecl_kw_data(field, ecl_kw); + + ecl_kw_free(ecl_kw); + return true; +} + + + +/* No type translation possible */ +static bool field_fload_ecl_grdecl(field_type * field , const char * filename, bool keep_inactive ) { + const char * key = field_config_get_ecl_kw_name(field->config); + int size = field_config_get_volume(field->config); + ecl_data_type data_type = field_config_get_ecl_data_type(field->config); + ecl_kw_type * ecl_kw = NULL; + { + FILE * stream = util_fopen__(filename , "r"); + if (stream) { + if (ecl_kw_grdecl_fseek_kw(key , false , stream)) + ecl_kw = ecl_kw_fscanf_alloc_grdecl_data(stream , size , data_type); + else + util_exit("%s: Can not locate %s keyword in %s \n",__func__ , key , filename); + fclose(stream); + + field_import3D(field , ecl_kw_get_void_ptr(ecl_kw) , false , keep_inactive, ecl_kw_get_data_type(ecl_kw)); + ecl_kw_free(ecl_kw); + return true; + } + } + return false; +} + + + + +bool field_fload_typed(field_type * field , const char * filename , field_file_format_type file_type, bool keep_inactive) { + bool loadOK = false; + switch (file_type) { + case(RMS_ROFF_FILE): + loadOK = field_fload_rms(field , filename, keep_inactive ); + break; + case(ECL_KW_FILE): + loadOK = field_fload_ecl_kw(field , filename, keep_inactive ); + break; + case(ECL_GRDECL_FILE): + loadOK = field_fload_ecl_grdecl(field , filename, keep_inactive); + break; + default: + util_abort("%s: file_type:%d not recognized - aborting \n",__func__ , file_type); + } + return loadOK; +} + + +static bool field_fload_custom__(field_type * field , const char * filename , bool keep_inactive) { + if (util_file_readable( filename )) { + field_file_format_type file_type = field_config_guess_file_type( filename ); + if (file_type == UNDEFINED_FORMAT) + util_abort("%s - could not automagically infer type for file: %s\n", __func__ , filename); + + return field_fload_typed(field , filename , file_type, keep_inactive); + } else + return false; +} + + +bool field_fload(field_type * field , const char * filename) { + bool keep_inactive = false; + return field_fload_custom__(field, filename, keep_inactive); +} + + +bool field_fload_keep_inactive(field_type * field , const char * filename) { + bool keep_inactive = true; + return field_fload_custom__(field , filename , keep_inactive); +} + + + +/*****************************************************************/ + + +C_USED void field_iadd(field_type * field1, const field_type * field2) { + field_config_assert_binary(field1->config , field2->config , __func__); + { + const int data_size = field_config_get_data_size( field1->config ); + const ecl_data_type data_type = field_config_get_ecl_data_type( field1->config ); + int i; + + if (ecl_type_is_float(data_type)) { + float * data1 = (float *) field1->data; + const float * data2 = (const float *) field2->data; + for (i = 0; i < data_size; i++) + data1[i] += data2[i]; + } else if (ecl_type_is_double(data_type)) { + double * data1 = (double *) field1->data; + const double * data2 = (const double *) field2->data; + for (i = 0; i < data_size; i++) + data1[i] += data2[i]; + } + } +} + + +C_USED void field_imul(field_type * field1, const field_type * field2) { + field_config_assert_binary(field1->config , field2->config , __func__); + { + const int data_size = field_config_get_data_size(field1->config ); + const ecl_data_type data_type = field_config_get_ecl_data_type(field1->config); + int i; + + if (ecl_type_is_float(data_type)) { + float * data1 = (float *) field1->data; + const float * data2 = (const float *) field2->data; + for (i = 0; i < data_size; i++) + data1[i] *= data2[i]; + } else if (ecl_type_is_double(data_type)) { + double * data1 = (double *) field1->data; + const double * data2 = (const double *) field2->data; + for (i = 0; i < data_size; i++) + data1[i] *= data2[i]; + } + } +} + + +C_USED void field_iaddsqr(field_type * field1, const field_type * field2) { + field_config_assert_binary(field1->config , field2->config , __func__); + { + const int data_size = field_config_get_data_size(field1->config ); + const ecl_data_type data_type = field_config_get_ecl_data_type(field1->config); + int i; + + if (ecl_type_is_float(data_type)) { + float * data1 = (float *) field1->data; + const float * data2 = (const float *) field2->data; + for (i = 0; i < data_size; i++) + data1[i] += data2[i] * data2[i]; + } else if (ecl_type_is_double(data_type)) { + double * data1 = (double *) field1->data; + const double * data2 = (const double *) field2->data; + for (i = 0; i < data_size; i++) + data1[i] += data2[i] * data2[i]; + } + } +} + + +void field_scale(field_type * field, double scale_factor) { + field_config_assert_unary(field->config, __func__); + { + const int data_size = field_config_get_data_size(field->config ); + const ecl_data_type data_type = field_config_get_ecl_data_type(field->config); + int i; + + if (ecl_type_is_float(data_type)) { + float * data = (float *) field->data; + for (i = 0; i < data_size; i++) + data[i] *= scale_factor; + } else if (ecl_type_is_double(data_type)) { + double * data = (double *) field->data; + for (i = 0; i < data_size; i++) + data[i] *= scale_factor; + } + } +} + + +C_USED void field_isqrt(field_type * field) { + field_apply(field , sqrtf); +} + +/** + Here, index_key is i a tree digit string with the i, j and k indicies of + the requested block separated by comma. E.g., 1,1,1. + + The string is supposed to contain indices in the range [1...nx] , + [1..ny] , [1...nz], they are immediately converted to C-based zero + offset indices. +*/ +C_USED bool field_user_get(const field_type * field, const char * index_key, int report_step , double * value) +{ + const bool internal_value = false; + bool valid = false; + int i=0,j=0,k=0; + int parse_user_key = field_config_parse_user_key(field->config , index_key , &i, &j , &k); + + + if (parse_user_key == 0) { + int active_index = field_config_active_index(field->config , i,j,k); + *value = field_iget_double(field, active_index); + valid = true; + } else { + if (parse_user_key == 1) + fprintf(stderr,"Failed to parse \"%s\" as three integers \n",index_key); + else if (parse_user_key == 2) + fprintf(stderr," ijk: %d , %d, %d is invalid \n",i+1 , j + 1 , k + 1); + else if (parse_user_key == 3) + fprintf(stderr," ijk: %d , %d, %d is an inactive cell. \n",i+1 , j + 1 , k + 1); + else + util_abort("%s: internal error -invalid value:%d \n", + __func__, parse_user_key); + *value = 0.0; + valid = false; + } + + if (!internal_value && valid) { + field_func_type * output_transform = field_config_get_output_transform(field->config); + if (output_transform != NULL) + *value = output_transform( *value ); + /* Truncation - ignored for now */ + } + return valid; +} + + + +#define INFLATE(inf,std,min) \ +{ \ + for (int i=0; i < data_size; i++) { \ + if (std_data[i] > 0) \ + inflation_data[i] = util_float_max( 1.0 , min_std_data[i] / std_data[i]); \ + else \ + inflation_data[i] = 1.0; \ + } \ +} + + +C_USED void field_set_inflation(field_type * inflation , const field_type * std , const field_type * min_std) { + const field_config_type * config = inflation->config; + ecl_data_type data_type = field_config_get_ecl_data_type( config ); + const int data_size = field_config_get_data_size( config ); + + if (ecl_type_is_float(data_type)) { + float * inflation_data = (float *) inflation->data; + const float * std_data = (const float *) std->data; + const float * min_std_data = (const float *) min_std->data; + + INFLATE(inflation_data , std_data , min_std_data ); + + } else if (ecl_type_is_double(data_type)) { + double * inflation_data = (double *) inflation->data; + const double * std_data = (const double *) std->data; + const double * min_std_data = (const double *) min_std->data; + + INFLATE(inflation_data , std_data , min_std_data ); + } +} +#undef INFLATE + + + +/******************************************************************/ +/* Anonumously generated functions used by the enkf_node object */ +/******************************************************************/ + +/* + These two functions assume float/double storage; will not work with + field which is internally based on char *. + + MATH_OPS(field) +*/ +UTIL_SAFE_CAST_FUNCTION(field , FIELD) +UTIL_SAFE_CAST_FUNCTION_CONST(field , FIELD) +UTIL_IS_INSTANCE_FUNCTION(field , FIELD) +VOID_ALLOC(field) +VOID_FREE(field) +VOID_ECL_WRITE (field) +VOID_COPY (field) +VOID_INITIALIZE(field); +VOID_USER_GET(field) +VOID_READ_FROM_BUFFER(field) +VOID_WRITE_TO_BUFFER(field) +VOID_CLEAR(field) +VOID_SERIALIZE(field) +VOID_DESERIALIZE(field) +VOID_SET_INFLATION(field) +VOID_IADD(field) +VOID_SCALE(field) +VOID_IADDSQR(field) +VOID_IMUL(field) +VOID_ISQRT(field) +VOID_FLOAD(field) diff --git a/libres/lib/enkf/field_config.cpp b/libres/lib/enkf/field_config.cpp new file mode 100644 index 00000000000..311735405bc --- /dev/null +++ b/libres/lib/enkf/field_config.cpp @@ -0,0 +1,820 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'field_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 +#include + +#include + +#include +#include +#include +#include + +/** + About transformations and truncations + ------------------------------------- + + The values of the fields data can be automagically manipulated through two methods: + + * You can specify a min and a max value which will serve as truncation. + + * You can specify transformation functions which are applied to the field as follows: + + init_transform: This function is applied to the field when the + field is loaded the first time, i.e. initialized. It is *NOT* + applied under subsequent loads of dynamic fields during the + execution. + + output_transform: This function is applied to the field before it + is exported to eclipse. + + input_transform: This function is applied each time a field is + loaded in from the forward model; i.e. this transformation + applies to dynamic fields. + + + + _______________________________ ___ + / \ /|\ + | Forward model (i.e. ECLIPSE) | | + | generates dynamic fields like | | + | PRESSURE and SATURATIONS | | + \_______________________________/ | This code is run + | | every time a field + | | is loaded FROM the + \|/ | forward model into + | | EnKF. + ________|_________ | + / \ | + | Input transform | | + \__________________/ | + | | + | | + \|/ | + | | + ________________|__________________ _\|/_ +_______________ ___________ / \ + \ / \ | The internal representation | + Geo Modelling | | init- | | of the field. This (should) | + creates a |==>===============>==| transform |===>===| be a normally distributed | + realization | | | | variable suitable for updates | +_______________/ \___________/ | with EnKF. | + \___________________________________/ ___ +|<---- This path is ONLY executed during INIT ------->| | /|\ + Observe that there is no truncation \|/ | + on load. _________|__________ | + / \ | This code is run + | Output transform | | every time a field + \____________________/ | is exported from + | | enkf to the forward + \|/ | model - i.e. ECLIPSE. + _________|__________ | + / \ | + | Truncate min/max | | + \____________________/ | + | | + \|/ | + _________|__________ | + / \ | + | FORWARD MODEL | | + \____________________/ _\|/_ + + + + + + +*/ + +/*Observe the following convention: + + global_index: [0 , nx*ny*nz) + active_index: [0 , nactive) +*/ + +#define FIELD_CONFIG_ID 78269 + +struct field_config_struct { + UTIL_TYPE_ID_DECLARATION; + + char * ecl_kw_name; /* Name/key ... */ + int data_size , nx,ny,nz; /* The number of elements in the three directions. */ + bool keep_inactive_cells; /* Whether the data contains only active cells or active and inactive cells */ + ecl_grid_type * grid; /* A shared reference to the grid this field is defined on. */ + bool private_grid; + + int truncation; /* How the field should be trunacted before exporting for simulation, and for the inital import. OR'd combination of truncation_type from enkf_types.h*/ + double min_value; /* The min value used in truncation. */ + double max_value; /* The maximum value used in truncation. */ + + field_file_format_type export_format; + field_file_format_type import_format; + ecl_data_type internal_data_type; + bool __enkf_mode; /* See doc of functions field_config_set_key() / field_config_enkf_OFF() */ + bool write_compressed; + + field_type_enum type; + field_type * min_std; + /*****************************************************************/ + field_trans_table_type * trans_table; /* Internalize a (pointer to) a table of the available transformation functions. */ + field_func_type * output_transform; /* Function to apply to the data before they are exported - NULL: no transform. */ + field_func_type * init_transform; /* Function to apply on the data when they are loaded the first time - i.e. initialized. NULL : no transform*/ + field_func_type * input_transform; /* Function to apply on the data when they are loaded from the forward model - i.e. for dynamic data. */ + + char * output_transform_name; + char * init_transform_name; + char * input_transform_name; +}; + + +UTIL_IS_INSTANCE_FUNCTION(field_config , FIELD_CONFIG_ID) + +/*****************************************************************/ + + +void field_config_set_ecl_data_type(field_config_type * config , ecl_data_type data_type) { + memcpy(&config->internal_data_type, &data_type, sizeof data_type); +} + + +/** + This function takes a field_file_format_type variable, and returns + a string containing a default extension for files of this type. For + ecl_kw_file it will return NULL, i.e. no default extension. + + rms_roff_file => ROFF + ecl_grdecl_file => GRDECL + ecl_kw_file_xxx => NULL + + It will return UPPERCASE or lowercase depending on the value of the + second argument. +*/ + + +const char * field_config_default_extension(field_file_format_type file_type, bool upper_case) { + if (file_type == RMS_ROFF_FILE) { + if (upper_case) + return "ROFF"; + else + return "roff"; + } else if (file_type == ECL_GRDECL_FILE) { + if (upper_case) + return "GRDECL"; + else + return "grdecl"; + } else + return NULL; +} + + + +field_file_format_type field_config_default_export_format(const char * filename) { + field_file_format_type export_format = FILE_FORMAT_NULL; + if (filename != NULL) { + export_format = ECL_KW_FILE_ALL_CELLS; /* Suitable for PERMX/PORO/... ; when this export format is + used IMPORT must be used in the datafile instead of + INCLUDE. This gives faster ECLIPSE startup time, but is + (unfortunately) quite unstandard. */ + + char * extension; + util_alloc_file_components(filename , NULL,NULL,&extension); + if (extension != NULL) { + util_strupr(extension); + if (strcmp(extension , "GRDECL") == 0) + export_format = ECL_GRDECL_FILE; + else if (strcmp(extension , "ROFF") == 0) + export_format = RMS_ROFF_FILE; + + free(extension); + } + + } + return export_format; +} + + + + + + + + +/** +This function takes in a filename and tries to guess the type of the +file. It can determine the following three types of files: + + ecl_kw_file: This is a file containg ecl_kw instances in the form found + in eclipse restart files. + + rms_roff_file: An rms roff file - obviously. + + ecl_grdecl_file: This is a file containing a parameter of the form + found in eclipse grid declaration files, i.e. formatted, one + keyword and all elements (active and not). + + The latter test is the weakest. Observe that the function will + happily return unkown_file if none of these types are recognized, + i.e. it is *essential* to check the return value. + +*/ +field_file_format_type field_config_guess_file_type(const char * filename ) { + bool fmt_file = util_fmt_bit8(filename ); + FILE * stream = util_fopen(filename , "r"); + fortio_type * fortio = fortio_alloc_FILE_wrapper(NULL , ECL_ENDIAN_FLIP , fmt_file , false , stream); + field_file_format_type file_type; + + if (ecl_kw_is_kw_file(fortio)) + file_type = ECL_KW_FILE; + else if (rms_file_is_roff(stream)) + file_type = RMS_ROFF_FILE; + else if (ecl_kw_grdecl_fseek_next_kw(stream)) /* This is the weakest test - and should be last in a cascading if / else hierarchy. */ + file_type = ECL_GRDECL_FILE; + else + file_type = UNDEFINED_FORMAT; /* MUST Check on this return value */ + + fortio_free_FILE_wrapper( fortio ); + fclose(stream); + return file_type; +} + +field_file_format_type field_config_get_export_format(const field_config_type * field_config) { + return field_config->export_format; +} + +/** + Will return the name of the init_transform function, or NULL if no + init_transform function has been registered. +*/ + + +const char * field_config_get_init_transform_name( const field_config_type * field_config ) { + return field_config->init_transform_name; +} + + +const char * field_config_get_output_transform_name( const field_config_type * field_config ) { + return field_config->output_transform_name; +} + + +/** + IFF the @private_grid parameter is true, the field_config instance + will take ownership of grid, i.e. freeing it in + field_config_free(). + + The field_config object exports a field_config_set_grid() function, + but that is actually quite misleading. If this function is called + during a run there are many other dependencies which must also be + updated, which are not handled. +*/ + + +void field_config_set_grid(field_config_type * config, ecl_grid_type * grid , bool private_grid) { + if ((config->private_grid) && (config->grid != NULL)) + ecl_grid_free( config->grid ); + + config->grid = grid; + config->private_grid = private_grid; + + ecl_grid_get_dims(grid , &config->nx , &config->ny , &config->nz , NULL); + config->data_size = field_config_get_data_size_from_grid(config); +} + + + + +const char * field_config_get_grid_name( const field_config_type * config) { + return ecl_grid_get_name( config->grid ); +} + + + +/* + The return value from this function is hardly usable. +*/ +field_config_type * field_config_alloc_empty( const char * ecl_kw_name , ecl_grid_type * ecl_grid , field_trans_table_type * trans_table, bool keep_inactive_cells ) { + + field_config_type * config = (field_config_type *)util_malloc(sizeof *config); + UTIL_TYPE_ID_INIT( config , FIELD_CONFIG_ID); + + config->keep_inactive_cells = keep_inactive_cells; + config->ecl_kw_name = util_alloc_string_copy( ecl_kw_name ); + config->private_grid = false; + config->__enkf_mode = true; + config->grid = NULL; + config->write_compressed = true; + config->type = UNKNOWN_FIELD_TYPE; + + config->output_transform = NULL; + config->input_transform = NULL; + config->init_transform = NULL; + config->output_transform_name = NULL; + config->input_transform_name = NULL; + config->init_transform_name = NULL; + + config->truncation = TRUNCATE_NONE; + config->min_std = NULL; + config->trans_table = trans_table; + + field_config_set_grid(config , ecl_grid , false); /* The grid is (currently) set on allocation and can NOT be updated afterwards. */ + field_config_set_ecl_data_type( config , ECL_FLOAT ); /* This is the internal type - currently not exported any API to change it. */ + return config; +} + + + + +static void field_config_set_init_transform( field_config_type * config , const char * __init_transform_name ) { + const char * init_transform_name = NULL; + if (field_trans_table_has_key( config->trans_table , __init_transform_name)) + init_transform_name = __init_transform_name; + else if (__init_transform_name != NULL) { + fprintf(stderr , "Sorry: the field transformation function:%s is not recognized \n\n",__init_transform_name); + field_trans_table_fprintf(config->trans_table , stderr); + util_exit("Exiting ... \n"); + } + + config->init_transform_name = util_realloc_string_copy( config->init_transform_name , init_transform_name ); + if (init_transform_name != NULL) + config->init_transform = field_trans_table_lookup( config->trans_table , init_transform_name); + else + config->init_transform = NULL; +} + + +static void field_config_set_output_transform( field_config_type * config , const char * __output_transform_name ) { + const char * output_transform_name = NULL; + if (field_trans_table_has_key( config->trans_table , __output_transform_name)) + output_transform_name = __output_transform_name; + else if (__output_transform_name) { + fprintf(stderr , "Sorry: the field transformation function:%s is not recognized \n\n",__output_transform_name); + field_trans_table_fprintf(config->trans_table , stderr); + util_exit("Exiting ... \n"); + } + + config->output_transform_name = util_realloc_string_copy( config->output_transform_name , output_transform_name ); + if (output_transform_name != NULL) + config->output_transform = field_trans_table_lookup( config->trans_table , output_transform_name); + else + config->output_transform = NULL; +} + + +static void field_config_set_input_transform( field_config_type * config , const char * __input_transform_name ) { + const char * input_transform_name = NULL; + if (field_trans_table_has_key( config->trans_table , __input_transform_name)) + input_transform_name = __input_transform_name; + else if (__input_transform_name != NULL) { + fprintf(stderr , "Sorry: the field transformation function:%s is not recognized \n\n",__input_transform_name); + field_trans_table_fprintf(config->trans_table , stderr); + util_exit("Exiting ... \n"); + } + + + config->input_transform_name = util_realloc_string_copy( config->input_transform_name , input_transform_name ); + if (input_transform_name != NULL) + config->input_transform = field_trans_table_lookup( config->trans_table , input_transform_name); + else + config->input_transform = NULL; +} + + +void field_config_update_parameter_field( field_config_type * config , int truncation, double min_value , double max_value, + field_file_format_type export_format , /* This can be guessed with the field_config_default_export_format( ecl_file ) function. */ + const char * init_transform , const char * output_transform ) { + field_config_set_truncation( config , truncation , min_value , max_value ); + config->type = ECLIPSE_PARAMETER; + + config->export_format = export_format; + config->import_format = UNDEFINED_FORMAT; /* Guess from filename when loading. */ + + config->input_transform = NULL; + + field_config_set_input_transform( config , NULL ); + field_config_set_init_transform( config , init_transform ); + field_config_set_output_transform( config , output_transform ); +} + + +void field_config_update_general_field( field_config_type * config , int truncation, double min_value , double max_value, + field_file_format_type export_format , /* This can be guessed with the field_config_default_export_format( ecl_file ) function. */ + const char * init_transform , + const char * input_transform , + const char * output_transform ) { + field_config_set_truncation( config , truncation , min_value , max_value ); + config->type = GENERAL; + + config->export_format = export_format; + config->import_format = UNDEFINED_FORMAT; /* Guess from filename when loading. */ + + field_config_set_input_transform( config , input_transform ); + field_config_set_init_transform( config , init_transform ); + field_config_set_output_transform( config , output_transform ); +} + + +/** + Requirements: + + ECLIPSE_PARAMETER: export_format != UNDEFINED_FORMAT + + ECLIPSE_RESTART : Validation can be finalized at the enkf_config_node level. + + GENERAL : export_format != UNDEFINED_FORMAT +*/ + +bool field_config_is_valid( const field_config_type * field_config ) { + bool valid = true; + + switch( field_config->type ) { + case ECLIPSE_PARAMETER: + if (field_config->export_format == UNDEFINED_FORMAT) + valid = false; + break; + case ECLIPSE_RESTART: + break; + case GENERAL: + if (field_config->export_format == UNDEFINED_FORMAT) + valid = false; + break; + default: + util_abort("%s: Internal inconsistency in field config \n",__func__); + } + return valid; + +} + + +field_type_enum field_config_get_type( const field_config_type * config) { + return config->type; +} + + +/* + Observe that the indices are zero-based, in contrast to those used + by eclipse which are based on one. + + This function will return an index in the interval: [0...nactive), + and -1 if i,j,k correspond to an inactive cell. +*/ + + +int field_config_active_index(const field_config_type * config , int i , int j , int k) { + return ecl_grid_get_active_index3( config->grid , i,j,k); +} + +int field_config_global_index(const field_config_type * config, int i, int j, int k) { + return ecl_grid_get_global_index3( config->grid , i,j,k); +} + + +/** + This function checks that i,j,k are in the intervals [0..nx), + [0..ny) and [0..nz). It does *NOT* check if the corresponding + index is active. +*/ + +bool field_config_ijk_valid(const field_config_type * config , int i , int j , int k) { + return ecl_grid_ijk_valid(config->grid , i,j,k); +} + + +/** + This function checks that i,j,k are in the intervals [0..nx), + [0..ny) and [0..nz) AND that the corresponding cell is active. If + the function returns false it is impossible to differentiate + between (i,j,k) values which are out of bounds and an inactive + cell. +*/ + +bool field_config_ijk_active(const field_config_type * config , int i , int j , int k) { + if (ecl_grid_ijk_valid(config->grid , i,j,k)) { + int active_index = ecl_grid_get_active_index3( config->grid , i , j , k); + + if (active_index >= 0) + return true; + else + return false; + } else + return false; +} + + +void field_config_set_truncation(field_config_type * config , int truncation, double min_value, double max_value) { + config->truncation = truncation; + config->min_value = min_value; + config->max_value = max_value; +} + + + + +int field_config_get_truncation_mode(const field_config_type * config ) { + return config->truncation; +} + +double field_config_get_truncation_min( const field_config_type * config ) { + return config->min_value; +} + +double field_config_get_truncation_max( const field_config_type * config ) { + return config->max_value; +} + + + +void field_config_free(field_config_type * config) { + free(config->ecl_kw_name); + free(config->input_transform_name); + free(config->output_transform_name); + free(config->init_transform_name); + if ((config->private_grid) && (config->grid != NULL)) ecl_grid_free( config->grid ); + free(config); +} + + + +int field_config_get_volume(const field_config_type * config) { + return config->nx * config->ny * config->nz; +} + + +ecl_data_type field_config_get_ecl_data_type(const field_config_type * config) { + return config->internal_data_type; +} + +int field_config_get_data_size_from_grid(const field_config_type * config) { + return config->keep_inactive_cells ? ecl_grid_get_global_size(config->grid) : ecl_grid_get_active_size(config->grid); +} + +int field_config_get_byte_size(const field_config_type * config) { + int num_cells = field_config_get_data_size_from_grid(config); + return num_cells * field_config_get_sizeof_ctype(config); +} + + +int field_config_get_sizeof_ctype(const field_config_type * config) { + return ecl_type_get_sizeof_ctype(config->internal_data_type); +} + + + +/** + Returns true / false whether a cell is active. +*/ +bool field_config_active_cell(const field_config_type * config , int i , int j , int k) { + int active_index = field_config_active_index(config , i,j,k); + if (active_index >= 0) + return true; + else + return false; +} + + + + void field_config_get_dims(const field_config_type * config , int *nx , int *ny , int *nz) { + *nx = config->nx; + *ny = config->ny; + *nz = config->nz; +} + + +int field_config_get_nx(const field_config_type * config ) { + return config->nx; +} + +int field_config_get_ny(const field_config_type * config ) { + return config->ny; +} + +int field_config_get_nz(const field_config_type * config ) { + return config->nz; +} + + + + + + + +/** + The field_config and field objects are mainly written for use in + the enkf application. In that setting a field instance is *NOT* + allowed to write on it's field_config object. + + However, when used in a stand-alone application, i.e. in the + field_convert program, it is desirable for the field object to be + allowed to write to / update the field_config object. In an attempt + to make this reasonably safe you must first call + field_config_enkf_OFF() to signal that you know what you are doing. + + After you have called field_config_enkf_OFF() you can subsequently + call field_config_set_key() to change the key of the field_config + object. This will typically be interesting when an unknown file is + loaded. + + Currently only the roff loader supports set operations on the + key. Also it is essential to observe that this will break **HARD** + is the file contains several parameters +*/ + + +void field_config_set_key(field_config_type * config , const char *key) { + if (config->__enkf_mode) + util_abort("%s: internal error - must call field_config_enkf_OFF() prior to calling: %s()\n",__func__ , __func__); + /* + Should be locked to protect against concurrent access. + */ + config->ecl_kw_name = util_realloc_string_copy(config->ecl_kw_name , key); +} + +const char * field_config_get_key(const field_config_type * field_config) { + return field_config->ecl_kw_name; +} + +bool field_config_keep_inactive_cells(const field_config_type * config) { + return config->keep_inactive_cells; +} + +void field_config_enkf_OFF(field_config_type * config) { + if (config->__enkf_mode) + fprintf(stderr , "** Warning: turning off EnKF mode for field:%s - you better know what you are doing! **\n",config->ecl_kw_name); + config->__enkf_mode = false; +} + + +bool field_config_enkf_mode(const field_config_type * config) { return config->__enkf_mode; } + + +field_func_type * field_config_get_output_transform(const field_config_type * config) { + return config->output_transform; +} + + +field_func_type * field_config_get_init_transform(const field_config_type * config) { + return config->init_transform; +} + + +/* + This function asserts that a unary function can be applied + to the field - i.e. that the underlying data_type is ecl_float or ecl_double. +*/ +void field_config_assert_unary( const field_config_type * field_config , const char * caller) { + const ecl_data_type data_type = field_config_get_ecl_data_type(field_config); + if (ecl_type_is_float(data_type) || ecl_type_is_double(data_type)) + return; + else + util_abort("%s: error in:%s unary functions can only be applied on fields of type ecl_float / ecl_double \n",__func__ , caller); +} + + +/* + Asserts that two fields can be combined in a binary operation. +*/ +void field_config_assert_binary( const field_config_type * config1 , const field_config_type * config2 , const char * caller) { + field_config_assert_unary(config1 , caller); + const ecl_data_type data_type1 = config1->internal_data_type; + const ecl_data_type data_type2 = config2->internal_data_type; + const int size1 = config1->data_size; + const int size2 = config2->data_size; + + if (!ecl_type_is_equal(data_type1, data_type2) || size1 != size2) + util_abort("%s: fields not equal enough - failure in:%s \n",__func__ , caller); +} + + + + + + +/** + Parses a string of the type "1,5,6", and returns the indices i,j,k + by reference. The return value of the function as a whole is + whether the string constitutes a valid cell: + + 0: All is OK. + 1: The string could not pe parsed to three valid integers. + 2: ijk are not in the grid. + 3: ijk correspond to an inactive cell. + + In cases 2 & 3 the i,j,k are valid (in the string-parsing sense). + The input string is assumed to have offset one, and the return + values (by reference) are offset zero. +*/ + + +bool field_config_parse_user_key__( const char * index_key , int *i , int *j , int *k) { + int length; + { + int_vector_type * indices = string_util_alloc_value_list( index_key ); + length = int_vector_size( indices ); + + if (length == 3) { + *i = int_vector_iget( indices , 0) - 1; + *j = int_vector_iget( indices , 1) - 1; + *k = int_vector_iget( indices , 2) - 1; + } + + int_vector_free( indices ); + } + if (length == 3) + return true; + else + return false; +} + + + +int field_config_parse_user_key(const field_config_type * config, const char * index_key , int *i , int *j , int *k) { + int return_value = 0; + + if (field_config_parse_user_key__( index_key , i , j , k)) { + if(field_config_ijk_valid(config, *i, *j, *k)) { + int active_index = field_config_active_index(config , *i,*j,*k); + if (active_index < 0) + return_value = 3; /* ijk corresponds to an inactive cell. */ + } else + return_value = 2; /* ijk is outside the grid. */ + } else + return_value = 1; /* Could not be parsed to three integers. */ + + return return_value; +} + + + +ecl_grid_type * field_config_get_grid(const field_config_type * config) { return config->grid; } + + +void field_config_fprintf_config( const field_config_type * config , + enkf_var_type var_type , + const char * outfile , + const char * infile , + const char * min_std_file , + FILE * stream) { + + if (var_type == PARAMETER) { + fprintf( stream , CONFIG_VALUE_FORMAT , PARAMETER_KEY ); + fprintf( stream , CONFIG_VALUE_FORMAT , outfile ); + } else { + if (true) + /* This is an ECLIPSE dynamic field. */ + fprintf( stream , CONFIG_VALUE_FORMAT , DYNAMIC_KEY ); + else { + /* Dynamic fields which are not ECLIPSE solution fields - not really very well supported. */ + fprintf( stream , CONFIG_VALUE_FORMAT , GENERAL_KEY ); + fprintf( stream , CONFIG_VALUE_FORMAT , outfile ); + fprintf( stream , CONFIG_VALUE_FORMAT , infile ); + } + } + + if (config->init_transform != NULL) + fprintf( stream , CONFIG_OPTION_FORMAT , INIT_TRANSFORM_KEY , config->init_transform_name ); + + if (config->output_transform != NULL) + fprintf( stream , CONFIG_OPTION_FORMAT , OUTPUT_TRANSFORM_KEY , config->output_transform_name ); + + if (config->input_transform != NULL) + fprintf( stream , CONFIG_OPTION_FORMAT , INPUT_TRANSFORM_KEY , config->input_transform_name ); + + if (min_std_file != NULL) + fprintf( stream , CONFIG_OPTION_FORMAT , MIN_STD_KEY , min_std_file ); + + if (config->truncation & TRUNCATE_MIN) + fprintf( stream , CONFIG_FLOAT_OPTION_FORMAT , MIN_KEY , config->min_value ); + + if (config->truncation & TRUNCATE_MAX) + fprintf( stream , CONFIG_FLOAT_OPTION_FORMAT , MAX_KEY , config->max_value ); +} + + +/*****************************************************************/ +UTIL_SAFE_CAST_FUNCTION(field_config , FIELD_CONFIG_ID) +UTIL_SAFE_CAST_FUNCTION_CONST(field_config , FIELD_CONFIG_ID) +CONFIG_GET_ECL_KW_NAME(field); +GET_DATA_SIZE(field) +VOID_GET_DATA_SIZE(field) +VOID_FREE(field_config) diff --git a/libres/lib/enkf/field_convert.cpp b/libres/lib/enkf/field_convert.cpp new file mode 100644 index 00000000000..d258ea1da4e --- /dev/null +++ b/libres/lib/enkf/field_convert.cpp @@ -0,0 +1,68 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'field_convert.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 + + +void usage(const char * cmd) { + printf("%s: GRID_FILE OUTPATH field1 field2 field3 ....\n",cmd); + exit(1); +} + + +int main(int argc , char ** argv) { + if (argc < 4) usage(argv[0]); + { + const char * grid_file = argv[1]; + const char * out_path = argv[2]; + const char ** file_list = (const char **) &argv[3]; + int num_files = argc - 3; + int ifile; + field_file_format_type file_type; + + ecl_grid_type * ecl_grid = ecl_grid_alloc(grid_file , true); + field_config_type *field_config = field_config_alloc_dynamic("XX" , NULL , NULL , ecl_grid); + field_type * field = field_alloc(field_config); + field_config_enkf_OFF(field_config); + + util_make_path(out_path); + file_type = field_config_manual_file_type("Export files to type: " , false); + printf("num_files:%d \n",num_files); + for (ifile = 0; ifile < num_files; ifile++) { + char * base_name; + char * target_file; + util_alloc_file_components(file_list[ifile] , NULL , &base_name , NULL); + target_file = util_alloc_filename(out_path , base_name , field_config_default_extension(file_type , true)); + field_fload(field , file_list[ifile] , true); + printf("Converting: %s -> %s \n",file_list[ifile] , target_file); + field_export(field , target_file , file_type); + free(target_file); + free(base_name); + } + + + field_free(field); + field_config_free(field_config); + ecl_grid_free(ecl_grid); + } + + +} diff --git a/libres/lib/enkf/field_trans.cpp b/libres/lib/enkf/field_trans.cpp new file mode 100644 index 00000000000..02a86c33575 --- /dev/null +++ b/libres/lib/enkf/field_trans.cpp @@ -0,0 +1,287 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'field_trans.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. +*/ + +/* + This file implements a number of functions used for init and output + transformations of fields. The prototype for these functions is very + simple: "one float in - one float out". + + It is mainly implemented in this file, so that it will be easy to + adde new transformation functions without diving into the the full + field / field_config complexity. + + Documentation on how to add a new transformation function is at the + bottom of the file. +*/ +#include +#include + +#include + +#include +/*****************************************************************/ + +struct field_trans_table_struct { + bool case_sensitive; + hash_type * function_table; +}; + + + +typedef struct { + char * key; + char * description; + field_func_type * func; +} field_func_node_type; + +/*****************************************************************/ + +static field_func_node_type * field_func_node_alloc(const char * key , const char * description , field_func_type * func) { + field_func_node_type * node = (field_func_node_type *)util_malloc( sizeof * node ); + + node->key = util_alloc_string_copy( key ); + node->description = util_alloc_string_copy( description ); + node->func = func; + + return node; +} + + +static void field_func_node_free(field_func_node_type * node) { + free(node->key); + free( node->description ); + free(node); +} + + +static void field_func_node_free__(void * node) { + field_func_node_free( (field_func_node_type *) node); +} + + +static void field_func_node_fprintf(const field_func_node_type * node , FILE * stream) { + if (node->description != NULL) + fprintf(stream , "%16s: %s \n",node->key , node->description); + else + fprintf(stream , "%16s: No description \n",node->key ); +} + + + + + + +/*****************************************************************/ + +void field_trans_table_add(field_trans_table_type * table , const char * _key , const char * description , field_func_type * func) { + char * key; + + if (table->case_sensitive) + key = util_alloc_string_copy( _key ); + else + key = util_alloc_strupr_copy( _key ); + + { + field_func_node_type * node = field_func_node_alloc( key , description , func ); + hash_insert_hash_owned_ref(table->function_table , key , node , field_func_node_free__); + } + free(key); +} + + +void field_trans_table_fprintf(const field_trans_table_type * table , FILE * stream) { + hash_iter_type * iter = hash_iter_alloc(table->function_table); + const char * key = hash_iter_get_next_key(iter); + fprintf(stream,"==========================================================================================\n"); + fprintf(stream,"Available transformations: \n"); + while (key != NULL) { + field_func_node_type * func_node = (field_func_node_type *)hash_get(table->function_table , key); + field_func_node_fprintf(func_node , stream); + key = hash_iter_get_next_key(iter); + } + fprintf(stream,"==========================================================================================\n"); + hash_iter_free(iter); +} + + + +/* + This function takes a key input, and returns a pointer to the + corresponding function. The function will fail if the key is not + recognized. +*/ + + +field_func_type * field_trans_table_lookup(field_trans_table_type * table , const char * _key) { + field_func_type * func; + char * key; + + if (table->case_sensitive) + key = util_alloc_string_copy(_key); + else + key = util_alloc_strupr_copy(_key); + + if (hash_has_key(table->function_table , key)) { + field_func_node_type * func_node = (field_func_node_type *)hash_get(table->function_table , key); + func = func_node->func; + } else { + fprintf(stderr , "Sorry: the field transformation function:%s is not recognized \n\n",key); + field_trans_table_fprintf(table , stderr); + util_exit("Exiting ... \n"); + func = NULL; /* Compiler shut up. */ + } + free( key ); + return func; +} + + +/** + Will return false if _key == NULL +*/ +bool field_trans_table_has_key(field_trans_table_type * table , const char * _key) { + bool has_key = false; + + if (_key != NULL) { + char * key; + if (table->case_sensitive) + key = util_alloc_string_copy(_key); + else + key = util_alloc_strupr_copy(_key); + + has_key = hash_has_key( table->function_table , key); + free(key); + } + + return has_key; +} + + +void field_trans_table_free(field_trans_table_type * table ) { + hash_free( table->function_table ); + free( table ); +} + + + + +/*****************************************************************/ +/*****************************************************************/ +/* Here comes the actual functions. To add a new function: */ +/* */ +/* 1. Write the function - as a float in - float out. */ +/* 2. Register the function in field_trans_table_alloc(). */ +/* */ +/*****************************************************************/ + +/*****************************************************************/ +/* Rubakumar specials: start */ +#define PERMX_MEAN 100 +#define PERMX_STD 1 + +#define PERMZ_MEAN 100 +#define PERMZ_STD 1 + +#define PORO_MEAN 100 +#define PORO_STD 1 + + +static float normalize(float x , float mean , float std) { + return (x - mean) / std; +} + +static float denormalize(float x , float mean , float std) { + return (x * std) + mean; +} + +static float normalize_permx(float x) { + return normalize(x , PERMX_MEAN , PERMX_STD); +} + +static float denormalize_permx(float x) { + return denormalize(x , PERMX_MEAN , PERMX_STD); +} + +static float normalize_permz(float x) { + return normalize(x , PERMZ_MEAN , PERMZ_STD); +} + +static float denormalize_permz(float x) { + return denormalize(x , PERMZ_MEAN , PERMZ_STD); +} + +static float normalize_poro(float x) { + return normalize(x , PORO_MEAN , PORO_STD); +} + +static float denormalize_poro(float x) { + return denormalize(x , PORO_MEAN , PORO_STD); +} + +/* Rubakumar specials: end */ +/*****************************************************************/ + + +static float field_trans_pow10(float x) { + return powf(10.0 , x); +} + + +static float trunc_pow10f(float x) { + return util_float_max(powf(10.0 , x) , 0.001); +} + +#define LN_SHIFT 0.0000001 +static float field_trans_ln0( float x ) { + return logf( x + LN_SHIFT ); +} + +static float field_trans_exp0( float x ) { + return expf( x ) - LN_SHIFT; +} +#undef LN_SHIFT + + + +field_trans_table_type * field_trans_table_alloc() { + field_trans_table_type * table = (field_trans_table_type *)util_malloc( sizeof * table); + table->function_table = hash_alloc(); + field_trans_table_add( table , "POW10" , "This function will raise x to the power of 10: y = 10^x." , field_trans_pow10); + field_trans_table_add( table , "TRUNC_POW10" , "This function will raise x to the power of 10 - and truncate lower values at 0.001." , trunc_pow10f); + field_trans_table_add( table , "LOG" , "This function will take the NATURAL logarithm of x: y = ln(x)" , logf); + field_trans_table_add( table , "LN" , "This function will take the NATURAL logarithm of x: y = ln(x)" , logf); + field_trans_table_add( table , "LOG10" , "This function will take the log10 logarithm of x: y = log10(x)" , log10f); + field_trans_table_add( table , "EXP" , "This function will calculate y = exp(x) " , expf); + field_trans_table_add( table , "LN0" , "This function will calculate y = ln(x + 0.000001)" , field_trans_ln0); + field_trans_table_add( table , "EXP0" , "This function will calculate y = exp(x) - 0.000001" , field_trans_exp0); + + //----------------------------------------------------------------- + // Rubakumar specials: + field_trans_table_add( table , "NORMALIZE_PERMX" , "..." , normalize_permx); + field_trans_table_add( table , "DENORMALIZE_PERMX" , "..." , denormalize_permx); + + field_trans_table_add( table , "NORMALIZE_PERMZ" , "..." , normalize_permz); + field_trans_table_add( table , "DENORMALIZE_PERMZ" , "..." , denormalize_permz); + + field_trans_table_add( table , "NORMALIZE_PORO" , "..." , normalize_poro); + field_trans_table_add( table , "DENORMALIZE_PORO" , "..." , denormalize_poro); + //----------------------------------------------------------------- + + table->case_sensitive = false; + return table; +} + diff --git a/libres/lib/enkf/forward_load_context.cpp b/libres/lib/enkf/forward_load_context.cpp new file mode 100644 index 00000000000..66af79239e3 --- /dev/null +++ b/libres/lib/enkf/forward_load_context.cpp @@ -0,0 +1,265 @@ +/* + Copyright (C) 2016 Equinor ASA, Norway. + + The file 'forward_load_context.c.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. +*/ + +#include +#include + +#include +#include +#include + + +#define FORWARD_LOAD_CONTEXT_TYPE_ID 644239127 + +struct forward_load_context_struct { + UTIL_TYPE_ID_DECLARATION; + // Everyuthing can be NULL here ... - when created from gen_data. + + ecl_sum_type * ecl_sum; + ecl_file_type * restart_file; + const run_arg_type * run_arg; + const ecl_config_type * ecl_config; // Can be NULL + + int step2; + stringlist_type * messages; // This is managed by external scope - can be NULL + + + /* The variables below are updated during the load process. */ + int load_step; + int load_result; + bool ecl_active; +}; + +UTIL_IS_INSTANCE_FUNCTION( forward_load_context , FORWARD_LOAD_CONTEXT_TYPE_ID) + + + +static void forward_load_context_load_ecl_sum(forward_load_context_type * load_context) { + ecl_sum_type * summary = NULL; + + if (load_context->ecl_active ) { + const run_arg_type * run_arg = forward_load_context_get_run_arg(load_context); + const char * run_path = run_arg_get_runpath( run_arg ); + const char * eclbase = run_arg_get_job_name( load_context->run_arg ); + + const bool fmt_file = ecl_config_get_formatted(load_context->ecl_config); + char * header_file = ecl_util_alloc_exfilename(run_path , eclbase , ECL_SUMMARY_HEADER_FILE , fmt_file , -1); + char * unified_file = ecl_util_alloc_exfilename(run_path , eclbase , ECL_UNIFIED_SUMMARY_FILE , fmt_file , -1); + stringlist_type * data_files = stringlist_alloc_new(); + + + /* Should we load from a unified summary file, or from several non-unified files? */ + if (unified_file != NULL) + /* Use unified file: */ + stringlist_append_copy( data_files , unified_file); + else { + /* Use several non unified files. */ + /* Bypassing the query to model_config_load_results() */ + int report_step = run_arg_get_load_start( run_arg ); + if (report_step == 0) + report_step++; // Ignore looking for the .S0000 summary file (it does not exist). + while (true) { + char * summary_file = ecl_util_alloc_exfilename(run_arg_get_runpath( run_arg ) , eclbase , ECL_SUMMARY_FILE , fmt_file , report_step); + + if (summary_file != NULL) { + stringlist_append_copy( data_files , summary_file); + free(summary_file); + } else + /* + We stop the loading at first 'hole' in the series of summary files; + the internalize layer must report failure if we are missing data. + */ + break; + + report_step++; + } + } + + if ((header_file != NULL) && (stringlist_get_size(data_files) > 0)) { + bool include_restart = false; + bool lazy_load = true; + int file_options = 0; + summary = ecl_sum_fread_alloc(header_file , data_files , SUMMARY_KEY_JOIN_STRING , include_restart, lazy_load, file_options); + { + time_t end_time = ecl_config_get_end_date( load_context->ecl_config ); + if (end_time > 0) { + if (ecl_sum_get_end_time( summary ) < end_time) { + /* The summary vector was shorter than expected; we interpret this as + a simulation failure and discard the current summary instance. */ + + if (forward_load_context_accept_messages(load_context)) { + int end_day,end_month,end_year; + int sum_day,sum_month,sum_year; + + util_set_date_values_utc( end_time , &end_day , &end_month , &end_year ); + util_set_date_values_utc( ecl_sum_get_end_time( summary ) , &sum_day , &sum_month , &sum_year ); + { + char * msg = util_alloc_sprintf("Summary ended at %02d/%02d/%4d - expected at least END_DATE: %02d/%02d/%4d" , + sum_day , sum_month , sum_year , + end_day , end_month , end_year ); + forward_load_context_add_message( load_context , msg ); + free( msg ); + } + } + + } + ecl_sum_free( summary ); + summary = NULL; + } + } + } + stringlist_free( data_files ); + free( header_file ); + free( unified_file ); + } + + if (summary) + load_context->ecl_sum = summary; + else + forward_load_context_update_result(load_context, LOAD_FAILURE); +} + + + + +forward_load_context_type * forward_load_context_alloc( const run_arg_type * run_arg , bool load_summary , const ecl_config_type * ecl_config , stringlist_type * messages) { + forward_load_context_type * load_context = (forward_load_context_type *)util_malloc( sizeof * load_context ); + UTIL_TYPE_ID_INIT( load_context , FORWARD_LOAD_CONTEXT_TYPE_ID ); + + load_context->ecl_active = false; + load_context->ecl_sum = NULL; + load_context->restart_file = NULL; + load_context->run_arg = run_arg; + load_context->load_step = -1; // Invalid - must call forward_load_context_select_step() + load_context->load_result = 0; + load_context->messages = messages; + load_context->ecl_config = ecl_config; + if (ecl_config) + load_context->ecl_active = ecl_config_active( ecl_config ); + + if (load_summary) + forward_load_context_load_ecl_sum(load_context); + + return load_context; +} + + + +bool forward_load_context_accept_messages( const forward_load_context_type * load_context ) { + if (load_context->messages) + return true; + else + return false; +} + + +/* + The messages can be NULL; in which case the message is completely ignored. +*/ + +void forward_load_context_add_message( forward_load_context_type * load_context , const char * message ) { + if (load_context->messages) + stringlist_append_copy( load_context->messages , message ); +} + + +int forward_load_context_get_result( const forward_load_context_type * load_context ) { + return load_context->load_result; +} + +void forward_load_context_update_result( forward_load_context_type * load_context , int flags) { + load_context->load_result |= flags; +} + + +void forward_load_context_free( forward_load_context_type * load_context ) { + if (load_context->restart_file) + ecl_file_close( load_context->restart_file ); + + if (load_context->ecl_sum) + ecl_sum_free( load_context->ecl_sum ); + + free( load_context ); +} + +bool forward_load_context_load_restart_file( forward_load_context_type * load_context, int report_step) { + if (load_context->ecl_config) { + const bool unified = ecl_config_get_unified_restart( load_context->ecl_config ); + if (unified) + util_abort("%s: sorry - unified restart files are not supported \n",__func__); + + forward_load_context_select_step(load_context, report_step); + { + const bool fmt_file = ecl_config_get_formatted( load_context->ecl_config ); + char * filename = ecl_util_alloc_exfilename( run_arg_get_runpath(load_context->run_arg) , + run_arg_get_job_name( load_context->run_arg ), + ECL_RESTART_FILE , + fmt_file , + load_context->load_step ); + + if (load_context->restart_file) + ecl_file_close( load_context->restart_file ); + load_context->restart_file = NULL; + + if (filename) { + load_context->restart_file = ecl_file_open( filename , 0 ); + free(filename); + } + + if (load_context->restart_file) + return true; + else + return false; + } + } else { + util_abort("%s: internal error - tried to load restart with load_context with ecl_config==NULL \n",__func__); + return false; + } +} + + + + +const ecl_sum_type * forward_load_context_get_ecl_sum( const forward_load_context_type * load_context) { + return load_context->ecl_sum; +} + +const run_arg_type * forward_load_context_get_run_arg( const forward_load_context_type * load_context ) { + return load_context->run_arg; +} + +const char * forward_load_context_get_run_path( const forward_load_context_type * load_context ) { + return run_arg_get_runpath( load_context->run_arg ); +} + + +enkf_fs_type * forward_load_context_get_sim_fs( const forward_load_context_type * load_context ) { + return run_arg_get_sim_fs( load_context->run_arg ); +} + + +void forward_load_context_select_step( forward_load_context_type * load_context , int report_step) { + load_context->load_step = report_step; +} + +int forward_load_context_get_load_step(const forward_load_context_type * load_context) { + if (load_context->load_step < 0) + util_abort("%s: this looks like an internal error - missing call to forward_load_context_select_step() \n",__func__); + + return load_context->load_step; +} diff --git a/libres/lib/enkf/fs_driver.cpp b/libres/lib/enkf/fs_driver.cpp new file mode 100644 index 00000000000..5a91abc1dd6 --- /dev/null +++ b/libres/lib/enkf/fs_driver.cpp @@ -0,0 +1,157 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'fs_driver.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 + +/* + The underlying base types (abstract - with no accompanying + implementation); these two type ID's are not exported outside this + file. They are not stored to disk, and only used in an attempt + yo verify run-time casts. +*/ +#define FS_DRIVER_ID 10 + + +/*****************************************************************/ +/* This fs driver implemenatition is common to both dynamic and + parameter info. */ + +void fs_driver_init(fs_driver_type * driver) { + driver->type_id = FS_DRIVER_ID; + + driver->load_node = NULL; + driver->save_node = NULL; + driver->has_node = NULL; + driver->unlink_node = NULL; + + driver->load_vector = NULL; + driver->save_vector = NULL; + driver->has_vector = NULL; + driver->unlink_vector = NULL; + + driver->free_driver = NULL; + driver->fsync_driver = NULL; +} + +fs_driver_type * fs_driver_safe_cast(void * __driver) { + fs_driver_type * driver = (fs_driver_type *) __driver; + if (driver->type_id != FS_DRIVER_ID) + util_abort("%s: runtime cast failed. \n",__func__); + return driver; +} + +/*****************************************************************/ + + +void fs_driver_init_fstab( FILE * stream, fs_driver_impl driver_id) { + util_fwrite_long( FS_MAGIC_ID , stream ); + util_fwrite_int ( CURRENT_FS_VERSION , stream ); + util_fwrite_int ( driver_id , stream ); +} + + + +/** + Will open fstab stream and return it. The semantics with respect to + existing/not existnig fstab file depends on the value of the + @create parameter: + + @create = True: If the fstab file exists the function will return + NULL, otherwise it will return a stream opened for writing to the + fstab file. + + @create = False: If the fstab file exists the the function will + return a stream opened for reading of the fstab file, otherwise + it will return NULL. + +*/ + +char * fs_driver_alloc_fstab_file( const char * path ) { + return util_alloc_filename( path , "ert_fstab" , NULL); +} + + +FILE * fs_driver_open_fstab( const char * path , bool create) { + FILE * stream = NULL; + char * fstab_file = fs_driver_alloc_fstab_file( path ); + if (create) + util_make_path( path ); + + if (util_file_exists( fstab_file ) != create) { + if (create) + stream = util_fopen( fstab_file , "w"); + else + stream = util_fopen( fstab_file , "r"); + } + free( fstab_file ); + return stream; +} + + +void fs_driver_assert_magic( FILE * stream ) { + long fs_magic = util_fread_long( stream ); + if (fs_magic != FS_MAGIC_ID) + util_abort("%s: Fstab magic marker incorrect \n",__func__); +} + + + +void fs_driver_assert_version( FILE * stream , const char * mount_point) { + int file_version = util_fread_int( stream ); + + if (file_version < MIN_SUPPORTED_FS_VERSION ) + util_exit("%s: The file system you are trying to access is created with a very old version of ert - sorry.\n",__func__); + + if (file_version > CURRENT_FS_VERSION) + util_exit("%s: The file system you are trying to access has been created with a newer version of ert - sorry.\n",__func__); + + if (file_version < CURRENT_FS_VERSION) { + if ((file_version == 105) && (CURRENT_FS_VERSION == 106)) + fprintf(stderr,"%s: The file system you are accessing has been written with an older version of ert - STATIC information ignored. \n",__func__); + else { + fprintf(stderr,"-----------------------------------------------------------------------------------------------------\n"); + fprintf(stderr," %s: The file system you are trying to access has been created with an old version of ert - sorry.\n",__func__); + fprintf(stderr," ert_fs_version: %d \n",CURRENT_FS_VERSION ); + fprintf(stderr," %s version: %d \n",mount_point , file_version); + + if ((file_version == 106) && (CURRENT_FS_VERSION == 107)) + fprintf(stderr," The utility: ert_upgrade_fs107 can be used to upgrade storage version from 106 to 107\n\n"); + + util_exit(" EXIT\n"); + fprintf(stderr,"-----------------------------------------------------------------------------------------------------\n"); + } + } + +} + + +int fs_driver_fread_version( FILE * stream ) { + long fs_magic = util_fread_long( stream ); + if (fs_magic != FS_MAGIC_ID) + return -1; + else { + int file_version = util_fread_int( stream ); + return file_version; + } +} + + +/*****************************************************************/ diff --git a/libres/lib/enkf/fs_types.cpp b/libres/lib/enkf/fs_types.cpp new file mode 100644 index 00000000000..d95a2cf6317 --- /dev/null +++ b/libres/lib/enkf/fs_types.cpp @@ -0,0 +1,35 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'fs_types.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 + +/* + The driver type DRIVER_STATIC has been removed completely as of + December 2015, but there will still be many mount map files with + this enum value around on disk. This function is a minor convenience + to handle that. + + The driver type DRIVER_DYNAMIC_ANALYZED was removed ~april 2016. +*/ + +bool fs_types_valid( fs_driver_enum driver_type) { + if ((driver_type == DRIVER_STATIC) || (driver_type == DRIVER_DYNAMIC_ANALYZED)) + return false; + else + return true; +} diff --git a/libres/lib/enkf/gen_common.cpp b/libres/lib/enkf/gen_common.cpp new file mode 100644 index 00000000000..ed42a14b1f4 --- /dev/null +++ b/libres/lib/enkf/gen_common.cpp @@ -0,0 +1,142 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'gen_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 + +/** + This file implements some (very basic) functionality which is used + by both the gen_data and gen_obs objects. +*/ + + +void * gen_common_fscanf_alloc(const char * file , ecl_data_type load_data_type , int * size) { + FILE * stream = util_fopen(file , "r"); + int sizeof_ctype = ecl_type_get_sizeof_ctype(load_data_type); + int buffer_elements = *size; + int current_size = 0; + int fscanf_return = 1; /* To keep the compiler happy .*/ + void * buffer; + + if (buffer_elements == 0) + buffer_elements = 100; + + buffer = util_calloc( buffer_elements , sizeof_ctype ); // CXX_CAST_ERROR + { + do { + if (ecl_type_is_float(load_data_type)) { + float * float_buffer = (float *) buffer; + fscanf_return = fscanf(stream , "%g" , &float_buffer[current_size]); + } else if (ecl_type_is_double(load_data_type)) { + double * double_buffer = (double *) buffer; + fscanf_return = fscanf(stream , "%lg" , &double_buffer[current_size]); + } else if (ecl_type_is_int(load_data_type)) { + int * int_buffer = (int *) buffer; + fscanf_return = fscanf(stream , "%d" , &int_buffer[current_size]); + } else + util_abort("%s: god dammit - internal error \n",__func__); + + if (fscanf_return == 1) + current_size += 1; + + if (current_size == buffer_elements) { + buffer_elements *= 2; + buffer = util_realloc( buffer , buffer_elements * sizeof_ctype ); + } + } while (fscanf_return == 1); + } + if (fscanf_return != EOF) + util_abort("%s: scanning of %s terminated before EOF was reached -- fix your file.\n" , __func__ , file); + + fclose(stream); + *size = current_size; + return buffer; +} + + + +void * gen_common_fread_alloc(const char * file , ecl_data_type load_data_type , int * size) { + const int max_read_size = 100000; + FILE * stream = util_fopen(file , "r"); + int sizeof_ctype = ecl_type_get_sizeof_ctype(load_data_type); + int read_size = 4096; /* Shot in the wild */ + int current_size = 0; + int buffer_elements; + int fread_return; + char * buffer; + + + buffer_elements = read_size; + buffer = (char * ) util_calloc( buffer_elements , sizeof_ctype ); + { + do { + fread_return = fread( &buffer[ current_size * sizeof_ctype] , sizeof_ctype , read_size , stream); + current_size += fread_return; + + if (!feof(stream)) { + /* Allocate more elements. */ + if (current_size == buffer_elements) { + read_size *= 2; + read_size = util_int_min(read_size , max_read_size); + buffer_elements += read_size; + buffer = (char * ) util_realloc( buffer , buffer_elements * sizeof_ctype ); + } else + util_abort("%s: internal error ?? \n",__func__); + } + } while (!feof(stream)); + } + fclose(stream); + *size = current_size; + return buffer; +} + + +/* + If the load_format is binary_float or binary_double, the ASCII_type + is *NOT* consulted. The load_type is set to float/double depending + on what was actually used when the data was loaded. +*/ + +void * gen_common_fload_alloc(const char * file, + gen_data_file_format_type load_format, + ecl_data_type ASCII_data_type, + ecl_type_enum * load_data_type, + int * size) { + void * buffer = NULL; + + if (load_format == ASCII) { + *load_data_type = ecl_type_get_type(ASCII_data_type); + buffer = gen_common_fscanf_alloc(file , ASCII_data_type , size); + } else if (load_format == BINARY_FLOAT) { + *load_data_type = ECL_FLOAT_TYPE; + buffer = gen_common_fread_alloc(file , ECL_FLOAT , size); + } else if (load_format == BINARY_DOUBLE) { + *load_data_type = ECL_DOUBLE_TYPE; + buffer = gen_common_fread_alloc(file , ECL_DOUBLE , size); + } else + util_abort("%s: trying to load with unsupported format:%s... \n" , load_format); + + return buffer; +} diff --git a/libres/lib/enkf/gen_data.cpp b/libres/lib/enkf/gen_data.cpp new file mode 100644 index 00000000000..5745848fe2f --- /dev/null +++ b/libres/lib/enkf/gen_data.cpp @@ -0,0 +1,713 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'gen_data.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 + + +/** + The file implements a general data type which can be used to update + arbitrary data which the EnKF system has *ABSOLUTELY NO IDEA* of + how is organised; how it should be used in the forward model and so + on. Similarly to the field objects, the gen_data objects can be + treated both as parameters and as dynamic data. + + Whether the forward_load function should be called (i.e. it is dynamic + data) is determined at the enkf_node level, and no busissiness of + the gen_data implementation. +*/ + + + + +struct gen_data_struct { + int __type_id; + gen_data_config_type * config; /* Thin config object - mainly contains filename for remote load */ + char * data; /* Actual storage - will be casted to double or float on use. */ + int current_report_step; /* Need this to look up the correct size in the config object. */ + bool_vector_type * active_mask; /* Mask of active/not active - loaded from a "_active" file created by the forward model. Not used when used as parameter*/ +}; + + + +void gen_data_assert_size( gen_data_type * gen_data , int size , int report_step) { + gen_data_config_assert_size(gen_data->config , size , report_step); + gen_data->current_report_step = report_step; +} + +gen_data_config_type * gen_data_get_config(const gen_data_type * gen_data) { return gen_data->config; } + +int gen_data_get_size( const gen_data_type * gen_data ) { + return gen_data_config_get_data_size( gen_data->config , gen_data->current_report_step ); +} + +/** + It is a bug to call this before some function has set the size. +*/ +void gen_data_realloc_data(gen_data_type * gen_data) { + int byte_size = gen_data_config_get_byte_size(gen_data->config , gen_data->current_report_step ); + gen_data->data = (char *) util_realloc(gen_data->data , byte_size ); +} + + + +gen_data_type * gen_data_alloc(const gen_data_config_type * config) { + gen_data_type * gen_data = (gen_data_type *)util_malloc(sizeof * gen_data); + gen_data->config = (gen_data_config_type *) config; + gen_data->data = NULL; + gen_data->__type_id = GEN_DATA; + gen_data->active_mask = bool_vector_alloc( 0 , true ); + gen_data->current_report_step = -1; /* God - if you ever read this .... */ + return gen_data; +} + + +void gen_data_copy(const gen_data_type * src , gen_data_type * target) { + if (src->config == target->config) { + target->current_report_step = src->current_report_step; + + if (src->data != NULL) { + int byte_size = gen_data_config_get_byte_size( src->config , src->current_report_step ); + target->data = (char *) util_realloc_copy(target->data , src->data , byte_size ); + } + } else + util_abort("%s: do not share config object \n",__func__); +} + + + +void gen_data_free(gen_data_type * gen_data) { + free(gen_data->data); + bool_vector_free( gen_data->active_mask ); + free(gen_data); +} + + + + +/** + Observe that this function writes parameter size to disk, that is + special. The reason is that the config object does not know the + size (on allocation). + + The function currently writes an empty file (with only a report + step and a size == 0) in the case where it does not have data. This + is controlled by the value of the variable write_zero_size; if this + is changed to false some semantics in the load code must be + changed. +*/ + + +C_USED bool gen_data_write_to_buffer(const gen_data_type * gen_data , buffer_type * buffer , int report_step) { + const bool write_zero_size = true; /* true:ALWAYS write a file false:only write files with size > 0. */ + { + bool write = write_zero_size; + int size = gen_data_config_get_data_size( gen_data->config , report_step ); + if (size > 0) + write = true; + + if (write) { + int byte_size = gen_data_config_get_byte_size( gen_data->config , report_step ); + buffer_fwrite_int( buffer , GEN_DATA ); + buffer_fwrite_int( buffer , size ); + buffer_fwrite_int( buffer , report_step); /* Why the heck do I need to store this ???? It was a mistake ...*/ + + buffer_fwrite_compressed( buffer , gen_data->data , byte_size); + return true; + } else + return false; /* When false is returned - the (empty) file will be removed */ + } +} + + + +C_USED void gen_data_read_from_buffer(gen_data_type * gen_data , buffer_type * buffer , enkf_fs_type * fs, int report_step) { + int size; + enkf_util_assert_buffer_type(buffer , GEN_DATA); + size = buffer_fread_int(buffer); + buffer_fskip_int( buffer ); /* Skipping report_step from the buffer - was a mistake to store it - I think ... */ + { + size_t byte_size = size * ecl_type_get_sizeof_ctype( gen_data_config_get_internal_data_type ( gen_data->config )); + size_t compressed_size = buffer_get_remaining_size( buffer ); + gen_data->data = (char *) util_realloc( gen_data->data , byte_size ); + buffer_fread_compressed( buffer , compressed_size , gen_data->data , byte_size ); + } + gen_data_assert_size( gen_data , size , report_step ); + + if (gen_data_config_is_dynamic(gen_data->config)) { + gen_data_config_load_active( gen_data->config , fs, report_step , false ); + } +} + + + + + + + + +void gen_data_serialize(const gen_data_type * gen_data , node_id_type node_id , const active_list_type * active_list , matrix_type * A , int row_offset , int column) { + const gen_data_config_type *config = gen_data->config; + const int data_size = gen_data_config_get_data_size( gen_data->config , gen_data->current_report_step ); + ecl_data_type data_type = gen_data_config_get_internal_data_type( config ); + + enkf_matrix_serialize( gen_data->data , data_size , data_type , active_list , A , row_offset , column ); +} + + +void gen_data_deserialize(gen_data_type * gen_data , node_id_type node_id , const active_list_type * active_list , const matrix_type * A , int row_offset , int column) { + { + const gen_data_config_type *config = gen_data->config; + const int data_size = gen_data_config_get_data_size( gen_data->config , gen_data->current_report_step ); + ecl_data_type data_type = gen_data_config_get_internal_data_type(config); + + enkf_matrix_deserialize( gen_data->data , data_size , data_type , active_list , A , row_offset , column); + } +} + + + + +/* + This function sets the data field of the gen_data instance after the + data has been loaded from file. +*/ + +static void gen_data_set_data__(gen_data_type * gen_data , int size, const forward_load_context_type * load_context, ecl_data_type load_data_type , const void * data) { + gen_data_assert_size(gen_data , size, forward_load_context_get_load_step( load_context )); + if (gen_data_config_is_dynamic( gen_data->config )) + gen_data_config_update_active( gen_data->config , load_context , gen_data->active_mask); + + gen_data_realloc_data(gen_data); + + if (size > 0) { + ecl_data_type internal_type = gen_data_config_get_internal_data_type( gen_data->config ); + int byte_size = ecl_type_get_sizeof_ctype( internal_type ) * size ; + + if (ecl_type_is_equal(load_data_type, internal_type)) + memcpy(gen_data->data , data , byte_size ); + else { + if (ecl_type_is_float(load_data_type)) + util_float_to_double((double *) gen_data->data , (const float *) data , size); + else + util_double_to_float((float *) gen_data->data , (const double *) data , size); + } + } +} + + + +static bool gen_data_fload_active__(gen_data_type * gen_data, const char * filename, int size) { + /* + Look for file @filename_active - if that file is found it is + interpreted as a an active|inactive mask created by the forward + model. + + The file is assumed to be an ASCII file with integers, 0 + indicates inactive elements and 1 active elements. The file + should of course be as long as @filename. + + If the file is not found the gen_data->active_mask is set to + all-true (i.e. the default true value is invoked). + */ + bool file_exists = false; + if (gen_data_config_is_dynamic( gen_data->config )) { + bool_vector_reset( gen_data->active_mask ); + bool_vector_iset( gen_data->active_mask , size - 1, true ); + { + char * active_file = util_alloc_sprintf("%s_active" , filename ); + if (util_file_exists( active_file )) { + file_exists = true; + FILE * stream = util_fopen( active_file , "r"); + int active_int; + for (int index=0; index < size; index++) { + if (fscanf( stream , "%d" , &active_int) == 1) { + if (active_int == 1) + bool_vector_iset( gen_data->active_mask , index , true); + else if (active_int == 0) + bool_vector_iset( gen_data->active_mask , index , false); + else + util_abort("%s: error when loading active mask from:%s only 0 and 1 allowed \n",__func__ , active_file); + } else + util_abort("%s: error when loading active mask from:%s - file not long enough.\n",__func__ , active_file ); + } + fclose( stream ); + res_log_finfo("GEN_DATA(%s): active information loaded from:%s.", + gen_data_get_key(gen_data), active_file); + } else + res_log_finfo("GEN_DATA(%s): active information NOT loaded.", + gen_data_get_key(gen_data)); + free( active_file ); + } + } + return file_exists; +} + + +/** + This functions loads data from file. Observe that there is *NO* + header information in this file - the size is determined by seeing + how much can be successfully loaded. + + The file is loaded with the gen_common_fload_alloc() function, and + can be in formatted ASCII or binary_float / binary_double. + + When the read is complete it is checked/verified with the config + object that this file was as long as the others we have loaded for + other members; it is perfectly OK for the file to not exist. In + which case a size of zero is set, for this report step. + + Return value is whether file was found or was empty + - might have to check this in calling scope. +*/ + +bool gen_data_fload_with_report_step( gen_data_type * gen_data , const char * filename , const forward_load_context_type * load_context) { + bool file_exists = util_file_exists(filename); + void * buffer = NULL; + if ( file_exists ) { + ecl_type_enum load_type; + ecl_data_type internal_type = gen_data_config_get_internal_data_type(gen_data->config); + gen_data_file_format_type input_format = gen_data_config_get_input_format( gen_data->config ); + int size = 0; + buffer = gen_common_fload_alloc( filename , input_format , internal_type , &load_type , &size); + res_log_finfo("GEN_DATA(%s): loading from: %s size:%d", + gen_data_get_key(gen_data), filename, size); + if (size > 0) { + gen_data_fload_active__(gen_data, filename, size); + } else { + bool_vector_reset( gen_data->active_mask ); + } + gen_data_set_data__(gen_data , size , load_context , ecl_type_create_from_type(load_type) , buffer ); + free(buffer); + } else + res_log_fwarning("GEN_DATA(%s): missing file: %s", + gen_data_get_key(gen_data), filename); + + return file_exists; +} + + + + + +bool gen_data_forward_load(gen_data_type * gen_data , const char * ecl_file , const forward_load_context_type * load_context) { + return gen_data_fload_with_report_step( gen_data , ecl_file , load_context); +} + + + +/** + This function initializes the parameter. This is based on loading a + file. The name of the file is derived from a path_fmt instance + owned by the config object. Observe that there is *NO* header + information in this file. We just read floating point numbers until + we reach EOF. + + When the read is complete it is checked/verified with the config + object that this file was as long as the files we have loaded for + other members. + + If gen_data_config_alloc_initfile() returns NULL that means that + the gen_data instance does not have any init function - that is OK. +*/ + + + +C_USED bool gen_data_initialize(gen_data_type * gen_data , int iens , const char * init_file , rng_type * rng) { + bool ret = false; + if (init_file) { + forward_load_context_type * load_context = forward_load_context_alloc( NULL , false , NULL , NULL ); + + forward_load_context_select_step(load_context, 0); + if (!gen_data_fload_with_report_step(gen_data , init_file , load_context)) + util_abort("%s: could not find file:%s \n",__func__ , init_file); + ret = true; + + forward_load_context_free( load_context ); + } + return ret; +} + + + + +static void gen_data_ecl_write_ASCII(const gen_data_type * gen_data , const char * file , gen_data_file_format_type export_format) { + FILE * stream = util_fopen(file , "w"); + char * template_buffer; + int template_data_offset, template_buffer_size , template_data_skip; + + if (export_format == ASCII_TEMPLATE) { + gen_data_config_get_template_data( gen_data->config , &template_buffer , &template_data_offset , &template_buffer_size , &template_data_skip); + util_fwrite( template_buffer , 1 , template_data_offset , stream , __func__); + } + + { + ecl_data_type internal_type = gen_data_config_get_internal_data_type(gen_data->config); + const int size = gen_data_config_get_data_size( gen_data->config , gen_data->current_report_step ); + int i; + if (ecl_type_is_float(internal_type)) { + float * float_data = (float *) gen_data->data; + for (i=0; i < size; i++) + fprintf(stream , "%g\n",float_data[i]); + } else if (ecl_type_is_double(internal_type)) { + double * double_data = (double *) gen_data->data; + for (i=0; i < size; i++) + fprintf(stream , "%lg\n",double_data[i]); + } else + util_abort("%s: internal error - wrong type \n",__func__); + } + + if (export_format == ASCII_TEMPLATE) { + int new_offset = template_data_offset + template_data_skip; + util_fwrite( &template_buffer[new_offset] , 1 , template_buffer_size - new_offset , stream , __func__); + } + fclose(stream); +} + + + +static void gen_data_ecl_write_binary(const gen_data_type * gen_data , const char * file , ecl_data_type export_type) { + FILE * stream = util_fopen(file , "w"); + int sizeof_ctype = ecl_type_get_sizeof_ctype( export_type ); + util_fwrite( gen_data->data , sizeof_ctype , gen_data_config_get_data_size( gen_data->config , gen_data->current_report_step) , stream , __func__); + fclose(stream); +} + + +void gen_data_export(const gen_data_type * gen_data , const char * full_path , gen_data_file_format_type export_type) { + switch (export_type) { + case(ASCII): + gen_data_ecl_write_ASCII(gen_data , full_path , export_type); + break; + case(ASCII_TEMPLATE): + gen_data_ecl_write_ASCII(gen_data , full_path , export_type); + break; + case(BINARY_DOUBLE): + gen_data_ecl_write_binary(gen_data , full_path , ECL_DOUBLE); + break; + case(BINARY_FLOAT): + gen_data_ecl_write_binary(gen_data , full_path , ECL_FLOAT); + break; + default: + util_abort("%s: internal error - export type is not set.\n",__func__); + } +} + +/** + It is the enkf_node layer which knows whether the node actually + has any data to export. If it is not supposed to write data to the + forward model, i.e. it is of enkf_type 'dynamic_result' that is + signaled down here with eclfile == NULL. +*/ + + +void gen_data_ecl_write(const gen_data_type * gen_data , const char * run_path , const char * eclfile , value_export_type * export_value) { + if (eclfile != NULL) { + char * full_path = util_alloc_filename( run_path , eclfile , NULL); + + gen_data_file_format_type export_type = gen_data_config_get_output_format( gen_data->config ); + gen_data_export( gen_data , full_path , export_type); + free( full_path ); + } +} + + +static void gen_data_assert_index(const gen_data_type * gen_data, int index) { + int current_size = gen_data_config_get_data_size( gen_data->config , gen_data->current_report_step ); + if ((index < 0) || (index >= current_size )) + util_abort("%s: index:%d invalid. Valid range: [0,%d) \n",__func__ , index , current_size); +} + + +double gen_data_iget_double(const gen_data_type * gen_data, int index) { + gen_data_assert_index(gen_data , index); + { + ecl_data_type internal_type = gen_data_config_get_internal_data_type(gen_data->config); + if (ecl_type_is_double(internal_type)) { + double * data = (double *) gen_data->data; + return data[index]; + } else { + float * data = (float *) gen_data->data; + return data[index]; + } + } +} + + +void gen_data_export_data(const gen_data_type * gen_data , double_vector_type * export_data) { + ecl_data_type internal_type = gen_data_config_get_internal_data_type(gen_data->config); + if (ecl_type_is_double(internal_type)) + double_vector_memcpy_from_data( export_data , (const double *) gen_data->data , gen_data_get_size( gen_data )); + else { + double_vector_reset( export_data ); + float * float_data = (float *) gen_data->data; + for (int i = 0; i < gen_data_get_size( gen_data ); i++) + double_vector_iset( export_data , i , float_data[i]); + } +} + + + +/** + The filesystem will (currently) store gen_data instances which do + not hold any data. Therefor it will be quite common to enter this + function with an empty instance, we therefor just set valid => + false, and return silently in that case. +*/ + +C_USED bool gen_data_user_get(const gen_data_type * gen_data, const char * index_key, int report_step , double * value) +{ + int index; + *value = 0.0; + + if (index_key != NULL) { + if (util_sscanf_int(index_key , &index)) { + if (index < gen_data_config_get_data_size( gen_data->config , gen_data->current_report_step )) { + *value = gen_data_iget_double( gen_data , index ); + return true; + } + } + } + + return false; +} + + +const char * gen_data_get_key( const gen_data_type * gen_data) { + return gen_data_config_get_key( gen_data->config ); +} + + +C_USED void gen_data_clear( gen_data_type * gen_data ) { + const gen_data_config_type * config = gen_data->config; + ecl_data_type internal_type = gen_data_config_get_internal_data_type( config ); + const int data_size = gen_data_config_get_data_size( gen_data->config , gen_data->current_report_step ); + + if (ecl_type_is_float(internal_type)) { + float * data = (float * ) gen_data->data; + for (int i = 0; i < data_size; i++) + data[i] = 0; + } else if (ecl_type_is_double(internal_type)) { + double * data = (double * ) gen_data->data; + for (int i = 0; i < data_size; i++) + data[i] = 0; + } +} + + + +C_USED void gen_data_isqrt(gen_data_type * gen_data) { + const int data_size = gen_data_config_get_data_size( gen_data->config , gen_data->current_report_step ); + const ecl_data_type internal_type = gen_data_config_get_internal_data_type(gen_data->config); + + if (ecl_type_is_float(internal_type)) { + float * data = (float *) gen_data->data; + for (int i=0; i < data_size; i++) + data[i] = sqrtf( data[i] ); + } else if (ecl_type_is_double(internal_type)) { + double * data = (double *) gen_data->data; + for (int i=0; i < data_size; i++) + data[i] = sqrt( data[i] ); + } +} + + + + +C_USED void gen_data_iadd(gen_data_type * gen_data1, const gen_data_type * gen_data2) { + //gen_data_config_assert_binary(gen_data1->config , gen_data2->config , __func__); + { + const int data_size = gen_data_config_get_data_size( gen_data1->config , gen_data1->current_report_step ); + const ecl_data_type internal_type = gen_data_config_get_internal_data_type(gen_data1->config); + int i; + + if (ecl_type_is_float(internal_type)) { + float * data1 = (float *) gen_data1->data; + const float * data2 = (const float *) gen_data2->data; + for (i = 0; i < data_size; i++) + data1[i] += data2[i]; + } else if (ecl_type_is_double(internal_type)) { + double * data1 = (double *) gen_data1->data; + const double * data2 = (const double *) gen_data2->data; + for (i = 0; i < data_size; i++) { + data1[i] += data2[i]; + } + } + } +} + + +C_USED void gen_data_imul(gen_data_type * gen_data1, const gen_data_type * gen_data2) { + //gen_data_config_assert_binary(gen_data1->config , gen_data2->config , __func__); + { + const int data_size = gen_data_config_get_data_size( gen_data1->config , gen_data1->current_report_step ); + const ecl_data_type internal_type = gen_data_config_get_internal_data_type(gen_data1->config); + int i; + + if (ecl_type_is_float(internal_type)) { + float * data1 = (float *) gen_data1->data; + const float * data2 = (const float *) gen_data2->data; + for (i = 0; i < data_size; i++) + data1[i] *= data2[i]; + } else if (ecl_type_is_double(internal_type)) { + double * data1 = (double *) gen_data1->data; + const double * data2 = (const double *) gen_data2->data; + for (i = 0; i < data_size; i++) + data1[i] *= data2[i]; + } + } +} + + +void gen_data_iaddsqr(gen_data_type * gen_data1, const gen_data_type * gen_data2) { + //gen_data_config_assert_binary(gen_data1->config , gen_data2->config , __func__); + { + const int data_size = gen_data_config_get_data_size( gen_data1->config , gen_data1->current_report_step ); + const ecl_data_type internal_type = gen_data_config_get_internal_data_type(gen_data1->config); + int i; + + if (ecl_type_is_float(internal_type)) { + float * data1 = (float *) gen_data1->data; + const float * data2 = (const float *) gen_data2->data; + for (i = 0; i < data_size; i++) + data1[i] += data2[i] * data2[i]; + } else if (ecl_type_is_double(internal_type)) { + double * data1 = (double *) gen_data1->data; + const double * data2 = (const double *) gen_data2->data; + for (i = 0; i < data_size; i++) + data1[i] += data2[i] * data2[i]; + } + } +} + + +void gen_data_scale(gen_data_type * gen_data, double scale_factor) { + //gen_data_config_assert_unary(gen_data->config, __func__); + { + const int data_size = gen_data_config_get_data_size( gen_data->config , gen_data->current_report_step ); + const ecl_data_type internal_type = gen_data_config_get_internal_data_type(gen_data->config); + int i; + + if (ecl_type_is_float(internal_type)) { + float * data = (float *) gen_data->data; + for (i = 0; i < data_size; i++) + data[i] *= scale_factor; + } else if (ecl_type_is_double(internal_type)) { + double * data = (double *) gen_data->data; + for (i = 0; i < data_size; i++) + data[i] *= scale_factor; + } + } +} + + +void gen_data_copy_to_double_vector(const gen_data_type * gen_data , double_vector_type * vector){ + const ecl_data_type internal_type = gen_data_config_get_internal_data_type(gen_data->config); + int size = gen_data_get_size( gen_data ); + if (ecl_type_is_float(internal_type)) { + float * data = (float *) gen_data->data; + double_vector_reset(vector); + for (int i = 0; i < size; i++){ + double_vector_append(vector , data[i]); + } + } else if (ecl_type_is_double(internal_type)) { + double * data = (double *) gen_data->data; + double_vector_memcpy_from_data( vector , data , size ); + } + + +} + +#define INFLATE(inf,std,min) \ +{ \ + for (int i=0; i < data_size; i++) { \ + if (std_data[i] > 0) \ + inflation_data[i] = util_float_max( 1.0 , min_std_data[i] / std_data[i]); \ + else \ + inflation_data[i] = 1.0; \ + } \ +} + + +/** + If the size changes during the simulation this will go 100% belly + up. +*/ + +C_USED void gen_data_set_inflation(gen_data_type * inflation , const gen_data_type * std , const gen_data_type * min_std) { + const gen_data_config_type * config = inflation->config; + ecl_data_type data_type = gen_data_config_get_internal_data_type( config ); + const int data_size = gen_data_config_get_data_size( std->config , std->current_report_step ); + + if (ecl_type_is_float(data_type)) { + float * inflation_data = (float *) inflation->data; + const float * std_data = (const float *) std->data; + const float * min_std_data = (const float *) min_std->data; + + INFLATE(inflation_data , std_data , min_std_data ); + + } else { + double * inflation_data = (double *) inflation->data; + const double * std_data = (const double *) std->data; + const double * min_std_data = (const double *) min_std->data; + + INFLATE(inflation_data , std_data , min_std_data ); + } +} +#undef INFLATE + + +/******************************************************************/ +/* Anonumously generated functions used by the enkf_node object */ +/******************************************************************/ +UTIL_SAFE_CAST_FUNCTION_CONST(gen_data , GEN_DATA) +UTIL_SAFE_CAST_FUNCTION(gen_data , GEN_DATA) +VOID_USER_GET(gen_data) +VOID_ALLOC(gen_data) +VOID_FREE(gen_data) +VOID_COPY (gen_data) +VOID_INITIALIZE(gen_data) +VOID_ECL_WRITE(gen_data) +VOID_FORWARD_LOAD(gen_data) +VOID_READ_FROM_BUFFER(gen_data); +VOID_WRITE_TO_BUFFER(gen_data); +VOID_SERIALIZE(gen_data) +VOID_DESERIALIZE(gen_data) +VOID_SET_INFLATION(gen_data) +VOID_CLEAR(gen_data) +VOID_SCALE(gen_data) +VOID_IMUL(gen_data) +VOID_IADD(gen_data) +VOID_IADDSQR(gen_data) +VOID_ISQRT(gen_data) diff --git a/libres/lib/enkf/gen_data_config.cpp b/libres/lib/enkf/gen_data_config.cpp new file mode 100644 index 00000000000..6f0fd741fda --- /dev/null +++ b/libres/lib/enkf/gen_data_config.cpp @@ -0,0 +1,646 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'gen_data_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 +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +/** + About deactivating by the forward model + --------------------------------------- + + For the gen_data instances the forward model has the capability to + deactivate elements in a gen_data vector. This is implemented in + the function gen_data_ecl_load which will look for a file with + extension "_data" and then activate / deactivate elements + accordingly. +*/ + + + + +#define GEN_DATA_CONFIG_ID 90051 +struct gen_data_config_struct { + UTIL_TYPE_ID_DECLARATION; + char * key; /* The key this gen_data instance is known under - needed for debugging. */ + ecl_data_type internal_type; /* The underlying type (float | double) of the data in the corresponding gen_data instances. */ + char * template_file; + char * template_buffer; /* Buffer containing the content of the template - read and internalized at boot time. */ + char * template_key; + int template_data_offset; /* The offset into the template buffer before the data should come. */ + int template_data_skip; /* The length of data identifier in the template.*/ + int template_buffer_size; /* The total size (bytes) of the template buffer .*/ + gen_data_file_format_type input_format; /* The format used for loading gen_data instances when the forward model has completed *AND* for loading the initial files.*/ + gen_data_file_format_type output_format; /* The format used when gen_data instances are written to disk for the forward model. */ + int_vector_type * data_size_vector; /* Data size, i.e. number of elements , indexed with report_step */ + int_vector_type * active_report_steps; /* The report steps where we expect to load data for this instance. */ + pthread_mutex_t update_lock; + /*****************************************************************/ + /* All the fields below this line are related to the capability of + the forward model to deactivate elements in a gen_data + instance. See documentation above. + */ + bool dynamic; + enkf_fs_type * last_read_fs; /* NBNB This will be NULL in the case of instances which are used as parameters. */ + int ens_size; + bool mask_modified; + bool_vector_type * active_mask; + int active_report_step; +}; + + +/*****************************************************************/ + +UTIL_IS_INSTANCE_FUNCTION(gen_data_config , GEN_DATA_CONFIG_ID) +UTIL_SAFE_CAST_FUNCTION(gen_data_config , GEN_DATA_CONFIG_ID) +UTIL_SAFE_CAST_FUNCTION_CONST(gen_data_config , GEN_DATA_CONFIG_ID) + +gen_data_file_format_type gen_data_config_get_input_format ( const gen_data_config_type * config) { return config->input_format; } +gen_data_file_format_type gen_data_config_get_output_format( const gen_data_config_type * config) { return config->output_format; } + + + +ecl_data_type gen_data_config_get_internal_data_type(const gen_data_config_type * config) { + return config->internal_type; +} + + +/** + If current_size as queried from config->data_size_vector == -1 + (i.e. not set); we seek through +*/ + +int gen_data_config_get_data_size__( const gen_data_config_type * config , int report_step) { + int current_size = int_vector_safe_iget( config->data_size_vector , report_step ); + return current_size; +} + +int gen_data_config_get_data_size( const gen_data_config_type * config , int report_step) { + int current_size = gen_data_config_get_data_size__(config , report_step); + if (current_size < 0) + util_abort("%s: Size not set for object:%s report_step:%d - internal error: \n",__func__ , config->key , report_step); + return current_size; +} + + + +int gen_data_config_get_initial_size( const gen_data_config_type * config ) { + int initial_size = int_vector_safe_iget( config->data_size_vector , 0); + if (initial_size < 0) + initial_size = 0; + + return initial_size; +} + + + +int gen_data_config_get_byte_size( const gen_data_config_type * config , int report_step) { + int byte_size = gen_data_config_get_data_size( config , report_step ) * ecl_type_get_sizeof_ctype( gen_data_config_get_internal_data_type(config) ); + return byte_size; +} + + + +static void gen_data_config_reset_template( gen_data_config_type * config ) { + free( config->template_buffer ); + free( config->template_key ); + free( config->template_file ); + + config->template_file = NULL; + config->template_buffer = NULL; + config->template_key = NULL; + config->template_data_offset = 0; + config->template_data_skip = 0; + config->template_buffer_size = 0; +} + + + +static gen_data_config_type * gen_data_config_alloc( const char * key , bool dynamic ) { + gen_data_config_type * config = (gen_data_config_type *)util_malloc(sizeof * config ); + UTIL_TYPE_ID_INIT( config , GEN_DATA_CONFIG_ID); + + config->key = util_alloc_string_copy( key ); + + config->template_file = NULL; + config->template_key = NULL; + config->template_buffer = NULL; + gen_data_config_reset_template( config ); + + ecl_data_type data_type = ECL_DOUBLE; + memcpy(&config->internal_type, &data_type, sizeof data_type); + config->input_format = GEN_DATA_UNDEFINED; + config->output_format = GEN_DATA_UNDEFINED; + config->data_size_vector = int_vector_alloc( 0 , -1 ); /* The default value: -1 - indicates "NOT SET" */ + config->active_report_steps= int_vector_alloc( 0 , 0 ); + config->active_mask = bool_vector_alloc(0 , true ); /* Elements are explicitly set to FALSE - this MUST default to true. */ + config->active_report_step = -1; + config->ens_size = -1; + config->last_read_fs = NULL; + config->dynamic = dynamic; + pthread_mutex_init( &config->update_lock , NULL ); + + + return config; +} + +gen_data_config_type * gen_data_config_alloc_GEN_PARAM( const char * key , gen_data_file_format_type output_format , gen_data_file_format_type input_format) { + gen_data_config_type * config = gen_data_config_alloc( key , false ); + + if (input_format == ASCII_TEMPLATE) + util_abort("%s: Sorry can not use INPUT_FORMAT:ASCII_TEMPLATE\n",__func__); + + if (output_format == GEN_DATA_UNDEFINED || input_format == GEN_DATA_UNDEFINED) + util_abort("%s: Sorry must specify valid values for both input and output format\n",__func__); + + config->output_format = output_format; + config->input_format = input_format; + return config; +} + + +gen_data_config_type * gen_data_config_alloc_GEN_DATA_result( const char * key , gen_data_file_format_type input_format) { + gen_data_config_type * config = gen_data_config_alloc( key , true ); + + if (input_format == ASCII_TEMPLATE) + util_abort("%s: Sorry can not use INPUT_FORMAT:ASCII_TEMPLATE\n",__func__); + + if (input_format == GEN_DATA_UNDEFINED) + util_abort("%s: Sorry must specify valid values for input format.\n",__func__); + + config->input_format = input_format; + return config; +} + +gen_data_config_type * gen_data_config_alloc_GEN_DATA_state( const char * key , gen_data_file_format_type output_format , gen_data_file_format_type input_format) { + gen_data_config_type * config = gen_data_config_alloc( key , true ); + + if (input_format == ASCII_TEMPLATE) + util_abort("%s: Sorry can not use INPUT_FORMAT:ASCII_TEMPLATE\n",__func__); + + if (output_format == GEN_DATA_UNDEFINED || input_format == GEN_DATA_UNDEFINED) + util_abort("%s: Sorry must specify valid values for both input and output format\n",__func__); + + config->output_format = output_format; + config->input_format = input_format; + return config; +} + + +const bool_vector_type * gen_data_config_get_active_mask( const gen_data_config_type * config ) { + if (config->dynamic) + return config->active_mask; + else + return NULL; /* GEN_PARAM instance will never be deactivated by the forward model. */ +} + + + + + +bool gen_data_config_set_template( gen_data_config_type * config , const char * template_ecl_file , const char * template_data_key ) { + char * template_buffer = NULL; + bool template_valid = true; + int template_buffer_size; + + if (template_ecl_file) { + if (util_file_readable( template_ecl_file )) { + template_buffer = util_fread_alloc_file_content( template_ecl_file , &template_buffer_size); + if (template_data_key) { + if (strstr(template_buffer , template_data_key) == NULL) + template_valid = false; + } + } else + template_valid = false; + } + + if (template_valid) { + + gen_data_config_reset_template(config); + if (template_ecl_file != NULL) { + char *data_ptr; + config->template_buffer = template_buffer; + config->template_buffer_size = template_buffer_size; + if (template_data_key != NULL) { + data_ptr = strstr(config->template_buffer , template_data_key); + if (data_ptr == NULL) + util_abort("%s: template:%s can not be used - could not find data key:%s \n",__func__ , template_ecl_file , template_data_key); + else { + config->template_data_offset = data_ptr - config->template_buffer; + config->template_data_skip = strlen( template_data_key ); + } + } else { /* We are using a template without a template_data_key - the + data is assumed to come at the end of the template. */ + config->template_data_offset = strlen( config->template_buffer ); + config->template_data_skip = 0; + } + + config->template_file = util_realloc_string_copy( config->template_file , template_ecl_file ); + config->template_key = util_realloc_string_copy( config->template_key , template_data_key ); + + if (config->output_format != ASCII_TEMPLATE) + fprintf(stderr,"**WARNING: The template settings will ignored for key:%s - use OUTPUT_FORMAT:ASCII_TEMPLATE to get template behaviour\n", config->key); + } + + } + return template_valid; +} + + +const char * gen_data_config_get_template_file( const gen_data_config_type * config ) { + return config->template_file; +} + +const char * gen_data_config_get_template_key( const gen_data_config_type * config ) { + return config->template_key; +} + + + + + + + + + +/** + This function takes a string representation of one of the + gen_data_file_format_type values, and returns the corresponding + integer value. + + Will return gen_data_undefined if the string is not recognized, + calling scope must check on this return value. +*/ + + +gen_data_file_format_type gen_data_config_check_format( const char * format_string ) { + gen_data_file_format_type type = GEN_DATA_UNDEFINED; + + if (format_string != NULL) { + + if (strcmp(format_string , "ASCII") == 0) + type = ASCII; + else if (strcmp(format_string , "ASCII_TEMPLATE") == 0) + type = ASCII_TEMPLATE; + else if (strcmp(format_string , "BINARY_DOUBLE") == 0) + type = BINARY_DOUBLE; + else if (strcmp(format_string , "BINARY_FLOAT") == 0) + type = BINARY_FLOAT; + + } + + return type; +} + + +/** + The valid options are: + + INPUT_FORMAT:(ASCII|ASCII_TEMPLATE|BINARY_DOUBLE|BINARY_FLOAT) + OUTPUT_FORMAT:(ASCII|ASCII_TEMPLATE|BINARY_DOUBLE|BINARY_FLOAT) + TEMPLATE:/some/template/file + KEY: + ECL_FILE: Forward model> (In the case of gen_param - this is extracted in the calling scope). + RESULT_FILE: + +*/ + + + + +void gen_data_config_free(gen_data_config_type * config) { + int_vector_free( config->data_size_vector ); + int_vector_free( config->active_report_steps ); + + free( config->key ); + free( config->template_buffer ); + free( config->template_file ); + free( config->template_key ); + bool_vector_free( config->active_mask ); + + free(config); +} + + + + +/** + This function gets a size (from a gen_data) instance, and verifies + that the size agrees with the currently stored size and + report_step. If the report_step is new we just record the new info, + otherwise it will break hard. +*/ + + +/** + Does not work properly with: + + 1. keep_run_path - the load_file will be left hanging around - and loaded again and again. + 2. Doing forward several steps - how to (time)index the files? + +*/ + + +void gen_data_config_assert_size(gen_data_config_type * config , int data_size, int report_step) { + pthread_mutex_lock( &config->update_lock ); + { + int current_size = int_vector_safe_iget( config->data_size_vector , report_step ); + if (current_size < 0) { + int_vector_iset( config->data_size_vector , report_step , data_size ); + current_size = data_size; + } + + if (current_size != data_size) { + util_abort("%s: Size mismatch when loading:%s from file - got %d elements - expected:%d [report_step:%d] \n", + __func__ , + gen_data_config_get_key( config ), + data_size , + current_size , + report_step); + } + } + pthread_mutex_unlock( &config->update_lock ); +} + + +static void update_config_to_datamask( + gen_data_config_type * config, + const forward_load_context_type * load_context, + const bool_vector_type * data_mask, + int report_step) +{ + // Is this the first ensemble member loading for this particular report_step? + if (config->active_report_step != report_step) { + bool_vector_reset(config->active_mask); + bool_vector_iset(config->active_mask, + int_vector_iget(config->data_size_vector, report_step) - 1, + true); + config->mask_modified = true; + } + + // set config inactive according to data_mask + for (int i = 0; i < bool_vector_size(data_mask); ++i) { + if (bool_vector_iget(data_mask, i)) + continue; + bool_vector_iset(config->active_mask, i, false); + config->mask_modified = true; + } + + if (!config->mask_modified) + return; // nothing to do + + // The global mask has been modified after the last load; + // i.e. we update the on-disk representation. + char * filename = util_alloc_sprintf("%s_active", config->key); + FILE * stream = enkf_fs_open_case_tstep_file(forward_load_context_get_sim_fs(load_context), + filename, + report_step, + "w"); + free(filename); + + bool_vector_fwrite(config->active_mask, stream); + fclose(stream); + config->mask_modified = false; +} + + +/** + When the forward model is creating results for GEN_DATA instances, + it can optionally signal that not all elements in the gen_data + should be active (i.e. the forward model failed in some way); that + is handled through this function. When all ensemble members have + called this function the mask config->active_mask should be true + ONLY for the elements which are true for all members. + + This MUST be called after gen_data_config_assert_size(). +*/ +void gen_data_config_update_active(gen_data_config_type * config, + const forward_load_context_type * load_context, + const bool_vector_type * data_mask) { + pthread_mutex_lock(&config->update_lock); + int report_step = forward_load_context_get_load_step(load_context); + if (int_vector_iget(config->data_size_vector, report_step) > 0) + update_config_to_datamask(config, load_context, data_mask, report_step); + config->active_report_step = report_step; + pthread_mutex_unlock(&config->update_lock); +} + + +bool gen_data_config_has_active_mask(const gen_data_config_type * config, + enkf_fs_type * fs, + int report_step) { + char * filename = util_alloc_sprintf("%s_active", config->key ); + FILE * stream = enkf_fs_open_excase_tstep_file(fs, filename, report_step); + + bool has_mask = false; + if (stream) { + has_mask = true; + fclose(stream); + } + free(filename); + return has_mask; +} + + +/** + This function will load an active map from the enkf_fs filesystem. +*/ +void gen_data_config_load_active(gen_data_config_type * config, + enkf_fs_type * fs, + int report_step, + bool force_load) { + if (!config->dynamic) + return; /* Used as GEN_PARAM instance; loading of mask is not an option. */ + + bool fs_changed = false; + if (fs != config->last_read_fs) { + config->last_read_fs = fs; + fs_changed = true; + } + + pthread_mutex_lock( &config->update_lock ); + + if ( force_load || (int_vector_iget( config->data_size_vector , report_step ) > 0)) { + if (config->active_report_step != report_step || fs_changed) { + char * filename = util_alloc_sprintf("%s_active" , config->key ); + FILE * stream = enkf_fs_open_excase_tstep_file( fs , filename , report_step); + + if (stream != NULL) { + bool_vector_fread( config->active_mask , stream ); + fclose( stream ); + } else { + int gen_data_size = int_vector_safe_iget( config->data_size_vector, report_step ); + if (gen_data_size < 0) { + fprintf(stderr,"** Fatal internal error in function:%s \n",__func__); + fprintf(stderr,"\n"); + fprintf(stderr," 1: The active mask file:%s was not found \n", filename); + fprintf(stderr," 2: The size of the gen_data vectors has not been set\n"); + fprintf(stderr,"\n"); + fprintf(stderr, "We can not create a suitable active_mask. "); + fprintf(stderr, "Code should call gen_data_config_has_active_mask()\n\n"); + + util_abort("%s: fatal internal error - could not create a suitable active_mask\n", + __func__); + } else { + res_log_finfo("Could not locate active data elements file %s, " + "filling active vector with true all elements active.", + filename); + bool_vector_reset( config->active_mask ); + bool_vector_iset( config->active_mask, gen_data_size - 1, true); + } + } + free( filename ); + } + } + config->active_report_step = report_step; + pthread_mutex_unlock( &config->update_lock ); +} + +int gen_data_config_num_report_step( const gen_data_config_type * config ) { + return int_vector_size( config->active_report_steps ); +} + +bool gen_data_config_has_report_step( const gen_data_config_type * config , int report_step) { + return int_vector_contains_sorted( config->active_report_steps , report_step ); +} + +void gen_data_config_add_report_step( gen_data_config_type * config , int report_step) { + if (config->dynamic) { + if (!gen_data_config_has_report_step( config , report_step)) { + int_vector_append( config->active_report_steps , report_step ); + int_vector_sort( config->active_report_steps ); + } + } +} + +int gen_data_config_iget_report_step( const gen_data_config_type *config , int index) { + return int_vector_iget( config->active_report_steps , index ); +} + + +const int_vector_type * gen_data_config_get_active_report_steps( const gen_data_config_type *config) { + return config->active_report_steps; +} + + +void gen_data_config_set_ens_size( gen_data_config_type * config , int ens_size) { + config->ens_size = ens_size; +} + + +bool gen_data_config_is_dynamic( const gen_data_config_type * config ) { + return config->dynamic; +} + +void gen_data_config_get_template_data( const gen_data_config_type * config , + char ** template_buffer , + int * template_data_offset , + int * template_buffer_size , + int * template_data_skip) { + + *template_buffer = config->template_buffer; + *template_data_offset = config->template_data_offset; + *template_buffer_size = config->template_buffer_size; + *template_data_skip = config->template_data_skip; + +} + + +bool gen_data_config_valid_result_format(const char * result_file_fmt) { + if (result_file_fmt) { + if (util_is_abs_path( result_file_fmt )) + return false; + else { + if (util_int_format_count(result_file_fmt) == 1) + return true; + else + return false; + } + } else + return false; +} + + +const char * gen_data_config_get_key( const gen_data_config_type * config) { + return config->key; +} + +static const char * gen_data_config_format_name( gen_data_file_format_type format_type) { + switch (format_type ) { + case GEN_DATA_UNDEFINED: + return "UNDEFINED"; + case ASCII: + return "ASCII"; + case ASCII_TEMPLATE: + return "ASCII_TEMPLATE"; + case BINARY_FLOAT: + return "BINARY_FLOAT"; + case BINARY_DOUBLE: + return "BINARY_DOUBLE"; + default: + util_abort("%s: What the f.. \n",__func__); + return NULL; + } +} + + +void gen_data_config_fprintf_config( const gen_data_config_type * config , enkf_var_type var_type , const char * outfile , const char * infile , + const char * min_std_file , FILE * stream) { + if (var_type == PARAMETER) + fprintf( stream , CONFIG_VALUE_FORMAT , outfile ); + else + fprintf( stream , CONFIG_OPTION_FORMAT , ECL_FILE_KEY , outfile ); + + if (min_std_file != NULL) + fprintf( stream , CONFIG_OPTION_FORMAT , MIN_STD_KEY , min_std_file ); + + if (config->template_file != NULL) + fprintf( stream , CONFIG_OPTION_FORMAT , TEMPLATE_KEY , config->template_file ); + + if (config->template_key != NULL) + fprintf( stream , CONFIG_OPTION_FORMAT , KEY_KEY , config->template_key ); + + if (infile != NULL) + fprintf( stream , CONFIG_OPTION_FORMAT , RESULT_FILE_KEY , infile ); + + if (config->input_format != GEN_DATA_UNDEFINED) + fprintf( stream , CONFIG_OPTION_FORMAT , INPUT_FORMAT_KEY , gen_data_config_format_name( config->input_format )); + + if (config->output_format != GEN_DATA_UNDEFINED) + fprintf( stream , CONFIG_OPTION_FORMAT , OUTPUT_FORMAT_KEY , gen_data_config_format_name( config->output_format )); +} + + + +/*****************************************************************/ + +VOID_FREE(gen_data_config) diff --git a/libres/lib/enkf/gen_kw.cpp b/libres/lib/enkf/gen_kw.cpp new file mode 100644 index 00000000000..73f487b9448 --- /dev/null +++ b/libres/lib/enkf/gen_kw.cpp @@ -0,0 +1,485 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'gen_kw.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 + +GET_DATA_SIZE_HEADER(gen_kw); + + +struct gen_kw_struct { + int __type_id; + const gen_kw_config_type * config; + double * data; + subst_list_type * subst_list; +}; + +/*****************************************************************/ + + + +void gen_kw_free(gen_kw_type *gen_kw) { + free( gen_kw->data ); + subst_list_free( gen_kw->subst_list ); + free(gen_kw); +} + + + + + +PY_USED gen_kw_type * gen_kw_alloc(const gen_kw_config_type * config) { + gen_kw_type * gen_kw = (gen_kw_type *)util_malloc(sizeof *gen_kw ); + gen_kw->__type_id = GEN_KW; + gen_kw->config = config; + gen_kw->subst_list = subst_list_alloc( NULL ); + gen_kw->data = (double *) util_calloc( gen_kw_config_get_data_size( config ) , sizeof * gen_kw->data ); + return gen_kw; +} + + +C_USED void gen_kw_clear(gen_kw_type * gen_kw) { + int i; + for (i=0; i < gen_kw_config_get_data_size( gen_kw->config ); i++) + gen_kw->data[i] = 0.0; +} + + + +void gen_kw_copy(const gen_kw_type * src , gen_kw_type * target) { + if (src->config == target->config) { + int buffer_size = gen_kw_config_get_data_size( src->config ) * sizeof src->data; + memcpy( target->data , src->data , buffer_size ); + } else + util_abort("%s: two elements do not share config object \n",__func__); +} + + + +int gen_kw_data_size( const gen_kw_type * gen_kw ) { + return gen_kw_config_get_data_size( gen_kw->config ); +} + + + +double gen_kw_data_iget( const gen_kw_type * gen_kw, int index , bool do_transform ) +{ + double value; + int size = gen_kw_config_get_data_size( gen_kw->config ); + if (( index < 0 ) || ( index >= size )) + util_abort( "%s: index:%d invalid. Valid interval: [0,%d>.\n" , __func__ , index , size ); + + if (do_transform) { + value = gen_kw_config_transform(gen_kw->config, index, gen_kw->data[index]); + } + else { + value = gen_kw->data[index]; + } + + return value; +} + + +void gen_kw_data_set_vector( gen_kw_type * gen_kw, const double_vector_type * values ) { + int size = gen_kw_config_get_data_size( gen_kw->config ); + if (size == double_vector_size( values )) { + for (int index = 0; index < size; index++) + gen_kw->data[index] = double_vector_iget( values , index); + } else + util_abort( "%s: Invalid size for vector:%d gen_Kw:%d \n",__func__ , double_vector_size( values ) , size); +} + + + +void gen_kw_data_iset( gen_kw_type * gen_kw, int index , double value ) +{ + int size = gen_kw_config_get_data_size( gen_kw->config ); + if (( index < 0 ) || ( index >= size )) + util_abort( "%s: index:%d invalid. Valid interval: [0,%d>.\n" , __func__ , index , size ); + + gen_kw->data[index] = value; +} + + +double gen_kw_data_get( gen_kw_type * gen_kw, const char * subkey, bool do_transform ) +{ + int index = gen_kw_config_get_index(gen_kw->config, subkey); + return gen_kw_data_iget(gen_kw, index, do_transform); +} + +void gen_kw_data_set( gen_kw_type * gen_kw, const char * subkey, double value ) +{ + int index = gen_kw_config_get_index(gen_kw->config, subkey); + return gen_kw_data_iset(gen_kw, index, value); +} + + +bool gen_kw_data_has_key( gen_kw_type * gen_kw, const char * subkey ) +{ + int index = gen_kw_config_get_index(gen_kw->config, subkey); + bool has_key = ((0 <= index) && (gen_kw_data_size(gen_kw) > index))? true : false; + return has_key; +} + +C_USED bool gen_kw_write_to_buffer(const gen_kw_type *gen_kw , buffer_type * buffer, int report_step) { + const int data_size = gen_kw_config_get_data_size( gen_kw->config ); + buffer_fwrite_int( buffer , GEN_KW ); + buffer_fwrite(buffer , gen_kw->data , sizeof *gen_kw->data , data_size); + return true; +} + + + + +/** + As of 17/03/09 (svn 1811) MULTFLT has been depreceated, and GEN_KW + has been inserted as a 'drop-in-replacement'. This implies that + existing storage labeled with implemantation type 'MULTFLT' should + be silently 'upgraded' to 'GEN_KW'. +*/ + + +#define MULTFLT 102 +void gen_kw_read_from_buffer(gen_kw_type * gen_kw , buffer_type * buffer, enkf_fs_type * fs, int report_step) { + const int data_size = gen_kw_config_get_data_size( gen_kw->config ); + ert_impl_type file_type; + file_type = (ert_impl_type) buffer_fread_int(buffer); + if ((file_type == GEN_KW) || (file_type == MULTFLT)) + buffer_fread(buffer , gen_kw->data , sizeof *gen_kw->data , data_size); +} +#undef MULTFLT + +C_USED static bool gen_kw_fload(gen_kw_type * , const char *); + +bool gen_kw_initialize(gen_kw_type *gen_kw , int iens , const char * init_file , rng_type * rng ) { + if (!init_file && !rng) + util_abort("%s internal error: both init_file and rng are NULL", __func__); + + bool ret = false; + + if (init_file) + ret = gen_kw_fload(gen_kw , init_file ); + else { + const double mean = 0.0; /* Mean and std are hardcoded - the variability should be in the transformation. */ + const double std = 1.0; + const int data_size = gen_kw_config_get_data_size( gen_kw->config ); + int i; + + for (i=0; i < data_size; i++) + gen_kw->data[i] = enkf_util_rand_normal(mean , std , rng); + + ret = true; + } + return ret; +} + + + + + +void gen_kw_serialize(const gen_kw_type *gen_kw , node_id_type node_id , const active_list_type * active_list , matrix_type * A , int row_offset , int column) { + const int data_size = gen_kw_config_get_data_size( gen_kw->config ); + enkf_matrix_serialize( gen_kw->data , data_size , ECL_DOUBLE , active_list , A , row_offset , column); +} + + +void gen_kw_deserialize(gen_kw_type *gen_kw , node_id_type node_id , const active_list_type * active_list , const matrix_type * A , int row_offset , int column) { + const int data_size = gen_kw_config_get_data_size( gen_kw->config ); + enkf_matrix_deserialize( gen_kw->data , data_size , ECL_DOUBLE , active_list , A , row_offset , column); +} + + + +void gen_kw_filter_file(const gen_kw_type * gen_kw , const char * target_file) { + const char * template_file = gen_kw_config_get_template_file(gen_kw->config); + if (template_file != NULL) { + const int size = gen_kw_config_get_data_size(gen_kw->config ); + int ikw; + + for (ikw = 0; ikw < size; ikw++) { + const char * key = gen_kw_config_get_tagged_name(gen_kw->config , ikw); + subst_list_append_owned_ref(gen_kw->subst_list , key , util_alloc_sprintf("%g" , gen_kw_config_transform( gen_kw->config , ikw , gen_kw->data[ikw] )) , NULL); + } + + /* + If the target_file already exists as a symbolic link the + symbolic link is removed before creating the target file. The is + to ensure against existing symlinks pointing to a common file + outside the realization root. + */ + if (util_is_link( target_file )) + remove( target_file ); + + subst_list_filter_file( gen_kw->subst_list , template_file , target_file); + } else + util_abort("%s: internal error - tried to filter gen_kw instance without template file.\n",__func__); +} + +void gen_kw_export_values(const gen_kw_type * gen_kw, value_export_type * export_value) { + const int size = gen_kw_config_get_data_size(gen_kw->config ); + + for (int ikw = 0; ikw < size; ++ikw) { + const char * key = gen_kw_config_get_key(gen_kw->config); + const char * parameter = gen_kw_config_iget_name(gen_kw->config , ikw); + + double value = gen_kw_config_transform( gen_kw->config , ikw , gen_kw->data[ikw] ); + + value_export_append( export_value, key, parameter , value ); + + if (gen_kw_config_should_use_log_scale(gen_kw->config, ikw)) { + double log_value = log10(value); + char * log_key = util_alloc_sprintf("LOG10_%s", key); + value_export_append( export_value, log_key, parameter , log_value ); + free( log_key ); + } + } +} + +void gen_kw_write_export_file(const gen_kw_type * gen_kw , const char * filename) { + value_export_type * export_value = value_export_alloc("", filename ); + gen_kw_export_values( gen_kw, export_value ); + value_export_txt__( export_value , filename ); + value_export_free( export_value ); +} + + +void gen_kw_ecl_write_template(const gen_kw_type * gen_kw , const char * file_name){ + gen_kw_filter_file(gen_kw , file_name); +} + + +void gen_kw_ecl_write(const gen_kw_type * gen_kw , const char * run_path , const char * base_file , value_export_type * export_value) { + char * target_file; + if (run_path) + target_file = util_alloc_filename( run_path , base_file , NULL); + else + target_file = util_alloc_string_copy( base_file ); + + gen_kw_filter_file(gen_kw , target_file); + free( target_file ); + + if (export_value) + gen_kw_export_values(gen_kw, export_value); +} + + + +const char * gen_kw_get_name(const gen_kw_type * gen_kw, int kw_nr) { + return gen_kw_config_iget_name(gen_kw->config , kw_nr); +} + + +/** + This function will load values for gen_kw instance from file. The + file should be formatted as either: + + ------- + Value1 + Value2 + Value3 + .... + ValueN + ------- + + Or + + ------------ + Key3 Value3 + Key5 Value5 + Key1 Value1 + ..... + ------------ + + I.e. you can either just dump in all the numbers in one long + vector, or you can interlace numbers and keys. In the latter case + the ordering is arbitrary. + + Observe the following: + + 1. All values must be specified. + 2. The values are in the N(0,1) domain, i.e. the untransformed variables. + +*/ + +static bool gen_kw_fload(gen_kw_type * gen_kw , const char * filename) { + FILE * stream = util_fopen__( filename , "r"); + if (stream) { + const int size = gen_kw_config_get_data_size(gen_kw->config ); + bool readOK = true; + + /* First try reading all the data as one long vector. */ + { + int index = 0; + while ((index < size) && readOK) { + double value; + if (fscanf(stream,"%lg" , &value) == 1) + gen_kw->data[index] = value; + else + readOK = false; + index++; + } + } + + /* + OK - rewind and try again with interlaced key + value + pairs. Observe that we still require that ALL the elements in the + gen_kw instance are set, i.e. it is not allowed to read only some + of the keywords; but the ordering is not relevant. + + The code will be fooled (and give undefined erronous results) if + the same key appears several times. Be polite! + */ + + if (!readOK) { + int counter = 0; + readOK = true; + fseek( stream , 0 , SEEK_SET ); + + while ((counter < size) && readOK) { + char key[128]; + double value; + int fscanf_return = fscanf(stream , "%s %lg" , key , &value); + + if (fscanf_return == 2) { + int index = gen_kw_config_get_index(gen_kw->config , key); + if (index >= 0) + gen_kw->data[index] = value; + else + util_abort("%s: key:%s not recognized as part of GEN_KW instance - error when reading file:%s \n",__func__ , key , filename); + counter++; + } else { + util_abort("%s: failed to read (key,value) pair at line:%d in file:%s \n",__func__ , util_get_current_linenr( stream ) , filename); + readOK = false; + } + } + } + + if (!readOK) + util_abort("%s: failed loading from file:%s \n",__func__ , filename); + + fclose(stream); + return true; + } else + return false; +} + + + +/** + Will return 0.0 on invalid input, and set valid -> false. It is the + responsibility of the calling scope to check valid. +*/ +C_USED bool gen_kw_user_get(const gen_kw_type * gen_kw, const char * key , int report_step , double * value) { + int index = gen_kw_config_get_index(gen_kw->config , key); + + if (index >= 0) { + *value = gen_kw_config_transform(gen_kw->config , index , gen_kw->data[ index ] ); + return true; + } else { + *value = 0.0; + fprintf(stderr,"** Warning:could not lookup key:%s in gen_kw instance \n",key); + return false; + } +} + + +C_USED void gen_kw_set_inflation(gen_kw_type * inflation , const gen_kw_type * std , const gen_kw_type * min_std) { + const int data_size = gen_kw_config_get_data_size(std->config ); + const double * std_data = std->data; + const double * min_std_data = min_std->data; + double * inflation_data = inflation->data; + + { + for (int i=0; i < data_size; i++) { + if (std_data[i] > 0) + inflation_data[i] = util_double_max( 1.0 , min_std_data[i] / std_data[i]); + else + inflation_data[i] = 1; + } + } +} + + +C_USED void gen_kw_iadd( gen_kw_type * gen_kw , const gen_kw_type * delta) { + const int data_size = gen_kw_config_get_data_size( gen_kw->config ); + for(int i=0; i < data_size; i++) + gen_kw->data[i] += delta->data[i]; +} + +C_USED void gen_kw_iaddsqr( gen_kw_type * gen_kw , const gen_kw_type * delta) { + const int data_size = gen_kw_config_get_data_size( gen_kw->config ); + for(int i=0; i < data_size; i++) + gen_kw->data[i] += (delta->data[i] * delta->data[i]); +} + +C_USED void gen_kw_imul( gen_kw_type * gen_kw , const gen_kw_type * delta) { + const int data_size = gen_kw_config_get_data_size( gen_kw->config ); + for(int i=0; i < data_size; i++) + gen_kw->data[i] *= delta->data[i]; +} + +C_USED void gen_kw_scale( gen_kw_type * gen_kw , double scale_factor) { + const int data_size = gen_kw_config_get_data_size( gen_kw->config ); + for(int i=0; i < data_size; i++) + gen_kw->data[i] *= scale_factor; +} + +C_USED void gen_kw_isqrt( gen_kw_type * gen_kw ) { + const int data_size = gen_kw_config_get_data_size( gen_kw->config ); + for(int i=0; i < data_size; i++) + gen_kw->data[i] = sqrt( gen_kw->data[i] ); +} + + +/******************************************************************/ +/* Anonumously generated functions used by the enkf_node object */ +/******************************************************************/ +UTIL_SAFE_CAST_FUNCTION(gen_kw , GEN_KW); +UTIL_SAFE_CAST_FUNCTION_CONST(gen_kw , GEN_KW); +VOID_ALLOC(gen_kw); +VOID_INITIALIZE(gen_kw); +VOID_COPY(gen_kw) +VOID_FREE(gen_kw) +VOID_ECL_WRITE(gen_kw) +VOID_USER_GET(gen_kw) +VOID_WRITE_TO_BUFFER(gen_kw) +VOID_READ_FROM_BUFFER(gen_kw) +VOID_SERIALIZE(gen_kw) +VOID_DESERIALIZE(gen_kw) +VOID_SET_INFLATION(gen_kw) +VOID_CLEAR(gen_kw) +VOID_IADD(gen_kw) +VOID_SCALE(gen_kw) +VOID_IMUL(gen_kw) +VOID_IADDSQR(gen_kw) +VOID_ISQRT(gen_kw) +VOID_FLOAD(gen_kw) diff --git a/libres/lib/enkf/gen_kw_config.cpp b/libres/lib/enkf/gen_kw_config.cpp new file mode 100644 index 00000000000..12e28583228 --- /dev/null +++ b/libres/lib/enkf/gen_kw_config.cpp @@ -0,0 +1,340 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'gen_kw_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 + +#include + +#include +#include +#include +#include +#include +#include + +#define GEN_KW_CONFIG_TYPE_ID 550761 +#define GEN_KW_PARAMETER_TYPE_ID 886201 + + +typedef struct { + UTIL_TYPE_ID_DECLARATION; + char * name; + char * tagged_name; + trans_func_type * trans_func; +} gen_kw_parameter_type; + + + +struct gen_kw_config_struct { + UTIL_TYPE_ID_DECLARATION; + char * key; + vector_type * parameters; /* Vector of gen_kw_parameter_type instances. */ + char * template_file; + char * parameter_file; + const char * tag_fmt; /* Pointer to the tag_format owned by the ensemble config object. */ +}; + + +/*****************************************************************/ + +UTIL_SAFE_CAST_FUNCTION( gen_kw_parameter , GEN_KW_PARAMETER_TYPE_ID ) +UTIL_SAFE_CAST_FUNCTION_CONST( gen_kw_parameter , GEN_KW_PARAMETER_TYPE_ID ) + + +UTIL_SAFE_CAST_FUNCTION( gen_kw_config , GEN_KW_CONFIG_TYPE_ID ) +UTIL_SAFE_CAST_FUNCTION_CONST( gen_kw_config , GEN_KW_CONFIG_TYPE_ID ) + +static void gen_kw_parameter_update_tagged_name( gen_kw_parameter_type * parameter , const char * tag_fmt) { + if (tag_fmt != NULL) + parameter->tagged_name = util_realloc_sprintf( parameter->tagged_name , tag_fmt , parameter->name ); +} + + +static gen_kw_parameter_type * gen_kw_parameter_alloc( const char * parameter_name , const char * tag_fmt ) { + gen_kw_parameter_type * parameter = (gen_kw_parameter_type *)util_malloc( sizeof * parameter ); + UTIL_TYPE_ID_INIT( parameter , GEN_KW_PARAMETER_TYPE_ID); + parameter->name = util_alloc_string_copy( parameter_name ); + parameter->tagged_name = NULL; + parameter->trans_func = NULL; + gen_kw_parameter_update_tagged_name( parameter , tag_fmt ); + return parameter; +} + + +static void gen_kw_parameter_free( gen_kw_parameter_type * parameter ) { + free( parameter->name ); + free( parameter->tagged_name ); + if (parameter->trans_func != NULL) + trans_func_free( parameter->trans_func ); + free( parameter ); +} + + +static void gen_kw_parameter_free__( void * __parameter ) { + gen_kw_parameter_type * parameter = gen_kw_parameter_safe_cast( __parameter ); + gen_kw_parameter_free( parameter ); +} + + +static void gen_kw_parameter_set_trans_func( gen_kw_parameter_type * parameter , trans_func_type * trans_func ) { + if (parameter->trans_func != NULL) + trans_func_free( parameter->trans_func ); + parameter->trans_func = trans_func; +} + + + +/*****************************************************************/ + + +const char * gen_kw_config_get_template_file(const gen_kw_config_type * config) { + return config->template_file; +} + + +/* + The input template file must point to an existing file. +*/ +void gen_kw_config_set_template_file( gen_kw_config_type * config , const char * template_file ) { + if (template_file != NULL) { + if (!util_file_exists(template_file)) + util_abort("%s: the template_file:%s does not exist - aborting.\n",__func__ , template_file); + } + + config->template_file = util_realloc_string_copy( config->template_file , template_file ); +} + + + +void gen_kw_config_set_parameter_file( gen_kw_config_type * config , const char * parameter_file ) { + config->parameter_file = util_realloc_string_copy( config->parameter_file , parameter_file ); + vector_clear( config->parameters ); + if (parameter_file != NULL) { + config_parser_type * parser = config_alloc(); + config_content_type * content = config_parse(parser, + parameter_file, + "--", + NULL, + NULL, + NULL, + CONFIG_UNRECOGNIZED_ADD, + false); + for (int item_index = 0; item_index < config_content_get_size( content); item_index++ ){ + const config_content_node_type * node = config_content_iget_node(content, item_index); + const char * parameter_name = config_content_node_get_kw(node); + gen_kw_parameter_type * parameter = gen_kw_parameter_alloc(parameter_name, config->tag_fmt); + trans_func_type * trans_func = trans_func_alloc(config_content_node_get_stringlist(node)); + if (trans_func) { + gen_kw_parameter_set_trans_func(parameter, trans_func); + vector_append_owned_ref( config->parameters , parameter , gen_kw_parameter_free__ ); + } else + util_abort("%s: failed to create tranformation function for %s\n",__func__, parameter_name); + } + config_content_free(content); + config_free(parser); + } +} + + + + +const char * gen_kw_config_get_parameter_file( const gen_kw_config_type * config ) { + return config->parameter_file; +} + + +/** + Unfortunately the GUI makes it necessary(??) to be able to create + halfways initialized gen_kw_config objects; and we then have to be + able to query the gen_kw_config object if it is valid. + + Requirements: + ------------- + * template_file != NULL + * parameter_file != NULL (this means that the special schedule_prediction_file keyword will be invalid). + +*/ + +bool gen_kw_config_is_valid( const gen_kw_config_type * config ) { + if (config->template_file != NULL && config->parameter_file != NULL) + return true; + else + return false; +} + + +/** + A call to gen_kw_config_update_tag_format() must be called + afterwards, otherwise all tagged strings will just be NULL. +*/ +gen_kw_config_type * gen_kw_config_alloc_empty( const char * key , const char * tag_fmt ) { + gen_kw_config_type * gen_kw_config = (gen_kw_config_type *)util_malloc(sizeof *gen_kw_config); + UTIL_TYPE_ID_INIT(gen_kw_config , GEN_KW_CONFIG_TYPE_ID); + + gen_kw_config->key = NULL; + gen_kw_config->template_file = NULL; + gen_kw_config->parameter_file = NULL; + gen_kw_config->parameters = vector_alloc_new(); + gen_kw_config->tag_fmt = tag_fmt; + gen_kw_config->key = util_alloc_string_copy( key ); + + return gen_kw_config; +} + + + +void gen_kw_config_update( gen_kw_config_type * config , const char * template_file , const char * parameter_file ) { + gen_kw_config_set_template_file( config , template_file); + gen_kw_config_set_parameter_file( config , parameter_file ); +} + + + +double gen_kw_config_transform(const gen_kw_config_type * config , int index, double x) { + const gen_kw_parameter_type * parameter = (const gen_kw_parameter_type *)vector_iget_const( config->parameters , index ); + return trans_func_eval( parameter->trans_func , x); +} + +bool gen_kw_config_should_use_log_scale(const gen_kw_config_type * config, int index) { + const gen_kw_parameter_type * parameter = (const gen_kw_parameter_type *)vector_iget_const( config->parameters , index ); + return trans_func_use_log_scale( parameter->trans_func); +} + +void gen_kw_config_free(gen_kw_config_type * gen_kw_config) { + free( gen_kw_config->key ); + free( gen_kw_config->template_file ); + free( gen_kw_config->parameter_file ); + + vector_free( gen_kw_config->parameters ); + free(gen_kw_config); +} + + + +int gen_kw_config_get_data_size(const gen_kw_config_type * gen_kw_config) { + return vector_get_size(gen_kw_config->parameters); +} + + + +const char * gen_kw_config_get_key(const gen_kw_config_type * config ) { + return config->key; +} + +const char * gen_kw_config_get_tag_fmt(const gen_kw_config_type * config) { + return config->tag_fmt; +} + +const char * gen_kw_config_iget_name(const gen_kw_config_type * config, int kw_nr) { + const gen_kw_parameter_type * parameter = (const gen_kw_parameter_type *)vector_iget( config->parameters , kw_nr ); + return parameter->name; +} + + + + +const char * gen_kw_config_get_tagged_name(const gen_kw_config_type * config, int kw_nr) { + const gen_kw_parameter_type * parameter = (const gen_kw_parameter_type *)vector_iget( config->parameters , kw_nr ); + return parameter->tagged_name; +} + + +void gen_kw_config_update_tag_format(gen_kw_config_type * config , const char * tag_format) { + int i; + + config->tag_fmt = tag_format; + for (i=0; i < vector_get_size( config->parameters ); i++) + gen_kw_parameter_update_tagged_name( (gen_kw_parameter_type *) vector_iget( config->parameters , i ) , config->tag_fmt); +} + + +stringlist_type * gen_kw_config_alloc_name_list( const gen_kw_config_type * config ) { + + stringlist_type * name_list = stringlist_alloc_new(); + int i; + for (i=0; i < vector_get_size( config->parameters ); i++) { + const gen_kw_parameter_type * parameter = (const gen_kw_parameter_type *)vector_iget_const( config->parameters , i ); + stringlist_append_copy( name_list , parameter->name ); /* If the underlying parameter goes out scope - whom bang .. */ + } + + return name_list; +} + + + + + +/** + Will return -1 if the index is invalid. +*/ +int gen_kw_config_get_index(const gen_kw_config_type * config , const char * key) { + const int size = gen_kw_config_get_data_size(config); + bool have_key = false; + int index = 0; + + while (index < size && !have_key) { + const gen_kw_parameter_type * parameter = (const gen_kw_parameter_type *)vector_iget_const( config->parameters , index ); + if (strcmp(parameter->name , key) == 0) + have_key = true; + else + index++; + } + + if (have_key) + return index; + else + return -1; +} + + + +void gen_kw_config_fprintf_config( const gen_kw_config_type * config , const char * outfile , const char * min_std_file , FILE * stream ) { + fprintf(stream , CONFIG_VALUE_FORMAT , config->template_file ); + fprintf(stream , CONFIG_VALUE_FORMAT , outfile ); + fprintf(stream , CONFIG_VALUE_FORMAT , config->parameter_file ); + + if (min_std_file != NULL) + fprintf( stream , CONFIG_OPTION_FORMAT , MIN_STD_KEY , min_std_file); + +} + +const char * gen_kw_config_iget_function_type(const gen_kw_config_type * config, int index) { + const gen_kw_parameter_type * parameter = (const gen_kw_parameter_type *)vector_iget_const(config->parameters, index); + return trans_func_get_name(parameter->trans_func); +} + +stringlist_type * gen_kw_config_iget_function_parameter_names( const gen_kw_config_type * config, int index ) { + const gen_kw_parameter_type * parameter = (const gen_kw_parameter_type *)vector_iget_const(config->parameters, index); + return trans_func_get_param_names(parameter->trans_func); +} + +double_vector_type * gen_kw_config_iget_function_parameter_values( const gen_kw_config_type * config, int index ) { + const gen_kw_parameter_type * parameter = (const gen_kw_parameter_type *)vector_iget_const(config->parameters, index); + return trans_func_get_params(parameter->trans_func); +} + + +/*****************************************************************/ + +VOID_FREE(gen_kw_config) +VOID_GET_DATA_SIZE(gen_kw) diff --git a/libres/lib/enkf/gen_obs.cpp b/libres/lib/enkf/gen_obs.cpp new file mode 100644 index 00000000000..f1c6b4be80a --- /dev/null +++ b/libres/lib/enkf/gen_obs.cpp @@ -0,0 +1,533 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'gen_obs.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. +*/ + +/** + See the overview documentation of the observation system in + enkf_obs.c +*/ +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + + +/** + This file implemenets a structure for general observations. A + general observation is just a vector of numbers - where EnKF has no + understanding whatsover of the type of these data. The actual data + is supposed to be found in a file. + + Currently it can only observe gen_data instances - but that should + be generalized. +*/ + +/* + The std_scaling field of the xxx_obs structure can be used to scale + the standard deviation used for the observations, either to support + workflows with multiple data assimilation or to reduce the effect of + observation correlations. + + When querying for the observation standard deviation using + gen_obs_iget_std() the user input value of standard deviation will + be returned, whereas when the function gen_obs_measure() is used the + std_scaling will be incorporated in the result. +*/ + +#define GEN_OBS_TYPE_ID 77619 + +struct gen_obs_struct { + UTIL_TYPE_ID_DECLARATION; + int obs_size; /* This is the total size of the observation vector. */ + int * data_index_list; /* The indexes which are observed in the corresponding gen_data instance - of length obs_size. */ + bool observe_all_data; /* Flag which indiactes whether all data in the gen_data instance should be observed - in that case we must do a size comparizon-check at use time. */ + + double * obs_data; /* The observed data. */ + double * obs_std; /* The observed standard deviation. */ + double * std_scaling; /* Scaling factor for the standard deviation */ + + char * obs_key; /* The key this observation is held by - in the enkf_obs structur (only for debug messages). */ + gen_data_file_format_type obs_format; /* The format, i.e. ASCII, binary_double or binary_float, of the observation file. */ + matrix_type * error_covar; + gen_data_config_type * data_config; +}; + +/******************************************************************/ + + +static UTIL_SAFE_CAST_FUNCTION_CONST(gen_obs , GEN_OBS_TYPE_ID) +static UTIL_SAFE_CAST_FUNCTION(gen_obs , GEN_OBS_TYPE_ID) + +void gen_obs_free(gen_obs_type * gen_obs) { + free(gen_obs->obs_data); + free(gen_obs->obs_std); + free(gen_obs->data_index_list); + free(gen_obs->obs_key); + free(gen_obs->std_scaling); + if (gen_obs->error_covar != NULL) + matrix_free( gen_obs->error_covar ); + + free(gen_obs); +} + + +static double IGET_SCALED_STD(const gen_obs_type * gen_obs, int index) { + return gen_obs->obs_std[index] * gen_obs->std_scaling[index]; +} + + +/** + This function loads the actual observations from disk, and + initializes the obs_data and obs_std pointers with the + observations. It also sets the obs_size field of the gen_obs + instance. + + The file with observations should be a long vector of 2N elements, + where the first N elements are data values, and the last N values + are the corresponding standard deviations. + + The file is loaded with the gen_common_fload_alloc() function, and + can be in formatted ASCII or binary_float / binary_double. Observe + that there is *NO* header information in this file. +*/ + + +static void gen_obs_set_data(gen_obs_type * gen_obs, int buffer_size , const double * buffer) { + gen_obs->obs_size = buffer_size / 2; + gen_obs->obs_data = (double *) util_realloc(gen_obs->obs_data , gen_obs->obs_size * sizeof * gen_obs->obs_data ); + gen_obs->obs_std = (double *) util_realloc(gen_obs->obs_std , gen_obs->obs_size * sizeof * gen_obs->obs_std ); + gen_obs->std_scaling = (double *) util_realloc(gen_obs->std_scaling , gen_obs->obs_size * sizeof * gen_obs->std_scaling ); + gen_obs->data_index_list = (int *) util_realloc(gen_obs->data_index_list , gen_obs->obs_size * sizeof * gen_obs->data_index_list ); + { + int iobs; + double * double_buffer = (double * ) buffer; + for (iobs = 0; iobs < gen_obs->obs_size; iobs++) { + gen_obs->obs_data[iobs] = double_buffer[2*iobs]; + gen_obs->obs_std[iobs] = double_buffer[2*iobs + 1]; + gen_obs->std_scaling[iobs] = 1.0; + gen_obs->data_index_list[iobs] = iobs; + + } + } +} + + +void gen_obs_load_observation(gen_obs_type * gen_obs, const char * obs_file) { + ecl_type_enum load_type; + void * buffer; + int buffer_size = 0; + buffer = gen_common_fload_alloc(obs_file , gen_obs->obs_format , ECL_DOUBLE , &load_type , &buffer_size); + + /** Ensure that the data is of type double. */ + if (load_type == ECL_FLOAT_TYPE) { + double * double_data = (double *)util_calloc(gen_obs->obs_size , sizeof * double_data ); + util_float_to_double(double_data , (const float *) buffer , buffer_size); + free(buffer); + buffer = double_data; + } + + gen_obs_set_data( gen_obs , buffer_size , (double *) buffer ); + free(buffer); +} + + + +void gen_obs_set_scalar( gen_obs_type * gen_obs , double scalar_value , double scalar_std) { + double buffer[2] = { scalar_value , scalar_std }; + gen_obs_set_data( gen_obs , 2 , buffer ); +} + +void gen_obs_attach_data_index( gen_obs_type * obs , const int_vector_type * data_index ) { + free( obs->data_index_list ); + obs->data_index_list = int_vector_alloc_data_copy( data_index ); + obs->observe_all_data = false; +} + + +void gen_obs_load_data_index( gen_obs_type * obs , const char * data_index_file) { + /* Parsing an a file with integers. */ + free( obs->data_index_list ); + obs->data_index_list = (int *) gen_common_fscanf_alloc( data_index_file , ECL_INT , &obs->obs_size); + obs->observe_all_data = false; +} + + +void gen_obs_parse_data_index( gen_obs_type * obs , const char * data_index_string) { + /* Parsing a string of the type "1,3,5,9-100,200,202,300-1000" */ + int_vector_type * index_list = string_util_alloc_active_list( data_index_string ); + int_vector_shrink( index_list ); + gen_obs_attach_data_index( obs , index_list ); + int_vector_free( index_list ); +} + + + +gen_obs_type * gen_obs_alloc__(const gen_data_config_type * data_config , const char * obs_key) { + gen_obs_type * obs = (gen_obs_type *)util_malloc(sizeof * obs); + UTIL_TYPE_ID_INIT( obs , GEN_OBS_TYPE_ID ); + obs->obs_data = NULL; + obs->obs_std = NULL; + obs->std_scaling = NULL; + obs->data_index_list = NULL; + obs->obs_format = ASCII; /* Hardcoded for now. */ + obs->obs_key = util_alloc_string_copy( obs_key ); + obs->data_config = (gen_data_config_type *) data_config; // casting away the const ... + obs->observe_all_data = true; + obs->error_covar = NULL; + return obs; +} + + +/** + data_index_file is the name of a file with indices which should be + observed, data_inde_string is the same, in the form of a + "1,2,3,4-10, 17,19,22-100" string. Only one of these items can be + != NULL. If both are NULL it is assumed that all the indices of the + gen_data instance should be observed. + + @error_covar_file is the name of file which contains a matrix of + error-covariance. The file data will be read with the function + matrix_fscanf_data(), i.e. it should consist of formatted + numbers. Since the matrix is symmetric it does not matter whether + it is represented in row-major or column-major order; newlines for + pretty reading can be inserted but are not necessary. + + The error_covar_file should contain NO header information. +*/ + + +gen_obs_type * gen_obs_alloc(const gen_data_config_type * data_config , const char * obs_key , const char * obs_file , double scalar_value , double scalar_error , const char * data_index_file , const char * data_index_string , const char * error_covar_file) { + gen_obs_type * obs = gen_obs_alloc__( data_config , obs_key ); + if (obs_file) + gen_obs_load_observation(obs , obs_file ); /* The observation data is loaded - and internalized at boot time - even though it might not be needed for a long time. */ + else + gen_obs_set_scalar( obs , scalar_value , scalar_error ); + + + if (data_index_file) + gen_obs_load_data_index( obs , data_index_file ); + else if (data_index_string) + gen_obs_parse_data_index( obs , data_index_string ); + + + if (error_covar_file != NULL) { + FILE * stream = util_fopen( error_covar_file , "r"); + + obs->error_covar = matrix_alloc( obs->obs_size , obs->obs_size ); + matrix_fscanf_data( obs->error_covar , false , stream ); + + fclose( stream ); + } else + obs->error_covar = NULL; + + return obs; +} + + + +static void gen_obs_assert_data_size(const gen_obs_type * gen_obs, const gen_data_type * gen_data) { + if (gen_obs->observe_all_data) { + int data_size = gen_data_get_size( gen_data ); + if (gen_obs->obs_size != data_size) + util_abort("%s: size mismatch: Observation: %s:%d Data: %s:%d \n" , __func__ , gen_obs->obs_key , gen_obs->obs_size , gen_data_get_key( gen_data ) , data_size); + + } + /* + Else the user has explicitly entered indices to observe in the + gen_data instances, and we just have to trust them (however the + gen_data_iget() does a range check. + */ +} + + +double gen_obs_chi2(const gen_obs_type * gen_obs , const gen_data_type * gen_data, node_id_type node_id) { + gen_obs_assert_data_size(gen_obs , gen_data); + { + const bool_vector_type * forward_model_active = gen_data_config_get_active_mask( gen_obs->data_config ); + double sum_chi2 = 0; + for (int iobs = 0; iobs < gen_obs->obs_size; iobs++) { + int data_index = gen_obs->data_index_list[iobs]; + if (forward_model_active && (bool_vector_iget( forward_model_active , data_index ) == false)) + continue; /* Forward model has deactivated this index - just continue. */ + { + double d = gen_data_iget_double( gen_data , data_index); + double x = (d - gen_obs->obs_data[iobs]) / gen_obs->obs_std[iobs]; + sum_chi2 += x*x; + } + } + return sum_chi2; + } +} + + + +void gen_obs_measure(const gen_obs_type * gen_obs , const gen_data_type * gen_data , node_id_type node_id , meas_data_type * meas_data, const active_list_type * __active_list) { + gen_obs_assert_data_size(gen_obs , gen_data); + { + int active_size = active_list_get_active_size( __active_list , gen_obs->obs_size ); + meas_block_type * meas_block = meas_data_add_block( meas_data , gen_obs->obs_key , node_id.report_step , active_size ); + active_mode_type active_mode = active_list_get_mode( __active_list ); + const bool_vector_type * forward_model_active = gen_data_config_get_active_mask( gen_obs->data_config ); + + int iobs; + if (active_mode == ALL_ACTIVE) { + for (iobs = 0; iobs < gen_obs->obs_size; iobs++) { + int data_index = gen_obs->data_index_list[iobs] ; + + if (forward_model_active != NULL) { + if (!bool_vector_iget( forward_model_active , data_index )) + continue; /* Forward model has deactivated this index - just continue. */ + } + + meas_block_iset( meas_block , node_id.iens , iobs , gen_data_iget_double( gen_data , data_index )); + } + } else if ( active_mode == PARTLY_ACTIVE) { + const int * active_list = active_list_get_active( __active_list ); + int index; + + for (index = 0; index < active_size; index++) { + iobs = active_list[ index ]; + int data_index = gen_obs->data_index_list[iobs] ; + if (forward_model_active != NULL) { + if (!bool_vector_iget( forward_model_active , data_index )) + continue; /* Forward model has deactivated this index - just continue. */ + } + meas_block_iset( meas_block , node_id.iens , index , gen_data_iget_double( gen_data , data_index )); + } + } + } +} + + + +C_USED void gen_obs_get_observations(gen_obs_type * gen_obs , obs_data_type * obs_data, enkf_fs_type * fs, int report_step , const active_list_type * __active_list) { + const bool_vector_type * forward_model_active = NULL; + if (gen_data_config_has_active_mask( gen_obs->data_config , fs, report_step)) { + gen_data_config_load_active( gen_obs->data_config , fs, report_step , true); + forward_model_active = gen_data_config_get_active_mask( gen_obs->data_config ); + } + + { + active_mode_type active_mode = active_list_get_mode( __active_list ); + int active_size = active_list_get_active_size( __active_list , gen_obs->obs_size ); + obs_block_type * obs_block = obs_data_add_block( obs_data , gen_obs->obs_key , active_size , NULL , false); + + if (active_mode == ALL_ACTIVE) { + for (int iobs = 0; iobs < gen_obs->obs_size; iobs++) + obs_block_iset( obs_block , iobs , gen_obs->obs_data[iobs] , IGET_SCALED_STD( gen_obs , iobs )); + + /* Setting some of the elements as missing, i.e. deactivated by the forward model. */ + if (forward_model_active != NULL) { + for (int iobs = 0; iobs < gen_obs->obs_size; iobs++) { + int data_index = gen_obs->data_index_list[ iobs ]; + if (!bool_vector_iget( forward_model_active , data_index )) + obs_block_iset_missing( obs_block , iobs ); + } + } + } else if (active_mode == PARTLY_ACTIVE) { + const int * active_list = active_list_get_active( __active_list ); + int active_size = active_list_get_active_size( __active_list , gen_obs->obs_size); + /* + There are three different indices active at the same time here: + + active_index : [0 ... active_size> - running over the size of + the current local observation. + + iobs : [0 ... size(obs)> - running over the complete size of + the observation node. + + data_index : The index in the data space corresponding to + the observation index iobs. + + */ + + for (int active_index = 0; active_index < active_size; active_index++) { + int iobs = active_list[active_index]; + obs_block_iset( obs_block , active_index , gen_obs->obs_data[iobs] , IGET_SCALED_STD( gen_obs , iobs )); + { + int data_index = gen_obs->data_index_list[ iobs ]; + if ((forward_model_active != NULL) && (!bool_vector_iget( forward_model_active , data_index ))) + obs_block_iset_missing( obs_block , active_index ); + } + } + } + } +} + + + +/** + In general the gen_obs observation vector can be smaller than the + gen_data field it is observing, i.e. we can have a situation like + this: + + Data Obs + ---- --- + + [ 6.0 ] ----\ + [ 2.0 ] \---> [ 6.3 ] + [ 3.0 ] ---------> [ 2.8 ] + [ 2.0 ] /---> [ 4.3 ] + [ 4.5 ] ----/ + + The situation here is as follows: + + 1. We have a gen data vector with five elements. + + 2. We have an observation vector of three elements, which observes + three of the elements in the gen_data vector, in this particular + case the data_index_list of the observation equals: [0 , 2 , 4]. + + Now when we want to look at the match of observation quality of the + last element in the observation vector it would be natural to use + the user_get key: "obs_key:2" - however this is an observation of + data element number 4, i.e. as seen from data context (when adding + observations to an ensemble plot) the natural indexing would be: + "data_key:4". + + + The function gen_obs_user_get_with_data_index() will do the + translation from data based indexing to observation based indexing, i.e. + + gen_obs_user_get_with_data_index("4") + + will do an inverse lookup of the '4' and further call + + gen_obs_user_get("2") + +*/ + + +void gen_obs_user_get(const gen_obs_type * gen_obs , const char * index_key , double * value , double * std , bool * valid) { + int index; + *valid = false; + + if (util_sscanf_int( index_key , &index)) { + if ((index >= 0) && (index < gen_obs->obs_size)) { + *valid = true; + *value = gen_obs->obs_data[ index ]; + *std = gen_obs->obs_std[ index ]; + } + } +} + + + +void gen_obs_user_get_with_data_index(const gen_obs_type * gen_obs , const char * index_key , double * value , double * std , bool * valid) { + if (gen_obs->observe_all_data) + /* The observation and data vectors are equally long - no reverse lookup necessary. */ + gen_obs_user_get(gen_obs , index_key , value , std , valid); + else { + *valid = false; + int data_index; + if (util_sscanf_int( index_key , &data_index )) { + int obs_index = 0; + do { + if (gen_obs->data_index_list[ obs_index ] == data_index) + /* Found it - will use the 'obs_index' value. */ + break; + + obs_index++; + } while (obs_index < gen_obs->obs_size); + if (obs_index < gen_obs->obs_size) { /* The reverse lookup succeeded. */ + *valid = true; + *value = gen_obs->obs_data[ obs_index ]; + *std = gen_obs->obs_std[ obs_index ]; + } + } + } +} + +C_USED void gen_obs_update_std_scale(gen_obs_type * gen_obs, double std_multiplier, const active_list_type * active_list) { + if (active_list_get_mode( active_list ) == ALL_ACTIVE) { + for (int i = 0; i < gen_obs->obs_size; i++) + gen_obs->std_scaling[i] = std_multiplier; + } else { + const int * active_index = active_list_get_active( active_list ); + int size = active_list_get_active_size( active_list , gen_obs->obs_size ); + for (int i=0; i < size; i++) { + int obs_index = active_index[i]; + if (obs_index >= gen_obs->obs_size) { + util_abort("[Gen_Obs] Index out of bounds %d [0, %d]", obs_index, gen_obs->obs_size - 1); + } + gen_obs->std_scaling[ obs_index ] = std_multiplier; + } + } +} + + + +int gen_obs_get_size(const gen_obs_type * gen_obs){ + return gen_obs->obs_size; +} + +double gen_obs_iget_std(const gen_obs_type * gen_obs, int index){ + return gen_obs->obs_std[index]; +} + +double gen_obs_iget_std_scaling(const gen_obs_type * gen_obs, int index) { + return gen_obs->std_scaling[index]; +} + + +double gen_obs_iget_value(const gen_obs_type * gen_obs, int index){ + return gen_obs->obs_data[index]; +} + +void gen_obs_load_values(const gen_obs_type * gen_obs, int size, double * data) { + for (int i=0; i < size; i++) { + data[i] = gen_obs->obs_data[i]; + } +} + + +void gen_obs_load_std(const gen_obs_type * gen_obs, int size, double * data) { + for (int i=0; i < size; i++) { + data[i] = gen_obs->obs_std[i]; + } +} + + +int gen_obs_get_obs_index(const gen_obs_type * gen_obs, int index){ + if(index < 0 || index >= gen_obs->obs_size){ + util_abort("[Gen_Obs] Index out of bounds %d [0, %d]", index, gen_obs->obs_size - 1); + } + + if (gen_obs->observe_all_data){ + return index; + } else { + return gen_obs->data_index_list[index]; + } +} + + + +/*****************************************************************/ +UTIL_IS_INSTANCE_FUNCTION(gen_obs , GEN_OBS_TYPE_ID) +VOID_FREE(gen_obs) +VOID_GET_OBS(gen_obs) +VOID_MEASURE(gen_obs , gen_data) +VOID_USER_GET_OBS(gen_obs) +VOID_CHI2(gen_obs , gen_data) +VOID_UPDATE_STD_SCALE(gen_obs) diff --git a/libres/lib/enkf/gen_test.cpp b/libres/lib/enkf/gen_test.cpp new file mode 100644 index 00000000000..cd293b4e2d4 --- /dev/null +++ b/libres/lib/enkf/gen_test.cpp @@ -0,0 +1,28 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'gen_test.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 + + + + +int main(int argc , char **argv) { +} diff --git a/libres/lib/enkf/hook_manager.cpp b/libres/lib/enkf/hook_manager.cpp new file mode 100644 index 00000000000..d40f1c9d14c --- /dev/null +++ b/libres/lib/enkf/hook_manager.cpp @@ -0,0 +1,203 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'hook_manager.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 + +struct hook_manager_struct { + vector_type * hook_workflow_list; /* vector of hook_workflow_type instances */ + runpath_list_type * runpath_list; + ert_workflow_list_type * workflow_list; + hash_type * input_context; +}; + +hook_manager_type * hook_manager_alloc( + ert_workflow_list_type * workflow_list, + const config_content_type * config_content) { + + hook_manager_type * hook_manager = hook_manager_alloc_default(workflow_list); + + if(config_content) + hook_manager_init(hook_manager, config_content); + + return hook_manager; +} + +void hook_manager_free( hook_manager_type * hook_manager ) { + if (hook_manager->runpath_list) + runpath_list_free( hook_manager->runpath_list ); + + vector_free( hook_manager->hook_workflow_list ); + hash_free( hook_manager->input_context ); + free( hook_manager ); +} + + +runpath_list_type * hook_manager_get_runpath_list(const hook_manager_type * hook_manager) { + return hook_manager->runpath_list; +} + + +static void hook_manager_add_workflow( hook_manager_type * hook_manager , const char * workflow_name , hook_run_mode_enum run_mode) { + if (ert_workflow_list_has_workflow( hook_manager->workflow_list , workflow_name) ){ + workflow_type * workflow = ert_workflow_list_get_workflow( hook_manager->workflow_list , workflow_name); + hook_workflow_type * hook = hook_workflow_alloc( workflow , run_mode ); + vector_append_owned_ref(hook_manager->hook_workflow_list, hook , hook_workflow_free__); + } + else { + fprintf(stderr, "** Warning: While hooking workflow: %s not recognized among the list of loaded workflows.", workflow_name); + } +} + +hook_manager_type * hook_manager_alloc_default(ert_workflow_list_type * workflow_list) { + hook_manager_type * hook_manager = (hook_manager_type *)util_malloc( sizeof * hook_manager ); + hook_manager->workflow_list = workflow_list; + + hook_manager->hook_workflow_list = vector_alloc_new(); + + config_parser_type * config = config_alloc(); + config_content_type * site_config_content = site_config_alloc_content(config); + + if (config_content_has_item( site_config_content , HOOK_WORKFLOW_KEY)) { + for (int ihook = 0; ihook < config_content_get_occurences(site_config_content , HOOK_WORKFLOW_KEY); ihook++) { + const char * workflow_name = config_content_iget( site_config_content , HOOK_WORKFLOW_KEY, ihook , 0 ); + hook_run_mode_enum run_mode = hook_workflow_run_mode_from_name(config_content_iget(site_config_content , HOOK_WORKFLOW_KEY , ihook , 1)); + hook_manager_add_workflow( hook_manager , workflow_name , run_mode ); + } + } + config_free(config); + config_content_free(site_config_content); + + hook_manager->runpath_list = NULL; + + hook_manager->input_context = hash_alloc(); + + return hook_manager; +} + +hook_manager_type * hook_manager_alloc_full( + ert_workflow_list_type * workflow_list, + const char * runpath_list_file, + const char ** hook_workflow_names, + const char ** hook_workflow_run_modes, + int hook_workflow_count) { + + hook_manager_type * hook_manager = hook_manager_alloc_default(workflow_list); + + for (int i = 0; i < hook_workflow_count; ++i) { + const char * workflow_name = hook_workflow_names[i]; + hook_run_mode_enum run_mode = hook_workflow_run_mode_from_name(hook_workflow_run_modes[i]); + hook_manager_add_workflow( hook_manager , workflow_name , run_mode ); + } + + hook_manager->runpath_list = runpath_list_alloc(runpath_list_file); + + return hook_manager; +} + +void hook_manager_init( hook_manager_type * hook_manager , const config_content_type * config_content) { + if (config_content_has_item( config_content , HOOK_WORKFLOW_KEY)) { + for (int ihook = 0; ihook < config_content_get_occurences(config_content , HOOK_WORKFLOW_KEY); ihook++) { + const char * workflow_name = config_content_iget( config_content , HOOK_WORKFLOW_KEY, ihook , 0 ); + hook_run_mode_enum run_mode = hook_workflow_run_mode_from_name(config_content_iget(config_content , HOOK_WORKFLOW_KEY , ihook , 1)); + hook_manager_add_workflow( hook_manager , workflow_name , run_mode ); + } + } + + { + char * runpath_list_file; + + if (config_content_has_item(config_content, RUNPATH_FILE_KEY)) + runpath_list_file = util_alloc_string_copy( config_content_get_value_as_abspath(config_content, RUNPATH_FILE_KEY)); + else + runpath_list_file = util_alloc_filename( config_content_get_config_path(config_content), RUNPATH_LIST_FILE, NULL); + + hook_manager->runpath_list = runpath_list_alloc(runpath_list_file); + free( runpath_list_file); + } +} + + + +void hook_manager_add_config_items( config_parser_type * config ) { + config_schema_item_type * item; + + item = config_add_schema_item( config , HOOK_WORKFLOW_KEY , false ); + config_schema_item_set_argc_minmax(item , 2 , 2 ); + config_schema_item_iset_type( item , 0 , CONFIG_STRING ); + config_schema_item_iset_type( item , 1 , CONFIG_STRING ); + { + stringlist_type * argv = stringlist_alloc_new(); + + stringlist_append_copy(argv, RUN_MODE_PRE_SIMULATION_NAME); + stringlist_append_copy(argv, RUN_MODE_POST_SIMULATION_NAME); + stringlist_append_copy(argv, RUN_MODE_PRE_UPDATE_NAME); + stringlist_append_copy(argv, RUN_MODE_POST_UPDATE_NAME); + config_schema_item_set_indexed_selection_set(item, 1, argv); + + stringlist_free( argv ); + } + + item = config_add_schema_item( config , RUNPATH_FILE_KEY , false ); + config_schema_item_set_argc_minmax(item , 1 , 1 ); + config_schema_item_iset_type(item, 0, CONFIG_PATH); +} + + +const char * hook_manager_get_runpath_list_file( const hook_manager_type * hook_manager) { + return runpath_list_get_export_file( hook_manager->runpath_list ); +} + + + +void hook_manager_run_workflows( const hook_manager_type * hook_manager , hook_run_mode_enum run_mode , void * self ) +{ + bool verbose = false; + for (int i=0; i < vector_get_size( hook_manager->hook_workflow_list ); i++) { + hook_workflow_type * hook_workflow = (hook_workflow_type *)vector_iget( hook_manager->hook_workflow_list , i ); + if (hook_workflow_get_run_mode(hook_workflow) == run_mode) { + workflow_type * workflow = hook_workflow_get_workflow( hook_workflow ); + workflow_run( workflow, self , verbose , ert_workflow_list_get_context( hook_manager->workflow_list )); + /* + The workflow_run function will return a bool to indicate + success/failure, and in the case of error the function + workflow_get_last_error() can be used to get a config_error + object. + */ + } + } +} + +const hook_workflow_type * hook_manager_iget_hook_workflow(const hook_manager_type * hook_manager, int index){ + return (hook_workflow_type *) vector_iget(hook_manager->hook_workflow_list, index); +} + +int hook_manager_get_size(const hook_manager_type * hook_manager){ + return vector_get_size(hook_manager->hook_workflow_list); +} diff --git a/libres/lib/enkf/hook_workflow.cpp b/libres/lib/enkf/hook_workflow.cpp new file mode 100644 index 00000000000..461269499c0 --- /dev/null +++ b/libres/lib/enkf/hook_workflow.cpp @@ -0,0 +1,105 @@ +/* + Copyright (C) 2015 Equinor ASA, Norway. + + The file 'hook_workflow.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 + +#define RUN_MODE_PRE_FIRST_UPDATE_NAME "PRE_FIRST_UPDATE" +#define RUN_MODE_PRE_SIMULATION_NAME "PRE_SIMULATION" +#define RUN_MODE_POST_SIMULATION_NAME "POST_SIMULATION" +#define RUN_MODE_PRE_UPDATE_NAME "PRE_UPDATE" +#define RUN_MODE_POST_UPDATE_NAME "POST_UPDATE" + +#define HOOK_WORKFLOW_TYPE_ID 7321780 + +struct hook_workflow_struct { + UTIL_TYPE_ID_DECLARATION; + hook_run_mode_enum run_mode; + workflow_type * workflow; +}; + + +static UTIL_SAFE_CAST_FUNCTION( hook_workflow , HOOK_WORKFLOW_TYPE_ID); + +hook_workflow_type * hook_workflow_alloc( workflow_type * workflow , hook_run_mode_enum run_mode ) { + hook_workflow_type * hook_workflow = (hook_workflow_type *)util_malloc( sizeof * hook_workflow ); + UTIL_TYPE_ID_INIT( hook_workflow , HOOK_WORKFLOW_TYPE_ID); + hook_workflow->run_mode = run_mode; + hook_workflow->workflow = workflow; + return hook_workflow; +} + +void hook_workflow_free( hook_workflow_type * hook_workflow ) { + free( hook_workflow ); +} + +void hook_workflow_free__( void * arg ) { + hook_workflow_type * hook_workflow = hook_workflow_safe_cast( arg ); + hook_workflow_free( hook_workflow ); +} + + + +workflow_type* hook_workflow_get_workflow( const hook_workflow_type * hook_workflow ) { + return hook_workflow->workflow; +} + + +bool hook_workflow_run_workflow( const hook_workflow_type * hook_workflow, ert_workflow_list_type * workflow_list, void * self) { + bool verbose = false; + if (hook_workflow->workflow != NULL ) { + bool result = ert_workflow_list_run_workflow__( workflow_list, hook_workflow->workflow , verbose , self); + return result; + } + else + return false; +} + + +hook_run_mode_enum hook_workflow_run_mode_from_name( const char * run_mode ) { + hook_run_mode_enum mode; + if (strcmp( run_mode , RUN_MODE_PRE_SIMULATION_NAME) == 0) + mode = PRE_SIMULATION; + else if (strcmp( run_mode , RUN_MODE_POST_SIMULATION_NAME) == 0) + mode = POST_SIMULATION; + else if (strcmp( run_mode , RUN_MODE_PRE_UPDATE_NAME) == 0) + mode = PRE_UPDATE; + else if (strcmp( run_mode , RUN_MODE_POST_UPDATE_NAME) == 0) + mode = POST_UPDATE; + else if (strcmp( run_mode , RUN_MODE_PRE_FIRST_UPDATE_NAME) == 0) + mode = PRE_FIRST_UPDATE; + else { + util_abort("%s: unrecognized run mode :%s \n",__func__ , run_mode); + mode = POST_UPDATE; /* Dummy */ + } + return mode; +} + + +hook_run_mode_enum hook_workflow_get_run_mode( const hook_workflow_type * hook_workflow ){ + return hook_workflow->run_mode; +} + diff --git a/libres/lib/enkf/lisp.el b/libres/lib/enkf/lisp.el new file mode 100644 index 00000000000..a352811e0f2 --- /dev/null +++ b/libres/lib/enkf/lisp.el @@ -0,0 +1,27 @@ +(defun insert-cast (type) + (interactive "sType: ") + (let ((const (y-or-n-p "Add const modifier"))) + (insert "(") + (if const (insert "const ")) + (insert (format "%s *) " type)) + (save-buffer))) + + +(defun insert-guard () + (interactive) + (save-excursion + (insert "#ifdef __cplusplus\n") + (insert "extern \"C\" {\n") + (insert "#endif\n") + + (search-forward "#endif") + (beginning-of-line) + (insert "#ifdef __cplusplus\n") + (insert "}\n") + (insert "#endif\n")) + (save-buffer)) + + + + + diff --git a/libres/lib/enkf/local_config.cpp b/libres/lib/enkf/local_config.cpp new file mode 100644 index 00000000000..8e37dae74eb --- /dev/null +++ b/libres/lib/enkf/local_config.cpp @@ -0,0 +1,292 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'local_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 +#include + +/******************************************************************/ + +/* + + +-------------------------- local_updatestep_type ---------------------------------------+ + | | + | | + | +----------------- local_ministep_type --------------------------------------+ | + | | | | + | | / +--- local_dataset_type ---+ | | + | | | | PRESSURE | | | + | | | | SWAT | | | + | | | | SGAS | | | + | | | +--------------------------+ | | + | | +-- local_obsset_type ---+ | | | + | | | WWCT:OP_2 | | +--- local_dataset_type ---+ | | + | | | WGOR:OP_1 | | | MULTFLT1 | | | + | | | RFT:WELL1 | <------| | MULTFLT2 | | | + | | | RFT:WELL3 | | | MULTFLT3 | | | + | | | WWCT:WELLX | | +--------------------------+ | | + | | +------------------------+ | | | + | | | +--- local_dataset_type ---+ | | + | | | | RELPERM1 | | | + | | | | RELPERM2 | | | + | | | | RELPERM3 | | | + | | \ +--------------------------+ | | + | | | | + | +----------------------------------------------------------------------------+ | + | | + | | + | +----------------- local_ministep_type --------------------------------------+ | + | | | | + | | / +--- local_dataset_type ---+ | | + | | +-- local_obsset_type ---+ | | PERMX PORO | | | + | | | 4D Seismic | | | PRESSURE SWAT | | | + | | | Gravimetri | | | SGAS | | | + | | | | <------| +--------------------------+ | | + | | | | | | | + | | | | | +--- local_dataset_type ---+ | | + | | +------------------------+ | | MULTFLT1 | | | + | | | | MULTFLT2 | | | + | | | | MULTFLT3 | | | + | | \ +--------------------------+ | | + | | | | + | +----------------------------------------------------------------------------+ | + | | + +----------------------------------------------------------------------------------------+ + +This figure illustrates the different objects when configuring local +analysis: + +local_updatestep_type: This is is the top level configuration of the + updating at one timestep. In principle you can have different + updatestep configurations at the different timesteps, but it will + typically be identical for all the time steps. Observe that the + update at one time step can typically conist of several enkf + updates, this is handled by using several local_ministep. + +local_ministep_type: The ministep defines a collection of observations + and state/parameter variables which are mutually dependant on + eachother and should be updated together. The local_ministep will + consist of *ONE* local_obsset of observations, and one or more + local_dataset of data which should be updated. + +local_obsset_type: This is a collection of observation data; there is + exactly one local_obsset for each local_ministep. + +local_dataset_type: This is a collection of data/parameters which + should be updated together in the EnKF updating. + + +How the local_dataset_type is configured is quite important for the +core EnKF updating: + + 1. All the members in one local_dataset instance are serialized and + packed in the A-matrix together; i.e. in the example above the + parameters RELPERM1,RELPERM2 and RELPERM3 are updated in one go. + + 2. When using the standard EnKF the X matrix is calculated using + the actual data vectors, and the results will be identical if we + use one large local_dataset instance or several small. However + when using more advanced techniques where the A matrix is used + explicitly when calculating the update this will matter. + + 3. If you have not entered a local configuration explicitly the + default ALL_ACTIVE local configuration will be used. +*/ + + +struct local_config_struct { + local_updatestep_type * default_updatestep; /* A default report step returned if no particular report step has been installed for this time index. */ + hash_type * updatestep_storage; /* These three hash tables are the 'holding area' for the local_updatestep, */ + hash_type * ministep_storage; /* local_ministep instances. */ + hash_type * dataset_storage; + hash_type * obsdata_storage; +}; + + +/** + Instances of local_updatestep and local_ministep are allocated from + the local_config object, and then subsequently manipulated from the calling scope. +*/ + +static local_updatestep_type * local_config_alloc_updatestep( local_config_type * local_config , const char * key ) { + local_updatestep_type * updatestep = local_updatestep_alloc( key ); + hash_insert_hash_owned_ref( local_config->updatestep_storage , key , updatestep , local_updatestep_free__); + return updatestep; +} + + + +/* + The local_config_clear() function will remove all current local configuration, + and then reallocate a new empty updatestep configuration. +*/ +void local_config_clear( local_config_type * local_config ) { + local_config->default_updatestep = NULL; + hash_clear( local_config->updatestep_storage ); + hash_clear( local_config->ministep_storage ); + hash_clear( local_config->dataset_storage ); + hash_clear( local_config->obsdata_storage ); + local_config->default_updatestep = local_config_alloc_updatestep(local_config, "DEFAULT"); +} + + +/* + The local_config_clear_active() function will reset the current active + updatestep, but the named building blocks of type ministep, local_dataset and + obsdata will be retained and can be reused through name based lookup when we + create a new local configuration. +*/ + +void local_config_clear_active( local_config_type * local_config ) { + hash_clear( local_config->updatestep_storage ); + local_config->default_updatestep = local_config_alloc_updatestep(local_config, "DEFAULT"); +} + + + +local_config_type * local_config_alloc( ) { + local_config_type * local_config = (local_config_type *)util_malloc( sizeof * local_config ); + + local_config->default_updatestep = NULL; + local_config->updatestep_storage = hash_alloc(); + local_config->ministep_storage = hash_alloc(); + local_config->dataset_storage = hash_alloc(); + local_config->obsdata_storage = hash_alloc(); + + local_config_clear( local_config ); + return local_config; +} + + +void local_config_free(local_config_type * local_config) { + hash_free( local_config->updatestep_storage ); + hash_free( local_config->ministep_storage); + hash_free( local_config->dataset_storage); + hash_free( local_config->obsdata_storage); + free( local_config ); +} + +local_ministep_type * local_config_alloc_ministep( local_config_type * local_config , const char * key, analysis_module_type* analysis_module) { + if (hash_has_key(local_config->ministep_storage, key)) + return nullptr; + + local_ministep_type * ministep = local_ministep_alloc( key, analysis_module ); + hash_insert_hash_owned_ref( local_config->ministep_storage , key , ministep , local_ministep_free__); + return ministep; +} + +local_obsdata_type * local_config_alloc_obsdata( local_config_type * local_config , const char * obsdata_name ) { + if (local_config_has_obsdata(local_config, obsdata_name)) + util_abort("%s: tried to add existing obsdata node key:%s \n",__func__ , obsdata_name); + + local_obsdata_type * obsdata = local_obsdata_alloc( obsdata_name ); + hash_insert_hash_owned_ref( local_config->obsdata_storage , obsdata_name , obsdata , local_obsdata_free__); + return obsdata; +} + +bool local_config_has_obsdata( const local_config_type * local_config , const char * key) { + return hash_has_key( local_config->obsdata_storage , key ); +} + + +local_dataset_type * local_config_alloc_dataset( local_config_type * local_config , const char * key ) { + if (local_config_has_dataset(local_config, key)) + util_abort("%s: tried to add existing dataset node key:%s \n",__func__ , key); + + local_dataset_type * dataset = local_dataset_alloc( key ); + hash_insert_hash_owned_ref( local_config->dataset_storage , key , dataset , local_dataset_free__); + return dataset; +} + +bool local_config_has_dataset( const local_config_type * local_config , const char * key) { + return hash_has_key( local_config->dataset_storage , key ); +} + + +local_dataset_type * local_config_alloc_dataset_copy( local_config_type * local_config , const char * src_key , const char * target_key) { + local_dataset_type * src_dataset = (local_dataset_type *)hash_get( local_config->dataset_storage , src_key ); + local_dataset_type * copy_dataset = local_dataset_alloc_copy( src_dataset , target_key ); + + hash_insert_hash_owned_ref( local_config->dataset_storage , target_key , copy_dataset , local_dataset_free__); + return copy_dataset; +} + + +local_obsdata_type * local_config_alloc_obsdata_copy( local_config_type * local_config , const char * src_key , const char * target_key) { + local_obsdata_type * src_obsdata = (local_obsdata_type *)hash_get( local_config->obsdata_storage , src_key ); + local_obsdata_type * copy_obsdata = local_obsdata_alloc_copy( src_obsdata , target_key ); + + hash_insert_hash_owned_ref( local_config->obsdata_storage , target_key , copy_obsdata , local_obsdata_free__); + return copy_obsdata; +} + + +local_ministep_type * local_config_get_ministep( const local_config_type * local_config , const char * key) { + local_ministep_type * ministep = (local_ministep_type *)hash_get( local_config->ministep_storage , key ); + return ministep; +} + + +local_obsdata_type * local_config_get_obsdata( const local_config_type * local_config , const char * key) { + local_obsdata_type * obsdata = (local_obsdata_type *)hash_get( local_config->obsdata_storage , key ); + return obsdata; +} + +local_dataset_type * local_config_get_dataset( const local_config_type * local_config , const char * key) { + local_dataset_type * dataset = (local_dataset_type *)hash_get( local_config->dataset_storage , key ); + return dataset; +} + +local_updatestep_type * local_config_get_updatestep( const local_config_type * local_config) { + local_updatestep_type * updatestep = local_config->default_updatestep; + + if (updatestep == NULL) + util_abort("%s: fatal error. No default updatestep configured. \n",__func__ ); + + return updatestep; +} + + +void local_config_summary_fprintf( const local_config_type * local_config , const char * config_file) { + + FILE * stream = util_mkdir_fopen( config_file , "w"); + + const local_updatestep_type * updatestep = local_config_get_updatestep( local_config ); // There is only one update step, the default + { + hash_iter_type * hash_iter = hash_iter_alloc( local_config->ministep_storage ); + + while (!hash_iter_is_complete( hash_iter )) { + const local_ministep_type * ministep = (const local_ministep_type *) hash_iter_get_next_value( hash_iter ); + + fprintf(stream , "UPDATE_STEP:%s,", local_updatestep_get_name(updatestep)); + + local_ministep_summary_fprintf( ministep , stream); + + } + + hash_iter_free( hash_iter ); + } + + fclose( stream ); +} diff --git a/libres/lib/enkf/local_dataset.cpp b/libres/lib/enkf/local_dataset.cpp new file mode 100644 index 00000000000..c6db579fc9c --- /dev/null +++ b/libres/lib/enkf/local_dataset.cpp @@ -0,0 +1,197 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'local_dataset.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 + +#define LOCAL_DATASET_TYPE_ID 6615409 + + + +struct local_dataset_struct { + UTIL_TYPE_ID_DECLARATION; + char * name; + hash_type * active_size; /* A hash table indexed by node keys - each element is an active_list instance. */ + std::unordered_map scaling; +}; + + + +UTIL_SAFE_CAST_FUNCTION(local_dataset , LOCAL_DATASET_TYPE_ID) +UTIL_IS_INSTANCE_FUNCTION(local_dataset , LOCAL_DATASET_TYPE_ID) + + +local_dataset_type * local_dataset_alloc( const char * name ) { + local_dataset_type * dataset = new local_dataset_type(); + + UTIL_TYPE_ID_INIT( dataset , LOCAL_DATASET_TYPE_ID ); + dataset->active_size = hash_alloc(); + dataset->name = util_alloc_string_copy( name ); + + return dataset; +} + +void local_dataset_free( local_dataset_type * dataset ) { + free(dataset->name); + hash_free( dataset->active_size ); + delete dataset; +} + +local_dataset_type * local_dataset_alloc_copy( local_dataset_type * src_dataset , const char * copy_name ) { + local_dataset_type * copy_dataset = local_dataset_alloc( copy_name ); + + hash_iter_type * size_iter = hash_iter_alloc( src_dataset->active_size ); + while (!hash_iter_is_complete( size_iter )) { + const char * key = hash_iter_get_next_key( size_iter ); + active_list_type * active_list = active_list_alloc_copy( (const active_list_type *) hash_get( src_dataset->active_size , key ) ); + hash_insert_hash_owned_ref( copy_dataset->active_size , key , active_list , active_list_free__); + } + hash_iter_free( size_iter ); + + for (const auto& scaling_pair : src_dataset->scaling) + copy_dataset->scaling.insert( scaling_pair ); + + return copy_dataset; +} + + + +void local_dataset_free__( void * arg ) { + local_dataset_type * local_dataset = local_dataset_safe_cast( arg ); + local_dataset_free( local_dataset ); +} + +const char * local_dataset_get_name( const local_dataset_type * dataset) { + return dataset->name; +} + + + +void local_dataset_add_node(local_dataset_type * dataset, const char *node_key) { + if (hash_has_key( dataset->active_size , node_key )) + util_abort("%s: tried to add existing node key:%s \n",__func__ , node_key); + + hash_insert_hash_owned_ref( dataset->active_size , node_key , active_list_alloc( ) , active_list_free__); +} + +bool local_dataset_has_key(const local_dataset_type * dataset, const char * key) { + return hash_has_key( dataset->active_size , key ); +} + + +void local_dataset_del_node( local_dataset_type * dataset , const char * node_key) { + hash_del( dataset->active_size , node_key ); +} + + +void local_dataset_clear( local_dataset_type * dataset) { + hash_clear( dataset->active_size ); +} + +const row_scaling * local_dataset_get_row_scaling(const local_dataset_type * dataset, const char * key) { + auto scaling_iter = dataset->scaling.find( key ); + if (scaling_iter != dataset->scaling.end()) + return &scaling_iter->second; + + return nullptr; +} + + +bool local_dataset_has_row_scaling(const local_dataset_type * dataset, const char * key) { + return (dataset->scaling.count(key) != 0); +} + + +row_scaling_type * local_dataset_get_or_create_row_scaling(local_dataset_type * dataset, const char * key) { + auto scaling_iter = dataset->scaling.find( key ); + if (scaling_iter == dataset->scaling.end()) { + if (!hash_has_key(dataset->active_size, key)) + throw std::invalid_argument("Tried to create row_scaling object for unknown key"); + + dataset->scaling.emplace( key, row_scaling{}); + } + return &dataset->scaling[key]; +} + + +active_list_type * local_dataset_get_node_active_list(const local_dataset_type * dataset , const char * node_key ) { + active_list_type * al = (active_list_type *) hash_get( dataset->active_size , node_key ); /* Fails hard if you do not have the key ... */ + return al; +} + +stringlist_type * local_dataset_alloc_keys( const local_dataset_type * dataset ) { + return hash_alloc_stringlist( dataset->active_size ); +} + +void local_dataset_summary_fprintf( const local_dataset_type * dataset , FILE * stream) { +{ + hash_iter_type * data_iter = hash_iter_alloc( dataset->active_size ); + while (!hash_iter_is_complete( data_iter )) { + const char * data_key = hash_iter_get_next_key( data_iter ); + fprintf(stream , "NAME OF DATA:%s,", data_key ); + + active_list_type * active_list = (active_list_type *)hash_get( dataset->active_size , data_key ); + active_list_summary_fprintf( active_list , local_dataset_get_name(dataset) , data_key , stream); + } + hash_iter_free( data_iter ); + } +} + + +int local_dataset_get_size( const local_dataset_type * dataset ) { + return hash_get_size( dataset->active_size ); +} + + +hash_iter_type * local_dataset_alloc_iter(const local_dataset_type * dataset) { + return hash_iter_alloc(dataset->active_size); +} + + +std::vector local_dataset_unscaled_keys(const local_dataset_type * dataset) { + std::vector keys; + hash_iter_type *node_iter = hash_iter_alloc( dataset->active_size ); + + while (!hash_iter_is_complete( node_iter )) { + const char * key = hash_iter_get_next_key( node_iter ); + if (!local_dataset_has_row_scaling(dataset, key)) + keys.emplace_back( key ); + } + + hash_iter_free( node_iter ); + return keys; +} + + +std::vector local_dataset_scaled_keys(const local_dataset_type * dataset) { + std::vector keys; + for (const auto& [key, _] : dataset->scaling) { + (void)_; + keys.push_back(key); + } + return keys; +} diff --git a/libres/lib/enkf/local_ministep.cpp b/libres/lib/enkf/local_ministep.cpp new file mode 100644 index 00000000000..aa6f0b9aadb --- /dev/null +++ b/libres/lib/enkf/local_ministep.cpp @@ -0,0 +1,206 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'local_ministep.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 + +/** + This file implements a 'ministep' configuration for active / + inactive observations and parameters for ONE enkf update. Observe + that the updating at one report step can consist of several + socalled ministeps, i.e. first the northern part of the field with + the relevant observations, and then the southern part. + + The implementation, in local_ministep_type, is quite simple, it + only contains the keys for the observations and nodes, with an + accompanying pointer to an active_list instance which denotes the + active indices. Observe that this implementation offers no access + to the internals of the underlying enkf_node / obs_node objects. +*/ + + +#define LOCAL_MINISTEP_TYPE_ID 661066 + + + + +struct local_ministep_struct { + UTIL_TYPE_ID_DECLARATION; + char * name; /* A name used for this ministep - string is also used as key in a hash table holding this instance. */ + hash_type * datasets; /* A hash table of local_dataset_type instances - indexed by the name of the datasets. */ + local_obsdata_type * observations; + analysis_module_type * analysis_module; + obs_data_type * obs_data; +}; + + +/** + Observe there is no link between the instances here and the real + observations/nodes (apart from the key in the hash). +*/ + +UTIL_SAFE_CAST_FUNCTION(local_ministep , LOCAL_MINISTEP_TYPE_ID) +UTIL_IS_INSTANCE_FUNCTION(local_ministep , LOCAL_MINISTEP_TYPE_ID) + +local_ministep_type * local_ministep_alloc(const char * name, analysis_module_type* analysis_module) { + local_ministep_type * ministep = (local_ministep_type *)util_malloc( sizeof * ministep ); + + ministep->name = util_alloc_string_copy( name ); + + const char* obsdata_name = "OBSDATA_"; + char* result = (char *) util_malloc(strlen(obsdata_name)+strlen(name)+1); + strcpy(result, obsdata_name); + strcat(result, name); + ministep->observations = local_obsdata_alloc(result); + free(result); + + + ministep->datasets = hash_alloc(); + ministep->analysis_module = analysis_module; + ministep->obs_data = NULL; + UTIL_TYPE_ID_INIT( ministep , LOCAL_MINISTEP_TYPE_ID); + + return ministep; +} + +void local_ministep_free(local_ministep_type * ministep) { + free(ministep->name); + hash_free( ministep->datasets ); + local_obsdata_free(ministep->observations); + if ( ministep->obs_data != NULL ) + obs_data_free(ministep->obs_data); + free( ministep ); +} + + +void local_ministep_free__(void * arg) { + local_ministep_type * ministep = local_ministep_safe_cast( arg ); + local_ministep_free( ministep ); +} + + + + + +/** + When adding observations and update nodes here observe the following: + + 1. The thing will fail hard if you try to add a node/obs which is + already in the hash table. + + 2. The newly added elements will be assigned an active_list + instance with mode ALL_ACTIVE. +*/ + + + +void local_ministep_add_dataset( local_ministep_type * ministep , const local_dataset_type * dataset) { + hash_insert_ref( ministep->datasets , local_dataset_get_name( dataset ) , dataset ); +} + +void local_ministep_add_obsdata( local_ministep_type * ministep , local_obsdata_type * obsdata) { + if (ministep->observations == NULL) + ministep->observations = obsdata; + else { // Add nodes from input observations to existing observations + int iobs; + for (iobs = 0; iobs < local_obsdata_get_size( obsdata ); iobs++) { + local_obsdata_node_type * obs_node = local_obsdata_iget( obsdata , iobs ); + local_obsdata_node_type * new_node = local_obsdata_node_alloc_copy(obs_node); + local_ministep_add_obsdata_node(ministep, new_node); + } + } +} + +void local_ministep_add_obs_data( local_ministep_type * ministep , obs_data_type * obs_data) { + if (ministep->obs_data != NULL){ + obs_data_free(ministep->obs_data); + ministep->obs_data = NULL; + } + ministep->obs_data = obs_data; +} + +void local_ministep_add_obsdata_node( local_ministep_type * ministep , local_obsdata_node_type * obsdatanode) { + local_obsdata_type * obsdata = local_ministep_get_obsdata(ministep); + local_obsdata_add_node(obsdata, obsdatanode); +} + +bool local_ministep_has_dataset( const local_ministep_type * ministep, const char * dataset_name) { + return hash_has_key( ministep->datasets, dataset_name ); +} + +int local_ministep_get_num_dataset( const local_ministep_type * ministep ) { + return hash_get_size( ministep->datasets ); +} + +local_dataset_type * local_ministep_get_dataset( const local_ministep_type * ministep, const char * dataset_name) { + return (local_dataset_type *) hash_get( ministep->datasets, dataset_name ); // CXX_CAST_ERROR +} + +local_obsdata_type * local_ministep_get_obsdata( const local_ministep_type * ministep ) { + return ministep->observations; +} + +obs_data_type * local_ministep_get_obs_data( const local_ministep_type * ministep ) { + return ministep->obs_data; +} + +const char * local_ministep_get_name( const local_ministep_type * ministep ) { + return ministep->name; +} + +/*****************************************************************/ + +hash_iter_type * local_ministep_alloc_dataset_iter( const local_ministep_type * ministep ) { + return hash_iter_alloc( ministep->datasets ); +} + + +bool local_ministep_has_analysis_module( const local_ministep_type * ministep){ + return ministep->analysis_module != NULL; +} + +analysis_module_type* local_ministep_get_analysis_module( const local_ministep_type * ministep ){ + return ministep->analysis_module; +} + +void local_ministep_summary_fprintf( const local_ministep_type * ministep , FILE * stream) { + + fprintf(stream , "MINISTEP:%s,", ministep->name); + + { + /* Dumping all the DATASET instances. */ + { + hash_iter_type * dataset_iter = hash_iter_alloc( ministep->datasets ); + while (!hash_iter_is_complete( dataset_iter )) { + const local_dataset_type * dataset = (const local_dataset_type *) hash_iter_get_next_value( dataset_iter ); + local_dataset_summary_fprintf(dataset, stream); + } + hash_iter_free( dataset_iter ); + } + + /* Only one OBSDATA */ + local_obsdata_type * obsdata = local_ministep_get_obsdata(ministep); + local_obsdata_summary_fprintf( obsdata , stream); + fprintf(stream, "\n"); + } +} diff --git a/libres/lib/enkf/local_obsdata.cpp b/libres/lib/enkf/local_obsdata.cpp new file mode 100644 index 00000000000..66e5280fdf3 --- /dev/null +++ b/libres/lib/enkf/local_obsdata.cpp @@ -0,0 +1,164 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'local_obsdata.c' + + 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 + + +#define LOCAL_OBSDATA_TYPE_ID 86331309 + +struct local_obsdata_struct { + UTIL_TYPE_ID_DECLARATION; + hash_type * nodes_map; + vector_type * nodes_list; + char * name; +}; + + + +UTIL_IS_INSTANCE_FUNCTION( local_obsdata , LOCAL_OBSDATA_TYPE_ID ) +static UTIL_SAFE_CAST_FUNCTION( local_obsdata , LOCAL_OBSDATA_TYPE_ID ) + +local_obsdata_type * local_obsdata_alloc( const char * name) { + local_obsdata_type * data = (local_obsdata_type *)util_malloc( sizeof * data ); + UTIL_TYPE_ID_INIT( data , LOCAL_OBSDATA_TYPE_ID ); + data->nodes_list = vector_alloc_new(); + data->nodes_map = hash_alloc(); + data->name = util_alloc_string_copy( name ); + return data; +} + + + +local_obsdata_type * local_obsdata_alloc_wrapper( local_obsdata_node_type * node ) { + local_obsdata_type * data = local_obsdata_alloc( local_obsdata_node_get_key( node )); + local_obsdata_add_node( data , node ); + return data; +} + + +local_obsdata_type * local_obsdata_alloc_copy( const local_obsdata_type * src, const char * target_key) { + local_obsdata_type * target = local_obsdata_alloc( target_key ); + int i; + for (i=0; i < local_obsdata_get_size( src ); i++ ) { + const local_obsdata_node_type * src_node = local_obsdata_iget( src , i ); + local_obsdata_node_type * target_node = local_obsdata_node_alloc_copy( src_node ); + local_obsdata_add_node( target , target_node ); + } + return target; +} + + + +void local_obsdata_free( local_obsdata_type * data ) { + vector_free( data->nodes_list ); + hash_free( data->nodes_map ); + free( data->name ); + free( data ); +} + +void local_obsdata_free__( void * arg) { + local_obsdata_type * data = local_obsdata_safe_cast( arg ); + return local_obsdata_free( data ); +} + + +const char * local_obsdata_get_name( const local_obsdata_type * data) { + return data->name; +} + + +int local_obsdata_get_size( const local_obsdata_type * data ) { + return vector_get_size( data->nodes_list ); +} + +/* + The @data instance will assume ownership of the node; i.e. calling + scope should NOT call local_obsdata_node_free(). +*/ + +bool local_obsdata_add_node( local_obsdata_type * data , local_obsdata_node_type * node ) { + const char * key = local_obsdata_node_get_key( node ); + if (local_obsdata_has_node(data , key)) + return false; + else { + vector_append_owned_ref( data->nodes_list , node , local_obsdata_node_free__ ); + hash_insert_ref( data->nodes_map , key , node ); + return true; + } +} + + void local_obsdata_del_node( local_obsdata_type * data , const char * key) { + local_obsdata_node_type * node = local_obsdata_get( data , key ); + int index = vector_find( data->nodes_list , node ); + + hash_del( data->nodes_map , key ); + vector_idel( data->nodes_list , index ); +} + + +local_obsdata_node_type * local_obsdata_iget( const local_obsdata_type * data , int index) { + return (local_obsdata_node_type *) vector_iget( data->nodes_list , index ); +} + + +local_obsdata_node_type * local_obsdata_get( const local_obsdata_type * data , const char * key) { + return (local_obsdata_node_type *) hash_get( data->nodes_map , key ); +} + + +bool local_obsdata_has_node( const local_obsdata_type * data , const char * key) { + return hash_has_key( data->nodes_map , key ); +} + +void local_obsdata_reset_tstep_list( local_obsdata_type * data , const int_vector_type * step_list) { + int i; + for (i=0; i < local_obsdata_get_size( data ); i++ ) { + local_obsdata_node_type * node = local_obsdata_iget( data , i ); + local_obsdata_node_reset_tstep_list(node, step_list); + } +} + +active_list_type * local_obsdata_get_copy_node_active_list(const local_obsdata_type * obsdata , const char * obs_key ) { + local_obsdata_node_type * obsdata_node = local_obsdata_get( obsdata , obs_key ); + active_list_type * active_list = local_obsdata_node_get_copy_active_list( obsdata_node ); + return active_list; +} + +active_list_type * local_obsdata_get_node_active_list(const local_obsdata_type * obsdata , const char * obs_key ) { + local_obsdata_node_type * obsdata_node = local_obsdata_get( obsdata , obs_key ); + active_list_type * active_list = local_obsdata_node_get_active_list( obsdata_node ); + return active_list; +} + +void local_obsdata_summary_fprintf( const local_obsdata_type * obsdata , FILE * stream) { + + fprintf(stream , "LOCAL OBSDATA NAME:%s,LOCAL OBSDATA SIZE:%d,", local_obsdata_get_name(obsdata), local_obsdata_get_size(obsdata) ); + + int i; + for (i = 0; i < local_obsdata_get_size( obsdata ); i++ ) { + local_obsdata_node_type * node = local_obsdata_iget( obsdata , i ); + const char * obs_key = local_obsdata_node_get_key(node); + fprintf(stream , "OBSERVATION:%s,", obs_key ); + } +} diff --git a/libres/lib/enkf/local_obsdata_node.cpp b/libres/lib/enkf/local_obsdata_node.cpp new file mode 100644 index 00000000000..ff002f394fc --- /dev/null +++ b/libres/lib/enkf/local_obsdata_node.cpp @@ -0,0 +1,178 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'local_obsdata_node.c' + + 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 LOCAL_OBSDATA_NODE_TYPE_ID 84441309 + +struct local_obsdata_node_struct { + UTIL_TYPE_ID_DECLARATION; + char * obs_key; + active_list_type * active_list; + int_vector_type * tstep_list; + bool all_timestep_active; +}; + + + +UTIL_IS_INSTANCE_FUNCTION( local_obsdata_node , LOCAL_OBSDATA_NODE_TYPE_ID ) +UTIL_SAFE_CAST_FUNCTION( local_obsdata_node , LOCAL_OBSDATA_NODE_TYPE_ID ) + +static local_obsdata_node_type * local_obsdata_node_alloc__( const char * obs_key , bool all_timestep_active) { + local_obsdata_node_type * node = (local_obsdata_node_type *)util_malloc( sizeof * node ); + UTIL_TYPE_ID_INIT( node , LOCAL_OBSDATA_NODE_TYPE_ID ); + node->obs_key = util_alloc_string_copy( obs_key ); + node->active_list = NULL; + node->tstep_list = NULL; + node->all_timestep_active = all_timestep_active; + return node; +} + + +local_obsdata_node_type * local_obsdata_node_alloc( const char * obs_key , bool all_timestep_active ) { + local_obsdata_node_type * node = local_obsdata_node_alloc__(obs_key , all_timestep_active); + + node->active_list = active_list_alloc( ); + node->tstep_list = int_vector_alloc(0,0); + + return node; +} + + +local_obsdata_node_type * local_obsdata_node_alloc_copy( const local_obsdata_node_type * src) { + local_obsdata_node_type * target = local_obsdata_node_alloc__( src->obs_key , src->all_timestep_active ); + + target->active_list = active_list_alloc_copy( src->active_list ); + target->tstep_list = int_vector_alloc_copy( src->tstep_list ); // CXX_CAST_ERROR + + return target; +} + + + +void local_obsdata_node_copy_active_list( local_obsdata_node_type * node , const active_list_type * active_list) { + active_list_copy( node->active_list , active_list ); +} + + +const char * local_obsdata_node_get_key( const local_obsdata_node_type * node ) { + return node->obs_key; +} + + + +void local_obsdata_node_free( local_obsdata_node_type * node ) { + if (node->active_list) + active_list_free( node->active_list ); + + if (node->tstep_list) + int_vector_free( node->tstep_list ); + + free( node->obs_key ); + free( node ); +} + + + +void local_obsdata_node_free__( void * arg ) { + local_obsdata_node_type * node = local_obsdata_node_safe_cast( arg ); + local_obsdata_node_free( node ); +} + + +active_list_type * local_obsdata_node_get_active_list( const local_obsdata_node_type * node ) { + return node->active_list; +} + + +active_list_type * local_obsdata_node_get_copy_active_list( const local_obsdata_node_type * node ) { + return active_list_alloc_copy(node->active_list); +} + + +bool local_obsdata_node_tstep_active( const local_obsdata_node_type * node , int tstep ) { + if (node->all_timestep_active) + return true; + else + return local_obsdata_node_has_tstep( node , tstep ); +} + + + +/* + This a temporarary function to support the change local_obsset -> + local_obsdata; should eventually be removed. +*/ + +void local_obsdata_node_reset_tstep_list( local_obsdata_node_type * node , const int_vector_type * step_list) { + int_vector_free(node->tstep_list); + node->tstep_list = int_vector_alloc_copy( step_list ); // CXX_CAST_ERROR + node->all_timestep_active = false; +} + + +bool local_obsdata_node_all_timestep_active( const local_obsdata_node_type * node) { + return node->all_timestep_active; +} + +/** + Observe that this function check for explicitly added timestep, + i.e. if the all_timestep_active flag is set to true this will + return false. +*/ + +bool local_obsdata_node_has_tstep( const local_obsdata_node_type * node , int tstep) { + const int_vector_type * tstep_list = node->tstep_list; + if (int_vector_index_sorted( tstep_list , tstep) == -1) + return false; + else + return true; +} + + +void local_obsdata_node_add_tstep( local_obsdata_node_type * node, int tstep) { + if (!local_obsdata_node_has_tstep( node , tstep)) { + + if (int_vector_size( node->tstep_list )) { + int last = int_vector_get_last( node->tstep_list ); + int_vector_append( node->tstep_list , tstep ); + if (tstep < last) + int_vector_sort( node->tstep_list); + } else + int_vector_append( node->tstep_list , tstep ); + + node->all_timestep_active = false; + } +} + + + + +void local_obsdata_node_add_range( local_obsdata_node_type * node, int step1 , int step2) { + int tstep; + for (tstep = step1; tstep <= step2; tstep++) + local_obsdata_node_add_tstep( node , tstep ); +} + +void local_obsdata_node_set_all_timestep_active( local_obsdata_node_type * node, bool flag) { + node->all_timestep_active = flag; +} diff --git a/libres/lib/enkf/local_updatestep.cpp b/libres/lib/enkf/local_updatestep.cpp new file mode 100644 index 00000000000..b977af75ef8 --- /dev/null +++ b/libres/lib/enkf/local_updatestep.cpp @@ -0,0 +1,88 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'local_updatestep.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 + +/** + One enkf update is described/configured by the data structure in + local_ministep.c. This file implements a local report_step, which + is a collection of ministeps - in many cases a local_updatestep will + only consist of one single local_ministep; but in principle it can + contain several. +*/ + +#define LOCAL_UPDATESTEP_TYPE_ID 77159 + +struct local_updatestep_struct { + UTIL_TYPE_ID_DECLARATION; + char * name; + vector_type * ministep; +}; + + + +UTIL_SAFE_CAST_FUNCTION(local_updatestep , LOCAL_UPDATESTEP_TYPE_ID) + + +local_updatestep_type * local_updatestep_alloc( const char * name ) { + local_updatestep_type * updatestep = (local_updatestep_type *)util_malloc( sizeof * updatestep ); + + UTIL_TYPE_ID_INIT( updatestep , LOCAL_UPDATESTEP_TYPE_ID ); + updatestep->name = util_alloc_string_copy( name ); + updatestep->ministep = vector_alloc_new(); + + return updatestep; +} + + +void local_updatestep_free( local_updatestep_type * updatestep) { + free( updatestep->name ); + vector_free( updatestep->ministep ); + free( updatestep ); +} + + +void local_updatestep_free__(void * arg) { + local_updatestep_type * updatestep = local_updatestep_safe_cast( arg ); + local_updatestep_free( updatestep ); +} + + +void local_updatestep_add_ministep( local_updatestep_type * updatestep , local_ministep_type * ministep) { + vector_append_ref( updatestep->ministep , ministep ); /* Observe that the vector takes NO ownership */ +} + + + +local_ministep_type * local_updatestep_iget_ministep( const local_updatestep_type * updatestep , int index) { + return (local_ministep_type *) vector_iget( updatestep->ministep , index ); +} + + +int local_updatestep_get_num_ministep( const local_updatestep_type * updatestep) { + return vector_get_size( updatestep->ministep ); +} + +const char * local_updatestep_get_name( const local_updatestep_type * updatestep ) { + return updatestep->name; +} diff --git a/libres/lib/enkf/log_config.cpp b/libres/lib/enkf/log_config.cpp new file mode 100644 index 00000000000..154aae0c250 --- /dev/null +++ b/libres/lib/enkf/log_config.cpp @@ -0,0 +1,180 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'log_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 +#include + +struct log_config_struct { + + char * log_file; + message_level_type message_level; + +}; + + +static void log_config_init(log_config_type *, const config_content_type *); + + +static log_config_type * log_config_alloc_default() { + log_config_type * log_config = (log_config_type *)util_malloc(sizeof * log_config); + + log_config->log_file = util_alloc_string_copy(DEFAULT_LOG_FILE); + log_config->message_level = DEFAULT_LOG_LEVEL; + + return log_config; +} + +log_config_type * log_config_alloc_full(const char * log_file, message_level_type message_level) { + log_config_type * log_config = (log_config_type *)util_malloc(sizeof * log_config); + log_config->log_file = util_alloc_string_copy(log_file); + log_config->message_level = message_level; + + return log_config; +} + +log_config_type * log_config_alloc_load(const char * config_file) { + config_parser_type * config_parser = config_alloc(); + config_content_type * config_content = NULL; + if(config_file) + config_content = model_config_alloc_content(config_file, config_parser); + + log_config_type * log_config = log_config_alloc(config_content); + + config_content_free(config_content); + config_free(config_parser); + + return log_config; +} + + +log_config_type * log_config_alloc(const config_content_type * config_content) { + log_config_type * log_config = log_config_alloc_default(); + + if(config_content) + log_config_init(log_config, config_content); + + return log_config; +} + + +const char * log_config_get_log_file(const log_config_type * log_config) { + return log_config->log_file; +} + + +const message_level_type log_config_get_log_level( + const log_config_type * log_config + ) { + + return log_config->message_level; +} + + +static void log_config_set_log_file( + log_config_type * log_config, + const char * log_file + ) { + + free(log_config->log_file); + log_config->log_file = util_alloc_string_copy(log_file); +} + + +/** + * This method parses the 'LOG_LEVEL_KEY' value according to the following + * rules: + * + * - If it is an integer 0 <= i <= 4 then it issues an deprecation warning and + * uses the approximately correct enum of message_level_type. + * + * - If it is one of the strings CRITICAL, ERROR, WARNING, INFO, DEBUG it + * returns the corresponding enum of message_level_type + * + * - Else it returns the DEFAULT_LOG_LEVEL + */ +message_level_type log_config_level_parser(const char * level) { + + typedef struct { + const char * log_keyword; // The keyword written in the config file + const message_level_type log_enum; // The enum for the new log-level + } log_tuple; + + const int nr_of_log_levels = 5; + log_tuple log_levels[] = {{LOG_CRITICAL_NAME, LOG_CRITICAL}, + {LOG_ERROR_NAME, LOG_ERROR}, + {LOG_WARNING_NAME, LOG_WARNING}, + {LOG_INFO_NAME, LOG_INFO}, + {LOG_DEBUG_NAME, LOG_DEBUG}}; + + + for (int i = 0; i < nr_of_log_levels; i++) { + log_tuple curr_log_level = log_levels[i]; + + if (strcmp(level, curr_log_level.log_keyword)==0) + return curr_log_level.log_enum; + } + + fprintf(stderr, "** The log_level: %s is not valid, using default log level\n", level); + return DEFAULT_LOG_LEVEL; +} + + +static void log_config_init( + log_config_type * log_config, + const config_content_type * content + ) { + + if (config_content_has_item(content, LOG_FILE_KEY)) { + const char * log_file = config_content_get_value_as_abspath(content, LOG_FILE_KEY); + log_config_set_log_file(log_config, log_file); + } + + // If no log file, make default log file relative to config file + else if(!util_is_abs_path(log_config->log_file)) { + char * working_dir; + util_alloc_file_components( + config_content_get_config_file(content, true), + &working_dir, NULL, NULL); + + char * abs_default_log_file = util_alloc_filename(working_dir, log_config->log_file, NULL); + + log_config_set_log_file(log_config, abs_default_log_file); + + free(working_dir); + free(abs_default_log_file); + } + + if(config_content_has_item(content, LOG_LEVEL_KEY)) { + const char * log_level_str = config_content_get_value(content, LOG_LEVEL_KEY); + log_config->message_level = log_config_level_parser(log_level_str); + } +} + + +void log_config_free(log_config_type * log_config) { + if(!log_config) + return; + + free(log_config->log_file); + free(log_config); +} diff --git a/libres/lib/enkf/meas_data.cpp b/libres/lib/enkf/meas_data.cpp new file mode 100644 index 00000000000..024b90b1bfa --- /dev/null +++ b/libres/lib/enkf/meas_data.cpp @@ -0,0 +1,401 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'meas_data.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. +*/ + +/** + See the file README.obs for ducumentation of the varios datatypes + involved with observations/measurement/+++. +*/ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define MEAS_BLOCK_TYPE_ID 661936407 +#define MEAS_DATA_TYPE_ID 561000861 + + +struct meas_data_struct { + UTIL_TYPE_ID_DECLARATION; + int active_ens_size; + vector_type * data; + pthread_mutex_t data_mutex; + hash_type * blocks; + bool_vector_type * ens_mask; +}; + + +struct meas_block_struct { + UTIL_TYPE_ID_DECLARATION; + int active_ens_size; + int obs_size; + int ens_stride; + int obs_stride; + int data_size; + char * obs_key; + double * data; + bool * active; + bool stat_calculated; + const bool_vector_type * ens_mask; + int_vector_type * index_map; +}; + + +UTIL_SAFE_CAST_FUNCTION( meas_block , MEAS_BLOCK_TYPE_ID ) + + +/** + Observe that meas_block instance must be allocated with a correct + value for obs_size; it can not grow during use, and it does also + not count the number of elements added. + + Observe that the input argument @obs_size should be the total size + of the observation; if parts of the observation have been excluded + due to local analysis it should still be included in the @obs_size + value. +*/ + +meas_block_type * meas_block_alloc( const char * obs_key , const bool_vector_type * ens_mask , int obs_size) { + meas_block_type * meas_block = (meas_block_type *)util_malloc( sizeof * meas_block ); + UTIL_TYPE_ID_INIT( meas_block , MEAS_BLOCK_TYPE_ID ); + meas_block->active_ens_size = bool_vector_count_equal( ens_mask , true ); + meas_block->ens_mask = ens_mask; + meas_block->obs_size = obs_size; + meas_block->obs_key = util_alloc_string_copy( obs_key ); + meas_block->data = (double *) util_calloc( (meas_block->active_ens_size + 2) * obs_size , sizeof * meas_block->data ); + meas_block->active = (bool *) util_calloc( obs_size , sizeof * meas_block->active ); + meas_block->ens_stride = 1; + meas_block->obs_stride = meas_block->active_ens_size + 2; + meas_block->data_size = (meas_block->active_ens_size + 2) * obs_size; + meas_block->index_map = bool_vector_alloc_active_index_list( meas_block->ens_mask , -1); + { + int i; + for (i=0; i < obs_size; i++) + meas_block->active[i] = false; + } + meas_block->stat_calculated = false; + return meas_block; +} + +void meas_block_free( meas_block_type * meas_block ) { + free( meas_block->obs_key ); + free( meas_block->data ); + free( meas_block->active ); + int_vector_free( meas_block->index_map ); + free( meas_block ); +} + + +static void meas_block_free__( void * arg ) { + meas_block_type * meas_block = meas_block_safe_cast( arg ); + meas_block_free( meas_block ); +} + + + +static void meas_block_initS( const meas_block_type * meas_block , matrix_type * S, int * __obs_offset) { + int obs_offset = *__obs_offset; + for (int iobs =0; iobs < meas_block->obs_size; iobs++) { + if (meas_block->active[iobs]) { + for (int iens =0; iens < meas_block->active_ens_size; iens++) { + int obs_index = iens * meas_block->ens_stride + iobs* meas_block->obs_stride; + + matrix_iset( S , obs_offset, iens , meas_block->data[ obs_index ]); + } + obs_offset++; + } + } + *__obs_offset = obs_offset; +} + +bool meas_block_iens_active( const meas_block_type * meas_block , int iens) { + return bool_vector_iget( meas_block->ens_mask , iens); +} + + +/* +static void meas_data_assign_block( meas_block_type * target_block , const meas_block_type * src_block , int target_iens , int src_iens ) { + int iobs; + for (iobs =0; iobs < target_block->obs_size; iobs++) { + int target_index = target_iens * target_block->ens_stride + iobs * target_block->obs_stride; + int src_index = src_iens * src_block->ens_stride + iobs * src_block->obs_stride; + target_block->data[ target_index ] = src_block->data[ src_index ]; + } + target_block->stat_calculated = false; +} +*/ + +static void meas_block_calculate_ens_stats( meas_block_type * meas_block ) { + bool include_inactive = true; + int iobs , iens; + for (iobs =0; iobs < meas_block->obs_size; iobs++) { + if (meas_block->active[iobs] || include_inactive) { + double M1 = 0; + double M2 = 0; + for (iens =0; iens < meas_block->active_ens_size; iens++) { + int index = iens * meas_block->ens_stride + iobs * meas_block->obs_stride; + M1 += meas_block->data[ index ]; + M2 += meas_block->data[ index ] * meas_block->data[ index ]; + } + { + int mean_index = (meas_block->active_ens_size + 0) * meas_block->ens_stride + iobs * meas_block->obs_stride; + int std_index = (meas_block->active_ens_size + 1) * meas_block->ens_stride + iobs * meas_block->obs_stride; + double mean = M1 / meas_block->active_ens_size; + double var = M2 / meas_block->active_ens_size - mean * mean; + meas_block->data[ mean_index ] = mean; + meas_block->data[ std_index ] = sqrt( util_double_max( 0.0 , var)); + } + } + } + meas_block->stat_calculated = true; +} + + +static void meas_block_assert_ens_stat( meas_block_type * meas_block ) { + if (!meas_block->stat_calculated) + meas_block_calculate_ens_stats( meas_block ); +} + + +static void meas_block_assert_iens_active( const meas_block_type * meas_block , int iens) { + if (!bool_vector_iget( meas_block->ens_mask , iens )) + util_abort("%s: fatal error - trying to access inactive ensemble member:%d \n",__func__ , iens); +} + + +void meas_block_iset( meas_block_type * meas_block , int iens , int iobs , double value) { + meas_block_assert_iens_active( meas_block , iens ); + { + int active_iens = int_vector_iget( meas_block->index_map , iens ); + int index = active_iens * meas_block->ens_stride + iobs * meas_block->obs_stride; + meas_block->data[ index ] = value; + if (!meas_block->active[ iobs ]) + meas_block->active[ iobs ] = true; + + meas_block->stat_calculated = false; + } +} + + +double meas_block_iget( const meas_block_type * meas_block , int iens , int iobs) { + meas_block_assert_iens_active( meas_block , iens ); + { + int active_iens = int_vector_iget( meas_block->index_map , iens ); + int index = active_iens * meas_block->ens_stride + iobs * meas_block->obs_stride; + return meas_block->data[ index ]; + } +} + + +static int meas_block_get_active_obs_size( const meas_block_type * meas_block ) { + int obs_size = 0; + int i; + + for (i=0; i < meas_block->obs_size; i++) + if (meas_block->active[i]) + obs_size++; + + return obs_size; +} + + +double meas_block_iget_ens_std( meas_block_type * meas_block , int iobs) { + meas_block_assert_ens_stat( meas_block ); + { + int std_index = (meas_block->active_ens_size + 1) * meas_block->ens_stride + iobs * meas_block->obs_stride; + return meas_block->data[ std_index ]; + } +} + + +double meas_block_iget_ens_mean( meas_block_type * meas_block , int iobs) { + meas_block_assert_ens_stat( meas_block ); + { + int mean_index = meas_block->active_ens_size * meas_block->ens_stride + iobs * meas_block->obs_stride; + return meas_block->data[ mean_index ]; + } +} + + +bool meas_block_iget_active( const meas_block_type * meas_block , int iobs) { + return meas_block->active[ iobs ]; +} + + +void meas_block_deactivate( meas_block_type * meas_block , int iobs ) { + if (meas_block->active[ iobs ]) + meas_block->active[ iobs ] = false; + meas_block->stat_calculated = false; +} + + +int meas_block_get_total_obs_size( const meas_block_type * meas_block ) { + return meas_block->obs_size; +} + + +int meas_block_get_active_ens_size( const meas_block_type * meas_block ) { + return meas_block->active_ens_size; +} + + +int meas_block_get_total_ens_size( const meas_block_type * meas_block ) { + return bool_vector_size( meas_block->ens_mask ); +} + + + + + + +/*****************************************************************/ + +UTIL_IS_INSTANCE_FUNCTION( meas_data , MEAS_DATA_TYPE_ID ) + +meas_data_type * meas_data_alloc( const bool_vector_type * ens_mask ) { + meas_data_type * meas = (meas_data_type *)util_malloc(sizeof * meas ); + UTIL_TYPE_ID_INIT( meas , MEAS_DATA_TYPE_ID ); + + meas->data = vector_alloc_new(); + meas->blocks = hash_alloc(); + meas->ens_mask = bool_vector_alloc_copy( ens_mask ); + meas->active_ens_size = bool_vector_count_equal( ens_mask , true ); + pthread_mutex_init( &meas->data_mutex , NULL ); + + return meas; +} + + + +void meas_data_free(meas_data_type * matrix) { + vector_free( matrix->data ); + hash_free( matrix->blocks ); + bool_vector_free( matrix->ens_mask ); + free( matrix ); +} + + + +void meas_data_reset(meas_data_type * matrix) { + hash_clear( matrix->blocks ); + vector_clear( matrix->data ); /* Will dump and discard all the meas_block instances. */ +} + + +/* + The obs_key is not alone unique over different report steps. +*/ +static char * meas_data_alloc_key( const char * obs_key , int report_step) { + return util_alloc_sprintf( "%s-%d" , obs_key , report_step ); +} + +/** + The code actually adding new blocks to the vector must be run in single-thread mode. +*/ + +meas_block_type * meas_data_add_block( meas_data_type * matrix , const char * obs_key , int report_step , int obs_size) { + char * lookup_key = meas_data_alloc_key( obs_key , report_step ); + pthread_mutex_lock( &matrix->data_mutex ); + { + if (!hash_has_key( matrix->blocks , lookup_key )) { + meas_block_type * new_block = meas_block_alloc(obs_key , matrix->ens_mask , obs_size); + vector_append_owned_ref( matrix->data , new_block , meas_block_free__ ); + hash_insert_ref( matrix->blocks , lookup_key , new_block ); + } + } + pthread_mutex_unlock( &matrix->data_mutex ); + free( lookup_key ); + return (meas_block_type * ) vector_get_last( matrix->data ); +} + + +/* + Observe that the key should compare with the keys created by meas_data_alloc_key(). +*/ +bool meas_data_has_block( const meas_data_type * matrix , const char * lookup_key) { + return hash_has_key( matrix->blocks , lookup_key); +} + +meas_block_type * meas_data_get_block( const meas_data_type * matrix , const char * lookup_key) { + return (meas_block_type * ) hash_get( matrix->blocks , lookup_key ); +} + + +meas_block_type * meas_data_iget_block( const meas_data_type * matrix , int block_nr) { + return (meas_block_type * ) vector_iget( matrix->data , block_nr); +} + + +const meas_block_type * meas_data_iget_block_const( const meas_data_type * matrix , int block_nr) { + return (const meas_block_type * ) vector_iget_const( matrix->data , block_nr); +} + + +int meas_data_get_active_obs_size( const meas_data_type * matrix ) { + int obs_size = 0; + + for (int block_nr = 0; block_nr < vector_get_size( matrix->data ); block_nr++) { + const meas_block_type * meas_block = (const meas_block_type *)vector_iget_const( matrix->data , block_nr); + obs_size += meas_block_get_active_obs_size( meas_block ); + } + + return obs_size; +} + + + +/* + Observe that this can return NULL is there is no data/observations. +*/ + +matrix_type * meas_data_allocS(const meas_data_type * matrix) { + int obs_offset = 0; + matrix_type * S = matrix_alloc( meas_data_get_active_obs_size( matrix ) , matrix->active_ens_size); + if (S) { + for (int block_nr = 0; block_nr < vector_get_size( matrix->data ); block_nr++) { + const meas_block_type * meas_block = (const meas_block_type *)vector_iget_const( matrix->data , block_nr); + meas_block_initS( meas_block , S , &obs_offset); + } + + matrix_set_name( S , "S"); + matrix_assert_finite( S ); + } + return S; +} + + +int meas_data_get_active_ens_size( const meas_data_type * meas_data ) { + return meas_data->active_ens_size; +} + + +int meas_data_get_total_ens_size( const meas_data_type * meas_data ) { + return bool_vector_size( meas_data->ens_mask ); +} + + +int meas_data_get_num_blocks( const meas_data_type * meas_data ) { + return vector_get_size( meas_data->data ); +} diff --git a/libres/lib/enkf/migrate_bfs.cpp b/libres/lib/enkf/migrate_bfs.cpp new file mode 100644 index 00000000000..4a2d83e807b --- /dev/null +++ b/libres/lib/enkf/migrate_bfs.cpp @@ -0,0 +1,131 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'migrate_bfs.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 + + + +static void migrate_file( const char * src_case, int num_src_drivers , const char * target_case, int num_target_drivers, const char * file, int block_size , msg_type * msg) { + block_fs_type ** target_fs = util_calloc( num_target_drivers , sizeof * target_fs ); // CXX_CAST_ERROR + int itarget; + for (itarget = 0; itarget < num_target_drivers; itarget++) { + char * path = util_alloc_sprintf("%s/mod_%d" , target_case , itarget ); + char * mount_file = util_alloc_sprintf("%s/mod_%d/%s.mnt" , target_case , itarget , file ); + util_make_path( path ); + + target_fs[itarget] = block_fs_mount( mount_file , 16 , 0 , 1.0, 0 , false , false ); + free( mount_file ); + free( path ); + } + + { + int isrc; + buffer_type * buffer = buffer_alloc(1024); + for (isrc = 0; isrc < num_src_drivers; isrc++) { + char * mount_file = util_alloc_sprintf("%s/mod_%d/%s.mnt" , src_case , isrc , file ); + block_fs_type * src_fs = block_fs_mount( mount_file , 16 , 1024 , 1.0 , 0 , true , true ); + vector_type * file_list = block_fs_alloc_filelist( src_fs , NULL , NO_SORT , false ); + int ifile; + msg_update( msg , mount_file ); + for (ifile = 0; ifile < vector_get_size( file_list ); ifile++) { + const file_node_type * node = (const file_node_type *)vector_iget_const( file_list , ifile ); + const char * filename = file_node_get_filename( node ); + int report_step , iens; + char * key; + if (block_fs_sscanf_key( filename , &key , &report_step , &iens )) { + block_fs_fread_realloc_buffer( src_fs , filename , buffer); + block_fs_fwrite_buffer( target_fs[(iens % num_target_drivers)] , filename , buffer ); + free( key ); + } else + util_abort("%s: All hell is loose - failed to parse:%s \n",__func__ , filename); + } + + vector_free( file_list ); + block_fs_close(src_fs , false); + } + buffer_free( buffer ); + } + + + for (itarget = 0; itarget < num_target_drivers; itarget++) + block_fs_close( target_fs[itarget] , false); + free( target_fs ); +} + + +static void copy_index( const char * src_case , const char * target_case) { + char * mount_src = util_alloc_filename(src_case , "INDEX" , "mnt"); + char * mount_target = util_alloc_filename(target_case , "INDEX" , "mnt"); + char * data_src = util_alloc_filename(src_case , "INDEX" , "data_0"); + char * data_target = util_alloc_filename(target_case , "INDEX" , "data_0"); + + util_copy_file( mount_src , mount_target ); + util_copy_file( data_src , data_target ); + + free( mount_src ); + free( mount_target ); + free( data_src ); + free( data_target ); +} + + +static void usage() { + printf("Use:\n"); + printf("bash%% migrate_bfs case\n"); + exit(1); +} + +int main(int argc, char ** argv) { + int num_src_drivers = 10; + int num_target_drivers = 32; + if (argc != 4) + usage(); + + { + char * src_path = argv[1] ; + char * target_path = argv[2] ; + char * dir = argv[3] ; + + util_make_path( target_path ); + if (util_same_file( src_path , target_path)) { + fprintf(stderr,"The two directories:%s and %s point to the same location \n" , src_path , target_path ); + exit(1); + } + + { + char * src_case = util_alloc_sprintf("%s/%s" , src_path , dir ); + char * target_case = util_alloc_sprintf("%s/%s" , target_path , dir ); + + msg_type * msg = msg_alloc("Copying from: " , false); + msg_show( msg ); + migrate_file(src_case , num_src_drivers , target_case , num_target_drivers , "ANALYZED" , 32 , msg); + migrate_file(src_case , num_src_drivers , target_case , num_target_drivers , "FORECAST" , 32 , msg); + migrate_file(src_case , num_src_drivers , target_case , num_target_drivers , "PARAMETER" , 32 , msg); + migrate_file(src_case , num_src_drivers , target_case , num_target_drivers , "STATIC" , 32 , msg); + copy_index( src_case , target_case ); + free( src_case); + free( target_case ); + msg_free( msg , true ); + } + } +} diff --git a/libres/lib/enkf/misfit_ensemble.cpp b/libres/lib/enkf/misfit_ensemble.cpp new file mode 100644 index 00000000000..be93c2a6a3b --- /dev/null +++ b/libres/lib/enkf/misfit_ensemble.cpp @@ -0,0 +1,231 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'misfit_ensemble.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 + + +/** + This file implements a type misfit_ensemble which is used to rank the + different realization according to various criteria. + + The top level datastructure in this file is the misfit_ensemble, and + that is the only exported datatype, but in addition there are the + misfit_member which is the misfit for one ensemble member, and + misfit_ts which is the misfit for one ensemble member / one + observation key. +*/ + + +struct misfit_ensemble_struct { + UTIL_TYPE_ID_DECLARATION; + bool initialized; + int history_length; + vector_type * ensemble; /* Vector of misfit_member_type instances - one for each ensemble member. */ +}; + + +/*****************************************************************/ + +static double ** __2d_malloc(int rows , int columns) { + double ** d = (double **) util_calloc( rows , sizeof * d ); + for (int i =0; i < rows; i++) + d[i] = (double *) util_calloc( columns , sizeof * d[i]); + return d; +} + +static void __2d_free(double ** d , int rows) { + for (int i =0; i < rows; i++) + free(d[i]); + free(d); +} + + +void misfit_ensemble_initialize( misfit_ensemble_type * misfit_ensemble , + const ensemble_config_type * ensemble_config , + const enkf_obs_type * enkf_obs , + enkf_fs_type * fs , + int ens_size , + int history_length, + bool force_init) { + + if (force_init || !misfit_ensemble->initialized) { + misfit_ensemble_clear( misfit_ensemble ); + + double ** chi2_work = __2d_malloc( history_length + 1 , ens_size ); + bool_vector_type * iens_valid = bool_vector_alloc( ens_size , true ); + + hash_iter_type * obs_iter = enkf_obs_alloc_iter( enkf_obs ); + const char * obs_key = hash_iter_get_next_key( obs_iter ); + + misfit_ensemble->history_length = history_length; + misfit_ensemble_set_ens_size( misfit_ensemble , ens_size ); + + while (obs_key != NULL) { + obs_vector_type * obs_vector = enkf_obs_get_vector( enkf_obs , obs_key ); + + bool_vector_reset( iens_valid ); + bool_vector_iset( iens_valid , ens_size - 1 , true ); + obs_vector_ensemble_chi2( obs_vector , + fs , + iens_valid , + 0 , + misfit_ensemble->history_length, + 0 , + ens_size , + chi2_work); + + /** + Internalizing the results from the chi2_work table into the misfit structure. + */ + for (int iens = 0; iens < ens_size; iens++) { + misfit_member_type * node = misfit_ensemble_iget_member( misfit_ensemble , iens ); + if (bool_vector_iget( iens_valid , iens)) + misfit_member_update( node , obs_key , misfit_ensemble->history_length , iens , (const double **) chi2_work); + } + obs_key = hash_iter_get_next_key( obs_iter ); + } + + bool_vector_free( iens_valid ); + hash_iter_free( obs_iter ); + + __2d_free( chi2_work , misfit_ensemble->history_length + 1); + misfit_ensemble->initialized = true; + } +} + + +void misfit_ensemble_fwrite( const misfit_ensemble_type * misfit_ensemble , FILE * stream ) { + int ens_size = vector_get_size( misfit_ensemble->ensemble); + util_fwrite_int( misfit_ensemble->history_length , stream ); + util_fwrite_int( vector_get_size( misfit_ensemble->ensemble ) , stream); + + /* Writing the nodes - one for each ensemble member */ + { + int iens; + for (iens = 0; iens < ens_size; iens++) + misfit_member_fwrite( (const misfit_member_type *) vector_iget( misfit_ensemble->ensemble , iens ) , stream ); + } + +} + + + + +/** + Observe that the object is NOT in a valid state when leaving this function, + must finalize in either misfit_ensemble_alloc() or misfit_ensemble_fread_alloc(). +*/ + +static misfit_ensemble_type * misfit_ensemble_alloc_empty() { + misfit_ensemble_type * table = (misfit_ensemble_type *)util_malloc( sizeof * table ); + + table->initialized = false; + table->ensemble = vector_alloc_new(); + + return table; +} + + +/** + This funcion is a feeble attempt at allowing the ensemble size to + change runtime. If the new ensemble size is larger than the current + ensemble size ALL the currently internalized misfit information is + dropped on the floor; if the the ensemble is shrinked only the the + last elements of the misfit table are discarded (NOT exactly battle-tested). + +*/ +void misfit_ensemble_set_ens_size( misfit_ensemble_type * misfit_ensemble , int ens_size) { + int iens; + if (ens_size > vector_get_size( misfit_ensemble->ensemble )) { + /* The new ensemble is larger than what we have currently internalized, + we drop everything and add empty misfit_member instances. */ + vector_clear( misfit_ensemble->ensemble ); + for (iens = 0; iens < ens_size; iens++) + vector_append_owned_ref( misfit_ensemble->ensemble , misfit_member_alloc( iens ) , misfit_member_free__); + + } else + /* We shrink the vector by removing the last elements. */ + vector_shrink( misfit_ensemble->ensemble , ens_size); +} + + +void misfit_ensemble_fread( misfit_ensemble_type * misfit_ensemble , FILE * stream ) { + misfit_ensemble_clear( misfit_ensemble ); + { + int ens_size; + + misfit_ensemble->history_length = util_fread_int( stream ); + ens_size = util_fread_int( stream ); + misfit_ensemble_set_ens_size( misfit_ensemble , ens_size ); + { + for (int iens = 0; iens < ens_size; iens++) { + misfit_member_type * node = misfit_member_fread_alloc( stream ); + vector_iset_owned_ref( misfit_ensemble->ensemble , iens , node , misfit_member_free__); + } + } + + } +} + + + +misfit_ensemble_type * misfit_ensemble_alloc( ) { + misfit_ensemble_type * table = misfit_ensemble_alloc_empty( ); + return table; +} + + + +misfit_member_type * misfit_ensemble_iget_member( const misfit_ensemble_type * table , int iens) { + return (misfit_member_type *) vector_iget( table->ensemble , iens); +} + + + + +void misfit_ensemble_clear( misfit_ensemble_type * table) { + vector_clear( table->ensemble ); + table->initialized = false; +} + + +void misfit_ensemble_free(misfit_ensemble_type * table ) { + vector_free( table->ensemble ); + free( table ); +} + + +bool misfit_ensemble_initialized( const misfit_ensemble_type * misfit_ensemble ) { + return misfit_ensemble->initialized; +} + + +/*****************************************************************/ + + +int misfit_ensemble_get_ens_size( const misfit_ensemble_type * misfit_ensemble ) { + return vector_get_size( misfit_ensemble->ensemble ); +} diff --git a/libres/lib/enkf/misfit_member.cpp b/libres/lib/enkf/misfit_member.cpp new file mode 100644 index 00000000000..a15a7740a1a --- /dev/null +++ b/libres/lib/enkf/misfit_member.cpp @@ -0,0 +1,120 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'misfit_member.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 MISFIT_MEMBER_TYPE_ID 541066 + +struct misfit_member_struct { + UTIL_TYPE_ID_DECLARATION; + int my_iens; + hash_type *obs; /* hash table of misfit_ts_type instances - indexed by observation keys. The structure + of this hash table is duplicated for each ensemble member.*/ +}; + + + +static UTIL_SAFE_CAST_FUNCTION(misfit_member , MISFIT_MEMBER_TYPE_ID); + + +static void misfit_member_free( misfit_member_type * node ) { + hash_free( node->obs ); + free( node ); +} + + +void misfit_member_free__( void * node ) { + misfit_member_free( misfit_member_safe_cast( node )); +} + + +misfit_member_type * misfit_member_alloc(int iens) { + misfit_member_type * node = (misfit_member_type *)util_malloc( sizeof * node ); + UTIL_TYPE_ID_INIT( node , MISFIT_MEMBER_TYPE_ID); + node->my_iens = iens; + node->obs = hash_alloc(); + return node; +} + + +static void misfit_member_install_vector( misfit_member_type * node , const char * key , misfit_ts_type * vector ) { + hash_insert_hash_owned_ref( node->obs, key , vector , misfit_ts_free__ ); +} + + +static misfit_ts_type * misfit_member_safe_get_vector( misfit_member_type * node , const char * obs_key , int history_length) { + if (!hash_has_key( node->obs , obs_key )) + misfit_member_install_vector(node , obs_key , misfit_ts_alloc( history_length ) ); + return (misfit_ts_type *) hash_get( node->obs , obs_key ); +} + + +misfit_ts_type * misfit_member_get_ts( const misfit_member_type * node , const char * obs_key ) { + return (misfit_ts_type *) hash_get( node->obs , obs_key ); +} + +bool misfit_member_has_ts( const misfit_member_type * node , const char * obs_key ) { + return hash_has_key( node->obs , obs_key ); +} + + +void misfit_member_update( misfit_member_type * node , const char * obs_key , int history_length , int iens , const double ** work_chi2) { + misfit_ts_type * vector = misfit_member_safe_get_vector( node , obs_key , history_length ); + for (int step = 0; step <= history_length; step++) + misfit_ts_iset( vector , step , work_chi2[step][iens]); +} + + +void misfit_member_fwrite( const misfit_member_type * node , FILE * stream) { + util_fwrite_int( node->my_iens , stream); + util_fwrite_int( hash_get_size( node->obs ) , stream); + { + hash_iter_type * obs_iter = hash_iter_alloc( node->obs ); + while ( !hash_iter_is_complete( obs_iter )) { + const char * key = hash_iter_get_next_key( obs_iter ); + misfit_ts_type * misfit_ts = (misfit_ts_type *)hash_get( node->obs , key ); + util_fwrite_string( key , stream ); + misfit_ts_fwrite( misfit_ts , stream); + } + hash_iter_free( obs_iter ); + } +} + + +misfit_member_type * misfit_member_fread_alloc( FILE * stream ) { + int my_iens = util_fread_int( stream ); + misfit_member_type * node = misfit_member_alloc( my_iens ); + int hash_size = util_fread_int( stream ); + { + int iobs; + for (iobs = 0; iobs < hash_size; iobs++) { + char * key = util_fread_alloc_string( stream ); + misfit_ts_type * misfit_ts = misfit_ts_fread_alloc( stream ); + misfit_member_install_vector( node , key , misfit_ts ); + free( key ); + } + } + return node; +} + diff --git a/libres/lib/enkf/misfit_ranking.cpp b/libres/lib/enkf/misfit_ranking.cpp new file mode 100644 index 00000000000..4b7ce2f6042 --- /dev/null +++ b/libres/lib/enkf/misfit_ranking.cpp @@ -0,0 +1,189 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'misfit_ranking.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 + +/** + This struct contains the misfits & sort keys for one particular + misfit_ranking. I.e. all the RFT measurements. +*/ + + + +#define MISFIT_RANKING_TYPE_ID 671108 + +struct misfit_ranking_struct { + UTIL_TYPE_ID_DECLARATION; + vector_type * ensemble; /* An ensemble of hash instances. Each hash instance is populated like this: hash_insert_double(hash , "WGOR" , 1.09); */ + double_vector_type * total; /* An enemble of total misfit values (for this misfit_ranking). */ + perm_vector_type * sort_permutation; /* This is how the ens members should be permuted to be sorted under this misfit_ranking. */ + int ens_size; +}; + +UTIL_SAFE_CAST_FUNCTION( misfit_ranking , MISFIT_RANKING_TYPE_ID ) +UTIL_IS_INSTANCE_FUNCTION( misfit_ranking , MISFIT_RANKING_TYPE_ID) + +void misfit_ranking_display( const misfit_ranking_type * misfit_ranking , FILE * stream) { + const int ens_size = double_vector_size( misfit_ranking->total ); + const perm_vector_type * permutations = misfit_ranking->sort_permutation; + hash_type * obs_hash = NULL; + { + // The ensemble vector can contain invalid nodes with NULL. + int index = 0; + while ((obs_hash == NULL) && (index < vector_get_size( misfit_ranking->ensemble))) { + obs_hash = (hash_type *) vector_iget( misfit_ranking->ensemble , index ); + index++; + } + if (obs_hash == NULL) { + fprintf(stderr,"Sorry: no valid results loaded for this misfit_ranking - returning\n"); + return; + } + } + + { + int i; + double summed_up = 0.0; + stringlist_type * obs_keys = hash_alloc_stringlist( obs_hash ); + int num_obs = stringlist_get_size( obs_keys ); + int num_obs_total = num_obs * ens_size; // SHould not count failed/missing members ... + + fprintf(stream,"\n\n"); + fprintf(stream," # Realization Normalized misfit Total misfit\n"); + fprintf(stream,"-------------------------------------------------------\n"); + for (i = 0; i < ens_size; i++) { + int iens = perm_vector_iget( permutations, i ); + double total_misfit = double_vector_iget( misfit_ranking->total , iens ); + double normalized_misfit = sqrt(total_misfit / num_obs_total); + summed_up = summed_up+total_misfit; + fprintf(stream,"%3d %3d %10.3f %10.3f \n",i,iens,normalized_misfit,total_misfit); + } + + { + double normalized_summed_up = sqrt(summed_up / (num_obs_total * ens_size)); + fprintf(stream," All %10.3f %10.3f \n",normalized_summed_up,summed_up); + } + fprintf(stream,"-------------------------------------------------------\n"); + } + +} + + +static misfit_ranking_type * misfit_ranking_alloc_empty( int ens_size ) { + misfit_ranking_type * misfit_ranking = (misfit_ranking_type *)util_malloc( sizeof * misfit_ranking ); + UTIL_TYPE_ID_INIT( misfit_ranking , MISFIT_RANKING_TYPE_ID ); + misfit_ranking->sort_permutation = NULL; + misfit_ranking->ensemble = vector_alloc_new(); + misfit_ranking->total = double_vector_alloc( 0 , INVALID_RANKING_VALUE ); + misfit_ranking->ens_size = ens_size; + return misfit_ranking; +} + + +/** + Step and step2 are inclusive. The time direction is flattened. +*/ + +misfit_ranking_type * misfit_ranking_alloc(const misfit_ensemble_type * misfit_ensemble , const stringlist_type * sort_keys , const int_vector_type * steps, const char * ranking_key) { + const int ens_size = misfit_ensemble_get_ens_size( misfit_ensemble ); + int iens; + misfit_ranking_type * ranking = misfit_ranking_alloc_empty(ens_size); + + for (iens = 0; iens < ens_size; iens++) { + const misfit_member_type * misfit_member = misfit_ensemble_iget_member( misfit_ensemble , iens ); /* Lookup in the master ensemble. */ + + { + double iens_valid = true; + double total = 0; + hash_type * obs_hash = hash_alloc(); + for (int ikey = 0; ikey < stringlist_get_size( sort_keys ); ikey++) { + const char * obs_key = stringlist_iget( sort_keys , ikey ); + if (misfit_member_has_ts( misfit_member , obs_key )) { + misfit_ts_type * ts = misfit_member_get_ts( misfit_member , obs_key ); + double value = misfit_ts_eval( ts , steps ); /* Sum up the misfit for this key - and these timesteps. */ + hash_insert_double( obs_hash , obs_key , value); + total += value; + } else + iens_valid = true; + } + if (iens_valid) + misfit_ranking_iset( ranking , iens , obs_hash , total ); + else + misfit_ranking_iset_invalid( ranking , iens ); + } + } + ranking->sort_permutation = double_vector_alloc_sort_perm( ranking->total ); + + return ranking; +} + + + + + +void misfit_ranking_free( misfit_ranking_type * misfit_ranking ) { + vector_free( misfit_ranking->ensemble ); + double_vector_free( misfit_ranking->total ); + + if (misfit_ranking->sort_permutation) + perm_vector_free( misfit_ranking->sort_permutation ); + + free( misfit_ranking ); +} + + + +void misfit_ranking_free__( void * arg ) { + misfit_ranking_type * misfit_ranking = misfit_ranking_safe_cast( arg ); + misfit_ranking_free( misfit_ranking ); +} + + + +void misfit_ranking_iset( misfit_ranking_type * misfit_ranking , int iens , hash_type * obs_hash , double total_misfit) { + if (iens > vector_get_size(misfit_ranking->ensemble)) + vector_grow_NULL( misfit_ranking->ensemble , iens ); + + if (obs_hash != NULL) + vector_iset_owned_ref( misfit_ranking->ensemble , iens , obs_hash , hash_free__ ); + else + vector_iset_ref( misfit_ranking->ensemble , iens , NULL ); + + double_vector_iset( misfit_ranking->total , iens , total_misfit ); +} + + +void misfit_ranking_iset_invalid( misfit_ranking_type * misfit_ranking , int iens ) { + misfit_ranking_iset( misfit_ranking , iens , NULL , INVALID_RANKING_VALUE ); +} + + +const perm_vector_type * misfit_ranking_get_permutation( const misfit_ranking_type * misfit_ranking ) { + return misfit_ranking->sort_permutation; +} diff --git a/libres/lib/enkf/misfit_ts.cpp b/libres/lib/enkf/misfit_ts.cpp new file mode 100644 index 00000000000..a91b983454a --- /dev/null +++ b/libres/lib/enkf/misfit_ts.cpp @@ -0,0 +1,101 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'misfit_ts.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 + + +#define MISFIT_TS_TYPE_ID 641066 + +struct misfit_ts_struct { + UTIL_TYPE_ID_DECLARATION; + double_vector_type * data; /* A double vector of length 'history_length' with actual misfit values. */ +}; + +static UTIL_SAFE_CAST_FUNCTION(misfit_ts , MISFIT_TS_TYPE_ID); + +/******************************************************************/ +/* + Implementation of the misfit_ts type. Contains the full + timeseries of misfit for one member/one observation key. +*/ + +misfit_ts_type * misfit_ts_alloc(int history_length) { + misfit_ts_type * misfit_ts = (misfit_ts_type *)util_malloc( sizeof * misfit_ts ); + UTIL_TYPE_ID_INIT(misfit_ts , MISFIT_TS_TYPE_ID); + + if (history_length > 0) + misfit_ts->data = double_vector_alloc( history_length + 1 , 0 ); + else + misfit_ts->data = NULL; /* Used by the xxx_fread_alloc() function below. */ + + return misfit_ts; +} + + +misfit_ts_type * misfit_ts_fread_alloc( FILE * stream ) { + misfit_ts_type * misfit_ts = misfit_ts_alloc( 0 ); + if (misfit_ts->data == NULL) + misfit_ts->data = double_vector_fread_alloc( stream ); + return misfit_ts; +} + + +void misfit_ts_fwrite( const misfit_ts_type * misfit_ts , FILE * stream ) { + double_vector_fwrite( misfit_ts->data , stream ); +} + + + + +static void misfit_ts_free( misfit_ts_type * misfit_ts) { + double_vector_free( misfit_ts->data ); + free( misfit_ts ); +} + + +void misfit_ts_free__( void * vector ) { + misfit_ts_free( misfit_ts_safe_cast( vector )); +} + + + + +void misfit_ts_iset( misfit_ts_type * vector , int time_index , double value ) { + double_vector_iset( vector->data , time_index , value ); +} + +/** Step2 is inclusive */ +double misfit_ts_eval( const misfit_ts_type * vector , const int_vector_type * steps) { + double misfit_sum = 0; + int step; + + for (int i = 0; i < int_vector_size(steps); ++i) { + step = int_vector_iget(steps, i); + misfit_sum += double_vector_iget(vector->data , step ); + } + + return misfit_sum; +} + diff --git a/libres/lib/enkf/model_config.cpp b/libres/lib/enkf/model_config.cpp new file mode 100644 index 00000000000..7942678b351 --- /dev/null +++ b/libres/lib/enkf/model_config.cpp @@ -0,0 +1,786 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'model_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 +#include + +#include + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + This struct contains configuration which is specific to this + particular model/run. Such of the information is actually accessed + directly through the enkf_state object; but this struct is the + owner of the information, and responsible for allocating/freeing + it. + + Observe that the distinction of what goes in model_config, and what + goes in ecl_config is not entirely clear; ECLIPSE is unfortunately + not (yet ??) exactly 'any' reservoir simulator in this context. + +*/ + + +/* + The runpath format is governed by a hash table where new runpaths + are added with model_config_add_runpath() and then current runpath + is selected with model_config_select_runpath(). However this + implementation is quite different from the way manipulation of the + runpath is exposed to the user: The runpath is controlled through + the RUNPATH config key (key DEFAULT_RUNPATH_KEY in the hash table) + This semantically predefined runpath is the only option visible to the user. + */ + +#define MODEL_CONFIG_TYPE_ID 661053 +struct model_config_struct { + UTIL_TYPE_ID_DECLARATION; + forward_model_type * forward_model; /* The forward_model - as loaded from the config file. Each enkf_state object internalizes its private copy of the forward_model. */ + time_map_type * external_time_map; + history_type * history; /* The history object. */ + path_fmt_type * current_runpath; /* path_fmt instance for runpath - runtime the call gets arguments: (iens, report_step1 , report_step2) - i.e. at least one %d must be present.*/ + char * current_path_key; + hash_type * runpath_map; + char * jobname_fmt; + char * enspath; + char * rftpath; + char * data_root; + char * default_data_root; + + int max_internal_submit; /* How many times to retry if the load fails. */ + const ecl_sum_type * refcase; /* A pointer to the refcase - can be NULL. Observe that this ONLY a pointer + to the ecl_sum instance owned and held by the ecl_config object. */ + char * gen_kw_export_name; + int num_realizations; + char * obs_config_file; + + /** The results are always loaded. */ + bool_vector_type * internalize_state; /* Should the (full) state be internalized (at this report_step). */ + bool_vector_type * __load_eclipse_restart; /* Internal variable: is it necessary to load the state? */ +}; + + +char * model_config_alloc_jobname( const model_config_type * model_config , int iens) { + return util_alloc_sprintf( model_config->jobname_fmt, iens); +} + +const char * model_config_get_jobname_fmt( const model_config_type * model_config ) { + return model_config->jobname_fmt; +} + +void model_config_set_jobname_fmt( model_config_type * model_config , const char * jobname_fmt) { + model_config->jobname_fmt = util_realloc_string_copy( model_config->jobname_fmt , jobname_fmt ); +} + +const char * model_config_get_obs_config_file(const model_config_type * model_config) { + return model_config->obs_config_file; +} + +path_fmt_type * model_config_get_runpath_fmt(const model_config_type * model_config) { + return model_config->current_runpath; +} + +const char * model_config_get_runpath_as_char( const model_config_type * model_config ) { + return path_fmt_get_fmt( model_config->current_runpath ); +} + +bool model_config_runpath_requires_iter( const model_config_type * model_config ) { + if (util_int_format_count( model_config_get_runpath_as_char( model_config)) > 1 ) + return true; + else + return false; +} + + + + +void model_config_add_runpath( model_config_type * model_config , const char * path_key , const char * fmt) { + path_fmt_type * path_fmt = path_fmt_alloc_directory_fmt( fmt ); + hash_insert_hash_owned_ref( model_config->runpath_map , path_key , path_fmt , path_fmt_free__ ); +} + + +/* + If the path_key does not exists it will return false and stay + silent. +*/ + +bool model_config_select_runpath( model_config_type * model_config , const char * path_key) { + if (hash_has_key( model_config->runpath_map , path_key )) { + model_config->current_runpath = (path_fmt_type * ) hash_get( model_config->runpath_map , path_key ); + if(model_config->current_path_key != path_key) // If ptrs are the same, there is nothing to do + model_config->current_path_key = util_realloc_string_copy( model_config->current_path_key , path_key); + return true; + } else { + if (model_config->current_runpath != NULL) // OK - we already have a valid selection - stick to that and return False. + return false; + else { + util_abort("%s: path_key:%s does not exist - and currently no valid runpath selected \n",__func__ , path_key); + return false; + } + } +} + + +void model_config_set_runpath(model_config_type * model_config , const char * fmt) { + if (model_config->current_path_key) { + model_config_add_runpath(model_config , model_config->current_path_key , fmt); + model_config_select_runpath( model_config , model_config->current_path_key ); + } else + util_abort("%s: current path has not been set \n",__func__); +} + + + +void model_config_set_gen_kw_export_name( model_config_type * model_config, const char * name) { + model_config->gen_kw_export_name = util_realloc_string_copy( model_config->gen_kw_export_name , name ); +} + +const char * model_config_get_gen_kw_export_name( const model_config_type * model_config) { + return model_config->gen_kw_export_name; +} + + + + void model_config_set_enspath( model_config_type * model_config , const char * enspath) { + model_config->enspath = util_realloc_string_copy( model_config->enspath , enspath ); + } + + void model_config_set_rftpath( model_config_type * model_config , const char * rftpath) { + model_config->rftpath = util_realloc_string_copy( model_config->rftpath , rftpath ); + } + + const char * model_config_get_enspath( const model_config_type * model_config) { + return model_config->enspath; + } + +const ecl_sum_type * model_config_get_refcase( const model_config_type * model_config ) { + return model_config->refcase; +} + +void model_config_set_refcase( model_config_type * model_config , const ecl_sum_type * refcase ) { + model_config->refcase = refcase; +} + + +history_source_type model_config_get_history_source( const model_config_type * model_config ) { + if(!model_config->history) + return HISTORY_SOURCE_INVALID; + + return history_get_source(model_config->history); +} + + +void model_config_select_refcase_history( model_config_type * model_config , const ecl_sum_type * refcase , bool use_history) { + if (model_config->history != NULL) + history_free( model_config->history ); + + if (refcase != NULL) { + model_config->history = history_alloc_from_refcase( refcase , use_history ); + } else + util_abort("%s: internal error - trying to load history from REFCASE - but no REFCASE has been loaded.\n",__func__); +} + + +int model_config_get_max_internal_submit( const model_config_type * config ) { + return config->max_internal_submit; +} + +void model_config_set_max_internal_submit( model_config_type * model_config , int max_resample ) { + model_config->max_internal_submit = max_resample; +} + + +UTIL_IS_INSTANCE_FUNCTION( model_config , MODEL_CONFIG_TYPE_ID) + +model_config_type * model_config_alloc_empty() { + model_config_type * model_config = (model_config_type *)util_malloc(sizeof * model_config ); + /** + There are essentially three levels of initialisation: + + 1. Initialize to NULL / invalid. + 2. Initialize with default values. + 3. Initialize with user supplied values. + + */ + UTIL_TYPE_ID_INIT(model_config , MODEL_CONFIG_TYPE_ID); + model_config->enspath = NULL; + model_config->rftpath = NULL; + model_config->data_root = NULL; + model_config->default_data_root = NULL; + model_config->current_runpath = NULL; + model_config->current_path_key = NULL; + model_config->history = NULL; + model_config->jobname_fmt = NULL; + model_config->forward_model = NULL; + model_config->external_time_map = NULL; + model_config->internalize_state = bool_vector_alloc( 0 , false ); + model_config->__load_eclipse_restart = bool_vector_alloc( 0 , false ); + model_config->runpath_map = hash_alloc(); + model_config->gen_kw_export_name = NULL; + model_config->refcase = NULL; + model_config->num_realizations = 0; + model_config->obs_config_file = NULL; + + model_config_set_enspath( model_config , DEFAULT_ENSPATH ); + model_config_set_rftpath( model_config , DEFAULT_RFTPATH ); + model_config_set_max_internal_submit( model_config , DEFAULT_MAX_INTERNAL_SUBMIT); + model_config_add_runpath( model_config , DEFAULT_RUNPATH_KEY , DEFAULT_RUNPATH); + model_config_select_runpath( model_config , DEFAULT_RUNPATH_KEY ); + model_config_set_gen_kw_export_name(model_config, DEFAULT_GEN_KW_EXPORT_NAME); + + return model_config; +} + +model_config_type * model_config_alloc( + const config_content_type * config_content, + const char * data_root, + const ext_joblist_type * joblist, + int last_history_restart, + const ecl_sum_type * refcase) +{ + model_config_type * model_config = model_config_alloc_empty(); + + if(config_content) + model_config_init(model_config, + config_content, + data_root, + 0, + joblist, + last_history_restart, + refcase); + + return model_config; +} + +model_config_type * model_config_alloc_full(int max_resample, + int num_realizations, + char * run_path, + char * data_root, + char * enspath, + char * job_name, + forward_model_type * forward_model, + char * obs_config, + time_map_type * time_map, + char * rftpath, + char * gen_kw_export_name, + history_source_type history_source, + const ext_joblist_type * joblist, + const ecl_sum_type * refcase) +{ + model_config_type * model_config = model_config_alloc_empty(); + model_config->max_internal_submit = max_resample; + model_config->num_realizations = num_realizations; + + model_config_add_runpath( model_config , DEFAULT_RUNPATH_KEY , run_path); + model_config_select_runpath( model_config , DEFAULT_RUNPATH_KEY ); + model_config_set_data_root(model_config, data_root); + + model_config->enspath = util_realloc_string_copy( model_config->enspath , enspath ); + model_config->jobname_fmt = util_realloc_string_copy( model_config->jobname_fmt , job_name ); + model_config->forward_model = forward_model; + model_config->obs_config_file = util_alloc_string_copy(obs_config); + model_config->external_time_map = time_map; + model_config->rftpath = util_realloc_string_copy( model_config->rftpath , rftpath ); + model_config->gen_kw_export_name = util_realloc_string_copy( model_config->gen_kw_export_name , gen_kw_export_name ); + model_config->refcase = refcase; + + model_config_select_history(model_config, history_source, refcase); + + if (model_config->history != NULL) { + int num_restart = model_config_get_last_history_restart(model_config); + bool_vector_iset( model_config->internalize_state , num_restart - 1 , false ); + bool_vector_iset( model_config->__load_eclipse_restart , num_restart - 1 , false ); + } + + return model_config; +} + + +bool model_config_select_history( model_config_type * model_config , history_source_type source_type, const ecl_sum_type * refcase) { + bool selectOK = false; + + if (((source_type == REFCASE_HISTORY) || (source_type == REFCASE_SIMULATED)) && refcase != NULL) { + if (source_type == REFCASE_HISTORY) + model_config_select_refcase_history( model_config , refcase , true); + else + model_config_select_refcase_history( model_config , refcase , false); + selectOK = true; + } + + return selectOK; +} + + +static bool model_config_select_any_history( model_config_type * model_config , const ecl_sum_type * refcase) { + bool selectOK = false; + + if ( refcase != NULL ) { + model_config_select_refcase_history( model_config , refcase , true); + selectOK = true; + } + + return selectOK; +} + +const char * model_config_get_data_root( const model_config_type * model_config ) { + if (model_config->data_root) + return model_config->data_root; + + return model_config->default_data_root; +} + +void model_config_set_data_root( model_config_type * model_config , const char * data_root) { + model_config->data_root = util_realloc_string_copy( model_config->data_root , data_root ); + setenv( "DATA_ROOT" , data_root , 1 ); +} + +static void model_config_set_default_data_root( model_config_type * model_config , const char * data_root) { + model_config->default_data_root = util_alloc_string_copy( data_root ); + setenv( "DATA_ROOT" , data_root , 1 ); +} + + +bool model_config_data_root_is_set( const model_config_type * model_config ) { + if (!model_config->data_root) + return false; + + return !util_string_equal( model_config->data_root, model_config->default_data_root); +} + + + +void model_config_init(model_config_type * model_config , + const config_content_type * config , + const char * data_root, + int ens_size , + const ext_joblist_type * joblist , + int last_history_restart , + const ecl_sum_type * refcase) { + + model_config->forward_model = forward_model_alloc( joblist ); + const subst_list_type * define_list = config_content_get_const_define_list(config); + model_config_set_refcase( model_config , refcase ); + model_config_set_default_data_root( model_config, data_root ); + + if (config_content_has_item(config, NUM_REALIZATIONS_KEY)) + model_config->num_realizations = config_content_get_value_as_int(config, NUM_REALIZATIONS_KEY); + + for (int i = 0; i < config_content_get_size(config); i++) { + const config_content_node_type * node = config_content_iget_node( config , i); + if (util_string_equal(config_content_node_get_kw(node), SIMULATION_JOB_KEY)) + forward_model_parse_job_args(model_config->forward_model, + config_content_node_get_stringlist(node), + define_list); + + if (util_string_equal(config_content_node_get_kw(node), FORWARD_MODEL_KEY) ) { + const char * arg = config_content_node_get_full_string(node, ""); + forward_model_parse_job_deprecated_args( model_config->forward_model, arg, define_list); + } + } + + + if (config_content_has_item( config, RUNPATH_KEY)) { + model_config_add_runpath( model_config , DEFAULT_RUNPATH_KEY , config_content_get_value_as_path(config , RUNPATH_KEY) ); + model_config_select_runpath( model_config , DEFAULT_RUNPATH_KEY ); + } + + history_source_type source_type = DEFAULT_HISTORY_SOURCE; + + if (config_content_has_item( config , HISTORY_SOURCE_KEY)) { + const char * history_source = config_content_iget(config , HISTORY_SOURCE_KEY, 0,0); + source_type = history_get_source_type( history_source ); + } + + if (!model_config_select_history( model_config , source_type , refcase )) + if (!model_config_select_history( model_config , DEFAULT_HISTORY_SOURCE , refcase )) { + model_config_select_any_history( model_config , refcase); + /* If even the last call return false, it means the configuration does not have any of + * these keys: HISTORY_SOURCE or REFCASE. + * History matching won't be supported for this configuration. + */ + } + + if (model_config->history != NULL) { + int num_restart = model_config_get_last_history_restart(model_config); + bool_vector_iset( model_config->internalize_state , num_restart - 1 , false ); + bool_vector_iset( model_config->__load_eclipse_restart , num_restart - 1 , false ); + } + + if (config_content_has_item( config , TIME_MAP_KEY)) { + const char * filename = config_content_get_value_as_path( config , TIME_MAP_KEY); + time_map_type * time_map = time_map_alloc(); + if (time_map_fscanf( time_map , filename)) + model_config->external_time_map = time_map; + else { + time_map_free( time_map ); + fprintf(stderr,"** ERROR: Loading external time map from:%s failed \n", filename); + } + } + + + + /* + The full treatment of the SCHEDULE_PREDICTION_FILE keyword is in + the ensemble_config file, because the functionality is implemented + as (quite) plain GEN_KW instance. Here we just check if it is + present or not. + */ + + if (config_content_has_item( config , ENSPATH_KEY)) + model_config_set_enspath( model_config , config_content_get_value_as_path(config , ENSPATH_KEY)); + + if (config_content_has_item( config , DATA_ROOT_KEY)) + model_config_set_data_root( model_config , config_content_get_value_as_path(config , DATA_ROOT_KEY)); + + /* + The keywords ECLBASE and JOBNAME can be used as synonyms. But + observe that: + + 1. The ecl_config object will also pick up the ECLBASE keyword, + and set the have_eclbase flag of that object. + + 2. If both ECLBASE and JOBNAME are in the config file the + JOBNAME keyword will be preferred. + */ + if (config_content_has_item( config , ECLBASE_KEY)) + model_config_set_jobname_fmt( model_config , config_content_get_value(config , ECLBASE_KEY)); + + if (config_content_has_item( config , JOBNAME_KEY)) { + model_config_set_jobname_fmt( model_config , config_content_get_value(config , JOBNAME_KEY)); + if (config_content_has_item( config , ECLBASE_KEY)) + res_log_warning("Can not have both JOBNAME and ECLBASE keywords. The ECLBASE keyword will be ignored."); + } + + if (config_content_has_item( config , RFTPATH_KEY)) + model_config_set_rftpath( model_config , config_content_get_value(config , RFTPATH_KEY)); + + if (config_content_has_item( config , MAX_RESAMPLE_KEY)) + model_config_set_max_internal_submit( model_config , config_content_get_value_as_int( config , MAX_RESAMPLE_KEY )); + + + { + if (config_content_has_item( config , GEN_KW_EXPORT_NAME_KEY)) { + const char * export_name = config_content_get_value(config, GEN_KW_EXPORT_NAME_KEY); + model_config_set_gen_kw_export_name(model_config, export_name); + } + } + + if (config_content_has_item(config, OBS_CONFIG_KEY)) { + const char * obs_config_file = config_content_get_value_as_abspath( + config, + OBS_CONFIG_KEY + ); + + model_config->obs_config_file = util_alloc_string_copy(obs_config_file); + } +} + + + + + +void model_config_free(model_config_type * model_config) { + free( model_config->enspath ); + free( model_config->rftpath ); + free( model_config->jobname_fmt ); + free( model_config->current_path_key); + free( model_config->gen_kw_export_name); + free( model_config->obs_config_file ); + free( model_config->data_root ); + free( model_config->default_data_root ); + + if (model_config->history) + history_free(model_config->history); + + if (model_config->forward_model) + forward_model_free(model_config->forward_model); + + if (model_config->external_time_map) + time_map_free( model_config->external_time_map ); + + bool_vector_free(model_config->internalize_state); + bool_vector_free(model_config->__load_eclipse_restart); + hash_free(model_config->runpath_map); + free(model_config); +} + + + +bool model_config_has_history(const model_config_type * config) { + if (config->history != NULL) + return true; + else + return false; +} + + +history_type * model_config_get_history(const model_config_type * config) { + return config->history; +} + +int model_config_get_num_realizations(const model_config_type * model_config) { + return model_config->num_realizations; +} + +/** + Will be NULL unless the user has explicitly loaded an external time + map with the TIME_MAP config option. +*/ + +time_map_type * model_config_get_external_time_map( const model_config_type * config) { + return config->external_time_map; +} + +int model_config_get_last_history_restart(const model_config_type * config) { + if (config->history) + return history_get_last_restart( config->history ); + else { + if (config->external_time_map) + return time_map_get_last_step( config->external_time_map); + else + return -1; + } +} + +forward_model_type * model_config_get_forward_model( const model_config_type * config) { + return config->forward_model; +} + + +/*****************************************************************/ + +/* Setting everything back to the default value: false. */ +void model_config_init_internalization( model_config_type * config ) { + bool_vector_reset(config->internalize_state); + bool_vector_reset(config->__load_eclipse_restart); +} + + +/** + This function sets the internalize_state flag to true for + report_step. Because of the coupling to the __load_eclipse_restart variable + this function can __ONLY__ be used to set internalize to true. +*/ + +void model_config_set_internalize_state( model_config_type * config , int report_step) { + bool_vector_iset(config->internalize_state , report_step , true); + bool_vector_iset(config->__load_eclipse_restart , report_step , true); +} + + +/*****************************************************************/ + +static char * model_config_alloc_user_config_file(const char * user_config_file, bool base_only) { + char * base_name; + char * extension; + util_alloc_file_components(user_config_file, NULL, &base_name, &extension); + + char * config_file; + if (base_only) + config_file = util_alloc_filename(NULL, base_name, NULL); + else + config_file = util_alloc_filename(NULL, base_name, extension); + + free(base_name); + free(extension); + + return config_file; +} + +static hash_type * alloc_predefined_kw_map(const char * user_config_file) { + char * config_file_base = model_config_alloc_user_config_file(user_config_file, true); + char * config_file = model_config_alloc_user_config_file(user_config_file, false); + char * config_path; + + hash_type * pre_defined_kw_map = hash_alloc(); + hash_insert_string(pre_defined_kw_map, "", config_file); + hash_insert_string(pre_defined_kw_map, "", config_file_base); + { + char * tmp_path; + util_alloc_file_components( user_config_file , &tmp_path , NULL , NULL ); + config_path = util_alloc_abs_path( tmp_path ); + free( tmp_path ); + } + hash_insert_string(pre_defined_kw_map, "", config_path); + free(config_path); + free(config_file) ; + free(config_file_base); + + return pre_defined_kw_map; +} + +static void model_config_init_user_config(config_parser_type * config ) { + config_schema_item_type * item; + + /*****************************************************************/ + /* config_add_schema_item(): */ + /* */ + /* 1. boolean - required? */ + /*****************************************************************/ + + ert_workflow_list_add_config_items( config ); + analysis_config_add_config_items( config ); + ensemble_config_add_config_items( config ); + ecl_config_add_config_items( config ); + rng_config_add_config_items( config ); + + /*****************************************************************/ + /* Required keywords from the ordinary model_config file */ + + config_add_key_value(config, LOG_LEVEL_KEY, false, CONFIG_STRING); + config_add_key_value(config, LOG_FILE_KEY, false, CONFIG_PATH); + + config_add_key_value(config, MAX_RESAMPLE_KEY, false, CONFIG_INT); + + + item = config_add_schema_item(config, NUM_REALIZATIONS_KEY, true); + config_schema_item_set_argc_minmax(item, 1, 1); + config_schema_item_iset_type(item, 0, CONFIG_INT); + config_add_alias(config, NUM_REALIZATIONS_KEY, "NUM_REALISATIONS"); + + /*****************************************************************/ + /* Optional keywords from the model config file */ + + item = config_add_schema_item(config, RUN_TEMPLATE_KEY, false); + config_schema_item_set_argc_minmax(item, 2, CONFIG_DEFAULT_ARG_MAX); + config_schema_item_iset_type(item, 0, CONFIG_EXISTING_PATH); + + config_add_key_value(config, RUNPATH_KEY, false, CONFIG_PATH); + config_add_key_value(config, DATA_ROOT_KEY, false, CONFIG_PATH); + config_add_key_value(config, ENSPATH_KEY, false, CONFIG_PATH); + + item = config_add_schema_item(config, JOBNAME_KEY, false); + config_schema_item_set_argc_minmax(item, 1, 1); + + item = config_add_schema_item(config, FORWARD_MODEL_KEY, false); + config_schema_item_set_argc_minmax(item , 1, CONFIG_DEFAULT_ARG_MAX); + + item = config_add_schema_item(config, SIMULATION_JOB_KEY, false); + config_schema_item_set_argc_minmax(item , 1, CONFIG_DEFAULT_ARG_MAX); + + item = config_add_schema_item(config, DATA_KW_KEY, false); + config_schema_item_set_argc_minmax(item, 2, 2); + + item = config_add_schema_item(config, OBS_CONFIG_KEY, false); + config_schema_item_set_argc_minmax(item , 1, 1); + config_schema_item_iset_type(item, 0, CONFIG_EXISTING_PATH); + + config_add_key_value(config, TIME_MAP_KEY, false, CONFIG_EXISTING_PATH); + + item = config_add_schema_item(config, RFTPATH_KEY, false); + config_schema_item_set_argc_minmax(item, 1, 1); + + item = config_add_schema_item(config, GEN_KW_EXPORT_NAME_KEY, false); + config_schema_item_set_argc_minmax(item, 1, 1); + + stringlist_type * refcase_dep = stringlist_alloc_new(); + stringlist_append_copy(refcase_dep, REFCASE_KEY); + item = config_add_schema_item(config, HISTORY_SOURCE_KEY, false); + config_schema_item_set_argc_minmax(item , 1 , 1); + { + stringlist_type * argv = stringlist_alloc_new(); + stringlist_append_copy(argv, "SCHEDULE"); + stringlist_append_copy(argv, "REFCASE_SIMULATED"); + stringlist_append_copy(argv, "REFCASE_HISTORY"); + + config_schema_item_set_common_selection_set(item, argv ); + stringlist_free(argv); + } + config_schema_item_set_required_children_on_value(item, "REFCASE_SIMULATED", refcase_dep); + config_schema_item_set_required_children_on_value(item, "REFCASE_HISTORY", refcase_dep); + + stringlist_free(refcase_dep); + + hook_manager_add_config_items(config); +} + + +void model_config_init_config_parser(config_parser_type * config_parser) { + model_config_init_user_config(config_parser); + site_config_add_config_items(config_parser, false); +} + +config_content_type * model_config_alloc_content( + const char * user_config_file, config_parser_type * config) { + + model_config_init_config_parser(config); + + hash_type * pre_defined_kw_map = alloc_predefined_kw_map(user_config_file); + config_content_type * content = config_parse( + config , user_config_file, + "--", INCLUDE_KEY, DEFINE_KEY, + pre_defined_kw_map, CONFIG_UNRECOGNIZED_WARN, true + ); + hash_free(pre_defined_kw_map); + + const stringlist_type * warnings = config_content_get_warnings(content); + if (stringlist_get_size( warnings ) > 0) { + fprintf( + stderr, + " ** There were warnings when parsing the configuration file: %s", + user_config_file + ); + + for (int i=0; i < stringlist_get_size( warnings ); i++) + fprintf(stderr, " %02d : %s \n", i, stringlist_iget(warnings, i)); + } + + if (!config_content_is_valid(content)) { + config_error_type * errors = config_content_get_errors(content); + config_error_fprintf(errors, true, stderr); + + util_abort( + "%s: Failed to load user configuration file: %s\n", + __func__, user_config_file + ); + } + + return content; +} + + + +bool model_config_report_step_compatible(const model_config_type * model_config, const ecl_sum_type * ecl_sum_simulated) { + bool ret = true; + const ecl_sum_type * ecl_sum_reference = model_config_get_refcase(model_config); + + if (ecl_sum_reference) //Can be NULL + ret = ecl_sum_report_step_compatible(ecl_sum_reference, ecl_sum_simulated); + + return ret; +} diff --git a/libres/lib/enkf/obs_data.cpp b/libres/lib/enkf/obs_data.cpp new file mode 100644 index 00000000000..7f51981d611 --- /dev/null +++ b/libres/lib/enkf/obs_data.cpp @@ -0,0 +1,676 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'obs_data.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. +*/ + +/** +See the file README.obs for ducumentation of the varios datatypes +involved with observations/measurement/+++. + + +The file contains two different variables holding the number of +observations, nrobs_total and nrobs_active. The first holds the total +number of observations at this timestep, and the second holds the +number of active measurements at this timestep; the inactive +measurements have been deactivated the obs_data_deactivate_outliers() +function. + +The flow is as follows: + + 1. All the observations have been collected in an obs_data instance, + and all the corresponding measurements of the state have been + collected in a meas_data instance - we are ready for analysis. + + 2. The functions meas_data_alloc_stats() is called to calculate + the ensemble mean and std of all the measurements. + + 3. The function obs_data_deactivate_outliers() is called to compare + the ensemble mean and std with the observations, in the case of + outliers the number obs_active flag of the obs_data instance is + set to false. + + 4. The remaining functions (and matrices) now refer to the number of + active observations, however the "raw" observations found in the + obs_data instance are in a vector with nrobs_total observations; + i.e. we must handle two indices and two total lengths. A bit + messy. + + +Variables of size nrobs_total: +------------------------------ + o obs->value / obs->std / obs->obs_active + o meanS , innov, stdS + + +variables of size nrobs_active: +------------------------------- +Matrices: S, D, E and various internal variables. +*/ + + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#define OBS_BLOCK_TYPE_ID 995833 + +struct obs_block_struct { + UTIL_TYPE_ID_DECLARATION; + char * obs_key; + int size; + double * value; + double * std; + + active_type * active_mode; + int active_size; + matrix_type * error_covar; + bool error_covar_owner; /* If true the error_covar matrix is free'd when construction of the R matrix is complete. */ + double global_std_scaling; +}; + + + +struct obs_data_struct { + vector_type * data; /* vector with obs_block instances. */ + bool_vector_type * mask; + double global_std_scaling; +}; + + + +static UTIL_SAFE_CAST_FUNCTION(obs_block , OBS_BLOCK_TYPE_ID ) + +obs_block_type * obs_block_alloc( const char * obs_key , int obs_size , matrix_type * error_covar , bool error_covar_owner, double global_std_scaling) { + obs_block_type * obs_block = (obs_block_type *)util_malloc( sizeof * obs_block ); + + UTIL_TYPE_ID_INIT( obs_block , OBS_BLOCK_TYPE_ID ); + obs_block->size = obs_size; + obs_block->obs_key = util_alloc_string_copy( obs_key ); + obs_block->value = (double *) util_calloc( obs_size , sizeof * obs_block->value ); + obs_block->std = (double *) util_calloc( obs_size , sizeof * obs_block->std ); + obs_block->active_mode = (active_type *) util_calloc( obs_size , sizeof * obs_block->active_mode ); + obs_block->error_covar = error_covar; + obs_block->error_covar_owner = error_covar_owner; + obs_block->global_std_scaling = global_std_scaling; + { + for (int iobs = 0; iobs < obs_size; iobs++) + obs_block->active_mode[iobs] = LOCAL_INACTIVE; + } + obs_block->active_size = 0; + return obs_block; +} + + + +void obs_block_free( obs_block_type * obs_block ) { + free( obs_block->obs_key ); + free( obs_block->value ); + free( obs_block->std ); + free( obs_block->active_mode ); + free( obs_block ); +} + + +static void obs_block_free__( void * arg ) { + obs_block_type * obs_block = obs_block_safe_cast( arg ); + obs_block_free( obs_block ); +} + + +void obs_block_deactivate( obs_block_type * obs_block , int iobs , bool verbose , const char * msg) { + if (obs_block->active_mode[ iobs ] == ACTIVE) { + if (verbose) + printf("Deactivating: %s(%d) : %s \n",obs_block->obs_key , iobs , msg); + obs_block->active_mode[ iobs ] = DEACTIVATED; + obs_block->active_size--; + } +} + + +const char * obs_block_get_key( const obs_block_type * obs_block) { return obs_block->obs_key; } + +void obs_block_iset( obs_block_type * obs_block , int iobs , double value , double std) { + obs_block->value[ iobs ] = value; + obs_block->std[ iobs ] = std; + if (obs_block->active_mode[ iobs ] != ACTIVE) { + obs_block->active_mode[iobs] = ACTIVE; + obs_block->active_size++; + } +} + +void obs_block_iset_missing( obs_block_type * obs_block , int iobs ) { + if (obs_block->active_mode[ iobs ] == ACTIVE) + obs_block->active_size--; + obs_block->active_mode[iobs] = MISSING; +} + + +double obs_block_iget_std( const obs_block_type * obs_block , int iobs) { + return obs_block->std[ iobs ] * obs_block->global_std_scaling; +} + + +double obs_block_iget_value( const obs_block_type * obs_block , int iobs) { + return obs_block->value[ iobs ]; +} + + +active_type obs_block_iget_active_mode( const obs_block_type * obs_block , int iobs) { + return obs_block->active_mode[ iobs ]; +} + + +bool obs_block_iget_is_active( const obs_block_type * obs_block , int iobs) { + return obs_block->active_mode[iobs] == ACTIVE; +} + + +int obs_block_get_size( const obs_block_type * obs_block ) { + return obs_block->size; +} + + +int obs_block_get_active_size( const obs_block_type * obs_block ) { + return obs_block->active_size; +} + + + + +/*Function that sets each element of the scaling factor equal to 1 divided by the prior standard deviation (from the + obs_data input file. +*/ +static void obs_block_init_scaling( const obs_block_type * obs_block , double * scale_factor , int * __obs_offset) { + int obs_offset = *__obs_offset; + int iobs; + for (iobs =0; iobs < obs_block->size; iobs++) { + if (obs_block->active_mode[iobs] == ACTIVE) { + scale_factor[ obs_offset ] = 1.0 / obs_block_iget_std(obs_block, iobs); + obs_offset++; + } + } + *__obs_offset = obs_offset; +} + + +/* +static void obs_block_init_innov( const obs_block_type * obs_block , const meas_block_type * meas_block , matrix_type * innov , int * __obs_offset) { + int obs_offset = *__obs_offset; + int iobs; + for (iobs =0; iobs < obs_block->size; iobs++) { + if (obs_block->active_mode[iobs] == ACTIVE) { + matrix_iset( innov , obs_offset , 0 , obs_block->value[ iobs ] - meas_block_iget_ens_mean( meas_block , iobs )); + obs_offset++; + } + } + *__obs_offset = obs_offset; +} +*/ + +static void obs_block_initdObs( const obs_block_type * obs_block , matrix_type * dObs , int * __obs_offset) { + int obs_offset = *__obs_offset; + int iobs; + for (iobs =0; iobs < obs_block->size; iobs++) { + if (obs_block->active_mode[iobs] == ACTIVE) { + matrix_iset( dObs , obs_offset , 0 , obs_block->value[ iobs ]); + matrix_iset( dObs , obs_offset , 1 , obs_block->std[ iobs ]); + obs_offset++; + } + } + *__obs_offset = obs_offset; +} + + + + + + +static void obs_block_initR( const obs_block_type * obs_block , matrix_type * R, int * __obs_offset) { + int obs_offset = *__obs_offset; + if (obs_block->error_covar == NULL) { + int iobs; + int iactive = 0; + for (iobs =0; iobs < obs_block->size; iobs++) { + if (obs_block->active_mode[iobs] == ACTIVE) { + double var = obs_block_iget_std(obs_block, iobs) * obs_block_iget_std(obs_block, iobs); + matrix_iset_safe(R , obs_offset + iactive, obs_offset + iactive, var); + iactive++; + } + } + } else { + int row_active = 0; /* We have a covar matrix */ + for (int row = 0; row < obs_block->size; row++) { + if (obs_block->active_mode[row] == ACTIVE) { + int col_active = 0; + for (int col = 0; col < obs_block->size; col++) { + if (obs_block->active_mode[col] == ACTIVE) { + matrix_iset_safe(R , obs_offset + row_active , obs_offset + col_active , matrix_iget( obs_block->error_covar , row , col )); + col_active++; + } + } + row_active++; + } + } + } + + *__obs_offset = obs_offset + obs_block->active_size; + if ((obs_block->error_covar_owner) && (obs_block->error_covar != NULL)) + matrix_free( obs_block->error_covar ); +} + + + +static void obs_block_initE( const obs_block_type * obs_block , matrix_type * E, const double * pert_var , int * __obs_offset) { + int ens_size = matrix_get_columns( E ); + int obs_offset = *__obs_offset; + int iobs; + for (iobs =0; iobs < obs_block->size; iobs++) { + if (obs_block->active_mode[iobs] == ACTIVE) { + double factor = obs_block_iget_std(obs_block, iobs) * sqrt( ens_size / pert_var[ obs_offset ]); + for (int iens = 0; iens < ens_size; iens++) + matrix_imul(E , obs_offset , iens , factor ); + + obs_offset++; + } + } + + *__obs_offset = obs_offset; +} + + +static void obs_block_initE_non_centred( const obs_block_type * obs_block , matrix_type * E, int * __obs_offset) { + int ens_size = matrix_get_columns( E ); + int obs_offset = *__obs_offset; + int iobs; + for (iobs =0; iobs < obs_block->size; iobs++) { + if (obs_block->active_mode[iobs] == ACTIVE) { + double factor = obs_block_iget_std(obs_block, iobs); + for (int iens = 0; iens < ens_size; iens++) + matrix_imul(E , obs_offset , iens , factor ); + + obs_offset++; + } + } + + *__obs_offset = obs_offset; +} + + + +static void obs_block_initD( const obs_block_type * obs_block , matrix_type * D, int * __obs_offset) { + int ens_size = matrix_get_columns( D ); + int obs_offset = *__obs_offset; + int iobs; + for (iobs =0; iobs < obs_block->size; iobs++) { + if (obs_block->active_mode[iobs] == ACTIVE) { + for (int iens = 0; iens < ens_size; iens++) + matrix_iadd(D , obs_offset , iens , obs_block->value[ iobs ]); + + obs_offset++; + } + } + + *__obs_offset = obs_offset; +} + + +static void obs_block_set_active_mask( const obs_block_type * obs_block, bool_vector_type * mask, int * offset) { + for (int i = 0; i < obs_block->size; i++) { + if (obs_block->active_mode[i] == ACTIVE) + bool_vector_iset(mask, *offset, true); + else + bool_vector_iset(mask, *offset, false); + (*offset)++; + } +} + + +/*****************************************************************/ + + +obs_data_type * obs_data_alloc(double global_std_scaling) { + obs_data_type * obs_data = (obs_data_type *)util_malloc(sizeof * obs_data ); + obs_data->data = vector_alloc_new(); + obs_data->mask = bool_vector_alloc(0, false); + obs_data->global_std_scaling = global_std_scaling; + obs_data_reset(obs_data); + return obs_data; +} + + + +void obs_data_reset(obs_data_type * obs_data) { + vector_clear( obs_data->data ); +} + + +obs_block_type * obs_data_add_block( obs_data_type * obs_data , const char * obs_key , int obs_size , matrix_type * error_covar, bool error_covar_owner) { + obs_block_type * new_block = obs_block_alloc( obs_key , obs_size , error_covar , error_covar_owner, obs_data->global_std_scaling); + vector_append_owned_ref( obs_data->data , new_block , obs_block_free__ ); + return new_block; +} + + +obs_block_type * obs_data_iget_block( obs_data_type * obs_data , int index ) { + return (obs_block_type * ) vector_iget( obs_data->data , index); // CXX_CAST_ERROR +} + + +const obs_block_type * obs_data_iget_block_const( const obs_data_type * obs_data , int index ) { + return (const obs_block_type * ) vector_iget_const( obs_data->data , index ); // CXX_CAST_ERROR +} + + +void obs_data_free(obs_data_type * obs_data) { + vector_free( obs_data->data ); + bool_vector_free( obs_data->mask ); + free(obs_data); +} + + + +matrix_type * obs_data_allocE(const obs_data_type * obs_data , rng_type * rng , int active_ens_size ) { + double *pert_mean , *pert_var; + matrix_type * E; + int iens, iobs_active; + int active_obs_size = obs_data_get_active_size( obs_data ); + + E = matrix_alloc( active_obs_size , active_ens_size); + + pert_mean = (double *) util_calloc(active_obs_size , sizeof * pert_mean ); + pert_var = (double *) util_calloc(active_obs_size , sizeof * pert_var ); + { + + for (int j=0; j < active_ens_size; j++) { + for (int i=0; i < active_obs_size; i++) { + matrix_iset( E , i , j , enkf_util_rand_normal(0, 1, rng)); + } + } + } + + for (iobs_active = 0; iobs_active < active_obs_size; iobs_active++) { + pert_mean[iobs_active] = 0; + pert_var[iobs_active] = 0; + } + + for (iens = 0; iens < active_ens_size; iens++) + for (iobs_active = 0; iobs_active < active_obs_size; iobs_active++) + pert_mean[iobs_active] += matrix_iget(E , iobs_active , iens); + + + for (iobs_active = 0; iobs_active < active_obs_size; iobs_active++) + pert_mean[iobs_active] /= active_ens_size; + + for (iens = 0; iens < active_ens_size; iens++) { + for (iobs_active = 0; iobs_active < active_obs_size; iobs_active++) { + double tmp; + matrix_iadd(E , iobs_active , iens , -pert_mean[iobs_active]); + tmp = matrix_iget(E , iobs_active , iens); + pert_var[iobs_active] += tmp * tmp; + } + } + + /* + The actual observed data are not accessed before this last block. + */ + { + int obs_offset = 0; + for (int block_nr = 0; block_nr < vector_get_size( obs_data->data ); block_nr++) { + const obs_block_type * obs_block = (const obs_block_type *)vector_iget_const( obs_data->data , block_nr); + obs_block_initE( obs_block , E , pert_var , &obs_offset); + } + } + + free(pert_mean); + free(pert_var); + + matrix_set_name( E , "E"); + matrix_assert_finite( E ); + return E; +} + + +matrix_type * obs_data_allocD(const obs_data_type * obs_data , const matrix_type * E , const matrix_type * S) { + matrix_type * D = matrix_alloc_copy( E ); + matrix_inplace_sub( D , S ); + + { + int obs_offset = 0; + for (int block_nr = 0; block_nr < vector_get_size( obs_data->data ); block_nr++) { + const obs_block_type * obs_block = (const obs_block_type *)vector_iget_const( obs_data->data , block_nr); + obs_block_initD( obs_block , D , &obs_offset); + } + } + + matrix_set_name( D , "D"); + matrix_assert_finite( D ); + return D; +} + + + + +matrix_type * obs_data_allocR(const obs_data_type * obs_data) { + int active_size = obs_data_get_active_size( obs_data ); + matrix_type * R = matrix_alloc( active_size , active_size ); + { + int obs_offset = 0; + for (int block_nr = 0; block_nr < vector_get_size( obs_data->data ); block_nr++) { + const obs_block_type * obs_block = (const obs_block_type *)vector_iget_const( obs_data->data , block_nr); + obs_block_initR( obs_block , R , &obs_offset); + } + } + + matrix_set_name( R , "R"); + matrix_assert_finite( R ); + return R; +} + +/* +matrix_type * obs_data_alloc_innov(const obs_data_type * obs_data , const meas_data_type * meas_data , int active_size) { + matrix_type * innov = matrix_alloc( active_size , 1 ); + { + int obs_offset = 0; + for (int block_nr = 0; block_nr < vector_get_size( obs_data->data ); block_nr++) { + const obs_block_type * obs_block = (const obs_block_type *)vector_iget_const( obs_data->data , block_nr ); + const meas_block_type * meas_block = meas_data_iget_block_const( meas_data , block_nr ); + + obs_block_init_innov( obs_block , meas_block , innov , &obs_offset); + } + } + return innov; +} +*/ + +matrix_type * obs_data_allocdObs(const obs_data_type * obs_data ) { + int active_size = obs_data_get_active_size( obs_data ); + matrix_type * dObs = matrix_alloc( active_size , 2 ); + { + int obs_offset = 0; + for (int block_nr = 0; block_nr < vector_get_size( obs_data->data ); block_nr++) { + const obs_block_type * obs_block = (const obs_block_type *)vector_iget_const( obs_data->data , block_nr ); + + obs_block_initdObs( obs_block , dObs , &obs_offset); + } + } + return dObs; +} + + +static void obs_data_scale_matrix__(matrix_type * m , const double * scale_factor) { + const int rows = matrix_get_rows( m ); + const int columns = matrix_get_columns( m ); + int i, j; + + for (i = 0; i < columns; i++) + for (j = 0; j < rows; j++) + matrix_imul(m , j,i, scale_factor[j]); + +} + + +static void obs_data_scale_Rmatrix__( matrix_type * R , const double * scale_factor) { + int nrobs_active = matrix_get_rows( R ); + + /* Scale the error covariance matrix*/ + for (int i=0; i < nrobs_active; i++) + for (int j=0; j < nrobs_active; j++) + matrix_imul(R , i , j , scale_factor[i] * scale_factor[j]); +} + + +static double * obs_data_alloc_scale_factor(const obs_data_type * obs_data ) { + int nrobs_active = obs_data_get_active_size( obs_data ); + double * scale_factor = (double *)util_calloc(nrobs_active , sizeof * scale_factor ); + int obs_offset = 0; + for (int block_nr = 0; block_nr < vector_get_size( obs_data->data ); block_nr++) { + const obs_block_type * obs_block = (const obs_block_type *)vector_iget_const( obs_data->data , block_nr ); + + /* Init. the scaling factor ( 1/std(dObs) ) */ + obs_block_init_scaling( obs_block , scale_factor , &obs_offset); + } + + return scale_factor; +} + + +void obs_data_scale_matrix(const obs_data_type * obs_data , matrix_type * matrix) { + double * scale_factor = obs_data_alloc_scale_factor( obs_data ); + obs_data_scale_matrix__( matrix , scale_factor ); + free( scale_factor ); +} + + +void obs_data_scale_Rmatrix(const obs_data_type * obs_data , matrix_type * R) { + double * scale_factor = obs_data_alloc_scale_factor( obs_data ); + obs_data_scale_Rmatrix__( R , scale_factor ); + free( scale_factor ); +} + + +void obs_data_scale(const obs_data_type * obs_data , matrix_type *S , matrix_type *E , matrix_type *D , matrix_type *R , matrix_type * dObs) { + double * scale_factor = obs_data_alloc_scale_factor( obs_data ); + + /* Scale the forecasted data so that they (in theory) have the same variance + (if the prior distribution for the observation errors is correct) */ + obs_data_scale_matrix__( S , scale_factor ); + + /* Scale the combined data matrix: D = DObs + E - S, where DObs is the iobs_active times ens_size matrix where + each column contains a copy of the observed data + */ + if (D != NULL) + obs_data_scale_matrix__( D , scale_factor ); + + /* Same with E (used for low rank representation of the error covariance matrix*/ + if (E != NULL) + obs_data_scale_matrix__( E , scale_factor ); + + if (dObs != NULL) + obs_data_scale_matrix__( dObs , scale_factor ); + + if (R != NULL) + obs_data_scale_Rmatrix__(R , scale_factor); + + free(scale_factor); +} + + +int obs_data_get_active_size( const obs_data_type * obs_data ) { + int active_size = 0; + for (int block_nr = 0; block_nr < vector_get_size( obs_data->data ); block_nr++) { + const obs_block_type * obs_block = (const obs_block_type *)vector_iget_const( obs_data->data , block_nr ); + active_size += obs_block->active_size; + } + + return active_size; +} + + +int obs_data_get_num_blocks( const obs_data_type * obs_data ) { + return vector_get_size( obs_data->data ); +} + + + +int obs_data_get_total_size( const obs_data_type * obs_data ) { + int total_size = 0; + for (int block_nr = 0; block_nr < vector_get_size( obs_data->data ); block_nr++) { + const obs_block_type * obs_block = (const obs_block_type *)vector_iget_const( obs_data->data , block_nr ); + total_size += obs_block->size; + } + return total_size; +} + + +static const obs_block_type * obs_data_lookup_block( const obs_data_type * obs_data, int total_index , int * block_offset) { + if (total_index < obs_data_get_total_size( obs_data )) { + const obs_block_type * obs_block; + int total_offset = 0; + int block_index = 0; + int block_size; + + + while (true) { + obs_block = (const obs_block_type * ) vector_iget_const( obs_data->data , block_index ); + block_size = obs_block->size; + if ((block_size + total_offset) > total_index) + break; + + total_offset += block_size; + block_index++; + } + *block_offset = total_offset; + return obs_block; + } else { + util_abort("%s: could not lookup obs-block \n",__func__); + return NULL; + } +} + + +double obs_data_iget_value( const obs_data_type * obs_data , int total_index ) { + int total_offset; + const obs_block_type * obs_block = obs_data_lookup_block( obs_data , total_index , &total_offset ); + return obs_block_iget_value( obs_block , total_index - total_offset ); +} + + +double obs_data_iget_std( const obs_data_type * obs_data , int total_index ) { + int total_offset; + const obs_block_type * obs_block = obs_data_lookup_block( obs_data , total_index , &total_offset ); + return obs_block_iget_std( obs_block , total_index - total_offset ); +} + + +const bool_vector_type * obs_data_get_active_mask( const obs_data_type * obs_data ) { + int total_size = obs_data_get_total_size( obs_data ); + bool_vector_resize(obs_data->mask, total_size, false); //too account for extra data blocks added/removed + + int offset = 0; + for (int block_nr = 0; block_nr < vector_get_size( obs_data->data ); block_nr++) { + const obs_block_type * obs_block = (const obs_block_type *)vector_iget_const( obs_data->data , block_nr ); + obs_block_set_active_mask( obs_block, obs_data->mask, &offset); + } + + return obs_data->mask; +} diff --git a/libres/lib/enkf/obs_vector.cpp b/libres/lib/enkf/obs_vector.cpp new file mode 100644 index 00000000000..8c9192ecab9 --- /dev/null +++ b/libres/lib/enkf/obs_vector.cpp @@ -0,0 +1,981 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'obs_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. +*/ + +/** + See the overview documentation of the observation system in enkf_obs.c +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include +#include +#include +#include + +#define OBS_VECTOR_TYPE_ID 120086 + +struct obs_vector_struct { + UTIL_TYPE_ID_DECLARATION; + obs_free_ftype *freef; /* Function used to free an observation node. */ + obs_get_ftype *get_obs; /* Function used to build the 'd' vector. */ + obs_meas_ftype *measure; /* Function used to measure on the state, and add to to the S matrix. */ + obs_user_get_ftype *user_get; /* Function to get an observation based on KEY:INDEX input from user.*/ + obs_chi2_ftype *chi2; /* Function to evaluate chi-squared for an observation. */ + obs_update_std_scale_ftype *update_std_scale; /* Function to scale the standard deviation with a given factor */ + + vector_type * nodes; + char * obs_key; /* The key this observation vector has in the enkf_obs layer. */ + enkf_config_node_type * config_node; /* The config_node of the node type we are observing - shared reference */ + obs_impl_type obs_type; + int num_active; /* The total number of timesteps where this observation is active (i.e. nodes[ ] != NULL) */ + int_vector_type * step_list; +}; + + +UTIL_IS_INSTANCE_FUNCTION(obs_vector , OBS_VECTOR_TYPE_ID) +UTIL_SAFE_CAST_FUNCTION(obs_vector , OBS_VECTOR_TYPE_ID) + +/*****************************************************************/ + + +static void obs_vector_prefer_RESTART_warning() { + fprintf(stderr," -------------------------------------------------------------------------------\n"); + fprintf(stderr," Warning: For GEN_OBS observations it is highly recommended to use the RESTART \n"); + fprintf(stderr," keyword to denote the time of the observation. The RESTART value \n"); + fprintf(stderr," should be matched with the report step embedded as part of the \n"); + fprintf(stderr," GEN_DATA result file created by the forward model. \n"); + fprintf(stderr,"\n"); + fprintf(stderr," In the future use OF DATE and DAYS will not be possible for GEN_OBS \n"); + fprintf(stderr," -------------------------------------------------------------------------------\n"); + fprintf(stderr,"\n"); + fprintf(stderr,"\n"); +} + + + +static int __conf_instance_get_restart_nr(const conf_instance_type * conf_instance, const char * obs_key , time_map_type * time_map , bool prefer_restart) { + int obs_restart_nr = -1; /* To shut up compiler warning. */ + + if(conf_instance_has_item(conf_instance, "RESTART")) { + obs_restart_nr = conf_instance_get_item_value_int(conf_instance, "RESTART"); + if (obs_restart_nr > time_map_get_last_step( time_map)) + util_abort("%s: Observation %s occurs at restart %i, but history file has only %i restarts.\n", __func__, obs_key, obs_restart_nr, time_map_get_last_step( time_map )); + } else { + time_t obs_time = time_map_get_start_time( time_map ); + + if(conf_instance_has_item(conf_instance, "DATE")) { + obs_time = conf_instance_get_item_value_time_t(conf_instance, "DATE" ); + if (prefer_restart) + obs_vector_prefer_RESTART_warning(); + } else if (conf_instance_has_item(conf_instance, "DAYS")) { + double days = conf_instance_get_item_value_double(conf_instance, "DAYS"); + util_inplace_forward_days_utc( &obs_time , days ); + if (prefer_restart) + obs_vector_prefer_RESTART_warning(); + } else if (conf_instance_has_item(conf_instance, "HOURS")) { + double hours = conf_instance_get_item_value_double(conf_instance, "HOURS"); + util_inplace_forward_seconds_utc( &obs_time , hours * 3600 ); + if (prefer_restart) + obs_vector_prefer_RESTART_warning(); + } else + util_abort("%s: Internal error. Invalid conf_instance?\n", __func__); + + obs_restart_nr = time_map_lookup_time_with_tolerance( time_map , obs_time , 30 , 30 ); + if (obs_restart_nr < 0) { + if (conf_instance_has_item(conf_instance, "DATE")) + printf("** ERROR: Could not determine REPORT step corresponding to DATE=%s\n", conf_instance_get_item_value_ref(conf_instance, "DATE")); + + if (conf_instance_has_item(conf_instance, "DAYS")) + printf("** ERROR: Could not determine REPORT step corresponding to DAYS=%s\n", conf_instance_get_item_value_ref(conf_instance, "DAYS")); + } + } + if (obs_restart_nr < 0) + util_abort("%s: Failed to look up restart nr correctly \n",__func__); + + return obs_restart_nr; +} + + + +/*****************************************************************/ + + +static void obs_vector_resize(obs_vector_type * vector , int new_size) { + int current_size = vector_get_size( vector->nodes ); + int i; + + for (i=current_size; i < new_size; i++) + vector_append_ref( vector->nodes , NULL); + +} + + +obs_vector_type * obs_vector_alloc(obs_impl_type obs_type , const char * obs_key , enkf_config_node_type * config_node, int num_reports) { + obs_vector_type * vector = (obs_vector_type *)util_malloc(sizeof * vector ); + + UTIL_TYPE_ID_INIT( vector , OBS_VECTOR_TYPE_ID); + vector->freef = NULL; + vector->measure = NULL; + vector->get_obs = NULL; + vector->user_get = NULL; + vector->chi2 = NULL; + vector->update_std_scale = NULL; + vector->step_list = int_vector_alloc(0,0); + + switch (obs_type) { + case(SUMMARY_OBS): + vector->freef = summary_obs_free__; + vector->measure = summary_obs_measure__; + vector->get_obs = summary_obs_get_observations__; + vector->user_get = summary_obs_user_get__; + vector->chi2 = summary_obs_chi2__; + vector->update_std_scale = summary_obs_update_std_scale__; + break; + case(BLOCK_OBS): + vector->freef = block_obs_free__; + vector->measure = block_obs_measure__; + vector->get_obs = block_obs_get_observations__; + vector->user_get = block_obs_user_get__; + vector->chi2 = block_obs_chi2__; + vector->update_std_scale = block_obs_update_std_scale__; + break; + case(GEN_OBS): + vector->freef = gen_obs_free__; + vector->measure = gen_obs_measure__; + vector->get_obs = gen_obs_get_observations__; + vector->user_get = gen_obs_user_get__; + vector->chi2 = gen_obs_chi2__; + vector->update_std_scale = gen_obs_update_std_scale__; + break; + default: + util_abort("%s: internal error - obs_type:%d not recognized \n",__func__ , obs_type); + } + + vector->obs_type = obs_type; + vector->config_node = config_node; + vector->obs_key = util_alloc_string_copy( obs_key ); + vector->num_active = 0; + vector->nodes = vector_alloc_new(); + obs_vector_resize(vector , num_reports + 1); /* +1 here ?? Ohh - these +/- problems. */ + + return vector; +} + +obs_impl_type obs_vector_get_impl_type(const obs_vector_type * obs_vector) { + return obs_vector->obs_type; +} + + +/** + This is the key for the enkf_node which this observation is + 'looking at'. I.e. if this observation is an RFT pressure + measurement, this function will return "PRESSURE". +*/ + +const char * obs_vector_get_state_kw(const obs_vector_type * obs_vector) { + return enkf_config_node_get_key( obs_vector->config_node ); +} + + +const char * obs_vector_get_key(const obs_vector_type * obs_vector) { + return obs_vector->obs_key; +} + + +enkf_config_node_type * obs_vector_get_config_node(const obs_vector_type * obs_vector) { + return obs_vector->config_node; +} + + + +void obs_vector_free(obs_vector_type * obs_vector) { + vector_free( obs_vector->nodes ); + free(obs_vector->obs_key); + int_vector_free(obs_vector->step_list); + free(obs_vector); +} + + +static void obs_vector_assert_node_type( const obs_vector_type * obs_vector , const void * node ) { + bool type_OK; + switch (obs_vector->obs_type) { + case(SUMMARY_OBS): + type_OK = summary_obs_is_instance( node ); + break; + case(BLOCK_OBS): + type_OK = block_obs_is_instance( node ); + break; + case(GEN_OBS): + type_OK = gen_obs_is_instance( node ); + break; + default: + util_abort("%s: Error in type check: \n",__func__); + type_OK = false; + } + if (!type_OK) + util_abort("%s: Type mismatch when trying to add observation node to observation vector \n",__func__); +} + + +void obs_vector_install_node(obs_vector_type * obs_vector , int index , void * node) { + obs_vector_assert_node_type( obs_vector , node ); + { + if (vector_iget_const( obs_vector->nodes , index ) == NULL) { + obs_vector->num_active++; + int_vector_append( obs_vector->step_list , index ); + int_vector_sort( obs_vector->step_list ); + } + + vector_iset_owned_ref( obs_vector->nodes , index , node , obs_vector->freef ); + } +} + +/** + Observe that @summary_key is the key used to look up the + corresponding simulated value in the ensemble, and not the + observation key - the two can be different. +*/ + +static void obs_vector_add_summary_obs( obs_vector_type * obs_vector, + int obs_index, + const char * summary_key, + const char * obs_key, + double value, + double std) { + summary_obs_type * summary_obs = summary_obs_alloc( summary_key , obs_key , value , std); + obs_vector_install_node( obs_vector , obs_index , summary_obs ); +} + + +/*****************************************************************/ + +int obs_vector_get_num_active(const obs_vector_type * vector) { + return vector->num_active; +} + + +const int_vector_type * obs_vector_get_step_list(const obs_vector_type * vector) { + return vector->step_list; +} + + +bool obs_vector_iget_active(const obs_vector_type * vector, int index) { + /* We accept this ... */ + if (index >= vector_get_size( vector->nodes )) + return false; + + { + void * obs_data = (void *)vector_iget( vector->nodes , index ); + if (obs_data != NULL) + return true; + else + return false; + } +} + + +/* + Will happily return NULL if index is not active. +*/ +void * obs_vector_iget_node(const obs_vector_type * vector, int index) { + return vector_iget( vector->nodes , index ); // CXX_CAST_ERROR +} + + + + +void obs_vector_user_get(const obs_vector_type * obs_vector , const char * index_key , int report_step , double * value , double * std , bool * valid) { + void * obs_node = obs_vector_iget_node( obs_vector , report_step ); + obs_vector->user_get(obs_node , index_key , value , std , valid); +} + +/* + This function returns the next active (i.e. node != NULL) report + step, starting with 'prev_step + 1'. If no more active steps are + found, it will return -1. +*/ + +int obs_vector_get_next_active_step(const obs_vector_type * obs_vector , int prev_step) { + if (prev_step >= (vector_get_size(obs_vector->nodes) - 1)) + return -1; + else { + int size = vector_get_size( obs_vector->nodes ); + int next_step = prev_step + 1; + while (( next_step < size) && (obs_vector_iget_node(obs_vector , next_step) == NULL)) + next_step++; + + if (next_step == size) + return -1; /* No more active steps. */ + else + return next_step; + } +} + + +/*****************************************************************/ +/** + All the obs_vector_load_from_XXXX() functions can safely return + NULL, in which case no observation is added to enkf_obs observation + hash table. +*/ + + +void obs_vector_load_from_SUMMARY_OBSERVATION(obs_vector_type * obs_vector , const conf_instance_type * conf_instance , time_map_type * obs_time , ensemble_config_type * ensemble_config) { + if(!conf_instance_is_of_class(conf_instance, "SUMMARY_OBSERVATION")) + util_abort("%s: internal error. expected \"SUMMARY_OBSERVATION\" instance, got \"%s\".\n", + __func__, conf_instance_get_class_name_ref(conf_instance) ); + + { + double obs_value = conf_instance_get_item_value_double(conf_instance, "VALUE" ); + double obs_error = conf_instance_get_item_value_double(conf_instance, "ERROR" ); + double min_error = conf_instance_get_item_value_double(conf_instance, "ERROR_MIN"); + const char * error_mode = conf_instance_get_item_value_ref( conf_instance, "ERROR_MODE"); + const char * sum_key = conf_instance_get_item_value_ref( conf_instance, "KEY" ); + const char * obs_key = conf_instance_get_name_ref(conf_instance); + int obs_restart_nr = __conf_instance_get_restart_nr(conf_instance , obs_key , obs_time , false); + + if (obs_restart_nr == 0) { + int day,month,year; + time_t start_time = time_map_iget( obs_time , 0 ); + util_set_date_values_utc( start_time , &day , &month , &year); + + fprintf(stderr,"** ERROR: It is unfortunately not possible to use summary observations from the\n"); + fprintf(stderr," start of the simulation. Problem with observation:%s at %02d/%02d/%4d\n",obs_key , day,month,year); + exit(1); + } + { + if (strcmp( error_mode , "REL") == 0) + obs_error *= obs_value; + else if (strcmp( error_mode , "RELMIN") == 0) + obs_error = util_double_max( min_error , obs_error * obs_value ); + + obs_vector_add_summary_obs( obs_vector , obs_restart_nr , sum_key , obs_key , obs_value , obs_error); + } + } +} + + + + +obs_vector_type * obs_vector_alloc_from_GENERAL_OBSERVATION(const conf_instance_type * conf_instance , time_map_type * obs_time , const ensemble_config_type * ensemble_config) { + if(!conf_instance_is_of_class(conf_instance, "GENERAL_OBSERVATION")) + util_abort("%s: internal error. expected \"GENERAL_OBSERVATION\" instance, got \"%s\".\n", + __func__, conf_instance_get_class_name_ref(conf_instance) ); + const char * obs_key = conf_instance_get_name_ref(conf_instance); + const char * state_kw = conf_instance_get_item_value_ref( conf_instance, "DATA" ); + if (ensemble_config_has_key( ensemble_config , state_kw )) { + const char * obs_key = conf_instance_get_name_ref(conf_instance); + int obs_restart_nr = __conf_instance_get_restart_nr(conf_instance , obs_key , obs_time , true); + const char * index_file = NULL; + const char * index_list = NULL; + const char * obs_file = NULL; + const char * error_covar_file = NULL; + + if (conf_instance_has_item(conf_instance , "INDEX_FILE")) + index_file = conf_instance_get_item_value_ref( conf_instance, "INDEX_FILE" ); + + if (conf_instance_has_item(conf_instance , "INDEX_LIST")) + index_list = conf_instance_get_item_value_ref( conf_instance, "INDEX_LIST" ); + + if (conf_instance_has_item(conf_instance , "OBS_FILE")) + obs_file = conf_instance_get_item_value_ref( conf_instance, "OBS_FILE" ); + + if (conf_instance_has_item(conf_instance , "ERROR_COVAR")) + error_covar_file = conf_instance_get_item_value_ref( conf_instance, "ERROR_COVAR" ); + + { + obs_vector_type * obs_vector = NULL; + const enkf_config_node_type * config_node = ensemble_config_get_node( ensemble_config , state_kw); + + if (enkf_config_node_get_impl_type(config_node) == GEN_DATA) { + double scalar_error = -1; + double scalar_value = -1; + gen_obs_type * gen_obs ; + const gen_data_config_type * config = (const gen_data_config_type *)enkf_config_node_get_ref( config_node ); + + if (gen_data_config_has_report_step( config , obs_restart_nr)) { + obs_vector = obs_vector_alloc( GEN_OBS , obs_key , ensemble_config_get_node(ensemble_config , state_kw ), time_map_get_last_step( obs_time )); + if (conf_instance_has_item(conf_instance , "VALUE")) { + scalar_value = conf_instance_get_item_value_double(conf_instance , "VALUE"); + scalar_error = conf_instance_get_item_value_double(conf_instance , "ERROR"); + } + + /** The config system has ensured that we have either OBS_FILE or (VALUE and ERROR). */ + gen_obs = gen_obs_alloc( (const gen_data_config_type * ) enkf_config_node_get_ref( config_node ) , + obs_key , + obs_file , + scalar_value , + scalar_error , + index_file , + index_list , + error_covar_file); + obs_vector_install_node( obs_vector , obs_restart_nr , gen_obs ); + } else + fprintf(stderr,"** ERROR: The GEN_DATA node:%s is not configured to load from report step:%d - the observation:%s will be ignored\n", state_kw , obs_restart_nr , obs_key); + } else { + ert_impl_type impl_type = enkf_config_node_get_impl_type(config_node); + fprintf(stderr,"** ERROR: %s: %s has implementation type:\'%s\' - expected:\'%s\' - observation:%s ignored.\n", + __func__ , state_kw , enkf_types_get_impl_name(impl_type) , enkf_types_get_impl_name(GEN_DATA) , obs_key); + } + return obs_vector; + + } + } else { + fprintf(stderr,"** Warning the ensemble key:%s does not exist - observation:%s not added \n", state_kw , obs_key); + return NULL; + } +} + + + +// Should check the refcase for key - if it is != NULL. + +bool obs_vector_load_from_HISTORY_OBSERVATION(obs_vector_type * obs_vector , + const conf_instance_type * conf_instance , + time_map_type * obs_time , + const history_type * history , + ensemble_config_type * ensemble_config, + double std_cutoff ) { + + if(!conf_instance_is_of_class(conf_instance, "HISTORY_OBSERVATION")) + util_abort("%s: internal error. expected \"HISTORY_OBSERVATION\" instance, got \"%s\".\n",__func__, conf_instance_get_class_name_ref(conf_instance) ); + + { + bool initOK = false; + int size , restart_nr; + double_vector_type * value = double_vector_alloc(0,0); + double_vector_type * std = double_vector_alloc(0,0); + bool_vector_type * valid = bool_vector_alloc(0 , false); + + double error = conf_instance_get_item_value_double(conf_instance, "ERROR" ); + double error_min = conf_instance_get_item_value_double(conf_instance, "ERROR_MIN" ); + const char * error_mode = conf_instance_get_item_value_ref( conf_instance, "ERROR_MODE"); + const char * sum_key = conf_instance_get_name_ref( conf_instance ); + + + // Get time series data from history object and allocate + size = time_map_get_last_step( obs_time ); + if (history_init_ts( history , sum_key , value , valid )) { + + // Create the standard deviation vector + if(strcmp(error_mode, "ABS") == 0) { + for( restart_nr = 0; restart_nr < size; restart_nr++) + double_vector_iset( std , restart_nr , error ); + } else if(strcmp(error_mode, "REL") == 0) { + for( restart_nr = 0; restart_nr < size; restart_nr++) + double_vector_iset( std , restart_nr , error * std::abs( double_vector_iget( value , restart_nr ))); + } else if(strcmp(error_mode, "RELMIN") == 0) { + for(restart_nr = 0; restart_nr < size; restart_nr++) { + double tmp_std = util_double_max( error_min , error * std::abs( double_vector_iget( value , restart_nr ))); + double_vector_iset( std , restart_nr , tmp_std); + } + } else + util_abort("%s: Internal error. Unknown error mode \"%s\"\n", __func__, error_mode); + + + // Handle SEGMENTs which can be used to customize the observation error. */ + { + stringlist_type * segment_keys = conf_instance_alloc_list_of_sub_instances_of_class_by_name(conf_instance, "SEGMENT"); + stringlist_sort( segment_keys , NULL ); + + int num_segments = stringlist_get_size(segment_keys); + + for(int segment_nr = 0; segment_nr < num_segments; segment_nr++) + { + const char * segment_name = stringlist_iget(segment_keys, segment_nr); + const conf_instance_type * segment_conf = conf_instance_get_sub_instance_ref(conf_instance, segment_name); + + int start = conf_instance_get_item_value_int( segment_conf, "START" ); + int stop = conf_instance_get_item_value_int( segment_conf, "STOP" ); + double error_segment = conf_instance_get_item_value_double(segment_conf, "ERROR" ); + double error_min_segment = conf_instance_get_item_value_double(segment_conf, "ERROR_MIN" ); + const char * error_mode_segment = conf_instance_get_item_value_ref( segment_conf, "ERROR_MODE"); + + if(start < 0) + { + printf("%s: WARNING - Segment out of bounds. Truncating start of segment to 0.\n", __func__); + start = 0; + } + + if(stop >= size) + { + printf("%s: WARNING - Segment out of bounds. Truncating end of segment to %d.\n", __func__, size - 1); + stop = size -1; + } + + if(start > stop) + { + printf("%s: WARNING - Segment start after stop. Truncating end of segment to %d.\n", __func__, start ); + stop = start; + } + + // Create the standard deviation vector + if(strcmp(error_mode_segment, "ABS") == 0) { + for( restart_nr = start; restart_nr <= stop; restart_nr++) + double_vector_iset( std , restart_nr , error_segment) ; + } else if(strcmp(error_mode_segment, "REL") == 0) { + for( restart_nr = start; restart_nr <= stop; restart_nr++) + double_vector_iset( std , restart_nr , error_segment * std::abs(double_vector_iget( value , restart_nr))); + } else if(strcmp(error_mode_segment, "RELMIN") == 0) { + for(restart_nr = start; restart_nr <= stop ; restart_nr++) { + double tmp_std = util_double_max( error_min_segment , error_segment * std::abs( double_vector_iget( value , restart_nr ))); + double_vector_iset( std , restart_nr , tmp_std); + } + } else + util_abort("%s: Internal error. Unknown error mode \"%s\"\n", __func__, error_mode); + } + stringlist_free(segment_keys); + } + + + /* + This is where the summary observations are finally added. + */ + for (restart_nr = 0; restart_nr < size; restart_nr++) { + if (bool_vector_safe_iget( valid , restart_nr)) { + if (double_vector_iget( std , restart_nr) > std_cutoff) { + obs_vector_add_summary_obs( obs_vector , restart_nr , sum_key , sum_key , + double_vector_iget( value ,restart_nr) , double_vector_iget( std , restart_nr )); + } else + fprintf(stderr,"** Warning: to small observation error in observation %s:%d - ignored. \n", sum_key , restart_nr); + } + } + initOK = true; + } + double_vector_free(std); + double_vector_free(value); + bool_vector_free(valid); + return initOK; + } +} + +void obs_vector_scale_std(obs_vector_type * obs_vector, const local_obsdata_node_type * local_node , double std_multiplier) { + const active_list_type * active_list = local_obsdata_node_get_active_list( local_node ); + int tstep = -1; + + while (true) { + tstep = obs_vector_get_next_active_step( obs_vector , tstep ); + if (tstep < 0) + break; + + if (local_obsdata_node_tstep_active(local_node, tstep)) { + void * observation = obs_vector_iget_node(obs_vector, tstep); + if (observation) + obs_vector->update_std_scale(observation, std_multiplier , active_list); + } + } + +} + + +static const char * __summary_kw( const char * field_name ) { + if (strcmp( field_name , "PRESSURE") == 0) + return "BPR"; + else if (strcmp( field_name , "SWAT") == 0) + return "BSWAT"; + else if (strcmp( field_name , "SGAS") == 0) + return "BSGAS"; + else { + util_abort("%s: sorry - could not \'translate\' field:%s to block summary variable\n",__func__ , field_name); + return NULL; + } +} + + +obs_vector_type * obs_vector_alloc_from_BLOCK_OBSERVATION(const conf_instance_type * conf_instance , + const ecl_grid_type * grid , + time_map_type * obs_time , + const ecl_sum_type * refcase , + ensemble_config_type * ensemble_config) { + + if(!conf_instance_is_of_class(conf_instance, "BLOCK_OBSERVATION")) + util_abort("%s: internal error. expected \"BLOCK_OBSERVATION\" instance, got \"%s\".\n", + __func__, conf_instance_get_class_name_ref(conf_instance) ); + + block_obs_source_type source_type = SOURCE_SUMMARY; + const char * obs_label = conf_instance_get_name_ref(conf_instance); + const char * source_string = conf_instance_get_item_value_ref(conf_instance , "SOURCE"); + const char * field_name = conf_instance_get_item_value_ref(conf_instance , "FIELD"); + const char * sum_kw = NULL; + bool OK = true; + + if (strcmp(source_string , "FIELD") == 0) { + source_type = SOURCE_FIELD; + if (!ensemble_config_has_key( ensemble_config , field_name)) { + OK = false; + fprintf(stderr,"** Warning the ensemble key:%s does not exist - observation:%s not added \n", field_name , obs_label); + } + } else if (strcmp( source_string , "SUMMARY") == 0) { + source_type = SOURCE_SUMMARY; + sum_kw = __summary_kw( field_name ); + } else + util_abort("%s: internal error \n",__func__); + + if (OK) { + obs_vector_type * obs_vector = NULL; + int size = time_map_get_last_step( obs_time ); + int obs_restart_nr ; + + stringlist_type * summary_keys = stringlist_alloc_new(); + stringlist_type * obs_pt_keys = conf_instance_alloc_list_of_sub_instances_of_class_by_name(conf_instance, "OBS"); + int num_obs_pts = stringlist_get_size(obs_pt_keys); + + double * obs_value = (double *)util_calloc(num_obs_pts , sizeof * obs_value); + double * obs_std = (double *)util_calloc(num_obs_pts , sizeof * obs_std ); + int * obs_i = (int *)util_calloc(num_obs_pts , sizeof * obs_i ); + int * obs_j = (int *)util_calloc(num_obs_pts , sizeof * obs_j ); + int * obs_k = (int *)util_calloc(num_obs_pts , sizeof * obs_k ); + + obs_restart_nr = __conf_instance_get_restart_nr(conf_instance , obs_label , obs_time , false); + + /** Build the observation. */ + for(int obs_pt_nr = 0; obs_pt_nr < num_obs_pts; obs_pt_nr++) { + const char * obs_key = stringlist_iget(obs_pt_keys, obs_pt_nr); + const conf_instance_type * obs_instance = conf_instance_get_sub_instance_ref(conf_instance, obs_key); + const char * error_mode = conf_instance_get_item_value_ref(obs_instance, "ERROR_MODE"); + double error = conf_instance_get_item_value_double(obs_instance, "ERROR"); + double value = conf_instance_get_item_value_double(obs_instance, "VALUE"); + double min_error = conf_instance_get_item_value_double(obs_instance, "ERROR_MIN"); + + if (strcmp( error_mode , "REL") == 0) + error *= value; + else if (strcmp( error_mode , "RELMIN") == 0) + error = util_double_max( error * value , min_error ); + + obs_value[obs_pt_nr] = value; + obs_std [obs_pt_nr] = error; + + /** + The input values i,j,k come from the user, and are offset 1. They + are immediately shifted with -1 to become C-based offset zero. + */ + obs_i[obs_pt_nr] = conf_instance_get_item_value_int( obs_instance, "I") - 1; + obs_j[obs_pt_nr] = conf_instance_get_item_value_int( obs_instance, "J") - 1; + obs_k[obs_pt_nr] = conf_instance_get_item_value_int( obs_instance, "K") - 1; + + if (source_type == SOURCE_SUMMARY) { + char * summary_key = smspec_alloc_block_ijk_key( SUMMARY_KEY_JOIN_STRING , sum_kw , + obs_i[obs_pt_nr] + 1 , + obs_j[obs_pt_nr] + 1 , + obs_k[obs_pt_nr] + 1 ); + + stringlist_append_copy( summary_keys , summary_key ); + free(summary_key); + } + } + + + if (source_type == SOURCE_FIELD) { + const enkf_config_node_type * config_node = ensemble_config_get_node( ensemble_config , field_name); + const field_config_type * field_config = (const field_config_type *)enkf_config_node_get_ref( config_node ); + block_obs_type * block_obs = block_obs_alloc_complete(obs_label, source_type , NULL , field_config , grid , num_obs_pts, obs_i, obs_j, obs_k, obs_value, obs_std); + + if (block_obs != NULL) { + obs_vector = obs_vector_alloc( BLOCK_OBS , obs_label , ensemble_config_get_node(ensemble_config , field_name), size ); + obs_vector_install_node( obs_vector , obs_restart_nr , block_obs); + } + } else if (source_type == SOURCE_SUMMARY) { + OK = true; + if (refcase != NULL) { + for (int i=0; i < stringlist_get_size( summary_keys ); i++) { + const char * sum_key = stringlist_iget( summary_keys , i ); + if (!ecl_sum_has_key(refcase , sum_key)) { + /* + If the + */ + fprintf(stderr,"** Warning missing summary %s for cell: (%d,%d,%d) in refcase - make sure that \"BPR %d %d %d\" is included in ECLIPSE summary specification \n" , + sum_key , obs_i[i]+1 , obs_j[i]+1 , obs_k[i]+1 , obs_i[i]+1 , obs_j[i]+1 , obs_k[i]+1 ); + //OK = false; + } + } + } + if (OK) { + // We can create the container node and add the summary nodes. + enkf_config_node_type * container_config = ensemble_config_add_container( ensemble_config , NULL ); + + for (int i=0; i < stringlist_get_size( summary_keys ); i++) { + const char * sum_key = stringlist_iget( summary_keys , i ); + enkf_config_node_type * child_node = ensemble_config_add_summary_observation( ensemble_config , sum_key , LOAD_FAIL_WARN ); + enkf_config_node_update_container( container_config , child_node ); + } + + { + block_obs_type * block_obs = block_obs_alloc_complete(obs_label, source_type , summary_keys , enkf_config_node_get_ref(container_config) , // CXX_CAST_ERROR + grid , num_obs_pts, obs_i, obs_j, obs_k, obs_value, obs_std); + if (block_obs != NULL) { + obs_vector = obs_vector_alloc( BLOCK_OBS , obs_label , container_config, size ); + obs_vector_install_node( obs_vector , obs_restart_nr , block_obs); + } + } + } + } else + util_abort("%s: invalid source value \n",__func__); + + free(obs_value); + free(obs_std); + free(obs_i); + free(obs_j); + free(obs_k); + stringlist_free(obs_pt_keys); + stringlist_free(summary_keys); + + return obs_vector; + } else { + fprintf(stderr,"** Warning the ensemble key:%s does not exist - observation:%s not added \n", field_name , obs_label); + return NULL; + } +} +/*****************************************************************/ + +void obs_vector_iget_observations(const obs_vector_type * obs_vector, int report_step , obs_data_type * obs_data, const active_list_type * active_list, enkf_fs_type * fs) { + void * obs_node = (void *)vector_iget( obs_vector->nodes , report_step ); + if ( obs_node != NULL) + obs_vector->get_obs(obs_node , obs_data , fs, report_step , active_list); +} + + +void obs_vector_measure(const obs_vector_type * obs_vector , + enkf_fs_type * fs , + int report_step , + const int_vector_type * ens_active_list , + meas_data_type * meas_data , + const active_list_type * active_list) { + + void * obs_node = (void *)vector_iget( obs_vector->nodes , report_step ); + if ( obs_node != NULL ) { + enkf_node_type * enkf_node = enkf_node_deep_alloc( obs_vector->config_node ); + + node_id_type node_id = { .report_step = report_step , + .iens = 0 }; + + int vec_size = int_vector_size( ens_active_list ); + for (int active_iens_index = 0; active_iens_index < vec_size; active_iens_index++) { + node_id.iens = int_vector_iget( ens_active_list , active_iens_index ); + + enkf_node_load(enkf_node , fs , node_id); + obs_vector->measure(obs_node , enkf_node_value_ptr(enkf_node) , node_id , meas_data , active_list); + } + + enkf_node_free( enkf_node ); + } +} + + +static bool obs_vector_has_data_at_report_step( const obs_vector_type * obs_vector , const bool_vector_type * active_mask , enkf_fs_type * fs, int report_step) { + void * obs_node = (void *)vector_iget( obs_vector->nodes , report_step ); + if ( obs_node ) { + node_id_type node_id = {.report_step = report_step }; + for (int iens = 0; iens < bool_vector_size( active_mask ); iens++) { + if (bool_vector_iget( active_mask , iens)) { + node_id.iens = iens; + if (! enkf_config_node_has_node(obs_vector->config_node , fs , node_id )) + return false; + } + } + } + + /* + Will return true unconditionally if we do not have observation data at this report step; + or alternatively if the active_mask is all false. + */ + return true; +} + + +/* + The has_vector_data() function will only check that we have a vector + stored, and not the actual length of the vector. This means we can + be fooled if the stored vector is shorter than what the observation + requires. + + Should ideally check that the vector is long enough, but that + requires changes in the enkf_node api for vector storage. +*/ + +static bool obs_vector_has_vector_data( const obs_vector_type * obs_vector , const bool_vector_type * active_mask , enkf_fs_type * fs) { + int vec_size = bool_vector_size( active_mask ); + + for (int iens = 0; iens < vec_size; iens++) { + const enkf_config_node_type * data_config = obs_vector->config_node; + if (bool_vector_iget( active_mask , iens )) { + if (!enkf_config_node_has_vector(data_config , fs , iens)) { + return false; + } + } + } + + return true; +} + + + +bool obs_vector_has_data( const obs_vector_type * obs_vector , const bool_vector_type * active_mask , enkf_fs_type * fs) { + const enkf_config_node_type * data_config = obs_vector->config_node; + if (enkf_config_node_vector_storage( data_config )) + return obs_vector_has_vector_data( obs_vector , active_mask , fs ); + + int vec_size = vector_get_size( obs_vector->nodes ); + for (int report_step = 0; report_step < vec_size; report_step++) { + if (!obs_vector_has_data_at_report_step( obs_vector , active_mask , fs, report_step)) + return false; + } + return true; +} + + + +/*****************************************************************/ +/** Here comes many different functions for misfit calculations. */ + +/** + This is the lowest level function: + + * It is checked that the obs_vector is active for the actual report + step; if it is not active 0.0 is returned without any further + ado. + + * It is assumed the enkf_node_instance contains valid data for this + report_step. This is not checked in this function, and is the + responsability of the calling scope. + + * The underlying chi2 function will do a type-check of node - and + fail hard if it is not correct. + +*/ + + +static double obs_vector_chi2__(const obs_vector_type * obs_vector , int report_step , const enkf_node_type * node, node_id_type node_id) { + void * obs_node = (void *)vector_iget( obs_vector->nodes , report_step ); + + if (obs_node) + return obs_vector->chi2( obs_node , enkf_node_value_ptr( node ), node_id); + else + return 0.0; /* Observation not active for this report step. */ + +} + + +/** + This function will evaluate the chi2 for the ensemble members + [iens1,iens2) and report steps [step1,step2). + + Observe that the chi2 pointer is assumed to be allocated for the + complete ensemble, altough this function only operates on part of + it. +*/ + + +//This will not work for container observations ..... + +void obs_vector_ensemble_chi2(const obs_vector_type * obs_vector , + enkf_fs_type * fs, + bool_vector_type * valid , + int step1 , + int step2 , + int iens1 , + int iens2 , + double ** chi2) { + + int step; + enkf_node_type * enkf_node = enkf_node_alloc( obs_vector->config_node ); + node_id_type node_id; + for (step = step1; step <= step2; step++) { + int iens; + node_id.report_step = step; + { + void * obs_node = (void *)vector_iget( obs_vector->nodes , step); + + if (obs_node == NULL) { + for (iens = iens1; iens < iens2; iens++) + chi2[step][iens] = 0; + } else { + for (iens = iens1; iens < iens2; iens++) { + node_id.iens = iens; + if (enkf_node_try_load( enkf_node , fs , node_id)) + chi2[step][iens] = obs_vector_chi2__(obs_vector , step , enkf_node , node_id); + else { + chi2[step][iens] = 0; + // Missing data - this member will be marked as invalid in the misfit calculations. + bool_vector_iset( valid , iens , false ); + } + } + } + } + } + enkf_node_free( enkf_node ); +} + + + +/** + This function will evaluate the total chi2 for one ensemble member + (i.e. sum over report steps). +*/ + + +double obs_vector_total_chi2(const obs_vector_type * obs_vector , enkf_fs_type * fs , int iens) { + double sum_chi2 = 0; + enkf_node_type * enkf_node = enkf_node_deep_alloc( obs_vector->config_node ); + node_id_type node_id = {.report_step = 0, .iens = iens }; + + int vec_size = vector_get_size( obs_vector->nodes ); + for (int report_step = 0; report_step < vec_size; report_step++) { + if (vector_iget(obs_vector->nodes , report_step) != NULL) { + node_id.report_step = report_step; + + if (enkf_node_try_load( enkf_node , fs , node_id)) + sum_chi2 += obs_vector_chi2__(obs_vector , report_step , enkf_node, node_id); + + } + } + enkf_node_free( enkf_node ); + return sum_chi2; +} + + +const char * obs_vector_get_obs_key( const obs_vector_type * obs_vector) { + return obs_vector->obs_key; +} + + +local_obsdata_node_type * obs_vector_alloc_local_node(const obs_vector_type * obs_vector) { + local_obsdata_node_type * obs_node = local_obsdata_node_alloc( obs_vector->obs_key , false ); + local_obsdata_node_reset_tstep_list(obs_node, obs_vector->step_list ); + return obs_node; +} + + +/*****************************************************************/ + + +VOID_FREE(obs_vector) diff --git a/libres/lib/enkf/queue_config.cpp b/libres/lib/enkf/queue_config.cpp new file mode 100644 index 00000000000..0222122606f --- /dev/null +++ b/libres/lib/enkf/queue_config.cpp @@ -0,0 +1,377 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'queue_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 +#include + +#include + +#include +#include +#include +#include + +struct queue_config_struct { + job_driver_type driver_type; + char * job_script; + hash_type * queue_drivers; + bool user_mode; + int max_submit; + int num_cpu; +}; + +static void queue_config_add_queue_driver(queue_config_type * queue_config, const char * driver_name, queue_driver_type * driver); +static bool queue_config_init(queue_config_type * queue_config, const config_content_type * config_content); + +static queue_config_type * queue_config_alloc_empty() { + queue_config_type * queue_config = (queue_config_type *)util_malloc(sizeof * queue_config); + queue_config->queue_drivers = hash_alloc(); + queue_config->job_script = NULL; + queue_config->driver_type = NULL_DRIVER; + queue_config->user_mode = false; + queue_config->max_submit = 2; // Default value + queue_config->num_cpu = 0; + + return queue_config; +} + + +static queue_config_type * queue_config_alloc_default() { + queue_config_type * queue_config = queue_config_alloc_empty(); + + config_parser_type * config = config_alloc(); + config_content_type * content = site_config_alloc_content(config); + + queue_config_init(queue_config, content); + + config_free(config); + config_content_free(content); + + return queue_config; +} + + +queue_config_type * queue_config_alloc_load(const char * user_config_file) { + config_parser_type * config = config_alloc(); + + config_content_type * content = NULL; + + if(user_config_file) + content = model_config_alloc_content(user_config_file, config); + + queue_config_type * queue_config = queue_config_alloc(content); + + config_free(config); + config_content_free(content); + + return queue_config; +} + +queue_config_type * queue_config_alloc(const config_content_type * config_content) { + queue_config_type * queue_config = queue_config_alloc_default(); + + if(config_content) { + queue_config->user_mode = true; + queue_config_init(queue_config, config_content); + } + + return queue_config; +} + +queue_config_type * queue_config_alloc_full(char * job_script, + bool user_mode, + int max_submit, + int num_cpu, + job_driver_type driver_type) { + + queue_config_type * queue_config = (queue_config_type *)util_malloc(sizeof * queue_config); + queue_config->queue_drivers = hash_alloc(); + queue_config_create_queue_drivers(queue_config); + queue_config->job_script = util_alloc_string_copy(job_script); + queue_config->driver_type = static_cast(driver_type); + queue_config->user_mode = user_mode; + queue_config->max_submit = max_submit; + queue_config->num_cpu = num_cpu; + + return queue_config; +} + + +queue_config_type * queue_config_alloc_local_copy( queue_config_type * queue_config) { + queue_config_type * queue_config_copy = (queue_config_type *)util_malloc(sizeof * queue_config_copy); + queue_config_copy->queue_drivers = hash_alloc(); + queue_config_add_queue_driver(queue_config_copy, LOCAL_DRIVER_NAME, queue_driver_alloc_local()); + queue_config_copy->user_mode = queue_config->user_mode; + + if (queue_config_has_job_script(queue_config)) { + queue_config_copy->job_script = util_alloc_string_copy( queue_config->job_script ); + } + else + queue_config_copy->job_script = NULL; + + if (queue_config->driver_type == NULL_DRIVER) + queue_config_copy->driver_type = NULL_DRIVER; + else + queue_config_copy->driver_type = LOCAL_DRIVER; + + queue_config_copy->max_submit = queue_config->max_submit; + + return queue_config_copy; +} + + +/* + The filenames "OK", "status.json" and "ERROR" passed to the job_queue_alloc + function must be syncronized with the script running the forward model. +*/ +job_queue_type * queue_config_alloc_job_queue(const queue_config_type * queue_config) { + job_queue_type * job_queue = job_queue_alloc(DEFAULT_MAX_SUBMIT, "OK", "status.json", "ERROR"); + const char * driver_name = queue_config_get_queue_system(queue_config); + if (driver_name != NULL) { + queue_driver_type * driver = queue_config_get_queue_driver(queue_config, driver_name); + job_queue_set_driver(job_queue, driver); + } + + job_queue_set_max_submit(job_queue, queue_config->max_submit); + + return job_queue; +} + +void queue_config_free(queue_config_type * queue_config) { + hash_free(queue_config->queue_drivers); + free(queue_config->job_script); + free(queue_config); +} + +const char * queue_config_get_queue_system(const queue_config_type * queue_config) { + switch(queue_config->driver_type) { + case LSF_DRIVER: + return LSF_DRIVER_NAME; + case RSH_DRIVER: + return RSH_DRIVER_NAME; + case LOCAL_DRIVER: + return LOCAL_DRIVER_NAME; + case TORQUE_DRIVER: + return TORQUE_DRIVER_NAME; + case SLURM_DRIVER: + return SLURM_DRIVER_NAME; + default: + return NULL; + } + return NULL; +} + + +int queue_config_get_max_submit(queue_config_type * queue_config) { + return queue_config->max_submit; +} + +const char * queue_config_get_job_script(const queue_config_type * queue_config) { + return queue_config->job_script; +} + + +bool queue_config_has_job_script( const queue_config_type * queue_config ) { + if (queue_config->job_script) + return true; + else + return false; +} + +bool queue_config_set_job_script(queue_config_type * queue_config, const char * job_script) { + if (!util_is_executable(job_script)) + return false; + + char * job_script_full_path = util_alloc_realpath(job_script); + queue_config->job_script = util_realloc_string_copy(queue_config->job_script, job_script_full_path); + free(job_script_full_path); + return true; +} + + +static void queue_config_set_queue_option(queue_config_type * queue_config, const char * driver_name, const char * option_key, const char * option_value) { + if (queue_config_has_queue_driver(queue_config, driver_name)) { + queue_driver_type * driver = queue_config_get_queue_driver(queue_config, driver_name); + if (!queue_driver_set_option(driver, option_key, option_value)) + fprintf(stderr, "** Warning: Option:%s or its value is not recognized by driver:%s- ignored \n", option_key, driver_name); + } else + fprintf(stderr, "** Warning: Driver:%s not recognized - ignored \n", driver_name); +} + +static void queue_config_unset_queue_option(queue_config_type * queue_config, const char * driver_name, const char * option_key) { + if (queue_config_has_queue_driver(queue_config, driver_name)) { + queue_driver_type * driver = queue_config_get_queue_driver(queue_config, driver_name); + if (!queue_driver_unset_option(driver, option_key)) + fprintf(stderr, "** Warning: Option:%s is not recognized by driver:%s- ignored \n", option_key, driver_name); + } else + fprintf(stderr, "** Warning: Driver:%s not recognized - ignored \n", driver_name); +} + + + +static void queue_config_add_queue_driver(queue_config_type * queue_config, const char * driver_name, queue_driver_type * driver) { + hash_insert_hash_owned_ref(queue_config->queue_drivers, driver_name, driver, queue_driver_free__); +} + + +queue_driver_type * queue_config_get_queue_driver(const queue_config_type * queue_config, const char * driver_name) { + return (queue_driver_type * ) hash_get(queue_config->queue_drivers, driver_name); +} + + +void queue_config_create_queue_drivers(queue_config_type * queue_config) { + queue_config_add_queue_driver(queue_config, + LSF_DRIVER_NAME, + queue_driver_alloc_LSF(NULL, + NULL, + NULL)); + queue_config_add_queue_driver(queue_config, TORQUE_DRIVER_NAME, queue_driver_alloc_TORQUE()); + queue_config_add_queue_driver(queue_config, RSH_DRIVER_NAME, queue_driver_alloc_RSH(NULL, NULL)); + queue_config_add_queue_driver(queue_config, LOCAL_DRIVER_NAME, queue_driver_alloc_local()); + queue_config_add_queue_driver(queue_config, SLURM_DRIVER_NAME, queue_driver_alloc_slurm()); +} + + +bool queue_config_has_queue_driver(const queue_config_type * queue_config, const char * driver_name) { + return hash_has_key(queue_config->queue_drivers, driver_name); +} + + +static bool queue_config_init(queue_config_type * queue_config, const config_content_type * config_content) +{ + + + if (!queue_config->user_mode) + queue_config_create_queue_drivers(queue_config); + + if (config_content_has_item(config_content, QUEUE_SYSTEM_KEY)) { + const char * queue_system = config_content_get_value(config_content, QUEUE_SYSTEM_KEY); + + if (strcmp(queue_system, LSF_DRIVER_NAME) == 0) { + queue_config->driver_type = LSF_DRIVER; + } else if (strcmp(queue_system, RSH_DRIVER_NAME) == 0) + queue_config->driver_type = RSH_DRIVER; + else if (strcmp(queue_system, LOCAL_DRIVER_NAME) == 0) + queue_config->driver_type = LOCAL_DRIVER; + else if (strcmp(queue_system, TORQUE_DRIVER_NAME) == 0) + queue_config->driver_type = TORQUE_DRIVER; + else if (strcmp(queue_system, SLURM_DRIVER_NAME) == 0) + queue_config->driver_type = SLURM_DRIVER; + else { + util_abort("%s: queue system :%s not recognized \n", __func__, queue_system); + queue_config->driver_type = NULL_DRIVER; + } + } + + if (config_content_has_item(config_content, NUM_CPU_KEY)) + queue_config->num_cpu = config_content_get_value_as_int(config_content, NUM_CPU_KEY); + + if (config_content_has_item(config_content, JOB_SCRIPT_KEY)) + { + queue_config_set_job_script(queue_config, config_content_get_value_as_executable(config_content, JOB_SCRIPT_KEY)); + } + + if (config_content_has_item(config_content, MAX_SUBMIT_KEY)) + queue_config->max_submit = config_content_get_value_as_int(config_content, MAX_SUBMIT_KEY); + + /* Setting QUEUE_OPTIONS */ + for (int i = 0; i < config_content_get_occurences(config_content, QUEUE_OPTION_KEY); i++) { + const stringlist_type * tokens = config_content_iget_stringlist_ref(config_content, QUEUE_OPTION_KEY, i); + const char * driver_name = stringlist_iget(tokens, 0); + const char * option_key = stringlist_iget(tokens, 1); + + if(stringlist_get_size(tokens) > 2) { + char * option_value = stringlist_alloc_joined_substring(tokens, 2, stringlist_get_size(tokens), " "); + + // If it is essential to keep the exact number of spaces in the + // option_value, it should be quoted with "" in the configuration + // file. + queue_config_set_queue_option(queue_config, driver_name, option_key, option_value); + free( option_value ); + } + else + queue_config_unset_queue_option(queue_config, driver_name, option_key); + } + + return true; +} + + + +job_driver_type queue_config_get_driver_type(const queue_config_type * queue_config) { + return queue_config->driver_type; +} + +int queue_config_get_num_cpu(const queue_config_type * queue_config) { + return queue_config->num_cpu; +} + + +void queue_config_add_config_items(config_parser_type * parser, bool site_mode) { + + { + config_schema_item_type * item = config_add_schema_item(parser, MAX_SUBMIT_KEY, false); + config_schema_item_set_argc_minmax(item, 1, 1); + config_schema_item_iset_type(item, 0, CONFIG_INT); + } + + { + config_schema_item_type * item = config_add_schema_item(parser, NUM_CPU_KEY, false); + config_schema_item_set_argc_minmax(item, 1, 1); + config_schema_item_iset_type(item, 0, CONFIG_INT); + } + + { + config_schema_item_type * item = config_add_schema_item(parser, QUEUE_SYSTEM_KEY, site_mode); + config_schema_item_set_argc_minmax(item, 1, 1); + } + + { + config_schema_item_type * item = config_add_schema_item(parser, QUEUE_OPTION_KEY, false); + config_schema_item_set_argc_minmax(item, 2, CONFIG_DEFAULT_ARG_MAX); + } + + { + config_schema_item_type * item = config_add_schema_item(parser, JOB_SCRIPT_KEY, false); + config_schema_item_set_argc_minmax(item, 1, 1); + config_schema_item_iset_type(item, 0, CONFIG_EXECUTABLE); + } +} + + +const char * queue_config_lsf_queue_name() { + return LSF_QUEUE; +} + +const char * queue_config_lsf_server() { + return LSF_SERVER; +} + +const char * queue_config_lsf_resource() { + return LSF_RESOURCE; +} + +const char * queue_config_lsf_driver_name() { + return LSF_DRIVER_NAME; +} diff --git a/libres/lib/enkf/ranking_table.cpp b/libres/lib/enkf/ranking_table.cpp new file mode 100644 index 00000000000..5ac5a2c8c42 --- /dev/null +++ b/libres/lib/enkf/ranking_table.cpp @@ -0,0 +1,135 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'ranking_table.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 + + +struct ranking_table_struct { + UTIL_TYPE_ID_DECLARATION; + int ens_size; // Will not really handle ensemble resize events + hash_type * ranking_table; +}; + + + +void ranking_table_free( ranking_table_type * table ) { + hash_free( table->ranking_table ); + free( table ); +} + + + +void ranking_table_set_ens_size( ranking_table_type * table, int ens_size) { + table->ens_size = ens_size; +} + +ranking_table_type * ranking_table_alloc( int ens_size ) { + ranking_table_type * table = (ranking_table_type *)util_malloc( sizeof * table ); + table->ranking_table = hash_alloc(); + ranking_table_set_ens_size(table, ens_size); + return table; +} + + +void ranking_table_add_data_ranking( ranking_table_type * ranking_table , bool sort_increasing , const char * ranking_key , const char * user_key , const char * key_index , + enkf_fs_type * fs , const enkf_config_node_type * config_node , int step) { + + data_ranking_type * ranking = data_ranking_alloc( sort_increasing , ranking_table->ens_size , user_key , key_index , fs , config_node , step ); + hash_insert_hash_owned_ref( ranking_table->ranking_table , ranking_key , ranking, data_ranking_free__ ); +} + + + +void ranking_table_add_misfit_ranking( ranking_table_type * ranking_table , const misfit_ensemble_type * misfit_ensemble , const stringlist_type * obs_keys , const int_vector_type * steps , const char * ranking_key) { + misfit_ranking_type * ranking = misfit_ranking_alloc( misfit_ensemble , obs_keys , steps , ranking_key ); + hash_insert_hash_owned_ref( ranking_table->ranking_table , ranking_key , ranking , misfit_ranking_free__ ); +} + + +bool ranking_table_display_ranking( const ranking_table_type * ranking_table , const char * ranking_key ) { + if (hash_has_key( ranking_table->ranking_table , ranking_key)) { + void * ranking = (void *)hash_get( ranking_table->ranking_table , ranking_key ); + + if (data_ranking_is_instance( ranking )) { + data_ranking_type * data_ranking = data_ranking_safe_cast( ranking ); + data_ranking_display( data_ranking , stdout ); + } else if (misfit_ranking_is_instance( ranking )) { + misfit_ranking_type * misfit_ranking = misfit_ranking_safe_cast( ranking ); + misfit_ranking_display( misfit_ranking , stdout ); + } else + util_abort("%s: internal error \n",__func__); + + + return true; + } else + return false; +} + + +bool ranking_table_fwrite_ranking( const ranking_table_type * ranking_table , const char * ranking_key, const char * filename ) { + if (hash_has_key( ranking_table->ranking_table , ranking_key)) { + void * ranking = (void *)hash_get( ranking_table->ranking_table , ranking_key ); + + FILE * file = util_mkdir_fopen(filename, "w"); + + if (data_ranking_is_instance( ranking )) { + data_ranking_type * data_ranking = data_ranking_safe_cast( ranking ); + data_ranking_display( data_ranking , file ); + } else if (misfit_ranking_is_instance( ranking )) { + misfit_ranking_type * misfit_ranking = misfit_ranking_safe_cast( ranking ); + misfit_ranking_display( misfit_ranking , file ); + } else + util_abort("%s: internal error \n",__func__); + + fclose(file); + + return true; + } else + return false; +} + + + + +const perm_vector_type * ranking_table_get_permutation( const ranking_table_type * ranking_table , const char * ranking_key) { + if (hash_has_key( ranking_table->ranking_table , ranking_key)) { + void * ranking = (void *)hash_get( ranking_table->ranking_table , ranking_key ); + + if (data_ranking_is_instance( ranking )) { + data_ranking_type * data_ranking = data_ranking_safe_cast( ranking ); + return data_ranking_get_permutation( data_ranking ); + } else if (misfit_ranking_is_instance( ranking )) { + misfit_ranking_type * misfit_ranking = misfit_ranking_safe_cast( ranking ); + return misfit_ranking_get_permutation( misfit_ranking ); + } else { + util_abort("%s: internal error \n"); + return NULL; + } + + } else + return NULL; +} diff --git a/libres/lib/enkf/readme.overview b/libres/lib/enkf/readme.overview new file mode 100644 index 00000000000..7e395aaa738 --- /dev/null +++ b/libres/lib/enkf/readme.overview @@ -0,0 +1,55 @@ +The EnKF functionality is organized in xxx libraries with different +functionalities. The different libraries depend on eachother, and the +libraries must be built in correct order. The dependencies is as +follows: + +libhash : +libutil : libhash +libecl : libhash libutil +librms : libecl libutil libhash +libsched : libecl linutil libhash +libenkf : libecl libsched librm linutil libhash + + +libhash: This library implements the classes hash_type, set_type and + list_type. + +libutil: This library is a collection utility routines. Observe that + this library only implements routines, and not statefull + objects. + +libecl: This library implements functions for reading/writing ECLIPSE + restart/summary/init/grid files. + +libsched: This library implements a basic SCHEDULE file parser. + +librms: This library implements (basic) reader and writer for binary + RMS ROFF files. + +libenkf: This library implements various high level objects for EnKF + functionality. + +----------------------------------------------------------------- + +All the makefiles start with the statement: + +include "path_config" + +The file path_config is *not* under version control, this is on +purpose because every user can/should have a private confiiguration of +paths. The file path_config should define make-variables for the +location of all the libraries, this is an example of a valid +path-config file: + + LIBHASH_HOME = /h/a152128/EnKF/EnKF/libhash + LIBUTIL_HOME = /h/a152128/EnKF/EnKF/libutil + LIBSCHED_HOME = /h/a152128/EnKF/EnKF/libsched + LIBRMS_HOME = /h/a152128/EnKF/EnKF/librms + LIBECL_HOME = /h/a152128/EnKF/EnKF/libecl + LIBENKF_HOME = /h/a152128/EnKF/EnKF/libenkf + +In this example all libraries have a common path prefix, that is not +a requirement. + + + diff --git a/libres/lib/enkf/res_config.cpp b/libres/lib/enkf/res_config.cpp new file mode 100644 index 00000000000..8c4801c21b5 --- /dev/null +++ b/libres/lib/enkf/res_config.cpp @@ -0,0 +1,378 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'res_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 + +struct res_config_struct { + + char * user_config_file; + char * config_dir; + + site_config_type * site_config; + rng_config_type * rng_config; + analysis_config_type * analysis_config; + ert_workflow_list_type * workflow_list; + subst_config_type * subst_config; + hook_manager_type * hook_manager; + ert_templates_type * templates; + ecl_config_type * ecl_config; + ensemble_config_type * ensemble_config; + model_config_type * model_config; + log_config_type * log_config; + queue_config_type * queue_config; + +}; + + +static char * res_config_alloc_config_directory(const char * user_config_file); + + +static res_config_type * res_config_alloc_empty() { + res_config_type * res_config = (res_config_type *)util_malloc(sizeof * res_config); + res_config->user_config_file = NULL; + res_config->config_dir = NULL; + + res_config->site_config = NULL; + res_config->rng_config = NULL; + res_config->analysis_config = NULL; + res_config->workflow_list = NULL; + res_config->subst_config = NULL; + res_config->hook_manager = NULL; + res_config->templates = NULL; + res_config->ecl_config = NULL; + res_config->ensemble_config = NULL; + res_config->model_config = NULL; + res_config->log_config = NULL; + res_config->queue_config = NULL; + + return res_config; +} + + +void res_config_add_config_items(config_parser_type * config_parser) { + config_add_key_value(config_parser, RES_CONFIG_FILE_KEY, false, CONFIG_EXISTING_PATH); + config_add_key_value(config_parser, CONFIG_DIRECTORY_KEY, false, CONFIG_EXISTING_PATH); +} + + +void res_config_init_config_parser(config_parser_type * config_parser) { + model_config_init_config_parser(config_parser); + res_config_add_config_items(config_parser); +} + + +static void res_config_install_config_key( + config_parser_type * config_parser, + config_content_type * config_content, + const char * key, const char * value + ) { + + config_schema_item_type * schema_item = config_get_schema_item(config_parser, key); + + if(!config_content_has_item(config_content, key)) + config_content_add_item(config_content, schema_item, NULL); + + config_content_item_type * content_item = config_content_get_item(config_content, key); + + config_content_node_type * new_node = config_content_item_alloc_node(content_item, NULL); + config_content_node_add_value(new_node, value); + + if(!new_node) + util_abort( + "%s: Failed to internally install %s: %s\n", + __func__, key, value + ); + + config_content_add_node(config_content, new_node); +} + + +config_content_type * res_config_alloc_user_content( + const char * user_config_file, + config_parser_type * config_parser) { + + if(!user_config_file) + return NULL; + + // Read config file + config_content_type * config_content = model_config_alloc_content( + user_config_file, + config_parser + ); + + res_config_add_config_items(config_parser); + + // Install config file name + char * res_config_file = (user_config_file ? + util_alloc_realpath(user_config_file) : + NULL + ); + + res_config_install_config_key(config_parser, + config_content, + RES_CONFIG_FILE_KEY, + res_config_file + ); + + // Install working directory + char * res_config_dir = res_config_alloc_config_directory(res_config_file); + + res_config_install_config_key(config_parser, + config_content, + CONFIG_DIRECTORY_KEY, + res_config_dir + ); + + free(res_config_file); + free(res_config_dir); + + return config_content; +} + + +res_config_type * res_config_alloc_load(const char * config_file) { + config_parser_type * config_parser = config_alloc(); + config_content_type * config_content = res_config_alloc_user_content(config_file, config_parser); + + res_config_type * res_config = res_config_alloc(config_content); + + config_content_free(config_content); + config_free(config_parser); + + return res_config; +} + + +static void res_config_init( + res_config_type * res_config, + const config_content_type * config_content) +{ + if(config_content_has_item(config_content, RES_CONFIG_FILE_KEY)) { + const char * res_config_file = config_content_get_value_as_abspath(config_content, RES_CONFIG_FILE_KEY); + res_config->user_config_file = util_alloc_string_copy(res_config_file); + } + + if(config_content_has_item(config_content, CONFIG_DIRECTORY_KEY)) { + const char * config_dir = config_content_get_value_as_abspath(config_content, CONFIG_DIRECTORY_KEY); + res_config->config_dir = util_alloc_string_copy(config_dir); + } +} + + +res_config_type * res_config_alloc(const config_content_type * config_content) { + res_config_type * res_config = res_config_alloc_empty(); + + if(config_content) + res_config_init(res_config, config_content); + + res_config->subst_config = subst_config_alloc(config_content); + res_config->site_config = site_config_alloc(config_content); + res_config->rng_config = rng_config_alloc(config_content); + res_config->analysis_config = analysis_config_alloc(config_content); + + res_config->workflow_list = ert_workflow_list_alloc( + subst_config_get_subst_list(res_config->subst_config), + config_content + ); + + res_config->hook_manager = hook_manager_alloc( + res_config->workflow_list, + config_content + ); + + res_config->templates = ert_templates_alloc( + subst_config_get_subst_list(res_config->subst_config), + config_content + ); + + res_config->ecl_config = ecl_config_alloc(config_content); + + res_config->ensemble_config = ensemble_config_alloc(config_content, + ecl_config_get_grid(res_config->ecl_config), + ecl_config_get_refcase(res_config->ecl_config) + ); + + res_config->model_config = model_config_alloc(config_content, + res_config->config_dir, + site_config_get_installed_jobs(res_config->site_config), + ecl_config_get_last_history_restart(res_config->ecl_config), + ecl_config_get_refcase(res_config->ecl_config) + ); + + res_config->log_config = log_config_alloc(config_content); + + res_config->queue_config = queue_config_alloc(config_content); + + return res_config; +} + +res_config_type * res_config_alloc_full(char * config_dir, + char * user_config_file, + subst_config_type * subst_config, + site_config_type * site_config, + rng_config_type * rng_config, + analysis_config_type * analysis_config, + ert_workflow_list_type * workflow_list, + hook_manager_type * hook_manager, + ert_templates_type * templates, + ecl_config_type * ecl_config, + ensemble_config_type * ensemble_config, + model_config_type * model_config, + log_config_type * log_config, + queue_config_type * queue_config){ + res_config_type * res_config = res_config_alloc_empty(); + + res_config->user_config_file = util_alloc_string_copy(user_config_file); + res_config->config_dir = util_alloc_string_copy(config_dir); + res_config->subst_config = subst_config; + res_config->site_config = site_config; + res_config->rng_config = rng_config; + res_config->analysis_config = analysis_config; + res_config->workflow_list = workflow_list; + res_config->hook_manager = hook_manager; + res_config->templates = templates; + res_config->ecl_config = ecl_config; + res_config->ensemble_config = ensemble_config; + res_config->model_config = model_config; + res_config->log_config = log_config; + res_config->queue_config = queue_config; + return res_config; +} + +void res_config_free(res_config_type * res_config) { + if(!res_config) + return; + + site_config_free(res_config->site_config); + rng_config_free(res_config->rng_config); + analysis_config_free(res_config->analysis_config); + ert_workflow_list_free(res_config->workflow_list); + subst_config_free(res_config->subst_config); + hook_manager_free(res_config->hook_manager); + ert_templates_free(res_config->templates); + ecl_config_free(res_config->ecl_config); + ensemble_config_free(res_config->ensemble_config); + model_config_free(res_config->model_config); + log_config_free(res_config->log_config); + + free(res_config->user_config_file); + free(res_config->config_dir); + queue_config_free(res_config->queue_config); + free(res_config); +} + +const site_config_type * res_config_get_site_config( + const res_config_type * res_config + ) { + return res_config->site_config; +} + +rng_config_type * res_config_get_rng_config( + const res_config_type * res_config + ) { + return res_config->rng_config; +} + +const analysis_config_type * res_config_get_analysis_config( + const res_config_type * res_config + ) { + return res_config->analysis_config; +} + +ert_workflow_list_type * res_config_get_workflow_list( + const res_config_type * res_config + ) { + return res_config->workflow_list; +} + +subst_config_type * res_config_get_subst_config( + const res_config_type * res_config + ) { + return res_config->subst_config; +} + +const hook_manager_type * res_config_get_hook_manager( + const res_config_type * res_config + ) { + return res_config->hook_manager; +} + +ert_templates_type * res_config_get_templates( + const res_config_type * res_config + ) { + return res_config->templates; +} + + +const ecl_config_type * res_config_get_ecl_config( + const res_config_type * res_config + ) { + return res_config->ecl_config; +} + +ensemble_config_type * res_config_get_ensemble_config( + const res_config_type * res_config + ) { + return res_config->ensemble_config; +} + +model_config_type * res_config_get_model_config( + const res_config_type * res_config + ) { + return res_config->model_config; +} + +const log_config_type * res_config_get_log_config( + const res_config_type * res_config + ) { + return res_config->log_config; +} + +queue_config_type * res_config_get_queue_config( + const res_config_type * res_config + ){ + return res_config->queue_config; +} + +static char * res_config_alloc_config_directory(const char * user_config_file) { + if(user_config_file == NULL) + return NULL; + + char * path = NULL; + char * realpath = util_alloc_link_target(user_config_file); + char * abspath = util_alloc_realpath(realpath); + util_alloc_file_components(abspath, &path, NULL, NULL); + free(realpath); + free(abspath); + + return path; +} + +const char * res_config_get_config_directory(const res_config_type * res_config) { + return res_config->config_dir; +} + +const char * res_config_get_user_config_file(const res_config_type * res_config) { + return res_config->user_config_file; +} diff --git a/libres/lib/enkf/rng_config.cpp b/libres/lib/enkf/rng_config.cpp new file mode 100644 index 00000000000..0f3f615d888 --- /dev/null +++ b/libres/lib/enkf/rng_config.cpp @@ -0,0 +1,145 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'rng_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 +#include +#include + + +struct rng_config_struct { + rng_alg_type type; + char * random_seed; +}; + + +static void rng_config_set_random_seed(rng_config_type * rng_config, const char * random_seed) { + rng_config->random_seed = util_realloc_string_copy(rng_config->random_seed, random_seed); +} + +static char * rng_config_alloc_formatted_random_seed(const rng_config_type * rng_config) { + unsigned int * fseed = (unsigned int*) malloc(RNG_STATE_SIZE * sizeof(unsigned int*)); + + int seed_len = strlen(rng_config->random_seed); + int seed_pos = 0; + for (int i = 0; i < RNG_STATE_SIZE; ++i) { + fseed[i] = 0; + for (int k = 0; k < RNG_STATE_DIGITS; ++k) { + fseed[i] *= 10; + fseed[i] += rng_config->random_seed[seed_pos] - '0'; + seed_pos = (seed_pos+1) % seed_len; + } + } + + return (char *) fseed; +} + +const char * rng_config_get_random_seed(const rng_config_type * rng_config) { + return rng_config->random_seed; +} + +void rng_config_set_type( rng_config_type * rng_config , rng_alg_type type) { + rng_config->type = type; +} + +rng_alg_type rng_config_get_type(const rng_config_type * rng_config ) { + return rng_config->type; +} + +static rng_config_type * rng_config_alloc_default(void) { + rng_config_type * rng_config = (rng_config_type *)util_malloc( sizeof * rng_config); + + rng_config_set_type( rng_config , MZRAN ); /* Only type ... */ + rng_config->random_seed = NULL; + + return rng_config; +} + +rng_config_type * rng_config_alloc_load_user_config(const char * user_config_file) { + config_parser_type * config_parser = config_alloc(); + config_content_type * config_content = NULL; + if(user_config_file) + config_content = model_config_alloc_content(user_config_file, config_parser); + + rng_config_type * rng_config = rng_config_alloc(config_content); + + config_content_free(config_content); + config_free(config_parser); + + return rng_config; +} + +rng_config_type * rng_config_alloc(const config_content_type * config_content) { + rng_config_type * rng_config = rng_config_alloc_default(); + + if(config_content) + rng_config_init(rng_config, config_content); + + return rng_config; +} +rng_config_type * rng_config_alloc_full(const char * random_seed) { + rng_config_type * rng_config = rng_config_alloc_default(); + rng_config->random_seed = util_realloc_string_copy(rng_config->random_seed, random_seed); + + return rng_config; +} + + +void rng_config_free( rng_config_type * rng) { + free( rng->random_seed ); + free( rng ); +} + + +rng_manager_type * rng_config_alloc_rng_manager( const rng_config_type * rng_config ) { + rng_manager_type * rng_manager; + + if (rng_config->random_seed) { + char * formatted_seed = rng_config_alloc_formatted_random_seed(rng_config); + rng_manager = rng_manager_alloc(formatted_seed); + } else { + rng_manager = rng_manager_alloc_random( ); + } + + rng_manager_log_state(rng_manager); + + return rng_manager; +} + + +/*****************************************************************/ + +void rng_config_add_config_items( config_parser_type * parser ) { + config_add_key_value(parser, RANDOM_SEED_KEY, false, CONFIG_STRING); +} + + +void rng_config_init(rng_config_type * rng_config, const config_content_type * config_content) { + if(config_content_has_item(config_content, RANDOM_SEED_KEY)) { + const char * random_seed = config_content_get_value(config_content, RANDOM_SEED_KEY); + rng_config_set_random_seed(rng_config, random_seed); + res_log_fcritical("Using RANDOM_SEED: %s", random_seed); + } +} diff --git a/libres/lib/enkf/rng_manager.cpp b/libres/lib/enkf/rng_manager.cpp new file mode 100644 index 00000000000..d34218c15e4 --- /dev/null +++ b/libres/lib/enkf/rng_manager.cpp @@ -0,0 +1,151 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'rng_manager.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 + +#define RNG_MANAGER_TYPE_ID 77250451 + + +struct rng_manager_struct { + UTIL_TYPE_ID_DECLARATION; + rng_alg_type rng_alg; + rng_type * internal_seed_rng; /* This is used to seed the RNG's which are managed. */ + rng_type * external_seed_rng; /* This is used to seed the RNG's which are managed by external scope. */ + vector_type * rng_list; +}; + + +UTIL_IS_INSTANCE_FUNCTION( rng_manager, RNG_MANAGER_TYPE_ID ) + +static rng_manager_type * rng_manager_alloc__(rng_init_mode init_mode) { + rng_manager_type * rng_manager = (rng_manager_type *)util_malloc( sizeof * rng_manager ); + UTIL_TYPE_ID_INIT( rng_manager, RNG_MANAGER_TYPE_ID ); + rng_manager->rng_list = vector_alloc_new( ); + rng_manager->rng_alg = MZRAN; + rng_manager->internal_seed_rng = rng_alloc( rng_manager->rng_alg, init_mode ); + rng_manager->external_seed_rng = rng_alloc( rng_manager->rng_alg, init_mode ); + + rng_rng_init( rng_manager->external_seed_rng, rng_manager->external_seed_rng ); + + return rng_manager; +} + +/** + * Allocates an rng_manager with the specified seed. + * + * NOTE: With random algorithm mzran the provided seed should contain 16 + * characters, less will result in undefined behavour. Characters beyond + * the required 16 will be ignored. + */ +rng_manager_type * rng_manager_alloc(const char * random_seed) { + rng_manager_type * rng_manager = rng_manager_alloc_default(); + + rng_set_state(rng_manager->internal_seed_rng, random_seed); + rng_set_state(rng_manager->external_seed_rng, random_seed); + + rng_rng_init(rng_manager->external_seed_rng, rng_manager->external_seed_rng); + + return rng_manager; +} + +rng_manager_type * rng_manager_alloc_load(const char * seed_file) { + if (!util_file_exists(seed_file)) + return NULL; + + rng_manager_type * rng_manager = rng_manager_alloc_default(); + + rng_load_state(rng_manager->internal_seed_rng, seed_file); + rng_load_state(rng_manager->external_seed_rng, seed_file); + + rng_rng_init(rng_manager->external_seed_rng, rng_manager->external_seed_rng); + + return rng_manager; +} + +rng_manager_type * rng_manager_alloc_default( ) { + return rng_manager_alloc__(INIT_DEFAULT ); +} + +rng_manager_type * rng_manager_alloc_random( ) { + return rng_manager_alloc__(INIT_DEV_URANDOM); +} + + + +void rng_manager_free( rng_manager_type * rng_manager ) { + vector_free( rng_manager->rng_list ); + rng_free( rng_manager->internal_seed_rng ); + rng_free( rng_manager->external_seed_rng ); + free( rng_manager ); +} + + +static void rng_manager_grow(rng_manager_type * rng_manager, int min_size) { + int new_size = util_int_max( min_size, 2*vector_get_size( rng_manager->rng_list )); + for (int i = vector_get_size( rng_manager->rng_list ); i < new_size; i++) { + rng_type * rng = rng_alloc( rng_manager->rng_alg, INIT_DEFAULT ); + rng_rng_init( rng, rng_manager->internal_seed_rng ); + vector_append_owned_ref( rng_manager->rng_list , rng, rng_free__ ); + } +} + + +rng_type * rng_manager_alloc_rng(rng_manager_type * rng_manager) { + rng_type * rng = rng_alloc( rng_manager->rng_alg, INIT_DEFAULT ); + rng_rng_init( rng, rng_manager->external_seed_rng ); + return rng; +} + + +rng_type * rng_manager_iget(rng_manager_type * rng_manager, int index) { + if (index >= vector_get_size( rng_manager->rng_list )) + rng_manager_grow( rng_manager, index + 1); + + return (rng_type * ) vector_iget( rng_manager->rng_list , index ); +} + + +void rng_manager_save_state(const rng_manager_type * rng_manager, const char * seed_file) { + rng_save_state( rng_manager->internal_seed_rng, seed_file ); +} + + +void rng_manager_log_state(const rng_manager_type * rng_manager) { + unsigned int random_seed [RNG_STATE_SIZE]; + rng_get_state(rng_manager->internal_seed_rng, (char *) random_seed); + + char random_seed_str[RNG_STATE_DIGITS*RNG_STATE_SIZE+1]; + random_seed_str[0] = '\0'; + char * uint_fmt = util_alloc_sprintf("%%0%du", RNG_STATE_DIGITS); + + for(int i = 0; i < RNG_STATE_SIZE; ++i) { + char * elem = util_alloc_sprintf(uint_fmt, random_seed[i]); + strcat(random_seed_str, elem); + free(elem); + } + free(uint_fmt); + + res_log_info("To repeat this experiment, add the following random seed to your config file:"); + res_log_finfo("RANDOM_SEED %s", random_seed_str); +} diff --git a/libres/lib/enkf/row_scaling.cpp b/libres/lib/enkf/row_scaling.cpp new file mode 100644 index 00000000000..16694333790 --- /dev/null +++ b/libres/lib/enkf/row_scaling.cpp @@ -0,0 +1,247 @@ +/* + Copyright (C) 2020 Equinor ASA, Norway. + + The file 'row_scaling.cpp' 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 + +#define ROW_SCALING_ID 6123199 +#define ROW_SCALING_RESOLUTION 1000 + + +/* + The values in the row_scaling container are distributed among + ROW_SCALING_RESOLUTION discreete values. The reason for this lumping is to be + able to reuse the scaled update matrix: + + X(a) = a*X0 + (1 - a)*I + + for several rows. This grouping is utilized in the row_scaling_multiply() + function. +*/ + + +namespace { + + void scaleX(matrix_type* X, const matrix_type * X0, double alpha) { + matrix_assign(X, X0); + matrix_scale(X, alpha); + for (int i=0; i < matrix_get_rows(X); i++) + matrix_iset(X, i, i, (1 - alpha) + matrix_iget(X, i, i)); + } + +} + +int row_scaling::size() const { + return this->data.size(); +} + + +double row_scaling::operator[](int index) const { + return this->data.at(index); +} + + +double row_scaling::clamp(double value) const { + return floor(value * this->resolution) / this->resolution; +} + + +void row_scaling::resize(int new_size) { + const double default_value = 0; + this->data.resize(new_size, default_value); +} + +double row_scaling::assign(int index, double value) { + if (value < 0 || value > 1) + throw std::invalid_argument("Invalid value "); + + if (this->data.size() <= index) + this->resize(index + 1); + + this->data.at(index) = this->clamp(value); + return this->data.at(index); +} + + +/* + The final step in the Ensemble Smoother update is the matrix multiplication + + A' = A * X + + The core of the row_scaling approach is that for each row i in the A matrix + the X matrix transformed as: + + X(alpha) = (1 - alpha)*I + alpha*X + + where 0 <= alpha <= 1 denotes the 'strength' of the update; alpha == 1 + corresponds to a normal smoother update and alpha == 0 corresponds to no + update. With the per row transformation of X the operation is no longer matrix + multiplication but the pseudo code looks like: + + A'(i,j) = \sum_{k} A(i,k) * X'(k,j) + + With X' = X(alpha(i)). When alpha varies as a function of the row index 'i' + the X matrix should be recalculated for every row. To reduce the number of X + recalculations the row_scaling values are fixed to a finite number of values + (given by the resolution member in the row_scaling class) and the + multiplications are grouped together where all rows with the same alpha valued + are multiplied in one go. + */ + + +void row_scaling::multiply(matrix_type * A, const matrix_type * X0) const { + if (this->data.size() != matrix_get_rows(A)) + throw std::invalid_argument("Size mismatch between row_scaling and A matrix"); + + if (matrix_get_columns(A) != matrix_get_rows(X0)) + throw std::invalid_argument("Size mismatch between X0 and A matrix"); + + if (matrix_get_columns(X0) != matrix_get_rows(X0)) + throw std::invalid_argument("X0 matrix is not quadratic"); + + matrix_type * X = matrix_alloc(matrix_get_rows(X0), matrix_get_columns(X0)); + + /* + The sort_index vector is an index permutation corresponding to sorted + row_scaling data. + */ + std::vector sort_index(this->size()); + std::iota(sort_index.begin(), sort_index.end(), 0); + std::sort(sort_index.begin(), sort_index.end(), [this] (int index1, int index2) { return this->operator[](index1) > this->operator[](index2); }); + + + /* + This is a double while loop where we eventually go through all the rows in + the A matrix / row_scaling vector. In the inner loop we identify a list of + rows which have the same row_scaling value, then we scale the X matrix + according to this shared alpha value and calculate the update. + */ + + std::size_t index_offset = 0; + while (true) { + if (index_offset == this->data.size()) + break; + + auto end_index = index_offset; + auto current_alpha = this->data[sort_index[end_index]]; + std::vector row_list; + + // 1: Identify rows with the same alpha value + while (true) { + if (end_index == this->data.size()) + break; + + if (this->data[sort_index[end_index]] != current_alpha ) + break; + + row_list.push_back(sort_index[end_index]); + end_index += 1; + } + if (row_list.empty()) + break; + + double alpha = this->data[row_list[0]]; + if (alpha == 0.0) + break; + + // 2: Calculate the scaled X matrix + scaleX(X, X0, alpha); + + // 3: Calculate A' = A * X for the rows with the same alpha + for (const auto& row : row_list) { + std::vector src_row(matrix_get_columns(A)); + std::vector target_row(matrix_get_columns(A)); + + for (int j=0; j < matrix_get_columns(A); j++) + src_row[j] = matrix_iget(A, row, j); + + for (int j=0; j < matrix_get_columns(A); j++) { + double sum = 0; + for (int i=0; i < matrix_get_columns(A); i++) + sum += src_row[i] * matrix_iget(X, i, j); + + target_row[j] = sum; + } + matrix_set_row(A, target_row.data(), row); + } + + index_offset = end_index; + } + matrix_free(X); +} + + +template +void row_scaling::assign(const T * data, int size) { + this->resize(size); + for (int index=0; index < size; index++) + this->assign(index, data[index]); +} + + +/* + Below here is a C api for binding to Python. +*/ + + +row_scaling_type * row_scaling_alloc() { + row_scaling_type * scaling = new row_scaling(); + return scaling; +} + +row_scaling_type * row_scaling_alloc_copy(const row_scaling_type * scaling) { + return new row_scaling(*scaling); +} + +void row_scaling_free(row_scaling_type * scaling) { + delete scaling; +} + +double row_scaling_iget(const row_scaling_type * scaling, int index) { + return scaling->operator[](index); +} + +double row_scaling_iset(row_scaling_type * scaling, int index, double value) { + return scaling->assign(index, value); +} + +double row_scaling_clamp(const row_scaling_type * scaling, double value) { + return scaling->clamp(value); +} + +void row_scaling_multiply(const row_scaling_type * scaling, matrix_type * A, const matrix_type * X0) { + scaling->multiply(A, X0); +} + +int row_scaling_get_size(const row_scaling_type * row_scaling) { + return row_scaling->size(); +} + +void row_scaling_assign_double(row_scaling_type * scaling, const double * data, int size) { + scaling->assign(data, size); +} + +void row_scaling_assign_float(row_scaling_type * scaling, const float * data, int size) { + scaling->assign(data, size); +} diff --git a/libres/lib/enkf/run_arg.cpp b/libres/lib/enkf/run_arg.cpp new file mode 100644 index 00000000000..d43904e5a54 --- /dev/null +++ b/libres/lib/enkf/run_arg.cpp @@ -0,0 +1,349 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'run_arg.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 RUN_ARG_TYPE_ID 66143287 +#define INVALID_QUEUE_INDEX -99 + + +struct run_arg_struct { + UTIL_TYPE_ID_DECLARATION; + int iens; + int max_internal_submit; /* How many times the enkf_state object should try to resubmit when the queueu has said everything is OK - but the load fails. */ + int num_internal_submit; + int load_start; /* When loading back results - start at this step. */ + int step1; /* The forward model is integrated: step1 -> step2 */ + int step2; + int iter; + char * run_path; /* The currently used runpath - is realloced / freed for every step. */ + char * job_name; /* Name of the job - will correspond to ECLBASE for eclipse jobs. */ + run_mode_type run_mode; /* What type of run this is */ + int queue_index; /* The job will in general have a different index in the queue than the iens number. */ + int geo_id; /* This will be used by WPRO - and mapped to context key ; set during submit. */ + enkf_fs_type * sim_fs; + enkf_fs_type * update_target_fs; + subst_list_type * subst_list; + + /******************************************************************/ + /* Return value - set by the called routine!! */ + run_status_type run_status; + char * run_id; +}; + + +UTIL_SAFE_CAST_FUNCTION( run_arg , RUN_ARG_TYPE_ID ) +UTIL_IS_INSTANCE_FUNCTION( run_arg , RUN_ARG_TYPE_ID ) + +static void run_arg_update_subst(run_arg_type * run_arg); + +static run_arg_type * run_arg_alloc(const char * run_id, + enkf_fs_type * sim_fs , + enkf_fs_type * update_target_fs , + int iens , + run_mode_type run_mode , + int step1 , + int step2 , + int iter , + const char * runpath, + const char * job_name, + const subst_list_type * subst_list) +{ + if ((sim_fs != NULL) && (sim_fs == update_target_fs)) + util_abort("%s: internal error - can not have sim_fs == update_target_fs \n",__func__); + { + run_arg_type * run_arg = (run_arg_type *)util_malloc(sizeof * run_arg ); + UTIL_TYPE_ID_INIT(run_arg , RUN_ARG_TYPE_ID); + run_arg->run_id = util_alloc_string_copy( run_id ); + run_arg->sim_fs = sim_fs; + run_arg->update_target_fs = update_target_fs; + + run_arg->iens = iens; + run_arg->run_mode = run_mode; + run_arg->step1 = step1; + run_arg->step2 = step2; + run_arg->iter = iter; + run_arg->run_path = util_alloc_abs_path( runpath ); + run_arg->job_name = util_alloc_string_copy( job_name ); + run_arg->num_internal_submit = 0; + run_arg->queue_index = INVALID_QUEUE_INDEX; + run_arg->run_status = JOB_NOT_STARTED; + run_arg->geo_id = -1; // -1 corresponds to not set + run_arg->load_start = step1; + + run_arg->subst_list = subst_list_alloc(subst_list); + run_arg_update_subst(run_arg); + + return run_arg; + } +} + + +run_arg_type * run_arg_alloc_ENSEMBLE_EXPERIMENT(const char * run_id, + enkf_fs_type * sim_fs, + int iens, + int iter, + const char * runpath, + const char * job_name, + const subst_list_type * subst_list) +{ + return run_arg_alloc(run_id, + sim_fs, + NULL, + iens, + ENSEMBLE_EXPERIMENT, + 0, + 0, + iter, + runpath, + job_name, + subst_list); +} + + +run_arg_type * run_arg_alloc_INIT_ONLY(const char * run_id, + enkf_fs_type * sim_fs, + int iens, + int iter, + const char * runpath, + const subst_list_type * subst_list) +{ + return run_arg_alloc(run_id, + sim_fs, + NULL, + iens, + INIT_ONLY, + 0, + 0, + iter, + runpath, + NULL, + subst_list); +} + + +run_arg_type * run_arg_alloc_SMOOTHER_RUN(const char * run_id, + enkf_fs_type * sim_fs, + enkf_fs_type * update_target_fs, + int iens, + int iter, + const char * runpath, + const char * job_name, + const subst_list_type * subst_list) +{ + return run_arg_alloc(run_id, + sim_fs, + update_target_fs, + iens, + ENSEMBLE_EXPERIMENT, + 0, + 0, + iter, + runpath, + job_name, + subst_list); +} + + + +void run_arg_free(run_arg_type * run_arg) { + free( run_arg->job_name ); + free(run_arg->run_path); + free( run_arg->run_id ); + subst_list_free(run_arg->subst_list); + free(run_arg); +} + + +void run_arg_free__(void * arg) { + run_arg_type * run_arg = run_arg_safe_cast( arg ); + run_arg_free( run_arg ); +} + + + + + +void run_arg_increase_submit_count( run_arg_type * run_arg ) { + run_arg->num_internal_submit++; +} + + +void run_arg_set_queue_index( run_arg_type * run_arg , int queue_index) { + if (run_arg->queue_index == INVALID_QUEUE_INDEX) + run_arg->queue_index = queue_index; + else + util_abort("%s: attempt to reset run_arg->queue_index. These objects should not be recycled\n",__func__); +} + + + +const char * run_arg_get_runpath( const run_arg_type * run_arg) { + return run_arg->run_path; +} + + +const char * run_arg_get_job_name( const run_arg_type * run_arg) { + return run_arg->job_name; +} + + +const char * run_arg_get_run_id( const run_arg_type * run_arg) { + return run_arg->run_id; +} + + + + +int run_arg_get_iter( const run_arg_type * run_arg ) { + return run_arg->iter; +} + + +int run_arg_get_iens( const run_arg_type * run_arg ) { + return run_arg->iens; +} + + +int run_arg_get_load_start( const run_arg_type * run_arg ) { + return run_arg->load_start; +} + + +int run_arg_get_step2( const run_arg_type * run_arg ) { + return run_arg->step2; +} + +bool run_arg_can_retry( const run_arg_type * run_arg ) { + if (run_arg->num_internal_submit < run_arg->max_internal_submit) + return true; + else + return false; +} + + +int run_arg_get_step1( const run_arg_type * run_arg ) { + return run_arg->step1; +} + + +int run_arg_get_queue_index_safe( const run_arg_type * run_arg ) { + if (run_arg->queue_index == INVALID_QUEUE_INDEX) + return -1; + + return run_arg->queue_index; +} + +int run_arg_get_queue_index( const run_arg_type * run_arg ) { + if (run_arg->queue_index == INVALID_QUEUE_INDEX) + util_abort("%s: sorry internal error - asking for the queue_index in a not-initialized run_arg object.\n" , __func__); + + return run_arg->queue_index; +} + +bool run_arg_is_submitted( const run_arg_type * run_arg ) { + if (run_arg->queue_index == INVALID_QUEUE_INDEX) + return false; + else + return true; +} + + +run_status_type run_arg_get_run_status( const run_arg_type * run_arg) { + return run_arg->run_status; +} + + +void run_arg_set_run_status( run_arg_type * run_arg , run_status_type run_status) { + run_arg->run_status = run_status; +} + + +void run_arg_set_geo_id( run_arg_type * run_arg , int geo_id) { + /* + * Providing the geo_id upon initialization is the last step to achieve + * immutability for run_arg. Although we allow setting the geo_id for now, it + * should only be done once. + */ + if(run_arg->geo_id != -1) + util_abort("%s: Tried to set run_arg's geo_id twice!\n", __func__); + + run_arg->geo_id = geo_id; + run_arg_update_subst(run_arg); +} + + +int run_arg_get_geo_id( const run_arg_type * run_arg) { + return run_arg->geo_id; +} + + +enkf_fs_type * run_arg_get_sim_fs(const run_arg_type * run_arg) { + if (run_arg->sim_fs) + return run_arg->sim_fs; + else { + util_abort("%s: internal error - tried to access run_arg->sim_fs when sim_fs == NULL\n",__func__); + return NULL; + } +} + + +enkf_fs_type * run_arg_get_update_target_fs(const run_arg_type * run_arg) { + if (run_arg->update_target_fs) + return run_arg->update_target_fs; + else { + util_abort("%s: internal error - tried to access run_arg->update_target_fs when update_target_fs == NULL\n",__func__); + return NULL; + } +} + +const subst_list_type * run_arg_get_subst_list(const run_arg_type * run_arg) +{ + return run_arg->subst_list; +} + +static void run_arg_update_subst(run_arg_type * run_arg) +{ + char * iens_str = util_alloc_sprintf("%d", run_arg->iens); + subst_list_prepend_owned_ref(run_arg->subst_list, "", iens_str, NULL); + + char * iter_str = util_alloc_sprintf("%d", run_arg->iter); + subst_list_prepend_owned_ref(run_arg->subst_list, "", iter_str, NULL); + + if (run_arg->geo_id != -1) { + char * geo_id_str = util_alloc_sprintf("%d", run_arg->geo_id); + subst_list_prepend_owned_ref(run_arg->subst_list, "", geo_id_str, NULL); + } + + if(run_arg->job_name) { + subst_list_update_string(run_arg->subst_list, &run_arg->job_name); + subst_list_prepend_ref(run_arg->subst_list, "", run_arg->job_name, NULL); + subst_list_prepend_ref(run_arg->subst_list, "", run_arg->job_name, NULL); + } + + subst_list_update_string(run_arg->subst_list, &run_arg->run_path); + + subst_list_prepend_ref(run_arg->subst_list, "", run_arg->run_path, NULL); +} diff --git a/libres/lib/enkf/runpath_list.cpp b/libres/lib/enkf/runpath_list.cpp new file mode 100644 index 00000000000..f849490c91d --- /dev/null +++ b/libres/lib/enkf/runpath_list.cpp @@ -0,0 +1,284 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + The file 'runpath_list.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 + +typedef struct runpath_node_struct runpath_node_type; + + +struct runpath_list_struct { + pthread_rwlock_t lock; + vector_type * list; + char * line_fmt; // Format string : Values are in the order: (iens , runpath , basename) + char * export_file; +}; + + +#define RUNPATH_NODE_TYPE_ID 661400541 +struct runpath_node_struct { + UTIL_TYPE_ID_DECLARATION; + int iens; + int iter; + char * runpath; + char * basename; +}; + + +/*****************************************************************/ + + UTIL_SAFE_CAST_FUNCTION( runpath_node , RUNPATH_NODE_TYPE_ID ) + UTIL_SAFE_CAST_FUNCTION_CONST( runpath_node , RUNPATH_NODE_TYPE_ID ) + + static runpath_node_type * runpath_node_alloc( int iens, int iter, const char * runpath , const char * basename) { + runpath_node_type * node = (runpath_node_type *)util_malloc( sizeof * node ); + UTIL_TYPE_ID_INIT( node , RUNPATH_NODE_TYPE_ID ); + + node->iens = iens; + node->iter = iter; + node->runpath = util_alloc_string_copy( runpath ); + node->basename = util_alloc_string_copy( basename ); + + return node; + } + + +static void runpath_node_free( runpath_node_type * node ) { + free(node->basename); + free(node->runpath); + free(node); +} + + +static void runpath_node_free__( void * arg ) { + runpath_node_type * node = runpath_node_safe_cast( arg ); + runpath_node_free( node ); +} + + +/* + The comparison is first based on iteration number and then on iens. +*/ + +static int runpath_node_cmp( const void * arg1 , const void * arg2) { + const runpath_node_type * node1 = runpath_node_safe_cast_const( arg1 ); + const runpath_node_type * node2 = runpath_node_safe_cast_const( arg2 ); + { + if (node1->iter > node2->iter) + return 1; + else if (node1->iter < node2->iter) + return -1; + else { + /* Iteration number is the same */ + if (node1->iens > node2->iens) + return 1; + else if (node1->iens < node2->iens) + return -1; + else + return 0; + } + } +} + + +static void runpath_node_fprintf( const runpath_node_type * node , const char * line_fmt , FILE * stream) { + fprintf(stream , line_fmt , node->iens, node->runpath , node->basename, node->iter); +} + + +/*****************************************************************/ + + +runpath_list_type * runpath_list_alloc(const char * export_file) { + if (export_file == NULL) + return NULL; + + if (strlen(export_file) == 0) + return NULL; + + runpath_list_type * list = (runpath_list_type *)util_malloc( sizeof * list ); + list->list = vector_alloc_new(); + list->line_fmt = NULL; + list->export_file = util_alloc_string_copy( export_file ); + pthread_rwlock_init( &list->lock , NULL ); + return list; +} + + +void runpath_list_free( runpath_list_type * list ) { + vector_free( list->list ); + free( list->line_fmt ); + free( list->export_file); + free( list ); +} + + +int runpath_list_size( const runpath_list_type * list ) { + return vector_get_size( list->list ); +} + + +void runpath_list_add( runpath_list_type * list , int iens , int iter, const char * runpath , const char * basename) { + runpath_node_type * node = runpath_node_alloc( iens , iter, runpath , basename ); + + pthread_rwlock_wrlock( &list->lock ); + { + vector_append_owned_ref( list->list , node , runpath_node_free__ ); + } + pthread_rwlock_unlock( &list->lock ); +} + + +void runpath_list_clear( runpath_list_type * list ) { + pthread_rwlock_wrlock( &list->lock ); + { + vector_clear( list->list ); + } + pthread_rwlock_unlock( &list->lock ); +} + +/*****************************************************************/ + +void runpath_list_set_line_fmt( runpath_list_type * list , const char * line_fmt ) { + list->line_fmt = util_realloc_string_copy( list->line_fmt , line_fmt ); +} + + +const char * runpath_list_get_line_fmt( const runpath_list_type * list ) { + if (list->line_fmt == NULL) + return RUNPATH_LIST_DEFAULT_LINE_FMT; + else + return list->line_fmt; +} + +/*****************************************************************/ + + +static const runpath_node_type * runpath_list_iget_node__( const runpath_list_type * list , int index) { + return (const runpath_node_type * ) vector_iget_const( list->list , index ); +} + + + +static const runpath_node_type * runpath_list_iget_node( runpath_list_type * list , int index) { + const runpath_node_type * node; + { + pthread_rwlock_rdlock( &list->lock ); + node = runpath_list_iget_node__( list , index ); + pthread_rwlock_unlock( &list->lock ); + } + return node; +} + + +int runpath_list_iget_iens( runpath_list_type * list , int index) { + const runpath_node_type * node = runpath_list_iget_node( list , index ); + return node->iens; +} + +int runpath_list_iget_iter( runpath_list_type * list , int index) { + const runpath_node_type * node = runpath_list_iget_node( list , index ); + return node->iter; +} + + +char * runpath_list_iget_runpath( runpath_list_type * list , int index) { + const runpath_node_type * node = runpath_list_iget_node( list , index ); + return node->runpath; +} + +char * runpath_list_iget_basename( runpath_list_type * list , int index) { + const runpath_node_type * node = runpath_list_iget_node( list , index ); + return node->basename; +} + +void runpath_list_fprintf(runpath_list_type * list ) { + pthread_rwlock_rdlock( &list->lock ); + { + FILE * stream = util_mkdir_fopen( list->export_file , "w"); + const char * line_fmt = runpath_list_get_line_fmt( list ); + int index; + vector_sort( list->list , runpath_node_cmp ); + for (index =0; index < vector_get_size( list->list ); index++) { + const runpath_node_type * node = runpath_list_iget_node__( list , index ); + runpath_node_fprintf( node , line_fmt , stream ); + } + fclose( stream ); + } + pthread_rwlock_unlock( &list->lock ); +} + + +const char * runpath_list_get_export_file( const runpath_list_type * list ) { + return list->export_file; +} + + +void runpath_list_set_export_file( runpath_list_type * list , const char * export_file ) { + list->export_file = util_realloc_string_copy( list->export_file , export_file ); +} + + +bool runpath_list_load(runpath_list_type * list) { + FILE * stream = fopen(list->export_file, "r"); + if (!stream) + return false; + + { + vector_type * tmp_nodes = vector_alloc_new(); + bool read_ok = true; + while (true) { + char basename[256], runpath[256]; + int iens, iter; + + int read_count = fscanf(stream, "%d %255s %255s %d", &iens, runpath, basename, &iter); + if (read_count == 4) + vector_append_ref(tmp_nodes, runpath_node_alloc(iens, iter, runpath, basename)); + else { + if (read_count != EOF) + read_ok = false; + + break; + } + } + fclose(stream); + + if (read_ok) { + pthread_rwlock_wrlock( &list->lock); + for (int i=0; i < vector_get_size(tmp_nodes); i++) { + runpath_node_type * node = (runpath_node_type *)vector_iget(tmp_nodes, i); + vector_append_owned_ref(list->list, node, runpath_node_free__); + } + pthread_rwlock_unlock(&list->lock); + } else { + for (int i=0; i < vector_get_size(tmp_nodes); i++) { + runpath_node_type * node = (runpath_node_type *)vector_iget(tmp_nodes, i); + runpath_node_free(node); + } + } + + vector_free(tmp_nodes); + return read_ok; + } +} diff --git a/libres/lib/enkf/scalar_config.cpp b/libres/lib/enkf/scalar_config.cpp new file mode 100644 index 00000000000..f3ce56dc93e --- /dev/null +++ b/libres/lib/enkf/scalar_config.cpp @@ -0,0 +1,87 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'scalar_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 +#include +#include +#include + +#define SCALAR_CONFIG_TYPE_ID 877065 + +struct scalar_config_struct { + UTIL_TYPE_ID_DECLARATION; + int data_size; + active_list_type * active_list; + + trans_func_type ** transform; +}; + + + + +scalar_config_type * scalar_config_alloc_empty(int size) { + scalar_config_type * scalar_config = (scalar_config_type *)util_malloc(sizeof *scalar_config); + UTIL_TYPE_ID_INIT( scalar_config , SCALAR_CONFIG_TYPE_ID ); + scalar_config->data_size = size; + scalar_config->active_list = active_list_alloc( ); + + scalar_config->transform = util_calloc(scalar_config->data_size , sizeof * scalar_config->transform ); // CXX_CAST_ERROR + return scalar_config; +} + + + +void scalar_config_transform(const scalar_config_type * config , const double * input_data , double *output_data) { + int index; + for (index = 0; index < config->data_size; index++) + output_data[index] = trans_func_eval( config->transform[index] , input_data[index] ); +} + + + + + + +void scalar_config_fscanf_line(scalar_config_type * config , int line_nr , FILE * stream) { + config->transform[line_nr] = trans_func_fscanf_alloc( stream ); +} + + + +void scalar_config_free(scalar_config_type * scalar_config) { + int i; + active_list_free(scalar_config->active_list); + for (i=0; i < scalar_config->data_size; i++) + trans_func_free( scalar_config->transform[i] ); + + free( scalar_config->transform ); + free(scalar_config); +} + + + +/*****************************************************************/ + +SAFE_CAST(scalar_config , SCALAR_CONFIG_TYPE_ID) +GET_DATA_SIZE(scalar); +GET_ACTIVE_LIST(scalar); +VOID_FREE(scalar_config); diff --git a/libres/lib/enkf/site_config.cpp b/libres/lib/enkf/site_config.cpp new file mode 100644 index 00000000000..b72f53974f2 --- /dev/null +++ b/libres/lib/enkf/site_config.cpp @@ -0,0 +1,508 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'site_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 +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +/** + This struct contains information which is specific to the site + where this enkf instance is running. Pointers to the fields in this + structure are passed on to e.g. the enkf_state->shared_info object, + but this struct is the *OWNER* of this information, and hence + responsible for booting and deleting these objects. + + The settings held by the site_config object are by default set in + the site-wide configuration file, but they can also be overridden + in the users configuration file. This makes both parsing, + validating and also storing the configuration information a bit + more tricky: + + Parsing: + -------- + When parsing the user configuration file all settings are optional, + that means that the required validation of the config system, can + not be used, instead every get must be preceeded by: + + if (config_content_has_item(config , KEY)) ... + + Furthermore everything is done twice; first with config as a + site-config instance, and later as user-config instance. + + + Saving: + ------- + A setting which originates from the site_config file should not be + stored in the user's config file, but additions/overrides from the + user's config file should of course be saved. This is 'solved' with + many fields having a xxx_site duplicate, where the xxx_site is only + updated during the initial parsing of the site-config file; when + the flag user_mode is set to true the xxx_site fields are not + updated. When saving only fields which are different from their + xxx_site counterpart are stored. + */ + +struct site_config_struct { + + char * config_file; + + ext_joblist_type * joblist; /* The list of external jobs which have been installed. + These jobs will be the parts of the forward model. */ + + env_varlist_type * env_varlist; //Container for the environment variables set in the user config file. + + mode_t umask; + + char * license_root_path; /* The license_root_path value set by the user. */ + char * license_root_path_site; /* The license_root_path value set by the site. */ + char * __license_root_path; /* The license_root_path value actually used - includes a user/pid subdirectory. */ + + bool user_mode; + bool search_path; +}; + +static bool site_config_init(site_config_type * site_config, const config_content_type * config); +static void site_config_init_env(site_config_type * site_config, const config_content_type * config); + +void site_config_set_umask(site_config_type * site_config, mode_t new_mask) { + umask(new_mask); + site_config->umask = new_mask; +} + +mode_t site_config_get_umask(const site_config_type * site_config) { + return site_config->umask; +} + + +static void site_config_set_config_file(site_config_type * site_config, const char * config_file) { + free(site_config->config_file); + site_config->config_file = util_realloc_string_copy(site_config->config_file, config_file); +} + +/** + This site_config object is not really ready for prime time. + */ +static site_config_type * site_config_alloc_empty() { + site_config_type * site_config = (site_config_type *)util_malloc(sizeof * site_config); + + site_config->joblist = ext_joblist_alloc(); + + site_config->config_file = NULL; + site_config->license_root_path = NULL; + site_config->license_root_path_site = NULL; + site_config->__license_root_path = NULL; + site_config->user_mode = false; + + site_config->env_varlist = env_varlist_alloc(); + + /* Some hooops to get the current umask. */ + site_config->umask = umask(0); + site_config_set_umask(site_config, site_config->umask); + + site_config->search_path = false; + return site_config; +} + +static void site_config_load_config(site_config_type * site_config) { + config_parser_type * config = config_alloc(); + config_content_type * content = site_config_alloc_content(config); + + site_config_init(site_config, content); + + config_free(config); + config_content_free(content); +} + +/* + * NOTE: The queue config is not loaded until the site_config_alloc_load_user. + */ +static site_config_type * site_config_alloc_default() { + site_config_type * site_config = site_config_alloc_empty(); + site_config_set_config_file(site_config, site_config_get_location()); + site_config_load_config(site_config); + + return site_config; +} + +site_config_type * site_config_alloc_load_user_config(const char * user_config_file) { + config_parser_type * config_parser = config_alloc(); + config_content_type * config_content = NULL; + + if(user_config_file) + config_content = model_config_alloc_content(user_config_file, config_parser); + + site_config_type * site_config = site_config_alloc(config_content); + + config_free(config_parser); + config_content_free(config_content); + + return site_config; +} + +site_config_type * site_config_alloc(const config_content_type * config_content) { + site_config_type * site_config = site_config_alloc_default(); + + if(config_content) { + site_config->user_mode = true; + site_config_init(site_config, config_content); + } + + return site_config; +} + +site_config_type * site_config_alloc_full(ext_joblist_type * ext_joblist, env_varlist_type * env_varlist, int umask) { + site_config_type * site_config = site_config_alloc_empty(); + site_config->joblist = ext_joblist; + site_config->env_varlist = env_varlist; + site_config->umask = umask; + return site_config; +} + +const char * site_config_get_license_root_path(const site_config_type * site_config) { + return site_config->license_root_path; +} + +/** + Observe that this variable can not "really" be set to different + values during a simulation, when creating ext_job instances they + will store a pointer to this variable on creation, if the variable + is later changed they will be left with a dangling copy. That is + not particularly elegant, however it should nonetheless work. + */ + +void site_config_set_license_root_path(site_config_type * site_config, const char * license_root_path) { + util_make_path(license_root_path); + { + char * full_license_root_path = util_alloc_realpath(license_root_path); + { + /** + Appending /user/pid to the license root path. Everything + including the pid is removed when exiting (gracefully ...). + + Dangling license directories after a crash can just be removed. + */ + site_config->license_root_path = util_realloc_string_copy(site_config->license_root_path, full_license_root_path); + site_config->__license_root_path = util_realloc_sprintf(site_config->__license_root_path, "%s%c%s%c%d", full_license_root_path, UTIL_PATH_SEP_CHAR, getenv("USER"), UTIL_PATH_SEP_CHAR, getpid()); + + if (!site_config->user_mode) + site_config->license_root_path_site = util_realloc_string_copy(site_config->license_root_path_site, full_license_root_path); + } + free(full_license_root_path); + } +} + + + +/** + Will return 0 if the job is added correctly, and a non-zero (not + documented ...) error code if the job is not added. + */ + +int site_config_install_job(site_config_type * site_config, const char * job_name, const char * install_file) { + ext_job_type * new_job = ext_job_fscanf_alloc(job_name, site_config->__license_root_path, site_config->user_mode, install_file, site_config->search_path); + if (new_job != NULL) { + ext_joblist_add_job(site_config->joblist, job_name, new_job); + return 0; + } else + return 1; /* Some undocumented error condition - the job is NOT added. */ +} + +static void site_config_add_jobs(site_config_type * site_config, const config_content_type * config) { + if (config_content_has_item(config, INSTALL_JOB_KEY)) { + const config_content_item_type * content_item = config_content_get_item(config, INSTALL_JOB_KEY); + int num_jobs = config_content_item_get_size(content_item); + for (int job_nr = 0; job_nr < num_jobs; job_nr++) { + config_content_node_type * node = config_content_item_iget_node(content_item, job_nr); + const char * job_key = config_content_node_iget(node, 0); + const char * description_file = config_content_node_iget_as_abspath(node, 1); + + site_config_install_job(site_config, job_key, description_file); + } + } + if (config_content_has_item(config, INSTALL_JOB_DIRECTORY_KEY)) { + const config_content_item_type * content_item = config_content_get_item(config, INSTALL_JOB_DIRECTORY_KEY); + int num_dirs = config_content_item_get_size(content_item); + for (int dir_nr = 0; dir_nr < num_dirs; dir_nr++) { + config_content_node_type * node = config_content_item_iget_node(content_item, dir_nr); + const char * directory = config_content_node_iget_as_abspath(node, 0); + + ext_joblist_add_jobs_in_directory(site_config->joblist , directory, site_config->__license_root_path, site_config->user_mode, site_config->search_path ); + } + } + +} + + +const env_varlist_type * site_config_get_env_varlist(const site_config_type * site_config) { + return site_config->env_varlist; +} + + + +static void site_config_init_env(site_config_type * site_config, const config_content_type * config) { + { + if (config_content_has_item( config , SETENV_KEY)) { + config_content_item_type * setenv_item = config_content_get_item(config, SETENV_KEY); + int i; + for (i = 0; i < config_content_item_get_size(setenv_item); i++) { + const config_content_node_type * setenv_node = config_content_item_iget_node(setenv_item, i); + const char * var = config_content_node_iget(setenv_node, 0); + const char * value = config_content_node_iget(setenv_node, 1); + + env_varlist_setenv(site_config->env_varlist, var, value); + } + } + } + + { + if (config_content_has_item( config , UPDATE_PATH_KEY)) { + config_content_item_type * path_item = config_content_get_item(config, UPDATE_PATH_KEY); + int i; + for (i = 0; i < config_content_item_get_size(path_item); i++) { + const config_content_node_type * path_node = config_content_item_iget_node(path_item, i); + const char * path = config_content_node_iget(path_node, 0); + const char * value = config_content_node_iget(path_node, 1); + + env_varlist_update_path(site_config->env_varlist, path, value); + } + } + } +} + +/** + This function will be called twice, first when the config instance + is an internalization of the site-wide configuration file, and + secondly when config is an internalisation of the user's + configuration file. The @user_config parameter will be true in the + latter case. + */ + + +static bool site_config_init(site_config_type * site_config, const config_content_type * config) { + + site_config_add_jobs(site_config, config); + site_config_init_env(site_config, config); + + /* + Set the umask for all file creation. A value of '0' will ensure + that all files and directories are created with 'equal rights' + for everyone - might be handy if you are helping someone... The + default equinor value is 0022, i.e. write access is removed from + group and others. + + The string is supposed to be in OCTAL representation (without any + prefix characters). + */ + + if (config_content_has_item(config, UMASK_KEY)) { + const char * string_mask = config_content_get_value(config, UMASK_KEY); + int umask_value; + if (util_sscanf_octal_int(string_mask, &umask_value)) + site_config_set_umask(site_config, (mode_t)umask_value); + else + util_abort("%s: failed to parse:\"%s\" as a valid octal literal \n", __func__, string_mask); + } + + if (config_content_has_item(config, LICENSE_PATH_KEY)) + site_config_set_license_root_path(site_config, config_content_get_value_as_abspath(config, LICENSE_PATH_KEY)); + + return true; +} + + +void site_config_free(site_config_type * site_config) { + ext_joblist_free(site_config->joblist); + + env_varlist_free(site_config->env_varlist); + + if (site_config->__license_root_path != NULL) + util_clear_directory(site_config->__license_root_path, true, true); + + free(site_config->config_file); + + free(site_config->license_root_path); + free(site_config->license_root_path_site); + free(site_config->__license_root_path); + + + free(site_config); +} + +ext_joblist_type * site_config_get_installed_jobs(const site_config_type * site_config) { + return site_config->joblist; +} + + +/*****************************************************************/ + + + +void site_config_add_config_items(config_parser_type * config, bool site_mode) { + queue_config_add_config_items(config, site_mode); + + config_schema_item_type * item; + ert_workflow_list_add_config_items(config); + + + /* + You can set environment variables which will be applied to the + run-time environment. Can unfortunately not use constructions + like PATH=$PATH:/some/new/path, use the UPDATE_PATH function instead. + */ + item = config_add_schema_item(config, SETENV_KEY, false); + config_schema_item_set_argc_minmax(item, 2, 2); + config_schema_item_set_envvar_expansion(item, false); /* Do not expand $VAR expressions (that is done in util_interp_setenv()). */ + + item = config_add_schema_item(config, UMASK_KEY, false); + config_schema_item_set_argc_minmax(item, 1, 1); + + /** + UPDATE_PATH LD_LIBRARY_PATH /path/to/some/funky/lib + + Will prepend "/path/to/some/funky/lib" at the front of LD_LIBRARY_PATH. + */ + item = config_add_schema_item(config, UPDATE_PATH_KEY, false); + config_schema_item_set_argc_minmax(item, 2, 2); + config_schema_item_set_envvar_expansion(item, false); /* Do not expand $VAR expressions (that is done in util_interp_setenv()). */ + + if (!site_mode) { + item = config_add_schema_item(config, LICENSE_PATH_KEY, false); + config_schema_item_set_argc_minmax(item, 1, 1); + config_schema_item_iset_type(item, 0, CONFIG_PATH); + } + + + /*****************************************************************/ + + item = config_add_schema_item(config, INSTALL_JOB_KEY, false); + config_schema_item_set_argc_minmax(item, 2, 2); + config_schema_item_iset_type(item, 1, CONFIG_EXISTING_PATH); + + item = config_add_schema_item(config, INSTALL_JOB_DIRECTORY_KEY, false); + config_schema_item_set_argc_minmax(item, 1, 1); + config_schema_item_iset_type(item, 0, CONFIG_PATH); + + item = config_add_schema_item( config , ANALYSIS_LOAD_KEY , false ); + config_schema_item_set_argc_minmax( item , 2 , 2); + + item = config_add_schema_item( config , HOOK_WORKFLOW_KEY , false ); + config_schema_item_set_argc_minmax(item , 2 , 2 ); + config_schema_item_iset_type( item , 0 , CONFIG_STRING ); + config_schema_item_iset_type( item , 1 , CONFIG_STRING ); + { + stringlist_type * argv = stringlist_alloc_new(); + stringlist_append_copy(argv, RUN_MODE_PRE_SIMULATION_NAME); + stringlist_append_copy(argv, RUN_MODE_POST_SIMULATION_NAME); + stringlist_append_copy(argv, RUN_MODE_PRE_UPDATE_NAME); + stringlist_append_copy(argv, RUN_MODE_POST_UPDATE_NAME); + stringlist_append_copy(argv, RUN_MODE_PRE_FIRST_UPDATE_NAME); + config_schema_item_set_indexed_selection_set(item, 1, argv); + stringlist_free( argv ); + } +} + +const char * site_config_get_config_file(const site_config_type * site_config) { + return site_config->config_file; +} + +config_content_type * site_config_alloc_content( + config_parser_type * config_parser) { + + const char * site_config_file = site_config_get_location(); + + if(site_config_file == NULL) + util_abort("%s: No config file specified.\n", __func__); + + if(!util_file_exists(site_config_file)) + util_abort( + "%s: can not locate site configuration file:%s \n",__func__, + site_config_file + ); + + site_config_add_config_items(config_parser, true); + config_content_type * content = config_parse( + config_parser, site_config_file, + "--", INCLUDE_KEY, DEFINE_KEY, NULL, + CONFIG_UNRECOGNIZED_WARN, false + ); + + if(!config_content_is_valid(content)) { + config_error_type * errors = config_content_get_errors(content); + fprintf(stderr, + "** ERROR: Parsing site configuration file:%s failed \n\n", + site_config_file + ); + config_error_fprintf( errors , true , stderr ); + util_abort( + "%s: Invalid configurations in site_config file: %s.\n", + __func__, site_config_file + ); + } + + return content; +} + + +static std::string _site_config; + +extern "C" +void set_site_config(const char * site_config) { + auto path = realpath(site_config, NULL); + if (!path) { + perror("Failed to set default site config file"); + } else { + _site_config = path; + free(path); + } +} + +const char * site_config_get_location() { + const char * site_config = _site_config.c_str(); + const char * env_site_config = getenv("ERT_SITE_CONFIG"); + + if(env_site_config != NULL) { + if (util_file_exists(env_site_config)) { + site_config = env_site_config; + } else { + fprintf(stderr, "The environment variable ERT_SITE_CONFIG points to non-existing file: %s - ignored\n", env_site_config); + } + } + + if (site_config == NULL) { + fprintf(stderr, "**WARNING** main enkf_config file is not set. Use environment variable \"ERT_SITE_CONFIG\" - or recompile.\n"); + } + + return site_config; +} diff --git a/libres/lib/enkf/state_map.cpp b/libres/lib/enkf/state_map.cpp new file mode 100644 index 00000000000..14d69406434 --- /dev/null +++ b/libres/lib/enkf/state_map.cpp @@ -0,0 +1,284 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + The file 'state_map.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 + + +#define STATE_MAP_TYPE_ID 500672132 + +struct state_map_struct { + UTIL_TYPE_ID_DECLARATION; + int_vector_type * state; + pthread_rwlock_t mutable rw_lock; + bool read_only; +}; + + +UTIL_IS_INSTANCE_FUNCTION( state_map , STATE_MAP_TYPE_ID ) + + +state_map_type * state_map_alloc( ) { + state_map_type * map = (state_map_type *)util_malloc( sizeof * map ); + UTIL_TYPE_ID_INIT( map , STATE_MAP_TYPE_ID ); + map->state = int_vector_alloc( 0 , STATE_UNDEFINED ); + pthread_rwlock_init( &map->rw_lock , NULL); + map->read_only = false; + return map; +} + + +state_map_type * state_map_fread_alloc( const char * filename ) { + state_map_type * map = state_map_alloc(); + if (util_file_exists( filename )) { + FILE * stream = util_fopen( filename , "r"); + int_vector_fread( map->state , stream ); + fclose( stream ); + } + return map; +} + +state_map_type * state_map_fread_alloc_readonly( const char * filename ) { + state_map_type * map = state_map_fread_alloc(filename); + map->read_only = true; + return map; +} + + +state_map_type * state_map_alloc_copy(const state_map_type * map) { + state_map_type * copy = state_map_alloc(); + pthread_rwlock_rdlock( &map->rw_lock ); + { + int_vector_memcpy( copy->state , map->state ); + } + pthread_rwlock_unlock( &map->rw_lock ); + return copy; +} + + +void state_map_free( state_map_type * map ) { + int_vector_free( map->state ); + free( map ); +} + + +int state_map_get_size(const state_map_type * map) { + int size; + pthread_rwlock_rdlock( &map->rw_lock ); + { + size = int_vector_size( map->state ); + } + pthread_rwlock_unlock( &map->rw_lock ); + return size; +} + + +bool state_map_equal(const state_map_type * map1, const state_map_type * map2) { + bool equal = true; + pthread_rwlock_rdlock( &map1->rw_lock ); + pthread_rwlock_rdlock( &map2->rw_lock ); + { + if (int_vector_size( map1->state) != int_vector_size( map2->state)) + equal = false; + + if (equal) + equal = int_vector_equal( map1->state , map2->state ); + } + pthread_rwlock_unlock( &map1->rw_lock ); + pthread_rwlock_unlock( &map2->rw_lock ); + return equal; +} + + +realisation_state_enum state_map_iget(const state_map_type * map , int index) { + realisation_state_enum state; + pthread_rwlock_rdlock( &map->rw_lock ); + { + state = (realisation_state_enum) int_vector_safe_iget( map->state , index ); + } + pthread_rwlock_unlock( &map->rw_lock ); + return state; +} + +bool state_map_legal_transition( realisation_state_enum state1 , realisation_state_enum state2) { + int target_mask = 0; + + if (state1 == STATE_UNDEFINED) + target_mask = STATE_INITIALIZED | STATE_PARENT_FAILURE; + else if (state1 == STATE_INITIALIZED) + target_mask = STATE_LOAD_FAILURE | STATE_HAS_DATA | STATE_INITIALIZED | STATE_PARENT_FAILURE; + else if (state1 == STATE_HAS_DATA) + target_mask = STATE_INITIALIZED | STATE_LOAD_FAILURE | STATE_HAS_DATA | STATE_PARENT_FAILURE; + else if (state1 == STATE_LOAD_FAILURE) + target_mask = STATE_HAS_DATA | STATE_INITIALIZED | STATE_LOAD_FAILURE; + else if (state1 == STATE_PARENT_FAILURE) + target_mask = STATE_INITIALIZED | STATE_PARENT_FAILURE; + + if (state2 & target_mask) + return true; + else + return false; +} + +static void state_map_assert_writable( const state_map_type * map) { + if (map->read_only) + util_abort("%s: tried to modify read_only state_map - aborting \n",__func__); +} + +static void state_map_iset__( state_map_type * map , int index , realisation_state_enum new_state) { + realisation_state_enum current_state = (realisation_state_enum ) int_vector_safe_iget( map->state , index ); + + if (state_map_legal_transition( current_state , new_state )) + int_vector_iset( map->state , index , new_state); + else + util_abort("%s: illegal state transition for realisation:%d %d -> %d \n" , __func__ , index , current_state , new_state ); +} + +void state_map_iset( state_map_type * map ,int index , realisation_state_enum state) { + state_map_assert_writable(map); + pthread_rwlock_wrlock( &map->rw_lock ); + { + state_map_iset__( map , index , state ); + } + pthread_rwlock_unlock( &map->rw_lock ); +} + + +void state_map_update_matching( state_map_type * map , int index , int state_mask , realisation_state_enum new_state) { + realisation_state_enum current_state = state_map_iget( map , index ); + if (current_state & state_mask) + state_map_iset( map , index , new_state ); +} + + +void state_map_update_undefined( state_map_type * map , int index , realisation_state_enum new_state) { + state_map_update_matching( map , index , STATE_UNDEFINED , new_state ); +} + + + + +void state_map_fwrite(const state_map_type * map, const char * filename) { + pthread_rwlock_rdlock( &map->rw_lock ); + { + FILE * stream = util_mkdir_fopen( filename , "w"); + if (stream) { + int_vector_fwrite( map->state , stream ); + fclose( stream ); + } else + util_abort("%s: failed to open:%s for writing \n",__func__ , filename ); + } + pthread_rwlock_unlock( &map->rw_lock ); +} + + + +bool state_map_fread( state_map_type * map , const char * filename) { + bool file_exists = false; + pthread_rwlock_wrlock( &map->rw_lock ); + { + if (util_file_exists( filename )) { + FILE * stream = util_fopen( filename , "r"); + if (stream) { + int_vector_fread( map->state , stream ); + fclose( stream ); + } else + util_abort("%s: failed to open:%s for reading \n",__func__ , filename ); + file_exists = true; + } else + int_vector_reset( map->state ); + } + pthread_rwlock_unlock( &map->rw_lock ); + return file_exists; +} + +/* + NB: This function does *not* resize select_target vector; i.e. realizations + beyond the size of the select_target vector will not be selected. +*/ +static void state_map_select_matching__(const state_map_type * map, bool_vector_type * select_target, int select_mask, bool select) { + pthread_rwlock_rdlock( &map->rw_lock ); + { + { + const int * map_ptr = int_vector_get_ptr( map->state ); + int size = util_int_min(int_vector_size( map->state ), bool_vector_size(select_target)); + for (int i=0; i < size; i++) { + int state_value = map_ptr[i]; + if (state_value & select_mask) + bool_vector_iset( select_target , i , select); + } + } + pthread_rwlock_unlock( &map->rw_lock ); + } +} + + +void state_map_select_matching(const state_map_type * map, bool_vector_type * select_target, int select_mask) { + state_map_select_matching__(map , select_target , select_mask , true ); +} + + + + void state_map_deselect_matching(const state_map_type * map, bool_vector_type * select_target, int select_mask) { + state_map_select_matching__(map , select_target , select_mask , false ); +} + + +static void state_map_set_from_mask__( state_map_type * map , const bool_vector_type * mask , realisation_state_enum state, bool invert) { + const bool * mask_ptr = bool_vector_get_ptr(mask); + for (int i=0; i < bool_vector_size( mask); i++) { + if (mask_ptr[i] != invert) + state_map_iset(map , i , state); + } +} + +void state_map_set_from_inverted_mask( state_map_type * state_map , const bool_vector_type * mask , realisation_state_enum state) { + state_map_set_from_mask__(state_map , mask , state , true); +} + +void state_map_set_from_mask( state_map_type * state_map , const bool_vector_type * mask , realisation_state_enum state) { + state_map_set_from_mask__(state_map , mask , state , false); +} + +bool state_map_is_readonly(const state_map_type * state_map) { + return state_map->read_only; +} + + +int state_map_count_matching(const state_map_type * state_map, int mask) { + int count = 0; + pthread_rwlock_rdlock( &state_map->rw_lock ); + { + const int * map_ptr = int_vector_get_ptr(state_map->state); + for (int i=0; i < int_vector_size( state_map->state ); i++) { + int state_value = map_ptr[i]; + if (state_value & mask) + count++; + } + } + pthread_rwlock_unlock(&state_map->rw_lock); + return count; + } + diff --git a/libres/lib/enkf/subst_config.cpp b/libres/lib/enkf/subst_config.cpp new file mode 100644 index 00000000000..aac4d71a0b6 --- /dev/null +++ b/libres/lib/enkf/subst_config.cpp @@ -0,0 +1,226 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'subst_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 +#include + +struct subst_config_struct { + + subst_func_pool_type * subst_func_pool; + subst_list_type * subst_list; + +}; + +static void subst_config_init_default(subst_config_type * subst_config); + +static void subst_config_install_config_directory( + subst_config_type * subst_config, + const char * user_config_file + ); + +static void subst_config_init_load( + subst_config_type * subst_config, + const config_content_type * content + ); + +static subst_config_type * subst_config_alloc_empty() { + subst_config_type * subst_config = (subst_config_type *)util_malloc(sizeof * subst_config); + + subst_config->subst_func_pool = NULL; + subst_config->subst_list = NULL; + + return subst_config; +} + +static subst_config_type * subst_config_alloc_default() { + subst_config_type * subst_config = subst_config_alloc_empty(); + + subst_config->subst_func_pool = subst_func_pool_alloc(); + subst_config->subst_list = subst_list_alloc(subst_config->subst_func_pool); + + subst_config_init_default(subst_config); + + return subst_config; +} + +subst_config_type * subst_config_alloc(const config_content_type * user_config) { + subst_config_type * subst_config = subst_config_alloc_default(); + + if(user_config) + subst_config_init_load(subst_config, user_config); + + return subst_config; +} + +subst_config_type * subst_config_alloc_full(const subst_list_type * define_list) { + subst_config_type * subst_config = subst_config_alloc_default(); + + // copy list of substitution keywords + for (int i=0; i < subst_list_get_size(define_list); i++) { + const char * key = subst_list_iget_key(define_list, i); + const char * value = subst_list_iget_value(define_list, i); + subst_config_add_subst_kw(subst_config, key, value); + } + + return subst_config; +} + + +void subst_config_free(subst_config_type * subst_config) { + if(!subst_config) + return; + + subst_func_pool_free(subst_config->subst_func_pool); + subst_list_free(subst_config->subst_list); + + free(subst_config); +} + +subst_list_type * subst_config_get_subst_list(subst_config_type * subst_type) { + return subst_type->subst_list; +} + +void subst_config_add_internal_subst_kw(subst_config_type * subst_config, const char * key , const char * value, const char * help_text) { + char * tagged_key = util_alloc_sprintf(INTERNAL_DATA_KW_TAG_FORMAT, key); + subst_list_append_copy(subst_config_get_subst_list(subst_config), tagged_key, value, help_text); + free(tagged_key); +} + +void subst_config_add_subst_kw(subst_config_type * subst_config , const char * key , const char * value) { + subst_list_append_copy(subst_config->subst_list, key, value, "Supplied by the user in the configuration file."); +} + +void subst_config_clear(subst_config_type * subst_config) { + subst_list_clear(subst_config->subst_list); +} + +static void subst_config_install_num_cpu(subst_config_type * subst_config, int num_cpu) { + char * num_cpu_string = util_alloc_sprintf("%d" , num_cpu); + subst_config_add_internal_subst_kw(subst_config, "NUM_CPU", num_cpu_string, "The number of CPU used for one forward model."); + free(num_cpu_string); +} + +static void subst_config_init_default(subst_config_type * subst_config) { + /* Here we add the functions which should be available for string substitution operations. */ + + subst_func_pool_add_func(subst_config->subst_func_pool, "EXP", "exp", subst_func_exp, false, 1, 1, NULL); + subst_func_pool_add_func(subst_config->subst_func_pool, "LOG", "log", subst_func_log, false, 1, 1, NULL); + subst_func_pool_add_func(subst_config->subst_func_pool, "POW10", "Calculates 10^x", subst_func_pow10, false, 1, 1, NULL); + subst_func_pool_add_func(subst_config->subst_func_pool, "ADD", "Adds arguments", subst_func_add, true, 1, 0, NULL); + subst_func_pool_add_func(subst_config->subst_func_pool, "MUL", "Multiplies arguments", subst_func_mul, true, 1, 0, NULL); + + /** + Allocating the parent subst_list instance. This will (should ...) + be the top level subst instance for all substitions in the ert + program. + + All the functions available or only installed in this + subst_list. + + The key->value replacements installed in this instance are + key,value pairs which are: + + o Common to all ensemble members. + + o Constant in time. + */ + + /* Installing the functions. */ + subst_list_insert_func(subst_config->subst_list, "EXP", "__EXP__"); + subst_list_insert_func(subst_config->subst_list, "LOG", "__LOG__"); + subst_list_insert_func(subst_config->subst_list, "POW10", "__POW10__"); + subst_list_insert_func(subst_config->subst_list, "ADD", "__ADD__"); + subst_list_insert_func(subst_config->subst_list, "MUL", "__MUL__"); + + /* + Installing the based (key,value) pairs which are common to all + ensemble members, and independent of time. + */ + + char * date_string = util_alloc_date_stamp_utc(); + subst_config_add_internal_subst_kw(subst_config, "DATE", date_string, "The current date."); + free( date_string ); + + subst_config_install_num_cpu(subst_config, 1); +} + +static void subst_config_install_config_directory(subst_config_type * subst_config, const char * config_dir) { + subst_config_add_internal_subst_kw(subst_config, "CWD", config_dir, "The current working directory we are running from - the location of the config file."); + subst_config_add_internal_subst_kw(subst_config, "CONFIG_PATH", config_dir, "The current working directory we are running from - the location of the config file."); +} + +static void subst_config_install_data_kw(subst_config_type * subst_config, hash_type * config_data_kw) { + /* + Installing the DATA_KW keywords supplied by the user - these are + at the very top level, so they can reuse everything defined later. + */ + if (config_data_kw) { + hash_iter_type * iter = hash_iter_alloc(config_data_kw); + const char * key = hash_iter_get_next_key(iter); + while (key != NULL) { + subst_config_add_subst_kw(subst_config, key, (const char * ) hash_get(config_data_kw, key)); + key = hash_iter_get_next_key(iter); + } + hash_iter_free(iter); + } +} + +static void subst_config_init_load( + subst_config_type * subst_config, + const config_content_type * content) { + + if(config_content_has_item(content, CONFIG_DIRECTORY_KEY)) { + const char * work_dir = config_content_get_value_as_abspath(content, CONFIG_DIRECTORY_KEY); + subst_config_install_config_directory(subst_config, work_dir); + } + + const subst_list_type * define_list = config_content_get_const_define_list(content); + for (int i=0; i < subst_list_get_size(define_list); i++) { + const char * key = subst_list_iget_key(define_list, i); + const char * value = subst_list_iget_value(define_list, i); + subst_config_add_subst_kw(subst_config, key, value); + } + + if (config_content_has_item( content , DATA_KW_KEY)) { + config_content_item_type * data_item = config_content_get_item(content, DATA_KW_KEY); + hash_type * data_kw = config_content_item_alloc_hash(data_item , true); + subst_config_install_data_kw(subst_config, data_kw); + hash_free(data_kw); + } + + const char * runpath_file = config_content_has_item(content, RUNPATH_FILE_KEY) ? + config_content_get_value_as_abspath(content, RUNPATH_FILE_KEY) : + util_alloc_filename(config_content_get_config_path( content ), RUNPATH_LIST_FILE, NULL); + subst_config_add_internal_subst_kw(subst_config, "RUNPATH_FILE", runpath_file, + "The name of a file with a list of run directories."); + + if (config_content_has_item(content, DATA_FILE_KEY)) { + const char * data_file = config_content_get_value_as_abspath(content, DATA_FILE_KEY); + + if (!util_file_exists(data_file)) + util_abort("%s: Could not find ECLIPSE data file: %s\n", __func__, data_file ? data_file : "NULL"); + + int num_cpu = ecl_util_get_num_cpu(data_file); + subst_config_install_num_cpu(subst_config, num_cpu); + } +} diff --git a/libres/lib/enkf/summary.cpp b/libres/lib/enkf/summary.cpp new file mode 100644 index 00000000000..90510958719 --- /dev/null +++ b/libres/lib/enkf/summary.cpp @@ -0,0 +1,360 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'summary.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 SUMMARY_UNDEF -9999 + +struct summary_struct { + int __type_id; /* Only used for run_time checking. */ + summary_config_type * config; /* Can not be NULL - var_type is set on first load. */ + double_vector_type * data_vector; +}; + + +/*****************************************************************/ + + + + + + + + +C_USED void summary_clear(summary_type * summary) { + double_vector_reset( summary->data_vector ); +} + + +summary_type * summary_alloc(const summary_config_type * summary_config) { + summary_type * summary = (summary_type *)util_malloc(sizeof *summary ); + summary->__type_id = SUMMARY; + summary->config = (summary_config_type *) summary_config; + summary->data_vector = double_vector_alloc(0, SUMMARY_UNDEF); + return summary; +} + + + +bool summary_active_value( double value ) { + if (value == SUMMARY_UNDEF) + return false; + + return true; +} + + +void summary_copy(const summary_type *src, summary_type * target) { + if (src->config == target->config) + double_vector_memcpy( target->data_vector, src->data_vector ); + else + util_abort("%s: do not share config objects \n",__func__); +} + + + + + + +void summary_read_from_buffer(summary_type * summary, + buffer_type * buffer, + enkf_fs_type * fs, + int report_step) { + enkf_util_assert_buffer_type( buffer, SUMMARY ); + { + int size = buffer_fread_int(buffer); + double default_value = buffer_fread_double(buffer); + + double_vector_set_default(summary->data_vector, default_value); + double_vector_resize(summary->data_vector, size, default_value); + buffer_fread(buffer, + double_vector_get_ptr(summary->data_vector), + double_vector_element_size(summary->data_vector), + size); + } +} + + +bool summary_write_to_buffer(const summary_type * summary, + buffer_type * buffer, + int report_step) { + buffer_fwrite_int( buffer, SUMMARY ); + buffer_fwrite_int( buffer, double_vector_size(summary->data_vector)); + buffer_fwrite_double( buffer, double_vector_get_default(summary->data_vector)); + buffer_fwrite( buffer, + double_vector_get_ptr(summary->data_vector), + double_vector_element_size(summary->data_vector), + double_vector_size(summary->data_vector)); + return true; +} + + +C_USED bool summary_has_data( const summary_type * summary, + int report_step) { + return double_vector_size(summary->data_vector) > report_step; +} + + +void summary_free(summary_type *summary) { + double_vector_free( summary->data_vector ); + free(summary); +} + + +void summary_serialize(const summary_type * summary, + node_id_type node_id, + const active_list_type * active_list, + matrix_type * A, + int row_offset, + int column) { + double value = summary_get( summary, node_id.report_step ); + enkf_matrix_serialize( &value, 1, ECL_DOUBLE, active_list, A, row_offset, column); +} + + +void summary_deserialize(summary_type * summary, + node_id_type node_id, + const active_list_type * active_list, + const matrix_type * A, + int row_offset, + int column) { + double value; + enkf_matrix_deserialize( &value, 1, ECL_DOUBLE, active_list, A, row_offset, column); + summary_set( summary, node_id.report_step, value ); +} + +int summary_length(const summary_type * summary) { + return double_vector_size(summary->data_vector); +} + +double summary_get(const summary_type * summary, int report_step) { + return double_vector_iget( summary->data_vector, report_step ); +} + + +void summary_set( summary_type * summary, + int report_step, + double value) { + double_vector_iset( summary->data_vector, report_step, value); +} + +double summary_undefined_value() { + return SUMMARY_UNDEF; +} + +bool summary_user_get(const summary_type * summary, + const char * index_key, + int report_step, + double * value) { + if (double_vector_size( summary->data_vector ) > report_step) { + *value = double_vector_iget( summary->data_vector, report_step); + return true; + } else { + *value = -1; + return false; + } +} + + + +void summary_user_get_vector(const summary_type * summary, + const char * index_key, + double_vector_type * value) { + double_vector_memcpy( value, summary->data_vector); +} + + + +/** + There are three typical reasons why the node data can not be loaded: + + 1. The ecl_sum instance is equal to NULL. + 2. The ecl_sum instance does not have the report step we are asking for. + 3. The ecl_sum instance does not have the variable we are asking for. + + In the two first cases the function will return false, ultimately + signaling that the simulation has failed. In the last case we check + the required flag of the variable, and if this is set to false we + return true. This is done because this is a typical situation for + e.g. a well which has not yet opened. +*/ + +bool summary_forward_load(summary_type * summary, + const char * ecl_file_name, + const forward_load_context_type * load_context) { + bool loadOK = false; + double load_value; + int report_step = forward_load_context_get_load_step( load_context ); + const ecl_sum_type * ecl_sum = forward_load_context_get_ecl_sum( load_context ); + if (ecl_sum == NULL) + return false; + + const char * var_key = summary_config_get_var(summary->config); + load_fail_type load_fail_action = summary_config_get_load_fail_mode(summary->config ); + + /* Check if the ecl_sum instance has this report step. */ + if (ecl_sum_has_report_step( ecl_sum, report_step )) { + int last_report_index = ecl_sum_iget_report_end( ecl_sum, report_step ); + + if (ecl_sum_has_general_var(ecl_sum, var_key)) { + load_value = ecl_sum_get_general_var(ecl_sum, last_report_index ,var_key ); + loadOK = true; + } else { + load_value = 0; + /* + The summary object does not have this variable - probably + meaning that it is a well/group which has not yet + opened. When required == false we do not signal load + failure in this situation. + + If the user has misspelled the name, we will go through + the whole simulation without detecting that error. + */ + if (load_fail_action == LOAD_FAIL_EXIT) + loadOK = false; + else { + loadOK = true; + if (load_fail_action == LOAD_FAIL_WARN) + fprintf(stderr,"** WARNING ** Failed summary:%s does not have key:%s \n", + ecl_sum_get_case( ecl_sum ), var_key); + } + } + } else { + load_value = 0; + if (report_step == 0) + loadOK = true; + /* + We do not signal load failure if we do not have the S0000 + summary file - which does not contain any useful information + anyway. + + Hmmm - there is a "if (report_step > 0)" check in the + enkf_state_internalize_x() function as well. + */ + else { + if (load_fail_action == LOAD_FAIL_EXIT) + loadOK = false; + else { + loadOK = true; + if (load_fail_action == LOAD_FAIL_WARN) + fprintf(stderr,"** WARNING ** Failed summary:%s does not have report_step:%d \n", + ecl_sum_get_case( ecl_sum ), report_step); + } + } + } + + if (loadOK) + summary_set( summary, report_step, load_value ); + + return loadOK; +} + + + +bool summary_forward_load_vector(summary_type * summary, + const char * ecl_file_name, + const forward_load_context_type * load_context, + const int_vector_type * time_index) { + bool loadOK = false; + + const ecl_sum_type * ecl_sum = forward_load_context_get_ecl_sum( load_context ); + if (ecl_sum == NULL) + return false; + + const char * var_key = summary_config_get_var(summary->config); + load_fail_type load_fail_action = summary_config_get_load_fail_mode(summary->config ); + bool normal_load = false; + + + if (load_fail_action != LOAD_FAIL_EXIT) { + /* + The load will always ~succeed - but if we do not have the data; + we will fill the vector with zeros. + */ + + if (!ecl_sum_has_general_var(ecl_sum, var_key)) { + for (int step = 0; step < int_vector_size( time_index ); step++) { + int summary_step = int_vector_iget( time_index, step ); + if (summary_step >= 0) + double_vector_iset( summary->data_vector, summary_step, 0); + } + loadOK = true; + + if (load_fail_action == LOAD_FAIL_WARN) + fprintf(stderr,"** WARNING ** Failed summary:%s does not have key:%s \n", + ecl_sum_get_case( ecl_sum ), var_key); + } else + normal_load = true; + + } + + + if (!normal_load) + return loadOK; + + int key_index = ecl_sum_get_general_var_params_index( ecl_sum, var_key ); + for (int store_index = 0; store_index < int_vector_size( time_index ); store_index++) { + int summary_index = int_vector_iget( time_index, store_index ); + + if (summary_index >= 0) { + if (ecl_sum_has_report_step( ecl_sum, summary_index )) { + int last_ministep_index = ecl_sum_iget_report_end( ecl_sum, summary_index ); + double_vector_iset( summary->data_vector, + store_index, + ecl_sum_iget(ecl_sum, + last_ministep_index, + key_index )); + } + } + } + return true; + +} + + +/******************************************************************/ +/* Anonymously generated functions used by the enkf_node object */ +/******************************************************************/ +UTIL_SAFE_CAST_FUNCTION(summary, SUMMARY) +UTIL_SAFE_CAST_FUNCTION_CONST(summary, SUMMARY) +VOID_ALLOC(summary) +VOID_FREE(summary) +VOID_COPY (summary) +VOID_FORWARD_LOAD(summary) +VOID_FORWARD_LOAD_VECTOR(summary) +VOID_USER_GET(summary) +VOID_USER_GET_VECTOR(summary) +VOID_WRITE_TO_BUFFER(summary) +VOID_READ_FROM_BUFFER(summary) +VOID_SERIALIZE(summary) +VOID_DESERIALIZE(summary) +VOID_CLEAR(summary) +VOID_HAS_DATA(summary) diff --git a/libres/lib/enkf/summary_config.cpp b/libres/lib/enkf/summary_config.cpp new file mode 100644 index 00000000000..8cd1aed76f6 --- /dev/null +++ b/libres/lib/enkf/summary_config.cpp @@ -0,0 +1,115 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'summary_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 + +#include + + +#define SUMMARY_CONFIG_TYPE_ID 63106 + +struct summary_config_struct { + int __type_id; + load_fail_type load_fail; + ecl_smspec_var_type var_type; /* The type of the variable - according to ecl_summary nomenclature. */ + char * var; /* This is ONE variable of summary.x format - i.e. WOPR:OP_2, RPR:4, ... */ + std::set obs_set; /* Set of keys (which fit in enkf_obs) which are observations of this node. */ +}; + + +/*****************************************************************/ + +UTIL_IS_INSTANCE_FUNCTION(summary_config , SUMMARY_CONFIG_TYPE_ID) + +const char * summary_config_get_var(const summary_config_type * config) { + return config->var; +} + + +load_fail_type summary_config_get_load_fail_mode( const summary_config_type * config) { + return config->load_fail; +} + +/** + Unfortunately it is a bit problematic to set the required flag to + TRUE for well and group variables because they do not exist in the + summary results before the well has actually opened, i.e. for a + partial summary case the results will not be there, and the loader + will incorrectly(?) signal failure. +*/ + +void summary_config_set_load_fail_mode( summary_config_type * config , load_fail_type load_fail) { + if ((config->var_type == ECL_SMSPEC_WELL_VAR) || (config->var_type == ECL_SMSPEC_GROUP_VAR)) + // For well and group variables load_fail will be LOAD_FAIL_SILENT anyway. + config->load_fail = LOAD_FAIL_SILENT; + else + config->load_fail = load_fail; +} + + +/** + This can only be used to increase the load_fail strictness. +*/ + +void summary_config_update_load_fail_mode( summary_config_type * config , load_fail_type load_fail) { + if (load_fail > config->load_fail) + summary_config_set_load_fail_mode( config , load_fail ); +} + + +summary_config_type * summary_config_alloc(const char * var , load_fail_type load_fail) { + summary_config_type * config = new summary_config_type(); + + config->__type_id = SUMMARY_CONFIG_TYPE_ID; + config->var = util_alloc_string_copy( var ); + config->var_type = ecl_smspec_identify_var_type( var ); + summary_config_set_load_fail_mode( config , load_fail); + + return config; +} + + + +void summary_config_free(summary_config_type * config) { + free(config->var); + delete config; +} + + + +int summary_config_get_data_size( const summary_config_type * config) { + return 1; +} + + + + + + +/*****************************************************************/ +UTIL_SAFE_CAST_FUNCTION(summary_config , SUMMARY_CONFIG_TYPE_ID) +UTIL_SAFE_CAST_FUNCTION_CONST(summary_config , SUMMARY_CONFIG_TYPE_ID) +VOID_GET_DATA_SIZE(summary) +VOID_CONFIG_FREE(summary) diff --git a/libres/lib/enkf/summary_key_matcher.cpp b/libres/lib/enkf/summary_key_matcher.cpp new file mode 100644 index 00000000000..5990b41476e --- /dev/null +++ b/libres/lib/enkf/summary_key_matcher.cpp @@ -0,0 +1,71 @@ +#include + +#include + +#include + + +#define SUMMARY_KEY_MATCHER_TYPE_ID 700672137 + +struct summary_key_matcher_struct { + UTIL_TYPE_ID_DECLARATION; + hash_type * key_set; +}; + + +UTIL_IS_INSTANCE_FUNCTION( summary_key_matcher , SUMMARY_KEY_MATCHER_TYPE_ID ) + + +summary_key_matcher_type * summary_key_matcher_alloc() { + summary_key_matcher_type * matcher = (summary_key_matcher_type *)util_malloc(sizeof * matcher); + UTIL_TYPE_ID_INIT( matcher , SUMMARY_KEY_MATCHER_TYPE_ID); + matcher->key_set = hash_alloc(); + return matcher; +} + +void summary_key_matcher_free(summary_key_matcher_type * matcher) { + hash_free(matcher->key_set); + free(matcher); +} + +int summary_key_matcher_get_size(const summary_key_matcher_type * matcher) { + return hash_get_size( matcher->key_set ); +} + +void summary_key_matcher_add_summary_key(summary_key_matcher_type * matcher, const char * summary_key) { + if(!hash_has_key(matcher->key_set, summary_key)) { + hash_insert_int(matcher->key_set, summary_key, !util_string_has_wildcard(summary_key)); + } +} + +bool summary_key_matcher_match_summary_key(const summary_key_matcher_type * matcher, const char * summary_key) { + stringlist_type * keys = hash_alloc_stringlist(matcher->key_set); + bool has_key = false; + + for (int i = 0; i < stringlist_get_size(keys); i++) { + const char * pattern = stringlist_iget(keys, i); + if (summary_key) + if(util_fnmatch(pattern, summary_key) == 0) { + has_key = true; + break; + } + } + + stringlist_free(keys); + + return has_key; +} + +stringlist_type * summary_key_matcher_get_keys(const summary_key_matcher_type * matcher) { + return hash_alloc_stringlist(matcher->key_set); +} + +bool summary_key_matcher_summary_key_is_required(const summary_key_matcher_type * matcher, const char * summary_key) { + bool is_required = false; + + if(!util_string_has_wildcard(summary_key) && hash_has_key(matcher->key_set, summary_key)) { + is_required = (bool) hash_get_int(matcher->key_set, summary_key); + } + + return is_required; +} diff --git a/libres/lib/enkf/summary_key_set.cpp b/libres/lib/enkf/summary_key_set.cpp new file mode 100644 index 00000000000..064472ae816 --- /dev/null +++ b/libres/lib/enkf/summary_key_set.cpp @@ -0,0 +1,165 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + The file 'state_map.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 SUMMARY_KEY_SET_TYPE_ID 700672133 + +struct summary_key_set_struct { + UTIL_TYPE_ID_DECLARATION; + hash_type * key_set; + pthread_rwlock_t rw_lock; + bool read_only; +}; + + +UTIL_IS_INSTANCE_FUNCTION( summary_key_set , SUMMARY_KEY_SET_TYPE_ID ) + + +summary_key_set_type * summary_key_set_alloc() { + summary_key_set_type * set = (summary_key_set_type *)util_malloc(sizeof * set); + UTIL_TYPE_ID_INIT( set , SUMMARY_KEY_SET_TYPE_ID); + set->key_set = hash_alloc(); + pthread_rwlock_init( &set->rw_lock , NULL); + set->read_only = false; + return set; +} + +summary_key_set_type * summary_key_set_alloc_from_file(const char * filename, bool read_only) { + summary_key_set_type * set = summary_key_set_alloc(); + summary_key_set_fread(set, filename); + set->read_only = read_only; + return set; +} + +void summary_key_set_free(summary_key_set_type * set) { + hash_free(set->key_set); + free(set); +} + +int summary_key_set_get_size(summary_key_set_type * set) { + int size; + pthread_rwlock_rdlock( &set->rw_lock ); + { + size = hash_get_size( set->key_set ); + } + pthread_rwlock_unlock( &set->rw_lock ); + return size; +} + + +bool summary_key_set_add_summary_key(summary_key_set_type * set, const char * summary_key) { + bool writable_and_non_existent = true; + + pthread_rwlock_wrlock( &set->rw_lock); + { + + if(hash_has_key(set->key_set, summary_key)) { + writable_and_non_existent = false; + } + + if(set->read_only) { + writable_and_non_existent = false; + } + + if(writable_and_non_existent) { + hash_insert_int(set->key_set, summary_key, 1); + } + } + pthread_rwlock_unlock( &set->rw_lock ); + + return writable_and_non_existent; +} + +bool summary_key_set_has_summary_key(summary_key_set_type * set, const char * summary_key) { + bool has_key = false; + + pthread_rwlock_rdlock( &set->rw_lock ); + { + has_key = hash_has_key(set->key_set, summary_key); + } + pthread_rwlock_unlock( &set->rw_lock ); + + return has_key; +} + +stringlist_type * summary_key_set_alloc_keys(summary_key_set_type * set) { + stringlist_type * keys; + + pthread_rwlock_rdlock( &set->rw_lock ); + { + keys = hash_alloc_stringlist(set->key_set); + } + pthread_rwlock_unlock( &set->rw_lock ); + + return keys; +} + + +bool summary_key_set_is_read_only(const summary_key_set_type * set) { + return set->read_only; +} + +void summary_key_set_fwrite(summary_key_set_type * set, const char * filename) { + pthread_rwlock_rdlock( &set->rw_lock ); + { + FILE * stream = util_mkdir_fopen(filename , "w"); + if (stream) { + stringlist_type * keys = hash_alloc_stringlist(set->key_set); + stringlist_fwrite(keys, stream); + stringlist_free(keys); + fclose( stream ); + } else { + util_abort("%s: failed to open: %s for writing \n", __func__, filename); + } + } + pthread_rwlock_unlock( &set->rw_lock ); +} + +bool summary_key_set_fread(summary_key_set_type * set, const char * filename) { + bool file_exists = false; + pthread_rwlock_wrlock( &set->rw_lock ); + { + hash_clear(set->key_set); + + if (util_file_exists(filename)) { + FILE * stream = util_fopen(filename, "r"); + if (stream) { + stringlist_type * key_set = stringlist_fread_alloc(stream); + + for (int i = 0; i < stringlist_get_size(key_set); i++) { + hash_insert_int(set->key_set, stringlist_iget(key_set, i), 1); + } + stringlist_free(key_set); + fclose( stream ); + } else { + util_abort("%s: failed to open: %s for reading \n",__func__ , filename ); + } + file_exists = true; + } + } + pthread_rwlock_unlock( &set->rw_lock ); + return file_exists; +} diff --git a/libres/lib/enkf/summary_obs.cpp b/libres/lib/enkf/summary_obs.cpp new file mode 100644 index 00000000000..8e9c63051a7 --- /dev/null +++ b/libres/lib/enkf/summary_obs.cpp @@ -0,0 +1,184 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'summary_obs.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. +*/ + +/** + See the overview documentation of the observation system in enkf_obs.c +*/ +#include + +#include + +#include + + +#define SUMMARY_OBS_TYPE_ID 66103 +#define OBS_SIZE 1 + +struct summary_obs_struct { + UTIL_TYPE_ID_DECLARATION; + char * summary_key; /** The observation, in summary.x syntax, e.g. GOPR:FIELD. */ + char * obs_key; + + double value; /** Observation value. */ + double std; /** Standard deviation of observation. */ + double std_scaling; +}; + + + + + + +/** + This function allocates a summary_obs instance. The summary_key + string should be of the format used by the summary.x program. + E.g., WOPR:P4 would condition on WOPR in well P4. + + Observe that this format is currently *not* checked before the actual + observation time. + + TODO + Should check summary_key on alloc. +*/ +summary_obs_type * summary_obs_alloc(const char * summary_key, + const char * obs_key , + double value , + double std) { + + summary_obs_type * obs = (summary_obs_type *)util_malloc(sizeof * obs ); + UTIL_TYPE_ID_INIT( obs , SUMMARY_OBS_TYPE_ID ) + + obs->summary_key = util_alloc_string_copy( summary_key ); + obs->obs_key = util_alloc_string_copy( obs_key ); + obs->value = value; + obs->std = std; + obs->std_scaling = 1.0; + + return obs; +} + + +static UTIL_SAFE_CAST_FUNCTION_CONST(summary_obs , SUMMARY_OBS_TYPE_ID); +static UTIL_SAFE_CAST_FUNCTION(summary_obs , SUMMARY_OBS_TYPE_ID); +UTIL_IS_INSTANCE_FUNCTION(summary_obs , SUMMARY_OBS_TYPE_ID); + + +void summary_obs_free(summary_obs_type * summary_obs) { + free(summary_obs->summary_key); + free(summary_obs->obs_key); + free(summary_obs); +} + + + + + + + + + +const char * summary_obs_get_summary_key(const summary_obs_type * summary_obs) +{ + return summary_obs->summary_key; +} + + +/** + Hardcodes an assumption that the size of summary data|observations + is always one; i.e. PARTLY_ACTIVE and ALL_ACTIVE are treated in the + same manner. +*/ +void summary_obs_get_observations(const summary_obs_type * summary_obs, + obs_data_type * obs_data, + enkf_fs_type * fs, + int report_step , + const active_list_type * __active_list) { + + int active_size = active_list_get_active_size( __active_list , OBS_SIZE ); + if (active_size == 1) { + obs_block_type * obs_block = obs_data_add_block( obs_data , summary_obs->obs_key , OBS_SIZE , NULL , false); + obs_block_iset( obs_block , 0 , summary_obs->value , summary_obs->std * summary_obs->std_scaling); + } +} + + + +void summary_obs_measure(const summary_obs_type * obs, const summary_type * summary, node_id_type node_id , meas_data_type * meas_data , const active_list_type * __active_list) { + int active_size = active_list_get_active_size( __active_list , OBS_SIZE ); + if (active_size == 1) { + meas_block_type * meas_block = meas_data_add_block( meas_data , obs->obs_key , node_id.report_step , active_size ); + meas_block_iset( meas_block , node_id.iens , 0 , summary_get(summary, node_id.report_step )); + } +} + + + +C_USED double summary_obs_chi2(const summary_obs_type * obs, + const summary_type * summary, + node_id_type node_id) { + double x = (summary_get(summary , node_id.report_step) - obs->value) / obs->std; + return x*x; +} + + + +void summary_obs_user_get(const summary_obs_type * summary_obs , const char * index_key , double * value , double * std, bool * valid) { + *valid = true; + *value = summary_obs->value; + *std = summary_obs->std; +} + + + +double summary_obs_get_value( const summary_obs_type * summary_obs ) { + return summary_obs->value; +} + +double summary_obs_get_std( const summary_obs_type * summary_obs ) { + return summary_obs->std; +} + +double summary_obs_get_std_scaling( const summary_obs_type * summary_obs ) { + return summary_obs->std_scaling; +} + + +void summary_obs_update_std_scale(summary_obs_type * summary_obs, double std_multiplier , const active_list_type * active_list) { + if (active_list_get_mode( active_list ) == ALL_ACTIVE) + summary_obs->std_scaling = std_multiplier; + else { + int size = active_list_get_active_size( active_list , OBS_SIZE ); + if (size > 0) + summary_obs->std_scaling = std_multiplier; + } +} + + +void summary_obs_set_std_scale(summary_obs_type * summary_obs, double std_multiplier) { + summary_obs->std_scaling = std_multiplier; +} + + +/*****************************************************************/ + +VOID_FREE(summary_obs) +VOID_GET_OBS(summary_obs) +VOID_USER_GET_OBS(summary_obs) +VOID_MEASURE(summary_obs , summary) +VOID_CHI2(summary_obs , summary) +VOID_UPDATE_STD_SCALE(summary_obs); diff --git a/libres/lib/enkf/surface.cpp b/libres/lib/enkf/surface.cpp new file mode 100644 index 00000000000..bef04f764be --- /dev/null +++ b/libres/lib/enkf/surface.cpp @@ -0,0 +1,240 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'surface.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 + + +/*****************************************************************/ + + +struct surface_struct { + int __type_id; /* Only used for run_time checking. */ + surface_config_type * config; /* Can not be NULL - var_type is set on first load. */ + double * data; /* Size should always be one */ +}; + + +C_USED void surface_clear(surface_type * surface) { + const int data_size = surface_config_get_data_size(surface->config); + for (int k=0; k < data_size; k++) + surface->data[k] = 0; +} + + +bool surface_fload(surface_type * surface, const char * filename) { + bool ret = false; + if (filename) { + const geo_surface_type * base_surface = surface_config_get_base_surface(surface->config); + ret = geo_surface_fload_irap_zcoord(base_surface, filename, surface->data); + } + return ret; +} + + +bool surface_initialize(surface_type *surface, int iens, const char * filename, rng_type * rng) { + return surface_fload(surface, filename); +} + + +surface_type * surface_alloc(const surface_config_type * surface_config) { + surface_type * surface = (surface_type *)util_malloc(sizeof *surface); + surface->__type_id = SURFACE; + surface->config = (surface_config_type *) surface_config; + { + const int data_size = surface_config_get_data_size(surface_config); + surface->data = (double * ) util_calloc(data_size, sizeof * surface->data); + } + return surface; +} + + +void surface_copy(const surface_type *src, surface_type * target) { + if (src->config == target->config) { + const int data_size = surface_config_get_data_size(src->config); + for (int k=0; k < data_size; k++) + target->data[k] = src->data[k]; + } else + util_abort("%s: do not share config objects \n",__func__); +} + + +void surface_read_from_buffer(surface_type * surface, buffer_type * buffer, enkf_fs_type * fs, int report_step) { + int size = surface_config_get_data_size(surface->config); + enkf_util_assert_buffer_type(buffer, SURFACE); + buffer_fread(buffer, surface->data, sizeof * surface->data, size); +} + + +bool surface_write_to_buffer(const surface_type * surface, buffer_type * buffer, int report_step) { + int size = surface_config_get_data_size(surface->config); + buffer_fwrite_int(buffer, SURFACE); + buffer_fwrite(buffer, surface->data, sizeof * surface->data, size); + return true; +} + + +void surface_free(surface_type *surface) { + free(surface->data); + free(surface); +} + + +void surface_serialize(const surface_type * surface, + node_id_type node_id, + const active_list_type * active_list, + matrix_type * A, + int row_offset, + int column) { + const surface_config_type *config = surface->config; + const int data_size = surface_config_get_data_size(config); + + enkf_matrix_serialize(surface->data, + data_size, + ECL_DOUBLE, + active_list, + A, + row_offset, + column); +} + + + +void surface_deserialize(surface_type * surface, + node_id_type node_id, + const active_list_type * active_list, + const matrix_type * A, + int row_offset, + int column) { + const surface_config_type *config = surface->config; + const int data_size = surface_config_get_data_size(config); + + enkf_matrix_deserialize(surface->data, + data_size, + ECL_DOUBLE, + active_list, + A, + row_offset, + column); +} + + +void surface_ecl_write(const surface_type * surface, + const char * run_path, + const char * base_file, + value_export_type * export_value) { + char * target_file = util_alloc_filename(run_path, base_file , NULL); + surface_config_ecl_write(surface->config, target_file, surface->data); + free(target_file); +} + + +bool surface_user_get(const surface_type * surface, + const char * index_key, + int report_step, + double * value) { + const int data_size = surface_config_get_data_size(surface->config); + int index; + + *value = 0.0; + + if (util_sscanf_int(index_key, &index)) + if ((index >= 0) && (index < data_size)) { + *value = surface->data[index]; + return true; + } + + // Not valid + return false; +} + + + +C_USED void surface_set_inflation(surface_type * inflation, + const surface_type * std, + const surface_type * min_std) { + int size = 1; + for (int i = 0; i < size; i++) + inflation->data[i] = util_double_max(1.0, min_std->data[i] / std->data[i]); +} + + +C_USED void surface_iadd(surface_type * surface, const surface_type * delta) { + int size = 1; + for (int i = 0; i < size; i++) + surface->data[i] += delta->data[i]; +} + + +C_USED void surface_iaddsqr(surface_type * surface, const surface_type * delta) { + int size = 1; + for (int i = 0; i < size; i++) + surface->data[i] += delta->data[i] * delta->data[i]; +} + + +C_USED void surface_imul(surface_type * surface, const surface_type * delta) { + int size = 1; + for (int i = 0; i < size; i++) + surface->data[i] *= delta->data[i]; +} + +C_USED void surface_scale(surface_type * surface, double scale_factor) { + int size = 1; + for (int i = 0; i < size; i++) + surface->data[i] *= scale_factor; +} + +C_USED void surface_isqrt(surface_type * surface) { + int size = 1; + for (int i = 0; i < size; i++) + surface->data[i] = sqrt(surface->data[i]); +} + + +/******************************************************************/ +/* Anonymously generated functions used by the enkf_node object */ +/******************************************************************/ +UTIL_SAFE_CAST_FUNCTION(surface, SURFACE) +UTIL_SAFE_CAST_FUNCTION_CONST(surface, SURFACE) +VOID_ALLOC(surface) +VOID_FREE(surface) +VOID_ECL_WRITE(surface) +VOID_COPY(surface) +VOID_USER_GET(surface) +VOID_WRITE_TO_BUFFER(surface) +VOID_READ_FROM_BUFFER(surface) +VOID_SERIALIZE(surface) +VOID_DESERIALIZE(surface) +VOID_INITIALIZE(surface) +VOID_SET_INFLATION(surface) +VOID_CLEAR(surface) +VOID_IADD(surface) +VOID_SCALE(surface) +VOID_IMUL(surface) +VOID_IADDSQR(surface) +VOID_ISQRT(surface) +VOID_FLOAD(surface) diff --git a/libres/lib/enkf/surface_config.cpp b/libres/lib/enkf/surface_config.cpp new file mode 100644 index 00000000000..45d26cb628c --- /dev/null +++ b/libres/lib/enkf/surface_config.cpp @@ -0,0 +1,79 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'surface_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 + +#define SURFACE_CONFIG_TYPE_ID 853317 + +struct surface_config_struct { + UTIL_TYPE_ID_DECLARATION; + geo_surface_type * base_surface; +}; + + + +surface_config_type * surface_config_alloc_empty( ) { + surface_config_type * config = (surface_config_type *)util_malloc( sizeof * config ); + UTIL_TYPE_ID_INIT( config , SURFACE_CONFIG_TYPE_ID ); + config->base_surface = NULL; + return config; +} + + + +void surface_config_free( surface_config_type * config ) { + if (config->base_surface != NULL) + geo_surface_free( config->base_surface ); + + free( config ); +} + + +void surface_config_set_base_surface( surface_config_type * config , const char * base_surface ) { + if (config->base_surface != NULL) + geo_surface_free( config->base_surface ); + config->base_surface = geo_surface_fload_alloc_irap( base_surface , false ); +} + + +const geo_surface_type * surface_config_get_base_surface( const surface_config_type * config ) { + return config->base_surface; +} + + +int surface_config_get_data_size( const surface_config_type * config ) { + return geo_surface_get_size( config->base_surface ); +} + +void surface_config_ecl_write( const surface_config_type * config , const char * filename , const double * zcoord) { + geo_surface_fprintf_irap_external_zcoord( config->base_surface , filename , zcoord ); +} + + +/*****************************************************************/ +UTIL_SAFE_CAST_FUNCTION(surface_config , SURFACE_CONFIG_TYPE_ID) +UTIL_SAFE_CAST_FUNCTION_CONST(surface_config , SURFACE_CONFIG_TYPE_ID) +VOID_GET_DATA_SIZE(surface) +VOID_CONFIG_FREE(surface) + + diff --git a/libres/lib/enkf/test.cpp b/libres/lib/enkf/test.cpp new file mode 100644 index 00000000000..d0cc6943caf --- /dev/null +++ b/libres/lib/enkf/test.cpp @@ -0,0 +1,39 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'test.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 + + + +int main(void) { +} + + + diff --git a/libres/lib/enkf/tests/data/config/analysis_load_config b/libres/lib/enkf/tests/data/config/analysis_load_config new file mode 100644 index 00000000000..657ba148c39 --- /dev/null +++ b/libres/lib/enkf/tests/data/config/analysis_load_config @@ -0,0 +1 @@ +NUM_REALIZATIONS 3 diff --git a/libres/lib/enkf/tests/data/config/analysis_load_site_config b/libres/lib/enkf/tests/data/config/analysis_load_site_config new file mode 100644 index 00000000000..c7be2f143e1 --- /dev/null +++ b/libres/lib/enkf/tests/data/config/analysis_load_site_config @@ -0,0 +1,2 @@ +ANALYSIS_LOAD RML_ENKF_SITE_CONFIG1 rml_enkf.so +ANALYSIS_LOAD RML_ENKF_SITE_CONFIG2 rml_enkf.so diff --git a/libres/lib/enkf/tests/data/config/analysis_load_site_config_osx b/libres/lib/enkf/tests/data/config/analysis_load_site_config_osx new file mode 100644 index 00000000000..3d42a788197 --- /dev/null +++ b/libres/lib/enkf/tests/data/config/analysis_load_site_config_osx @@ -0,0 +1,2 @@ +ANALYSIS_LOAD RML_ENKF_SITE_CONFIG1 rml_enkf.dylib +ANALYSIS_LOAD RML_ENKF_SITE_CONFIG2 rml_enkf.dylib diff --git a/libres/lib/enkf/tests/data/config/ert_report_list b/libres/lib/enkf/tests/data/config/ert_report_list new file mode 100644 index 00000000000..eebf2be7446 --- /dev/null +++ b/libres/lib/enkf/tests/data/config/ert_report_list @@ -0,0 +1,3 @@ +REPORT_TIMEOUT 167 + +REPORT_LARGE TRUE diff --git a/libres/lib/enkf/tests/data/config/forward/ert/FAULT_TEMPLATE b/libres/lib/enkf/tests/data/config/forward/ert/FAULT_TEMPLATE new file mode 100644 index 00000000000..de1f12ca5b5 --- /dev/null +++ b/libres/lib/enkf/tests/data/config/forward/ert/FAULT_TEMPLATE @@ -0,0 +1 @@ + diff --git a/libres/lib/enkf/tests/data/config/forward/ert/MULTFLT.TXT b/libres/lib/enkf/tests/data/config/forward/ert/MULTFLT.TXT new file mode 100644 index 00000000000..c193c485289 --- /dev/null +++ b/libres/lib/enkf/tests/data/config/forward/ert/MULTFLT.TXT @@ -0,0 +1 @@ +MULTFLT NORMAL 0 1 diff --git a/libres/lib/enkf/tests/data/config/forward/ert/config_GEN_KW_false b/libres/lib/enkf/tests/data/config/forward/ert/config_GEN_KW_false new file mode 100644 index 00000000000..2722ecd85f5 --- /dev/null +++ b/libres/lib/enkf/tests/data/config/forward/ert/config_GEN_KW_false @@ -0,0 +1,8 @@ +JOBNAME Job%d +RUNPATH simulations/run%d +NUM_REALIZATIONS 1 + +ENSPATH Storage +JOB_SCRIPT script.sh + +GEN_KW MULTFLT FAULT_TEMPLATE MULTFLT.INC MULTFLT.TXT INIT_FILES:MULTFLT_INIT diff --git a/libres/lib/enkf/tests/data/config/forward/ert/config_GEN_KW_true b/libres/lib/enkf/tests/data/config/forward/ert/config_GEN_KW_true new file mode 100644 index 00000000000..50dec421a58 --- /dev/null +++ b/libres/lib/enkf/tests/data/config/forward/ert/config_GEN_KW_true @@ -0,0 +1,11 @@ +JOBNAME Job%d +RUNPATH simulations/run%d +NUM_REALIZATIONS 1 + +ENSPATH Storage +JOB_SCRIPT script.sh + +GEN_KW MULTFLT FAULT_TEMPLATE MULTFLT.INC MULTFLT.TXT INIT_FILES:MULTFLT_INIT FORWARD_INIT:TRUE +GEN_KW MULTFLT2 FAULT_TEMPLATE MULTFLT2.INC MULTFLT.TXT + + diff --git a/libres/lib/enkf/tests/data/config/forward/ert/config_GEN_PARAM_false b/libres/lib/enkf/tests/data/config/forward/ert/config_GEN_PARAM_false new file mode 100644 index 00000000000..1ba090e11d4 --- /dev/null +++ b/libres/lib/enkf/tests/data/config/forward/ert/config_GEN_PARAM_false @@ -0,0 +1,9 @@ +JOBNAME Job%d +RUNPATH simulations/run%d +NUM_REALIZATIONS 1 + +ENSPATH Storage +JOB_SCRIPT script.sh + + +GEN_PARAM PARAM PARAM.INC INIT_FILES:PARAM_INIT INPUT_FORMAT:ASCII OUTPUT_FORMAT:ASCII diff --git a/libres/lib/enkf/tests/data/config/forward/ert/config_GEN_PARAM_true b/libres/lib/enkf/tests/data/config/forward/ert/config_GEN_PARAM_true new file mode 100644 index 00000000000..6ba8d1c4f4d --- /dev/null +++ b/libres/lib/enkf/tests/data/config/forward/ert/config_GEN_PARAM_true @@ -0,0 +1,10 @@ +JOBNAME Job%d +RUNPATH simulations/run%d +NUM_REALIZATIONS 1 + +ENSPATH Storage +JOB_SCRIPT script.sh + + +GEN_PARAM PARAM PARAM.INC INIT_FILES:PARAM_INIT INPUT_FORMAT:ASCII OUTPUT_FORMAT:ASCII FORWARD_INIT:TRUE + diff --git a/libres/lib/enkf/tests/data/config/forward/ert/script.sh b/libres/lib/enkf/tests/data/config/forward/ert/script.sh new file mode 100755 index 00000000000..e6062956255 --- /dev/null +++ b/libres/lib/enkf/tests/data/config/forward/ert/script.sh @@ -0,0 +1 @@ +# An executable must be present for the testing. diff --git a/libres/lib/enkf/tests/data/config/gen_kw_logarithmic/FAULT_TEMPLATE b/libres/lib/enkf/tests/data/config/gen_kw_logarithmic/FAULT_TEMPLATE new file mode 100644 index 00000000000..de1f12ca5b5 --- /dev/null +++ b/libres/lib/enkf/tests/data/config/gen_kw_logarithmic/FAULT_TEMPLATE @@ -0,0 +1 @@ + diff --git a/libres/lib/enkf/tests/data/config/gen_kw_logarithmic/FAULT_TEMPLATE2 b/libres/lib/enkf/tests/data/config/gen_kw_logarithmic/FAULT_TEMPLATE2 new file mode 100644 index 00000000000..bb7d23f1dd3 --- /dev/null +++ b/libres/lib/enkf/tests/data/config/gen_kw_logarithmic/FAULT_TEMPLATE2 @@ -0,0 +1 @@ + diff --git a/libres/lib/enkf/tests/data/config/gen_kw_logarithmic/MULTFLT.TXT b/libres/lib/enkf/tests/data/config/gen_kw_logarithmic/MULTFLT.TXT new file mode 100644 index 00000000000..ec7d5e43ede --- /dev/null +++ b/libres/lib/enkf/tests/data/config/gen_kw_logarithmic/MULTFLT.TXT @@ -0,0 +1 @@ +MULTFLT LOGUNIF 0.0001 0.01 diff --git a/libres/lib/enkf/tests/data/config/gen_kw_logarithmic/MULTFLT2.TXT b/libres/lib/enkf/tests/data/config/gen_kw_logarithmic/MULTFLT2.TXT new file mode 100644 index 00000000000..d2f6e43865c --- /dev/null +++ b/libres/lib/enkf/tests/data/config/gen_kw_logarithmic/MULTFLT2.TXT @@ -0,0 +1 @@ +MULTFLT2 LOGUNIF 0.0000001 0.00001 diff --git a/libres/lib/enkf/tests/data/config/gen_kw_logarithmic/config_GEN_KW_logarithmic b/libres/lib/enkf/tests/data/config/gen_kw_logarithmic/config_GEN_KW_logarithmic new file mode 100644 index 00000000000..e8316a73c09 --- /dev/null +++ b/libres/lib/enkf/tests/data/config/gen_kw_logarithmic/config_GEN_KW_logarithmic @@ -0,0 +1,9 @@ +JOBNAME Job%d +RUNPATH simulations/run%d +NUM_REALIZATIONS 1 + +ENSPATH Storage +JOB_SCRIPT script.sh + +GEN_KW MULTFLT FAULT_TEMPLATE MULTFLT.INC MULTFLT.TXT +GEN_KW MULTFLT2 FAULT_TEMPLATE2 MULTFLT2.INC MULTFLT2.TXT diff --git a/libres/lib/enkf/tests/data/config/gen_kw_logarithmic/script.sh b/libres/lib/enkf/tests/data/config/gen_kw_logarithmic/script.sh new file mode 100755 index 00000000000..e6062956255 --- /dev/null +++ b/libres/lib/enkf/tests/data/config/gen_kw_logarithmic/script.sh @@ -0,0 +1 @@ +# An executable must be present for the testing. diff --git a/libres/lib/enkf/tests/data/config/gen_kw_plot/Parameters.txt b/libres/lib/enkf/tests/data/config/gen_kw_plot/Parameters.txt new file mode 100644 index 00000000000..ba7f5b2386d --- /dev/null +++ b/libres/lib/enkf/tests/data/config/gen_kw_plot/Parameters.txt @@ -0,0 +1,5 @@ +PARAM1 CONST 100 +PARAM2 UNIFORM 0 1 +PARAM3 LOGUNIF 0.001 1000 +PARAM4 LOGNORMAL 0.25 100 + diff --git a/libres/lib/enkf/tests/data/config/gen_kw_plot/Template.tmpl b/libres/lib/enkf/tests/data/config/gen_kw_plot/Template.tmpl new file mode 100644 index 00000000000..9cf3011745d --- /dev/null +++ b/libres/lib/enkf/tests/data/config/gen_kw_plot/Template.tmpl @@ -0,0 +1 @@ +-- Template; not used diff --git a/libres/lib/enkf/tests/data/config/gen_kw_plot/config b/libres/lib/enkf/tests/data/config/gen_kw_plot/config new file mode 100644 index 00000000000..4b83fd19bfe --- /dev/null +++ b/libres/lib/enkf/tests/data/config/gen_kw_plot/config @@ -0,0 +1,2 @@ +NUM_REALIZATIONS 25 +GEN_KW GEN_KW Template.tmpl Target.inc Parameters.txt diff --git a/libres/lib/enkf/tests/data/config/rng b/libres/lib/enkf/tests/data/config/rng new file mode 100644 index 00000000000..016f35882a8 --- /dev/null +++ b/libres/lib/enkf/tests/data/config/rng @@ -0,0 +1,4 @@ +NUM_REALIZATIONS 25 + +-- The settings below here are artifacts which should not be necessary ... +JOB_SCRIPT script.sh diff --git a/libres/lib/enkf/tests/data/config/runpath_list/ARGECHO_JOB b/libres/lib/enkf/tests/data/config/runpath_list/ARGECHO_JOB new file mode 100644 index 00000000000..9cd8129c859 --- /dev/null +++ b/libres/lib/enkf/tests/data/config/runpath_list/ARGECHO_JOB @@ -0,0 +1,2 @@ +INTERNAL FALSE +EXECUTABLE arg_echo.sh diff --git a/libres/lib/enkf/tests/data/config/runpath_list/ARGECHO_WF b/libres/lib/enkf/tests/data/config/runpath_list/ARGECHO_WF new file mode 100644 index 00000000000..b2718930966 --- /dev/null +++ b/libres/lib/enkf/tests/data/config/runpath_list/ARGECHO_WF @@ -0,0 +1 @@ +ARGECHO_JOB runpath_list.txt diff --git a/libres/lib/enkf/tests/data/config/runpath_list/arg_echo.sh b/libres/lib/enkf/tests/data/config/runpath_list/arg_echo.sh new file mode 100755 index 00000000000..e7fffa0c13e --- /dev/null +++ b/libres/lib/enkf/tests/data/config/runpath_list/arg_echo.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +echo $1 > $2 diff --git a/libres/lib/enkf/tests/data/config/runpath_list/config b/libres/lib/enkf/tests/data/config/runpath_list/config new file mode 100644 index 00000000000..ded46130bb9 --- /dev/null +++ b/libres/lib/enkf/tests/data/config/runpath_list/config @@ -0,0 +1,5 @@ +RUNPATH_FILE runpath/runpath-file.txt +NUM_REALIZATIONS 10 + +LOAD_WORKFLOW_JOB ARGECHO_JOB +LOAD_WORKFLOW ARGECHO_WF diff --git a/libres/lib/enkf/tests/data/config/script.sh b/libres/lib/enkf/tests/data/config/script.sh new file mode 100644 index 00000000000..bf49abd440f --- /dev/null +++ b/libres/lib/enkf/tests/data/config/script.sh @@ -0,0 +1,2 @@ +#!/bin/sh +#Dummy script diff --git a/libres/lib/enkf/tests/data/config/test_context/FAULT_TEMPLATE b/libres/lib/enkf/tests/data/config/test_context/FAULT_TEMPLATE new file mode 100644 index 00000000000..de1f12ca5b5 --- /dev/null +++ b/libres/lib/enkf/tests/data/config/test_context/FAULT_TEMPLATE @@ -0,0 +1 @@ + diff --git a/libres/lib/enkf/tests/data/config/test_context/MULTFLT.TXT b/libres/lib/enkf/tests/data/config/test_context/MULTFLT.TXT new file mode 100644 index 00000000000..c193c485289 --- /dev/null +++ b/libres/lib/enkf/tests/data/config/test_context/MULTFLT.TXT @@ -0,0 +1 @@ +MULTFLT NORMAL 0 1 diff --git a/libres/lib/enkf/tests/data/config/test_context/config b/libres/lib/enkf/tests/data/config/test_context/config new file mode 100644 index 00000000000..7e702c6b143 --- /dev/null +++ b/libres/lib/enkf/tests/data/config/test_context/config @@ -0,0 +1,11 @@ +JOBNAME Job%d +RUNPATH simulations/run%d +NUM_REALIZATIONS 1 + +ENSPATH Storage +JOB_SCRIPT script.sh + +GEN_KW MULTFLT FAULT_TEMPLATE MULTFLT.INC MULTFLT.TXT INIT_FILES:MULTFLT_INIT FORWARD_INIT:TRUE + + +SUMMARY RPR:8 diff --git a/libres/lib/enkf/tests/data/config/test_context/script.sh b/libres/lib/enkf/tests/data/config/test_context/script.sh new file mode 100755 index 00000000000..e6062956255 --- /dev/null +++ b/libres/lib/enkf/tests/data/config/test_context/script.sh @@ -0,0 +1 @@ +# An executable must be present for the testing. diff --git a/libres/lib/enkf/tests/data/config/test_context/wf_job b/libres/lib/enkf/tests/data/config/test_context/wf_job new file mode 100644 index 00000000000..ed3a5838fbe --- /dev/null +++ b/libres/lib/enkf/tests/data/config/test_context/wf_job @@ -0,0 +1,5 @@ +INTERNAL True +FUNCTION enkf_main_select_case_JOB +MIN_ARG 1 +MAX_ARG 1 +ARG_TYPE 0 STRING diff --git a/libres/lib/enkf/tests/data/config/test_context/wf_job_fail b/libres/lib/enkf/tests/data/config/test_context/wf_job_fail new file mode 100644 index 00000000000..b8d5206384b --- /dev/null +++ b/libres/lib/enkf/tests/data/config/test_context/wf_job_fail @@ -0,0 +1,5 @@ +INTERNAL True +FUNCTION unknown_function__ +MIN_ARG 1 +MAX_ARG 1 +ARG_TYPE 0 STRING diff --git a/libres/lib/enkf/tests/data/ensemble/GEN_PARAM b/libres/lib/enkf/tests/data/ensemble/GEN_PARAM new file mode 100644 index 00000000000..69062cca227 --- /dev/null +++ b/libres/lib/enkf/tests/data/ensemble/GEN_PARAM @@ -0,0 +1 @@ +GEN_PARAM GP GP.txt INIT_FILES:GP/GP.txt INPUT_FORMAT:ASCII OUTPUT_FORMAT:ASCII diff --git a/libres/lib/enkf/tests/data/workflow_jobs/CONF1 b/libres/lib/enkf/tests/data/workflow_jobs/CONF1 new file mode 100644 index 00000000000..c42a844b298 --- /dev/null +++ b/libres/lib/enkf/tests/data/workflow_jobs/CONF1 @@ -0,0 +1,5 @@ +INTERNAL True +FUNCTION util_malloc +MIN_ARG 1 +MAX_ARG 1 +ARG_TYPE 0 FLOAT diff --git a/libres/lib/enkf/tests/data/workflow_jobs/CONF2 b/libres/lib/enkf/tests/data/workflow_jobs/CONF2 new file mode 100644 index 00000000000..04dd2746324 --- /dev/null +++ b/libres/lib/enkf/tests/data/workflow_jobs/CONF2 @@ -0,0 +1,2 @@ +This should not be loaded; +instead the CONF2@1 should be loaded in testing. diff --git a/libres/lib/enkf/tests/data/workflow_jobs/CONF2@1 b/libres/lib/enkf/tests/data/workflow_jobs/CONF2@1 new file mode 100644 index 00000000000..c42a844b298 --- /dev/null +++ b/libres/lib/enkf/tests/data/workflow_jobs/CONF2@1 @@ -0,0 +1,5 @@ +INTERNAL True +FUNCTION util_malloc +MIN_ARG 1 +MAX_ARG 1 +ARG_TYPE 0 FLOAT diff --git a/libres/lib/enkf/tests/data/workflow_jobs/CONF3@1.2.3 b/libres/lib/enkf/tests/data/workflow_jobs/CONF3@1.2.3 new file mode 100644 index 00000000000..c42a844b298 --- /dev/null +++ b/libres/lib/enkf/tests/data/workflow_jobs/CONF3@1.2.3 @@ -0,0 +1,5 @@ +INTERNAL True +FUNCTION util_malloc +MIN_ARG 1 +MAX_ARG 1 +ARG_TYPE 0 FLOAT diff --git a/libres/lib/enkf/tests/data/workflow_jobs/CONF4@1.2.0 b/libres/lib/enkf/tests/data/workflow_jobs/CONF4@1.2.0 new file mode 100644 index 00000000000..c42a844b298 --- /dev/null +++ b/libres/lib/enkf/tests/data/workflow_jobs/CONF4@1.2.0 @@ -0,0 +1,5 @@ +INTERNAL True +FUNCTION util_malloc +MIN_ARG 1 +MAX_ARG 1 +ARG_TYPE 0 FLOAT diff --git a/libres/lib/enkf/tests/data/workflow_jobs/CONF5@1.2 b/libres/lib/enkf/tests/data/workflow_jobs/CONF5@1.2 new file mode 100644 index 00000000000..c42a844b298 --- /dev/null +++ b/libres/lib/enkf/tests/data/workflow_jobs/CONF5@1.2 @@ -0,0 +1,5 @@ +INTERNAL True +FUNCTION util_malloc +MIN_ARG 1 +MAX_ARG 1 +ARG_TYPE 0 FLOAT diff --git a/libres/lib/enkf/tests/data/workflow_jobs/CONF5@1.2.0 b/libres/lib/enkf/tests/data/workflow_jobs/CONF5@1.2.0 new file mode 100644 index 00000000000..679c0971bfb --- /dev/null +++ b/libres/lib/enkf/tests/data/workflow_jobs/CONF5@1.2.0 @@ -0,0 +1 @@ +Wrong .... diff --git a/libres/lib/enkf/tests/enkf_active_list.cpp b/libres/lib/enkf/tests/enkf_active_list.cpp new file mode 100644 index 00000000000..f12a0ea0d41 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_active_list.cpp @@ -0,0 +1,62 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_active_list.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 + + + +int main(int argc , char ** argv) { + active_list_type * active_list1 = active_list_alloc( ); + active_list_type * active_list2 = active_list_alloc( ); + + + test_assert_true( active_list_is_instance( active_list1 )); + + test_assert_true( active_list_equal( active_list1 , active_list2 )); + + active_list_add_index( active_list1 , 11 ); + test_assert_false(active_list_equal( active_list1 , active_list2 )); + + active_list_add_index( active_list1 , 12 ); + test_assert_false(active_list_equal( active_list1 , active_list2 )); + + active_list_add_index( active_list2 , 11 ); + test_assert_false(active_list_equal( active_list1 , active_list2 )); + + active_list_add_index( active_list2 , 12 ); + test_assert_true(active_list_equal( active_list1 , active_list2 )); + + active_list_add_index( active_list2 , 13 ); + test_assert_false(active_list_equal( active_list1 , active_list2 )); + + active_list_add_index( active_list1 , 13 ); + test_assert_true(active_list_equal( active_list1 , active_list2 )); + + active_list_add_index( active_list2 , 27 ); + test_assert_false(active_list_equal( active_list1 , active_list2 )); + active_list_copy( active_list1 , active_list2 ); + test_assert_true(active_list_equal( active_list1 , active_list2 )); + + active_list_free( active_list1 ); + active_list_free( active_list2 ); + exit(0); +} + diff --git a/libres/lib/enkf/tests/enkf_analysis_config.cpp b/libres/lib/enkf/tests/enkf_analysis_config.cpp new file mode 100644 index 00000000000..4e96ee13126 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_analysis_config.cpp @@ -0,0 +1,203 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_analysis_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 + +#include + +#include +#include + + +analysis_config_type * create_analysis_config() { + analysis_config_type * ac = analysis_config_alloc_default(); + return ac; +} + + +void test_create() { + analysis_config_type * ac = create_analysis_config( ); + test_assert_true( analysis_config_is_instance( ac ) ); + analysis_config_free( ac ); +} + + +void test_min_realizations(const char * num_realizations_str, const char * min_realizations_str, int min_realizations_expected_needed) { + ecl::util::TestArea ta("min_realizations"); + { + FILE * config_file_stream = util_mkdir_fopen("config_file", "w"); + test_assert_not_NULL(config_file_stream); + + fputs(num_realizations_str, config_file_stream); + fputs(min_realizations_str, config_file_stream); + fclose(config_file_stream); + + config_parser_type * c = config_alloc(); + config_schema_item_type * item = config_add_schema_item(c , NUM_REALIZATIONS_KEY , true ); + config_schema_item_set_default_type(item, CONFIG_INT); + config_schema_item_set_argc_minmax( item , 1 , 1); + + item = config_add_schema_item(c , MIN_REALIZATIONS_KEY , false ); + config_schema_item_set_argc_minmax( item , 1 , 2); + { + config_content_type * content = config_parse(c , "config_file" , "--" , NULL , NULL , NULL , CONFIG_UNRECOGNIZED_IGNORE, true); + test_assert_true(config_content_is_valid(content)); + + analysis_config_type * ac = create_analysis_config( ); + analysis_config_init(ac, content); + + int num_realizations = config_content_get_value_as_int(content, NUM_REALIZATIONS_KEY); + + test_assert_false(analysis_config_have_enough_realisations(ac, min_realizations_expected_needed - 1, num_realizations )); + test_assert_true(analysis_config_have_enough_realisations(ac, min_realizations_expected_needed, num_realizations )); + test_assert_true(analysis_config_have_enough_realisations(ac, min_realizations_expected_needed + 1, num_realizations )); + + int min_realizations = analysis_config_get_min_realisations(ac); + if(min_realizations > 0) + { + test_assert_true(analysis_config_have_enough_realisations(ac, min_realizations - 1, min_realizations - 2)); + } + analysis_config_free( ac ); + config_content_free( content ); + config_free( c ); + } + } +} + + +void test_have_enough_realisations_defaulted( ) { + analysis_config_type * ac = create_analysis_config( ); + int ensemble_size = 20; + + // min_realizations not set, should then require 20 (ensemble_size) + test_assert_false( analysis_config_have_enough_realisations( ac , 0, ensemble_size )); + test_assert_false( analysis_config_have_enough_realisations( ac , 10, ensemble_size )); + test_assert_true( analysis_config_have_enough_realisations( ac , 20, ensemble_size )); + + analysis_config_free( ac ); +} + + +void test_current_module_options() { + analysis_config_type * ac = create_analysis_config( ); + test_assert_NULL( analysis_config_get_active_module( ac )); + analysis_config_load_internal_module(ac , "STD_ENKF"); + + test_assert_false( analysis_config_get_module_option( ac , ANALYSIS_SCALE_DATA)); + test_assert_true(analysis_config_select_module(ac , "STD_ENKF")); + test_assert_false( analysis_config_select_module(ac , "DOES_NOT_EXIST")); + + test_assert_true( analysis_module_is_instance( analysis_config_get_active_module( ac ))); + test_assert_true( analysis_config_get_module_option( ac , ANALYSIS_SCALE_DATA)); + test_assert_false( analysis_config_get_module_option( ac , ANALYSIS_ITERABLE)); + analysis_config_free( ac ); +} + +void test_stop_long_running( ) { + analysis_config_type * ac = create_analysis_config( ); + test_assert_bool_equal( false , analysis_config_get_stop_long_running( ac ) ); + analysis_config_set_stop_long_running( ac , true ); + test_assert_bool_equal( true , analysis_config_get_stop_long_running( ac ) ); + analysis_config_free( ac ); +} + +void test_min_realizations_percent() { + { + const char * num_realizations_str = "NUM_REALIZATIONS 80\n"; + const char * min_realizations_str = "MIN_REALIZATIONS 10%\n"; + int min_realizations_expected_needed = 8; + test_min_realizations(num_realizations_str, min_realizations_str, min_realizations_expected_needed); + } + { + const char * num_realizations_str = "NUM_REALIZATIONS 5\n"; + const char * min_realizations_str = "MIN_REALIZATIONS 2%\n"; + int min_realizations_expected_needed = 1; + test_min_realizations(num_realizations_str, min_realizations_str, min_realizations_expected_needed); + } + { + const char * num_realizations_str = "NUM_REALIZATIONS 8\n"; + const char * min_realizations_str = "MIN_REALIZATIONS 50%\n"; + int min_realizations_expected_needed = 4; + test_min_realizations(num_realizations_str, min_realizations_str, min_realizations_expected_needed ); + } + { + const char * num_realizations_str = "NUM_REALIZATIONS 8\n"; + const char * min_realizations_str = "MIN_REALIZATIONS 100%\n"; + int min_realizations_expected_needed = 8; + test_min_realizations(num_realizations_str, min_realizations_str, min_realizations_expected_needed ); + } + { + const char * num_realizations_str = "NUM_REALIZATIONS 8\n"; + const char * min_realizations_str = "MIN_REALIZATIONS 10%\n"; + int min_realizations_expected_needed = 1; // Expect 1 because 10 % of 8 will be calculated to 1 (ceiling). + test_min_realizations(num_realizations_str, min_realizations_str, min_realizations_expected_needed ); + } +} + +void test_min_realizations_number() { + { + const char * num_realizations_str = "NUM_REALIZATIONS 80\n"; + const char * min_realizations_str = "MIN_REALIZATIONS 0\n"; + int min_realizations_expected_needed = 80; + test_min_realizations(num_realizations_str, min_realizations_str, min_realizations_expected_needed); + } + { + const char * num_realizations_str = "NUM_REALIZATIONS 900\n"; + int min_realizations_expected_needed = 900; // Nothing specified, expect NUM_REALIZATIONS + test_min_realizations(num_realizations_str, "", min_realizations_expected_needed); + } + { + const char * num_realizations_str = "NUM_REALIZATIONS 900\n"; + const char * min_realizations_str = "MIN_REALIZATIONS 10 \n"; + int min_realizations_expected_needed = 10; + test_min_realizations(num_realizations_str, min_realizations_str, min_realizations_expected_needed); + } + { + const char * num_realizations_str = "NUM_REALIZATIONS 80\n"; + const char * min_realizations_str = "MIN_REALIZATIONS 50\n"; + int min_realizations_expected_needed = 50; + test_min_realizations(num_realizations_str, min_realizations_str, min_realizations_expected_needed); + } + { + const char * num_realizations_str = "NUM_REALIZATIONS 80\n"; + const char * min_realizations_str = "MIN_REALIZATIONS 80\n"; + int min_realizations_expected_needed = 80; + test_min_realizations(num_realizations_str, min_realizations_str, min_realizations_expected_needed); + } + { + const char * num_realizations_str = "NUM_REALIZATIONS 80\n"; + const char * min_realizations_str = "MIN_REALIZATIONS 100\n"; + int min_realizations_expected_needed = 80; + test_min_realizations(num_realizations_str, min_realizations_str, min_realizations_expected_needed); + } +} + +int main(int argc , char ** argv) { + test_create(); + test_have_enough_realisations_defaulted(); + test_min_realizations_percent(); + test_min_realizations_number(); + test_current_module_options(); + test_stop_long_running(); + exit(0); +} + diff --git a/libres/lib/enkf/tests/enkf_analysis_config_analysis_load.cpp b/libres/lib/enkf/tests/enkf_analysis_config_analysis_load.cpp new file mode 100644 index 00000000000..8df264b4fd0 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_analysis_config_analysis_load.cpp @@ -0,0 +1,43 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'enkf_analysis_config_analysis_load.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 + +int main(int argc , const char ** argv) { + util_install_signals(); + { + const char * config_file = argv[1]; + + ert_test_context_type * test_context = ert_test_context_alloc("AnalysisLoadFromSiteConfig" , config_file); + enkf_main_type * enkf_main = ert_test_context_get_main(test_context); + + test_assert_true(analysis_config_has_module(enkf_main_get_analysis_config(enkf_main), "RML_ENKF_SITE_CONFIG1")); + analysis_module_type * analysis_module = analysis_config_get_module(enkf_main_get_analysis_config(enkf_main), "RML_ENKF_SITE_CONFIG1"); + test_assert_string_equal(analysis_module_get_name(analysis_module), "RML_ENKF_SITE_CONFIG1"); + + test_assert_true(analysis_config_has_module(enkf_main_get_analysis_config(enkf_main), "RML_ENKF_SITE_CONFIG2")); + analysis_module_type * analysis_module2 = analysis_config_get_module(enkf_main_get_analysis_config(enkf_main), "RML_ENKF_SITE_CONFIG2"); + test_assert_string_equal(analysis_module_get_name(analysis_module2), "RML_ENKF_SITE_CONFIG2"); + + ert_test_context_free(test_context); + } +} + + diff --git a/libres/lib/enkf/tests/enkf_analysis_config_ext_module.cpp b/libres/lib/enkf/tests/enkf_analysis_config_ext_module.cpp new file mode 100644 index 00000000000..a7ae6fc892a --- /dev/null +++ b/libres/lib/enkf/tests/enkf_analysis_config_ext_module.cpp @@ -0,0 +1,47 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_analysis_config_ext_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 + + +void test_load_external_module( analysis_config_type * ac , const char * user_name , const char * lib_name) { + test_assert_true( analysis_config_load_external_module(ac , lib_name , user_name )); +} + + + +int main(int argc , char ** argv) { + analysis_config_type * analysis_config = analysis_config_alloc_default(); + + for (int i = 1; i < argc; i+= 2) { + const char * user_name = argv[i]; + const char * lib_name = argv[i + 1]; + test_load_external_module( analysis_config , user_name , lib_name ); + } + + analysis_config_free(analysis_config); + exit(0); +} + + + diff --git a/libres/lib/enkf/tests/enkf_analysis_update_job.cpp b/libres/lib/enkf/tests/enkf_analysis_update_job.cpp new file mode 100644 index 00000000000..4ba59c2f0d9 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_analysis_update_job.cpp @@ -0,0 +1,99 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_analysis_update_job.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 + + +void test_update_default(const char * config_file , const char * job_file) { + ert_test_context_type * test_context = ert_test_context_alloc("AnalysisJob0" , config_file); + + stringlist_type * args = stringlist_alloc_new(); + test_assert_true( ert_test_context_install_workflow_job( test_context , "JOB" , job_file )); + test_assert_true( ert_test_context_run_worklow_job( test_context , "JOB" , args) ); + stringlist_free( args ); + + ert_test_context_free( test_context ); +} + + +void test_update_new_case(const char * config_file , const char * job_file) { + ert_test_context_type * test_context = ert_test_context_alloc("AnalysisJob1" , config_file); + + stringlist_type * args = stringlist_alloc_new(); + stringlist_append_copy( args , "NewCase" ); + ert_test_context_install_workflow_job( test_context , "JOB" , job_file ); + test_assert_true( ert_test_context_run_worklow_job( test_context , "JOB" , args) ); + stringlist_free( args ); + + ert_test_context_free( test_context ); +} + + +void test_update_new_case_step(const char * config_file , const char * job_file) { + ert_test_context_type * test_context = ert_test_context_alloc("AnalysisJob2" , config_file); + + stringlist_type * args = stringlist_alloc_new(); + stringlist_append_copy( args , "NewCase" ); + stringlist_append_copy( args , "20" ); + ert_test_context_install_workflow_job( test_context , "JOB" , job_file ); + test_assert_true( ert_test_context_run_worklow_job( test_context , "JOB" , args) ); + stringlist_free( args ); + + ert_test_context_free( test_context ); +} + + +void test_update_new_case_step_selected(const char * config_file , const char * job_file) { + ert_test_context_type * test_context = ert_test_context_alloc("AnalysisJob2" , config_file ); + + stringlist_type * args = stringlist_alloc_new(); + stringlist_append_copy( args , "NewCase" ); + stringlist_append_copy( args , "20" ); + stringlist_append_copy( args , "10" ); + stringlist_append_copy( args , ",20" ); + stringlist_append_copy( args , ",30-50" ); + ert_test_context_install_workflow_job( test_context , "JOB" , job_file ); + test_assert_true( ert_test_context_run_worklow_job( test_context , "JOB" , args) ); + stringlist_free( args ); + + ert_test_context_free( test_context ); +} + + + +int main(int argc , char ** argv) { + const char * config_file = argv[1]; + const char * job_file = argv[2]; + + test_update_default( config_file , job_file); + test_update_new_case( config_file , job_file ); + test_update_new_case_step( config_file , job_file ); + test_update_new_case_step_selected( config_file , job_file ); + + exit(0); +} + diff --git a/libres/lib/enkf/tests/enkf_block_obs.cpp b/libres/lib/enkf/tests/enkf_block_obs.cpp new file mode 100644 index 00000000000..5055ebd6319 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_block_obs.cpp @@ -0,0 +1,78 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'enkf_plot_blockdata.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 + + +void test_create_invalid_data(ecl_grid_type * grid) { + void * data_config = NULL; + test_assert_NULL(block_obs_alloc( "ObsKey" , data_config , grid )); +} + + + +void test_create_from_field(ecl_grid_type * grid) { + field_config_type * field_config = field_config_alloc_empty( "PRESSURE" , grid , NULL, false ); + block_obs_type * block_obs = block_obs_alloc( "ObsKey" , field_config , grid ); + + test_assert_true( block_obs_is_instance( block_obs )); + test_assert_int_equal(0 , block_obs_get_size( block_obs )); + block_obs_append_field_obs( block_obs , 10 , 12 , 8 , 100 , 25); + test_assert_int_equal(1 , block_obs_get_size( block_obs )); + block_obs_append_field_obs( block_obs , 10 , 12 , 9 , 100 , 25); + test_assert_int_equal(2 , block_obs_get_size( block_obs )); + block_obs_free( block_obs ); + field_config_free( field_config ); +} + + +void test_create_from_summary(ecl_grid_type * grid) { + container_config_type * container_config = container_config_alloc( "Container"); + block_obs_type * block_obs = block_obs_alloc( "ObsKey" , container_config , grid ); + + test_assert_true( block_obs_is_instance( block_obs )); + test_assert_int_equal(0 , block_obs_get_size( block_obs )); + + + block_obs_append_summary_obs( block_obs , 10 , 12 , 8 , "BPR:111,13,9" , 100 , 25); + test_assert_int_equal(1 , block_obs_get_size( block_obs )); + block_obs_append_summary_obs( block_obs , 10 , 12 , 9 , "BPR:11,13,10" , 100 , 25); + test_assert_int_equal(2 , block_obs_get_size( block_obs )); + block_obs_free( block_obs ); + + container_config_free( container_config ); +} + + + +int main (int argc , char ** argv) { + ecl_grid_type * grid = ecl_grid_alloc( argv[1] ); + { + test_create_invalid_data( grid ); + test_create_from_field(grid); + test_create_from_summary( grid); + } + ecl_grid_free( grid ); + exit(0); +} diff --git a/libres/lib/enkf/tests/enkf_cases_config.cpp b/libres/lib/enkf/tests/enkf_cases_config.cpp new file mode 100644 index 00000000000..b376c511585 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_cases_config.cpp @@ -0,0 +1,39 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_cases_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 + + +void test_create_get_set_and_get() { + cases_config_type * cases_config = cases_config_alloc( ); + test_assert_int_equal( 0, cases_config_get_iteration_number( cases_config ) ); + cases_config_set_int( cases_config , "iteration_number" , 12); + test_assert_int_equal( 12, cases_config_get_iteration_number( cases_config ) ); + cases_config_fwrite( cases_config , "TEST_CASES_CONFIG" ); + cases_config_fread( cases_config , "TEST_CASES_CONFIG" ); + cases_config_free( cases_config ); +} + +int main(int argc , char ** argv) { + test_create_get_set_and_get(); + exit(0); +} + diff --git a/libres/lib/enkf/tests/enkf_config_node.cpp b/libres/lib/enkf/tests/enkf_config_node.cpp new file mode 100644 index 00000000000..99d4d721c9b --- /dev/null +++ b/libres/lib/enkf/tests/enkf_config_node.cpp @@ -0,0 +1,24 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'enkf_config_node.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. +*/ + +#include + + +int main(int argc , char ** argv) { + exit(0); +} diff --git a/libres/lib/enkf/tests/enkf_ecl_config.cpp b/libres/lib/enkf/tests/enkf_ecl_config.cpp new file mode 100644 index 00000000000..f394a812c76 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_ecl_config.cpp @@ -0,0 +1,50 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_ecl_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 + +int main(int argc , char ** argv) { + ecl_config_type * ecl_config = ecl_config_alloc(NULL); + + if (argc == 2) { + test_assert_true(ecl_config_load_refcase( ecl_config , argv[1])); + + ecl_refcase_list_type * refcase_list = ecl_config_get_refcase_list( ecl_config ); + test_assert_int_equal( ecl_refcase_list_get_size( refcase_list ) , 1 ); + { + const ecl_sum_type * iget0 = ecl_refcase_list_iget_case( refcase_list , 0 ); + const ecl_sum_type * def = ecl_refcase_list_get_default( refcase_list ); + + test_assert_ptr_equal( iget0 , def ); + test_assert_string_equal( argv[1] , ecl_sum_get_case( def )); + test_assert_string_equal( ecl_refcase_list_iget_pathcase( refcase_list , 0) , ecl_sum_get_case( def )); + + } + } + test_assert_false(ecl_config_load_refcase( ecl_config , "DOES_NOT_EXIST" )); + test_assert_true(ecl_config_load_refcase( ecl_config , NULL )); + + + + ecl_config_free( ecl_config ); + exit(0); +} + diff --git a/libres/lib/enkf/tests/enkf_ecl_config_config.cpp b/libres/lib/enkf/tests/enkf_ecl_config_config.cpp new file mode 100644 index 00000000000..ea55881cfd4 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_ecl_config_config.cpp @@ -0,0 +1,56 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_ecl_config_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 + +int main(int argc , char ** argv) { + util_install_signals(); + { + char * config_file = util_alloc_abs_path(argv[1]); + + ecl_config_type * ecl_config = ecl_config_alloc(NULL); + ecl_refcase_list_type * refcase_list = ecl_config_get_refcase_list( ecl_config ); + { + config_parser_type * config = config_alloc(); + config_content_type * content; + + ecl_config_add_config_items( config ); + content = config_parse( config , config_file , "--" , NULL , NULL , NULL , CONFIG_UNRECOGNIZED_WARN , true); + + test_assert_true( config_content_is_valid( content )); + ecl_config_init( ecl_config , content); + + config_content_free( content ); + config_free( config ); + } + + test_assert_true( ecl_config_has_refcase( ecl_config )); + test_assert_int_equal( ecl_refcase_list_get_size( refcase_list) , 17); + + ecl_config_free( ecl_config ); + free(config_file); + } + exit(0); +} + diff --git a/libres/lib/enkf/tests/enkf_enkf_config_node_gen_data.cpp b/libres/lib/enkf/tests/enkf_enkf_config_node_gen_data.cpp new file mode 100644 index 00000000000..95b0bdb542d --- /dev/null +++ b/libres/lib/enkf/tests/enkf_enkf_config_node_gen_data.cpp @@ -0,0 +1,36 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_enkf_config_node_gen_data.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 + + +void test_create() { + enkf_config_node_type * node = enkf_config_node_alloc_GEN_PARAM("key" , false, ASCII , ASCII , "init%d" , "out.txt"); + enkf_config_node_free( node ); +} + + + +int main( int argc , char **argv ) { + test_create(); + exit(0); +} diff --git a/libres/lib/enkf/tests/enkf_ensemble.cpp b/libres/lib/enkf/tests/enkf_ensemble.cpp new file mode 100644 index 00000000000..c64cc2930ff --- /dev/null +++ b/libres/lib/enkf/tests/enkf_ensemble.cpp @@ -0,0 +1,32 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_ensemble.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 + + +int main(int argc , char ** argv) { + ensemble_config_type * ensemble = ensemble_config_alloc(NULL, NULL, NULL); + ensemble_config_free( ensemble ); + exit(0); +} + diff --git a/libres/lib/enkf/tests/enkf_ensemble_GEN_PARAM.cpp b/libres/lib/enkf/tests/enkf_ensemble_GEN_PARAM.cpp new file mode 100644 index 00000000000..6f08df56de1 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_ensemble_GEN_PARAM.cpp @@ -0,0 +1,53 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_ensemble_GEN_PARAM.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 + + +int main(int argc , char ** argv) { + const char * config_file = argv[1]; + config_parser_type * config = config_alloc(); + config_content_type * content; + ensemble_config_type * ensemble = ensemble_config_alloc(NULL, NULL, NULL); + + enkf_config_node_add_GEN_PARAM_config_schema( config ); + + content = config_parse( config , config_file , "--" , NULL , NULL , NULL , CONFIG_UNRECOGNIZED_WARN , true ); + { + config_error_type * errors = config_content_get_errors( content ); + config_error_fprintf( errors , true , stdout ); + } + + test_assert_true( config_content_is_valid( content ) ); + + ensemble_config_init_GEN_PARAM( ensemble, content ); + + config_content_free( content ); + config_free( config ); + ensemble_config_free( ensemble ); + exit(0); +} + diff --git a/libres/lib/enkf/tests/enkf_ensemble_config.cpp b/libres/lib/enkf/tests/enkf_ensemble_config.cpp new file mode 100644 index 00000000000..3c7da426ea6 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_ensemble_config.cpp @@ -0,0 +1,46 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_gen_data_config_parse.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 + +void add_NULL_node( void * arg) { + ensemble_config_type * ens_config = ensemble_config_safe_cast( arg ); + ensemble_config_add_node( ens_config , NULL ); +} + + + +void test_abort_on_add_NULL() { + ensemble_config_type * ensemble_config = ensemble_config_alloc(NULL, NULL, NULL); + + test_assert_true( ensemble_config_is_instance( ensemble_config )); + test_assert_util_abort("ensemble_config_add_node" , add_NULL_node , ensemble_config ); + + ensemble_config_free( ensemble_config ); +} + + +int main( int argc , char ** argv) { + test_abort_on_add_NULL(); + exit(0); +} diff --git a/libres/lib/enkf/tests/enkf_ert_run_context.cpp b/libres/lib/enkf/tests/enkf_ert_run_context.cpp new file mode 100644 index 00000000000..5fa29cab158 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_ert_run_context.cpp @@ -0,0 +1,155 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'ert_run_context.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_create() { + bool_vector_type * iactive = bool_vector_alloc(10,true); + bool_vector_iset( iactive , 6 , false ); + bool_vector_iset( iactive , 8 , false ); + { + const int iter = 7; + enkf_fs_type * fs = NULL; + subst_list_type * subst_list = subst_list_alloc( NULL ); + path_fmt_type * runpath_fmt = path_fmt_alloc_directory_fmt("/tmp/path/%04d"); + ert_run_context_type * context = ert_run_context_alloc_ENSEMBLE_EXPERIMENT( fs, iactive , runpath_fmt , "job%d", subst_list , iter ); + + test_assert_true( ert_run_context_is_instance( context )); + test_assert_int_equal( 10 , ert_run_context_get_size( context )); + + { + run_arg_type * run_arg0 = ert_run_context_iget_arg( context , 0 ); + + test_assert_int_equal( iter , run_arg_get_iter( run_arg0 )); + test_assert_string_equal( "/tmp/path/0000" , run_arg_get_runpath( run_arg0 )); + + test_assert_true( run_arg_is_instance( run_arg0 )); + } + + test_assert_NULL( ert_run_context_iget_arg( context, 6 )); + test_assert_NULL( ert_run_context_iget_arg( context, 8 )); + ert_run_context_free( context ); + path_fmt_free( runpath_fmt ); + } + bool_vector_free( iactive ); +} + + +void test_create_ENSEMBLE_EXPERIMENT() { + bool_vector_type * iactive = bool_vector_alloc(10,true); + bool_vector_iset( iactive , 0 , false ); + bool_vector_iset( iactive , 8 , false ); + { + subst_list_type * subst_list = subst_list_alloc( NULL ); + path_fmt_type * runpath_fmt = path_fmt_alloc_directory_fmt("/tmp/path/%04d/%d"); + enkf_fs_type * fs = NULL; + ert_run_context_type * context = ert_run_context_alloc_ENSEMBLE_EXPERIMENT( fs, iactive , runpath_fmt , "Job%d", subst_list , 7 ); + + test_assert_true( ert_run_context_is_instance( context )); + test_assert_int_equal( 10 , ert_run_context_get_size( context )); + + { + run_arg_type * run_arg0 = ert_run_context_iens_get_arg( context , 0 ); + run_arg_type * run_arg2 = ert_run_context_iens_get_arg( context , 2 ); + run_arg_type * run_argi = ert_run_context_iget_arg( context , 1 ); + + test_assert_NULL( run_arg0 ); + test_assert_true( run_arg_is_instance( run_argi )); + test_assert_ptr_equal( run_arg2 , run_argi); + } + + { + run_arg_type * run_arg1 = ert_run_context_iget_arg( context , 1 ); + + test_assert_int_equal( 7 , run_arg_get_iter( run_arg1 )); + test_assert_string_equal( "/tmp/path/0001/7" , run_arg_get_runpath( run_arg1 )); + + test_assert_true( run_arg_is_instance( run_arg1 )); + } + + for (int i=0; i < ert_run_context_get_size( context ); i++) + test_assert_bool_equal( ert_run_context_iactive( context , i), bool_vector_iget( iactive, i)); + + ert_run_context_free( context ); + path_fmt_free( runpath_fmt ); + subst_list_free( subst_list ); + } + bool_vector_free( iactive ); +} + + + + +void test_iactive_update() { + subst_list_type * subst_list = subst_list_alloc( NULL ); + path_fmt_type * runpath_fmt = path_fmt_alloc_directory_fmt("/tmp/path/%04d/%d"); + enkf_fs_type * fs = NULL; + bool_vector_type * iactive = bool_vector_alloc(10,true); + ert_run_context_type * context = ert_run_context_alloc_ENSEMBLE_EXPERIMENT( fs, iactive , runpath_fmt , "Job%d", subst_list , 7 ); + + ert_run_context_deactivate_realization( context , 0 ); + ert_run_context_deactivate_realization( context , 5 ); + ert_run_context_deactivate_realization( context , 9 ); + + test_assert_not_NULL( ert_run_context_get_id( context )); + test_assert_int_equal( ert_run_context_get_active_size(context), 7); + + auto check_iactive = [](bool_vector_type const* iactive) { + test_assert_int_equal( bool_vector_count_equal( iactive , true ) , 7 ); + test_assert_false( bool_vector_iget( iactive , 0 )); + test_assert_false( bool_vector_iget( iactive , 5 )); + test_assert_false( bool_vector_iget( iactive , 9 )); + }; + + check_iactive(ert_run_context_get_iactive(context)); + + bool_vector_type * iactive2 = ert_run_context_alloc_iactive(context); + check_iactive(iactive2); + bool_vector_free( iactive2 ); + + path_fmt_free( runpath_fmt ); + subst_list_free( subst_list ); + bool_vector_free(iactive); + ert_run_context_free( context ); +} + + + +void test_create_CASE_INIT() { + bool_vector_type * iactive = bool_vector_alloc(100, true); + enkf_fs_type * sim_fs = NULL; + ert_run_context_type * context = ert_run_context_alloc_CASE_INIT(sim_fs, iactive); + ert_run_context_free( context ); + bool_vector_free( iactive ); +} + + +int main( int argc , char ** argv) { + util_install_signals(); + test_create(); + test_create_ENSEMBLE_EXPERIMENT(); + test_iactive_update(); + test_create_CASE_INIT(); + exit(0); +} diff --git a/libres/lib/enkf/tests/enkf_ert_test_context.cpp b/libres/lib/enkf/tests/enkf_ert_test_context.cpp new file mode 100644 index 00000000000..27f06f4541e --- /dev/null +++ b/libres/lib/enkf/tests/enkf_ert_test_context.cpp @@ -0,0 +1,144 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'enkf_ert_test_context.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 + + + + +void test_create_invalid(const char * config_file) { + ert_test_context_type * test_context = ert_test_context_alloc("CREATE_CONTEXT" , config_file ); + test_assert_NULL( test_context ); +} + + + +void test_create_valid( const char * config_file ) { + char * cwd0 = util_alloc_cwd(); + ert_test_context_type * test_context = ert_test_context_alloc("CREATE_CONTEXT" , config_file ); + test_assert_true( ert_test_context_is_instance( test_context )); + test_assert_true( enkf_main_is_instance( ert_test_context_get_main( test_context ))); + { + char * cwd1 = util_alloc_cwd(); + test_assert_string_not_equal(cwd1 , cwd0); + free( cwd1 ); + } + free( cwd0 ); + ert_test_context_free( test_context ); +} + + + +void test_install_job( const char * config_file, const char * job_file_OK , const char * job_file_ERROR) { + ert_test_context_type * test_context = ert_test_context_alloc("CREATE_CONTEXT_JOB" , config_file ); + + test_assert_false( ert_test_context_install_workflow_job( test_context , "JOB" , "File/does/not/exist")); + test_assert_false( ert_test_context_install_workflow_job( test_context , "ERROR" , job_file_ERROR)); + test_assert_true( ert_test_context_install_workflow_job( test_context , "OK" , job_file_OK)); + + ert_test_context_free( test_context ); +} + + + +void test_run_workflow_job( const char * config_file , const char * job_file ) { + ert_test_context_type * test_context = ert_test_context_alloc("CREATE_CONTEXT_JOB" , config_file ); + stringlist_type * args0 = stringlist_alloc_new( ); + stringlist_type * args1 = stringlist_alloc_new( ); + + stringlist_append_copy( args1 , "NewCase"); + test_assert_false( ert_test_context_run_worklow_job( test_context , "NO-this-does-not-exist" , args1)); + ert_test_context_install_workflow_job( test_context , "JOB" , job_file ); + + test_assert_false( ert_test_context_run_worklow_job( test_context , "JOB" , args0)); + test_assert_true( ert_test_context_run_worklow_job( test_context , "JOB" , args1)); + + stringlist_free( args0 ); + stringlist_free( args1 ); + ert_test_context_free( test_context ); +} + + +void test_install_workflow( const char * config_file , const char * job_file ) { + ert_test_context_type * test_context = ert_test_context_alloc("INSTALL_WORKFLOW" , config_file ); + const char * wf_file = "WFLOW"; + + ert_test_context_install_workflow_job( test_context , "JOB" , job_file ); + { + FILE * stream = util_fopen( wf_file , "w"); + stringlist_type * args = stringlist_alloc_new( ); + stringlist_append_copy( args , "NewCase"); + ert_test_context_fwrite_workflow_job( stream , "JOB" , args); + stringlist_free( args ); + fclose( stream ); + } + test_assert_true( ert_test_context_install_workflow( test_context , "WFLOW" , wf_file )); + ert_test_context_free( test_context ); +} + + +void test_run_workflow(const char * config_file , const char * job_file) { + ert_test_context_type * test_context = ert_test_context_alloc("INSTALL_WORKFLOW" , config_file ); + test_assert_false( ert_test_context_run_worklow( test_context , "No-does.not.exist")); + + ert_test_context_install_workflow_job( test_context , "JOB" , job_file ); + { + FILE * stream1 = util_fopen( "WFLOW1", "w"); + FILE * stream2 = util_fopen( "WFLOW2", "w"); + stringlist_type * args = stringlist_alloc_new( ); + ert_test_context_fwrite_workflow_job( stream1 , "JOB" , args); + stringlist_append_copy( args , "NewCase"); + ert_test_context_fwrite_workflow_job( stream2 , "JOB" , args); + + stringlist_free( args ); + fclose( stream1 ); + fclose( stream2 ); + } + test_assert_true( ert_test_context_install_workflow( test_context , "WFLOW1" , "WFLOW1")); + test_assert_true( ert_test_context_install_workflow( test_context , "WFLOW2" , "WFLOW2")); + + test_assert_true( ert_test_context_run_worklow( test_context , "WFLOW2")); + test_assert_false( ert_test_context_run_worklow( test_context , "WFLOW1")); + + ert_test_context_free( test_context ); +} + + + + + + +int main( int argc , char ** argv) { + char * config_file = argv[1]; + char * wf_job_fileOK = argv[2]; + char * wf_job_fileERROR = argv[3]; + + test_create_invalid( "DoesNotExist" ); + test_create_valid( config_file ); + test_install_job( config_file , wf_job_fileOK, wf_job_fileERROR ); + test_install_workflow( config_file , wf_job_fileOK); + test_run_workflow( config_file , wf_job_fileOK); + test_run_workflow_job( config_file , wf_job_fileOK); +} + + diff --git a/libres/lib/enkf/tests/enkf_ert_workflow_list.cpp b/libres/lib/enkf/tests/enkf_ert_workflow_list.cpp new file mode 100644 index 00000000000..bc44bdf44e2 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_ert_workflow_list.cpp @@ -0,0 +1,82 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'enkf_ert_workflow_list.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_create_workflow_list() { + ert_workflow_list_type * wf_list = ert_workflow_list_alloc_empty(NULL); + test_assert_true( ert_workflow_list_is_instance( wf_list )); + ert_workflow_list_free( wf_list ); +} + + + +void test_add_alias( const char * job) { + ecl::util::TestArea ta("alias"); + ert_workflow_list_type * wf_list = ert_workflow_list_alloc_empty(NULL); + ert_workflow_list_add_job( wf_list , "JOB" , job ); + + { + FILE * stream = util_fopen("WF1" , "w"); + fprintf(stream , "SCALE_STD 0.25\n"); + fclose(stream); + } + + + { + FILE * stream = util_fopen("WF2" , "w"); + fprintf(stream , "SCALE_STD 0.25\n"); + fclose(stream); + } + + test_assert_true( workflow_is_instance( ert_workflow_list_add_workflow( wf_list , "WF1" , "WF"))); + test_assert_int_equal( 1 , ert_workflow_list_get_size( wf_list )); + test_assert_false( ert_workflow_list_has_workflow( wf_list , "WF1")); + test_assert_true( ert_workflow_list_has_workflow( wf_list , "WF")); + + ert_workflow_list_add_alias( wf_list , "WF" , "alias"); + test_assert_int_equal( 2 , ert_workflow_list_get_size( wf_list )); + test_assert_true( ert_workflow_list_has_workflow( wf_list , "WF")); + test_assert_true( ert_workflow_list_has_workflow( wf_list , "alias")); + test_assert_true( workflow_is_instance( ert_workflow_list_get_workflow( wf_list , "WF"))); + test_assert_true( workflow_is_instance( ert_workflow_list_get_workflow( wf_list , "alias"))); + + test_assert_true( workflow_is_instance( ert_workflow_list_add_workflow( wf_list , "WF2" , "WF"))); + test_assert_int_equal( 2 , ert_workflow_list_get_size( wf_list )); + test_assert_true( ert_workflow_list_has_workflow( wf_list , "WF")); + test_assert_true( ert_workflow_list_has_workflow( wf_list , "alias")); + test_assert_true( workflow_is_instance( ert_workflow_list_get_workflow( wf_list , "WF"))); + test_assert_true( workflow_is_instance( ert_workflow_list_get_workflow( wf_list , "alias"))); +} + + +int main(int argc , char ** argv) { + const char * job = argv[1]; + test_create_workflow_list(); + test_add_alias(job); + exit(0); +} + diff --git a/libres/lib/enkf/tests/enkf_executable_path.cpp b/libres/lib/enkf/tests/enkf_executable_path.cpp new file mode 100644 index 00000000000..0da58a56cb9 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_executable_path.cpp @@ -0,0 +1,24 @@ +#include +#include +#include + +#include +#include + +int main() { + util_install_signals(); + ecl::util::TestArea ta("executable"); + const char * user_config_file = "path.txt"; + + config_parser_type * parser = config_alloc( ); + queue_config_add_config_items( parser, true ); + test_assert_true( config_has_schema_item( parser , JOB_SCRIPT_KEY ) ); + + FILE* stream = util_fopen(user_config_file, "w"); + fprintf(stream, "NUM_REALIZATIONS 14\n"); + fprintf(stream, "JOB_SCRIPT ls\n"); + fclose(stream); + + queue_config_alloc_load( user_config_file ); + return 0; +} diff --git a/libres/lib/enkf/tests/enkf_export_field_test.cpp b/libres/lib/enkf/tests/enkf_export_field_test.cpp new file mode 100644 index 00000000000..3b1b036f7b2 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_export_field_test.cpp @@ -0,0 +1,63 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_export_field_test.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 + + +void test_export_field(ert_test_context_type * test_context , const char * job_name , const char * job_file) { + + test_assert_true( ert_test_context_install_workflow_job( test_context , job_name , job_file )); + { + stringlist_type * args = stringlist_alloc_new(); + + stringlist_append_copy(args, "PERMZ"); + stringlist_append_copy(args, "TEST_EXPORT/test_export_field/PermZ%d.grdecl"); + stringlist_append_copy(args, "0"); + stringlist_append_copy(args, "FORECAST"); + stringlist_append_copy(args, "0, 2"); + + test_assert_true( ert_test_context_run_worklow_job( test_context , job_name , args) ); + stringlist_free( args ); + } + test_assert_true( util_file_exists("TEST_EXPORT/test_export_field/PermZ0.grdecl") ); + test_assert_true( util_file_exists("TEST_EXPORT/test_export_field/PermZ2.grdecl") ); +} + + +int main(int argc , const char ** argv) { + enkf_main_install_SIGNALS(); + + const char * config_file = argv[1]; + const char * job_file_export_field = argv[2]; + const char * job_file_export_field_ecl_grdecl = argv[3]; + const char * job_file_export_field_rms_roff = argv[4]; + + ert_test_context_type * test_context = ert_test_context_alloc("ExportFieldsJobs" , config_file); + enkf_main_type * enkf_main = ert_test_context_get_main( test_context ); + + enkf_main_select_fs( enkf_main , "default" ); + { + test_export_field(test_context, "JOB1" , job_file_export_field); + test_export_field(test_context, "JOB2" , job_file_export_field_ecl_grdecl); + test_export_field(test_context, "JOB3" , job_file_export_field_rms_roff); + } + ert_test_context_free( test_context ); + exit(0); +} diff --git a/libres/lib/enkf/tests/enkf_export_inactive_cells.cpp b/libres/lib/enkf/tests/enkf_export_inactive_cells.cpp new file mode 100644 index 00000000000..e4894d0ce25 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_export_inactive_cells.cpp @@ -0,0 +1,198 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'enkf_export_inactive_cells.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 check_exported_data(const char * exported_file, + const char * init_file, + field_file_format_type file_type, + const field_config_type * field_config, + const field_type * field, + int nx, + int ny, + int nz) { + + FILE * original_stream = NULL; + ecl_kw_type * kw_original = NULL; + FILE * exported_stream = NULL; + ecl_kw_type * kw_exported = NULL; + field_type * exported_field = NULL; + field_config_type * exported_field_config = NULL; + + { + if (init_file) { + original_stream = util_fopen( init_file , "r"); + kw_original = ecl_kw_fscanf_alloc_grdecl_dynamic( original_stream , field_config_get_key(field_config) , ECL_DOUBLE ); + } + + if (ECL_GRDECL_FILE == file_type) { + exported_stream = util_fopen( exported_file , "r"); + kw_exported = ecl_kw_fscanf_alloc_grdecl_dynamic( exported_stream , field_config_get_key(field_config) , ECL_DOUBLE ); + } else if (RMS_ROFF_FILE == file_type) { + ecl_grid_type * grid = field_config_get_grid(field_config); + exported_field_config = field_config_alloc_empty(field_config_get_key(field_config), grid, NULL, true); + exported_field = field_alloc(exported_field_config); + + bool keep_inactive = true; + field_fload_rms(exported_field, exported_file, keep_inactive); + } + } + + + { + int k, j, i = 0; + + for (k=0; k < nz; k++) { + for (j=0; j < ny; j++) { + for (i=0; i < nx; i++) { + bool active = field_config_active_cell(field_config, i, j, k); + double field_value = active ? field_ijk_get_double(field, i, j, k) : 0.0; + int global_index = field_config_global_index(field_config , i , j , k); + double exported_value = 0.0; + if (ECL_GRDECL_FILE == file_type) + exported_value = ecl_kw_iget_as_double(kw_exported, global_index); + else if (RMS_ROFF_FILE == file_type) { + exported_value = field_ijk_get_double(exported_field, i, j, k); + } + double initial_value = init_file ? ecl_kw_iget_as_double(kw_original, global_index) : 0.0; + + if (active) + test_assert_double_equal(field_value, exported_value); + else if (init_file) + test_assert_double_equal(initial_value, exported_value); + else if (file_type == RMS_ROFF_FILE) + test_assert_double_equal(RMS_INACTIVE_DOUBLE, exported_value); + else + test_assert_double_equal(0.0, exported_value); + } + } + } + } + + + if (init_file) { + fclose(original_stream); + ecl_kw_free(kw_original); + } + + if (ECL_GRDECL_FILE == file_type) { + fclose(exported_stream); + ecl_kw_free(kw_exported); + } else + field_free(exported_field); +} + + + +void forward_initialize_node(enkf_main_type * enkf_main, const char * init_file, enkf_node_type * field_node) { + enkf_fs_type * fs = enkf_main_get_fs(enkf_main); + subst_list_type * subst_list = subst_list_alloc(NULL); + { + const int ens_size = enkf_main_get_ensemble_size( enkf_main ); + bool_vector_type * iactive = bool_vector_alloc(ens_size, true); + const path_fmt_type * runpath_fmt = model_config_get_runpath_fmt( enkf_main_get_model_config( enkf_main )); + ert_run_context_type * run_context = ert_run_context_alloc_INIT_ONLY( fs, INIT_CONDITIONAL , iactive, runpath_fmt, subst_list , 0 ); + + enkf_main_create_run_path(enkf_main , run_context ); + bool_vector_free(iactive); + ert_run_context_free( run_context ); + } + + { + int iens = 0; + enkf_state_type * state = enkf_main_iget_state( enkf_main , iens ); + + run_arg_type * run_arg = run_arg_alloc_ENSEMBLE_EXPERIMENT( "RUN_ID", fs , 0 ,0 , "simulations/run0", "path", subst_list); + + ensemble_config_forward_init( enkf_state_get_ensemble_config( state ) , run_arg); + } + + subst_list_free(subst_list); +} + + + +int main(int argc , char ** argv) { + enkf_main_install_SIGNALS(); + + const char * config_file = argv[1]; + const char * init_file = argv[2]; + const char * key = "PORO"; + int iens = 0; + + ert_test_context_type * test_context = ert_test_context_alloc("ExportInactiveCellsTest" , config_file); + enkf_main_type * enkf_main = ert_test_context_get_main(test_context); + enkf_fs_type * fs = enkf_main_get_fs(enkf_main); + const ensemble_config_type * ensemble_config = enkf_main_get_ensemble_config(enkf_main); + enkf_config_node_type * config_node = ensemble_config_get_node(ensemble_config , key); + const field_config_type * field_config = (const field_config_type *)enkf_config_node_get_ref( config_node ); + enkf_node_type * field_node = enkf_node_alloc( config_node ); + field_type * field = (field_type *) enkf_node_value_ptr(field_node); + + { + forward_initialize_node(enkf_main, init_file, field_node); + node_id_type node_id = {.report_step = 0 , .iens = iens }; + test_assert_true(enkf_node_try_load(field_node , fs , node_id)); + field_scale(field, 3.0); + } + + int nx,ny,nz; + field_config_get_dims(field_config , &nx , &ny , &nz); + const char * export_file_grdecl = "my_test_dir/exported_field_test_file_grdecl"; + const char * export_file_roff = "my_test_dir/exported_field_test_file_roff"; + field_file_format_type file_type; + model_config_type * mc = enkf_main_get_model_config(enkf_main); + path_fmt_type * runpath_fmt = model_config_get_runpath_fmt(mc); + const char * found_init_file = enkf_config_node_get_FIELD_fill_file(config_node, runpath_fmt); + { + file_type = ECL_GRDECL_FILE; + field_export(field, export_file_grdecl, NULL, file_type, false, found_init_file); + check_exported_data(export_file_grdecl, init_file, file_type, field_config, field, nx, ny, nz); + } + { + file_type = RMS_ROFF_FILE; + field_export(field, export_file_roff, NULL, file_type, false, found_init_file); + check_exported_data(export_file_roff, init_file, file_type, field_config, field, nx, ny, nz); + } + + found_init_file = NULL; + { + file_type = ECL_GRDECL_FILE; + field_export(field, export_file_grdecl, NULL, file_type, false, found_init_file); + check_exported_data(export_file_grdecl, found_init_file, file_type, field_config, field, nx, ny, nz); + } + { + file_type = RMS_ROFF_FILE; + field_export(field, export_file_roff, NULL, file_type, false, found_init_file); + check_exported_data(export_file_roff, found_init_file, file_type, field_config, field, nx, ny, nz); + } + + + + ert_test_context_free(test_context); + exit(0); +} + diff --git a/libres/lib/enkf/tests/enkf_forward_init_FIELD.cpp b/libres/lib/enkf/tests/enkf_forward_init_FIELD.cpp new file mode 100644 index 00000000000..0eb7ce6d7a0 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_forward_init_FIELD.cpp @@ -0,0 +1,191 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_forward_init_FIELD.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 + +void create_runpath(enkf_main_type * enkf_main, int iter) { + const int ens_size = enkf_main_get_ensemble_size( enkf_main ); + bool_vector_type * iactive = bool_vector_alloc(ens_size,true); + const path_fmt_type * runpath_fmt = model_config_get_runpath_fmt( enkf_main_get_model_config( enkf_main )); + const subst_list_type * subst_list = subst_config_get_subst_list(enkf_main_get_subst_config(enkf_main)); + enkf_fs_type * fs = enkf_main_get_fs(enkf_main); + ert_run_context_type * run_context = ert_run_context_alloc_INIT_ONLY( fs, INIT_CONDITIONAL, iactive, runpath_fmt, subst_list , 0 ); + + enkf_main_create_run_path(enkf_main , run_context ); + ert_run_context_free( run_context ); + bool_vector_free(iactive); +} + + + +void install_file( const ecl::util::TestArea& ta, const char * input_src_file ) { + if (util_is_abs_path( input_src_file)) + return; + else { + std::string src_file = ta.original_path(input_src_file); + char * src_path; + + util_alloc_file_components(input_src_file, &src_path, NULL, NULL); + + if (!util_entry_exists( src_path )) + util_make_path( src_path ); + + if (util_file_exists( src_file.c_str() )) { + char * target_file = util_alloc_filename( ta.test_cwd().c_str(), input_src_file, NULL ); + util_copy_file( src_file.c_str() , target_file ); + free( target_file ); + } + + free(src_path); + } +} + + + + + + +int main(int argc , char ** argv) { + enkf_main_install_SIGNALS(); + const char * root_path = argv[1]; + const char * config_file = argv[2]; + const char * init_file = argv[3]; + const char * forward_init_string = argv[4]; + ecl::util::TestArea ta("FIELD"); + ta.copy_directory_content(root_path); + install_file(ta, init_file); + + { + bool forward_init; + bool strict = true; + enkf_main_type * enkf_main; + + test_assert_true( util_sscanf_bool( forward_init_string , &forward_init)); + + util_clear_directory( "Storage" , true , true ); + res_config_type * res_config = res_config_alloc_load(config_file); + enkf_main = enkf_main_alloc(res_config, strict, true); + { + ensemble_config_type * ens_config = enkf_main_get_ensemble_config( enkf_main ); + enkf_config_node_type * field_config_node = ensemble_config_get_node( ens_config , "PORO" ); + { + char * init_file1 = enkf_config_node_alloc_initfile( field_config_node , NULL , 0); + char * init_file2 = enkf_config_node_alloc_initfile( field_config_node , "/tmp", 0); + + test_assert_bool_equal( enkf_config_node_use_forward_init( field_config_node ) , forward_init ); + test_assert_string_equal( init_file1 , "petro.grdecl"); + test_assert_string_equal( init_file2 , "/tmp/petro.grdecl"); + + free( init_file1 ); + free( init_file2 ); + } + rng_type * rng = rng_alloc( MZRAN , INIT_DEFAULT ); + enkf_node_type * field_node = enkf_node_alloc( field_config_node ); + test_assert_bool_equal( enkf_node_use_forward_init( field_node ) , forward_init ); + if (forward_init) + test_assert_bool_not_equal( enkf_node_initialize( field_node , 0 ,rng) , forward_init); + // else hard_failure() + enkf_node_free( field_node ); + rng_free( rng ); + } + test_assert_bool_equal( forward_init, ensemble_config_have_forward_init( enkf_main_get_ensemble_config( enkf_main ))); + + if (forward_init) { + ensemble_config_type * ens_config = enkf_main_get_ensemble_config( enkf_main ); + const enkf_config_node_type * field_config_node = ensemble_config_get_node( ens_config , "PORO" ); + enkf_fs_type * fs = enkf_main_get_fs( enkf_main ); + enkf_node_type * field_node = enkf_node_alloc( field_config_node ); + const subst_list_type * subst_list = subst_config_get_subst_list(enkf_main_get_subst_config(enkf_main)); + run_arg_type * run_arg = run_arg_alloc_ENSEMBLE_EXPERIMENT( "run_id", fs, 0 ,0 , "simulations/run0", "BASE", subst_list); + node_id_type node_id = {.report_step = 0 , + .iens = 0 }; + + create_runpath( enkf_main, 0 ); + test_assert_true( util_is_directory( "simulations/run0" )); + + { + int result; + stringlist_type * msg_list = stringlist_alloc_new(); + + + test_assert_false( enkf_node_has_data( field_node , fs, node_id )); + + util_unlink_existing( "simulations/run0/petro.grdecl" ); + + test_assert_false(enkf_node_forward_init(field_node, "simulations/run0", 0)); + enkf_state_type * state = enkf_main_iget_state( enkf_main , 0 ); + result = ensemble_config_forward_init(ens_config, run_arg); + test_assert_true(LOAD_FAILURE & result); + + result = 0; + { + enkf_fs_type * fs = enkf_main_get_fs(enkf_main); + state_map_type * state_map = enkf_fs_get_state_map(fs); + state_map_iset(state_map, 0, STATE_INITIALIZED); + } + result = enkf_state_load_from_forward_model(state, run_arg , msg_list); + stringlist_free(msg_list); + test_assert_true(LOAD_FAILURE & result); + } + + + util_copy_file( init_file , "simulations/run0/petro.grdecl"); + { + int result; + stringlist_type * msg_list = stringlist_alloc_new(); + enkf_state_type * state = enkf_main_iget_state( enkf_main , 0 ); + + test_assert_true( enkf_node_forward_init( field_node , "simulations/run0" , 0)); + result = ensemble_config_forward_init( ens_config , run_arg); + test_assert_int_equal( result, 0 ); + result = enkf_state_load_from_forward_model( state , run_arg , msg_list ); + + stringlist_free( msg_list ); + test_assert_int_equal(result , 0); + + { + double value; + test_assert_true( enkf_node_user_get( field_node , fs , "5,5,5" , node_id , &value)); + test_assert_double_equal( 0.28485405445 , value); + } + } + util_clear_directory( "simulations" , true , true ); + create_runpath( enkf_main, 0 ); + test_assert_true( util_is_directory( "simulations/run0" )); + test_assert_true( util_is_file( "simulations/run0/PORO.grdecl" )); + test_assert_true( enkf_node_fload( field_node , "simulations/run0/PORO.grdecl")); + { + double value; + test_assert_true( enkf_node_user_get( field_node , fs , "4,4,4" , node_id , &value)); + test_assert_double_equal( 0.130251303315 , value); + } + util_clear_directory( "simulations" , true , true ); + run_arg_free( run_arg ); + } + enkf_main_free( enkf_main ); + res_config_free(res_config); + } +} + diff --git a/libres/lib/enkf/tests/enkf_forward_init_GEN_KW.cpp b/libres/lib/enkf/tests/enkf_forward_init_GEN_KW.cpp new file mode 100644 index 00000000000..7cded22698a --- /dev/null +++ b/libres/lib/enkf/tests/enkf_forward_init_GEN_KW.cpp @@ -0,0 +1,169 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_forward_init_GEN_KW.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 + + +void create_runpath(enkf_main_type * enkf_main, int iter ) { + const int ens_size = enkf_main_get_ensemble_size( enkf_main ); + bool_vector_type * iactive = bool_vector_alloc(ens_size,true); + const path_fmt_type * runpath_fmt = model_config_get_runpath_fmt( enkf_main_get_model_config( enkf_main )); + const subst_list_type * subst_list = subst_config_get_subst_list(enkf_main_get_subst_config(enkf_main));; + enkf_fs_type * fs = enkf_main_get_fs(enkf_main); + ert_run_context_type * run_context = ert_run_context_alloc_INIT_ONLY( fs, INIT_CONDITIONAL, iactive, runpath_fmt, subst_list , iter ); + + enkf_main_create_run_path(enkf_main , run_context); + ert_run_context_free( run_context ); + bool_vector_free(iactive); +} + + +int main(int argc , char ** argv) { + enkf_main_install_SIGNALS(); + rng_type * rng = rng_alloc( MZRAN , INIT_DEFAULT ); + const char * root_path = argv[1]; + const char * config_file = argv[2]; + const char * forward_init_string = argv[3]; + ecl::util::TestArea ta("GEN_KW"); + ta.copy_directory_content(root_path); + { + bool forward_init; + bool strict = true; + enkf_main_type * enkf_main; + + test_assert_true( util_sscanf_bool( forward_init_string , &forward_init)); + + util_clear_directory( "Storage" , true , true ); + res_config_type * res_config = res_config_alloc_load(config_file); + enkf_main = enkf_main_alloc(res_config, strict, true); + { + const enkf_config_node_type * gen_kw_config_node = ensemble_config_get_node( enkf_main_get_ensemble_config( enkf_main ) , "MULTFLT" ); + enkf_node_type * gen_kw_node = enkf_node_alloc( gen_kw_config_node ); + { + + char * init_file1 = enkf_config_node_alloc_initfile( gen_kw_config_node , NULL , 0); + char * init_file2 = enkf_config_node_alloc_initfile( gen_kw_config_node , "/tmp", 0); + + test_assert_bool_equal( enkf_config_node_use_forward_init( gen_kw_config_node ) , forward_init ); + test_assert_string_equal( init_file1 , "MULTFLT_INIT"); + test_assert_string_equal( init_file2 , "/tmp/MULTFLT_INIT"); + + free( init_file1 ); + free( init_file2 ); + } + + test_assert_bool_equal( enkf_node_use_forward_init( gen_kw_node ) , forward_init ); + if (forward_init) + test_assert_bool_not_equal( enkf_node_initialize( gen_kw_node , 0 , rng) , forward_init); + // else hard_failure() + enkf_node_free( gen_kw_node ); + } + test_assert_bool_equal( forward_init, ensemble_config_have_forward_init( enkf_main_get_ensemble_config( enkf_main ))); + + if (forward_init) { + const ensemble_config_type * ens_config = enkf_main_get_ensemble_config( enkf_main ); + enkf_state_type * state = enkf_main_iget_state( enkf_main , 0 ); + enkf_fs_type * fs = enkf_main_get_fs( enkf_main ); + const subst_list_type * subst_list = subst_config_get_subst_list(enkf_main_get_subst_config(enkf_main)); + run_arg_type * run_arg = run_arg_alloc_ENSEMBLE_EXPERIMENT("run_id", fs , 0 , 0 , "simulations/run0", "BASE", subst_list); + const enkf_config_node_type * gen_kw_config_node = ensemble_config_get_node( enkf_main_get_ensemble_config( enkf_main ), "MULTFLT"); + enkf_node_type * gen_kw_node = enkf_node_alloc( gen_kw_config_node ); + node_id_type node_id = {.report_step = 0 , + .iens = 0 }; + + create_runpath( enkf_main, 0 ); + test_assert_true( util_is_directory( "simulations/run0" )); + + { + int error; + stringlist_type * msg_list = stringlist_alloc_new(); + bool_vector_type * iactive = bool_vector_alloc( enkf_main_get_ensemble_size( enkf_main ) , true); + + test_assert_false( enkf_node_has_data( gen_kw_node , fs, node_id )); + util_unlink_existing( "simulations/run0/MULTFLT_INIT" ); + + + test_assert_false( enkf_node_forward_init( gen_kw_node , "simulations/run0" , 0 )); + error = ensemble_config_forward_init( ens_config , run_arg ); + test_assert_true(LOAD_FAILURE & error); + + { + enkf_fs_type * fs = enkf_main_get_fs( enkf_main ); + state_map_type * state_map = enkf_fs_get_state_map(fs); + state_map_iset(state_map , 0 , STATE_INITIALIZED); + } + error = enkf_state_load_from_forward_model( state , run_arg , msg_list ); + stringlist_free( msg_list ); + bool_vector_free( iactive ); + test_assert_true(LOAD_FAILURE & error); + } + + + + { + FILE * stream = util_fopen("simulations/run0/MULTFLT_INIT" , "w"); + fprintf(stream , "123456.0\n" ); + fclose( stream ); + } + + { + int error; + stringlist_type * msg_list = stringlist_alloc_new(); + + test_assert_true( enkf_node_forward_init( gen_kw_node , "simulations/run0" , 0 )); + error = ensemble_config_forward_init( ens_config , run_arg ); + test_assert_int_equal(0, error); + error = enkf_state_load_from_forward_model( state , run_arg , msg_list ); + + stringlist_free( msg_list ); + test_assert_int_equal(0, error); + + { + double value; + test_assert_true( enkf_node_user_get( gen_kw_node , fs , "MULTFLT" , node_id , &value)); + test_assert_double_equal( 123456.0 , value); + } + } + + util_clear_directory( "simulations" , true , true ); + create_runpath( enkf_main, 0 ); + test_assert_true( util_is_directory( "simulations/run0" )); + test_assert_true( util_is_file( "simulations/run0/MULTFLT.INC" )); + { + FILE * stream = util_fopen("simulations/run0/MULTFLT.INC" , "r"); + double value; + fscanf(stream , "%lg" , &value); + fclose( stream ); + test_assert_double_equal( 123456.0 , value); + } + util_clear_directory( "simulations" , true , true ); + run_arg_free( run_arg ); + enkf_node_free( gen_kw_node ); + } + enkf_main_free( enkf_main ); + res_config_free(res_config); + } + rng_free( rng ); +} diff --git a/libres/lib/enkf/tests/enkf_forward_init_GEN_PARAM.cpp b/libres/lib/enkf/tests/enkf_forward_init_GEN_PARAM.cpp new file mode 100644 index 00000000000..da675cf34df --- /dev/null +++ b/libres/lib/enkf/tests/enkf_forward_init_GEN_PARAM.cpp @@ -0,0 +1,163 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_forward_init_GEN_PARAM.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 + + +void create_runpath(enkf_main_type * enkf_main, int iter ) { + const int ens_size = enkf_main_get_ensemble_size( enkf_main ); + bool_vector_type * iactive = bool_vector_alloc(ens_size, true); + const path_fmt_type * runpath_fmt = model_config_get_runpath_fmt( enkf_main_get_model_config( enkf_main )); + const subst_list_type * subst_list = subst_config_get_subst_list(enkf_main_get_subst_config(enkf_main)); + enkf_fs_type * fs = enkf_main_get_fs(enkf_main); + ert_run_context_type * run_context = ert_run_context_alloc_INIT_ONLY( fs, INIT_CONDITIONAL, iactive, runpath_fmt, subst_list , iter ); + + enkf_main_create_run_path(enkf_main , run_context); + bool_vector_free(iactive); + ert_run_context_free( run_context ); +} + + + +int main(int argc , char ** argv) { + enkf_main_install_SIGNALS(); + rng_type * rng = rng_alloc( MZRAN , INIT_DEFAULT ); + const char * root_path = argv[1]; + const char * config_file = argv[2]; + const char * forward_init_string = argv[3]; + ecl::util::TestArea ta("main"); + ta.copy_directory_content(root_path); + { + bool forward_init; + bool strict = true; + enkf_main_type * enkf_main; + + test_assert_true( util_sscanf_bool( forward_init_string , &forward_init)); + + util_clear_directory( "Storage" , true , true ); + res_config_type * res_config = res_config_alloc_load(config_file); + enkf_main = enkf_main_alloc(res_config, strict, true); + { + const enkf_config_node_type * config_node = ensemble_config_get_node( enkf_main_get_ensemble_config( enkf_main ) , "PARAM" ); + enkf_node_type * gen_param_node = enkf_node_alloc( config_node ); + { + const enkf_config_node_type * gen_param_config_node = enkf_node_get_config( gen_param_node ); + char * init_file1 = enkf_config_node_alloc_initfile( gen_param_config_node , NULL , 0); + char * init_file2 = enkf_config_node_alloc_initfile( gen_param_config_node , "/tmp", 0); + + test_assert_bool_equal( enkf_config_node_use_forward_init( gen_param_config_node ) , forward_init ); + test_assert_string_equal( init_file1 , "PARAM_INIT"); + test_assert_string_equal( init_file2 , "/tmp/PARAM_INIT"); + + free( init_file1 ); + free( init_file2 ); + } + + test_assert_bool_equal( enkf_node_use_forward_init( gen_param_node ) , forward_init ); + if (forward_init) + test_assert_bool_not_equal( enkf_node_initialize( gen_param_node , 0 , rng), forward_init); + // else hard_failure() + enkf_node_free( gen_param_node ); + } + test_assert_bool_equal( forward_init, ensemble_config_have_forward_init( enkf_main_get_ensemble_config( enkf_main ))); + + if (forward_init) { + const ensemble_config_type * ens_config = enkf_main_get_ensemble_config( enkf_main ); + enkf_state_type * state = enkf_main_iget_state( enkf_main , 0 ); + const enkf_config_node_type * config_node = ensemble_config_get_node( enkf_main_get_ensemble_config( enkf_main ) , "PARAM" ); + enkf_node_type * gen_param_node = enkf_node_alloc( config_node ); + enkf_fs_type * fs = enkf_main_get_fs( enkf_main ); + const subst_list_type * subst_list = subst_config_get_subst_list(enkf_main_get_subst_config(enkf_main)); + run_arg_type * run_arg = run_arg_alloc_ENSEMBLE_EXPERIMENT( "run_id", fs , 0 , 0 , "simulations/run0", "BASE", subst_list); + + node_id_type node_id = {.report_step = 0 , + .iens = 0}; + + create_runpath( enkf_main, 0 ); + test_assert_true( util_is_directory( "simulations/run0" )); + + test_assert_false( enkf_node_has_data( gen_param_node , fs, node_id )); + util_unlink_existing( "simulations/run0/PARAM_INIT" ); + + { + FILE * stream = util_fopen("simulations/run0/PARAM_INIT" , "w"); + fprintf(stream , "0\n1\n2\n3\n" ); + fclose( stream ); + } + + { + int error; + stringlist_type * msg_list = stringlist_alloc_new(); + + test_assert_true( enkf_node_forward_init( gen_param_node , "simulations/run0" , 0 )); + + error = ensemble_config_forward_init( ens_config , run_arg ); + test_assert_int_equal(0, error); + { + enkf_fs_type * fs = enkf_main_get_fs( enkf_main ); + state_map_type * state_map = enkf_fs_get_state_map(fs); + state_map_iset(state_map , 0 , STATE_INITIALIZED); + } + error = enkf_state_load_from_forward_model( state , run_arg , msg_list ); + + stringlist_free( msg_list ); + test_assert_int_equal(0, error); + + { + double value; + test_assert_true( enkf_node_user_get( gen_param_node , fs , "0" , node_id , &value)); + test_assert_double_equal( 0 , value); + + test_assert_true( enkf_node_user_get( gen_param_node , fs , "1" , node_id , &value)); + test_assert_double_equal( 1 , value); + + test_assert_true( enkf_node_user_get( gen_param_node , fs , "2" , node_id , &value)); + test_assert_double_equal( 2 , value); + } + } + util_clear_directory( "simulations" , true , true ); + create_runpath( enkf_main, 0 ); + test_assert_true( util_is_directory( "simulations/run0" )); + test_assert_true( util_is_file( "simulations/run0/PARAM.INC" )); + { + FILE * stream = util_fopen("simulations/run0/PARAM.INC" , "r"); + double v0,v1,v2,v3; + fscanf(stream , "%lg %lg %lg %lg" , &v0,&v1,&v2,&v3); + fclose( stream ); + test_assert_double_equal( 0 , v0); + test_assert_double_equal( 1 , v1); + test_assert_double_equal( 2 , v2); + test_assert_double_equal( 3 , v3); + } + util_clear_directory( "simulations" , true , true ); + run_arg_free( run_arg ); + enkf_node_free( gen_param_node ); + } + enkf_main_free( enkf_main ); + res_config_free(res_config); + } + rng_free( rng ); +} + diff --git a/libres/lib/enkf/tests/enkf_forward_init_SURFACE.cpp b/libres/lib/enkf/tests/enkf_forward_init_SURFACE.cpp new file mode 100644 index 00000000000..4db40416a69 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_forward_init_SURFACE.cpp @@ -0,0 +1,189 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_forward_init_SURFACE.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 + +void create_runpath(enkf_main_type * enkf_main, int iter ) { + const int ens_size = enkf_main_get_ensemble_size( enkf_main ); + bool_vector_type * iactive = bool_vector_alloc(ens_size, true); + const path_fmt_type * runpath_fmt = model_config_get_runpath_fmt( enkf_main_get_model_config( enkf_main )); + const subst_list_type * subst_list = subst_config_get_subst_list(enkf_main_get_subst_config(enkf_main)); + enkf_fs_type * fs = enkf_main_get_fs(enkf_main); + ert_run_context_type * run_context = ert_run_context_alloc_INIT_ONLY( fs, INIT_CONDITIONAL, iactive, runpath_fmt, subst_list , iter ); + + enkf_main_create_run_path(enkf_main , run_context); + bool_vector_free(iactive); + ert_run_context_free( run_context ); +} + +void install_file( const ecl::util::TestArea& ta, const char * input_src_file ) { + if (util_is_abs_path( input_src_file)) + return; + else { + std::string src_file = ta.original_path(input_src_file); + char * src_path; + + util_alloc_file_components(input_src_file, &src_path, NULL, NULL); + + if (!util_entry_exists( src_path )) + util_make_path( src_path ); + + if (util_file_exists( src_file.c_str() )) { + char * target_file = util_alloc_filename( ta.test_cwd().c_str(), input_src_file, NULL ); + util_copy_file( src_file.c_str() , target_file ); + free( target_file ); + } + + free(src_path); + } +} + + + + +int main(int argc , char ** argv) { + enkf_main_install_SIGNALS(); + const char * root_path = argv[1]; + const char * config_file = argv[2]; + const char * init_file = argv[3]; + const char * forward_init_string = argv[4]; + + ecl::util::TestArea ta("surface"); + ta.copy_directory_content(root_path); + install_file(ta, init_file); + { + + bool forward_init; + bool strict = true; + enkf_main_type * enkf_main; + + test_assert_true( util_sscanf_bool( forward_init_string , &forward_init)); + + util_clear_directory( "Storage" , true , true ); + res_config_type * res_config = res_config_alloc_load(config_file); + enkf_main = enkf_main_alloc(res_config, strict, true); + ensemble_config_type * ens_config = enkf_main_get_ensemble_config( enkf_main ); + { + const enkf_config_node_type * surface_config_node = ensemble_config_get_node( ens_config , "SURFACE"); + enkf_node_type * surface_node = enkf_node_alloc( surface_config_node ); + char * init_file1 = enkf_config_node_alloc_initfile( surface_config_node , NULL , 0); + char * init_file2 = enkf_config_node_alloc_initfile( surface_config_node , "/tmp", 0); + + test_assert_bool_equal( enkf_config_node_use_forward_init( surface_config_node ) , forward_init ); + test_assert_string_equal( init_file1 , "Surface.irap"); + test_assert_string_equal( init_file2 , "/tmp/Surface.irap"); + + free( init_file1 ); + free( init_file2 ); + + rng_type * rng = rng_alloc( MZRAN, INIT_DEFAULT ); + test_assert_bool_equal( enkf_node_use_forward_init( surface_node ) , forward_init ); + if (forward_init) + test_assert_bool_not_equal( enkf_node_initialize( surface_node , 0 , rng) , forward_init); + // else hard_failure() + enkf_node_free( surface_node ); + rng_free( rng ); + } + test_assert_bool_equal( forward_init, ensemble_config_have_forward_init( enkf_main_get_ensemble_config( enkf_main ))); + + if (forward_init) { + const enkf_config_node_type * config_node = ensemble_config_get_node( ens_config , "SURFACE"); + enkf_state_type * state = enkf_main_iget_state( enkf_main , 0 ); + enkf_fs_type * fs = enkf_main_get_fs( enkf_main ); + const subst_list_type * subst_list = subst_config_get_subst_list(enkf_main_get_subst_config(enkf_main)); + run_arg_type * run_arg = run_arg_alloc_ENSEMBLE_EXPERIMENT( "run_id", fs , 0 ,0 , "simulations/run0", "BASE", subst_list); + enkf_node_type * surface_node = enkf_node_alloc( config_node ); + node_id_type node_id = {.report_step = 0 , + .iens = 0 }; + + create_runpath( enkf_main, 0 ); + test_assert_true( util_is_directory( "simulations/run0" )); + + { + int error; + stringlist_type * msg_list = stringlist_alloc_new(); + + + test_assert_false( enkf_node_has_data( surface_node , fs, node_id )); + + util_unlink_existing( "simulations/run0/Surface.irap" ); + + test_assert_false( enkf_node_forward_init( surface_node , "simulations/run0" , 0 )); + error = ensemble_config_forward_init( ens_config , run_arg ); + test_assert_true(LOAD_FAILURE & error); + + { + enkf_fs_type * fs = enkf_main_get_fs(enkf_main); + state_map_type * state_map = enkf_fs_get_state_map(fs); + state_map_iset(state_map, 0, STATE_INITIALIZED); + } + error = enkf_state_load_from_forward_model(state, run_arg , msg_list); + stringlist_free( msg_list ); + test_assert_true(LOAD_FAILURE & error); + } + + + util_copy_file( init_file , "simulations/run0/Surface.irap"); + { + int error; + stringlist_type * msg_list = stringlist_alloc_new(); + + + test_assert_true( enkf_node_forward_init( surface_node , "simulations/run0" , 0 )); + error = ensemble_config_forward_init( ens_config , run_arg ); + test_assert_int_equal(0, error); + error = enkf_state_load_from_forward_model( state , run_arg , msg_list ); + stringlist_free( msg_list ); + test_assert_int_equal(0, error); + + { + double value; + test_assert_true( enkf_node_user_get( surface_node , fs , "0" , node_id , &value)); + test_assert_double_equal( 2735.7461 , value); + + test_assert_true( enkf_node_user_get( surface_node , fs , "5" , node_id , &value)); + test_assert_double_equal( 2737.0122 , value); + } + } + util_clear_directory( "simulations" , true , true ); + create_runpath( enkf_main, 0 ); + test_assert_true( util_is_directory( "simulations/run0" )); + test_assert_true( util_is_file( "simulations/run0/SURFACE.INC" )); + test_assert_true( enkf_node_fload( surface_node , "simulations/run0/SURFACE.INC")); + { + double value; + test_assert_true( enkf_node_user_get( surface_node , fs , "0" , node_id , &value)); + test_assert_double_equal( 2735.7461 , value); + + test_assert_true( enkf_node_user_get( surface_node , fs , "5" , node_id , &value)); + test_assert_double_equal( 2737.0122 , value); + } + util_clear_directory( "simulations" , true , true ); + } + enkf_main_free( enkf_main ); + res_config_free(res_config); + } +} + diff --git a/libres/lib/enkf/tests/enkf_forward_init_transform.cpp b/libres/lib/enkf/tests/enkf_forward_init_transform.cpp new file mode 100644 index 00000000000..efec2a953f6 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_forward_init_transform.cpp @@ -0,0 +1,137 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_forward_init_transform.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 + + +void create_runpath(enkf_main_type * enkf_main, int iter ) { + const int ens_size = enkf_main_get_ensemble_size( enkf_main ); + bool_vector_type * iactive = bool_vector_alloc(ens_size, true); + const path_fmt_type * runpath_fmt = model_config_get_runpath_fmt( enkf_main_get_model_config( enkf_main )); + const subst_list_type * subst_list = subst_config_get_subst_list(enkf_main_get_subst_config(enkf_main)); + enkf_fs_type * fs = enkf_main_get_fs(enkf_main); + ert_run_context_type * run_context = ert_run_context_alloc_INIT_ONLY( fs, INIT_CONDITIONAL, iactive, runpath_fmt, subst_list , iter ); + + enkf_main_create_run_path(enkf_main , run_context); + bool_vector_free(iactive); + ert_run_context_free( run_context ); +} + + +bool check_original_exported_data_equal(const enkf_node_type * field_node) { + FILE * original_stream = util_fopen( "petro.grdecl" , "r"); + ecl_kw_type * kw_original = ecl_kw_fscanf_alloc_grdecl_dynamic( original_stream , "PORO" , ECL_DOUBLE ); + + enkf_node_ecl_write(field_node, "tmp", NULL, 0); + FILE * exported_stream = util_fopen( "tmp/PORO.grdecl" , "r"); + ecl_kw_type * kw_exported = ecl_kw_fscanf_alloc_grdecl_dynamic( exported_stream , "PORO" , ECL_DOUBLE ); + + bool ret = ecl_kw_numeric_equal(kw_original, kw_exported, 1e-5 , 1e-5); + + fclose(original_stream); + fclose(exported_stream); + ecl_kw_free(kw_original); + ecl_kw_free(kw_exported); + + return ret; +} + + +void install_file( const ecl::util::TestArea& ta, const char * input_src_file ) { + if (util_is_abs_path( input_src_file)) + return; + else { + std::string src_file = ta.original_path(input_src_file); + char * src_path; + + util_alloc_file_components(input_src_file, &src_path, NULL, NULL); + + if (!util_entry_exists( src_path )) + util_make_path( src_path ); + + if (util_file_exists( src_file.c_str() )) { + char * target_file = util_alloc_filename( ta.test_cwd().c_str(), input_src_file, NULL ); + util_copy_file( src_file.c_str() , target_file ); + free( target_file ); + } + + free(src_path); + } +} + + + + +int main(int argc , char ** argv) { + enkf_main_install_SIGNALS(); + const char * root_path = argv[1]; + const char * config_file = argv[2]; + const char * init_file = argv[3]; + const char * forward_init_string = argv[4]; + + ecl::util::TestArea ta("init_transform"); + ta.copy_directory_content(root_path); + install_file(ta, init_file); + + bool strict = true; + res_config_type * res_config = res_config_alloc_load(config_file); + enkf_main_type * enkf_main = enkf_main_alloc(res_config, strict, true); + ensemble_config_type * ens_config = enkf_main_get_ensemble_config( enkf_main ); + enkf_fs_type * init_fs = enkf_main_get_fs(enkf_main); + const subst_list_type * subst_list = subst_config_get_subst_list(enkf_main_get_subst_config(enkf_main)); + run_arg_type * run_arg = run_arg_alloc_ENSEMBLE_EXPERIMENT( "run_id", init_fs , 0 ,0 , "simulations/run0", "base", subst_list); + enkf_config_node_type * config_node = ensemble_config_get_node( ens_config , "PORO"); + enkf_node_type * field_node = enkf_node_alloc( config_node ); + + bool forward_init; + test_assert_true( util_sscanf_bool( forward_init_string , &forward_init)); + test_assert_bool_equal( enkf_node_use_forward_init( field_node ) , forward_init ); + + util_clear_directory( "Storage" , true , true ); + + create_runpath( enkf_main, 0 ); + test_assert_true( util_is_directory( "simulations/run0" )); + + if (forward_init) + util_copy_file( init_file , "simulations/run0/petro.grdecl"); + + { + enkf_state_type * state = enkf_main_iget_state( enkf_main , 0 ); + bool_vector_type * iactive = bool_vector_alloc( enkf_main_get_ensemble_size(enkf_main) , true); + int error; + stringlist_type * msg_list = stringlist_alloc_new(); + error = enkf_state_load_from_forward_model( state , run_arg , msg_list ); + stringlist_free( msg_list ); + bool_vector_free( iactive ); + test_assert_int_equal(error, 0); + } + + test_assert_true(check_original_exported_data_equal(field_node)); + + run_arg_free( run_arg ); + enkf_main_free(enkf_main); + res_config_free(res_config); +} + diff --git a/libres/lib/enkf/tests/enkf_forward_load_context.cpp b/libres/lib/enkf/tests/enkf_forward_load_context.cpp new file mode 100644 index 00000000000..1afd8c9b33b --- /dev/null +++ b/libres/lib/enkf/tests/enkf_forward_load_context.cpp @@ -0,0 +1,136 @@ +/* + Copyright (C) 2016 Equinor ASA, Norway. + + The file 'forward_load_context.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 + + +void test_update_result() { + forward_load_context_type * load_context = forward_load_context_alloc( NULL , false , NULL , NULL); + test_assert_int_equal( forward_load_context_get_result( load_context ) , 0 ); + forward_load_context_update_result( load_context , 1 ); + test_assert_int_equal( forward_load_context_get_result( load_context ) , 1 ); + + forward_load_context_update_result( load_context , 1 ); + test_assert_int_equal( forward_load_context_get_result( load_context ) , 1 ); + + forward_load_context_update_result( load_context , 2 ); + test_assert_int_equal( forward_load_context_get_result( load_context ) , 3 ); + + forward_load_context_update_result( load_context , 5 ); + test_assert_int_equal( forward_load_context_get_result( load_context ) , 7 ); + + forward_load_context_free( load_context ); +} + +void test_create() { + forward_load_context_type * load_context = forward_load_context_alloc( NULL , false , NULL , NULL ); + test_assert_true( forward_load_context_is_instance( load_context )); + forward_load_context_free( load_context ); +} + +void test_load_restart1() { + subst_list_type * subst_list = subst_list_alloc(NULL); + run_arg_type * run_arg = run_arg_alloc_ENSEMBLE_EXPERIMENT("run_id", NULL, 0 , 0 , "run", "BASE", subst_list); + ecl_config_type * ecl_config = ecl_config_alloc(NULL); + + forward_load_context_type * load_context = forward_load_context_alloc( run_arg , false , ecl_config , NULL ); + + test_assert_false( forward_load_context_load_restart_file( load_context , 10 )); + + forward_load_context_free( load_context ); + ecl_config_free( ecl_config ); + run_arg_free( run_arg ); + subst_list_free(subst_list); +} + + +void make_restart_mock( const char * path , const char * eclbase , int report_step) { + char * filename = ecl_util_alloc_filename( path , eclbase , ECL_RESTART_FILE , false , report_step ); + ecl_kw_type * kw = ecl_kw_alloc( "KW" , 100 , ECL_FLOAT); + fortio_type * f = fortio_open_writer( filename , false , true ); + ecl_kw_fwrite( kw , f ); + fortio_fclose( f ); + ecl_kw_free( kw ); + free( filename ); +} + +void test_load_restart2() { + ecl::util::TestArea ta("load_restart"); + { + subst_list_type * subst_list = subst_list_alloc(NULL); + run_arg_type * run_arg = run_arg_alloc_ENSEMBLE_EXPERIMENT("run_id", NULL , 0 , 0 , "run", "BASE", subst_list); + ecl_config_type * ecl_config = ecl_config_alloc(NULL); + forward_load_context_type * load_context = forward_load_context_alloc( run_arg , false , ecl_config , NULL ); + util_make_path("run"); + make_restart_mock( "run" , "BASE" , 1 ); + make_restart_mock( "run" , "BASE" , 3 ); + + test_assert_false( forward_load_context_load_restart_file( load_context , 0 )); + test_assert_true( forward_load_context_load_restart_file( load_context , 1 )); + test_assert_false( forward_load_context_load_restart_file( load_context , 2 )); + test_assert_true( forward_load_context_load_restart_file( load_context , 3 )); + + forward_load_context_free( load_context ); + ecl_config_free( ecl_config ); + run_arg_free( run_arg ); + subst_list_free(subst_list); + } +} + + + +void test_add_message() { + { + forward_load_context_type * load_context = forward_load_context_alloc( NULL , false , NULL , NULL ); + forward_load_context_add_message( load_context , "MESSAGE" ); + test_assert_false( forward_load_context_accept_messages( load_context )); + forward_load_context_free( load_context ); + } + + { + stringlist_type * message_list = stringlist_alloc_new( ); + forward_load_context_type * load_context = forward_load_context_alloc( NULL , false , NULL , message_list ); + + test_assert_true( forward_load_context_accept_messages( load_context )); + forward_load_context_add_message( load_context , "MESSAGE1" ); + forward_load_context_add_message( load_context , "MESSAGE2" ); + forward_load_context_free( load_context ); + + test_assert_int_equal( 2 , stringlist_get_size( message_list )); + test_assert_string_equal( stringlist_iget( message_list , 0 ) , "MESSAGE1" ); + test_assert_string_equal( stringlist_iget( message_list , 1 ) , "MESSAGE2" ); + stringlist_free( message_list ); + } +} + + + + +int main(int argc , char ** argv) { + util_install_signals(); + test_create(); + test_load_restart1(); + test_load_restart2(); + test_add_message(); + test_update_result(); + exit(0); +} diff --git a/libres/lib/enkf/tests/enkf_fs.cpp b/libres/lib/enkf/tests/enkf_fs.cpp new file mode 100644 index 00000000000..8ff2a6b06e0 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_fs.cpp @@ -0,0 +1,151 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_fs.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 + + +typedef struct +{ + pthread_mutex_t mutex1; + pthread_mutex_t mutex2; +} shared_data; + +static shared_data* data = NULL; + +void test_mount() { + ecl::util::TestArea ta("mount"); + + test_assert_false( enkf_fs_exists( "mnt" )); + test_assert_NULL( enkf_fs_create_fs("mnt" , BLOCK_FS_DRIVER_ID , NULL , false)); + test_assert_true( enkf_fs_exists( "mnt" )); + { + enkf_fs_type * fs = enkf_fs_mount( "mnt" ); + test_assert_true( util_file_exists("mnt/mnt.lock")); + test_assert_true( enkf_fs_is_instance( fs )); + enkf_fs_decref( fs ); + test_assert_false( util_file_exists("mnt/mnt.lock")); + } + { + enkf_fs_type * fs = enkf_fs_create_fs( "mnt2" , BLOCK_FS_DRIVER_ID , NULL , true); + test_assert_true( enkf_fs_is_instance( fs )); + enkf_fs_decref( fs ); + } +} + +void test_refcount() { + ecl::util::TestArea ta("ref_count"); + enkf_fs_create_fs("mnt" , BLOCK_FS_DRIVER_ID , NULL , false); + { + enkf_fs_type * fs = enkf_fs_mount( "mnt" ); + test_assert_int_equal( 1 , enkf_fs_get_refcount( fs )); + enkf_fs_decref( fs ); + } +} + +void createFS() { + + pthread_mutex_lock(&data->mutex1); + pid_t pid = fork(); + + if (pid == 0) { + enkf_fs_type * fs_false = enkf_fs_mount( "mnt" ); + test_assert_false(enkf_fs_is_read_only(fs_false)); + test_assert_true( util_file_exists("mnt/mnt.lock")); + pthread_mutex_unlock(&data->mutex1); + pthread_mutex_lock(&data->mutex2); + enkf_fs_decref( fs_false ); + pthread_mutex_unlock(&data->mutex2); + exit(0); + } +} + +void test_fwrite_readonly( void * arg ) { + enkf_fs_type * fs = enkf_fs_safe_cast( arg ); + /* + The arguments here are completely bogus; the important thing is + that this fwrite call should be intercepted by a util_abort() + call (which is again intercepted by the testing function) before + the argument are actually accessed. + */ + enkf_fs_fwrite_node( fs , NULL , "KEY" , PARAMETER , 100 , 1 ); +} + +void initialise_shared() +{ + // place our shared data in shared memory + int prot = PROT_READ | PROT_WRITE; +#ifdef __linux + int flags = MAP_SHARED | MAP_ANONYMOUS; +#elif __APPLE__ + int flags = MAP_SHARED | MAP_ANON; +#endif + + data = (shared_data *) mmap(NULL, sizeof(shared_data), prot, flags, -1, 0); + assert(data); + + // initialise mutex so it works properly in shared memory + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); + pthread_mutex_init(&data->mutex1, &attr); + pthread_mutex_init(&data->mutex2, &attr); +} + +/* + This test needs to fork off a seperate process to test the cross-process file locking. +*/ +void test_read_only2() { + initialise_shared(); + { + ecl::util::TestArea ta("ro2"); + enkf_fs_create_fs("mnt" , BLOCK_FS_DRIVER_ID , NULL , false); + pthread_mutex_lock(&data->mutex2); + createFS(); + pthread_mutex_lock(&data->mutex1); + { + enkf_fs_type * fs_false = enkf_fs_mount( "mnt" ); + test_assert_true(enkf_fs_is_read_only(fs_false)); + test_assert_util_abort( "enkf_fs_fwrite_node" , test_fwrite_readonly , fs_false ); + enkf_fs_decref( fs_false ); + } + pthread_mutex_unlock(&data->mutex2); + pthread_mutex_unlock(&data->mutex1); + pthread_mutex_lock(&data->mutex2); + } + pthread_mutex_unlock(&data->mutex2); + munmap(data, sizeof(data)); +} + +int main(int argc, char ** argv) { + test_mount(); + test_refcount(); + test_read_only2(); + exit(0); +} diff --git a/libres/lib/enkf/tests/enkf_gen_data_config.cpp b/libres/lib/enkf/tests/enkf_gen_data_config.cpp new file mode 100644 index 00000000000..7807dc67db5 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_gen_data_config.cpp @@ -0,0 +1,352 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_gen_data_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 +#include +#include + + +void test_report_steps_param() { + + gen_data_config_type * config = gen_data_config_alloc_GEN_PARAM("KEY" , ASCII , ASCII); + test_assert_false( gen_data_config_is_dynamic( config )); + test_assert_int_equal( 0 , gen_data_config_num_report_step( config )); + test_assert_false( gen_data_config_has_report_step( config , 0 )); + + /* Add to parameter should fail. */ + gen_data_config_add_report_step( config , 10 ); + test_assert_int_equal( 0 , gen_data_config_num_report_step( config )); + test_assert_false( gen_data_config_has_report_step( config , 10 )); + + gen_data_config_free( config ); +} + + +void test_report_steps_dynamic() { + gen_data_config_type * config = gen_data_config_alloc_GEN_DATA_result("KEY" , ASCII); + test_assert_true( gen_data_config_is_dynamic( config )); + test_assert_int_equal( 0 , gen_data_config_num_report_step( config )); + test_assert_false( gen_data_config_has_report_step( config , 0 )); + + gen_data_config_add_report_step( config , 10 ); + test_assert_int_equal( 1 , gen_data_config_num_report_step( config )); + test_assert_true( gen_data_config_has_report_step( config , 10 )); + test_assert_int_equal( gen_data_config_iget_report_step( config , 0 ) , 10); + + gen_data_config_add_report_step( config , 10 ); + test_assert_int_equal( 1 , gen_data_config_num_report_step( config )); + test_assert_true( gen_data_config_has_report_step( config , 10 )); + + + gen_data_config_add_report_step( config , 5 ); + test_assert_int_equal( 2 , gen_data_config_num_report_step( config )); + test_assert_true( gen_data_config_has_report_step( config , 10 )); + test_assert_int_equal( gen_data_config_iget_report_step( config , 0 ) , 5); + test_assert_int_equal( gen_data_config_iget_report_step( config , 1 ) , 10); + + { + const int_vector_type * active_steps = gen_data_config_get_active_report_steps( config ); + + test_assert_int_equal( int_vector_iget( active_steps , 0 ) , 5); + test_assert_int_equal( int_vector_iget( active_steps , 1 ) , 10); + } + + gen_data_config_free( config ); +} + + +void test_gendata_fload(const char * filename) { + ecl::util::TestArea ta("gendata_fload"); + gen_data_config_type * config = gen_data_config_alloc_GEN_DATA_result("KEY" , ASCII); + gen_data_type * gen_data = gen_data_alloc(config); + + const char * cwd = ta.original_cwd().c_str(); + enkf_fs_type * write_fs = enkf_fs_create_fs(cwd, BLOCK_FS_DRIVER_ID, NULL , true); + subst_list_type * subst_list = subst_list_alloc(NULL); + run_arg_type * run_arg = run_arg_alloc_ENSEMBLE_EXPERIMENT("run_id", write_fs, 0,0,"path", "base", subst_list); + forward_load_context_type * load_context = forward_load_context_alloc( run_arg , false , NULL , NULL); + forward_load_context_select_step(load_context , 0 ); + gen_data_fload_with_report_step(gen_data, filename , load_context); + int data_size = gen_data_config_get_data_size(config, 0); + test_assert_true(data_size > 0); + enkf_fs_decref( write_fs ); + + gen_data_free(gen_data); + gen_data_config_free( config ); + run_arg_free( run_arg ); + subst_list_free(subst_list); + forward_load_context_free( load_context ); +} + + +void test_gendata_fload_empty_file(const char * filename) { + ecl::util::TestArea ta("fload_empty"); + gen_data_config_type * config = gen_data_config_alloc_GEN_DATA_result("KEY" , ASCII); + gen_data_type * gen_data = gen_data_alloc(config); + const char * cwd = ta.original_cwd().c_str(); + enkf_fs_type * write_fs = enkf_fs_create_fs(cwd, BLOCK_FS_DRIVER_ID, NULL , true); + subst_list_type * subst_list = subst_list_alloc(NULL); + run_arg_type * run_arg = run_arg_alloc_ENSEMBLE_EXPERIMENT("run_id", write_fs, 0,0,"path", "base", subst_list); + forward_load_context_type * load_context = forward_load_context_alloc( run_arg , false , NULL , NULL); + + forward_load_context_select_step(load_context , 0 ); + gen_data_fload_with_report_step(gen_data, filename, load_context); + int data_size = gen_data_config_get_data_size(config, 0); + test_assert_true(data_size == 0); + enkf_fs_decref( write_fs ); + + gen_data_free(gen_data); + gen_data_config_free( config ); + run_arg_free( run_arg ); + subst_list_free(subst_list); + forward_load_context_free( load_context ); +} + + +void test_result_format() { + test_assert_true( gen_data_config_valid_result_format("path/file%d/extra")); + test_assert_true( gen_data_config_valid_result_format("file%04d")); + test_assert_false( gen_data_config_valid_result_format("/path/file%04d")); + + test_assert_false( gen_data_config_valid_result_format("/path/file%s")); + test_assert_false( gen_data_config_valid_result_format("/path/file")); + test_assert_false( gen_data_config_valid_result_format("/path/file%f")); + + test_assert_false( gen_data_config_valid_result_format(NULL)); +} + + +void alloc_invalid_io_format1( void * arg) { + gen_data_config_type * config = gen_data_config_alloc_GEN_DATA_result("KEY" , ASCII_TEMPLATE ); + gen_data_config_free( config ); +} + + +void alloc_invalid_io_format2( void * arg) { + gen_data_config_type * config = gen_data_config_alloc_GEN_DATA_state("KEY" , GEN_DATA_UNDEFINED , ASCII); + gen_data_config_free( config ); +} + + +void alloc_invalid_io_format3( void *arg) { + gen_data_config_type * config = gen_data_config_alloc_GEN_PARAM("KEY" , ASCII , ASCII_TEMPLATE ); + gen_data_config_free( config ); +} + + + +void test_set_invalid_format() { + test_assert_util_abort( "gen_data_config_alloc_GEN_DATA_result" , alloc_invalid_io_format1 , NULL); + test_assert_util_abort( "gen_data_config_alloc_GEN_DATA_state" , alloc_invalid_io_format2 , NULL); + test_assert_util_abort( "gen_data_config_alloc_GEN_PARAM" , alloc_invalid_io_format3 , NULL); +} + + +void test_format_check() { + test_assert_int_equal( GEN_DATA_UNDEFINED , gen_data_config_check_format( NULL )); + test_assert_int_equal( GEN_DATA_UNDEFINED , gen_data_config_check_format("Error?")); + test_assert_int_equal( ASCII , gen_data_config_check_format("ASCII")); + test_assert_int_equal( ASCII_TEMPLATE , gen_data_config_check_format("ASCII_TEMPLATE")); + test_assert_int_equal( BINARY_DOUBLE , gen_data_config_check_format("BINARY_DOUBLE")); + test_assert_int_equal( BINARY_FLOAT , gen_data_config_check_format("BINARY_FLOAT")); +} + + +void test_set_template_invalid() { + ecl::util::TestArea ta("invalid"); + gen_data_config_type * config = gen_data_config_alloc_GEN_PARAM("KEY" , ASCII , ASCII); + + test_assert_false( gen_data_config_set_template( config , "does/not/exist" , NULL ) ); + + { + FILE * stream = util_fopen("template.txt" , "w"); + fprintf(stream , "Header1\n\nHeader2\n"); + fclose( stream ); + + gen_data_config_set_template( config , "template.txt" , ""); + test_assert_string_equal( "template.txt" , gen_data_config_get_template_file( config )); + test_assert_string_equal( "" , gen_data_config_get_template_key( config )); + + + { + char * buffer; + int data_offset , buffer_size , data_skip; + gen_data_config_get_template_data( config , &buffer , &data_offset , &buffer_size , &data_skip); + + test_assert_string_equal( buffer , "Header1\n\nHeader2\n"); + test_assert_int_equal( data_offset , 8 ); + test_assert_int_equal( buffer_size , 22 ); + test_assert_int_equal( data_skip , 5 ); + } + } + + + { + FILE * stream = util_fopen("template2.txt" , "w"); + fprintf(stream , "Template XYZ \n"); + fclose( stream ); + + test_assert_false( gen_data_config_set_template( config , "template2.txt" , "")); + + test_assert_string_equal( "template.txt" , gen_data_config_get_template_file( config )); + test_assert_string_equal( "" , gen_data_config_get_template_key( config )); + { + char * buffer; + int data_offset , buffer_size , data_skip; + gen_data_config_get_template_data( config , &buffer , &data_offset , &buffer_size , &data_skip); + + test_assert_string_equal( buffer , "Header1\n\nHeader2\n"); + test_assert_int_equal( data_offset , 8 ); + test_assert_int_equal( buffer_size , 22 ); + test_assert_int_equal( data_skip , 5 ); + } + } + + gen_data_config_free( config ); +} + + + +void test_set_template() { + ecl::util::TestArea ta("set_template"); + { + gen_data_config_type * config = gen_data_config_alloc_GEN_PARAM("KEY" , ASCII , ASCII); + + test_assert_true( gen_data_config_set_template( config , NULL , NULL ) ); + test_assert_NULL( gen_data_config_get_template_file( config )); + test_assert_NULL( gen_data_config_get_template_key( config )); + + { + char * buffer; + int data_offset , buffer_size , data_skip; + gen_data_config_get_template_data( config , &buffer , &data_offset , &buffer_size , &data_skip); + + test_assert_NULL( buffer ); + test_assert_int_equal( data_offset , 0 ); + test_assert_int_equal( buffer_size , 0 ); + test_assert_int_equal( data_skip , 0 ); + } + + + { + FILE * stream = util_fopen("template.txt" , "w"); + fprintf(stream , "Header\n"); + fclose( stream ); + + test_assert_true( gen_data_config_set_template( config , "template.txt" , NULL )); + test_assert_string_equal( "template.txt" , gen_data_config_get_template_file( config )); + test_assert_NULL( gen_data_config_get_template_key( config )); + + + { + char * buffer; + int data_offset , buffer_size , data_skip; + gen_data_config_get_template_data( config , &buffer , &data_offset , &buffer_size , &data_skip); + + test_assert_string_equal( buffer , "Header\n"); + test_assert_int_equal( data_offset , 7 ); + test_assert_int_equal( buffer_size , 7 ); + test_assert_int_equal( data_skip , 0 ); + } + } + + { + FILE * stream = util_fopen("template.txt" , "w"); + fprintf(stream , "Header1\n\nHeader2\n"); + fclose( stream ); + + gen_data_config_set_template( config , "template.txt" , ""); + test_assert_string_equal( "template.txt" , gen_data_config_get_template_file( config )); + test_assert_string_equal( "" , gen_data_config_get_template_key( config )); + + + { + char * buffer; + int data_offset , buffer_size , data_skip; + gen_data_config_get_template_data( config , &buffer , &data_offset , &buffer_size , &data_skip); + + test_assert_string_equal( buffer , "Header1\n\nHeader2\n"); + test_assert_int_equal( data_offset , 8 ); + test_assert_int_equal( buffer_size , 22 ); + test_assert_int_equal( data_skip , 5 ); + } + } + + + gen_data_config_set_template( config , NULL , NULL ); + test_assert_NULL( gen_data_config_get_template_file( config )); + test_assert_NULL( gen_data_config_get_template_key( config )); + + { + char * buffer; + int data_offset , buffer_size , data_skip; + gen_data_config_get_template_data( config , &buffer , &data_offset , &buffer_size , &data_skip); + + test_assert_NULL( buffer ); + test_assert_int_equal( data_offset , 0 ); + test_assert_int_equal( buffer_size , 0 ); + test_assert_int_equal( data_skip , 0 ); + } + + + test_assert_true( gen_data_config_set_template( config , NULL , "KEY")); + test_assert_NULL( gen_data_config_get_template_file( config )); + test_assert_NULL( gen_data_config_get_template_key( config )); + + { + char * buffer; + int data_offset , buffer_size , data_skip; + gen_data_config_get_template_data( config , &buffer , &data_offset , &buffer_size , &data_skip); + + test_assert_NULL( buffer ); + test_assert_int_equal( data_offset , 0 ); + test_assert_int_equal( buffer_size , 0 ); + test_assert_int_equal( data_skip , 0 ); + } + + + gen_data_config_free( config ); + } +} + + +int main(int argc , char ** argv) { + + const char * gendata_file = argv[1]; + const char * gendata_file_empty = argv[2]; + util_install_signals(); + + test_report_steps_param(); + test_report_steps_dynamic(); + test_result_format(); + test_set_template(); + test_set_template_invalid(); + test_set_invalid_format(); + test_format_check(); + test_gendata_fload(gendata_file); + test_gendata_fload_empty_file(gendata_file_empty); + + exit(0); +} + diff --git a/libres/lib/enkf/tests/enkf_gen_data_config_parse.cpp b/libres/lib/enkf/tests/enkf_gen_data_config_parse.cpp new file mode 100644 index 00000000000..a18d5b25a97 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_gen_data_config_parse.cpp @@ -0,0 +1,244 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_gen_data_config_parse.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 + + +enkf_config_node_type * parse_alloc_GEN_PARAM( const char * config_string , bool parse_valid) { + config_parser_type * config = config_alloc(); + enkf_config_node_type * enkf_config_node = NULL; + + enkf_config_node_add_GEN_PARAM_config_schema( config ); + { + FILE * stream = util_fopen("config.txt" , "w"); + fputs(config_string, stream); + fclose( stream ); + } + + { + config_content_type * content = config_parse( config , "config.txt" , "--" , NULL , NULL , NULL , CONFIG_UNRECOGNIZED_IGNORE , true); + + test_assert_bool_equal( parse_valid , config_content_is_valid( content )); + if (parse_valid) { + const config_content_item_type * config_item = config_content_get_item( content , GEN_PARAM_KEY ); + const config_content_node_type * config_node = config_content_item_iget_node( config_item , 0 ); + + enkf_config_node = enkf_config_node_alloc_GEN_PARAM_from_config( config_node ); + } + config_content_free( content ); + config_free( config ); + } + return enkf_config_node; +} + + + + + +void test_parse_gen_param() { + ecl::util::TestArea ta("parse"); + // Parse error: missing eclfile + { + enkf_config_node_type * config_node = parse_alloc_GEN_PARAM( "GEN_PARAM KEY\n" , false); + test_assert_NULL( config_node ); + } + + // Missing all required KEY: arguments + { + enkf_config_node_type * config_node = parse_alloc_GEN_PARAM( "GEN_PARAM KEY ECLFILE\n" , true); + test_assert_NULL( config_node ); + } + + + // OUTPUT_FORMAT: Is incorrectly spelled + { + enkf_config_node_type * config_node = parse_alloc_GEN_PARAM( "GEN_PARAM KEY ECLFILE INIT_FILES:XXX INPUT_FORMAT:ASCII OutPutFOrmat:ASCII\n" , true); + test_assert_NULL( config_node ); + } + + + // OUTPUT_FORMAT: ASCII is incorrectly spelled + { + enkf_config_node_type * config_node = parse_alloc_GEN_PARAM( "GEN_PARAM KEY ECLFILE INIT_FILES:XXX INPUT_FORMAT:ASCII OUTPUT_FORMAT:ASCI\n" , true); + test_assert_NULL( config_node ); + } + + // Invalid value for INPUT_FORMAT + { + enkf_config_node_type * config_node = parse_alloc_GEN_PARAM( "GEN_PARAM KEY ECLFILE INIT_FILES:XXX INPUT_FORMAT:ASCII_TEMPLATE OUTPUT_FORMAT:ASCII\n" , true); + test_assert_NULL( config_node ); + } + + + // Correct + { + enkf_config_node_type * config_node = parse_alloc_GEN_PARAM( "GEN_PARAM KEY ECLFILE INPUT_FORMAT:BINARY_DOUBLE OUTPUT_FORMAT:ASCII INIT_FILES:INIT%d\n" , true); + + test_assert_string_equal( "ECLFILE" , enkf_config_node_get_enkf_outfile( config_node )); + test_assert_NULL( enkf_config_node_get_enkf_infile( config_node )); + test_assert_string_equal( "INIT%d" , enkf_config_node_get_init_file_fmt( config_node )); + test_assert_int_equal( PARAMETER , enkf_config_node_get_var_type( config_node )); + { + gen_data_config_type * gen_data_config = (gen_data_config_type *)enkf_config_node_get_ref( config_node ); + test_assert_int_equal( BINARY_DOUBLE , gen_data_config_get_input_format( gen_data_config )); + test_assert_int_equal( ASCII , gen_data_config_get_output_format( gen_data_config )); + } + + enkf_config_node_free( config_node ); + } +} + + + +enkf_config_node_type * parse_alloc_GEN_DATA_result( const char * config_string , bool parse_valid) { + config_parser_type * config = config_alloc(); + enkf_config_node_type * enkf_config_node = NULL; + + enkf_config_node_add_GEN_DATA_config_schema( config ); + { + FILE * stream = util_fopen("config.txt" , "w"); + fputs(config_string, stream); + fclose( stream ); + } + { + config_content_type * content = config_parse( config , "config.txt" , "--" , NULL , NULL , NULL , CONFIG_UNRECOGNIZED_IGNORE , true); + test_assert_bool_equal( parse_valid ,config_content_is_valid( content ) ); + if (parse_valid) { + const config_content_item_type * config_item = config_content_get_item( content , GEN_DATA_KEY ); + const config_content_node_type * config_node = config_content_item_iget_node( config_item , 0 ); + + enkf_config_node = enkf_config_node_alloc_GEN_DATA_from_config( config_node ); + } + + config_content_free( content ); + config_free( config ); + } + return enkf_config_node; +} + + + +void test_parse_gen_data_result() { + ecl::util::TestArea ta("GEN_DATA_RESULT_parse"); + // Parse error: missing KEY + { + enkf_config_node_type * config_node = parse_alloc_GEN_DATA_result( "GEN_DATA\n" , false); + test_assert_NULL( config_node ); + } + + // Validation error: missing INPUT_FORMAT: + { + enkf_config_node_type * config_node = parse_alloc_GEN_DATA_result( "GEN_DATA GEN_DATA_KEY RESULT_FILE:Results%d REPORT_STEPS:10 \n" , true); + test_assert_NULL( config_node ); + } + + + // Validation error: Invalid INPUT_FORMAT: + { + enkf_config_node_type * config_node = parse_alloc_GEN_DATA_result( "GEN_DATA GEN_DATA_KEY RESULT_FILE:Results%d INPUT_FORMAT:AsCiiiiii REPORT_STEPS:10 \n" , true); + test_assert_NULL( config_node ); + } + + + // Validation error: missing RESULT_FILE: + { + enkf_config_node_type * config_node = parse_alloc_GEN_DATA_result( "GEN_DATA GEN_DATA_KEY INPUT_FORMAT:ASCII REPORT_STEPS:10 \n" , true); + test_assert_NULL( config_node ); + } + + + // Validation error: Invalid RESULT_FILE: + { + enkf_config_node_type * config_node = parse_alloc_GEN_DATA_result( "GEN_DATA GEN_DATA_KEY RESULT_FILE:Results INPUT_FORMAT:ASCII REPORT_STEPS:10 \n" , true); + test_assert_NULL( config_node ); + } + + // Validation error: Missing REPORT_STEPS: + { + enkf_config_node_type * config_node = parse_alloc_GEN_DATA_result( "GEN_DATA GEN_DATA_KEY RESULT_FILE:Results%d INPUT_FORMAT:ASCII \n" , true); + test_assert_NULL( config_node ); + } + + // Validation error: Invalid REPORT_STEPS + { + enkf_config_node_type * config_node = parse_alloc_GEN_DATA_result( "GEN_DATA GEN_DATA_KEY RESULT_FILE:Results%d INPUT_FORMAT:ASCII REPORT_STEPS:XXX\n" , true); + test_assert_NULL( config_node ); + } + + // Valid + { + enkf_config_node_type * config_node = parse_alloc_GEN_DATA_result( "GEN_DATA GEN_DATA_KEY RESULT_FILE:Results%d INPUT_FORMAT:ASCII REPORT_STEPS:10,20,30\n" , true); + test_assert_true( enkf_config_node_is_instance( config_node )); + + test_assert_string_equal( "Results%d" , enkf_config_node_get_enkf_infile( config_node )); + test_assert_NULL( enkf_config_node_get_init_file_fmt( config_node )); + test_assert_NULL( enkf_config_node_get_enkf_outfile( config_node )); + test_assert_int_equal( DYNAMIC_RESULT , enkf_config_node_get_var_type( config_node )); + { + gen_data_config_type * gen_data_config = (gen_data_config_type *)enkf_config_node_get_ref( config_node ); + test_assert_int_equal( ASCII , gen_data_config_get_input_format( gen_data_config )); + test_assert_int_equal( GEN_DATA_UNDEFINED , gen_data_config_get_output_format( gen_data_config )); + + test_assert_int_equal( 3 , gen_data_config_num_report_step( gen_data_config )); + test_assert_int_equal( 10 , gen_data_config_iget_report_step( gen_data_config , 0 )); + test_assert_int_equal( 30 , gen_data_config_iget_report_step( gen_data_config , 2 )); + + + test_assert_true( gen_data_config_has_report_step( gen_data_config , 10 )); + test_assert_true( gen_data_config_has_report_step( gen_data_config , 20 )); + test_assert_true( gen_data_config_has_report_step( gen_data_config , 30 )); + + test_assert_false( gen_data_config_has_report_step( gen_data_config , 05 )); + test_assert_false( gen_data_config_has_report_step( gen_data_config , 15 )); + test_assert_false( gen_data_config_has_report_step( gen_data_config , 25 )); + test_assert_false( gen_data_config_has_report_step( gen_data_config , 35 )); + + } + test_assert_true( enkf_config_node_internalize( config_node , 10 )); + test_assert_true( enkf_config_node_internalize( config_node , 20 )); + test_assert_true( enkf_config_node_internalize( config_node , 30 )); + + test_assert_false( enkf_config_node_internalize( config_node , 05 )); + test_assert_false( enkf_config_node_internalize( config_node , 15 )); + test_assert_false( enkf_config_node_internalize( config_node , 25 )); + test_assert_false( enkf_config_node_internalize( config_node , 35 )); + + enkf_config_node_free( config_node ); + } +} + + + + +int main(int argc , char ** argv) { + test_parse_gen_param(); + test_parse_gen_data_result(); + exit(0); +} + diff --git a/libres/lib/enkf/tests/enkf_gen_obs_load.cpp b/libres/lib/enkf/tests/enkf_gen_obs_load.cpp new file mode 100644 index 00000000000..786fc98b284 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_gen_obs_load.cpp @@ -0,0 +1,51 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_gen_obs_load.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 + + + +void test_obs_check_report_steps(const char * config_file ) { + ert_test_context_type * test_context = ert_test_context_alloc( "GEN_OBS" , config_file ); + enkf_main_type * enkf_main = ert_test_context_get_main( test_context ); + enkf_obs_type * obs = enkf_main_get_obs( enkf_main ); + + test_assert_true( enkf_obs_has_key( obs , "GEN_OBS10")); + test_assert_true( enkf_obs_has_key( obs , "GEN_OBS20")); + test_assert_false( enkf_obs_has_key( obs , "GEN_OBS30")); + + ert_test_context_free( test_context ); +} + + + + + + +int main(int argc , char ** argv) { + const char * config_file = argv[1]; + + test_obs_check_report_steps( config_file ); + +} + + diff --git a/libres/lib/enkf/tests/enkf_iter_config.cpp b/libres/lib/enkf/tests/enkf_iter_config.cpp new file mode 100644 index 00000000000..df6526b45d6 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_iter_config.cpp @@ -0,0 +1,100 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_iter_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 +#include +#include + + +#define TMP_PATH "/tmp" +char * create_config_file( const char * enspath_fmt , const char * runpath_fmt , int iter_count) { + char * config_file = util_alloc_tmp_file(TMP_PATH , "iter-config" , false); + FILE * stream = util_fopen( config_file , "w"); + fprintf(stream , "%s %s\n" , ITER_CASE_KEY , enspath_fmt); + fprintf(stream , "%s %d\n" , ITER_COUNT_KEY , iter_count); + fclose( stream ); + return config_file; +} + + + +void test_set() { + analysis_iter_config_type * iter_config = analysis_iter_config_alloc(); + + test_assert_false( analysis_iter_config_case_fmt_set( iter_config )); + analysis_iter_config_set_case_fmt( iter_config , "case%d"); + test_assert_true( analysis_iter_config_case_fmt_set( iter_config )); + + test_assert_false( analysis_iter_config_num_iterations_set( iter_config )); + analysis_iter_config_set_num_iterations( iter_config , 77 ); + test_assert_true( analysis_iter_config_num_iterations_set( iter_config )); + + test_assert_int_equal( analysis_iter_config_get_num_retries_per_iteration(iter_config), 4); + analysis_iter_config_set_num_retries_per_iteration(iter_config , 10 ); + test_assert_int_equal( analysis_iter_config_get_num_retries_per_iteration(iter_config), 10); + + + analysis_iter_config_free( iter_config ); +} + + + + +int main(int argc , char ** argv) { + const char * enspath_fmt = "iter%d"; + const char * runpath_fmt = "run/iter%d/real%d"; + const int iter_count = 10; + char * config_file = create_config_file( enspath_fmt , runpath_fmt , iter_count); + + + config_parser_type * config = config_alloc(); + config_content_type * content; + analysis_iter_config_add_config_items( config ); + + content = config_parse( config , config_file , NULL , NULL , NULL , NULL , CONFIG_UNRECOGNIZED_ERROR , true); + test_assert_true( config_content_is_valid( content) ); + + test_assert_true( config_content_has_item( content , ITER_CASE_KEY )); + test_assert_true( config_content_has_item( content , ITER_COUNT_KEY )); + + { + analysis_iter_config_type * iter_config = analysis_iter_config_alloc(); + char itercase[50]; + sprintf(itercase,DEFAULT_ANALYSIS_ITER_CASE,5); + test_assert_string_equal( analysis_iter_config_iget_case( iter_config , 5) , itercase ); + analysis_iter_config_init( iter_config , content ); + + test_assert_int_equal( analysis_iter_config_get_num_iterations( iter_config ) , iter_count ); + test_assert_string_equal( analysis_iter_config_iget_case( iter_config , 5) , "iter5"); + + analysis_iter_config_free( iter_config ); + } + remove( config_file ); + free( config_file ); + config_content_free( content ); + config_free( config ); + + test_set(); + exit(0); +} diff --git a/libres/lib/enkf/tests/enkf_local_dataset.cpp b/libres/lib/enkf/tests/enkf_local_dataset.cpp new file mode 100644 index 00000000000..52cb4dbbcb0 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_local_dataset.cpp @@ -0,0 +1,76 @@ +/* + Copyright (C) 2020 Equinor ASA, Norway. + + The file 'enkf_local_dataset.cpp' 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 + + +bool vector_contains(const std::vector& keys, const std::string& key) { + auto iter = std::find(keys.begin(), keys.end(), key); + return (iter != keys.end()); +} + + +void test_create() { + local_dataset_type * ld = local_dataset_alloc("DATA"); + local_dataset_add_node(ld, "PERMX"); + local_dataset_add_node(ld, "PERMY"); + local_dataset_add_node(ld, "PERMZ"); + test_assert_false( local_dataset_has_row_scaling(ld, "PERMX")); + test_assert_false( local_dataset_has_row_scaling(ld, "PERMY")); + test_assert_false( local_dataset_has_row_scaling(ld, "PERMZ")); + + const auto& unscaled_keys = local_dataset_unscaled_keys(ld); + test_assert_int_equal( unscaled_keys.size(), 3 ); + test_assert_true( vector_contains(unscaled_keys, "PERMX")); + test_assert_true( vector_contains(unscaled_keys, "PERMY")); + test_assert_true( vector_contains(unscaled_keys, "PERMZ")); + + const auto& scaled_keys = local_dataset_scaled_keys(ld); + test_assert_int_equal( scaled_keys.size(), 0 ); + + local_dataset_free(ld); +} + + +void test_create_row_scaling() { + local_dataset_type * ld = local_dataset_alloc("DATA"); + test_assert_throw(local_dataset_get_or_create_row_scaling(ld, "NO_SUCH_KEY"), std::invalid_argument); + + local_dataset_add_node(ld, "PERMX"); + row_scaling_type * rs = local_dataset_get_or_create_row_scaling(ld, "PERMX"); + test_assert_true( local_dataset_has_row_scaling(ld, "PERMX")); + + test_assert_false( local_dataset_has_row_scaling(ld, "PERMY")); + + local_dataset_add_node(ld, "PERMZ"); + test_assert_false( local_dataset_has_row_scaling(ld, "PERMZ")); + + local_dataset_free(ld); +} + +int main(int argc , char ** argv) { + test_create(); + test_create_row_scaling(); +} + diff --git a/libres/lib/enkf/tests/enkf_local_obsdata.cpp b/libres/lib/enkf/tests/enkf_local_obsdata.cpp new file mode 100644 index 00000000000..b6faa3edc24 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_local_obsdata.cpp @@ -0,0 +1,59 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_local_obsdata.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 + + +void test_wrapper() { + local_obsdata_node_type * node = local_obsdata_node_alloc("KEY" , true); + local_obsdata_type * data = local_obsdata_alloc_wrapper( node ); + test_assert_true( local_obsdata_is_instance( data )); + test_assert_int_equal( 1 , local_obsdata_get_size( data )); + test_assert_ptr_equal( node , local_obsdata_iget( data , 0 )); + test_assert_true( local_obsdata_has_node( data , "KEY" )); + test_assert_false( local_obsdata_has_node( data , "KEYX" )); + test_assert_string_equal( local_obsdata_node_get_key( node ) , local_obsdata_get_name( data )); + local_obsdata_free( data ); +} + + +int main(int argc , char ** argv) { + local_obsdata_type * obsdata; + + obsdata = local_obsdata_alloc( "KEY"); + test_assert_true( local_obsdata_is_instance( obsdata )); + test_assert_int_equal( 0 , local_obsdata_get_size( obsdata )); + test_assert_string_equal( "KEY" , local_obsdata_get_name( obsdata )); + + { + local_obsdata_node_type * obsnode = local_obsdata_node_alloc( "KEY" , true); + test_assert_true( local_obsdata_add_node( obsdata , obsnode ) ); + test_assert_false( local_obsdata_add_node( obsdata , obsnode ) ); + test_assert_int_equal( 1 , local_obsdata_get_size( obsdata )); + test_assert_ptr_equal( obsnode , local_obsdata_iget( obsdata , 0)); + } + + local_obsdata_free( obsdata ); + + test_wrapper(); + exit(0); +} + diff --git a/libres/lib/enkf/tests/enkf_local_obsdata_node.cpp b/libres/lib/enkf/tests/enkf_local_obsdata_node.cpp new file mode 100644 index 00000000000..e3f2f33542d --- /dev/null +++ b/libres/lib/enkf/tests/enkf_local_obsdata_node.cpp @@ -0,0 +1,98 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_local_obsdata_node.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 + + +void test_content( local_obsdata_node_type * node ) { + const active_list_type * active_list = local_obsdata_node_get_active_list( node ); + + test_assert_not_NULL( active_list ); + test_assert_true( active_list_is_instance( active_list )); + + { + active_list_type * new_active_list = active_list_alloc( ); + + active_list_add_index( new_active_list , 1098 ); + + test_assert_false( active_list_equal( new_active_list , local_obsdata_node_get_active_list( node ))); + local_obsdata_node_copy_active_list( node , new_active_list ); + test_assert_true( active_list_equal( new_active_list , local_obsdata_node_get_active_list( node ))); + + } + { + + + local_obsdata_node_add_tstep( node , 20 ); + local_obsdata_node_add_tstep( node , 10 ); + local_obsdata_node_add_tstep( node , 10 ); // Second add - ignored + + test_assert_false( local_obsdata_node_tstep_active(node, 5)); + test_assert_true( local_obsdata_node_tstep_active(node, 10)); + test_assert_true( local_obsdata_node_tstep_active(node, 20)); + test_assert_true( local_obsdata_node_has_tstep( node , 10 )); + test_assert_true( local_obsdata_node_has_tstep( node , 20 )); + test_assert_false( local_obsdata_node_has_tstep( node , 15 )); + + + local_obsdata_node_add_range( node , 5 , 7 ); + test_assert_true( local_obsdata_node_tstep_active(node, 5)); + test_assert_true( local_obsdata_node_tstep_active(node, 7)); + } + +} + +void test_all_active() { + local_obsdata_node_type * node = local_obsdata_node_alloc( "KEY" , true); + + test_assert_true( local_obsdata_node_all_timestep_active( node )); + test_assert_true( local_obsdata_node_tstep_active( node , 0 )); + test_assert_true( local_obsdata_node_tstep_active( node , 10 )); + test_assert_true( local_obsdata_node_tstep_active( node , 20 )); + + local_obsdata_node_free( node ); +} + + + + + + +int main(int argc , char ** argv) { + const char * obs_key = "1234"; + + { + local_obsdata_node_type * node = local_obsdata_node_alloc( obs_key , true); + + test_assert_true( local_obsdata_node_is_instance( node )); + test_assert_string_equal( obs_key , local_obsdata_node_get_key( node )); + test_content( node ); + local_obsdata_node_free( node ); + } + + { + void * node = local_obsdata_node_alloc( obs_key ,true ); + local_obsdata_node_free__( node ); + } + test_all_active(); + exit(0); +} + diff --git a/libres/lib/enkf/tests/enkf_magic_string_in_workflows.cpp b/libres/lib/enkf/tests/enkf_magic_string_in_workflows.cpp new file mode 100644 index 00000000000..12219af852c --- /dev/null +++ b/libres/lib/enkf/tests/enkf_magic_string_in_workflows.cpp @@ -0,0 +1,68 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'enkf_magic_string_in_workflows.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 + + +void test_magic_strings( ert_test_context_type * test_context ) { + enkf_main_type * enkf_main = ert_test_context_get_main( test_context ); + test_assert_true( ert_test_context_run_worklow( test_context , "MAGIC_PRINT") ); + test_assert_true( util_file_exists( "magic-list.txt") ); + + { + FILE * stream = util_fopen("magic-list.txt" , "r"); + char string[128]; + + fscanf( stream , "%s" , string); + test_assert_string_equal( string , enkf_fs_get_case_name( enkf_main_get_fs( enkf_main ))); + + fscanf( stream , "%s" , string); + test_assert_string_equal( string , "MagicAllTheWayToWorkFlow"); + + fclose( stream ); + } +} + + +void test_has_job(ert_test_context_type * test_context ) { + enkf_main_type * enkf_main = ert_test_context_get_main( test_context ); + ert_workflow_list_type * workflows = enkf_main_get_workflow_list( enkf_main ); + test_assert_true( ert_workflow_list_has_job( workflows , "MAGIC_PRINT" )); +} + + +int main( int argc , char ** argv) { + const char * model_config = argv[1]; + ert_test_context_type * test_context = ert_test_context_alloc( "MAGIC-STRINGS" , model_config); + enkf_main_type * enkf_main = ert_test_context_get_main( test_context ); + + { + test_has_job( test_context ); + + enkf_main_select_fs(enkf_main , "default"); + test_assert_string_equal( "default" , enkf_fs_get_case_name( enkf_main_get_fs( enkf_main ))); + test_magic_strings( test_context ); + + enkf_main_select_fs(enkf_main , "extraCase"); + test_assert_string_equal( "extraCase" , enkf_fs_get_case_name( enkf_main_get_fs( enkf_main ))); + test_magic_strings( test_context ); + } + ert_test_context_free( test_context ); +} diff --git a/libres/lib/enkf/tests/enkf_main.cpp b/libres/lib/enkf/tests/enkf_main.cpp new file mode 100644 index 00000000000..78cb686c51b --- /dev/null +++ b/libres/lib/enkf/tests/enkf_main.cpp @@ -0,0 +1,70 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_main.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 + + +void test_case_initialized(const char * config_path, const char * config_file) { + ecl::util::TestArea ta("case_initialized"); + ta.copy_directory_content(config_path); + { + res_config_type * res_config = res_config_alloc_load(config_file); + enkf_main_type * enkf_main = enkf_main_alloc(res_config, true, true); + model_config_type * model_config = enkf_main_get_model_config(enkf_main); + const char * new_case = "fs/case"; + char * mount_point = util_alloc_sprintf("%s/%s" , model_config_get_enspath(model_config) , new_case); + enkf_fs_create_fs(mount_point , BLOCK_FS_DRIVER_ID , NULL , false); + + test_assert_false(enkf_main_case_is_initialized(enkf_main , "does/not/exist" , NULL)); + test_assert_true(enkf_main_case_is_initialized(enkf_main , new_case , NULL)); + + enkf_main_free(enkf_main); + res_config_free(res_config); + } +} + + + +void test_create(const char * config_path, const char * config_file) { + ecl::util::TestArea ta("create"); + ta.copy_directory_content(config_path); + + res_config_type * res_config = res_config_alloc_load(config_file); + enkf_main_type * enkf_main = enkf_main_alloc(res_config, true, true); + test_assert_true( enkf_main_is_instance( enkf_main ) ); + + enkf_main_free( enkf_main ); + res_config_free(res_config); +} + + + +int main(int argc , char ** argv) { + const char * config_path = argv[1]; + const char * config_file = argv[2]; + util_install_signals(); + test_create(config_path, config_file); + test_case_initialized(config_path, config_file); + exit(0); +} + diff --git a/libres/lib/enkf/tests/enkf_main_fs.cpp b/libres/lib/enkf/tests/enkf_main_fs.cpp new file mode 100644 index 00000000000..4b8eb7aeca0 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_main_fs.cpp @@ -0,0 +1,117 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_main_fs.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 + + +int main(int argc, char ** argv) { + const char * config_file = argv[1]; + ecl::util::TestArea ta("main"); + char * model_config; + util_alloc_file_components( config_file , NULL , &model_config , NULL); + ta.copy_parent_content(config_file); + { + res_config_type * res_config = res_config_alloc_load(model_config); + enkf_main_type * enkf_main = enkf_main_alloc(res_config, false, false); + + enkf_main_select_fs( enkf_main , "enkf"); + test_assert_true( enkf_main_case_is_current( enkf_main , "enkf")); + test_assert_false( enkf_main_case_is_current( enkf_main , "default_fs")); + test_assert_false( enkf_main_case_is_current( enkf_main , "does_not_exist")); + + test_assert_int_equal( 1 , enkf_fs_get_refcount( enkf_main_get_fs( enkf_main ))); + { + enkf_fs_type * fs_ref = enkf_main_get_fs_ref( enkf_main ); + test_assert_int_equal( 2 , enkf_fs_get_refcount( enkf_main_get_fs( enkf_main ))); + enkf_fs_decref( fs_ref ); + test_assert_int_equal( 1 , enkf_fs_get_refcount( enkf_main_get_fs( enkf_main ))); + } + + { + state_map_type * map1 = enkf_fs_get_state_map( enkf_main_get_fs( enkf_main )); + state_map_type * map2 = enkf_main_alloc_readonly_state_map(enkf_main , "enkf"); + test_assert_true(state_map_equal( map1 , map2 )); + state_map_free( map2 ); + } + { + enkf_fs_type * fs1 = enkf_main_mount_alt_fs( enkf_main , "default" , false ); + enkf_fs_type * fs2 = enkf_main_mount_alt_fs( enkf_main , "enkf" , false ); + + test_assert_int_equal( 2 , enkf_fs_get_refcount( enkf_main_get_fs( enkf_main ))); + test_assert_int_equal( 2 , enkf_fs_get_refcount( fs2 )); + test_assert_int_equal( 1 , enkf_fs_get_refcount( fs1 )); + + enkf_fs_decref( fs1 ); + enkf_fs_decref( fs2 ); + } + + { + enkf_fs_type * enkf_fs = enkf_main_mount_alt_fs( enkf_main , "enkf" , false ); + + enkf_main_select_fs( enkf_main , "default"); + test_assert_int_equal( 1 , enkf_fs_get_refcount( enkf_main_get_fs( enkf_main ))); + enkf_fs_decref( enkf_fs ); + } + + { + enkf_fs_type * default_fs = enkf_main_mount_alt_fs( enkf_main , "default" , false ); + + test_assert_int_equal( 2 , enkf_fs_get_refcount( enkf_main_get_fs( enkf_main ))); + enkf_main_select_fs( enkf_main , "default"); + test_assert_int_equal( 2 , enkf_fs_get_refcount( enkf_main_get_fs( enkf_main ))); + enkf_fs_decref( default_fs ); + test_assert_int_equal( 1 , enkf_fs_get_refcount( enkf_main_get_fs( enkf_main ))); + } + /*****************************************************************/ + { + enkf_fs_type * fs = enkf_main_mount_alt_fs( enkf_main , "default" , false ); + test_assert_int_equal( 2 , enkf_fs_get_refcount( enkf_main_get_fs( enkf_main ))); + + enkf_main_set_fs( enkf_main , fs , NULL ); + enkf_fs_decref( fs ); + test_assert_int_equal( 1 , enkf_fs_get_refcount( enkf_main_get_fs( enkf_main ))); + } + { + enkf_fs_type * fs = enkf_main_mount_alt_fs( enkf_main , "enkf" , false ); + enkf_fs_type * current = enkf_main_mount_alt_fs( enkf_main , "default" , false ); + + test_assert_int_equal( 2 , enkf_fs_get_refcount( current )); + test_assert_int_equal( 1 , enkf_fs_get_refcount( fs)); + enkf_main_set_fs( enkf_main , fs , NULL); + test_assert_int_equal( 2 , enkf_fs_get_refcount( fs)); + test_assert_int_equal( 1 , enkf_fs_get_refcount( current )); + + enkf_fs_decref( current ); + enkf_fs_decref( fs); + } + + + + + test_assert_int_equal( 1 , enkf_fs_get_refcount( enkf_main_get_fs( enkf_main ))); + enkf_main_free( enkf_main ); + res_config_free(res_config); + } + exit(0); +} diff --git a/libres/lib/enkf/tests/enkf_main_fs_current_file_test.cpp b/libres/lib/enkf/tests/enkf_main_fs_current_file_test.cpp new file mode 100644 index 00000000000..2b11aaf6b78 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_main_fs_current_file_test.cpp @@ -0,0 +1,98 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_main_fs_current_file_test.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 + +void test_current_file_not_present_symlink_present(const char * model_config) { + test_assert_true(util_file_exists("Storage/enkf")); + util_make_slink("enkf", "Storage/current" ); + res_config_type * res_config = res_config_alloc_load(model_config); + enkf_main_type * enkf_main = enkf_main_alloc(res_config, false, false); + test_assert_true( enkf_main_case_is_current( enkf_main , "enkf")); + test_assert_false(util_file_exists("Storage/current")); + test_assert_true(util_file_exists("Storage/current_case")); + char * current_case = enkf_main_read_alloc_current_case_name(enkf_main); + test_assert_string_equal(current_case, "enkf"); + free(current_case); + enkf_main_free(enkf_main); + res_config_free(res_config); +} + +void test_current_file_present(const char * model_config) { + test_assert_true(util_file_exists("Storage/current_case")); + res_config_type * res_config = res_config_alloc_load(model_config); + enkf_main_type * enkf_main = enkf_main_alloc(res_config, false, false); + test_assert_true( enkf_main_case_is_current( enkf_main , "enkf")); + test_assert_false(util_file_exists("Storage/current")); + char * current_case = enkf_main_read_alloc_current_case_name(enkf_main); + test_assert_string_equal(current_case, "enkf"); + free(current_case); + enkf_main_free(enkf_main); + res_config_free(res_config); +} + + +void test_change_case(const char * model_config) { + res_config_type * res_config = res_config_alloc_load(model_config); + enkf_main_type * enkf_main = enkf_main_alloc(res_config, false, false); + enkf_main_select_fs( enkf_main , "default"); + test_assert_true( enkf_main_case_is_current( enkf_main , "default")); + test_assert_false( enkf_main_case_is_current(enkf_main , "enkf")); + { + char * current_case = enkf_main_read_alloc_current_case_name(enkf_main); + test_assert_string_equal(current_case, "default"); + free(current_case); + } + + enkf_main_select_fs( enkf_main , "enkf"); + test_assert_true( enkf_main_case_is_current( enkf_main , "enkf")); + test_assert_false( enkf_main_case_is_current(enkf_main , "default")); + { + char * current_case = enkf_main_read_alloc_current_case_name(enkf_main); + test_assert_string_equal(current_case, "enkf"); + free(current_case); + } + + enkf_fs_type * enkf_fs = enkf_main_mount_alt_fs( enkf_main , "default" , false ); + enkf_main_select_fs( enkf_main , "default"); + test_assert_true( enkf_main_case_is_current( enkf_main , "default")); + enkf_fs_decref( enkf_fs ); + enkf_main_free(enkf_main); + res_config_free(res_config); +} + +int main(int argc, char ** argv) { + const char * config_file = argv[1]; + ecl::util::TestArea ta("current_file"); + char * model_config; + util_alloc_file_components( config_file , NULL , &model_config , NULL); + ta.copy_parent_content(config_file); + + test_current_file_not_present_symlink_present(model_config); + test_current_file_present(model_config); + test_change_case(model_config); + + free(model_config); + exit(0); +} diff --git a/libres/lib/enkf/tests/enkf_meas_data.cpp b/libres/lib/enkf/tests/enkf_meas_data.cpp new file mode 100644 index 00000000000..27f7e552ac5 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_meas_data.cpp @@ -0,0 +1,79 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_meas_data.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 + + + +void meas_block_iset_abort(void * arg) { + meas_block_type * block = meas_block_safe_cast( arg ); + meas_block_iset( block , 0 , 0 , 100); +} + + +void meas_block_iget_abort(void * arg) { + meas_block_type * block = meas_block_safe_cast( arg ); + meas_block_iget( block , 0 , 0 ); +} + + + +void create_test() { + int_vector_type * ens_active_list = int_vector_alloc(0 , false); + bool_vector_type * ens_mask; + int_vector_append( ens_active_list , 10 ); + int_vector_append( ens_active_list , 20 ); + int_vector_append( ens_active_list , 30 ); + + ens_mask = int_vector_alloc_mask(ens_active_list); + { + meas_data_type * meas_data = meas_data_alloc( ens_mask ); + test_assert_int_equal( 3 , meas_data_get_active_ens_size( meas_data )); + + { + meas_block_type * block = meas_data_add_block(meas_data , "OBS" , 10 , 10); + + meas_block_iset(block , 10 , 0 , 100); + test_assert_double_equal( 100 , meas_block_iget( block , 10 , 0 )); + + test_assert_bool_equal( true , meas_block_iens_active( block , 10 )); + test_assert_bool_equal( false , meas_block_iens_active( block , 11 )); + + test_assert_util_abort( "meas_block_assert_iens_active" , meas_block_iset_abort , block); + test_assert_util_abort( "meas_block_assert_iens_active" , meas_block_iget_abort , block); + } + meas_data_free( meas_data ); + } + + + bool_vector_free( ens_mask ); + int_vector_free( ens_active_list ); +} + + + +int main(int argc , char ** argv) { + create_test(); + exit(0); +} + diff --git a/libres/lib/enkf/tests/enkf_model_config.cpp b/libres/lib/enkf/tests/enkf_model_config.cpp new file mode 100644 index 00000000000..0398d2f9fcc --- /dev/null +++ b/libres/lib/enkf/tests/enkf_model_config.cpp @@ -0,0 +1,77 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_model_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 + + +void test_create() { + model_config_type * model_config = model_config_alloc_empty(); + test_assert_true( model_config_is_instance( model_config)); + model_config_free( model_config ); +} + + +void test_runpath() { + model_config_type * model_config = model_config_alloc_empty(); + model_config_add_runpath(model_config , "KEY" , "RunPath%d"); + model_config_add_runpath(model_config , "KEY2" , "2-RunPath%d"); + test_assert_true( model_config_select_runpath(model_config , "KEY")); + test_assert_false( model_config_select_runpath(model_config , "KEYX")); + test_assert_string_equal("RunPath%d" , model_config_get_runpath_as_char(model_config)); + + model_config_set_runpath( model_config , "PATH%d"); + test_assert_string_equal("PATH%d" , model_config_get_runpath_as_char(model_config)); + test_assert_true( model_config_select_runpath(model_config , "KEY2")); + test_assert_string_equal("2-RunPath%d" , model_config_get_runpath_as_char(model_config)); + test_assert_true( model_config_select_runpath(model_config , "KEY")); + test_assert_string_equal("PATH%d" , model_config_get_runpath_as_char(model_config)); + + test_assert_false( model_config_runpath_requires_iter( model_config )); + model_config_set_runpath( model_config , "iens%d/iter%d" ); + test_assert_true( model_config_runpath_requires_iter( model_config )); + + model_config_free( model_config ); +} + + +void test_data_root( ) { + model_config_type * model_config = model_config_alloc_empty(); + test_assert_false( model_config_data_root_is_set( model_config )); + model_config_set_data_root( model_config , "root"); + test_assert_true( model_config_data_root_is_set( model_config )); + model_config_free( model_config ); +} + + +void test_export_file( ) { + model_config_type * model_config = model_config_alloc_empty(); + + model_config_free( model_config ); +} + +int main(int argc , char ** argv) { + test_create(); + test_runpath( ); + test_data_root( ); + test_export_file( ); + exit(0); +} + diff --git a/libres/lib/enkf/tests/enkf_obs_fs.cpp b/libres/lib/enkf/tests/enkf_obs_fs.cpp new file mode 100644 index 00000000000..1ee34c19013 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_obs_fs.cpp @@ -0,0 +1,131 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'enkf_obs_fs.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 testS( ert_test_context_type * test_context ) { + { + enkf_main_type * enkf_main = ert_test_context_get_main( test_context ); + enkf_obs_type * enkf_obs = enkf_main_get_obs( enkf_main ); + enkf_fs_type * fs = enkf_main_get_fs( enkf_main ); + int_vector_type * active_list = int_vector_alloc(0,0); + obs_data_type * obs_data = obs_data_alloc(1.0); + local_obsdata_type * obs_set = local_obsdata_alloc( "KEY" ); + bool_vector_type * ens_mask; + meas_data_type * meas_data; + + + for (int i= 0; i < enkf_main_get_ensemble_size( enkf_main); i++) + int_vector_append( active_list , i ); + ens_mask = int_vector_alloc_mask( active_list); + + obs_data = obs_data_alloc(1.0); + meas_data = meas_data_alloc( ens_mask ); + + enkf_obs_add_local_nodes_with_data( enkf_obs , obs_set , fs , ens_mask ); + enkf_obs_get_obs_and_measure_data( enkf_obs , fs , obs_set, active_list , meas_data , obs_data); + + { + FILE * stream = util_fopen("analysis/Smatrix" , "r"); + matrix_type * S = meas_data_allocS( meas_data ); + matrix_type * S0 = matrix_fread_alloc( stream ); + + test_assert_true( matrix_equal( S0 , S )); + + matrix_free( S ); + matrix_free( S0 ); + fclose( stream ); + } + int_vector_free( active_list ); + meas_data_free( meas_data ); + + //deactivating a block and check for mask + obs_block_type * obs_block = obs_data_iget_block( obs_data , 0 ); + obs_block_deactivate( obs_block , 0 , false , "---"); + int obs_size = obs_data_get_total_size( obs_data ); + const bool_vector_type * mask = obs_data_get_active_mask(obs_data); + test_assert_false( bool_vector_iget(mask, 0) ); + test_assert_true( bool_vector_iget(mask, 1) ); + test_assert_true( bool_vector_iget(mask, obs_size-1) ); + + obs_data_free( obs_data ); + local_obsdata_free( obs_set ); + bool_vector_free( ens_mask ); + } +} + + + +void test_iget(ert_test_context_type * test_context) { + enkf_main_type * enkf_main = ert_test_context_get_main( test_context ); + enkf_obs_type * enkf_obs = enkf_main_get_obs( enkf_main ); + + test_assert_int_equal( 32 , enkf_obs_get_size( enkf_obs ) ); + for (int iobs = 0; iobs < enkf_obs_get_size( enkf_obs ); iobs++) { + obs_vector_type * vec1 = enkf_obs_iget_vector( enkf_obs , iobs ); + obs_vector_type * vec2 = enkf_obs_get_vector( enkf_obs , obs_vector_get_key( vec1 )); + + test_assert_ptr_equal( vec1 , vec2 ); + } +} + + +void test_container( ert_test_context_type * test_context ) { + enkf_config_node_type * config_node = enkf_config_node_new_container( "CONTAINER" ); + enkf_config_node_type * wwct1_node = enkf_config_node_alloc_summary( "WWCT:OP_1" , LOAD_FAIL_SILENT); + enkf_config_node_type * wwct2_node = enkf_config_node_alloc_summary( "WWCT:OP_2" , LOAD_FAIL_SILENT); + enkf_config_node_type * wwct3_node = enkf_config_node_alloc_summary( "WWCT:OP_3" , LOAD_FAIL_SILENT); + + + enkf_config_node_update_container( config_node , wwct1_node ); + enkf_config_node_update_container( config_node , wwct2_node ); + enkf_config_node_update_container( config_node , wwct3_node ); + { + enkf_node_type * container = enkf_node_deep_alloc( config_node ); + enkf_node_free( container ); + } + + + enkf_config_node_free( wwct3_node ); + enkf_config_node_free( wwct2_node ); + enkf_config_node_free( wwct1_node ); + enkf_config_node_free( config_node ); +} + + +int main(int argc , char ** argv) { + util_install_signals(); + { + const char * config_file = argv[1]; + ert_test_context_type * test_context = ert_test_context_alloc( "ENKF_OBS_FS" , config_file ); + { + testS( test_context ); + test_iget( test_context ); + test_container( test_context ); + } + ert_test_context_free( test_context ); + exit(0); + } +} diff --git a/libres/lib/enkf/tests/enkf_obs_invalid_path.cpp b/libres/lib/enkf/tests/enkf_obs_invalid_path.cpp new file mode 100644 index 00000000000..2079223dc48 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_obs_invalid_path.cpp @@ -0,0 +1,92 @@ +/* + Copyright (C) 2018 Equinor ASA, Norway. + + The file 'enkf_obs_invalid_file.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 + + +void test_invalid_path() { + ecl::util::TestArea ta("conf"); + util_make_path("obs_path"); + { + FILE * stream = util_fopen("obs_path/conf.txt","w"); + fprintf(stream, + "GENERAL_OBSERVATION WPR_DIFF_1 {" + "DATA = SNAKE_OIL_WPR_DIFF;" + "INDEX_LIST = 400,800,1200,1800;" + "RESTART = 199;" + "OBS_FILE = obs_path/obs.txt;" + "};"); + fclose(stream); + } + { + FILE * stream = util_fopen("obs_path/obs.txt","w"); + fclose(stream); + } + + conf_class_type * enkf_conf_class = enkf_obs_get_obs_conf_class(); + conf_instance_type * enkf_conf = conf_instance_alloc_from_file(enkf_conf_class, + "enkf_conf", + "obs_path/conf.txt"); + test_assert_true(conf_instance_get_path_error(enkf_conf)); + test_assert_false(conf_instance_validate(enkf_conf)); + + conf_instance_free(enkf_conf); +} + + +void test_valid_path() { + ecl::util::TestArea ta("valid"); + util_make_path("obs_path"); + { + FILE * stream = util_fopen("obs_path/conf.txt","w"); + fprintf(stream, + "GENERAL_OBSERVATION WPR_DIFF_1 {\n" + "DATA = SNAKE_OIL_WPR_DIFF;\n" + "INDEX_LIST = 400,800,1200,1800;\n" + "RESTART = 199;\n" + "OBS_FILE = obs.txt;\n" + "};"); + fclose(stream); + } + { + FILE * stream = util_fopen("obs_path/obs.txt","w"); + fclose(stream); + } + + conf_class_type * enkf_conf_class = enkf_obs_get_obs_conf_class(); + conf_instance_type * enkf_conf = conf_instance_alloc_from_file(enkf_conf_class, + "enkf_conf", + "obs_path/conf.txt"); + + test_assert_false(conf_instance_get_path_error(enkf_conf)); + test_assert_true(conf_instance_validate(enkf_conf)); + + conf_instance_free(enkf_conf); +} + + + +int main(int argc , char ** argv) { + test_valid_path(); + test_invalid_path(); +} diff --git a/libres/lib/enkf/tests/enkf_obs_tests.cpp b/libres/lib/enkf/tests/enkf_obs_tests.cpp new file mode 100644 index 00000000000..2ddc5724a64 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_obs_tests.cpp @@ -0,0 +1,60 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_enkf_obs_tests.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 + + +int main(int argc, char ** argv) { + history_type * history = NULL; + time_map_type * external_time_map = NULL; + ecl_grid_type * grid = NULL; + ensemble_config_type * ensemble_config = NULL; + ecl_sum_type * refcase = NULL; + + enkf_obs_type * enkf_obs = enkf_obs_alloc(history , external_time_map , grid , refcase , ensemble_config); + + obs_vector_type * obs_vector = obs_vector_alloc(SUMMARY_OBS, "WWCT", NULL, 2); + summary_obs_type * summary_obs1 = summary_obs_alloc( "SummaryKey" , "ObservationKey" , 43.2, 2.0); + obs_vector_install_node( obs_vector , 0 , summary_obs1 ); + + summary_obs_type * summary_obs2 = summary_obs_alloc( "SummaryKey2" , "ObservationKey2" , 4.2, 0.1); + obs_vector_install_node( obs_vector , 1 , summary_obs2 ); + + obs_vector_type * obs_vector2 = obs_vector_alloc(SUMMARY_OBS, "WWCT2", NULL, 2); + summary_obs_type * summary_obs3 = summary_obs_alloc( "SummaryKey" , "ObservationKey" , 43.2, 2.0); + obs_vector_install_node( obs_vector2 , 0 , summary_obs3 ); + + summary_obs_type * summary_obs4 = summary_obs_alloc( "SummaryKey2" , "ObservationKey2" , 4.2, 0.1); + obs_vector_install_node( obs_vector2 , 1 , summary_obs4 ); + + enkf_obs_add_obs_vector(enkf_obs, obs_vector); + enkf_obs_add_obs_vector(enkf_obs, obs_vector2); + + enkf_obs_scale_std(enkf_obs, 3.3); + + enkf_obs_free(enkf_obs); + + exit(0); +} + diff --git a/libres/lib/enkf/tests/enkf_obs_vector.cpp b/libres/lib/enkf/tests/enkf_obs_vector.cpp new file mode 100644 index 00000000000..212b4984c3b --- /dev/null +++ b/libres/lib/enkf/tests/enkf_obs_vector.cpp @@ -0,0 +1,78 @@ +/* + Copyright (C) 2015 Equinor ASA, Norway. + + The file 'enkf_obs_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 + + + +void test_create(enkf_config_node_type * config_node ) { + obs_vector_type * obs_vector = obs_vector_alloc( SUMMARY_OBS , "OBS" , config_node , 100 ); + test_assert_true( obs_vector_is_instance( obs_vector )); + { + const int_vector_type * step_list = obs_vector_get_step_list( obs_vector ); + + { + summary_obs_type * obs_node = summary_obs_alloc( "FOPT" , "FOPT" , 10 , 1); + obs_vector_install_node( obs_vector , 10 , obs_node ); + test_assert_int_equal( 1 , int_vector_size( step_list )); + test_assert_int_equal( 10 , int_vector_iget( step_list , 0)); + } + + { + summary_obs_type * obs_node = summary_obs_alloc( "FOPT" , "FOPT" , 10 , 1 ); + obs_vector_install_node( obs_vector , 10 , obs_node ); + test_assert_int_equal( 1 , int_vector_size( step_list )); + test_assert_int_equal( 10 , int_vector_iget( step_list , 0)); + } + + { + summary_obs_type * obs_node = summary_obs_alloc( "FOPT" , "FOPT" , 10 , 1); + obs_vector_install_node( obs_vector , 5 , obs_node ); + test_assert_int_equal( 2 , int_vector_size( step_list )); + test_assert_int_equal( 5 , int_vector_iget( step_list , 0)); + test_assert_int_equal( 10 , int_vector_iget( step_list , 1)); + } + + { + summary_obs_type * obs_node = summary_obs_alloc( "FOPT" , "FOPT" , 10 , 1); + obs_vector_install_node( obs_vector , 15 , obs_node ); + test_assert_int_equal( 3 , int_vector_size( step_list )); + test_assert_int_equal( 5 , int_vector_iget( step_list , 0)); + test_assert_int_equal( 10 , int_vector_iget( step_list , 1)); + test_assert_int_equal( 15 , int_vector_iget( step_list , 2)); + } + } + obs_vector_free( obs_vector ); +} + + + +int main(int argc , char ** argv) { + enkf_config_node_type * config_node = enkf_config_node_alloc_summary("FOPR" , LOAD_FAIL_EXIT); + { + test_create( config_node ); + } + enkf_config_node_free( config_node ); +} + diff --git a/libres/lib/enkf/tests/enkf_obs_vector_fs.cpp b/libres/lib/enkf/tests/enkf_obs_vector_fs.cpp new file mode 100644 index 00000000000..2595eaec337 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_obs_vector_fs.cpp @@ -0,0 +1,110 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'enkf_obs_vector_fs.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 + + +void test_valid_obs_vector( enkf_main_type * enkf_main , const char * obs_key) { + enkf_fs_type * fs = enkf_main_get_fs(enkf_main); + enkf_obs_type * enkf_obs = enkf_main_get_obs( enkf_main ); + obs_vector_type * obs_vector = enkf_obs_get_vector( enkf_obs , obs_key ); + bool_vector_type * active_mask = bool_vector_alloc( enkf_main_get_ensemble_size( enkf_main ) , true); + + test_assert_true( obs_vector_has_data( obs_vector , active_mask , fs )); + bool_vector_free( active_mask ); +} + + +/* + This test will modify the enkf_obs container with invalid data; must + be the last test. +*/ + +void test_invalid_obs_vector( enkf_main_type * enkf_main , const char * obs_key) { + enkf_fs_type * fs = enkf_main_get_fs(enkf_main); + enkf_obs_type * enkf_obs = enkf_main_get_obs( enkf_main ); + obs_vector_type * obs_vector = enkf_obs_get_vector( enkf_obs , obs_key ); + bool_vector_type * active_mask = bool_vector_alloc( enkf_main_get_ensemble_size( enkf_main ) , true); + + test_assert_false( obs_vector_has_data( obs_vector , active_mask , fs )); + bool_vector_free( active_mask ); +} + + +void test_container( ert_test_context_type * test_context ) { + enkf_main_type * enkf_main = ert_test_context_get_main( test_context ); + enkf_obs_type * enkf_obs = enkf_main_get_obs( enkf_main ); + obs_vector_type * rft_obs = enkf_obs_get_vector( enkf_obs , "RFT_TEST"); + enkf_fs_type * fs = enkf_main_get_fs( enkf_main ); + bool_vector_type * active_mask = bool_vector_alloc( enkf_main_get_ensemble_size( enkf_main ) , true ); + + test_assert_true( obs_vector_has_data( rft_obs , active_mask , fs )); + bool_vector_free( active_mask ); +} + + + +void test_measure( ert_test_context_type * test_context ) { + enkf_main_type * enkf_main = ert_test_context_get_main( test_context ); + enkf_fs_type * fs = enkf_main_get_fs( enkf_main ); + enkf_obs_type * enkf_obs = enkf_main_get_obs( enkf_main ); + obs_vector_type * rft_obs = enkf_obs_get_vector( enkf_obs , "RFT_TEST"); + int_vector_type * ens_active_list = int_vector_alloc(0,0); + active_list_type * active_list = active_list_alloc( ); + meas_data_type * meas_data_RFT; + + for (int i=0; i < enkf_main_get_ensemble_size( enkf_main ); i++) + int_vector_append( ens_active_list , i ); + + { + bool_vector_type * ens_mask; + ens_mask = int_vector_alloc_mask( ens_active_list ); + meas_data_RFT = meas_data_alloc( ens_mask ); + bool_vector_free( ens_mask ); + } + + obs_vector_measure( rft_obs , fs , 20 , ens_active_list , meas_data_RFT , active_list ); + + int_vector_free( ens_active_list ); + active_list_free( active_list ); + meas_data_free( meas_data_RFT ); +} + + + + +int main(int argc , char ** argv) { + const char * config_file = argv[1]; + ert_test_context_type * context = ert_test_context_alloc( "OBS_VECTOR_FS" , config_file); + enkf_main_type * enkf_main = ert_test_context_get_main( context ); + + { + test_valid_obs_vector( enkf_main , "WWCT:OP_3"); + test_container( context ); + test_measure( context ); + test_invalid_obs_vector( enkf_main , "GOPT:OP"); + } + ert_test_context_free( context ); +} + diff --git a/libres/lib/enkf/tests/enkf_plot_data.cpp b/libres/lib/enkf/tests/enkf_plot_data.cpp new file mode 100644 index 00000000000..df941e34daf --- /dev/null +++ b/libres/lib/enkf/tests/enkf_plot_data.cpp @@ -0,0 +1,38 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_plot_data.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 + + + +void test_create() { + enkf_plot_data_type * plot_data = enkf_plot_data_alloc( NULL ); + test_assert_true( enkf_plot_data_is_instance( plot_data )); + test_assert_int_equal( 0 , enkf_plot_data_get_size( plot_data )); + enkf_plot_data_free( plot_data ); +} + + + +int main(int argc , char ** argv) { + test_create(); +} diff --git a/libres/lib/enkf/tests/enkf_plot_data_fs.cpp b/libres/lib/enkf/tests/enkf_plot_data_fs.cpp new file mode 100644 index 00000000000..d118e0947d5 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_plot_data_fs.cpp @@ -0,0 +1,121 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_plot_data_fs.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 + + +void test_load_GEN_KW( enkf_main_type * enkf_main , const char * key , const char * index_key) { + ensemble_config_type * ensemble_config = enkf_main_get_ensemble_config( enkf_main ); + const enkf_config_node_type * config_node = ensemble_config_get_node( ensemble_config , key ); + enkf_plot_data_type * plot_data = enkf_plot_data_alloc( config_node ); + + { + enkf_fs_type * enkf_fs = enkf_main_mount_alt_fs( enkf_main , "enkf" , true ); + + enkf_plot_data_load( plot_data , enkf_fs , index_key , NULL ); + test_assert_int_equal( 25 , enkf_plot_data_get_size( plot_data )); + { + enkf_plot_tvector_type * plot_vector = enkf_plot_data_iget( plot_data , 10 ); + test_assert_true( enkf_plot_tvector_is_instance( plot_vector )); + test_assert_int_equal( 63 , enkf_plot_tvector_size( plot_vector )); + + test_assert_true( enkf_plot_tvector_iget_active( plot_vector , 0 )); + test_assert_true( enkf_plot_tvector_iget_active( plot_vector , 10 )); + test_assert_true( enkf_plot_tvector_iget_active( plot_vector , 20 )); + test_assert_true( enkf_plot_tvector_iget_active( plot_vector , 30 )); + + test_assert_false( enkf_plot_tvector_iget_active( plot_vector , 1 )); + test_assert_false( enkf_plot_tvector_iget_active( plot_vector , 11 )); + test_assert_false( enkf_plot_tvector_iget_active( plot_vector , 21 )); + test_assert_false( enkf_plot_tvector_iget_active( plot_vector , 31 )); + } + enkf_fs_decref( enkf_fs ); + } + enkf_plot_data_free( plot_data ); +} + + + +void test_load_summary( enkf_main_type * enkf_main , const char * summary_key) { + ensemble_config_type * ensemble_config = enkf_main_get_ensemble_config( enkf_main ); + const enkf_config_node_type * config_node = ensemble_config_get_node( ensemble_config , summary_key ); + enkf_plot_data_type * plot_data = enkf_plot_data_alloc( config_node ); + + { + enkf_fs_type * enkf_fs = enkf_main_mount_alt_fs( enkf_main , "enkf" , true ); + enkf_plot_data_load( plot_data , enkf_fs , NULL , NULL ); + test_assert_int_equal( 25 , enkf_plot_data_get_size( plot_data )); + { + enkf_plot_tvector_type * plot_vector = enkf_plot_data_iget( plot_data , 10 ); + test_assert_true( enkf_plot_tvector_is_instance( plot_vector )); + test_assert_false( enkf_plot_tvector_iget_active( plot_vector , 0 )); + test_assert_int_equal( 63 , enkf_plot_tvector_size( plot_vector )); + } + enkf_fs_decref( enkf_fs ); + } + + { + enkf_fs_type * enkf_fs = enkf_main_mount_alt_fs( enkf_main , "default" , true ); + enkf_plot_data_load( plot_data , enkf_fs , NULL , NULL ); + test_assert_int_equal( 25 , enkf_plot_data_get_size( plot_data )); + { + enkf_plot_tvector_type * plot_vector = enkf_plot_data_iget( plot_data , 0 ); + test_assert_true( enkf_plot_tvector_is_instance( plot_vector )); + test_assert_false( enkf_plot_tvector_iget_active( plot_vector , 0 )); + test_assert_int_equal( 63 , enkf_plot_tvector_size( plot_vector )); + + plot_vector = enkf_plot_data_iget( plot_data , 1 ); + test_assert_true( enkf_plot_tvector_is_instance( plot_vector )); + test_assert_int_equal( 0 , enkf_plot_tvector_size( plot_vector )); + } + enkf_fs_decref( enkf_fs ); + } + enkf_plot_data_free( plot_data ); +} + + + + + +int main(int argc, char ** argv) { + util_install_signals(); + { + const char * config_file = argv[1]; + ecl::util::TestArea ta("plot_fs"); + char * model_config; + util_alloc_file_components( config_file , NULL , &model_config , NULL); + ta.copy_parent_content(config_file); + { + res_config_type * res_config = res_config_alloc_load(model_config); + enkf_main_type * enkf_main = enkf_main_alloc(res_config, false, false); + + test_load_summary(enkf_main , "WWCT:OP_3"); + test_load_GEN_KW( enkf_main , "MULTFLT" , "F3"); + enkf_main_free( enkf_main ); + res_config_free(res_config); + } + exit(0); + } +} diff --git a/libres/lib/enkf/tests/enkf_plot_gen_kw.cpp b/libres/lib/enkf/tests/enkf_plot_gen_kw.cpp new file mode 100644 index 00000000000..2008bf33e8a --- /dev/null +++ b/libres/lib/enkf/tests/enkf_plot_gen_kw.cpp @@ -0,0 +1,55 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'enkf_plot_gen_kw.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 + +void test_create_invalid() { + enkf_config_node_type * config_node = enkf_config_node_alloc_summary( "WWCT" , LOAD_FAIL_SILENT); + enkf_plot_gen_kw_type * gen_kw = enkf_plot_gen_kw_alloc( config_node ); + + test_assert_NULL( gen_kw ); + enkf_config_node_free( config_node ); +} + + +void test_create() { + enkf_config_node_type * config_node = enkf_config_node_new_gen_kw( "GEN_KW" , DEFAULT_GEN_KW_TAG_FORMAT, false); + + { + enkf_plot_gen_kw_type * gen_kw = enkf_plot_gen_kw_alloc( config_node ); + test_assert_true( enkf_plot_gen_kw_is_instance( gen_kw )); + test_assert_int_equal( 0 , enkf_plot_gen_kw_get_size( gen_kw )); + enkf_plot_gen_kw_free( gen_kw ); + } + + enkf_config_node_free( config_node ); +} + + + + +int main( int argc , char ** argv) { + test_create(); + test_create_invalid(); + exit(0); +} diff --git a/libres/lib/enkf/tests/enkf_plot_gen_kw_fs.cpp b/libres/lib/enkf/tests/enkf_plot_gen_kw_fs.cpp new file mode 100644 index 00000000000..76047b5852c --- /dev/null +++ b/libres/lib/enkf/tests/enkf_plot_gen_kw_fs.cpp @@ -0,0 +1,79 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'enkf_plot_gen_kw_fs.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_load(const char * config_file) { + ert_test_context_type * test_context = ert_test_context_alloc( "GEN_KW" , config_file ); + enkf_main_type * enkf_main = ert_test_context_get_main( test_context ); + int ens_size = enkf_main_get_ensemble_size( enkf_main ); + stringlist_type * param_list = stringlist_alloc_new(); + enkf_fs_type * init_fs = enkf_fs_create_fs( "fs" , BLOCK_FS_DRIVER_ID , NULL , true ); + bool_vector_type * iens_mask = bool_vector_alloc( ens_size , true ); + path_fmt_type * runpath_fmt = model_config_get_runpath_fmt(enkf_main_get_model_config(enkf_main)); + ert_run_context_type * run_context = ert_run_context_alloc_INIT_ONLY(init_fs, INIT_CONDITIONAL, iens_mask, runpath_fmt, NULL, 0); + + stringlist_append_copy( param_list , "GEN_KW"); + enkf_main_initialize_from_scratch( enkf_main , param_list, run_context ); + { + ensemble_config_type * ensemble_config = enkf_main_get_ensemble_config( enkf_main ); + enkf_config_node_type * config_node = ensemble_config_get_node( ensemble_config , "GEN_KW"); + enkf_plot_gen_kw_type * plot_gen_kw = enkf_plot_gen_kw_alloc( config_node ); + bool_vector_type * input_mask = bool_vector_alloc( ens_size , true ); + gen_kw_config_type * gen_kw_config = (gen_kw_config_type *)enkf_config_node_get_ref( config_node ); + + enkf_plot_gen_kw_load( plot_gen_kw , init_fs , true , 0 , input_mask ); + + test_assert_int_equal( ens_size , enkf_plot_gen_kw_get_size( plot_gen_kw )); + + test_assert_int_equal(4, enkf_plot_gen_kw_get_keyword_count(plot_gen_kw)); + + { + enkf_plot_gen_kw_vector_type * vector = enkf_plot_gen_kw_iget( plot_gen_kw , 0 ); + for (int i=0; i < enkf_plot_gen_kw_vector_get_size( vector ); i++) + test_assert_string_equal( enkf_plot_gen_kw_iget_key( plot_gen_kw , i ) , gen_kw_config_iget_name( gen_kw_config , i)); + } + bool_vector_free( input_mask ); + } + + bool_vector_free( iens_mask ); + stringlist_free( param_list ); + enkf_fs_decref( init_fs ); + ert_test_context_free( test_context ); +} + + + + + +int main( int argc , char ** argv) { + util_install_signals(); + { + const char * config_file = argv[1]; + test_load( config_file ); + exit(0); + } +} + diff --git a/libres/lib/enkf/tests/enkf_plot_gen_kw_vector.cpp b/libres/lib/enkf/tests/enkf_plot_gen_kw_vector.cpp new file mode 100644 index 00000000000..de5da06976d --- /dev/null +++ b/libres/lib/enkf/tests/enkf_plot_gen_kw_vector.cpp @@ -0,0 +1,44 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'enkf_plot_gen_kw_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 +#include + + +void test_create() { + enkf_config_node_type * config_node = enkf_config_node_new_gen_kw( "GEN_KW" , DEFAULT_GEN_KW_TAG_FORMAT, false); + + enkf_plot_gen_kw_vector_type * vector = enkf_plot_gen_kw_vector_alloc( config_node , 0 ); + test_assert_true( enkf_plot_gen_kw_vector_is_instance( vector )); + test_assert_int_equal( 0 , enkf_plot_gen_kw_vector_get_size( vector )); + + enkf_plot_gen_kw_vector_free( vector ); + enkf_config_node_free( config_node ); +} + + + + +int main( int argc , char ** argv) { + test_create(); + exit(0); +} diff --git a/libres/lib/enkf/tests/enkf_plot_gendata.cpp b/libres/lib/enkf/tests/enkf_plot_gendata.cpp new file mode 100644 index 00000000000..e576b9770f3 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_plot_gendata.cpp @@ -0,0 +1,50 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'enkf_plot_gendata.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 + +void test_create_invalid() { + enkf_config_node_type * config_node = enkf_config_node_alloc_summary( "WWCT" , LOAD_FAIL_SILENT); + obs_vector_type * obs_vector = obs_vector_alloc(SUMMARY_OBS , "OBS" , config_node , 100); + enkf_plot_gendata_type * gen_data = enkf_plot_gendata_alloc_from_obs_vector( obs_vector ); + test_assert_NULL( gen_data ); + enkf_config_node_free( config_node ); + obs_vector_free( obs_vector ); +} + + +void test_create() { + enkf_config_node_type * config_node = enkf_config_node_alloc_GEN_DATA_result( "key" , ASCII , "Result:%d"); + enkf_plot_gendata_type * gen_data = enkf_plot_gendata_alloc( config_node ); + test_assert_true( enkf_plot_gendata_is_instance( gen_data )); + test_assert_int_equal( 0 , enkf_plot_gendata_get_size( gen_data )); + enkf_config_node_free( config_node ); + enkf_plot_gendata_free( gen_data); +} + + + + +int main( int argc , char ** argv) { + test_create(); + test_create_invalid(); + exit(0); +} diff --git a/libres/lib/enkf/tests/enkf_plot_gendata_fs.cpp b/libres/lib/enkf/tests/enkf_plot_gendata_fs.cpp new file mode 100644 index 00000000000..aad5c7108e2 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_plot_gendata_fs.cpp @@ -0,0 +1,96 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'enkf_plot_gendata_fs.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 + + + + +void test_gendata( enkf_main_type * enkf_main , const char * obs_key , int report_step ) { + enkf_obs_type * enkf_obs = enkf_main_get_obs( enkf_main ); + + obs_vector_type * obs_vector = enkf_obs_get_vector( enkf_obs , obs_key); + + { + enkf_plot_gendata_type * gen_data = enkf_plot_gendata_alloc_from_obs_vector( obs_vector ); + + enkf_fs_type * fs = enkf_main_get_fs( enkf_main ); + gen_obs_type * gen_obs = (gen_obs_type *) obs_vector_iget_node( obs_vector , report_step ); + + { + + double value; + double std; + bool valid; + gen_obs_user_get_with_data_index(gen_obs , "0" , &value , &std , &valid ); + test_assert_double_equal( 0.143841 , value ); + test_assert_double_equal( 0.0300 , std ); + test_assert_true( valid ); + + } + + enkf_plot_gendata_load(gen_data, fs, report_step, NULL); + + test_assert_int_equal( enkf_main_get_ensemble_size( enkf_main ) , enkf_plot_gendata_get_size( gen_data )); + + { + enkf_plot_genvector_type * vector = enkf_plot_gendata_iget( gen_data , 24); + test_assert_true( enkf_plot_genvector_is_instance( vector )); + test_assert_double_equal( 0.675537 , enkf_plot_genvector_iget( vector , 0 )); + test_assert_double_equal( 0.682635 , enkf_plot_genvector_iget( vector , 1 )); + test_assert_double_equal( 0.616371 , enkf_plot_genvector_iget( vector , 2 )); + + + } + + { + enkf_plot_genvector_type * vector = enkf_plot_gendata_iget( gen_data , 9 ); + test_assert_true( enkf_plot_genvector_is_instance( vector )); + test_assert_double_equal( -0.515033 , enkf_plot_genvector_iget( vector , 0 )); + test_assert_double_equal( -0.507350 , enkf_plot_genvector_iget( vector , 1 )); + test_assert_double_equal( -0.541030 , enkf_plot_genvector_iget( vector , 2 )); + } + + + enkf_plot_gendata_free( gen_data ); + } + + +} + + + +int main( int argc , char ** argv) { + const char * config_file = argv[1]; + util_install_signals(); + ert_test_context_type * test_context = ert_test_context_alloc("GENDATA" , config_file ); + enkf_main_type * enkf_main = ert_test_context_get_main( test_context ); + + test_gendata( enkf_main , "GEN_TIMESHIFT" , 60); + + ert_test_context_free( test_context ); + exit(0); +} diff --git a/libres/lib/enkf/tests/enkf_plot_genvector.cpp b/libres/lib/enkf/tests/enkf_plot_genvector.cpp new file mode 100644 index 00000000000..58651ea3b42 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_plot_genvector.cpp @@ -0,0 +1,43 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'enkf_plot_genvector.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_create() { + enkf_config_node_type * config_node = enkf_config_node_alloc_GEN_DATA_result( "Key" , ASCII , "Result%d"); + enkf_plot_genvector_type * gen_vector = enkf_plot_genvector_alloc( config_node , 0 ); + test_assert_true( enkf_plot_genvector_is_instance( gen_vector )); + test_assert_int_equal( 0 , enkf_plot_genvector_get_size( gen_vector )); + enkf_config_node_free( config_node ); + enkf_plot_genvector_free(gen_vector); +} + + + + +int main( int argc , char ** argv) { + test_create(); + exit(0); +} diff --git a/libres/lib/enkf/tests/enkf_plot_tvector.cpp b/libres/lib/enkf/tests/enkf_plot_tvector.cpp new file mode 100644 index 00000000000..99b3aed8493 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_plot_tvector.cpp @@ -0,0 +1,105 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_plot_tvector.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 + + +void create_test() { + enkf_config_node_type * config_node = enkf_config_node_alloc_summary("KEY" , LOAD_FAIL_SILENT); + enkf_plot_tvector_type * tvector = enkf_plot_tvector_alloc( config_node , 0 ); + test_assert_true( enkf_plot_tvector_is_instance( tvector )); + enkf_plot_tvector_free( tvector ); +} + + + +void test_iset() { + enkf_config_node_type * config_node = enkf_config_node_alloc_summary("KEY" , LOAD_FAIL_SILENT); + enkf_plot_tvector_type * tvector = enkf_plot_tvector_alloc( config_node , 0 ); + enkf_plot_tvector_iset( tvector , 10 , 0 , 100 ); + + test_assert_int_equal( 11 , enkf_plot_tvector_size( tvector )); + test_assert_time_t_equal( 0 , enkf_plot_tvector_iget_time( tvector , 10 )); + test_assert_double_equal( 100 , enkf_plot_tvector_iget_value( tvector , 10 )); + { + for (int i=0; i < (enkf_plot_tvector_size( tvector ) - 1); i++) + test_assert_false( enkf_plot_tvector_iget_active( tvector , i )); + + test_assert_true( enkf_plot_tvector_iget_active( tvector , 10 )); + } + + enkf_plot_tvector_free( tvector ); +} + + +void test_all_active() { + enkf_config_node_type * config_node = enkf_config_node_alloc_summary("KEY" , LOAD_FAIL_SILENT); + enkf_plot_tvector_type * tvector = enkf_plot_tvector_alloc( config_node , 0); + test_assert_true( enkf_plot_tvector_all_active( tvector )); + + enkf_plot_tvector_iset( tvector , 00 , 0 , 100 ); + test_assert_true( enkf_plot_tvector_all_active( tvector )); + + enkf_plot_tvector_iset( tvector , 1 , 0 , 100 ); + test_assert_true( enkf_plot_tvector_all_active( tvector )); + + enkf_plot_tvector_iset( tvector , 10 , 0 , 100 ); + test_assert_false( enkf_plot_tvector_all_active( tvector )); +} + + + +void test_iget() { + enkf_config_node_type * config_node = enkf_config_node_alloc_summary("KEY" , LOAD_FAIL_SILENT); + enkf_plot_tvector_type * tvector = enkf_plot_tvector_alloc( config_node , 0); + enkf_plot_tvector_iset( tvector , 0 , 0 , 0 ); + enkf_plot_tvector_iset( tvector , 1 , 100 , 10 ); + enkf_plot_tvector_iset( tvector , 2 , 200 , 20 ); + enkf_plot_tvector_iset( tvector , 3 , 300 , 30 ); + enkf_plot_tvector_iset( tvector , 4 , 400 , 40 ); + + enkf_plot_tvector_iset( tvector , 6 , 600 , 60 ); + + + test_assert_int_equal( 7 , enkf_plot_tvector_size( tvector )); + for (int i=0; i < 7; i++) { + if (i == 5) + test_assert_false( enkf_plot_tvector_iget_active( tvector , i )); + else { + test_assert_true( enkf_plot_tvector_iget_active( tvector , i )); + test_assert_time_t_equal( i * 100 , enkf_plot_tvector_iget_time( tvector , i )); + test_assert_double_equal( i * 10 , enkf_plot_tvector_iget_value( tvector , i )); + } + } +} + + + +int main(int argc , char ** argv) { + create_test(); + test_iset(); + test_all_active(); + test_iget(); + + exit(0); +} + diff --git a/libres/lib/enkf/tests/enkf_queue_config.cpp b/libres/lib/enkf/tests/enkf_queue_config.cpp new file mode 100644 index 00000000000..25a5c453aa7 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_queue_config.cpp @@ -0,0 +1,115 @@ +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + + +void test_empty() { + queue_config_type * queue_config = queue_config_alloc_load(NULL); + queue_config_type * queue_config_copy = queue_config_alloc_local_copy( queue_config ); + queue_config_free(queue_config); + queue_config_free(queue_config_copy); +} + +void test_job_script() { + ecl::util::TestArea ta("script"); + queue_config_type * queue_config = queue_config_alloc_load(NULL); + test_assert_false(queue_config_set_job_script( queue_config , "/does/not/exist" )); + + FILE * job_script = util_fopen("Script.sh" , "w"); + fclose( job_script ); + + test_assert_false( queue_config_set_job_script( queue_config , "Script.sh" )); + + chmod("Script.sh" , S_IRWXU ); + test_assert_true( queue_config_set_job_script( queue_config , "Script.sh" )); + test_assert_false( queue_config_set_job_script( queue_config , "DoesNotExits")); + + char * full_path = util_alloc_realpath( "Script.sh" ); + test_assert_string_equal( full_path , queue_config_get_job_script( queue_config)); + + free( full_path ); + queue_config_free( queue_config ); +} + +void test_parse() { + ecl::util::TestArea ta("parse"); + const char * user_config_file = "queue_config.txt"; + config_parser_type * parser = config_alloc( ); + + queue_config_add_config_items( parser, true ); + test_assert_true( config_has_schema_item( parser , QUEUE_SYSTEM_KEY )); + test_assert_true( config_has_schema_item( parser , QUEUE_OPTION_KEY )); + test_assert_true( config_has_schema_item( parser , JOB_SCRIPT_KEY )); + + FILE* stream1 = util_fopen("tiny_executable", "w"); + fclose(stream1); + util_chmod_if_owner("tiny_executable", 0777); + + FILE* stream = util_fopen(user_config_file, "w"); + fprintf(stream, "NUM_REALIZATIONS 14\n"); + fprintf(stream, "QUEUE_SYSTEM LSF\n"); + fprintf(stream, "LSF_SERVER be-grid01\n"); + fprintf(stream, "QUEUE_OPTION LSF BJOBS_CMD the_path\n"); + fprintf(stream, "JOB_SCRIPT tiny_executable\n"); + fprintf(stream, "MAX_SUBMIT 6\n"); + fclose(stream); + + config_content_type * config_content = config_parse(parser, user_config_file, NULL, NULL, NULL, NULL, CONFIG_UNRECOGNIZED_ERROR, true); + test_assert_true(config_content_has_item(config_content, QUEUE_SYSTEM_KEY)); + + test_assert_true(config_content_has_item(config_content, QUEUE_OPTION_KEY)); + test_assert_true(config_content_has_item(config_content, MAX_SUBMIT_KEY)); + + queue_config_type * queue_config = queue_config_alloc_load(user_config_file); + + test_assert_true(queue_config_has_queue_driver(queue_config, "LSF")); + test_assert_true(queue_config_get_driver_type(queue_config) == LSF_DRIVER); + + test_check_double_equal(queue_config_get_max_submit(queue_config), 6); + + queue_driver_type * lsf_driver = queue_config_get_queue_driver(queue_config, LSF_DRIVER_NAME); + test_assert_true(queue_driver_is_instance(lsf_driver)); + test_assert_string_equal((const char *) queue_driver_get_option(lsf_driver, LSF_BJOBS_CMD) , "the_path"); + + test_assert_true(queue_config_has_job_script(queue_config)); + test_assert_string_equal(queue_config_get_queue_system(queue_config), LSF_DRIVER_NAME); + + //test for licence path + job_queue_type * job_queue = queue_config_alloc_job_queue(queue_config); + test_assert_double_equal(job_queue_get_max_submit(job_queue), 6); + + + //testing for copy with local driver only + queue_config_type * queue_config_copy = queue_config_alloc_local_copy( queue_config ); + + test_assert_true( strcmp(queue_config_get_job_script(queue_config), + queue_config_get_job_script(queue_config_copy)) == 0); + + test_assert_true( queue_config_get_driver_type(queue_config_copy) == LOCAL_DRIVER ); + + test_assert_true( queue_config_get_max_submit(queue_config_copy) == 6); + + queue_config_free( queue_config_copy ); + queue_config_free( queue_config ); + job_queue_free(job_queue); + config_content_free(config_content); + config_free( parser ); +} + +int main() { + + util_install_signals(); + test_empty(); + test_parse(); + test_job_script(); + return 0; +} diff --git a/libres/lib/enkf/tests/enkf_refcase_list.cpp b/libres/lib/enkf/tests/enkf_refcase_list.cpp new file mode 100644 index 00000000000..01500364b44 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_refcase_list.cpp @@ -0,0 +1,118 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_refcase_list.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 + + +int main(int argc , char ** argv) { + const char * case1 = argv[1]; + const char * case_glob = argv[2]; + + { + ecl_refcase_list_type * refcase_list = ecl_refcase_list_alloc( ); + + test_assert_false( ecl_refcase_list_has_case( refcase_list , "DoesNotExist" )); + test_assert_NULL( ecl_refcase_list_get_case( refcase_list , "DoesNotExist")); + + test_assert_int_equal( ecl_refcase_list_add_matching( refcase_list , "DoesNotExist") , 0); + ecl_refcase_list_add_case( refcase_list , "DoesNotExist" ); + test_assert_false( ecl_refcase_list_has_case( refcase_list , "DoesNotExist")); + + ecl_refcase_list_add_case( refcase_list , case1 ); + test_assert_true( ecl_refcase_list_has_case( refcase_list , case1)); + + + test_assert_not_NULL( refcase_list ); + test_assert_false( ecl_refcase_list_has_default( refcase_list )); + test_assert_NULL( ecl_refcase_list_get_default( refcase_list )); + + test_assert_false( ecl_refcase_list_set_default( refcase_list , "DoesNotExist")); + test_assert_false( ecl_refcase_list_has_default( refcase_list )); + test_assert_NULL( ecl_refcase_list_get_default( refcase_list )); + test_assert_int_equal( 1 , ecl_refcase_list_get_size( refcase_list )); + + test_assert_true( ecl_refcase_list_set_default( refcase_list , case1)); + test_assert_true( ecl_refcase_list_has_default( refcase_list )); + test_assert_not_NULL( ecl_refcase_list_get_default( refcase_list )); + test_assert_int_equal( 1 , ecl_refcase_list_get_size( refcase_list )); + + test_assert_false( ecl_refcase_list_set_default( refcase_list , "DoesNotExist")); + test_assert_true( ecl_refcase_list_has_default( refcase_list )); + test_assert_not_NULL( ecl_refcase_list_get_default( refcase_list )); + test_assert_int_equal( 1 , ecl_refcase_list_get_size( refcase_list )); + test_assert_NULL( ecl_refcase_list_iget_case( refcase_list , 100)); + + ecl_refcase_list_free( refcase_list ); + } + + { + ecl_refcase_list_type * refcase_list = ecl_refcase_list_alloc( ); + test_assert_int_equal( ecl_refcase_list_add_matching( refcase_list , case_glob ) , 11); + test_assert_int_equal( 11 , ecl_refcase_list_get_size( refcase_list )); + + test_assert_true( ecl_refcase_list_set_default( refcase_list , case1)); + test_assert_true( ecl_refcase_list_has_default( refcase_list )); + test_assert_not_NULL( ecl_refcase_list_get_default( refcase_list )); + test_assert_int_equal( 11 , ecl_refcase_list_get_size( refcase_list )); + + test_assert_int_equal( ecl_refcase_list_add_matching( refcase_list , case_glob ) , 0); + test_assert_int_equal( ecl_refcase_list_add_matching( refcase_list , case_glob ) , 0); + { + const ecl_sum_type * ecl_sum = ecl_refcase_list_iget_case( refcase_list , 0 ); + test_assert_not_NULL( ecl_sum ); + } + test_assert_int_equal( 11 , ecl_refcase_list_get_size( refcase_list )); + + { + stringlist_type * case_list = stringlist_alloc_new( ); + const int N = ecl_refcase_list_get_size( refcase_list ); + int i; + for (i=0; i < N; i++) + stringlist_append_copy( case_list , ecl_refcase_list_iget_pathcase( refcase_list , N - 1 - i )); + + { + bool equal = true; + for (i=0; i < N; i++) + equal = equal && util_string_equal( stringlist_iget( case_list , i ) , ecl_refcase_list_iget_pathcase( refcase_list , i)); + + test_assert_false( equal ); + stringlist_sort( case_list , (string_cmp_ftype *) util_strcmp_int); + + equal = true; + for (i=0; i < N; i++) + equal = equal && util_string_equal( stringlist_iget( case_list , i ) , ecl_refcase_list_iget_pathcase( refcase_list , i)); + test_assert_true( equal ); + } + stringlist_free( case_list ); + } + ecl_refcase_list_add_matching( refcase_list , "DoesNotExist*"); + test_assert_int_equal( 11 , ecl_refcase_list_get_size( refcase_list )); + ecl_refcase_list_free( refcase_list ); + } + + + exit(0); +} + diff --git a/libres/lib/enkf/tests/enkf_run_arg.cpp b/libres/lib/enkf/tests/enkf_run_arg.cpp new file mode 100644 index 00000000000..bef4fa57285 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_run_arg.cpp @@ -0,0 +1,159 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'ert_run_context.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 + + +void call_get_queue_index( void * arg ) { + run_arg_type * run_arg = run_arg_safe_cast( arg ); + run_arg_get_queue_index( run_arg ); +} + +void call_set_queue_index( void * arg ) { + run_arg_type * run_arg = run_arg_safe_cast( arg ); + run_arg_set_queue_index( run_arg , 88 ); +} + + +void test_queue_index() { + ecl::util::TestArea ta("queue_index"); + { + enkf_fs_type * fs = enkf_fs_create_fs("sim" , BLOCK_FS_DRIVER_ID , NULL , true); + subst_list_type * subst_list = subst_list_alloc(NULL); + run_arg_type * run_arg = run_arg_alloc_ENSEMBLE_EXPERIMENT("run_id", fs , 0 , 6 , "path", "base", subst_list); + + test_assert_false( run_arg_is_submitted( run_arg ) ); + test_assert_util_abort("run_arg_get_queue_index" , call_get_queue_index , run_arg ); + + int qi = run_arg_get_queue_index_safe( run_arg ); + test_assert_int_equal( -1, qi ); // not submitted: index == -1 + + run_arg_set_queue_index(run_arg, 78); + test_assert_true( run_arg_is_submitted( run_arg ) ); + test_assert_int_equal( 78 , run_arg_get_queue_index( run_arg )); + + test_assert_util_abort("run_arg_set_queue_index" , call_set_queue_index , run_arg ); + run_arg_free( run_arg ); + subst_list_free(subst_list); + enkf_fs_decref( fs ); + } +} + +void call_get_sim_fs( void * arg ) { + run_arg_type * run_arg = run_arg_safe_cast( arg ); + run_arg_get_sim_fs( run_arg ); +} + + +void call_get_update_target_fs( void * arg ) { + run_arg_type * run_arg = run_arg_safe_cast( arg ); + run_arg_get_update_target_fs( run_arg ); +} + + + +void test_SMOOTHER_RUN( ) { + ecl::util::TestArea ta("smoother"); + { + enkf_fs_type * sim_fs = enkf_fs_create_fs("sim" , BLOCK_FS_DRIVER_ID , NULL , true); + enkf_fs_type * target_fs = enkf_fs_create_fs("target" , BLOCK_FS_DRIVER_ID , NULL , true); + subst_list_type * subst_list = subst_list_alloc(NULL); + run_arg_type * run_arg = run_arg_alloc_SMOOTHER_RUN("run_id", sim_fs , target_fs , 0 , 6 , "path", "BASE", subst_list); + test_assert_true( run_arg_is_instance( run_arg )); + test_assert_ptr_equal( run_arg_get_sim_fs( run_arg ) , sim_fs ); + test_assert_ptr_equal( run_arg_get_update_target_fs( run_arg ) , target_fs ); + run_arg_free( run_arg ); + subst_list_free(subst_list); + + enkf_fs_decref( sim_fs ); + enkf_fs_decref( target_fs ); + } +} + + +void alloc_invalid_run_arg(void *arg) { + ecl::util::TestArea ta("invalid_run"); + { + enkf_fs_type * fs = enkf_fs_create_fs("fs" , BLOCK_FS_DRIVER_ID , NULL , true); + subst_list_type * subst_list = subst_list_alloc(NULL); + run_arg_type * run_arg = run_arg_alloc_SMOOTHER_RUN("run_id", fs , fs , 0 , 6 , "path", "BASE", subst_list); // This should explode ... + run_arg_free( run_arg ); + subst_list_free(subst_list); + enkf_fs_decref( fs ); + } +} + + +void test_invalid_update_on_self( ) { + test_assert_util_abort( "run_arg_alloc" , alloc_invalid_run_arg , NULL); +} + + +void test_INIT_ONLY( ) { + ecl::util::TestArea ta("INIT"); + { + enkf_fs_type * init_fs = enkf_fs_create_fs("sim" , BLOCK_FS_DRIVER_ID , NULL , true); + + subst_list_type * subst_list = subst_list_alloc(NULL); + run_arg_type * run_arg = run_arg_alloc_INIT_ONLY("run_id", init_fs , 0 , 6 , "path", subst_list); + test_assert_true( run_arg_is_instance( run_arg )); + test_assert_ptr_equal( run_arg_get_sim_fs( run_arg ) , init_fs ); + + test_assert_util_abort( "run_arg_get_update_target_fs" , call_get_update_target_fs , run_arg ); + run_arg_free( run_arg ); + subst_list_free(subst_list); + + enkf_fs_decref( init_fs ); + } +} + + +void test_ENSEMBLE_EXPERIMENT( ) { + ecl::util::TestArea ta("ens"); + { + enkf_fs_type * fs = enkf_fs_create_fs("sim" , BLOCK_FS_DRIVER_ID , NULL , true); + + subst_list_type * subst_list = subst_list_alloc(NULL); + run_arg_type * run_arg = run_arg_alloc_ENSEMBLE_EXPERIMENT("run_id", fs , 0 , 6 , "path", "BASE", subst_list); + test_assert_true( run_arg_is_instance( run_arg )); + + test_assert_ptr_equal( run_arg_get_sim_fs( run_arg ) , fs ); + test_assert_util_abort( "run_arg_get_update_target_fs" , call_get_update_target_fs , run_arg ); + + test_assert_string_equal( run_arg_get_run_id( run_arg ) , "run_id"); + run_arg_free( run_arg ); + subst_list_free(subst_list); + enkf_fs_decref( fs ); + } +} + +// TODO: Write tests for the new functionality + +int main(int argc , char ** argv) { + test_queue_index(); + test_SMOOTHER_RUN(); + test_INIT_ONLY(); + test_ENSEMBLE_EXPERIMENT(); + test_invalid_update_on_self(); + exit(0); +} diff --git a/libres/lib/enkf/tests/enkf_runpath_list.cpp b/libres/lib/enkf/tests/enkf_runpath_list.cpp new file mode 100644 index 00000000000..ffe29ea28d8 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_runpath_list.cpp @@ -0,0 +1,153 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_runpath_list.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 + +void * add_pathlist( void * arg ) { + arg_pack_type * arg_pack = arg_pack_safe_cast( arg ); + runpath_list_type * list = (runpath_list_type *) arg_pack_iget_ptr( arg_pack , 0 ); + int offset = arg_pack_iget_int( arg_pack , 1 ); + int bs = arg_pack_iget_int( arg_pack , 2 ); + + int i; + for (i=0; i < bs; i++) + runpath_list_add( list , i + offset , 0, "Path" , "Basename"); + + return NULL; +} + +void test_runpath_list() { + runpath_list_type * list = runpath_list_alloc("DefaultFile"); + + test_assert_int_equal( runpath_list_size( list ) , 0 ); + + runpath_list_add( list , 3 , 0, "path" , "base"); + runpath_list_add( list , 2 , 0, "path" , "base"); + runpath_list_add( list , 1 , 0, "path" , "base"); + + runpath_list_add( list , 3 , 1, "path" , "base"); + runpath_list_add( list , 2 , 1, "path" , "base"); + runpath_list_add( list , 1 , 1, "path" , "base"); + + test_assert_int_equal( runpath_list_size( list ) , 6 ); + test_assert_int_equal( runpath_list_iget_iens( list , 0 ) , 3 ); + test_assert_int_equal( runpath_list_iget_iens( list , 2 ) , 1 ); + test_assert_int_equal( runpath_list_iget_iter( list , 3 ) , 1 ); + + runpath_list_clear( list ); + test_assert_int_equal( runpath_list_size( list ) , 0 ); + + test_assert_string_equal( runpath_list_get_line_fmt( list ) , RUNPATH_LIST_DEFAULT_LINE_FMT ); + { + const char * other_line = "%d %s %s"; + runpath_list_set_line_fmt( list , other_line ); + test_assert_string_equal( runpath_list_get_line_fmt( list ) , other_line ); + } + runpath_list_set_line_fmt( list , NULL ); + test_assert_string_equal( runpath_list_get_line_fmt( list ) , RUNPATH_LIST_DEFAULT_LINE_FMT ); + + { + const int block_size = 100; + const int threads = 100; + thread_pool_type * tp = thread_pool_alloc( threads , true ); + int it; + + for (it = 0; it < threads; it++) { + int iens_offset = it * block_size; + arg_pack_type * arg_pack = arg_pack_alloc(); + + arg_pack_append_ptr( arg_pack , list ); + arg_pack_append_int( arg_pack , iens_offset ); + arg_pack_append_int( arg_pack , block_size ); + + thread_pool_add_job( tp , add_pathlist , arg_pack ); + } + thread_pool_join( tp ); + test_assert_int_equal( runpath_list_size( list ) , block_size * threads ); + + { + ecl::util::TestArea ta("runpath_list"); + runpath_list_fprintf( list ); + { + int file_iens; + int file_iter; + char file_path[256]; + char file_base[256]; + int iens; + FILE * stream = util_fopen( runpath_list_get_export_file(list) , "r"); + for (iens = 0; iens < threads * block_size; iens++) { + int fscanf_return = fscanf( stream , "%d %s %s %d" , &file_iens , file_path , file_base, &file_iter); + test_assert_int_equal(fscanf_return, 4 ); + test_assert_int_equal( file_iens , iens ); + test_assert_int_equal( file_iter , 0 ); + } + fclose( stream ); + } + } + } + runpath_list_free( list ); +} + + + +void test_config( const char * config_file ) { + ert_test_context_type * test_context = ert_test_context_alloc( "RUNPATH_FILE" , config_file ); + enkf_main_type * enkf_main = ert_test_context_get_main( test_context ); + const hook_manager_type * hook_manager = enkf_main_get_hook_manager( enkf_main ); + + ert_test_context_run_worklow( test_context , "ARGECHO_WF"); + { + FILE * stream = util_fopen("runpath_list.txt" , "r"); + char runpath_file[256]; + fscanf(stream , "%s" , runpath_file ); + fclose( stream ); + test_assert_string_equal( runpath_file , hook_manager_get_runpath_list_file( hook_manager )); + } + + ert_test_context_free( test_context ); +} + + +void test_filename() { + runpath_list_type * list = runpath_list_alloc("DefaultFile"); + test_assert_string_equal( "DefaultFile" , runpath_list_get_export_file(list)); + runpath_list_set_export_file( list , "/tmp/file.txt"); + test_assert_string_equal( "/tmp/file.txt" , runpath_list_get_export_file(list)); + runpath_list_free( list ); +} + +int main(int argc , char ** argv) { + util_install_signals(); + { + test_runpath_list(); + test_config( argv[1] ); + test_filename(); + exit(0); + } +} + diff --git a/libres/lib/enkf/tests/enkf_scale_correlated_std.cpp b/libres/lib/enkf/tests/enkf_scale_correlated_std.cpp new file mode 100644 index 00000000000..0abb30ac4a9 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_scale_correlated_std.cpp @@ -0,0 +1,54 @@ +/* + Copyright (C) 2015 Equinor ASA, Norway. + + The file 'enkf_scale_correlated_std.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 + + +int main(int argc , const char ** argv) { + const char * config_file = argv[1]; + const char * workflow_job_file = argv[2]; + enkf_main_install_SIGNALS(); + { + ert_test_context_type * test_context = ert_test_context_alloc("std_scale_test" , config_file); + stringlist_type * args = stringlist_alloc_new(); + + ert_test_context_install_workflow_job( test_context , "STD_SCALE" , workflow_job_file ); + stringlist_append_copy( args, "WWCT:OP_1"); + test_assert_true( ert_test_context_run_worklow_job(test_context, "STD_SCALE", args)); + + stringlist_append_copy( args, "WWCT:OP_2"); + test_assert_true( ert_test_context_run_worklow_job(test_context, "STD_SCALE", args)); + + stringlist_clear(args); + stringlist_append_copy( args, "RPR2_1"); + stringlist_append_copy( args, "RPR2_2"); + stringlist_append_copy( args, "RPR2_3"); + stringlist_append_copy( args, "RPR2_4"); + stringlist_append_copy( args, "RPR2_5"); + stringlist_append_copy( args, "RPR2_6"); + stringlist_append_copy( args, "RPR2_7"); + stringlist_append_copy( args, "RPR2_8"); + test_assert_true( ert_test_context_run_worklow_job(test_context, "STD_SCALE", args)); + + stringlist_free( args ); + ert_test_context_free( test_context ); + } + exit(0); +} diff --git a/libres/lib/enkf/tests/enkf_select_case_job.cpp b/libres/lib/enkf/tests/enkf_select_case_job.cpp new file mode 100644 index 00000000000..2de6ad76f0c --- /dev/null +++ b/libres/lib/enkf/tests/enkf_select_case_job.cpp @@ -0,0 +1,49 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_select_case_job.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 + + +int main(int argc , const char ** argv) { + enkf_main_install_SIGNALS(); + + const char * config_file = argv[1]; + const char * select_case_job = argv[2]; + + ert_test_context_type * test_context = ert_test_context_alloc("SELECT_CASE" , config_file ); + + test_assert_true( ert_test_context_install_workflow_job( test_context , "SELECT_CASE" , select_case_job)); + { + enkf_main_type * enkf_main = ert_test_context_get_main( test_context ); + stringlist_type * args = stringlist_alloc_new(); + stringlist_append_copy( args , "OtherCase"); + + test_assert_string_not_equal( "OtherCase" , enkf_main_get_current_fs( enkf_main )); + ert_test_context_run_worklow_job( test_context , "SELECT_CASE" , args); + test_assert_true( ert_test_context_run_worklow_job( test_context , "SELECT_CASE" , args) ); + test_assert_string_equal( "OtherCase" , enkf_main_get_current_fs( enkf_main )); + + stringlist_free( args ); + } + ert_test_context_free( test_context ); + exit(0); +} diff --git a/libres/lib/enkf/tests/enkf_site_config.cpp b/libres/lib/enkf/tests/enkf_site_config.cpp new file mode 100644 index 00000000000..455b595eac2 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_site_config.cpp @@ -0,0 +1,43 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_site_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 + + +#define INCLUDE_KEY "INCLUDE" +#define DEFINE_KEY "DEFINE" + + +void test_init(const char * config_file) { + site_config_type * site_config = site_config_alloc_load_user_config(NULL); + site_config_free( site_config ); +} + +int main(int argc , char ** argv) { + const char * site_config_file = argv[1]; + + util_install_signals(); + + test_init( site_config_file ); + + exit(0); +} + diff --git a/libres/lib/enkf/tests/enkf_state_manual_load_test.cpp b/libres/lib/enkf/tests/enkf_state_manual_load_test.cpp new file mode 100644 index 00000000000..e47fdcf2f34 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_state_manual_load_test.cpp @@ -0,0 +1,82 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_state_manual_load_test.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 + + +int test_load_manually_to_new_case(enkf_main_type * enkf_main) { + int result = 0; + int iens = 0; + int iter = 0; + const char * casename = "new_case"; + char * job_name = model_config_alloc_jobname( enkf_main_get_model_config( enkf_main ), 0); + enkf_main_select_fs( enkf_main , casename ); + + + enkf_fs_type * fs = enkf_main_get_fs( enkf_main ); + const subst_list_type * subst_list = subst_config_get_subst_list(enkf_main_get_subst_config(enkf_main)); + run_arg_type * run_arg = run_arg_alloc_ENSEMBLE_EXPERIMENT("run_id", + fs, + iens, + iter, + "simulations/run0", + job_name, + subst_list); + { + arg_pack_type * arg_pack = arg_pack_alloc(); + arg_pack_append_ptr( arg_pack , enkf_main_iget_state(enkf_main, 0)); + arg_pack_append_ptr( arg_pack , run_arg ); + arg_pack_append_owned_ptr( arg_pack , stringlist_alloc_new() , stringlist_free__); + arg_pack_append_bool( arg_pack, true ); + arg_pack_append_ptr( arg_pack, &result ); + + enkf_state_load_from_forward_model_mt(arg_pack); + arg_pack_free(arg_pack); + } + free(job_name); + return result; +} + + + + +int main(int argc , char ** argv) { + enkf_main_install_SIGNALS(); + const char * root_path = argv[1]; + const char * config_file = argv[2]; + + ecl::util::TestArea ta(config_file); + ta.copy_directory_content(root_path); + { + bool strict = true; + res_config_type * res_config = res_config_alloc_load(config_file); + enkf_main_type * enkf_main = enkf_main_alloc(res_config, strict, true); + + test_assert_int_equal( 0 , test_load_manually_to_new_case(enkf_main)); + + enkf_main_free( enkf_main ); + res_config_free(res_config); + } + exit(0); +} + diff --git a/libres/lib/enkf/tests/enkf_state_map.cpp b/libres/lib/enkf/tests/enkf_state_map.cpp new file mode 100644 index 00000000000..7fc9423a33a --- /dev/null +++ b/libres/lib/enkf/tests/enkf_state_map.cpp @@ -0,0 +1,332 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_state_map.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 + + +void create_test() { + state_map_type * state_map = state_map_alloc(); + test_assert_true( state_map_is_instance( state_map )); + test_assert_int_equal( 0 , state_map_get_size( state_map )); + test_assert_false( state_map_is_readonly( state_map )); + state_map_free( state_map ); +} + +void get_test( ) { + state_map_type * state_map = state_map_alloc(); + test_assert_int_equal( STATE_UNDEFINED , state_map_iget( state_map , 0 )); + test_assert_int_equal( STATE_UNDEFINED , state_map_iget( state_map , 100 )); + state_map_free( state_map ); +} + +void set_test( ) { + state_map_type * state_map = state_map_alloc(); + state_map_iset( state_map , 0 , STATE_INITIALIZED ); + test_assert_int_equal( STATE_INITIALIZED , state_map_iget( state_map , 0 )); + + state_map_iset( state_map , 100 , STATE_INITIALIZED ); + test_assert_int_equal( STATE_INITIALIZED , state_map_iget( state_map , 100 )); + + test_assert_int_equal( STATE_UNDEFINED , state_map_iget( state_map , 50 )); + test_assert_int_equal( 101 , state_map_get_size( state_map )); + state_map_free( state_map ); +} + + +void load_empty_test() { + state_map_type * state_map = state_map_fread_alloc( "File/does/not/exists" ); + test_assert_true( state_map_is_instance( state_map )); + test_assert_int_equal( 0 , state_map_get_size( state_map )); + state_map_free( state_map ); +} + + +void test_equal() { + state_map_type * state_map1 = state_map_alloc(); + state_map_type * state_map2 = state_map_alloc(); + + test_assert_true( state_map_equal( state_map1 , state_map2 )); + for (int i =0; i < 25; i++) { + state_map_iset( state_map1 , i , STATE_INITIALIZED ); + state_map_iset( state_map2 , i , STATE_INITIALIZED ); + } + test_assert_true( state_map_equal( state_map1 , state_map2 )); + + state_map_iset( state_map2 , 15 , STATE_HAS_DATA ); + test_assert_false( state_map_equal( state_map1 , state_map2 )); + state_map_iset( state_map2 , 15 , STATE_LOAD_FAILURE ); + state_map_iset( state_map2 , 15 , STATE_INITIALIZED ); + test_assert_true( state_map_equal( state_map1 , state_map2 )); + + state_map_iset( state_map2 , 150 , STATE_INITIALIZED ); + test_assert_false( state_map_equal( state_map1 , state_map2 )); +} + + +void test_copy() { + state_map_type * state_map = state_map_alloc(); + state_map_iset( state_map , 0 , STATE_INITIALIZED ); + state_map_iset( state_map , 100 , STATE_INITIALIZED ); + { + state_map_type * copy = state_map_alloc_copy( state_map ); + test_assert_true( state_map_equal( copy , state_map )); + + state_map_iset( state_map , 10 , STATE_INITIALIZED ); + test_assert_false( state_map_equal( copy , state_map )); + + state_map_free( copy ); + } + state_map_free( state_map ); +} + + +void test_io( ) { + ecl::util::TestArea ta("state_map_io"); + { + state_map_type * state_map = state_map_alloc(); + state_map_type * copy1 , *copy2; + state_map_iset( state_map , 0 , STATE_INITIALIZED ); + state_map_iset( state_map , 100 , STATE_INITIALIZED ); + state_map_fwrite( state_map , "map"); + + copy1 = state_map_fread_alloc( "map" ); + test_assert_true( state_map_equal( state_map , copy1 )); + + copy2 = state_map_alloc(); + test_assert_true( state_map_fread( copy2 , "map" ) ); + test_assert_true( state_map_equal( state_map , copy2 )); + + state_map_iset( copy2 , 67 , STATE_INITIALIZED ); + test_assert_false(state_map_equal( state_map , copy2 )); + + state_map_fread( copy2 , "map"); + test_assert_true( state_map_equal( state_map , copy2 )); + + test_assert_false(state_map_fread( copy2 , "DoesNotExist")); + test_assert_int_equal( 0 , state_map_get_size( copy2 )); + } +} + + + +void test_update_undefined( ) { + state_map_type * map = state_map_alloc( ); + + state_map_iset( map , 10 , STATE_INITIALIZED ); + test_assert_int_equal( STATE_UNDEFINED , state_map_iget( map , 5 ) ); + test_assert_int_equal( STATE_INITIALIZED , state_map_iget( map , 10 ) ); + + state_map_update_undefined( map , 5 , STATE_INITIALIZED ); + test_assert_int_equal( STATE_INITIALIZED , state_map_iget( map , 5 ) ); + + state_map_update_undefined( map , 10 , STATE_INITIALIZED ); + test_assert_int_equal( STATE_INITIALIZED , state_map_iget( map , 10 ) ); + + state_map_free( map ); +} + + +void test_update_matching( ) { + state_map_type * map = state_map_alloc( ); + + state_map_iset( map , 10 , STATE_INITIALIZED ); + state_map_iset( map , 3 , STATE_PARENT_FAILURE ); + test_assert_int_equal( STATE_UNDEFINED , state_map_iget( map , 5 ) ); + test_assert_int_equal( STATE_INITIALIZED , state_map_iget( map , 10 ) ); + + state_map_update_matching( map , 5 , STATE_UNDEFINED | STATE_LOAD_FAILURE , STATE_INITIALIZED ); + state_map_update_matching( map , 10 , STATE_UNDEFINED | STATE_LOAD_FAILURE , STATE_INITIALIZED ); + state_map_update_matching( map , 3 , STATE_UNDEFINED | STATE_LOAD_FAILURE , STATE_INITIALIZED ); + + test_assert_int_equal( STATE_INITIALIZED , state_map_iget( map , 5 ) ); + test_assert_int_equal( STATE_INITIALIZED , state_map_iget( map , 10 ) ); + test_assert_int_equal( STATE_PARENT_FAILURE , state_map_iget( map , 3 ) ); + + state_map_update_undefined( map , 10 , STATE_INITIALIZED ); + test_assert_int_equal( STATE_INITIALIZED , state_map_iget( map , 10 ) ); + + state_map_free( map ); +} + + +void test_select_matching( ) { + state_map_type * map = state_map_alloc( ); + bool_vector_type * mask1 = bool_vector_alloc(21 , false); + bool_vector_type * mask2 = bool_vector_alloc(1000 , true); + + state_map_iset( map , 10 , STATE_INITIALIZED ); + state_map_iset( map , 10 , STATE_HAS_DATA ); + state_map_iset( map , 20 , STATE_INITIALIZED ); + state_map_select_matching( map , mask1 , STATE_HAS_DATA | STATE_INITIALIZED ); + state_map_select_matching( map , mask2 , STATE_HAS_DATA | STATE_INITIALIZED ); + + for (int i=0; i < bool_vector_size( mask1 ); i++) { + if (i==10) + test_assert_true( bool_vector_iget( mask1 , i )); + else if (i== 20) + test_assert_true( bool_vector_iget( mask1 , i )); + else { + test_assert_false( bool_vector_iget( mask1 , i )); + test_assert_true( bool_vector_iget( mask2 , i )); + } + } + + state_map_iset( map , 50 , STATE_INITIALIZED ); + state_map_select_matching( map , mask1 , STATE_HAS_DATA | STATE_INITIALIZED ); + test_assert_int_equal(bool_vector_size( mask1 ), 21); + + bool_vector_free( mask1 ); + bool_vector_free( mask2 ); + state_map_free( map ); +} + + +void test_deselect_matching( ) { + state_map_type * map = state_map_alloc( ); + bool_vector_type * mask1 = bool_vector_alloc(0 , false); + bool_vector_type * mask2 = bool_vector_alloc(1000 , true); + + state_map_iset( map , 10 , STATE_INITIALIZED ); + state_map_iset( map , 10 , STATE_HAS_DATA ); + state_map_iset( map , 20 , STATE_INITIALIZED ); + state_map_deselect_matching( map , mask1 , STATE_HAS_DATA | STATE_INITIALIZED ); + state_map_deselect_matching( map , mask2 , STATE_HAS_DATA | STATE_INITIALIZED ); + + test_assert_int_equal( state_map_get_size( map ) , bool_vector_size( mask1 )); + + for (int i=0; i < bool_vector_size( mask1 ); i++) { + if (i==10) + test_assert_false( bool_vector_iget( mask1 , i )); + else if (i== 20) + test_assert_false( bool_vector_iget( mask2 , i )); + else { + test_assert_false( bool_vector_iget( mask1 , i )); + test_assert_true( bool_vector_iget( mask2 , i )); + } + } + + bool_vector_free( mask1 ); + bool_vector_free( mask2 ); + state_map_free( map ); +} + + +void test_count_matching() { + state_map_type * map1 = state_map_alloc(); + state_map_iset(map1 , 10 , STATE_INITIALIZED ); + + state_map_iset(map1 , 15 , STATE_INITIALIZED ); + state_map_iset(map1 , 15 , STATE_HAS_DATA ); + + state_map_iset(map1 , 16 , STATE_INITIALIZED ); + state_map_iset(map1 , 16 , STATE_HAS_DATA ); + state_map_iset(map1 , 16 , STATE_LOAD_FAILURE ); + + test_assert_int_equal( 1 , state_map_count_matching( map1 , STATE_HAS_DATA)); + test_assert_int_equal( 2 , state_map_count_matching( map1 , STATE_HAS_DATA | STATE_LOAD_FAILURE)); + test_assert_int_equal( 3 , state_map_count_matching( map1 , STATE_HAS_DATA | STATE_LOAD_FAILURE | STATE_INITIALIZED)); + + state_map_free( map1 ); +} + +// Probably means that the target should be explicitly set to +// undefined before workflows which automatically change case. +void test_transitions() { + + test_assert_false( state_map_legal_transition(STATE_UNDEFINED , STATE_UNDEFINED )); + test_assert_true( state_map_legal_transition(STATE_UNDEFINED , STATE_INITIALIZED )); + test_assert_false( state_map_legal_transition(STATE_UNDEFINED , STATE_HAS_DATA )); + test_assert_false( state_map_legal_transition(STATE_UNDEFINED , STATE_LOAD_FAILURE )); + test_assert_true( state_map_legal_transition(STATE_UNDEFINED , STATE_PARENT_FAILURE )); + + test_assert_false( state_map_legal_transition(STATE_INITIALIZED , STATE_UNDEFINED )); + test_assert_true( state_map_legal_transition(STATE_INITIALIZED , STATE_INITIALIZED )); + test_assert_true( state_map_legal_transition(STATE_INITIALIZED , STATE_HAS_DATA )); + test_assert_true( state_map_legal_transition(STATE_INITIALIZED , STATE_LOAD_FAILURE )); + test_assert_true( state_map_legal_transition(STATE_INITIALIZED , STATE_PARENT_FAILURE )); // Should maybe false - if the commenta baove is taken into account. + + test_assert_false( state_map_legal_transition(STATE_HAS_DATA , STATE_UNDEFINED )); + test_assert_true( state_map_legal_transition(STATE_HAS_DATA , STATE_INITIALIZED )); + test_assert_true( state_map_legal_transition(STATE_HAS_DATA , STATE_HAS_DATA )); + test_assert_true( state_map_legal_transition(STATE_HAS_DATA , STATE_LOAD_FAILURE )); + test_assert_true( state_map_legal_transition(STATE_HAS_DATA , STATE_PARENT_FAILURE )); // Rerun + + test_assert_false( state_map_legal_transition(STATE_LOAD_FAILURE , STATE_UNDEFINED )); + test_assert_true( state_map_legal_transition(STATE_LOAD_FAILURE , STATE_INITIALIZED )); + test_assert_true( state_map_legal_transition(STATE_LOAD_FAILURE , STATE_HAS_DATA )); + test_assert_true( state_map_legal_transition(STATE_LOAD_FAILURE , STATE_LOAD_FAILURE )); + test_assert_false( state_map_legal_transition(STATE_LOAD_FAILURE , STATE_PARENT_FAILURE )); + + test_assert_false( state_map_legal_transition(STATE_PARENT_FAILURE , STATE_UNDEFINED )); + test_assert_true( state_map_legal_transition(STATE_PARENT_FAILURE , STATE_INITIALIZED )); + test_assert_false( state_map_legal_transition(STATE_PARENT_FAILURE , STATE_HAS_DATA )); + test_assert_false( state_map_legal_transition(STATE_PARENT_FAILURE , STATE_LOAD_FAILURE )); + test_assert_true( state_map_legal_transition(STATE_PARENT_FAILURE , STATE_PARENT_FAILURE )); +} + + + +void test_readonly() { + { + state_map_type * map1 = state_map_fread_alloc_readonly("FileDoesNotExist"); + + test_assert_true(state_map_is_instance(map1)); + test_assert_int_equal(0 , state_map_get_size( map1 )); + test_assert_true( state_map_is_readonly( map1 )); + state_map_free(map1); + } + { + ecl::util::TestArea ta("ro"); + state_map_type * map1 = state_map_alloc(); + + state_map_iset(map1 , 5 , STATE_INITIALIZED); + state_map_iset(map1 , 9 , STATE_INITIALIZED); + + state_map_fwrite(map1 , "map1"); + { + state_map_type * map2 = state_map_fread_alloc_readonly("map1"); + + test_assert_true(state_map_equal(map1 , map2)); + state_map_free(map2); + } + state_map_free(map1); + } +} + + +int main(int argc , char ** argv) { + create_test(); + get_test(); + set_test(); + load_empty_test(); + test_equal(); + test_copy(); + test_io(); + test_update_undefined( ); + test_select_matching(); + test_count_matching(); + test_transitions(); + test_readonly(); + exit(0); +} + diff --git a/libres/lib/enkf/tests/enkf_state_report_step_compatible.cpp b/libres/lib/enkf/tests/enkf_state_report_step_compatible.cpp new file mode 100644 index 00000000000..5f2f20ebdb3 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_state_report_step_compatible.cpp @@ -0,0 +1,75 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_state_report_step_compatible.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 + + +bool check_ecl_sum_compatible(const enkf_main_type * enkf_main) +{ + stringlist_type * msg_list = stringlist_alloc_new(); + enkf_state_type * state = enkf_main_iget_state( enkf_main , 0 ); + enkf_fs_type * fs = enkf_main_get_fs( enkf_main ); + char * job_name = model_config_alloc_jobname( enkf_main_get_model_config( enkf_main ), 0); + const subst_list_type * subst_list = subst_config_get_subst_list(enkf_main_get_subst_config(enkf_main)); + + run_arg_type * run_arg = run_arg_alloc_ENSEMBLE_EXPERIMENT("run_id", + fs, + 0, + 0, + "simulations/run0", + job_name, + subst_list); + + state_map_type * state_map = enkf_fs_get_state_map(fs); + state_map_iset(state_map, 0, STATE_INITIALIZED); + + int error = enkf_state_load_from_forward_model( state , run_arg , msg_list ); + + free( job_name ); + stringlist_free( msg_list ); + return (REPORT_STEP_INCOMPATIBLE & error) ? false : true; +} + + + +int main(int argc , char ** argv) { + enkf_main_install_SIGNALS(); + const char * root_path = argv[1]; + const char * config_file = argv[2]; + const char * compatible_str = argv[3]; + bool check_compatible; + + test_assert_true( util_sscanf_bool( compatible_str , &check_compatible)); + + ecl::util::TestArea ta("compatible"); + ta.copy_directory_content(root_path); + + bool strict = true; + res_config_type * res_config = res_config_alloc_load(config_file); + enkf_main_type * enkf_main = enkf_main_alloc(res_config, strict, true); + + test_assert_bool_equal(check_compatible , check_ecl_sum_compatible(enkf_main)); + + enkf_main_free( enkf_main ); + res_config_free(res_config); +} diff --git a/libres/lib/enkf/tests/enkf_state_skip_summary_load_test.cpp b/libres/lib/enkf/tests/enkf_state_skip_summary_load_test.cpp new file mode 100644 index 00000000000..1b1603b6076 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_state_skip_summary_load_test.cpp @@ -0,0 +1,69 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_state_no_summary_test.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 + +bool check_ecl_sum_loaded(const enkf_main_type * enkf_main) +{ + enkf_fs_type * fs = enkf_main_get_fs( enkf_main ); + stringlist_type * msg_list = stringlist_alloc_new(); + enkf_state_type * state1 = enkf_main_iget_state( enkf_main , 0 ); + char * job_name = model_config_alloc_jobname( enkf_main_get_model_config( enkf_main ), 0); + const subst_list_type * subst_list = subst_config_get_subst_list(enkf_main_get_subst_config(enkf_main)); + run_arg_type * run_arg1 = run_arg_alloc_ENSEMBLE_EXPERIMENT( "run_id", fs, 0 , 0 , "simulations/run0", job_name, subst_list); + enkf_state_type * state2 = enkf_main_iget_state( enkf_main , 1 ); + run_arg_type * run_arg2 = run_arg_alloc_ENSEMBLE_EXPERIMENT( "run_id", fs, 0 , 0 , "simulations/run1", job_name, subst_list); + + + state_map_type * state_map = enkf_fs_get_state_map(fs); + state_map_iset(state_map, 0, STATE_INITIALIZED); + + int error = enkf_state_load_from_forward_model( state1 , run_arg1 , msg_list ); + + + state_map_iset(state_map, 1, STATE_INITIALIZED); + error = enkf_state_load_from_forward_model( state2 , run_arg2 , msg_list ); + + free( job_name ); + stringlist_free( msg_list ); + return (0 == error); +} + + + +int main(int argc , char ** argv) { + enkf_main_install_SIGNALS(); + const char * root_path = argv[1]; + const char * config_file = argv[2]; + + ecl::util::TestArea ta("summary_load"); + ta.copy_directory_content( root_path ); + + bool strict = true; + res_config_type * res_config = res_config_alloc_load(config_file); + enkf_main_type * enkf_main = enkf_main_alloc(res_config, strict, true); + + test_assert_true( check_ecl_sum_loaded(enkf_main) ); + + enkf_main_free( enkf_main ); + res_config_free(res_config); +} diff --git a/libres/lib/enkf/tests/enkf_time_map.cpp b/libres/lib/enkf/tests/enkf_time_map.cpp new file mode 100644 index 00000000000..868c4d764a3 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_time_map.cpp @@ -0,0 +1,389 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_time_map.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 + +void ecl_test( const char * ecl_case ) { + ecl_sum_type * ecl_sum = ecl_sum_fread_alloc_case( ecl_case , ":"); + time_t start_time = ecl_sum_get_start_time( ecl_sum ); + time_t end_time = ecl_sum_get_end_time( ecl_sum ); + time_map_type * ecl_map = time_map_alloc( ); + + test_assert_true( time_map_summary_update( ecl_map , ecl_sum ) ); + test_assert_true( time_map_summary_update( ecl_map , ecl_sum ) ); + + test_assert_time_t_equal( time_map_get_start_time( ecl_map ) , start_time ); + test_assert_time_t_equal( time_map_get_end_time( ecl_map ) , end_time ); + test_assert_double_equal( time_map_get_end_days( ecl_map ) , ecl_sum_get_sim_length( ecl_sum )); + + time_map_clear( ecl_map ); + time_map_update( ecl_map , 1 , 256 ); + time_map_set_strict( ecl_map , false ); + test_assert_false( time_map_summary_update( ecl_map , ecl_sum )); + + time_map_free( ecl_map ); + ecl_sum_free( ecl_sum ); +} + + +static void map_update( void * arg ) { + vector_type * arg_vector = vector_safe_cast( arg ); + time_map_type * tmap = (time_map_type *)vector_iget( arg_vector , 0 ); + ecl_sum_type * sum = (ecl_sum_type *)vector_iget( arg_vector , 1 ); + + time_map_summary_update( tmap , sum ); +} + + + +void test_inconsistent_summary( const char * case1, const char * case2) { + ecl_sum_type * ecl_sum1 = ecl_sum_fread_alloc_case( case1 , ":"); + ecl_sum_type * ecl_sum2 = ecl_sum_fread_alloc_case( case2 , ":"); + + time_map_type * ecl_map = time_map_alloc( ); + + test_assert_true( time_map_summary_update( ecl_map , ecl_sum1 ) ); + { + vector_type * arg = vector_alloc_new(); + vector_append_ref( arg , ecl_map ); + vector_append_ref( arg , ecl_sum2 ); + test_assert_util_abort("time_map_summary_update_abort" , map_update , arg); + vector_free( arg ); + } + + time_map_free( ecl_map ); + ecl_sum_free( ecl_sum1 ); + ecl_sum_free( ecl_sum2 ); +} + +static void alloc_index_map( void * arg) { + arg_pack_type * arg_pack = arg_pack_safe_cast( arg ); + time_map_type * map = (time_map_type *) arg_pack_iget_ptr( arg_pack , 0 ); + ecl_sum_type * sum = (ecl_sum_type *) arg_pack_iget_ptr( arg_pack , 1 ); + + time_map_alloc_index_map( map , sum ); +} + + + +void test_refcase( const char * refcase_name , const char * case1, const char * case2 , const char * case3 , const char * case4) { + ecl_sum_type * refcase = ecl_sum_fread_alloc_case( refcase_name , ":"); + ecl_sum_type * ecl_sum1 = ecl_sum_fread_alloc_case( case1 , ":"); + ecl_sum_type * ecl_sum2 = ecl_sum_fread_alloc_case( case2 , ":"); + ecl_sum_type * ecl_sum3 = ecl_sum_fread_alloc_case( case3 , ":"); + ecl_sum_type * ecl_sum4 = ecl_sum_fread_alloc_case( case4 , ":"); + + { + time_map_type * ecl_map = time_map_alloc( ); + test_assert_false( time_map_has_refcase( ecl_map )); + test_assert_true( time_map_attach_refcase( ecl_map , refcase ) ); + test_assert_true( time_map_has_refcase( ecl_map )); + time_map_free( ecl_map ); + } + + + { + time_map_type * ecl_map = time_map_alloc( ); + time_map_attach_refcase( ecl_map , refcase ); + test_assert_true( time_map_summary_update( ecl_map , ecl_sum1 ) ); + } + + { + time_map_type * ecl_map = time_map_alloc( ); + + time_map_set_strict( ecl_map , false ); + time_map_attach_refcase( ecl_map , refcase ); + + test_assert_false( time_map_summary_update( ecl_map , ecl_sum2 ) ); + test_assert_int_equal( 25 , time_map_get_size( ecl_map )); + test_assert_true( time_map_summary_update( ecl_map , ecl_sum1 ) ); + test_assert_int_equal( 63 , time_map_get_size( ecl_map )); + } + + { + time_map_type * ecl_map = time_map_alloc( ); + test_assert_true( time_map_summary_update( ecl_map , ecl_sum2 ) ); + test_assert_false( time_map_attach_refcase( ecl_map , refcase )); + } + + + { + ecl::util::TestArea ta("x"); + { + time_map_type * ecl_map = time_map_alloc( ); + test_assert_true( time_map_summary_update( ecl_map , refcase ) ); + test_assert_true( time_map_update( ecl_map , ecl_sum_get_last_report_step( refcase ) + 1 , ecl_sum_get_end_time( refcase ) + 100 )); + test_assert_true( time_map_update( ecl_map , ecl_sum_get_last_report_step( refcase ) + 2 , ecl_sum_get_end_time( refcase ) + 200 )); + test_assert_true( time_map_update( ecl_map , ecl_sum_get_last_report_step( refcase ) + 3 , ecl_sum_get_end_time( refcase ) + 300 )); + time_map_fwrite( ecl_map , "time_map"); + time_map_free( ecl_map ); + } + { + time_map_type * ecl_map = time_map_alloc( ); + time_map_fread(ecl_map , "time_map"); + test_assert_true( time_map_attach_refcase( ecl_map , refcase ) ); + time_map_free( ecl_map ); + } + } + + + + ecl_sum_free( refcase ); + ecl_sum_free( ecl_sum1 ); + ecl_sum_free( ecl_sum2 ); + ecl_sum_free( ecl_sum3 ); + ecl_sum_free( ecl_sum4 ); +} + + +void test_index_map( const char * case1, const char * case2 , const char * case3 , const char * case4) { + ecl_sum_type * ecl_sum1 = ecl_sum_fread_alloc_case( case1 , ":"); + ecl_sum_type * ecl_sum2 = ecl_sum_fread_alloc_case( case2 , ":"); + ecl_sum_type * ecl_sum3 = ecl_sum_fread_alloc_case( case3 , ":"); + ecl_sum_type * ecl_sum4 = ecl_sum_fread_alloc_case( case4 , ":"); + + time_map_type * ecl_map = time_map_alloc( ); + + { + int_vector_type * index_map = time_map_alloc_index_map( ecl_map , ecl_sum1 ); + test_assert_int_equal( int_vector_size( index_map ) , 0); + int_vector_free( index_map ); + } + + test_assert_true( time_map_summary_update( ecl_map , ecl_sum1 ) ); + { + int_vector_type * index_map = time_map_alloc_index_map( ecl_map , ecl_sum1 ); + + for (int i= ecl_sum_get_first_report_step( ecl_sum1); i < int_vector_size( index_map ); i++) + test_assert_int_equal( i , int_vector_iget( index_map , i )); + + test_assert_int_equal( int_vector_size( index_map ) , ecl_sum_get_last_report_step( ecl_sum1) + 1); + int_vector_free( index_map ); + } + + /* case2 has an extra tstep in the middle of the case. */ + time_map_set_strict( ecl_map , false ); + test_assert_false( time_map_summary_update( ecl_map , ecl_sum2 ) ); + { + int_vector_type * index_map = time_map_alloc_index_map( ecl_map , ecl_sum2 ); + test_assert_int_equal( int_vector_size( index_map ) , ecl_sum_get_last_report_step( ecl_sum2)); + test_assert_int_equal( int_vector_iget( index_map , 24) , 24); + test_assert_int_equal( int_vector_iget( index_map , 25) , 26); + int_vector_free( index_map ); + } + + + /* case3 has an extra tstep in the middle, and ends prematurely */ + test_assert_false( time_map_summary_update( ecl_map , ecl_sum3 ) ); + { + int_vector_type * index_map = time_map_alloc_index_map( ecl_map , ecl_sum3 ); + test_assert_int_equal( int_vector_size( index_map ) , ecl_sum_get_last_report_step( ecl_sum3)); + int_vector_free( index_map ); + } + + + /* case4 has a missing tstep in the middle - that is not handled; and we abort */ + test_assert_false( time_map_summary_update( ecl_map , ecl_sum4 ) ); + { + arg_pack_type * arg = arg_pack_alloc(); + arg_pack_append_ptr( arg , ecl_map ); + arg_pack_append_ptr( arg , ecl_sum4 ); + + test_assert_util_abort( "time_map_alloc_index_map" , alloc_index_map , arg); + arg_pack_free( arg ); + } + + + + time_map_free( ecl_map ); + ecl_sum_free( ecl_sum1 ); + ecl_sum_free( ecl_sum2 ); + ecl_sum_free( ecl_sum3 ); + ecl_sum_free( ecl_sum4 ); +} + + +void simple_test() { + time_map_type * time_map = time_map_alloc( ); + ecl::util::TestArea ta("simple"); + const char * mapfile = "map"; + + time_map_set_strict( time_map , false ); + test_assert_true( time_map_update( time_map , 0 , 100 ) ); + test_assert_true( time_map_update( time_map , 1 , 200 ) ); + test_assert_true( time_map_update( time_map , 1 , 200 ) ); + test_assert_false( time_map_update( time_map , 1 , 250 ) ); + + test_assert_true( time_map_equal( time_map , time_map ) ); + time_map_fwrite( time_map , mapfile); + { + time_map_type * time_map2 = time_map_alloc( ); + + test_assert_false( time_map_equal( time_map , time_map2 ) ); + time_map_fread( time_map2 , mapfile ); + test_assert_true( time_map_equal( time_map , time_map2 ) ); + time_map_free( time_map2 ); + } + { + time_t mtime1 = util_file_mtime( mapfile ); + sleep(2); + time_map_fwrite( time_map , mapfile); + + test_assert_time_t_equal( mtime1 , util_file_mtime( mapfile ) ); + time_map_update( time_map , 2 , 300 ); + time_map_fwrite( time_map , mapfile); + test_assert_time_t_not_equal( mtime1 , util_file_mtime( mapfile ) ); + } +} + + +static void simple_update(void * arg) { + time_map_type * tmap = time_map_safe_cast( arg ); + + time_map_update( tmap , 0 , 101 ); +} + + + +void simple_test_inconsistent() { + time_map_type * time_map = time_map_alloc( ); + + test_assert_true( time_map_update( time_map , 0 , 100 ) ); + time_map_set_strict( time_map , false ); + test_assert_false( time_map_update( time_map , 0 , 101 ) ); + + time_map_set_strict( time_map , true ); + test_assert_util_abort( "time_map_update_abort" , simple_update , time_map ); + + time_map_free( time_map ); +} + + + +#define MAP_SIZE 10000 + +void * update_time_map( void * arg ) { + time_map_type * time_map = time_map_safe_cast( arg ); + int i; + for (i=0; i < MAP_SIZE; i++) + time_map_update( time_map , i , i ); + + test_assert_int_equal( MAP_SIZE , time_map_get_size( time_map )); + return NULL; +} + + +void thread_test() { + time_map_type * time_map = time_map_alloc( ); + test_assert_false( time_map_is_readonly( time_map )); + { + int pool_size = 1000; + thread_pool_type * tp = thread_pool_alloc( pool_size/2 , true ); + + thread_pool_add_job( tp , update_time_map , time_map ); + + thread_pool_join(tp); + thread_pool_free(tp); + } + { + int i; + for (i=0; i < MAP_SIZE; i++) + test_assert_true( time_map_iget( time_map , i ) == i ); + } + time_map_free( time_map ); +} + + + +void test_read_only() { + ecl::util::TestArea ta("read_only"); + { + time_map_type * tm = time_map_alloc( ); + + test_assert_true( time_map_is_instance( tm )); + test_assert_true( time_map_is_strict( tm )); + test_assert_false( time_map_is_readonly( tm )); + + time_map_update( tm , 0 , 0 ); + time_map_update( tm , 1 , 10 ); + time_map_update( tm , 2 , 20 ); + + time_map_fwrite( tm , "case/files/time-map" ); + time_map_free( tm ); + } + { + time_map_type * tm = time_map_fread_alloc_readonly( "case/files/time-map"); + test_assert_time_t_equal( 0 , time_map_iget( tm , 0 )); + test_assert_time_t_equal( 10 , time_map_iget( tm , 1 )); + test_assert_time_t_equal( 20 , time_map_iget( tm , 2 )); + test_assert_int_equal( 3 , time_map_get_size( tm )); + time_map_free( tm ); + } + { + time_map_type * tm = enkf_fs_alloc_readonly_time_map( "case" ); + test_assert_time_t_equal( 0 , time_map_iget( tm , 0 )); + test_assert_time_t_equal( 10 , time_map_iget( tm , 1 )); + test_assert_time_t_equal( 20 , time_map_iget( tm , 2 )); + test_assert_int_equal( 3 , time_map_get_size( tm )); + time_map_free( tm ); + } + { + time_map_type * tm = time_map_fread_alloc_readonly( "DoesNotExist"); + test_assert_true( time_map_is_instance( tm )); + test_assert_true( time_map_is_readonly( tm )); + test_assert_int_equal(0 , time_map_get_size( tm )); + time_map_free( tm ); + } +} + + +int main(int argc , char ** argv) { + + enkf_main_install_SIGNALS(); + res_log_init_log(LOG_CRITICAL, NULL , false ); // Make sure there will be no logging. + + if (argc == 1) { + simple_test(); + simple_test_inconsistent(); + thread_test(); + } else { + ecl_test( argv[1] ); + test_inconsistent_summary( argv[1] , argv[2]); + test_index_map(argv[1] , argv[2] , argv[3] , argv[4]); + test_refcase( argv[1] , argv[1] , argv[2] , argv[3] , argv[4]); + } + + test_read_only(); + + exit(0); +} + diff --git a/libres/lib/enkf/tests/enkf_umask_config_test.cpp b/libres/lib/enkf/tests/enkf_umask_config_test.cpp new file mode 100644 index 00000000000..9dea5c18a0a --- /dev/null +++ b/libres/lib/enkf/tests/enkf_umask_config_test.cpp @@ -0,0 +1,56 @@ +/* + 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 + + +int main(int argc , char ** argv) { + enkf_main_install_SIGNALS(); + + const char * config_file = argv[1]; + ert_test_context_type * test_context = ert_test_context_alloc("VerifyJobsFileTest" , config_file); + enkf_main_type * enkf_main = ert_test_context_get_main(test_context); + + { + const int ens_size = enkf_main_get_ensemble_size( enkf_main ); + bool_vector_type * iactive = bool_vector_alloc(ens_size, true); + const path_fmt_type * runpath_fmt = model_config_get_runpath_fmt( enkf_main_get_model_config( enkf_main )); + const subst_list_type * subst_list = NULL; + enkf_fs_type * fs = enkf_main_get_fs(enkf_main); + ert_run_context_type * run_context = ert_run_context_alloc_INIT_ONLY( fs , INIT_CONDITIONAL, iactive, runpath_fmt, subst_list , 0 ); + + enkf_main_create_run_path(enkf_main , run_context ); + ert_run_context_free( run_context ); + bool_vector_free(iactive); + } + + const char * filename = util_alloc_filename(ert_test_context_get_cwd(test_context), + "simulations/run0/jobs.json", NULL); + const char * jobs_file_content = util_fread_alloc_file_content(filename, NULL); + + test_assert_true (strstr(jobs_file_content, "\"umask\" : \"0022\"") != NULL); + test_assert_false (strstr(jobs_file_content, "\"umask\" : \"0032\"") != NULL); + test_assert_false (strstr(jobs_file_content, "\"umask\" : \"0122\"") != NULL); + test_assert_false (strstr(jobs_file_content, "\"umask\" : \"1022\"") != NULL); + + ert_test_context_free(test_context); + exit(0); +} diff --git a/libres/lib/enkf/tests/enkf_update_log_test.cpp b/libres/lib/enkf/tests/enkf_update_log_test.cpp new file mode 100644 index 00000000000..4958a18d156 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_update_log_test.cpp @@ -0,0 +1,95 @@ +#include +#include + +#include +#include +#include + +#include +#include +#include + + +void test_obs_in_log() { + int_vector_type * ens_active_list = int_vector_alloc(0 , false); + bool_vector_type * ens_mask; + int_vector_append( ens_active_list , 10 ); + + ens_mask = int_vector_alloc_mask(ens_active_list); + meas_data_type * meas_data = meas_data_alloc( ens_mask ); + meas_block_type * block = meas_data_add_block(meas_data , "OBS" , 10 , 10); + + obs_data_type * obs_data = obs_data_alloc(1.0); + obs_block_type * obs_block = obs_data_add_block( obs_data , "OBS", 10 , NULL , false ); + for (int iobs = 0; iobs < 10; iobs++) + obs_block_iset( obs_block , iobs , iobs , 0.1); + /* Set one obs block as missing*/ + obs_block_iset_missing(obs_block, 0); + + const char * ministep_name; + ministep_name = "A random name"; + + /* Check that the update_log contains the first observation name */ + char *str_stream; + size_t size; + FILE *stream; + stream = open_memstream (&str_stream, &size); + enkf_analysis_fprintf_obs_summary(obs_data, meas_data, ens_active_list, ministep_name, stream); + fclose(stream); + test_assert_true((strstr(str_stream, "OBS") != NULL)); + test_assert_false((strstr(str_stream, "* Local inactive") != NULL)); + + free(str_stream); + obs_data_free( obs_data ); + meas_data_free( meas_data ); + bool_vector_free( ens_mask ); + int_vector_free( ens_active_list ); +} + + +void test_local_inactive() { + int_vector_type * ens_active_list = int_vector_alloc(0 , false); + bool_vector_type * ens_mask; + int_vector_append( ens_active_list , 10 ); + + ens_mask = int_vector_alloc_mask(ens_active_list); + meas_data_type * meas_data = meas_data_alloc( ens_mask ); + meas_block_type * block = meas_data_add_block(meas_data , "OBS" , 10 , 10); + + obs_data_type * obs_data = obs_data_alloc(1.0); + obs_block_type * obs_block = obs_data_add_block( obs_data , "OBS", 10 , NULL , false ); + + /* By not setting all obs we set one obs block as local inactive*/ + for (int iobs = 1; iobs < 10; iobs++) + obs_block_iset( obs_block , iobs , iobs , 0.1); + + + const char * ministep_name; + ministep_name = "A random name"; + + /* Check that the update_log contains the first observation name */ + char *str_stream; + size_t size; + FILE *stream; + stream = open_memstream (&str_stream, &size); + enkf_analysis_fprintf_obs_summary(obs_data, meas_data, ens_active_list, ministep_name, stream); + fclose(stream); + + test_assert_true((strstr(str_stream, "OBS") != NULL)); + test_assert_true((strstr(str_stream, "* Local inactive") != NULL)); + + + free(str_stream); + obs_data_free( obs_data ); + meas_data_free( meas_data ); + bool_vector_free( ens_mask ); + int_vector_free( ens_active_list ); +} + + +int main(int argc , char ** argv) { + test_obs_in_log(); + test_local_inactive(); + exit(0); +} + diff --git a/libres/lib/enkf/tests/enkf_workflow_job_test.cpp b/libres/lib/enkf/tests/enkf_workflow_job_test.cpp new file mode 100644 index 00000000000..221453f3b59 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_workflow_job_test.cpp @@ -0,0 +1,537 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_workflow_job_test.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 + + +ert_test_context_type * create_context( const char * config_file, const char * name ) { + ert_test_context_type * test_context = ert_test_context_alloc(name , config_file); + test_assert_not_NULL(test_context); + return test_context; +} + +void test_pre_simulation_copy__( ert_test_context_type * test_context, + const char * job_name, + const char * source_path, + const char * target_path) { + + stringlist_type * args = stringlist_alloc_new(); + stringlist_append_copy( args , source_path ); + if (target_path) + stringlist_append_copy( args , target_path ); + ert_test_context_run_worklow_job( test_context , job_name , args); + + stringlist_free( args ); +} + +void test_pre_simulation_copy(ert_test_context_type * test_context, const char * job_name , const char * job_file) { + enkf_main_type * enkf_main = ert_test_context_get_main( test_context ); + model_config_type * model_config = enkf_main_get_model_config( enkf_main ); + test_assert_false( model_config_data_root_is_set( model_config )); + + test_assert_true( ert_test_context_install_workflow_job( test_context , job_name , job_file )); + + test_pre_simulation_copy__( test_context , job_name, "does_not_exist" , "target"); + + { + FILE * f = util_mkdir_fopen("input/path/xxx/model/file", "w"); + fprintf(f, "File \n"); + fclose( f ); + } + test_pre_simulation_copy__( test_context , job_name, "input/path/xxx/model" , NULL); + test_pre_simulation_copy__( test_context , job_name, "input/path/xxx/model" , "target"); + test_pre_simulation_copy__( test_context , job_name, "input/path/xxx/model/file" , "target/extra_path"); + test_pre_simulation_copy__( test_context , job_name, "input/path/xxx/model" , "target/extra_path2"); + + test_assert_false( util_is_file( "root/model/file")); + test_assert_false( util_is_file( "root/target/model/file")); + test_assert_false( util_is_file( "root/target/extra_path/file")); + test_assert_false( util_is_file( "root/target/extra_path2/model/file")); + + model_config_set_data_root( model_config , "root"); + test_assert_true( model_config_data_root_is_set( model_config )); + + test_pre_simulation_copy__( test_context , job_name, "input/path/xxx/model" , NULL ); + test_pre_simulation_copy__( test_context , job_name, "input/path/xxx/model" , "target"); + test_pre_simulation_copy__( test_context , job_name, "input/path/xxx/model/file" , "target/extra_path"); + test_pre_simulation_copy__( test_context , job_name, "input/path/xxx/model" , "target/extra_path2"); + + test_assert_true( util_is_file( "root/model/file")); + test_assert_true( util_is_file( "root/target/model/file")); + test_assert_true( util_is_file( "root/target/extra_path/file")); + test_assert_true( util_is_file( "root/target/extra_path2/model/file")); +} + + +void test_create_case_job(ert_test_context_type * test_context, const char * job_name , const char * job_file) { + stringlist_type * args = stringlist_alloc_new(); + stringlist_append_copy( args , "newly_created_case"); + test_assert_true( ert_test_context_install_workflow_job( test_context , job_name , job_file )); + test_assert_true( ert_test_context_run_worklow_job( test_context , job_name , args) ); + + char * new_case = util_alloc_filename( "storage" , "newly_created_case" , NULL); + test_assert_true(util_is_directory(new_case)); + free(new_case); + + stringlist_free( args ); +} + + +void test_init_case_job(ert_test_context_type * test_context, const char * job_name , const char * job_file) { + stringlist_type * args = stringlist_alloc_new(); + enkf_main_type * enkf_main = ert_test_context_get_main(test_context); + + test_assert_true( ert_test_context_install_workflow_job( test_context , "JOB" , job_file ) ); + + //Test init current case from existing + { + enkf_fs_type * cur_fs = enkf_main_mount_alt_fs( enkf_main , "new_current_case" , true ); + enkf_main_select_fs(enkf_main, "new_current_case"); + + test_assert_ptr_not_equal(cur_fs , enkf_main_get_fs( enkf_main )); + + stringlist_append_copy( args, "default"); //case to init from + test_assert_true( ert_test_context_run_worklow_job( test_context , "JOB" , args) ); + + enkf_fs_decref(cur_fs); + } + + { + const char * current_case = enkf_main_get_current_fs( enkf_main ); + test_assert_string_equal(current_case, "new_current_case"); + test_assert_true(enkf_fs_has_node(enkf_main_get_fs(enkf_main), "PERMZ", PARAMETER, 0, 0)); // This had state = ANALYZED; might be unfixable. + + enkf_fs_type * default_fs = enkf_main_mount_alt_fs( enkf_main , "default" , true ); + state_map_type * default_state_map = enkf_fs_get_state_map(default_fs); + state_map_type * current_state_map = enkf_fs_get_state_map(enkf_main_get_fs(enkf_main)); + test_assert_int_equal(state_map_get_size(default_state_map), state_map_get_size(current_state_map)); + enkf_fs_decref(default_fs); + } + + + //Test init case from existing case: + stringlist_clear(args); + stringlist_append_copy(args, "default"); //case to init from + stringlist_append_copy(args, "new_not_current_case"); + test_assert_true( ert_test_context_run_worklow_job( test_context , "JOB" , args) ); + { + enkf_fs_type * fs = enkf_main_mount_alt_fs(enkf_main, "new_not_current_case", true); + test_assert_not_NULL( fs ); + test_assert_true( enkf_fs_has_node(fs, "PERMZ", PARAMETER, 0, 0)); // This had state = ANALYZED; might be unfixable. + + enkf_fs_type * default_fs = enkf_main_mount_alt_fs( enkf_main , "default" , true ); + state_map_type * default_state_map = enkf_fs_get_state_map(default_fs); + state_map_type * new_state_map = enkf_fs_get_state_map(fs); + test_assert_int_equal(state_map_get_size(default_state_map), state_map_get_size(new_state_map)); + enkf_fs_decref(fs); + } + + stringlist_free( args ); +} + + +void test_load_results_job(ert_test_context_type * test_context , const char * job_name , const char * job_file) { + stringlist_type * args = stringlist_alloc_new(); + ert_test_context_install_workflow_job( test_context , job_name , job_file ); + stringlist_append_copy( args , "0"); + stringlist_append_copy( args , ","); + stringlist_append_copy( args , "1"); + test_assert_true( ert_test_context_run_worklow_job( test_context , job_name , args) ); + stringlist_free( args ); +} + + +void test_load_results_iter_job(ert_test_context_type * test_context , const char * job_name , const char * job_file) { + + stringlist_type * args = stringlist_alloc_new(); + ert_test_context_install_workflow_job( test_context , job_name , job_file ); + stringlist_append_copy( args , "0"); + stringlist_append_copy( args , "0"); + stringlist_append_copy( args , ","); + stringlist_append_copy( args , "1"); + test_assert_true( ert_test_context_run_worklow_job( test_context , job_name , args) ); + stringlist_free( args ); +} + + +void test_rank_realizations_on_observations_job(ert_test_context_type * test_context , const char * job_name , const char * job_file) { + stringlist_type * args = stringlist_alloc_new(); + ert_test_context_install_workflow_job( test_context , job_name , job_file ); + + stringlist_append_copy( args , "NameOfObsRanking1"); + stringlist_append_copy( args , "|"); + stringlist_append_copy( args , "WOPR:*"); + test_assert_true( ert_test_context_run_worklow_job( test_context , job_name , args) ); + + stringlist_clear(args); + stringlist_append_copy( args , "NameOfObsRanking2"); + stringlist_append_copy( args, "1-5"); + stringlist_append_copy( args, "55"); + stringlist_append_copy( args , "|"); + stringlist_append_copy( args , "WWCT:*"); + stringlist_append_copy( args , "WOPR:*"); + test_assert_true( ert_test_context_run_worklow_job( test_context , job_name , args) ); + + stringlist_clear(args); + stringlist_append_copy( args , "NameOfObsRanking3"); + stringlist_append_copy( args, "5"); + stringlist_append_copy( args, "55"); + stringlist_append_copy( args, "|"); + test_assert_true( ert_test_context_run_worklow_job( test_context , job_name , args) ); + + stringlist_clear(args); + stringlist_append_copy( args , "NameOfObsRanking4"); + stringlist_append_copy( args, "1,3,5-10"); + stringlist_append_copy( args, "55"); + test_assert_true( ert_test_context_run_worklow_job( test_context , job_name , args) ); + + stringlist_clear(args); + stringlist_append_copy( args , "NameOfObsRanking5"); + test_assert_true( ert_test_context_run_worklow_job( test_context , job_name , args) ); + + stringlist_clear(args); + stringlist_append_copy( args , "NameOfObsRanking6"); + stringlist_append_copy( args, "|"); + stringlist_append_copy( args , "UnrecognizableObservation"); + test_assert_true( ert_test_context_run_worklow_job( test_context , job_name , args) ); + + stringlist_free( args ); +} + + +void test_rank_realizations_on_data_job(ert_test_context_type * test_context , const char * job_name , const char * job_file) { + stringlist_type * args = stringlist_alloc_new(); + ert_test_context_install_workflow_job( test_context , job_name , job_file ); + + stringlist_append_copy( args , "NameOfDataRanking"); + stringlist_append_copy( args , "PORO:1,2,3"); + stringlist_append_copy( args , "false"); + stringlist_append_copy( args , "0"); + test_assert_true( ert_test_context_run_worklow_job( test_context , job_name , args) ); + + stringlist_clear(args); + stringlist_append_copy( args , "NameOfDataRanking2"); + stringlist_append_copy( args , "PORO:1,2,3"); + stringlist_append_copy( args , "false"); + test_assert_true( ert_test_context_run_worklow_job( test_context , job_name , args) ); + + stringlist_free( args ); +} + +void test_export_ranking(ert_test_context_type * test_context , const char * job_name , const char * job_file) { + stringlist_type * args = stringlist_alloc_new(); + ert_test_context_install_workflow_job( test_context , job_name , job_file ); + + stringlist_append_copy( args , "NameOfDataRanking"); + stringlist_append_copy( args , "/tmp/fileToSaveDataRankingIn.txt"); + test_assert_true( ert_test_context_run_worklow_job( test_context , job_name , args) ); + + stringlist_clear(args); + stringlist_append_copy( args , "NameOfObsRanking1"); + stringlist_append_copy( args , "/tmp/fileToSaveObservationRankingIn1.txt"); + test_assert_true( ert_test_context_run_worklow_job( test_context , job_name , args) ); + + stringlist_clear(args); + stringlist_append_copy( args , "NameOfObsRanking6"); + stringlist_append_copy( args , "/tmp/fileToSaveObservationRankingIn6.txt"); + test_assert_true( ert_test_context_run_worklow_job( test_context , job_name , args) ); + + stringlist_free( args ); +} + + +void test_init_misfit_table(ert_test_context_type * test_context , const char * job_name , const char * job_file) { + stringlist_type * args = stringlist_alloc_new(); + ert_test_context_install_workflow_job( test_context , job_name , job_file ); + + enkf_main_type * enkf_main = ert_test_context_get_main(test_context); + enkf_fs_type * fs = enkf_main_get_fs(enkf_main); + + misfit_ensemble_type * misfit_ensemble = enkf_fs_get_misfit_ensemble( fs ); + test_assert_false(misfit_ensemble_initialized(misfit_ensemble)); + + test_assert_true( ert_test_context_run_worklow_job( test_context , job_name , args) ); + + test_assert_true(misfit_ensemble_initialized(misfit_ensemble)); + + stringlist_free( args ); +} + + + + +static void test_export_runpath_file(ert_test_context_type * test_context, + const char * job_name, + const char * job_file, + stringlist_type * args, + int_vector_type * iens_values, + int_vector_type * iter_values) { + + ert_test_context_install_workflow_job( test_context , job_name , job_file ); + test_assert_true( ert_test_context_run_worklow_job( test_context , job_name , args) ); + + { + const enkf_main_type * enkf_main = ert_test_context_get_main(test_context); + const hook_manager_type * hook_manager = enkf_main_get_hook_manager( enkf_main ); + const char * runpath_file_name = hook_manager_get_runpath_list_file(hook_manager); + + const model_config_type * model_config = enkf_main_get_model_config(enkf_main); + const char * jobname_fmt = model_config_get_jobname_fmt( model_config ); + const char * runpath_fmt = model_config_get_runpath_as_char(model_config); + + test_assert_true(util_file_exists(runpath_file_name)); + FILE * file = util_fopen(runpath_file_name, "r"); + + int file_iens = 0; + char file_path[256]; + char file_base[256]; + int file_iter = 0; + char * cwd = util_alloc_cwd(); + int counter = 0; + int iens_index = 0; + int iter_index = 0; + + while (4 == fscanf( file , "%d %s %s %d" , &file_iens , file_path , file_base, &file_iter)) { + ++ counter; + + test_assert_true(int_vector_size(iens_values) >= iens_index+1); + test_assert_true(int_vector_size(iter_values) >= iter_index+1); + + int iens = int_vector_iget(iens_values, iens_index); + int iter = int_vector_iget(iter_values, iter_index); + + test_assert_int_equal(file_iens, iens); + test_assert_int_equal(file_iter, iter); + + char * base = util_alloc_sprintf("--%d", iens); + if (jobname_fmt && (util_int_format_count(jobname_fmt) == 1)) + base = util_alloc_sprintf(jobname_fmt, iens); + + test_assert_string_equal(base, file_base); + + const char * runpath = ""; + if (util_int_format_count(runpath_fmt) == 1) + runpath = util_alloc_sprintf(runpath_fmt, iens); + else if (util_int_format_count(runpath_fmt) == 2) + runpath = util_alloc_sprintf(runpath_fmt, iens,iter); + + test_assert_string_equal(runpath, file_path); + + if (iens_index+1 < int_vector_size(iens_values)) + ++iens_index; + else if ((iens_index+1 == int_vector_size(iens_values))) { + ++iter_index; + iens_index = 0; + } + + free(base); + } + + int linecount = int_vector_size(iens_values) * int_vector_size(iter_values); + test_assert_int_equal(linecount, counter); + free(cwd); + fclose(file); + } +} + + + +void test_export_runpath_files(const char * config_file, + const char * config_file_iterations, + const char * job_file_export_runpath) { + + stringlist_type * args = stringlist_alloc_new(); + const char * job_name = "export_job"; + + ert_test_context_type * test_context_iterations = create_context( config_file_iterations, "enkf_workflow_job_test_export_runpath_iter" ); + + { + int_vector_type * iens_values = int_vector_alloc(5,0); + const int iens[5] = {0,1,2,3,4}; + int_vector_set_many(iens_values, 0, &iens[0], 5); + int_vector_type * iter_values = int_vector_alloc(1,0); + + test_export_runpath_file(test_context_iterations, job_name, job_file_export_runpath, args, iens_values, iter_values); + + int_vector_free(iens_values); + int_vector_free(iter_values); + } + { + stringlist_append_copy( args, "0-2"); //realization range + + int_vector_type * iens_values = int_vector_alloc(3,0); + const int iens[] = {0,1,2}; + int_vector_set_many(iens_values, 0, &iens[0], 3); + int_vector_type * iter_values = int_vector_alloc(1,0); + + test_export_runpath_file(test_context_iterations, job_name, job_file_export_runpath, args, iens_values, iter_values); + + int_vector_free(iens_values); + int_vector_free(iter_values); + + stringlist_clear(args); + } + { + stringlist_append_copy( args, "0,3-5"); //realization range + + int_vector_type * iens_values = int_vector_alloc(4,0); + const int iens[] = {0,3,4,5}; + int_vector_set_many(iens_values, 0, &iens[0], 4); + int_vector_type * iter_values = int_vector_alloc(1,0); + + test_export_runpath_file(test_context_iterations, job_name, job_file_export_runpath, args, iens_values, iter_values); + + int_vector_free(iens_values); + int_vector_free(iter_values); + + stringlist_clear(args); + } + { + stringlist_append_copy( args, "1-2"); //realization range + stringlist_append_copy( args, "|"); //delimiter + stringlist_append_copy( args, "1-3"); //iteration range + + int_vector_type * iens_values = int_vector_alloc(2,0); + int iens[] = {1,2}; + int_vector_set_many(iens_values, 0, &iens[0], 2); + int_vector_type * iter_values = int_vector_alloc(3,0); + int iter[] = {1,2,3}; + int_vector_set_many(iter_values, 0, &iter[0], 3); + + test_export_runpath_file(test_context_iterations, job_name, job_file_export_runpath, args, iens_values, iter_values); + + int_vector_free(iens_values); + int_vector_free(iter_values); + + stringlist_clear(args); + } + { + stringlist_append_copy( args, "*"); //realization range + stringlist_append_copy( args, "|"); //delimiter + stringlist_append_copy( args, "*"); //iteration range + + int_vector_type * iens_values = int_vector_alloc(5,0); + int iens[] = {0,1,2,3,4}; + int_vector_set_many(iens_values, 0, &iens[0], 5); + int_vector_type * iter_values = int_vector_alloc(4,0); + int iter[] = {0,1,2,3}; + int_vector_set_many(iter_values, 0, &iter[0], 4); + + test_export_runpath_file(test_context_iterations, job_name, job_file_export_runpath, args, iens_values, iter_values); + + int_vector_free(iens_values); + int_vector_free(iter_values); + + stringlist_clear(args); + } + + ert_test_context_free(test_context_iterations); + ert_test_context_type * test_context = create_context( config_file, "enkf_workflow_job_test_export_runpath" ); + + { + int_vector_type * iens_values = int_vector_alloc(1,0); + int_vector_init_range(iens_values, 0, 25, 1); + int_vector_type * iter_values = int_vector_alloc(1,0); + + test_export_runpath_file(test_context, job_name, job_file_export_runpath, args, iens_values, iter_values); + + int_vector_free(iens_values); + int_vector_free(iter_values); + + stringlist_clear(args); + } + { + stringlist_append_copy( args, "1-3"); //realization range + + int_vector_type * iens_values = int_vector_alloc(3,0); + int iens[] = {1,2,3}; + int_vector_set_many(iens_values, 0, &iens[0], 3); + int_vector_type * iter_values = int_vector_alloc(1,0); + + test_export_runpath_file(test_context, job_name, job_file_export_runpath, args, iens_values, iter_values); + + int_vector_free(iens_values); + int_vector_free(iter_values); + + stringlist_clear(args); + } + { + stringlist_append_copy( args, "1,2"); //realization range + stringlist_append_copy( args, "|"); //delimiter + stringlist_append_copy( args, "1-3"); //iteration range + + int_vector_type * iens_values = int_vector_alloc(2,0); + int iens[] = {1,2}; + int_vector_set_many(iens_values, 0, &iens[0], 2); + int_vector_type * iter_values = int_vector_alloc(1,0); + + test_export_runpath_file(test_context, job_name, job_file_export_runpath, args, iens_values, iter_values); + + int_vector_free(iens_values); + int_vector_free(iter_values); + + stringlist_clear(args); + } + + + ert_test_context_free(test_context); + + + stringlist_free( args ); +} + + + + +int main(int argc , const char ** argv) { + enkf_main_install_SIGNALS(); + + const char * config_file = argv[1]; + const char * config_file_iterations = argv[2]; + const char * job_file_create_case = argv[3]; + const char * job_file_init_case_job = argv[4]; + const char * job_file_load_results = argv[5]; + const char * job_file_load_results_iter = argv[6]; + const char * job_file_observation_ranking = argv[7]; + const char * job_file_data_ranking = argv[8]; + const char * job_file_ranking_export = argv[9]; + const char * job_file_init_misfit_table = argv[10]; + const char * job_file_export_runpath = argv[11]; + const char * job_file_pre_simulation_copy = argv[12]; + + + ert_test_context_type * test_context = create_context( config_file, "enkf_workflow_job_test" ); + { + test_create_case_job(test_context, "JOB1" , job_file_create_case); + test_init_case_job(test_context, "JOB2", job_file_init_case_job); + test_load_results_job(test_context, "JOB3" , job_file_load_results); + test_load_results_iter_job( test_context, "JOB4" , job_file_load_results_iter ); + test_init_misfit_table(test_context, "JOB5" , job_file_init_misfit_table); + test_rank_realizations_on_observations_job(test_context, "JOB6" , job_file_observation_ranking); + test_rank_realizations_on_data_job(test_context , "JOB7" , job_file_data_ranking); + test_export_ranking(test_context, "JOB8" , job_file_ranking_export); + test_pre_simulation_copy(test_context , "JOBB" , job_file_pre_simulation_copy); + } + ert_test_context_free( test_context ); + + test_export_runpath_files(config_file, config_file_iterations, job_file_export_runpath); + exit(0); +} diff --git a/libres/lib/enkf/tests/enkf_workflow_job_test2.cpp b/libres/lib/enkf/tests/enkf_workflow_job_test2.cpp new file mode 100644 index 00000000000..0ba02a4539f --- /dev/null +++ b/libres/lib/enkf/tests/enkf_workflow_job_test2.cpp @@ -0,0 +1,61 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'enkf_workflow_job_test2.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 + + +ert_test_context_type * create_context( const char * config_file, const char * name ) { + ert_test_context_type * test_context = ert_test_context_alloc__(name , config_file, true); + usleep( 2 * 100000 ); + return test_context; +} + + + +void test_load_results_job(ert_test_context_type * test_context , const char * job_name , const char * job_file) { + stringlist_type * args = stringlist_alloc_new(); + ert_test_context_install_workflow_job( test_context , job_name , job_file ); + stringlist_append_copy( args , "0"); + stringlist_append_copy( args , ","); + stringlist_append_copy( args , "1"); + test_assert_true( ert_test_context_run_worklow_job( test_context , job_name , args) ); + stringlist_free( args ); + + enkf_main_type * enkf_main = ert_test_context_get_main( test_context ); + enkf_fs_type * fs = enkf_main_get_fs( enkf_main ); + time_map_type * time_map = enkf_fs_get_time_map( fs ); + test_assert_true( time_map_get_last_step( time_map ) > 0 ); +} + + + +int main(int argc , const char ** argv) { + enkf_main_install_SIGNALS(); + + const char * config_file = argv[1]; + const char * job_file_load_results = argv[2]; + + ert_test_context_type * test_context = create_context( config_file, "enkf_workflow_job_test2" ); + test_load_results_job(test_context, "JOB" , job_file_load_results); + ert_test_context_free( test_context ); + + exit(0); +} diff --git a/libres/lib/enkf/tests/enkf_workflow_job_test_directory.cpp b/libres/lib/enkf/tests/enkf_workflow_job_test_directory.cpp new file mode 100644 index 00000000000..64c2f9b7463 --- /dev/null +++ b/libres/lib/enkf/tests/enkf_workflow_job_test_directory.cpp @@ -0,0 +1,43 @@ +/* + Copyright (C) 2016 Equinor ASA, Norway. + + The file 'enkf_workflow_job_test_version.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 + + + + +int main(int argc , const char ** argv) { + enkf_main_install_SIGNALS(); + { + const char * path = argv[1]; + ert_workflow_list_type * workflows = ert_workflow_list_alloc_empty(NULL); + ert_workflow_list_add_jobs_in_directory( workflows , path ); + + test_assert_true( ert_workflow_list_has_job( workflows , "CONF1")); + test_assert_true( ert_workflow_list_has_job( workflows , "CONF2@1")); + test_assert_false( ert_workflow_list_has_job( workflows , "CONF2")); + test_assert_true( ert_workflow_list_has_job( workflows , "CONF3@1.2.3")); + test_assert_true( ert_workflow_list_has_job( workflows , "CONF4@1.2.0")); + test_assert_false( ert_workflow_list_has_job( workflows , "CONF5")); + + ert_workflow_list_free( workflows ); + } + exit(0); +} diff --git a/libres/lib/enkf/tests/gen_kw_logarithmic_test.cpp b/libres/lib/enkf/tests/gen_kw_logarithmic_test.cpp new file mode 100644 index 00000000000..072a484946c --- /dev/null +++ b/libres/lib/enkf/tests/gen_kw_logarithmic_test.cpp @@ -0,0 +1,109 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'gen_kw_test.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 + + +void verify_parameters_txt( ) { + int buffer_size = 0; + char * file_content = util_fread_alloc_file_content("simulations/run0/parameters.txt", &buffer_size); + + stringlist_type * token_list = stringlist_alloc_from_split(file_content, " \n"); + double value = stringlist_iget_as_double(token_list, 5, NULL); + + test_assert_true(value > 0.0); //Verify precision + test_assert_true(NULL != strstr(file_content, "LOG10_")); //Verify log entry + + stringlist_free(token_list); + free(file_content); +} + + +void test_write_gen_kw_export_file(enkf_main_type * enkf_main) { + enkf_fs_type * init_fs = enkf_main_get_fs( enkf_main ); + ensemble_config_type * ens_config = enkf_main_get_ensemble_config( enkf_main ); + enkf_node_type * enkf_node = enkf_node_alloc( ensemble_config_get_node( ens_config , "MULTFLT" )); + enkf_node_type * enkf_node2 = enkf_node_alloc( ensemble_config_get_node( ens_config , "MULTFLT2" )); + test_assert_true(enkf_node_get_impl_type(enkf_node) == GEN_KW); + test_assert_true(enkf_node_get_impl_type(enkf_node2) == GEN_KW); + + { + gen_kw_type * gen_kw = (gen_kw_type *) enkf_node_value_ptr(enkf_node); + gen_kw_type * gen_kw2 = (gen_kw_type *) enkf_node_value_ptr(enkf_node2); + + + { + rng_type * rng = rng_alloc( MZRAN , INIT_DEFAULT ); + const enkf_config_node_type * config = enkf_node_get_config(enkf_node); + const int data_size = enkf_config_node_get_data_size( config, 0 ); + const double mean = 0.0; /* Mean and std are hardcoded - the variability should be in the transformation. */ + const double std = 1.0; + + for (int i=0; i < data_size; ++i) { + double random_number = enkf_util_rand_normal(mean , std , rng); + gen_kw_data_iset(gen_kw, i, random_number); + gen_kw_data_iset(gen_kw2, i, random_number); + } + + rng_free(rng); + } + node_id_type node_id = {.report_step = 0, + .iens = 0 }; + + enkf_node_store(enkf_node, init_fs, node_id); + enkf_node_store(enkf_node2, init_fs, node_id); + } + + { + const subst_list_type * subst_list = subst_config_get_subst_list(enkf_main_get_subst_config(enkf_main)); + run_arg_type * run_arg = run_arg_alloc_INIT_ONLY( "run_id", init_fs , 0 ,0 , "simulations/run0", subst_list); + enkf_state_ecl_write( enkf_main_get_ensemble_config( enkf_main ), + enkf_main_get_model_config( enkf_main ), + run_arg , + init_fs); + test_assert_true(util_file_exists("simulations/run0/parameters.txt")); + run_arg_free( run_arg ); + } + enkf_node_free( enkf_node ); + enkf_node_free( enkf_node2 ); + + verify_parameters_txt( ); +} + + + +int main(int argc , char ** argv) { + util_install_signals(); + { + const char * config_file = argv[1]; + ert_test_context_type * test_context = ert_test_context_alloc("gen_kw_logarithmic_test" , config_file ); + enkf_main_type * enkf_main = ert_test_context_get_main(test_context); + + test_assert_not_NULL(enkf_main); + + test_write_gen_kw_export_file(enkf_main); + + ert_test_context_free( test_context ); + } + exit(0); +} + diff --git a/libres/lib/enkf/tests/gen_kw_test.cpp b/libres/lib/enkf/tests/gen_kw_test.cpp new file mode 100644 index 00000000000..f227b98384f --- /dev/null +++ b/libres/lib/enkf/tests/gen_kw_test.cpp @@ -0,0 +1,103 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'gen_kw_test.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_write_gen_kw_export_file(enkf_main_type * enkf_main) +{ + stringlist_type * key_list = ensemble_config_alloc_keylist_from_var_type( enkf_main_get_ensemble_config( enkf_main ) , PARAMETER ); + enkf_state_type * state = enkf_main_iget_state( enkf_main , 0 ); + enkf_fs_type * init_fs = enkf_main_get_fs( enkf_main ); + const subst_list_type * subst_list = subst_config_get_subst_list(enkf_main_get_subst_config(enkf_main)); + run_arg_type * run_arg = run_arg_alloc_INIT_ONLY( "run_id", init_fs , 0 ,0 , "simulations/run0", subst_list); + rng_manager_type * rng_manager = enkf_main_get_rng_manager( enkf_main ); + rng_type * rng = rng_manager_iget( rng_manager, run_arg_get_iens( run_arg )); + + enkf_state_initialize( state , rng, init_fs, key_list , INIT_FORCE ); + enkf_state_ecl_write( enkf_main_get_ensemble_config( enkf_main ), + enkf_main_get_model_config( enkf_main ), + run_arg , + init_fs); + test_assert_true(util_file_exists("simulations/run0/parameters.txt")); + test_assert_true(util_file_exists("simulations/run0/parameters.json")); + run_arg_free( run_arg ); + + stringlist_free(key_list); +} + + + +static void read_erroneous_gen_kw_file( void * arg) { + vector_type * arg_vector = vector_safe_cast( arg ); + gen_kw_config_type * gen_kw_config = (gen_kw_config_type *)vector_iget( arg_vector, 0 ); + const char * filename = (const char *) vector_iget_const( arg_vector, 1 ); + gen_kw_config_set_parameter_file(gen_kw_config, filename); +} + + +void test_read_erroneous_gen_kw_file() { + const char * parameter_filename = "MULTFLT_with_errors.txt"; + const char * tmpl_filename = "MULTFLT.tmpl"; + + { + FILE * stream = util_fopen(parameter_filename, "w"); + const char * data = util_alloc_sprintf("MULTFLT1 NORMAL 0\nMULTFLT2 RAW\nMULTFLT3 NORMAL 0"); + util_fprintf_string(data, 30, right_pad , stream); + fclose(stream); + + FILE * tmpl_stream = util_fopen(tmpl_filename, "w"); + const char * tmpl_data = util_alloc_sprintf(" \n"); + util_fprintf_string(tmpl_data, 30, right_pad, tmpl_stream); + fclose(tmpl_stream); + } + + gen_kw_config_type * gen_kw_config = gen_kw_config_alloc_empty("MULTFLT", "<%s>"); + vector_type * arg = vector_alloc_new(); + vector_append_ref( arg , gen_kw_config ); + vector_append_ref(arg, parameter_filename); + + test_assert_util_abort("gen_kw_config_set_parameter_file", read_erroneous_gen_kw_file, arg); + + vector_free(arg); + gen_kw_config_free(gen_kw_config); +} + + +int main(int argc , char ** argv) { + const char * config_file = argv[1]; + ert_test_context_type * test_context = ert_test_context_alloc("gen_kw_test" , config_file ); + enkf_main_type * enkf_main = ert_test_context_get_main(test_context); + test_assert_not_NULL(enkf_main); + + test_write_gen_kw_export_file(enkf_main); + test_read_erroneous_gen_kw_file(); + + ert_test_context_free( test_context ); + exit(0); +} + diff --git a/libres/lib/enkf/tests/log_config_level_parse.cpp b/libres/lib/enkf/tests/log_config_level_parse.cpp new file mode 100644 index 00000000000..87dd5b0e2f2 --- /dev/null +++ b/libres/lib/enkf/tests/log_config_level_parse.cpp @@ -0,0 +1,47 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'enkf_res_log_level_parse.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 + +typedef struct { + const char * log_keyword; // keyword written in the config file + const message_level_type log_enum; // enum for the new log-level +} log_tuple; + +log_tuple log_levels[] = {{LOG_CRITICAL_NAME, LOG_CRITICAL}, + {LOG_ERROR_NAME, LOG_ERROR}, + {LOG_WARNING_NAME, LOG_WARNING}, + {LOG_INFO_NAME, LOG_INFO}, + {LOG_DEBUG_NAME, LOG_DEBUG}}; + +void test_parse_keywords_positive() { + for (int i = 0; i < 5; i++) { + test_assert_int_equal(log_config_level_parser(log_levels[i].log_keyword), log_levels[i].log_enum); + } +} + +void test_parse_negative_becomes_default() { + for (int i = 0; i < 5; i++) { + test_assert_int_equal(log_config_level_parser(log_levels[i].log_keyword), log_levels[i].log_enum); + } +} + +int main( int argc , char ** argv) { + test_parse_keywords_positive(); + test_parse_negative_becomes_default(); +} diff --git a/libres/lib/enkf/tests/obs_vector_tests.cpp b/libres/lib/enkf/tests/obs_vector_tests.cpp new file mode 100644 index 00000000000..b6964ff7227 --- /dev/null +++ b/libres/lib/enkf/tests/obs_vector_tests.cpp @@ -0,0 +1,244 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_obs_vector_tests.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 + +bool alloc_strippedparameters_noerrors() { + obs_vector_type * obs_vector = obs_vector_alloc(SUMMARY_OBS, "WHAT", NULL, 0); + obs_vector_free(obs_vector); + return true; +} + +/*******Summary obs tests*******************/ +bool scale_std_summary_nodata_no_errors() { + obs_vector_type * obs_vector = obs_vector_alloc(SUMMARY_OBS, "WHAT", NULL, 0); + local_obsdata_node_type * local_node = obs_vector_alloc_local_node( obs_vector ); + obs_vector_scale_std(obs_vector, local_node , 2.0); + obs_vector_free(obs_vector); + local_obsdata_node_free( local_node ); + return true; +} + +bool scale_std_summarysingleobservation_no_errors() { + obs_vector_type * obs_vector = obs_vector_alloc(SUMMARY_OBS, "WHAT", NULL, 1); + summary_obs_type * summary_obs = summary_obs_alloc("SummaryKey", "ObservationKey", 43.2, 2.0); + obs_vector_install_node(obs_vector, 0, summary_obs); + test_assert_double_equal(2.0, summary_obs_get_std(summary_obs)); + test_assert_double_equal(1.0, summary_obs_get_std_scaling(summary_obs)); + + { + local_obsdata_node_type * local_node = obs_vector_alloc_local_node( obs_vector ); + obs_vector_scale_std(obs_vector, local_node , 2.0); + local_obsdata_node_free( local_node ); + } + test_assert_double_equal(2.0, summary_obs_get_std_scaling(summary_obs)); + + obs_vector_free(obs_vector); + return true; +} + +bool scale_std_summarymanyobservations_no_errors() { + int num_observations = 100; + double scaling_factor = 1.456; + + obs_vector_type * obs_vector = obs_vector_alloc(SUMMARY_OBS, "WHAT", NULL, num_observations); + + test_assert_bool_equal(0, obs_vector_get_num_active(obs_vector)); + + summary_obs_type * observations[num_observations]; + for (int i = 0; i < num_observations; i++) { + summary_obs_type * summary_obs = summary_obs_alloc("SummaryKey", "ObservationKey", 43.2, i); + obs_vector_install_node(obs_vector, i, summary_obs); + observations[i] = summary_obs; + } + + for (int i = 0; i < num_observations; i++) { + summary_obs_type * before_scale = observations[i]; + test_assert_double_equal(i, summary_obs_get_std(before_scale)); + } + + test_assert_bool_equal(num_observations, obs_vector_get_num_active(obs_vector)); + { + local_obsdata_node_type * local_node = obs_vector_alloc_local_node( obs_vector ); + obs_vector_scale_std(obs_vector, local_node , scaling_factor); + local_obsdata_node_free( local_node ); + } + for (int i = 0; i < num_observations; i++) { + summary_obs_type * after_scale = observations[i]; + test_assert_double_equal(scaling_factor, summary_obs_get_std_scaling(after_scale)); + } + + obs_vector_free(obs_vector); + return true; +} + +/************ Block obs tests *****************************************************/ + +bool scale_std_block_nodata_no_errors() { + obs_vector_type * obs_vector = obs_vector_alloc(BLOCK_OBS, "WHAT", NULL, 0); + local_obsdata_node_type * local_node = obs_vector_alloc_local_node( obs_vector ); + obs_vector_scale_std(obs_vector, local_node , 2.0); + local_obsdata_node_free( local_node ); + obs_vector_free(obs_vector); + return true; +} + +block_obs_type * create_block_obs(ecl_grid_type * grid, int size, double value, double std_dev) { + field_config_type * field_config = field_config_alloc_empty( "PRESSURE" , grid , NULL, false ); + int * i = (int *)util_calloc(size, sizeof * i); + int * j = (int *)util_calloc(size, sizeof * j); + int * k = (int *)util_calloc(size, sizeof * k); + double * obs_value = (double *)util_calloc(size, sizeof * obs_value); + double * obs_std = (double *)util_calloc(size, sizeof * obs_std); + + for (int num = 0; num < size; num++) { + obs_value[num] = value; + obs_std[num] = std_dev; + i[num] = num; + j[num] = num; + k[num] = num; + } + + block_obs_type * block_obs = block_obs_alloc_complete("Label", SOURCE_FIELD, NULL, field_config , grid, size, i, j, k, obs_value, obs_std); + + free(i); + free(j); + free(k); + free(obs_value); + free(obs_std); + field_config_free( field_config ); + + return block_obs; +} + +bool scale_std_block100observations_no_errors() { + int num_observations = 100; + int num_points = 10; + + obs_vector_type * obs_vector = obs_vector_alloc(BLOCK_OBS, "WHAT", NULL, num_observations); + ecl_grid_type * grid = ecl_grid_alloc_rectangular(num_points, num_points, num_points, 1.0, 1.0, 1.0, NULL); + + double scale_factor = 3.3; + double obs_value = 44; + double obs_std = 3.2; + + block_obs_type * observations[num_observations]; + + for (int i = 0; i < num_observations; i++) { + block_obs_type * block_obs = create_block_obs(grid, num_points, obs_value, obs_std); + obs_vector_install_node(obs_vector, i, block_obs); + observations[i] = block_obs; + } + + for (int i = 0; i < num_observations; i++) { + for (int point_nr = 0; point_nr < num_points; point_nr++) { + double value = block_obs_iget_value(observations[i], point_nr); + double std = block_obs_iget_std(observations[i], point_nr); + test_assert_double_equal(obs_value, value); + test_assert_double_equal(obs_std, std); + } + } + + { + local_obsdata_node_type * local_node = obs_vector_alloc_local_node( obs_vector ); + obs_vector_scale_std(obs_vector, local_node , scale_factor); + local_obsdata_node_free( local_node ); + } + + for (int i = 0; i < num_observations; i++) { + for (int point_nr = 0; point_nr < num_points; point_nr++) { + double value = block_obs_iget_value(observations[i], point_nr); + double std = block_obs_iget_std(observations[i], point_nr); + double std_scale = block_obs_iget_std_scaling( observations[i] , point_nr); + test_assert_double_equal(obs_value, value); + test_assert_double_equal(obs_std , std); + test_assert_double_equal(scale_factor , std_scale); + } + } + + ecl_grid_free(grid); + obs_vector_free(obs_vector); + return true; +} + +/*************Gen obs tests************************************************/ + +bool scale_std_gen_nodata_no_errors() { + obs_vector_type * obs_vector = obs_vector_alloc(GEN_OBS, "WHAT", NULL, 0); + local_obsdata_node_type * local_node = obs_vector_alloc_local_node( obs_vector ); + obs_vector_scale_std(obs_vector, local_node , 2.0); + obs_vector_free(obs_vector); + local_obsdata_node_free( local_node ); + return true; +} + +bool scale_std_gen_withdata_no_errors() { + int num_observations = 100; + double value = 42; + double std_dev = 2.2; + double multiplier = 3.4; + + obs_vector_type * obs_vector = obs_vector_alloc(GEN_OBS, "WHAT", NULL, num_observations); + + gen_obs_type * observations[num_observations]; + for (int i = 0; i < num_observations; i++) { + gen_obs_type * gen_obs = gen_obs_alloc(NULL, "WWCT-GEN", NULL, value, std_dev, NULL, NULL, NULL); + obs_vector_install_node(obs_vector, i, gen_obs); + observations[i] = gen_obs; + } + + { + local_obsdata_node_type * local_node = obs_vector_alloc_local_node( obs_vector ); + obs_vector_scale_std(obs_vector, local_node , multiplier); + local_obsdata_node_free( local_node ); + } + + for (int i = 0; i < num_observations; i++) { + char * index_key = util_alloc_sprintf("%d", 0); + double value_new, std_new; + bool valid; + gen_obs_user_get_with_data_index(observations[i], index_key, &value_new, &std_new, &valid); + test_assert_double_equal(std_dev , std_new); + test_assert_double_equal(value, value_new); + test_assert_double_equal(multiplier , gen_obs_iget_std_scaling( observations[i] , 0 )); + free(index_key); + } + + obs_vector_free(obs_vector); + return true; +} + +int main(int argc, char ** argv) { + test_assert_bool_equal(alloc_strippedparameters_noerrors(), true); + test_assert_bool_equal(scale_std_summary_nodata_no_errors(), true); + test_assert_bool_equal(scale_std_summarysingleobservation_no_errors(), true); + test_assert_bool_equal(scale_std_summarymanyobservations_no_errors(), true); + + test_assert_bool_equal(scale_std_block_nodata_no_errors(), true); + test_assert_bool_equal(scale_std_block100observations_no_errors(), true); + + test_assert_bool_equal(scale_std_gen_nodata_no_errors(), true); + test_assert_bool_equal(scale_std_gen_withdata_no_errors(), true); + + exit(0); +} + diff --git a/libres/lib/enkf/tests/rng_config.cpp b/libres/lib/enkf/tests/rng_config.cpp new file mode 100644 index 00000000000..14ef5822a18 --- /dev/null +++ b/libres/lib/enkf/tests/rng_config.cpp @@ -0,0 +1,141 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'rng_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 + + + +#define MAX_INT 999999 + +static void create_config( + const char * user_config_file, + const char * random_seed) +{ + FILE * stream = util_fopen(user_config_file, "w"); + fprintf(stream, "NUM_REALIZATIONS 17\n"); + if(random_seed) + fprintf(stream, "RANDOM_SEED %s\n", random_seed); + fclose(stream); +} + +static char * alloc_read_random_seed(const char * log_file) +{ + FILE * stream = util_fopen(log_file, "r"); + char word [256]; + char random_seed [256]; + while(fscanf(stream, "%s", word) == 1) + if (strcmp("RANDOM_SEED", word) == 0) + fscanf(stream, "%s", random_seed); + + fclose(stream); + + return util_alloc_string_copy(random_seed); +} + +void test_init() +{ + ecl::util::TestArea ta("rng_init"); + res_log_init_log(LOG_DEBUG, "log", true); + + const char * config_file = "my_rng_config"; + const char * random_seed = "13371338"; + + create_config(config_file, random_seed); + + rng_config_type * rng_config = rng_config_alloc_load_user_config(config_file); + test_assert_string_equal(random_seed, rng_config_get_random_seed(rng_config)); + + rng_manager_free(rng_config_alloc_rng_manager(rng_config)); + + // To get the random seed written to the log + + char * logged_random_seed = alloc_read_random_seed("log"); + test_assert_true(strlen(logged_random_seed) > 0); + + free(logged_random_seed); + free(rng_config); +} + +static void alloc_reproduced_rng_config( + const char * random_seed, + rng_config_type ** orig_rng_config, + rng_config_type ** rep_rng_config, + rng_manager_type ** orig_rng_man, + rng_manager_type ** rep_rng_man) +{ + ecl::util::TestArea ta("rng_conifg"); + res_log_init_log(LOG_DEBUG, "log", true); + + const char * config_file = "my_rng_config"; + create_config(config_file, random_seed); + *orig_rng_config = rng_config_alloc_load_user_config(config_file); + + rng_manager_type * rng_man = rng_config_alloc_rng_manager(*orig_rng_config); + if(orig_rng_man) + *orig_rng_man = rng_man; + else + rng_manager_free(rng_man); + + const char * rep_config_file = "rep_config"; + char * logged_random_seed = alloc_read_random_seed("log"); + create_config(rep_config_file, logged_random_seed); + *rep_rng_config = rng_config_alloc_load_user_config(rep_config_file); + + if(rep_rng_man) + *rep_rng_man = rng_config_alloc_rng_manager(*rep_rng_config); + + free(logged_random_seed); +} + +void test_reproducibility(const char * random_seed) +{ + + rng_config_type * orig_rng_config; + rng_config_type * rep_rng_config; + + rng_manager_type * orig_rng_man; + rng_manager_type * rep_rng_man; + + alloc_reproduced_rng_config(random_seed, + &orig_rng_config, &rep_rng_config, + &orig_rng_man, &rep_rng_man); + + test_assert_not_NULL(orig_rng_man); + test_assert_not_NULL(rep_rng_man); + + rng_type * orig_rng_0 = rng_manager_iget(orig_rng_man, 0); + rng_type * orig_rng_100 = rng_manager_iget(orig_rng_man, 100); + + rng_type * rep_rng_0 = rng_manager_iget(rep_rng_man, 0); + rng_type * rep_rng_100 = rng_manager_iget(rep_rng_man, 100); + + test_assert_int_equal(rng_get_int(orig_rng_0, MAX_INT), rng_get_int(rep_rng_0, MAX_INT)); + test_assert_int_equal(rng_get_int(orig_rng_100, MAX_INT), rng_get_int(rep_rng_100, MAX_INT)); +} + +int main(int argc , char ** argv) { + test_init(); + test_reproducibility(NULL); // Random seed + test_reproducibility("42"); + test_reproducibility("423543854372895743289507289532"); + test_reproducibility("423543854372895743289507289532423543854372895743289507289532"); +} diff --git a/libres/lib/enkf/tests/rng_manager.cpp b/libres/lib/enkf/tests/rng_manager.cpp new file mode 100644 index 00000000000..d0bd8b42111 --- /dev/null +++ b/libres/lib/enkf/tests/rng_manager.cpp @@ -0,0 +1,183 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'rng_manager.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 MAX_INT 999999 + +void test_create() { + rng_manager_type * rng_manager = rng_manager_alloc_load("file/does/not/exist"); + test_assert_NULL( rng_manager ); + + rng_manager = rng_manager_alloc_default( ); + test_assert_true( rng_manager_is_instance( rng_manager )); + rng_manager_free( rng_manager ); +} + + +void test_default( ) { + rng_manager_type * rng_manager1 = rng_manager_alloc_default( ); + rng_manager_type * rng_manager2 = rng_manager_alloc_default( ); + rng_manager_type * rng_manager3 = rng_manager_alloc_default( ); + + rng_type * rng1 = rng_manager_alloc_rng( rng_manager1 ); + rng_type * rng1_0 = rng_manager_iget( rng_manager1, 0 ); + rng_type * rng1_100 = rng_manager_iget( rng_manager1, 100 ); + + rng_type * rng2_0 = rng_manager_iget( rng_manager2, 0 ); + rng_type * rng2 = rng_manager_alloc_rng( rng_manager2 ); + rng_type * rng2_100 = rng_manager_iget( rng_manager2, 100 ); + + rng_type * rng3_100 = rng_manager_iget( rng_manager3, 100 ); + rng_type * rng3_0 = rng_manager_iget( rng_manager3, 0 ); + rng_type * rng3 = rng_manager_alloc_rng( rng_manager3 ); + + test_assert_int_equal( rng_get_int( rng1_0, MAX_INT ), rng_get_int( rng2_0, MAX_INT)); + rng_get_int( rng3_0 , MAX_INT); + test_assert_int_equal( rng_get_int( rng1_0, MAX_INT ), rng_get_int( rng3_0, MAX_INT)); + + test_assert_int_equal( rng_get_int( rng1_100, MAX_INT ), rng_get_int( rng2_100, MAX_INT)); + rng_get_int( rng3_100 , MAX_INT); + test_assert_int_equal( rng_get_int( rng1_100, MAX_INT ), rng_get_int( rng3_100, MAX_INT)); + + test_assert_int_equal( rng_get_int( rng1, MAX_INT ), rng_get_int( rng2, MAX_INT)); + rng_get_int( rng3 , MAX_INT); + test_assert_int_equal( rng_get_int( rng1, MAX_INT ), rng_get_int( rng3, MAX_INT)); + + rng_free( rng1 ); + rng_free( rng2 ); + rng_free( rng3 ); + rng_manager_free( rng_manager3 ); + rng_manager_free( rng_manager2 ); + rng_manager_free( rng_manager1 ); +} + + +void test_state( ) { + rng_manager_type * rng_manager = rng_manager_alloc_default( ); + ecl::util::TestArea ta("test_rng"); + rng_manager_iget(rng_manager , 100 ); + rng_manager_save_state( rng_manager , "seed.txt"); + test_assert_true( util_file_exists( "seed.txt" )); + { + rng_manager_type * rng_manager1 = rng_manager_alloc_load( "seed.txt"); + rng_manager_type * rng_manager2 = rng_manager_alloc_load( "seed.txt"); + + rng_type * rng1_0 = rng_manager_iget( rng_manager1, 0 ); + rng_type * rng1_100 = rng_manager_iget( rng_manager1, 100 ); + + rng_type * rng2_0 = rng_manager_iget( rng_manager2, 0 ); + rng_type * rng2_100 = rng_manager_iget( rng_manager2, 100 ); + + test_assert_int_equal( rng_get_int( rng1_0, MAX_INT ), rng_get_int( rng2_0, MAX_INT)); + test_assert_int_equal( rng_get_int( rng1_100, MAX_INT ), rng_get_int( rng2_100, MAX_INT)); + + rng_manager_free( rng_manager2 ); + rng_manager_free( rng_manager1 ); + } + rng_manager_free( rng_manager ); +} + +void test_state_restore( ) { + ecl::util::TestArea ta("restore"); + rng_manager_type * rng_manager1 = rng_manager_alloc_default( ); + rng_manager_save_state( rng_manager1 , "seed.txt"); + rng_type * rng1 = rng_manager_alloc_rng( rng_manager1 ); + { + rng_manager_type * rng_manager2 = rng_manager_alloc_load("seed.txt"); + + rng_type * rng1_0 = rng_manager_iget( rng_manager1, 0 ); + rng_type * rng1_100 = rng_manager_iget( rng_manager1, 100 ); + + rng_type * rng2_100 = rng_manager_iget( rng_manager2, 100 ); + rng_type * rng2_0 = rng_manager_iget( rng_manager2, 0 ); + + rng_type * rng2 = rng_manager_alloc_rng( rng_manager2 ); + test_assert_int_equal( rng_get_int( rng1_0, MAX_INT ), rng_get_int( rng2_0, MAX_INT)); + test_assert_int_equal( rng_get_int( rng1_100, MAX_INT ), rng_get_int( rng2_100, MAX_INT)); + test_assert_int_equal( rng_get_int( rng1, MAX_INT ), rng_get_int( rng2, MAX_INT)); + + rng_free( rng1 ); + rng_free( rng2 ); + rng_manager_free( rng_manager2 ); + } + rng_manager_free( rng_manager1 ); +} + + + +void test_random( ) { + rng_manager_type * rng_manager1 = rng_manager_alloc_random( ); + rng_manager_type * rng_manager2 = rng_manager_alloc_random( ); + + rng_type * rng1_0 = rng_manager_iget( rng_manager1, 0 ); + rng_type * rng1_100 = rng_manager_iget( rng_manager1, 100 ); + + rng_type * rng2_0 = rng_manager_iget( rng_manager2, 0 ); + rng_type * rng2_100 = rng_manager_iget( rng_manager2, 100 ); + + test_assert_int_not_equal( rng_get_int( rng1_0, MAX_INT ), rng_get_int( rng2_0, MAX_INT)); + test_assert_int_not_equal( rng_get_int( rng1_100, MAX_INT ), rng_get_int( rng2_100, MAX_INT)); + + rng_manager_free( rng_manager2 ); + rng_manager_free( rng_manager1 ); +} + + +static void test_alloc() { + const char * random_seed1 = "apekatterbesting"; + const char * random_seed2 = "apekatterbesxing"; + + rng_manager_type * rng_man0 = rng_manager_alloc(random_seed1); + rng_manager_type * rng_man1 = rng_manager_alloc(random_seed1); + rng_manager_type * rng_man_odd = rng_manager_alloc(random_seed2); + + rng_type * rng0_0 = rng_manager_iget(rng_man0, 0); + rng_type * rng0_42 = rng_manager_iget(rng_man0, 42); + + rng_type * rng1_0 = rng_manager_iget(rng_man1, 0); + rng_type * rng1_42 = rng_manager_iget(rng_man1, 42); + + rng_type * rng_odd_0 = rng_manager_iget(rng_man_odd, 0); + rng_type * rng_odd_42 = rng_manager_iget(rng_man_odd, 42); + + test_assert_int_equal(rng_get_int(rng0_0, MAX_INT), rng_get_int(rng1_0, MAX_INT)); + test_assert_int_equal(rng_get_int(rng0_42, MAX_INT), rng_get_int(rng1_42, MAX_INT)); + + test_assert_int_not_equal(rng_get_int(rng0_0, MAX_INT), rng_get_int(rng_odd_0, MAX_INT)); + test_assert_int_not_equal(rng_get_int(rng0_42, MAX_INT), rng_get_int(rng_odd_42, MAX_INT)); + + rng_manager_free(rng_man0); + rng_manager_free(rng_man1); + rng_manager_free(rng_man_odd); +} + + +int main(int argc , char ** argv) { + test_alloc(); + test_create(); + test_default(); + test_state(); + test_state_restore(); + test_random( ); +} + diff --git a/libres/lib/enkf/tests/row_scaling.cpp b/libres/lib/enkf/tests/row_scaling.cpp new file mode 100644 index 00000000000..1d4f1206087 --- /dev/null +++ b/libres/lib/enkf/tests/row_scaling.cpp @@ -0,0 +1,177 @@ +/* + Copyright (C) 2020 Equinor ASA, Norway. + + The file 'row_scaling.cpp' 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 + + +void test_create() { + row_scaling_type * row_scaling = row_scaling_alloc(); + test_assert_int_equal(row_scaling_get_size(row_scaling), 0); + + test_assert_throw(row_scaling_iget(row_scaling, -1), std::out_of_range); + test_assert_throw(row_scaling_iget(row_scaling, 1000), std::out_of_range); + + test_assert_throw(row_scaling_iset(row_scaling, -1, 0), std::out_of_range); + test_assert_throw(row_scaling_iset(row_scaling, 0, -1), std::invalid_argument); + test_assert_throw(row_scaling_iset(row_scaling, 0, 2), std::invalid_argument); + + row_scaling_iset(row_scaling, 9, 0.25); + test_assert_double_equal(row_scaling_iget(row_scaling, 9), 0.25); + + row_scaling_free(row_scaling); +} + +static void scaleX(matrix_type* X, const matrix_type * X0, double alpha) { + matrix_assign(X, X0); + matrix_scale(X, alpha); + for (int i=0; i < matrix_get_rows(X); i++) + matrix_iset(X, i, i, (1 - alpha) + matrix_iget(X,i,i)); +} + +void row_scaling_multiply2(const row_scaling_type * row_scaling, matrix_type * A, const matrix_type * X0) { + matrix_type * X = matrix_alloc(matrix_get_rows(X0), matrix_get_columns(X0)); + for (int row=0; row < row_scaling_get_size(row_scaling); row++) { + double alpha = row_scaling_iget(row_scaling, row); + scaleX(X, X0, alpha); + + std::vector row_data(matrix_get_columns(A)); + for (int j=0; j < matrix_get_columns(A); j++) { + double sum = 0; + for (int i=0; i < matrix_get_columns(A); i++) + sum += matrix_iget(A, row, i) * matrix_iget(X,i,j); + + row_data[j] = sum; + } + matrix_set_row(A, row_data.data(), row); + } + matrix_free(X); +} + +void test_multiply(const row_scaling_type * row_scaling, const matrix_type * A0, const matrix_type * X0) { + matrix_type * A1 = matrix_alloc_copy(A0); + matrix_type * A2 = matrix_alloc_copy(A0); + + row_scaling_multiply(row_scaling, A1, X0); + row_scaling_multiply2(row_scaling, A2, X0); + test_assert_true(matrix_equal(A1, A2)); + + matrix_free(A1); + matrix_free(A2); +} + + +void test_multiply() { + const int data_size = 200000; + const int ens_size = 100; + matrix_type * A0 = matrix_alloc(data_size, ens_size); + matrix_type * X0 = matrix_alloc(ens_size, ens_size); + rng_type * rng = rng_alloc(MZRAN, INIT_DEFAULT); + matrix_random_init(A0, rng); + + const int project_iens = 4; + for (int col=0; col < ens_size; col++) + matrix_iset(X0, project_iens, col, 1.0); + + + // alpha == 1: Full update, should project out realizations project_iens + { + row_scaling_type * row_scaling = row_scaling_alloc(); + matrix_type * A = matrix_alloc_copy(A0); + for (int row = 0; row < data_size; row++) + row_scaling_iset(row_scaling, row, 1); + row_scaling_multiply(row_scaling, A, X0); + + for (int row = 0; row < data_size; row++) + for (int col=0; col < ens_size; col++) + test_assert_double_equal(matrix_iget(A, row, col), matrix_iget(A0, row, project_iens)); + + matrix_free(A); + test_multiply(row_scaling, A0, X0); + row_scaling_free(row_scaling); + } + + // alpha == 0: No update - should have A == A0 + { + row_scaling_type * row_scaling = row_scaling_alloc(); + matrix_type * A = matrix_alloc_copy(A0); + std::vector row_data(data_size); + for (int row = 0; row < data_size; row++) + row_data[row] = 0; + + row_scaling_assign_float(row_scaling, row_data.data(), row_data.size()); + row_scaling_multiply(row_scaling, A, X0); + + for (int row = 0; row < data_size; row++) + for (int col=0; col < ens_size; col++) + test_assert_double_equal(matrix_iget(A, row, col), matrix_iget(A0, row, col)); + + matrix_free(A); + test_multiply(row_scaling, A0, X0); + row_scaling_free(row_scaling); + } + + + // General alpha + { + row_scaling_type * row_scaling = row_scaling_alloc(); + matrix_type * A = matrix_alloc_copy(A0); + std::vector row_data(data_size); + + row_scaling_iset(row_scaling, 2*data_size, 1.0); + test_assert_int_equal( row_scaling_get_size(row_scaling), 2*data_size + 1); + + for (int row = 0; row < data_size; row++) + row_data[row] = rng_get_double(rng); + + row_scaling_assign_double(row_scaling, row_data.data(), row_data.size()); + test_assert_int_equal( row_scaling_get_size(row_scaling), data_size); + + row_scaling_multiply(row_scaling, A, X0); + for (int row = 0; row < data_size; row++) { + double alpha = row_scaling_iget(row_scaling, row); + for (int col=0; col < ens_size; col++) { + double expected = alpha * matrix_iget(A0, row, project_iens) + (1 - alpha) * matrix_iget(A0, row, col); + test_assert_double_equal(matrix_iget(A, row, col), expected ); + } + } + + test_multiply(row_scaling, A0, X0); + matrix_free(A); + row_scaling_free(row_scaling); + } + + rng_free(rng); + matrix_free(X0); + matrix_free(A0); +} + + +int main(int argc , char ** argv) { + test_create(); + test_multiply(); +} + diff --git a/libres/lib/enkf/tests/trans_func.cpp b/libres/lib/enkf/tests/trans_func.cpp new file mode 100644 index 00000000000..e8e941724a0 --- /dev/null +++ b/libres/lib/enkf/tests/trans_func.cpp @@ -0,0 +1,94 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'trans_func.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 + + + +void test_triangular() { + stringlist_type * args = stringlist_alloc_new(); + stringlist_append_copy(args , "TRIANGULAR"); + stringlist_append_copy(args, "0"); + stringlist_append_copy(args,"0.5"); + stringlist_append_copy(args, "1.0"); + + trans_func_type * trans_func = trans_func_alloc(args); + test_assert_double_equal( trans_func_eval(trans_func, 0.0), 0.50); + trans_func_free( trans_func ); + stringlist_free(args); +} + +void test_triangular_assymetric() { + stringlist_type * args = stringlist_alloc_new(); + stringlist_append_copy(args , "TRIANGULAR"); + stringlist_append_copy(args, "0"); + stringlist_append_copy(args,"1.0"); + stringlist_append_copy(args, "4.0"); + + trans_func_type * trans_func = trans_func_alloc(args); + test_assert_double_equal( trans_func_eval(trans_func, -1.0), 0.7966310411513150456286); + test_assert_double_equal( trans_func_eval(trans_func, 1.1), 2.72407181575270778882286); + trans_func_free( trans_func ); + stringlist_free(args); +} + +void test_create() { + { + stringlist_type * args = stringlist_alloc_new(); + stringlist_append_copy(args , "UNKNOWN_FUNCTION"); + test_assert_NULL( trans_func_alloc(args)); + stringlist_free(args); + } + { + stringlist_type * args = stringlist_alloc_new(); + stringlist_append_copy(args , "UNIFORM"); + stringlist_append_copy(args, "0"); + stringlist_append_copy(args,"1"); + + trans_func_type * trans_func = trans_func_alloc(args); + test_assert_double_equal( trans_func_eval(trans_func, 0.0), 0.50); + trans_func_free( trans_func ); + + stringlist_free(args); + } + { + stringlist_type * args = stringlist_alloc_new(); + stringlist_append_copy(args , "UNIFORM"); + stringlist_append_copy(args, "0"); + stringlist_append_copy(args,"X"); + test_assert_NULL( trans_func_alloc(args)); + stringlist_free(args); + } + { + stringlist_type * args = stringlist_alloc_new(); + stringlist_append_copy(args , "UNIFORM"); + stringlist_append_copy(args, "0"); + test_assert_NULL( trans_func_alloc(args)); + stringlist_free(args); + } +} + +int main(int argc , char ** argv) { + test_create(); + test_triangular(); + test_triangular_assymetric(); +} + diff --git a/libres/lib/enkf/tests/value_export.cpp b/libres/lib/enkf/tests/value_export.cpp new file mode 100644 index 00000000000..afff064dfff --- /dev/null +++ b/libres/lib/enkf/tests/value_export.cpp @@ -0,0 +1,222 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'value_export.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 + + +namespace { + +// This is super ugly but I could not find any way to do this without using a +// a global variable. Of course the correct thing to do would be to implement +// a new walk_directory that either collects first and return (include +// max_depth, remove callbacks), or behaves like an iterator. But that would +// require quite some efforts, and it's probably not worth it for just one +// test. Especially considering that C++17 includes the filesystem library that +// would make this trivial (but it's a bit unclear what compiler we should take +// as a reference) + +std::vector storage; + +void file_cb( + char const* path, + char const* name, + void * +) { + storage.push_back(std::string(path) + UTIL_PATH_SEP_STRING + name); +} + +bool dir_cb ( + char const* path, + char const* name, + int, + void * +) { + storage.push_back(std::string(path) + UTIL_PATH_SEP_STRING + name); + return false; +} + +std::vector directory_tree(std::string const& root) { + storage.clear(); + util_walk_directory( + root.c_str(), + file_cb, + nullptr, + dir_cb, + nullptr); + return storage; // this creates a copy of storage +} + +} /* unnamed namespace */ + + +void test_create() { + ecl::util::TestArea ta("value_export"); + value_export_type * export_value = value_export_alloc( "", "parameters"); + + test_assert_int_equal( 0 , value_export_size( export_value )); + + + test_assert_true( value_export_is_instance( export_value )); + + value_export_txt( export_value ); + test_assert_false( util_file_exists( "parameters.txt" )); + + value_export_json( export_value ); + test_assert_false( util_file_exists( "parameters.json" )); + + value_export_free( export_value ); +} + + + +void test_export_json() { + ecl::util::TestArea ta("value_export_json"); + value_export_type * export_value = value_export_alloc( "path", "parameters"); + util_make_path( "path" ); + + value_export_append(export_value, "KEY100", "SUBKEY1", 100); + value_export_append(export_value, "KEY200", "SUBKEY2", 200); + value_export_append(export_value, "KEY100", "SUBKEY2", 300); + value_export_append(export_value, "KEY200", "SUBKEY1", 400); + value_export_append(export_value, "KEY200", "SUBKEY3", 500); + test_assert_int_equal( 5 , value_export_size( export_value )); + value_export_json( export_value ); + + test_assert_true( util_file_exists( "path/parameters.json" )); + + std::ifstream f("path/parameters.json"); + auto const strJSON = std::string( + std::istreambuf_iterator(f), std::istreambuf_iterator()); + + cJSON *json = cJSON_Parse(strJSON.c_str()); + test_assert_not_NULL(json); + + const cJSON * key1 = cJSON_GetObjectItemCaseSensitive(json, "KEY100"); + test_assert_true(cJSON_IsObject(key1)); + + const cJSON * subkey1 = cJSON_GetObjectItemCaseSensitive(key1, "SUBKEY1"); + test_assert_true(cJSON_IsNumber(subkey1)); + + const cJSON * compkey1 = cJSON_GetObjectItemCaseSensitive(json, "KEY100:SUBKEY1"); + test_assert_double_equal(compkey1->valuedouble, 100); + + // Export again with more values + value_export_append(export_value, "KEY300", "SUBKEY1", 600); + test_assert_int_equal( 6 , value_export_size( export_value )); + value_export_json( export_value ); + std::ifstream f2("path/parameters.json"); + auto const strJSON2 = std::string( + std::istreambuf_iterator(f2), std::istreambuf_iterator()); + test_assert_true(strJSON2.size() > strJSON.size()); + + auto tree = directory_tree("path"); + test_assert_size_t_equal(tree.size(), 2); + std::sort( + std::begin(tree), + std::end(tree), + [](std::string const& l, std::string const& r) -> bool { + return l.size() < r.size(); + }); + test_assert_string_equal(tree[0].c_str(), "path/parameters.json"); + test_assert_string_equal( + tree[1].substr(0, 30).c_str(), + "path/parameters.json_backup_20"); // Fix this in 80 years + + value_export_free( export_value ); +} + + +void test_export_txt() { + ecl::util::TestArea ta("export_txt"); + value_export_type * export_value = value_export_alloc( "path", "parameters"); + util_make_path( "path" ); + + value_export_append(export_value, "KEY100", "SUBKEY1", 100); + value_export_append(export_value, "KEY200", "SUBKEY2", 200); + test_assert_int_equal( 2 , value_export_size( export_value )); + + value_export_txt( export_value ); + test_assert_true( util_file_exists( "path/parameters.txt" )); + + + value_export_txt__( export_value , "path/parameters__.txt"); + test_assert_true( util_file_exists( "path/parameters__.txt" )); + test_assert_true( util_files_equal( "path/parameters__.txt", "path/parameters.txt")); + { + FILE * stream = util_fopen("path/parameters.txt", "r"); + char key1[100],key2[100], subkey1[100], subkey2[100]; + double v1,v2; + + fscanf( stream, "%[^:]:%s %lg %[^:]:%s %lg" , key1, subkey1, &v1, key2, subkey2, &v2); + fclose( stream ); + + test_assert_string_equal( key1, "KEY100"); + test_assert_string_equal( subkey1, "SUBKEY1"); + test_assert_string_equal( key2, "KEY200"); + test_assert_string_equal( subkey2, "SUBKEY2"); + + test_assert_double_equal( v1, 100 ); + test_assert_double_equal( v2, 200 ); + } + + + // Export again with more values + value_export_append(export_value, "KEY300", "SUBKEY1", 600); + test_assert_int_equal( 3 , value_export_size( export_value )); + value_export_txt( export_value ); + + auto tree = directory_tree("path"); + test_assert_size_t_equal(tree.size(), 3); // there is also parameters__.txt + std::sort( + std::begin(tree), + std::end(tree), + [](std::string const& l, std::string const& r) -> bool { + return l.size() < r.size(); + }); + + test_assert_string_equal(tree[0].c_str(), "path/parameters.txt"); + test_assert_string_equal( + tree[2].substr(0, 29).c_str(), + "path/parameters.txt_backup_20"); // Fix this in 80 years + + test_assert_false(util_files_equal(tree[0].c_str(), tree[1].c_str())); + test_assert_true(util_files_equal(tree[2].c_str(), tree[1].c_str())); + value_export_free( export_value ); +} + + + +int main(int argc , char ** argv) { + test_create(); + test_export_txt(); + test_export_json(); + exit(0); +} + diff --git a/libres/lib/enkf/time_map.cpp b/libres/lib/enkf/time_map.cpp new file mode 100644 index 00000000000..7f4f4bfba89 --- /dev/null +++ b/libres/lib/enkf/time_map.cpp @@ -0,0 +1,715 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + The file 'time_map.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 + +#define DEFAULT_TIME -1 + +static time_t time_map_iget__( const time_map_type * map , int step ); +static void time_map_update_abort( time_map_type * map , int step , time_t time); +static void time_map_summary_update_abort( time_map_type * map , const ecl_sum_type * ecl_sum); + +#define TIME_MAP_TYPE_ID 7751432 +struct time_map_struct { + UTIL_TYPE_ID_DECLARATION; + time_t_vector_type * map; + pthread_rwlock_t rw_lock; + bool modified; + bool read_only; + bool strict; + const ecl_sum_type * refcase; +}; + + +UTIL_SAFE_CAST_FUNCTION( time_map , TIME_MAP_TYPE_ID ) +UTIL_IS_INSTANCE_FUNCTION( time_map , TIME_MAP_TYPE_ID ) + + +time_map_type * time_map_alloc( ) { + time_map_type * map = (time_map_type *)util_malloc( sizeof * map ); + UTIL_TYPE_ID_INIT( map , TIME_MAP_TYPE_ID ); + + map->map = time_t_vector_alloc(0 , DEFAULT_TIME ); + map->modified = false; + map->read_only = false; + map->strict = true; + map->refcase = NULL; + pthread_rwlock_init( &map->rw_lock , NULL); + return map; +} + +bool time_map_is_strict( const time_map_type * time_map ){ + return time_map->strict; +} + +/** + The refcase will only be attached if it is consistent with the + current time map; we will accept attaching a refcase which is + shorter than the current case. +*/ +bool time_map_attach_refcase( time_map_type * time_map , const ecl_sum_type * refcase) { + bool attach_ok = true; + pthread_rwlock_rdlock( &time_map->rw_lock ); + + { + int step; + int max_step = util_int_min( time_map_get_size(time_map) , ecl_sum_get_last_report_step( refcase ) + 1); + + for (step = 0; step < max_step; step++) { + time_t current_time = time_map_iget__( time_map , step ); + time_t sim_time = ecl_sum_get_report_time( refcase , step ); + + if (current_time != sim_time) { + /* + The treatment of report step 0 has been fraught with uncertainty. + Report step 0 is not really valid, and previously both the time_map + and the ecl_sum instance have returned -1 as an indication of + undefined value. + + As part of the refactoring when the unsmry_loader was added to the + ecl_sum implementation ecl_sum will return the start date for report + step 0, then this test will fail for existing time_maps created prior + to this change of behaviour. We therefor special case the step 0 here. + + July 2018. + */ + if (step == 0) + continue; + + attach_ok = false; + break; + } + } + + if (attach_ok) + time_map->refcase = refcase; + } + pthread_rwlock_unlock( &time_map->rw_lock ); + + return attach_ok; +} + +bool time_map_has_refcase( const time_map_type * time_map ) { + if (time_map->refcase) + return true; + else + return false; +} + + +void time_map_set_strict( time_map_type * time_map , bool strict) { + time_map->strict = strict; +} + + +time_map_type * time_map_fread_alloc_readonly( const char * filename) { + time_map_type * tm = time_map_alloc(); + + if (util_file_exists(filename)) + time_map_fread( tm , filename ); + tm->read_only = true; + + return tm; +} + + +bool time_map_fscanf(time_map_type * map , const char * filename) { + bool fscanf_ok = true; + if (util_is_file( filename )) { + time_t_vector_type * time_vector = time_t_vector_alloc(0,0); + + { + FILE * stream = util_fopen(filename , "r"); + time_t last_date = 0; + while (true) { + char date_string[128]; + if (fscanf(stream , "%s" , date_string) == 1) { + time_t date; + if (util_sscanf_date_utc(date_string , &date)) { + if (date > last_date) + time_t_vector_append( time_vector , date ); + else { + fprintf(stderr,"** ERROR: The dates in %s must be in stricly increasing order\n",filename); + fscanf_ok = false; + break; + } + } else { + fprintf(stderr,"** ERROR: The string \'%s\' was not correctly parsed as a date (format: DD/MM/YYYY) ",date_string); + fscanf_ok = false; + break; + } + last_date = date; + } else + break; + } + fclose( stream ); + + if (fscanf_ok) { + int i; + time_map_clear( map ); + for (i=0; i < time_t_vector_size( time_vector ); i++) + time_map_update( map , i , time_t_vector_iget( time_vector , i )); + } + + } + time_t_vector_free( time_vector ); + } else + fscanf_ok = false; + + return fscanf_ok; +} + + +bool time_map_equal( const time_map_type * map1 , const time_map_type * map2) { + return time_t_vector_equal( map1->map , map2->map ); +} + + +void time_map_free( time_map_type * map ) { + time_t_vector_free( map->map ); + free( map ); +} + + +bool time_map_is_readonly( const time_map_type * tm) { + return tm->read_only; +} + + + +/** + Must hold the write lock. When a refcase is supplied we gurantee + that all values written into the map agree with the refcase + values. However the time map is not preinitialized with the refcase + values. +*/ + +static bool time_map_update__( time_map_type * map , int step , time_t update_time) { + bool updateOK = true; + time_t current_time = time_t_vector_safe_iget( map->map , step); + + if (current_time == DEFAULT_TIME) { + if (map->refcase) { + if (step <= ecl_sum_get_last_report_step( map->refcase )) { + time_t ref_time = ecl_sum_get_report_time( map->refcase , step ); + + if (ref_time != update_time) { + updateOK = false; + res_log_error("Tried to load data where report step/data is incompatible with refcase - ignored"); + } + } + } + } else if (current_time != update_time) + updateOK = false; + + + if (updateOK) { + map->modified = true; + time_t_vector_iset( map->map , step , update_time ); + } + + return updateOK; +} + + +static bool time_map_summary_update__( time_map_type * map , const ecl_sum_type * ecl_sum) { + bool updateOK = true; + int first_step = ecl_sum_get_first_report_step( ecl_sum ); + int last_step = ecl_sum_get_last_report_step( ecl_sum ); + int step; + + for (step = first_step; step <= last_step; step++) { + if (ecl_sum_has_report_step(ecl_sum , step)) { + time_t sim_time = ecl_sum_get_report_time( ecl_sum , step ); + + updateOK = (updateOK && time_map_update__( map , step , sim_time )); + } + } + + updateOK = (updateOK && time_map_update__(map , 0 , ecl_sum_get_start_time( ecl_sum ))); + return updateOK; +} + + +static time_t time_map_iget__( const time_map_type * map , int step ) { + return time_t_vector_safe_iget( map->map , step ); +} + + +/*****************************************************************/ + +double time_map_iget_sim_days( time_map_type * map , int step ) { + double days; + + pthread_rwlock_rdlock( &map->rw_lock ); + { + time_t start_time = time_map_iget__( map , 0 ); + time_t sim_time = time_map_iget__( map , step ); + + if (sim_time >= start_time) + days = 1.0 * (sim_time - start_time) / (3600 * 24); + else + days = -1; + } + pthread_rwlock_unlock( &map->rw_lock ); + + return days; +} + + +time_t time_map_iget( time_map_type * map , int step ) { + time_t t; + + pthread_rwlock_rdlock( &map->rw_lock ); + t = time_map_iget__( map , step ); + pthread_rwlock_unlock( &map->rw_lock ); + + return t; +} + +static void time_map_assert_writable( const time_map_type * map) { + if (map->read_only) + util_abort("%s: attempt to modify read-only time-map. \n",__func__); +} + + +/** + Observe that the locking is opposite of the function name; i.e. + the time_map_fwrite() function reads the time_map and takes the + read lock, whereas the time_map_fread() function takes the write + lock. +*/ + +void time_map_fwrite( time_map_type * map , const char * filename ) { + pthread_rwlock_rdlock( &map->rw_lock ); + { + if (map->modified) { + FILE * stream = util_mkdir_fopen(filename , "w"); + time_t_vector_fwrite( map->map , stream ); + fclose( stream ); + } + map->modified = false; + } + pthread_rwlock_unlock( &map->rw_lock ); +} + + +void time_map_fread( time_map_type * map , const char * filename) { + time_map_assert_writable( map ); + pthread_rwlock_wrlock( &map->rw_lock ); + { + if (util_file_exists( filename )) { + FILE * stream = util_fopen( filename , "r"); + time_t_vector_type * file_map = time_t_vector_fread_alloc( stream ); + + for (int step=0; step < time_t_vector_size( file_map ); step++) + time_map_update__( map , step , time_t_vector_iget( file_map , step )); + + time_t_vector_free( file_map ); + fclose( stream ); + } + } + pthread_rwlock_unlock( &map->rw_lock ); + time_map_get_last_step( map ); + map->modified = false; +} + + + + + +/* + Observe that the return value from this function is an inclusive + value; i.e. it should be permissible to ask for results at this report + step. +*/ + +int time_map_get_last_step( time_map_type * map) { + int last_step; + + pthread_rwlock_rdlock( &map->rw_lock ); + last_step = time_t_vector_size( map->map ) - 1; + pthread_rwlock_unlock( &map->rw_lock ); + + return last_step; +} + +int time_map_get_size( time_map_type * map) { + return time_map_get_last_step( map ) + 1; +} + +time_t time_map_get_start_time( time_map_type * map) { + return time_map_iget( map , 0 ); +} + + +time_t time_map_get_end_time( time_map_type * map) { + int last_step = time_map_get_last_step( map ); + return time_map_iget( map , last_step ); +} + +double time_map_get_end_days( time_map_type * map) { + int last_step = time_map_get_last_step( map ); + return time_map_iget_sim_days( map , last_step ); +} + +/*****************************************************************/ + + +bool time_map_update( time_map_type * map , int step , time_t time) { + bool updateOK = time_map_try_update( map , step , time ); + if (!updateOK) { + if (map->strict) + time_map_update_abort(map , step , time); + else + res_log_error("Report step/true time inconsistency - data will be ignored"); + } + return updateOK; +} + + +bool time_map_try_update( time_map_type * map , int step , time_t time) { + bool updateOK; + time_map_assert_writable( map ); + pthread_rwlock_wrlock( &map->rw_lock ); + { + updateOK = time_map_update__( map , step , time ); + } + pthread_rwlock_unlock( &map->rw_lock ); + return updateOK; +} + + + +bool time_map_summary_update( time_map_type * map , const ecl_sum_type * ecl_sum) { + bool updateOK = time_map_try_summary_update( map , ecl_sum ); + + if (!updateOK) { + if (map->strict) + time_map_summary_update_abort( map , ecl_sum ); + else + res_log_error("Report step/true time inconsistency - data will be ignored"); + } + + return updateOK; +} + + +bool time_map_try_summary_update( time_map_type * map , const ecl_sum_type * ecl_sum) { + bool updateOK; + + time_map_assert_writable( map ); + pthread_rwlock_wrlock( &map->rw_lock ); + { + updateOK = time_map_summary_update__( map , ecl_sum ); + } + pthread_rwlock_unlock( &map->rw_lock ); + + return updateOK; +} + + +int time_map_lookup_time( time_map_type * map , time_t time) { + int index = -1; + pthread_rwlock_rdlock( &map->rw_lock ); + { + int current_index = 0; + while (true) { + if (current_index >= time_t_vector_size( map->map )) + break; + + if (time_map_iget__( map , current_index ) == time) { + index = current_index; + break; + } + + current_index++; + } + } + pthread_rwlock_unlock( &map->rw_lock ); + return index; +} + +static bool time_map_valid_time__(const time_map_type * map , time_t time) { + if (time_t_vector_size( map->map ) > 0) { + if ((time >= time_map_iget__(map , 0)) && + (time <= time_map_iget__(map , time_t_vector_size( map->map ) - 1))) + return true; + else + return false; + } else + return false; +} + + + +int time_map_lookup_time_with_tolerance( time_map_type * map , time_t time , int seconds_before_tolerance, int seconds_after_tolerance) { + int nearest_index = -1; + pthread_rwlock_rdlock( &map->rw_lock ); + { + if (time_map_valid_time__( map , time )) { + time_t nearest_diff = 999999999999; + int current_index = 0; + while (true) { + time_t diff = time - time_map_iget__( map , current_index ); + if (diff == 0) { + nearest_index = current_index; + break; + } + + if (std::fabs(diff) < nearest_diff) { + bool inside_tolerance = true; + if (seconds_after_tolerance >= 0) { + if (diff >= seconds_after_tolerance) + inside_tolerance = false; + } + + if (seconds_before_tolerance >= 0) { + if (diff <= -seconds_before_tolerance) + inside_tolerance = false; + } + + if (inside_tolerance) { + nearest_diff = diff; + nearest_index = current_index; + } + } + + current_index++; + + if (current_index >= time_t_vector_size( map->map )) + break; + } + } + } + pthread_rwlock_unlock( &map->rw_lock ); + return nearest_index; +} + + + +int time_map_lookup_days( time_map_type * map , double sim_days) { + int index = -1; + pthread_rwlock_rdlock( &map->rw_lock ); + { + if (time_t_vector_size( map->map ) > 0) { + time_t time = time_map_iget__(map , 0 ); + util_inplace_forward_days_utc( &time , sim_days ); + index = time_map_lookup_time( map , time ); + } + } + pthread_rwlock_unlock( &map->rw_lock ); + return index; +} + + +void time_map_clear( time_map_type * map ) { + time_map_assert_writable( map ); + pthread_rwlock_wrlock( &map->rw_lock ); + { + time_t_vector_reset( map->map ); + map->modified = true; + } + pthread_rwlock_unlock( &map->rw_lock ); +} + + +/* + This is a function specifically written to upgrade an on-disk + time_map which is using localtime (fs_version <= 106) to a utc based + time_map (fs_version >= 107). +*/ + +void time_map_summary_upgrade107( time_map_type * map , const ecl_sum_type * ecl_sum) { + int first_step = ecl_sum_get_first_report_step( ecl_sum ); + int last_step = ecl_sum_get_last_report_step( ecl_sum ); + + time_t_vector_resize( map->map , last_step + 1, DEFAULT_TIME); + time_t_vector_iset_block( map->map , 0 , first_step , DEFAULT_TIME); + for (int step=first_step; step <= last_step; step++) { + if (ecl_sum_has_report_step(ecl_sum , step)) { + time_t sim_time = ecl_sum_get_report_time( ecl_sum , step ); + time_t_vector_iset( map->map , step , sim_time); + } + } + map->modified = true; +} + + +/*****************************************************************/ + +static void time_map_update_abort( time_map_type * map , int step , time_t time) { + time_t current_time = time_map_iget__( map , step ); + int current[3]; + int new_time[3]; + + util_set_date_values_utc( current_time , ¤t[0] , ¤t[1] , ¤t[2]); + util_set_date_values_utc( time , &new_time[0] , &new_time[1] , &new_time[2]); + + util_abort("%s: time mismatch for step:%d New_Time: %02d/%02d/%04d existing: %02d/%02d/%04d \n",__func__ , step , + new_time[0], new_time[1], new_time[2] , + current[0] , current[1] , current[2]); +} + + +static void time_map_summary_update_abort( time_map_type * map , const ecl_sum_type * ecl_sum) { + /* + If the normal summary update fails we just play through all + time steps to pinpoint exactly the step where the update fails. + */ + + int first_step = ecl_sum_get_first_report_step( ecl_sum ); + int last_step = ecl_sum_get_last_report_step( ecl_sum ); + int step; + + for (step = first_step; step <= last_step; step++) { + if (ecl_sum_has_report_step(ecl_sum , step)) { + time_t time = ecl_sum_get_report_time( ecl_sum , step ); + + if (map->refcase) { + if (ecl_sum_get_last_report_step( ecl_sum ) >= step) { + time_t ref_time = ecl_sum_get_report_time( map->refcase , step ); + if (ref_time != time) { + int ref[3]; + int new_time[3]; + + util_set_date_values_utc( time , &new_time[0] , &new_time[1] , &new_time[2]); + util_set_date_values_utc( ref_time , &ref[0] , &ref[1] , &ref[2]); + + fprintf(stderr," Time mismatch for step:%d New_Time: %02d/%02d/%04d refcase: %02d/%02d/%04d \n", step , + new_time[0] , new_time[1] , new_time[2] , + ref[0] , ref[1] , ref[2]); + } + } + } + + { + time_t current_time = time_map_iget__( map , step ); + if (current_time != time) { + int current[3]; + int new_time[3]; + + util_set_date_values_utc( current_time , ¤t[0] , ¤t[1] , ¤t[2]); + util_set_date_values_utc( time , &new_time[0] , &new_time[1] , &new_time[2]); + + fprintf(stderr,"Time mismatch for step:%d New_Time: %02d/%02d/%04d existing: %02d/%02d/%04d \n",step , + new_time[0] , new_time[1] , new_time[2] , + current[0] , current[1] , current[2]); + } + } + } + } + + util_abort("%s: inconsistency when updating time map \n",__func__); +} + + + +/*****************************************************************/ + + +/* + This function creates an integer index mapping from the time map + into the summary case. In general the time <-> report step mapping + of the summary data should coincide exactly with the one maintained + in the time_map, however we allow extra timesteps in the summary + instance. The extra timesteps will be ignored, holes in the summary + timestep is not allowed - that will lead to a hard crash. + + time map Summary + ------------------------------------------------- + 0: 01/01/2000 <------- 0: 01/01/2000 + + 1: 01/02/2000 <------- 1: 01/02/2000 + + 2: 01/03/2000 <-\ 2: 02/02/2000 (Ignored) + \ + \-- 3: 01/03/2000 + + 3: 01/04/2000 <------- 4: 01/04/2000 + + + index_map = { 0 , 1 , 3 , 4 } + + Observe that the time_map_update_summary() must be called prior to + calling this function, to ensure that the time_map is sufficiently + long. If timesteps are missing from the summary case we crash hard: + + + time map Summary + ------------------------------------------------- + 0: 01/01/2000 <------- 0: 01/01/2000 + + 1: 01/02/2000 <------- 1: 01/02/2000 + + 2: 01/03/2000 ## ERROR -> util_abort() + + 3: 01/04/2000 <------- 2: 01/04/2000 + +*/ + + + +int_vector_type * time_map_alloc_index_map( time_map_type * map , const ecl_sum_type * ecl_sum ) { + int_vector_type * index_map = int_vector_alloc(0, -1); + pthread_rwlock_rdlock( &map->rw_lock ); + + int sum_index = ecl_sum_get_first_report_step(ecl_sum); + int time_map_index = ecl_sum_get_first_report_step(ecl_sum); + for (; time_map_index < time_map_get_size(map); ++time_map_index) { + time_t map_time = time_map_iget__(map, time_map_index); + if (map_time == DEFAULT_TIME) + continue; + + for(; sum_index <= ecl_sum_get_last_report_step(ecl_sum); ++sum_index) { + time_t sum_time = ecl_sum_get_report_time(ecl_sum, sum_index); + if (sum_time == map_time) + break; + + if (sum_time > map_time) { + int day, month, year; + util_set_date_values_utc(map_time, &day, &month, &year); + util_abort("%s: The eclipse summary cases is missing data for date:%02d/%02d/%4d - aborting\n", __func__ , day , month , year); + } + } + + if(sum_index > ecl_sum_get_last_report_step(ecl_sum)) { + res_log_error("Inconsistency in time_map - data will be ignored"); + break; + } + + int_vector_iset(index_map, time_map_index, sum_index); + + } + + pthread_rwlock_unlock( &map->rw_lock ); + return index_map; +} + + + diff --git a/libres/lib/enkf/trans_errf.m b/libres/lib/enkf/trans_errf.m new file mode 100644 index 00000000000..f88965d8ad8 --- /dev/null +++ b/libres/lib/enkf/trans_errf.m @@ -0,0 +1,3 @@ +function y = trans_errf(x , min , max , skewness , width) + +y = min + (max - min) * 0.5*(1 + erf((x + skewness)/(width * sqrt(2.0)))); diff --git a/libres/lib/enkf/trans_func.cpp b/libres/lib/enkf/trans_func.cpp new file mode 100644 index 00000000000..09ea989af6e --- /dev/null +++ b/libres/lib/enkf/trans_func.cpp @@ -0,0 +1,359 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'trans_func.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 + + + + + + +struct trans_func_struct { + char * name; /* The name this function is registered as. */ + double_vector_type * params; /* The parameter values registered for this function. */ + transform_ftype * func; /* A pointer to the actual transformation function. */ + validate_ftype * validate; /* A pointer to a a function which can be used to validate the parameters - can be NULL. */ + stringlist_type * param_names; /* A list of the parameter names. */ + bool use_log; +}; + + + + + + +/** + Width = 1 => uniform + Width > 1 => unimodal peaked + Width < 1 => bimoal peaks + + + Skewness < 0 => shifts towards the left + Skewness = 0 => symmetric + Skewness > 0 => Shifts towards the right + + The width is a relavant scale for the value of skewness. +*/ + +static double trans_errf(double x, const double_vector_type * arg) { + double min = double_vector_iget(arg , 0); + double max = double_vector_iget(arg , 1); + double skewness = double_vector_iget(arg , 2); + double width = double_vector_iget(arg , 3); + double y; + + y = 0.5*(1 + erf((x + skewness)/(width * sqrt(2.0)))); + return min + y * (max - min); +} + + + + +static double trans_const(double x , const double_vector_type * arg) { + return double_vector_iget(arg , 0); +} + + +static double trans_raw(double x , const double_vector_type * arg) { + return x; +} + + + +/* Observe that the argument of the shift should be "+" */ +static double trans_derrf(double x , const double_vector_type * arg) { + int steps = double_vector_iget(arg , 0); + double min = double_vector_iget(arg , 1); + double max = double_vector_iget(arg , 2); + double skewness = double_vector_iget(arg , 3); + double width = double_vector_iget(arg , 4); + double y; + + y = floor( steps * 0.5*(1 + erf((x + skewness)/(width * sqrt(2.0)))) / (steps - 1) ); + return min + y * (max - min); +} + + + + + +static double trans_unif(double x , const double_vector_type * arg) { + double y; + double min = double_vector_iget(arg , 0); + double max = double_vector_iget(arg , 1); + y = 0.5*(1 + erf(x/sqrt(2.0))); /* 0 - 1 */ + return y * (max - min) + min; +} + + + +static double trans_dunif(double x , const double_vector_type * arg) { + double y; + int steps = double_vector_iget(arg , 0); + double min = double_vector_iget(arg , 1); + double max = double_vector_iget(arg , 2); + + y = 0.5*(1 + erf(x/sqrt(2.0))); /* 0 - 1 */ + return (floor( y * steps) / (steps - 1)) * (max - min) + min; +} + + + + +static double trans_normal(double x , const double_vector_type * arg) { + double mu , std; + mu = double_vector_iget(arg , 0 ); + std = double_vector_iget(arg , 1 ); + return x * std + mu; +} + + +static double trans_truncated_normal(double x , const double_vector_type * arg) { + double mu , std , min , max; + + mu = double_vector_iget(arg , 0 ); + std = double_vector_iget(arg , 1 ); + min = double_vector_iget(arg , 2 ); + max = double_vector_iget(arg , 3 ); + + { + double y = x * std + mu; + util_clamp_double( &y , min , max ); + return y; + } +} + + + + +static double trans_lognormal(double x, const double_vector_type * arg) { + double mu, std; + mu = double_vector_iget(arg , 0 ); /* The expectation of log( y ) */ + std = double_vector_iget(arg , 1 ); + return exp(x * std + mu); +} + + + +/** + Used to sample values between min and max - BUT it is the logarithm + of y which is uniformly distributed. Relates to the uniform + distribution in the same manner as the lognormal distribution + relates to the normal distribution. +*/ +static double trans_logunif(double x , const double_vector_type * arg) { + double log_min = log(double_vector_iget(arg , 0)); + double log_max = log(double_vector_iget(arg , 1)); + double log_y; + { + double tmp = 0.5*(1 + erf(x/sqrt(2.0))); /* 0 - 1 */ + log_y = log_min + tmp * (log_max - log_min); /* Shift according to max / min */ + } + return exp(log_y); +} + + +static double trans_triangular(double x, const double_vector_type * arg) { + double xmin = double_vector_iget(arg, 0); + double xmode = double_vector_iget(arg, 1); + double xmax = double_vector_iget(arg,2); + + double inv_norm_left = (xmax - xmin) * (xmode - xmin); + double inv_norm_right = (xmax - xmin) * (xmax - xmode); + double ymode = (xmode - xmin) / (xmax - xmin); + double y = 0.5*(1 + erf(x/sqrt(2.0))); /* 0 - 1 */ + + if (y < ymode) + return xmin + sqrt(y * inv_norm_left); + else + return xmax - sqrt((1 - y)*inv_norm_right); +} + + +/*****************************************************************/ + + +void trans_func_free( trans_func_type * trans_func ) { + stringlist_free( trans_func->param_names ); + double_vector_free( trans_func->params ); + free( trans_func->name ); + free( trans_func ); +} + + +static trans_func_type * trans_func_alloc_empty( const char * func_name) { + trans_func_type * trans_func = (trans_func_type *)util_malloc( sizeof * trans_func ); + + + trans_func->params = double_vector_alloc(0,0); + trans_func->func = NULL; + trans_func->validate = NULL; + trans_func->name = util_alloc_string_copy( func_name ); + trans_func->param_names = stringlist_alloc_new(); + trans_func->use_log = false; + + return trans_func; +} + + + +trans_func_type * trans_func_alloc( const stringlist_type * args ) { + const char * func_name = stringlist_iget(args, 0); + trans_func_type * trans_func = trans_func_alloc_empty(func_name); + + if (util_string_equal(func_name , "NORMAL")) { + stringlist_append_copy( trans_func->param_names , "MEAN"); + stringlist_append_copy( trans_func->param_names , "STD" ); + trans_func->func = trans_normal; + } + + if (util_string_equal( func_name , "LOGNORMAL")) { + stringlist_append_copy( trans_func->param_names , "MEAN"); + stringlist_append_copy( trans_func->param_names , "STD" ); + trans_func->func = trans_lognormal; + trans_func->use_log = true; + } + + if (util_string_equal( func_name , "TRUNCATED_NORMAL")) { + stringlist_append_copy( trans_func->param_names , "MEAN"); + stringlist_append_copy( trans_func->param_names , "STD" ); + stringlist_append_copy( trans_func->param_names , "MIN"); + stringlist_append_copy( trans_func->param_names , "MAX" ); + + trans_func->func = trans_truncated_normal; + } + + if (util_string_equal(func_name, "TRIANGULAR")) { + stringlist_append_copy( trans_func->param_names, "XMIN"); + stringlist_append_copy( trans_func->param_names, "XMODE"); + stringlist_append_copy( trans_func->param_names, "XMAX"); + + trans_func->func = trans_triangular; + } + + if (util_string_equal( func_name , "UNIFORM")) { + stringlist_append_copy( trans_func->param_names , "MIN"); + stringlist_append_copy( trans_func->param_names , "MAX" ); + trans_func->func = trans_unif; + } + + + if (util_string_equal( func_name , "DUNIF")) { + stringlist_append_copy( trans_func->param_names , "STEPS"); + stringlist_append_copy( trans_func->param_names , "MIN"); + stringlist_append_copy( trans_func->param_names , "MAX" ); + + trans_func->func = trans_dunif; + } + + + if (util_string_equal( func_name , "ERRF")) { + stringlist_append_copy( trans_func->param_names , "MIN"); + stringlist_append_copy( trans_func->param_names , "MAX" ); + stringlist_append_copy( trans_func->param_names , "SKEWNESS"); + stringlist_append_copy( trans_func->param_names , "WIDTH" ); + + trans_func->func = trans_errf; + } + + + if (util_string_equal( func_name , "DERRF")) { + stringlist_append_copy( trans_func->param_names , "STEPS"); + stringlist_append_copy( trans_func->param_names , "MIN"); + stringlist_append_copy( trans_func->param_names , "MAX" ); + stringlist_append_copy( trans_func->param_names , "SKEWNESS"); + stringlist_append_copy( trans_func->param_names , "WIDTH" ); + + trans_func->func = trans_derrf; + } + + + if (util_string_equal( func_name , "LOGUNIF")) { + stringlist_append_copy( trans_func->param_names , "MIN"); + stringlist_append_copy( trans_func->param_names , "MAX" ); + + trans_func->func = trans_logunif; + trans_func->use_log = true; + } + + + if (util_string_equal( func_name , "CONST")) { + stringlist_append_copy( trans_func->param_names , "VALUE"); + trans_func->func = trans_const; + } + + + if (util_string_equal( func_name , "RAW")) + trans_func->func = trans_raw; + + + /* Parsing parameter values. */ + + if (!trans_func->func) { + trans_func_free( trans_func ); + return NULL; + } + + if (stringlist_get_size(args) - stringlist_get_size(trans_func->param_names) != 1) { + trans_func_free( trans_func ); + return NULL; + } + + for (int iarg=0; iarg < stringlist_get_size(trans_func->param_names); iarg++) { + double param_value; + + if (util_sscanf_double(stringlist_iget(args, iarg + 1), ¶m_value)) + double_vector_append(trans_func->params, param_value); + else { + fprintf(stderr,"%s: could not parse: %s as floating point value\n",__func__, stringlist_iget(args,iarg + 1)); + trans_func_free(trans_func); + return NULL; + } + } + + return trans_func; +} + + + +double trans_func_eval( const trans_func_type * trans_func , double x) { + double y = trans_func->func( x , trans_func->params ); + return y; +} + +bool trans_func_use_log_scale(const trans_func_type * trans_func) { + return trans_func->use_log; +} + +stringlist_type * trans_func_get_param_names(const trans_func_type * trans_func){ + return trans_func->param_names; +} +double_vector_type * trans_func_get_params(const trans_func_type * trans_func){ + return trans_func->params; +} +const char * trans_func_get_name(const trans_func_type * trans_func){ + return trans_func->name; +} diff --git a/libres/lib/enkf/trans_test.m b/libres/lib/enkf/trans_test.m new file mode 100644 index 00000000000..83ff575003e --- /dev/null +++ b/libres/lib/enkf/trans_test.m @@ -0,0 +1,12 @@ +%% This is matlab file used to test/vizualize the various +%% distributions in trans_func.c + +N = 100000; +x = random('normal',0 , 1 , N ,1); + + + +y = trans_errf(x , 0.1 , 15 , -3 , 2.0); + +disp(sprintf(' = %g' , mean(y))); +hist(y , sqrt(N)) diff --git a/libres/lib/enkf/value_export.cpp b/libres/lib/enkf/value_export.cpp new file mode 100644 index 00000000000..6d64294150b --- /dev/null +++ b/libres/lib/enkf/value_export.cpp @@ -0,0 +1,223 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'value_export.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 + +#define VALUE_EXPORT_TYPE_ID 5741761 + + +struct value_export_struct { + UTIL_TYPE_ID_DECLARATION; + std::string directory; + std::string base_name; + std::map> values; + +}; + + +static void backup_if_existing(const char * filename) { + if(not util_file_exists(filename)) + return; + + auto time_to_string = [](const std::tm* tmb, const char* fmt) -> std::string { + char buffer[256]; // typically enough for time and date + auto const count = strftime(buffer, sizeof(buffer), fmt, tmb); + if(count < 0) + return ""; + return std::string(buffer, count); + }; + + auto const backup_filename = [filename, &time_to_string]() { + auto const tt = std::chrono::system_clock::to_time_t( + std::chrono::system_clock::now()); + auto constexpr format = "%Y-%m-%d_%H-%M-%SZ"; + std::stringstream fname_stream; + fname_stream + << filename + << "_backup_" + << time_to_string(gmtime(&tt), format); + for(int i = 0; + util_file_exists(fname_stream.str().c_str()) && i < 100; + ++i + ) { + fname_stream.clear(); + fname_stream + << filename + << "_backup_" + << time_to_string(gmtime(&tt), format) + << "_" + << i; + } + return fname_stream.str(); + }(); + util_move_file(filename, backup_filename.c_str()); +} + + +value_export_type * value_export_alloc(std::string directory, std::string base_name) { + + value_export_type * value = new value_export_type; + UTIL_TYPE_ID_INIT( value , VALUE_EXPORT_TYPE_ID ); + value->directory = directory; + value->base_name = base_name; + return value; + +} + + +void value_export_free(value_export_type * value) { + delete value; +} + +int value_export_size( const value_export_type * value) { + int size = 0; + for(const auto& key_map_pair:value->values) + { + size += key_map_pair.second.size(); + } + + return size; +} + + +void value_export_txt__(const value_export_type * value, const char * filename) { + + if (!value->values.empty()) { + FILE * stream = util_fopen( filename , "w"); + for (const auto & key_map_pair: value->values) { + for (const auto &sub_key_value_pair : key_map_pair.second) { + fprintf(stream, "%s:%s %g\n", key_map_pair.first.c_str(), sub_key_value_pair.first.c_str(), sub_key_value_pair.second); + } + } + fclose( stream ); + } +} + +void value_export_txt(const value_export_type * value) { + std::string filename = value->directory +"/" + value->base_name + ".txt"; + backup_if_existing(filename.c_str()); + value_export_txt__( value, filename.c_str() ); + +} + +static void generate_hirarchical_keys(const value_export_type * value, FILE * stream) +{ + for (auto iterMaps = value->values.begin(); iterMaps!= value->values.end(); ++iterMaps ) { + std::string key = (*iterMaps).first; + std::map subMap = (*iterMaps).second; + fprintf(stream, "\"%s\" : {\n", key.c_str()); + + for (auto iterValues = subMap.begin(); iterValues != subMap.end(); ++iterValues) { + + std::string subkey = (*iterValues).first; + + double double_value = (*iterValues).second; + if (std::isnan(double_value)) + fprintf(stream, "\"%s\" : NaN", subkey.c_str()); + else + fprintf(stream, "\"%s\" : %g", subkey.c_str(), double_value); + + + if (std::next(iterValues) != subMap.end()) + fprintf(stream, ","); + fprintf(stream, "\n"); + } + + fprintf(stream, "},\n"); + + } + +} + +static void generate_comosite_keys(const value_export_type * value, FILE * stream) +{ + for (auto iterMaps = value->values.begin(); iterMaps!= value->values.end(); ++iterMaps ) { + std::string key = (*iterMaps).first; + std::map subMap = (*iterMaps).second; + + for (auto iterValues = subMap.begin(); iterValues != subMap.end(); ++iterValues) { + + std::string subkey = (*iterValues).first; + + double double_value = (*iterValues).second; + if (std::isnan(double_value)) + fprintf(stream, "\"%s\" : NaN", key.c_str()); + else + fprintf(stream, "\"%s:%s\" : %g", key.c_str(), subkey.c_str(), double_value); + + if (std::next(iterValues) != subMap.end()) { + fprintf(stream, ","); + fprintf(stream, "\n"); + } + } + + + if (std::next(iterMaps) != value->values.end()) + fprintf(stream, ","); + + fprintf(stream, "\n"); + + } + +} + +void value_export_json(const value_export_type * value) { + std::string filename = value->directory +"/" + value->base_name + ".json"; + backup_if_existing(filename.c_str()); + + if (!value->values.empty()) { + FILE * stream = util_fopen( filename.c_str() , "w"); + fprintf(stream, "{\n"); + generate_hirarchical_keys(value, stream); + generate_comosite_keys(value, stream); + fprintf(stream, "}\n"); + fclose( stream ); + } + +} + +void value_export(const value_export_type * value) { + value_export_txt( value ); + value_export_json( value ); +} + + +void value_export_append(value_export_type * value, const std::string key, const std::string subkey, double double_value){ + + if(value->values.find(key) == value->values.end()){ + value->values[key] = std::map(); + } + + value->values[key][subkey] = double_value; +} + +/*****************************************************************/ + +UTIL_IS_INSTANCE_FUNCTION( value_export , VALUE_EXPORT_TYPE_ID ) diff --git a/libres/lib/external/JSON/cJSON.c b/libres/lib/external/JSON/cJSON.c new file mode 100644 index 00000000000..e29d27a1334 --- /dev/null +++ b/libres/lib/external/JSON/cJSON.c @@ -0,0 +1,2932 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning (push) +/* disable warning about single line comments in system headers */ +#pragma warning (disable : 4001) +#endif + +#include +#include +#include +#include +#include + +#ifdef ENABLE_LOCALES +#include +#endif + +#if defined(_MSC_VER) +#pragma warning (pop) +#endif +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + +#include "cJSON.h" + +/* define our own boolean type */ +#define true ((cJSON_bool)1) +#define false ((cJSON_bool)0) + +typedef struct { + const unsigned char *json; + size_t position; +} error; +static error global_error = { NULL, 0 }; + +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) +{ + return (const char*) (global_error.json + global_error.position); +} + +CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item) { + if (!cJSON_IsString(item)) { + return NULL; + } + + return item->valuestring; +} + +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 8) + #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif + +CJSON_PUBLIC(const char*) cJSON_Version(void) +{ + static char version[15]; + sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); + + return version; +} + +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) +{ + if ((string1 == NULL) || (string2 == NULL)) + { + return 1; + } + + if (string1 == string2) + { + return 0; + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +typedef struct internal_hooks +{ + void *(CJSON_CDECL *allocate)(size_t size); + void (CJSON_CDECL *deallocate)(void *pointer); + void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); +} internal_hooks; + +#if defined(_MSC_VER) +/* work around MSVC error C2322: '...' address of dillimport '...' is not static */ +static void * CJSON_CDECL internal_malloc(size_t size) +{ + return malloc(size); +} +static void CJSON_CDECL internal_free(void *pointer) +{ + free(pointer); +} +static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) +{ + return realloc(pointer, size); +} +#else +#define internal_malloc malloc +#define internal_free free +#define internal_realloc realloc +#endif + +static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; + +static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) +{ + size_t length = 0; + unsigned char *copy = NULL; + + if (string == NULL) + { + return NULL; + } + + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*)hooks->allocate(length); + if (copy == NULL) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (hooks == NULL) + { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; + return; + } + + global_hooks.allocate = malloc; + if (hooks->malloc_fn != NULL) + { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if (hooks->free_fn != NULL) + { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) + { + global_hooks.reallocate = realloc; + } +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(const internal_hooks * const hooks) +{ + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if (node) + { + memset(node, '\0', sizeof(cJSON)); + } + + return node; +} + +/* Delete a cJSON structure. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) +{ + cJSON *next = NULL; + while (item != NULL) + { + next = item->next; + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) + { + cJSON_Delete(item->child); + } + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) + { + global_hooks.deallocate(item->valuestring); + } + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + global_hooks.deallocate(item->string); + } + global_hooks.deallocate(item); + item = next; + } +} + +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) +{ +#ifdef ENABLE_LOCALES + struct lconv *lconv = localeconv(); + return (unsigned char) lconv->decimal_point[0]; +#else + return '.'; +#endif +} + +typedef struct +{ + const unsigned char *content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) +{ + double number = 0; + unsigned char *after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for strtod) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) + { + switch (buffer_at_offset(input_buffer)[i]) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + number = strtod((const char*)number_c_string, (char**)&after_end); + if (number_c_string == after_end) + { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if (number >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (number <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)number; + } + + item->type = cJSON_Number; + + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; +} + +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) +{ + if (number >= INT_MAX) + { + object->valueint = INT_MAX; + } + else if (number <= (double)INT_MIN) + { + object->valueint = INT_MIN; + } + else + { + object->valueint = (int)number; + } + + return object->valuedouble = number; +} + +typedef struct +{ + unsigned char *buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; +} printbuffer; + +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char* ensure(printbuffer * const p, size_t needed) +{ + unsigned char *newbuffer = NULL; + size_t newsize = 0; + + if ((p == NULL) || (p->buffer == NULL)) + { + return NULL; + } + + if ((p->length > 0) && (p->offset >= p->length)) + { + /* make sure that offset is valid */ + return NULL; + } + + if (needed > INT_MAX) + { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if (needed <= p->length) + { + return p->buffer + p->offset; + } + + if (p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if (needed > (INT_MAX / 2)) + { + /* overflow of int, use INT_MAX if possible */ + if (needed <= INT_MAX) + { + newsize = INT_MAX; + } + else + { + return NULL; + } + } + else + { + newsize = needed * 2; + } + + if (p->hooks.reallocate != NULL) + { + /* reallocate with realloc if available */ + newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + if (newbuffer == NULL) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + } + else + { + /* otherwise reallocate manually */ + newbuffer = (unsigned char*)p->hooks.allocate(newsize); + if (!newbuffer) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + if (newbuffer) + { + memcpy(newbuffer, p->buffer, p->offset + 1); + } + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; +} + +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer * const buffer) +{ + const unsigned char *buffer_pointer = NULL; + if ((buffer == NULL) || (buffer->buffer == NULL)) + { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*)buffer_pointer); +} + +/* Render the number nicely from the given item into a string. */ +static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26]; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test; + + if (output_buffer == NULL) + { + return false; + } + + /* This checks for NaN and Infinity */ + if ((d * 0) != 0) + { + length = sprintf((char*)number_buffer, "null"); + } + else + { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = sprintf((char*)number_buffer, "%1.15g", d); + + /* Check whether the original double can be recovered */ + if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || ((double)test != d)) + { + /* If not, print with 17 decimal places of precision */ + length = sprintf((char*)number_buffer, "%1.17g", d); + } + } + + /* sprintf failed or buffer overrun occured */ + if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) + { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); + if (output_pointer == NULL) + { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for (i = 0; i < ((size_t)length); i++) + { + if (number_buffer[i] == decimal_point) + { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; +} + +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char * const input) +{ + unsigned int h = 0; + size_t i = 0; + + for (i = 0; i < 4; i++) + { + /* parse digit */ + if ((input[i] >= '0') && (input[i] <= '9')) + { + h += (unsigned int) input[i] - '0'; + } + else if ((input[i] >= 'A') && (input[i] <= 'F')) + { + h += (unsigned int) 10 + input[i] - 'A'; + } + else if ((input[i] >= 'a') && (input[i] <= 'f')) + { + h += (unsigned int) 10 + input[i] - 'a'; + } + else /* invalid */ + { + return 0; + } + + if (i < 3) + { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; +} + +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) +{ + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if ((input_end - first_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); + + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) + { + goto fail; + } + + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) + { + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if ((input_end - second_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) + { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) + { + /* invalid second half of the surrogate pair */ + goto fail; + } + + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } + else + { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) + { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } + else if (codepoint < 0x800) + { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } + else if (codepoint < 0x10000) + { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } + else if (codepoint <= 0x10FFFF) + { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } + else + { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) + { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if (utf8_length > 1) + { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } + else + { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) +{ + const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; + + /* not a string */ + if (buffer_at_offset(input_buffer)[0] != '\"') + { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) + { + /* is escape sequence */ + if (input_end[0] == '\\') + { + if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) + { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) + { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if (output == NULL) + { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while (input_pointer < input_end) + { + if (*input_pointer != '\\') + { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else + { + unsigned char sequence_length = 2; + if ((input_end - input_pointer) < 1) + { + goto fail; + } + + switch (input_pointer[1]) + { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if (sequence_length == 0) + { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*)output; + + input_buffer->offset = (size_t) (input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if (output != NULL) + { + input_buffer->hooks.deallocate(output); + } + + if (input_pointer != NULL) + { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) +{ + const unsigned char *input_pointer = NULL; + unsigned char *output = NULL; + unsigned char *output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if (output_buffer == NULL) + { + return false; + } + + /* empty string */ + if (input == NULL) + { + output = ensure(output_buffer, sizeof("\"\"")); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "\"\""); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for (input_pointer = input; *input_pointer; input_pointer++) + { + switch (*input_pointer) + { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) + { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if (output == NULL) + { + return false; + } + + /* no characters have to be escaped */ + if (escape_characters == 0) + { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) + { + if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) + { + /* normal character, copy */ + *output_pointer = *input_pointer; + } + else + { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch (*input_pointer) + { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + sprintf((char*)output_pointer, "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; +} + +/* Invoke print_string_ptr (which is useful) on an item. */ +static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) +{ + return print_string_ptr((unsigned char*)item->valuestring, p); +} + +/* Predeclare these prototypes. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); + +/* Utility to jump whitespace and cr/lf */ +static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL)) + { + return NULL; + } + + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) + { + buffer->offset++; + } + + if (buffer->offset == buffer->length) + { + buffer->offset--; + } + + return buffer; +} + +/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ +static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) + { + return NULL; + } + + if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) + { + buffer->offset += 3; + } + + return buffer; +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + cJSON *item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if (value == NULL) + { + goto fail; + } + + buffer.content = (const unsigned char*)value; + buffer.length = strlen((const char*)value) + sizeof(""); + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if (item == NULL) /* memory fail */ + { + goto fail; + } + + if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) + { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) + { + buffer_skip_whitespace(&buffer); + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') + { + goto fail; + } + } + if (return_parse_end) + { + *return_parse_end = (const char*)buffer_at_offset(&buffer); + } + + return item; + +fail: + if (item != NULL) + { + cJSON_Delete(item); + } + + if (value != NULL) + { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if (buffer.offset < buffer.length) + { + local_error.position = buffer.offset; + } + else if (buffer.length > 0) + { + local_error.position = buffer.length - 1; + } + + if (return_parse_end != NULL) + { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + + global_error = local_error; + } + + return NULL; +} + +/* Default options for cJSON_Parse */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) +{ + return cJSON_ParseWithOpts(value, 0, 0); +} + +#define cjson_min(a, b) ((a < b) ? a : b) + +static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) +{ + static const size_t default_buffer_size = 256; + printbuffer buffer[1]; + unsigned char *printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); + buffer->length = default_buffer_size; + buffer->format = format; + buffer->hooks = *hooks; + if (buffer->buffer == NULL) + { + goto fail; + } + + /* print the value */ + if (!print_value(item, buffer)) + { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if (hooks->reallocate != NULL) + { + printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); + if (printed == NULL) { + goto fail; + } + buffer->buffer = NULL; + } + else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char*) hooks->allocate(buffer->offset + 1); + if (printed == NULL) + { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + } + + return printed; + +fail: + if (buffer->buffer != NULL) + { + hooks->deallocate(buffer->buffer); + } + + if (printed != NULL) + { + hooks->deallocate(printed); + } + + return NULL; +} + +/* Render a cJSON item/entity/structure to text. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) +{ + return (char*)print(item, true, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) +{ + return (char*)print(item, false, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if (prebuffer < 0) + { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if (!p.buffer) + { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if (!print_value(item, &p)) + { + global_hooks.deallocate(p.buffer); + return NULL; + } + + return (char*)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if ((len < 0) || (buf == NULL)) + { + return false; + } + + p.buffer = (unsigned char*)buf; + p.length = (size_t)len; + p.offset = 0; + p.noalloc = true; + p.format = fmt; + p.hooks = global_hooks; + + return print_value(item, &p); +} + +/* Parser core - when encountering text, process appropriately. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) +{ + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) + { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) + { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) + { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) + { + return parse_string(item, input_buffer); + } + /* number */ + if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) + { + return parse_number(item, input_buffer); + } + /* array */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) + { + return parse_array(item, input_buffer); + } + /* object */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) + { + return parse_object(item, input_buffer); + } + + return false; +} + +/* Render a value to text. */ +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output = NULL; + + if ((item == NULL) || (output_buffer == NULL)) + { + return false; + } + + switch ((item->type) & 0xFF) + { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: + { + size_t raw_length = 0; + if (item->valuestring == NULL) + { + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if (output == NULL) + { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; + } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; + } +} + +/* Build an array from input text. */ +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (buffer_at_offset(input_buffer)[0] != '[') + { + /* not an array */ + goto fail; + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) + { + /* empty array */ + goto success; + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') + { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an array to text */ +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; + + while (current_element != NULL) + { + if (!print_value(current_element, output_buffer)) + { + return false; + } + update_offset(output_buffer); + if (current_element->next) + { + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ','; + if(output_buffer->format) + { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Build an object from the text. */ +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* linked list head */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) + { + goto fail; /* not an object */ + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) + { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_string(current_item, input_buffer)) + { + goto fail; /* faile to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) + { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) + { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an object to text. */ +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_item = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output: */ + length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while (current_item) + { + if (output_buffer->format) + { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if (output_pointer == NULL) + { + return false; + } + for (i = 0; i < output_buffer->depth; i++) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ':'; + if (output_buffer->format) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if (!print_value(current_item, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + if (current_item->next) + { + *output_pointer++ = ','; + } + + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if (output_pointer == NULL) + { + return false; + } + if (output_buffer->format) + { + size_t i; + for (i = 0; i < (output_buffer->depth - 1); i++) + { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Get Array size/item / object item. */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) +{ + cJSON *child = NULL; + size_t size = 0; + + if (array == NULL) + { + return 0; + } + + child = array->child; + + while(child != NULL) + { + size++; + child = child->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)size; +} + +static cJSON* get_array_item(const cJSON *array, size_t index) +{ + cJSON *current_child = NULL; + + if (array == NULL) + { + return NULL; + } + + current_child = array->child; + while ((current_child != NULL) && (index > 0)) + { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) +{ + if (index < 0) + { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) +{ + cJSON *current_element = NULL; + + if ((object == NULL) || (name == NULL)) + { + return NULL; + } + + current_element = object->child; + if (case_sensitive) + { + while ((current_element != NULL) && (strcmp(name, current_element->string) != 0)) + { + current_element = current_element->next; + } + } + else + { + while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) + { + current_element = current_element->next; + } + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) +{ + return cJSON_GetObjectItem(object, string) ? 1 : 0; +} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev, cJSON *item) +{ + prev->next = item; + item->prev = prev; +} + +/* Utility for handling references. */ +static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) +{ + cJSON *reference = NULL; + if (item == NULL) + { + return NULL; + } + + reference = cJSON_New_Item(hooks); + if (reference == NULL) + { + return NULL; + } + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; +} + +static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) +{ + cJSON *child = NULL; + + if ((item == NULL) || (array == NULL)) + { + return false; + } + + child = array->child; + + if (child == NULL) + { + /* list is empty, start new one */ + array->child = item; + } + else + { + /* append to the end */ + while (child->next) + { + child = child->next; + } + suffix_object(child, item); + } + + return true; +} + +/* Add item to array/object. */ +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ + add_item_to_array(array, item); +} + +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif +/* helper function to cast away const */ +static void* cast_away_const(const void* string) +{ + return (void*)string; +} +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic pop +#endif + + +static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) +{ + char *new_key = NULL; + int new_type = cJSON_Invalid; + + if ((object == NULL) || (string == NULL) || (item == NULL)) + { + return false; + } + + if (constant_key) + { + new_key = (char*)cast_away_const(string); + new_type = item->type | cJSON_StringIsConst; + } + else + { + new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); + if (new_key == NULL) + { + return false; + } + + new_type = item->type & ~cJSON_StringIsConst; + } + + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + hooks->deallocate(item->string); + } + + item->string = new_key; + item->type = new_type; + + return add_item_to_array(object, item); +} + +CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + add_item_to_object(object, string, item, &global_hooks, false); +} + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +{ + add_item_to_object(object, string, item, &global_hooks, true); +} + +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + if (array == NULL) + { + return; + } + + add_item_to_array(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +{ + if ((object == NULL) || (string == NULL)) + { + return; + } + + add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) +{ + cJSON *null = cJSON_CreateNull(); + if (add_item_to_object(object, name, null, &global_hooks, false)) + { + return null; + } + + cJSON_Delete(null); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) +{ + cJSON *true_item = cJSON_CreateTrue(); + if (add_item_to_object(object, name, true_item, &global_hooks, false)) + { + return true_item; + } + + cJSON_Delete(true_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) +{ + cJSON *false_item = cJSON_CreateFalse(); + if (add_item_to_object(object, name, false_item, &global_hooks, false)) + { + return false_item; + } + + cJSON_Delete(false_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) +{ + cJSON *bool_item = cJSON_CreateBool(boolean); + if (add_item_to_object(object, name, bool_item, &global_hooks, false)) + { + return bool_item; + } + + cJSON_Delete(bool_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) +{ + cJSON *number_item = cJSON_CreateNumber(number); + if (add_item_to_object(object, name, number_item, &global_hooks, false)) + { + return number_item; + } + + cJSON_Delete(number_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) +{ + cJSON *string_item = cJSON_CreateString(string); + if (add_item_to_object(object, name, string_item, &global_hooks, false)) + { + return string_item; + } + + cJSON_Delete(string_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) +{ + cJSON *raw_item = cJSON_CreateRaw(raw); + if (add_item_to_object(object, name, raw_item, &global_hooks, false)) + { + return raw_item; + } + + cJSON_Delete(raw_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) +{ + cJSON *object_item = cJSON_CreateObject(); + if (add_item_to_object(object, name, object_item, &global_hooks, false)) + { + return object_item; + } + + cJSON_Delete(object_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) +{ + cJSON *array = cJSON_CreateArray(); + if (add_item_to_object(object, name, array, &global_hooks, false)) + { + return array; + } + + cJSON_Delete(array); + return NULL; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) +{ + if ((parent == NULL) || (item == NULL)) + { + return NULL; + } + + if (item->prev != NULL) + { + /* not the first element */ + item->prev->next = item->next; + } + if (item->next != NULL) + { + /* not the last element */ + item->next->prev = item->prev; + } + + if (item == parent->child) + { + /* first element */ + parent->child = item->next; + } + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) +{ + if (which < 0) + { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) +{ + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +} + +/* Replace array/object items with new ones. */ +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) +{ + cJSON *after_inserted = NULL; + + if (which < 0) + { + return; + } + + after_inserted = get_array_item(array, (size_t)which); + if (after_inserted == NULL) + { + add_item_to_array(array, newitem); + return; + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if (after_inserted == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) +{ + if ((parent == NULL) || (replacement == NULL) || (item == NULL)) + { + return false; + } + + if (replacement == item) + { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if (replacement->next != NULL) + { + replacement->next->prev = replacement; + } + if (replacement->prev != NULL) + { + replacement->prev->next = replacement; + } + if (parent->child == item) + { + parent->child = replacement; + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + if (which < 0) + { + return; + } + + cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) +{ + if ((replacement == NULL) || (string == NULL)) + { + return false; + } + + /* replace the name in the replacement */ + if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) + { + cJSON_free(replacement->string); + } + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + replacement->type &= ~cJSON_StringIsConst; + + cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); + + return true; +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +{ + replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) +{ + replace_item_in_object(object, string, newitem, true); +} + +/* Create basic types: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_NULL; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool b) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = b ? cJSON_True : cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if (num >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (num <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) + { + item->type = cJSON_String | cJSON_IsReference; + item->valuestring = (char*)cast_away_const(string); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Object | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Array | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type=cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) + { + item->type = cJSON_Object; + } + + return item; +} + +/* Create Arrays: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if (!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber((double)numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0;a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (strings == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateString(strings[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p,n); + } + p = n; + } + + return a; +} + +/* Duplication */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) +{ + cJSON *newitem = NULL; + cJSON *child = NULL; + cJSON *next = NULL; + cJSON *newchild = NULL; + + /* Bail on bad ptr */ + if (!item) + { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if (!newitem) + { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if (item->valuestring) + { + newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if (!newitem->valuestring) + { + goto fail; + } + } + if (item->string) + { + newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if (!newitem->string) + { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if (!recurse) + { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while (child != NULL) + { + newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) + { + goto fail; + } + if (next != NULL) + { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } + else + { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + + return newitem; + +fail: + if (newitem != NULL) + { + cJSON_Delete(newitem); + } + + return NULL; +} + +CJSON_PUBLIC(void) cJSON_Minify(char *json) +{ + unsigned char *into = (unsigned char*)json; + + if (json == NULL) + { + return; + } + + while (*json) + { + if (*json == ' ') + { + json++; + } + else if (*json == '\t') + { + /* Whitespace characters. */ + json++; + } + else if (*json == '\r') + { + json++; + } + else if (*json=='\n') + { + json++; + } + else if ((*json == '/') && (json[1] == '/')) + { + /* double-slash comments, to end of line. */ + while (*json && (*json != '\n')) + { + json++; + } + } + else if ((*json == '/') && (json[1] == '*')) + { + /* multiline comments. */ + while (*json && !((*json == '*') && (json[1] == '/'))) + { + json++; + } + json += 2; + } + else if (*json == '\"') + { + /* string literals, which are \" sensitive. */ + *into++ = (unsigned char)*json++; + while (*json && (*json != '\"')) + { + if (*json == '\\') + { + *into++ = (unsigned char)*json++; + } + *into++ = (unsigned char)*json++; + } + *into++ = (unsigned char)*json++; + } + else + { + /* All other characters. */ + *into++ = (unsigned char)*json++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a)) + { + return false; + } + + /* check if type is valid */ + switch (a->type & 0xFF) + { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if (a == b) + { + return true; + } + + switch (a->type & 0xFF) + { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if (a->valuedouble == b->valuedouble) + { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) + { + return false; + } + if (strcmp(a->valuestring, b->valuestring) == 0) + { + return true; + } + + return false; + + case cJSON_Array: + { + cJSON *a_element = a->child; + cJSON *b_element = b->child; + + for (; (a_element != NULL) && (b_element != NULL);) + { + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: + { + cJSON *a_element = NULL; + cJSON *b_element = NULL; + cJSON_ArrayForEach(a_element, a) + { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) + { + return false; + } + + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) + { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) + { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) + { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void *) cJSON_malloc(size_t size) +{ + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) cJSON_free(void *object) +{ + global_hooks.deallocate(object); +} + diff --git a/libres/lib/include/ert/analysis/analysis_module.hpp b/libres/lib/include/ert/analysis/analysis_module.hpp new file mode 100644 index 00000000000..dfe30743d59 --- /dev/null +++ b/libres/lib/include/ert/analysis/analysis_module.hpp @@ -0,0 +1,127 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'analysis_module.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 ERT_ANALYSIS_MODULE_H +#define ERT_ANALYSIS_MODULE_H +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include + +/* + These are option flag values which are used by the core ert code to + query the module of it's needs and capabilities. For instance to to + determine whether the data should be scaled prior to analysis the + core code will issue the call: + + if (analysis_module_get_option( module, ANALYSIS_SCALE_DATA)) + obs_data_scale( obs_data , S , E , D , R , dObs ); + + It is the responsability of the module to set the various flags. +*/ + + +typedef enum { + ANALYSIS_NEED_ED = 1, + ANALYSIS_USE_A = 4, // The module will read the content of A - but not modify it. + ANALYSIS_UPDATE_A = 8, // The update will be based on modifying A directly, and not on an X matrix. + ANALYSIS_SCALE_DATA = 16, + ANALYSIS_ITERABLE = 32 // The module can bu used as an iterative smoother. +} analysis_module_flag_enum; + +#define EXTERNAL_MODULE_NAME "analysis_table" +#define EXTERNAL_MODULE_SYMBOL analysis_table + + typedef enum { + LOAD_OK = 0, + DLOPEN_FAILURE = 1, + LOAD_SYMBOL_TABLE_NOT_FOUND = 2 + } analysis_module_load_status_enum; + + + typedef struct analysis_module_struct analysis_module_type; + + analysis_module_type * analysis_module_alloc_internal__( const char * symbol_table , bool verbose , analysis_module_load_status_enum * load_status); + analysis_module_type * analysis_module_alloc_internal( const char * symbol_table ); + + analysis_module_type * analysis_module_alloc_external__( const char * lib_name , bool verbose , analysis_module_load_status_enum * load_status); + analysis_module_type * analysis_module_alloc_external( const char * libname ); + + void analysis_module_free( analysis_module_type * module ); + + 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); + + + 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); + + + 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); + + + const char * analysis_module_get_lib_name( const analysis_module_type * module); + bool analysis_module_internal( const analysis_module_type * module ); + bool analysis_module_set_var( analysis_module_type * module , const char * var_name , const char * string_value ); + const char * analysis_module_get_table_name( const analysis_module_type * module); + const char * analysis_module_get_name( const analysis_module_type * module ); + void analysis_module_set_name( analysis_module_type * module , const char * name); + bool analysis_module_check_option( const analysis_module_type * module , long flag); + void analysis_module_complete_update( analysis_module_type * module ); + + bool analysis_module_has_var( const analysis_module_type * module , const char * var ); + double analysis_module_get_double( const analysis_module_type * module , const char * var); + int analysis_module_get_int( const analysis_module_type * module , const char * var); + bool analysis_module_get_bool( const analysis_module_type * module , const char * var); + void * analysis_module_get_ptr( const analysis_module_type * module , const char * var); + + + + UTIL_IS_INSTANCE_HEADER( analysis_module ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/analysis/analysis_table.hpp b/libres/lib/include/ert/analysis/analysis_table.hpp new file mode 100644 index 00000000000..12a0f89d25f --- /dev/null +++ b/libres/lib/include/ert/analysis/analysis_table.hpp @@ -0,0 +1,99 @@ +#ifndef ERT_ANALYSIS_TABLE_H +#define ERT_ANALYSIS_TABLE_H + +#ifdef __cplusplus +extern "C" { +#endif + + +#include +#include +#include + +#include + + + typedef void (analysis_updateA_ftype) (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); + + + typedef void (analysis_initX_ftype) (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); + + + typedef bool (analysis_set_int_ftype) (void * module_data , const char * flag , int value); + typedef bool (analysis_set_bool_ftype) (void * module_data , const char * flag , bool value); + typedef bool (analysis_set_double_ftype) (void * module_data , const char * var , double value); + typedef bool (analysis_set_string_ftype) (void * module_data , const char * var , const char * value); + typedef void (analysis_free_ftype) (void * ); + typedef void * (analysis_alloc_ftype) ( ); + + + typedef void (analysis_init_update_ftype) (void * module_data, + 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); + + typedef void (analysis_complete_update_ftype) (void * module_data ); + + typedef long (analysis_get_options_ftype) (void * module_data , long option); + + typedef bool (analysis_has_var_ftype) (const void * module_data , const char * var_name); + typedef int (analysis_get_int_ftype) (const void * module_data , const char * var_name ); + typedef double (analysis_get_double_ftype) (const void * module_data , const char * var_name ); + typedef bool (analysis_get_bool_ftype) (const void * module_data , const char * var_name ); + typedef void * (analysis_get_ptr_ftype) (const void * module_data , const char * var_name ); + +/*****************************************************************/ + + +typedef struct { + const char * name; + analysis_updateA_ftype * updateA; + analysis_initX_ftype * initX; + analysis_init_update_ftype * init_update; + analysis_complete_update_ftype * complete_update; + + analysis_free_ftype * freef; + analysis_alloc_ftype * alloc; + + 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_get_options_ftype * get_options; + + 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; +} analysis_table_type; + + + + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/analysis/cv_enkf.hpp b/libres/lib/include/ert/analysis/cv_enkf.hpp new file mode 100644 index 00000000000..9efcee25624 --- /dev/null +++ b/libres/lib/include/ert/analysis/cv_enkf.hpp @@ -0,0 +1,62 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'cv_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. +*/ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct cv_enkf_data_struct cv_enkf_data_type; + +void * cv_enkf_data_alloc( ); +void cv_enkf_data_free( void * arg ); + +void cv_enkf_init_update( void * arg , + const bool_vector_type * ens_mask , + const bool_vector_type * obs_back , + const matrix_type * S , + const matrix_type * R , + const matrix_type * dObs , + const matrix_type * E , + const matrix_type * D, + rng_type * rng); + +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); + +bool cv_enkf_set_double( void * arg , const char * var_name , double value); +bool cv_enkf_set_int( void * arg , const char * var_name , int value); +bool cv_enkf_set_bool( void * arg , const char * var_name , bool value ); + +void cv_enkf_set_truncation( cv_enkf_data_type * data , double truncation ); +void cv_enkf_set_pen_press( cv_enkf_data_type * data , bool value ); +void cv_enkf_set_subspace_dimension( cv_enkf_data_type * data , int subspace_dimension); + +#ifdef __cplusplus +} +#endif diff --git a/libres/lib/include/ert/analysis/enkf_linalg.hpp b/libres/lib/include/ert/analysis/enkf_linalg.hpp new file mode 100644 index 00000000000..9c3560aa6b8 --- /dev/null +++ b/libres/lib/include/ert/analysis/enkf_linalg.hpp @@ -0,0 +1,123 @@ +#ifndef ERT_ENKF_LINALG_H +#define ERT_ENKF_LINALG_H + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +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); + +int enkf_linalg_num_PC(const matrix_type * S , double truncation ); + + +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); + + +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); + + +void enkf_linalg_Cee(matrix_type * B, int nrens , const matrix_type * R , const matrix_type * U0 , const double * inv_sig0); + + +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 enkf_linalg_svdS(const matrix_type * S , + double truncation , + int ncomp , + dgesvd_vector_enum jobVT , + double * sig0, + matrix_type * U0 , + matrix_type * V0T); + + + +matrix_type * enkf_linalg_alloc_innov( const matrix_type * dObs , const matrix_type * S); + +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); + + + +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); + +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); + +void enkf_linalg_genX2(matrix_type * X2 , const matrix_type * S , const matrix_type * W , const double * eig); +void enkf_linalg_genX3(matrix_type * X3 , const matrix_type * W , const matrix_type * D , const double * eig); + +void enkf_linalg_meanX5(const matrix_type * S , + const matrix_type * W , + const double * eig , + const matrix_type * innov , + matrix_type * X5); + + +void enkf_linalg_X5sqrt(matrix_type * X2 , matrix_type * X5 , const matrix_type * randrot, int nrobs); + +matrix_type * enkf_linalg_alloc_mp_randrot(int ens_size , rng_type * rng); +void enkf_linalg_set_randrot( matrix_type * Q , rng_type * rng); +void enkf_linalg_checkX(const matrix_type * X , bool bootstrap); + + +//rml_enkf functions + +void enkf_linalg_rml_enkfX1(matrix_type *X1, matrix_type * Udr ,matrix_type * S ,matrix_type *R); +void enkf_linalg_rml_enkfX2(matrix_type *X2, double *Wdr, matrix_type * X1 ,double a , int nsign); +void enkf_linalg_rml_enkfX3(matrix_type *X3, matrix_type *VdTr, double *Wdr,matrix_type *X2, int nsign); + +double enkf_linalg_data_mismatch(matrix_type *D , matrix_type *R , matrix_type *Sk); +void enkf_linalg_Covariance(matrix_type *Cd, const matrix_type *E, double nsc ,int nrobs); +void enkf_linalg_rml_enkfAm(matrix_type * Um, const double * Wm,int nsign1); + +void enkf_linalg_rml_enkfX7(matrix_type * X7, matrix_type * VdT, double * Wdr, double a,matrix_type * X6); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/analysis/fwd_step_enkf.hpp b/libres/lib/include/ert/analysis/fwd_step_enkf.hpp new file mode 100644 index 00000000000..b5feed8deb1 --- /dev/null +++ b/libres/lib/include/ert/analysis/fwd_step_enkf.hpp @@ -0,0 +1,41 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'fwd_step_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. +*/ + +#include +#include + +#include +#include + +typedef struct fwd_step_enkf_data_struct fwd_step_enkf_data_type; + +void * fwd_step_enkf_data_alloc( ); +void fwd_step_enkf_data_free( void * arg ); + +void fwd_step_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); + + + diff --git a/libres/lib/include/ert/analysis/fwd_step_log.hpp b/libres/lib/include/ert/analysis/fwd_step_log.hpp new file mode 100644 index 00000000000..17be312a2b4 --- /dev/null +++ b/libres/lib/include/ert/analysis/fwd_step_log.hpp @@ -0,0 +1,45 @@ +/* + Copyright (C) 2016 Equinor ASA, Norway. + + The file 'fwd_step_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 FWD_STEP_LOG_H +#define FWD_STEP_LOG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + + typedef struct fwd_step_log_struct fwd_step_log_type; + + fwd_step_log_type * fwd_step_log_alloc(); + void fwd_step_log_free(fwd_step_log_type * fwd_step_log); + bool fwd_step_log_get_clear_log( const fwd_step_log_type * data ); + void fwd_step_log_set_clear_log( fwd_step_log_type * data , bool clear_log); + void fwd_step_log_set_log_file( fwd_step_log_type * data , const char * log_file ); + const char * fwd_step_log_get_log_file( const fwd_step_log_type * data); + void fwd_step_log_open( fwd_step_log_type * fwd_step_log ); + void fwd_step_log_close( fwd_step_log_type * fwd_step_log ); + void fwd_step_log_line( fwd_step_log_type * fwd_step_log , const char * fmt , ...); + bool fwd_step_log_is_open( const fwd_step_log_type * fwd_step_log ); + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/libres/lib/include/ert/analysis/module_data_block.hpp b/libres/lib/include/ert/analysis/module_data_block.hpp new file mode 100644 index 00000000000..4358ce17436 --- /dev/null +++ b/libres/lib/include/ert/analysis/module_data_block.hpp @@ -0,0 +1,53 @@ +/* + Copyright (C) 2016 Equinor ASA, Norway. + + The file 'module_data_blocks.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 MODULE_DATA_BLOCKS_H +#define MODULE_DATA_BLOCKS_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + struct module_data_block_struct { + UTIL_TYPE_ID_DECLARATION; + char * key; + const int * index_list; + int A_row_start; + int n_active; + }; + + typedef struct module_data_block_struct module_data_block_type; + + module_data_block_type * module_data_block_alloc( const char * key, const int * index_list , const int row_start, const int n_active); + const char * module_data_block_get_key(const module_data_block_type * module_data_block); + const int module_data_block_get_row_start(const module_data_block_type * module_data_block); + const int module_data_block_get_row_end(const module_data_block_type * module_data_block); + const int * module_data_block_get_active_indices(const module_data_block_type * module_data_block ); + void module_data_block_free(module_data_block_type * module_data_block); + void module_data_block_free__( void * arg ); + + UTIL_IS_INSTANCE_HEADER( module_data_block ); + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/libres/lib/include/ert/analysis/module_data_block_vector.hpp b/libres/lib/include/ert/analysis/module_data_block_vector.hpp new file mode 100644 index 00000000000..5208c692170 --- /dev/null +++ b/libres/lib/include/ert/analysis/module_data_block_vector.hpp @@ -0,0 +1,44 @@ +/* + Copyright (C) 2016 Equinor ASA, Norway. + + The file 'module_data_block_vector.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 ERT_MODULE_DATA_BLOCK_VECTOR_H +#define ERT_MODULE_DATA_BLOCK_VECTOR_H + +#ifdef __cplusplus +extern "C" { +#endif + + + +#include +#include + + typedef struct module_data_block_vector_struct module_data_block_vector_type; + + module_data_block_vector_type * module_data_block_vector_alloc(); + void module_data_block_vector_add_data_block( module_data_block_vector_type * module_data_block_vector , const module_data_block_type * data_block); + module_data_block_type * module_data_block_vector_iget_module_data_block(const module_data_block_vector_type * module_data_block_vector, int index); + int module_data_block_vector_get_size(const module_data_block_vector_type * module_data_block_vector); + + void module_data_block_vector_free( module_data_block_vector_type * module_data_block_vector ); + + UTIL_IS_INSTANCE_HEADER( module_data_block_vector ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/analysis/module_info.hpp b/libres/lib/include/ert/analysis/module_info.hpp new file mode 100644 index 00000000000..fe75da429b8 --- /dev/null +++ b/libres/lib/include/ert/analysis/module_info.hpp @@ -0,0 +1,43 @@ +/* + Copyright (C) 2016 Equinor ASA, Norway. + + The file 'module_info.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 ERT_MODULE_INFO_H +#define ERT_MODULE_INFO_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + + typedef struct module_info_struct module_info_type; + + module_info_type * module_info_alloc(const char* ministep_name); + void module_info_free(module_info_type * module_info); + char * module_info_get_ministep_name(const module_info_type * module_info); + module_data_block_vector_type * module_info_get_data_block_vector(const module_info_type * module_info); + module_obs_block_vector_type * module_info_get_obs_block_vector(const module_info_type * module_info); + + UTIL_IS_INSTANCE_HEADER( module_info ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/analysis/module_obs_block.hpp b/libres/lib/include/ert/analysis/module_obs_block.hpp new file mode 100644 index 00000000000..fe615a2b61b --- /dev/null +++ b/libres/lib/include/ert/analysis/module_obs_block.hpp @@ -0,0 +1,52 @@ +/* + Copyright (C) 2016 Equinor ASA, Norway. + + The file 'module_obs_blocks.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 MODULE_OBS_BLOCKS_H +#define MODULE_OBS_BLOCKS_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + struct module_obs_block_struct { + UTIL_TYPE_ID_DECLARATION; + char * key; + const int * index_list; + int D_row_start; + int n_active; + }; + + typedef struct module_obs_block_struct module_obs_block_type; + + module_obs_block_type * module_obs_block_alloc( const char * key, const int * index_list, const int row_start, const int n_active); + const char * module_obs_block_get_key(const module_obs_block_type * module_obs_block); + const int module_obs_block_get_row_start(const module_obs_block_type * module_obs_block); + const int module_obs_block_get_row_end(const module_obs_block_type * module_obs_block); + const int * module_obs_block_get_active_indices(const module_obs_block_type * module_obs_block ); + void module_obs_block_free(module_obs_block_type * module_obs_block); + void module_obs_block_free__( void * arg ); + + UTIL_IS_INSTANCE_HEADER( module_obs_block ); + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/libres/lib/include/ert/analysis/module_obs_block_vector.hpp b/libres/lib/include/ert/analysis/module_obs_block_vector.hpp new file mode 100644 index 00000000000..2732b976f4d --- /dev/null +++ b/libres/lib/include/ert/analysis/module_obs_block_vector.hpp @@ -0,0 +1,44 @@ +/* + Copyright (C) 2016 Equinor ASA, Norway. + + The file 'module_obs_block_vector.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 ERT_MODULE_OBS_BLOCK_VECTOR_H +#define ERT_MODULE_OBS_BLOCK_VECTOR_H + +#ifdef __cplusplus +extern "C" { +#endif + + + +#include +#include + + typedef struct module_obs_block_vector_struct module_obs_block_vector_type; + + module_obs_block_vector_type * module_obs_block_vector_alloc(); + void module_obs_block_vector_free( module_obs_block_vector_type * 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); + module_obs_block_type * module_obs_block_vector_iget_module_obs_block(const module_obs_block_vector_type * module_obs_block_vector, int 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); + int module_obs_block_vector_get_size(const module_obs_block_vector_type * module_obs_block_vector); + + UTIL_IS_INSTANCE_HEADER( module_obs_block_vector ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/analysis/std_enkf.hpp b/libres/lib/include/ert/analysis/std_enkf.hpp new file mode 100644 index 00000000000..966eb3c48a7 --- /dev/null +++ b/libres/lib/include/ert/analysis/std_enkf.hpp @@ -0,0 +1,56 @@ +#ifndef ERT_STD_ENKF_H +#define ERT_STD_ENKF_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include + +#define DEFAULT_ENKF_TRUNCATION_ 0.98 +#define ENKF_TRUNCATION_KEY_ "ENKF_TRUNCATION" +#define ENKF_NCOMP_KEY_ "ENKF_NCOMP" +#define USE_EE_KEY_ "USE_EE" +#define USE_GE_KEY_ "USE_GE" +#define ANALYSIS_SCALE_DATA_KEY_ "ANALYSIS_SCALE_DATA" + + typedef struct std_enkf_data_struct std_enkf_data_type; + + + bool std_enkf_set_double( void * arg , const char * var_name , double value); + + int std_enkf_get_subspace_dimension( std_enkf_data_type * data ); + void std_enkf_set_truncation( std_enkf_data_type * data , double truncation ); + void std_enkf_set_subspace_dimension( std_enkf_data_type * data , int subspace_dimension); + bool std_enkf_has_var( const void * arg, const char * var_name); + + double std_enkf_get_truncation( std_enkf_data_type * data ); + void * std_enkf_data_alloc( ); + void std_enkf_data_free( void * module_data ); + + bool std_enkf_get_bool( const void * arg, const char * var_name); + int std_enkf_get_int( const void * arg, const char * var_name); + double std_enkf_get_double( const void * arg, const char * var_name); + bool std_enkf_has_var( const void * arg, const char * var_name); + long std_enkf_get_options( void * arg , long flag ); + bool std_enkf_set_bool( void * arg , const char * var_name , bool value); + bool std_enkf_set_int( void * arg , const char * var_name , int value); + bool std_enkf_set_double( void * arg , const char * var_name , double value); + void std_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); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libres/lib/include/ert/analysis/stepwise.hpp b/libres/lib/include/ert/analysis/stepwise.hpp new file mode 100644 index 00000000000..eb08c7e79d0 --- /dev/null +++ b/libres/lib/include/ert/analysis/stepwise.hpp @@ -0,0 +1,32 @@ +#ifndef ERT_STEPWISE_H +#define ERT_STEPWISE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + + typedef struct stepwise_struct stepwise_type; + + + stepwise_type * stepwise_alloc1(int nsample, int nvar, rng_type * rng, const matrix_type* St, const matrix_type* Et); + void stepwise_free( stepwise_type * stepwise); + + void stepwise_set_Y0( stepwise_type * stepwise , matrix_type * Y); + int stepwise_get_n_active( stepwise_type * stepwise ); + bool_vector_type * stepwise_get_active_set( stepwise_type * stepwise ); + double stepwise_iget_beta(const stepwise_type * stepwise, const int index ); + double stepwise_get_sum_beta(const stepwise_type * stepwise ); + + void stepwise_estimate( stepwise_type * stepwise , double deltaR2_limit , int CV_blocks); + double stepwise_eval( const stepwise_type * stepwise , const matrix_type * x ); + + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libres/lib/include/ert/config/conf.hpp b/libres/lib/include/ert/config/conf.hpp new file mode 100644 index 00000000000..f6d72595db4 --- /dev/null +++ b/libres/lib/include/ert/config/conf.hpp @@ -0,0 +1,341 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'conf.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 ERT_CONF_H +#define ERT_CONF_H + +/* libconfig: lightweight configuration parser + * + * + * + * Introduction + * + * This library provides a lightweight configuration parser for the + * enkf application. The goal of the library is to provide the + * developer with a tool for rapid specification of configuration + * files, automatic checking of user provided configuration files + * and typed access to configuration items. + * + * + * + * A Simple Example + * + * Let us consider a simple example of user provided configuration + * file that can be used with the parser: + * + * + * res_sim FrontSim2007 + * { + * executable = /bin/frontsim2007; + * version = 2007; + * + * run_host bgo179lin + * { + * hostname = bgo179lin.nho.hydro.com; + * num_jobs = 4; + * }; + * }; + * + * + * Note that the newlines are not neccessary. In the example above, + * the user has provided an instance of the class "res_sim" with name + * FrontSim2007. Further, the user has set the items executable and version. + * He has also provided a instance of the sub class "run_host" with name + * bgo179lin and allocated 4 jobs to this machine. + * + * + * + * Structure + * + * The system is built around four basic objects: + * + * - Class definitions. + * - Item specifications. + * - Instances of classes. + * - Instances of item specifications, i.e. items. + * + * The relationship between the objects is as follows : + * + * - Class: + * . Can have contain both classes and item specifications. + * . Can not contain items or class instances. + * + * - Item specifications: + * . Can not contain any of the other objects. + * + * - Instances of classes: + * . Can contain class instances and items. + * + * - Items: + * . Can not contain any of the other objects. + * + * + * + * General Use + * + * The parser is designed to be used in the following way: + * + * - The developer creates the classes and item specifications needed. + * - Using the library and the classes, user provided configuration + * files are read and validated. + * - If the validation fails, the developer can choose to exit. + * - Using the library, the devloper has typed access to all + * information provided by the user. + * + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +#include + + + +typedef struct conf_class_struct conf_class_type; +typedef struct conf_instance_struct conf_instance_type; +typedef struct conf_item_spec_struct conf_item_spec_type; +typedef struct conf_item_struct conf_item_type; +typedef struct conf_item_mutex_struct conf_item_mutex_type; + + +/** D E F A U L T A L L O C / F R E E F U N C T I O N S */ + + + +conf_class_type * conf_class_alloc_empty( + const char * class_name, + bool require_instance, + bool singleton, + const char * help); + +void conf_class_free( + conf_class_type * conf_class); + +void conf_class_free__( + void * conf_class); + + +conf_instance_type * conf_instance_copyc( + const conf_instance_type * conf_instance); + +void conf_instance_free( + conf_instance_type * conf_instance); + +void conf_instance_free__( + void * conf_instance); + + + +conf_item_spec_type * conf_item_spec_alloc( + const char * name, + bool required_set, + dt_enum dt, + const char * help); + +void conf_item_spec_free( + conf_item_spec_type * conf_item_spec); + +void conf_item_spec_free__( + void * conf_item_spec); + + + +conf_item_type * conf_item_alloc( + const conf_item_spec_type * conf_item_spec, + const char * value); + +conf_item_type * conf_item_copyc( + const conf_item_type * conf_item); + +void conf_item_free( + conf_item_type * conf_item); + +void conf_item_free__( + void * conf_item); + + + +void conf_item_mutex_free( + conf_item_mutex_type * conf_item_mutex); + +void conf_item_mutex_free__( + void * conf_item_mutex); + + + +/** M A N I P U L A T O R S , I N S E R T I O N */ + + + +void conf_class_insert_owned_sub_class( + conf_class_type * conf_class, + conf_class_type * sub_conf_class); + +void conf_class_insert_owned_item_spec( + conf_class_type * conf_class, + conf_item_spec_type * item_spec); + +void conf_instance_insert_owned_sub_instance( + conf_instance_type * conf_instance, + conf_instance_type * sub_conf_instance); + +void conf_instance_insert_owned_item( + conf_instance_type * conf_instance, + conf_item_type * conf_item); + +void conf_instance_insert_item( + conf_instance_type * conf_instance, + const char * item_name, + const char * value); + +conf_item_mutex_type * conf_class_new_item_mutex( + conf_class_type * conf_class, + bool require_one, + bool inverse); + +void conf_item_mutex_add_item_spec( + conf_item_mutex_type * conf_item_mutex, + const conf_item_spec_type * conf_item_spec); + + + +/** M A N I P U L A T O R S , C L A S S A N D I T E M S P E C I F I C A T I O N */ + + + +void conf_class_set_help( + conf_class_type * conf_class, + const char * help); + + + +void conf_item_spec_add_restriction( + conf_item_spec_type * conf_item_spec, + const char * restriction); + +void conf_item_spec_set_default_value( + conf_item_spec_type * conf_item_spec, + const char * default_value); + +void conf_item_spec_set_help( + conf_item_spec_type * conf_item_spec, + const char * help); + + + +/** A C C E S S O R S */ + + + +bool conf_class_has_item_spec( + const conf_class_type * conf_class, + const char * item_name); + +bool conf_class_has_sub_class( + const conf_class_type * conf_class, + const char * sub_class_name); + +const conf_item_spec_type * conf_class_get_item_spec_ref( + const conf_class_type * conf_class, + const char * item_name); + +const conf_class_type * conf_class_get_sub_class_ref( + const conf_class_type * conf_class, + const char * sub_class_name); + + + +const char * conf_instance_get_name_ref( + const conf_instance_type * conf_instance); + +bool conf_instance_is_of_class( + const conf_instance_type * conf_instance, + const char * class_name); + +bool conf_instance_has_item( + const conf_instance_type * conf_instance, + const char * item_name); + +const conf_instance_type * conf_instance_get_sub_instance_ref( + const conf_instance_type * conf_instance, + const char * sub_instance_name); + +stringlist_type * conf_instance_alloc_list_of_sub_instances_of_class( + const conf_instance_type * conf_instance, + const conf_class_type * conf_class); + +stringlist_type * conf_instance_alloc_list_of_sub_instances_of_class_by_name( + const conf_instance_type * conf_instance, + const char * sub_class_name); + +const char * conf_instance_get_class_name_ref( + const conf_instance_type * conf_instance); + +const char * conf_instance_get_item_value_ref( + const conf_instance_type * conf_instance, + const char * item_name); + +/** If the dt supports it, these functions will parse the item + value to the requested types. + + NOTE: + If the dt does not support it, or the conf_instance + does not have the item, the functions will abort your program. +*/ +int conf_instance_get_item_value_int( + const conf_instance_type * conf_instance, + const char * item_name); + +double conf_instance_get_item_value_double( + const conf_instance_type * conf_instance, + const char * item_name); + +time_t conf_instance_get_item_value_time_t( + const conf_instance_type * conf_instance, + const char * item_name); + +bool conf_instance_get_path_error( + const conf_instance_type * conf_instance); + +/** V A L I D A T O R S */ + + + +bool conf_instance_validate( + const conf_instance_type * conf_instance); + +bool conf_instance_has_path_error(const conf_instance_type * conf_instance); + + +/** A L L O C F R O M F I L E */ + + +conf_instance_type * conf_instance_alloc_from_file( + const conf_class_type * conf_class, + const char * name, + const char * file_name); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/config/conf_data.hpp b/libres/lib/include/ert/config/conf_data.hpp new file mode 100644 index 00000000000..54a04c06e92 --- /dev/null +++ b/libres/lib/include/ert/config/conf_data.hpp @@ -0,0 +1,53 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'conf_data.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 ERT_CONF_DATA_H +#define ERT_CONF_DATA_H +#ifdef __cplusplus +extern "C" { +#endif +#include +#include + +typedef enum { + DT_STR, + DT_INT, + DT_POSINT, + DT_FLOAT, + DT_POSFLOAT, + DT_FILE, + DT_EXEC, + DT_FOLDER, + DT_DATE + } dt_enum; + + +const char *conf_data_get_dt_name_ref(dt_enum dt); + +bool conf_data_validate_string_as_dt_value(dt_enum dt, const char *str); + +int conf_data_get_int_from_string(dt_enum dt, const char *str); + +double conf_data_get_double_from_string(dt_enum dt, const char *str); + +time_t conf_data_get_time_t_from_string(dt_enum dt, const char *str); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/config/conf_util.hpp b/libres/lib/include/ert/config/conf_util.hpp new file mode 100644 index 00000000000..59c450694c3 --- /dev/null +++ b/libres/lib/include/ert/config/conf_util.hpp @@ -0,0 +1,32 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'conf_util.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 ERT_CONF_UTIL_H +#define ERT_CONF_UTIL_H +#ifdef __cplusplus +extern "C" { +#endif + +char * conf_util_fscanf_alloc_token_buffer( const char * file_name ); + +char * conf_util_alloc_next_token( char ** buffer_position ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/config/config_content.hpp b/libres/lib/include/ert/config/config_content.hpp new file mode 100644 index 00000000000..fd1ebf8c5bf --- /dev/null +++ b/libres/lib/include/ert/config/config_content.hpp @@ -0,0 +1,87 @@ +/* + Copyright (C) 2015 Equinor ASA, Norway. + + The file 'config_content.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 ERT_CONFIG_CONTENT_H +#define ERT_CONFIG_CONTENT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include +#include +#include +#include + +typedef struct config_content_struct config_content_type; + + + config_content_type * config_content_alloc(const char * filename); + void config_content_free( config_content_type * content ); + void config_content_set_valid( config_content_type * content); + bool config_content_is_valid( const config_content_type * content ); + bool config_content_has_item( const config_content_type * content , const char * key); + void config_content_add_item( config_content_type * content , const config_schema_item_type * schema_item , const config_path_elm_type * path_elm); + config_content_item_type * config_content_get_item( const config_content_type * content , const char * key); + void config_content_add_node( config_content_type * content , config_content_node_type * content_node ); + config_error_type * config_content_get_errors( const config_content_type * content); + + const char * config_content_iget( const config_content_type * content , const char * key , int occurence , int index); + int config_content_iget_as_int( const config_content_type * content , const char * key , int occurence , int index); + bool config_content_iget_as_bool( const config_content_type * content , const char * key , int occurence , int index); + double config_content_iget_as_double( const config_content_type * content , const char * key , int occurence , int index); + const char * config_content_safe_iget(const config_content_type * content , const char *kw, int occurence , int index); + int config_content_get_occurences(const config_content_type * content, const char * kw); + + bool config_content_get_value_as_bool(const config_content_type * config , const char * kw); + int config_content_get_value_as_int(const config_content_type * config , const char * kw); + double config_content_get_value_as_double(const config_content_type * config , const char * kw); + const char * config_content_get_value_as_path( const config_content_type * config , const char * kw); + const char * config_content_get_value_as_abspath( const config_content_type * config , const char * kw); + const char * config_content_get_value_as_relpath( const config_content_type * config , const char * kw); + const char * config_content_get_value_as_executable( const config_content_type * config , const char * kw); + const char * config_content_get_value(const config_content_type * config , const char * kw); + const stringlist_type * config_content_iget_stringlist_ref(const config_content_type * content , const char * kw, int occurence); + config_content_node_type * config_content_get_value_node( const config_content_type * content , const char * kw); + void config_content_add_define( config_content_type * content , const char * key , const char * value ); + subst_list_type * config_content_get_define_list( config_content_type * content ); + const subst_list_type * config_content_get_const_define_list(const config_content_type * content); + const char * config_content_get_config_file( const config_content_type * content , bool abs_path ); + int config_content_get_size(const config_content_type * content); + const config_content_node_type * config_content_iget_node( const config_content_type * content , int index); + bool config_content_add_file( config_content_type * content , const char * config_file); + config_root_path_type * config_content_get_invoke_path( config_content_type * content ); + config_path_elm_type * config_content_add_path_elm( config_content_type * content , const char * path ); + const stringlist_type * config_content_get_warnings( const config_content_type * content); + const char * config_content_get_config_path( const config_content_type * content ); + void config_content_pop_path_stack( config_content_type * content ); + stringlist_type * config_content_alloc_keys(const config_content_type * content); + + + + UTIL_IS_INSTANCE_HEADER( config_content ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/config/config_content_item.hpp b/libres/lib/include/ert/config/config_content_item.hpp new file mode 100644 index 00000000000..fd1635ad1fe --- /dev/null +++ b/libres/lib/include/ert/config/config_content_item.hpp @@ -0,0 +1,61 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'config_content_item.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 ERT_CONFIG_CONTENT_ITEM_H +#define ERT_CONFIG_CONTENT_ITEM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include +#include +#include +#include + +typedef struct config_content_item_struct config_content_item_type; + + int config_content_item_get_size(const config_content_item_type * item); + config_content_node_type * config_content_item_get_last_node(const config_content_item_type * item); + config_content_node_type * config_content_item_iget_node(const config_content_item_type * item , int index); + const stringlist_type * config_content_item_iget_stringlist_ref(const config_content_item_type * item, int occurence); + hash_type * config_content_item_alloc_hash(const config_content_item_type * item , bool copy); + const char * config_content_item_iget(const config_content_item_type * item , int occurence , int index); + bool config_content_item_iget_as_bool(const config_content_item_type * item, int occurence , int index); + int config_content_item_iget_as_int(const config_content_item_type * item, int occurence , int index); + double config_content_item_iget_as_double(const config_content_item_type * item, int occurence , int index); + void config_content_item_clear( config_content_item_type * item ); + void config_content_item_free( config_content_item_type * item ); + void config_content_item_free__( void * arg ); + config_content_item_type * config_content_item_alloc( const config_schema_item_type * schema , const config_path_elm_type * path_elm); + config_content_node_type * config_content_item_alloc_node( const config_content_item_type * item , const config_path_elm_type * path_elm); + const config_schema_item_type * config_content_item_get_schema( const config_content_item_type * item ); + const config_path_elm_type * config_content_item_get_path_elm( const config_content_item_type * item ); + + UTIL_IS_INSTANCE_HEADER( config_content_item ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libres/lib/include/ert/config/config_content_node.hpp b/libres/lib/include/ert/config/config_content_node.hpp new file mode 100644 index 00000000000..9f11da1ed7a --- /dev/null +++ b/libres/lib/include/ert/config/config_content_node.hpp @@ -0,0 +1,62 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'config_content_node.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 ERT_CONFIG_CONTENT_NODE_H +#define ERT_CONFIG_CONTENT_NODE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include + +typedef struct config_content_node_struct config_content_node_type; + + config_item_types config_content_node_iget_type( const config_content_node_type * node , int index); + config_content_node_type * config_content_node_alloc( const config_schema_item_type * schema , const config_path_elm_type * cwd); + void config_content_node_add_value(config_content_node_type * node , const char * value); + void config_content_node_set(config_content_node_type * node , const stringlist_type * token_list); + char * config_content_node_alloc_joined_string(const config_content_node_type * node, const char * sep); + void config_content_node_free(config_content_node_type * node); + void config_content_node_free__(void * arg); + const char * config_content_node_get_full_string(const config_content_node_type * node, + const char * sep); + const char * config_content_node_iget(const config_content_node_type * node , int index); + bool config_content_node_iget_as_bool(const config_content_node_type * node , int index); + int config_content_node_iget_as_int(const config_content_node_type * node , int index); + double config_content_node_iget_as_double(const config_content_node_type * node , int index); + const char * config_content_node_iget_as_path(config_content_node_type * node , int index); + const char * config_content_node_iget_as_abspath( config_content_node_type * node , int index); + const char * config_content_node_iget_as_relpath( config_content_node_type * node , int index); + const char * config_content_node_iget_as_executable( config_content_node_type * node , int index); + time_t config_content_node_iget_as_isodate(const config_content_node_type * node , int index); + const stringlist_type * config_content_node_get_stringlist( const config_content_node_type * node ); + const char * config_content_node_safe_iget(const config_content_node_type * node , int index); + int config_content_node_get_size( const config_content_node_type * node ); + const char * config_content_node_get_kw( const config_content_node_type * node ); + void config_content_node_assert_key_value( const config_content_node_type * node ); + void config_content_node_init_opt_hash( const config_content_node_type * node , hash_type * opt_hash , int elm_offset); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/config/config_error.hpp b/libres/lib/include/ert/config/config_error.hpp new file mode 100644 index 00000000000..ccd50282eb7 --- /dev/null +++ b/libres/lib/include/ert/config/config_error.hpp @@ -0,0 +1,44 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'config_error.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 ERT_CONFIG_ERROR_H +#define ERT_CONFIG_ERROR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct config_error_struct config_error_type; + + + config_error_type * config_error_alloc(); + config_error_type * config_error_alloc_copy( const config_error_type * src_error); + void config_error_free(config_error_type * error); + const char * config_error_iget(const config_error_type * error , int index); + void config_error_add( config_error_type * error , char * new_error ); + int config_error_count( const config_error_type * error ); + void config_error_fprintf( const config_error_type * error , bool add_count , FILE * stream ); + bool config_error_equal( const config_error_type * error1 , const config_error_type * error2); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libres/lib/include/ert/config/config_parser.hpp b/libres/lib/include/ert/config/config_parser.hpp new file mode 100644 index 00000000000..eacafb8c340 --- /dev/null +++ b/libres/lib/include/ert/config/config_parser.hpp @@ -0,0 +1,85 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file '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 ERT_CONFIG_H +#define ERT_CONFIG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + + +typedef struct config_parser_struct config_parser_type; + + + void config_free(config_parser_type *); + config_parser_type * config_alloc( ); + config_content_type * config_parse(config_parser_type * config, + const char * filename, + const char * comment_string, + const char * include_kw, + const char * define_kw, + const hash_type * pre_defined_kw_map, + config_schema_unrecognized_enum unrecognized_behaviour, + bool validate); + bool config_has_schema_item(const config_parser_type * config , const char * kw); + +/*****************************************************************/ + + config_schema_item_type * config_get_schema_item(const config_parser_type *, const char *); + void config_add_alias(config_parser_type * , const char * , const char * ); + void config_install_message(config_parser_type * , const char * , const char * ); + + config_schema_item_type * config_add_schema_item(config_parser_type * config, + const char * kw, + bool required); + + config_schema_item_type * config_add_key_value( config_parser_type * config , const char * key , bool required , config_item_types item_type); + + int config_get_schema_size( const config_parser_type * config ); + void config_parser_deprecate(config_parser_type * config , const char * kw, const char * msg); + + void config_validate(config_parser_type * config, config_content_type * content); + + bool config_parser_add_key_values(config_parser_type * config, + config_content_type * content, + const char * kw, + stringlist_type * values, + const config_path_elm_type * current_path_elm, + const char * config_filename, + config_schema_unrecognized_enum unrecognized); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/config/config_path_elm.hpp b/libres/lib/include/ert/config/config_path_elm.hpp new file mode 100644 index 00000000000..546758a21d9 --- /dev/null +++ b/libres/lib/include/ert/config/config_path_elm.hpp @@ -0,0 +1,42 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'config_path_elm.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 ERT_CONFIG_PATH_ELM_H +#define ERT_CONFIG_PATH_ELM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct config_path_elm_struct config_path_elm_type; + +void config_path_elm_free( config_path_elm_type * path_elm ); +void config_path_elm_free__( void * arg ); +config_path_elm_type * config_path_elm_alloc( const config_root_path_type * root_path , const char * path); +const char * config_path_elm_get_abspath( const config_path_elm_type * path_elm ); +const char * config_path_elm_get_relpath( const config_path_elm_type * path_elm ); +char * config_path_elm_alloc_abspath(const config_path_elm_type * path_elm , const char * input_path); +char * config_path_elm_alloc_relpath(const config_path_elm_type * path_elm , const char * input_path); +char * config_path_elm_alloc_path(const config_path_elm_type * path_elm , const char * input_path); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/config/config_path_stack.hpp b/libres/lib/include/ert/config/config_path_stack.hpp new file mode 100644 index 00000000000..0971a33e5ef --- /dev/null +++ b/libres/lib/include/ert/config/config_path_stack.hpp @@ -0,0 +1,40 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'config_path_stack.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 ERT_CONFIG_PATH_STACK_H +#define ERT_CONFIG_PATH_STACK_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct config_path_stack_struct config_path_stack_type; + +void config_path_stack_free( config_path_stack_type * path_stack ); +config_path_stack_type * config_path_stack_alloc( ); +void config_path_stack_append( config_path_stack_type * path_stack , config_path_elm_type * path_elm); +int config_path_stack_size( const config_path_stack_type * path_stack ); +const config_path_elm_type * config_path_stack_get_last( const config_path_stack_type * path_stack ); +void config_path_stack_pop( config_path_stack_type * path_stack ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/config/config_root_path.hpp b/libres/lib/include/ert/config/config_root_path.hpp new file mode 100644 index 00000000000..b10e9589236 --- /dev/null +++ b/libres/lib/include/ert/config/config_root_path.hpp @@ -0,0 +1,38 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'config_root_path.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 ERT_CONFIG_ROOT_PATH_H +#define ERT_CONFIG_ROOT_PATH_H + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef struct config_root_path_struct config_root_path_type; + +void config_root_path_free( config_root_path_type * root_path ); +config_root_path_type * config_root_path_alloc( const char * input_path ); +const char * config_root_path_get_input_path( const config_root_path_type * root_path ); +const char * config_root_path_get_rel_path( const config_root_path_type * root_path ); +const char * config_root_path_get_abs_path( const config_root_path_type * root_path ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/config/config_schema_item.hpp b/libres/lib/include/ert/config/config_schema_item.hpp new file mode 100644 index 00000000000..1766c51ee74 --- /dev/null +++ b/libres/lib/include/ert/config/config_schema_item.hpp @@ -0,0 +1,111 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'config_schema_item.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 ERT_CONFIG_SCHEMA_ITEM_H +#define ERT_CONFIG_SCHEMA_ITEM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include + +/** + Types used for validation of config items. +*/ +typedef enum { + CONFIG_STRING = 1, + CONFIG_INT = 2, + CONFIG_FLOAT = 4, + CONFIG_PATH = 8, + CONFIG_EXISTING_PATH = 16, + CONFIG_BOOL = 32, + CONFIG_CONFIG = 64, + CONFIG_BYTESIZE = 128, + CONFIG_EXECUTABLE = 256, + CONFIG_ISODATE = 512, + CONFIG_INVALID = 1024, + CONFIG_RUNTIME_INT = 2048, + CONFIG_RUNTIME_FILE = 4096 +} config_item_types; + + +typedef enum { + CONFIG_UNRECOGNIZED_IGNORE = 0, + CONFIG_UNRECOGNIZED_WARN = 1, + CONFIG_UNRECOGNIZED_ERROR = 2, + CONFIG_UNRECOGNIZED_ADD = 3 +} config_schema_unrecognized_enum; + + + +#define CONFIG_DEFAULT_ARG_MIN -1 +#define CONFIG_DEFAULT_ARG_MAX -1 + + + + typedef struct config_schema_item_struct config_schema_item_type; + + + config_schema_item_type * config_schema_item_alloc(const char * kw , bool required); + bool config_schema_item_validate_set(const config_schema_item_type * item , + stringlist_type * token_list , + const char * config_file, + const config_path_elm_type * path_elm, + config_error_type * error_list); + + void config_schema_item_free( config_schema_item_type * item); + void config_schema_item_free__ (void * void_item); + + void config_schema_item_set_required_children_on_value(config_schema_item_type * item , const char * value , stringlist_type * child_list); + void config_schema_item_set_common_selection_set(config_schema_item_type * item , const stringlist_type * argv); + void config_schema_item_set_indexed_selection_set(config_schema_item_type * item , int index , const stringlist_type * argv); + void config_schema_item_add_indexed_alternative(config_schema_item_type * item , int index , const char * value); + void config_schema_item_add_required_children(config_schema_item_type * item , const char * child_key); + void config_schema_item_set_envvar_expansion( config_schema_item_type * item , bool expand_envvar ); + void config_schema_item_set_argc_minmax(config_schema_item_type * item , + int argc_min , + int argc_max); + void config_schema_item_assure_type(const config_schema_item_type * item , int index , int type_mask); + + int config_schema_item_num_required_children(const config_schema_item_type * item); + const char * config_schema_item_iget_required_child( const config_schema_item_type * item , int index); + const char * config_schema_item_get_kw( const config_schema_item_type * item ); + bool config_schema_item_required( const config_schema_item_type * item ); + bool config_schema_item_expand_envvar( const config_schema_item_type * item ); + void config_schema_item_get_argc( const config_schema_item_type * item , int *argc_min , int *argc_max); + bool config_schema_item_has_required_children_value( const config_schema_item_type * item ); + stringlist_type * config_schema_item_get_required_children_value(const config_schema_item_type * item , const char * value); + + void config_schema_item_iset_type( config_schema_item_type * item , int index , config_item_types type); + config_item_types config_schema_item_iget_type(const config_schema_item_type * item , int index ); + void config_schema_item_set_default_type( config_schema_item_type * item , config_item_types type); + bool config_schema_item_is_deprecated( const config_schema_item_type * item); + const char * config_schema_item_get_deprecate_msg( const config_schema_item_type * item); + void config_schema_item_set_deprecated( config_schema_item_type * item , const char * msg); + bool config_schema_item_valid_string(config_item_types value_type , const char * value, bool runtime); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libres/lib/include/ert/config/config_settings.hpp b/libres/lib/include/ert/config/config_settings.hpp new file mode 100644 index 00000000000..4ddcff2a386 --- /dev/null +++ b/libres/lib/include/ert/config/config_settings.hpp @@ -0,0 +1,65 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'config_settings.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. +*/ + +#ifndef ERT_CONFIG_SETTINGS_H +#define ERT_CONFIG_SETTINGS_H + +#ifdef __cplusplus +extern "C" { +#endif +#include + +#include + +#include +#include +#include + +typedef struct config_settings_struct config_settings_type; + +config_settings_type * config_settings_alloc( const char * root_key ); +void config_settings_free( config_settings_type * settings); +bool config_settings_has_key( const config_settings_type * settings , const char * key); +config_item_types config_settings_get_value_type( const config_settings_type * config_settings , const char * key); +bool config_settings_set_value( const config_settings_type * config_settings , const char * key, const char * value); +void config_settings_init_parser( const config_settings_type * config_settings, config_parser_type * config , bool required); +void config_settings_init_parser__( const char * root_key , config_parser_type * config , bool required); +void config_settings_apply(config_settings_type * config_settings , const config_content_type * config ); +stringlist_type * config_settings_alloc_keys( const config_settings_type * config_settings ); + +bool config_settings_add_setting(config_settings_type * settings , const char* key, config_item_types value_type , const char* initial_value); +void config_settings_add_int_setting(config_settings_type * settings , const char* key, int initial_value); +void config_settings_add_double_setting(config_settings_type * settings , const char* key, double initial_value); +void config_settings_add_string_setting(config_settings_type * settings , const char* key, const char * initial_value); +void config_settings_add_bool_setting(config_settings_type * settings , const char* key, bool initial_value); + + +const char * config_settings_get_value( const config_settings_type * config_settings , const char * key); +int config_settings_get_int_value( const config_settings_type * config_settings , const char * key); +bool config_settings_get_bool_value( const config_settings_type * config_settings , const char * key); +double config_settings_get_double_value( const config_settings_type * config_settings , const char * key); + +bool config_settings_set_value( const config_settings_type * config_settings , const char * key, const char * value); +bool config_settings_set_int_value( const config_settings_type * config_settings , const char * key, int value); +bool config_settings_set_double_value( const config_settings_type * config_settings , const char * key, double value); +bool config_settings_set_bool_value( const config_settings_type * config_settings , const char * key, bool value); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/README.new_type.hpp b/libres/lib/include/ert/enkf/README.new_type.hpp new file mode 100644 index 00000000000..de390a4ffa2 --- /dev/null +++ b/libres/lib/include/ert/enkf/README.new_type.hpp @@ -0,0 +1,124 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'README.new_type.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. +*/ + +/** +The two files README.new_type.c and README.new_type_config.c (along +with the corresponding header files) are meant to serve as a +documentation and reference on how to add new object types to the enkf +system. + + +new_type.h +========== +When implementing a new type, the header file should contain a typedef like this: + + typedef struct new_type_struct new_type; + +This typedef means two things: + + 1. The entity "new_type" is to be interpreted as a "struc + new_type_struct". + + 2. We are informing the compiler that a declaration of the struct + new_type_struct will come at a later stage. However for the rest + of the header file we can refer only to "new_type" + +The advantages of this way to do it is that only the existence of +"struct new_type_struct" is exported, the actual implementation is +hidden for other files; it is a bit like making all the data private +in a C++ class. + + _____________________ E X A M P L E _____________________________ + / + | new.h + | ----- + | typedef struct new_struct new_type; + | + | new_type * new_type_alloc(int , const char *); + | double * new_get_data_ref(const new_type *); + | void new_type_free(new_type *); + | + | + | new.c + | ----- + | #include + | #include + | + | struct new_struct { + | double *data; + | } + | + | new_type * new_type_alloc(int size) { + | new_type * new = util_malloc(sizeof * new_type , __func__); + new->date = util_malloc(size * sizeof new->data); + | return new; + | } + | + | double * new_get_data_ref(const new_type * new) { + | return new->data; + | } + | + | + | void new_type_free(new_type * new) { + | free(new->data); + | free(new); + | } + | + | + | other.c + | ------- + | #include + | + | void some_func() { + | new_type * new = new_type_alloc(100 , "Programmer ..."); + | .... + | .... + | new_type_free(new); + | } + \_________________________________________________________________ + +What happen in this little example is the following things: + + 1. In the header file "new.h" we say that an implementation of a + struct new_type_struct will be forthcoming. This struct can + (without the implementation) be referred to as new_type. + + In the header file we also claim that the three functions: + + i new_type * new_type_alloc(int , const chat*); + ii double * new_get_data_ref(const new_type *); + iii void new_type_free(new_type *); + + will be coming. + + 2. In the source file new.c we have the implementation of the struct + new_struct, along with the three functions listed above. + + 3. In the third file, other.c which includes "new.h", we can refer to + the type new_type, and the three functions listed in the + header. However we can *NOT* get to the fields in the struct of + type new_type_struct, i.e. code like: + + .... + new->data[17] = 178.9; + .... + + in "other.c" will *NOT* compile. It will fail with the message + "dereferencing pointer to imcomplete type." + +*/ diff --git a/libres/lib/include/ert/enkf/README.new_type_config.hpp b/libres/lib/include/ert/enkf/README.new_type_config.hpp new file mode 100644 index 00000000000..769b41a13c2 --- /dev/null +++ b/libres/lib/include/ert/enkf/README.new_type_config.hpp @@ -0,0 +1,28 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'README.new_type_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. +*/ + +/** +The two files README.new_type.c and README.new_type_config.c (along +with the corresponding header files) are meant to serve as a +documentation and reference on how to add new object types to the enkf +system. + + +new_type_config.h +========== +*/ diff --git a/libres/lib/include/ert/enkf/active_config.hpp b/libres/lib/include/ert/enkf/active_config.hpp new file mode 100644 index 00000000000..05601874a7b --- /dev/null +++ b/libres/lib/include/ert/enkf/active_config.hpp @@ -0,0 +1,23 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'active_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 ERT_ACTIVE_CONFIG_H +#define ERT_ACTIVE_CONFIG_H + + +#endif diff --git a/libres/lib/include/ert/enkf/active_list.hpp b/libres/lib/include/ert/enkf/active_list.hpp new file mode 100644 index 00000000000..474e8c79338 --- /dev/null +++ b/libres/lib/include/ert/enkf/active_list.hpp @@ -0,0 +1,50 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'active_list.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 ERT_ACTIVE_LIST_H +#define ERT_ACTIVE_LIST_H + +#ifdef __cplusplus +extern "C" { +#endif +#include + +#include + + +typedef struct active_list_struct active_list_type; + + active_list_type * active_list_alloc( ); + void active_list_add_index(active_list_type * , int); + void active_list_free( active_list_type *); + const int * active_list_get_active(const active_list_type * ); + int active_list_get_active_size(const active_list_type * , int total_size ); + active_mode_type active_list_get_mode(const active_list_type * ); + void active_list_free__( void * arg ); + active_list_type * active_list_alloc_copy( const active_list_type * src); + void active_list_summary_fprintf( const active_list_type * active_list , const char * dataset_key , const char * key , FILE * stream); + bool active_list_iget( const active_list_type * active_list , int index ); + bool active_list_equal( const active_list_type * active_list1 , const active_list_type * active_list2); + void active_list_copy( active_list_type * target , const active_list_type * src); + +UTIL_IS_INSTANCE_HEADER( active_list ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/active_node.hpp b/libres/lib/include/ert/enkf/active_node.hpp new file mode 100644 index 00000000000..a151daf510b --- /dev/null +++ b/libres/lib/include/ert/enkf/active_node.hpp @@ -0,0 +1,29 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'active_node.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 ERT_ACTIVE_NODE_H +#define ERT_ACTIVE_NODE_H + + +typedef struct active_var_struct active_var_type; +typedef struct active_obs_struct active_obs_type; + + +typedef void (active_config_destructor_ftype) (void *); /* A destructor for the (node-spesific) object holding inactive/active information. */ + +#endif diff --git a/libres/lib/include/ert/enkf/analysis_config.hpp b/libres/lib/include/ert/enkf/analysis_config.hpp new file mode 100644 index 00000000000..b507ada9373 --- /dev/null +++ b/libres/lib/include/ert/enkf/analysis_config.hpp @@ -0,0 +1,120 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'analysis_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 ERT_ANALYSIS_CONFIG_H +#define ERT_ANALYSIS_CONFIG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +#include +#include + +#include + +#include +#include + + + + +typedef struct analysis_config_struct analysis_config_type; + +analysis_iter_config_type * analysis_config_get_iter_config( const analysis_config_type * config ); +analysis_module_type * analysis_config_get_module(const analysis_config_type * config , const char * module_name ); +bool analysis_config_has_module(const analysis_config_type * config , const char * module_name ); +void analysis_config_load_internal_module( analysis_config_type * config , const char * symbol_table ); +void analysis_config_load_internal_modules( analysis_config_type * analysis ); +bool analysis_config_get_module_option( const analysis_config_type * config , long flag); +bool analysis_config_load_external_module( analysis_config_type * config , const char * lib_name, const char * user_name); +void analysis_config_load_all_external_modules_from_config ( analysis_config_type * analysis_config, const config_content_type * config); + +PY_USED stringlist_type * analysis_config_alloc_module_names( const analysis_config_type * config ); +const char * analysis_config_get_log_path( const analysis_config_type * config ); +void analysis_config_init( analysis_config_type * analysis , const config_content_type * config); +PY_USED analysis_config_type * analysis_config_alloc_full( + double alpha, + bool merge_observations, + bool rerun, + int rerun_start, + const char * log_path, + double std_cutoff, + bool stop_long_running, + bool single_node_update, + bool std_scale_correlated_obs, + double global_std_scaling, + int max_runtime, + int min_realisations +); +analysis_config_type * analysis_config_alloc_default(void); +analysis_config_type * analysis_config_alloc_load(const char * user_config_file); +analysis_config_type * analysis_config_alloc(const config_content_type * config_content); +void analysis_config_free( analysis_config_type * ); +PY_USED bool analysis_config_get_merge_observations(const analysis_config_type * ); +double analysis_config_get_alpha(const analysis_config_type * config); +PY_USED bool analysis_config_get_rerun(const analysis_config_type * config); +PY_USED int analysis_config_get_rerun_start(const analysis_config_type * config); +void analysis_config_set_rerun(analysis_config_type * config , bool rerun); +void analysis_config_set_rerun_start( analysis_config_type * config , int rerun_start ); +void analysis_config_set_alpha( analysis_config_type * config , double alpha); +void analysis_config_set_merge_observations( analysis_config_type * config , bool merge_observations); +void analysis_config_set_log_path(analysis_config_type * config , const char * log_path ); +void analysis_config_set_std_cutoff( analysis_config_type * config , double std_cutoff ); +double analysis_config_get_std_cutoff( const analysis_config_type * config ); +void analysis_config_add_config_items( config_parser_type * config ); + +bool analysis_config_select_module( analysis_config_type * config , const char * module_name ); +analysis_module_type * analysis_config_get_active_module(const analysis_config_type * config ); +void analysis_config_set_single_node_update(analysis_config_type * config , bool single_node_update); +bool analysis_config_get_single_node_update(const analysis_config_type * config); + +void analysis_config_set_store_PC( analysis_config_type * config , bool store_PC); +bool analysis_config_get_store_PC( const analysis_config_type * config ); +void analysis_config_set_PC_filename( analysis_config_type * config , const char * filename ); +const char * analysis_config_get_PC_filename( const analysis_config_type * config ); +void analysis_config_set_PC_path( analysis_config_type * config , const char * path ); +const char * analysis_config_get_PC_path( const analysis_config_type * config ); +bool analysis_config_have_enough_realisations( const analysis_config_type* config, int realisations, int ensemble_size); +void analysis_config_set_stop_long_running( analysis_config_type * config, bool stop_long_running ); +bool analysis_config_get_stop_long_running( const analysis_config_type * config); +void analysis_config_set_max_runtime( analysis_config_type * config, int max_runtime ); +PY_USED int analysis_config_get_max_runtime( const analysis_config_type * config ); +int analysis_config_get_min_realisations( const analysis_config_type * config ); +PY_USED const char * analysis_config_get_active_module_name( const analysis_config_type * config ); +bool analysis_config_get_std_scale_correlated_obs( const analysis_config_type * config); +void analysis_config_set_std_scale_correlated_obs( analysis_config_type * config, bool std_scale_correlated_obs); + +double analysis_config_get_global_std_scaling(const analysis_config_type * config); +PY_USED void analysis_config_set_global_std_scaling(analysis_config_type * config, double global_std_scaling); +PY_USED void analysis_config_add_module_copy( analysis_config_type * config , + const char * src_name , + const char * target_name); + + UTIL_IS_INSTANCE_HEADER( analysis_config ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libres/lib/include/ert/enkf/analysis_iter_config.hpp b/libres/lib/include/ert/enkf/analysis_iter_config.hpp new file mode 100644 index 00000000000..076a5a74e7b --- /dev/null +++ b/libres/lib/include/ert/enkf/analysis_iter_config.hpp @@ -0,0 +1,49 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'analysis_iter_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 ERT_ANALYSIS_ITER_CONFIG_H +#define ERT_ANALYSIS_ITER_CONFIG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +typedef struct analysis_iter_config_struct analysis_iter_config_type; + + void analysis_iter_config_set_num_iterations( analysis_iter_config_type * config , int num_iterations); + int analysis_iter_config_get_num_iterations( const analysis_iter_config_type * config ); + void analysis_iter_config_set_num_retries_per_iteration( analysis_iter_config_type * config , int num_retries); + int analysis_iter_config_get_num_retries_per_iteration( const analysis_iter_config_type * config ); + void analysis_iter_config_set_case_fmt( analysis_iter_config_type * config, const char * case_fmt); + PY_USED char * analysis_iter_config_get_case_fmt( analysis_iter_config_type * config); + analysis_iter_config_type * analysis_iter_config_alloc(); + PY_USED analysis_iter_config_type * analysis_iter_config_alloc_full(const char * case_fmt, int num_iterations, int num_iter_tries); + void analysis_iter_config_free( analysis_iter_config_type * config ); + const char * analysis_iter_config_iget_case( analysis_iter_config_type * config , int iter); + void analysis_iter_config_add_config_items( config_parser_type * config ); + void analysis_iter_config_init(analysis_iter_config_type * iter_config , const config_content_type * config); + bool analysis_iter_config_case_fmt_set( const analysis_iter_config_type * config ); + bool analysis_iter_config_num_iterations_set( const analysis_iter_config_type * config ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/block_fs_driver.hpp b/libres/lib/include/ert/enkf/block_fs_driver.hpp new file mode 100644 index 00000000000..70c63aa0850 --- /dev/null +++ b/libres/lib/include/ert/enkf/block_fs_driver.hpp @@ -0,0 +1,45 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'block_fs_driver.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 ERT_BLOCK_FS_DRIVER_H +#define ERT_BLOCK_FS_DRIVER_H +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + + typedef struct block_fs_driver_struct block_fs_driver_type; + + bool block_fs_sscanf_key(const char * key , char ** config_key , int * __report_step , int * __iens); + void * block_fs_driver_open(FILE * fstab_stream , const char * mount_point , fs_driver_enum driver_type , bool read_only); + void block_fs_driver_create_fs( FILE * stream , + const char * mount_point , + fs_driver_enum driver_type , + int num_fs , + const char * ens_path_fmt, + const char * filename ); + void block_fs_driver_fskip(FILE * fstab_stream); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/block_obs.hpp b/libres/lib/include/ert/enkf/block_obs.hpp new file mode 100644 index 00000000000..c6315531ee1 --- /dev/null +++ b/libres/lib/include/ert/enkf/block_obs.hpp @@ -0,0 +1,91 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'block_obs.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 ERT_BLOCK_OBS_H +#define ERT_BLOCK_OBS_H + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + + +typedef struct block_obs_struct block_obs_type; + + typedef enum { + SOURCE_FIELD = 10, + SOURCE_SUMMARY = 12 + } block_obs_source_type; + + +block_obs_type * block_obs_alloc_complete(const char * obs_label, + block_obs_source_type source_type , + const stringlist_type * summary_keys , + const void * data_config , + const ecl_grid_type * grid , + int size, + const int * i, + const int * j, + const int * k, + const double * obs_value, + const double * obs_std); + +block_obs_type * block_obs_alloc(const char * obs_key, + const void * data_config , + const ecl_grid_type * grid); + +void block_obs_free(block_obs_type *block_obs); + +PY_USED double block_obs_iget_depth( const block_obs_type * block_obs , int index); +PY_USED int block_obs_iget_i(const block_obs_type *, int index); +PY_USED int block_obs_iget_j(const block_obs_type *, int index); +PY_USED int block_obs_iget_k(const block_obs_type *, int index); +int block_obs_get_size(const block_obs_type * ); +double block_obs_iget_value(const block_obs_type * block_obs, int index ); +double block_obs_iget_std(const block_obs_type * block_obs, int index ); +double block_obs_iget_data( const block_obs_type * block_obs, const void * state , int iobs , node_id_type node_id ); +double block_obs_iget_std_scaling(const block_obs_type * block_obs, int index ); +PY_USED void block_obs_update_std_scale(block_obs_type * block_obs, double scale_factor, const active_list_type * active_list); +void block_obs_append_field_obs( block_obs_type * block_obs , int i , int j , int k , double value , double std); +void block_obs_append_summary_obs( block_obs_type * block_obs , int i , int j , int k , const char * sum_key , double value , double std); + +VOID_FREE_HEADER(block_obs); +VOID_GET_OBS_HEADER(block_obs); +UTIL_IS_INSTANCE_HEADER(block_obs); +VOID_MEASURE_HEADER(block_obs); +VOID_USER_GET_OBS_HEADER(block_obs); +VOID_CHI2_HEADER(block_obs); +VOID_UPDATE_STD_SCALE_HEADER(block_obs); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/callback_arg.hpp b/libres/lib/include/ert/enkf/callback_arg.hpp new file mode 100644 index 00000000000..8eaa20c8c5a --- /dev/null +++ b/libres/lib/include/ert/enkf/callback_arg.hpp @@ -0,0 +1,52 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'callback_arg.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 CALLBACK_ARG_H +#define CALLBACK_ARG_H + +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct callback_arg_struct callback_arg_type; + +struct callback_arg_struct { + UTIL_TYPE_ID_DECLARATION; + const res_config_type * res_config; + run_arg_type * run_arg; + rng_type * rng; +}; + + +callback_arg_type * callback_arg_alloc(const res_config_type * res_config, + run_arg_type * run_arg, + rng_type * rng); + +UTIL_IS_INSTANCE_HEADER( callback_arg ); +UTIL_SAFE_CAST_HEADER( callback_arg ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/cases_config.hpp b/libres/lib/include/ert/enkf/cases_config.hpp new file mode 100644 index 00000000000..20b2566da85 --- /dev/null +++ b/libres/lib/include/ert/enkf/cases_config.hpp @@ -0,0 +1,39 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'cases_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 ERT_CASES_CONFIG_H +#define ERT_CASES_CONFIG_H + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef struct cases_config_struct cases_config_type; + + bool cases_config_set_int( cases_config_type * config , const char * var_name, int num_iterations); + int cases_config_get_iteration_number( const cases_config_type * config ); + void cases_config_fwrite( cases_config_type * config , const char * filename ); + void cases_config_fread( cases_config_type * config , const char * filename); + cases_config_type * cases_config_alloc(); + void cases_config_free( cases_config_type * config ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/config_keys.hpp b/libres/lib/include/ert/enkf/config_keys.hpp new file mode 100644 index 00000000000..1ed204fefc4 --- /dev/null +++ b/libres/lib/include/ert/enkf/config_keys.hpp @@ -0,0 +1,282 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'config_keys.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 ERT_CONFIG_KEYS_H +#define ERT_CONFIG_KEYS_H +#ifdef __cplusplus +extern "C" { +#endif + +/* These keys are used as options in KEY:VALUE statements */ +#define BASE_SURFACE_KEY "BASE_SURFACE" +#define DEFINE_KEY "DEFINE" +#define DYNAMIC_KEY "DYNAMIC" +#define ECL_FILE_KEY "ECL_FILE" +#define FORWARD_INIT_KEY "FORWARD_INIT" +#define GENERAL_KEY "GENERAL" +#define INCLUDE_KEY "INCLUDE" +#define INIT_FILES_KEY "INIT_FILES" +#define INIT_TRANSFORM_KEY "INIT_TRANSFORM" +#define INPUT_FORMAT_KEY "INPUT_FORMAT" +#define INPUT_TRANSFORM_KEY "INPUT_TRANSFORM" +#define KEY_KEY "KEY" +#define MAX_KEY "MAX" +#define MIN_KEY "MIN" +#define MIN_STD_KEY "MIN_STD" +#define OUTPUT_FILE_KEY "OUTPUT_FILE" +#define OUTPUT_FORMAT_KEY "OUTPUT_FORMAT" +#define OUTPUT_TRANSFORM_KEY "OUTPUT_TRANSFORM" +#define PARAMETER_KEY "PARAMETER" +#define REPORT_STEPS_KEY "REPORT_STEPS" +#define RESULT_FILE_KEY "RESULT_FILE" +#define TEMPLATE_KEY "TEMPLATE" +#define PRED_KEY "PRED_KEY" + + +#define ANALYSIS_COPY_KEY "ANALYSIS_COPY" +#define ANALYSIS_LOAD_KEY "ANALYSIS_LOAD" +#define ANALYSIS_SET_VAR_KEY "ANALYSIS_SET_VAR" +#define ANALYSIS_SELECT_KEY "ANALYSIS_SELECT" +#define CONTAINER_KEY "CONTAINER" +#define DATA_ROOT_KEY "DATA_ROOT" +#define DATA_FILE_KEY "DATA_FILE" +#define DATA_KW_KEY "DATA_KW" +#define ECLBASE_KEY "ECLBASE" +#define END_DATE_KEY "END_DATE" +#define ENKF_ALPHA_KEY "ENKF_ALPHA" +#define ENKF_CROSS_VALIDATION_KEY "ENKF_CROSS_VALIDATION" +#define ENKF_MERGE_OBSERVATIONS_KEY "ENKF_MERGE_OBSERVATIONS" +#define ENKF_RERUN_KEY "ENKF_RERUN" +#define ENKF_TRUNCATION_KEY "ENKF_TRUNCATION" +#define ENSPATH_KEY "ENSPATH" +#define ITER_CASE_KEY "ITER_CASE" +#define ITER_COUNT_KEY "ITER_COUNT" +#define ITER_RETRY_COUNT_KEY "ITER_RETRY_COUNT" +#define FIELD_KEY "FIELD" +#define FORWARD_MODEL_KEY "FORWARD_MODEL" +#define GEN_DATA_KEY "GEN_DATA" +#define GEN_KW_KEY "GEN_KW" +#define GEN_KW_TAG_FORMAT_KEY "GEN_KW_TAG_FORMAT" +#define GEN_KW_EXPORT_NAME_KEY "GEN_KW_EXPORT_NAME" +#define GEN_PARAM_KEY "GEN_PARAM" +#define GRID_KEY "GRID" +#define HISTORY_SOURCE_KEY "HISTORY_SOURCE" +#define INSTALL_JOB_KEY "INSTALL_JOB" +#define INSTALL_JOB_DIRECTORY_KEY "INSTALL_JOB_DIRECTORY" +#define JOB_SCRIPT_KEY "JOB_SCRIPT" +#define JOBNAME_KEY "JOBNAME" +#define LICENSE_PATH_KEY "LICENSE_PATH" +#define LOG_FILE_KEY "LOG_FILE" +#define LOG_LEVEL_KEY "LOG_LEVEL" +#define MAX_RESAMPLE_KEY "MAX_RESAMPLE" +#define MAX_SUBMIT_KEY "MAX_SUBMIT" +#define NUM_REALIZATIONS_KEY "NUM_REALIZATIONS" +#define MIN_REALIZATIONS_KEY "MIN_REALIZATIONS" +#define OBS_CONFIG_KEY "OBS_CONFIG" +#define QUEUE_SYSTEM_KEY "QUEUE_SYSTEM" +#define QUEUE_OPTION_KEY "QUEUE_OPTION" +#define HOOK_WORKFLOW_KEY "HOOK_WORKFLOW" +#define REFCASE_KEY "REFCASE" +#define REFCASE_LIST_KEY "REFCASE_LIST" +#define RERUN_START_KEY "RERUN_START" +#define RUNPATH_FILE_KEY "RUNPATH_FILE" +#define RUNPATH_KEY "RUNPATH" +#define RUN_TEMPLATE_KEY "RUN_TEMPLATE" +#define RFTPATH_KEY "RFTPATH" +#define SCHEDULE_PREDICTION_FILE_KEY "SCHEDULE_PREDICTION_FILE" +#define SETENV_KEY "SETENV" +#define SIMULATION_JOB_KEY "SIMULATION_JOB" +#define STD_CUTOFF_KEY "STD_CUTOFF" +#define SUMMARY_KEY "SUMMARY" +#define SURFACE_KEY "SURFACE" +#define UPDATE_LOG_PATH_KEY "UPDATE_LOG_PATH" +#define UPDATE_PATH_KEY "UPDATE_PATH" +#define SINGLE_NODE_UPDATE_KEY "SINGLE_NODE_UPDATE" +#define RANDOM_SEED_KEY "RANDOM_SEED" +#define UMASK_KEY "UMASK" +#define WORKFLOW_JOB_DIRECTORY_KEY "WORKFLOW_JOB_DIRECTORY" +#define LOAD_WORKFLOW_KEY "LOAD_WORKFLOW" +#define LOAD_WORKFLOW_JOB_KEY "LOAD_WORKFLOW_JOB" +#define RUN_MODE_PRE_SIMULATION_NAME "PRE_SIMULATION" +#define RUN_MODE_POST_SIMULATION_NAME "POST_SIMULATION" +#define RUN_MODE_PRE_UPDATE_NAME "PRE_UPDATE" +#define RUN_MODE_POST_UPDATE_NAME "POST_UPDATE" +#define RUN_MODE_PRE_FIRST_UPDATE_NAME "PRE_FIRST_UPDATE" +#define STOP_LONG_RUNNING_KEY "STOP_LONG_RUNNING" +#define MAX_RUNTIME_KEY "MAX_RUNTIME" +#define TIME_MAP_KEY "TIME_MAP" +#define STD_SCALE_CORRELATED_OBS_KEY "STD_SCALE_CORRELATED_OBS" +#define UPDATE_SETTING_KEY "UPDATE_SETTINGS" +#define NUM_CPU_KEY "NUM_CPU" + +#define CONFIG_DIRECTORY_KEY "CONFIG_DIRECTORY" +#define RES_CONFIG_FILE_KEY "RES_CONFIG_FILE" + +#define SLURM_SBATCH_OPTION "SBATCH" +#define SLURM_SCANCEL_OPTION "SCANCEL" +#define SLURM_SCONTROL_OPTION "SCONTROL" +#define SLURM_SQUEUE_OPTION "SQUEUE" +#define SLURM_PARTITION_OPTION "PARTITION" +#define SLURM_SQUEUE_TIMEOUT_OPTION "SQUEUE_TIMEOUT" + +// Observe that the SLURM_MAX_RUNTIME_OPTION expects a time limit in seconds, +// whereas slurm uses a time limit in minutes +#define SLURM_MAX_RUNTIME_OPTION "MAX_RUNTIME" +#define SLURM_MEMORY_OPTION "MEMORY" +#define SLURM_MEMORY_PER_CPU_OPTION "MEMORY_PER_CPU" + + +// For the EXCLUDE and INCLUDE host options the slurm driver +// maintains an internal list of hostnames, and the option can be called +// repeatedly. It is possible to add multiple hosts separated by space or comma +// in one option call: +// +// QUEUE_OPTION SLURM EXCLUDE_HOST host1,host2,host3 +// QUEUE_OPTION SLURM EXCLUDE_HOST host5 host6,host7 +#define SLURM_EXCLUDE_HOST_OPTION "EXCLUDE_HOST" +#define SLURM_INCLUDE_HOST_OPTION "INCLUDE_HOST" + +// All of these functions are probably only used by Python libraries + const char * config_keys_get_config_directory_key(); + const char * config_keys_get_config_file_key(); + const char * config_keys_get_queue_system_key(); + const char * config_keys_get_run_template_key(); + const char * config_keys_get_gen_kw_key(); + const char * config_keys_get_queue_option_key(); + const char * config_keys_get_install_job_key(); + const char * config_keys_get_install_job_directory_key(); + + const char * config_keys_get_log_file_key(); + const char * config_keys_get_log_level_key(); + const char * config_keys_get_update_log_path_key(); + const char * config_keys_get_summary_key(); + const char * config_keys_get_max_runtime_key(); + const char * config_keys_get_min_realizations_key(); + const char * config_keys_get_umask_key(); + const char * config_keys_get_data_kw_key(); + const char * config_keys_get_runpath_file_key(); + const char * config_keys_get_max_submit_key(); + const char * config_keys_get_gen_data_key(); + + /* ************* ECL config ************* */ + const char * config_keys_get_eclbase_key(); + const char * config_keys_get_data_file_key(); + const char * config_keys_get_grid_key(); + const char * config_keys_get_refcase_key(); + const char * config_keys_get_refcase_list_key(); + const char * config_keys_get_end_date_key(); + const char * config_keys_get_schedule_prediction_file_key(); + /* ************* ECL config ************* */ + + const char * config_keys_get_result_file(); + const char * config_keys_get_report_steps(); + const char * config_keys_get_input_format(); + const char * config_keys_get_ecl_file(); + const char * config_keys_get_output_format(); + const char * config_keys_get_init_files(); + const char * config_keys_get_random_seed(); + const char * config_keys_get_license_path_key(); + const char * config_keys_get_setenv_key(); + const char * config_keys_get_job_script_key(); + const char * config_keys_get_num_cpu_key(); + const char * config_keys_get_define_key(); + const char * config_keys_get_load_workflow_key(); + const char * config_keys_get_load_workflow_job_key(); + const char * config_keys_get_workflow_job_directory_key(); + + //*********analysis_iter_config keys************// + const char * config_keys_get_iter_case_key(); + const char * config_keys_get_iter_count_key(); + const char * config_keys_get_iter_retry_count_key(); + //*********analysis_iter_config keys************// + +/////***** Analysis config keys*******//////////// + const char * config_keys_get_alpha(); + const char * config_keys_get_std_cutoff(); + const char * config_keys_get_stop_long_running(); + const char * config_keys_get_single_node_update(); + const char * config_keys_get_std_scale_correlated_obs(); + const char * config_keys_get_rerun(); + const char * config_keys_get_rerun_start(); + const char * config_keys_get_merge_observations(); + const char * config_keys_get_analysis_load(); + const char * config_keys_get_analysis_copy(); + const char * config_keys_get_analysis_select(); + const char * config_keys_get_analysis_set_var(); +/////***** Analysis config keys*******//////////// + + // hook_manager config keys + const char * config_keys_get_hook_workflow_key(); + // hook_manager config keys + + /* ************* Model config ************* */ + const char * config_keys_get_forward_model_key(); + const char * config_keys_get_jobname_key(); + const char * config_keys_get_simulation_job_key(); + const char * config_keys_get_runpath_key(); + const char * config_keys_get_num_realizations_key(); + const char * config_keys_get_history_source_key(); + const char * config_keys_get_obs_config_key(); + const char * config_keys_get_time_map_key(); + const char * config_keys_get_enspath_key(); + const char * config_keys_get_max_resample_key(); + const char * config_keys_get_data_root_key(); + const char * config_keys_get_rftpath_key(); + const char * config_keys_get_gen_kw_export_name_key(); + /* ************* Model config ************* */ + + /* ************* Ensemble config ************* */ + const char * config_keys_get_gen_param_key(); + const char * config_keys_get_forward_init_key(); + const char * config_keys_get_min_std_key(); + const char * config_keys_get_template_key(); + const char * config_keys_get_key_key(); + const char * config_keys_get_kw_tag_format_key(); + const char * config_keys_get_surface_key(); + const char * config_keys_get_base_surface_key(); + const char * config_keys_get_field_key(); + const char * config_keys_get_init_transform_key(); + const char * config_keys_get_input_transform_key(); + const char * config_keys_get_output_transform_key(); + const char * config_keys_get_min_key(); + const char * config_keys_get_max_key(); + const char * config_keys_get_parameter_key(); + const char * config_keys_get_general_key(); + const char * config_keys_get_pred_key(); + const char * config_keys_get_container_key(); + /* ************* Ensemble config ************* */ + + /* ************* Slurm options **************** */ + const char * config_keys_get_slurm_sbatch_option(); + const char * config_keys_get_slurm_scancel_option(); + const char * config_keys_get_slurm_scontrol_option(); + const char * config_keys_get_slurm_squeue_option(); + const char * config_keys_get_slurm_partition_option(); + const char * config_keys_get_slurm_squeue_timeout_option(); + const char * config_keys_get_slurm_max_runtime_option(); + const char * config_keys_get_slurm_memory_option(); + const char * config_keys_get_slurm_memory_per_cpu_option(); + const char * config_keys_get_slurm_exclude_host_option(); + const char * config_keys_get_slurm_include_host_option(); + /* ************* Slurm options **************** */ + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/config_parser.hpp b/libres/lib/include/ert/enkf/config_parser.hpp new file mode 100644 index 00000000000..232efb194ad --- /dev/null +++ b/libres/lib/include/ert/enkf/config_parser.hpp @@ -0,0 +1,22 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'config_parser.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 ERT_CONFIG_PARSER_H +#define ERT_CONFIG_PARSER_H + +#endif diff --git a/libres/lib/include/ert/enkf/container.hpp b/libres/lib/include/ert/enkf/container.hpp new file mode 100644 index 00000000000..b836f0f5240 --- /dev/null +++ b/libres/lib/include/ert/enkf/container.hpp @@ -0,0 +1,43 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'container.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 ERT_CONTAINER_H +#define ERT_CONTAINER_H + +#ifdef __cplusplus +extern "C" { +#endif +#include + + + typedef struct container_struct container_type; + + void container_add_node(container_type * container , void * child_node ); + const void * container_iget_node(const container_type * container , int index); + int container_get_size( const container_type * container ); + void container_assert_size( const container_type * container ); + + VOID_ALLOC_HEADER(container); + VOID_FREE_HEADER(container); + UTIL_IS_INSTANCE_HEADER(container); + UTIL_SAFE_CAST_HEADER_CONST(container); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/container_config.hpp b/libres/lib/include/ert/enkf/container_config.hpp new file mode 100644 index 00000000000..b611e98e39b --- /dev/null +++ b/libres/lib/include/ert/enkf/container_config.hpp @@ -0,0 +1,49 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'container_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 ERT_CONTAINER_CONFIG_H +#define ERT_CONTAINER_CONFIG_H + + + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct container_config_struct container_config_type; + + + container_config_type * container_config_alloc( const char * key ); + void container_config_free( container_config_type * container ); + void container_config_add_node( container_config_type * container, const enkf_config_node_type * config_node); + int container_config_get_size( const container_config_type * container_config ); + + UTIL_IS_INSTANCE_HEADER(container_config); + UTIL_SAFE_CAST_HEADER_CONST(container_config); + GET_DATA_SIZE_HEADER(container); + VOID_GET_DATA_SIZE_HEADER(container); + VOID_CONFIG_FREE_HEADER(container); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/data_ranking.hpp b/libres/lib/include/ert/enkf/data_ranking.hpp new file mode 100644 index 00000000000..8f73c4e55d7 --- /dev/null +++ b/libres/lib/include/ert/enkf/data_ranking.hpp @@ -0,0 +1,51 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'data_ranking.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 ERT_DATA_RANKING_H +#define ERT_DATA_RANKING_H + +#ifdef __cplusplus +extern "C" { +#endif +#include +#include + +#include +#include + +#include + + typedef struct data_ranking_struct data_ranking_type; + + UTIL_IS_INSTANCE_HEADER( data_ranking ); + UTIL_SAFE_CAST_HEADER(data_ranking); + + const perm_vector_type * data_ranking_get_permutation( const data_ranking_type * data_ranking ); + data_ranking_type * data_ranking_alloc( bool sort_increasing , int ens_size , const char * user_key , const char * key_index , enkf_fs_type * fs , const enkf_config_node_type * config_node , int step ); + void data_ranking_free__( void * arg ); + void data_ranking_display( const data_ranking_type * data_ranking , FILE * stream); + +#ifdef __cplusplus +} +#endif +#endif + + + + + diff --git a/libres/lib/include/ert/enkf/ecl_config.hpp b/libres/lib/include/ert/enkf/ecl_config.hpp new file mode 100644 index 00000000000..f39c914d98f --- /dev/null +++ b/libres/lib/include/ert/enkf/ecl_config.hpp @@ -0,0 +1,95 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'ecl_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 ERT_ECL_CONFIG_H +#define ERT_ECL_CONFIG_H +#include + +#include + +#include +#include + +#include +#include +#include + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct ecl_config_struct ecl_config_type; + + const char * ecl_config_get_data_file(const ecl_config_type * ); + void ecl_config_set_data_file( ecl_config_type * ecl_config , const char * data_file); + ui_return_type * ecl_config_validate_data_file(const ecl_config_type * ecl_config, const char * data_file); + + ui_return_type * ecl_config_validate_eclbase( const ecl_config_type * ecl_config , const char * eclbase_fmt ); + + void ecl_config_set_grid( ecl_config_type * ecl_config , const char * grid_file ); + const char * ecl_config_get_gridfile( const ecl_config_type * ecl_config ); + ecl_grid_type * ecl_config_get_grid(const ecl_config_type * ); + ui_return_type * ecl_config_validate_grid( const ecl_config_type * ecl_config , const char * grid_file ); + + bool ecl_config_load_refcase( ecl_config_type * ecl_config , const char * refcase); + ui_return_type * ecl_config_validate_refcase( const ecl_config_type * ecl_config , const char * refcase ); + const ecl_sum_type * ecl_config_get_refcase(const ecl_config_type * ecl_config); + bool ecl_config_has_refcase( const ecl_config_type * ecl_config ); + ecl_refcase_list_type * ecl_config_get_refcase_list( const ecl_config_type * ecl_config ); + + /*****************************************************************/ + + bool ecl_config_active( const ecl_config_type * config ); + time_t ecl_config_get_end_date( const ecl_config_type * ecl_config ); + time_t ecl_config_get_start_date( const ecl_config_type * ecl_config ); + + const char * ecl_config_get_schedule_prediction_file( const ecl_config_type * ecl_config ); + void ecl_config_set_schedule_prediction_file( ecl_config_type * ecl_config , const char * schedule_prediction_file ); + + int ecl_config_get_num_cpu( const ecl_config_type * ecl_config ); + void ecl_config_init( ecl_config_type * ecl_config , const config_content_type * config); + void ecl_config_free( ecl_config_type *); + + bool ecl_config_get_formatted(const ecl_config_type * ); + bool ecl_config_get_unified_restart(const ecl_config_type * ); + int ecl_config_get_last_history_restart( const ecl_config_type * ); + bool ecl_config_can_restart( const ecl_config_type * ecl_config ); + void ecl_config_assert_restart( const ecl_config_type * ecl_config ); + const char * ecl_config_get_refcase_name( const ecl_config_type * ecl_config); + ecl_config_type * ecl_config_alloc(const config_content_type * config_content); + PY_USED ecl_config_type * ecl_config_alloc_full(bool have_eclbase, + char * data_file, + ecl_grid_type * grid, + char * refcase_default, + stringlist_type * ref_case_list, + time_t end_date, + char * sched_prediction_file + ); + void ecl_config_add_config_items( config_parser_type * config ); + const char * ecl_config_get_depth_unit( const ecl_config_type * ecl_config ); + const char * ecl_config_get_pressure_unit( const ecl_config_type * ecl_config ); + bool ecl_config_have_eclbase(const ecl_config_type * ecl_config); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/ecl_refcase_list.hpp b/libres/lib/include/ert/enkf/ecl_refcase_list.hpp new file mode 100644 index 00000000000..a336e0f8dbe --- /dev/null +++ b/libres/lib/include/ert/enkf/ecl_refcase_list.hpp @@ -0,0 +1,49 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'ecl_refcase_list.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 ERT_ECL_REFCASE_LIST_H +#define ERT_ECL_REFCASE_LIST_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + + + typedef struct ecl_refcase_list_struct ecl_refcase_list_type; + + ecl_refcase_list_type * ecl_refcase_list_alloc( ); + void ecl_refcase_list_free( ecl_refcase_list_type * refcase_list ); + + bool ecl_refcase_list_has_default( ecl_refcase_list_type * refcase_list ); + const ecl_sum_type * ecl_refcase_list_get_default( ecl_refcase_list_type * refcase_list ); + bool ecl_refcase_list_set_default( ecl_refcase_list_type * refcase_list , const char * default_case); + int ecl_refcase_list_get_size(ecl_refcase_list_type * refcase_list ); + int ecl_refcase_list_add_matching( ecl_refcase_list_type * refcase_list , const char * glob_string); + int ecl_refcase_list_add_case( ecl_refcase_list_type * refcase_list , const char * case_name); + const char * ecl_refcase_list_iget_pathcase( ecl_refcase_list_type * refcase_list , int index); + const ecl_sum_type * ecl_refcase_list_iget_case( ecl_refcase_list_type * refcase_list , int index); + const ecl_sum_type * ecl_refcase_list_get_case( ecl_refcase_list_type * refcase_list , const char * case_name); + bool ecl_refcase_list_has_case( ecl_refcase_list_type * refcase_list , const char * case_name); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/enkf.hpp b/libres/lib/include/ert/enkf/enkf.hpp new file mode 100644 index 00000000000..430ffcb89cc --- /dev/null +++ b/libres/lib/include/ert/enkf/enkf.hpp @@ -0,0 +1,29 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file '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. +*/ + +#define SPLASH_LENGTH 8 +const char *splash_text[SPLASH_LENGTH] = { +" 8888888888 888 d8P 8888888888", +" 888 888 d8P 888 ", +" 888 888 d8P 888 ", +" 8888888 88888b. 888d88K 8888888 ", +" 888 888 '88b 8888888b 888 ", +" 888 888 888 888 Y88b 888 ", +" 888 888 888 888 Y88b 888 ", +" 8888888888 888 888 888 Y88b 888 "}; + diff --git a/libres/lib/include/ert/enkf/enkf_analysis.hpp b/libres/lib/include/ert/enkf/enkf_analysis.hpp new file mode 100644 index 00000000000..4602655d672 --- /dev/null +++ b/libres/lib/include/ert/enkf/enkf_analysis.hpp @@ -0,0 +1,57 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'enkf_analysis.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 ERT_ENKF_ANALYSIS_H +#define ERT_ENKF_ANALYSIS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include + +#include + + + +void enkf_analysis_fprintf_obs_summary(const obs_data_type * obs_data , + const meas_data_type * meas_data , + const int_vector_type * step_list , + const char * ministep_name , + FILE * stream ); + +void enkf_analysis_deactivate_outliers(obs_data_type * obs_data , + meas_data_type * meas_data , + double std_cutoff , + double alpha, + bool verbose); + +PY_USED void enkf_analysis_deactivate_std_zero(obs_data_type * obs_data , + meas_data_type * meas_data, + bool verbose); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libres/lib/include/ert/enkf/enkf_config_node.hpp b/libres/lib/include/ert/enkf/enkf_config_node.hpp new file mode 100644 index 00000000000..e61b8e3d935 --- /dev/null +++ b/libres/lib/include/ert/enkf/enkf_config_node.hpp @@ -0,0 +1,217 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'enkf_config_node.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 ERT_ENKF_CONFIG_NODE_H +#define ERT_ENKF_CONFIG_NODE_H + +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + typedef void (config_free_ftype) (void *); + typedef int (get_data_size_ftype) (const void *); + typedef void (config_fprintf_ftype) (const void * , enkf_var_type , FILE * ); + + typedef struct enkf_config_node_struct enkf_config_node_type; + typedef struct enkf_node_struct enkf_node_type; + + + bool enkf_config_node_has_vector( const enkf_config_node_type * node , enkf_fs_type * fs , int iens); + bool enkf_config_node_has_node( const enkf_config_node_type * node , enkf_fs_type * fs , node_id_type node_id); + bool enkf_config_node_vector_storage( const enkf_config_node_type * config_node); + + void enkf_config_node_update_min_std( enkf_config_node_type * config_node , const char * min_std_file ); + + enkf_config_node_type * enkf_config_node_alloc_GEN_PARAM( const char * node_key , + bool forward_init , + gen_data_file_format_type input_format , + gen_data_file_format_type output_format , + const char * init_file_fmt , + const char * ert_outfile_fmt); + + + enkf_config_node_type * enkf_config_node_alloc_GEN_DATA_result( const char * key , + gen_data_file_format_type input_format, + const char * enkf_infile_fmt ); + + enkf_config_node_type * enkf_config_node_new_surface( const char * key , bool forward_init); + + + void enkf_config_node_update_surface( enkf_config_node_type * config_node , + const char * base_surface , + const char * init_file_fmt , + const char * output_file , + const char * min_std_file ); + + + void enkf_config_node_update_gen_kw( enkf_config_node_type * config_node , + const char * enkf_outfile_fmt , /* The include file created by ERT for the forward model. */ + const char * template_file , + const char * parameter_file , + const char * min_std_file , + const char * init_file_fmt ); + + enkf_config_node_type * enkf_config_node_alloc(enkf_var_type , + ert_impl_type , + bool , + const char * , + const char * , + const char * , + const char * , + void * ); + + + enkf_config_node_type * enkf_config_node_alloc_summary( const char * key , load_fail_type load_fail); + + + + void enkf_config_node_update_parameter_field( enkf_config_node_type * config_node , + const char * enkf_outfile_fmt , + const char * init_file_fmt , + const char * min_std_file , + int truncation , double value_min , double value_max , + const char * init_transform , + const char * output_transform ); + + + enkf_config_node_type * enkf_config_node_alloc_GEN_DATA_everest( const char * key , + const char * result_file_fmt, + const int_vector_type * report_steps); + + void enkf_config_node_update_general_field( enkf_config_node_type * config_node , + const char * enkf_outfile_fmt , + const char * enkf_infile_fmt , + const char * init_file_fmt , + const char * min_std_file , + int truncation , + double value_min , + double value_max , + const char * init_transform , + const char * input_transform , + const char * output_transform ); + + + +/*****************************************************************/ + + enkf_config_node_type * enkf_config_node_new_gen_kw( const char * key , const char * tag_fmt , bool forward_init); + enkf_config_node_type * enkf_config_node_alloc_field( const char * key , ecl_grid_type * ecl_grid, field_trans_table_type * trans_table, bool forward_init); + int enkf_config_node_get_data_size( const enkf_config_node_type * node , int report_step); + char * enkf_config_node_alloc_infile(const enkf_config_node_type * , int ); + char * enkf_config_node_alloc_outfile(const enkf_config_node_type * , int ); + int enkf_config_node_get_num_obs( const enkf_config_node_type * config_node ); + int enkf_config_node_load_obs( const enkf_config_node_type * config_node , enkf_obs_type * enkf_obs ,const char * key_index , int obs_count , time_t * sim_time , double * y , double * std); + const stringlist_type * enkf_config_node_get_obs_keys(const enkf_config_node_type *); + void enkf_config_node_add_obs_key(enkf_config_node_type * , const char * ); + void enkf_config_node_clear_obs_keys(enkf_config_node_type * config_node); + void enkf_config_node_free(enkf_config_node_type * ); + bool enkf_config_node_include_type(const enkf_config_node_type * , int ); + bool enkf_config_node_include_type(const enkf_config_node_type * , int); + ert_impl_type enkf_config_node_get_impl_type(const enkf_config_node_type *); + enkf_var_type enkf_config_node_get_var_type(const enkf_config_node_type *); + void * enkf_config_node_get_ref(const enkf_config_node_type * ); + const char * enkf_config_node_get_key(const enkf_config_node_type * ); + const char * enkf_config_node_get_FIELD_fill_file(enkf_config_node_type * config_node, const path_fmt_type * runpath_fmt); + const char * enkf_config_node_get_min_std_file( const enkf_config_node_type * config_node ); + const char * enkf_config_node_get_enkf_outfile( const enkf_config_node_type * conifg_node ); + const char * enkf_config_node_get_enkf_infile( const enkf_config_node_type * config_node ); + const char * enkf_config_node_get_init_file_fmt( const enkf_config_node_type * config_node ); + char * enkf_config_node_alloc_initfile( const enkf_config_node_type * node , const char * path , int iens); + + void enkf_config_node_set_internalize(enkf_config_node_type * node, int report_step); + bool enkf_config_node_internalize(const enkf_config_node_type * node, int report_step); + + void enkf_config_node_fprintf_config( const enkf_config_node_type * config_node , FILE * stream ); + enkf_config_node_type * enkf_config_node_container_iget( const enkf_config_node_type * node , int index); + int enkf_config_node_container_size( const enkf_config_node_type * node ); + + enkf_config_node_type * enkf_config_node_new_container( const char * key ); + void enkf_config_node_update_container( enkf_config_node_type * config_node , const enkf_config_node_type * child_node); + PY_USED const char * enkf_config_node_iget_container_key( const enkf_config_node_type * config_node , int index); + /* + The enkf_node_free() function declaration is in the enkf_config_node.h header, + because the enkf_config_node needs to know how to free the min_std node. + */ + void enkf_node_free(enkf_node_type *enkf_node); + const enkf_node_type * enkf_config_node_get_min_std( const enkf_config_node_type * config_node ); + + bool enkf_config_node_use_forward_init(const enkf_config_node_type * config_node); + + /*****************************************************************/ + + void enkf_config_node_add_GEN_DATA_config_schema( config_parser_type * config ); + void enkf_config_node_add_GEN_PARAM_config_schema( config_parser_type * config ); + enkf_config_node_type * enkf_config_node_alloc_GEN_PARAM_from_config( const config_content_node_type * config_node ); + enkf_config_node_type * enkf_config_node_alloc_GEN_DATA_from_config( const config_content_node_type * node ); + + /* ************* FULL ALLOCS ************* */ + PY_USED enkf_config_node_type * enkf_config_node_alloc_GEN_PARAM_full( const char * node_key , + bool forward_init , + gen_data_file_format_type input_format , + gen_data_file_format_type output_format , + const char * init_file_fmt , + const char * ecl_file, + const char * min_std_file, + const char * template_file, + const char * data_key); + + PY_USED enkf_config_node_type * enkf_config_node_alloc_GEN_DATA_full( const char * node_key , + const char * result_file, + gen_data_file_format_type input_format , + const int_vector_type * report_steps, + const char * ecl_file, + const char * init_file_fmt , + const char * template_file, + const char * data_key); + + PY_USED enkf_config_node_type * enkf_config_node_alloc_GEN_KW_full(const char * node_key, + bool forward_init, + const char * gen_kw_format, + const char * template_file, + const char * enkf_outfile, + const char * parameter_file, + const char * min_std_file, + const char * init_file_fmt); + + PY_USED enkf_config_node_type * enkf_config_node_alloc_SURFACE_full(const char * node_key, + bool forward_init, + const char * output_file, + const char * base_surface, + const char * min_std_file, + const char * init_file_fmt); + +UTIL_IS_INSTANCE_HEADER( enkf_config_node ); +UTIL_SAFE_CAST_HEADER(enkf_config_node); +VOID_FREE_HEADER(enkf_config_node); +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/enkf_config_private.hpp b/libres/lib/include/ert/enkf/enkf_config_private.hpp new file mode 100644 index 00000000000..062ab759f2a --- /dev/null +++ b/libres/lib/include/ert/enkf/enkf_config_private.hpp @@ -0,0 +1,27 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'enkf_config_private.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. +*/ + +struct enkf_config_struct { + int ens_size; + hash_type *config_hash; + hash_type *obs_hash; + bool endian_swap; + path_fmt_type *run_path; + int Nwells; + char **well_list; +}; diff --git a/libres/lib/include/ert/enkf/enkf_defaults.hpp b/libres/lib/include/ert/enkf/enkf_defaults.hpp new file mode 100644 index 00000000000..c3f8091b3b2 --- /dev/null +++ b/libres/lib/include/ert/enkf/enkf_defaults.hpp @@ -0,0 +1,178 @@ +/** + This file contains verious default values which are compiled into + the enkf executable. Everytime you add something here you should + ask yourself: + + * Can we determine this automagically. + * Should the user be required to enter this information. + +*/ + +#ifndef ERT_ENKF_DEFAULT +#define ERT_ENKF_DEFAULT +#include + +#define DEFAULT_LOG_FILE "log.txt" + +#define DEFAULT_RUNPATH_KEY "DEFAULT_RUNPATH" + + +/** + The format string used when creating "search-strings" which should + be replaced in the gen_kw template files - MUST contain one %s + placeholder which will be replaced with the parameter name. +*/ +#define DEFAULT_GEN_KW_TAG_FORMAT "<%s>" + + +/** + Default file name for export file for GEN_KW parameters +*/ +#ifdef __cplusplus +extern "C" { +#endif +#define DEFAULT_GEN_KW_EXPORT_NAME "parameters" + + +const char * enkf_defaults_get_default_gen_kw_export_name(); + +#ifdef __cplusplus +} +#endif +/** + The format string used when creating search strings from user input + with the 'DATA_KW' keyword. The format string must contain one '%s' + placeholder which will be replaced with the user supplied key; can + be just '%s' which means no ERT induced transformations. + + Example: + ------- + DATA_KW KEY1 VALUE1 + + DATA_KW_FORMAT = [<%s>] + + In this case all occurences of '[]' will be replaced with + 'VALUE1'. The DATA_KW_TAG_FORMAT used on user supplied tag keys can + in principle be different from the internal format, but this can of + course be confusing. The internal format is hard linked to job + description files, and can not easily be changed. +*/ + +//#define DEFAULT_DATA_KW_TAG_FORMAT "<%s>" + + +/** + This is the format used for tagging the internal variables like + IENS, and ECLBASE. These values are written into the various job + description files, care should therefor be taken before changing + the value of this variable. It is not user modifiable, and can only + be changed by recompiling. + +*/ +#define INTERNAL_DATA_KW_TAG_FORMAT "<%s>" + + +/** + The default number of block_fs instances allocated. +*/ + +/* Eclipse IO related stuff */ +#define DEFAULT_FORMATTED false +#define DEFAULT_UNIFIED false + + + +/* + Where the history is coming from - default value for config item: + HISTORY_SOURCE Observe that the function: + model_config_set_history_source() does currently not handle a + default value different from SCHEDULE. +*/ +#define DEFAULT_HISTORY_SOURCE REFCASE_HISTORY + + +#define DEFAULT_MAX_SUBMIT 2 /* The number of times to resubmit - default value for config item: MAX_SUBMIT */ +#define DEFAULT_MAX_INTERNAL_SUBMIT 1 /** Attached to keyword : MAX_RETRY */ + + + + +/* + Defaults for the EnKF analysis. The analysis_config object is + instantiated with these values. +*/ +#define DEFAULT_NCOMP 1 +#define DEFAULT_ENKF_TRUNCATION 0.99 +#define DEFAULT_ENKF_ALPHA 3.0 +#define DEFAULT_ENKF_STD_CUTOFF 1e-6 +#define DEFAULT_MERGE_OBSERVATIONS false +#define DEFAULT_RERUN false +#define DEFAULT_RERUN_START 0 +#define DEFAULT_UPDATE_LOG_PATH "update_log" +#define DEFAULT_SINGLE_NODE_UPDATE false +#define DEFAULT_ANALYSIS_MODULE "STD_ENKF" +#define DEFAULT_ANALYSIS_NUM_ITERATIONS 4 +#define DEFAULT_ANALYSIS_ITER_CASE "ITERATED_ENSEMBLE_SMOOTHER%d" +#define DEFAULT_ANALYSIS_ITER_RUNPATH "Simulations/Real%d" +#define DEFAULT_ANALYSIS_MIN_REALISATIONS 0 // 0: No lower limit +#define DEFAULT_ANALYSIS_STOP_LONG_RUNNING false +#define DEFAULT_MAX_RUNTIME 0 +#define DEFAULT_ITER_RETRY_COUNT 4 + + +/* Default directories. */ +#define DEFAULT_RUNPATH "simulations/realization%d" +#define DEFAULT_ENSPATH "storage" +#define DEFAULT_RFTPATH "rft" +#define DEFAULT_REPORT_TIMEOUT 120 + +#define DEFAULT_STORE_PC true +#define DEFAULT_PC_FILENAME "PC_%04d-%04d-%s" +#define DEFAULT_PC_PATH "PC" + +#define SUMMARY_KEY_JOIN_STRING ":" +#define USER_KEY_JOIN_STRING ":" + +#define DEFAULT_WORKFLOW_VERBOSE false + +/* + Some #define symbols used when saving configuration files. +*/ +#define CONFIG_OPTION_FORMAT " %s:%s" +#define CONFIG_FLOAT_OPTION_FORMAT " %s:%g" +#define CONFIG_KEY_FORMAT "%-24s" +#define CONFIG_VALUE_FORMAT " %-32s" +#define CONFIG_FLOAT_FORMAT " %32.4f" /* One size - fits all :-) */ +#define CONFIG_SHORT_VALUE_FORMAT " %-12s" +#define CONFIG_ENDVALUE_FORMAT " %-32s\n" +#define CONFIG_COMMENTLINE_FORMAT "----------------------------------------------------------------------\n" + +/* + The string added at the beginning and end of string which should be + replaced with the template parser. +*/ + +#define DEFAULT_START_TAG "<" +#define DEFAULT_END_TAG ">" + +/*****************************************************************/ +/* file system spesific defaults. */ + +/** + Name of the default case. +*/ + +#define CASE_LOG "case-log" +#define CURRENT_CASE "current" +#define DEFAULT_CASE "default" +#define CURRENT_CASE_FILE "current_case" + +#define DEFAULT_CASE_PATH "%s/files" // mountpoint +#define DEFAULT_CASE_MEMBER_PATH "%s/mem%03d/files" // mountpoint/member +#define DEFAULT_CASE_TSTEP_PATH "%s/%04d/files" // mountpoint/tstep +#define DEFAULT_CASE_TSTEP_MEMBER_PATH "%s/%04d/mem%03d/files" // mountpoint/tstep/member +// mountpoint = ENSPATH/case + + + +#endif diff --git a/libres/lib/include/ert/enkf/enkf_fs.hpp b/libres/lib/include/ert/enkf/enkf_fs.hpp new file mode 100644 index 00000000000..364d6f0749c --- /dev/null +++ b/libres/lib/include/ert/enkf/enkf_fs.hpp @@ -0,0 +1,111 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'enkf_fs.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 ERT_ENKF_FS_H +#define ERT_ENKF_FS_H +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + const char * enkf_fs_get_mount_point( const enkf_fs_type * fs ); + const char * enkf_fs_get_case_name( const enkf_fs_type * fs ); + bool enkf_fs_is_read_only(const enkf_fs_type * fs); + void enkf_fs_fsync( enkf_fs_type * fs ); + + enkf_fs_type * enkf_fs_get_ref( enkf_fs_type * fs ); + int enkf_fs_decref( enkf_fs_type * fs ); + int enkf_fs_incref( enkf_fs_type * fs ); + int enkf_fs_get_refcount( const enkf_fs_type * fs ); + enkf_fs_type * enkf_fs_mount( const char * path ); + PY_USED bool enkf_fs_update_disk_version(const char * mount_point , int src_version , int target_version); + int enkf_fs_disk_version(const char * mount_point ); + int enkf_fs_get_version104( const char * path ); + void enkf_fs_fwrite_node(enkf_fs_type * enkf_fs , buffer_type * buffer , const char * node_key, enkf_var_type var_type, + int report_step , int iens); + + void enkf_fs_fwrite_vector(enkf_fs_type * enkf_fs , + buffer_type * buffer , + const char * node_key, + enkf_var_type var_type, + int iens); + + bool enkf_fs_exists( const char * mount_point ); + + void enkf_fs_fread_node(enkf_fs_type * enkf_fs , buffer_type * buffer , + const char * node_key , enkf_var_type var_type , + int report_step , int iens); + + void enkf_fs_fread_vector(enkf_fs_type * enkf_fs , buffer_type * buffer , + const char * node_key , + enkf_var_type var_type , + int iens); + + + bool enkf_fs_has_vector(enkf_fs_type * enkf_fs , const char * node_key , enkf_var_type var_type , int iens); + bool enkf_fs_has_node(enkf_fs_type * enkf_fs , const char * node_key , enkf_var_type var_type , int report_step , int iens); + + enkf_fs_type * enkf_fs_create_fs( const char * mount_point , fs_driver_impl driver_id , void * arg, bool mount); + + char * enkf_fs_alloc_case_filename( const enkf_fs_type * fs , const char * input_name); + char * enkf_fs_alloc_case_tstep_filename( const enkf_fs_type * fs , int tstep , const char * input_name); + char * enkf_fs_alloc_case_tstep_member_filename( const enkf_fs_type * fs , int tstep , int iens , const char * input_name); + + FILE * enkf_fs_open_case_file( const enkf_fs_type * fs , const char * input_name , const char * mode); + FILE * enkf_fs_open_case_tstep_file( const enkf_fs_type * fs , const char * input_name , int tstep , const char * mode); + + FILE * enkf_fs_open_excase_file( const enkf_fs_type * fs , const char * input_name); + FILE * enkf_fs_open_excase_tstep_file( const enkf_fs_type * fs , const char * input_name , int tstep ); + + time_map_type * enkf_fs_alloc_readonly_time_map( const char * mount_point ); + state_map_type * enkf_fs_alloc_readonly_state_map( const char * mount_point ); + state_map_type * enkf_fs_get_state_map( const enkf_fs_type * fs ); + time_map_type * enkf_fs_get_time_map( const enkf_fs_type * fs ); + cases_config_type * enkf_fs_get_cases_config( const enkf_fs_type * fs); + misfit_ensemble_type * enkf_fs_get_misfit_ensemble( const enkf_fs_type * fs ); + summary_key_set_type * enkf_fs_get_summary_key_set( const enkf_fs_type * fs ); + + void enkf_fs_increase_run_count(enkf_fs_type * fs); + void enkf_fs_decrease_run_count(enkf_fs_type * fs); + PY_USED bool enkf_fs_is_running(const enkf_fs_type * fs); + + + UTIL_SAFE_CAST_HEADER( enkf_fs ); + UTIL_IS_INSTANCE_HEADER( enkf_fs ); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/enkf_fs_type.hpp b/libres/lib/include/ert/enkf/enkf_fs_type.hpp new file mode 100644 index 00000000000..eb45a6e8a76 --- /dev/null +++ b/libres/lib/include/ert/enkf/enkf_fs_type.hpp @@ -0,0 +1,23 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'enkf_fs_type.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 ERT_ENKF_FS_TYPES_H +#define ERT_ENKF_FS_TYPES_H +typedef struct enkf_fs_struct enkf_fs_type; +#endif + diff --git a/libres/lib/include/ert/enkf/enkf_macros.hpp b/libres/lib/include/ert/enkf/enkf_macros.hpp new file mode 100644 index 00000000000..f757c92cc07 --- /dev/null +++ b/libres/lib/include/ert/enkf/enkf_macros.hpp @@ -0,0 +1,335 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'enkf_macros.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 ERT_ENKF_MACROS_H +#define ERT_ENKF_MACROS_H + +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + + +/*****************************************************************/ + +#define VOID_CONFIG_FREE(prefix) void prefix ## _config_free__(void *void_arg) { prefix ## _config_free((prefix ## _config_type *) void_arg); } +#define VOID_CONFIG_FREE_HEADER(prefix) void prefix ## _config_free__(void *); + +/*****************************************************************/ + +#define GET_DATA_SIZE(prefix) int prefix ## _config_get_data_size (const prefix ## _config_type *arg) { return arg->data_size; } +#define GET_DATA_SIZE_HEADER(prefix) int prefix ## _config_get_data_size (const prefix ## _config_type *arg); + +#define VOID_GET_DATA_SIZE(prefix) int prefix ## _config_get_data_size__ (const void * arg) {\ + const prefix ## _config_type * config = prefix ## _config_safe_cast_const( arg ); \ + return prefix ## _config_get_data_size( config ); \ +} +#define VOID_GET_DATA_SIZE_HEADER(prefix) int prefix ## _config_get_data_size__ (const void * arg); + +/*****************************************************************/ + + + +#define VOID_ALLOC(prefix) \ +void * prefix ## _alloc__(const void *void_config) { \ + const prefix ## _config_type * config = prefix ## _config_safe_cast_const( void_config ); \ + return prefix ## _alloc(config); \ +} + +#define VOID_ALLOC_HEADER(prefix) void * prefix ## _alloc__(const void *); + +/*****************************************************************/ + +#define VOID_HAS_DATA(prefix) \ + bool prefix ##_has_data__(const void * void_arg , int report_step) { \ + const prefix ## _type * arg = prefix ## _safe_cast_const( void_arg ); \ + return prefix ## _has_data(arg , report_step); \ +} + +#define VOID_HAS_DATA_HEADER(prefix) bool prefix ##_has_data__(const void * , int); + +/*****************************************************************/ +#define VOID_WRITE_TO_BUFFER(prefix) \ + bool prefix ## _write_to_buffer__(const void * void_arg , buffer_type * buffer , int report_step) { \ + const prefix ## _type * arg = prefix ## _safe_cast_const( void_arg ); \ + return prefix ## _write_to_buffer(arg , buffer , report_step); \ +} + + +#define VOID_READ_FROM_BUFFER(prefix) \ + void prefix ## _read_from_buffer__(void * void_arg , buffer_type * buffer , enkf_fs_type * fs, int report_step) { \ + prefix ## _type * arg = prefix ## _safe_cast( void_arg ); \ + prefix ## _read_from_buffer(arg , buffer , fs, report_step); \ +} + +#define VOID_WRITE_TO_BUFFER_HEADER(prefix) bool prefix ## _write_to_buffer__(const void * , buffer_type * , int); +#define VOID_READ_FROM_BUFFER_HEADER(prefix) void prefix ## _read_from_buffer__(void * , buffer_type * , enkf_fs_type * , int); + +#define VOID_FLOAD(prefix) \ +bool prefix ## _fload__(void * void_arg , const char * filename) { \ + prefix ## _type * arg = prefix ## _safe_cast( void_arg ); \ + return prefix ## _fload(arg , filename); \ +} +#define VOID_FLOAD_HEADER(prefix) bool prefix ## _fload__(void * , const char * ); + + +/*****************************************************************/ + +#define VOID_ECL_WRITE(prefix) \ +void prefix ## _ecl_write__(const void * void_arg , const char * path , const char * file , value_export_type * export_value) { \ + const prefix ## _type * arg = prefix ## _safe_cast_const( void_arg ); \ + prefix ## _ecl_write(arg , path , file , export_value); \ +} + +#define VOID_ECL_WRITE_HEADER(prefix) void prefix ## _ecl_write__(const void * , const char * , const char * , value_export_type * export_value); + +/*****************************************************************/ + +#define VOID_FORWARD_LOAD(prefix) \ +bool prefix ## _forward_load__(void * void_arg , const char * ecl_file , const forward_load_context_type * load_context) { \ + prefix ## _type * arg = prefix ## _safe_cast( void_arg ); \ + return prefix ## _forward_load(arg , ecl_file , load_context); \ +} + +#define VOID_FORWARD_LOAD_HEADER(prefix) bool prefix ## _forward_load__(void * , const char * , const forward_load_context_type * load_context); + + /*****************************************************************/ + +#define VOID_FORWARD_LOAD_VECTOR(prefix) \ + bool prefix ## _forward_load_vector__(void * void_arg , const char * ecl_file , const forward_load_context_type * load_context, const int_vector_type * time_index) { \ + prefix ## _type * arg = prefix ## _safe_cast( void_arg ); \ + return prefix ## _forward_load_vector(arg , ecl_file ,load_context , time_index); \ +} + +#define VOID_FORWARD_LOAD_VECTOR_HEADER(prefix) bool prefix ## _forward_load_vector__(void * , const char * , const forward_load_context_type * load_context, const int_vector_type * time_index); + + +/*****************************************************************/ + +#define VOID_FREE(prefix) \ +void prefix ## _free__(void * void_arg) { \ + prefix ## _type * arg = prefix ## _safe_cast( void_arg ); \ + prefix ## _free( arg ); \ +} + +#define VOID_FREE_HEADER(prefix) void prefix ## _free__(void * ); + + +/*****************************************************************/ + +#define VOID_USER_GET(prefix) \ +bool prefix ## _user_get__(void * void_arg , const char * key , int report_step , double * value) { \ + prefix ## _type * arg = prefix ## _safe_cast( void_arg ); \ + return prefix ## _user_get(arg , key , report_step, value); \ +} + +#define VOID_USER_GET_HEADER(prefix) bool prefix ## _user_get__(void * , const char * , int, double *); + + +/*****************************************************************/ + +#define VOID_USER_GET_VECTOR(prefix) \ +void prefix ## _user_get_vector__(void * void_arg , const char * key , double_vector_type * value) { \ + prefix ## _type * arg = prefix ## _safe_cast( void_arg ); \ + prefix ## _user_get_vector(arg , key , value); \ +} + +#define VOID_USER_GET_VECTOR_HEADER(prefix) void prefix ## _user_get_vector__(void * , const char * , double_vector_type *); + +/*****************************************************************/ + +#define VOID_USER_GET_OBS(prefix) \ +void prefix ## _user_get__(void * void_arg , const char * key , double * value, double * std, bool * valid) { \ + prefix ## _user_get((prefix ## _type *) void_arg , key , value , std , valid); \ +} + +#define VOID_USER_GET_OBS_HEADER(prefix) void prefix ## _user_get__(void * , const char * , double * , double * , bool *); + + +/*****************************************************************/ + +#define VOID_COPY(prefix) \ +void prefix ## _copy__(const void * void_src, void * void_target) { \ + const prefix ## _type * src = prefix ## _safe_cast_const( void_src ); \ + prefix ## _type * target = prefix ## _safe_cast( void_target ); \ + prefix ## _copy( src , target ); \ +} +#define VOID_COPY_HEADER(prefix) void prefix ## _copy__(const void * , void * ); + +/*****************************************************************/ + + +#define CONFIG_GET_ECL_KW_NAME(prefix) const char * prefix ## _config_get_ecl_kw_name(const prefix ## _config_type * config) { return config->ecl_kw_name; } +#define CONFIG_GET_ECL_KW_NAME_HEADER(prefix) const char * prefix ## _config_get_ecl_kw_name(const prefix ## _config_type * ) + + +/*****************************************************************/ + +#define VOID_SERIALIZE(prefix) \ + void prefix ## _serialize__(const void *void_arg, node_id_type node_id , const active_list_type * active_list , matrix_type * A , int row_offset , int column) { \ + const prefix ## _type *arg = prefix ## _safe_cast_const( void_arg ); \ + prefix ## _serialize (arg , node_id , active_list , A , row_offset , column); \ +} +#define VOID_SERIALIZE_HEADER(prefix) void prefix ## _serialize__(const void * , node_id_type , const active_list_type * , matrix_type * , int , int); + + +#define VOID_DESERIALIZE(prefix) \ + void prefix ## _deserialize__(void *void_arg, node_id_type node_id , const active_list_type * active_list , const matrix_type * A , int row_offset , int column) { \ + prefix ## _type *arg = prefix ## _safe_cast( void_arg ); \ + prefix ## _deserialize (arg , node_id , active_list , A , row_offset , column); \ +} +#define VOID_DESERIALIZE_HEADER(prefix) void prefix ## _deserialize__(void * , node_id_type , const active_list_type * , const matrix_type * , int , int); + + + + +/*****************************************************************/ + +#define VOID_INITIALIZE(prefix) \ + bool prefix ## _initialize__(void *void_arg, int iens , const char * init_file, rng_type * rng) { \ + prefix ## _type *arg = prefix ## _safe_cast(void_arg); \ + return prefix ## _initialize (arg , iens , init_file , rng); \ +} +#define VOID_INITIALIZE_HEADER(prefix) bool prefix ## _initialize__(void *, int , const char * , rng_type * ); + +/*****************************************************************/ + +#define VOID_SET_INFLATION(prefix) \ +void prefix ## _set_inflation__( void * void_inflation , const void * void_std , const void * void_min_std) { \ + prefix ## _set_inflation( prefix ## _safe_cast( void_inflation ) , prefix ## _safe_cast_const( void_std ) , prefix ## _safe_cast_const( void_min_std )); \ +} +#define VOID_SET_INFLATION_HEADER(prefix) void prefix ## _set_inflation__( void * void_inflation , const void * void_std , const void * void_min_std ); + + +/*****************************************************************/ + +#define VOID_GET_OBS(prefix) \ +void prefix ## _get_observations__(const void * void_arg , obs_data_type * obs_data, enkf_fs_type * fs, int report_step , const active_list_type * __active_list) { \ + prefix ## _get_observations((prefix ## _type *) void_arg , obs_data , fs, report_step , __active_list); \ +} + +#define VOID_GET_OBS_HEADER(prefix) void prefix ## _get_observations__(const void * , obs_data_type * , enkf_fs_type *, int , const active_list_type * ) + +/*****************************************************************/ + +#define VOID_MEASURE(obs_prefix, state_prefix) \ +void obs_prefix ## _measure__(const void * void_obs , const void * void_state , node_id_type node_id , meas_data_type * meas_data , const active_list_type * __active_list) { \ + const obs_prefix ## _type * obs = obs_prefix ## _safe_cast_const( void_obs ); \ + const state_prefix ## _type * state = state_prefix ## _safe_cast_const( void_state ); \ + obs_prefix ## _measure(obs , state , node_id , meas_data , __active_list); \ +} + +#define VOID_MEASURE_UNSAFE(obs_prefix, state_prefix) \ +void obs_prefix ## _measure__(const void * void_obs , const void * state , node_id_type node_id , meas_data_type * meas_data , const active_list_type * __active_list) { \ + const obs_prefix ## _type * obs = obs_prefix ## _safe_cast_const( void_obs ); \ + obs_prefix ## _measure(obs , state , node_id , meas_data , __active_list); \ +} + + +#define VOID_MEASURE_HEADER(obs_prefix) void obs_prefix ## _measure__(const void * , const void * , node_id_type , meas_data_type * , const active_list_type *) + +/*****************************************************************/ + +#define VOID_UPDATE_STD_SCALE(prefix)\ +void prefix ## _update_std_scale__( void * void_obs , double std_multiplier , const active_list_type * active_list) { \ + prefix ## _type * obs = prefix ## _safe_cast( void_obs ); \ + prefix ## _update_std_scale( obs , std_multiplier , active_list ); \ +} + +#define VOID_UPDATE_STD_SCALE_HEADER(prefix) void prefix ## _update_std_scale__(void * void_obs , double std_multiplier , const active_list_type * active_list); + +/*****************************************************************/ + +#define VOID_CHI2(obs_prefix, state_prefix) \ +double obs_prefix ## _chi2__(const void * void_obs , const void * void_state, node_id_type node_id) { \ + const obs_prefix ## _type * obs = obs_prefix ## _safe_cast_const( void_obs ); \ + const state_prefix ## _type * state = (const state_prefix ## _type *) void_state; \ + return obs_prefix ## _chi2(obs , state , node_id); \ +} + +#define VOID_CHI2_HEADER(obs_prefix) double obs_prefix ## _chi2__(const void * , const void *, node_id_type); + + +/*****************************************************************/ + +#define VOID_TRUNCATE(prefix) void prefix ## _truncate__(void * void_arg) { prefix ## _truncate( (prefix ## _type *) void_arg); } +#define VOID_TRUNCATE_HEADER(prefix) void prefix ## _truncate__(void * ) + +/*****************************************************************/ +#define VOID_SCALE(prefix) void prefix ## _scale__(void * void_arg , double scale_factor) { prefix ## _scale( prefix ## _safe_cast( void_arg ) , scale_factor ); } +#define VOID_SCALE_HEADER(prefix) void prefix ## _scale__(void * , double ); + +/*****************************************************************/ + +#define VOID_CLEAR(prefix) void prefix ## _clear__(void * void_arg) { prefix ## _clear( prefix ## _safe_cast( void_arg )); } +#define VOID_CLEAR_HEADER(prefix) void prefix ## _clear__(void * ) + +/*****************************************************************/ + + +#define VOID_ISQRT(prefix) void prefix ## _isqrt__(void * void_arg) { prefix ## _isqrt( prefix ## _safe_cast( void_arg )); } +#define VOID_ISQRT_HEADER(prefix) void prefix ## _isqrt__(void * ) + +/*****************************************************************/ + +#define VOID_IADD(prefix) void prefix ## _iadd__( void * void_arg , const void * void_delta ) { \ + prefix ## _iadd( prefix ## _safe_cast( void_arg ) , prefix ## _safe_cast_const( void_delta ) ); \ +} + +#define VOID_IADD_HEADER(prefix) void prefix ## _iadd__( void * void_arg , const void * void_delta ); + +/*****************************************************************/ + +#define VOID_IMUL(prefix) void prefix ## _imul__( void * void_arg , const void * void_delta ) { \ + prefix ## _imul( prefix ## _safe_cast( void_arg ) , prefix ## _safe_cast_const( void_delta ) ); \ +} + +#define VOID_IMUL_HEADER(prefix) void prefix ## _imul__( void * void_arg , const void * void_delta ); + +/*****************************************************************/ + +#define VOID_IADDSQR(prefix) void prefix ## _iaddsqr__( void * void_arg , const void * void_delta ) { \ + prefix ## _iaddsqr( prefix ## _safe_cast( void_arg ) , prefix ## _safe_cast_const( void_delta ) ); \ +} + +#define VOID_IADDSQR_HEADER(prefix) void prefix ## _iaddsqr__( void * void_arg , const void * void_delta ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/enkf_main.hpp b/libres/lib/include/ert/enkf/enkf_main.hpp new file mode 100644 index 00000000000..14e0336d85e --- /dev/null +++ b/libres/lib/include/ert/enkf/enkf_main.hpp @@ -0,0 +1,254 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'enkf_main.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 ERT_ENKF_MAIN_H +#define ERT_ENKF_MAIN_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + /*****************************************************************/ + + + typedef struct enkf_main_struct enkf_main_type; + const char * enkf_main_get_user_config_file( const enkf_main_type * enkf_main ); + + ert_templates_type * enkf_main_get_templates( enkf_main_type * enkf_main ); + + void enkf_main_free(enkf_main_type * ); + void enkf_main_exit(enkf_main_type * enkf_main); + void enkf_main_init_run( enkf_main_type * enkf_main, const ert_run_context_type * run_context); + + bool enkf_main_UPDATE(enkf_main_type * enkf_main , const int_vector_type * step_list, enkf_fs_type * source_fs, enkf_fs_type * target_fs , int target_step , run_mode_type run_mode); + bool enkf_main_smoother_update(enkf_main_type * enkf_main , enkf_fs_type * source_fs, enkf_fs_type * target_fs); + void enkf_main_create_run_path(enkf_main_type * enkf_main , const ert_run_context_type * run_context); + + enkf_main_type * enkf_main_alloc(const res_config_type *, bool, bool); + + enkf_state_type * enkf_main_iget_state(const enkf_main_type * , int ); + + ranking_table_type * enkf_main_get_ranking_table( const enkf_main_type * enkf_main ); + const ecl_config_type * enkf_main_get_ecl_config(const enkf_main_type * enkf_main); + ensemble_config_type * enkf_main_get_ensemble_config(const enkf_main_type * enkf_main); + int enkf_main_get_ensemble_size( const enkf_main_type * enkf_main ); + int enkf_main_get_history_length( const enkf_main_type * ); + model_config_type * enkf_main_get_model_config( const enkf_main_type * ); + local_config_type * enkf_main_get_local_config( const enkf_main_type * enkf_main ); + bool enkf_main_load_obs(enkf_main_type *, const char *, bool); + enkf_obs_type * enkf_main_get_obs(const enkf_main_type * ); + bool enkf_main_have_obs( const enkf_main_type * enkf_main ); + const analysis_config_type * enkf_main_get_analysis_config(const enkf_main_type * ); + const log_config_type * enkf_main_get_log_config(const enkf_main_type *); + + subst_config_type * enkf_main_get_subst_config(const enkf_main_type * enkf_main); + subst_list_type * enkf_main_get_data_kw( const enkf_main_type * enkf_main ); + PY_USED void enkf_main_clear_data_kw( enkf_main_type * enkf_main ); + const site_config_type * enkf_main_get_site_config( const enkf_main_type * enkf_main ); + void enkf_main_resize_ensemble( enkf_main_type * enkf_main , int new_ens_size ); + void enkf_main_get_observations( const enkf_main_type * enkf_main, const char * user_key , int obs_count , time_t * obs_time , double * y , double * std); + int enkf_main_get_observation_count( const enkf_main_type * enkf_main, const char * user_key ); + + /*****************************************************************/ + void enkf_main_install_SIGNALS(void); + void enkf_main_add_node(enkf_main_type * enkf_main, enkf_config_node_type * enkf_config_node); + int_vector_type * enkf_main_update_alloc_step_list( const enkf_main_type * enkf_main , int load_start , int step2 , int stride); + + const hook_manager_type * enkf_main_get_hook_manager( const enkf_main_type * enkf_main ); + + void enkf_main_get_PC( const matrix_type * S, + const matrix_type * dObs, + double truncation , + int ncomp , + matrix_type * PC , + matrix_type * PC_obs, + double_vector_type * singular_values); + + + void enkf_main_fprintf_PC(const char * filename , + matrix_type * PC , + matrix_type * PC_obs); + + + void enkf_main_set_verbose( enkf_main_type * enkf_main , bool verbose); + + ert_workflow_list_type * enkf_main_get_workflow_list( enkf_main_type * enkf_main ); + bool enkf_main_run_workflow( enkf_main_type * enkf_main , const char * workflow); + + rng_config_type * enkf_main_get_rng_config( const enkf_main_type * enkf_main ); + void enkf_main_rng_init( enkf_main_type * enkf_main); + rng_type * enkf_main_get_shared_rng( enkf_main_type * enkf_main ); + + bool enkf_main_export_field(const enkf_main_type * enkf_main, + const char * kw, + const char * path, + bool_vector_type * iactive, + field_file_format_type file_type, + int report_step); + + bool enkf_main_export_field_with_fs(const enkf_main_type * enkf_main, + const char * kw, + const char * path, + bool_vector_type * iactive, + field_file_format_type file_type, + int report_step, + enkf_fs_type * fs); + + + int enkf_main_load_from_forward_model_with_fs(enkf_main_type * enkf_main, int iter , bool_vector_type * iactive, stringlist_type ** realizations_msg_list, enkf_fs_type * fs); + int enkf_main_load_from_forward_model(enkf_main_type * enkf_main, int iter , bool_vector_type * iactive, stringlist_type ** realizations_msg_list); + PY_USED int enkf_main_load_from_forward_model_from_gui(enkf_main_type * enkf_main, int iter , bool_vector_type * iactive, enkf_fs_type * fs); + int enkf_main_load_from_run_context(enkf_main_type* enkf_main, ert_run_context_type* run_context, stringlist_type** realizations_msg_list, enkf_fs_type* fs); + int enkf_main_load_from_run_context_from_gui(enkf_main_type* enkf_main, ert_run_context_type* run_context, enkf_fs_type* fs); + + void enkf_main_rank_on_observations(enkf_main_type * enkf_main, + const char * ranking_key, + const stringlist_type * obs_ranking_keys, + const int_vector_type * steps); + + + + void enkf_main_rank_on_data(enkf_main_type * enkf_main, + const char * ranking_key, + const char * data_key, + bool sort_increasing, + int step); + + + void enkf_main_export_ranking(enkf_main_type * enkf_main, const char * ranking_key, const char * ranking_file); + + + + + + + + /********* File System / Case Management ********** + Implementation: enkf_main_manage_fs.c */ + + + bool enkf_main_case_is_current(const enkf_main_type * enkf_main , const char * case_path); + char * enkf_main_read_alloc_current_case_name(const enkf_main_type * enkf_main); + stringlist_type * enkf_main_alloc_caselist( const enkf_main_type * enkf_main ); + + void enkf_main_initialize_from_scratch(enkf_main_type * enkf_main , + const stringlist_type * param_list , + const ert_run_context_type * run_context); + + void enkf_main_init_current_case_from_existing(enkf_main_type * enkf_main, + enkf_fs_type * source_case_fs, + int source_report_step); + + PY_USED void enkf_main_init_current_case_from_existing_custom(enkf_main_type * enkf_main, + enkf_fs_type * source_case_fs, + int source_report_step, + stringlist_type * node_list, + bool_vector_type * iactive); + + + void enkf_main_init_case_from_existing(const enkf_main_type * enkf_main, + enkf_fs_type * source_case_fs, + int source_report_step, + enkf_fs_type * target_case); + + void enkf_main_init_case_from_existing_custom(const enkf_main_type * enkf_main, + enkf_fs_type * source_case_fs, + int source_report_step, + enkf_fs_type * target_case, + stringlist_type * node_list, + bool_vector_type * iactive); + + bool enkf_main_case_is_initialized( const enkf_main_type * enkf_main , + const char * case_name , + bool_vector_type * __mask); + + bool enkf_main_is_initialized( const enkf_main_type * enkf_main ,bool_vector_type * __mask); + + char * enkf_main_alloc_mount_point( const enkf_main_type * enkf_main , const char * case_path); + enkf_fs_type * enkf_main_get_fs(const enkf_main_type * ); + enkf_fs_type * enkf_main_job_get_fs(const enkf_main_type * ); + enkf_fs_type * enkf_main_get_fs_ref(const enkf_main_type * enkf_main); + const char * enkf_main_get_current_fs( const enkf_main_type * enkf_main ); + enkf_fs_type * enkf_main_mount_alt_fs(const enkf_main_type * enkf_main , const char * case_path , bool create); + void enkf_main_set_fs( enkf_main_type * enkf_main , enkf_fs_type * fs , const char * case_path ); + void enkf_main_select_fs( enkf_main_type * enkf_main , const char * case_path ); + bool enkf_main_fs_exists(const enkf_main_type * enkf_main, const char * input_case); + const char * enkf_main_get_mount_root( const enkf_main_type * enkf_main); + + + state_map_type * enkf_main_alloc_readonly_state_map( const enkf_main_type * enkf_main , const char * case_path); + PY_USED time_map_type * enkf_main_alloc_readonly_time_map( const enkf_main_type * enkf_main , const char * case_path ); + + runpath_list_type * enkf_main_alloc_runpath_list(const enkf_main_type * enkf_main); + runpath_list_type * enkf_main_get_runpath_list( const enkf_main_type * enkf_main ); + PY_USED ert_run_context_type * enkf_main_alloc_ert_run_context_ENSEMBLE_EXPERIMENT(const enkf_main_type * enkf_main , enkf_fs_type * fs , bool_vector_type * iactive , int iter); + + queue_config_type * enkf_main_get_queue_config(enkf_main_type * enkf_main ); + + rng_manager_type * enkf_main_get_rng_manager(const enkf_main_type * enkf_main ); + void enkf_main_isubmit_job( enkf_main_type * enkf_main , run_arg_type * run_arg , job_queue_type * job_queue); + const char * enkf_main_get_site_config_file( const enkf_main_type * enkf_main ); + const char * enkf_main_get_schedule_prediction_file( const enkf_main_type * enkf_main ); + void enkf_main_add_data_kw(enkf_main_type * enkf_main , const char * key , const char * value); + const res_config_type * enkf_main_get_res_config(const enkf_main_type * enkf_main); + void * enkf_main_isubmit_job__( void * arg ); + +UTIL_SAFE_CAST_HEADER(enkf_main); +UTIL_IS_INSTANCE_HEADER(enkf_main); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/enkf_main_jobs.hpp b/libres/lib/include/ert/enkf/enkf_main_jobs.hpp new file mode 100644 index 00000000000..e2629fe7827 --- /dev/null +++ b/libres/lib/include/ert/enkf/enkf_main_jobs.hpp @@ -0,0 +1,35 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_main_jobs.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 ENKF_MAIN_JOBS_H +#define ENKF_MAIN_JOBS_H + +#ifdef __cplusplus +extern "C" { +#endif + + +void * enkf_main_select_case_JOB( void * self , const stringlist_type * args); +void * enkf_main_create_case_JOB( void * self , const stringlist_type * args); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libres/lib/include/ert/enkf/enkf_main_update.hpp b/libres/lib/include/ert/enkf/enkf_main_update.hpp new file mode 100644 index 00000000000..33a847e40af --- /dev/null +++ b/libres/lib/include/ert/enkf/enkf_main_update.hpp @@ -0,0 +1,28 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'enkf_main_update.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 ERT_ENKF_MAIN_UPDATE +#define ERT_ENKF_MAIN_UPDATE + +#include + +void enkf_main_update(enkf_main_type * enkf_main , int step1 , int step2); + + + +#endif diff --git a/libres/lib/include/ert/enkf/enkf_node.hpp b/libres/lib/include/ert/enkf/enkf_node.hpp new file mode 100644 index 00000000000..fb8dcbd9de9 --- /dev/null +++ b/libres/lib/include/ert/enkf/enkf_node.hpp @@ -0,0 +1,177 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'enkf_node.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 ERT_ENKF_NODE_H +#define ERT_ENKF_NODE_H +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/**********************************/ + + + typedef void (serialize_ftype) (const void * , node_id_type , const active_list_type * , matrix_type * , int , int); + typedef void (deserialize_ftype) ( void * , node_id_type , const active_list_type * , const matrix_type * , int , int); + + + + + typedef void (ecl_write_ftype) (const void * , /* Node object */ + const char * , /* Directory to write to. */ + const char * , /* Filename - can be NULL. */ + value_export_type *); + + typedef bool (fload_ftype) ( void * , const char *); + typedef void (read_from_buffer_ftype) ( void * , buffer_type * , enkf_fs_type * , int); + typedef bool (write_to_buffer_ftype) (const void * , buffer_type * , int); + typedef bool (has_data_ftype) (const void * , int); + + + typedef void (set_inflation_ftype) (void * , + const void * , /* Node object with the ensemble standard deviation. */ + const void * ); /* Node object with the minimum standard deviation - supplied by the user. */ + + + typedef void (user_get_vector_ftype) (void * , const char * , double_vector_type *); + typedef bool (user_get_ftype) (void * , const char * , int , double *); + typedef void * (alloc_ftype) (const void *); + typedef bool (initialize_ftype) ( void * , int , const char * , rng_type * ); + typedef bool (forward_load_ftype) (void * , const char * , const forward_load_context_type *); + typedef bool (forward_load_vector_ftype) (void * , const char * , const forward_load_context_type *, const int_vector_type *); + typedef void (free_data_ftype) (void * ); + typedef void (node_free_ftype) ( void *); + typedef void (clear_ftype) ( void *); + typedef void (node_copy_ftype) (const void * , void *); + typedef void (isqrt_ftype) ( void *); + typedef void (scale_ftype) ( void * , double); + typedef void (iadd_ftype) ( void * , const void *); + typedef void (imul_ftype) ( void * , const void *); + typedef void (iaddsqr_ftype) ( void * , const void *); + typedef void (ensemble_mulX_vector_ftype) ( void * , int , const void ** , const double *); + + + typedef enum {alloc_func = 0, + ecl_write_func = 1, + forward_load_func = 2, + fread_func = 3, + fwrite_func = 4, + copy_func = 5, + initialize_func = 6, + free_func = 7, + free_data_func = 8, + clear_serial_state_func = 9, + serialize = 10, + deserialize = 11} node_function_type; + + + typedef void (enkf_node_ftype1) (enkf_node_type *); + + + bool enkf_node_user_get_vector( enkf_node_type * enkf_node , enkf_fs_type * fs , const char * key , int iens , double_vector_type * values); + bool enkf_node_user_get_no_id(enkf_node_type * enkf_node , enkf_fs_type * fs , const char * key , int report_step, int iens, double * value); + bool enkf_node_user_get(enkf_node_type * , enkf_fs_type * , const char * , node_id_type , double * ); + enkf_node_type * enkf_node_deep_alloc(const enkf_config_node_type * config); + enkf_node_type * enkf_node_alloc(const enkf_config_node_type *); + enkf_node_type * enkf_node_copyc(const enkf_node_type * ); + /* + The enkf_node_free() function declaration is in the enkf_config_node.h header, + because the enkf_config_node needs to know how to free the min_std node. + + void enkf_node_free(enkf_node_type *enkf_node); + */ + + bool enkf_node_forward_init(enkf_node_type * enkf_node , const char * run_path , int iens); + bool enkf_node_has_data( enkf_node_type * enkf_node , enkf_fs_type * fs , node_id_type node_id); + //void enkf_node_free_data(enkf_node_type * ); + void enkf_node_free__(void *); + void * enkf_node_value_ptr(const enkf_node_type * ); + ert_impl_type enkf_node_get_impl_type(const enkf_node_type * ); + bool enkf_node_use_forward_init( const enkf_node_type * enkf_node ); + void enkf_node_serialize(enkf_node_type * enkf_node , enkf_fs_type * fs , node_id_type node_id , const active_list_type * active_list , matrix_type * A , int row_offset , int column); + void enkf_node_deserialize(enkf_node_type *enkf_node , enkf_fs_type * fs , node_id_type node_id , const active_list_type * active_list , const matrix_type * A , int row_offset , int column); + + bool enkf_node_forward_load_vector(enkf_node_type *enkf_node , const forward_load_context_type * load_context , const int_vector_type * time_index); + bool enkf_node_forward_load (enkf_node_type *, const forward_load_context_type * load_context); + void enkf_node_ecl_write (const enkf_node_type *, const char * , value_export_type * , int); +bool enkf_node_initialize(enkf_node_type *enkf_node , int , rng_type * ); +void enkf_node_clear(enkf_node_type *); + + void enkf_node_copy(const enkf_config_node_type * config_node , + enkf_fs_type * src_case , + enkf_fs_type * target_case , + node_id_type src_id , + node_id_type target_id ); + enkf_node_type * enkf_node_load_alloc( const enkf_config_node_type * config_node , enkf_fs_type * fs , node_id_type node_id); + bool enkf_node_fload( enkf_node_type * enkf_node , const char * filename ); + void enkf_node_load(enkf_node_type * enkf_node , enkf_fs_type * fs , node_id_type node_id ); + void enkf_node_load_vector( enkf_node_type * enkf_node , enkf_fs_type * fs , int iens); + bool enkf_node_store(enkf_node_type * enkf_node , enkf_fs_type * fs , node_id_type node_id); + bool enkf_node_store_vector(enkf_node_type *enkf_node , enkf_fs_type * fs , int iens ); + bool enkf_node_try_load(enkf_node_type *enkf_node , enkf_fs_type * fs , node_id_type node_id); + bool enkf_node_try_load_vector(enkf_node_type *enkf_node , enkf_fs_type * fs , int iens ); + bool enkf_node_vector_storage( const enkf_node_type * node ); + enkf_node_type * enkf_node_alloc_shared_container(const enkf_config_node_type * config, hash_type * node_hash); + enkf_node_type * enkf_node_alloc_private_container(const enkf_config_node_type * config); +/*****************************************************************/ +/* Function callbacks */ + + +void enkf_node_set_inflation( enkf_node_type * inflation , const enkf_node_type * std , const enkf_node_type * min_std); +void enkf_node_sqrt(enkf_node_type *enkf_node); +void enkf_node_scale(enkf_node_type * , double ); +void enkf_node_iadd(enkf_node_type * , const enkf_node_type * ); +void enkf_node_iaddsqr(enkf_node_type * , const enkf_node_type * ); +void enkf_node_imul(enkf_node_type * , const enkf_node_type * ); +const enkf_config_node_type * enkf_node_get_config(const enkf_node_type * ); +const char * enkf_node_get_key(const enkf_node_type * ); +bool enkf_node_has_func(const enkf_node_type * , node_function_type ); +bool enkf_node_internalize(const enkf_node_type * , int ); + +UTIL_IS_INSTANCE_HEADER( enkf_node ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/enkf_obs.hpp b/libres/lib/include/ert/enkf/enkf_obs.hpp new file mode 100644 index 00000000000..372a915e0c3 --- /dev/null +++ b/libres/lib/include/ert/enkf/enkf_obs.hpp @@ -0,0 +1,111 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'enkf_obs.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 ERT_ENKF_OBS_H +#define ERT_ENKF_OBS_H +#include + +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + bool enkf_obs_have_obs(const enkf_obs_type * enkf_obs); + bool enkf_obs_is_valid(const enkf_obs_type*); + + enkf_obs_type * enkf_obs_alloc( const history_type * history , + time_map_type * external_time_map , + const ecl_grid_type * grid , + const ecl_sum_type * refcase, + ensemble_config_type * ensemble_config ); + + void enkf_obs_free( enkf_obs_type * enkf_obs); + + obs_vector_type * enkf_obs_iget_vector(const enkf_obs_type * obs, int index); + obs_vector_type * enkf_obs_get_vector(const enkf_obs_type * , const char * ); + void enkf_obs_add_obs_vector(enkf_obs_type * enkf_obs, + const obs_vector_type * vector); + + void enkf_obs_load(enkf_obs_type*, const char*, double); + void enkf_obs_clear( enkf_obs_type * enkf_obs ); + obs_impl_type enkf_obs_get_type(const enkf_obs_type * enkf_obs , const char * key); + + void enkf_obs_get_obs_and_measure_node( const enkf_obs_type * enkf_obs, + enkf_fs_type * fs, + const local_obsdata_node_type * obs_node , + const int_vector_type * ens_active_list , + meas_data_type * meas_data, + obs_data_type * obs_data); + + + void enkf_obs_get_obs_and_measure_data(const enkf_obs_type * enkf_obs, + enkf_fs_type * fs, + const local_obsdata_type * local_obsdata , + const int_vector_type * ens_active_list , + meas_data_type * meas_data, + obs_data_type * obs_data); + + + stringlist_type * enkf_obs_alloc_typed_keylist( enkf_obs_type * enkf_obs , obs_impl_type ); + hash_type * enkf_obs_alloc_data_map(enkf_obs_type * enkf_obs); + + bool enkf_obs_has_key(const enkf_obs_type * , const char * ); + int enkf_obs_get_size( const enkf_obs_type * obs ); + + hash_iter_type * enkf_obs_alloc_iter( const enkf_obs_type * enkf_obs ); + + stringlist_type * enkf_obs_alloc_matching_keylist(const enkf_obs_type * enkf_obs , const char * input_string); + time_t enkf_obs_iget_obs_time(const enkf_obs_type * enkf_obs , int report_step); + void enkf_obs_scale_std(enkf_obs_type * enkf_obs, double scale_factor); + void enkf_obs_local_scale_std( const enkf_obs_type * enkf_obs , const local_obsdata_type * local_obsdata, double scale_factor); + void enkf_obs_add_local_nodes_with_data(const enkf_obs_type * enkf_obs , local_obsdata_type * local_obs , enkf_fs_type *fs , const bool_vector_type * ens_mask); + double enkf_obs_scale_correlated_std(const enkf_obs_type * enkf_obs , + enkf_fs_type * fs , + const int_vector_type * ens_active_list , + const local_obsdata_type * local_obsdata, + double alpha, + double std_cutoff, + bool verbose); + local_obsdata_type * enkf_obs_alloc_all_active_local_obs( const enkf_obs_type * enkf_obs , const char * key); + conf_class_type * enkf_obs_get_obs_conf_class(); + UTIL_IS_INSTANCE_HEADER( enkf_obs ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/enkf_plot_data.hpp b/libres/lib/include/ert/enkf/enkf_plot_data.hpp new file mode 100644 index 00000000000..8fa950e0175 --- /dev/null +++ b/libres/lib/include/ert/enkf/enkf_plot_data.hpp @@ -0,0 +1,52 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'enkf_plot_data.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 ERT_ENKF_PLOT_DATA_H +#define ERT_ENKF_PLOT_DATA_H + +#ifdef __cplusplus +extern "C" { +#endif +#include + +#include +#include + +#include +#include +#include +#include + + typedef struct enkf_plot_data_struct enkf_plot_data_type; + + enkf_plot_data_type * enkf_plot_data_alloc( const enkf_config_node_type * config_node ); + void enkf_plot_data_free( enkf_plot_data_type * plot_data ); + void enkf_plot_data_load( enkf_plot_data_type * plot_data , + enkf_fs_type * fs , + const char * user_key , + const bool_vector_type * input_mask); + int enkf_plot_data_get_size( const enkf_plot_data_type * plot_data ); + enkf_plot_tvector_type * enkf_plot_data_iget( const enkf_plot_data_type * plot_data , int index); + + UTIL_IS_INSTANCE_HEADER( enkf_plot_data ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/enkf_plot_gen_kw.hpp b/libres/lib/include/ert/enkf/enkf_plot_gen_kw.hpp new file mode 100644 index 00000000000..a9ec500a106 --- /dev/null +++ b/libres/lib/include/ert/enkf/enkf_plot_gen_kw.hpp @@ -0,0 +1,55 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'enkf_plot_gen_kw.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 ERT_ENKF_PLOT_GEN_KW_H +#define ERT_ENKF_PLOT_GEN_KW_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include +#include + + typedef struct enkf_plot_gen_kw_struct enkf_plot_gen_kw_type; + + enkf_plot_gen_kw_type * enkf_plot_gen_kw_alloc( const enkf_config_node_type * enkf_config_node); + void enkf_plot_gen_kw_free( enkf_plot_gen_kw_type * gen_kw ); + int enkf_plot_gen_kw_get_size( const enkf_plot_gen_kw_type * gen_kw ); + enkf_plot_gen_kw_vector_type * enkf_plot_gen_kw_iget( const enkf_plot_gen_kw_type * vector , int index); + void enkf_plot_gen_kw_load( enkf_plot_gen_kw_type * gen_kw , + enkf_fs_type * fs , + bool transform_data , + int report_step , + const bool_vector_type * input_mask); + + const char * enkf_plot_gen_kw_iget_key( const enkf_plot_gen_kw_type * plot_gen_kw, int index); + int enkf_plot_gen_kw_get_keyword_count( const enkf_plot_gen_kw_type * gen_kw ); + bool enkf_plot_gen_kw_should_use_log_scale(const enkf_plot_gen_kw_type * gen_kw , int index); + + UTIL_IS_INSTANCE_HEADER( enkf_plot_gen_kw ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/enkf_plot_gen_kw_vector.hpp b/libres/lib/include/ert/enkf/enkf_plot_gen_kw_vector.hpp new file mode 100644 index 00000000000..4dd879576c9 --- /dev/null +++ b/libres/lib/include/ert/enkf/enkf_plot_gen_kw_vector.hpp @@ -0,0 +1,46 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'enkf_plot_gen_kw.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 ERT_ENKF_PLOT_GEN_KW_VECTOR_H +#define ERT_ENKF_PLOT_GEN_KW_VECTOR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + + typedef struct enkf_plot_gen_kw_vector_struct enkf_plot_gen_kw_vector_type; + + enkf_plot_gen_kw_vector_type * enkf_plot_gen_kw_vector_alloc( const enkf_config_node_type * config_node , int iens ); + void enkf_plot_gen_kw_vector_free( enkf_plot_gen_kw_vector_type * vector ); + int enkf_plot_gen_kw_vector_get_size( const enkf_plot_gen_kw_vector_type * vector ); + void enkf_plot_gen_kw_vector_reset( enkf_plot_gen_kw_vector_type * vector ); + void enkf_plot_gen_kw_vector_load( enkf_plot_gen_kw_vector_type * vector , enkf_fs_type * fs , bool transform_data , int report_step ); + double enkf_plot_gen_kw_vector_iget( const enkf_plot_gen_kw_vector_type * vector , int index); + + UTIL_IS_INSTANCE_HEADER( enkf_plot_gen_kw_vector ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/enkf_plot_gendata.hpp b/libres/lib/include/ert/enkf/enkf_plot_gendata.hpp new file mode 100644 index 00000000000..938a9b04c18 --- /dev/null +++ b/libres/lib/include/ert/enkf/enkf_plot_gendata.hpp @@ -0,0 +1,57 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'enkf_plot_gendata.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 ERT_ENKF_PLOT_GENDATA_H +#define ERT_ENKF_PLOT_GENDATA_H + + +#include +#include + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef struct enkf_plot_gendata_struct enkf_plot_gendata_type; + +enkf_plot_gendata_type * enkf_plot_gendata_alloc( const enkf_config_node_type * enkf_config_node); +enkf_plot_gendata_type * enkf_plot_gendata_alloc_from_obs_vector( const obs_vector_type * obs_vector ); +void enkf_plot_gendata_free( enkf_plot_gendata_type * data ); +int enkf_plot_gendata_get_size( const enkf_plot_gendata_type * data ); +enkf_plot_genvector_type * enkf_plot_gendata_iget( const enkf_plot_gendata_type * plot_data , int index); +void enkf_plot_gendata_load( enkf_plot_gendata_type * plot_data , + enkf_fs_type * fs , + int report_step , + const bool_vector_type * input_mask); + +double_vector_type * enkf_plot_gendata_get_min_values(enkf_plot_gendata_type * plot_data); +double_vector_type * enkf_plot_gendata_get_max_values(enkf_plot_gendata_type * plot_data); + + +UTIL_IS_INSTANCE_HEADER( enkf_plot_gendata ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/enkf_plot_genvector.hpp b/libres/lib/include/ert/enkf/enkf_plot_genvector.hpp new file mode 100644 index 00000000000..03b925b0b8a --- /dev/null +++ b/libres/lib/include/ert/enkf/enkf_plot_genvector.hpp @@ -0,0 +1,44 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'enkf_plot_genvector.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 ERT_ENKF_PLOT_GENVECTOR_H +#define ERT_ENKF_PLOT_GENVECTOR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +typedef struct enkf_plot_genvector_struct enkf_plot_genvector_type; + +enkf_plot_genvector_type * enkf_plot_genvector_alloc( const enkf_config_node_type * enkf_config_node , int iens); +void enkf_plot_genvector_free( enkf_plot_genvector_type * vector ); +int enkf_plot_genvector_get_size( const enkf_plot_genvector_type * vector ); +void * enkf_plot_genvector_load__( void * arg ); +double enkf_plot_genvector_iget( const enkf_plot_genvector_type * vector , int index); + +UTIL_IS_INSTANCE_HEADER( enkf_plot_genvector ); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/enkf_plot_tvector.hpp b/libres/lib/include/ert/enkf/enkf_plot_tvector.hpp new file mode 100644 index 00000000000..683bcc44b5d --- /dev/null +++ b/libres/lib/include/ert/enkf/enkf_plot_tvector.hpp @@ -0,0 +1,60 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'enkf_plot_tvector.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 ERT_ENKF_PLOT_TVECTOR_H +#define ERT_ENKF_PLOT_TVECTOR_H + +#include +#include + +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct enkf_plot_tvector_struct enkf_plot_tvector_type; + + UTIL_SAFE_CAST_HEADER( enkf_plot_tvector ); + UTIL_IS_INSTANCE_HEADER( enkf_plot_tvector ); + + + + void enkf_plot_tvector_reset( enkf_plot_tvector_type * plot_tvector ); + enkf_plot_tvector_type * enkf_plot_tvector_alloc( const enkf_config_node_type * config_node , int iens); + void enkf_plot_tvector_load( enkf_plot_tvector_type * plot_tvector , enkf_fs_type * fs , const char * user_key ); + void * enkf_plot_tvector_load__( void * arg ); + void enkf_plot_tvector_free( enkf_plot_tvector_type * plot_tvector ); + void enkf_plot_tvector_iset( enkf_plot_tvector_type * plot_tvector , int index , time_t time , double value); + + int enkf_plot_tvector_size( const enkf_plot_tvector_type * plot_tvector ); + double enkf_plot_tvector_iget_value( const enkf_plot_tvector_type * plot_tvector , int index); + time_t enkf_plot_tvector_iget_time( const enkf_plot_tvector_type * plot_tvector , int index); + bool enkf_plot_tvector_iget_active( const enkf_plot_tvector_type * plot_tvector , int index); + bool enkf_plot_tvector_all_active( const enkf_plot_tvector_type * plot_tvector ); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/enkf_serialize.hpp b/libres/lib/include/ert/enkf/enkf_serialize.hpp new file mode 100644 index 00000000000..70105b583a0 --- /dev/null +++ b/libres/lib/include/ert/enkf/enkf_serialize.hpp @@ -0,0 +1,58 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'enkf_serialize.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 ERT_ENKF_SERIALIZE_H +#define ERT_ENKF_SERIALIZE_H +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + +#include + +#include + + + + +void enkf_matrix_serialize(const void * __node_data , + int node_size , + ecl_data_type node_type , + const active_list_type * __active_list , + matrix_type * A, + int row_offset, + int column); + + +void enkf_matrix_deserialize(void * __node_data , + int node_size , + ecl_data_type node_type , + const active_list_type * __active_list , + const matrix_type * A, + int row_offset, + int column); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/enkf_state.hpp b/libres/lib/include/ert/enkf/enkf_state.hpp new file mode 100644 index 00000000000..a564db4906e --- /dev/null +++ b/libres/lib/include/ert/enkf/enkf_state.hpp @@ -0,0 +1,94 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'enkf_state.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 ERT_ENKF_STATE_H +#define ERT_ENKF_STATE_H + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct enkf_state_struct enkf_state_type; + + void * enkf_state_load_from_forward_model_mt( void * arg ); + void enkf_state_initialize(enkf_state_type * enkf_state , rng_type * rng, enkf_fs_type * fs, const stringlist_type * param_list , init_mode_type init_mode); + + int enkf_state_load_from_forward_model(enkf_state_type * enkf_state , + run_arg_type * run_arg , + stringlist_type * msg_list); + + void enkf_state_init_eclipse(const res_config_type * res_config, + const run_arg_type * run_arg ); + + enkf_state_type * enkf_state_alloc(int , + rng_type * main_rng , + model_config_type * , + ensemble_config_type * , + const site_config_type * , + const ecl_config_type * , + ert_templates_type * templates); + + void enkf_state_add_node(enkf_state_type * , const char * , const enkf_config_node_type * ); + void enkf_state_ecl_write(const ensemble_config_type * ens_config, const model_config_type * model_config, const run_arg_type * run_arg , enkf_fs_type * fs); + void enkf_state_free(enkf_state_type * ); + + const ensemble_config_type * enkf_state_get_ensemble_config( const enkf_state_type * enkf_state ); + +/******************************************************************/ +/* Forward model callbacks: */ + bool enkf_state_complete_forward_modelOK__(void * arg ); + bool enkf_state_complete_forward_modelRETRY__(void * arg ); + bool enkf_state_complete_forward_modelEXIT__(void * arg ); + + bool enkf_state_complete_forward_modelOK(const res_config_type * res_config, + run_arg_type * run_arg); + bool enkf_state_complete_forward_model_EXIT_handler__(run_arg_type * run_arg); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/enkf_types.hpp b/libres/lib/include/ert/enkf/enkf_types.hpp new file mode 100644 index 00000000000..972f3fe0241 --- /dev/null +++ b/libres/lib/include/ert/enkf/enkf_types.hpp @@ -0,0 +1,218 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'enkf_types.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 ERT_ENKF_TYPES_H +#define ERT_ENKF_TYPES_H +#ifdef __cplusplus +extern "C" { +#endif + +#include + + + +/* + This enum signals the three different states a "cell" in + observation/data node can be in: + + ACTIVE: The cell is active and should be used/updated in EnKF + analysis. + + LOCAL_INACTIVE: The cell is not included in the current local + analysis ministep + + DEACTIVATED: The cell has been deactivated by the functionality + deactivating outliers. + +*/ + +typedef enum { ACTIVE = 1, + LOCAL_INACTIVE = 2, /* Not active in current local update scheme. */ + DEACTIVATED = 3, /* Deactivaed due to to small overlap, or... */ + MISSING = 4} active_type; /* Set as missing by the forward model. */ + + + +/* + The enkf_var_type enum defines logical groups of variables. All + variables in the same group, i.e. 'parameter' are typically treated + in the same manner. So the reason for creating this type is to be + able to say for instance: "Load all dynamic_state variables". + + Observe that these are used as bitmask's, i.e. the numerical values + must be a power of 2 series. +*/ + +typedef enum {INVALID_VAR = 0 , /* */ + PARAMETER = 1 , /* A parameter which is updated with enkf: PORO , MULTFLT , ..*/ + DYNAMIC_RESULT = 4 , /* Dynamic results which are NOT needed for a restart - i.e. well rates. */ + INDEX_STATE = 16 , /* Index data - enum value is used for storage classification */ + EXT_PARAMETER = 32 } /* Parameter fully managed by external scope. */ +enkf_var_type; + + + + +/* + ert_impl_type are the actual node implementation types. Observe + that one ert_impl_type can be used in several ways as + enkf_var_type. For instance the pressure is implemented with a + field, and behaves as a dynamic_state variable, on the other hand + the permeability is also implemented as a field, but this is a + parameter. + + These correspond to implementation types. The numbers are on disk, + and should **NOT BE UPDATED**. The ERT_MIN_TYPE and MAX_TYPE + identifiers are needed for the block_fs_driver. +*/ + + + +typedef enum {INVALID = 0 , + IMPL_TYPE_OFFSET = 100 , + FIELD = 104 , /* WELL has been removed */ + GEN_KW = 107 , /* RELPERM has been removed & HAVANA_FAULT */ + SUMMARY = 110 , /* TPGZONE has been removed */ + GEN_DATA = 113 , /* PILOT_POINT has been removed */ + SURFACE = 114 , + CONTAINER = 115 , + EXT_PARAM = 116 } ert_impl_type; + + + +/* + Should update the functions enkf_types_get_impl_name() and + enkf_types_get_impl_type__() when this enum is updated. + In addition to enkf_config_add_type(). +*/ + + + + + +typedef enum { REPORT_STEP_INCOMPATIBLE = 1, + LOAD_FAILURE = 2} enkf_fw_load_result_enum; + + + + + + + /** + These are 2^n bitmasks. + */ + +typedef enum { TRUNCATE_NONE = 0, + TRUNCATE_MIN = 1, + TRUNCATE_MAX = 2 } truncation_type; + + + + +/** + This enum is used to differentiate between different types of + run. The point is that depending on this mode we can be more or + less restrictive on the amount of input we require from the user. + + In mode enkf_assimlation ( which is the default ), we require quite + a lot of info, whereas in the case screening_experiment we require + less. + + screening_experiment: + - SIZE + - RUNPATH + - ECLBASE + - SCHEDULE_FILE + - DATA_FILE + - FORWARD_MODEL. + + ensemble_experiment: + - ENSPATH + - INIT_FILE (or estimation of EQUIL) + +*/ + +typedef enum { //ENKF_ASSIMILATION = 1, + ENSEMBLE_EXPERIMENT = 2, + SMOOTHER_RUN = 4 , + INIT_ONLY = 8 , + SMOOTHER_UPDATE = 16, + CASE_INIT_ONLY = 32 } run_mode_type; + + + + typedef enum { JOB_NOT_STARTED = 0, + JOB_SUBMITTED = 1, // This implies that it has been submitted to the internal queue system; we don't know if it is actually running or not. + JOB_RUN_FAILURE = 2, + JOB_LOAD_FAILURE = 3, + JOB_RUN_OK = 4 } run_status_type; + + +/*****************************************************************/ + +/** + This enum is used when we are setting up the dependencies between + observations and variables. The modes all_active and inactive are + sufficient information, for the values partly active we need + additional information. + + The same type is used both for variables (PRESSURE/PORO/MULTZ/...) + and for observations. +*/ + +typedef enum { + ALL_ACTIVE = 1, /* The variable/observation is fully active, i.e. all cells/all faults/all .. */ + INACTIVE = 2, /* Fully inactive */ + PARTLY_ACTIVE = 3 /* Partly active - must supply additonal type spesific information on what is active.*/ +} active_mode_type; + + + typedef struct { + int report_step; + int iens; + } node_id_type; + + + +/*****************************************************************/ +/* Possible transitions: */ + typedef enum { + STATE_UNDEFINED = 1, + STATE_INITIALIZED = 2, + STATE_HAS_DATA = 4, + STATE_LOAD_FAILURE = 8, + STATE_PARENT_FAILURE = 16 + } realisation_state_enum; + + + typedef enum { + INIT_NONE = 0, + INIT_CONDITIONAL = 1, + INIT_FORCE = 2 + } init_mode_type; + + + +typedef struct enkf_obs_struct enkf_obs_type; + +const char * enkf_types_get_impl_name(ert_impl_type ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/enkf_util.hpp b/libres/lib/include/ert/enkf/enkf_util.hpp new file mode 100644 index 00000000000..71cfb7c84c6 --- /dev/null +++ b/libres/lib/include/ert/enkf/enkf_util.hpp @@ -0,0 +1,43 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'enkf_util.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 ERT_ENKF_UTIL_H +#define ERT_ENKF_UTIL_H +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include + +#include +#include + +#include +#include + +#include + +void enkf_util_truncate(void * , int , ecl_data_type , void * , void *); +double enkf_util_rand_normal(double , double , rng_type * rng); +void enkf_util_assert_buffer_type(buffer_type * buffer, ert_impl_type target_type); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/ensemble_config.hpp b/libres/lib/include/ert/enkf/ensemble_config.hpp new file mode 100644 index 00000000000..ac81a4ece1d --- /dev/null +++ b/libres/lib/include/ert/enkf/ensemble_config.hpp @@ -0,0 +1,91 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'ensemble_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 ERT_ENSEMBLE_CONFIG_H +#define ERT_ENSEMBLE_CONFIG_H +#include + +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef struct ensemble_config_struct ensemble_config_type; + + void ensemble_config_set_refcase( ensemble_config_type * ensemble_config , const ecl_sum_type * refcase); + void ensemble_config_set_gen_kw_format( ensemble_config_type * ensemble_config , const char * gen_kw_format_string); + enkf_config_node_type * ensemble_config_add_container( ensemble_config_type * ensemble_config , const char * key); + enkf_config_node_type * ensemble_config_add_surface( ensemble_config_type * ensemble_config , const char * key , bool forward_init); + + void ensemble_config_add_node( ensemble_config_type * ensemble_config , enkf_config_node_type * node); + enkf_config_node_type * ensemble_config_add_gen_data( ensemble_config_type * config , const char * key , bool dynamic , bool forward_init); + enkf_config_node_type * ensemble_config_add_summary(ensemble_config_type * ensemble_config , const char * key, load_fail_type load_fail); + enkf_config_node_type * ensemble_config_add_summary_observation(ensemble_config_type * ensemble_config , const char * key, load_fail_type load_fail); + enkf_config_node_type * ensemble_config_add_gen_kw( ensemble_config_type * config , const char * key , bool forward_init); + enkf_config_node_type * ensemble_config_add_field( ensemble_config_type * config , const char * key , ecl_grid_type * ecl_grid , bool forward_init); + int ensemble_config_get_observations( const ensemble_config_type * config , enkf_obs_type * enkf_obs , const char * user_key , int obs_count , + time_t * obs_time , double * y , double * std); + void ensemble_config_clear_obs_keys(ensemble_config_type * ensemble_config); + void ensemble_config_add_obs_key(ensemble_config_type * , const char * , const char * ); + const enkf_config_node_type * ensemble_config_user_get_node(const ensemble_config_type * , const char * , char **); + void ensemble_config_free(ensemble_config_type * ); + bool ensemble_config_has_key(const ensemble_config_type * , const char * ); + bool ensemble_config_has_impl_type(const ensemble_config_type * config, const ert_impl_type impl_type); + bool ensemble_config_have_forward_init( const ensemble_config_type * ensemble_config ); + bool ensemble_config_require_summary(const ensemble_config_type * config); + void ensemble_config_add_config_items(config_parser_type * ); + + void ensemble_config_init_GEN_PARAM( ensemble_config_type * ensemble_config , const config_content_type * config ); + + field_trans_table_type * ensemble_config_get_trans_table( const ensemble_config_type * ensemble_config ); + enkf_config_node_type * ensemble_config_get_node(const ensemble_config_type * , const char * ); + enkf_config_node_type * ensemble_config_get_or_create_summary_node(ensemble_config_type * ensemble_config, const char * key); + stringlist_type * ensemble_config_alloc_keylist(const ensemble_config_type *); + stringlist_type * ensemble_config_alloc_keylist_from_var_type(const ensemble_config_type * , int var_mask); + stringlist_type * ensemble_config_alloc_keylist_from_impl_type(const ensemble_config_type *, ert_impl_type); + ensemble_config_type * ensemble_config_alloc_load(const char *, ecl_grid_type *, const ecl_sum_type *); + ensemble_config_type * ensemble_config_alloc(const config_content_type *, ecl_grid_type *, const ecl_sum_type *); + PY_USED ensemble_config_type * ensemble_config_alloc_full(const char * gen_kw_format_string); + void ensemble_config_init_SUMMARY_full( ensemble_config_type *, const char *, const ecl_sum_type *); + + const summary_key_matcher_type * ensemble_config_get_summary_key_matcher(const ensemble_config_type * ensemble_config); + int ensemble_config_get_size(const ensemble_config_type * ensemble_config ); + int ensemble_config_forward_init(const ensemble_config_type * ens_config, + const run_arg_type * run_arg); + + UTIL_IS_INSTANCE_HEADER( ensemble_config ); + UTIL_SAFE_CAST_HEADER( ensemble_config ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/ert_log.h.gch b/libres/lib/include/ert/enkf/ert_log.h.gch new file mode 100644 index 00000000000..237c896cd4e Binary files /dev/null and b/libres/lib/include/ert/enkf/ert_log.h.gch differ diff --git a/libres/lib/include/ert/enkf/ert_run_context.hpp b/libres/lib/include/ert/enkf/ert_run_context.hpp new file mode 100644 index 00000000000..7ab905f2a68 --- /dev/null +++ b/libres/lib/include/ert/enkf/ert_run_context.hpp @@ -0,0 +1,109 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'ert_run_context.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 ERT_RUN_CONTEXT_H +#define ERT_RUN_CONTEXT_H + +#include +#include + +#include +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ert_run_context_struct ert_run_context_type; + + stringlist_type * ert_run_context_alloc_runpath_list(const bool_vector_type * iactive , + const path_fmt_type * runpath_fmt , + const subst_list_type * subst_list , + int iter); + + char * ert_run_context_alloc_runpath( int iens , const path_fmt_type * runpath_fmt , const subst_list_type * subst_list , int iter); + + ert_run_context_type * ert_run_context_alloc(run_mode_type run_mode, + init_mode_type init_mode, + enkf_fs_type * sim_fs , + enkf_fs_type * target_update_fs , + bool_vector_type * iactive , + path_fmt_type * runpath_fmt , + const char * jobname_fmt, + subst_list_type * subst_list , + int iter); + + ert_run_context_type * ert_run_context_alloc_ENSEMBLE_EXPERIMENT(enkf_fs_type * sim_fs , + bool_vector_type * iactive , + const path_fmt_type * runpath_fmt , + const char * jobname_fmt, + const subst_list_type * subst_list , + int iter); + + ert_run_context_type * ert_run_context_alloc_INIT_ONLY(enkf_fs_type * sim_fs, + init_mode_type init_mode, + bool_vector_type * iactive , + const path_fmt_type * runpath_fmt , + const subst_list_type * subst_list , + int iter); + + ert_run_context_type * ert_run_context_alloc_SMOOTHER_RUN(enkf_fs_type * sim_fs , enkf_fs_type * target_update_fs , + bool_vector_type * iactive , + const path_fmt_type * runpath_fmt , + const char * jobname_fmt, + const subst_list_type * subst_list , + int iter); + + ert_run_context_type * ert_run_context_alloc_SMOOTHER_UPDATE(enkf_fs_type * sim_fs , enkf_fs_type * target_update_fs ); + + ert_run_context_type * ert_run_context_alloc_CASE_INIT(enkf_fs_type * sim_fs, + const bool_vector_type * iactive); + + void ert_run_context_set_sim_fs(ert_run_context_type * context, enkf_fs_type * sim_fs); + void ert_run_context_set_update_target_fs(ert_run_context_type * context, enkf_fs_type * update_target_fs); + + void ert_run_context_free( ert_run_context_type * ); + int ert_run_context_get_size( const ert_run_context_type * context ); + run_mode_type ert_run_context_get_mode( const ert_run_context_type * context ); + bool_vector_type * ert_run_context_alloc_iactive(const ert_run_context_type * context); + bool_vector_type const * ert_run_context_get_iactive(const ert_run_context_type * context); + int ert_run_context_get_iter( const ert_run_context_type * context ); + int ert_run_context_get_active_size(const ert_run_context_type * context); + int ert_run_context_get_step1( const ert_run_context_type * context ); + run_arg_type * ert_run_context_iget_arg( const ert_run_context_type * context , int index); + run_arg_type * ert_run_context_iens_get_arg( const ert_run_context_type * context , int iens); + void ert_run_context_deactivate_realization( ert_run_context_type * context , int iens); + const char * ert_run_context_get_id( const ert_run_context_type * context ); + init_mode_type ert_run_context_get_init_mode( const ert_run_context_type * context ); + char * ert_run_context_alloc_run_id( ); + + enkf_fs_type * ert_run_context_get_sim_fs(const ert_run_context_type * run_context); + enkf_fs_type * ert_run_context_get_update_target_fs(const ert_run_context_type * run_context); + bool ert_run_context_iactive( const ert_run_context_type * context , int iens); + + UTIL_IS_INSTANCE_HEADER( ert_run_context ); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/ert_template.hpp b/libres/lib/include/ert/enkf/ert_template.hpp new file mode 100644 index 00000000000..a98589cc7fa --- /dev/null +++ b/libres/lib/include/ert/enkf/ert_template.hpp @@ -0,0 +1,61 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'ert_template.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 ERT_TEMPLATE_H +#define ERT_TEMPLATE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include +#include + +typedef struct ert_template_struct ert_template_type; +typedef struct ert_templates_struct ert_templates_type; + + +stringlist_type * ert_templates_alloc_list( ert_templates_type * ert_templates); +ert_template_type * ert_template_alloc( const char * template_file , const char * target_file, subst_list_type * parent_subst) ; +ert_templates_type * ert_templates_alloc_default(subst_list_type * parent_subst); +void ert_template_free( ert_template_type * ert_tamplete ); +void ert_template_instantiate( ert_template_type * ert_template , const char * path , const subst_list_type * arg_list ); +void ert_template_add_arg( ert_template_type * ert_template , const char * key , const char * value ); +subst_list_type * ert_template_get_arg_list( ert_template_type * ert_template); +void ert_template_free__(void * arg); + +void ert_templates_clear( ert_templates_type * ert_templates ); +ert_template_type * ert_templates_get_template( ert_templates_type * ert_templates , const char * key); + +ert_templates_type * ert_templates_alloc(subst_list_type *, const config_content_type *); +void ert_templates_free( ert_templates_type * ert_templates ); +ert_template_type * ert_templates_add_template( ert_templates_type * ert_templates , const char * key , const char * template_file , const char * target_file , const char * arg_string); +void ert_templates_instansiate( ert_templates_type * ert_templates , const char * path , const subst_list_type * arg_list); +void ert_templates_del_template( ert_templates_type * ert_templates , const char * key); + +const char * ert_template_get_template_file( const ert_template_type * ert_template); +const char * ert_template_get_target_file( const ert_template_type * ert_template); +void ert_templates_init( ert_templates_type * templates , const config_content_type * config ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/ert_test_context.hpp b/libres/lib/include/ert/enkf/ert_test_context.hpp new file mode 100644 index 00000000000..5a52eb04df4 --- /dev/null +++ b/libres/lib/include/ert/enkf/ert_test_context.hpp @@ -0,0 +1,57 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'ert_test_context.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 ERT_TEST_CONTEXT_H +#define ERT_TEST_CONTEXT_H + +#include + +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ert_test_context_struct ert_test_context_type; + +ert_test_context_type * ert_test_context_alloc( const char * test_name , const char * model_config); +ert_test_context_type * ert_test_context_alloc__( const char * test_name , const char * model_config, bool store_area); +PY_USED ert_test_context_type * ert_test_context_alloc_python( test_work_area_type * work_area, res_config_type * res_config); +void ert_test_context_free( ert_test_context_type * test_context ); +enkf_main_type * ert_test_context_get_main( ert_test_context_type * test_context ); +bool ert_test_context_install_workflow_job( ert_test_context_type * test_context , const char * job_name , const char * job_file); +bool ert_test_context_run_worklow_job( ert_test_context_type * test_context , const char * job_name, const stringlist_type * args); +void ert_test_context_fwrite_workflow_job( FILE * stream , const char * job_name , const stringlist_type * args); +bool ert_test_context_install_workflow( ert_test_context_type * test_context , const char * workflow_name , const char * workflow_file); +bool ert_test_context_run_worklow( ert_test_context_type * test_context , const char * workflow_name); +const char * ert_test_context_get_cwd( const ert_test_context_type * test_context ); + +UTIL_IS_INSTANCE_HEADER( ert_test_context ); + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/libres/lib/include/ert/enkf/ert_workflow_list.hpp b/libres/lib/include/ert/enkf/ert_workflow_list.hpp new file mode 100644 index 00000000000..9df0d933bc7 --- /dev/null +++ b/libres/lib/include/ert/enkf/ert_workflow_list.hpp @@ -0,0 +1,70 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'ert_workflow_list.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 ERT_WORKFLOW_LIST_H +#define ERT_WORKFLOW_LIST_H + + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include +#include +#include + +#include +#include + + + typedef struct ert_workflow_list_struct ert_workflow_list_type; + + workflow_type * ert_workflow_list_get_workflow(ert_workflow_list_type * workflow_list , const char * workflow_name ); + workflow_type * ert_workflow_list_add_workflow( ert_workflow_list_type * workflow_list , const char * workflow_file , const char * workflow_name); + void ert_workflow_list_free( ert_workflow_list_type * workflow_list ); + ert_workflow_list_type * ert_workflow_list_alloc_empty( const subst_list_type * subst_list ); + ert_workflow_list_type * ert_workflow_list_alloc_load_site_config(const subst_list_type *); + ert_workflow_list_type * ert_workflow_list_alloc(const subst_list_type * context, const config_content_type * config_content); + PY_USED ert_workflow_list_type * ert_workflow_list_alloc_full(const subst_list_type * context, workflow_joblist_type * workflow_joblist); + + void ert_workflow_list_add_jobs_in_directory( ert_workflow_list_type * workflow_list , const char * path ); + void ert_workflow_list_add_job( ert_workflow_list_type * workflow_list , const char * job_name , const char * config_file ); + bool ert_workflow_list_has_job( const ert_workflow_list_type * workflow_list , const char * job_name); + const workflow_job_type * ert_workflow_list_get_job( const ert_workflow_list_type * workflow_list , const char * job_name); + stringlist_type * ert_workflow_list_get_job_names(const ert_workflow_list_type * workflow_list); + void ert_workflow_list_add_alias( ert_workflow_list_type * workflow_list , const char * real_name , const char * alias); + void ert_workflow_list_add_config_items( config_parser_type * config ); + bool ert_workflow_list_run_workflow__(ert_workflow_list_type * workflow_list, workflow_type * workflow, bool verbose , void * self); + bool ert_workflow_list_has_workflow(ert_workflow_list_type * workflow_list , const char * workflow_name ); + stringlist_type * ert_workflow_list_alloc_namelist( ert_workflow_list_type * workflow_list ); + void ert_workflow_list_set_verbose( ert_workflow_list_type * workflow_list , bool verbose); + bool ert_workflow_list_run_workflow_blocking(ert_workflow_list_type * workflow_list , const char * workflow_name , void * self); + const subst_list_type * ert_workflow_list_get_context(const ert_workflow_list_type * workflow_list); + int ert_workflow_list_get_size( const ert_workflow_list_type * workflow_list); + + + UTIL_IS_INSTANCE_HEADER( ert_workflow_list ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libres/lib/include/ert/enkf/ext_param.hpp b/libres/lib/include/ert/enkf/ext_param.hpp new file mode 100644 index 00000000000..1da753ee12f --- /dev/null +++ b/libres/lib/include/ert/enkf/ext_param.hpp @@ -0,0 +1,55 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'ext_param_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 EXT_PARAM_H +#define EXT_PARAM_H +#ifdef __cplusplus +extern "C" { +#endif +#include + +#include + +typedef struct ext_param_struct ext_param_type; + + bool ext_param_iset( ext_param_type * param, int index , double value); + bool ext_param_iiset( ext_param_type * param, int index , int suffix_index, double value); + double ext_param_iget(const ext_param_type * param, int index); + double ext_param_iiget(const ext_param_type * param, int index, int suffix_index); + bool ext_param_key_set( ext_param_type * param, const char * key, double value); + bool ext_param_key_suffix_set( ext_param_type * param, const char * key, const char * suffix, double value); + double ext_param_key_get( const ext_param_type * param, const char * key ); + double ext_param_key_suffix_get( const ext_param_type * param, const char * key, const char * suffix); + void ext_param_json_export(const ext_param_type * ext_param, const char * json_file); + void ext_param_free(ext_param_type *ext_param); + ext_param_type * ext_param_alloc(const ext_param_config_type * config); + ext_param_config_type const* ext_param_get_config(const ext_param_type * param); + + +UTIL_SAFE_CAST_HEADER(ext_param); +UTIL_SAFE_CAST_HEADER_CONST(ext_param); +VOID_FREE_HEADER(ext_param); +VOID_ALLOC_HEADER(ext_param); +VOID_ECL_WRITE_HEADER(ext_param); +VOID_WRITE_TO_BUFFER_HEADER(ext_param) +VOID_READ_FROM_BUFFER_HEADER(ext_param) + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/ext_param_config.hpp b/libres/lib/include/ert/enkf/ext_param_config.hpp new file mode 100644 index 00000000000..6651f71a339 --- /dev/null +++ b/libres/lib/include/ert/enkf/ext_param_config.hpp @@ -0,0 +1,49 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'ext_param_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 EXT_PARAM_CONFIG_H +#define EXT_PARAM_CONFIG_H +#ifdef __cplusplus +extern "C" { +#endif +#include + +typedef struct ext_param_config_struct ext_param_config_type; + +ext_param_config_type * ext_param_config_alloc(const char * key, const stringlist_type * keys); + +void ext_param_config_free (ext_param_config_type * config ); +void ext_param_config_ikey_set_suffixes(ext_param_config_type * config, int ikey, const stringlist_type * suffixes); + +int ext_param_config_get_data_size (const ext_param_config_type * config); +const char * ext_param_config_iget_key (const ext_param_config_type * config, int index); +int ext_param_config_get_key_index (const ext_param_config_type * config, const char * key); +bool ext_param_config_has_key (const ext_param_config_type * config, const char * key); +int ext_param_config_ikey_get_suffix_count(const ext_param_config_type * config, int key_id); +const char * ext_param_config_ikey_iget_suffix (const ext_param_config_type * config, int key_id, int suffix_id); +int ext_param_config_ikey_get_suffix_index(const ext_param_config_type * config, int key_id, const char * suffix); + +UTIL_SAFE_CAST_HEADER(ext_param_config); +UTIL_SAFE_CAST_HEADER_CONST(ext_param_config); +VOID_FREE_HEADER(ext_param_config); +VOID_GET_DATA_SIZE_HEADER(ext_param); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/field.hpp b/libres/lib/include/ert/enkf/field.hpp new file mode 100644 index 00000000000..faa089ba4cd --- /dev/null +++ b/libres/lib/include/ert/enkf/field.hpp @@ -0,0 +1,83 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'field.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 ERT_FIELD_H +#define ERT_FIELD_H +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Typedef field_type moved to field_config.h */ + + void field_scale(field_type * field, double scale_factor); + double field_iget_double(const field_type * , int ); + double field_ijk_get_double(const field_type * field, int , int , int ); + float field_iget_float(const field_type * , int ); + PY_USED void field_ijk_get(const field_type * , int , int , int , void *); + void field_ecl_write1D_fortio(const field_type * , fortio_type *); + void field_ecl_write3D_fortio(const field_type * , fortio_type *, const char *); + void field_ROFF_export(const field_type * , const char * , const char *); + void field_copy_ecl_kw_data(field_type * , const ecl_kw_type * ); + void field_free(field_type *); + bool field_fload_keep_inactive(field_type * field , const char * filename); + bool field_fload_rms(field_type * field , const char * filename, bool keep_inactive); + void field_export3D(const field_type * , void *, bool, ecl_data_type , void *, const char *); + void field_export(const field_type * , const char * , fortio_type * , field_file_format_type , bool, const char *); + int field_get_size(const field_type * field); + + void field_inplace_output_transform(field_type * field); + + + + UTIL_IS_INSTANCE_HEADER(field); + UTIL_SAFE_CAST_HEADER_CONST(field); + VOID_ALLOC_HEADER(field); + VOID_FREE_HEADER(field); + VOID_COPY_HEADER (field); + VOID_INITIALIZE_HEADER(field); + VOID_ECL_WRITE_HEADER (field); + VOID_USER_GET_HEADER(field); + VOID_READ_FROM_BUFFER_HEADER(field); + VOID_WRITE_TO_BUFFER_HEADER(field); + VOID_SERIALIZE_HEADER(field); + VOID_DESERIALIZE_HEADER(field); + VOID_CLEAR_HEADER(field); + VOID_SET_INFLATION_HEADER(field); + VOID_IMUL_HEADER(field); + VOID_IADD_HEADER(field); + VOID_IADDSQR_HEADER(field); + VOID_SCALE_HEADER(field); + VOID_ISQRT_HEADER(field); + VOID_FLOAD_HEADER(field); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/field_common.hpp b/libres/lib/include/ert/enkf/field_common.hpp new file mode 100644 index 00000000000..cdfd7873bc6 --- /dev/null +++ b/libres/lib/include/ert/enkf/field_common.hpp @@ -0,0 +1,37 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'field_common.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 ERT_FIELD_COMMON_H +#define ERT_FIELD_COMMON_H + +/* + Contains some headers which both field.c and field_config.c need - + split like this to avoid circular dependencies. +*/ + + + +typedef struct field_config_struct field_config_type; +typedef struct field_struct field_type; + +field_type * field_alloc(const field_config_type * ); +bool field_fload(field_type * , const char * ); + + + +#endif diff --git a/libres/lib/include/ert/enkf/field_config.hpp b/libres/lib/include/ert/enkf/field_config.hpp new file mode 100644 index 00000000000..227a5f605a2 --- /dev/null +++ b/libres/lib/include/ert/enkf/field_config.hpp @@ -0,0 +1,183 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'field_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 ERT_FIELD_CONFIG_H +#define ERT_FIELD_CONFIG_H + + +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + This is purely a convenience structure used during initialization, + to denote which arguments are required and, which should be + defualted. + +*/ + +typedef enum { + ECLIPSE_RESTART = 1, + ECLIPSE_PARAMETER = 2, + GENERAL = 3, + UNKNOWN_FIELD_TYPE= 4 +} field_type_enum; + + +/** + The field_file_format_type denotes different ways to store a + field. Unfortunately the different elements in the enum definition + have somewhat different properties: + + + 1. ecl_kw_file is for input - either pack or unpacked. + + 2. ecl_kw_file_active_cells / ecl_kw_file_all_cells are for output. + + 3. Except for ecl_restart_file all formats are for A FILE (with a + filename), more or less assuming that this field is the only + content in the file, whereas ecl_restart_file is for a restart + block, and not a file. + + This has some slightly unlogical consequences: + + 1. The enum has 'file_format' in the name, but ecl_restart_file + is not a file. + + 2. The functions which guess/determine a file type can not return + all possible values of the enum. + + 3. Treatment is not symmetric for input/output. + +*/ + + + + +typedef enum { UNDEFINED_FORMAT = 0, + RMS_ROFF_FILE = 1, + ECL_KW_FILE = 2, /* ecl_kw format either packed (i.e. active cells) *or* all cells - used when reading from file. */ + ECL_KW_FILE_ACTIVE_CELLS = 3, /* ecl_kw format, only active cells - used writing to file. */ + ECL_KW_FILE_ALL_CELLS = 4, /* ecl_kw_format, all cells - used when writing to file. */ + ECL_GRDECL_FILE = 5, + ECL_FILE = 6, /* Assumes packed on export. */ + FILE_FORMAT_NULL = 7} field_file_format_type; /* Used when the guess functions are given NULL to check -should never be read. */ + + +/* active_cells currently not really implemented */ + + +void field_config_update_parameter_field( field_config_type * config , int truncation, double min_value , double max_value, + field_file_format_type export_format , + const char * init_transform , const char * output_transform ); + + +void field_config_update_general_field( field_config_type * config , int truncation, double min_value , double max_value, + field_file_format_type export_format , /* This can be guessed with the field_config_default_export_format( ecl_file ) function. */ + const char * init_transform , + const char * input_transform , + const char * output_transform ); + + +field_config_type * field_config_alloc_empty( const char * ecl_kw_name , ecl_grid_type * ecl_grid , field_trans_table_type * trans_table, bool global_size ); + + +C_USED const char * field_config_default_extension(field_file_format_type , bool ); +field_file_format_type field_config_guess_file_type(const char * ); +ecl_data_type field_config_get_ecl_data_type(const field_config_type *); +void field_config_get_dims(const field_config_type * , int * , int * , int *); +PY_USED int field_config_get_nx(const field_config_type * config ); +PY_USED int field_config_get_ny(const field_config_type * config ); +PY_USED int field_config_get_nz(const field_config_type * config ); +void field_config_free(field_config_type *); +int field_config_get_volume(const field_config_type * ); +int field_config_get_data_size_from_grid(const field_config_type * config); +void field_config_set_ecl_data_type(field_config_type * , ecl_data_type ); +int field_config_get_byte_size(const field_config_type * ); +int field_config_get_sizeof_ctype(const field_config_type * ); +int field_config_active_index(const field_config_type * , int , int , int ); +int field_config_global_index(const field_config_type * , int , int , int ); +bool field_config_ijk_valid(const field_config_type * , int , int , int ); +bool field_config_ijk_active(const field_config_type * config , int i , int j , int k); +bool field_config_active_cell(const field_config_type * , int , int , int); +field_file_format_type field_config_get_export_format(const field_config_type * ); +void field_config_set_key(field_config_type * , const char *); +void field_config_enkf_OFF(field_config_type * ); +bool field_config_enkf_mode(const field_config_type * config); +const char * field_config_get_key(const field_config_type * ); +bool field_config_keep_inactive_cells(const field_config_type *); +field_func_type * field_config_get_init_transform(const field_config_type * ); +field_func_type * field_config_get_output_transform(const field_config_type * ); +bool field_config_is_valid( const field_config_type * field_config ); +void field_config_assert_binary( const field_config_type * , const field_config_type * , const char * ); +void field_config_assert_unary( const field_config_type * , const char * ); + +const char * field_config_get_init_transform_name( const field_config_type * field_config ); +const char * field_config_get_output_transform_name( const field_config_type * field_config ); + +void field_config_set_truncation(field_config_type * , int , double , double ); +int field_config_get_truncation_mode(const field_config_type * config ); +double field_config_get_truncation_min( const field_config_type * config ); +double field_config_get_truncation_max( const field_config_type * config ); +ecl_grid_type * field_config_get_grid(const field_config_type * ); +const char * field_config_get_grid_name( const field_config_type * ); + +int field_config_parse_user_key(const field_config_type * config, const char * index_key , int *i , int *j , int *k); +bool field_config_parse_user_key__( const char * index_key , int *i , int *j , int *k); + +field_file_format_type field_config_default_export_format(const char * filename); +const char * field_config_get_output_transform_name( const field_config_type * field_config ) ; +const char * field_config_get_init_transform_name( const field_config_type * field_config ) ; + +void field_config_fprintf_config( const field_config_type * config , enkf_var_type var_type , const char * outfile , const char * infile , + const char * min_std_file , FILE * stream); + +field_type_enum field_config_get_type( const field_config_type * config); + + +/*Generated headers */ +UTIL_IS_INSTANCE_HEADER(field_config); +UTIL_SAFE_CAST_HEADER(field_config); +UTIL_SAFE_CAST_HEADER_CONST(field_config); +CONFIG_GET_ECL_KW_NAME_HEADER(field); +VOID_FREE_HEADER(field_config); +VOID_GET_DATA_SIZE_HEADER(field); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/field_trans.hpp b/libres/lib/include/ert/enkf/field_trans.hpp new file mode 100644 index 00000000000..c4aaafa699e --- /dev/null +++ b/libres/lib/include/ert/enkf/field_trans.hpp @@ -0,0 +1,44 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'field_trans.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 ERT_FIELD_TRANS_H +#define ERT_FIELD_TRANS_H +#ifdef __cplusplus +extern "C" { +#endif +#include +#include + + +typedef float (field_func_type) ( float ); +typedef struct field_trans_table_struct field_trans_table_type; + + +void field_trans_table_fprintf(const field_trans_table_type * , FILE * ); +void field_trans_table_free(field_trans_table_type * ); +void field_trans_table_add(field_trans_table_type * , const char * , const char * , field_func_type * ); +field_trans_table_type * field_trans_table_alloc(); +bool field_trans_table_has_key(field_trans_table_type * , const char * ); +field_func_type * field_trans_table_lookup(field_trans_table_type * , const char * ); + + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/forward_load_context.hpp b/libres/lib/include/ert/enkf/forward_load_context.hpp new file mode 100644 index 00000000000..029c30a1601 --- /dev/null +++ b/libres/lib/include/ert/enkf/forward_load_context.hpp @@ -0,0 +1,57 @@ +/* + Copyright (C) 2016 Equinor ASA, Norway. + + The file 'forward_load_context.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 ERT_FORWARD_LOAD_CONTEXT_H +#define ERT_FORWARD_LOAD_CONTEXT_H + +#include +#include + +#include +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct forward_load_context_struct forward_load_context_type; + + bool forward_load_context_accept_messages( const forward_load_context_type * load_context ); + void forward_load_context_add_message( forward_load_context_type * load_context , const char * message ); + void forward_load_context_update_result( forward_load_context_type * load_context , int flags); + int forward_load_context_get_result( const forward_load_context_type * load_context ); + forward_load_context_type * forward_load_context_alloc( const run_arg_type * run_arg , bool load_summary , const ecl_config_type * ecl_config , stringlist_type * messages); + void forward_load_context_free( forward_load_context_type * load_context ); + const ecl_sum_type * forward_load_context_get_ecl_sum( const forward_load_context_type * load_context); + const run_arg_type * forward_load_context_get_run_arg( const forward_load_context_type * load_context ); + const char * forward_load_context_get_run_path( const forward_load_context_type * load_context ); + int forward_load_context_get_load_step(const forward_load_context_type * load_context); + enkf_fs_type * forward_load_context_get_sim_fs( const forward_load_context_type * load_context ); + bool forward_load_context_load_restart_file( forward_load_context_type * load_context , int report_step ); + void forward_load_context_select_step( forward_load_context_type * load_context , int report_step); + + UTIL_IS_INSTANCE_HEADER( forward_load_context ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/fs_driver.hpp b/libres/lib/include/ert/enkf/fs_driver.hpp new file mode 100644 index 00000000000..497d8ec4e90 --- /dev/null +++ b/libres/lib/include/ert/enkf/fs_driver.hpp @@ -0,0 +1,239 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'fs_driver.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 ERT_FS_DRIVER_H +#define ERT_FS_DRIVER_H +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define FS_MAGIC_ID 123998L +#define CURRENT_FS_VERSION 107 +#define MIN_SUPPORTED_FS_VERSION 105 + +/** + Version history: + + 0 : + + + File system version | First svn version | Last svn version + -------------------------------------------------------------------------- + 100 | | 1799 + 101 | 1810 | 1886 + 102 | 1887/1902 | 1996 + 103 | 1997 | + | 2047: sqlite added | 2125 + 104 | 2127 | + | 2140: block_fs_index added + | 2190: started to distribute ert binary internally + 105 | 3918 | + 106 | Git ~ Desember 2015 + 107 | Git ~ September 2017 + ------------------------------------------------------------------------- + + + Version: 100 + ------------ + First version with subdirectories for different cases. + + + + Version: 101 + ------------ + Have changed the format for storing static keywords. Instead of a + directory with numbered files in them, the keywords get an integer + appended: + + INTEHEAD/0 ==> INTEHEAD_0 + + The actual changing of keyword is done in the function + __realloc_static_kw() in enkf_state.c. + + + + Version: 102 + ------------ + Have removed the type spesific plain drivers, now it is only + plain_driver and plain_driver index. The special functionality for + parameters/static/dynamic is now implemented at the enkf_fs level. + The upgrade from 101 only involves the mount info and the + implementation, the actual stored files are not touched by this + upgrade. + + + + Version: 103 + ------------ + Have changed the reading/writing to go through the buffer + type. This should simplify introducing other drivers than the + simple plain file based driver. + + The upgrade to version is 103 is quite extensive - all node types + have specific _103_ functions. The xxx_fread() and xxx_fwrite() + functions were removed in svn:2046. + + A very experimental version of the sqlite driver was added in + svn:2047. When (if ??) this stabilizes it should probably lead to + an upgrade to version 104. + + At the very last checkin of this version the fs prototypes also + changed to use (const enkf_config_node_type *) instances instead of + (const char * ) for the main key. + + + + Version: 104 + ------------ + In this version the treatment of read_xx and write_xx drivers has + changed. There are no longer separate read_xx and write_xx drivers, + instead the drivers have internal state which differentiates + between read and write. + + The block_fs driver is added - and reasonably stabilized in this + version. + + + Observe that all the upgrade functions were removed at svn:3305. + + + Version 104B + ------------ + Vector storage; each case is a seperate enkf_fs instance. Current + is stored with a symlink. Read about 104 -> 104B problems in the + function upgrade104B() in fs_driver.c + + + Version: 105 + ------------ + Using time_map to store time information; common to all members in + ensemble. + + + Version: 106 + ------------ + + Dropped drivers for storing STATIC eclipse keywords. The upgrade + from version 105 to 106 happens silently; the only thing happening + is that the information about the static driver is ignored when + reading the mount map. An older version of ert - with + CURRENT_VERSION == 105 can also read a VERSION == 106 filesystem + with a minor backport and some default heuristics. + + + Version: 107 + ------------ + + Using UTC instead of localtime throughout the code. This implies + that the timemaps written to disk with old versions have time_t + values corresponding to localtime, whereas everything now is + expected to be in utc. + + If we detect a filesystem with version below 107 we stop the + program. If a refcase is supplied the user is given a suggested + commandline to perform an inplace upgrade. +*/ + + + + + typedef struct fs_driver_struct fs_driver_type; + + typedef void (save_kwlist_ftype) (void * , int , int , buffer_type * buffer); /* Functions used to load/store restart_kw_list instances. */ + typedef void (load_kwlist_ftype) (void * , int , int , buffer_type * buffer); + + typedef void (load_node_ftype) (void * driver, const char * , int , int , buffer_type * ); + typedef void (save_node_ftype) (void * driver, const char * , int , int , buffer_type * ); + typedef void (unlink_node_ftype) (void * driver, const char * , int , int ); + typedef bool (has_node_ftype) (void * driver, const char * , int , int ); + + typedef void (load_vector_ftype) (void * driver, const char * , int , buffer_type * ); + typedef void (save_vector_ftype) (void * driver, const char * , int , buffer_type * ); + typedef void (unlink_vector_ftype) (void * driver, const char * , int ); + typedef bool (has_vector_ftype) (void * driver, const char * , int ); + + typedef void (fsync_driver_ftype) (void * driver); + typedef void (free_driver_ftype) (void * driver); + + +/** + The fs_driver_type contains a number of function pointers + and a type_id used for run-time cast checking. + + The fs_driver_type is never actually used, but the point is that + all drivers must implement the fs driver "interface". In practice + this is done by including the macro FS_DRIVER_FIELDS *at the start* + of the definition of another driver, i.e. the simplest actually + working driver, the plain_driver is implemented like this: + + struct plain_driver_struct { + FS_DRIVER_FIELDS + int plain_driver_id; + path_fmt_type * path; + } + + +*/ + + + +#define FS_DRIVER_FIELDS \ +load_node_ftype * load_node; \ +save_node_ftype * save_node; \ +has_node_ftype * has_node; \ +unlink_node_ftype * unlink_node; \ +load_vector_ftype * load_vector; \ +save_vector_ftype * save_vector; \ +has_vector_ftype * has_vector; \ +unlink_vector_ftype * unlink_vector; \ +free_driver_ftype * free_driver; \ +fsync_driver_ftype * fsync_driver; \ +int type_id + + + + +struct fs_driver_struct { + FS_DRIVER_FIELDS; +}; + + + + /*****************************************************************/ + + void fs_driver_init(fs_driver_type * ); + fs_driver_type * fs_driver_safe_cast(void * ); + + void fs_driver_init_fstab( FILE * stream, fs_driver_impl driver_id ); + char * fs_driver_alloc_fstab_file( const char * path ); + FILE * fs_driver_open_fstab( const char * path , bool create); + void fs_driver_assert_magic( FILE * stream ); + void fs_driver_assert_version( FILE * stream , const char * mount_point); + int fs_driver_fread_version( FILE * stream ); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/fs_types.hpp b/libres/lib/include/ert/enkf/fs_types.hpp new file mode 100644 index 00000000000..5ca960f4d2d --- /dev/null +++ b/libres/lib/include/ert/enkf/fs_types.hpp @@ -0,0 +1,80 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'fs_types.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 ERT_FS_TYPES_H +#define ERT_FS_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + The various driver implementations - this goes on disk all over the + place, and the numbers should be considered SET IN STONE. When a new + driver is added the switch statement in the enkf_fs_mount() function + must be updated. +*/ + +// Version from ~ svn:3720 +//typedef enum { +// INVALID_DRIVER_ID = 0, +// PLAIN_DRIVER_INDEX_ID = 1001, +// PLAIN_DRIVER_STATIC_ID = 1002, /* Depreceated */ +// PLAIN_DRIVER_DYNAMIC_ID = 1003, /* Depreceated */ +// PLAIN_DRIVER_PARAMETER_ID = 1004, /* Depreceated */ +// PLAIN_DRIVER_ID = 1005, +// BLOCK_FS_DRIVER_ID = 3001, +// BLOCK_FS_DRIVER_INDEX_ID = 3002 } fs_driver_impl; + + +typedef enum { + INVALID_DRIVER_ID = 0, + BLOCK_FS_DRIVER_ID = 3001} fs_driver_impl; + + + + + + +/* + The categories of drivers. To reduce the risk of programming + error (or at least to detect it ...), there should be no overlap + between these ID's and the ID's of the actual implementations + above. The same comment about permanent storage applies to these + numbers as well. +*/ + +typedef enum { + DRIVER_PARAMETER = 1, + DRIVER_STATIC = 2, // Driver static is no longer in use since December 2015 - but it must be retained here for old mount files on disk. + DRIVER_INDEX = 4, // DRIVER_DYNAMIC = 3; removed at svn ~ 3720. + DRIVER_DYNAMIC_FORECAST = 5, + DRIVER_DYNAMIC_ANALYZED = 6 // Driver DYNAMIC_ANALYZED is no longer in use since April 2016 - but it must be retained here for old mount files on disk. +} fs_driver_enum; + + + + + +bool fs_types_valid( fs_driver_enum driver_type); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/gen_common.hpp b/libres/lib/include/ert/enkf/gen_common.hpp new file mode 100644 index 00000000000..8fc77bdb83f --- /dev/null +++ b/libres/lib/include/ert/enkf/gen_common.hpp @@ -0,0 +1,39 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'gen_common.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 ERT_GEN_COMMON_H +#define ERT_GEN_COMMON_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + +void * gen_common_fscanf_alloc(const char * , ecl_data_type , int * ); +void * gen_common_fread_alloc(const char * , ecl_data_type , int * ); +void * gen_common_fload_alloc(const char * , gen_data_file_format_type , ecl_data_type , ecl_type_enum * , int * ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libres/lib/include/ert/enkf/gen_data.hpp b/libres/lib/include/ert/enkf/gen_data.hpp new file mode 100644 index 00000000000..39ea721ea62 --- /dev/null +++ b/libres/lib/include/ert/enkf/gen_data.hpp @@ -0,0 +1,76 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'gen_data.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 ERT_GEN_DATA_H +#define ERT_GEN_DATA_H + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +void gen_data_assert_size( gen_data_type * gen_data , int size , int report_step); +bool gen_data_forward_load(gen_data_type * gen_data , const char * ecl_file , const forward_load_context_type * load_context); +void gen_data_free(gen_data_type * ); +double gen_data_iget_double(const gen_data_type * , int ); +int gen_data_get_size(const gen_data_type * ); +double gen_data_iget_double(const gen_data_type * , int ); +void gen_data_export(const gen_data_type * gen_data , const char * full_path , gen_data_file_format_type export_type); +void gen_data_export_data(const gen_data_type * gen_data , double_vector_type * export_data); +const char * gen_data_get_key( const gen_data_type * gen_data); +int gen_data_get_size( const gen_data_type * gen_data ); +void gen_data_copy_to_double_vector(const gen_data_type * gen_data , double_vector_type * vector); +bool gen_data_fload_with_report_step( gen_data_type * gen_data , const char * filename , const forward_load_context_type * load_context); + +UTIL_SAFE_CAST_HEADER(gen_data); +UTIL_SAFE_CAST_HEADER_CONST(gen_data); +VOID_USER_GET_HEADER(gen_data); +VOID_ALLOC_HEADER(gen_data); +VOID_FREE_HEADER(gen_data); +VOID_COPY_HEADER (gen_data); +VOID_ECL_WRITE_HEADER(gen_data); +VOID_FORWARD_LOAD_HEADER(gen_data); +VOID_INITIALIZE_HEADER(gen_data); +VOID_READ_FROM_BUFFER_HEADER(gen_data); +VOID_WRITE_TO_BUFFER_HEADER(gen_data); +VOID_SERIALIZE_HEADER(gen_data) +VOID_DESERIALIZE_HEADER(gen_data) +VOID_SET_INFLATION_HEADER(gen_data); +VOID_CLEAR_HEADER(gen_data); +VOID_IMUL_HEADER(gen_data); +VOID_IADD_HEADER(gen_data); +VOID_IADDSQR_HEADER(gen_data); +VOID_SCALE_HEADER(gen_data); +VOID_ISQRT_HEADER(gen_data); +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/gen_data_common.hpp b/libres/lib/include/ert/enkf/gen_data_common.hpp new file mode 100644 index 00000000000..242c1abcaf1 --- /dev/null +++ b/libres/lib/include/ert/enkf/gen_data_common.hpp @@ -0,0 +1,40 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'gen_data_common.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 ERT_GEN_DATA_COMMON_H +#define ERT_GEN_DATA_COMMON_H + +/* + Contains some headers which both gen_data.c and gen_data_config.c need - + split like this to avoid circular dependencies. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct gen_data_config_struct gen_data_config_type; +typedef struct gen_data_struct gen_data_type; + +gen_data_type * gen_data_alloc(const gen_data_config_type * ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libres/lib/include/ert/enkf/gen_data_config.hpp b/libres/lib/include/ert/enkf/gen_data_config.hpp new file mode 100644 index 00000000000..01031356999 --- /dev/null +++ b/libres/lib/include/ert/enkf/gen_data_config.hpp @@ -0,0 +1,92 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'gen_data_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 ERT_GEN_DATA_CONFIG_H +#define ERT_GEN_DATA_CONFIG_H +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { GEN_DATA_UNDEFINED = 0, + ASCII = 1, /* The file is ASCII file with a vector of numbers formatted with "%g". */ + ASCII_TEMPLATE = 2, /* The data is inserted into a user defined template file. */ + BINARY_DOUBLE = 3, /* The data is in a binary file with doubles. */ + BINARY_FLOAT = 4} /* The data is in a binary file with floats. */ + gen_data_file_format_type; + + bool gen_data_config_is_dynamic( const gen_data_config_type * config ); + void gen_data_config_load_active( gen_data_config_type * config , enkf_fs_type * fs, int report_step , bool force_load); + bool gen_data_config_valid_result_format(const char * result_file_fmt); + bool gen_data_config_set_template( gen_data_config_type * config , const char * template_ecl_file , const char * template_data_key ); + + bool gen_data_config_has_active_mask( const gen_data_config_type * config , enkf_fs_type * fs , int report_step); + + /* + Observe that the format ASCII_template can *NOT* be used for + loading files. + */ + gen_data_config_type * gen_data_config_alloc_GEN_PARAM( const char * key , gen_data_file_format_type output_format , gen_data_file_format_type input_format); + gen_data_config_type * gen_data_config_alloc_GEN_DATA_result( const char * key , gen_data_file_format_type input_format); + gen_data_config_type * gen_data_config_alloc_GEN_DATA_state( const char * key , gen_data_file_format_type output_format , gen_data_file_format_type input_format); + void gen_data_config_set_ens_size( gen_data_config_type * config , int ens_size ); + gen_data_file_format_type gen_data_config_get_input_format ( const gen_data_config_type * ); + gen_data_file_format_type gen_data_config_get_output_format ( const gen_data_config_type * ); + ecl_data_type gen_data_config_get_internal_data_type(const gen_data_config_type *); + void gen_data_config_free(gen_data_config_type * ); + PY_USED int gen_data_config_get_initial_size( const gen_data_config_type * config ); + void gen_data_config_assert_size(gen_data_config_type * , int , int); + const bool_vector_type * gen_data_config_get_active_mask( const gen_data_config_type * config ); + void gen_data_config_update_active(gen_data_config_type * config , const forward_load_context_type * load_context , const bool_vector_type * data_mask); + void gen_data_config_get_template_data( const gen_data_config_type * , char ** , int * , int * , int *); + const char * gen_data_config_get_key( const gen_data_config_type * config); + int gen_data_config_get_byte_size( const gen_data_config_type * config , int report_step); + int gen_data_config_get_data_size( const gen_data_config_type * config , int report_step); + gen_data_file_format_type gen_data_config_check_format( const char * format_string ); + + const int_vector_type * gen_data_config_get_active_report_steps( const gen_data_config_type *config); + int gen_data_config_iget_report_step( const gen_data_config_type *config , int index); + void gen_data_config_add_report_step( gen_data_config_type * config , int report_step); + bool gen_data_config_has_report_step( const gen_data_config_type * config , int report_step); + int gen_data_config_num_report_step( const gen_data_config_type * config ); + const char * gen_data_config_get_template_file( const gen_data_config_type * config ); + const char * gen_data_config_get_template_key( const gen_data_config_type * config ); + void gen_data_config_fprintf_config( const gen_data_config_type * config , enkf_var_type var_type , const char * outfile , const char * infile , + const char * min_std_file , FILE * stream); + int gen_data_config_get_data_size__( const gen_data_config_type * config , int report_step); + + UTIL_IS_INSTANCE_HEADER(gen_data_config); + UTIL_SAFE_CAST_HEADER(gen_data_config); + UTIL_SAFE_CAST_HEADER_CONST(gen_data_config); + VOID_FREE_HEADER(gen_data_config) + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/gen_kw.hpp b/libres/lib/include/ert/enkf/gen_kw.hpp new file mode 100644 index 00000000000..69602743384 --- /dev/null +++ b/libres/lib/include/ert/enkf/gen_kw.hpp @@ -0,0 +1,74 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'gen_kw.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 ERT_GEN_KW_H +#define ERT_GEN_KW_H +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include + +void gen_kw_ecl_write(const gen_kw_type * gen_kw , const char * run_path , const char * base_file , value_export_type * export_value); +PY_USED void gen_kw_write_export_file(const gen_kw_type * gen_kw , const char * filename); + +void gen_kw_free(gen_kw_type *); +int gen_kw_data_size( const gen_kw_type * ); +double gen_kw_data_iget( const gen_kw_type * , int , bool ); +void gen_kw_data_iset( gen_kw_type * , int , double ); +PY_USED void gen_kw_data_set_vector( gen_kw_type * gen_kw, const double_vector_type * values ); +double gen_kw_data_get( gen_kw_type * , const char * , bool ); +void gen_kw_data_set( gen_kw_type *, const char *, double ); +PY_USED bool gen_kw_data_has_key( gen_kw_type *, const char *); +const char * gen_kw_get_name(const gen_kw_type * , int ); +void gen_kw_filter_file(const gen_kw_type * , const char * ); +void gen_kw_ecl_write_template(const gen_kw_type * gen_kw , const char * file_name); + +UTIL_SAFE_CAST_HEADER(gen_kw); +UTIL_SAFE_CAST_HEADER_CONST(gen_kw); +VOID_ECL_WRITE_HEADER (gen_kw) +VOID_COPY_HEADER (gen_kw); +VOID_INITIALIZE_HEADER(gen_kw); +VOID_FREE_HEADER (gen_kw); +VOID_ALLOC_HEADER(gen_kw); +VOID_ECL_WRITE_HEADER(gen_kw); +VOID_USER_GET_HEADER(gen_kw); +VOID_WRITE_TO_BUFFER_HEADER(gen_kw); +VOID_READ_FROM_BUFFER_HEADER(gen_kw); +VOID_FLOAD_HEADER(gen_kw); +VOID_CLEAR_HEADER(gen_kw); +VOID_SERIALIZE_HEADER(gen_kw) +VOID_DESERIALIZE_HEADER(gen_kw) +VOID_IADD_HEADER(gen_kw); +VOID_IMUL_HEADER(gen_kw); +VOID_SCALE_HEADER(gen_kw); +VOID_IADDSQR_HEADER(gen_kw); +VOID_ISQRT_HEADER(gen_kw); +VOID_SET_INFLATION_HEADER(gen_kw); +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/gen_kw_common.hpp b/libres/lib/include/ert/enkf/gen_kw_common.hpp new file mode 100644 index 00000000000..e4bac693347 --- /dev/null +++ b/libres/lib/include/ert/enkf/gen_kw_common.hpp @@ -0,0 +1,42 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'gen_kw_common.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 ERT_GEN_KW_COMMON_H +#define ERT_GEN_KW_COMMON_H + +/* + Contains some headers which both gen_kw.c and gen_kw_config.c need - + split like this to avoid circular dependencies. +*/ +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct gen_kw_config_struct gen_kw_config_type; +typedef struct gen_kw_struct gen_kw_type; + +PY_USED gen_kw_type * gen_kw_alloc(const gen_kw_config_type * ); + + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/gen_kw_config.hpp b/libres/lib/include/ert/enkf/gen_kw_config.hpp new file mode 100644 index 00000000000..b565044153f --- /dev/null +++ b/libres/lib/include/ert/enkf/gen_kw_config.hpp @@ -0,0 +1,65 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'gen_kw_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 ERT_GEN_KW_CONFIG_H +#define ERT_GEN_KW_CONFIG_H +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include +#include + +#include +#include +#include + +const char * gen_kw_config_get_tag_fmt(const gen_kw_config_type * config); +bool gen_kw_config_is_valid( const gen_kw_config_type * config ); +void gen_kw_config_fprintf_config( const gen_kw_config_type * config , const char * outfile , const char * min_std_file , FILE * stream ); +void gen_kw_config_set_parameter_file( gen_kw_config_type * config , const char * parameter_file ); +PY_USED const char * gen_kw_config_get_parameter_file( const gen_kw_config_type * config ); +const char * gen_kw_config_get_key(const gen_kw_config_type * config ); +const char * gen_kw_config_get_template_file(const gen_kw_config_type * ); +void gen_kw_config_free(gen_kw_config_type *); +double gen_kw_config_transform(const gen_kw_config_type * , int index, double x); +bool gen_kw_config_should_use_log_scale(const gen_kw_config_type * config, int index); +int gen_kw_config_get_data_size(const gen_kw_config_type * ); +const char * gen_kw_config_iget_name(const gen_kw_config_type * , int ); +const char * gen_kw_config_get_tagged_name(const gen_kw_config_type * , int ); +stringlist_type * gen_kw_config_alloc_name_list( const gen_kw_config_type * config); +int gen_kw_config_get_index(const gen_kw_config_type * , const char * ); +void gen_kw_config_set_template_file( gen_kw_config_type * config , const char * template_file ); +gen_kw_config_type * gen_kw_config_alloc_empty( const char * key , const char * tag_fmt ); +void gen_kw_config_update( gen_kw_config_type * config , const char * template_file , const char * parameter_file); +void gen_kw_config_update_tag_format(gen_kw_config_type * config , const char * tag_format); +PY_USED const char * gen_kw_config_iget_function_type(const gen_kw_config_type * config, int index); +double_vector_type * gen_kw_config_iget_function_parameter_values( const gen_kw_config_type * config, int index ); +stringlist_type * gen_kw_config_iget_function_parameter_names( const gen_kw_config_type * config, int index ); + +UTIL_SAFE_CAST_HEADER_CONST( gen_kw_config ); +UTIL_SAFE_CAST_HEADER(gen_kw_config); +VOID_FREE_HEADER(gen_kw_config); +VOID_GET_DATA_SIZE_HEADER(gen_kw); +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/gen_obs.hpp b/libres/lib/include/ert/enkf/gen_obs.hpp new file mode 100644 index 00000000000..0b010c3ef96 --- /dev/null +++ b/libres/lib/include/ert/enkf/gen_obs.hpp @@ -0,0 +1,69 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'gen_obs.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 ERT_GEN_OBS_H +#define ERT_GEN_OBS_H + +#include +#include +#include +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + + +typedef struct gen_obs_struct gen_obs_type; + +gen_obs_type * gen_obs_alloc(const gen_data_config_type * config , const char * obs_key , const char * , double , double , const char * , const char * , const char * ); +gen_obs_type * gen_obs_alloc__(const gen_data_config_type * data_config , const char * obs_key); // for python bindings +void gen_obs_user_get_with_data_index(const gen_obs_type * gen_obs , const char * index_key , double * value , double * std , bool * valid); + +PY_USED void gen_obs_update_std_scale(gen_obs_type * gen_obs, double std_multiplier , const active_list_type * active_list); +int gen_obs_get_size(const gen_obs_type * gen_obs); +double gen_obs_iget_std(const gen_obs_type * gen_obs, int index); +void gen_obs_load_std(const gen_obs_type * gen_obs, int size, double * data); +double gen_obs_iget_value(const gen_obs_type * gen_obs, int index); +void gen_obs_load_values(const gen_obs_type * gen_obs, int size, double * data); +double gen_obs_iget_std_scaling(const gen_obs_type * gen_obs, int index); +PY_USED int gen_obs_get_obs_index(const gen_obs_type * gen_obs, int index); +void gen_obs_load_observation(gen_obs_type * gen_obs, const char * obs_file); +void gen_obs_set_scalar( gen_obs_type * gen_obs , double scalar_value , double scalar_std); +void gen_obs_attach_data_index( gen_obs_type * gen_obs , const int_vector_type * data_index ); +void gen_obs_load_data_index( gen_obs_type * obs , const char * data_index_file); +void gen_obs_parse_data_index( gen_obs_type * obs , const char * data_index_string); +void gen_obs_free(gen_obs_type * gen_obs); + + + +VOID_CHI2_HEADER(gen_obs); +UTIL_IS_INSTANCE_HEADER(gen_obs); +VOID_FREE_HEADER(gen_obs); +VOID_GET_OBS_HEADER(gen_obs); +VOID_MEASURE_HEADER(gen_obs); +VOID_USER_GET_OBS_HEADER(gen_obs); +VOID_UPDATE_STD_SCALE_HEADER(gen_obs); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/hook_manager.hpp b/libres/lib/include/ert/enkf/hook_manager.hpp new file mode 100644 index 00000000000..fdb3f68065d --- /dev/null +++ b/libres/lib/include/ert/enkf/hook_manager.hpp @@ -0,0 +1,61 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'hook_manager.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 ERT_HOOK_MANAGER_H +#define ERT_HOOK_MANAGER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include +#include +#include + + typedef struct hook_manager_struct hook_manager_type; + + hook_manager_type * hook_manager_alloc_default(ert_workflow_list_type * workflow_list); + hook_manager_type * hook_manager_alloc(ert_workflow_list_type *, const config_content_type *); + + PY_USED hook_manager_type * hook_manager_alloc_full( + ert_workflow_list_type * workflow_list, + const char * runpath_list_file, + const char ** hook_workflow_names, + const char ** hook_workflow_run_modes, + int hook_workflow_count + ); + + void hook_manager_free(hook_manager_type * hook_manager); + + void hook_manager_init(hook_manager_type * hook_manager, const config_content_type * config); + void hook_manager_add_config_items( config_parser_type * config ); + + runpath_list_type * hook_manager_get_runpath_list(const hook_manager_type * hook_manager); + const char * hook_manager_get_runpath_list_file(const hook_manager_type * hook_manager); + void hook_manager_run_workflows( const hook_manager_type * hook_manager , hook_run_mode_enum run_mode , void * self); + + PY_USED const hook_workflow_type * hook_manager_iget_hook_workflow(const hook_manager_type * hook_manager, int index); + int hook_manager_get_size(const hook_manager_type * hook_manager); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/hook_workflow.hpp b/libres/lib/include/ert/enkf/hook_workflow.hpp new file mode 100644 index 00000000000..9462b88dfe0 --- /dev/null +++ b/libres/lib/include/ert/enkf/hook_workflow.hpp @@ -0,0 +1,55 @@ +/* + Copyright (C) 2015 Equinor ASA, Norway. + + The file 'hook_workflow.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 ERT_HOOK_WORKFLOW_H +#define ERT_HOOK_WORKFLOW_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +#include + + + typedef enum {PRE_SIMULATION = 0, + POST_SIMULATION = 1, + PRE_UPDATE = 2, + POST_UPDATE = 3, + PRE_FIRST_UPDATE = 4} hook_run_mode_enum; + + typedef struct hook_workflow_struct hook_workflow_type; + + + hook_workflow_type * hook_workflow_alloc( workflow_type * workflow , hook_run_mode_enum run_mode); + void hook_workflow_free(hook_workflow_type * hook_workflow); + void hook_workflow_free__( void * arg ); + + + workflow_type * hook_workflow_get_workflow( const hook_workflow_type * hook_workflow ); + bool hook_workflow_run_workflow(const hook_workflow_type * hook_workflow, ert_workflow_list_type * workflow_list, void * self); + hook_run_mode_enum hook_workflow_get_run_mode( const hook_workflow_type * hook_workflow ); + + hook_run_mode_enum hook_workflow_run_mode_from_name( const char * run_mode ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/local_config.hpp b/libres/lib/include/ert/enkf/local_config.hpp new file mode 100644 index 00000000000..a9f471325a9 --- /dev/null +++ b/libres/lib/include/ert/enkf/local_config.hpp @@ -0,0 +1,59 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'local_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 ERT_LOCAL_CONFIG_H +#define ERT_LOCAL_CONFIG_H + +#include + +#include + +#include + +#include +#include +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct local_config_struct local_config_type; + PY_USED local_dataset_type * local_config_alloc_dataset_copy( local_config_type * local_config , const char * src_key , const char * target_key); + local_obsdata_type * local_config_get_obsdata( const local_config_type * local_config , const char * key); + local_dataset_type * local_config_get_dataset( const local_config_type * local_config , const char * key); + PY_USED local_obsdata_type * local_config_alloc_obsdata_copy( local_config_type * local_config , const char * src_key , const char * target_key); + +local_config_type * local_config_alloc( ); +void local_config_clear( local_config_type * local_config ); +void local_config_clear_active( local_config_type * local_config ); +void local_config_free( local_config_type * local_config ); +local_ministep_type * local_config_alloc_ministep( local_config_type * local_config , const char * key, analysis_module_type* analysis_module ); +local_updatestep_type * local_config_get_updatestep( const local_config_type * local_config ); +local_ministep_type * local_config_get_ministep( const local_config_type * local_config , const char * key); +PY_USED void local_config_summary_fprintf( const local_config_type * local_config , const char * config_file); +local_obsdata_type * local_config_alloc_obsdata( local_config_type * local_config , const char * obsdata_name ); +bool local_config_has_obsdata( const local_config_type * local_config , const char * obsdata_name); +local_dataset_type * local_config_alloc_dataset( local_config_type * local_config , const char * key ); +bool local_config_has_dataset( const local_config_type * local_config , const char * key); +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/local_dataset.hpp b/libres/lib/include/ert/enkf/local_dataset.hpp new file mode 100644 index 00000000000..c1cf5e55636 --- /dev/null +++ b/libres/lib/include/ert/enkf/local_dataset.hpp @@ -0,0 +1,64 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'local_dataset.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 ERT_LOCAL_DATASET_H +#define ERT_LOCAL_DATASET_H +#include +#include + +#include +#include +#include + +#include +#include + +typedef struct local_dataset_struct local_dataset_type; + +#ifdef __cplusplus +extern "C" { +#endif + +local_dataset_type * local_dataset_alloc_copy( local_dataset_type * src_dataset , const char * copy_name ); +local_dataset_type * local_dataset_alloc( const char * name ); +void local_dataset_free( local_dataset_type * dataset ); +void local_dataset_free__( void * arg ); +void local_dataset_add_node(local_dataset_type * dataset, const char *node_key); +void local_dataset_del_node( local_dataset_type * dataset , const char * node_key); +PY_USED void local_dataset_clear( local_dataset_type * dataset); +const char * local_dataset_get_name( const local_dataset_type * dataset); +void local_dataset_summary_fprintf( const local_dataset_type * dataset , FILE * stream); +active_list_type * local_dataset_get_node_active_list(const local_dataset_type * dataset , const char * node_key ); +stringlist_type * local_dataset_alloc_keys( const local_dataset_type * dataset ); +int local_dataset_get_size( const local_dataset_type * dataset ); +void local_dataset_del_node( local_dataset_type * dataset , const char * node_key); +PY_USED bool local_dataset_has_key(const local_dataset_type * dataset, const char * key); +hash_iter_type * local_dataset_alloc_iter(const local_dataset_type * dataset); +const row_scaling_type * local_dataset_get_row_scaling(const local_dataset_type * dataset, const char * key); +row_scaling_type * local_dataset_get_or_create_row_scaling(local_dataset_type * dataset, const char * key); +bool local_dataset_has_row_scaling(const local_dataset_type * dataset, const char * key); + +#ifdef __cplusplus +} +#endif + +std::vector local_dataset_scaled_keys(const local_dataset_type * dataset); +std::vector local_dataset_unscaled_keys(const local_dataset_type * dataset); + +#endif diff --git a/libres/lib/include/ert/enkf/local_ministep.hpp b/libres/lib/include/ert/enkf/local_ministep.hpp new file mode 100644 index 00000000000..98b6a0d0374 --- /dev/null +++ b/libres/lib/include/ert/enkf/local_ministep.hpp @@ -0,0 +1,64 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'local_ministep.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 ERT_LOCAL_MINISTEP_H +#define ERT_LOCAL_MINISTEP_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + +#include +#include +#include +#include + +typedef struct local_ministep_struct local_ministep_type; + +local_ministep_type * local_ministep_alloc(const char * name, analysis_module_type* analysis_module); +void local_ministep_free(local_ministep_type * ministep); +void local_ministep_free__(void * arg); +hash_iter_type * local_ministep_alloc_dataset_iter( const local_ministep_type * ministep ); +const char * local_ministep_get_name( const local_ministep_type * ministep ); +void local_ministep_summary_fprintf( const local_ministep_type * ministep , FILE * stream); +void local_ministep_add_dataset( local_ministep_type * ministep , const local_dataset_type * dataset); +void local_ministep_add_obsdata( local_ministep_type * ministep , local_obsdata_type * obsdata); +void local_ministep_add_obsdata_node( local_ministep_type * ministep , local_obsdata_node_type * obsdatanode); +local_obsdata_type * local_ministep_get_obsdata(const local_ministep_type * ministep); +local_dataset_type * local_ministep_get_dataset( const local_ministep_type * ministep, const char * dataset_name); +bool local_ministep_has_dataset( const local_ministep_type * ministep, const char * dataset_name); +int local_ministep_get_num_dataset( const local_ministep_type * ministep ); +bool local_ministep_has_analysis_module( const local_ministep_type * ministep ); +analysis_module_type* local_ministep_get_analysis_module( const local_ministep_type * ministep ); +void local_ministep_add_obs_data( local_ministep_type * ministep , obs_data_type * obs_data ); +obs_data_type * local_ministep_get_obs_data( const local_ministep_type * ministep ); + +UTIL_SAFE_CAST_HEADER(local_ministep); +UTIL_IS_INSTANCE_HEADER(local_ministep); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/local_obsdata.hpp b/libres/lib/include/ert/enkf/local_obsdata.hpp new file mode 100644 index 00000000000..56caf09c383 --- /dev/null +++ b/libres/lib/include/ert/enkf/local_obsdata.hpp @@ -0,0 +1,54 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'local_obsdata.h' + + 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 ERT_LOCAL_OBSDATA_H +#define ERT_LOCAL_OBSDATA_H + +#ifdef __cplusplus +extern "C" { +#endif +#include + +#include + +#include + +typedef struct local_obsdata_struct local_obsdata_type; + + void local_obsdata_free__( void * arg); + bool local_obsdata_has_node( const local_obsdata_type * data , const char * key); + local_obsdata_type * local_obsdata_alloc_copy( const local_obsdata_type * src, const char * target_key); + local_obsdata_type * local_obsdata_alloc( const char * name ); + void local_obsdata_free( local_obsdata_type * data ); + int local_obsdata_get_size( const local_obsdata_type * data ); + bool local_obsdata_add_node( local_obsdata_type * data , local_obsdata_node_type * node ); + local_obsdata_node_type * local_obsdata_iget( const local_obsdata_type * data , int index); + local_obsdata_type * local_obsdata_alloc_wrapper( local_obsdata_node_type * node ); + const char * local_obsdata_get_name( const local_obsdata_type * data); + local_obsdata_node_type * local_obsdata_get( const local_obsdata_type * data , const char * key); + void local_obsdata_del_node( local_obsdata_type * data , const char * key); + void local_obsdata_reset_tstep_list( local_obsdata_type * data , const int_vector_type * step_list); + active_list_type * local_obsdata_get_node_active_list(const local_obsdata_type * obsdata , const char * obs_key ); + void local_obsdata_summary_fprintf( const local_obsdata_type * obsdata , FILE * stream); + PY_USED active_list_type * local_obsdata_get_copy_node_active_list(const local_obsdata_type * obsdata , const char * obs_key); + +UTIL_IS_INSTANCE_HEADER( local_obsdata ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/local_obsdata_node.hpp b/libres/lib/include/ert/enkf/local_obsdata_node.hpp new file mode 100644 index 00000000000..f25c0c1641a --- /dev/null +++ b/libres/lib/include/ert/enkf/local_obsdata_node.hpp @@ -0,0 +1,56 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'local_obsdata_node.h' + + 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 ERT_LOCAL_OBSDATA_NODE_H +#define ERT_LOCAL_OBSDATA_NODE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + + + typedef struct local_obsdata_node_struct local_obsdata_node_type; + + local_obsdata_node_type * local_obsdata_node_alloc( const char * obs_key , bool all_timestep_active ); + local_obsdata_node_type * local_obsdata_node_alloc_copy( const local_obsdata_node_type * src); + const char * local_obsdata_node_get_key( const local_obsdata_node_type * node ); + void local_obsdata_node_free( local_obsdata_node_type * node ); + void local_obsdata_node_free__( void * arg ); + active_list_type * local_obsdata_node_get_active_list( const local_obsdata_node_type * node ); + active_list_type * local_obsdata_node_get_copy_active_list( const local_obsdata_node_type * node ); + void local_obsdata_node_copy_active_list( local_obsdata_node_type * node , const active_list_type * active_list); + void local_obsdata_node_add_tstep( local_obsdata_node_type * node, int tstep); + void local_obsdata_node_add_range( local_obsdata_node_type * node, int step1, int step2); + + bool local_obsdata_node_tstep_active( const local_obsdata_node_type * node , int tstep ); + bool local_obsdata_node_all_timestep_active( const local_obsdata_node_type * node); + bool local_obsdata_node_has_tstep( const local_obsdata_node_type * node , int tstep); + void local_obsdata_node_reset_tstep_list( local_obsdata_node_type * node , const int_vector_type * step_list); + void local_obsdata_node_set_all_timestep_active( local_obsdata_node_type * node, bool flag); + + UTIL_IS_INSTANCE_HEADER( local_obsdata_node ); + UTIL_SAFE_CAST_HEADER( local_obsdata_node ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/local_updatestep.hpp b/libres/lib/include/ert/enkf/local_updatestep.hpp new file mode 100644 index 00000000000..fdd4bff691a --- /dev/null +++ b/libres/lib/include/ert/enkf/local_updatestep.hpp @@ -0,0 +1,43 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'local_updatestep.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 ERT_LOCAL_UPDATESTEP_H +#define ERT_LOCAL_UPDATESTEP_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct local_updatestep_struct local_updatestep_type; + +local_updatestep_type * local_updatestep_alloc( const char * name ); +void local_updatestep_free__(void * arg); +void local_updatestep_free( local_updatestep_type * updatestep); +void local_updatestep_add_ministep( local_updatestep_type * updatestep , local_ministep_type * ministep); +local_ministep_type * local_updatestep_iget_ministep( const local_updatestep_type * updatestep , int index); +int local_updatestep_get_num_ministep( const local_updatestep_type * updatestep ); +const char * local_updatestep_get_name( const local_updatestep_type * updatestep ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/log_config.hpp b/libres/lib/include/ert/enkf/log_config.hpp new file mode 100644 index 00000000000..9ef10e73235 --- /dev/null +++ b/libres/lib/include/ert/enkf/log_config.hpp @@ -0,0 +1,52 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'log_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 ERT_LOG_CONFIG_H +#define ERT_LOG_CONFIG_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#define LOG_CRITICAL_NAME "CRITICAL" +#define LOG_ERROR_NAME "ERROR" +#define LOG_WARNING_NAME "WARNING" +#define LOG_INFO_NAME "INFO" +#define LOG_DEBUG_NAME "DEBUG" + + +typedef struct log_config_struct log_config_type; + +log_config_type * log_config_alloc_load(const char *); +log_config_type * log_config_alloc(const config_content_type *); +PY_USED log_config_type * log_config_alloc_full(const char * log_file, message_level_type message_level); +void log_config_free(log_config_type *); + +const char * log_config_get_log_file(const log_config_type *); +const message_level_type log_config_get_log_level(const log_config_type *); + +message_level_type log_config_level_parser(const char * level); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/meas_data.hpp b/libres/lib/include/ert/enkf/meas_data.hpp new file mode 100644 index 00000000000..9f41d0afd2d --- /dev/null +++ b/libres/lib/include/ert/enkf/meas_data.hpp @@ -0,0 +1,73 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'meas_data.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 ERT_MEAS_DATA_H +#define ERT_MEAS_DATA_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include +#include + +#include +#include + +typedef struct meas_data_struct meas_data_type; +typedef struct meas_block_struct meas_block_type; + +UTIL_IS_INSTANCE_HEADER( meas_data ); +UTIL_SAFE_CAST_HEADER( meas_block ); +meas_block_type * meas_block_alloc( const char * obs_key , const bool_vector_type * ens_mask , int obs_size); +int meas_block_get_total_ens_size( const meas_block_type * meas_block ); +PY_USED int meas_block_get_active_ens_size( const meas_block_type * meas_block ); +bool meas_block_iens_active( const meas_block_type * meas_block , int iens); +void meas_block_iset( meas_block_type * meas_block , int iens , int iobs , double value); +double meas_block_iget( const meas_block_type * meas_block , int iens , int iobs); +double meas_block_iget_ens_mean( meas_block_type * meas_block , int iobs ); +double meas_block_iget_ens_std( meas_block_type * meas_block , int iobs); +void meas_block_deactivate( meas_block_type * meas_block , int iobs ); +bool meas_block_iget_active( const meas_block_type * meas_block , int iobs); +void meas_block_free( meas_block_type * meas_block ); + +bool meas_data_has_block( const meas_data_type * matrix , const char * lookup_key); + +meas_block_type * meas_data_get_block( const meas_data_type * matrix , const char * lookup_key); +void meas_data_reset(meas_data_type * ); +meas_data_type * meas_data_alloc( const bool_vector_type * ens_mask); +void meas_data_free(meas_data_type * ); +matrix_type * meas_data_allocS(const meas_data_type * matrix); +int meas_data_get_active_obs_size( const meas_data_type * matrix ); +int meas_data_get_total_ens_size( const meas_data_type * matrix ); +int meas_data_get_active_ens_size( const meas_data_type * meas_data ); +meas_block_type * meas_data_add_block( meas_data_type * matrix , const char * obs_key , int report_step , int obs_size); +int meas_data_get_num_blocks( const meas_data_type * meas_block ); +meas_block_type * meas_data_iget_block( const meas_data_type * matrix , int block_mnr); +const meas_block_type * meas_data_iget_block_const( const meas_data_type * matrix , int block_nr ); +int meas_block_get_total_obs_size( const meas_block_type * meas_block ); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libres/lib/include/ert/enkf/misfit_ensemble.hpp b/libres/lib/include/ert/enkf/misfit_ensemble.hpp new file mode 100644 index 00000000000..218be087832 --- /dev/null +++ b/libres/lib/include/ert/enkf/misfit_ensemble.hpp @@ -0,0 +1,64 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'misfit_table.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 ERT_MISFIT_ENSEMBLE_H +#define ERT_MISFIT_ENSEMBLE_H + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +#include + + + + void misfit_ensemble_fread( misfit_ensemble_type * misfit_ensemble , FILE * stream ); + void misfit_ensemble_clear( misfit_ensemble_type * table); + misfit_ensemble_type * misfit_ensemble_alloc( ); + void misfit_ensemble_free( misfit_ensemble_type * table ); + void misfit_ensemble_fwrite( const misfit_ensemble_type * misfit_ensemble , FILE * stream); + bool misfit_ensemble_initialized( const misfit_ensemble_type * misfit_ensemble ); + + void misfit_ensemble_initialize( misfit_ensemble_type * misfit_ensemble , + const ensemble_config_type * ensemble_config , + const enkf_obs_type * enkf_obs , + enkf_fs_type * fs , + int ens_size , + int history_length, + bool force_init); + + void misfit_ensemble_set_ens_size( misfit_ensemble_type * misfit_ensemble , int ens_size); + int misfit_ensemble_get_ens_size( const misfit_ensemble_type * misfit_ensemble ); + + misfit_member_type * misfit_ensemble_iget_member( const misfit_ensemble_type * table , int iens); + + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libres/lib/include/ert/enkf/misfit_ensemble_typedef.hpp b/libres/lib/include/ert/enkf/misfit_ensemble_typedef.hpp new file mode 100644 index 00000000000..2c5adff7bf9 --- /dev/null +++ b/libres/lib/include/ert/enkf/misfit_ensemble_typedef.hpp @@ -0,0 +1,6 @@ +#ifndef ERT_MISFIT_ENSEMBLE_TYPEDEF_H +#define ERT_MISFIT_ENSEMBLE_TYPEDEF_H + +typedef struct misfit_ensemble_struct misfit_ensemble_type; + +#endif diff --git a/libres/lib/include/ert/enkf/misfit_member.hpp b/libres/lib/include/ert/enkf/misfit_member.hpp new file mode 100644 index 00000000000..b153f00c162 --- /dev/null +++ b/libres/lib/include/ert/enkf/misfit_member.hpp @@ -0,0 +1,44 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'misfit_member.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 ERT_MISFIT_MEMBER_H +#define ERT_MISFIT_MEMBER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + + typedef struct misfit_member_struct misfit_member_type; + misfit_ts_type * misfit_member_get_ts( const misfit_member_type * member , const char * obs_key ); + bool misfit_member_has_ts( const misfit_member_type * member , const char * obs_key ); + misfit_member_type * misfit_member_fread_alloc( FILE * stream ); + void misfit_member_fwrite( const misfit_member_type * node , FILE * stream ); + void misfit_member_update( misfit_member_type * node , const char * obs_key , int history_length , int iens , const double ** work_chi2); + void misfit_member_free__( void * node ); + misfit_member_type * misfit_member_alloc(int iens); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libres/lib/include/ert/enkf/misfit_ranking.hpp b/libres/lib/include/ert/enkf/misfit_ranking.hpp new file mode 100644 index 00000000000..d4ae1c91b59 --- /dev/null +++ b/libres/lib/include/ert/enkf/misfit_ranking.hpp @@ -0,0 +1,53 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'misfit_ranking.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 ERT_MISFIT_RANKING_H +#define ERT_MISFIT_RANKING_H + +#include +#include +#include + +#include +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct misfit_ranking_struct misfit_ranking_type; + + UTIL_IS_INSTANCE_HEADER( misfit_ranking ); + UTIL_SAFE_CAST_HEADER( misfit_ranking ); + + void misfit_ranking_display( const misfit_ranking_type * misfit_ranking , FILE * stream); + misfit_ranking_type * misfit_ranking_alloc(const misfit_ensemble_type * ensemble , const stringlist_type * sort_keys , const int_vector_type * steps, const char * ranking_key); + void misfit_ranking_free( misfit_ranking_type * misfit_ranking ); + void misfit_ranking_free__( void * arg ); + const perm_vector_type * misfit_ranking_get_permutation( const misfit_ranking_type * misfit_ranking ); + void misfit_ranking_iset_invalid( misfit_ranking_type * misfit_ranking , int iens ); + void misfit_ranking_iset( misfit_ranking_type * misfit_ranking , int iens , hash_type * obs_hash , double total_misfit); + void misfit_ranking_init_sort( misfit_ranking_type * misfit_ranking ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libres/lib/include/ert/enkf/misfit_ts.hpp b/libres/lib/include/ert/enkf/misfit_ts.hpp new file mode 100644 index 00000000000..f517c339cac --- /dev/null +++ b/libres/lib/include/ert/enkf/misfit_ts.hpp @@ -0,0 +1,43 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'misfit_ts.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 ERT_MISFIT_TS_H +#define ERT_MISFIT_TS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + + typedef struct misfit_ts_struct misfit_ts_type; + + + void misfit_ts_fwrite( const misfit_ts_type * misfit_ts , FILE * stream ); + double misfit_ts_eval( const misfit_ts_type * ts , const int_vector_type * steps ); + misfit_ts_type * misfit_ts_alloc(int history_length); + misfit_ts_type * misfit_ts_fread_alloc( FILE * stream ); + void misfit_ts_free__( void * vector ); + void misfit_ts_iset( misfit_ts_type * vector , int time_index , double value ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libres/lib/include/ert/enkf/model_config.hpp b/libres/lib/include/ert/enkf/model_config.hpp new file mode 100644 index 00000000000..b113c61a6cb --- /dev/null +++ b/libres/lib/include/ert/enkf/model_config.hpp @@ -0,0 +1,112 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'model_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 ERT_MODEL_CONFIG_H +#define ERT_MODEL_CONFIG_H + + +#include +#include + +#include + +#include +#include + +#include +#include + +#include + +#include + +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + + typedef struct model_config_struct model_config_type; + const char * model_config_get_data_root( const model_config_type * model_config ); + void model_config_set_data_root( model_config_type * model_config , const char * data_root); + bool model_config_data_root_is_set( const model_config_type * model_config ); + + char * model_config_alloc_jobname( const model_config_type * model_config , int iens); + const char * model_config_get_jobname_fmt( const model_config_type * model_config ); + void model_config_set_jobname_fmt( model_config_type * model_config , const char * jobname_fmt); + void model_config_set_enspath( model_config_type * model_config , const char * enspath); + void model_config_set_rftpath( model_config_type * model_config , const char * rftpath); + const char * model_config_get_enspath( const model_config_type * model_config); + const ecl_sum_type * model_config_get_refcase( const model_config_type * model_config ); + void model_config_init_internalization( model_config_type * ); + void model_config_set_internalize_state( model_config_type * , int ); + bool model_config_has_prediction(const model_config_type * ); + PY_USED bool model_config_has_history(const model_config_type * config); + int model_config_get_last_history_restart(const model_config_type * ); + time_map_type * model_config_get_external_time_map( const model_config_type * config); + int model_config_get_num_realizations(const model_config_type * model_config); + const char * model_config_get_obs_config_file(const model_config_type * model_config); + void model_config_init(model_config_type * model_config , const config_content_type * , const char * data_root, int ens_size , const ext_joblist_type * , int , const ecl_sum_type * refcase); + void model_config_free(model_config_type *); + bool model_config_runpath_requires_iter( const model_config_type * model_config ); + path_fmt_type * model_config_get_runpath_fmt(const model_config_type * ); + history_type * model_config_get_history(const model_config_type * ); + forward_model_type * model_config_get_forward_model( const model_config_type * ); + void model_config_set_max_internal_submit(model_config_type * config, int max_resample); + PY_USED int model_config_get_max_internal_submit( const model_config_type * config ); + bool model_config_select_runpath( model_config_type * model_config , const char * path_key); + void model_config_add_runpath( model_config_type * model_config , const char * path_key , const char * fmt ); + const char * model_config_get_runpath_as_char( const model_config_type * model_config ); + PY_USED history_source_type model_config_get_history_source( const model_config_type * model_config ); + void model_config_set_refcase( model_config_type * model_config , const ecl_sum_type * refcase ); + model_config_type * model_config_alloc_empty(); + model_config_type * model_config_alloc(const config_content_type*, const char * data_root, const ext_joblist_type *, int, const ecl_sum_type*); + model_config_type * model_config_alloc_full(int max_resample, + int num_realizations, + char * run_path, + char * data_root, + char * enspath, + char * job_name, + forward_model_type * forward_model, + char * obs_config, + time_map_type * time_map, + char * rftpath, + char * gen_kw_export_name, + history_source_type history_source, + const ext_joblist_type * joblist, + const ecl_sum_type * refcase); + bool model_config_select_history( model_config_type * model_config , history_source_type source_type , const ecl_sum_type * refcase); + void model_config_set_runpath(model_config_type * model_config , const char * fmt); + void model_config_set_gen_kw_export_name( model_config_type * model_config, const char * name); + const char * model_config_get_gen_kw_export_name( const model_config_type * model_config); + + config_content_type * model_config_alloc_content(const char*, config_parser_type*); + void model_config_init_config_parser(config_parser_type * config_parser); + bool model_config_report_step_compatible(const model_config_type * model_config, const ecl_sum_type * ecl_sum_simulated); + + UTIL_IS_INSTANCE_HEADER( model_config); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/obs_data.hpp b/libres/lib/include/ert/enkf/obs_data.hpp new file mode 100644 index 00000000000..2075a62c9aa --- /dev/null +++ b/libres/lib/include/ert/enkf/obs_data.hpp @@ -0,0 +1,78 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'obs_data.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 ERT_OBS_DATA_H +#define ERT_OBS_DATA_H +#ifdef __cplusplus +extern "C" { +#endif +#include +#include + +#include +#include + +#include +#include +#include + +typedef struct obs_data_struct obs_data_type; +typedef struct obs_block_struct obs_block_type; +void obs_block_free( obs_block_type * obs_block ); +active_type obs_block_iget_active_mode( const obs_block_type * obs_block , int iobs); +obs_block_type * obs_block_alloc( const char * obs_key , int obs_size , matrix_type * error_covar , bool error_covar_owner, double global_std_scaling); +int obs_block_get_active_size( const obs_block_type * obs_block ); + +void obs_block_deactivate( obs_block_type * obs_block , int iobs , bool verbose , const char * msg); +int obs_block_get_size( const obs_block_type * obs_block ); +void obs_block_iset( obs_block_type * obs_block , int iobs , double value , double std); +void obs_block_iset_missing( obs_block_type * obs_block , int iobs ); + +double obs_block_iget_std( const obs_block_type * obs_block , int iobs); +double obs_block_iget_value( const obs_block_type * obs_block , int iobs); + +double obs_data_iget_value( const obs_data_type * obs_data , int index ); +PY_USED double obs_data_iget_std( const obs_data_type * obs_data, int i_ndex ); +obs_block_type * obs_data_iget_block( obs_data_type * obs_data , int index ); +const obs_block_type * obs_data_iget_block_const( const obs_data_type * obs_data , int block_nr); +obs_block_type * obs_data_add_block( obs_data_type * obs_data , const char * obs_key , int obs_size , matrix_type * error_covar , bool error_covar_owner); +PY_USED void obs_data_scale_matrix(const obs_data_type * obs_data , matrix_type * matrix); +PY_USED void obs_data_scale_Rmatrix(const obs_data_type * obs_data , matrix_type * matrix); + +obs_data_type * obs_data_alloc(double global_std_scaling); +void obs_data_free(obs_data_type *); +void obs_data_reset(obs_data_type * obs_data); +matrix_type * obs_data_allocD(const obs_data_type * obs_data , const matrix_type * E , const matrix_type * S); +matrix_type * obs_data_allocR(const obs_data_type * obs_data ); +matrix_type * obs_data_allocdObs(const obs_data_type * obs_data ); +matrix_type * obs_data_allocE(const obs_data_type * obs_data , rng_type * rng , int active_ens_size); + void obs_data_scale(const obs_data_type * obs_data , matrix_type *S , matrix_type *E , matrix_type *D , matrix_type *R , matrix_type * O); +int obs_data_get_active_size(const obs_data_type * obs_data ); +int obs_data_get_total_size( const obs_data_type * obs_data ); +int obs_data_get_num_blocks( const obs_data_type * obs_data ); +const char * obs_block_get_key( const obs_block_type * obs_block) ; +double obs_data_iget_value( const obs_data_type * obs_data , int total_index ); +PY_USED double obs_data_iget_std( const obs_data_type * obs_data , int total_index ); +PY_USED bool obs_block_iget_is_active( const obs_block_type * obs_block , int iobs ); + +const bool_vector_type * obs_data_get_active_mask( const obs_data_type * obs_data ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/obs_vector.hpp b/libres/lib/include/ert/enkf/obs_vector.hpp new file mode 100644 index 00000000000..1b2d02131fd --- /dev/null +++ b/libres/lib/include/ert/enkf/obs_vector.hpp @@ -0,0 +1,106 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'obs_vector.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 ERT_OBS_VECTOR_H +#define ERT_OBS_VECTOR_H + + +#include + +#include +#include + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + typedef void (obs_free_ftype) (void *); + typedef void (obs_get_ftype) (const void * , obs_data_type * , enkf_fs_type *, int , const active_list_type * ); + typedef void (obs_meas_ftype) (const void * , const void *, node_id_type , meas_data_type * , const active_list_type * ); + typedef void (obs_user_get_ftype) (void * , const char * , double * , double * , bool *); + typedef void (obs_update_std_scale_ftype) (void * , double , const active_list_type * ); + typedef double (obs_chi2_ftype) (const void * , const void *, node_id_type ); + + typedef enum { GEN_OBS = 1, + SUMMARY_OBS = 2, + BLOCK_OBS = 3} obs_impl_type; + + typedef struct obs_vector_struct obs_vector_type; + + + void obs_vector_free(obs_vector_type * ); + int obs_vector_get_num_active(const obs_vector_type * ); + bool obs_vector_iget_active(const obs_vector_type * , int ); + void obs_vector_iget_observations(const obs_vector_type * , int , obs_data_type * , const active_list_type * active_list, enkf_fs_type * fs); + bool obs_vector_has_data( const obs_vector_type * obs_vector , const bool_vector_type * active_mask , enkf_fs_type * fs); + void obs_vector_measure(const obs_vector_type * , enkf_fs_type * fs, int report_step , const int_vector_type * ens_active_list , meas_data_type * , const active_list_type * active_list); + const char * obs_vector_get_state_kw(const obs_vector_type * ); + const char * obs_vector_get_key(const obs_vector_type * ); + obs_impl_type obs_vector_get_impl_type(const obs_vector_type * ); + const int_vector_type * obs_vector_get_step_list(const obs_vector_type * vector); + void obs_vector_user_get(const obs_vector_type * obs_vector , const char * index_key , int report_step , double * value , double * std , bool * valid); + int obs_vector_get_next_active_step(const obs_vector_type * , int ); + void * obs_vector_iget_node(const obs_vector_type * , int ); + obs_vector_type * obs_vector_alloc_from_GENERAL_OBSERVATION(const conf_instance_type * , time_map_type * obs_time , const ensemble_config_type * ); + void obs_vector_load_from_SUMMARY_OBSERVATION(obs_vector_type * obs_vector , const conf_instance_type * , time_map_type * obs_time , ensemble_config_type * ); + bool obs_vector_load_from_HISTORY_OBSERVATION(obs_vector_type * obs_vector , const conf_instance_type * , time_map_type * obs_time , const history_type * , ensemble_config_type * , double std_cutoff ); + obs_vector_type * obs_vector_alloc_from_BLOCK_OBSERVATION(const conf_instance_type * , const ecl_grid_type * grid , time_map_type * obs_time , const ecl_sum_type * refcase , ensemble_config_type * ); + obs_vector_type * obs_vector_alloc(obs_impl_type obs_type , const char * obs_key , enkf_config_node_type * config_node, int num_reports); + void obs_vector_scale_std(obs_vector_type * obs_vector, const local_obsdata_node_type * local_node , double std_multiplier); + void obs_vector_install_node(obs_vector_type * obs_vector , int obs_index , void * node ); + + void obs_vector_ensemble_chi2(const obs_vector_type * obs_vector , + enkf_fs_type * fs, + bool_vector_type * valid , + int step1 , int step2 , + int iens1 , int iens2 , + double ** chi2); + + double obs_vector_total_chi2(const obs_vector_type * , enkf_fs_type * , int ); + enkf_config_node_type * obs_vector_get_config_node(const obs_vector_type * ); + const char * obs_vector_get_obs_key( const obs_vector_type * obs_vector); + local_obsdata_node_type * obs_vector_alloc_local_node(const obs_vector_type * obs_vector); + + + + UTIL_IS_INSTANCE_HEADER(obs_vector); + UTIL_SAFE_CAST_HEADER(obs_vector); + VOID_FREE_HEADER(obs_vector); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/queue_config.hpp b/libres/lib/include/ert/enkf/queue_config.hpp new file mode 100644 index 00000000000..38a163ad14c --- /dev/null +++ b/libres/lib/include/ert/enkf/queue_config.hpp @@ -0,0 +1,92 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'queue_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 ERT_QUEUE_CONFIG_H +#define ERT_QUEUE_CONFIG_H +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +#include + +#define LSF_DRIVER_NAME "LSF" +#define LOCAL_DRIVER_NAME "LOCAL" +#define RSH_DRIVER_NAME "RSH" +#define TORQUE_DRIVER_NAME "TORQUE" +#define SLURM_DRIVER_NAME "SLURM" + + +typedef struct queue_config_struct queue_config_type; + + queue_config_type * queue_config_alloc_load(const char * user_config_file); + queue_config_type * queue_config_alloc(const config_content_type * config_content); + PY_USED queue_config_type * queue_config_alloc_full(char * job_script, + bool user_mode, + int max_submit, + int num_cpu, + job_driver_type driver_type); + queue_config_type * queue_config_alloc_local_copy( queue_config_type * queue_config); + void queue_config_free(queue_config_type * queue_config); + + int queue_config_get_max_submit(queue_config_type * queue_config); + bool queue_config_has_job_script( const queue_config_type * queue_config ); + bool queue_config_set_job_script(queue_config_type * queue_config, const char * job_script); + const char * queue_config_get_job_script(const queue_config_type * queue_config); + + job_driver_type queue_config_get_driver_type(const queue_config_type * queue_config); + + queue_driver_type * queue_config_get_queue_driver(const queue_config_type * queue_config, + const char * driver_name); + bool queue_config_has_queue_driver(const queue_config_type * queue_config, + const char * driver_name); + void queue_config_create_queue_drivers(queue_config_type * queue_config); + + /** + * Queue system is typically one of LSF, LOCAL, TORQUE, RHS, ... Given a + * queue system, you can obtain the _driver_ (e.g. lsf_driver). + * + * Should not be confused with queue_name, which is typically just a + * parameter we can send to the LSF cluster to get a certain queue, + * e.g. "mr". + */ + const char * queue_config_get_queue_system(const queue_config_type * queue_config); + + void queue_config_add_config_items(config_parser_type * parser, bool site_mode); + + job_queue_type * queue_config_alloc_job_queue(const queue_config_type * queue_config); + + int queue_config_get_num_cpu(const queue_config_type * queue_config); + + PY_USED const char * queue_config_lsf_queue_name(); + PY_USED const char * queue_config_lsf_server(); + PY_USED const char * queue_config_lsf_resource(); + PY_USED const char * queue_config_lsf_driver_name(); + +UTIL_SAFE_CAST_HEADER(queue_config); +UTIL_IS_INSTANCE_HEADER(queue_config); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/ranking_common.hpp b/libres/lib/include/ert/enkf/ranking_common.hpp new file mode 100644 index 00000000000..fa3555268b9 --- /dev/null +++ b/libres/lib/include/ert/enkf/ranking_common.hpp @@ -0,0 +1,25 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'ranking_common.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 ERT_RANKING_COMMON_H +#define ERT_RANKING_COMMON_H +#include + +#define INVALID_RANKING_VALUE INFINITY + +#endif diff --git a/libres/lib/include/ert/enkf/ranking_table.hpp b/libres/lib/include/ert/enkf/ranking_table.hpp new file mode 100644 index 00000000000..7f519c31546 --- /dev/null +++ b/libres/lib/include/ert/enkf/ranking_table.hpp @@ -0,0 +1,65 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'ranking_table.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 ERT_RANKING_TABLE_H +#define ERT_RANKING_TABLE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +#include + + typedef struct ranking_table_struct ranking_table_type; + + void ranking_table_set_ens_size( ranking_table_type * table, int ens_size); + ranking_table_type * ranking_table_alloc(int ens_size) ; + void ranking_table_free( ranking_table_type * table ); + + void ranking_table_add_data_ranking( ranking_table_type * ranking_table , + bool sort_increasing , + const char * ranking_key , + const char * user_key , + const char * key_index , + enkf_fs_type * fs , + const enkf_config_node_type * config_node , + int step); + + bool ranking_table_display_ranking( const ranking_table_type * ranking_table , const char * ranking_key ); + bool ranking_table_fwrite_ranking( const ranking_table_type * ranking_table , const char * ranking_key, const char * filename ); + + void ranking_table_add_misfit_ranking( ranking_table_type * ranking_table , + const misfit_ensemble_type * misfit_ensemble , + const stringlist_type * obs_keys , + const int_vector_type * steps, + const char * ranking_key); + + + const perm_vector_type * ranking_table_get_permutation( const ranking_table_type * ranking_table , const char * ranking_key); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/libres/lib/include/ert/enkf/res_config.hpp b/libres/lib/include/ert/enkf/res_config.hpp new file mode 100644 index 00000000000..d2d20bc1c45 --- /dev/null +++ b/libres/lib/include/ert/enkf/res_config.hpp @@ -0,0 +1,86 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'res_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 ERT_RES_CONFIG_H +#define ERT_RES_CONFIG_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef struct res_config_struct res_config_type; + + void res_config_init_config_parser(config_parser_type * config_parser); + res_config_type * res_config_alloc_load(const char *); + res_config_type * res_config_alloc(const config_content_type *); + + PY_USED res_config_type * res_config_alloc_full(char * config_dir, + char * user_config_file, + subst_config_type * subst_config, + site_config_type * site_config, + rng_config_type * rng_config, + analysis_config_type * analysis_config, + ert_workflow_list_type * workflow_list, + hook_manager_type * hook_manager, + ert_templates_type * templates, + ecl_config_type * ecl_config, + ensemble_config_type * ensemble_config, + model_config_type * model_config, + log_config_type * log_config, + queue_config_type * queue_config); + void res_config_free(res_config_type *); + void res_config_add_config_items(config_parser_type * config_parser); + +config_content_type * res_config_alloc_user_content(const char * user_config_file, + config_parser_type * config_parser); +const site_config_type * res_config_get_site_config(const res_config_type *); +rng_config_type * res_config_get_rng_config(const res_config_type *); +const analysis_config_type * res_config_get_analysis_config(const res_config_type *); +ert_workflow_list_type * res_config_get_workflow_list(const res_config_type *); +subst_config_type * res_config_get_subst_config(const res_config_type * res_config); +const hook_manager_type * res_config_get_hook_manager(const res_config_type * res_config); +ert_templates_type * res_config_get_templates(const res_config_type * res_config); +const ecl_config_type * res_config_get_ecl_config(const res_config_type * res_config); +ensemble_config_type * res_config_get_ensemble_config(const res_config_type * res_config); +model_config_type * res_config_get_model_config(const res_config_type * res_config); +const log_config_type * res_config_get_log_config(const res_config_type * res_config); +queue_config_type * res_config_get_queue_config(const res_config_type * res_config); + +PY_USED const char * res_config_get_config_directory(const res_config_type *); +const char * res_config_get_user_config_file(const res_config_type *); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/rng_config.hpp b/libres/lib/include/ert/enkf/rng_config.hpp new file mode 100644 index 00000000000..58706a5d975 --- /dev/null +++ b/libres/lib/include/ert/enkf/rng_config.hpp @@ -0,0 +1,47 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'rng_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 ERT_RNG_CONFIG_H +#define ERT_RNG_CONFIG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + +typedef struct rng_config_struct rng_config_type; + + void rng_config_init(rng_config_type * rng_config, const config_content_type * config); + void rng_config_set_type(rng_config_type * rng_config, rng_alg_type type); + rng_alg_type rng_config_get_type(const rng_config_type * rng_config ); + const char * rng_config_get_random_seed(const rng_config_type * rng_config); + rng_config_type * rng_config_alloc_load_user_config(const char * user_config_file); + rng_config_type * rng_config_alloc(const config_content_type * config_content); + PY_USED rng_config_type * rng_config_alloc_full(const char * random_seed); + void rng_config_free(rng_config_type * rng); + void rng_config_add_config_items(config_parser_type * config ); + rng_manager_type * rng_config_alloc_rng_manager(const rng_config_type * rng_config ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/rng_manager.hpp b/libres/lib/include/ert/enkf/rng_manager.hpp new file mode 100644 index 00000000000..f77ffac7793 --- /dev/null +++ b/libres/lib/include/ert/enkf/rng_manager.hpp @@ -0,0 +1,62 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'rng_manager.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 ERT_RNG_MANAGER_H +#define ERT_RNG_MANAGER_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The number of unsigned int's necessary to represent the state of the rng + * algorithm. Since the current algorithm used is mzran, this value is set to + * 4. + */ +#define RNG_STATE_SIZE 4 + +/** + * The number of digits used to print each unigned int representing the rng + * state. + */ +#define RNG_STATE_DIGITS 10 + + +typedef struct rng_manager_struct rng_manager_type; + +rng_manager_type * rng_manager_alloc(const char * random_seed); +rng_manager_type * rng_manager_alloc_load( const char * seed_file ); +rng_manager_type * rng_manager_alloc_default( ); +rng_manager_type * rng_manager_alloc_random( ); + +rng_type * rng_manager_alloc_rng(rng_manager_type * rng_manager); +rng_type * rng_manager_iget(rng_manager_type * rng_manager, int index); +void rng_manager_free( rng_manager_type * rng_manager ); +void rng_manager_save_state(const rng_manager_type * rng_manager, const char * seed_file); +void rng_manager_log_state(const rng_manager_type * rng_manager); + + +UTIL_IS_INSTANCE_HEADER( rng_manager ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/row_scaling.hpp b/libres/lib/include/ert/enkf/row_scaling.hpp new file mode 100644 index 00000000000..d5acde8d9a7 --- /dev/null +++ b/libres/lib/include/ert/enkf/row_scaling.hpp @@ -0,0 +1,68 @@ +/* + Copyright (C) 2020 Equinor ASA, Norway. + + The file 'row_scaling.hpp' 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 ROW_SCALING_H +#define ROW_SCALING_H + +#include + +class row_scaling { +public: + double operator[](int index) const; + double assign(int index, double value); + double clamp(double value) const; + void multiply(matrix_type * A, const matrix_type * X0) const; + int size() const; + + template + void assign(const T * data, int size); +private: + void resize(int new_size); + int resolution = 1000; + std::vector data; +}; + +typedef row_scaling row_scaling_type; + +#ifdef __cplusplus +extern "C" { +#endif + +#include + + +row_scaling_type * row_scaling_alloc(); +row_scaling_type * row_scaling_alloc_copy(const row_scaling_type * row_scaling); +void row_scaling_free(row_scaling_type * row_scaling); +void row_scaling_multiply(const row_scaling_type * row_scaling, matrix_type * A, const matrix_type * X0); +int row_scaling_get_size(const row_scaling_type * row_scaling); +double row_scaling_iget(const row_scaling_type * row_scaling, int index); +double row_scaling_iset(row_scaling_type * row_scaling, int index, double value); +double row_scaling_clamp(const row_scaling_type * row_scaling, double value); +void row_scaling_assign_double(row_scaling_type * scaling, const double * data, int size); +void row_scaling_assign_float(row_scaling_type * scaling, const float * data, int size); + +UTIL_IS_INSTANCE_HEADER( row_scaling ); + + +#ifdef __cplusplus +} +#endif +#endif + + diff --git a/libres/lib/include/ert/enkf/run_arg.hpp b/libres/lib/include/ert/enkf/run_arg.hpp new file mode 100644 index 00000000000..4e05c18e87a --- /dev/null +++ b/libres/lib/include/ert/enkf/run_arg.hpp @@ -0,0 +1,98 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'run_arg.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. +*/ + +#ifndef ERT_RUN_ARG_H +#define ERT_RUN_ARG_H + +#include +#include +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +UTIL_SAFE_CAST_HEADER( run_arg ); +UTIL_IS_INSTANCE_HEADER( run_arg ); + + + run_arg_type * run_arg_alloc_ENSEMBLE_EXPERIMENT(const char * run_id, + enkf_fs_type * sim_fs, + int iens, + int iter, + const char * runpath, + const char * job_name, + const subst_list_type * subst_list); + + run_arg_type * run_arg_alloc_INIT_ONLY(const char * run_id, + enkf_fs_type * sim_fs, + int iens, + int iter, + const char * runpath, + const subst_list_type * subst_list); + + run_arg_type * run_arg_alloc_SMOOTHER_RUN(const char * run_id, + enkf_fs_type * sim_fs, + enkf_fs_type * update_target_fs, + int iens, + int iter, + const char * runpath, + const char * job_name, + const subst_list_type * subst_list); + + int run_arg_get_step1( const run_arg_type * run_arg ); + int run_arg_get_step2( const run_arg_type * run_arg ); + int run_arg_get_load_start( const run_arg_type * run_arg ); + int run_arg_get_iens( const run_arg_type * run_arg ); + int run_arg_get_iter( const run_arg_type * run_arg ); + void run_arg_increase_submit_count( run_arg_type * run_arg ); + void run_arg_set_queue_index( run_arg_type * run_arg , int queue_index); + + void run_arg_free(run_arg_type * run_arg); + void run_arg_free__(void * arg); + const char * run_arg_get_job_name( const run_arg_type * run_arg); + const char * run_arg_get_runpath( const run_arg_type * run_arg); + const char * run_arg_get_run_id( const run_arg_type * run_arg); + run_status_type run_arg_get_run_status( const run_arg_type * run_arg ); + + int run_arg_get_queue_index_safe( const run_arg_type * run_arg ); + int run_arg_get_queue_index( const run_arg_type * run_arg ); + bool run_arg_is_submitted( const run_arg_type * run_arg ); + + bool run_arg_can_retry( const run_arg_type * run_arg ); + + run_status_type run_arg_get_run_status( const run_arg_type * run_arg); + void run_arg_set_run_status( run_arg_type * run_arg , run_status_type run_status); + + enkf_fs_type * run_arg_get_update_target_fs(const run_arg_type * run_arg); + enkf_fs_type * run_arg_get_sim_fs(const run_arg_type * run_arg); + + void run_arg_set_geo_id( run_arg_type * run_arg , int geo_id); + int run_arg_get_geo_id( const run_arg_type * run_arg); + + const subst_list_type * run_arg_get_subst_list(const run_arg_type * run_arg); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/run_arg_type.hpp b/libres/lib/include/ert/enkf/run_arg_type.hpp new file mode 100644 index 00000000000..1b7b79ddb00 --- /dev/null +++ b/libres/lib/include/ert/enkf/run_arg_type.hpp @@ -0,0 +1,25 @@ +/* + Copyright (C) 2016 Equinor ASA, Norway. + + The file 'run_arg_type.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 ERT_RUN_ARG_TYPE_H +#define ERT_RUN_ARG_TYPE_H + +typedef struct run_arg_struct run_arg_type; + +#endif + diff --git a/libres/lib/include/ert/enkf/runpath_list.hpp b/libres/lib/include/ert/enkf/runpath_list.hpp new file mode 100644 index 00000000000..40bc2f11aea --- /dev/null +++ b/libres/lib/include/ert/enkf/runpath_list.hpp @@ -0,0 +1,54 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + The file 'runpath_list.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 ERT_RUNPATH_LIST_H +#define ERT_RUNPATH_LIST_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define RUNPATH_LIST_DEFAULT_LINE_FMT "%03d %s %s %03d\n" +#define RUNPATH_LIST_FILE ".ert_runpath_list" + + + typedef struct runpath_list_struct runpath_list_type; + + void runpath_list_free( runpath_list_type * list ); + runpath_list_type * runpath_list_alloc(const char * export_file ); + int runpath_list_size( const runpath_list_type * list ); + void runpath_list_add( runpath_list_type * list , int iens , int iter, const char * runpath , const char * basename); + void runpath_list_clear( runpath_list_type * list ); + int runpath_list_iget_iens( runpath_list_type * list , int index); + int runpath_list_iget_iter( runpath_list_type * list , int index); + PY_USED char * runpath_list_iget_runpath( runpath_list_type * list , int index); + char * runpath_list_iget_basename( runpath_list_type * list , int index); + void runpath_list_set_line_fmt( runpath_list_type * list , const char * line_fmt ); + const char * runpath_list_get_line_fmt( const runpath_list_type * list ); + void runpath_list_fprintf( runpath_list_type * list); + const char * runpath_list_get_export_file( const runpath_list_type * list ); + void runpath_list_set_export_file( runpath_list_type * list , const char * export_file ); + PY_USED bool runpath_list_load(runpath_list_type * list); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libres/lib/include/ert/enkf/scalar_config.hpp b/libres/lib/include/ert/enkf/scalar_config.hpp new file mode 100644 index 00000000000..a348eb77a5d --- /dev/null +++ b/libres/lib/include/ert/enkf/scalar_config.hpp @@ -0,0 +1,50 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'scalar_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 ERT_SCALAR_CONFIG_H +#define ERT_SCALAR_CONFIG_H +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +typedef struct scalar_config_struct scalar_config_type; + + + +scalar_config_type * scalar_config_alloc_empty(int); +void scalar_config_free(scalar_config_type *); +const char * scalar_config_get_ensfile_ref(const scalar_config_type * ); +const char * scalar_config_get_eclfile_ref(const scalar_config_type * ); +void scalar_config_transform(const scalar_config_type * , const double * , double *); +double scalar_config_transform_item(const scalar_config_type *, double, int); +void scalar_config_fscanf_line(scalar_config_type * , int , FILE * ); +int scalar_config_get_active_size(const scalar_config_type *); + +SAFE_CAST_HEADER(scalar_config) +GET_DATA_SIZE_HEADER(scalar); +VOID_FREE_HEADER(scalar_config); +GET_ACTIVE_LIST_HEADER(scalar); +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/site_config.hpp b/libres/lib/include/ert/enkf/site_config.hpp new file mode 100644 index 00000000000..5b4e03cca46 --- /dev/null +++ b/libres/lib/include/ert/enkf/site_config.hpp @@ -0,0 +1,60 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'site_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 ERT_SITE_CONFIG_H +#define ERT_SITE_CONFIG_H +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include + + +typedef struct site_config_struct site_config_type; + + const char * site_config_get_location(); + const char * site_config_get_config_file(const site_config_type*); + PY_USED const char * site_config_get_license_root_path( const site_config_type * site_config ); + void site_config_set_license_root_path( site_config_type * site_config , const char * license_root_path); + void site_config_free(site_config_type *); + ext_joblist_type * site_config_get_installed_jobs( const site_config_type * ); + const env_varlist_type * site_config_get_env_varlist(const site_config_type * site_config); + int site_config_install_job(site_config_type * site_config , const char * job_name , const char * install_file); + void site_config_set_umask( site_config_type * site_config , mode_t umask); + mode_t site_config_get_umask( const site_config_type * site_config ); + site_config_type * site_config_alloc_load_user_config(const char *); + site_config_type * site_config_alloc(const config_content_type * config_content); + site_config_type * site_config_alloc_full(ext_joblist_type * ext_joblist, env_varlist_type * env_varlist, int umask); + config_content_type * site_config_alloc_content(config_parser_type*); + void site_config_add_config_items( config_parser_type * config , bool site_mode); +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/state_map.hpp b/libres/lib/include/ert/enkf/state_map.hpp new file mode 100644 index 00000000000..a022cbe888d --- /dev/null +++ b/libres/lib/include/ert/enkf/state_map.hpp @@ -0,0 +1,60 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + The file 'state_map.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. +*/ + +#ifndef ERT_STATE_MAP_H +#define ERT_STATE_MAP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + + typedef struct state_map_struct state_map_type; + + + state_map_type * state_map_alloc( ); + state_map_type * state_map_fread_alloc( const char * filename ); + state_map_type * state_map_fread_alloc_readonly( const char * filename ); + state_map_type * state_map_alloc_copy(const state_map_type * map ); + bool state_map_is_readonly(const state_map_type * state_map); + void state_map_free( state_map_type * map ); + int state_map_get_size(const state_map_type * map); + realisation_state_enum state_map_iget(const state_map_type * map , int index); + void state_map_update_undefined( state_map_type * map , int index , realisation_state_enum new_state); + void state_map_update_matching( state_map_type * map , int index , int state_mask , realisation_state_enum new_state); + void state_map_iset( state_map_type * map ,int index , realisation_state_enum state); + bool state_map_equal(const state_map_type * map1, const state_map_type * map2); + void state_map_fwrite(const state_map_type * map , const char * filename); + bool state_map_fread( state_map_type * map , const char * filename); + void state_map_select_matching(const state_map_type * map, bool_vector_type * select_target, int select_mask); + void state_map_deselect_matching(const state_map_type * map, bool_vector_type * select_target, int select_mask); + void state_map_set_from_inverted_mask(state_map_type * map, const bool_vector_type *mask , realisation_state_enum state); + void state_map_set_from_mask(state_map_type * map, const bool_vector_type *mask , realisation_state_enum state); + int state_map_count_matching(const state_map_type * state_map , int mask); + bool state_map_legal_transition( realisation_state_enum state1 , realisation_state_enum state2); + + UTIL_IS_INSTANCE_HEADER( state_map ); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/subst_config.hpp b/libres/lib/include/ert/enkf/subst_config.hpp new file mode 100644 index 00000000000..be3566179da --- /dev/null +++ b/libres/lib/include/ert/enkf/subst_config.hpp @@ -0,0 +1,43 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'subst_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 ERT_SUBST_CONFIG_H +#define ERT_SUBST_CONFIG_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct subst_config_struct subst_config_type; + +subst_config_type * subst_config_alloc(const config_content_type * user_config); +PY_USED subst_config_type * subst_config_alloc_full(const subst_list_type * define_list); +void subst_config_free(subst_config_type * subst_config); + +subst_list_type * subst_config_get_subst_list(subst_config_type * subst_type); + +void subst_config_add_internal_subst_kw(subst_config_type *, const char *, const char *, const char *); +void subst_config_add_subst_kw(subst_config_type * subst_config , const char * key , const char * value); +void subst_config_clear(subst_config_type * subst_config); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/summary.hpp b/libres/lib/include/ert/enkf/summary.hpp new file mode 100644 index 00000000000..729929ba636 --- /dev/null +++ b/libres/lib/include/ert/enkf/summary.hpp @@ -0,0 +1,71 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'summary.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 ERT_SUMMARY_H +#define ERT_SUMMARY_H +#include + +#include +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + + +summary_type * summary_alloc(const summary_config_type * summary_config); +void summary_free(summary_type *summary); +double summary_get(const summary_type * summary, int report_step ); +void summary_set( summary_type * summary, int report_step, double value); +bool summary_active_value( double value ); +int summary_length(const summary_type * summary); +double summary_undefined_value(); + + +VOID_HAS_DATA_HEADER(summary); +UTIL_SAFE_CAST_HEADER(summary); +UTIL_SAFE_CAST_HEADER_CONST(summary); +VOID_ALLOC_HEADER(summary); +VOID_FREE_HEADER(summary); +VOID_COPY_HEADER(summary); +VOID_FORWARD_LOAD_HEADER(summary); +VOID_FORWARD_LOAD_VECTOR_HEADER(summary); +VOID_USER_GET_HEADER(summary); +VOID_USER_GET_VECTOR_HEADER(summary); +VOID_WRITE_TO_BUFFER_HEADER(summary); +VOID_READ_FROM_BUFFER_HEADER(summary); +VOID_SERIALIZE_HEADER(summary) +VOID_DESERIALIZE_HEADER(summary) +VOID_SET_INFLATION_HEADER(summary); +VOID_CLEAR_HEADER(summary); + +VOID_IADD_HEADER(summary); +VOID_SCALE_HEADER(summary); +VOID_IMUL_HEADER(summary); +VOID_IADDSQR_HEADER(summary); +VOID_ISQRT_HEADER(summary); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/summary_config.hpp b/libres/lib/include/ert/enkf/summary_config.hpp new file mode 100644 index 00000000000..c2d581a1154 --- /dev/null +++ b/libres/lib/include/ert/enkf/summary_config.hpp @@ -0,0 +1,71 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'summary_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 ERT_SUMMARY_CONFIG_H +#define ERT_SUMMARY_CONFIG_H + + +#include +#include + +#include + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif +/* + How should the run system handle a load problem of a summary + variable. Observe that the numerical enum values are actually used - + they should be listed with the most strict mode having the + numerically largest value. +*/ + + +typedef enum { LOAD_FAIL_SILENT = 0, // We just try to load - and if it is not there we do not care at all. + LOAD_FAIL_WARN = 2, // If the key can not be found we will print a warning on stdout - but the run will still be flagged as successfull. + LOAD_FAIL_EXIT = 4 } // The data is deemed important - and we let the run fail if this data can not be found. + load_fail_type; + + + + typedef struct summary_config_struct summary_config_type; + typedef struct summary_struct summary_type; + + void summary_config_update_load_fail_mode( summary_config_type * config , load_fail_type load_fail); + void summary_config_set_load_fail_mode( summary_config_type * config , load_fail_type load_fail); + load_fail_type summary_config_get_load_fail_mode( const summary_config_type * config); + const char * summary_config_get_var(const summary_config_type * ); + summary_config_type * summary_config_alloc(const char * , load_fail_type load_fail); + void summary_config_free(summary_config_type * ); + + UTIL_IS_INSTANCE_HEADER(summary_config); + UTIL_SAFE_CAST_HEADER(summary_config); + UTIL_SAFE_CAST_HEADER_CONST(summary_config); + GET_DATA_SIZE_HEADER(summary); + VOID_GET_DATA_SIZE_HEADER(summary); + VOID_CONFIG_FREE_HEADER(summary); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/summary_key_matcher.hpp b/libres/lib/include/ert/enkf/summary_key_matcher.hpp new file mode 100644 index 00000000000..86b48694b9c --- /dev/null +++ b/libres/lib/include/ert/enkf/summary_key_matcher.hpp @@ -0,0 +1,28 @@ +#ifndef ERT_SUMMARY_KEY_MATCHER_H +#define ERT_SUMMARY_KEY_MATCHER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + + typedef struct summary_key_matcher_struct summary_key_matcher_type; + + summary_key_matcher_type * summary_key_matcher_alloc(); + void summary_key_matcher_free(summary_key_matcher_type * matcher); + int summary_key_matcher_get_size(const summary_key_matcher_type * matcher); + void summary_key_matcher_add_summary_key(summary_key_matcher_type * matcher, const char * summary_key); + bool summary_key_matcher_match_summary_key(const summary_key_matcher_type * matcher, const char * summary_key); + bool summary_key_matcher_summary_key_is_required(const summary_key_matcher_type * matcher, const char * summary_key); + stringlist_type * summary_key_matcher_get_keys(const summary_key_matcher_type * matcher); + + UTIL_IS_INSTANCE_HEADER( summary_key_matcher ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/summary_key_set.hpp b/libres/lib/include/ert/enkf/summary_key_set.hpp new file mode 100644 index 00000000000..71f367ac3ad --- /dev/null +++ b/libres/lib/include/ert/enkf/summary_key_set.hpp @@ -0,0 +1,34 @@ +#ifndef ERT_SUMMARY_KEY_SET_H +#define ERT_SUMMARY_KEY_SET_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + + typedef struct summary_key_set_struct summary_key_set_type; + + summary_key_set_type * summary_key_set_alloc(); + summary_key_set_type * summary_key_set_alloc_from_file(const char * filename, bool read_only); + void summary_key_set_free(summary_key_set_type * set); + int summary_key_set_get_size(summary_key_set_type * set); + bool summary_key_set_add_summary_key(summary_key_set_type * set, const char * summary_key); + PY_USED bool summary_key_set_has_summary_key(summary_key_set_type * set, const char * summary_key); + stringlist_type * summary_key_set_alloc_keys(summary_key_set_type * set); + bool summary_key_set_is_read_only(const summary_key_set_type * set); + + void summary_key_set_fwrite(summary_key_set_type * set, const char * filename); + bool summary_key_set_fread(summary_key_set_type * set, const char * filename); + + + + UTIL_IS_INSTANCE_HEADER( summary_key_set ); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/summary_obs.hpp b/libres/lib/include/ert/enkf/summary_obs.hpp new file mode 100644 index 00000000000..a5b49d43f6a --- /dev/null +++ b/libres/lib/include/ert/enkf/summary_obs.hpp @@ -0,0 +1,79 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'summary_obs.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 ERT_SUMMARY_OBS_H +#define ERT_SUMMARY_OBS_H + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef struct summary_obs_struct summary_obs_type; + + +void summary_obs_free( + summary_obs_type * summary_obs); + +summary_obs_type * summary_obs_alloc( + const char * summary_key, + const char * obs_key , + double value , + double std); + + + double summary_obs_get_value( const summary_obs_type * summary_obs ); + double summary_obs_get_std( const summary_obs_type * summary_obs ); + double summary_obs_get_std_scaling( const summary_obs_type * summary_obs ); + +bool summary_obs_default_used( + const summary_obs_type * summary_obs, + int restart_nr); + +PY_USED const char * summary_obs_get_summary_key( + const summary_obs_type * summary_obs); + +void summary_obs_update_std_scale(summary_obs_type * summary_obs, double std_multiplier , const active_list_type * active_list); + +void summary_obs_set_std_scale(summary_obs_type * summary_obs, double std_multiplier); + +VOID_FREE_HEADER(summary_obs); +VOID_GET_OBS_HEADER(summary_obs); +VOID_MEASURE_HEADER(summary_obs); +UTIL_IS_INSTANCE_HEADER(summary_obs); +VOID_USER_GET_OBS_HEADER(summary_obs); +VOID_CHI2_HEADER(summary_obs); +VOID_UPDATE_STD_SCALE_HEADER(summary_obs); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/surface.hpp b/libres/lib/include/ert/enkf/surface.hpp new file mode 100644 index 00000000000..a4ce5f811e5 --- /dev/null +++ b/libres/lib/include/ert/enkf/surface.hpp @@ -0,0 +1,57 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'surface.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 ERT_SURFACE_H +#define ERT_SURFACE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + + typedef struct surface_struct surface_type; + + UTIL_SAFE_CAST_HEADER(surface); + UTIL_SAFE_CAST_HEADER_CONST(surface); + VOID_ALLOC_HEADER(surface); + VOID_FREE_HEADER(surface); + VOID_ECL_WRITE_HEADER(surface); + VOID_COPY_HEADER(surface); + VOID_USER_GET_HEADER(surface); + VOID_WRITE_TO_BUFFER_HEADER(surface); + VOID_READ_FROM_BUFFER_HEADER(surface); + VOID_SERIALIZE_HEADER(surface); + VOID_DESERIALIZE_HEADER(surface); + VOID_SET_INFLATION_HEADER(surface); + VOID_CLEAR_HEADER(surface); + VOID_IADD_HEADER(surface); + VOID_SCALE_HEADER(surface); + VOID_IMUL_HEADER(surface); + VOID_IADDSQR_HEADER(surface); + VOID_ISQRT_HEADER(surface); + VOID_INITIALIZE_HEADER(surface); + VOID_FLOAD_HEADER(surface); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libres/lib/include/ert/enkf/surface_config.hpp b/libres/lib/include/ert/enkf/surface_config.hpp new file mode 100644 index 00000000000..4e682df1eb1 --- /dev/null +++ b/libres/lib/include/ert/enkf/surface_config.hpp @@ -0,0 +1,51 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'surface_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 ERT_SURFACE_CONFIG_H +#define ERT_SURFACE_CONFIG_H + + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct surface_config_struct surface_config_type; + + void surface_config_ecl_write( const surface_config_type * config , const char * filename , const double * zcoord); + const geo_surface_type * surface_config_get_base_surface( const surface_config_type * config ); + void surface_config_free( surface_config_type * config ); + int surface_config_get_data_size(const surface_config_type * config ); + surface_config_type * surface_config_alloc_empty( ); + void surface_config_set_base_surface( surface_config_type * config , const char * base_surface ); + + UTIL_SAFE_CAST_HEADER(surface_config); + UTIL_SAFE_CAST_HEADER_CONST(surface_config); + GET_DATA_SIZE_HEADER(surface); + VOID_GET_DATA_SIZE_HEADER(surface); + VOID_CONFIG_FREE_HEADER(surface); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libres/lib/include/ert/enkf/time_map.hpp b/libres/lib/include/ert/enkf/time_map.hpp new file mode 100644 index 00000000000..311419dd9a6 --- /dev/null +++ b/libres/lib/include/ert/enkf/time_map.hpp @@ -0,0 +1,69 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + The file 'time_map.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. +*/ +#ifndef ERT_TIME_MAP_H +#define ERT_TIME_MAP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include +#include +#include + +typedef struct time_map_struct time_map_type; + + UTIL_SAFE_CAST_HEADER( time_map ); + UTIL_IS_INSTANCE_HEADER( time_map ); + + bool time_map_try_summary_update( time_map_type * map , const ecl_sum_type * ecl_sum); + bool time_map_try_update( time_map_type * map , int step , time_t time); + bool time_map_attach_refcase( time_map_type * time_map , const ecl_sum_type * refcase); + bool time_map_has_refcase( const time_map_type * time_map ); + bool time_map_is_strict( const time_map_type * time_map ); + void time_map_set_strict( time_map_type * time_map , bool strict); + void time_map_clear( time_map_type * map ); + bool time_map_equal( const time_map_type * map1 , const time_map_type * map2); + time_map_type * time_map_alloc( ); + void time_map_free( time_map_type * map ); + bool time_map_update( time_map_type * map , int step , time_t time); + bool time_map_summary_update( time_map_type * map , const ecl_sum_type * ecl_sum); + time_t time_map_iget( time_map_type * map , int step ); + void time_map_fwrite( time_map_type * map , const char * filename); + void time_map_fread( time_map_type * map , const char * filename); + bool time_map_fscanf(time_map_type * map , const char * filename); + double time_map_iget_sim_days( time_map_type * map , int step ); + int time_map_get_last_step( time_map_type * map); + int time_map_get_size( time_map_type * map); + time_t time_map_get_start_time( time_map_type * map); + time_t time_map_get_end_time( time_map_type * map); + double time_map_get_end_days( time_map_type * map); + bool time_map_is_readonly( const time_map_type * tm); + time_map_type * time_map_fread_alloc_readonly( const char * filename); + int_vector_type * time_map_alloc_index_map( time_map_type * map , const ecl_sum_type * ecl_sum ); + int time_map_lookup_time( time_map_type * map , time_t time); + int time_map_lookup_days( time_map_type * map , double sim_days); + int time_map_lookup_time_with_tolerance( time_map_type * map , time_t time , int seconds_before_tolerance, int seconds_after_tolerance); + PY_USED void time_map_summary_upgrade107( time_map_type * map , const ecl_sum_type * ecl_sum); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/trans_func.hpp b/libres/lib/include/ert/enkf/trans_func.hpp new file mode 100644 index 00000000000..30b5af34254 --- /dev/null +++ b/libres/lib/include/ert/enkf/trans_func.hpp @@ -0,0 +1,48 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'trans_func.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 ERT_TRANS_FUNC_H +#define ERT_TRANS_FUNC_H +#ifdef __cplusplus +extern "C" { +#endif +#include +#include + +#include + +#include + + +typedef struct trans_func_struct trans_func_type; +typedef double (transform_ftype) (double , const double_vector_type *); +typedef bool (validate_ftype) (const trans_func_type * ); + +trans_func_type * trans_func_alloc(const stringlist_type * args); +double trans_func_eval( const trans_func_type * trans_func , double x); + +void trans_func_free( trans_func_type * trans_func ); +bool trans_func_use_log_scale(const trans_func_type * trans_func ); +stringlist_type * trans_func_get_param_names(const trans_func_type * trans_func); +double_vector_type * trans_func_get_params(const trans_func_type * trans_func); +const char * trans_func_get_name(const trans_func_type * trans_func); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/enkf/value_export.hpp b/libres/lib/include/ert/enkf/value_export.hpp new file mode 100644 index 00000000000..a078dd58519 --- /dev/null +++ b/libres/lib/include/ert/enkf/value_export.hpp @@ -0,0 +1,48 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'value_export.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 VALUE_EXPORT_H +#define VALUE_EXPORT_H + +#ifdef __cplusplus +#include +extern "C" { +#endif + + + + +#include + + typedef struct value_export_struct value_export_type; + + void value_export_free(value_export_type * value); + value_export_type * value_export_alloc(std::string directory, std::string base_name); + int value_export_size(const value_export_type * value); + void value_export_json(const value_export_type * value); + void value_export_txt(const value_export_type * value); + void value_export_txt__(const value_export_type * value, const char * filename); + void value_export(const value_export_type * value); + void value_export_append(value_export_type * value, const std::string key, const std::string subkey, double double_value); + +UTIL_IS_INSTANCE_HEADER(value_export); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/job_queue/environment_varlist.hpp b/libres/lib/include/ert/job_queue/environment_varlist.hpp new file mode 100644 index 00000000000..486196ea2ff --- /dev/null +++ b/libres/lib/include/ert/job_queue/environment_varlist.hpp @@ -0,0 +1,41 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'environment_varlist.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 ENVIRONMENT_VARLIST_H +#define ENVIRONMENT_VARLIST_H +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct env_varlist_struct env_varlist_type; + +env_varlist_type * env_varlist_alloc(); + +void env_varlist_update_path(env_varlist_type * list, const char * path_var, const char * new_path); +void env_varlist_setenv(env_varlist_type * list, const char * var, const char * value); +void env_varlist_json_fprintf(const env_varlist_type * list, FILE * stream); +int env_varlist_get_size(env_varlist_type * list); + +void env_varlist_free(env_varlist_type * list); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/job_queue/ext_job.hpp b/libres/lib/include/ert/job_queue/ext_job.hpp new file mode 100644 index 00000000000..c5b23d87bbe --- /dev/null +++ b/libres/lib/include/ert/job_queue/ext_job.hpp @@ -0,0 +1,96 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'ext_job.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 ERT_EXT_JOB_H +#define ERT_EXT_JOB_H +#ifdef __cplusplus +extern "C" { +#endif +#include + +#include +#include +#include +#include +#include + +typedef struct ext_job_struct ext_job_type; + + +const char * ext_job_get_help_text( const ext_job_type * job ); +void ext_job_set_help_text( ext_job_type * job , const char * help_text); + +ext_job_type * ext_job_alloc_copy(const ext_job_type * ); +void ext_job_free_deprecated_argv(ext_job_type * ext_job); //DEPRECATED +ext_job_type * ext_job_alloc(const char * , const char * license_root_path , bool private_job); +const char * ext_job_get_name(const ext_job_type * ); +void ext_job_free(ext_job_type * ) ; +void ext_job_free__(void * ); +void ext_job_add_environment(ext_job_type *, const char * , const char * ) ; +void ext_job_save( const ext_job_type * ext_job ); +void ext_job_fprintf(const ext_job_type * , FILE * stream ); +void ext_job_set_private_arg(ext_job_type * , const char * , const char * ); +void ext_job_set_define_args(ext_job_type * ext_job, const subst_list_type * define_args); + +void ext_job_json_fprintf(const ext_job_type*, int job_index, FILE*, const subst_list_type*); +ext_job_type * ext_job_fscanf_alloc(const char * , const char * , bool private_job , const char *, bool search_path); +const stringlist_type * ext_job_get_arglist( const ext_job_type * ext_job ); +const stringlist_type * ext_job_get_argvalues( const ext_job_type * ext_job ); +bool ext_job_is_shared( const ext_job_type * ext_job ); +bool ext_job_is_private( const ext_job_type * ext_job ); + +void ext_job_set_executable(ext_job_type * ext_job, const char * executable_abs, const char * executable_input, bool search_path); +const char * ext_job_get_executable(const ext_job_type * ext_job); + + +void ext_job_set_args(ext_job_type * ext_job, const stringlist_type * argv); +void ext_job_set_config_file(ext_job_type * ext_job, const char * config_file); +const char * ext_job_get_config_file(const ext_job_type * ext_job); +void ext_job_set_target_file(ext_job_type * ext_job, const char * target_file); +const char * ext_job_get_target_file(const ext_job_type * ext_job); +void ext_job_set_start_file(ext_job_type * ext_job, const char * start_file); +const char * ext_job_get_start_file(const ext_job_type * ext_job); +const char * ext_job_get_name(const ext_job_type * ext_job); +void ext_job_set_stdin_file(ext_job_type * ext_job, const char * stdin_file); +const char * ext_job_get_stdin_file(const ext_job_type * ext_job); +void ext_job_set_stdout_file(ext_job_type * ext_job, const char * stdout_file); +const char * ext_job_get_stdout_file(const ext_job_type * ext_job); +void ext_job_set_stderr_file(ext_job_type * ext_job, const char * stderr_file); +const char * ext_job_get_stderr_file(const ext_job_type * ext_job); + +PY_USED int ext_job_get_min_arg(const ext_job_type * ext_job); +PY_USED int ext_job_get_max_arg(const ext_job_type * ext_job); +config_item_types ext_job_iget_argtype( const ext_job_type * ext_job, int index); + +void ext_job_set_max_running( ext_job_type * ext_job , int max_running); +int ext_job_get_max_running( const ext_job_type * ext_job ); +void ext_job_set_max_running_minutes( ext_job_type * ext_job , int max_running_minutes); +int ext_job_get_max_running_minutes( const ext_job_type * ext_job ); +void ext_job_add_environment(ext_job_type *ext_job , const char * key , const char * value); +void ext_job_clear_environment( ext_job_type * ext_job ); +hash_type * ext_job_get_environment( ext_job_type * ext_job ); +int ext_job_set_private_args_from_string( ext_job_type * ext_job , const char * arg_string ); +const char * ext_job_get_license_path(const ext_job_type*); + +const char * ext_job_get_error_file(const ext_job_type * ext_job); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/job_queue/ext_joblist.hpp b/libres/lib/include/ert/job_queue/ext_joblist.hpp new file mode 100644 index 00000000000..50c27bc7697 --- /dev/null +++ b/libres/lib/include/ert/job_queue/ext_joblist.hpp @@ -0,0 +1,53 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'ext_joblist.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 ERT_EXT_JOBLIST_H +#define ERT_EXT_JOBLIST_H +#ifdef __cplusplus +extern "C" { +#endif +#include + +#include +#include +#include + +#include + + +typedef struct ext_joblist_struct ext_joblist_type; + +ext_joblist_type * ext_joblist_alloc(); +void ext_joblist_free(ext_joblist_type * ); +void ext_joblist_add_job(ext_joblist_type * joblist , const char * name , ext_job_type * new_job); +ext_job_type * ext_joblist_get_job(const ext_joblist_type * , const char * ); +ext_job_type * ext_joblist_get_job_copy(const ext_joblist_type * , const char * ); +//void ext_joblist_python_fprintf(const ext_joblist_type * , const stringlist_type * , const char * , const subst_list_type *); +bool ext_joblist_has_job(const ext_joblist_type * , const char * ); +stringlist_type * ext_joblist_alloc_list( const ext_joblist_type * joblist); +bool ext_joblist_del_job( ext_joblist_type * joblist , const char * job_name ); +void ext_joblist_add_jobs_in_directory(ext_joblist_type * joblist , const char * path, const char * license_root_path, bool user_mode, bool search_path ); +int ext_joblist_get_size( const ext_joblist_type * joblist ); + +hash_type * ext_joblist_get_jobs( const ext_joblist_type * joblist ); + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/libres/lib/include/ert/job_queue/forward_model.hpp b/libres/lib/include/ert/job_queue/forward_model.hpp new file mode 100644 index 00000000000..1b90f423afa --- /dev/null +++ b/libres/lib/include/ert/job_queue/forward_model.hpp @@ -0,0 +1,53 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'forward_model.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 ERT_FORWARD_MODEL_H +#define ERT_FORWARD_MODEL_H +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include + +#include +#include + +typedef struct forward_model_struct forward_model_type ; + + + stringlist_type * forward_model_alloc_joblist( const forward_model_type * forward_model ); + PY_USED void forward_model_clear( forward_model_type * forward_model ); + forward_model_type * forward_model_alloc(const ext_joblist_type * ext_joblist); + void forward_model_parse_job_args(forward_model_type * model, const stringlist_type * list, const subst_list_type * define_args); + void forward_model_parse_job_deprecated_args(forward_model_type * forward_model, const char * input_string, const subst_list_type * define_args); //DEPRECATED + void forward_model_formatted_fprintf(const forward_model_type * , const char * run_id, const char *, const char * , const subst_list_type * , + mode_t umask, const env_varlist_type * list); + void forward_model_free( forward_model_type * ); + ext_job_type * forward_model_iget_job( forward_model_type * forward_model , int index); + int forward_model_get_length( const forward_model_type * forward_model ); + + ext_job_type * forward_model_add_job(forward_model_type * forward_model , const char * job_name); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/job_queue/job_kw_definitions.hpp b/libres/lib/include/ert/job_queue/job_kw_definitions.hpp new file mode 100644 index 00000000000..79f33903fa5 --- /dev/null +++ b/libres/lib/include/ert/job_queue/job_kw_definitions.hpp @@ -0,0 +1,43 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'job_kw_definitions.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 ERT_JOB_KW_DEFINITIONS_H +#define ERT_JOB_KW_DEFINITIONS_H +#ifdef __cplusplus +extern "C" { +#endif + +#define MIN_ARG_KEY "MIN_ARG" +#define MAX_ARG_KEY "MAX_ARG" +#define ARG_TYPE_KEY "ARG_TYPE" +#define EXECUTABLE_KEY "EXECUTABLE" + +#define JOB_STRING_TYPE "STRING" +#define JOB_INT_TYPE "INT" +#define JOB_FLOAT_TYPE "FLOAT" +#define JOB_BOOL_TYPE "BOOL" +#define JOB_RUNTIME_FILE_TYPE "RUNTIME_FILE" +#define JOB_RUNTIME_INT_TYPE "RUNTIME_INT" + +config_item_types job_kw_get_type(const char * arg_type); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/job_queue/job_list.hpp b/libres/lib/include/ert/job_queue/job_list.hpp new file mode 100644 index 00000000000..d3db0136274 --- /dev/null +++ b/libres/lib/include/ert/job_queue/job_list.hpp @@ -0,0 +1,53 @@ +/* + Copyright (C) 2015 Equinor ASA, Norway. + + The file 'job_node.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 ERT_JOB_LIST_H +#define ERT_JOB_LIST_H + + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include + + +typedef struct job_list_struct job_list_type; + + job_list_type * job_list_alloc(); + void job_list_free( job_list_type * job_list ); + int job_list_get_size( const job_list_type * job_list ); + void job_list_add_job( job_list_type * job_list , job_queue_node_type * job_node ); + job_queue_node_type * job_list_iget_job( const job_list_type * job_list , int queue_index); + void job_list_reset( job_list_type * job_list ); + void job_list_get_wrlock( job_list_type * list); + void job_list_get_rdlock( job_list_type * list); + void job_list_reader_wait( job_list_type * list, int usleep_time1, int usleep_time2); + void job_list_unlock( job_list_type * list); + + UTIL_SAFE_CAST_HEADER( job_list ); + UTIL_IS_INSTANCE_HEADER( job_list ); + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/libres/lib/include/ert/job_queue/job_node.hpp b/libres/lib/include/ert/job_queue/job_node.hpp new file mode 100644 index 00000000000..7e7c7f3ee89 --- /dev/null +++ b/libres/lib/include/ert/job_queue/job_node.hpp @@ -0,0 +1,125 @@ +/* + Copyright (C) 2015 Equinor ASA, Norway. + + The file 'job_node.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 ERT_JOB_NODE_H +#define ERT_JOB_NODE_H + + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/** + This struct holds the job_queue information about one job. Observe + the following: + + 1. This struct is purely static - i.e. it is invisible outside of + this file-scope. + + 2. Typically the driver would like to store some additional + information, i.e. the PID of the running process for the local + driver; that is stored in a (driver specific) struct under the + field job_data. + + 3. If the driver detects that a job has failed it leaves an EXIT + file, the exit status is (currently) not reliably transferred + back to to the job_queue layer. + +*/ + +typedef bool (job_callback_ftype) (void *); +typedef struct job_queue_node_struct job_queue_node_type; + + + time_t job_queue_node_get_timestamp(const job_queue_node_type * node); + bool job_queue_node_status_transition( job_queue_node_type * node , job_queue_status_type * status , job_status_type new_status); + submit_status_type job_queue_node_submit( job_queue_node_type * node , job_queue_status_type * status , queue_driver_type * driver); + PY_USED submit_status_type job_queue_node_submit_simple(job_queue_node_type * node, + queue_driver_type * driver); + void job_queue_node_free_error_info( job_queue_node_type * node ); + void job_queue_node_fscanf_EXIT( job_queue_node_type * node ); + void job_queue_node_free_data(job_queue_node_type * node); + job_queue_node_type * job_queue_node_alloc( const char * job_name , + const char * run_path , + const char * run_cmd , + int argc , + char const * const * argv , + int num_cpu , + const char * ok_file, + const char * status_file, + const char * exit_file, + job_callback_ftype * done_callback, + job_callback_ftype * retry_callback, + job_callback_ftype * exit_callback, + void * callback_arg); + + + job_queue_node_type * job_queue_node_alloc_simple( const char * job_name , + const char * run_path , + const char * run_cmd , + int argc , + const char ** argv ); + + PY_USED job_queue_node_type * job_queue_node_alloc_python( const char * job_name , + const char * run_path , + const char * run_cmd , + int argc , + const stringlist_type * arguments, + int num_cpu, + const char * ok_file, + const char * status_file, + const char * exit_file); + + bool job_queue_node_kill( job_queue_node_type * node , job_queue_status_type * status , queue_driver_type * driver); + PY_USED bool job_queue_node_kill_simple(job_queue_node_type * node, queue_driver_type * driver); + void job_queue_node_free(job_queue_node_type * node); + job_status_type job_queue_node_get_status(const job_queue_node_type * node); + void job_queue_node_free_driver_data( job_queue_node_type * node , queue_driver_type * driver); + PY_USED bool job_queue_node_update_status( job_queue_node_type * node , job_queue_status_type * status , queue_driver_type * driver); + PY_USED bool job_queue_node_update_status_simple(job_queue_node_type * node, queue_driver_type * driver ); + int job_queue_node_get_submit_attempt( const job_queue_node_type * node); + void job_queue_node_reset_submit_attempt( job_queue_node_type * node); + void job_queue_node_dec_submit_attempt( job_queue_node_type * node); + + time_t job_queue_node_get_sim_start( const job_queue_node_type * node ); + time_t job_queue_node_get_sim_end( const job_queue_node_type * node ); + double job_queue_node_time_since_sim_start( const job_queue_node_type * node ) ; + + const char * job_queue_node_get_ok_file( const job_queue_node_type * node); + const char * job_queue_node_get_exit_file( const job_queue_node_type * node); + + bool job_queue_node_run_DONE_callback( job_queue_node_type * node ); + bool job_queue_node_run_RETRY_callback( job_queue_node_type * node ); + void job_queue_node_run_EXIT_callback( job_queue_node_type * node ); + int job_queue_node_get_queue_index( const job_queue_node_type * node ); + void job_queue_node_set_queue_index( job_queue_node_type * node , int queue_index); + + void * job_queue_node_get_driver_data( job_queue_node_type * node ); + void job_queue_node_set_status(job_queue_node_type * node , job_status_type new_status); + + UTIL_IS_INSTANCE_HEADER( job_queue_node ); + UTIL_SAFE_CAST_HEADER( job_queue_node ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/job_queue/job_queue.hpp b/libres/lib/include/ert/job_queue/job_queue.hpp new file mode 100644 index 00000000000..a4b7f3aec54 --- /dev/null +++ b/libres/lib/include/ert/job_queue/job_queue.hpp @@ -0,0 +1,103 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'job_queue.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 ERT_JOB_QUEUE_H +#define ERT_JOB_QUEUE_H +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include + +#include + +#include +#include + + typedef struct job_queue_struct job_queue_type; + PY_USED void job_queue_submit_complete( job_queue_type * queue ); + void job_queue_set_driver(job_queue_type * queue , queue_driver_type * driver); + bool job_queue_has_driver(const job_queue_type * queue ); + job_queue_type * job_queue_alloc( int , const char * ok_file , const char * status_file, const char * exit_file); + void job_queue_free(job_queue_type *); + + int job_queue_add_job(job_queue_type * , + const char * run_cmd , + job_callback_ftype * done_callback, + job_callback_ftype * retry_callback, + job_callback_ftype * exit_callback, + void * callback_arg , + int num_cpu , + const char * , + const char * , + int argc , + const char ** argv ); + + bool job_queue_accept_jobs(const job_queue_type * queue); + + void job_queue_run_jobs(job_queue_type * queue, int num_total_run, bool verbose); + PY_USED void job_queue_run_jobs_threaded(job_queue_type * queue , int num_total_run, bool verbose); + void * job_queue_run_jobs__(void * ); + void job_queue_start_manager_thread( job_queue_type * job_queue , pthread_t * queue_thread , int job_size , bool verbose); + + job_status_type job_queue_iget_job_status(job_queue_type * , int ); + + int job_queue_iget_status_summary( const job_queue_type * queue , job_status_type status); + time_t job_queue_iget_sim_start( job_queue_type * queue, int job_index); + time_t job_queue_iget_sim_end( job_queue_type * queue, int job_index); + + PY_USED void job_queue_set_max_job_duration(job_queue_type * queue, int max_duration_seconds); + int job_queue_get_max_job_duration(const job_queue_type * queue); + void job_queue_set_job_stop_time(job_queue_type * queue, time_t time); + time_t job_queue_get_job_stop_time(const job_queue_type * queue); + void job_queue_set_auto_job_stop_time(job_queue_type * queue); + bool job_queue_kill_job( job_queue_type * queue , int job_index); + PY_USED bool job_queue_is_running( const job_queue_type * queue ); + void job_queue_set_max_submit( job_queue_type * job_queue , int max_submit ); + int job_queue_get_max_submit(const job_queue_type * job_queue ); + bool job_queue_get_open(const job_queue_type * job_queue); + PY_USED bool job_queue_get_pause( const job_queue_type * job_queue ); + PY_USED void job_queue_set_pause_on( job_queue_type * job_queue); + PY_USED void job_queue_set_pause_off( job_queue_type * job_queue); + PY_USED bool job_queue_start_user_exit( job_queue_type * queue); + PY_USED bool job_queue_get_user_exit( const job_queue_type * queue); + + PY_USED int job_queue_get_active_size( const job_queue_type * queue ); + int job_queue_get_num_running( const job_queue_type * queue); + int job_queue_get_num_pending( const job_queue_type * queue); + int job_queue_get_num_waiting( const job_queue_type * queue); + int job_queue_get_num_complete( const job_queue_type * queue); + PY_USED void * job_queue_iget_driver_data( job_queue_type * queue , int job_index); + job_queue_node_type * job_queue_iget_job( job_queue_type * job_queue , int job_nr ); + bool job_queue_has_driver(const job_queue_type * queue ); + int job_queue_get_max_running( const job_queue_type * queue ); + + PY_USED void job_queue_set_max_running( job_queue_type * queue , int max_running ); + + char * job_queue_get_ok_file(const job_queue_type * queue); + PY_USED char * job_queue_get_exit_file(const job_queue_type * queue); + PY_USED char * job_queue_get_status_file(const job_queue_type * queue); + PY_USED int job_queue_add_job_node(job_queue_type * queue, job_queue_node_type * node); + + UTIL_SAFE_CAST_HEADER( job_queue ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/job_queue/job_queue_status.hpp b/libres/lib/include/ert/job_queue/job_queue_status.hpp new file mode 100644 index 00000000000..e7ef0360d7b --- /dev/null +++ b/libres/lib/include/ert/job_queue/job_queue_status.hpp @@ -0,0 +1,46 @@ +/* + Copyright (C) 2015 Equinor ASA, Norway. + + The file 'job_status_test.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 ERT_JOB_QUEUE_STATUS_H +#define ERT_JOB_QUEUE_STATUS_H + +#ifdef __cplusplus +extern "C" { +#endif +#include + +#include +#include + + typedef struct job_queue_status_struct job_queue_status_type; + + job_queue_status_type * job_queue_status_alloc(); + void job_queue_status_free( job_queue_status_type * status ); + int job_queue_status_get_count( job_queue_status_type * status , int job_status_mask); + void job_queue_status_clear( job_queue_status_type * status ); + void job_queue_status_inc( job_queue_status_type * status_count , job_status_type status_type); + bool job_queue_status_transition( job_queue_status_type * status_count , job_status_type src_status , job_status_type target_status); + int job_queue_status_get_total_count( const job_queue_status_type * status ); + + UTIL_IS_INSTANCE_HEADER( job_queue_status ); + UTIL_SAFE_CAST_HEADER( job_queue_status ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/job_queue/job_status.hpp b/libres/lib/include/ert/job_queue/job_status.hpp new file mode 100644 index 00000000000..ec4b31bd4af --- /dev/null +++ b/libres/lib/include/ert/job_queue/job_status.hpp @@ -0,0 +1,130 @@ +/* + Copyright (C) 2018 Equinor ASA, Norway. + + The file 'job_status.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 JOB_STATUS_H +#define JOB_STATUS_H +#ifdef __cplusplus +extern "C" { +#endif + +#include +/* + +---------------------------------+ + | | ++---------------------------------+ | JOB_QUEUE_WAITING <----------------------------------+ +| | | <--------------+ | +| JOB_QUEUE_NOT_ACTIVE | +---------------+-----------------+ | | +| | | | | ++---------------------------------+ | | | + | | | + +---------------v-----------------+ | | + | | | | ++---------------------------------+ | JOB_QUEUE_SUBMITTED | | | +| | | | | | +|JOB_QUEUE_STATUS_FAILURE | +-------+--------------------+----+ | | +| | | | | | ++---------------------------------+ | | +----------------+----------------+ | + +-------------------------v-------+ | | | | + | | | | JOB_QUEUE_DO_KILL_NODE_FAILURE | | + | JOB_QUEUE_PENDING | | | | | + | | | +---------------------^-----------+ | + +-------------------------+-------+ | | | + | | | | + | +---------------v-----------------+ | | + | | | | | + +----> JOB_QUEUE_RUNNING +------+ | + +----------------------------------------------------------+ | | + | +---+-------------------+---------+ | + | | | | + | | | | + +--------------v------------------+ +---------------------------------+ | +---------v-----------------------+ | + | | | | | | | | + | JOB_QUEUE_DO_KILL | | JOB_QUEUE_DONE +<---+ +---> JOB_QUEUE_EXIT | | + | | | | | | | | + +--------------+------------------+ +----------------+----------------+ | +-----------------+---------------+ | + | | | | | + | | | | | + | | | | | + | +----------------v----------------+ | +-----------------v---------------+ | + | | | | | | | + | |JOB_QUEUE_RUNNING_DONE_CALLBACK +----------+ | JOB_QUEUE_RUNNING_EXIT_CALLBACK +-------+ + | | | | | + | +----------------+----------------+ +----------------+----------------+ + | | | + | | | + | | | + +--------------v------------------+ +----------------v----------------+ +----------------v----------------+ + | | | | | | + | JOB_QUEUE_IS_KILLED | | JOB_QUEUE_SUCCESS | | JOB_QUEUE_FAILED | + | | | | | | + +---------------------------------+ +---------------------------------+ +---------------------------------+ + +*/ + + + +/* + NB: the status count algorithm has a HARD assumption that these + values are on the 2^N form - without holes in the series. +*/ + +typedef enum { + JOB_QUEUE_NOT_ACTIVE = 1, /* This value is used in external query routines - for jobs which are (currently) not active. */ + JOB_QUEUE_WAITING = 2, /* A node which is waiting in the internal queue. */ + JOB_QUEUE_SUBMITTED = 4, /* Internal status: It has has been submitted - the next status update will (should) place it as pending or running. */ + JOB_QUEUE_PENDING = 8, /* A node which is pending - a status returned by the external system. I.e LSF */ + JOB_QUEUE_RUNNING = 16, /* The job is running */ + JOB_QUEUE_DONE = 32, /* The job is done - but we have not yet checked if the target file is produced */ + JOB_QUEUE_EXIT = 64, /* The job has exited - check attempts to determine if we retry or go to complete_fail */ + JOB_QUEUE_IS_KILLED = 128, /* The job has been killed, following a JOB_QUEUE_DO_KILL*/ + JOB_QUEUE_DO_KILL = 256, /* The the job should be killed, either due to user request, or automated measures - the job can NOT be restarted. */ + JOB_QUEUE_SUCCESS = 512, + JOB_QUEUE_RUNNING_DONE_CALLBACK = 1024, + JOB_QUEUE_RUNNING_EXIT_CALLBACK = 2048, + JOB_QUEUE_STATUS_FAILURE = 4096, + JOB_QUEUE_FAILED = 8192, + JOB_QUEUE_DO_KILL_NODE_FAILURE = 16384, + JOB_QUEUE_UNKNOWN = 32768 + } job_status_type; + +#define JOB_QUEUE_RUNNING_CALLBACK (JOB_QUEUE_RUNNING_DONE_CALLBACK + JOB_QUEUE_RUNNING_EXIT_CALLBACK) + +#define JOB_QUEUE_STATUS_ALL (JOB_QUEUE_NOT_ACTIVE + JOB_QUEUE_WAITING + JOB_QUEUE_SUBMITTED + JOB_QUEUE_PENDING + JOB_QUEUE_RUNNING + JOB_QUEUE_DONE + \ + JOB_QUEUE_EXIT + JOB_QUEUE_IS_KILLED + JOB_QUEUE_DO_KILL + JOB_QUEUE_SUCCESS + JOB_QUEUE_RUNNING_CALLBACK + \ + JOB_QUEUE_STATUS_FAILURE + JOB_QUEUE_FAILED + JOB_QUEUE_DO_KILL_NODE_FAILURE + JOB_QUEUE_UNKNOWN) + +#define JOB_QUEUE_MAX_STATE 16 + + /* + These are the jobs which can be killed. It is OK to try to kill a + job which is not in this state, the only thing happening is that the + function job_queue_kill_simulation() wil return false. + */ +#define JOB_QUEUE_CAN_KILL (JOB_QUEUE_WAITING + JOB_QUEUE_RUNNING + JOB_QUEUE_PENDING + JOB_QUEUE_SUBMITTED + JOB_QUEUE_DO_KILL + JOB_QUEUE_DO_KILL_NODE_FAILURE) + +#define JOB_QUEUE_CAN_UPDATE_STATUS (JOB_QUEUE_RUNNING + JOB_QUEUE_PENDING + JOB_QUEUE_SUBMITTED + JOB_QUEUE_UNKNOWN) + +#define JOB_QUEUE_COMPLETE_STATUS (JOB_QUEUE_IS_KILLED + JOB_QUEUE_SUCCESS + JOB_QUEUE_FAILED) + + +const char * job_status_get_name(job_status_type status); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/job_queue/local_driver.hpp b/libres/lib/include/ert/job_queue/local_driver.hpp new file mode 100644 index 00000000000..d6be286229a --- /dev/null +++ b/libres/lib/include/ert/job_queue/local_driver.hpp @@ -0,0 +1,51 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'local_driver.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 ERT_LOCAL_DRIVER_H +#define ERT_LOCAL_DRIVER_H +#ifdef __cplusplus +extern "C" { +#endif + +#include + + typedef struct local_driver_struct local_driver_type; + + void * local_driver_alloc(); + + + void * local_driver_submit_job(void * __driver , + const char * submit_cmd , + int num_cpu , + const char * run_path , + const char * job_name , + int argc, + const char ** argv ); + void local_driver_kill_job(void * __driver , void * __job); + void local_driver_free__(void * __driver ); + job_status_type local_driver_get_job_status(void * __driver , void * __job); + void local_driver_free_job(void * __job); + void local_driver_init_option_list(stringlist_type * option_list); + + + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/job_queue/lsb.hpp b/libres/lib/include/ert/job_queue/lsb.hpp new file mode 100644 index 00000000000..84338b0e9c6 --- /dev/null +++ b/libres/lib/include/ert/job_queue/lsb.hpp @@ -0,0 +1,56 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'lsb.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 ERT_LSB_H +#define ERT_LSB_H + + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +#include + + typedef struct lsb_struct lsb_type; + + + lsb_type * lsb_alloc(); + void lsb_free( lsb_type * lsb); + bool lsb_ready( const lsb_type * lsb); + + int lsb_initialize( const lsb_type * lsb); + int lsb_submitjob( const lsb_type * lsb , struct submit * , struct submitReply *); + int lsb_killjob( const lsb_type * lsb , int lsf_jobnr); + int lsb_openjob( const lsb_type * lsb , int lsf_jobnr); + struct jobInfoEnt * lsb_readjob( const lsb_type * lsb ); + int lsb_closejob( const lsb_type * lsb ); + char * lsb_sys_msg( const lsb_type * lsb ); + stringlist_type * lsb_get_error_list( const lsb_type * lsb ); + + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libres/lib/include/ert/job_queue/lsf_driver.hpp b/libres/lib/include/ert/job_queue/lsf_driver.hpp new file mode 100644 index 00000000000..d5389c3b937 --- /dev/null +++ b/libres/lib/include/ert/job_queue/lsf_driver.hpp @@ -0,0 +1,108 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'lsf_driver.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 ERT_LSF_DRIVER_H +#define ERT_LSF_DRIVER_H +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + The options supported by the LSF driver. +*/ +#define LSF_QUEUE "LSF_QUEUE" +#define LSF_RESOURCE "LSF_RESOURCE" +#define LSF_SERVER "LSF_SERVER" +#define LSF_RSH_CMD "LSF_RSH_CMD" // This option is set to DEFAULT_RSH_CMD at driver creation. +#define LSF_LOGIN_SHELL "LSF_LOGIN_SHELL" // Not fully implemented yet +#define LSF_BSUB_CMD "BSUB_CMD" +#define LSF_BJOBS_CMD "BJOBS_CMD" +#define LSF_BKILL_CMD "BKILL_CMD" +#define LSF_BHIST_CMD "BHIST_CMD" +#define LSF_BJOBS_TIMEOUT "BJOBS_TIMEOUT" +#define LSF_DEBUG_OUTPUT "DEBUG_OUTPUT" +#define LSF_SUBMIT_SLEEP "SUBMIT_SLEEP" +#define LSF_EXCLUDE_HOST "EXCLUDE_HOST" +#define LSF_PROJECT_CODE "PROJECT_CODE" + +#define LOCAL_LSF_SERVER "LOCAL" +#define NULL_LSF_SERVER "NULL" +#define DEFAULT_SUBMIT_SLEEP "0" + + typedef enum { + LSF_SUBMIT_INVALID = 0, + LSF_SUBMIT_INTERNAL = 1, + LSF_SUBMIT_LOCAL_SHELL = 2, + LSF_SUBMIT_REMOTE_SHELL = 3 + } lsf_submit_method_enum; + + +typedef struct lsf_driver_struct lsf_driver_type; +typedef struct lsf_job_struct lsf_job_type; + + void lsf_job_free(lsf_job_type * job); + long lsf_job_get_jobnr( const lsf_job_type * job ); + + void * lsf_driver_alloc( ); + stringlist_type * lsf_driver_alloc_cmd(lsf_driver_type * driver , + const char * run_path , + const char * job_name , + const char * submit_cmd , + int num_cpu , + int job_argc, + const char ** job_argv); + + void * lsf_driver_submit_job(void * __driver , + const char * submit_cmd , + int num_cpu , + const char * run_path , + const char * job_name , + int argc, + const char ** argv ); + job_status_type lsf_driver_convert_status( int lsf_status ); + void lsf_driver_blacklist_node(void * __driver , void * __job ); + void lsf_driver_kill_job(void * __driver , void * __job ); + void lsf_driver_free__(void * __driver ); + void lsf_driver_free( lsf_driver_type * driver ); + job_status_type lsf_driver_get_job_status(void * __driver , void * __job); + int lsf_driver_get_job_status_lsf(void * __driver , void * __job); + void lsf_driver_free_job(void * __job); + void lsf_driver_set_bjobs_refresh_interval( lsf_driver_type * driver , int refresh_interval); + + void lsf_driver_add_exclude_hosts( lsf_driver_type * driver , const char * excluded); + lsf_submit_method_enum lsf_driver_get_submit_method( const lsf_driver_type * driver ); + + const void * lsf_driver_get_option( const void * __driver , const char * option_key); + bool lsf_driver_set_option( void * __driver , const char * option_key , const void * value); + bool lsf_driver_has_project_code( const lsf_driver_type * driver ); + void lsf_driver_init_option_list(stringlist_type * option_list); + int lsf_job_parse_bsub_stdout(const char * bsub_cmd, const char * stdout_file); + char * lsf_job_write_bjobs_to_file(const char * bjobs_cmd, lsf_driver_type * driver, const long jobid); + + stringlist_type * lsf_job_alloc_parse_hostnames(const char* fname); + UTIL_SAFE_CAST_HEADER( lsf_driver ); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/job_queue/lsf_job_stat.hpp b/libres/lib/include/ert/job_queue/lsf_job_stat.hpp new file mode 100644 index 00000000000..ab9543c3974 --- /dev/null +++ b/libres/lib/include/ert/job_queue/lsf_job_stat.hpp @@ -0,0 +1,32 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'lsf_driver.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. +*/ + +#ifdef HAVE_LSF_LIBRARY +#include +#else +#define JOB_STAT_NULL 0 +#define JOB_STAT_PEND 1 +#define JOB_STAT_SSUSP 0x08 +#define JOB_STAT_USUSP 0x10 +#define JOB_STAT_PSUSP 0x02 +#define JOB_STAT_RUN 0x04 +#define JOB_STAT_EXIT 0x20 +#define JOB_STAT_DONE 0x40 +#define JOB_STAT_PDONE 0x80 +#define JOB_STAT_UNKWN 0x10000 +#endif diff --git a/libres/lib/include/ert/job_queue/queue_driver.hpp b/libres/lib/include/ert/job_queue/queue_driver.hpp new file mode 100644 index 00000000000..2ccd7dcd096 --- /dev/null +++ b/libres/lib/include/ert/job_queue/queue_driver.hpp @@ -0,0 +1,97 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'queue_driver.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 ERT_QUEUE_DRIVER_H +#define ERT_QUEUE_DRIVER_H +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + + typedef enum { + NULL_DRIVER = 0, + LSF_DRIVER = 1, + LOCAL_DRIVER = 2, + RSH_DRIVER = 3, + TORQUE_DRIVER = 4, + SLURM_DRIVER = 5 + } job_driver_type; + +#define JOB_DRIVER_ENUM_SIZE 5 + + /* + The options supported by the base queue_driver. + */ +#define MAX_RUNNING "MAX_RUNNING" + + + typedef struct queue_driver_struct queue_driver_type; + + typedef void * (submit_job_ftype) (void * data, const char * cmd, int num_cpu, const char * run_path, const char * job_name, int argc, const char ** argv); + typedef void (blacklist_node_ftype) (void *, void *); + typedef void (kill_job_ftype) (void *, void *); + typedef job_status_type(get_status_ftype) (void *, void *); + typedef void (free_job_ftype) (void *); + typedef void (free_queue_driver_ftype) (void *); + typedef bool (set_option_ftype) (void *, const char*, const void *); + typedef const void * (get_option_ftype) (const void *, const char *); + typedef void (init_option_list_ftype) (stringlist_type *); + + + queue_driver_type * queue_driver_alloc_RSH(const char * rsh_cmd, const hash_type * rsh_hostlist); + queue_driver_type * queue_driver_alloc_LSF(const char * queue_name, const char * resource_request, const char * remote_lsf_server); + queue_driver_type * queue_driver_alloc_TORQUE(); + queue_driver_type * queue_driver_alloc_local(); + queue_driver_type * queue_driver_alloc_slurm(); + queue_driver_type * queue_driver_alloc(job_driver_type type); + + void * queue_driver_submit_job(queue_driver_type * driver, const char * run_cmd, int num_cpu, const char * run_path, const char * job_name, int argc, const char ** argv); + void queue_driver_free_job(queue_driver_type * driver, void * job_data); + void queue_driver_blacklist_node(queue_driver_type * driver, void * job_data); + void queue_driver_kill_job(queue_driver_type * driver, void * job_data); + job_status_type queue_driver_get_status(queue_driver_type * driver, void * job_data); + + PY_USED const char * queue_driver_get_name(const queue_driver_type * driver); + + bool queue_driver_set_option(queue_driver_type * driver, const char * option_key, const void * value); + bool queue_driver_unset_option(queue_driver_type * driver, const char * option_key); + const void * queue_driver_get_option(queue_driver_type * driver, const char * option_key); + void queue_driver_init_option_list(queue_driver_type * driver, stringlist_type * option_list); + + void queue_driver_free(queue_driver_type * driver); + void queue_driver_free__(void * driver); + + void queue_driver_set_max_running(queue_driver_type * driver, int max_running); + PY_USED int queue_driver_get_max_running(const queue_driver_type * driver); + + typedef enum {SUBMIT_OK = 0 , + SUBMIT_JOB_FAIL = 1 , /* Typically no more attempts. */ + SUBMIT_DRIVER_FAIL = 2 , /* The driver would not take the job - for whatever reason?? */ + SUBMIT_QUEUE_CLOSED = 3 } /* The queue is currently not accepting more jobs - either (temporarilty) + because of pause or it is going down. */ submit_status_type; + + UTIL_IS_INSTANCE_HEADER( queue_driver ); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/job_queue/rsh_driver.hpp b/libres/lib/include/ert/job_queue/rsh_driver.hpp new file mode 100644 index 00000000000..526fa67e518 --- /dev/null +++ b/libres/lib/include/ert/job_queue/rsh_driver.hpp @@ -0,0 +1,57 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'rsh_driver.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 ERT_RSH_DRIVER_H +#define ERT_RSH_DRIVER_H +#ifdef __cplusplus +extern "C" { +#endif +#include + +#define RSH_HOST "RSH_HOST" +#define RSH_HOSTLIST "RSH_HOSTLIST" +#define RSH_CMD "RSH_CMD" +#define RSH_CLEAR_HOSTLIST "RSH_CLEAR_HOSTLIST" + + typedef struct rsh_driver_struct rsh_driver_type; + typedef struct rsh_job_struct rsh_job_type; + + void rsh_driver_add_host(rsh_driver_type * , const char * , int ); + void * rsh_driver_alloc( ); + + void * rsh_driver_submit_job(void * __driver , + const char * submit_cmd , + int num_cpu , + const char * run_path , + const char * job_name , + int argc, + const char ** argv ); + void rsh_driver_kill_job(void * __driver , void * __job); + void rsh_driver_free__(void * __driver ); + job_status_type rsh_driver_get_job_status(void * __driver , void * __job); + void rsh_driver_free_job(void * __job); + + + bool rsh_driver_set_option( void * __driver, const char * option_key , const void * value ); + const void * rsh_driver_get_option( const void * __driver , const char * option_key); + void rsh_driver_init_option_list(stringlist_type * option_list); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/job_queue/slurm_driver.hpp b/libres/lib/include/ert/job_queue/slurm_driver.hpp new file mode 100644 index 00000000000..6bb1a881266 --- /dev/null +++ b/libres/lib/include/ert/job_queue/slurm_driver.hpp @@ -0,0 +1,58 @@ +/* + Copyright (C) 2020 Equinor ASA, Norway. + + The file 'slurm_driver.hpp' 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 ERT_SLURM_DRIVER_H +#define ERT_SLURM_DRIVER_H + + +#include +#include + +#include +#include +#ifdef __cplusplus +extern "C" { +#endif + +/* + The options supported by the Slurm driver; these string constants will be used + in the user configuration file - i.e. they are very much part of API and + remain stable. +*/ + + +typedef struct slurm_driver_struct slurm_driver_type; +typedef struct slurm_job_struct slurm_job_type; + + void * slurm_driver_alloc(); + void slurm_driver_free(slurm_driver_type * driver); + void slurm_driver_free__(void * __driver ); + const void * slurm_driver_get_option( const void * __driver, const char * option_key); + bool slurm_driver_set_option( void * __driver, const char * option_key, const void * value); + void slurm_driver_init_option_list(stringlist_type * option_list); + void * slurm_driver_submit_job( void * __driver, const char * cmd, int num_cpu, const char * run_path, const char * job_name, int argc, const char ** argv); + job_status_type slurm_driver_get_job_status(void * __driver , void * __job); + void slurm_driver_kill_job(void * __driver , void * __job ); + void slurm_driver_free_job(void * __job); + + UTIL_SAFE_CAST_HEADER( slurm_driver ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/job_queue/torque_driver.hpp b/libres/lib/include/ert/job_queue/torque_driver.hpp new file mode 100644 index 00000000000..ab4d21759b0 --- /dev/null +++ b/libres/lib/include/ert/job_queue/torque_driver.hpp @@ -0,0 +1,88 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'torque_driver.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 TORQUE_DRIVER_H +#define TORQUE_DRIVER_H + +#ifdef __cplusplus +extern "C" { +#endif +#include + +#include +#include + + /* + The options supported by the Torque driver. + */ +#define TORQUE_QSUB_CMD "QSUB_CMD" +#define TORQUE_QSTAT_CMD "QSTAT_CMD" +#define TORQUE_QDEL_CMD "QDEL_CMD" +#define TORQUE_QUEUE "QUEUE" +#define TORQUE_NUM_CPUS_PER_NODE "NUM_CPUS_PER_NODE" +#define TORQUE_NUM_NODES "NUM_NODES" +#define TORQUE_KEEP_QSUB_OUTPUT "KEEP_QSUB_OUTPUT" +#define TORQUE_CLUSTER_LABEL "CLUSTER_LABEL" +#define TORQUE_JOB_PREFIX_KEY "JOB_PREFIX" +#define TORQUE_SUBMIT_SLEEP "SUBMIT_SLEEP" +#define TORQUE_DEBUG_OUTPUT "DEBUG_OUTPUT" + +#define TORQUE_DEFAULT_QSUB_CMD "qsub" +#define TORQUE_DEFAULT_QSTAT_CMD "qstat" +#define TORQUE_DEFAULT_QDEL_CMD "qdel" +#define TORQUE_DEFAULT_SUBMIT_SLEEP "0" + + + typedef struct torque_driver_struct torque_driver_type; + typedef struct torque_job_struct torque_job_type; + + + void * torque_driver_alloc(); + + + void * torque_driver_submit_job(void * __driver, + const char * submit_cmd, + int num_cpu, + const char * run_path, + const char * job_name, + int argc, + const char ** argv); + + void torque_driver_kill_job(void * __driver, void * __job); + void torque_driver_free__(void * __driver); + void torque_driver_free(torque_driver_type * driver); + job_status_type torque_driver_get_job_status(void * __driver, void * __job); + void torque_driver_free_job(void * __job); + void torque_driver_set_qstat_refresh_interval(torque_driver_type * driver, int refresh_interval); + + const void * torque_driver_get_option(const void * __driver, const char * option_key); + bool torque_driver_set_option(void * __driver, const char * option_key, const void * value); + void torque_driver_init_option_list(stringlist_type * option_list); + + void torque_job_create_submit_script(const char * run_path, const char * submit_cmd, int argc, const char ** job_argv); + int torque_driver_get_submit_sleep( const torque_driver_type * driver ); + FILE * torque_driver_get_debug_stream( const torque_driver_type * driver ); + job_status_type torque_driver_parse_status(const char * qstat_file, const char * jobnr); + + UTIL_SAFE_CAST_HEADER(torque_driver); + +#ifdef __cplusplus +} +#endif + +#endif /* TORQUE_DRIVER_H */ + diff --git a/libres/lib/include/ert/job_queue/workflow.hpp b/libres/lib/include/ert/job_queue/workflow.hpp new file mode 100644 index 00000000000..907179c2341 --- /dev/null +++ b/libres/lib/include/ert/job_queue/workflow.hpp @@ -0,0 +1,57 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'workflow.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 ERT_WORKFLOW_H +#define ERT_WORKFLOW_H + + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +#include +#include + + typedef struct workflow_struct workflow_type; + + const config_error_type * workflow_get_last_error( const workflow_type * workflow); + workflow_type * workflow_alloc( const char * src_file , workflow_joblist_type * joblist); + bool workflow_run( workflow_type * workflow, void * self , bool verbose , const subst_list_type * context); + void workflow_free( workflow_type * workflow ); + void workflow_free__( void * arg ); + + int workflow_get_stack_size( const workflow_type * workflow ); + void * workflow_iget_stack_ptr( const workflow_type * workflow , int index); + void * workflow_pop_stack( workflow_type * workflow ); + + int workflow_size( const workflow_type * workflow); + const workflow_job_type * workflow_iget_job( const workflow_type * workflow, int index); + stringlist_type * workflow_iget_arguments( const workflow_type * workflow, int index); + bool workflow_try_compile( workflow_type * script , const subst_list_type * context); + + UTIL_IS_INSTANCE_HEADER( workflow ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libres/lib/include/ert/job_queue/workflow_job.hpp b/libres/lib/include/ert/job_queue/workflow_job.hpp new file mode 100644 index 00000000000..44706f3d06b --- /dev/null +++ b/libres/lib/include/ert/job_queue/workflow_job.hpp @@ -0,0 +1,64 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'workflow_job.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 ERT_WORKFLOW_JOB_H +#define ERT_WORKFLOW_JOB_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + + + typedef void * (workflow_job_ftype) (void * self , const stringlist_type * arg ); + typedef struct workflow_job_struct workflow_job_type; + + const char * workflow_job_get_name( const workflow_job_type * workflow_job ); + PY_USED bool workflow_job_internal( const workflow_job_type * workflow_job ); + config_parser_type * workflow_job_alloc_config(); + workflow_job_type * workflow_job_alloc(const char * name , bool internal); + void workflow_job_free( workflow_job_type * workflow_job ); + void workflow_job_free__( void * arg); + void workflow_job_set_executable( workflow_job_type * workflow_job , const char * executable ); + workflow_job_type * workflow_job_config_alloc( const char * name , config_parser_type * config , const char * config_file); + + void workflow_job_update_config_compiler( const workflow_job_type * workflow_job , config_parser_type * config_compiler ); + void workflow_job_set_executable( workflow_job_type * workflow_job , const char * executable); + PY_USED char * workflow_job_get_executable( workflow_job_type * workflow_job); + + void workflow_job_set_internal_script( workflow_job_type * workflow_job , const char * script_path); + PY_USED char* workflow_job_get_internal_script_path( const workflow_job_type * workflow_job); + bool workflow_job_is_internal_script( const workflow_job_type * workflow_job); + + void workflow_job_set_function( workflow_job_type * workflow_job , const char * function); + PY_USED char * workflow_job_get_function( workflow_job_type * workflow_job); + void workflow_job_set_module( workflow_job_type * workflow_job , const char * module); + PY_USED char * workflow_job_get_module( workflow_job_type * workflow_job); + void * workflow_job_run( const workflow_job_type * job, void * self , bool verbose , const stringlist_type * arg); + + PY_USED int workflow_job_get_min_arg( const workflow_job_type * workflow_job ); + PY_USED int workflow_job_get_max_arg( const workflow_job_type * workflow_job ); + PY_USED config_item_types workflow_job_iget_argtype( const workflow_job_type * workflow_job, int index); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libres/lib/include/ert/job_queue/workflow_joblist.hpp b/libres/lib/include/ert/job_queue/workflow_joblist.hpp new file mode 100644 index 00000000000..ca18f74ee65 --- /dev/null +++ b/libres/lib/include/ert/job_queue/workflow_joblist.hpp @@ -0,0 +1,45 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'workflow_joblist.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 ERT_WORKFLOW_JOBLIST_H +#define ERT_WORKFLOW_JOBLIST_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct workflow_joblist_struct workflow_joblist_type; + + workflow_joblist_type * workflow_joblist_alloc(); + void workflow_joblist_free( workflow_joblist_type * joblist); + const workflow_job_type * workflow_joblist_get_job( const workflow_joblist_type * joblist , const char * job_name); + void workflow_joblist_add_job( workflow_joblist_type * joblist , const workflow_job_type * job); + bool workflow_joblist_add_job_from_file( workflow_joblist_type * joblist , const char * job_name , const char * config_file ); + config_parser_type * workflow_joblist_get_compiler( const workflow_joblist_type * joblist ); + bool workflow_joblist_has_job( const workflow_joblist_type * joblist , const char * job_name); + stringlist_type * workflow_joblist_get_job_names(const workflow_joblist_type * joblist); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/res_util/arg_pack.h b/libres/lib/include/ert/res_util/arg_pack.h new file mode 100644 index 00000000000..e2bcd83f1f6 --- /dev/null +++ b/libres/lib/include/ert/res_util/arg_pack.h @@ -0,0 +1,9 @@ +/* + Warning: The libecl code has changed to be compiled as a C++ project. This + header file is retained for a period for compatibility, but you are encouraged + to switch to include the new hpp header directly in your code. +*/ + +#include + + diff --git a/libres/lib/include/ert/res_util/arg_pack.hpp b/libres/lib/include/ert/res_util/arg_pack.hpp new file mode 100644 index 00000000000..f3a2afcae57 --- /dev/null +++ b/libres/lib/include/ert/res_util/arg_pack.hpp @@ -0,0 +1,87 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'arg_pack.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 ERT_ARG_PACK_H +#define ERT_ARG_PACK_H +#ifdef __cplusplus +extern "C" { +#endif +#include +#include + +#include +#include +#include + +typedef struct arg_pack_struct arg_pack_type; +typedef void (arg_node_free_ftype) (void *); +typedef void * (arg_node_copyc_ftype) (const void *); + + arg_pack_type * arg_pack_alloc(); + UTIL_SAFE_CAST_HEADER( arg_pack ); + UTIL_SAFE_CAST_HEADER_CONST( arg_pack ); + UTIL_IS_INSTANCE_HEADER( arg_pack ); + + void arg_pack_free(arg_pack_type * ); + void arg_pack_clear(arg_pack_type *); + + void arg_pack_append_ptr(arg_pack_type * , void *); + void arg_pack_append_const_ptr(arg_pack_type * , const void *); + void arg_pack_append_owned_ptr(arg_pack_type * , void * , arg_node_free_ftype *); + + const void * arg_pack_iget_const_ptr( const arg_pack_type * arg_pack , int index); + void * arg_pack_iget_ptr(const arg_pack_type * , int); + void * arg_pack_iget_adress(const arg_pack_type * , int); + + int arg_pack_size( const arg_pack_type * arg_pack ); + + /*****************************************************************/ + +#define APPEND_TYPED_HEADER(type) void arg_pack_append_ ## type (arg_pack_type * , type); +#define IGET_TYPED_HEADER(type) type arg_pack_iget_ ## type( const arg_pack_type * , int ); +#define ISET_TYPED_HEADER(type) void arg_pack_iset_ ## type( arg_pack_type * , int , type value); + +APPEND_TYPED_HEADER(int) +APPEND_TYPED_HEADER(bool) +APPEND_TYPED_HEADER(char) +APPEND_TYPED_HEADER(float) +APPEND_TYPED_HEADER(double) +APPEND_TYPED_HEADER(size_t) + +IGET_TYPED_HEADER(int) +IGET_TYPED_HEADER(bool) +IGET_TYPED_HEADER(char) +IGET_TYPED_HEADER(float) +IGET_TYPED_HEADER(double) +IGET_TYPED_HEADER(size_t) + +ISET_TYPED_HEADER(int) +ISET_TYPED_HEADER(bool) +ISET_TYPED_HEADER(char) +ISET_TYPED_HEADER(float) +ISET_TYPED_HEADER(double) +ISET_TYPED_HEADER(size_t) + +#undef APPEND_TYPED_HEADER +#undef GET_TYPED_HEADER + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/res_util/block_fs.hpp b/libres/lib/include/ert/res_util/block_fs.hpp new file mode 100644 index 00000000000..d3220d628f6 --- /dev/null +++ b/libres/lib/include/ert/res_util/block_fs.hpp @@ -0,0 +1,61 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'block_fs.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 ERT_BLOCK_FS +#define ERT_BLOCK_FS +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct block_fs_struct block_fs_type; + typedef struct user_file_node_struct user_file_node_type; + + typedef enum { + NO_SORT = 0, + STRING_SORT = 1, + OFFSET_SORT = 2 + } block_fs_sort_type; + + void block_fs_fsync( block_fs_type * block_fs ); + bool block_fs_is_readonly( const block_fs_type * block_fs); + block_fs_type * block_fs_mount( const char * mount_file , + int block_size , + int max_cache_size , + float fragmentation_limit , + int fsync_interval , + bool preload , + bool read_only, + bool use_lockfile); + void block_fs_close( block_fs_type * block_fs , bool unlink_empty); + void block_fs_fwrite_file(block_fs_type * block_fs , const char * filename , const void * ptr , size_t byte_size); + void block_fs_fwrite_buffer(block_fs_type * block_fs , const char * filename , const buffer_type * buffer); + void block_fs_fread_realloc_buffer( block_fs_type * block_fs , const char * filename , buffer_type * buffer); + void block_fs_unlink_file( block_fs_type * block_fs , const char * filename); + bool block_fs_has_file( block_fs_type * block_fs , const char * filename); + vector_type * block_fs_alloc_filelist( block_fs_type * block_fs , const char * pattern , block_fs_sort_type sort_mode , bool include_free_nodes ); + +UTIL_IS_INSTANCE_HEADER( block_fs ); +UTIL_SAFE_CAST_HEADER( block_fs ); +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/res_util/es_testdata.hpp b/libres/lib/include/ert/res_util/es_testdata.hpp new file mode 100644 index 00000000000..e6c331955c3 --- /dev/null +++ b/libres/lib/include/ert/res_util/es_testdata.hpp @@ -0,0 +1,58 @@ +/* + Copyright (C) 2019 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. +*/ + + +#ifndef ES_TESTDATA_HPP +#define ES_TESTDATA_HPP + +#include + +#include + +#include + +namespace res { +class es_testdata { +public: + std::string path; + + matrix_type * S; + matrix_type * E; + matrix_type * R; + matrix_type * D; + matrix_type * dObs; + int active_obs_size; + int active_ens_size; + bool_vector_type * obs_mask; + bool_vector_type * ens_mask; + int state_size; + + es_testdata(const matrix_type* S, const matrix_type * R, const matrix_type * dObs, const matrix_type *D , const matrix_type * E); + es_testdata(const char * path); + ~es_testdata(); + + matrix_type * alloc_matrix(const std::string& name, int rows, int columns) const; + void save_matrix(const std::string& name, const matrix_type * m) const; + matrix_type * alloc_state(const std::string& name) const; + void save(const std::string& path) const; + void deactivate_obs(int iobs); + void deactivate_realization(int iens); +}; + +} + +#endif diff --git a/libres/lib/include/ert/res_util/log.hpp b/libres/lib/include/ert/res_util/log.hpp new file mode 100644 index 00000000000..2ff451e6f43 --- /dev/null +++ b/libres/lib/include/ert/res_util/log.hpp @@ -0,0 +1,71 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file '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 ERT_LOG_H +#define ERT_LOG_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +//Same as pythons default log levels, but with different numeric values. +typedef enum { + // A serious error, indicating that the program itself may be unable to + // continue running. + LOG_CRITICAL = 50, + + // Due to a more serious problem, the software has not been able to perform + // some function. + LOG_ERROR = 40, + + // An indication that something unexpected happened, or indicative of some + // problem in the near future (e.g. "disk space low"). The software is still + // working as expected. + LOG_WARNING = 30, + + // Confirmation that things are working as expected. + LOG_INFO = 20, + + // Detailed information, typically of interest only when diagnosing problems. + LOG_DEBUG = 10 +} message_level_type; + + +typedef struct log_struct log_type; + log_type * log_open_file(const char *filename, message_level_type log_level); + log_type * log_open_stream(FILE * stream, message_level_type log_level); + + void log_add_message_stream(FILE * stream, bool add_timestamp, message_level_type message_level, const char * message); + void log_add_message(log_type *logh, message_level_type message_level, const char* message); + PY_USED void log_set_level( log_type * logh , message_level_type new_level); + void log_close( log_type * logh ); + void log_sync(log_type * logh); + const char * log_get_filename( const log_type * logh ); + int log_get_msg_count(const log_type * logh); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libres/lib/include/ert/res_util/matrix.hpp b/libres/lib/include/ert/res_util/matrix.hpp new file mode 100644 index 00000000000..523f256562a --- /dev/null +++ b/libres/lib/include/ert/res_util/matrix.hpp @@ -0,0 +1,182 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'matrix.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 ERT_MATRIX_H +#define ERT_MATRIX_H +#include +#include +#include + +#include +#include +#include +#include + +#ifdef HAVE_THREAD_POOL +#include +#endif + +#ifdef _MSC_VER +#define __forceinline inline +#elif __GNUC__ +/* Also clang defines the __GNUC__ symbol */ +#define __forceinline inline __attribute__((always_inline)) +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +struct matrix_struct { + UTIL_TYPE_ID_DECLARATION; + char * name; /* A name of the matrix - for printing - can be NULL. */ + double * data; /* The actual storage */ + bool data_owner; /* is this matrix instance the owner of data? */ + size_t data_size; /* What is the length of data (number of double values). */ + + int rows; /* The number of rows in the matrix. */ + int columns; /* The number of columns in the matrix. */ + int alloc_rows; + int alloc_columns; + int row_stride; /* The distance in data between two conscutive row values. */ + int column_stride; /* The distance in data between to consecutive column values. */ + + /* + Observe that the stride is considered an internal property - if + the matrix is stored to disk and then recovered the strides might + change, and also matrix_alloc_copy() will not respect strides. + */ +}; + +typedef struct matrix_struct matrix_type; + + __forceinline size_t GET_INDEX( const matrix_type * m , size_t i , size_t j) {return m->row_stride *i + m->column_stride *j;} + matrix_type * matrix_fread_alloc(FILE * stream); + void matrix_fread(matrix_type * matrix , FILE * stream); + void matrix_fwrite(const matrix_type * matrix , FILE * stream); + bool matrix_check_dims( const matrix_type * m , int rows , int columns); + void matrix_fscanf_data( matrix_type * matrix , bool row_major_order , FILE * stream ); + void matrix_fprintf_data( const matrix_type * matrix , bool row_major_order, FILE * stream ); + void matrix_fprintf( const matrix_type * matrix , const char * fmt , FILE * stream ); + void matrix_dump_csv( const matrix_type * matrix ,const char * filename); + void matrix_pretty_fprint(const matrix_type * matrix , const char * name , const char * fmt , FILE * stream); + void matrix_pretty_fprint_submat(const matrix_type * matrix , const char * name , const char * fmt , FILE * stream, int m, int M, int n, int N); + matrix_type * matrix_alloc(int rows, int columns); + matrix_type * matrix_alloc_identity(int dim); + bool matrix_resize(matrix_type * matrix , int rows , int columns , bool copy_content); + matrix_type * matrix_alloc_sub_copy( const matrix_type * src , int row_offset , int column_offset , int rows, int columns); + matrix_type * matrix_alloc_copy(const matrix_type * src); + matrix_type * matrix_alloc_column_compressed_copy(const matrix_type * src, const bool_vector_type * mask); + void matrix_column_compressed_memcpy(matrix_type * target, const matrix_type * src, const bool_vector_type * mask); + matrix_type * matrix_realloc_copy(matrix_type * T , const matrix_type * src); + void matrix_set_row(matrix_type * matrix , const double * data , int row); + + matrix_type * matrix_alloc_shared(const matrix_type * src , int row , int column , int rows , int columns); + void matrix_free(matrix_type * matrix); + void matrix_safe_free( matrix_type * matrix ); + void matrix_pretty_print(const matrix_type * matrix , const char * name , const char * fmt); + void matrix_set(matrix_type * matrix, double value); + void matrix_set_name( matrix_type * matrix , const char * name); + void matrix_scale(matrix_type * matrix, double value); + void matrix_shift(matrix_type * matrix, double value); + + void matrix_assign(matrix_type * A , const matrix_type * B); + void matrix_inplace_add(matrix_type * A , const matrix_type * B); + void matrix_inplace_sub(matrix_type * A , const matrix_type * B); + void matrix_sub(matrix_type * A , const matrix_type * B , const matrix_type * C); + void matrix_transpose(const matrix_type * A , matrix_type * T); + void matrix_inplace_add_column(matrix_type * A , const matrix_type * B, int colA , int colB); + void matrix_inplace_sub_column(matrix_type * A , const matrix_type * B, int colA , int colB); + void matrix_inplace_transpose(matrix_type * A ); + + void matrix_iset_safe(matrix_type * matrix , int i , int j, double value); + void matrix_iset(matrix_type * matrix , int i , int j, double value); + double matrix_iget(const matrix_type * matrix , int i , int j); + double matrix_iget_safe(const matrix_type * matrix , int i , int j); + void matrix_iadd(matrix_type * matrix , int i , int j , double value); + void matrix_isub(matrix_type * matrix , int i , int j , double value); + void matrix_imul(matrix_type * matrix , int i , int j , double value); + + + void matrix_inplace_matmul(matrix_type * A, const matrix_type * B); + void matrix_inplace_matmul_mt1(matrix_type * A, const matrix_type * B , int num_threads); +#ifdef HAVE_THREAD_POOL + void matrix_inplace_matmul_mt2(matrix_type * A, const matrix_type * B , thread_pool_type * thread_pool); +#endif + + void matrix_shift_row(matrix_type * matrix , int row , double shift); + double matrix_get_column_sum(const matrix_type * matrix , int column); + double matrix_get_row_sum(const matrix_type * matrix , int column); + double matrix_get_column_sum2(const matrix_type * matrix , int column); + double matrix_get_column_abssum(const matrix_type * matrix , int column); + void matrix_subtract_row_mean(matrix_type * matrix); + void matrix_subtract_and_store_row_mean(matrix_type * matrix, matrix_type * row_mean); + void matrix_scale_column(matrix_type * matrix , int column , double scale_factor); + void matrix_scale_row(matrix_type * matrix , int row , double scale_factor); + void matrix_set_const_column(matrix_type * matrix , const double value , int column); + void matrix_copy_column(matrix_type * target_matrix, const matrix_type * src_matrix , int src_column, int target_column); + + double * matrix_get_data(const matrix_type * matrix); + + void matrix_set_column(matrix_type * matrix , const double * data , int column); + void matrix_set_many_on_column(matrix_type * matrix , int row_offset , int elements , const double * data , int column); + void matrix_shrink_header(matrix_type * matrix , int rows , int columns); + void matrix_full_size( matrix_type * matrix ); + int matrix_get_rows(const matrix_type * matrix); + int matrix_get_columns(const matrix_type * matrix); + int matrix_get_column_stride(const matrix_type * matrix); + void matrix_get_dims(const matrix_type * matrix , int * rows , int * columns , int * row_stride , int * column_stride); + bool matrix_is_quadratic(const matrix_type * matrix); + bool matrix_equal( const matrix_type * m1 , const matrix_type * m2); + bool matrix_similar( const matrix_type * m1 , const matrix_type * m2, double epsilon); + bool matrix_columns_equal( const matrix_type * m1 , int col1 , const matrix_type * m2 , int col2); + + void matrix_diag_set_scalar(matrix_type * matrix , double value); + void matrix_diag_set(matrix_type * matrix , const double * diag); + void matrix_random_init(matrix_type * matrix , rng_type * rng); + void matrix_delete_row(matrix_type * m1, int row); + void matrix_delete_column(matrix_type * m1, int row); + + void matrix_imul_col( matrix_type * matrix , int column , double factor); + double matrix_column_column_dot_product(const matrix_type * m1 , int col1 , const matrix_type * m2 , int col2); + double matrix_row_column_dot_product(const matrix_type * m1 , int row1 , const matrix_type * m2 , int col2); + matrix_type * matrix_alloc_transpose( const matrix_type * A); + void matrix_copy_row(matrix_type * target_matrix, const matrix_type * src_matrix , int target_row, int src_row); + void matrix_copy_block( matrix_type * target_matrix , int target_row , int target_column , int rows , int columns, + const matrix_type * src_matrix , int src_row , int src_column); + + void matrix_scalar_set( matrix_type * matrix , double value); + void matrix_inplace_diag_sqrt(matrix_type *Cd); + double matrix_trace(const matrix_type *matrix); + double matrix_diag_std(const matrix_type * Sk,double mean); + double matrix_det2( const matrix_type * A); + double matrix_det3( const matrix_type * A); + double matrix_det4( const matrix_type * A); + + #ifdef ERT_HAVE_ISFINITE + bool matrix_is_finite(const matrix_type * matrix); + void matrix_assert_finite( const matrix_type * matrix ); + #endif + + UTIL_SAFE_CAST_HEADER( matrix ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/res_util/matrix_blas.hpp b/libres/lib/include/ert/res_util/matrix_blas.hpp new file mode 100644 index 00000000000..fa63fbdaa41 --- /dev/null +++ b/libres/lib/include/ert/res_util/matrix_blas.hpp @@ -0,0 +1,43 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'matrix_blas.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 ERT_MATRIX_BLAS +#define ERT_MATRIX_BLAS +#include + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +void matrix_dgemm(matrix_type *C , const matrix_type *A , const matrix_type * B , bool transA, bool transB , double alpha , double beta); +void matrix_matmul_with_transpose(matrix_type * C, const matrix_type * A , const matrix_type * B , bool transA , bool transB); +void matrix_matmul(matrix_type * A, const matrix_type *B , const matrix_type * C); +matrix_type * matrix_alloc_matmul(const matrix_type * A, const matrix_type * B); +void matrix_dgemv(const matrix_type * A , const double * x , double * y , bool transA , double alpha , double beta); +void matrix_gram_set( const matrix_type * X , matrix_type * G, bool col); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libres/lib/include/ert/res_util/matrix_lapack.hpp b/libres/lib/include/ert/res_util/matrix_lapack.hpp new file mode 100644 index 00000000000..dbd2f783a2d --- /dev/null +++ b/libres/lib/include/ert/res_util/matrix_lapack.hpp @@ -0,0 +1,88 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'matrix_lapack.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 ERT_MATRIX_LAPACK_H +#define ERT_MATRIX_LAPACK_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + This enum is just a simple way to label the different ways the + singular vectors in U and VT are returned to the calling scope. The + low level lapack routine uses a character variable, indicated + below. +*/ + + + + + + typedef enum { + /* A */ DGESVD_ALL, /* Returns all the singular vectors in U/VT. */ + /* S */ DGESVD_MIN_RETURN, /* Return the first min(m,n) vectors in U/VT. */ + /* O */ DGESVD_MIN_OVERWRITE, /* Return the first min(m,n) vectors of U/VT by overwriteing in A. */ + /* N */ DGESVD_NONE} /* Do not compute any singular vectors for U/VT */ + dgesvd_vector_enum; + + + typedef enum { + /* A */ DSYEVX_ALL, /* Compute all the eigenvalues */ + /* V */ DSYEVX_VALUE_INTERVAL, /* Computes eigenvalues in half open interval + for more details. +*/ + + +#ifndef ERT_MATRIX_STAT_H +#define ERT_MATRIX_STAT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif //__cplusplus + +typedef enum { + LLSQ_SUCCESS = 0, + LLSQ_INVALID_DIM = 1, + LLSQ_UNDETERMINED = 2 +} llsq_result_enum; + + +llsq_result_enum matrix_stat_llsq_estimate( matrix_type * beta , const matrix_type * X , const matrix_type * Y , const matrix_type * S); +llsq_result_enum matrix_stat_polyfit( matrix_type * beta , const matrix_type * X0 , const matrix_type * Y0 , const matrix_type * S); + +#ifdef __cplusplus +} +#endif //__cplusplus + +#endif diff --git a/libres/lib/include/ert/res_util/path_fmt.hpp b/libres/lib/include/ert/res_util/path_fmt.hpp new file mode 100644 index 00000000000..02a4d482dbd --- /dev/null +++ b/libres/lib/include/ert/res_util/path_fmt.hpp @@ -0,0 +1,46 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'path_fmt.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 ERT_PATH_FMT_H +#define ERT_PATH_FMT_H + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct path_fmt_struct path_fmt_type; + + path_fmt_type * path_fmt_alloc_directory_fmt(const char * ); + path_fmt_type * path_fmt_alloc_path_fmt(const char * ); + char * path_fmt_alloc_path(const path_fmt_type * , bool , ...); + char * path_fmt_alloc_file(const path_fmt_type * , bool , ...); + void path_fmt_free(path_fmt_type * ); + void path_fmt_free__( void * arg ); + const char * path_fmt_get_fmt(const path_fmt_type * ); + void path_fmt_reset_fmt(path_fmt_type * , const char * ); + path_fmt_type * path_fmt_realloc_path_fmt( path_fmt_type * path_fmt, const char * fmt ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/res_util/regression.hpp b/libres/lib/include/ert/res_util/regression.hpp new file mode 100644 index 00000000000..fbe002db701 --- /dev/null +++ b/libres/lib/include/ert/res_util/regression.hpp @@ -0,0 +1,32 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'regression.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 ERT_REGRESSION_H +#define ERT_REGRESSION_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif +void regression_augmented_OLS(const matrix_type * X , const matrix_type * Y , const matrix_type *E, matrix_type * beta); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/res_util/res_env.hpp b/libres/lib/include/ert/res_util/res_env.hpp new file mode 100644 index 00000000000..b06b2090e96 --- /dev/null +++ b/libres/lib/include/ert/res_util/res_env.hpp @@ -0,0 +1,40 @@ +/* + Copyright (C) 2018 Equinor ASA, Norway. + + The file 'res_env.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 RESENV_H +#define RESENV_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + + char ** res_env_alloc_PATH_list(); + char * res_env_alloc_PATH_executable(const char * executable ); + void res_env_setenv( const char * variable , const char * value); + const char * res_env_interp_setenv( const char * variable , const char * value); + void res_env_unsetenv( const char * variable); + char * res_env_alloc_envvar( const char * value ); + char * res_env_isscanf_alloc_envvar( const char * string , int env_index ); + const char * res_env_update_path_var(const char * variable, const char * value, bool append); + +#ifdef __cplusplus +} +#endif +#endif // RESLOG_H diff --git a/libres/lib/include/ert/res_util/res_log.hpp b/libres/lib/include/ert/res_util/res_log.hpp new file mode 100644 index 00000000000..efa6beeee89 --- /dev/null +++ b/libres/lib/include/ert/res_util/res_log.hpp @@ -0,0 +1,53 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'res_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 RESLOG_H +#define RESLOG_H + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +bool res_log_init_log(message_level_type log_level,const char * log_file_name, bool verbose); +void res_log_close(); +const char * res_log_get_filename(); + +void res_log_add_message(message_level_type message_level, const char* message); + +void res_log_debug(const char* msg); +void res_log_info(const char* msg); +void res_log_warning(const char* msg); +void res_log_error(const char* msg); +void res_log_critical(const char* msg); + +void res_log_fdebug(const char * fmt, ...); +void res_log_finfo(const char * fmt, ...); +void res_log_fwarning(const char * fmt, ...); +void res_log_ferror(const char * fmt, ...); +void res_log_fcritical(const char * fmt, ...); + +#ifdef __cplusplus +} +#endif +#endif // RESLOG_H diff --git a/libres/lib/include/ert/res_util/res_portability.hpp b/libres/lib/include/ert/res_util/res_portability.hpp new file mode 100644 index 00000000000..340fa670503 --- /dev/null +++ b/libres/lib/include/ert/res_util/res_portability.hpp @@ -0,0 +1,31 @@ +/* + Copyright (C) 2018 Equinor ASA, Norway. + + The file 'res_portability.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 RES_PORTABILITY_H +#define RES_PORTABILITY_H + +#ifdef __cplusplus +extern "C" { +#endif + +void res_yield(); + +#ifdef __cplusplus +} +#endif +#endif // RESLOG_H diff --git a/libres/lib/include/ert/res_util/res_util_defaults.hpp b/libres/lib/include/ert/res_util/res_util_defaults.hpp new file mode 100644 index 00000000000..32457852046 --- /dev/null +++ b/libres/lib/include/ert/res_util/res_util_defaults.hpp @@ -0,0 +1,5 @@ +/** + This file contains various default values used in libres_util. +*/ + +#define DEFAULT_LOG_LEVEL LOG_WARNING diff --git a/libres/lib/include/ert/res_util/res_version.hpp b/libres/lib/include/ert/res_util/res_version.hpp new file mode 100644 index 00000000000..5ba6a3dfe09 --- /dev/null +++ b/libres/lib/include/ert/res_util/res_version.hpp @@ -0,0 +1,40 @@ +/* + Copyright (C) 2016 Equinor ASA, Norway. + + The file 'ecl_version.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 ECL_VERSION +#define ECL_VERSION + +#include +#include + +#ifdef __cplusplus +extern"C" { +#endif + +PY_USED int res_version_get_major_version(); +PY_USED int res_version_get_minor_version(); +PY_USED const char * res_version_get_micro_version(); +PY_USED const char * res_version_get_git_commit(); +PY_USED const char * res_version_get_build_time(); +PY_USED bool res_version_is_devel_version(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libres/lib/include/ert/res_util/subst_func.hpp b/libres/lib/include/ert/res_util/subst_func.hpp new file mode 100644 index 00000000000..633eab319f3 --- /dev/null +++ b/libres/lib/include/ert/res_util/subst_func.hpp @@ -0,0 +1,54 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'subst_func.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 ERT_SUBST_FUNC_H +#define ERT_SUBST_FUNC_H +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef char * (subst_func_ftype) (const stringlist_type * , void * ); +typedef struct subst_func_struct subst_func_type; +typedef struct subst_func_pool_struct subst_func_pool_type; + + + char * subst_func_eval( const subst_func_type * subst_func , const stringlist_type * args); + +/*****************************************************************/ + + subst_func_pool_type * subst_func_pool_alloc( ); + void subst_func_pool_free( subst_func_pool_type * pool ); + void subst_func_pool_add_func( subst_func_pool_type * pool , const char * func_name , const char * doc_string , subst_func_ftype * func , bool vararg, int argc_min , int argc_max , void * arg); +subst_func_type * subst_func_pool_get_func( const subst_func_pool_type * pool , const char * func_name ); +bool subst_func_pool_has_func( const subst_func_pool_type * pool , const char * func_name ); +UTIL_IS_INSTANCE_HEADER( subst_func_pool ); + +/*****************************************************************/ +char * subst_func_add( const stringlist_type * args , void * arg); +char * subst_func_mul( const stringlist_type * args , void * arg); +char * subst_func_exp( const stringlist_type * args , void * arg); +char * subst_func_log( const stringlist_type * args , void * arg); +char * subst_func_pow10( const stringlist_type * args , void * arg); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/res_util/subst_list.hpp b/libres/lib/include/ert/res_util/subst_list.hpp new file mode 100644 index 00000000000..2bfaeb11675 --- /dev/null +++ b/libres/lib/include/ert/res_util/subst_list.hpp @@ -0,0 +1,63 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'subst_list.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 ERT_SUBST_H +#define ERT_SUBST_H + +#ifdef __cplusplus +extern "C" { +#endif +#include +#include + +#include +#include + +#include + + typedef struct subst_list_struct subst_list_type; + bool subst_list_update_buffer( const subst_list_type * subst_list , buffer_type * buffer ); + void subst_list_insert_func(subst_list_type * subst_list , const char * func_name , const char * local_func_name); + void subst_list_fprintf(const subst_list_type * , FILE * stream); + void subst_list_set_parent( subst_list_type * subst_list , const subst_list_type * parent); + subst_list_type * subst_list_alloc( const void * input_arg ); + subst_list_type * subst_list_alloc_deep_copy(const subst_list_type * ); + void subst_list_free(subst_list_type *); + void subst_list_clear( subst_list_type * subst_list ); + void subst_list_append_copy(subst_list_type * , const char * , const char * , const char * doc_string); + void subst_list_append_owned_ref(subst_list_type * , const char * , const char * , const char * doc_string); + void subst_list_prepend_copy(subst_list_type * , const char * , const char * , const char * doc_string); + void subst_list_prepend_ref(subst_list_type * , const char * , const char * , const char * doc_string); + void subst_list_prepend_owned_ref(subst_list_type * , const char * , const char * , const char * doc_string); + + bool subst_list_filter_file(const subst_list_type * , const char * , const char * ); + bool subst_list_update_string(const subst_list_type * , char ** ); + char * subst_list_alloc_filtered_string(const subst_list_type * , const char * ); + int subst_list_get_size( const subst_list_type *); + const char * subst_list_get_value( const subst_list_type * subst_list , const char * key); + const char * subst_list_iget_value( const subst_list_type * subst_list , int index); + const char * subst_list_iget_key( const subst_list_type * subst_list , int index); + const char * subst_list_get_doc_string( const subst_list_type * subst_list , const char * key); + bool subst_list_has_key( const subst_list_type * subst_list , const char * key); + int subst_list_add_from_string( subst_list_type * subst_list , const char * arg_string, bool append); + + UTIL_IS_INSTANCE_HEADER( subst_list ); +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/res_util/template.hpp b/libres/lib/include/ert/res_util/template.hpp new file mode 100644 index 00000000000..c0acf65b2cf --- /dev/null +++ b/libres/lib/include/ert/res_util/template.hpp @@ -0,0 +1,51 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'template.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 ERT_UTIL_TEMPLATE_H +#define ERT_UTIL_TEMPLATE_H +#ifdef __cplusplus +extern "C" { +#endif + + +#include + +#include + +typedef struct template_struct template_type; + + +template_type * template_alloc( const char * template_file , bool internalize_template, subst_list_type * parent_subst); +void template_free( template_type * _template ); +void template_instantiate( const template_type * _template , const char * __target_file , const subst_list_type * arg_list , bool override_symlink); +void template_add_arg( template_type * _template , const char * key , const char * value ); +subst_list_type * template_get_args_list(template_type * _template); + +void template_clear_args( template_type * _template ); +int template_add_args_from_string( template_type * _template , const char * arg_string); +char * template_get_args_as_string( template_type * _template ); +void template_set_template_file( template_type * _template , const char * template_file); +const char * template_get_template_file( const template_type * _template ); + +//void template_eval_loops( const template_type * template_ , buffer_type * buffer ); + + +#endif +#ifdef __cplusplus +} +#endif diff --git a/libres/lib/include/ert/res_util/template_type.hpp b/libres/lib/include/ert/res_util/template_type.hpp new file mode 100644 index 00000000000..eaa2734e2d4 --- /dev/null +++ b/libres/lib/include/ert/res_util/template_type.hpp @@ -0,0 +1,43 @@ +#ifndef ERT_TEMPLATE_TYPE_H +#define ERT_TEMPLATE_TYPE_H + +#include + +#include + +#ifdef ERT_HAVE_REGEXP +#include +#endif //ERT_HAVE_REGEXP + +#define TEMPLATE_TYPE_ID 7781045 + +#ifdef __cplusplus +extern "C" { +#endif + +struct template_struct { + UTIL_TYPE_ID_DECLARATION; + char * template_file; /* The template file - if internalize_template == false this filename can contain keys which will be replaced at instantiation time. */ + char * template_buffer; /* The content of the template buffer; only has valid content if internalize_template == true. */ + bool internalize_template; /* Should the template be loadad and internalized at template_alloc(). */ + subst_list_type * arg_list; /* Key-value mapping established at alloc time. */ + char * arg_string; /* A string representation of the arguments - ONLY used for a _get_ function. */ + #ifdef ERT_HAVE_REGEXP + regex_t start_regexp; + regex_t end_regexp; + #endif +}; + +#ifdef ERT_HAVE_REGEXP +typedef struct loop_struct loop_type; +void template_init_loop_regexp( struct template_struct* ); +int template_eval_loop( const struct template_struct* , buffer_type * buffer , int global_offset , struct loop_struct * ); +void template_eval_loops( const struct template_struct* template_ , buffer_type * buffer ); +#endif //ERT_HAVE_REGEXP + +#endif //ERT_TEMPLATE_TYPE_H + + +#ifdef __cplusplus +} +#endif diff --git a/libres/lib/include/ert/res_util/thread_pool.hpp b/libres/lib/include/ert/res_util/thread_pool.hpp new file mode 100644 index 00000000000..e3311e9cc78 --- /dev/null +++ b/libres/lib/include/ert/res_util/thread_pool.hpp @@ -0,0 +1,42 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'thread_pool.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 ERT_THREAD_POOL_H +#define ERT_THREAD_POOL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + + typedef struct thread_pool_struct thread_pool_type; + + void thread_pool_join(thread_pool_type * ); + thread_pool_type * thread_pool_alloc(int , bool start_queue); + void thread_pool_add_job(thread_pool_type * ,void * (*) (void *) , void *); + void thread_pool_free(thread_pool_type *); + void thread_pool_restart( thread_pool_type * tp ); + int thread_pool_get_max_running( const thread_pool_type * pool ); + bool thread_pool_try_join(thread_pool_type * pool, int timeout_seconds); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/libres/lib/include/ert/res_util/ui_return.hpp b/libres/lib/include/ert/res_util/ui_return.hpp new file mode 100644 index 00000000000..c0d140bcd58 --- /dev/null +++ b/libres/lib/include/ert/res_util/ui_return.hpp @@ -0,0 +1,58 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'ui_return.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 ERT_UI_RETURN_H +#define ERT_UI_RETURN_H + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ui_return_struct ui_return_type; + + +typedef enum { + UI_RETURN_OK = 1, + UI_RETURN_FAIL = 2 +} ui_return_status_enum; + + + +ui_return_type * ui_return_alloc(ui_return_status_enum status); +void ui_return_free(ui_return_type * ui_return); +ui_return_status_enum ui_return_get_status(const ui_return_type * ui_return); +int ui_return_get_error_count(const ui_return_type * ui_return); +bool ui_return_add_error(ui_return_type * ui_return, const char * error_msg); +void ui_return_add_help(ui_return_type * ui_return, const char * help_text); +PY_USED const char * ui_return_get_first_error(const ui_return_type * ui_return); +const char * ui_return_get_last_error(const ui_return_type * ui_return); +PY_USED const char * ui_return_get_help(const ui_return_type * ui_return); +const char * ui_return_iget_error( const ui_return_type * ui_return , int index); + + +UTIL_IS_INSTANCE_HEADER(ui_return); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/res_util/util_printf.hpp b/libres/lib/include/ert/res_util/util_printf.hpp new file mode 100644 index 00000000000..e3087cefd1c --- /dev/null +++ b/libres/lib/include/ert/res_util/util_printf.hpp @@ -0,0 +1,23 @@ +#ifndef RES_UTIL_PRINTF + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum {left_pad = 0, + right_pad = 1, + center_pad = 2} string_alignement_type; + + void util_fprintf_string(const char * , int , string_alignement_type , FILE * ); + void util_fprintf_double(double value , int width , int decimals , char base_fmt , FILE * stream); + void util_fprintf_int(int value , int width , FILE * stream); + +#ifdef __cplusplus +} +#endif + + + +#endif diff --git a/libres/lib/include/ert/rms/rms_file.hpp b/libres/lib/include/ert/rms/rms_file.hpp new file mode 100644 index 00000000000..b1d2b1d3eb3 --- /dev/null +++ b/libres/lib/include/ert/rms/rms_file.hpp @@ -0,0 +1,53 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'rms_file.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 ERT_RMS_FILE_H +#define ERT_RMS_FILE_H +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include + +typedef struct rms_file_struct rms_file_type; + +void rms_file_fclose(rms_file_type *); +FILE * rms_file_fopen_r(rms_file_type *rms_file); +FILE * rms_file_fopen_w(rms_file_type *rms_file); +void rms_file_set_filename(rms_file_type * , const char * , bool); +rms_file_type * rms_file_alloc (const char *, bool ); +void rms_file_free (rms_file_type *); +void rms_file_free_data (rms_file_type *); +rms_tag_type * rms_file_get_dim_tag_ref(const rms_file_type * ); +rms_tag_type * rms_file_get_tag_ref (const rms_file_type *, const char *, const char *, const char * , bool); +rms_tag_type * rms_file_fread_alloc_tag(rms_file_type * , const char *, const char *, const char *); +rms_tagkey_type * rms_file_fread_alloc_data_tagkey(rms_file_type * , const char *, const char *, const char *); +void rms_file_complete_fwrite(const rms_file_type *); +void rms_file_init_fwrite(const rms_file_type * , const char *); +void rms_file_get_dims(const rms_file_type * , int * ); +FILE * rms_file_get_FILE(const rms_file_type * ); +bool rms_file_is_roff(FILE * ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/rms/rms_tag.hpp b/libres/lib/include/ert/rms/rms_tag.hpp new file mode 100644 index 00000000000..445ae25bb13 --- /dev/null +++ b/libres/lib/include/ert/rms/rms_tag.hpp @@ -0,0 +1,52 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'rms_tag.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 ERT_RMS_TAG_H +#define ERT_RMS_TAG_H +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include + +#include + +#include + + + +typedef struct rms_tag_struct rms_tag_type; + +const char * rms_tag_get_namekey_name(const rms_tag_type * ); +const char * rms_tag_get_name(const rms_tag_type *); +void rms_tag_free(rms_tag_type *); +void rms_tag_free__(void * arg); +rms_tag_type * rms_tag_fread_alloc(FILE *, hash_type *, bool , bool *); +bool rms_tag_name_eq(const rms_tag_type *, const char * , const char *, const char *); +rms_tagkey_type * rms_tag_get_key(const rms_tag_type *, const char *); +void rms_tag_fwrite_filedata(const char * , FILE *stream); +void rms_tag_fwrite_eof(FILE *stream); +void rms_tag_fwrite(const rms_tag_type * , FILE * ); +rms_tag_type * rms_tag_alloc_dimensions(int , int , int ); +void rms_tag_fwrite_dimensions(int , int , int , FILE *); +void rms_tag_fwrite_parameter(const char *, const rms_tagkey_type *, FILE *); +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/rms/rms_tagkey.hpp b/libres/lib/include/ert/rms/rms_tagkey.hpp new file mode 100644 index 00000000000..4f69d923385 --- /dev/null +++ b/libres/lib/include/ert/rms/rms_tagkey.hpp @@ -0,0 +1,69 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'rms_tagkey.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 ERT_RMS_TAGKEY_H +#define ERT_RMS_TAGKEY_H +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + +#include + +#include + +typedef struct rms_tagkey_struct rms_tagkey_type; + + +void rms_tagkey_free(rms_tagkey_type *); +rms_tagkey_type * rms_tagkey_alloc_empty(bool); +rms_tagkey_type * rms_tagkey_alloc_complete(const char * , int , rms_type_enum , const void * , bool); +const char * rms_tagkey_get_name(const rms_tagkey_type *); +rms_type_enum rms_tagkey_get_rms_type(const rms_tagkey_type * ); +ecl_data_type rms_tagkey_get_ecl_data_type(const rms_tagkey_type *); +void rms_tagkey_set_data(rms_tagkey_type * , const void * ); + +bool rms_tagkey_char_eq(const rms_tagkey_type *, const char *); +void rms_tagkey_free_(void *); +void rms_tagkey_load(rms_tagkey_type *, bool , FILE *, hash_type *); +void * rms_tagkey_get_data_ref(const rms_tagkey_type *); +void rms_tagkey_fwrite(const rms_tagkey_type * , FILE *); +rms_tagkey_type * rms_tagkey_copyc(const rms_tagkey_type *); +int rms_tagkey_get_size(const rms_tagkey_type *); + + +rms_tagkey_type * rms_tagkey_alloc_byteswap(); +rms_tagkey_type * rms_tagkey_alloc_creationDate(); +rms_tagkey_type * rms_tagkey_alloc_filetype(const char * ); +rms_tagkey_type * rms_tagkey_alloc_dim(const char * , int ); +rms_tagkey_type * rms_tagkey_alloc_parameter_name(const char * ); + +void rms_tagkey_inplace_log10(rms_tagkey_type * ); +void rms_tagkey_inplace_sqr(rms_tagkey_type *); +void rms_tagkey_inplace_sqrt(rms_tagkey_type *); +void rms_tagkey_inplace_add_scaled(rms_tagkey_type * , const rms_tagkey_type * , double); +void rms_tagkey_clear(rms_tagkey_type * ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/rms/rms_type.hpp b/libres/lib/include/ert/rms/rms_type.hpp new file mode 100644 index 00000000000..07a9f08f137 --- /dev/null +++ b/libres/lib/include/ert/rms/rms_type.hpp @@ -0,0 +1,47 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'rms_type.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 ERT_RMS_TYPE_H +#define ERT_RMS_TYPE_H +#ifdef __cplusplus +extern "C" { +#endif +#include +#include + +enum rms_type_enum_def {rms_char_type , rms_float_type , rms_double_type , rms_bool_type , rms_byte_type , rms_int_type}; + +typedef enum rms_type_enum_def rms_type_enum; + +/* This *really* should not be exported ... */ + +typedef struct { + rms_type_enum rms_type; + int sizeof_ctype; + +} __rms_type; + +/*****************************************************************/ + +void rms_type_free(void *); +__rms_type * rms_type_alloc(rms_type_enum , int ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/rms/rms_util.hpp b/libres/lib/include/ert/rms/rms_util.hpp new file mode 100644 index 00000000000..70dfe1674b8 --- /dev/null +++ b/libres/lib/include/ert/rms/rms_util.hpp @@ -0,0 +1,50 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'rms_util.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 ERT_RMS_UTIL_H +#define ERT_RMS_UTIL_H +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + +#include + +#define RMS_INACTIVE_DOUBLE -999.00 +#define RMS_INACTIVE_FLOAT -999.00 +#define RMS_INACTIVE_INT -999 + + +rms_type_enum rms_util_convert_ecl_type(ecl_data_type); +int rms_util_global_index_from_eclipse_ijk(int, int, int, int, int, int); +void rms_util_translate_undef(void * , int , int , const void * , const void * ); +void rms_util_fskip_string(FILE *); +int rms_util_fread_strlen(FILE *); +bool rms_util_fread_string(char * , int , FILE *); +void rms_util_fwrite_string(const char * string , FILE *stream); +void rms_util_fwrite_comment(const char * , FILE *); +void rms_util_fwrite_newline(FILE *stream); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/sched/history.hpp b/libres/lib/include/ert/sched/history.hpp new file mode 100644 index 00000000000..d0bd0227e27 --- /dev/null +++ b/libres/lib/include/ert/sched/history.hpp @@ -0,0 +1,69 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'history.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 ERT_HISTORY_H +#define ERT_HISTORY_H + +#include +#include +#include + +#include +#include +#include + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef enum { + SCHEDULE = 0, + REFCASE_SIMULATED = 1, /* ecl_sum_get_well_var( "WWCT" ); */ + REFCASE_HISTORY = 2, /* ecl_sum_get_well_var( "WWCTH" ); */ + HISTORY_SOURCE_INVALID = 10 +} history_source_type; + + + +typedef struct history_struct history_type; + + history_source_type history_get_source_type( const char * string_source ); + +// Manipulators. + void history_free(history_type *); + history_type * history_alloc_from_refcase(const ecl_sum_type * refcase , bool use_h_keywords); + PY_USED const char * history_get_source_string( history_source_type history_source ); + bool history_init_ts( const history_type * history , const char * summary_key , double_vector_type * value, bool_vector_type * valid); + +// Accessors. + time_t history_get_start_time( const history_type * history ); + int history_get_last_restart(const history_type *); + time_t history_get_time_t_from_restart_nr( const history_type * history , int restart_nr); + history_source_type history_get_source(const history_type * history); + + UTIL_IS_INSTANCE_HEADER( history ); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libres/lib/include/ert/tooling.hpp b/libres/lib/include/ert/tooling.hpp new file mode 100644 index 00000000000..3c9ddb407a4 --- /dev/null +++ b/libres/lib/include/ert/tooling.hpp @@ -0,0 +1,14 @@ +#ifndef RES_TOOLING_HPP +#define RES_TOOLING_HPP + +// The following defines are used to mark declarations as potentially used +// this to avoid warnings from the compiler that a declaration is not used. + +// This define is used for seemingly unused declarations but usually are indirectly +// used in C code. For example through a macro expansion. +#define C_USED [[gnu::used]] + +// This define is used for declarations that are only used in Python. +#define PY_USED [[gnu::used]] + +#endif diff --git a/libres/lib/job_queue/environment_varlist.cpp b/libres/lib/job_queue/environment_varlist.cpp new file mode 100644 index 00000000000..72d3888b705 --- /dev/null +++ b/libres/lib/job_queue/environment_varlist.cpp @@ -0,0 +1,78 @@ +/* + Copyright (C) 2017 Equinor ASA, Norway. + + The file 'environment_varlist.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 ENV_VAR_KEY_STRING "global_environment" +#define UPDATE_PATH_KEY_STRING "global_update_path" + +struct env_varlist_struct { + hash_type * varlist; + hash_type * updatelist; +}; + +env_varlist_type * env_varlist_alloc() { + env_varlist_type * list = (env_varlist_type*)util_malloc( sizeof * list ); + list->varlist = hash_alloc(); + list->updatelist = hash_alloc(); + return list; +} + +void env_varlist_update_path(env_varlist_type * list, const char * path_var, const char * new_path) { + hash_insert_string( list->updatelist, path_var , res_env_update_path_var( path_var , new_path , false)); +} + +void env_varlist_setenv(env_varlist_type * list, const char * key, const char * value) { + const char * interp_value = res_env_interp_setenv(key, value); + hash_insert_string(list->varlist, key, interp_value); +} + + +static void env_varlist_fprintf_hash(const hash_type * list, const char * keystring, FILE * stream) { + int size = hash_get_size(list); + fprintf(stream, "\"%s\" : {", keystring); + stringlist_type * stringlist = hash_alloc_stringlist(list); + int i_max = size - 1; + for (int i = 0; i < size; i++) { + const char * key = stringlist_iget(stringlist, i); + fprintf(stream, "\"%s\" : \"%s\"", key, (char*)hash_get(list, key) ); + if (i < i_max) + fprintf(stream, ", "); + } + fprintf(stream, "}"); + stringlist_free(stringlist); +} + +void env_varlist_json_fprintf(const env_varlist_type * list, FILE * stream) { + env_varlist_fprintf_hash(list->varlist, ENV_VAR_KEY_STRING, stream); fprintf(stream, ",\n"); + env_varlist_fprintf_hash(list->updatelist, UPDATE_PATH_KEY_STRING, stream); +} + +int env_varlist_get_size(env_varlist_type * list) { + return hash_get_size(list->varlist); +} + +void env_varlist_free(env_varlist_type * list) { + hash_free(list->varlist); + hash_free(list->updatelist); + free(list); +} diff --git a/libres/lib/job_queue/ext_job.cpp b/libres/lib/job_queue/ext_job.cpp new file mode 100644 index 00000000000..6c947490cfe --- /dev/null +++ b/libres/lib/job_queue/ext_job.cpp @@ -0,0 +1,1157 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'ext_job.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 + + +/* + About arguments + --------------- + How a job is run is defined in terms of the following variables: + + o stdout_file / stdin_file / stderr_file + o arglist + o .... + + These variables will then contain string values from when the job + configuration is read in, for example this little job + + STDOUT my_stdout + STDERR my_stderr + ARGLIST my_job_input my_job_output + + stdout & stderr are redirected to the files 'my_stdout' and + 'my_stderr' respectively, and when invoked with an exec() call the + job is given the argumentlist: + + my_job_input my_job_output + + This implies that _every_time_ this job is invoked the argumentlist + will be identical; that is clearly quite limiting! To solve this we + have the possibility of performing string substitutions on the + strings in the job defintion prior to executing the job, this is + handled with the privat_args substitutions. The definition for a + copy-file job: + + + EXECUTABLE /bin/cp + ARGLIST + + + This can then be invoked several times, with different key=value + arguments for the SRC_FILE and TARGET_FILE: + + + COPY_FILE(SRC_FILE = file1 , TARGET_FILE = /tmp/file1) + COPY_FILE(SRC_FILE = file2 , TARGET_FILE = /tmp/file2) + +*/ +/* + More on STDOUT/STDERR + --------------------- + If STDOUT/STDERR is is not defined, output is directed to: + JOB_NAME.stdout.x or JOB_NAME.stderr.x, respectively + + STDOUT null directs output to screen + STDERR null directs error messages to screen +*/ +/* + + +jobList = [ + {"executable" : None, + "environment" : {"LM_LICENSE_PATH" : "1700@osl001lic.hda.hydro.com:1700@osl002lic.hda.hydro.com:1700@osl003lic.hda.hydro.com", + "F_UFMTENDIAN" : "big"}, + "target_file":"222", + "argList" : [], + "stdout" : "eclipse.stdout", + "stderr" : "eclipse.stdout", + "stdin" : "eclipse.stdin"}] +*/ + + +#define EXT_JOB_TYPE_ID 763012 + + +#define EXT_JOB_STDOUT "stdout" +#define EXT_JOB_STDERR "stderr" +#define EXT_JOB_NO_STD_FILE "null" //Setting STDOUT null or STDERR null in forward model directs output to screen + + + + + + +struct ext_job_struct { + UTIL_TYPE_ID_DECLARATION; + char * name; + char * executable; + char * target_file; + char * error_file; /* Job has failed if this is present. */ + char * start_file; /* Will not start if not this file is present */ + char * stdout_file; + char * stdin_file; + char * stderr_file; + char * license_path; /* If this is NULL - it will be unrestricted ... */ + char * license_root_path; + char * config_file; + int max_running; /* 0 means unlimited. */ + int max_running_minutes; /* The maximum number of minutes this job is allowed to run - 0: unlimited. */ + + int min_arg; + int max_arg; + int_vector_type * arg_types; + stringlist_type * argv; /* Currently not in use, but will replace deprected_argv */ + subst_list_type * private_args; /* A substitution list of input arguments which is performed before the external substitutions - + these are the arguments supplied as key=value pairs in the forward model call. */ + const subst_list_type * define_args; + char * private_args_string; + char * argv_string; + stringlist_type * deprecated_argv; /* This should *NOT* start with the executable */ + hash_type * environment; + hash_type * default_mapping; + hash_type * exec_env; + char * help_text; + + bool private_job; /* Can the current user/delete this job? (private_job == true) means the user can edit it. */ + bool __valid; /* Temporary variable consulted during the bootstrap - when the ext_job is completely initialized this should NOT be consulted anymore. */ +}; + + +static UTIL_SAFE_CAST_FUNCTION( ext_job , EXT_JOB_TYPE_ID) + + + + + + + +static ext_job_type * ext_job_alloc__(const char * name , const char * license_root_path , bool private_job) { + ext_job_type * ext_job = (ext_job_type*)util_malloc(sizeof * ext_job ); + + UTIL_TYPE_ID_INIT( ext_job , EXT_JOB_TYPE_ID); + ext_job->name = util_alloc_string_copy( name ); + ext_job->license_root_path = util_alloc_string_copy( license_root_path ); + ext_job->executable = NULL; + ext_job->stdout_file = NULL; + ext_job->target_file = NULL; + ext_job->error_file = NULL; + ext_job->start_file = NULL; + ext_job->stdin_file = NULL; + ext_job->stderr_file = NULL; + ext_job->environment = hash_alloc(); + ext_job->default_mapping = hash_alloc(); + ext_job->exec_env = hash_alloc(); + ext_job->argv = stringlist_alloc_new(); + ext_job->define_args = NULL; + ext_job->deprecated_argv = NULL; + ext_job->argv_string = NULL; + ext_job->__valid = true; + ext_job->license_path = NULL; + ext_job->config_file = NULL; + ext_job->max_running = 0; /* 0 means unlimited. */ + ext_job->max_running_minutes = 0; /* 0 means unlimited. */ + ext_job->min_arg = -1; + ext_job->max_arg = -1; + ext_job->arg_types = int_vector_alloc( 0 , CONFIG_STRING ); + ext_job->private_job = private_job; /* If private_job == true the job is user editable. */ + ext_job->help_text = NULL; + ext_job->private_args_string = NULL; + + /* + ext_job->private_args is set explicitly in the ext_job_alloc() + and ext_job_alloc_copy() functions. + */ + return ext_job; +} + + +void ext_job_free_deprecated_argv(ext_job_type * ext_job) { + if (ext_job->deprecated_argv) { + stringlist_free(ext_job->deprecated_argv); + ext_job->deprecated_argv = NULL; + } +} + +const char * ext_job_get_help_text( const ext_job_type * job ) { + if (job->help_text != NULL) + return job->help_text; + else + return "No help text installed for this job."; +} + + +void ext_job_set_help_text( ext_job_type * job , const char * help_text) { + job->help_text = util_realloc_string_copy( job->help_text , help_text ); +} + +/* + Exported function - must have name != NULL. Observe that the + instance returned from this function is not really usable for + anything. + + Should probably define a minium set of parameters which must be set + before the job is in a valid initialized state. +*/ + +ext_job_type * ext_job_alloc(const char * name , const char * license_root_path , bool private_job) { + ext_job_type * ext_job = ext_job_alloc__(name , license_root_path , private_job); + ext_job->private_args = subst_list_alloc( NULL ); + return ext_job; +} + + + +ext_job_type * ext_job_alloc_copy(const ext_job_type * src_job) { + ext_job_type * new_job = ext_job_alloc__( src_job->name , src_job->license_root_path , true /* All copies are by default private jobs. */); + + new_job->config_file = util_alloc_string_copy(src_job->config_file); + new_job->executable = util_alloc_string_copy(src_job->executable); + new_job->target_file = util_alloc_string_copy(src_job->target_file); + new_job->error_file = util_alloc_string_copy(src_job->error_file); + new_job->start_file = util_alloc_string_copy(src_job->start_file); + new_job->stdout_file = util_alloc_string_copy(src_job->stdout_file); + new_job->stdin_file = util_alloc_string_copy(src_job->stdin_file); + new_job->stderr_file = util_alloc_string_copy(src_job->stderr_file); + new_job->license_path = util_alloc_string_copy(src_job->license_path); + + ext_job_set_help_text( new_job , src_job->help_text ); + + new_job->max_running_minutes = src_job->max_running_minutes; + new_job->max_running = src_job->max_running; + new_job->min_arg = src_job->min_arg; + new_job->max_arg = src_job->max_arg; + new_job->arg_types = int_vector_alloc_copy(src_job->arg_types); + + new_job->private_args = subst_list_alloc_deep_copy( src_job->private_args ); + + /* Copying over all the keys in the environment hash table */ + { + hash_iter_type * iter = hash_iter_alloc( src_job->environment ); + const char * key = hash_iter_get_next_key(iter); + while (key != NULL) { + char * value = (char*)hash_get( src_job->environment , key); + if (value) + hash_insert_hash_owned_ref( new_job->environment , key , util_alloc_string_copy(value) , free); + else + hash_insert_ref(new_job->environment, key, NULL); + key = hash_iter_get_next_key(iter); + } + hash_iter_free(iter); + } + + { + hash_iter_type * iter = hash_iter_alloc( src_job->exec_env ); + const char * key = hash_iter_get_next_key(iter); + while (key != NULL) { + char * value = (char*)hash_get( src_job->exec_env , key); + if (value) + hash_insert_hash_owned_ref( new_job->exec_env , key , util_alloc_string_copy(value) , free); + else + hash_insert_ref(new_job->exec_env, key, NULL); + key = hash_iter_get_next_key(iter); + } + hash_iter_free(iter); + } + + + /* The default mapping. */ + { + hash_iter_type * iter = hash_iter_alloc( src_job->default_mapping ); + const char * key = hash_iter_get_next_key(iter); + while (key != NULL) { + char * value = (char*)hash_get( src_job->default_mapping , key); + hash_insert_hash_owned_ref( new_job->default_mapping , key , util_alloc_string_copy(value) , free); + key = hash_iter_get_next_key(iter); + } + hash_iter_free(iter); + } + + + if (src_job->deprecated_argv) { + new_job->deprecated_argv = stringlist_alloc_new(); + stringlist_deep_copy( new_job->deprecated_argv , src_job->deprecated_argv ); + } + + return new_job; +} + + +void ext_job_free(ext_job_type * ext_job) { + free(ext_job->name); + free(ext_job->executable); + free(ext_job->stdout_file); + free(ext_job->stdin_file); + free(ext_job->target_file); + free(ext_job->error_file); + free(ext_job->stderr_file); + free(ext_job->license_path); + free(ext_job->license_root_path); + free(ext_job->config_file); + free(ext_job->argv_string); + free(ext_job->help_text); + free(ext_job->private_args_string); + + hash_free( ext_job->default_mapping); + hash_free( ext_job->environment ); + hash_free( ext_job->exec_env); + + stringlist_free(ext_job->argv); + if (ext_job->deprecated_argv) + stringlist_free(ext_job->deprecated_argv); + subst_list_free( ext_job->private_args ); + + int_vector_free(ext_job->arg_types); + free(ext_job); +} + +void ext_job_free__(void * __ext_job) { + ext_job_free ( ext_job_safe_cast(__ext_job) ); +} + + +static void __update_mode( const char * filename , mode_t add_mode) { + util_addmode_if_owner( filename , add_mode); +} + + +/** + The license_path = + + root_license_path / job_name / job_name + +*/ + +static void ext_job_init_license_control(ext_job_type * ext_job) { + if (ext_job->license_path == NULL) { + ext_job->license_path = util_alloc_sprintf("%s%c%s" , ext_job->license_root_path , UTIL_PATH_SEP_CHAR , ext_job->name ); + util_make_path( ext_job->license_path ); + } +} + + + +void ext_job_set_max_time( ext_job_type * ext_job , int max_time ) { + ext_job->max_running_minutes = max_time; +} + + + +/** + @executable parameter: + The raw executable is either + - an absolute path read directly from config + - an absolute path constructed from the relative path from config + with the assumption that the path was a relative path from the + location of the job description file to the executable. + + @executable_raw parameter: + The raw executable as read from config, unprocessed. + + This method have the following logic: + + @executable exists: + We store the full path as the executable field of the job; and + try to update the mode of the full_path executable to make sure it + is executable. + + @executable does not exist, but @executable_raw exists: + We have found an executable relative to the current working + directory. This is deprecated behaviour, support will later be + removed. Suggest new path to executable to user, relative to job + description file and do a recursive call to this method, using + the absolute path as @executable parameter + + @executable does not exist, @executable_raw does not exist and + is an absolute path: + Write error message + + @executable does not exist, @executable_raw does not exist and + is a relative path: + Search trough the PATH variable to try to locate the executable. + If found, do a recursive call to this method, using the absolute path + as @executable parameter + +*/ + +void ext_job_set_executable(ext_job_type * ext_job, const char * executable_abs, const char * executable_input,bool search_path) { + + if (util_file_exists(executable_abs)) { + /* + The @executable parameter points to an existing file; we store + the full path as the executable field of the job; we also try + to update the mode of the full_path executable to make sure it + is executable. + */ + char * full_path = (char*)util_alloc_realpath( executable_abs ); + __update_mode( full_path , S_IRUSR + S_IWUSR + S_IXUSR + S_IRGRP + S_IWGRP + S_IXGRP + S_IROTH + S_IXOTH); /* u:rwx g:rwx o:rx */ + ext_job->executable = util_realloc_string_copy(ext_job->executable , full_path); + free( full_path ); + } else if (util_is_abs_path( executable_input )) { + /* If you have given an absolute path (i.e. starting with '/' to + a non existing job we mark it as invalid - no possibility to + provide context replacement afterwards. The job will be + discarded by the calling scope. + */ + fprintf(stderr , "** Warning: the executable:%s can not be found,\n" + " job:%s will not be available.\n" , executable_abs , ext_job->name ); + ext_job->__valid = false; + } else { + if (search_path){ + /* Go through the PATH variable to try to locate the executable. */ + char * path_executable = res_env_alloc_PATH_executable( executable_input ); + + if (path_executable != NULL) { + ext_job_set_executable( ext_job , path_executable, NULL, search_path ); + free( path_executable ); + } else { + /* We take the chance that user will supply a valid subst key for this later; + if the final executable is not an actually executable file when exporting the + job from ext_job_python_fprintf() a big warning will be written on stderr. + */ + fprintf(stderr , "** Warning: Unable to locate the executable %s for job %s.\n" + " Path to executable must be relative to the job description file, or an absolute path.\n" + " Please update job EXECUTABLE for job %s. \n" , executable_abs , ext_job->name, ext_job->name); + ext_job->__valid = false; + } + } else { + ext_job->executable = util_realloc_string_copy(ext_job->executable , executable_input); + } + } + + /* + If in the end we do not have execute rights to the executable : + discard the job. + */ + if (ext_job->executable != NULL) { + if (util_file_exists(executable_abs)) { + if (!util_is_executable( ext_job->executable )) { + fprintf(stderr , "** You do not have execute rights to:%s - job will not be available.\n" , ext_job->executable); + ext_job->__valid = false; /* Mark the job as NOT successfully installed - the ext_job + instance will later be freed and discarded. */ + } + } + } +} + + + +/** + Observe that this does NOT reread the ext_job instance from the new + config_file. +*/ + + +/*****************************************************************/ +/* Scalar set and get functions */ + +void ext_job_set_args(ext_job_type * ext_job, const stringlist_type * argv) { + stringlist_deep_copy(ext_job->argv, argv); +} + +void ext_job_set_config_file(ext_job_type * ext_job, const char * config_file) { + ext_job->config_file = util_realloc_string_copy(ext_job->config_file , config_file); +} + +const char * ext_job_get_config_file(const ext_job_type * ext_job) { + return ext_job->config_file; +} + +void ext_job_set_target_file(ext_job_type * ext_job, const char * target_file) { + ext_job->target_file = util_realloc_string_copy(ext_job->target_file , target_file); +} + +const char * ext_job_get_target_file(const ext_job_type * ext_job) { + return ext_job->target_file; +} + +void ext_job_set_error_file(ext_job_type * ext_job, const char * error_file) { + ext_job->error_file = util_realloc_string_copy(ext_job->error_file , error_file); +} + +const char * ext_job_get_error_file(const ext_job_type * ext_job) { + return ext_job->error_file; +} + +const char * ext_job_get_executable(const ext_job_type * ext_job) { + return ext_job->executable; +} + +void ext_job_set_start_file(ext_job_type * ext_job, const char * start_file) { + ext_job->start_file = util_realloc_string_copy(ext_job->start_file , start_file); +} + +const char * ext_job_get_start_file(const ext_job_type * ext_job) { + return ext_job->start_file; +} + +const char * ext_job_get_license_path(const ext_job_type * ext_job) { + return ext_job->license_path; +} + +const char * ext_job_get_name(const ext_job_type * ext_job) { + return ext_job->name; +} +void ext_job_set_stdin_file(ext_job_type * ext_job, const char * stdin_file) { + ext_job->stdin_file = util_realloc_string_copy(ext_job->stdin_file , stdin_file); +} + +const char * ext_job_get_stdin_file(const ext_job_type * ext_job) { + return ext_job->stdin_file; +} + +void ext_job_set_stdout_file(ext_job_type * ext_job, const char * stdout_file) { + if (!util_string_equal(stdout_file, EXT_JOB_NO_STD_FILE)) + ext_job->stdout_file = util_realloc_string_copy(ext_job->stdout_file , stdout_file); +} + +const char * ext_job_get_stdout_file(const ext_job_type * ext_job) { + return ext_job->stdout_file; +} + +void ext_job_set_stderr_file(ext_job_type * ext_job, const char * stderr_file) { + if (strcmp(stderr_file, EXT_JOB_NO_STD_FILE) != 0) + ext_job->stderr_file = util_realloc_string_copy(ext_job->stderr_file , stderr_file); +} + +const char * ext_job_get_stderr_file(const ext_job_type * ext_job) { + return ext_job->stderr_file; +} + +void ext_job_set_max_running( ext_job_type * ext_job , int max_running) { + ext_job->max_running = max_running; + if (max_running > 0) + ext_job_init_license_control( ext_job ); +} + +int ext_job_get_max_running( const ext_job_type * ext_job ) { + return ext_job->max_running; +} + +void ext_job_set_max_running_minutes( ext_job_type * ext_job , int max_running_minutes) { + ext_job->max_running_minutes = max_running_minutes; +} + +int ext_job_get_max_running_minutes( const ext_job_type * ext_job ) { + return ext_job->max_running_minutes; +} + +static void ext_job_set_min_arg(ext_job_type * ext_job, int min_arg) { + ext_job->min_arg = min_arg; +} + +static void ext_job_set_max_arg(ext_job_type * ext_job, int max_arg) { + ext_job->max_arg = max_arg; +} + +int ext_job_get_min_arg(const ext_job_type * ext_job) { + return ext_job->min_arg; +} + +int ext_job_get_max_arg(const ext_job_type * ext_job) { + return ext_job->max_arg; +} + +/*****************************************************************/ + +void ext_job_set_private_arg(ext_job_type * ext_job, const char * key , const char * value) { + subst_list_append_copy( ext_job->private_args , key , value , NULL); +} + +void ext_job_set_define_args(ext_job_type * ext_job, const subst_list_type * define_args) { + ext_job->define_args = subst_list_alloc_deep_copy(define_args); +} + +void ext_job_add_environment(ext_job_type *ext_job , const char * key , const char * value) { + hash_insert_hash_owned_ref( ext_job->environment , key , util_alloc_string_copy( value ) , free); +} + + +void ext_job_clear_environment( ext_job_type * ext_job ) { + hash_clear( ext_job->environment ); +} + +hash_type * ext_job_get_environment( ext_job_type * ext_job ) { + return ext_job->environment; +} + + +/*****************************************************************/ + + +static char * __alloc_filtered_string( const char * src_string , const subst_list_type * private_args, const subst_list_type * global_args) { + char * tmp1 = subst_list_alloc_filtered_string( private_args , src_string ); /* internal filtering first */ + char * tmp2; + + if (global_args != NULL) { + tmp2 = subst_list_alloc_filtered_string( global_args , tmp1 ); /* Global filtering. */ + free( tmp1 ); + } else + tmp2 = tmp1; + + return tmp2; + +} + +static void __fprintf_string(FILE * stream , const char * s , const subst_list_type * private_args, const subst_list_type * global_args) { + char * filtered_string = __alloc_filtered_string(s , private_args , global_args ); + fprintf(stream , "\"%s\"" , filtered_string ); + free( filtered_string ); +} + + +static void __fprintf_python_string(FILE * stream , + const char * prefix, + const char * id, + const char * value, + const char * suffix, + const subst_list_type * private_args, + const subst_list_type * global_args, + const char * null_value) { + fprintf(stream , "%s\"%s\" : " , prefix, id); + if (value == NULL) + fprintf(stream, "%s", null_value); + else + __fprintf_string(stream , value , private_args , global_args); + fprintf(stream, "%s", suffix); +} + + +static void __fprintf_init_python_list( FILE * stream , const char * id ) { + fprintf(stream , "\"%s\" : " , id); + fprintf(stream,"["); +} + +static void __fprintf_close_python_list( FILE * stream ) { + fprintf(stream,"]"); +} + + + +static hash_type * __alloc_filtered_hash(hash_type * input_hash, + bool include_angular_values, + const subst_list_type * private_args, + const subst_list_type * global_args) { + + hash_type * output_hash = hash_alloc(); + hash_iter_type * iter = hash_iter_alloc(input_hash); + const char * key = hash_iter_get_next_key(iter); + while (key != NULL) { + const char * value = (const char*) hash_get(input_hash , key); + /* + If the value is NULL or alternatively the special string value "null" we + print the @null_value variable and continue. + */ + if (!value || strcmp(value, "null") == 0) + hash_insert_ref(output_hash, key, NULL); + else { + char * fv = __alloc_filtered_string(value, private_args, global_args); + /* + If the value string contains a string which is not represented in + the substitutionlists we will not print out the literal. + */ + if (include_angular_values) + hash_insert_hash_owned_ref(output_hash, key, fv, free); + else { + if ( !(fv[0] == '<' && fv[strlen(fv) -1] == '>') ) + hash_insert_hash_owned_ref(output_hash, key, fv, free); + } + + } + + key = hash_iter_get_next_key(iter); + } + return output_hash; +} + + + +static void __fprintf_python_hash(FILE * stream, + const char * prefix, + const char * id, + hash_type * input_hash, + const char * suffix, + const subst_list_type * private_args, + const subst_list_type * global_args, + const char * null_value) { + bool print_angular_values = false; + hash_type * output_hash = __alloc_filtered_hash(input_hash, print_angular_values, private_args, global_args); + int hash_size = hash_get_size(output_hash); + + fprintf(stream , "%s\"%s\" : " , prefix, id); + if (hash_size > 0) { + bool first = true; + fprintf(stream,"{"); + + hash_iter_type * iter = hash_iter_alloc(output_hash); + const char * key = hash_iter_get_next_key(iter); + + while (key != NULL) { + const char * value = (const char*) hash_get(output_hash , key); + if (!first) + fprintf(stream,","); + + if (value) + fprintf(stream,"\"%s\" : \"%s\"" , key, value); + else + fprintf(stream, "\"%s\" : %s", key, null_value); + + key = hash_iter_get_next_key(iter); + first = false; + } + + fprintf(stream,"}"); + } else + fprintf(stream, "%s", null_value); + fprintf(stream, "%s", suffix); + + hash_free(output_hash); +} + + +static void __fprintf_python_int(FILE * stream, + const char * prefix, + const char * key, + int value, + const char * suffix, + const char * null_value) { + fprintf(stream, "%s", prefix); + if (value > 0) + fprintf(stream , "\"%s\" : %d" , key , value); + else + fprintf(stream , "\"%s\" : %s" , key, null_value); + fprintf(stream, "%s", suffix); +} + +/* + This is special cased to support the default mapping. +*/ + +static void __fprintf_python_argList(FILE * stream, + const char * prefix, + const ext_job_type * ext_job, + const char * suffix, + const subst_list_type * global_args) { + + stringlist_type * argv; + if (ext_job->deprecated_argv) + argv = ext_job->deprecated_argv; + else + argv = ext_job->argv; + + fprintf(stream, "%s", prefix); + __fprintf_init_python_list( stream , "argList" ); + { + for (int index = 0; index < stringlist_get_size( argv ); index++) { + const char * src_string = stringlist_iget( argv , index ); + char * filtered_string = __alloc_filtered_string(src_string , ext_job->private_args , global_args ); + if (hash_has_key( ext_job->default_mapping , filtered_string )) + filtered_string = (char *)util_realloc_string_copy( filtered_string , (const char *)hash_get( ext_job->default_mapping , filtered_string )); + + fprintf(stream , "\"%s\"" , filtered_string ); + if (index < (stringlist_get_size( argv) - 1)) + fprintf(stream , "," ); + + free( filtered_string ); + } + } + __fprintf_close_python_list( stream ); + fprintf(stream, "%s", suffix); +} + + +static void __fprintf_python_arg_types(FILE * stream, + const char * prefix, + const char * key, + const ext_job_type * ext_job, + const char * suffix, + const char * null_value) { + fprintf(stream, "%s", prefix); + if (!ext_job->arg_types) { + fprintf(stream , "\"%s\" : %s" , key, null_value); + goto postfix; + } + + fprintf(stream, "\"%s\" : [", key); + for (int i = 0; i < ext_job->max_arg; i++) { + + const char * arg_type = NULL; + int type = int_vector_safe_iget(ext_job->arg_types, i); + switch(type) { + case CONFIG_INT: arg_type = JOB_INT_TYPE; break; + case CONFIG_FLOAT: arg_type = JOB_FLOAT_TYPE; break; + case CONFIG_STRING: arg_type = JOB_STRING_TYPE; break; + case CONFIG_BOOL: arg_type = JOB_BOOL_TYPE; break; + case CONFIG_RUNTIME_FILE: arg_type = JOB_RUNTIME_FILE_TYPE; break; + case CONFIG_RUNTIME_INT: arg_type = JOB_RUNTIME_INT_TYPE; break; + default: util_abort("%s unknown config type %d", __func__, type); + } + + fprintf(stream, "\"%s\"", arg_type); + if ((i + 1) < ext_job->max_arg) + fprintf(stream, ", "); + } + fprintf(stream, "]"); + + postfix: fprintf(stream, "%s", suffix); +} + + +void ext_job_json_fprintf(const ext_job_type * ext_job, int job_index, FILE * stream, const subst_list_type * global_args) { + const char * null_value = "null"; + + char * file_stdout_index = NULL; + char * file_stderr_index = NULL; + + file_stdout_index = util_alloc_sprintf("%s.%d",ext_job->stdout_file, job_index); + file_stderr_index = util_alloc_sprintf("%s.%d",ext_job->stderr_file, job_index); + + fprintf(stream," {"); + { + __fprintf_python_string( stream, "", "name", ext_job->name, ",\n", ext_job->private_args, NULL, null_value); + __fprintf_python_string( stream, " ", "executable", ext_job->executable, ",\n", ext_job->private_args, global_args, null_value); + __fprintf_python_string( stream, " ", "target_file", ext_job->target_file, ",\n", ext_job->private_args, global_args, null_value); + __fprintf_python_string( stream, " ", "error_file", ext_job->error_file, ",\n", ext_job->private_args, global_args, null_value); + __fprintf_python_string( stream, " ", "start_file", ext_job->start_file, ",\n", ext_job->private_args, global_args, null_value); + __fprintf_python_string( stream, " ", "stdout", file_stdout_index, ",\n", ext_job->private_args, global_args, null_value); + __fprintf_python_string( stream, " ", "stderr", file_stderr_index, ",\n", ext_job->private_args, global_args, null_value); + __fprintf_python_string( stream, " ", "stdin", ext_job->stdin_file, ",\n", ext_job->private_args, global_args, null_value); + __fprintf_python_argList( stream, " ", ext_job, ",\n", global_args ); + __fprintf_python_hash( stream, " ", "environment", ext_job->environment, ",\n", ext_job->private_args, global_args, null_value); + __fprintf_python_hash( stream, " ", "exec_env", ext_job->exec_env, ",\n", ext_job->private_args, global_args, null_value); + __fprintf_python_string( stream, " ", "license_path", ext_job->license_path, ",\n", ext_job->private_args, global_args, null_value); + __fprintf_python_int( stream, " ", "max_running_minutes", ext_job->max_running_minutes, ",\n", null_value); + __fprintf_python_int( stream, " ", "max_running", ext_job->max_running, ",\n", null_value); + __fprintf_python_int( stream, " ", "min_arg", ext_job->min_arg, ",\n", null_value); + + __fprintf_python_arg_types( stream, " ", "arg_types", ext_job, ",\n", null_value); + + __fprintf_python_int( stream, " ", "max_arg", ext_job->max_arg, "\n", null_value); + + } + fprintf(stream,"}"); + + free( file_stdout_index ); + free( file_stderr_index ); +} + + + +#define PRINT_KEY_STRING( stream , key , value ) \ +if (value != NULL) \ +{ \ + fprintf(stream , "%16s ", key); \ + fprintf(stream , "%s\n" , value); \ +} + + +#define PRINT_KEY_INT( stream , key , value ) \ +if (value != 0) \ +{ \ + fprintf(stream , "%16s ", key); \ + fprintf(stream , "%d\n" , value); \ +} + + +/** + Observe that the job will save itself to the internalized + config_file; if you wish to save to some other place you must call + ext_job_set_config_file() first. +*/ + +void ext_job_save( const ext_job_type * ext_job ) { + FILE * stream = util_mkdir_fopen( ext_job->config_file , "w" ); + + PRINT_KEY_STRING( stream , "EXECUTABLE" , ext_job->executable); + PRINT_KEY_STRING( stream , "STDIN" , ext_job->stdin_file); + PRINT_KEY_STRING( stream , "STDERR" , ext_job->stderr_file); + PRINT_KEY_STRING( stream , "STDOUT" , ext_job->stdout_file); + PRINT_KEY_STRING( stream , "TARGET_FILE" , ext_job->target_file); + PRINT_KEY_STRING( stream , "START_FILE" , ext_job->start_file); + PRINT_KEY_STRING( stream , "ERROR_FILE" , ext_job->error_file); + PRINT_KEY_INT( stream , "MAX_RUNNING" , ext_job->max_running); + PRINT_KEY_INT( stream , "MAX_RUNNING_MINUTES" , ext_job->max_running_minutes); + + stringlist_type * list; + if (ext_job->deprecated_argv) + list = ext_job->deprecated_argv; + else + list = ext_job->argv; + + if (stringlist_get_size( list) > 0) { + fprintf(stream , "%16s" , "ARGLIST"); + stringlist_fprintf( list , " " , stream ); + fprintf(stream , "\n"); + } + if (hash_get_size( ext_job->environment ) > 0) { + hash_iter_type * hash_iter = hash_iter_alloc( ext_job->environment ); + while (!hash_iter_is_complete( hash_iter )) { + const char * key = hash_iter_get_next_key( hash_iter ); + fprintf(stream, "%16s %16s %s\n" , "ENV" , key , (const char *) hash_get( ext_job->environment , key )); + } + hash_iter_free( hash_iter ); + } + fclose( stream ); +} + +#undef PRINT_KEY_STRING +#undef PRINT_KEY_INT + + + +void ext_job_fprintf(const ext_job_type * ext_job , FILE * stream) { + fprintf(stream , "%s", ext_job->name); + if (subst_list_get_size( ext_job->private_args ) > 0) { + fprintf(stream , "("); + subst_list_fprintf(ext_job->private_args , stream); + fprintf(stream , ")"); + } + fprintf(stream , " "); +} + + +config_item_types ext_job_iget_argtype( const ext_job_type * ext_job, int index) { + return (config_item_types)int_vector_safe_iget( ext_job->arg_types , index ); +} + + +static void ext_job_iset_argtype_string( ext_job_type * ext_job , int iarg , const char * arg_type) { + config_item_types type = job_kw_get_type(arg_type); + if (type != CONFIG_INVALID) + int_vector_iset( ext_job->arg_types , iarg , type ); +} + + +ext_job_type * ext_job_fscanf_alloc(const char * name , const char * license_root_path , bool private_job , const char * config_file, bool search_path) { + { + mode_t target_mode = S_IRUSR + S_IWUSR + S_IRGRP + S_IWGRP + S_IROTH; /* u+rw g+rw o+r */ + __update_mode( config_file , target_mode ); + } + + if (util_entry_readable( config_file)) { + ext_job_type * ext_job = NULL; + config_parser_type * config = config_alloc( ); + + { + config_schema_item_type * item; + item = config_add_schema_item(config , "MAX_RUNNING" , false ); config_schema_item_set_argc_minmax(item , 1 , 1 ); config_schema_item_iset_type( item , 0 , CONFIG_INT ); + item = config_add_schema_item(config , "STDIN" , false ); config_schema_item_set_argc_minmax(item , 1 , 1 ); + item = config_add_schema_item(config , "STDOUT" , false ); config_schema_item_set_argc_minmax(item , 1 , 1 ); + item = config_add_schema_item(config , "STDERR" , false ); config_schema_item_set_argc_minmax(item , 1 , 1 ); + item = config_add_schema_item(config , EXECUTABLE_KEY , true ); config_schema_item_set_argc_minmax(item , 1 , 1 ); config_schema_item_iset_type(item, 0, CONFIG_PATH); + item = config_add_schema_item(config , "TARGET_FILE" , false ); config_schema_item_set_argc_minmax(item , 1 , 1 ); + item = config_add_schema_item(config , "ERROR_FILE" , false ); config_schema_item_set_argc_minmax(item , 1 , 1 ); + item = config_add_schema_item(config , "START_FILE" , false ); config_schema_item_set_argc_minmax(item , 1 , 1 ); + item = config_add_schema_item(config , "ENV" , false ); config_schema_item_set_argc_minmax(item , 1 , 2 ); + item = config_add_schema_item(config , "EXEC_ENV" , false ); config_schema_item_set_argc_minmax(item , 1 , 2 ); + item = config_add_schema_item(config , "DEFAULT" , false ); config_schema_item_set_argc_minmax(item , 2 , 2 ); + item = config_add_schema_item(config , "ARGLIST" , false ); config_schema_item_set_argc_minmax(item , 1 , CONFIG_DEFAULT_ARG_MAX ); + item = config_add_schema_item(config , "MAX_RUNNING_MINUTES" , false ); config_schema_item_set_argc_minmax(item , 1 , 1 ); config_schema_item_iset_type( item , 0 , CONFIG_INT ); + item = config_add_schema_item(config , MIN_ARG_KEY , false ); config_schema_item_set_argc_minmax(item , 1 , 1 ); config_schema_item_iset_type( item , 0 , CONFIG_INT ); + item = config_add_schema_item(config , MAX_ARG_KEY , false ); config_schema_item_set_argc_minmax(item , 1 , 1 ); config_schema_item_iset_type( item , 0 , CONFIG_INT ); + item = config_add_schema_item(config , ARG_TYPE_KEY , false ); config_schema_item_set_argc_minmax( item , 2 , 2 ); config_schema_item_iset_type( item , 0 , CONFIG_INT ); + + stringlist_type * var_types = stringlist_alloc_new(); + stringlist_append_copy(var_types, JOB_STRING_TYPE); + stringlist_append_copy(var_types, JOB_INT_TYPE); + stringlist_append_copy(var_types, JOB_FLOAT_TYPE); + stringlist_append_copy(var_types, JOB_BOOL_TYPE); + stringlist_append_copy(var_types, JOB_RUNTIME_FILE_TYPE); + stringlist_append_copy(var_types, JOB_RUNTIME_INT_TYPE); + + config_schema_item_set_indexed_selection_set( item , 1 , var_types); + stringlist_free(var_types); + } + config_add_alias(config , "EXECUTABLE" , "PORTABLE_EXE"); + { + config_content_type * content = config_parse(config , config_file , "--" , NULL , NULL , NULL , CONFIG_UNRECOGNIZED_WARN , true); + if (config_content_is_valid( content )) { + ext_job = ext_job_alloc(name , license_root_path , private_job); + ext_job_set_config_file( ext_job , config_file ); + + + if (config_content_has_item(content , "STDIN")) ext_job_set_stdin_file(ext_job , config_content_iget(content , "STDIN" , 0,0)); + if (config_content_has_item(content , "STDOUT")) ext_job_set_stdout_file(ext_job , config_content_iget(content , "STDOUT" , 0,0)); + else ext_job->stdout_file = util_alloc_filename( NULL, ext_job->name, EXT_JOB_STDOUT); + + if (config_content_has_item(content , "STDERR")) ext_job_set_stderr_file(ext_job , config_content_iget(content , "STDERR" , 0,0)); + else ext_job->stderr_file = util_alloc_filename( NULL, ext_job->name, EXT_JOB_STDERR); + + if (config_content_has_item(content , "ERROR_FILE")) ext_job_set_error_file(ext_job , config_content_iget(content , "ERROR_FILE" , 0,0)); + if (config_content_has_item(content , "TARGET_FILE")) ext_job_set_target_file(ext_job , config_content_iget(content , "TARGET_FILE" , 0,0)); + if (config_content_has_item(content , "START_FILE")) ext_job_set_start_file(ext_job , config_content_iget(content , "START_FILE" , 0,0)); + if (config_content_has_item(content , "MAX_RUNNING")) ext_job_set_max_running(ext_job , config_content_iget_as_int(content , "MAX_RUNNING" , 0,0)); + if (config_content_has_item(content , "MAX_RUNNING_MINUTES")) ext_job_set_max_time(ext_job , config_content_iget_as_int(content , "MAX_RUNNING_MINUTES" , 0,0)); + if (config_content_has_item(content , MIN_ARG_KEY)) ext_job_set_min_arg(ext_job , config_content_iget_as_int(content , MIN_ARG_KEY , 0,0)); + if (config_content_has_item(content , MAX_ARG_KEY)) ext_job_set_max_arg(ext_job , config_content_iget_as_int(content , MAX_ARG_KEY , 0,0)); + + for (int i = 0; i < config_content_get_occurences( content , ARG_TYPE_KEY); i++) { + int iarg = config_content_iget_as_int( content , ARG_TYPE_KEY , i , 0 ); + const char * arg_type = config_content_iget( content , ARG_TYPE_KEY , i , 1 ); + + ext_job_iset_argtype_string( ext_job , iarg , arg_type ); + } + + { + const char * executable = config_content_get_value_as_executable(content , EXECUTABLE_KEY); + const char * executable_raw = config_content_iget(content , EXECUTABLE_KEY , 0,0); + ext_job_set_executable(ext_job , executable, executable_raw, search_path); + } + + + { + if (config_content_has_item( content , "ARGLIST")) { + ext_job->deprecated_argv = stringlist_alloc_new(); + config_content_node_type * arg_node = config_content_get_value_node( content , "ARGLIST"); + int i; + for (i=0; i < config_content_node_get_size( arg_node ); i++) + stringlist_append_copy( ext_job->deprecated_argv , config_content_node_iget( arg_node , i )); + } + } + + + if (config_content_has_item( content , "ENV")) { + const config_content_item_type * env_item = config_content_get_item( content , "ENV" ); + for (int ivar = 0; ivar < config_content_item_get_size( env_item ); ivar++) { + const config_content_node_type * env_node = config_content_item_iget_node( env_item , ivar ); + const char * key = config_content_node_iget( env_node, 0); + if (config_content_node_get_size( env_node ) > 1) { + const char * value = config_content_node_iget( env_node , 1); + hash_insert_hash_owned_ref( ext_job->environment, key , util_alloc_string_copy( value ) , free); + } else + hash_insert_ref(ext_job->environment, key, NULL); + } + } + + if (config_content_has_item( content , "EXEC_ENV")) { + const config_content_item_type * env_item = config_content_get_item( content , "EXEC_ENV" ); + for (int ivar = 0; ivar < config_content_item_get_size( env_item ); ivar++) { + const config_content_node_type * env_node = config_content_item_iget_node( env_item , ivar ); + const char * key = config_content_node_iget( env_node, 0); + if (config_content_node_get_size( env_node ) > 1) { + const char * value = config_content_node_iget( env_node , 1); + hash_insert_hash_owned_ref( ext_job->exec_env, key , util_alloc_string_copy( value ) , free); + } else + hash_insert_ref(ext_job->exec_env, key, NULL); + } + } + + + /* Default mappings; these are used to set values in the argList + which have not been supplied by the calling context. */ + { + if (config_content_has_item( content , "DEFAULT")) { + const config_content_item_type * default_item = config_content_get_item( content , "DEFAULT"); + for (int ivar = 0; ivar < config_content_item_get_size( default_item ); ivar++) { + const config_content_node_type * default_node = config_content_item_iget_node( default_item , ivar ); + for (int i=0; i < config_content_node_get_size( default_node ); i+= 2) { + const char * key = config_content_node_iget( default_node , i ); + const char * value = config_content_node_iget( default_node , i + 1); + hash_insert_hash_owned_ref( ext_job->default_mapping, key , util_alloc_string_copy( value ) , free); + } + } + } + } + + if (!ext_job->__valid) { + /* + Something NOT OK (i.e. EXECUTABLE now); free the job instance and return NULL: + */ + ext_job_free( ext_job ); + ext_job = NULL; + fprintf(stderr,"** Warning: job: \'%s\' not available ... \n", name ); + } + } else { + config_error_type * error = config_content_get_errors( content ); + config_error_fprintf( error , true , stderr ); + fprintf(stderr,"** Warning: job: \'%s\' not available ... \n", name ); + } + config_content_free( content ); + } + config_free(config); + + return ext_job; + } else { + fprintf(stderr,"** Warning: you do not have permission to read file:\'%s\' - job:%s not available. \n", config_file , name); + return NULL; + } +} + + +const stringlist_type * ext_job_get_arglist( const ext_job_type * ext_job ) { + if (ext_job->deprecated_argv) + return ext_job->deprecated_argv; + else + return ext_job->argv; +} + +const stringlist_type * ext_job_get_argvalues( const ext_job_type * ext_job ) { + stringlist_type * result = stringlist_alloc_new(); + + const stringlist_type * argv = ext_job_get_arglist(ext_job); + for (int i = 0; i < stringlist_get_size(argv); i++) { + const char * src_string = stringlist_iget(argv, i); + char * filtered_string = __alloc_filtered_string(src_string, ext_job->private_args, ext_job->define_args ); + if (hash_has_key( ext_job->default_mapping, filtered_string )) + filtered_string = (char *)util_realloc_string_copy(filtered_string, (const char *)hash_get(ext_job->default_mapping, filtered_string)); + + stringlist_append_copy(result, filtered_string); + } + return result; +} + +/** + Set the internal arguments of the job based on an input string + @arg_string which is of the form: + + key1=value1, key2=value2 , key3=value3 + + The internal private argument list is cleared before adding these + arguments. +*/ + +int ext_job_set_private_args_from_string( ext_job_type * ext_job , const char * arg_string ) { + subst_list_clear( ext_job->private_args ); + return subst_list_add_from_string( ext_job->private_args , arg_string , true ); +} + + + +bool ext_job_is_shared( const ext_job_type * ext_job ) { + return !ext_job->private_job; +} + +bool ext_job_is_private( const ext_job_type * ext_job ) { + return ext_job->private_job; +} + + +#undef ASSERT_TOKENS diff --git a/libres/lib/job_queue/ext_joblist.cpp b/libres/lib/job_queue/ext_joblist.cpp new file mode 100644 index 00000000000..9d85a6aeda5 --- /dev/null +++ b/libres/lib/job_queue/ext_joblist.cpp @@ -0,0 +1,181 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'ext_joblist.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 + + +//#define MODULE_NAME "jobs.py" +//#define JOBLIST_NAME "jobList" + + +/** + About the 'license' system: + --------------------------- + + There is a simple possibility to limit the number of jobs which are + running in parallell. It works like this: + + 1. For the joblist as a whole a license_path is created. This + license path should contain both a uid and pid of the current + process. This ensures that: + + a. The license count is per user and per ert instance. + b. Each ert instance starts with a fresh license count. A + license path, and license files left dangling after unclean + shutdown can just be removed. + + 2. For each job in the joblist a subdirectory is created under the + license_path. + + 3. For each job a license_file is created, and for each time a new + instance is checked out a hard_link to this license_file is + created - i.e. the number of checked out licenses is a + hard_link count (-1). + + Step three here is implemented by the job_dispatch script + actually running the jobs. + + It is essential that the license_root_path is on a volume which is + accessible from all the nodes which will run jobs. Using e.g. /tmp + as license_root_path will fail HARD. + +*/ + + + +/*****************************************************************/ + +struct ext_joblist_struct { + hash_type * jobs; +}; + + + + +ext_joblist_type * ext_joblist_alloc( ) { + ext_joblist_type * joblist = (ext_joblist_type*)util_malloc( sizeof * joblist ); + joblist->jobs = hash_alloc(); + return joblist; +} + + +void ext_joblist_free(ext_joblist_type * joblist) { + hash_free(joblist->jobs); + free(joblist); +} + + +void ext_joblist_add_job(ext_joblist_type * joblist , const char * name , ext_job_type * new_job) { + hash_insert_hash_owned_ref(joblist->jobs , name , new_job , ext_job_free__); +} + + +ext_job_type * ext_joblist_get_job(const ext_joblist_type * joblist , const char * job_name) { + if (hash_has_key(joblist->jobs , job_name)) + return (ext_job_type*)hash_get(joblist->jobs , job_name); + else { + util_abort("%s: asked for job:%s which does not exist\n",__func__ , job_name); + return NULL; + } +} + + +ext_job_type * ext_joblist_get_job_copy(const ext_joblist_type * joblist , const char * job_name) { + if (hash_has_key(joblist->jobs , job_name)) + return ext_job_alloc_copy((const ext_job_type*)hash_get(joblist->jobs , job_name)); + else { + util_abort("%s: asked for job:%s which does not exist\n",__func__ , job_name); + return NULL; + } +} + + +bool ext_joblist_has_job(const ext_joblist_type * joblist , const char * job_name) { + return hash_has_key(joblist->jobs , job_name); +} + + +stringlist_type * ext_joblist_alloc_list( const ext_joblist_type * joblist) { + return hash_alloc_stringlist( joblist->jobs ); +} + + +/** + Will attempt to remove the job @job_name from the joblist; if the + job is marked as a shared_job (i.e. installed centrally) the user + is not allowed to delete it. In this case the function will fail + silently. + + Returns true if the job is actually removed, and false otherwise. +*/ + +bool ext_joblist_del_job( ext_joblist_type * joblist , const char * job_name ) { + ext_job_type * job = ext_joblist_get_job( joblist , job_name ); + if (!ext_job_is_shared( job )) { + hash_del( joblist->jobs , job_name ); + return true; + } else + return false; +} + + +hash_type * ext_joblist_get_jobs( const ext_joblist_type * joblist ) { + return joblist->jobs; +} + +void ext_joblist_add_jobs_in_directory(ext_joblist_type * joblist , const char * path, const char * license_root_path, bool user_mode, bool search_path ) { + DIR * dirH = opendir( path ); + if (dirH) { + while (true) { + struct dirent * entry = readdir( dirH ); + if (entry != NULL) { + if ((strcmp(entry->d_name , ".") != 0) && (strcmp(entry->d_name , "..") != 0)) { + char * full_path = (char*)util_alloc_filename( path , entry->d_name , NULL ); + if (util_is_file( full_path )) { + ext_job_type * new_job = ext_job_fscanf_alloc(entry->d_name, license_root_path, user_mode, full_path, search_path); + if (new_job != NULL) { + ext_joblist_add_job(joblist, entry->d_name, new_job); + } + else{ + fprintf(stderr," Failed to add forward model job: %s \n",full_path); + } + } + free( full_path ); + } + } else + break; + } + closedir( dirH ); + } else + fprintf(stderr, "** Warning: failed to open jobs directory: %s\n", path); +} + + +int ext_joblist_get_size( const ext_joblist_type * joblist ) { + return hash_get_size( joblist->jobs ); +} + diff --git a/libres/lib/job_queue/forward_model.cpp b/libres/lib/job_queue/forward_model.cpp new file mode 100644 index 00000000000..2c7d8181d65 --- /dev/null +++ b/libres/lib/job_queue/forward_model.cpp @@ -0,0 +1,215 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'forward_model.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 + + +/** + This file implements a 'forward-model' object. I +*/ + +struct forward_model_struct { + vector_type * jobs; /* The actual jobs in this forward model. */ + const ext_joblist_type * ext_joblist; /* This is the list of external jobs which have been installed - which we can choose from. */ +}; + +#define DEFAULT_JOB_JSON "jobs.json" +#define DEFAULT_STATUS_JSON "status.json" +#define DEFAULT_JOB_MODULE "jobs.py" +#define DEFAULT_JOBLIST_NAME "jobList" + + + + +forward_model_type * forward_model_alloc(const ext_joblist_type * ext_joblist) { + forward_model_type * forward_model = (forward_model_type*)util_malloc( sizeof * forward_model ); + + forward_model->jobs = vector_alloc_new(); + forward_model->ext_joblist = ext_joblist; + + return forward_model; +} + + +/** + Allocates and returns a stringlist with all the names in the + current forward_model. +*/ +stringlist_type * forward_model_alloc_joblist( const forward_model_type * forward_model ) { + stringlist_type * names = stringlist_alloc_new( ); + int i; + for (i=0; i < vector_get_size( forward_model->jobs ); i++) { + const ext_job_type * job = (const ext_job_type*)vector_iget_const( forward_model->jobs , i); + stringlist_append_copy( names , ext_job_get_name( job )); + } + + return names; +} + + +/** + This function adds the job named 'job_name' to the forward model. The return + value is the newly created ext_job instance. This can be used to set private + arguments for this job. +*/ + +ext_job_type * forward_model_add_job(forward_model_type * forward_model , const char * job_name) { + ext_job_type * new_job = ext_joblist_get_job_copy(forward_model->ext_joblist , job_name); + vector_append_owned_ref( forward_model->jobs , new_job , ext_job_free__); + return new_job; +} + + +void forward_model_clear( forward_model_type * forward_model ) { + vector_clear( forward_model->jobs ); +} + + + +void forward_model_free( forward_model_type * forward_model) { + vector_free( forward_model->jobs ); + free(forward_model); +} + +/* + Used with SIMULATION_JOB keyword +*/ + +void forward_model_parse_job_args(forward_model_type * forward_model, const stringlist_type * list, const subst_list_type * define_args ) { + + stringlist_type * args = stringlist_alloc_deep_copy(list); + const char * job_name = stringlist_iget(args, 0); + ext_job_type * current_job = forward_model_add_job(forward_model , job_name); + ext_job_free_deprecated_argv(current_job); + stringlist_idel(args, 0); + ext_job_set_args(current_job, args); + ext_job_set_define_args(current_job, define_args); +} + +/** + DEPRECATED, used with the old FORWARD_MODEL keyword + + this function takes an input string of the type: + + job3(arg1 = value1, arg2 = value2, arg3= value3) + + and adds a job to the forward. observe the following rules: + + * if the function takes private arguments it is not allowed with space + between the end of the function name and the opening parenthesis. + +*/ + +void forward_model_parse_job_deprecated_args(forward_model_type * forward_model, const char * input_string, const subst_list_type * define_args ) { + char * p1 = (char *) input_string; + char * job_name; + { + int job_length = strcspn(p1 , " ("); /* scanning until we meet ' ' or '(' */ + job_name = util_alloc_substring_copy(p1 , 0 , job_length); + p1 += job_length; + } + + ext_job_type * current_job = forward_model_add_job(forward_model , job_name); + + if (*p1 == '(') { /* the function has arguments. */ + int arg_length = strcspn(p1 , ")"); + if (arg_length == strlen(p1)) + util_abort("%s: paranthesis not terminated for job:%s \n",__func__ , job_name); + { + char * arg_string = (char *)util_alloc_substring_copy((p1 + 1) , 0 , arg_length - 1); + ext_job_set_private_args_from_string( current_job , arg_string ); + ext_job_set_define_args(current_job, define_args); + free( arg_string ); + } + } + + free(job_name); +} + + + +static void forward_model_json_fprintf(const forward_model_type * forward_model, + const char * run_id, + const char * path, + const char * data_root, + const subst_list_type * global_args, + mode_t umask, + const env_varlist_type * varlist) { + char * json_file = (char*)util_alloc_filename(path , DEFAULT_JOB_JSON, NULL); + FILE * stream = util_fopen(json_file, "w"); + int job_index; + + fprintf(stream, "{\n"); + + fprintf(stream, "\"umask\" : \"%04o\",\n", umask); + fprintf(stream, "\"DATA_ROOT\": \"%s\",\n", data_root); + env_varlist_json_fprintf(varlist, stream); fprintf(stream, ",\n"); + fprintf(stream, "\"jobList\" : ["); + for (job_index=0; job_index < vector_get_size(forward_model->jobs); job_index++) { + const ext_job_type * job = (const ext_job_type*)vector_iget_const(forward_model->jobs , job_index); + ext_job_json_fprintf(job , job_index, stream , global_args); + if (job_index < (vector_get_size( forward_model->jobs ) - 1)) + fprintf(stream,",\n"); + } + fprintf(stream, "],\n"); + + fprintf(stream, "\"run_id\" : \"%s\",\n", run_id); + fprintf(stream, "\"ert_pid\" : \"%ld\"\n", (long)getpid()); //Long is big enough to hold __pid_t + fprintf(stream, "}\n"); + fclose(stream); + free(json_file); + + char * status_file = (char*)util_alloc_filename(path , DEFAULT_STATUS_JSON, NULL); + remove(status_file); + free(status_file); + +} + +void forward_model_formatted_fprintf(const forward_model_type * forward_model , + const char * run_id, + const char * path, + const char * data_root, + const subst_list_type * global_args, + mode_t umask, + const env_varlist_type * list) { + forward_model_json_fprintf( forward_model, run_id, path, data_root, global_args, umask, list); +} + +#undef DEFAULT_JOB_JSON +#undef DEFAULT_JOB_MODULE +#undef DEFAULT_JOBLIST_NAME + +ext_job_type * forward_model_iget_job( forward_model_type * forward_model , int index) { + return (ext_job_type*)vector_iget( forward_model->jobs , index ); +} + +int forward_model_get_length( const forward_model_type * forward_model ) { + return vector_get_size( forward_model->jobs ); +} diff --git a/libres/lib/job_queue/job_kw_definitions.cpp b/libres/lib/job_queue/job_kw_definitions.cpp new file mode 100644 index 00000000000..2f39a5c682a --- /dev/null +++ b/libres/lib/job_queue/job_kw_definitions.cpp @@ -0,0 +1,27 @@ + + +#include + +#include + + +config_item_types job_kw_get_type(const char * arg_type) { + + config_item_types type = CONFIG_INVALID; + + if (strcmp( arg_type , JOB_STRING_TYPE) == 0) + type = CONFIG_STRING; + else if (strcmp( arg_type , JOB_INT_TYPE) == 0) + type = CONFIG_INT; + else if (strcmp( arg_type , JOB_FLOAT_TYPE) == 0) + type = CONFIG_FLOAT; + else if (strcmp( arg_type , JOB_BOOL_TYPE) == 0) + type = CONFIG_BOOL; + else if (strcmp( arg_type , JOB_RUNTIME_FILE_TYPE) == 0) + type = CONFIG_RUNTIME_FILE; + else if (strcmp( arg_type , JOB_RUNTIME_INT_TYPE) == 0) + type = CONFIG_RUNTIME_INT; + + return type; +} + diff --git a/libres/lib/job_queue/job_list.cpp b/libres/lib/job_queue/job_list.cpp new file mode 100644 index 00000000000..06442c4e50d --- /dev/null +++ b/libres/lib/job_queue/job_list.cpp @@ -0,0 +1,145 @@ +/* + Copyright (C) 2015 Equinor ASA, Norway. + + The file 'job_list.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 + + +#define JOB_LIST_TYPE_ID 8154222 + +struct job_list_struct { + UTIL_TYPE_ID_DECLARATION; + int active_size; + int alloc_size; + job_queue_node_type ** jobs; + pthread_rwlock_t lock; +}; + + +UTIL_IS_INSTANCE_FUNCTION( job_list , JOB_LIST_TYPE_ID ) +UTIL_SAFE_CAST_FUNCTION( job_list , JOB_LIST_TYPE_ID ) + +job_list_type * job_list_alloc() { + job_list_type * job_list = (job_list_type*)util_malloc( sizeof * job_list ); + UTIL_TYPE_ID_INIT( job_list , JOB_LIST_TYPE_ID ); + job_list->active_size = 0; + job_list->alloc_size = 0; + job_list->jobs = NULL; + pthread_rwlock_init( &job_list->lock , NULL); + return job_list; +} + + +void job_list_reset( job_list_type * job_list ) { + int queue_index; + for (queue_index = 0; queue_index < job_list->active_size; queue_index++) { + job_queue_node_type * node = job_list_iget_job( job_list , queue_index ); + job_queue_node_free( node ); + job_list->jobs[queue_index] = NULL; + } + job_list->active_size = 0; +} + + +int job_list_get_size( const job_list_type * job_list ) { + return job_list->active_size; +} + + +/* + This takes ownership to the job node instance. +*/ +void job_list_add_job( job_list_type * job_list , job_queue_node_type * job_node ) { + if (job_list->alloc_size == job_list->active_size) { + +#ifdef QUEUE_DEBUG + int new_alloc_size = job_list->alloc_size + 1; + job_queue_node_type ** new_jobs = (job_queue_node_type**)util_malloc( sizeof * new_jobs * new_alloc_size ); + memcpy( new_jobs , job_list->jobs , sizeof * new_jobs * job_list->active_size ); + free( job_list->jobs ); + job_list->jobs = new_jobs; +#else + int new_alloc_size = util_int_max( 16 , job_list->alloc_size * 2); + job_list->jobs = (job_queue_node_type**)util_realloc( job_list->jobs , sizeof * job_list->jobs * new_alloc_size ); +#endif + + job_list->alloc_size = new_alloc_size; + } + + { + int queue_index = job_list_get_size( job_list ); + job_queue_node_set_queue_index(job_node, queue_index ); + job_list->jobs[queue_index] = job_node; + } + job_list->active_size++; + +} + + +job_queue_node_type * job_list_iget_job( const job_list_type * job_list , int queue_index) { + if (queue_index >= 0 && queue_index < job_list->active_size) + return job_list->jobs[queue_index]; + else { + util_abort("%s: invalid queue_index:%d Valid range: [0,%d)\n", __func__, queue_index, job_list->active_size); + return NULL; + } +} + + +void job_list_free( job_list_type * job_list ) { + if (job_list->alloc_size > 0) { + job_list_reset( job_list ); + free( job_list->jobs ); + } + free( job_list ); +} + + + +void job_list_get_wrlock( job_list_type * list) { + pthread_rwlock_wrlock( &list->lock ); +} + +void job_list_get_rdlock( job_list_type * list) { + pthread_rwlock_rdlock( &list->lock ); +} + + +void job_list_unlock( job_list_type * list) { + pthread_rwlock_unlock( &list->lock ); +} + + +void job_list_reader_wait( job_list_type * list, int usleep_time1, int usleep_time2) { + if (pthread_rwlock_tryrdlock( &list->lock ) == 0) { + // Seems to be no writers waiting - take a short sleep and return. + pthread_rwlock_unlock( &list->lock ); + usleep( usleep_time1 ); + } else + // A writer already has the lock - let more writers get access; sleep longer. + usleep( usleep_time2 ); + +} diff --git a/libres/lib/job_queue/job_node.cpp b/libres/lib/job_queue/job_node.cpp new file mode 100644 index 00000000000..7ac29e49ed2 --- /dev/null +++ b/libres/lib/job_queue/job_node.cpp @@ -0,0 +1,768 @@ +/* + Copyright (C) 2015 Equinor ASA, Norway. + + The file 'job_node.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 JOB_QUEUE_NODE_TYPE_ID 3315299 +#define INVALID_QUEUE_INDEX -999 + +struct job_queue_node_struct { + UTIL_TYPE_ID_DECLARATION; + int num_cpu; /* How many cpu's will this job need - the driver is free to ignore if not relevant. */ + char *run_cmd; /* The path to the actual executable. */ + char *exit_file; /* The queue will look for the occurence of this file to detect a failure. */ + char *ok_file; /* The queue will look for this file to verify that the job was OK - can be NULL - in which case it is ignored. */ + char *status_file; /* The queue will look for this file to verify that the job is running or has run. */ + char *job_name; /* The name of the job. */ + char *run_path; /* Where the job is run - absolute path. */ + job_callback_ftype *done_callback; + job_callback_ftype *retry_callback; /* To determine if job can be retried */ + job_callback_ftype *exit_callback; /* Callback to perform any cleanup */ + void *callback_arg; + int argc; /* The number of commandline arguments to pass when starting the job. */ + char **argv; /* The commandline arguments. */ + int queue_index; + + /*-----------------------------------------------------------------*/ + char *failed_job; /* Name of the job (in the chain) which has failed. */ + char *error_reason; /* The error message from the failed job. */ + char *stderr_capture; + char *stderr_file; /* Name of the file containing stderr information. */ + /*-----------------------------------------------------------------*/ + + int submit_attempt; /* Which attempt is this ... */ + job_status_type job_status; /* The current status of the job. */ + bool confirmed_running;/* Set to true if file status_file has been detected written. */ + pthread_mutex_t data_mutex; /* Protecting the access to the job_data pointer. */ + void *job_data; /* Driver specific data about this job - fully handled by the driver. */ + time_t submit_time; /* When was the job added to job_queue - the FIRST TIME. */ + time_t sim_start; /* When did the job change status -> RUNNING - the LAST TIME. */ + time_t sim_end ; /* When did the job finish successfully */ + time_t max_confirm_wait;/* Max waiting between sim_start and confirmed_running is 2 minutes */ + time_t progress_timestamp; /* Timestamp of the status update update file. */ +}; + + + +void job_queue_node_free_error_info( job_queue_node_type * node ) { + free(node->error_reason); + free(node->stderr_capture); + free(node->stderr_file); + free(node->failed_job); +} + + + +/* + When the job script has detected failure it will create a "EXIT" + file in the runpath directory; this function will inspect the EXIT + file and determine which job has failed, the reason the job script + has given to fail the job (typically missing TARGET_FILE) and + capture the stderr from the job. + + The file is XML formatted: + + ------------------------------------------------ + + + Name of job + Reason why the job failed + + Capture of stderr from the job, can typically be + a multiline string. + + + ------------------------------------------------ + + This format is written by the dump_EXIT_file() function in the + job_dispatch.py script. +*/ + +/* + This extremely half-assed XML "parsing" should of course be kept a + secret... +*/ + +static char * __alloc_tag_content( const char * xml_buffer , const char * tag) { + char * open_tag = (char*)util_alloc_sprintf("<%s>" , tag); + char * close_tag = (char*)util_alloc_sprintf("" , tag); + + const char * start_ptr = strstr( xml_buffer , open_tag ); + const char * end_ptr = strstr( xml_buffer , close_tag ); + char * tag_content = NULL; + + if ((start_ptr != NULL) && (end_ptr != NULL)) { + int length; + start_ptr += strlen(open_tag); + + length = end_ptr - start_ptr; + tag_content = util_alloc_substring_copy( start_ptr , 0 , length ); + } + + free( open_tag ); + free( close_tag ); + return tag_content; +} + + + + +/** + This code is meant to capture which of the jobs has failed; why it + has failed and the stderr stream of the failing job. Depending on + the failure circumstances the EXIT file might not be around. +*/ + +void job_queue_node_fscanf_EXIT( job_queue_node_type * node ) { + job_queue_node_free_error_info( node ); + if (node->exit_file) { + if (util_file_exists( node->exit_file )) { + char * xml_buffer = util_fread_alloc_file_content( node->exit_file, NULL); + + node->failed_job = __alloc_tag_content( xml_buffer , "job" ); + node->error_reason = __alloc_tag_content( xml_buffer , "reason" ); + node->stderr_capture = __alloc_tag_content( xml_buffer , "stderr"); + node->stderr_file = __alloc_tag_content( xml_buffer , "stderr_file"); + + free( xml_buffer ); + } else + node->failed_job = util_alloc_sprintf("EXIT file:%s not found - load failure?" , node->exit_file); + } +} + + + + + + +UTIL_IS_INSTANCE_FUNCTION( job_queue_node , JOB_QUEUE_NODE_TYPE_ID ) +UTIL_SAFE_CAST_FUNCTION( job_queue_node , JOB_QUEUE_NODE_TYPE_ID ) + + + +int job_queue_node_get_queue_index( const job_queue_node_type * node ) { + if (node->queue_index == INVALID_QUEUE_INDEX) + util_abort("%s: internal error: asked for not-yet-initialized node->queue_index\n",__func__); + return node->queue_index; +} + +void job_queue_node_set_queue_index( job_queue_node_type * node , int queue_index) { + if (node->queue_index == INVALID_QUEUE_INDEX) + node->queue_index = queue_index; + else + util_abort("%s: internal error: attempt to reset queue_index \n", __func__); +} + + +/* + The error information is retained even after the job has completed + completely, so that calling scope can ask for it - that is the + reason there are separate free() and clear functions for the error related fields. +*/ + +void job_queue_node_free_data(job_queue_node_type * node) { + free( node->job_name ); + free( node->exit_file ); + free( node->ok_file ); + free( node->status_file ); + free( node->run_cmd ); + util_free_stringlist( node->argv , node->argc ); +} + + +void job_queue_node_free(job_queue_node_type * node) { + /* + The callback_arg will not be freed; that will be the responsability of the + calling scope. + */ + job_queue_node_free_data(node); + job_queue_node_free_error_info(node); + free(node->run_path); + free(node); +} + + +job_status_type job_queue_node_get_status(const job_queue_node_type * node) { + return node->job_status; +} + + + + + + +/******************************************************************/ +/* + These four functions all require that the caller has aquired the + data lock before entering. +*/ + + + + + +void job_queue_node_reset_submit_attempt( job_queue_node_type * node) { + node->submit_attempt = 0; +} + +void job_queue_node_dec_submit_attempt( job_queue_node_type * node) { + node->submit_attempt--; +} + +int job_queue_node_get_submit_attempt( const job_queue_node_type * node) { + return node->submit_attempt; +} + + + + + + + + + +job_queue_node_type * job_queue_node_alloc_simple( const char * job_name , + const char * run_path , + const char * run_cmd , + int argc , + const char ** argv) { + return job_queue_node_alloc( job_name , run_path , run_cmd , argc , argv , 1, NULL , NULL, NULL, NULL, NULL, NULL, NULL); +} + +job_queue_node_type * job_queue_node_alloc_python( const char * job_name , + const char * run_path , + const char * run_cmd , + int argc , + const stringlist_type * arguments, + int num_cpu, + const char * ok_file, + const char * status_file, + const char * exit_file) { + char ** argv = stringlist_alloc_char_ref( arguments ); + job_queue_node_type * out = job_queue_node_alloc( job_name , run_path , run_cmd , argc , argv , num_cpu, ok_file , status_file, exit_file, NULL, NULL, NULL, NULL); + free(argv); + return out; +} + + + +job_queue_node_type * job_queue_node_alloc( const char * job_name , + const char * run_path , + const char * run_cmd , + int argc , + char const * const * argv, + int num_cpu, + const char * ok_file, + const char * status_file, + const char * exit_file, + job_callback_ftype * done_callback, + job_callback_ftype * retry_callback, + job_callback_ftype * exit_callback, + void * callback_arg) { + + if (!util_is_directory( run_path )) + return NULL; + + job_queue_node_type * node = (job_queue_node_type*)util_malloc(sizeof * node ); + node->confirmed_running = false; + node->progress_timestamp = time(NULL); + UTIL_TYPE_ID_INIT( node , JOB_QUEUE_NODE_TYPE_ID ); + + /* The data initialized in this block should *NEVER* change. */ + std::string path = job_name; + std::string basename = path.substr(path.find_last_of("/\\") + 1); + node->job_name = util_alloc_string_copy( basename.data() ); + + node->run_path = util_alloc_realpath( run_path ); + + node->run_cmd = util_alloc_string_copy( run_cmd ); + node->argc = argc; + node->argv = util_alloc_stringlist_copy( argv , argc ); // Please fix const ** in libecl + node->num_cpu = num_cpu; + + if (ok_file) + node->ok_file = util_alloc_filename(node->run_path , ok_file , NULL); + else + node->ok_file = NULL; + + if (status_file) + node->status_file = util_alloc_filename(node->run_path , status_file , NULL); + else + node->status_file = NULL; + + if (exit_file) + node->exit_file = util_alloc_filename(node->run_path , exit_file , NULL); + else + node->exit_file = NULL; + + node->exit_callback = exit_callback; + node->retry_callback = retry_callback; + node->done_callback = done_callback; + node->callback_arg = callback_arg; + + node->error_reason = NULL; + node->stderr_capture = NULL; + node->stderr_file = NULL; + node->failed_job = NULL; + + node->job_status = JOB_QUEUE_NOT_ACTIVE; + node->queue_index = INVALID_QUEUE_INDEX; + node->submit_attempt = 0; + node->job_data = NULL; // assume allocation is run in single thread mode + node->sim_start = 0; + node->sim_end = 0; + node->submit_time = time( NULL ); + node->max_confirm_wait= 60*2; // 2 minutes before we consider job dead. + + pthread_mutex_init( &node->data_mutex , NULL ); + return node; +} + + +const char * job_queue_node_get_exit_file( const job_queue_node_type * node) { + return node->exit_file; +} + + +const char * job_queue_node_get_ok_file( const job_queue_node_type * node) { + return node->ok_file; +} + + +time_t job_queue_node_get_sim_start( const job_queue_node_type * node ) { + return node->sim_start; +} + + +time_t job_queue_node_get_sim_end( const job_queue_node_type * node ) { + return node->sim_end; +} + +double job_queue_node_time_since_sim_start (const job_queue_node_type * node ) { + return util_difftime_seconds( node->sim_start , time(NULL)); +} + +bool job_queue_node_run_DONE_callback( job_queue_node_type * node ) { + bool OK = true; + if (node->done_callback) + OK = node->done_callback( node->callback_arg ); + + return OK; +} + + +bool job_queue_node_run_RETRY_callback( job_queue_node_type * node ) { + bool retry = false; + if (node->retry_callback) + retry = node->retry_callback( node->callback_arg ); + + return retry; +} + + +void job_queue_node_run_EXIT_callback( job_queue_node_type * node ) { + if (node->exit_callback) + node->exit_callback( node->callback_arg ); +} + +void job_queue_node_set_status(job_queue_node_type * node , job_status_type new_status) { + if (new_status == node->job_status) + return; + + res_log_fdebug("Set %s(%d) to %s", + node->job_name, + node->queue_index, + job_status_get_name(new_status)); + node->job_status = new_status; + + /* + We record sim start when the node is in state JOB_QUEUE_WAITING + to be sure that we do not miss the start time completely for + very fast jobs which are registered in the state + JOB_QUEUE_RUNNING. + */ + if (new_status == JOB_QUEUE_WAITING) + node->sim_start = time( NULL ); + + if (new_status == JOB_QUEUE_RUNNING) + node->sim_start = time( NULL ); + + + if (!(new_status & JOB_QUEUE_COMPLETE_STATUS)) + return; + + node->sim_end = time( NULL ); + node->progress_timestamp = node->sim_end; + + if (new_status == JOB_QUEUE_FAILED) + job_queue_node_fscanf_EXIT( node ); + +} + + +submit_status_type job_queue_node_submit(job_queue_node_type * node, + job_queue_status_type * status, + queue_driver_type * driver) { + submit_status_type submit_status; + pthread_mutex_lock( &node->data_mutex ); + + void * job_data = queue_driver_submit_job( driver, + node->run_cmd, + node->num_cpu, + node->run_path, + node->job_name, + node->argc, + (const char **) node->argv); + job_status_type old_status; + job_status_type new_status; + + if (job_data == NULL) { + /* + In this case the status of the job itself will be + unmodified; i.e. it will still be WAITING, and a new attempt + to submit it will be performed in the next round. + */ + submit_status = SUBMIT_DRIVER_FAIL; + res_log_fwarning("Failed to submit job %s (attempt %d)", + node->job_name, + node->submit_attempt); + goto cleanup; + } + + old_status = node->job_status; + new_status = JOB_QUEUE_SUBMITTED; + + res_log_finfo("Submitted job %s (attempt %d)", + node->job_name, + node->submit_attempt); + + node->job_data = job_data; + node->submit_attempt++; + /* + The status JOB_QUEUE_SUBMITTED is internal, and not + exported anywhere. The job_queue_update_status() will + update this to PENDING or RUNNING at the next call. The + important difference between SUBMITTED and WAITING is + that SUBMITTED have job_data != NULL and the + job_queue_node free function must be called on it. + */ + submit_status = SUBMIT_OK; + job_queue_node_set_status( node , new_status); + job_queue_status_transition(status, old_status, new_status); + + +cleanup: + pthread_mutex_unlock( &node->data_mutex ); + return submit_status; +} + +submit_status_type job_queue_node_submit_simple(job_queue_node_type * node, + queue_driver_type * driver) { + submit_status_type submit_status; + pthread_mutex_lock( &node->data_mutex ); + job_queue_node_set_status( node , JOB_QUEUE_SUBMITTED); + void * job_data = queue_driver_submit_job( driver, + node->run_cmd, + node->num_cpu, + node->run_path, + node->job_name, + node->argc, + (const char **) node->argv); + job_status_type old_status; + job_status_type new_status; + + if (job_data == NULL) { + /* + In this case the status of the job itself will be + unmodified; i.e. it will still be WAITING, and a new attempt + to submit it will be performed in the next round. + */ + submit_status = SUBMIT_DRIVER_FAIL; + res_log_fwarning("Failed to submit job %s (attempt %d)", + node->job_name, + node->submit_attempt); + pthread_mutex_unlock( &node->data_mutex ); + return submit_status; + } + + old_status = node->job_status; + new_status = JOB_QUEUE_SUBMITTED; + + res_log_finfo("Submitted job %s (attempt %d)", + node->job_name, + node->submit_attempt); + + node->job_data = job_data; + node->submit_attempt++; + /* + The status JOB_QUEUE_SUBMITTED is internal, and not + exported anywhere. The job_queue_update_status() will + update this to PENDING or RUNNING at the next call. The + important difference between SUBMITTED and WAITING is + that SUBMITTED have job_data != NULL and the + job_queue_node free function must be called on it. + */ + submit_status = SUBMIT_OK; + job_queue_node_set_status( node , new_status); + pthread_mutex_unlock( &node->data_mutex ); + return submit_status; +} + + + +static bool job_queue_node_status_update_confirmed_running__(job_queue_node_type * node) { + if (node->confirmed_running) + return true; + + if (!node->status_file) { + node->confirmed_running = true; + return true; + } + + if (util_file_exists(node->status_file)) + node->confirmed_running = true; + return node->confirmed_running; +} + + +static void job_queue_node_update_timestamp(job_queue_node_type * node) { + if (node->job_status != JOB_QUEUE_RUNNING) + return; + + if (!node->status_file) + return; + + time_t mtime = util_file_mtime( node->status_file ); + if (mtime > 0) + node->progress_timestamp = mtime; +} + + +// if status = running, and current_time > sim_start + max_confirm_wait +// (usually 2 min), check if job is confirmed running (status_file exists). +// If not confirmed, set job to JOB_QUEUE_FAILED. +bool job_queue_node_update_status(job_queue_node_type * node, + job_queue_status_type * status, + queue_driver_type * driver ) { + bool status_change = false; + pthread_mutex_lock(&node->data_mutex); + + job_status_type current_status; + bool confirmed; + + if (!node->job_data) + goto cleanup; + + current_status = job_queue_node_get_status(node); + + confirmed = job_queue_node_status_update_confirmed_running__(node); + + if ((current_status & JOB_QUEUE_RUNNING) && !confirmed) { + // it's running, but not confirmed running. + double runtime = job_queue_node_time_since_sim_start(node); + if (runtime >= node->max_confirm_wait) { + res_log_finfo("max_confirm_wait (%d) has passed since sim_start" + "without success; %s is dead (attempt %d)", + node->max_confirm_wait, + node->job_name, + node->submit_attempt); + job_status_type new_status = JOB_QUEUE_DO_KILL_NODE_FAILURE; + status_change = job_queue_status_transition(status, current_status, new_status); + job_queue_node_set_status(node, new_status); + } + } + + current_status = job_queue_node_get_status(node); + if (current_status & JOB_QUEUE_CAN_UPDATE_STATUS) { + job_status_type new_status = queue_driver_get_status( driver , node->job_data); + status_change = job_queue_status_transition(status , current_status , new_status); + job_queue_node_set_status(node,new_status); + } + +cleanup: + job_queue_node_update_timestamp(node); + pthread_mutex_unlock( &node->data_mutex ); + return status_change; +} + +bool job_queue_node_update_status_simple(job_queue_node_type * node, + queue_driver_type * driver ) { + bool status_change = false; + pthread_mutex_lock( &node->data_mutex ); + job_status_type current_status; + bool confirmed; + + if (!node->job_data){ + job_queue_node_update_timestamp(node); + pthread_mutex_unlock( &node->data_mutex ); + return status_change; + } + + current_status = job_queue_node_get_status(node); + + confirmed = job_queue_node_status_update_confirmed_running__(node); + + if ((current_status & JOB_QUEUE_RUNNING) && !confirmed) { + // it's running, but not confirmed running. + double runtime = job_queue_node_time_since_sim_start(node); + if (runtime >= node->max_confirm_wait) { + res_log_finfo("max_confirm_wait (%d) has passed since sim_start" + "without success; %s is dead (attempt %d)", + node->max_confirm_wait, + node->job_name, + node->submit_attempt); + job_status_type new_status = JOB_QUEUE_DO_KILL_NODE_FAILURE; + job_queue_node_set_status(node, new_status); + } + } + + current_status = job_queue_node_get_status(node); + if (current_status & JOB_QUEUE_CAN_UPDATE_STATUS) { + job_status_type new_status = queue_driver_get_status( driver , node->job_data); + job_queue_node_set_status(node,new_status); + } + pthread_mutex_unlock( &node->data_mutex ); + return status_change; +} + +bool job_queue_node_status_transition(job_queue_node_type * node, + job_queue_status_type * status, + job_status_type new_status) { + bool status_change = false; + pthread_mutex_lock( &node->data_mutex ); + + job_status_type old_status = job_queue_node_get_status( node ); + status_change = job_queue_status_transition(status , old_status, new_status); + + if (status_change) + job_queue_node_set_status( node , new_status ); + + pthread_mutex_unlock( &node->data_mutex ); + return status_change; +} + +bool job_queue_node_kill(job_queue_node_type * node, + job_queue_status_type * status, + queue_driver_type * driver) { + bool result = false; + pthread_mutex_lock( &node->data_mutex ); + + job_status_type current_status = job_queue_node_get_status( node ); + if (current_status & JOB_QUEUE_CAN_KILL) { + /* + If the job is killed before it is even started no driver + specific job data has been assigned; we therefor must check + the node->job_data pointer before entering. + */ + if (node->job_data) { + queue_driver_kill_job( driver , node->job_data ); + queue_driver_free_job( driver , node->job_data ); + node->job_data = NULL; + } + job_queue_status_transition(status, current_status, JOB_QUEUE_IS_KILLED); + job_queue_node_set_status( node , JOB_QUEUE_IS_KILLED); + res_log_finfo("job %s set to killed", + node->job_name); + result = true; + } else { + res_log_fwarning("node_kill called but cannot kill %s", + node->job_name); + } + + pthread_mutex_unlock( &node->data_mutex ); + return result; +} + +bool job_queue_node_kill_simple(job_queue_node_type * node, + queue_driver_type * driver) { + bool result = false; + pthread_mutex_lock( &node->data_mutex ); + job_status_type current_status = job_queue_node_get_status( node ); + if (current_status & JOB_QUEUE_CAN_KILL) { + /* + If the job is killed before it is even started no driver + specific job data has been assigned; we therefor must check + the node->job_data pointer before entering. + */ + if (node->job_data) { + queue_driver_kill_job( driver , node->job_data ); + queue_driver_free_job( driver , node->job_data ); + node->job_data = NULL; + } + job_queue_node_set_status( node , JOB_QUEUE_IS_KILLED); + res_log_finfo("job %s set to killed", + node->job_name); + result = true; + } else { + res_log_fwarning("node_kill called but cannot kill %s", + node->job_name); + } + pthread_mutex_unlock( &node->data_mutex ); + return result; +} + +/* + This frees the storage allocated by the driver - the storage + allocated by the queue layer is retained. + + In the case of jobs which are first marked as successful by the + queue layer, and then subsequently set to status EXIT by the + DONE_callback this function will be called twice; i.e. we must + protect against a double free. +*/ + +void job_queue_node_free_driver_data(job_queue_node_type * node, + queue_driver_type * driver) { + pthread_mutex_lock( &node->data_mutex ); + + if (node->job_data) + queue_driver_free_job( driver , node->job_data ); + node->job_data = NULL; + + pthread_mutex_unlock( &node->data_mutex ); +} + + +/* + This returns a pointer to a very internal datastructure; used by the + Job class in Python which interacts directly with the driver + implementation. This is too low level, and the whole Driver / Job + implementation in Python should be changed to only expose the higher + level queue class. +*/ + +void * job_queue_node_get_driver_data( job_queue_node_type * node ) { + return node->job_data; +} + + +time_t job_queue_node_get_timestamp(const job_queue_node_type * node) { + return node->progress_timestamp; +} diff --git a/libres/lib/job_queue/job_queue.cpp b/libres/lib/job_queue/job_queue.cpp new file mode 100644 index 00000000000..bbdad13f0c1 --- /dev/null +++ b/libres/lib/job_queue/job_queue.cpp @@ -0,0 +1,1278 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'job_queue.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. +*/ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE /* Must define this to get access to pthread_rwlock_t */ +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + + +/** + + The running of external jobs is handled through an abstract + job_queue implemented in this file; the job_queue then contains a + 'driver' which actually runs the job. All drivers must support the + following functions + + submit: This will submit a job, and return a pointer to a + newly allocated queue_job instance. + + clean: This will clear up all resources used by the job. + + abort: This will stop the job, and then call clean. + + status: This will get the status of the job. + + + When calling the various driver functions the queue layer needs to + dereference the driver structures, i.e. to get access to the + driver->submit_jobs function. This is currently (rather clumsily?? + implemented like this): + + When implementing a driver the driver struct MUST start like + this: + + struct some_driver { + UTIL_TYPE_ID_DECLARATION + QUEUE_DRIVER_FUNCTIONS + .... + .... + } + + The function allocating a driver instance will just return a + (void *) however in the queue layer the driver is stored as a + queue_driver_type instance which is a struct like this: + + struct queue_driver_struct { + UTIL_TYPE_ID_DECLARATION + QUEUE_DRIVER_FIELDS + } + + I.e. it only contains the pointers common to all the driver + implementations. When calling a driver function the spesific + driver will cast to it's datatype. + + Observe that this library also contains the files ext_joblist and + ext_job, those files implement a particular way of dispatching + external jobs in a series; AFTER THEY HAVE BEEN SUBMITTED. So seen + from the this scope those files do not provide any particluar + functionality; there is no compile-time dependencies either. +*/ + + + +/* + Some words about status + ======================= + + The status of a particular job is given by the job_status field of + the job_queue_node_type, the possible values are given by the enum + job_status_type, defined in queue_driver.h. + + To actually __GET__ the status of a job we use the driver->status() + function which will invoke a driver specific function and return the + new status. + + 1. The driver->status() function is invoked by the + job_queue_update_status() function. This should be invoked by + the same thread as is running the main queue management in + job_queue_run_jobs(). + + + 2. The actual change of status is handled by the function + job_queue_change_node_status(); arbitrary assignments of the + type job->status = new_status is STRICTLY ILLEGAL. + + + 3. When external functions query about the status of a particular + job they get the status value currently stored (i.e. cached) in + the job_node; external scope can NOT initiate a + driver->status() function call. + + This might result in external scope getting a outdated status - + live with it. + + + 4. The name 'status' indicates that this is read-only property; + that is actually not the case. In the main manager function + job_queue_run_jobs() action is taken based on the value of the + status field, and to initiate certain action on jobs the queue + system (and also external scope) can explicitly set the status + of a job (by using the job_queue_change_node_status() function). + + The most prominent example of this is when we want to run a + certain job again, that is achieved with: + + job_queue_node_change_status( queue , node , JOB_QUEUE_WAITING ); + + When the queue manager subsequently finds the job with status + 'JOB_QUEUE_WAITING' it will (re)submit this job. +*/ + + + +/* + Communicating success/failure between the job_script and the job_queue: + ======================================================================= + + The system for communicating success/failure between the queue system + (i.e. this file) and the job script is quite elaborate. There are + essentially three problems which make this complicated: + + 1. The exit status of the jobs is NOT reliably captured - the job + might very well fail without us detecting it with the exit + status. + + 2. Synchronizing of disks can be quite slow, so although a job has + completed successfully the files we expect to find might not + present. + + 3. There is layer upon layer here - this file scope (i.e. the + internal queue_system) spawns external jobs in the form of a job + script. This script again spawns a series of real external jobs + like e.g. ECLIPSE and RMS. The job_script does not reliably + capture the exit status of the external programs. + + + The approach to this is as follows: + + 1. If the job (i.e. the job script) finishes with a failure status + we communicate the failure back to the calling scope with no + more ado. + + 2. When a job has finished (seemingly OK) we try hard to determine + whether the job has failed or not. This is based on the + following tests: + + a) If the job has produced an EXIT file it has failed. + + b) If the job has produced an OK file it has succeeded. + + c) If neither EXIT nor OK files have been produced we spin for a + while waiting for one of the files, if none turn up we will + eventually mark the job as failed. + +*/ + + + + + +/** + This struct holds the job_queue information about one job. Observe + the following: + + 1. This struct is purely static - i.e. it is invisible outside of + this file-scope. + + 2. Typically the driver would like to store some additional + information, i.e. the PID of the running process for the local + driver; that is stored in a (driver specific) struct under the + field job_data. + + 3. If the driver detects that a job has failed it leaves an EXIT + file, the exit status is (currently) not reliably transferred + back to to the job_queue layer. + +*/ + +/*****************************************************************/ + +#define JOB_QUEUE_TYPE_ID 665210 + +struct job_queue_struct { + UTIL_TYPE_ID_DECLARATION; + job_list_type * job_list; + job_queue_status_type * status; + char * exit_file; /* The queue will look for the occurrence of this file to detect a failure. */ + char * ok_file; /* The queue will look for this file to verify that the job was OK - can be NULL - in which case it is ignored. */ + char * status_file; /* The queue will look for this file to verify that the job is running or has run. If not, ok_file is ignored. */ + queue_driver_type * driver; /* A pointer to a driver instance (LSF|LOCAL|RSH) which actually 'does it'. */ + + bool open; /* True if the queue has been reset and is ready for use, false if the queue has been used and not reset */ + bool user_exit; /* If there comes an external signal to abandon the whole thing user_exit will be set to true, and things start to dwindle down. */ + bool running; + bool pause_on; + bool submit_complete; + + int max_submit; /* The maximum number of submit attempts for one job. */ + int max_ok_wait_time; /* Seconds to wait for an OK file - when the job itself has said all OK. */ + int max_duration; /* Maximum allowed time for a job to run, 0 = unlimited */ + time_t stop_time; /* A job is only allowed to run until this time. 0 = no time set, ignore stop_time */ + time_t progress_timestamp; /* Global timestamp for last progress update. */ + unsigned long usleep_time; /* The sleep time before checking for updates. */ + pthread_mutex_t run_mutex; /* This mutex is used to ensure that ONLY one thread is executing the job_queue_run_jobs(). */ + thread_pool_type * work_pool; +}; + + + + + + +/* + Must hold on to: + + 1. A write lock for the job node. + 3. A read lock for the job_list + +*/ +static bool job_queue_change_node_status(job_queue_type * queue , job_queue_node_type * node , job_status_type new_status) { + return job_queue_node_status_transition( node , queue->status , new_status ); +} + + + + +/*****************************************************************/ + + + + + + + +/** + Observe that this function should only query the driver for state + change when the job is currently in one of the states: + + JOB_QUEUE_WAITING || JOB_QUEUE_PENDING || JOB_QUEUE_RUNNING + + The other state transitions are handled by the job_queue itself, + without consulting the driver functions. +*/ + +/* + Will return true if there is any status change. Must already hold + on to joblist readlock +*/ + +static bool job_queue_update_status(job_queue_type * queue ) { + bool update = false; + int ijob; + + for (ijob = 0; ijob < job_list_get_size( queue->job_list ); ijob++) { + job_queue_node_type * node = job_list_iget_job( queue->job_list , ijob ); + update |= job_queue_node_update_status( node , queue->status , queue->driver ); + queue->progress_timestamp = util_time_t_max(queue->progress_timestamp, job_queue_node_get_timestamp(node)); + } + return update; +} + +/* + Must hold on to joblist readlock +*/ + +static submit_status_type job_queue_submit_job(job_queue_type * queue , int queue_index) { + submit_status_type submit_status; + if (queue->user_exit || queue->pause_on) + submit_status = SUBMIT_QUEUE_CLOSED; /* The queue is currently not accepting more jobs. */ + else { + { + job_queue_node_type * node = job_list_iget_job( queue->job_list , queue_index ); + submit_status = job_queue_node_submit( node , queue->status , queue->driver ); + } + } + return submit_status; +} + + + + + + + + + +/** + Will return the number of jobs with status @status. + + #include + + printf("Running jobs...: %03d \n", job_queue_iget_status_summary( queue , JOB_QUEUE_RUNNING )); + printf("Waiting jobs:..: %03d \n", job_queue_iget_status_summary( queue , JOB_QUEUE_WAITING )); + + Observe that if this function is called repeatedly the status might change between + calls, with the consequence that the total number of jobs does not add up + properly. The handles itself autonomously so as long as the return value from this + function is only used for information purposes this does not matter. Alternatively + the function job_queue_export_status_summary(), which does proper locking, can be + used. +*/ + +int job_queue_iget_status_summary( const job_queue_type * queue , job_status_type status) { + return job_queue_status_get_count(queue->status, status); +} + +int job_queue_get_num_running( const job_queue_type * queue) { + return job_queue_iget_status_summary( queue , JOB_QUEUE_RUNNING ); +} + +int job_queue_get_num_pending( const job_queue_type * queue) { + return job_queue_iget_status_summary( queue , JOB_QUEUE_PENDING ); +} + +int job_queue_get_num_waiting( const job_queue_type * queue) { + return job_queue_iget_status_summary( queue , JOB_QUEUE_WAITING ); +} + +int job_queue_get_num_complete( const job_queue_type * queue) { + return job_queue_iget_status_summary( queue , JOB_QUEUE_SUCCESS ); +} + +int job_queue_get_active_size( const job_queue_type * queue ) { + return job_list_get_size( queue->job_list ); +} + +void job_queue_set_max_job_duration(job_queue_type * queue, int max_duration_seconds) { + queue->max_duration = max_duration_seconds; +} + +int job_queue_get_max_job_duration(const job_queue_type * queue) { + return queue->max_duration; +} + +void job_queue_set_job_stop_time(job_queue_type * queue, time_t time) { + queue->stop_time = time; +} + +time_t job_queue_get_job_stop_time(const job_queue_type * queue) { + return queue->stop_time; +} + +void job_queue_set_auto_job_stop_time(job_queue_type * queue) { + time_t sum_run_time_succeded_jobs = 0; + int num_succeded_jobs = 0; + + for (int i = 0; i < job_list_get_size( queue->job_list ); i++) { + if (job_queue_iget_job_status(queue,i) == JOB_QUEUE_SUCCESS) { + sum_run_time_succeded_jobs += difftime(job_queue_iget_sim_end(queue, i), job_queue_iget_sim_start(queue, i)); + num_succeded_jobs++; + } + } + + if (num_succeded_jobs > 0) { + time_t avg_run_time_succeded_jobs = sum_run_time_succeded_jobs / num_succeded_jobs; + time_t stop_time = time(NULL) + (avg_run_time_succeded_jobs * 0.25); + job_queue_set_job_stop_time(queue, stop_time); + } +} + +/** + Observe that jobs with status JOB_QUEUE_WAITING can also be killed; for those + jobs the kill should be interpreted as "Forget about this job for now and set + the status JOB_QUEUE_IS_KILLED", however it is important that we not call + the driver->kill() function on it because the job slot will have no data + (i.e. LSF jobnr), and the driver->kill() function will fail if presented with + such a job. + + Only jobs which have a status matching "JOB_QUEUE_CAN_KILL" can be + killed; if the job is not in a killable state the function will do + nothing. This includes trying to kill a job which is not even + found. + + Observe that jobs (slots) with status JOB_QUEUE_NOT_ACTIVE can NOT be + meaningfully killed; that is because these jobs have not yet been submitted + to the queue system, and there is not yet established a mapping between + external id and queue_index. + + Must hold on to joblist:read lock. +*/ + +static bool job_queue_kill_job_node( job_queue_type * queue , job_queue_node_type * node) { + bool result = job_queue_node_kill( node , queue->status , queue->driver ); + return result; +} + +#define ASSIGN_LOCKED_ATTRIBUTE( var , func , ...) \ +job_list_get_rdlock( queue->job_list ); \ +{ \ + job_queue_node_type * node = job_list_iget_job( queue->job_list , job_index ); \ + var = func(__VA_ARGS__); \ +} \ +job_list_unlock( queue->job_list ); + + + +bool job_queue_kill_job( job_queue_type * queue , int job_index) { + bool result; + ASSIGN_LOCKED_ATTRIBUTE( result , job_queue_kill_job_node , queue , node); + return result; +} + + +time_t job_queue_iget_sim_start( job_queue_type * queue, int job_index) { + time_t sim_start; + ASSIGN_LOCKED_ATTRIBUTE( sim_start , job_queue_node_get_sim_start , node ); + return sim_start; +} + + +time_t job_queue_iget_sim_end( job_queue_type * queue, int job_index) { + time_t sim_end; + ASSIGN_LOCKED_ATTRIBUTE( sim_end , job_queue_node_get_sim_end , node ); + return sim_end; +} + + +job_status_type job_queue_iget_job_status( job_queue_type * queue , int job_index) { + job_status_type job_status; + ASSIGN_LOCKED_ATTRIBUTE(job_status, job_queue_node_get_status , node ); + return job_status; +} + +/* + This returns a pointer to a very internal datastructure; used by the + Job class in Python which interacts directly with the driver + implementation. This is too low level, and the whole Driver / Job + implementation in Python should be changed to only expose the higher + level queue class. +*/ + +void * job_queue_iget_driver_data( job_queue_type * queue , int job_index) { + void * driver_data; + ASSIGN_LOCKED_ATTRIBUTE(driver_data, job_queue_node_get_driver_data , node ); + return driver_data; +} + + + + + + +static void job_queue_print_summary(job_queue_type *queue, bool status_change ) { + const char * status_fmt = "Waiting: %3d Pending: %3d Running: %3d Checking/Loading: %3d Failed: %3d Complete: %3d [ ]\b\b"; + int string_length = 105; + + if (status_change) { + for (int i=0; i < string_length; i++) + printf("\b"); + + { + int waiting = job_queue_status_get_count( queue->status , JOB_QUEUE_WAITING ); + int pending = job_queue_status_get_count( queue->status , JOB_QUEUE_PENDING ); + + /* + EXIT and DONE are included in "xxx_running", because the target + file has not yet been checked. + */ + int running = job_queue_status_get_count( queue->status , JOB_QUEUE_RUNNING ) + + job_queue_status_get_count( queue->status , JOB_QUEUE_DONE ) + + job_queue_status_get_count( queue->status , JOB_QUEUE_EXIT ); + int complete = job_queue_status_get_count( queue->status , JOB_QUEUE_SUCCESS ); + int failed = job_queue_status_get_count( queue->status , JOB_QUEUE_FAILED ) + + job_queue_status_get_count( queue->status , JOB_QUEUE_IS_KILLED ); + int loading = job_queue_status_get_count( queue->status , JOB_QUEUE_RUNNING_DONE_CALLBACK ); + + printf(status_fmt , waiting , pending , running , loading , failed , complete); + } + } +} + + + + + +bool job_queue_is_running( const job_queue_type * queue ) { + return queue->running; +} + + +static void job_queue_user_exit__( job_queue_type * queue ) { + int queue_index; + for (queue_index = 0; queue_index < job_list_get_size( queue->job_list ); queue_index++) { + job_queue_node_type * node = job_list_iget_job( queue->job_list , queue_index ); + + if (JOB_QUEUE_CAN_KILL & job_queue_node_get_status(node)) + job_queue_node_status_transition(node,queue->status,JOB_QUEUE_DO_KILL); + } +} + + +static bool job_queue_check_node_status_files(const job_queue_type * job_queue, + job_queue_node_type * node) { + const char * exit_file = job_queue_node_get_exit_file( node ); + if (exit_file && util_file_exists(exit_file)) + return false; // job has failed + + const char * ok_file = job_queue_node_get_ok_file( node ); + + // If the ok-file has not been set we just return true immediately. + if (!ok_file) + return true; + + int ok_sleep_time = 1; // Time to wait between checks for OK|EXIT file + int total_wait_time = 0; + + /* Wait for OK file */ + while (total_wait_time < job_queue->max_ok_wait_time) { + if (util_file_exists( ok_file )) + return true; + + if (exit_file && util_file_exists(exit_file)) + return false; // job has failed + + sleep( ok_sleep_time ); + total_wait_time += ok_sleep_time; + } + return false; +} + + +static void * job_queue_run_DONE_callback( void * arg ) { + arg_pack_type * arg_pack = arg_pack_safe_cast( arg ); + job_queue_type * job_queue = (job_queue_type*)arg_pack_iget_ptr( arg_pack , 0 ); + int queue_index = arg_pack_iget_int( arg_pack , 1 ); + job_list_get_rdlock( job_queue->job_list ); + { + job_queue_node_type * node = job_list_iget_job( job_queue->job_list , queue_index ); + bool OK = job_queue_check_node_status_files( job_queue , node ); + + if (OK) + OK = job_queue_node_run_DONE_callback( node ); + + if (OK) + job_queue_change_node_status( job_queue , node , JOB_QUEUE_SUCCESS ); + else + job_queue_change_node_status( job_queue , node , JOB_QUEUE_EXIT ); + + job_queue_node_free_driver_data( node , job_queue->driver ); + } + job_list_unlock(job_queue->job_list ); + arg_pack_free( arg_pack ); + return NULL; +} + +static void job_queue_handle_DONE( job_queue_type * queue , job_queue_node_type * node) { + job_queue_change_node_status(queue , node , JOB_QUEUE_RUNNING_DONE_CALLBACK ); + { + arg_pack_type * arg_pack = arg_pack_alloc(); + arg_pack_append_ptr( arg_pack , queue ); + arg_pack_append_int( arg_pack , job_queue_node_get_queue_index(node)); + thread_pool_add_job( queue->work_pool , job_queue_run_DONE_callback , arg_pack ); + } +} + + +static void * job_queue_run_EXIT_callback( void * arg ) { + arg_pack_type * arg_pack = arg_pack_safe_cast( arg ); + job_queue_type * job_queue = (job_queue_type*)arg_pack_iget_ptr( arg_pack , 0 ); + int queue_index = arg_pack_iget_int( arg_pack , 1 ); + + job_list_get_rdlock( job_queue->job_list ); + { + job_queue_node_type * node = job_list_iget_job( job_queue->job_list , queue_index ); + + if (job_queue_node_get_submit_attempt( node ) < job_queue->max_submit) + job_queue_change_node_status( job_queue , node , JOB_QUEUE_WAITING ); /* The job will be picked up for antother go. */ + else { + bool retry = job_queue_node_run_RETRY_callback( node ); + + if (retry) { + /* OK - we have invoked the retry_callback() - and that has returned true; + giving this job a brand new start. */ + job_queue_node_reset_submit_attempt( node ); + job_queue_change_node_status(job_queue , node , JOB_QUEUE_WAITING); + } else { + // It's time to call it a day + + job_queue_node_run_EXIT_callback( node ); + job_queue_change_node_status(job_queue , node , JOB_QUEUE_FAILED); + } + } + job_queue_node_free_driver_data( node , job_queue->driver ); + } + job_list_unlock(job_queue->job_list ); + arg_pack_free( arg_pack ); + + return NULL; +} + + +/* + In this case the assumption is that we do not have proper contact + with the node running the job, and we just switch the job status to + JOB_QUEUE_EXIT without calling the driver->kill_job( ) function. +*/ + +static void job_queue_handle_DO_KILL_NODE_FAILURE(job_queue_type * queue, job_queue_node_type * node) { + queue_driver_blacklist_node( queue->driver, job_queue_node_get_driver_data(node) ); + job_queue_change_node_status(queue, node, JOB_QUEUE_EXIT); + job_queue_node_dec_submit_attempt(node); +} + +static void job_queue_handle_DO_KILL( job_queue_type * queue , job_queue_node_type * node) { + job_queue_kill_job_node(queue, node); + job_queue_node_free_driver_data( node , queue->driver ); + job_queue_change_node_status(queue , node , JOB_QUEUE_IS_KILLED ); +} + +static void job_queue_handle_EXIT( job_queue_type * queue , job_queue_node_type * node) { + job_queue_change_node_status(queue , node , JOB_QUEUE_RUNNING_EXIT_CALLBACK ); + { + arg_pack_type * arg_pack = arg_pack_alloc(); + arg_pack_append_ptr( arg_pack , queue ); + arg_pack_append_int( arg_pack , job_queue_node_get_queue_index(node)); + thread_pool_add_job( queue->work_pool , job_queue_run_EXIT_callback , arg_pack ); + } +} + + + +/*****************************************************************/ + +static void job_queue_check_expired(job_queue_type * queue) { + if ((job_queue_get_max_job_duration(queue) <= 0) && (job_queue_get_job_stop_time(queue) <= 0)) + return; + + for (int i = 0; i < job_list_get_size( queue->job_list ); i++) { + job_queue_node_type * node = job_list_iget_job( queue->job_list , i ); + + if (job_queue_node_get_status(node) != JOB_QUEUE_RUNNING) + continue; + + time_t now = time(NULL); + double max_duration = job_queue_get_max_job_duration(queue); + + // max_duration == 0 means unlimited; never kill it due to duration + if ( max_duration > 0) { + double elapsed = difftime(now, job_queue_node_get_sim_start( node )); + if (elapsed > max_duration) { + res_log_finfo("Time limit exceeded, %fs > %fs. Scheduled for kill.", + elapsed, + max_duration); + job_queue_change_node_status(queue, node, JOB_QUEUE_DO_KILL); + } + } + + if (job_queue_get_job_stop_time(queue) > 0) { + if (now >= job_queue_get_job_stop_time(queue)) + job_queue_change_node_status(queue, node, JOB_QUEUE_DO_KILL); + } + + } +} + + + +bool job_queue_get_open(const job_queue_type * job_queue) { + return job_queue->open; +} + +void job_queue_check_open(job_queue_type* queue) { + if (!job_queue_get_open(queue)) + util_abort("%s: queue not open and not ready for use; method job_queue_reset must be called before using the queue - aborting\n", __func__ ); +} + +bool job_queue_accept_jobs(const job_queue_type * queue) { + if (queue->user_exit) + return false; + + return queue->open; +} + + +/* Submit new jobs and return whether we actually did. + * + * And we do if we have waiting jobs are allowed to submit jobs + */ +static bool submit_new_jobs(job_queue_type * queue) { + + int max_submit = 5; /* This is the maximum number of jobs submitted in one while() { ... } below. + Only to ensure that the waiting time before a status update is not too long. */ + int total_active = job_queue_status_get_count(queue->status, JOB_QUEUE_PENDING) + + job_queue_status_get_count(queue->status, JOB_QUEUE_RUNNING); + + + int max_running = job_queue_get_max_running(queue); + int num_submit_new = util_int_min(max_submit, max_running - total_active); + + // If max_running == 0 that should be interpreted as no limit; i.e. the queue + // layer will attempt to send an unlimited number of jobs to the driver - the + // driver can reject the jobs. + if (max_running == 0) + num_submit_new = util_int_min(max_submit, + job_queue_status_get_count(queue->status, + JOB_QUEUE_WAITING)); + + + bool new_jobs = false; + if (job_queue_status_get_count(queue->status, JOB_QUEUE_WAITING) > 0) /* We have waiting jobs at all */ + if (num_submit_new > 0) /* The queue can allow more running jobs */ + new_jobs = true; + + if (new_jobs) { + int submit_count = 0; + int queue_index = 0; + + while ((queue_index < job_list_get_size(queue->job_list)) && (num_submit_new > 0)) { + job_queue_node_type * node = job_list_iget_job(queue->job_list, queue_index); + if (job_queue_node_get_status(node) == JOB_QUEUE_WAITING) { + submit_status_type submit_status = job_queue_submit_job(queue, queue_index); + + if (submit_status == SUBMIT_OK) { + num_submit_new--; + submit_count++; + } else if ((submit_status == SUBMIT_DRIVER_FAIL) || (submit_status == SUBMIT_QUEUE_CLOSED)) + break; + } + queue_index++; + } + } + + return new_jobs; +} + + +static void run_handlers(job_queue_type * queue) { + /* + Checking for complete / exited / overtime jobs + */ + for (int i = 0; i < job_list_get_size(queue->job_list); ++i) { + job_queue_node_type * node = job_list_iget_job(queue->job_list, i); + + switch (job_queue_node_get_status(node)) { + case(JOB_QUEUE_DONE): + job_queue_handle_DONE(queue, node); + break; + case(JOB_QUEUE_EXIT): + job_queue_handle_EXIT(queue, node); + break; + case(JOB_QUEUE_DO_KILL_NODE_FAILURE): + job_queue_handle_DO_KILL_NODE_FAILURE(queue, node); + break; + case(JOB_QUEUE_DO_KILL): + job_queue_handle_DO_KILL(queue, node); + break; + default: + break; + } + } + +} + + +/* + * UI code: if verbose update spinner and print summary + */ +static void loop_status_spinner(job_queue_type * queue, bool update_status, bool new_jobs, int* phase, bool verbose) { + if (!verbose) + return; + + if (update_status || new_jobs) + job_queue_print_summary(queue, update_status); + + const char * spinner = "-\\|/"; + int spinner_length = strlen( spinner ); + + printf("%c\b" , spinner[ (*phase % spinner_length) ]); + fflush(stdout); + (*phase) += 1; +} + + +static void job_queue_loop(job_queue_type * queue, int num_total_run, bool verbose) { + bool new_jobs = false; + bool complete = false; // we have submitted enough jobs + bool exit = false; // the user has indic + + int phase = 0; // UI code: this is the visual spinner + + do { // while !complete && !exit + job_list_get_rdlock(queue->job_list); + + if (queue->user_exit) {/* An external thread has called the job_queue_user_exit() function, and we should kill + all jobs, do some clearing up and go home. Observe that we will go through the + queue handling codeblock below ONE LAST TIME before exiting. */ + res_log_info("Received queue->user_exit in inner loop of job_queue_run_jobs, exiting"); + job_queue_user_exit__(queue); + exit = true; + } + + job_queue_check_expired(queue); + + bool update_status = job_queue_update_status(queue); // this has side effects + loop_status_spinner(queue, update_status, new_jobs, &phase, verbose); // UI code + + int num_complete = job_queue_status_get_count(queue->status, JOB_QUEUE_SUCCESS) + + job_queue_status_get_count(queue->status, JOB_QUEUE_FAILED) + + job_queue_status_get_count(queue->status, JOB_QUEUE_IS_KILLED); + + if ((num_total_run > 0) && (num_total_run == num_complete)) + /* The number of jobs completed is equal to the number + of jobs we have said we want to run; so we are finished. + */ + complete = true; + else if (num_total_run == 0) { + /* We have not informed about how many jobs we will + run. To check if we are complete we perform the two + tests: + + 1. All the jobs which have been added with + job_queue_add_job() have completed. + + 2. The user has used job_queue_complete_submit() + to signal that no more jobs will be forthcoming. + */ + if ((num_complete == job_list_get_size(queue->job_list)) && queue->submit_complete) + complete = true; + } + + if (!complete) { + new_jobs = submit_new_jobs(queue); + run_handlers(queue); + } else + /* print an updated status to stdout before exiting. */ + if (verbose) + job_queue_print_summary(queue, true); + + job_list_unlock(queue->job_list); + + if (!exit) { + res_yield(); + job_list_reader_wait(queue->job_list, queue->usleep_time, 8 * queue->usleep_time); + } + + } while (!complete && !exit); + + if (verbose) + printf("\n"); + +} + + + +/* This is run from job_queue_run_jobs when we have got an exclusive lock to the + * run_jobs code. + * + * Its sole purpose is to set up the work_pool thread and initiate the main loop + */ +static void handle_run_jobs(job_queue_type * queue, int num_total_run, bool verbose) { + + // Check if queue is open. Fails hard if not open + job_queue_check_open(queue); + + /* + The number of threads in the thread pool running callbacks. Memory consumption can + potentially be quite high while running the DONE callback - should therefor not use + too many threads. + */ + const int NUM_WORKER_THREADS = 1; + queue->work_pool = thread_pool_alloc(NUM_WORKER_THREADS, true); + res_log_debug("Allocated thread pool in job_queue_run_jobs"); + + queue->running = true; + job_queue_loop(queue, num_total_run, verbose); + + thread_pool_join(queue->work_pool); + thread_pool_free(queue->work_pool); +} + + + +/** + If the total number of jobs is not known in advance the job_queue_run_jobs + function can be called with @num_total_run == 0. In that case it is paramount + to call the function job_queue_submit_complete() whan all jobs have been submitted. + + Observe that this function is assumed to have ~exclusive access to + the jobs array; meaning that: + + 1. The jobs array is read without taking a reader lock. + + 2. Other functions accessing the jobs array concurrently must + take a read lock. + + 3. This function should be the *only* function modifying + the jobs array, and that is done *with* the write lock. + +*/ + +void job_queue_run_jobs(job_queue_type * queue, int num_total_run, bool verbose) { + + int trylock = pthread_mutex_trylock(&queue->run_mutex); + if (trylock != 0) + util_abort("%s: another thread is already running the queue_manager\n",__func__); + + /* */ + if (!queue->user_exit) + handle_run_jobs(queue, num_total_run, verbose); + else + res_log_info("queue->user_exit = true in job_queue, received external signal to abandon the whole thing"); + + /* + Set the queue's "open" flag to false to signal that the queue is + not ready to be used in a new job_queue_run_jobs or + job_queue_add_job method call as it has not been reset yet. Not + resetting the queue here implies that the queue object is still + available for queries after this method has finished + */ + queue->open = false; + queue->running = false; + pthread_mutex_unlock(&queue->run_mutex); +} + + + + + +void * job_queue_run_jobs__(void * __arg_pack) { + arg_pack_type * arg_pack = arg_pack_safe_cast(__arg_pack); + job_queue_type * queue = (job_queue_type*)arg_pack_iget_ptr(arg_pack , 0); + int num_total_run = arg_pack_iget_int(arg_pack , 1); + bool verbose = arg_pack_iget_bool(arg_pack , 2); + + job_queue_run_jobs(queue , num_total_run , verbose); + arg_pack_free( arg_pack ); + return NULL; +} + + +void job_queue_start_manager_thread( job_queue_type * job_queue , pthread_t * queue_thread , int job_size , bool verbose) { + + arg_pack_type * queue_args = arg_pack_alloc(); /* This arg_pack will be freed() in the job_queue_run_jobs__() */ + arg_pack_append_ptr(queue_args , job_queue); + arg_pack_append_int(queue_args , job_size); + arg_pack_append_bool(queue_args , verbose); + + job_queue->running = true; + pthread_create( queue_thread , NULL , job_queue_run_jobs__ , queue_args); +} + + + + +/** + The most flexible use scenario is as follows: + + 1. The job_queue_run_jobs() is run by one thread. + 2. Jobs are added asyncronously with job_queue_add_job() from othread threads(s). + + + Unfortunately it does not work properly (i.e. Ctrl-C breaks) to use a Python + thread to invoke the job_queue_run_jobs() function; and this function is + mainly a workaround around that problem. The function will create a new + thread and run job_queue_run_jobs() in that thread; the calling thread will + just return. + + No reference is retained to the thread actually running the + job_queue_run_jobs() function. +*/ + + +void job_queue_run_jobs_threaded(job_queue_type * queue , int num_total_run, bool verbose) { + pthread_t queue_thread; + job_queue_start_manager_thread( queue , &queue_thread , num_total_run , verbose ); + pthread_detach( queue_thread ); /* Signal that the thread resources should be cleaned up when + the thread has exited. */ +} + + + +/*****************************************************************/ +/* Adding new jobs - it is complicated ... */ + + +/** + This initializes the non-driver-spesific fields of a job, i.e. the + name, runpath and so on, and sets the job->status == + JOB_QUEUE_WAITING. This status means the job is ready to be + submitted proper to one of the drivers (when a slot is ready). + When submitted the job will get (driver specific) job_data != NULL + and status SUBMITTED. +*/ + + + + +int job_queue_add_job(job_queue_type * queue , + const char * run_cmd , + job_callback_ftype * done_callback, + job_callback_ftype * retry_callback, + job_callback_ftype * exit_callback, + void * callback_arg , + int num_cpu , + const char * run_path , + const char * job_name , + int argc , + const char ** argv) { + + + if (job_queue_accept_jobs(queue)) { + int queue_index; + job_queue_node_type * node = job_queue_node_alloc( job_name , + run_path , + run_cmd , + argc , + argv , + num_cpu , + queue->ok_file , + queue->status_file , + queue->exit_file, + done_callback , + retry_callback , + exit_callback , + callback_arg ); + if (node) { + job_list_get_wrlock( queue->job_list ); + { + job_list_add_job( queue->job_list , node ); + queue_index = job_queue_node_get_queue_index(node); + job_queue_change_node_status(queue , node , JOB_QUEUE_WAITING); + } + job_list_unlock( queue->job_list ); + return queue_index; /* Handle used by the calling scope. */ + } else { + char * cwd = (char*)util_alloc_cwd(); + util_abort("%s: failed to create job: %s in path:%s[%d] cwd:%s\n",__func__ , job_name , run_path , util_is_directory(run_path), cwd); + return -1; + } + } else + return -1; +} + + +UTIL_SAFE_CAST_FUNCTION( job_queue , JOB_QUEUE_TYPE_ID) + + +/** + Observe that the job_queue returned by this function is NOT ready + for use; a driver must be set explicitly with a call to + job_queue_set_driver() first. +*/ + +job_queue_type * job_queue_alloc(int max_submit , + const char * ok_file , + const char * status_file , + const char * exit_file ) { + + + + job_queue_type * queue = (job_queue_type*)util_malloc(sizeof * queue ); + UTIL_TYPE_ID_INIT( queue , JOB_QUEUE_TYPE_ID); + queue->usleep_time = 250000; /* 1000000 : 1 second */ + queue->max_ok_wait_time = 60; + queue->max_duration = 0; + queue->stop_time = 0; + queue->max_submit = max_submit; + queue->driver = NULL; + queue->ok_file = util_alloc_string_copy( ok_file ); + queue->exit_file = util_alloc_string_copy( exit_file ); + queue->status_file = util_alloc_string_copy( status_file ); + queue->open = true; + queue->user_exit = false; + queue->pause_on = false; + queue->running = false; + queue->submit_complete = false; + queue->work_pool = NULL; + queue->job_list = job_list_alloc( ); + queue->status = job_queue_status_alloc( ); + queue->progress_timestamp = time(NULL); + + pthread_mutex_init( &queue->run_mutex , NULL ); + + + + + return queue; +} + + +/** + When the job_queue_run_jobs() has been called with @total_num_jobs + == 0 that means that the total number of jobs to run is not known + in advance. In that case it is essential to signal the queue when + we will not submit any more jobs, so that it can finalize and + return. That is done with the function job_queue_submit_complete() +*/ + +void job_queue_submit_complete( job_queue_type * queue ){ + queue->submit_complete = true; +} + + + +/** + The calling scope must retain a handle to the current driver and + free it. Should (in principle) be possible to change driver on a + running system whoaaa. Will read and update the max_running value + from the driver. +*/ + +void job_queue_set_driver(job_queue_type * queue , queue_driver_type * driver) { + queue->driver = driver; +} + + +bool job_queue_has_driver(const job_queue_type * queue ) { + if (queue->driver == NULL) + return false; + else + return true; +} + + +void job_queue_set_max_submit( job_queue_type * job_queue , int max_submit ) { + job_queue->max_submit = max_submit; +} + + +int job_queue_get_max_submit(const job_queue_type * job_queue ) { + return job_queue->max_submit; +} + + +/** + Returns true if the queue is currently paused, which means that no + more jobs are submitted. +*/ + +bool job_queue_get_pause( const job_queue_type * job_queue ) { + return job_queue->pause_on; +} + + +void job_queue_set_pause_on( job_queue_type * job_queue) { + job_queue->pause_on = true; +} + + +void job_queue_set_pause_off( job_queue_type * job_queue) { + job_queue->pause_on = false; +} + +/* + An external thread sets the user_exit flag to true, then + subsequently the thread managing the queue will see this, and close + down the queue. Will check that the queue is actually running before + setting the user_exit flag. If the queue does not change to running + state within a timeout limit the user_exit flag is not set, and the + function return false. +*/ + +bool job_queue_start_user_exit( job_queue_type * queue) { + if (!queue->user_exit) { + int timeout_limit = 10 * 1000000; // 10 seconds + int usleep_time = 100000; // 0.1 second + int total_sleep = 0; + + while (true) { + if (queue->running) { + queue->user_exit = true; + break; + } + usleep( usleep_time ); + total_sleep += usleep_time; + + if (total_sleep > timeout_limit) + break; + } + } + return queue->user_exit; +} + +bool job_queue_get_user_exit( const job_queue_type * queue) { + return queue->user_exit; +} + +void job_queue_free(job_queue_type * queue) { + free( queue->ok_file ); + free( queue->exit_file ); + free( queue->status_file ); + job_list_free( queue->job_list ); + job_queue_status_free( queue->status ); + free(queue); +} + + + + + + +int job_queue_get_max_running_option(queue_driver_type * driver) { + char * max_running_string = (char*)queue_driver_get_option(driver, MAX_RUNNING); + int max_running; + if (!util_sscanf_int(max_running_string, &max_running)) { + fprintf(stderr, "%s: Unable to parse option MAX_RUNNING with value %s to an int", __func__, max_running_string); + } + return max_running; +} + + +void job_queue_set_max_running_option(queue_driver_type * driver, int max_running) { + char * max_running_string = (char*)util_alloc_sprintf("%d", max_running); + queue_driver_set_option(driver, MAX_RUNNING, max_running_string); + free(max_running_string); +} + + +/** + Observe that if the max number of running jobs is decreased, + nothing will be done to reduce the number of jobs currently + running; but no more jobs will be submitted until the number of + running has fallen below the new limit. + + The updated value will also be pushed down to the current driver. + + NOTE: These next three *max_running functions should not be used, rather + use the set_option feature, with MAX_RUNNING. They are (maybe) used by python + therefore not removed. +*/ +int job_queue_get_max_running( const job_queue_type * queue ) { + return job_queue_get_max_running_option(queue->driver); +} + +void job_queue_set_max_running( job_queue_type * queue , int max_running ) { + job_queue_set_max_running_option(queue->driver, max_running); +} + +char * job_queue_get_ok_file(const job_queue_type * queue) { + return queue->ok_file; +} + +char * job_queue_get_exit_file(const job_queue_type * queue) { + return queue->exit_file; +} + +char * job_queue_get_status_file(const job_queue_type * queue) { + return queue->status_file; +} + +int job_queue_add_job_node(job_queue_type * queue, job_queue_node_type * node) { + job_list_get_wrlock( queue->job_list ); + + job_list_add_job( queue->job_list , node ); + job_queue_change_node_status(queue , node , JOB_QUEUE_WAITING); + int queue_index = job_queue_node_get_queue_index(node); + job_list_unlock( queue->job_list ); + return queue_index; +} diff --git a/libres/lib/job_queue/job_queue_status.cpp b/libres/lib/job_queue/job_queue_status.cpp new file mode 100644 index 00000000000..0a3009df2c2 --- /dev/null +++ b/libres/lib/job_queue/job_queue_status.cpp @@ -0,0 +1,176 @@ +/* + Copyright (C) 2015 Equinor ASA, Norway. + + The file 'job_status_test.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 JOB_QUEUE_STATUS_TYPE_ID 777620306 + +struct job_queue_status_struct { + UTIL_TYPE_ID_DECLARATION; + int status_list[JOB_QUEUE_MAX_STATE]; + pthread_rwlock_t rw_lock; + int status_index[JOB_QUEUE_MAX_STATE]; + time_t timestamp; +}; + + +static int STATUS_INDEX(const job_queue_status_type * status_count, job_status_type status ) { + int index = 0; + + while (true) { + if (status_count->status_index[index] == status) + return index; + + index++; + if (index == JOB_QUEUE_MAX_STATE) + util_abort("%s: failed to get index from status:%d \n",__func__ , status); + + } + return 0; +} + + +UTIL_IS_INSTANCE_FUNCTION( job_queue_status , JOB_QUEUE_STATUS_TYPE_ID ) +UTIL_SAFE_CAST_FUNCTION( job_queue_status , JOB_QUEUE_STATUS_TYPE_ID ) + + +job_queue_status_type * job_queue_status_alloc() { + job_queue_status_type * status = (job_queue_status_type*)util_malloc( sizeof * status ); + UTIL_TYPE_ID_INIT( status , JOB_QUEUE_STATUS_TYPE_ID ); + pthread_rwlock_init( &status->rw_lock , NULL); + job_queue_status_clear( status ); + status->timestamp = time(NULL); + + status->status_index[0] = JOB_QUEUE_NOT_ACTIVE; // Initial, allocated job state, job not added - controlled by job_queue + status->status_index[1] = JOB_QUEUE_WAITING; // The job is ready to be started - controlled by job_queue + status->status_index[2] = JOB_QUEUE_SUBMITTED; // Job is submitted to driver - temporary state - controlled by job_queue + status->status_index[3] = JOB_QUEUE_PENDING; // Job is pending, before actual execution - controlled by queue_driver + status->status_index[4] = JOB_QUEUE_RUNNING; // Job is executing - controlled by queue_driver + status->status_index[5] = JOB_QUEUE_DONE; // Job is done (successful or not), temporary state - controlled/returned by by queue_driver + status->status_index[6] = JOB_QUEUE_EXIT; //Job is done, with exit status != 0, temporary state - controlled/returned by by queue_driver + status->status_index[7] = JOB_QUEUE_IS_KILLED; // Job has been killed, due to JOB_QUEUE_DO_KILL, FINAL STATE - controlled by job_queue + status->status_index[8] = JOB_QUEUE_DO_KILL; // User / queue system has requested killing of job - controlled by job_queue / external scope + status->status_index[9] = JOB_QUEUE_SUCCESS; // All good, comes after JOB_QUEUE_DONE, with additional checks, FINAL STATE - controlled by job_queue + status->status_index[10] = JOB_QUEUE_RUNNING_DONE_CALLBACK; // Temporary state, while running requested callbacks after an ended job - controlled by job_queue + status->status_index[11] = JOB_QUEUE_RUNNING_EXIT_CALLBACK; // Temporary state, while running requested callbacks after an ended job - controlled by job_queue + status->status_index[12] = JOB_QUEUE_STATUS_FAILURE; //The driver call to get status has failed, job status remains unchanged + status->status_index[13] = JOB_QUEUE_FAILED; // Job has failed, no more retries, FINAL STATE + status->status_index[14] = JOB_QUEUE_DO_KILL_NODE_FAILURE; // Job has failed, node should be blacklisted + status->status_index[15] = JOB_QUEUE_UNKNOWN; // Unable to get status from submitted job + + return status; +} + + +void job_queue_status_free( job_queue_status_type * status ) { + free( status ); +} + + +void job_queue_status_clear( job_queue_status_type * status ) { + int index; + for (index = 0; index < JOB_QUEUE_MAX_STATE; index++) + status->status_list[ index ] = 0; +} + + +int job_queue_status_get_count( job_queue_status_type * status_count , int job_status_mask) { + int count = 0; + pthread_rwlock_rdlock( &status_count->rw_lock ); + { + int index = 0; + int status = 1; + + while (true) { + if ((status & job_status_mask) == status) { + job_status_mask -= status; + count += status_count->status_list[index]; + } + + if (job_status_mask == 0) + break; + + index++; + status <<= 1; + if (index == JOB_QUEUE_MAX_STATE) + util_abort("%s: internal error: remaining unrecognized status value:%d \n",__func__ , job_status_mask); + } + } + pthread_rwlock_unlock( &status_count->rw_lock ); + return count; +} + +void job_queue_status_inc( job_queue_status_type * status_count , job_status_type status_type) { + int index = STATUS_INDEX(status_count, status_type ); + + pthread_rwlock_wrlock( &status_count->rw_lock ); + { + int count = status_count->status_list[index]; + status_count->status_list[index] = count + 1; + } + status_count->timestamp = time(NULL); + pthread_rwlock_unlock( &status_count->rw_lock ); +} + + +static void job_queue_status_dec( job_queue_status_type * status_count , job_status_type status_type) { + int index = STATUS_INDEX(status_count, status_type ); + + pthread_rwlock_wrlock( &status_count->rw_lock ); + { + int count = status_count->status_list[index]; + status_count->status_list[index] = count - 1; + } + pthread_rwlock_unlock( &status_count->rw_lock ); +} + + +/* + The important point is that each individual ++ and -- operation is + atomic, if the different status counts do not add up perfectly at + all times that is ok. +*/ +bool job_queue_status_transition(job_queue_status_type * status_count, + job_status_type src_status, + job_status_type target_status) { + if (src_status == target_status) + return false; + + /* + The target_status indicates that the routine which queried for new + status failed; we just remain in the current status. + */ + if (target_status == JOB_QUEUE_STATUS_FAILURE) + return false; + + job_queue_status_dec( status_count, src_status ); + job_queue_status_inc( status_count, target_status ); + return true; +} + + +int job_queue_status_get_total_count( const job_queue_status_type * status ) { + int total_count = 0; + for (int index = 0; index < JOB_QUEUE_MAX_STATE; index++) + total_count += status->status_list[ index ]; + return total_count; +} diff --git a/libres/lib/job_queue/job_status.cpp b/libres/lib/job_queue/job_status.cpp new file mode 100644 index 00000000000..2bda58f58a7 --- /dev/null +++ b/libres/lib/job_queue/job_status.cpp @@ -0,0 +1,77 @@ +/* + Copyright (C) 2018 Equinor ASA, Norway. + + The file 'job_status.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 + + +const char * job_status_get_name(job_status_type status) { + switch(status) { + case JOB_QUEUE_NOT_ACTIVE: + return "JOB_QUEUE_NOT_ACTIVE"; + break; + case JOB_QUEUE_WAITING: + return "JOB_QUEUE_WAITING"; + break; + case JOB_QUEUE_SUBMITTED: + return "JOB_QUEUE_SUBMITTED"; + break; + case JOB_QUEUE_PENDING: + return "JOB_QUEUE_PENDING"; + break; + case JOB_QUEUE_RUNNING: + return "JOB_QUEUE_RUNNING"; + break; + case JOB_QUEUE_DONE: + return "JOB_QUEUE_DONE"; + break; + case JOB_QUEUE_EXIT: + return "JOB_QUEUE_EXIT"; + break; + case JOB_QUEUE_IS_KILLED: + return "JOB_QUEUE_IS_KILLED"; + break; + case JOB_QUEUE_DO_KILL: + return "JOB_QUEUE_DO_KILL"; + break; + case JOB_QUEUE_SUCCESS: + return "JOB_QUEUE_SUCCESS"; + break; + case JOB_QUEUE_RUNNING_DONE_CALLBACK: + return "JOB_QUEUE_RUNNING_DONE_CALLBACK"; + break; + case JOB_QUEUE_RUNNING_EXIT_CALLBACK: + return "JOB_QUEUE_RUNNING_EXIT_CALLBACK"; + break; + case JOB_QUEUE_STATUS_FAILURE: + return "JOB_QUEUE_STATUS_FAILURE"; + break; + case JOB_QUEUE_FAILED: + return "JOB_QUEUE_FAILED"; + break; + case JOB_QUEUE_DO_KILL_NODE_FAILURE: + return "JOB_QUEUE_DO_KILL_NODE_FAILURE"; + break; + case JOB_QUEUE_UNKNOWN: + return "JOB_QUEUE_UNKNOWN"; + break; + } + + util_abort("%s: internal error", __func__); + return NULL; +} diff --git a/libres/lib/job_queue/local_driver.cpp b/libres/lib/job_queue/local_driver.cpp new file mode 100644 index 00000000000..68503c7ce1b --- /dev/null +++ b/libres/lib/job_queue/local_driver.cpp @@ -0,0 +1,194 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'local_driver.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 + + +typedef struct local_job_struct local_job_type; + +struct local_job_struct { + UTIL_TYPE_ID_DECLARATION; + bool active; + job_status_type status; + pthread_t run_thread; + pid_t child_process; +}; + + +#define LOCAL_DRIVER_TYPE_ID 66196305 +#define LOCAL_JOB_TYPE_ID 63056619 + +struct local_driver_struct { + UTIL_TYPE_ID_DECLARATION; + pthread_attr_t thread_attr; + pthread_mutex_t submit_lock; +}; + +/*****************************************************************/ + + +static UTIL_SAFE_CAST_FUNCTION( local_driver , LOCAL_DRIVER_TYPE_ID ) +static UTIL_SAFE_CAST_FUNCTION( local_job , LOCAL_JOB_TYPE_ID ) + + +static local_job_type * local_job_alloc() { + local_job_type * job; + job = (local_job_type*)util_malloc(sizeof * job ); + UTIL_TYPE_ID_INIT( job , LOCAL_JOB_TYPE_ID ); + job->active = false; + job->status = JOB_QUEUE_WAITING; + return job; +} + + + +job_status_type local_driver_get_job_status(void * __driver, void * __job) { + if (__job == NULL) + /* The job has not been registered at all ... */ + return JOB_QUEUE_NOT_ACTIVE; + else { + local_job_type * job = local_job_safe_cast( __job ); + return job->status; + } +} + + + +void local_driver_free_job( void * __job ) { + local_job_type * job = local_job_safe_cast( __job ); + if (!job->active) + free(job); +} + + +void local_driver_kill_job( void * __driver , void * __job) { + local_job_type * job = local_job_safe_cast( __job ); + if (job->child_process > 0) + kill( job->child_process , SIGTERM ); +} + + + +/* + This function needs to dereference the job pointer after the waitpid() call is + complete, it is therefor essential that no other threads have called free(job) + while the external process is running. +*/ + +void * submit_job_thread__(void * __arg) { + arg_pack_type *arg_pack = arg_pack_safe_cast(__arg); + const char *executable = (const char*)arg_pack_iget_const_ptr(arg_pack, 0); + /* + The arg_pack contains a run_path field as the second argument, + it has therefore been left here as a comment: + + const char * run_path = arg_pack_iget_const_ptr(arg_pack , 1); + */ + int argc = arg_pack_iget_int(arg_pack, 2); + char **argv = (char**)arg_pack_iget_ptr(arg_pack, 3); + local_job_type *job = (local_job_type*)arg_pack_iget_ptr(arg_pack, 4); + { + int wait_status; + job->child_process = util_spawn(executable, argc, (const char**) argv, NULL, NULL); + util_free_stringlist(argv, argc); + arg_pack_free(arg_pack); + waitpid(job->child_process, &wait_status, 0); + + job->active = false; + job->status = JOB_QUEUE_EXIT; + if (WIFEXITED(wait_status)) + if (WEXITSTATUS(wait_status) == 0) + job->status = JOB_QUEUE_DONE; + + } + return NULL; +} + + + +void * local_driver_submit_job(void * __driver , + const char * submit_cmd , + int num_cpu , /* Ignored */ + const char * run_path , + const char * job_name , + int argc , + const char ** argv ) { + local_driver_type * driver = local_driver_safe_cast( __driver ); + { + local_job_type * job = local_job_alloc(); + arg_pack_type * arg_pack = arg_pack_alloc(); + arg_pack_append_const_ptr( arg_pack , submit_cmd); + arg_pack_append_const_ptr( arg_pack , run_path ); + arg_pack_append_int( arg_pack , argc ); + arg_pack_append_ptr( arg_pack , util_alloc_stringlist_copy( argv , argc )); /* Due to conflict with threads and python GC we take a local copy. */ + arg_pack_append_ptr( arg_pack , job ); + + pthread_mutex_lock( &driver->submit_lock ); + job->active = true; + job->status = JOB_QUEUE_RUNNING; + + if (pthread_create( &job->run_thread , &driver->thread_attr , submit_job_thread__ , arg_pack) != 0) + util_abort("%s: failed to create run thread - aborting \n",__func__); + + pthread_mutex_unlock( &driver->submit_lock ); + return job; + } +} + + + +void local_driver_free(local_driver_type * driver) { + pthread_attr_destroy ( &driver->thread_attr ); + free(driver); + driver = NULL; +} + + +void local_driver_free__(void * __driver) { + local_driver_type * driver = local_driver_safe_cast( __driver ); + local_driver_free( driver ); +} + + +void * local_driver_alloc() { + local_driver_type * local_driver = (local_driver_type*)util_malloc(sizeof * local_driver ); + UTIL_TYPE_ID_INIT( local_driver , LOCAL_DRIVER_TYPE_ID); + pthread_mutex_init( &local_driver->submit_lock , NULL ); + pthread_attr_init( &local_driver->thread_attr ); + pthread_attr_setdetachstate( &local_driver->thread_attr , PTHREAD_CREATE_DETACHED ); + + return local_driver; +} + +void local_driver_init_option_list(stringlist_type * option_list) { + //No options specific for local driver; do nothing +} + +#undef LOCAL_DRIVER_ID +#undef LOCAL_JOB_ID + +/*****************************************************************/ diff --git a/libres/lib/job_queue/lsb.cpp b/libres/lib/job_queue/lsb.cpp new file mode 100644 index 00000000000..3e1945fba71 --- /dev/null +++ b/libres/lib/job_queue/lsb.cpp @@ -0,0 +1,210 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'lsb.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. +*/ + +/* + This file implements a very small wrapper structure around the + lsb_xxxx() functions from the libbat.so shared library which are + used to submit, monitor and control simulations with LSF. + + Loading and initializing the lsf libraries is quite painful, in an + attempt to reduce unecessary dependencies the lsf libraries are + loaded with dlopen() in the lsb_alloc() function below. This means + that the libjob_queue.so shared library can be loaded without access + to the lsf libraries. +*/ + +#include +#include + +#include + +#include +#include + +#include + + + +typedef int (lsb_submit_ftype) ( struct submit * , struct submitReply *); +typedef int (lsb_openjobinfo_ftype) (int , char * , char * , char * , char * , int); +typedef struct jobInfoEnt * (lsb_readjobinfo_ftype) (int * ); +typedef int (lsb_closejobinfo_ftype) ( ); +typedef int (lsb_forcekilljob_ftype) ( int ); +typedef int (lsb_init_ftype) ( char * ); +typedef char * (lsb_sysmsg_ftype) ( ); + + + +struct lsb_struct { + lsb_submit_ftype * submit; + lsb_openjobinfo_ftype * open_job; + lsb_readjobinfo_ftype * read_job; + lsb_closejobinfo_ftype * close_job; + lsb_forcekilljob_ftype * kill_job; + lsb_init_ftype * lsb_init; + lsb_sysmsg_ftype * sys_msg; + + stringlist_type * error_list; + void * lib_bat; + void * lib_nsl; + void * lib_lsf; + bool ready; +}; + + + +static void * lsb_dlsym( lsb_type * lsb , const char * function_name ) { + void * function = dlsym( lsb->lib_bat , function_name ); + if (!function) { + lsb->ready = false; + stringlist_append_owned_ref( lsb->error_list , util_alloc_sprintf( "Failed to locate symbol:%s dlerror:%s" , function_name , dlerror())); + } + + return function; +} + + +void * lsb_dlopen( lsb_type * lsb , const char * lib_name) { + void * lib_handle = dlopen( lib_name , RTLD_NOW | RTLD_GLOBAL); + if (!lib_handle) { + lsb->ready = false; + stringlist_append_owned_ref( lsb->error_list , util_alloc_sprintf("dlopen(%s) - failed:%s \n" , lib_name , dlerror())); + } + return lib_handle; +} + +/* + The following environment variables must be set (at some stage) before + LSF will work properly: + + LSF_BINDIR $LSF_HOME/bin + LSF_LIBDIR $LSF_HOME/lib + XLSF_UIDDIR $LSF_HOME/lib/uid + LSF_SERVERDIR $LSF_HOME/etc + LSF_ENVDIR /prog/LSF/conf + + The runtime linker must locate the libnsl, libbat and liblsf + libraries using whatever method it usually does. If the loading + fails the lsb object will get the ->ready flag set to false, and the + lsf_driver will discard the lsb instance (and hopefully use shell + commands to perform job management). +*/ + +lsb_type * lsb_alloc() { + lsb_type * lsb = (lsb_type*)util_malloc( sizeof * lsb ); + lsb->ready = true; + lsb->error_list = stringlist_alloc_new(); + + lsb->lib_nsl = lsb_dlopen(lsb , "libnsl.so" ); + lsb->lib_lsf = lsb_dlopen(lsb , "liblsf.so" ); + lsb->lib_bat = lsb_dlopen(lsb , "libbat.so"); + + if (lsb->lib_bat) { + lsb->submit = (lsb_submit_ftype *) lsb_dlsym( lsb , "lsb_submit"); + lsb->open_job = (lsb_openjobinfo_ftype *) lsb_dlsym( lsb , "lsb_openjobinfo"); + lsb->read_job = (lsb_readjobinfo_ftype *) lsb_dlsym( lsb , "lsb_readjobinfo"); + lsb->close_job = (lsb_closejobinfo_ftype *) lsb_dlsym( lsb , "lsb_closejobinfo"); + lsb->kill_job = (lsb_forcekilljob_ftype *) lsb_dlsym( lsb , "lsb_forcekilljob"); + lsb->lsb_init = (lsb_init_ftype *) lsb_dlsym( lsb , "lsb_init"); + lsb->sys_msg = (lsb_sysmsg_ftype *) lsb_dlsym( lsb , "lsb_sysmsg"); + } + + return lsb; +} + + + +void lsb_free( lsb_type * lsb) { + stringlist_free( lsb->error_list ); + + if (lsb->lib_nsl) + dlclose( lsb->lib_nsl ); + + if (lsb->lib_lsf) + dlclose( lsb->lib_lsf ); + + if (lsb->lib_bat) + dlclose( lsb->lib_bat ); + + free( lsb ); +} + + + +bool lsb_ready( const lsb_type * lsb) { + if (!lsb) + return false; + + return lsb->ready; +} + +stringlist_type * lsb_get_error_list( const lsb_type * lsb ) { + return lsb->error_list; +} + + +/*****************************************************************/ + +int lsb_initialize( const lsb_type * lsb) { + /* + The environment variable LSF_ENVDIR must be set to point the + directory containing LSF configuration information, the whole + thing will crash and burn if this is not properly set. + */ + if ( lsb->lsb_init(NULL) != 0 ) { + + fprintf(stderr,"LSF_ENVDIR: "); + if (getenv("LSF_ENVDIR") != NULL) + fprintf(stderr,"%s\n", getenv("LSF_ENVDIR")); + else + fprintf(stderr, "not set\n"); + + util_abort("%s failed to initialize LSF environment : %s \n",__func__ , lsb->sys_msg() ); + } + return 0; +} + + +int lsb_submitjob( const lsb_type * lsb , struct submit * submit_data, struct submitReply * reply_data) { + return lsb->submit( submit_data , reply_data ); +} + + +int lsb_killjob( const lsb_type * lsb , int lsf_jobnr) { + return lsb->kill_job(lsf_jobnr); +} + + +int lsb_openjob( const lsb_type * lsb , int lsf_jobnr) { + return lsb->open_job(lsf_jobnr , NULL , NULL , NULL , NULL , ALL_JOB); +} + + +int lsb_closejob( const lsb_type * lsb) { + return lsb->close_job(); +} + +char * lsb_sys_msg( const lsb_type * lsb) { + return lsb->sys_msg(); +} + + +struct jobInfoEnt * lsb_readjob( const lsb_type * lsb ) { + struct jobInfoEnt * job_info = lsb->read_job( NULL ); + return job_info; +} diff --git a/libres/lib/job_queue/lsf_driver.cpp b/libres/lib/job_queue/lsf_driver.cpp new file mode 100644 index 00000000000..a75bd6ef1cf --- /dev/null +++ b/libres/lib/job_queue/lsf_driver.cpp @@ -0,0 +1,1410 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'lsf_driver.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 + +#define LSF_JSON "lsf_info.json" + +#ifdef HAVE_LSF_LIBRARY +#include +#endif + + + + +/** + Documentation/examples of programming towards the lsf libraries can + be found in /prog/LSF/7.0/misc/examples +*/ + + +/* + How to call the lsf commands bsub/bjobs/bkill: + ---------------------------------------------- + + The commands to submit, monitor and modify LSF jobs are available + through library calls through the lsf library. This is a good + solution which works well. + + Unfortunately only quite few of the workstations in Equinor are + "designated LSF machines", meaning that they are allowed to talk to + the LIM servers, to be able to use the low-level lsb_xxx() function + calls the host making the calls must configured (by an LSF + administrator) to be a LSF client. + + The lsf_driver can either make use of the proper lsf library calls + (lsb_submit(), lsb_openjobinfo(), ...) or alternatively it can issue + ssh calls to an external LSF_SERVER and call up the bsub/bkill/bjob + executables on the remote server. + + All the functions with 'library' in the name are based on library + calls, and the functions with 'shell' in the name are based on + external functions (the actual calls are through the + util_spawn() function). + + By default the driver will use the library, but if a value is + provided with the LSF_SERVER option, the shell based functions will + be used. Internally this is goverened by the boolean flag + 'use_library_calls'. + + Even though you only intend to submit through the shell commands + bsub / bjobs / bkill the build process still requires access to the + lsf headers and the lsf library; that is probably not optimal. + + + Remote login shell + ------------------ + + When submitting with LSF the job will inherit the current + environment on the submitting host, and not read the users login + files on the remote host where the job is actually executed. E.g. in + situations where submitting host and executing host are + e.g. different operating system versions this might be + unfortunate. The '-L @shell' switch can used with bsub to force lsf + to source schell specific input files prior to executing your + job. This can be achieved with the LSF_LOGIN_SHELL option: + + lsf_driver_set_option( driver , LSF_LOGIN_SHELL , "/bin/csh" ); + +*/ + + + + + +#define LSF_DRIVER_TYPE_ID 10078365 +#define LSF_JOB_TYPE_ID 9963900 +#define MAX_ERROR_COUNT 100 +#define SUBMIT_ERROR_SLEEP 2 +#define BJOBS_REFRESH_TIME "10" +#define DEFAULT_RSH_CMD "/usr/bin/ssh" +#define DEFAULT_BSUB_CMD "bsub" +#define DEFAULT_BJOBS_CMD "bjobs" +#define DEFAULT_BKILL_CMD "bkill" +#define DEFAULT_BHIST_CMD "bhist" + + + +struct lsf_job_struct { + UTIL_TYPE_ID_DECLARATION; + long int lsf_jobnr; + int num_exec_host; + char **exec_host; + char * lsf_jobnr_char; /* Used to look up the job status in the bjobs_cache hash table */ + char * job_name; +}; + + + +struct lsf_driver_struct { + UTIL_TYPE_ID_DECLARATION; + char * queue_name; + char * resource_request; + std::vector exclude_hosts; + char * login_shell; + char * project_code; + pthread_mutex_t submit_lock; + + lsf_submit_method_enum submit_method; + int submit_sleep; + + int error_count; + int max_error_count; + int submit_error_sleep; + + /*-----------------------------------------------------------------*/ + /* Fields used by the lsf library functions */ +#ifdef HAVE_LSF_LIBRARY + struct submit lsf_request; + struct submitReply lsf_reply; + lsb_type * lsb; +#endif + + /*-----------------------------------------------------------------*/ + /* Fields used by the shell based functions */ + bool debug_output; + int bjobs_refresh_interval; + time_t last_bjobs_update; + hash_type * my_jobs; /* A hash table of all jobs submitted by this ERT instance - + to ensure that we do not check status of old jobs in e.g. ZOMBIE status. */ + hash_type * status_map; + hash_type * bjobs_cache; /* The output of calling bjobs is cached in this table. */ + pthread_mutex_t bjobs_mutex; /* Only one thread should update the bjobs_chache table. */ + char * remote_lsf_server; + char * rsh_cmd; + char * bsub_cmd; + char * bjobs_cmd; + char * bkill_cmd; + char * bhist_cmd; +}; + + + + +/*****************************************************************/ + +UTIL_SAFE_CAST_FUNCTION( lsf_driver , LSF_DRIVER_TYPE_ID) +static UTIL_SAFE_CAST_FUNCTION_CONST( lsf_driver , LSF_DRIVER_TYPE_ID) +static UTIL_SAFE_CAST_FUNCTION( lsf_job , LSF_JOB_TYPE_ID) + +static lsf_job_type * lsf_job_alloc( const char * job_name ) { + lsf_job_type * job; + job = (lsf_job_type *)util_malloc(sizeof * job); + job->num_exec_host = 0; + job->exec_host = NULL; + + job->lsf_jobnr = 0; + job->lsf_jobnr_char = NULL; + job->job_name = util_alloc_string_copy( job_name ); + UTIL_TYPE_ID_INIT( job , LSF_JOB_TYPE_ID); + return job; +} + + + +void lsf_job_free(lsf_job_type * job) { + free(job->lsf_jobnr_char); + util_free_stringlist(job->exec_host , job->num_exec_host); + free( job->job_name ); + free(job); +} + + +long lsf_job_get_jobnr( const lsf_job_type * job ) { + return job->lsf_jobnr; +} + +int lsf_job_parse_bsub_stdout(const char * bsub_cmd, const char * stdout_file) { + int jobid = 0; + if ((util_file_exists(stdout_file)) && (util_file_size(stdout_file) > 0)) { + FILE * stream = util_fopen(stdout_file , "r"); + if (util_fseek_string(stream , "<" , true , true)) { + char * jobid_string = util_fscanf_alloc_upto(stream , ">" , false); + if (jobid_string != NULL) { + util_sscanf_int( jobid_string , &jobid); + free( jobid_string ); + } + } + fclose( stream ); + + if (jobid == 0) { + char * file_content = util_fread_alloc_file_content( stdout_file , NULL ); + fprintf(stderr,"Failed to get lsf job id from file: %s \n",stdout_file ); + fprintf(stderr,"bsub command : %s \n",bsub_cmd ); + fprintf(stderr,"%s\n", file_content); + free( file_content ); + util_abort("%s: \n",__func__); + } + } + return jobid; +} + +/** + * Assumes fname points to a file with content "hname1:hname2:hname3" as written by lsf_job_write_bjobs_to_file + */ +stringlist_type * lsf_job_alloc_parse_hostnames(const char* fname) { + FILE *stream = util_fopen(fname, "r"); + + bool at_eof = false; + while (!at_eof) { + char * line = util_fscanf_alloc_line(stream, &at_eof); + if (line != NULL) { + stringlist_type * hosts = stringlist_alloc_from_split(line, ":"); // bjobs uses : as std. delimiter + + for (int i = 0; i < stringlist_get_size(hosts); i++) { + const char * host = stringlist_iget(hosts, i); + stringlist_type * h = stringlist_alloc_from_split(host, "*"); + stringlist_iset_copy(hosts, i, stringlist_iget(h, stringlist_get_size(h) - 1)); // hostname 4*be-lsf01 -> be-lsf01 + stringlist_free(h); + } + + free(line); + fclose(stream); + return hosts; + } + } + fclose(stream); + return stringlist_alloc_new(); +} + +char* lsf_job_write_bjobs_to_file(const char * bjobs_cmd, lsf_driver_type * driver, const long jobid) { + // will typically run "bjobs -noheader -o 'EXEC_HOST' jobid" + + const char * noheader = "-noheader"; + const char * fields = "EXEC_HOST"; + char * cmd = (char*)util_alloc_sprintf("%s %s -o '%s' %d", bjobs_cmd, noheader, fields, jobid); + + char * tmp_file = (char*)util_alloc_tmp_file("/tmp", "ert_job_exec_host", true); + + if (driver->submit_method == LSF_SUBMIT_REMOTE_SHELL) { + char ** argv = (char**)util_calloc(2, sizeof *argv); + argv[0] = driver->remote_lsf_server; + argv[1] = cmd; + util_spawn_blocking(driver->rsh_cmd, 2, (const char **) argv, tmp_file, NULL); + free(argv); + } else if (driver->submit_method == LSF_SUBMIT_LOCAL_SHELL) + util_spawn_blocking(cmd, 0 , NULL , tmp_file, NULL); + + free(cmd); + + return tmp_file; +} + + +static void lsf_driver_internal_error( const lsf_driver_type * driver ) { + fprintf(stderr , "\n\n"); + fprintf(stderr , "*****************************************************************\n"); + fprintf(stderr , "** The LSF driver can be configured and used in many different **\n"); + fprintf(stderr , "** ways. The important point is how we choose to submit: **\n"); + fprintf(stderr , "** **\n"); + fprintf(stderr , "** 1. Using the lsf library calls **\n"); + fprintf(stderr , "** 2. Using the bsub/bjobs/bkill commands locally **\n"); + fprintf(stderr , "** 3. Using the bsub/bjobs/bkill commands through ssh **\n"); + fprintf(stderr , "** **\n"); + fprintf(stderr , "** To chose between these three alternatives you set the remote**\n"); + fprintf(stderr , "** server with the lsf_driver_set_option() function. Passing **\n"); + fprintf(stderr , "** the value NULL will give alternative 1, passing the special **\n"); + fprintf(stderr , "** string \'%s\' will give alternative 2, and any other **\n",LOCAL_LSF_SERVER); + fprintf(stderr , "** value will submit through that host using ssh. **\n"); + fprintf(stderr , "** **\n"); + fprintf(stderr , "** The ability to submit thorugh lsf library calls must be **\n"); + fprintf(stderr , "** compiled in by defining the symbol \'HAVE_LSF_LIBRARY\' when **\n"); + fprintf(stderr , "** compiling. **\n"); + fprintf(stderr , "** **\n"); +#ifdef HAVE_LSF_LIBRARY + fprintf(stderr , "** This lsf driver has support for using lsf library calls. **\n"); +#else + fprintf(stderr , "** This lsf driver does NOT have support for using lsf **\n"); + fprintf(stderr , "** library calls; but you have tried to submit without setting **\n"); + fprintf(stderr , "** a value for LSF_SERVER. Set this and try again. **\n"); +#endif + fprintf(stderr , "*****************************************************************\n\n"); + res_log_error("In lsf_driver, attempt at submitting without setting a value for LSF_SERVER."); + exit(1); +} + + + +static void lsf_driver_assert_submit_method( const lsf_driver_type * driver ) { + if (driver->submit_method == LSF_SUBMIT_INVALID) { + lsf_driver_internal_error(driver); + } +} + + +static std::string join_strings(const std::vector& strings, const std::string& sep) { + if (strings.empty()) + return ""; + + std::stringstream s; + s << strings[0]; + for (int i=1; i < strings.size(); i++) { + s << sep; + s << strings[i]; + } + return s.str(); +} + + +/* + A resource string can be "span[host=1] select[A && B] bla[xyz]". + The blacklisting feature is to have select[hname!=bad1 && hname!=bad2]. + + This function injects additional "hname!=node1 && ... && hname!=node2" into + the select[..] clause. This addition is the result of '&&'.join(select_list). +*/ +char* alloc_composed_resource_request(const lsf_driver_type * driver, + const std::vector& select_list) { + char* resreq = util_alloc_string_copy(driver->resource_request); + std::string excludes_string = join_strings(select_list, " && "); + + char* req = NULL; + char* pos = strstr(resreq, "select["); // find select[...] + + if (pos == NULL) { + // no select string in request, add select[...] + req = util_alloc_sprintf("%s select[%s]", resreq, excludes_string.c_str()); + } else { + // add select string to existing select[...] + char* endpos = strstr(pos, "]"); + if (endpos != NULL) + *endpos = ' '; + else + util_abort("%s could not find termination of select statement: %s", + __func__, resreq); + + // We split string into (before) "bla[..] bla[..] select[xxx_" + // and (after) "... bla[..] bla[..]". (we replaced one ']' with ' ') + // Then we make final string: before + &&excludes] + after + int before_size = endpos - resreq; + char * before = (char*)util_alloc_substring_copy(resreq, 0, before_size); + char * after = (char*)util_alloc_string_copy(&resreq[before_size]); + + req = util_alloc_sprintf("%s && %s]%s", before, excludes_string.c_str(), after); + } + free(resreq); + return req; +} + + +/* + The resource request string contains spaces, and when passed + through the shell it must be protected with \"..\"; this applies + when submitting to a remote lsf server with ssh. However when + submitting to the local workstation using a bsub command the + command will be invoked with the util_spawn() command - and no + shell is involved. In this latter case we must avoid the \"...\" + quoting. +*/ +static char * alloc_quoted_resource_string(const lsf_driver_type * driver) { + char * req = NULL; + if (driver->exclude_hosts.size() == 0) { + if (driver->resource_request) + req = util_alloc_string_copy(driver->resource_request); + } else { + std::vector select_list; + for (const auto& host : driver->exclude_hosts) { + std::string exclude_host = "hname!='" + host + "'"; + select_list.push_back(exclude_host); + } + + // select_list is non-empty + if (driver->resource_request != NULL) { + req = alloc_composed_resource_request(driver, select_list); + } else { + std::string select_string = join_strings(select_list, " && "); + req = util_alloc_sprintf("select[%s]", select_string.c_str()); + } + } + + char * quoted_resource_request = NULL; + if (req) { + if (driver->submit_method == LSF_SUBMIT_REMOTE_SHELL) + quoted_resource_request = util_alloc_sprintf("\"%s\"", req); + else + quoted_resource_request = util_alloc_string_copy(req); + free(req); + } + return quoted_resource_request; +} + + + +stringlist_type * lsf_driver_alloc_cmd(lsf_driver_type * driver , + const char * lsf_stdout , + const char * job_name , + const char * submit_cmd , + int num_cpu , + int job_argc, + const char ** job_argv) { + + stringlist_type * argv = stringlist_alloc_new(); + char * num_cpu_string = (char*)util_alloc_sprintf("%d" , num_cpu); + + char * quoted_resource_request = alloc_quoted_resource_string(driver); + + if (driver->submit_method == LSF_SUBMIT_REMOTE_SHELL) + stringlist_append_copy( argv , driver->bsub_cmd); + + stringlist_append_copy( argv , "-o" ); + stringlist_append_copy( argv , lsf_stdout ); + if (driver->queue_name != NULL) { + stringlist_append_copy( argv , "-q" ); + stringlist_append_copy( argv , driver->queue_name ); + } + stringlist_append_copy( argv , "-J" ); + stringlist_append_copy( argv , job_name ); + stringlist_append_copy( argv , "-n" ); + stringlist_append_copy( argv , num_cpu_string ); + + if (quoted_resource_request != NULL) { + stringlist_append_copy( argv , "-R"); + stringlist_append_copy( argv , quoted_resource_request ); + } + + if (driver->login_shell != NULL) { + stringlist_append_copy( argv , "-L"); + stringlist_append_copy( argv , driver->login_shell ); + } + + if (driver->project_code) { + stringlist_append_copy( argv , "-P"); + stringlist_append_copy( argv , driver->project_code ); + } + + stringlist_append_copy( argv , submit_cmd); + for (int iarg = 0; iarg < job_argc; iarg++) + stringlist_append_copy( argv , job_argv[ iarg ]); + + free( num_cpu_string ); + free( quoted_resource_request ); + return argv; +} + + +/** + * Submit internal job (LSF_SUBMIT_INTERNAL) using system calls instead of + * invoking shell commands. This method only works when actually called from + * an LSF node. + * + * Note that this method does not support the EXCLUDE_HOST configuration option. + */ +static int lsf_driver_submit_internal_job( lsf_driver_type * driver , + const char * lsf_stdout , + const char * job_name , + const char * submit_cmd , + int num_cpu , + int argc, + const char ** argv) { + +#ifdef HAVE_LSF_LIBRARY + char * command; + { + buffer_type * command_buffer = buffer_alloc( 256 ); + buffer_strcat( command_buffer , submit_cmd ); + for (int iarg = 0; iarg < argc; iarg++) { + buffer_strcat( command_buffer , " "); + buffer_strcat( command_buffer , argv[ iarg ]); + } + command = buffer_get_data( command_buffer ); + buffer_free_container( command_buffer ); + } + + { + int options = SUB_JOB_NAME + SUB_OUT_FILE; + + if (driver->queue_name != NULL) + options += SUB_QUEUE; + + if (driver->resource_request != NULL) + options += SUB_RES_REQ; + + if (driver->login_shell != NULL) + options += SUB_LOGIN_SHELL; + + driver->lsf_request.options = options; + } + + driver->lsf_request.resReq = driver->resource_request; + driver->lsf_request.loginShell = driver->login_shell; + driver->lsf_request.queue = driver->queue_name; + driver->lsf_request.jobName = (char *) job_name; + driver->lsf_request.outFile = (char *) lsf_stdout; + driver->lsf_request.command = command; + driver->lsf_request.numProcessors = num_cpu; + + { + int lsf_jobnr = lsb_submitjob( driver->lsb , &driver->lsf_request , &driver->lsf_reply ); + free( command ); /* I trust the lsf layer is finished with the command? */ + if (lsf_jobnr <= 0) + fprintf(stderr,"%s: ** Warning: lsb_submit() failed: %s \n",__func__ , lsb_sys_msg( driver->lsb )); + + return lsf_jobnr; + } +#else + lsf_driver_internal_error( driver ); + return -1; +#endif +} + + + +static int lsf_driver_submit_shell_job(lsf_driver_type * driver , + const char * lsf_stdout , + const char * job_name , + const char * submit_cmd , + int num_cpu , + int job_argc, + const char ** job_argv) { + int job_id; + char * tmp_file = (char*)util_alloc_tmp_file("/tmp" , "enkf-submit" , true); + + { + stringlist_type * remote_argv = lsf_driver_alloc_cmd( driver , lsf_stdout , job_name , submit_cmd , num_cpu , job_argc , job_argv); + + if (driver->submit_method == LSF_SUBMIT_REMOTE_SHELL) { + char ** argv = (char**)util_calloc( 2 , sizeof * argv ); + argv[0] = driver->remote_lsf_server; + argv[1] = stringlist_alloc_joined_string( remote_argv , " "); + + if (driver->debug_output) + printf("Submitting: %s %s %s \n",driver->rsh_cmd , argv[0] , argv[1]); + res_log_fdebug("Submitting: %s %s %s \n", driver->rsh_cmd, argv[0], argv[1]); + + util_spawn_blocking(driver->rsh_cmd, 2, (const char **) argv, tmp_file, NULL); + + free( argv[1] ); + free( argv ); + } else if (driver->submit_method == LSF_SUBMIT_LOCAL_SHELL) { + char ** argv = stringlist_alloc_char_ref( remote_argv ); + + if (driver->debug_output) { + printf("Submitting: %s ",driver->bsub_cmd); + stringlist_fprintf(remote_argv , " " , stdout); + printf("\n"); + } + util_spawn_blocking(driver->bsub_cmd, stringlist_get_size( remote_argv), (const char **) argv, tmp_file, tmp_file); + free( argv ); + } + + stringlist_free( remote_argv ); + } + + job_id = lsf_job_parse_bsub_stdout(driver->bsub_cmd , tmp_file); + util_unlink_existing( tmp_file ); + free(tmp_file); + return job_id; +} + + + +static int lsf_driver_get_status__(lsf_driver_type * driver , const char * status, const char * job_id) { + if (hash_has_key( driver->status_map , status)) + return hash_get_int( driver->status_map , status); + else { + util_exit("The lsf_status:%s for job:%s is not recognized; call your LSF administrator - sorry :-( \n", status , job_id); + return -1; + } +} + + + +static void lsf_driver_update_bjobs_table(lsf_driver_type * driver) { + char * tmp_file = (char*)util_alloc_tmp_file("/tmp" , "enkf-bjobs" , true); + + if (driver->submit_method == LSF_SUBMIT_REMOTE_SHELL) { + char ** argv = (char**)util_calloc( 2 , sizeof * argv); + argv[0] = driver->remote_lsf_server; + argv[1] = util_alloc_sprintf("%s -a" , driver->bjobs_cmd); + util_spawn_blocking(driver->rsh_cmd, 2, (const char **) argv, tmp_file, NULL); + free( argv[1] ); + free( argv ); + } else if (driver->submit_method == LSF_SUBMIT_LOCAL_SHELL) { + const char ** argv = (const char**)util_calloc( 1 , sizeof * argv); + argv[0] = "-a"; + util_spawn_blocking(driver->bjobs_cmd, 1, (const char **) argv, tmp_file, NULL); + free( argv ); + } + + { + char user[32]; + char status[16]; + FILE *stream = util_fopen(tmp_file , "r"); + bool at_eof = false; + hash_clear(driver->bjobs_cache); + util_fskip_lines(stream , 1); + while (!at_eof) { + char * line = util_fscanf_alloc_line(stream , &at_eof); + if (line != NULL) { + int job_id_int; + + if (sscanf(line , "%d %s %s", &job_id_int , user , status) == 3) { + char * job_id = (char*)util_alloc_sprintf("%d" , job_id_int); + + if (hash_has_key( driver->my_jobs , job_id )) /* Consider only jobs submitted by this ERT instance - not old jobs lying around from the same user. */ + hash_insert_int(driver->bjobs_cache , job_id , lsf_driver_get_status__( driver , status , job_id)); + + free(job_id); + } + free(line); + } + } + fclose(stream); + } + util_unlink_existing(tmp_file); + free(tmp_file); +} + + + +static int lsf_driver_get_job_status_libary(void * __driver , void * __job) { + if (__job == NULL) + /* the job has not been registered at all ... */ + return JOB_QUEUE_NOT_ACTIVE; + else { + int status; + lsf_driver_type * driver = lsf_driver_safe_cast( __driver ); +#ifdef HAVE_LSF_LIBRARY + lsf_job_type * job = lsf_job_safe_cast( __job ); + if (lsb_openjob( driver->lsb , job->lsf_jobnr) != 1) { + /* + Failed to get information about the job - we boldly assume + the following situation has occured: + + 1. The job is running happily along. + 2. The lsf deamon is not responding for a long time. + 3. The job finishes, and is eventually expired from the LSF job database. + 4. The lsf deamon answers again - but can not find the job... + + */ + fprintf(stderr,"Warning: failed to get status information for job:%ld - assuming it is finished. \n", job->lsf_jobnr); + status = JOB_QUEUE_DONE; + } else { + struct jobInfoEnt *job_info = lsb_readjob( driver->lsb ); + if (job->num_exec_host == 0) { + job->num_exec_host = job_info->numExHosts; + job->exec_host = util_alloc_stringlist_copy( (const char **) job_info->exHosts , job->num_exec_host); + } + status = job_info->status; + lsb_closejob(driver->lsb); + } +#else + lsf_driver_internal_error( driver ); + /* the above function calls exit(), so this value is never returned */ + status = JOB_QUEUE_FAILED; +#endif + + return status; + } +} + + +static bool lsf_driver_run_bhist(lsf_driver_type * driver , lsf_job_type * job , int * pend_time , int * run_time) { + bool bhist_ok = true; + char * output_file = (char*)util_alloc_tmp_file("/tmp" , "bhist" , true); + + if (driver->submit_method == LSF_SUBMIT_REMOTE_SHELL) { + char ** argv = (char**)util_calloc( 2 , sizeof * argv); + argv[0] = driver->remote_lsf_server; + argv[1] = util_alloc_sprintf("%s %s" , driver->bhist_cmd , job->lsf_jobnr_char); + util_spawn_blocking(driver->rsh_cmd, 2, (const char **) argv, output_file, NULL); + free( argv[1] ); + free( argv ); + } else if (driver->submit_method == LSF_SUBMIT_LOCAL_SHELL) { + char ** argv = (char**)util_calloc( 1 , sizeof * argv); + argv[0] = job->lsf_jobnr_char; + util_spawn_blocking(driver->bjobs_cmd, 2, (const char **) argv, output_file, NULL); + free( argv ); + } + + { + char job_id[16]; + char user[32]; + char job_name[32]; + int psusp_time; + + FILE *stream = util_fopen(output_file , "r");; + util_fskip_lines(stream , 2); + + if ( fscanf( stream , "%s %s %s %d %d %d" , job_id , user , job_name , pend_time , &psusp_time , run_time) == 6) + bhist_ok = true; + else + bhist_ok = false; + + fclose( stream ); + } + util_unlink_existing(output_file); + free(output_file); + + return bhist_ok; +} + +/* + When a job has completed you can query the status using the bjobs + command for a while, and then the job will be evicted from the LSF + status table. If there have been connection problems with the LSF + server we can risk a situation where a job has completed and + subsequently evicted from the LSF status table, before we are able + to record the DONE/EXIT status. + + When a job is missing from the bjobs_cache table we as a last resort + invoke the bhist command (which is based on internal LSF data with + much longer lifetime) and measure the change in run_time and + pend_time between two subsequent calls: + + + 1. ((pend_time1 == pend_time2) && (run_time1 == run_time2)) : + Nothing is happening, and we assume that the job is DONE (this + method can not distinguish between DONE and EXIT). + + 2. (run_time2 > run_time1) : The job is running. + + 3. (pend_tim2 > pend_time1) : The job is pending. + + 4. Status unknown - have not got a clue?! +*/ + + +static int lsf_driver_get_bhist_status_shell( lsf_driver_type * driver , lsf_job_type * job) { + int status = JOB_STAT_UNKWN; + int sleep_time = 4; + int run_time1, run_time2, pend_time1 , pend_time2; + + res_log_ferror("** Warning: could not find status of job:%s/%s using \'bjobs\'" + " - trying with \'bhist\'.\n", + job->lsf_jobnr_char, + job->job_name); + if (!lsf_driver_run_bhist( driver , job , &pend_time1 , &run_time1)) + return status; + + sleep( sleep_time ); + if (!lsf_driver_run_bhist( driver , job , &pend_time2 , &run_time2)) + return status; + + if ((run_time1 == run_time2) && (pend_time1 == pend_time2)) + status = JOB_STAT_DONE; + + if (pend_time2 > pend_time1) + status = JOB_STAT_PEND; + + if (run_time2 > run_time1) + status = JOB_STAT_RUN; + + return status; +} + + +static int lsf_driver_get_job_status_shell(void * __driver , void * __job) { + int status = JOB_STAT_NULL; + + if (__job != NULL) { + lsf_job_type * job = lsf_job_safe_cast( __job ); + lsf_driver_type * driver = lsf_driver_safe_cast( __driver ); + + { + /** + Updating the bjobs_table of the driver involves a significant change in + the internal state of the driver; that is semantically a bit + unfortunate because this is clearly a get() function; to protect + against concurrent updates of this table we use a mutex. + */ + pthread_mutex_lock( &driver->bjobs_mutex ); + { + bool update_cache = ((difftime(time(NULL) , driver->last_bjobs_update) > driver->bjobs_refresh_interval) || + (!hash_has_key( driver->bjobs_cache , job->lsf_jobnr_char) )); + if (update_cache) { + lsf_driver_update_bjobs_table(driver); + driver->last_bjobs_update = time( NULL ); + } + } + pthread_mutex_unlock( &driver->bjobs_mutex ); + + if (hash_has_key( driver->bjobs_cache , job->lsf_jobnr_char) ) + status = hash_get_int(driver->bjobs_cache , job->lsf_jobnr_char); + else { + /* + The job was not in the status cache, this *might* mean that + it has completed/exited and fallen out of the bjobs status + table maintained by LSF. We try calling bhist to get the status. + */ + res_log_warning("In lsf_driver we found that job was not in the " + "status cache, this *might* mean that it has " + "completed/exited and fallen out of the bjobs " + "status table maintained by LSF."); + if (!driver->debug_output) { + driver->debug_output = true; + res_log_info("Have turned lsf debug info ON."); + } + status = lsf_driver_get_bhist_status_shell( driver , job ); + hash_insert_int( driver->bjobs_cache , job->lsf_jobnr_char , status ); + } + } + } + + return status; +} + + +job_status_type lsf_driver_convert_status( int lsf_status ) { + job_status_type job_status; + switch (lsf_status) { + case JOB_STAT_NULL: + job_status = JOB_QUEUE_NOT_ACTIVE; + break; + case JOB_STAT_PEND: + job_status = JOB_QUEUE_PENDING; + break; + case JOB_STAT_SSUSP: + job_status = JOB_QUEUE_RUNNING; + break; + case JOB_STAT_USUSP: + job_status = JOB_QUEUE_RUNNING; + break; + case JOB_STAT_PSUSP: + job_status = JOB_QUEUE_RUNNING; + break; + case JOB_STAT_RUN: + job_status = JOB_QUEUE_RUNNING; + break; + case JOB_STAT_DONE: + job_status = JOB_QUEUE_DONE; + break; + case JOB_STAT_EXIT: + job_status = JOB_QUEUE_EXIT; + break; + case JOB_STAT_UNKWN: // Have lost contact with one of the daemons. + job_status = JOB_QUEUE_UNKNOWN; + break; + case JOB_STAT_DONE + JOB_STAT_PDONE: // = 192. JOB_STAT_PDONE: the job had a + // post-execution script which completed + // successfully. + job_status = JOB_QUEUE_DONE; + break; + default: + job_status = JOB_QUEUE_NOT_ACTIVE; + util_abort("%s: unrecognized lsf status code:%d \n",__func__ , lsf_status ); + } + return job_status; +} + + +int lsf_driver_get_job_status_lsf(void * __driver , void * __job) { + int lsf_status; + lsf_driver_type * driver = lsf_driver_safe_cast( __driver ); + + if (driver->submit_method == LSF_SUBMIT_INTERNAL) + lsf_status = lsf_driver_get_job_status_libary(__driver , __job); + else + lsf_status = lsf_driver_get_job_status_shell(__driver , __job); + + return lsf_status; +} + + + +job_status_type lsf_driver_get_job_status(void * __driver , void * __job) { + int lsf_status = lsf_driver_get_job_status_lsf( __driver , __job ); + return lsf_driver_convert_status( lsf_status ); +} + + + + +void lsf_driver_free_job(void * __job) { + lsf_job_type * job = lsf_job_safe_cast( __job ); + lsf_job_free(job); +} + +static void lsf_driver_node_failure(lsf_driver_type * driver, const lsf_job_type * job) { + long lsf_job_id = lsf_job_get_jobnr(job); + char * fname = lsf_job_write_bjobs_to_file(driver->bjobs_cmd, driver, lsf_job_id); + stringlist_type * hosts = lsf_job_alloc_parse_hostnames(fname); + char* hostnames = stringlist_alloc_joined_string(hosts, ", "); + + res_log_ferror("The job:%ld/%s never started - the nodes: " + "%s will be excluded, the job will be resubmitted to LSF.\n", + lsf_job_id, + job->job_name, + hostnames); + lsf_driver_add_exclude_hosts(driver, hostnames); + if (!driver->debug_output) { + driver->debug_output = true; + res_log_info("Have turned lsf debug info ON."); + } + free(hostnames); + stringlist_free(hosts); + free(fname); +} + + +void lsf_driver_blacklist_node(void * __driver, void * __job) { + lsf_driver_type * driver = lsf_driver_safe_cast(__driver); + lsf_job_type * job = lsf_job_safe_cast(__job); + lsf_driver_node_failure(driver, job); +} + + +void lsf_driver_kill_job(void * __driver , void * __job) { + lsf_driver_type * driver = lsf_driver_safe_cast( __driver ); + lsf_job_type * job = lsf_job_safe_cast( __job ); + { + if (driver->submit_method == LSF_SUBMIT_INTERNAL) { +#ifdef HAVE_LSF_LIBRARY + lsb_killjob( driver->lsb , job->lsf_jobnr); +#else + lsf_driver_internal_error( driver ); +#endif + } else { + if (driver->submit_method == LSF_SUBMIT_REMOTE_SHELL) { + char ** argv = (char**)util_calloc( 2, sizeof * argv ); + argv[0] = driver->remote_lsf_server; + argv[1] = util_alloc_sprintf("%s %s" , driver->bkill_cmd , job->lsf_jobnr_char); + + util_spawn_blocking(driver->rsh_cmd, 2, (const char **) argv, NULL, NULL); + + free( argv[1] ); + free( argv ); + } else if (driver->submit_method == LSF_SUBMIT_LOCAL_SHELL) { + util_spawn_blocking(driver->bkill_cmd, 1, (const char **) &job->lsf_jobnr_char, NULL, NULL); + } + } + } +} + + + + + +void * lsf_driver_submit_job(void * __driver , + const char * submit_cmd , + int num_cpu , + const char * run_path , + const char * job_name , + int argc, + const char ** argv ) { + lsf_driver_type * driver = lsf_driver_safe_cast( __driver ); + lsf_driver_assert_submit_method( driver ); + { + lsf_job_type * job = lsf_job_alloc( job_name ); + usleep( driver->submit_sleep ); + + { + char * lsf_stdout = (char*)util_alloc_filename(run_path , job_name , "LSF-stdout"); + lsf_submit_method_enum submit_method = driver->submit_method; + pthread_mutex_lock( &driver->submit_lock ); + + res_log_finfo("LSF DRIVER submitting using method:%d \n", submit_method); + + if (submit_method == LSF_SUBMIT_INTERNAL) { + if (driver->exclude_hosts.size() > 0){ + res_log_warning("EXCLUDE_HOST is not supported with submit method LSF_SUBMIT_INTERNAL"); + } + job->lsf_jobnr = lsf_driver_submit_internal_job( driver , lsf_stdout , job_name , submit_cmd , num_cpu , argc, argv); + } else { + job->lsf_jobnr = lsf_driver_submit_shell_job( driver , lsf_stdout , job_name , submit_cmd , num_cpu , argc, argv); + job->lsf_jobnr_char = util_alloc_sprintf("%ld" , job->lsf_jobnr); + hash_insert_ref( driver->my_jobs , job->lsf_jobnr_char , NULL ); + } + + pthread_mutex_unlock( &driver->submit_lock ); + free( lsf_stdout ); + } + + if (job->lsf_jobnr > 0) { + char * json_file = (char*)util_alloc_filename(run_path, LSF_JSON, NULL); + FILE * stream = util_fopen(json_file, "w"); + fprintf(stream, "{\"job_id\" : %ld}\n", job->lsf_jobnr ); + free(json_file); + fclose(stream); + return job; + } + else { + /* + The submit failed - the queue system shall handle + NULL return values. + */ + driver->error_count++; + + if (driver->error_count >= driver->max_error_count) + util_exit("Maximum number of submit errors exceeded - giving up\n"); + else { + res_log_error("** ERROR ** Failed when submitting to LSF - will try again."); + if (!driver->debug_output) { + driver->debug_output = true; + res_log_finfo("Have turned lsf debug info ON."); + } + usleep( driver->submit_error_sleep ); + } + + lsf_job_free(job); + return NULL; + } + } +} + + + +void lsf_driver_free(lsf_driver_type * driver ) { + free(driver->login_shell); + free(driver->queue_name); + free(driver->resource_request ); + free(driver->remote_lsf_server ); + free(driver->rsh_cmd ); + free( driver->bhist_cmd ); + free( driver->bkill_cmd ); + free( driver->bjobs_cmd ); + free( driver->bsub_cmd ); + free( driver->project_code ); + + hash_free(driver->status_map); + hash_free(driver->bjobs_cache); + hash_free(driver->my_jobs); + +#ifdef HAVE_LSF_LIBRARY + if (driver->lsb != NULL) + lsb_free( driver->lsb ); +#endif + + delete driver; + driver = NULL; +} + +void lsf_driver_free__(void * __driver ) { + lsf_driver_type * driver = lsf_driver_safe_cast( __driver ); + lsf_driver_free( driver ); +} + + +static void lsf_driver_set_project_code( lsf_driver_type * driver, const char * project_code) { + driver->project_code = util_realloc_string_copy( driver->project_code , project_code); +} + +static void lsf_driver_set_queue( lsf_driver_type * driver, const char * queue ) { + driver->queue_name = util_realloc_string_copy( driver->queue_name , queue); +} + + +static void lsf_driver_set_login_shell( lsf_driver_type * driver, const char * login_shell ) { + driver->login_shell = util_realloc_string_copy( driver->login_shell , login_shell); +} + +static void lsf_driver_set_rsh_cmd( lsf_driver_type * driver , const char * rsh_cmd) { + driver->rsh_cmd = util_realloc_string_copy( driver->rsh_cmd , rsh_cmd ); +} + +static void lsf_driver_set_bsub_cmd( lsf_driver_type * driver , const char * bsub_cmd) { + driver->bsub_cmd = util_realloc_string_copy( driver->bsub_cmd , bsub_cmd ); +} + +static void lsf_driver_set_bjobs_cmd( lsf_driver_type * driver , const char * bjobs_cmd) { + driver->bjobs_cmd = util_realloc_string_copy( driver->bjobs_cmd , bjobs_cmd ); +} + +static void lsf_driver_set_bkill_cmd( lsf_driver_type * driver , const char * bkill_cmd) { + driver->bkill_cmd = util_realloc_string_copy( driver->bkill_cmd , bkill_cmd ); +} + +static void lsf_driver_set_bhist_cmd( lsf_driver_type * driver , const char * bhist_cmd) { + driver->bhist_cmd = util_realloc_string_copy( driver->bhist_cmd , bhist_cmd ); +} + +#ifdef HAVE_LSF_LIBRARY +static void lsf_driver_set_internal_submit( lsf_driver_type * driver) { + /* No remote server has been set - assuming we can issue proper library calls. */ + /* The BSUB_QUEUE variable must NOT be set when using the shell + function, because then stdout is redirected and read. */ + + util_setenv("BSUB_QUIET" , "yes"); + driver->submit_method = LSF_SUBMIT_INTERNAL; + free( driver->remote_lsf_server ); + driver->remote_lsf_server = NULL; +} +#endif + +static void lsf_driver_set_remote_server( lsf_driver_type * driver , const char * remote_server) { + if (remote_server == NULL) { +#ifdef HAVE_LSF_LIBRARY + if (driver->lsb) + lsf_driver_set_internal_submit( driver ); + else + lsf_driver_set_remote_server( driver , LOCAL_LSF_SERVER ); // If initializing the lsb layer failed we try the local shell commands. +#endif + } else { + driver->remote_lsf_server = util_realloc_string_copy( driver->remote_lsf_server , remote_server ); + res_env_unsetenv( "BSUB_QUIET" ); + { + char * tmp_server = (char*)util_alloc_strupr_copy( remote_server ); + + if (strcmp(tmp_server , LOCAL_LSF_SERVER) == 0) + driver->submit_method = LSF_SUBMIT_LOCAL_SHELL; + else if (strcmp(tmp_server , NULL_LSF_SERVER) == 0) // We trap the special string 'NULL' and call again with a true NULL pointer. + lsf_driver_set_remote_server( driver , NULL); + else + driver->submit_method = LSF_SUBMIT_REMOTE_SHELL; + + free( tmp_server ); + } + } +} + +void lsf_driver_add_exclude_hosts(lsf_driver_type * driver, const char * excluded) { + stringlist_type * host_list = stringlist_alloc_from_split(excluded, ", "); + for (int i = 0; i < stringlist_get_size(host_list); i++) { + const char * excluded = stringlist_iget(host_list, i); + const auto& iter = std::find( driver->exclude_hosts.begin(), driver->exclude_hosts.end(), std::string(excluded)); + if (iter == driver->exclude_hosts.end()) + driver->exclude_hosts.push_back(excluded); + } +} + +lsf_submit_method_enum lsf_driver_get_submit_method( const lsf_driver_type * driver ) { + return driver->submit_method; +} + + +static bool lsf_driver_set_debug_output( lsf_driver_type * driver , const char * arg) { + bool debug_output; + bool OK = util_sscanf_bool( arg , &debug_output); + if (OK) + driver->debug_output = debug_output; + + return OK; +} + + +static bool lsf_driver_set_submit_sleep( lsf_driver_type * driver , const char * arg) { + double submit_sleep; + bool OK = util_sscanf_double( arg , &submit_sleep); + if (OK) + driver->submit_sleep = (int) (1000000 * submit_sleep); + + return OK; +} + +void lsf_driver_set_bjobs_refresh_interval_option( lsf_driver_type * driver , const char * option_value) { + int refresh_interval; + if (util_sscanf_int( option_value , &refresh_interval)) + lsf_driver_set_bjobs_refresh_interval( driver , refresh_interval ); +} + + + +/*****************************************************************/ +/* Generic functions for runtime manipulation of options. + + LSF_SERVER + LSF_QUEUE + LSF_RESOURCE +*/ + +bool lsf_driver_set_option( void * __driver , const char * option_key , const void * value_) { + const char * value = (const char *) value_; + lsf_driver_type * driver = lsf_driver_safe_cast( __driver ); + bool has_option = true; + { + if (strcmp( LSF_RESOURCE , option_key ) == 0) + driver->resource_request = util_realloc_string_copy( driver->resource_request , value ); + else if (strcmp( LSF_SERVER , option_key) == 0) + lsf_driver_set_remote_server( driver , value ); + else if (strcmp( LSF_QUEUE , option_key) == 0) + lsf_driver_set_queue( driver , value ); + else if (strcmp( LSF_LOGIN_SHELL , option_key) == 0) + lsf_driver_set_login_shell( driver , value ); + else if (strcmp( LSF_RSH_CMD , option_key) == 0) + lsf_driver_set_rsh_cmd( driver , value ); + else if (strcmp( LSF_BSUB_CMD , option_key) == 0) + lsf_driver_set_bsub_cmd( driver , value ); + else if (strcmp( LSF_BJOBS_CMD , option_key) == 0) + lsf_driver_set_bjobs_cmd( driver , value ); + else if (strcmp( LSF_BKILL_CMD , option_key) == 0) + lsf_driver_set_bkill_cmd( driver , value ); + else if (strcmp( LSF_BHIST_CMD , option_key) == 0) + lsf_driver_set_bhist_cmd( driver , value ); + else if (strcmp( LSF_DEBUG_OUTPUT , option_key) == 0) + lsf_driver_set_debug_output( driver , value ); + else if (strcmp( LSF_SUBMIT_SLEEP , option_key) == 0) + lsf_driver_set_submit_sleep( driver , value ); + else if (strcmp( LSF_EXCLUDE_HOST , option_key) == 0) + lsf_driver_add_exclude_hosts( driver , value ); + else if (strcmp( LSF_BJOBS_TIMEOUT , option_key) == 0) + lsf_driver_set_bjobs_refresh_interval_option( driver , value ); + else if (strcmp( LSF_PROJECT_CODE , option_key) == 0) + lsf_driver_set_project_code( driver , value ); + else + has_option = false; + } + return has_option; +} + + +const void * lsf_driver_get_option( const void * __driver , const char * option_key) { + const lsf_driver_type * driver = lsf_driver_safe_cast_const( __driver ); + { + if (strcmp( LSF_RESOURCE , option_key ) == 0) + return driver->resource_request; + else if (strcmp( LSF_SERVER , option_key ) == 0) + return driver->remote_lsf_server; + else if (strcmp( LSF_QUEUE , option_key ) == 0) + return driver->queue_name; + else if (strcmp( LSF_LOGIN_SHELL , option_key ) == 0) + return driver->login_shell; + else if (strcmp( LSF_RSH_CMD , option_key ) == 0) + return driver->rsh_cmd; + else if (strcmp( LSF_BJOBS_CMD , option_key ) == 0) + return driver->bjobs_cmd; + else if (strcmp( LSF_BSUB_CMD , option_key ) == 0) + return driver->bsub_cmd; + else if (strcmp( LSF_BKILL_CMD , option_key ) == 0) + return driver->bkill_cmd; + else if (strcmp( LSF_BHIST_CMD , option_key ) == 0) + return driver->bhist_cmd; + else if (strcmp( LSF_PROJECT_CODE , option_key ) == 0) + /* Will be NULL if the project code has not been set. */ + return driver->project_code; + else if (strcmp( LSF_BJOBS_TIMEOUT , option_key ) == 0) { + /* This will leak. */ + char * timeout_string = (char*)util_alloc_sprintf( "%d" , driver->bjobs_refresh_interval ); + return timeout_string; + } else { + util_abort("%s: option_id:%s not recognized for LSF driver \n",__func__ , option_key); + return NULL; + } + } +} + + + +void lsf_driver_init_option_list(stringlist_type * option_list) { + stringlist_append_copy(option_list, LSF_QUEUE); + stringlist_append_copy(option_list, LSF_RESOURCE); + stringlist_append_copy(option_list, LSF_SERVER); + stringlist_append_copy(option_list, LSF_RSH_CMD); + stringlist_append_copy(option_list, LSF_LOGIN_SHELL); + stringlist_append_copy(option_list, LSF_BSUB_CMD); + stringlist_append_copy(option_list, LSF_BJOBS_CMD); + stringlist_append_copy(option_list, LSF_BKILL_CMD); + stringlist_append_copy(option_list, LSF_BHIST_CMD); + stringlist_append_copy(option_list, LSF_BJOBS_TIMEOUT); +} + + + +/*****************************************************************/ + +/* + Observe that this driver IS not properly initialized when returning + from this function, the option interface must be used to set the + keys: +*/ + +void lsf_driver_set_bjobs_refresh_interval( lsf_driver_type * driver , int refresh_interval) { + driver->bjobs_refresh_interval = refresh_interval; +} + + + + +static void lsf_driver_lib_init( lsf_driver_type * lsf_driver ) { +#ifdef HAVE_LSF_LIBRARY + memset(&lsf_driver->lsf_request , 0 , sizeof (lsf_driver->lsf_request)); + lsf_driver->lsf_request.beginTime = 0; + lsf_driver->lsf_request.termTime = 0; + lsf_driver->lsf_request.numProcessors = 1; + lsf_driver->lsf_request.maxNumProcessors = 1; + { + int i; + for (i=0; i < LSF_RLIM_NLIMITS; i++) + lsf_driver->lsf_request.rLimits[i] = DEFAULT_RLIMIT; + } + lsf_driver->lsf_request.options2 = 0; + + lsf_driver->lsb = lsb_alloc(); + if (lsb_ready(lsf_driver->lsb)) + lsb_initialize(lsf_driver->lsb); + else { + lsb_free( lsf_driver->lsb ); + lsf_driver->lsb = NULL; + } +#endif +} + + + +static void lsf_driver_shell_init( lsf_driver_type * lsf_driver ) { + lsf_driver->last_bjobs_update = time( NULL ); + lsf_driver->bjobs_cache = hash_alloc(); + lsf_driver->my_jobs = hash_alloc(); + lsf_driver->status_map = hash_alloc(); + lsf_driver->bsub_cmd = NULL; + lsf_driver->bjobs_cmd = NULL; + lsf_driver->bkill_cmd = NULL; + lsf_driver->bhist_cmd = NULL; + + + hash_insert_int(lsf_driver->status_map , "PEND" , JOB_STAT_PEND); + hash_insert_int(lsf_driver->status_map , "SSUSP" , JOB_STAT_SSUSP); + hash_insert_int(lsf_driver->status_map , "PSUSP" , JOB_STAT_PSUSP); + hash_insert_int(lsf_driver->status_map , "USUSP" , JOB_STAT_USUSP); + hash_insert_int(lsf_driver->status_map , "RUN" , JOB_STAT_RUN); + hash_insert_int(lsf_driver->status_map , "EXIT" , JOB_STAT_EXIT); + hash_insert_int(lsf_driver->status_map , "ZOMBI" , JOB_STAT_EXIT); /* The ZOMBI status does not seem to be available from the api. */ + hash_insert_int(lsf_driver->status_map , "DONE" , JOB_STAT_DONE); + hash_insert_int(lsf_driver->status_map , "PDONE" , JOB_STAT_PDONE); /* Post-processor is done. */ + hash_insert_int(lsf_driver->status_map , "UNKWN" , JOB_STAT_UNKWN); /* Uncertain about this one */ + pthread_mutex_init( &lsf_driver->bjobs_mutex , NULL ); +} + + +/* + If the lsb library is compiled in and the runtime loading of the lsb libraries + has succeeded we default to submitting through internal library calls, + otherwise we will submit using shell commands on the local workstation. +*/ + +static void lsf_driver_init_submit_method( lsf_driver_type * driver ) { +#ifdef HAVE_LSF_LIBRARY + if (lsb_ready(driver->lsb)) { + driver->submit_method = LSF_SUBMIT_INTERNAL; + return; + } +#endif + + driver->submit_method = LSF_SUBMIT_LOCAL_SHELL; +} + +bool lsf_driver_has_project_code( const lsf_driver_type * driver ) { + if (driver->project_code) + return true; + else + return false; +} + + +void * lsf_driver_alloc( ) { + lsf_driver_type * lsf_driver = new lsf_driver_type(); + + UTIL_TYPE_ID_INIT( lsf_driver , LSF_DRIVER_TYPE_ID); + lsf_driver->submit_method = LSF_SUBMIT_INVALID; + lsf_driver->login_shell = NULL; + lsf_driver->queue_name = NULL; + lsf_driver->remote_lsf_server = NULL; + lsf_driver->rsh_cmd = NULL; + lsf_driver->resource_request = NULL; + lsf_driver->project_code = NULL; + lsf_driver->error_count = 0; + lsf_driver->max_error_count = MAX_ERROR_COUNT; + lsf_driver->submit_error_sleep = SUBMIT_ERROR_SLEEP * 1000000; + pthread_mutex_init( &lsf_driver->submit_lock , NULL ); + + lsf_driver_lib_init( lsf_driver ); + lsf_driver_shell_init( lsf_driver ); + lsf_driver_init_submit_method( lsf_driver ); + + lsf_driver_set_option( lsf_driver , LSF_SERVER , NULL ); + lsf_driver_set_option( lsf_driver , LSF_RSH_CMD , DEFAULT_RSH_CMD ); + lsf_driver_set_option( lsf_driver , LSF_BSUB_CMD , DEFAULT_BSUB_CMD ); + lsf_driver_set_option( lsf_driver , LSF_BJOBS_CMD , DEFAULT_BJOBS_CMD ); + lsf_driver_set_option( lsf_driver , LSF_BKILL_CMD , DEFAULT_BKILL_CMD ); + lsf_driver_set_option( lsf_driver , LSF_BHIST_CMD , DEFAULT_BHIST_CMD ); + lsf_driver_set_option( lsf_driver , LSF_DEBUG_OUTPUT , "FALSE"); + lsf_driver_set_option( lsf_driver , LSF_SUBMIT_SLEEP , DEFAULT_SUBMIT_SLEEP); + lsf_driver_set_option( lsf_driver , LSF_BJOBS_TIMEOUT , BJOBS_REFRESH_TIME); + return lsf_driver; +} + + +/*****************************************************************/ diff --git a/libres/lib/job_queue/queue_driver.cpp b/libres/lib/job_queue/queue_driver.cpp new file mode 100644 index 00000000000..e449b1abe34 --- /dev/null +++ b/libres/lib/job_queue/queue_driver.cpp @@ -0,0 +1,411 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'queue_driver.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 + + +/** + This file implements the datatype queue_driver_type which is an + abstract datatype for communicating with a subsystem for + communcating with other low-level systems for running external + jobs. The job_queue instance, which will handle a queue of jobs, + interacts with the jobs through a queue_driver instance. + + The queue_driver type is a quite small datastructure which "wraps" + and underlying specific driver instance; examples of specific + driver instances are the lsf_driver which communicates with the LSF + system and the local_driver which runs jobs directly on the current + workstation. The queue_driver type contains essentially three + different types of fields: + + 1. Functions pointers for manipulating the jobs, and the state of + the low-level driver. + + 2. An opaque (i.e. void *) pointer to the state of the low level + driver. This will be passed as first argument to all the + function pointers, e.g. like the "self" in Python methods. + + 3. Some data fields which are common to all driver types. + + */ + +#define QUEUE_DRIVER_ID 86516032 + +struct queue_driver_struct { + UTIL_TYPE_ID_DECLARATION; + /* + Function pointers - pointing to low level functions in the implementations of + e.g. lsf_driver. + */ + submit_job_ftype * submit; + free_job_ftype * free_job; + kill_job_ftype * kill_job; + blacklist_node_ftype * blacklist_node; + get_status_ftype * get_status; + free_queue_driver_ftype * free_driver; + set_option_ftype * set_option; + get_option_ftype * get_option; + init_option_list_ftype * init_options; + + void * data; /* Driver specific data - passed as first argument to the driver functions above. */ + + /* + Generic data - common to all driver types. + */ + char * name; /* String name of driver. */ + job_driver_type driver_type; /* Enum value for driver. */ + char * max_running_string; + int max_running; /* Possible to maintain different max_running values for different + drivers; the value 0 is interpreted as no limit - i.e. the queue layer + will (try) to send an unlimited number of jobs to the driver. */ + +}; + +UTIL_IS_INSTANCE_FUNCTION( queue_driver, QUEUE_DRIVER_ID ) + +/*****************************************************************/ + + +/*****************************************************************/ + +void queue_driver_set_max_running(queue_driver_type * driver, int max_running) { + driver->max_running_string = util_realloc_sprintf(driver->max_running_string,"%d", max_running); + driver->max_running = max_running; +} + +int queue_driver_get_max_running(const queue_driver_type * driver) { + return driver->max_running; +} + +const char * queue_driver_get_name(const queue_driver_type * driver) { + return driver->name; +} + + +static bool queue_driver_set_generic_option__(queue_driver_type * driver, const char * option_key, const void * value_) { + const char * value = (const char*) value_; + bool option_set = true; + { + if (strcmp(MAX_RUNNING, option_key) == 0) { + int max_running_int = 0; + if (util_sscanf_int(value, &max_running_int)) { + queue_driver_set_max_running(driver, max_running_int); + option_set = true; + } + else + option_set = false; + } else + option_set = false; + } + return option_set; +} + +static bool queue_driver_unset_generic_option__(queue_driver_type * driver, const char * option_key) { + bool option_unset = false; + if (strcmp(MAX_RUNNING, option_key) == 0) { + queue_driver_set_max_running(driver, 0); + option_unset = true; + } + return option_unset; +} + + +static void * queue_driver_get_generic_option__(queue_driver_type * driver, const char * option_key) { + if (strcmp(MAX_RUNNING, option_key) == 0) { + return driver->max_running_string; + } else { + util_abort("%s: driver:%s does not support generic option %s\n", __func__, driver->name, option_key); + return NULL; + } +} + +static bool queue_driver_has_generic_option__(queue_driver_type * driver, const char * option_key) { + if (strcmp(MAX_RUNNING, option_key) == 0) + return true; + else + return false; +} + +/** + Set option - can also be used to perform actions - not only setting + of parameters. There is no limit :-) + */ +bool queue_driver_set_option(queue_driver_type * driver, const char * option_key, const void * value) { + if (queue_driver_set_generic_option__(driver, option_key, value)) { + return true; + } else if (driver->set_option != NULL) + /* The actual low level set functions can not fail! */ + return driver->set_option(driver->data, option_key, value); + else { + util_abort("%s: driver:%s does not support run time setting of options\n", __func__, driver->name); + return false; + } + return false; +} + +/** + Unset the given option. If the option cannot be unset, it is restored to its default value. + */ +bool queue_driver_unset_option(queue_driver_type * driver, const char * option_key) { + if (queue_driver_unset_generic_option__(driver, option_key)) { + return true; + } else if (driver->set_option != NULL) + /* The actual low level set functions can not fail! */ + return driver->set_option(driver->data, option_key, NULL); + else { + util_abort("%s: driver:%s does not support run time setting of options\n", __func__, driver->name); + return false; + } + return false; +} + + + +/** + Observe that after the driver instance has been allocated it does + NOT support modification of the common fields, only the data owned + by the specific low level driver, i.e. the LSF data, can be + modified runtime. + + The driver returned from the queue_driver_alloc_empty() function is + NOT properly initialized and NOT ready for use. + */ + +static queue_driver_type * queue_driver_alloc_empty() { + queue_driver_type * driver = (queue_driver_type*)util_malloc(sizeof * driver); + UTIL_TYPE_ID_INIT(driver, QUEUE_DRIVER_ID); + driver->driver_type = NULL_DRIVER; + driver->submit = NULL; + driver->get_status = NULL; + driver->kill_job = NULL; + driver->free_job = NULL; + driver->free_driver = NULL; + driver->get_option = NULL; + driver->set_option = NULL; + driver->name = NULL; + driver->data = NULL; + driver->max_running_string = NULL; + driver->init_options = NULL; + driver->blacklist_node = NULL; + queue_driver_set_generic_option__(driver, MAX_RUNNING, "0"); + + return driver; +} + +static UTIL_SAFE_CAST_FUNCTION(queue_driver, QUEUE_DRIVER_ID) + + +/** + The driver created in this function has all the function pointers + correctly initialized; but no options have been set. I.e. unless + the driver in question needs no options (e.g. the LOCAL driver) the + returned driver will NOT be ready for use. + */ + + +queue_driver_type * queue_driver_alloc(job_driver_type type) { + queue_driver_type * driver = queue_driver_alloc_empty(); + driver->driver_type = type; + switch (type) { + case LSF_DRIVER: + driver->submit = lsf_driver_submit_job; + driver->get_status = lsf_driver_get_job_status; + driver->blacklist_node = lsf_driver_blacklist_node; + driver->kill_job = lsf_driver_kill_job; + driver->free_job = lsf_driver_free_job; + driver->free_driver = lsf_driver_free__; + driver->set_option = lsf_driver_set_option; + driver->get_option = lsf_driver_get_option; + driver->name = util_alloc_string_copy("LSF"); + driver->init_options = lsf_driver_init_option_list; + driver->data = lsf_driver_alloc(); + break; + case LOCAL_DRIVER: + driver->submit = local_driver_submit_job; + driver->get_status = local_driver_get_job_status; + driver->kill_job = local_driver_kill_job; + driver->free_job = local_driver_free_job; + driver->free_driver = local_driver_free__; + driver->name = util_alloc_string_copy("local"); + driver->init_options = local_driver_init_option_list; + driver->data = local_driver_alloc(); + break; + case RSH_DRIVER: + driver->submit = rsh_driver_submit_job; + driver->get_status = rsh_driver_get_job_status; + driver->kill_job = rsh_driver_kill_job; + driver->free_job = rsh_driver_free_job; + driver->free_driver = rsh_driver_free__; + driver->set_option = rsh_driver_set_option; + driver->get_option = rsh_driver_get_option; + driver->name = util_alloc_string_copy("RSH"); + driver->init_options = rsh_driver_init_option_list; + driver->data = rsh_driver_alloc(); + break; + case TORQUE_DRIVER: + driver->submit = torque_driver_submit_job; + driver->get_status = torque_driver_get_job_status; + driver->kill_job = torque_driver_kill_job; + driver->free_job = torque_driver_free_job; + driver->free_driver = torque_driver_free__; + driver->set_option = torque_driver_set_option; + driver->get_option = torque_driver_get_option; + driver->name = util_alloc_string_copy("TORQUE"); + driver->init_options = torque_driver_init_option_list; + driver->data = torque_driver_alloc(); + break; + case SLURM_DRIVER: + driver->name = util_alloc_string_copy("SLURM"); + driver->set_option = slurm_driver_set_option; + driver->get_option = slurm_driver_get_option; + driver->init_options = slurm_driver_init_option_list; + driver->free_driver = slurm_driver_free__; + driver->kill_job = slurm_driver_kill_job; + driver->free_job = slurm_driver_free_job; + driver->submit = slurm_driver_submit_job; + driver->get_status = slurm_driver_get_job_status; + driver->data = slurm_driver_alloc(); + break; + default: + util_abort("%s: unrecognized driver type:%d \n", __func__, type); + } + + queue_driver_set_generic_option__(driver, MAX_RUNNING, "0"); + return driver; +} + + +/*****************************************************************/ + +const void * queue_driver_get_option(queue_driver_type * driver, const char * option_key) { + if (queue_driver_has_generic_option__(driver, option_key)) { + return queue_driver_get_generic_option__(driver, option_key); + } else if (driver->get_option != NULL) + /* The actual low level set functions can not fail! */ + return driver->get_option(driver->data, option_key); + else { + util_abort("%s: driver:%s does not support run time reading of options\n", __func__, driver->name); + return NULL; + } + return NULL; +} + +/*****************************************************************/ + +void queue_driver_init_option_list(queue_driver_type * driver, stringlist_type * option_list) { + //Add options common for all driver types + stringlist_append_copy(option_list, MAX_RUNNING); + + //Add options for the specific driver type + if (driver->init_options) + driver->init_options(option_list); + else + util_abort("%s: driver:%s does not support run time reading of options\n", __func__, driver->name); + } + + +queue_driver_type * queue_driver_alloc_LSF(const char * queue_name, + const char * lsf_resource, + const char * lsf_server) { + queue_driver_type * driver = queue_driver_alloc(LSF_DRIVER); + + queue_driver_set_option(driver, LSF_QUEUE, queue_name); + queue_driver_set_option(driver, LSF_RESOURCE, lsf_resource); + queue_driver_set_option(driver, LSF_SERVER, lsf_server); + + return driver; +} + +queue_driver_type * queue_driver_alloc_TORQUE() { + queue_driver_type * driver = queue_driver_alloc(TORQUE_DRIVER); + return driver; +} + +queue_driver_type * queue_driver_alloc_RSH(const char * rsh_cmd, const hash_type * rsh_hostlist) { + queue_driver_type * driver = queue_driver_alloc(RSH_DRIVER); + + queue_driver_set_option(driver, RSH_HOSTLIST, rsh_hostlist); + queue_driver_set_option(driver, RSH_CMD, rsh_cmd); + + return driver; +} + +queue_driver_type * queue_driver_alloc_local() { + queue_driver_type * driver = queue_driver_alloc(LOCAL_DRIVER); + + /* No options set for the local driver. */ + + return driver; +} + +queue_driver_type * queue_driver_alloc_slurm() { + queue_driver_type * driver = queue_driver_alloc(SLURM_DRIVER); + return driver; +} + +/* These are the functions used by the job_queue layer. */ + +void * queue_driver_submit_job(queue_driver_type * driver, const char * run_cmd, int num_cpu, const char * run_path, const char * job_name, int argc, const char ** argv) { + return driver->submit(driver->data, run_cmd, num_cpu, run_path, job_name, argc, argv); +} + +void queue_driver_free_job(queue_driver_type * driver, void * job_data) { + driver->free_job(job_data); +} + +void queue_driver_blacklist_node(queue_driver_type * driver, void * job_data) { + if (driver->driver_type == LSF_DRIVER) + driver->blacklist_node(driver->data, job_data); +} + +void queue_driver_kill_job(queue_driver_type * driver, void * job_data) { + driver->kill_job(driver->data, job_data); +} + +job_status_type queue_driver_get_status(queue_driver_type * driver, void * job_data) { + job_status_type status = driver->get_status(driver->data, job_data); + return status; +} + +void queue_driver_free_driver(queue_driver_type * driver) { + driver->free_driver(driver->data); +} + +/*****************************************************************/ + +void queue_driver_free(queue_driver_type * driver) { + queue_driver_free_driver(driver); + free(driver->name); + free(driver->max_running_string); + free(driver); +} + +void queue_driver_free__(void * driver) { + queue_driver_type * queue_driver = queue_driver_safe_cast(driver); + queue_driver_free(queue_driver); +} diff --git a/libres/lib/job_queue/rsh_driver.cpp b/libres/lib/job_queue/rsh_driver.cpp new file mode 100644 index 00000000000..fe3092da6a8 --- /dev/null +++ b/libres/lib/job_queue/rsh_driver.cpp @@ -0,0 +1,477 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'rsh_driver.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 + + + + + + +struct rsh_job_struct { + UTIL_TYPE_ID_DECLARATION; + bool active; /* Means that it allocated - not really in use */ + job_status_type status; + pthread_t run_thread; + const char * host_name; /* Currently not set */ + char * run_path; +}; + + + +typedef struct { + char * host_name; + int max_running; /* How many can the host handle. */ + int running; /* How many are currently running on the host (goverened by this driver instance that is). */ + pthread_mutex_t host_mutex; +} rsh_host_type; + + + +#define RSH_DRIVER_TYPE_ID 44963256 +#define RSH_JOB_TYPE_ID 63256701 + + +struct rsh_driver_struct { + UTIL_TYPE_ID_DECLARATION; + pthread_mutex_t submit_lock; + pthread_attr_t thread_attr; + char * rsh_command; + int num_hosts; + int last_host_index; + rsh_host_type **host_list; + hash_type *__host_hash; /* Redundancy ... */ +}; + + + +/******************************************************************/ +static UTIL_SAFE_CAST_FUNCTION_CONST( rsh_driver , RSH_DRIVER_TYPE_ID ) +static UTIL_SAFE_CAST_FUNCTION( rsh_driver , RSH_DRIVER_TYPE_ID ) +static UTIL_SAFE_CAST_FUNCTION( rsh_job , RSH_JOB_TYPE_ID ) + + + +/** + If the host is for some reason not available, NULL should be + returned. Will also return NULL if some funny guy tries to allocate + with max_running <= 0. +*/ + +static rsh_host_type * rsh_host_alloc(const char * host_name , int max_running) { + if (max_running > 0) { + struct addrinfo * result; + if (getaddrinfo(host_name , NULL , NULL , &result) == 0) { + rsh_host_type * host = (rsh_host_type*)util_malloc(sizeof * host ); + + host->host_name = util_alloc_string_copy(host_name); + host->max_running = max_running; + host->running = 0; + pthread_mutex_init( &host->host_mutex , NULL ); + + freeaddrinfo( result ); + return host; + } else { + fprintf(stderr,"** Warning: could not locate server: %s \n",host_name); + return NULL; + } + } else + return NULL; +} + + + +static void rsh_host_free(rsh_host_type * rsh_host) { + free(rsh_host->host_name); + free(rsh_host); +} + + +static bool rsh_host_available(rsh_host_type * rsh_host) { + bool available; + + pthread_mutex_lock( &rsh_host->host_mutex ); + { + available = false; + if ((rsh_host->max_running - rsh_host->running) > 0) { // The host has free slots() + available = true; + rsh_host->running++; + } + } + pthread_mutex_unlock( &rsh_host->host_mutex ); + + return available; +} + + + + + +static void rsh_host_submit_job(rsh_host_type * rsh_host , rsh_job_type * job, const char * rsh_cmd , const char * submit_cmd , int num_cpu , int job_argc , const char ** job_argv) { + /* + Observe that this job has already been added to the running jobs + in the rsh_host_available function. + */ + int argc = job_argc + 2; + const char ** argv = (const char**)util_malloc( argc * sizeof * argv ); + + argv[0] = rsh_host->host_name; + argv[1] = submit_cmd; + { + int iarg; + for (iarg = 0; iarg < job_argc; iarg++) + argv[iarg + 2] = job_argv[iarg]; + } + + util_spawn_blocking(rsh_cmd, argc, argv, NULL, NULL); /* This call is blocking. */ + job->status = JOB_QUEUE_DONE; + + pthread_mutex_lock( &rsh_host->host_mutex ); + rsh_host->running--; + pthread_mutex_unlock( &rsh_host->host_mutex ); + free( argv ); +} + + +/* + static const char * rsh_host_get_hostname(const rsh_host_type * host) { return host->host_name; } +*/ + + + +static void * rsh_host_submit_job__(void * __arg_pack) { + arg_pack_type * arg_pack = arg_pack_safe_cast(__arg_pack); + char * rsh_cmd = (char *) arg_pack_iget_ptr(arg_pack , 0); + rsh_host_type * rsh_host = (rsh_host_type *)arg_pack_iget_ptr(arg_pack , 1); + char * submit_cmd = (char *) arg_pack_iget_ptr(arg_pack , 2); + int num_cpu = arg_pack_iget_int(arg_pack , 3); + int argc = arg_pack_iget_int(arg_pack , 4); + const char ** argv = (const char **) arg_pack_iget_ptr(arg_pack , 5); + rsh_job_type * job = (rsh_job_type*) arg_pack_iget_ptr(arg_pack , 6); + + rsh_host_submit_job(rsh_host , job , rsh_cmd , submit_cmd , num_cpu , argc , argv); + pthread_exit( NULL ); + arg_pack_free( arg_pack ); +} + + + + + +/*****************************************************************/ + + +/*****************************************************************/ + + + + +rsh_job_type * rsh_job_alloc(const char * run_path) { + rsh_job_type * job; + job = (rsh_job_type*)util_malloc(sizeof * job ); + job->active = false; + job->status = JOB_QUEUE_WAITING; + job->run_path = util_alloc_string_copy(run_path); + UTIL_TYPE_ID_INIT( job , RSH_JOB_TYPE_ID ); + return job; +} + + + +void rsh_job_free(rsh_job_type * job) { + free(job->run_path); + free(job); +} + + + + +job_status_type rsh_driver_get_job_status(void * __driver , void * __job) { + if (__job == NULL) + /* The job has not been registered at all ... */ + return JOB_QUEUE_NOT_ACTIVE; + else { + rsh_job_type * job = rsh_job_safe_cast( __job ); + { + if (job->active == false) { + util_abort("%s: internal error - should not query status on inactive jobs \n" , __func__); + return JOB_QUEUE_NOT_ACTIVE; /* Dummy to shut up compiler */ + } else + return job->status; + } + } +} + + + +void rsh_driver_free_job( void * __job ) { + rsh_job_type * job = rsh_job_safe_cast( __job ); + rsh_job_free(job); +} + + + +void rsh_driver_kill_job(void * __driver ,void * __job) { + rsh_job_type * job = rsh_job_safe_cast( __job ); + if (job->active) + pthread_cancel( job->run_thread ); + rsh_job_free( job ); +} + + + +void * rsh_driver_submit_job(void * __driver, + const char * submit_cmd , + int num_cpu , /* Ignored */ + const char * run_path , + const char * job_name , + int argc, + const char ** argv ) { + + rsh_driver_type * driver = rsh_driver_safe_cast( __driver ); + rsh_job_type * job = NULL; + { + /* + command is freed in the start_routine() function + */ + pthread_mutex_lock( &driver->submit_lock ); + { + rsh_host_type * host = NULL; + int ihost; + int host_index = 0; + + if (driver->num_hosts == 0) + util_abort("%s: fatal error - no hosts added to the rsh driver.\n",__func__); + + for (ihost = 0; ihost < driver->num_hosts; ihost++) { + host_index = (ihost + driver->last_host_index) % driver->num_hosts; + if (rsh_host_available(driver->host_list[host_index])) { + host = driver->host_list[host_index]; + break; + } + } + driver->last_host_index = (host_index + 1) % driver->num_hosts; + + if (host != NULL) { + /* A host is available */ + arg_pack_type * arg_pack = arg_pack_alloc(); /* The arg_pack is freed() in the rsh_host_submit_job__() function. + freeing it here is dangerous, because we might free it before the + thread-called function is finished with it. */ + + job = rsh_job_alloc(run_path); + + arg_pack_append_ptr(arg_pack , driver->rsh_command); + arg_pack_append_ptr(arg_pack , host); + arg_pack_append_ptr(arg_pack , (char *) submit_cmd); + arg_pack_append_int(arg_pack , num_cpu ); + arg_pack_append_int(arg_pack , argc ); + arg_pack_append_ptr(arg_pack , argv ); + arg_pack_append_ptr(arg_pack , job); + + { + int pthread_return_value = pthread_create( &job->run_thread , &driver->thread_attr , rsh_host_submit_job__ , arg_pack); + if (pthread_return_value != 0) + util_abort("%s failed to create thread ERROR:%d \n", __func__ , pthread_return_value); + } + job->status = JOB_QUEUE_RUNNING; + job->active = true; + } + } + pthread_mutex_unlock( &driver->submit_lock ); + } + return job; +} + + +void rsh_driver_clear_host_list( rsh_driver_type * driver ) { + int ihost; + for (ihost =0; ihost < driver->num_hosts; ihost++) + rsh_host_free(driver->host_list[ihost]); + free(driver->host_list); + + driver->num_hosts = 0; + driver->host_list = NULL; + driver->last_host_index = 0; +} + + +void rsh_driver_free(rsh_driver_type * driver) { + rsh_driver_clear_host_list( driver ); + pthread_attr_destroy ( &driver->thread_attr ); + free(driver->rsh_command ); + hash_free( driver->__host_hash ); + free(driver); + driver = NULL; +} + + +void rsh_driver_free__(void * __driver) { + rsh_driver_type * driver = rsh_driver_safe_cast( __driver ); + rsh_driver_free( driver ); +} + + +void rsh_driver_set_host_list( rsh_driver_type * rsh_driver , const hash_type * rsh_host_list) { + rsh_driver_clear_host_list( rsh_driver ); + if (rsh_host_list != NULL) { + hash_iter_type * hash_iter = hash_iter_alloc( rsh_host_list ); + while (!hash_iter_is_complete( hash_iter )) { + const char * host = hash_iter_get_next_key( hash_iter ); + int max_running = hash_get_int( rsh_host_list , host ); + rsh_driver_add_host(rsh_driver , host , max_running); + } + if (rsh_driver->num_hosts == 0) + util_abort("%s: failed to add any valid RSH hosts - aborting.\n",__func__); + } +} + + + + +/** + +*/ + +void * rsh_driver_alloc( ) { + rsh_driver_type * rsh_driver = (rsh_driver_type*)util_malloc( sizeof * rsh_driver ); + UTIL_TYPE_ID_INIT( rsh_driver , RSH_DRIVER_TYPE_ID ); + pthread_mutex_init( &rsh_driver->submit_lock , NULL ); + pthread_attr_init( &rsh_driver->thread_attr ); + pthread_attr_setdetachstate( &rsh_driver->thread_attr , PTHREAD_CREATE_DETACHED ); + + /** + To simplify the Python wrapper it is possible to pass in NULL as + rsh_host_list pointer, and then subsequently add hosts with + rsh_driver_add_host(). + */ + rsh_driver->num_hosts = 0; + rsh_driver->host_list = NULL; + rsh_driver->last_host_index = 0; + rsh_driver->rsh_command = NULL; + rsh_driver->__host_hash = hash_alloc(); + return rsh_driver; +} + + + +void rsh_driver_add_host(rsh_driver_type * rsh_driver , const char * hostname , int host_max_running) { + rsh_host_type * new_host = rsh_host_alloc(hostname , host_max_running); /* Could in principle update an existing node if the host name is old. */ + if (new_host != NULL) { + rsh_driver->num_hosts++; + rsh_driver->host_list = (rsh_host_type**)util_realloc(rsh_driver->host_list , rsh_driver->num_hosts * sizeof * rsh_driver->host_list ); + rsh_driver->host_list[(rsh_driver->num_hosts - 1)] = new_host; + } +} + + +/** + Hostname should be a string as host:max_running, the ":max_running" + part is optional, and will default to 1. +*/ + +void rsh_driver_add_host_from_string(rsh_driver_type * rsh_driver , const char * hostname) { + int host_max_running; + char ** tmp; + char * host; + int tokens; + + util_split_string( hostname , ":" , &tokens , &tmp); + if (tokens > 1) { + if (!util_sscanf_int( tmp[tokens - 1] , &host_max_running)) + util_abort("%s: failed to parse out integer from: %s \n",__func__ , hostname); + host = util_alloc_joined_string((const char **) tmp , tokens - 1 , ":"); + } else + host = util_alloc_string_copy( tmp[0] ); + rsh_driver_add_host( rsh_driver , host , host_max_running ); + + util_free_stringlist( tmp , tokens ); + free( host ); +} + + + + +bool rsh_driver_set_option( void * __driver , const char * option_key , const void * value_ ) { + const char * value = (const char*) value_; + rsh_driver_type * driver = rsh_driver_safe_cast( __driver ); + bool has_option = true; + { + if (strcmp(RSH_HOST , option_key) == 0) /* Add one host - value should be hostname:max */ + rsh_driver_add_host_from_string( driver , value ); + else if (strcmp(RSH_HOSTLIST , option_key) == 0) { /* Set full host list - value should be hash of integers. */ + if (value != NULL) { + const hash_type * hash_value = hash_safe_cast_const( value ); + rsh_driver_set_host_list( driver , hash_value ); + } + } else if (strcmp( RSH_CLEAR_HOSTLIST , option_key) == 0) + /* Value is not considered - this is an action, and not a _set operation. */ + rsh_driver_set_host_list( driver , NULL ); + else if (strcmp( RSH_CMD , option_key) == 0) + driver->rsh_command = util_realloc_string_copy( driver->rsh_command , value ); + else + has_option = false; + } + return has_option; +} + + +const void * rsh_driver_get_option( const void * __driver , const char * option_key ) { + const rsh_driver_type * driver = rsh_driver_safe_cast_const( __driver ); + { + if (strcmp( RSH_CMD , option_key ) == 0) + return driver->rsh_command; + else if (strcmp( RSH_HOSTLIST , option_key) == 0) { + int ihost; + hash_clear( driver->__host_hash ); + for (ihost = 0; ihost < driver->num_hosts; ihost++) { + rsh_host_type * host = driver->host_list[ ihost ]; + hash_insert_int( driver->__host_hash , host->host_name , host->max_running); + } + return driver->__host_hash; + } else { + util_abort("%s: get not implemented fro option_id:%s for rsh \n",__func__ , option_key ); + return NULL; + } + } +} + + +void rsh_driver_init_option_list(stringlist_type * option_list) { + stringlist_append_copy(option_list, RSH_HOST); + stringlist_append_copy(option_list, RSH_HOSTLIST); + stringlist_append_copy(option_list, RSH_CMD); + stringlist_append_copy(option_list, RSH_CLEAR_HOSTLIST); +} + +#undef RSH_JOB_ID + +/*****************************************************************/ + diff --git a/libres/lib/job_queue/slurm_driver.cpp b/libres/lib/job_queue/slurm_driver.cpp new file mode 100644 index 00000000000..09b376f42df --- /dev/null +++ b/libres/lib/job_queue/slurm_driver.cpp @@ -0,0 +1,616 @@ +/* + Copyright (C) 2020 Equinor ASA, Norway. + + The file 'slurm_driver.cpp' 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 +#include + +#include +#include + + +struct SlurmJob { + SlurmJob(int job_id) : + job_id(job_id), + string_id(std::to_string(job_id)) + { + } + + int job_id; + std::string string_id; +}; + + + +class SlurmStatus { +public: + + void update(int job_id, job_status_type status) { + pthread_rwlock_wrlock(&this->lock); + this->jobs[job_id] = status; + pthread_rwlock_unlock(&this->lock); + } + + + void new_job(int job_id) { + this->update(job_id, JOB_QUEUE_PENDING); + } + + static bool active_status(job_status_type status) { + if (status == JOB_QUEUE_RUNNING) + return true; + + if (status == JOB_QUEUE_PENDING) + return true; + + return false; + } + + + /* + This function is used when the status of the jobs is updated with squeue. The + semantics is as follows: + + 1. The squeue command is run and the squeue_jobs variable is filled up with + a job_id -> status mapping. + + 2. We run through all the jobs registered in the SlurmStatus object, jobs + which are registered in an active state (i.e. PENDING or RUNNING) which + are *not* included in the squeue_jobs argument must have completed and + fallen out of squeue update, these jobs are assembled in the active_jobs + variable. + + 3. The return value is a list of jobs which were previously registered as + active, but are not fallen out. Calling scope must update their status + with calls to scontrol. + */ + + std::vector squeue_update(const std::unordered_map& squeue_jobs) { + std::vector active_jobs; + + pthread_rwlock_wrlock(&this->lock); + for (auto& job_pair : this->jobs) { + auto job_id = job_pair.first; + auto job_status = job_pair.second; + + auto squeue_pair = squeue_jobs.find( job_id ); + if (squeue_pair == squeue_jobs.end()) { + if (this->active_status(job_status)) + active_jobs.push_back(job_id); + } else + this->jobs[job_id] = squeue_pair->second; + } + pthread_rwlock_unlock(&this->lock); + + return active_jobs; + } + + + job_status_type get(int job_id) const { + + pthread_rwlock_rdlock(&this->lock); + auto status = this->jobs.at(job_id); + pthread_rwlock_unlock(&this->lock); + + return status; + } + +private: + std::unordered_map jobs; + mutable pthread_rwlock_t lock = PTHREAD_RWLOCK_INITIALIZER; +}; + + +#define SLURM_DRIVER_TYPE_ID 70555081 +#define DEFAULT_SBATCH_CMD "sbatch" +#define DEFAULT_SCANCEL_CMD "scancel" +#define DEFAULT_SQUEUE_CMD "squeue" +#define DEFAULT_SCONTROL_CMD "scontrol" +#define DEFAULT_SQUEUE_TIMEOUT 10 + +#define SLURM_PENDING_STATUS "PENDING" +#define SLURM_COMPLETED_STATUS "COMPLETED" +#define SLURM_RUNNING_STATUS "RUNNING" +#define SLURM_FAILED_STATUS "FAILED" +#define SLURM_CANCELED_STATUS "CANCELLED" +#define SLURM_COMPLETING_STATUS "COMPLETING" +#define SLURM_CONFIGURING_STATUS "CONFIGURING" + + +struct slurm_driver_struct { + UTIL_TYPE_ID_DECLARATION; + + std::string sbatch_cmd; + std::string scancel_cmd; + std::string squeue_cmd; + std::string scontrol_cmd; + std::string partition; + std::string memory; + std::string memory_per_cpu; + std::string username; + std::pair max_runtime; + std::pair,std::string> exclude; + std::pair,std::string> include; + mutable SlurmStatus status; + mutable std::time_t status_timestamp; + double status_timeout = DEFAULT_SQUEUE_TIMEOUT; + std::string status_timeout_string; +}; + + +static std::string load_file(const char * fname) { + char * buffer = util_fread_alloc_file_content(fname, nullptr); + std::string s = buffer; + free(buffer); + return s; +} + + +static std::string load_stdout(const char * cmd, int argc, const char ** argv) { + std::string fname = std::string(cmd) + "-stdout"; + char * stdout = (char*) util_alloc_tmp_file("/tmp", fname.c_str(), true); + + auto exit_status = util_spawn_blocking(cmd, argc, argv, stdout, nullptr); + auto file_content = load_file(stdout); + + if (exit_status != 0) + res_log_fwarning("Calling shell command %s ... returned non zero exitcode: %d", cmd, exit_status); + + util_unlink_existing(stdout); + free(stdout); + return file_content; +} + + +static std::string load_stdout(const char * cmd, const std::vector& args) { + const char ** argv = static_cast(util_calloc( args.size(), sizeof * argv )); + for (std::size_t i=0; i < args.size(); i++) + argv[i] = args[i].c_str(); + + auto file_content = load_stdout(cmd, args.size(), argv); + free(argv); + return file_content; +} + +static std::vector split_string(const std::string& string_value) { + std::vector strings; + std::size_t offset = string_value.find_first_not_of(", "); + while(offset != std::string::npos) { + auto item_end = string_value.find_first_of(", ", offset); + strings.push_back( string_value.substr(offset, item_end - offset)); + offset = string_value.find_first_not_of(", ", item_end); + } + return strings; +} + +template +static std::string join_string(const C& strings) { + const std::string sep = ","; + std::string full_string; + bool first = true; + for (const auto& s : strings) { + if (!first) + full_string += sep; + full_string += s; + first = false; + } + return full_string; +} + + +UTIL_SAFE_CAST_FUNCTION( slurm_driver , SLURM_DRIVER_TYPE_ID) +static UTIL_SAFE_CAST_FUNCTION_CONST( slurm_driver , SLURM_DRIVER_TYPE_ID) + +void * slurm_driver_alloc() { + slurm_driver_type * driver = new slurm_driver_type(); + UTIL_TYPE_ID_INIT(driver, SLURM_DRIVER_TYPE_ID); + driver->sbatch_cmd = DEFAULT_SBATCH_CMD; + driver->scancel_cmd = DEFAULT_SCANCEL_CMD; + driver->squeue_cmd = DEFAULT_SQUEUE_CMD; + driver->scontrol_cmd = DEFAULT_SCONTROL_CMD; + driver->status_timeout_string = std::to_string(driver->status_timeout); + + auto pwname = getpwuid( geteuid() ); + driver->username = pwname->pw_name; + return driver; +} + +void slurm_driver_free(slurm_driver_type * driver) { + delete driver; +} + +void slurm_driver_free__(void * __driver ) { + slurm_driver_type * driver = slurm_driver_safe_cast( __driver ); + slurm_driver_free( driver ); +} + + +const void * slurm_driver_get_option( const void * __driver, const char * option_key) { + const slurm_driver_type * driver = slurm_driver_safe_cast_const( __driver ); + if (strcmp(option_key, SLURM_SBATCH_OPTION) == 0) + return driver->sbatch_cmd.c_str(); + + if (strcmp(option_key, SLURM_SCANCEL_OPTION) == 0) + return driver->scancel_cmd.c_str(); + + if (strcmp(option_key, SLURM_SCONTROL_OPTION) == 0) + return driver->scontrol_cmd.c_str(); + + if (strcmp(option_key, SLURM_SQUEUE_OPTION) == 0) + return driver->squeue_cmd.c_str(); + + if (strcmp(option_key, SLURM_PARTITION_OPTION) == 0) + return driver->partition.c_str(); + + if (strcmp(option_key, SLURM_SQUEUE_TIMEOUT_OPTION) == 0) + return driver->status_timeout_string.c_str(); + + if (strcmp(option_key, SLURM_MEMORY_OPTION) == 0) + return driver->memory.c_str(); + + if (strcmp(option_key, SLURM_MEMORY_PER_CPU_OPTION) == 0) + return driver->memory_per_cpu.c_str(); + + if (strcmp(option_key, SLURM_MAX_RUNTIME_OPTION) == 0) + return driver->max_runtime.first.c_str(); + + if (strcmp(option_key, SLURM_EXCLUDE_HOST_OPTION) == 0) + return driver->exclude.second.c_str(); + + if (strcmp(option_key, SLURM_INCLUDE_HOST_OPTION) == 0) + return driver->include.second.c_str(); + + return nullptr; +} + + +bool slurm_driver_set_option( void * __driver, const char * option_key, const void * value) { + slurm_driver_type * driver = slurm_driver_safe_cast( __driver ); + if (strcmp(option_key, SLURM_SBATCH_OPTION) == 0) { + driver->sbatch_cmd = static_cast(value); + return true; + } + + if (strcmp(option_key, SLURM_SCANCEL_OPTION) == 0) { + driver->scancel_cmd = static_cast(value); + return true; + } + + if (strcmp(option_key, SLURM_SQUEUE_OPTION) == 0) { + driver->squeue_cmd = static_cast(value); + return true; + } + + if (strcmp(option_key, SLURM_SCONTROL_OPTION) == 0) { + driver->scontrol_cmd = static_cast(value); + return true; + } + + if (strcmp(option_key, SLURM_PARTITION_OPTION) == 0) { + driver->partition = static_cast(value); + return true; + } + + if (strcmp(option_key, SLURM_MEMORY_OPTION) == 0) { + driver->memory= static_cast(value); + return true; + } + + if (strcmp(option_key, SLURM_MEMORY_PER_CPU_OPTION) == 0) { + driver->memory_per_cpu = static_cast(value); + return true; + } + + if (strcmp(option_key, SLURM_EXCLUDE_HOST_OPTION) == 0) { + std::string string_value = static_cast(value); + auto host_list = split_string(string_value); + driver->exclude.first.insert( host_list.begin(), host_list.end() ); + driver->exclude.second = join_string( driver->exclude.first ); + return true; + } + + if (strcmp(option_key, SLURM_INCLUDE_HOST_OPTION) == 0) { + std::string string_value = static_cast(value); + auto host_list = split_string(string_value); + driver->include.first.insert( host_list.begin(), host_list.end() ); + driver->include.second = join_string( driver->include.first ); + return true; + } + + if (strcmp(option_key, SLURM_SQUEUE_TIMEOUT_OPTION) == 0) { + const char * string_value = static_cast(value); + double timeout; + if (util_sscanf_double(string_value, &timeout)) { + driver->status_timeout = timeout; + driver->status_timeout_string = string_value; + return true; + } else + return false; + } + + /* + The --time option in slurm which is used to set the maximum runtime of a job + is in minutes, whereas the libres option system uses seconds. This is to + ensure overall consistency in libres for timeouts. + */ + if (strcmp(option_key, SLURM_MAX_RUNTIME_OPTION) == 0) { + const char * string_value = static_cast(value); + int max_runtime_seconds; + if (util_sscanf_int(string_value, &max_runtime_seconds)) { + driver->max_runtime = std::make_pair(std::string(string_value), std::ceil(1.0 * max_runtime_seconds / 60)); + return true; + } else + return false; + } + + return false; +} + + +void slurm_driver_init_option_list(stringlist_type * option_list) { + stringlist_append_copy(option_list, SLURM_PARTITION_OPTION); + stringlist_append_copy(option_list, SLURM_SBATCH_OPTION); + stringlist_append_copy(option_list, SLURM_SCONTROL_OPTION); + stringlist_append_copy(option_list, SLURM_SQUEUE_OPTION); + stringlist_append_copy(option_list, SLURM_SCANCEL_OPTION); + stringlist_append_copy(option_list, SLURM_MAX_RUNTIME_OPTION); + stringlist_append_copy(option_list, SLURM_SQUEUE_TIMEOUT_OPTION); + stringlist_append_copy(option_list, SLURM_MEMORY_OPTION); + stringlist_append_copy(option_list, SLURM_MEMORY_PER_CPU_OPTION); + stringlist_append_copy(option_list, SLURM_INCLUDE_HOST_OPTION); + stringlist_append_copy(option_list, SLURM_EXCLUDE_HOST_OPTION); +} + +/* + Slurm allows very fine control over how a parallel job should be distributed + over available nodes and CPUs, the current approach is an absolutely simplest + way - where we just say how many processors we will need in total with the + --ntasks=$num_cpu setting. +*/ +static std::string make_submit_script(const slurm_driver_type * driver, const char * cmd, const char * job_name, int num_cpu, int argc, const char ** argv) { + char * submit = (char*) util_alloc_tmp_file("/tmp" , "slurm-submit" , true); + + FILE * submit_stream = util_fopen(submit, "w"); + fprintf(submit_stream, "#!/bin/sh\n"); + fprintf(submit_stream, "#SBATCH --ntasks=%d\n", num_cpu); + fprintf(submit_stream, "#SBATCH --output=%s.stdout\n", job_name); + fprintf(submit_stream, "#SBATCH --error=%s.stderr\n", job_name); + if (driver->memory.size() > 0) + fprintf(submit_stream, "#SBATCH --mem=%s\n", driver->memory.c_str()); + if (driver->memory_per_cpu.size() > 0) + fprintf(submit_stream, "#SBATCH --mem-per-cpu=%s\n", driver->memory_per_cpu.c_str()); + if (driver->max_runtime.second != 0) + fprintf(submit_stream, "#SBATCH --time=%d\n", driver->max_runtime.second); + if (!driver->exclude.first.empty()) + fprintf(submit_stream, "#SBATCH --exclude=%s\n", driver->exclude.second.c_str()); + if (!driver->include.first.empty()) + fprintf(submit_stream, "#SBATCH --nodelist=%s\n", driver->include.second.c_str()); + + + fprintf(submit_stream, "%s", cmd); // Without srun? + for (int iarg=0; iarg < argc; iarg++) + fprintf(submit_stream, " %s", argv[iarg]); + fprintf(submit_stream, "\n"); + + fclose(submit_stream); + chmod(submit, S_IRWXU + S_IRGRP + S_IROTH); + + std::string submit_script = submit; + free( submit ); + return submit_script; +} + + +/* + The slurm jobs are submitted by first creating a submit script, which is a + small shell which contains the command to run along with possible slurm + options, and then this script is submitted with the 'sbatch' command. +*/ + +void * slurm_driver_submit_job( void * __driver, const char * cmd, int num_cpu, const char * run_path, const char * job_name, int argc, const char ** argv) { + slurm_driver_type * driver = slurm_driver_safe_cast( __driver ); + + auto submit_script = make_submit_script( driver, cmd, job_name, num_cpu, argc, argv); + std::vector sbatch_argv = {"--workdir=" + std::string(run_path), "--job-name=" + std::string(job_name), "--parsable"}; + if (!driver->partition.empty()) + sbatch_argv.push_back( "--partition=" + driver->partition ); + sbatch_argv.push_back( submit_script ); + + + auto file_content = load_stdout(driver->sbatch_cmd.c_str(), sbatch_argv); + util_unlink_existing( submit_script.c_str() ); + + int job_id; + try { + job_id = std::stoi(file_content); + } catch (std::invalid_argument& exc) { + return nullptr; + } + + driver->status.new_job(job_id); + return new SlurmJob(job_id); +} + + +static job_status_type slurm_driver_translate_status(const std::string& status_string, const std::string& string_id) { + if (status_string == SLURM_PENDING_STATUS) + return JOB_QUEUE_PENDING; + + if (status_string == SLURM_COMPLETED_STATUS) + return JOB_QUEUE_DONE; + + if (status_string == SLURM_COMPLETING_STATUS) + return JOB_QUEUE_RUNNING; + + if (status_string == SLURM_RUNNING_STATUS) + return JOB_QUEUE_RUNNING; + + if (status_string == SLURM_CONFIGURING_STATUS) + return JOB_QUEUE_RUNNING; + + if (status_string == SLURM_FAILED_STATUS) + return JOB_QUEUE_EXIT; + + if (status_string == SLURM_CANCELED_STATUS) + return JOB_QUEUE_IS_KILLED; + + res_log_fwarning("The job status: \'%s\' for job:%s is not recognized", status_string.c_str(), string_id.c_str()); + return JOB_QUEUE_UNKNOWN; +} + + + +static std::unordered_map load_scontrol(const slurm_driver_type * driver, const std::string& string_id) { + auto file_content = load_stdout(driver->scontrol_cmd.c_str(), {"show", "jobid", string_id}); + + std::unordered_map options; + std::size_t offset = 0; + while(true) { + auto new_offset = file_content.find_first_of("\n ", offset); + if (new_offset == std::string::npos) + break; + + std::string key_value = file_content.substr(offset, new_offset - offset); + auto split_pos = key_value.find('='); + if (split_pos != std::string::npos) { + std::string key = key_value.substr(0, split_pos); + std::string value = key_value.substr(split_pos + 1); + + options.insert({key,value}); + } + offset = file_content.find_first_not_of("\n ", new_offset); + } + return options; +} + + +static job_status_type slurm_driver_get_job_status_scontrol(const slurm_driver_type * driver, const std::string& string_id) { + auto values = load_scontrol(driver, string_id); + const auto status_iter = values.find("JobState"); + + /* + When a job has finished running it quite quickly - the order of minutes - + falls out of the slurm database, and the scontrol command will not give any + output. In this situation we guess that the job has completed succesfully + and return status JOB_QUEUE_DONE. If the job has actually not succeded this + should be picked up the libres post run checking. + */ + if (status_iter == values.end()) { + res_log_fwarning("The command \'scontrol show jobid %s\' gave no output for job:%s - assuming it is COMPLETED", string_id.c_str(), string_id.c_str()); + return JOB_QUEUE_DONE; + } + + const auto& status_string = status_iter->second; + auto status = slurm_driver_translate_status(status_string, string_id); + + if (status == JOB_QUEUE_UNKNOWN) { + res_log_fwarning("The job status: \'%s\' for job:%s is not recognized - assuming it is RUNNING", status_string.c_str(), string_id.c_str()); + status = JOB_QUEUE_RUNNING; + } + + return status; +} + +static job_status_type slurm_driver_get_job_status_scontrol(const slurm_driver_type * driver, int job_id) { + return slurm_driver_get_job_status_scontrol(driver, std::to_string(job_id)); +} + +static void slurm_driver_update_status_cache(const slurm_driver_type * driver) { + driver->status_timestamp = time( nullptr ); + const std::string space = " \n"; + auto squeue_output = load_stdout(driver->squeue_cmd.c_str(), {"-h", "--user=" + driver->username, "--format=%i %T"}); + auto offset = squeue_output.find_first_not_of(space); + + std::unordered_map squeue_jobs; + while (offset != std::string::npos) { + auto id_end = squeue_output.find_first_of(space, offset); + auto job_string = squeue_output.substr( offset, id_end - offset); + int job_id = std::stoi(squeue_output.substr( offset, id_end - offset)); + + auto status_start = squeue_output.find_first_not_of(space, id_end + 1); + auto status_end = squeue_output.find_first_of(space, status_start); + auto status = slurm_driver_translate_status(squeue_output.substr( status_start, status_end - status_start ), std::to_string(job_id)); + + squeue_jobs.insert( {job_id, status} ); + offset = squeue_output.find_first_not_of(space, status_end); + } + + const auto& active_jobs = driver->status.squeue_update(squeue_jobs); + for (const auto& job_id : active_jobs) { + auto status = slurm_driver_get_job_status_scontrol(driver, job_id); + driver->status.update(job_id, status); + } +} + +/* + Getting the status of jobs involves two different executables - 'squeue' and + 'scontrol'. While a job is pending in the queue and when it is actually + running the squeue command will give the status, but as soon as the job has + finished running the status is no longer reported by the squeue command. This + is in contrast to the 'bjobs' command used in LSF, which will report EXIT and + DONE status also after the job has finished running. + + Because of fall out of the squeue status we must keep track of which jobs are + running, and then query with the scontrol command for the jobs which are not + reported by squeue. Unfortunately also the scontrol looses jobs after a couple + of minutes, when this happens we have hopefully recorded the eventual status + of the job. +*/ + +job_status_type slurm_driver_get_job_status(void * __driver , void * __job) { + slurm_driver_type * driver = slurm_driver_safe_cast( __driver ); + const auto * job = static_cast(__job); + auto update_cache = difftime(time(nullptr), driver->status_timestamp) > driver->status_timeout; + if (update_cache) + slurm_driver_update_status_cache(driver); + + return driver->status.get( job->job_id ); +} + + +void slurm_driver_kill_job(void * __driver , void * __job ) { + slurm_driver_type * driver = slurm_driver_safe_cast( __driver ); + const auto * job = static_cast(__job); + const char ** argv = static_cast(util_calloc( 1, sizeof * argv )); + + argv[0] = job->string_id.c_str(); + util_spawn_blocking(driver->scancel_cmd.c_str(), 1, argv, nullptr, nullptr); + free(argv); +} + +void slurm_driver_free_job(void * __job) { + SlurmJob * job = static_cast(__job); + delete job; +} diff --git a/libres/lib/job_queue/tests/create_file.cpp b/libres/lib/job_queue/tests/create_file.cpp new file mode 100644 index 00000000000..bb035504eb1 --- /dev/null +++ b/libres/lib/job_queue/tests/create_file.cpp @@ -0,0 +1,28 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'create_file.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 + + +int main( int argc , char ** argv) { + char * filename = argv[1]; + int value = atoi( argv[2] ); + FILE * stream = fopen( filename , "w"); + fprintf(stream , "%d\n", value ); + fclose( stream ); +} diff --git a/libres/lib/job_queue/tests/data/externalFail b/libres/lib/job_queue/tests/data/externalFail new file mode 100644 index 00000000000..0f53d812c3a --- /dev/null +++ b/libres/lib/job_queue/tests/data/externalFail @@ -0,0 +1,5 @@ +INTERNAL False +MIN_ARG 1 +MAX_ARG 2 +ARG_TYPE 0 STRING +ARG_TYPE 1 INT diff --git a/libres/lib/job_queue/tests/data/externalOK b/libres/lib/job_queue/tests/data/externalOK new file mode 100644 index 00000000000..67dec0bef25 --- /dev/null +++ b/libres/lib/job_queue/tests/data/externalOK @@ -0,0 +1,6 @@ +INTERNAL False +EXECUTABLE /usr/bin/python +MIN_ARG 1 +MAX_ARG 2 +ARG_TYPE 0 STRING +ARG_TYPE 1 INT diff --git a/libres/lib/job_queue/tests/data/external_job b/libres/lib/job_queue/tests/data/external_job new file mode 100644 index 00000000000..d809e47bb7c --- /dev/null +++ b/libres/lib/job_queue/tests/data/external_job @@ -0,0 +1,5 @@ +EXECUTABLE /private/joaho/ERT/git/ert/build/bin/create_file +ARG_TYPE 0 STRING +ARG_TYPE 1 INT +MIN_ARG 2 +MAX_ARG 2 diff --git a/libres/lib/job_queue/tests/data/internalFail b/libres/lib/job_queue/tests/data/internalFail new file mode 100644 index 00000000000..40686fc1d5d --- /dev/null +++ b/libres/lib/job_queue/tests/data/internalFail @@ -0,0 +1,6 @@ +INTERNAL True +MODULE NULL +MIN_ARG 1 +MAX_ARG 2 +ARG_TYPE 0 STRING +ARG_TYPE 1 INT diff --git a/libres/lib/job_queue/tests/data/internalOK b/libres/lib/job_queue/tests/data/internalOK new file mode 100644 index 00000000000..6252998faaf --- /dev/null +++ b/libres/lib/job_queue/tests/data/internalOK @@ -0,0 +1,7 @@ +INTERNAL True +MODULE NULL +FUNCTION workflow_job_alloc +MIN_ARG 1 +MAX_ARG 2 +ARG_TYPE 0 STRING +ARG_TYPE 1 INT diff --git a/libres/lib/job_queue/tests/data/internal_job b/libres/lib/job_queue/tests/data/internal_job new file mode 100644 index 00000000000..2c86544a442 --- /dev/null +++ b/libres/lib/job_queue/tests/data/internal_job @@ -0,0 +1,5 @@ +INTERNAL True +FUNCTION read_file +ARG_TYPE 0 STRING +MIN_ARG 1 +MAX_ARG 1 diff --git a/libres/lib/job_queue/tests/data/jobs/util/COPY_FILE b/libres/lib/job_queue/tests/data/jobs/util/COPY_FILE new file mode 100755 index 00000000000..bf48b50e9f8 --- /dev/null +++ b/libres/lib/job_queue/tests/data/jobs/util/COPY_FILE @@ -0,0 +1,2 @@ +EXECUTABLE /bin/cp +ARGLIST diff --git a/libres/lib/job_queue/tests/data/jobs/util/REPLACE b/libres/lib/job_queue/tests/data/jobs/util/REPLACE new file mode 100755 index 00000000000..79e5f19b014 --- /dev/null +++ b/libres/lib/job_queue/tests/data/jobs/util/REPLACE @@ -0,0 +1,6 @@ +STDOUT replace.stdout +STDERR replace.stderr + +EXECUTABLE /project/res/x86_64_RH_4/bin/replace.x +ARGLIST + diff --git a/libres/lib/job_queue/tests/data/jobs/util/STORE_FILE b/libres/lib/job_queue/tests/data/jobs/util/STORE_FILE new file mode 100755 index 00000000000..94ce1b8c51d --- /dev/null +++ b/libres/lib/job_queue/tests/data/jobs/util/STORE_FILE @@ -0,0 +1,5 @@ +STDOUT store_file.stdout +STDERR store_file.stderr + +EXECUTABLE ../../../Scripts/store_file.py +ARGLIST diff --git a/libres/lib/job_queue/tests/data/jobs/util/SYMLINK b/libres/lib/job_queue/tests/data/jobs/util/SYMLINK new file mode 100755 index 00000000000..72ebb65a7c4 --- /dev/null +++ b/libres/lib/job_queue/tests/data/jobs/util/SYMLINK @@ -0,0 +1,6 @@ +STDERR SYMLINK.stderr +STDOUT SYMLINK.stdout + +EXECUTABLE /bin/ln +ARGLIST -sf + diff --git a/libres/lib/job_queue/tests/data/qsub_emulators/qdel b/libres/lib/job_queue/tests/data/qsub_emulators/qdel new file mode 100755 index 00000000000..f7201e9c4c3 --- /dev/null +++ b/libres/lib/job_queue/tests/data/qsub_emulators/qdel @@ -0,0 +1,5 @@ +#!/bin/sh +echo "#!/bin/sh" > qstat +echo "echo \"Job id Name User Time Use S Queue\"" >> qstat +echo "echo \"------------------------- ---------------- --------------- -------- - -----\"" >> qstat +echo "echo \"1612427.st-lcmm ...130getupdates fama 00:00:01 E normal\"" >> qstat diff --git a/libres/lib/job_queue/tests/data/qsub_emulators/qstat b/libres/lib/job_queue/tests/data/qsub_emulators/qstat new file mode 100755 index 00000000000..0eda52fe306 --- /dev/null +++ b/libres/lib/job_queue/tests/data/qsub_emulators/qstat @@ -0,0 +1 @@ +Content will be piped into this file diff --git a/libres/lib/job_queue/tests/data/qsub_emulators/qsub b/libres/lib/job_queue/tests/data/qsub_emulators/qsub new file mode 100755 index 00000000000..48440a58142 --- /dev/null +++ b/libres/lib/job_queue/tests/data/qsub_emulators/qsub @@ -0,0 +1,6 @@ +#!/bin/sh +echo 1612427.greier.og.greier +echo "#!/bin/sh" > qstat +echo "echo \"Job id Name User Time Use S Queue\"" >> qstat +echo "echo \"------------------------- ---------------- --------------- -------- - -----\"" >> qstat +echo "echo \"1612427.st-lcmm ...130getupdates fama 00:00:01 R normal\"" >> qstat diff --git a/libres/lib/job_queue/tests/ext_job_test.cpp b/libres/lib/job_queue/tests/ext_job_test.cpp new file mode 100644 index 00000000000..b41ae043e67 --- /dev/null +++ b/libres/lib/job_queue/tests/ext_job_test.cpp @@ -0,0 +1,104 @@ +/* + Copyright (C) 2018 Equinor ASA, Norway. + + The file 'ext_job_test.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 + + +void test_angular() { + subst_list_type * subst_list = subst_list_alloc(NULL); + { + FILE * stream = util_fopen("ANGULAR", "w"); + fprintf(stream,"EXECUTABLE script\n"); + fprintf(stream, "EXECUTABLE script\n"); + fprintf(stream, "ENV VAR0 \n"); + fprintf(stream, "EXEC_ENV NOT_SET\n"); + fprintf(stream, "EXEC_ENV VAR1 \n"); + fprintf(stream, "EXEC_ENV VAR2 VALUE\n"); + fclose(stream); + } + { + ext_job_type * ext_job = ext_job_fscanf_alloc("ANGULAR", NULL, false, "ANGULAR", false); + { + FILE * stream = util_fopen("angular.json", "w"); + ext_job_json_fprintf(ext_job, 0, stream, subst_list); + fclose(stream); + } + cJSON *json; + { + int buffer_size; + char * buffer = util_fread_alloc_file_content("angular.json", &buffer_size); + json = cJSON_Parse(buffer); + { + cJSON * env = cJSON_GetObjectItem(json, "environment"); + test_assert_true( env->type == cJSON_NULL ); + } + + { + cJSON * exec_env = cJSON_GetObjectItem(json, "exec_env"); + test_assert_true( exec_env->type == cJSON_Object ); + test_assert_NULL( cJSON_GetObjectItem( exec_env, "VAR1")); + test_assert_not_NULL( cJSON_GetObjectItem( exec_env, "VAR2")); + { + cJSON * value = cJSON_GetObjectItem(exec_env, "VAR2"); + test_assert_string_equal("VALUE", value->valuestring); + } + { + cJSON * not_set = cJSON_GetObjectItem(exec_env, "NOT_SET"); + test_assert_true( not_set->type == cJSON_NULL); + } + } + + { + cJSON * exec_env = cJSON_GetObjectItem(json, "exec_env"); + test_assert_true( exec_env->type == cJSON_Object ); + test_assert_NULL( cJSON_GetObjectItem( exec_env, "VAR1")); + test_assert_not_NULL( cJSON_GetObjectItem( exec_env, "VAR2")); + { + cJSON * value = cJSON_GetObjectItem(exec_env, "VAR2"); + test_assert_string_equal("VALUE", value->valuestring); + } + } + + free(buffer); + } + cJSON_Delete(json); + ext_job_free(ext_job); + } + subst_list_free(subst_list); +} + + + +int main( int argc , char ** argv) { + ecl::util::TestArea ta("joblist"); + { + FILE * stream = util_fopen("script", "w"); + fprintf(stream, "Dummy script"); + fclose(stream); + chmod("script", 0777); + } + test_angular(); +} diff --git a/libres/lib/job_queue/tests/ext_joblist_test.cpp b/libres/lib/job_queue/tests/ext_joblist_test.cpp new file mode 100644 index 00000000000..ec00d66e6a5 --- /dev/null +++ b/libres/lib/job_queue/tests/ext_joblist_test.cpp @@ -0,0 +1,34 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'ext_joblist_test.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 + +void load_job_directory(ext_joblist_type * joblist , const char * path, const char * license_root_path) { + bool user_mode = false; + ext_joblist_add_jobs_in_directory(joblist , path, license_root_path, user_mode, true ); + test_assert_true( ext_joblist_has_job(joblist, "SYMLINK")); +} + +int main( int argc , char ** argv) { + int status = 0; + ext_joblist_type * joblist = ext_joblist_alloc(); + load_job_directory(joblist , argv[1], argv[2] ); + ext_joblist_free(joblist); + exit( status ); +} diff --git a/libres/lib/job_queue/tests/job_list_test.cpp b/libres/lib/job_queue/tests/job_list_test.cpp new file mode 100644 index 00000000000..268031ed576 --- /dev/null +++ b/libres/lib/job_queue/tests/job_list_test.cpp @@ -0,0 +1,77 @@ +/* + Copyright (C) 2015 Equinor ASA, Norway. + + The file 'job_node_test.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_create() { + job_list_type * list = job_list_alloc(); + test_assert_true( job_list_is_instance( list )); + test_assert_int_equal( 0 , job_list_get_size( list )); + job_list_free( list ); +} + + + +void call_add_job( void * arg ) { + arg_pack_type * arg_pack = arg_pack_safe_cast( arg ); + job_list_type * job_list = (job_list_type *) arg_pack_iget_ptr( arg_pack , 0 ); + job_queue_node_type * node = (job_queue_node_type *) arg_pack_iget_ptr( arg_pack , 1 ); + job_list_add_job( job_list , node ); +} + + +void call_iget_job( void * arg ) { + job_list_type * job_list = job_list_safe_cast( arg ); + job_list_iget_job( job_list , 10); +} + + + +void test_add_job() { + job_list_type * list = job_list_alloc(); + job_queue_node_type * node = job_queue_node_alloc_simple("name" , "/tmp" , "/bin/ls" , 0 , NULL); + job_list_add_job( list , node ); + test_assert_int_equal( job_list_get_size( list ) , 1 ); + test_assert_int_equal( job_queue_node_get_queue_index(node) , 0 ); + test_assert_ptr_equal( node , job_list_iget_job(list , 0)); + { + arg_pack_type * arg_pack = arg_pack_alloc( ); + arg_pack_append_ptr( arg_pack , list ); + arg_pack_append_ptr( arg_pack , node ); + test_assert_util_abort("job_queue_node_set_queue_index", call_add_job, arg_pack ); + arg_pack_free( arg_pack ); + } + test_assert_util_abort("job_list_iget_job", call_iget_job, list); + job_list_reset( list ); + test_assert_int_equal( 0 , job_list_get_size( list )); + job_list_free( list ); +} + + +int main( int argc , char ** argv) { + util_install_signals(); + test_create(); + test_add_job(); +} diff --git a/libres/lib/job_queue/tests/job_loadFail.cpp b/libres/lib/job_queue/tests/job_loadFail.cpp new file mode 100644 index 00000000000..efa985ca790 --- /dev/null +++ b/libres/lib/job_queue/tests/job_loadFail.cpp @@ -0,0 +1,59 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'job_loadFail.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 + + + +bool loadConfig(config_parser_type * config , const char * config_file, config_parser_type * config_compiler) { + bool OK = false; + workflow_job_type * cmd = workflow_job_config_alloc( "NAME" , config , config_file); + + if (cmd != NULL) { + OK = true; + workflow_job_update_config_compiler( cmd , config_compiler ); + workflow_job_free( cmd ); + } + + return OK; +} + + + +int main( int argc , char ** argv) { + int status = 0; + { + config_parser_type * config = workflow_job_alloc_config(); + config_parser_type * config_compiler = config_alloc(); + int iarg; + bool OK = true; + + for (iarg = 1; iarg < argc; iarg++) + OK = OK && (loadConfig( config , argv[iarg] , config_compiler ) == false); + + if (!OK) + status = 1; + + config_free(config_compiler); + config_free(config); + } + exit( status ); +} diff --git a/libres/lib/job_queue/tests/job_loadOK.cpp b/libres/lib/job_queue/tests/job_loadOK.cpp new file mode 100644 index 00000000000..a101fd79050 --- /dev/null +++ b/libres/lib/job_queue/tests/job_loadOK.cpp @@ -0,0 +1,59 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'job_loadOK.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 + + + +bool loadConfig(config_parser_type * config , const char * config_file , config_parser_type * config_compiler) { + bool OK = false; + workflow_job_type * cmd = workflow_job_config_alloc( "NAME" , config , config_file); + + if (cmd != NULL) { + OK = true; + workflow_job_update_config_compiler( cmd , config_compiler ); + workflow_job_free( cmd ); + } + + return OK; +} + + + +int main( int argc , char ** argv) { + int status = 0; + { + config_parser_type * config = workflow_job_alloc_config(); + config_parser_type * config_compiler = config_alloc(); + int iarg; + bool OK = true; + + for (iarg = 1; iarg < argc; iarg++) + OK = OK && loadConfig( config , argv[iarg] , config_compiler); + + if (!OK) + status = 1; + + config_free(config_compiler); + config_free(config); + } + exit( status ); +} diff --git a/libres/lib/job_queue/tests/job_lsb.cpp b/libres/lib/job_queue/tests/job_lsb.cpp new file mode 100644 index 00000000000..1bcb1a755b4 --- /dev/null +++ b/libres/lib/job_queue/tests/job_lsb.cpp @@ -0,0 +1,47 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'job_lsb.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 + +/* + This test should ideally be run twice in two different environments; + with and without dlopen() access to the lsf libraries. +*/ + +int main( int argc , char ** argv) { + lsb_type * lsb = lsb_alloc(); + + test_assert_not_NULL( lsb ); + if (!lsb_ready(lsb)) { + const stringlist_type * error_list = lsb_get_error_list( lsb ); + stringlist_fprintf(error_list , "\n", stdout); + } + + if (dlopen( "libbat.so" , RTLD_NOW | RTLD_GLOBAL)) + test_assert_true( lsb_ready( lsb )); + else + test_assert_false( lsb_ready( lsb )); + + lsb_free( lsb ); + exit(0); +} diff --git a/libres/lib/job_queue/tests/job_lsb_test.cpp b/libres/lib/job_queue/tests/job_lsb_test.cpp new file mode 100644 index 00000000000..d861f549803 --- /dev/null +++ b/libres/lib/job_queue/tests/job_lsb_test.cpp @@ -0,0 +1,60 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'job_lsb_test.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_server(lsf_driver_type * driver , const char * server, lsf_submit_method_enum submit_method) { + lsf_driver_set_option(driver , LSF_SERVER , server ); + test_assert_true( lsf_driver_get_submit_method( driver ) == submit_method ); +} + + + +/* + This test should ideally be run twice in two different environments; + with and without dlopen() access to the lsf libraries. +*/ + +int main( int argc , char ** argv) { + lsf_submit_method_enum submit_NULL; + lsb_type * lsb = lsb_alloc(); + if (lsb_ready(lsb)) + submit_NULL = LSF_SUBMIT_INTERNAL; + else + submit_NULL = LSF_SUBMIT_LOCAL_SHELL; + + + test_server( driver , NULL , submit_NULL ); + test_server( driver , "LoCaL" , LSF_SUBMIT_LOCAL_SHELL ); + test_server( driver , "LOCAL" , LSF_SUBMIT_LOCAL_SHELL ); + test_server( driver , "XLOCAL" , LSF_SUBMIT_REMOTE_SHELL ); + test_server( driver , NULL , submit_NULL ); + test_server( driver , "NULL" , submit_NULL ); + test_server( driver , "be-grid01" , LSF_SUBMIT_REMOTE_SHELL ); + printf("Servers OK\n"); + + lsb_free( lsb ); + exit(0); +} diff --git a/libres/lib/job_queue/tests/job_lsf_exclude_hosts_test.cpp b/libres/lib/job_queue/tests/job_lsf_exclude_hosts_test.cpp new file mode 100644 index 00000000000..a462c776cfd --- /dev/null +++ b/libres/lib/job_queue/tests/job_lsf_exclude_hosts_test.cpp @@ -0,0 +1,151 @@ +/* + * 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 + +static char * add_excluded(lsf_driver_type * driver) { + const char * node1 = "enern"; + const char * node2 = "toern"; + const char * node3 = "tre-ern.equinor.org"; + char * black1 = util_alloc_sprintf("hname!='%s'", node1); + char * black2 = util_alloc_sprintf("hname!='%s'", node2); + char * black3 = util_alloc_sprintf("hname!='%s'", node3); + + lsf_driver_add_exclude_hosts(driver, node1); + lsf_driver_add_exclude_hosts(driver, node2); + lsf_driver_add_exclude_hosts(driver, node3); + char * select = util_alloc_sprintf("select[%s && %s && %s]", + black1, black2, black3); + free(black1); + free(black2); + free(black3); + return select; +} + +static bool stringlist_contains_substring(const stringlist_type * lst, + const char * substr) { + for (int i = 0; i < stringlist_get_size(lst); i++) + if (strstr(stringlist_iget(lst, i), substr)) + return true; + return false; +} + +static void assert_stringlist_contains_substring(const stringlist_type * lst, + const char * substr, + const char * caller) { + if (!stringlist_contains_substring(lst, substr)) { + printf("%s stringlist does not contain \"%s\" as substring\n", + caller, substr); + printf("\twas: %s\n", stringlist_alloc_joined_string(lst, " ")); + exit(1); + } +} + + +void test_submit_with_select_resources() { + lsf_driver_type * driver = (lsf_driver_type *) lsf_driver_alloc(); + + // mimic: QUEUE_OPTION LSF LSF_RESOURCE span[hosts=1] (see ERT-1403) + lsf_driver_set_option(driver, "LSF_RESOURCE", + "bs[yes] " + "select[hname!='xxx' && hname!='yyy'] " + "span[hosts=1]" + ); + + add_excluded(driver); + const char * select = + "select[hname!='xxx' && hname!='yyy' && " + "hname!='enern' && hname!='toern' && hname!='tre-ern.equinor.org']"; + stringlist_type * argv = lsf_driver_alloc_cmd(driver, "", "NAME", "bsub", 1, 0, NULL); + lsf_driver_free(driver); + + assert_stringlist_contains_substring(argv, select, __func__); + assert_stringlist_contains_substring(argv, "span[hosts=1]", __func__); + assert_stringlist_contains_substring(argv, "bs[yes]", __func__); +} + + +void test_submit_with_resources() { + lsf_driver_type * driver = (lsf_driver_type *) lsf_driver_alloc(); + lsf_driver_set_option(driver, "LSF_RESOURCE", "span[hosts=1]"); + + char * select = add_excluded(driver); + stringlist_type * argv = lsf_driver_alloc_cmd(driver, "", "NAME", "bsub", 1, 0, NULL); + lsf_driver_free(driver); + + + assert_stringlist_contains_substring(argv, select, __func__); + assert_stringlist_contains_substring(argv, "span[hosts=1]", __func__); +} + +void test_submit() { + lsf_driver_type * driver = (lsf_driver_type *) lsf_driver_alloc(); + char * select = add_excluded(driver); + stringlist_type * argv = lsf_driver_alloc_cmd(driver, "", "NAME", "bsub", 1, 0, NULL); + lsf_driver_free(driver); + + if (!stringlist_contains(argv, select)) { + printf("%s lsf_driver_alloc_cmd argv does not contain %s\n", __func__, select); + printf("%s lsf_driver_alloc_cmd was %s\n", __func__, stringlist_alloc_joined_string(argv, " ")); + exit(1); + } +} + +void test_bjobs_parse_hosts() { + const char* full_hostnames = "hname1:4*hname2:13*st-rst666-01-42.st.example.org:1*hname4:hname5\n"; + const char* hostnames = "hname1:hname2:st-rst666-01-42.st.example.org:hname4:hname5"; + stringlist_type * expected = stringlist_alloc_from_split(hostnames,":"); + if (stringlist_get_size(expected) != 5) { + printf("Even expected has wrong size.\n"); + exit(1); + } + + char * fname = util_alloc_tmp_file("/tmp", "ert_job_exec_host", true); + + FILE * fptr; + fptr = fopen(fname, "w"); + fputs(full_hostnames, fptr); // : is std bjobs delimiter + fclose(fptr); + + stringlist_type * hosts = lsf_job_alloc_parse_hostnames(fname); + + if (!stringlist_equal(expected, hosts)) { + printf("hosts differ: expected [%s] got [%s]\n", + stringlist_alloc_joined_string(expected, ":"), + stringlist_alloc_joined_string(hosts, ":")); + exit(1); + } + + util_unlink_existing(fname); + free(fname); + stringlist_free( hosts ); +} + +int main(int argc, char ** argv) { + test_submit(); + test_submit_with_resources(); + test_submit_with_select_resources(); + + test_bjobs_parse_hosts(); + exit(0); +} diff --git a/libres/lib/job_queue/tests/job_lsf_library_submit_test.cpp b/libres/lib/job_queue/tests/job_lsf_library_submit_test.cpp new file mode 100644 index 00000000000..a4f072c6fce --- /dev/null +++ b/libres/lib/job_queue/tests/job_lsf_library_submit_test.cpp @@ -0,0 +1,64 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'job_lsf_submit_test.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 + + + +void test_submit(lsf_driver_type * driver, const char * cmd) { + assert( lsf_driver_set_option(driver , LSF_DEBUG_OUTPUT , "TRUE" ) ); + assert( LSF_SUBMIT_INTERNAL == lsf_driver_get_submit_method( driver )); + { + char * run_path = util_alloc_cwd(); + lsf_job_type * job = lsf_driver_submit_job( driver , cmd , 1 , run_path , "NAME" , 0 , NULL ); + assert( job ); + { + { + int lsf_status = lsf_driver_get_job_status_lsf( driver , job ); + assert( (lsf_status == JOB_STAT_RUN) || (lsf_status == JOB_STAT_PEND) ); + } + + lsf_driver_kill_job( driver , job ); + lsf_driver_set_bjobs_refresh_interval( driver , 0 ); + sleep(1); + + { + int lsf_status = lsf_driver_get_job_status_lsf( driver , job ); + assert( lsf_status == JOB_STAT_EXIT); + } + } + + free( run_path ); + } +} + + + +int main( int argc , char ** argv) { + lsf_driver_type * driver = lsf_driver_alloc(); + test_submit(driver , argv[1]); + lsf_driver_free( driver ); + exit(0); +} diff --git a/libres/lib/job_queue/tests/job_lsf_parse_bsub_stdout.cpp b/libres/lib/job_queue/tests/job_lsf_parse_bsub_stdout.cpp new file mode 100644 index 00000000000..72ee198e6d1 --- /dev/null +++ b/libres/lib/job_queue/tests/job_lsf_parse_bsub_stdout.cpp @@ -0,0 +1,81 @@ +/* + Copyright (C) 2015 Equinor ASA, Norway. + + The file 'job_lsf_submit_test.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 + + +void test_empty_file() { + const char * stdout_file = "bsub_empty"; + { + FILE * stream = util_fopen(stdout_file , "w"); + fclose( stream ); + } + test_assert_int_equal( lsf_job_parse_bsub_stdout("bsub" , stdout_file ) , 0); +} + + +void test_OK() { + const char * stdout_file = "bsub_OK"; + { + FILE * stream = util_fopen(stdout_file , "w"); + fprintf(stream , "Job <12345> is submitted to default queue .\n"); + fclose( stream ); + } + test_assert_int_equal( lsf_job_parse_bsub_stdout("bsub" , stdout_file ) , 12345); +} + + +void test_file_does_not_exist() { + test_assert_int_equal( lsf_job_parse_bsub_stdout("bsub" , "does/not/exist") , 0); +} + + + +void parse_invalid( void * arg ) { + const char * filename = (const char*) arg; + lsf_job_parse_bsub_stdout("bsub" , filename); +} + + +void test_parse_fail_abort() { + const char * stdout_file = "bsub_abort"; + { + FILE * stream = util_fopen(stdout_file , "w"); + fprintf(stream , "Job 12345 is submitted to default queue .\n"); + fclose( stream ); + } + test_assert_util_abort( "lsf_job_parse_bsub_stdout" , parse_invalid , (void *) stdout_file ); +} + + +int main(int argc, char ** argv) { + ecl::util::TestArea ta("lsf_parse"); + { + test_empty_file(); + test_file_does_not_exist( ); + test_OK(); + test_parse_fail_abort(); + } +} + + diff --git a/libres/lib/job_queue/tests/job_lsf_remote_submit_test.cpp b/libres/lib/job_queue/tests/job_lsf_remote_submit_test.cpp new file mode 100644 index 00000000000..bffc8fad9ff --- /dev/null +++ b/libres/lib/job_queue/tests/job_lsf_remote_submit_test.cpp @@ -0,0 +1,94 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'job_lsf_submit_test.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 + + +void test_submit(lsf_driver_type * driver , const char * server , const char * bsub_cmd , const char * bjobs_cmd , const char * bkill_cmd , const char * cmd) { + + test_assert_true( lsf_driver_set_option(driver , LSF_DEBUG_OUTPUT , "TRUE" ) ); + test_assert_true( lsf_driver_set_option(driver , LSF_SERVER , server ) ); + + if (bsub_cmd != NULL) + test_assert_true( lsf_driver_set_option(driver , LSF_BSUB_CMD , server )); + + if (bjobs_cmd != NULL) + test_assert_true( lsf_driver_set_option(driver , LSF_BJOBS_CMD , server ) ); + + if (bkill_cmd != NULL) + test_assert_true( lsf_driver_set_option(driver , LSF_BKILL_CMD , server )); + + { + char * run_path = util_alloc_cwd(); + lsf_job_type * job = lsf_driver_submit_job( driver , cmd , 1 , run_path , "NAME" , 0 , NULL ); + if (job) { + { + int lsf_status = lsf_driver_get_job_status_lsf( driver , job ); + if (!((lsf_status == JOB_STAT_RUN) || (lsf_status == JOB_STAT_PEND))) + test_error_exit("Got lsf_status:%d expected: %d or %d \n",lsf_status , JOB_STAT_RUN , JOB_STAT_PEND); + } + + lsf_driver_kill_job( driver , job ); + lsf_driver_set_bjobs_refresh_interval( driver , 0 ); + sleep(2); + + { + int lsf_status = 0; + for(int i=0; i < 10; i++){ + lsf_status = lsf_driver_get_job_status_lsf( driver , job ); + if (lsf_status != JOB_STAT_EXIT){ + sleep(2); + }else{ + break; + } + } + if (lsf_status != JOB_STAT_EXIT) + test_error_exit("Got lsf_status:%d expected: %d \n",lsf_status , JOB_STAT_EXIT ); + } + } else + test_error_exit("lsf_driver_submit_job() returned NULL \n"); + + + free( run_path ); + } +} + + +int main( int argc , char ** argv) { + util_install_signals(); + { + int iarg; + lsf_driver_type * driver = lsf_driver_alloc(); + + for (iarg = 2; iarg < argc; iarg++) { + const char * server = argv[iarg]; + printf("Testing lsf server:%s \n",server); + test_submit(driver , server , NULL , NULL , NULL , argv[1]); + } + + lsf_driver_free( driver ); + } + exit(0); +} diff --git a/libres/lib/job_queue/tests/job_lsf_submit_library_test.cpp b/libres/lib/job_queue/tests/job_lsf_submit_library_test.cpp new file mode 100644 index 00000000000..e6ff34471d6 --- /dev/null +++ b/libres/lib/job_queue/tests/job_lsf_submit_library_test.cpp @@ -0,0 +1,66 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'job_lsf_submit_test.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 + + +void test_submit(lsf_driver_type * driver) { + test_assert_true( lsf_driver_set_option(driver , LSF_DEBUG_OUTPUT , "TRUE" ) ); + test_assert_int_equal( LSF_SUBMIT_INTERNAL , lsf_driver_get_submit_method( driver )); + { + char * run_path = util_alloc_cwd(); + lsf_job_type * job = lsf_driver_submit_job( driver , cmd , 1 , run_path , "NAME" , 0 , NULL ); + if (job) { + { + int lsf_status = lsf_driver_get_job_status_lsf( driver , job ); + if (!((lsf_status == JOB_STAT_RUN) || (lsf_status == JOB_STAT_PEND))) + test_error_exit("Got lsf_status:%d expected: %d or %d \n",lsf_status , JOB_STAT_RUN , JOB_STAT_PEND); + } + + lsf_driver_kill_job( driver , job ); + lsf_driver_set_bjobs_refresh_interval( driver , 0 ); + sleep(1); + + { + int lsf_status = lsf_driver_get_job_status_lsf( driver , job ); + if (lsf_status != JOB_STAT_EXIT) + test_error_exit("Got lsf_status:%d expected: %d \n",lsf_status , JOB_STAT_EXIT ); + } + } else + test_error_exit("lsf_driver_submit_job() returned NULL \n"); + + + free( run_path ); + } +} + + + +int main( int argc , char ** argv) { + lsf_driver_type * driver = lsf_driver_alloc(); + test_submit(driver); + lsf_driver_free( driver ); + exit(0); +} diff --git a/libres/lib/job_queue/tests/job_lsf_test.cpp b/libres/lib/job_queue/tests/job_lsf_test.cpp new file mode 100644 index 00000000000..b0c9ee6f290 --- /dev/null +++ b/libres/lib/job_queue/tests/job_lsf_test.cpp @@ -0,0 +1,126 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'job_lsf_test.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 + + +void test_option(lsf_driver_type * driver , const char * option , const char * value) { + test_assert_true( lsf_driver_set_option( driver , option , value)); + test_assert_string_equal((const char *) lsf_driver_get_option( driver , option) , value); +} + + + +void test_status(int lsf_status , job_status_type job_status) { + test_assert_true( lsf_driver_convert_status( lsf_status ) == job_status); +} + +void test_options(void) { + lsf_driver_type * driver = (lsf_driver_type *) lsf_driver_alloc(); + test_assert_false( lsf_driver_has_project_code( driver )); + + // test setting values + test_option( driver , LSF_BSUB_CMD , "Xbsub"); + test_option( driver , LSF_BJOBS_CMD , "Xbsub"); + test_option( driver , LSF_BKILL_CMD , "Xbsub"); + test_option( driver , LSF_RSH_CMD , "RSH"); + test_option( driver , LSF_LOGIN_SHELL , "shell"); + test_option( driver , LSF_BSUB_CMD , "bsub"); + test_option( driver , LSF_PROJECT_CODE, "my-ppu"); + test_option( driver , LSF_BJOBS_TIMEOUT, "1234"); + + test_assert_true( lsf_driver_has_project_code( driver )); + + // test unsetting/resetting options to default values + test_option( driver , LSF_BSUB_CMD , NULL); + test_option( driver , LSF_BJOBS_CMD , NULL); + test_option( driver , LSF_BKILL_CMD , NULL); + test_option( driver , LSF_RSH_CMD , NULL); + test_option( driver , LSF_LOGIN_SHELL , NULL); + test_option( driver , LSF_PROJECT_CODE, NULL); + + // Setting NULL to numerical options should leave the value unchanged + lsf_driver_set_option( driver, LSF_BJOBS_TIMEOUT, NULL); + test_assert_string_equal( (const char *) lsf_driver_get_option( driver, LSF_BJOBS_TIMEOUT ), "1234"); + + lsf_driver_free( driver ); +} + +void test_status_tr() { + test_status( JOB_STAT_PEND , JOB_QUEUE_PENDING ); + test_status( JOB_STAT_PSUSP , JOB_QUEUE_RUNNING ); + test_status( JOB_STAT_USUSP , JOB_QUEUE_RUNNING ); + test_status( JOB_STAT_SSUSP , JOB_QUEUE_RUNNING ); + test_status( JOB_STAT_RUN , JOB_QUEUE_RUNNING ); + test_status( JOB_STAT_NULL , JOB_QUEUE_NOT_ACTIVE ); + test_status( JOB_STAT_DONE , JOB_QUEUE_DONE ); + test_status( JOB_STAT_EXIT , JOB_QUEUE_EXIT ); + test_status( JOB_STAT_UNKWN , JOB_QUEUE_UNKNOWN ); + test_status( 192 , JOB_QUEUE_DONE ); +} + + +void test_cmd(void) { + const char * project_code = "XXX_PROJECT"; + lsf_driver_type * driver = (lsf_driver_type *) lsf_driver_alloc(); + { + stringlist_type * cmd = lsf_driver_alloc_cmd( driver , "out" , "job", "/bin/echo" , 1 , 0 , NULL ); + test_assert_false( lsf_driver_has_project_code( driver )); + test_assert_false( stringlist_contains( cmd , "-P" )); + stringlist_free( cmd ); + } + + lsf_driver_set_option( driver , LSF_PROJECT_CODE , project_code); + { + stringlist_type * cmd = lsf_driver_alloc_cmd( driver , "out" , "job", "/bin/echo" , 1 , 0 , NULL ); + int P_index = stringlist_find_first( cmd , "-P" ); + test_assert_true( lsf_driver_has_project_code( driver )); + test_assert_true( P_index >= 0 ); + test_assert_string_equal( stringlist_iget( cmd , P_index + 1) , project_code ); + stringlist_free( cmd ); + } + + lsf_driver_free(driver); +} + + +void test_submit_method() { + lsf_driver_type * driver = (lsf_driver_type *) lsf_driver_alloc(); +#ifdef HAVE_LSF_LIBRARY + test_assert_int_not_equal( lsf_driver_get_submit_method(driver), LSF_SUBMIT_INVALID); +#else + test_assert_int_equal(lsf_driver_get_submit_method(driver), LSF_SUBMIT_LOCAL_SHELL); +#endif + lsf_driver_free(driver); +} + +int main( int argc , char ** argv) { + util_install_signals(); + + test_options(); + test_status_tr( ); + test_cmd(); + test_submit_method(); + + exit(0); +} diff --git a/libres/lib/job_queue/tests/job_mock_slurm.cpp b/libres/lib/job_queue/tests/job_mock_slurm.cpp new file mode 100644 index 00000000000..a4ef617807a --- /dev/null +++ b/libres/lib/job_queue/tests/job_mock_slurm.cpp @@ -0,0 +1,272 @@ +/* + Copyright (C) 2020 Equinor ASA, Norway. + + The file 'job_mock_slurm.cpp' 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 + + +/* + This test tests the interaction between slurm and libres by using mock scripts + for slurm functionality. The scripts are very simple, and just return suitable + "results" on stdout which the slurm driver in libres interprets. Observe that + this testing is 100% stateless and does not invoke running external "jobs" in + any kind. + + The different jobs have different behaviour based on the the job name; the + names are just the strings "0", "1", "2", "3" and "4". The behaviour of the + different job is as follows: + + 0: The sbatch command fails with exit != 0 when this job is started. The test + verifies that the queue_driver_submit_job() returns nullptr, and after that + we do not hear naything more from this job. + + 1: This job is submitted as it should, but then subsequently cancelled. The + cancel script actually does not do anything, but afterwards we test that + the status is correctly returned. Before the CANCELLED status is reached we + go through two steps: + + a) We run the squeue command, that does not report on cancelled jobs and + will not report status for job 1. + + b) We run the scontrol command which through a detailed request finds the + cancelled status of the job. + + 2: / 3: These jobs are PENDING and RUNNING respoectively, that status is + reported by the squeue command. + + 4: This job has been completed. As with the canceled job 1 we need to go + through both squeue and scontrol before we get the status of the job. + +*/ + + +void make_sleep_job(const char * fname, int sleep_time) { + FILE * stream = util_fopen(fname, "w"); + fprintf(stream, "sleep %d \n", sleep_time); + fclose(stream); + + mode_t fmode = S_IRWXU; + chmod( fname, fmode); +} + +void make_script(const char* fname, const std::string& content) { + FILE * stream = util_fopen(fname, "w"); + fprintf(stream, "%s" , content.c_str()); + fclose(stream); + + mode_t fmode = S_IRWXU; + chmod( fname, fmode); +} + + +void install_script(queue_driver_type * driver, const char * option, const std::string& content) { + char * fname = util_alloc_abs_path( option ); + make_script(fname, content); + queue_driver_set_option(driver, option, fname); + free( fname ); +} + + +void make_slurm_commands(queue_driver_type * driver) { + std::string sbatch = R"(#!/bin/bash + +if [ $2 = "--job-name=0" ]; then + exit 1 +fi + +if [ $2 = "--job-name=1" ]; then + echo 1 +fi + +if [ $2 = "--job-name=2" ]; then + echo 2 +fi + +if [ $2 = "--job-name=3" ]; then + echo 3 +fi + +if [ $2 = "--job-name=4" ]; then + echo 4 +fi +)"; + + + std::string scancel = R"(#!/bin/bash + +exit 0 +)"; + + + std::string scontrol = R"(#!/bin/bash +if [ $3 = "1" ]; then +cat < jobs; + + + make_sleep_job(cmd, 10); + make_slurm_commands(driver); + + test_assert_NULL( submit_job(driver, ta, "0", cmd)); + + { + auto job = submit_job(driver, ta, "1", cmd); + test_assert_not_NULL(job); + jobs.push_back(job); + } + + { + auto job = submit_job(driver, ta, "2", cmd); + test_assert_not_NULL(job); + jobs.push_back(job); + } + + { + auto job = submit_job(driver, ta, "3", cmd); + test_assert_not_NULL(job); + jobs.push_back(job); + } + + { + auto job = submit_job(driver, ta, "4", cmd); + test_assert_not_NULL(job); + jobs.push_back(job); + } + + queue_driver_kill_job(driver, jobs[0]); + auto job1_status = queue_driver_get_status(driver, jobs[0]); + test_assert_int_equal(job1_status, JOB_QUEUE_IS_KILLED); + + auto job2_status = queue_driver_get_status(driver, jobs[1]); + test_assert_int_equal(job2_status, JOB_QUEUE_PENDING); + + auto job3_status = queue_driver_get_status(driver, jobs[2]); + test_assert_int_equal(job3_status, JOB_QUEUE_RUNNING); + + auto job4_status = queue_driver_get_status(driver, jobs[3]); + test_assert_int_equal(job4_status, JOB_QUEUE_DONE); + + for (auto job : jobs) + queue_driver_free_job(driver, job); + + free(cmd); + queue_driver_free(driver); +} + + + + +int main( int argc , char ** argv) { + run(); +} diff --git a/libres/lib/job_queue/tests/job_node_test.cpp b/libres/lib/job_queue/tests/job_node_test.cpp new file mode 100644 index 00000000000..d91ae1c7a74 --- /dev/null +++ b/libres/lib/job_queue/tests/job_node_test.cpp @@ -0,0 +1,57 @@ +/* + Copyright (C) 2015 Equinor ASA, Norway. + + The file 'job_node_test.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 + + +void test_create() { + job_queue_node_type * node = job_queue_node_alloc_simple("name" , "/tmp" , "/bin/ls" , 0 , NULL); + test_assert_true( job_queue_node_is_instance( node )); + job_queue_node_free( node ); +} + + + +void call_get_queue_index( void * arg ) { + job_queue_node_type * node = job_queue_node_safe_cast( arg ); + job_queue_node_get_queue_index( node ); +} + + + + +void test_queue_index() { + job_queue_node_type * node = job_queue_node_alloc_simple( "name" , "/tmp" , "/bin/ls" , 0 , NULL ); + test_assert_util_abort("job_queue_node_get_queue_index" , call_get_queue_index , node ); +} + + +void test_path_does_not_exist() { + job_queue_node_type * node = job_queue_node_alloc_simple( "name" , "does-not-exist" , "/bin/ls" , 0 , NULL); + test_assert_NULL( node ); +} + + +int main( int argc , char ** argv) { + util_install_signals(); + test_create(); + test_queue_index(); + test_path_does_not_exist(); +} diff --git a/libres/lib/job_queue/tests/job_program.cpp b/libres/lib/job_queue/tests/job_program.cpp new file mode 100644 index 00000000000..ad5b754d6dd --- /dev/null +++ b/libres/lib/job_queue/tests/job_program.cpp @@ -0,0 +1,35 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'job_program.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 + + +int main( int argc , char ** argv) { + int max_count = 100; + int count = 0; + while (true) { + sleep(1); + count++; + printf("%d/%d \n",count , max_count); + if (count == max_count) + break; + } + exit(0); +} diff --git a/libres/lib/job_queue/tests/job_program_output.cpp b/libres/lib/job_queue/tests/job_program_output.cpp new file mode 100644 index 00000000000..b90e398130a --- /dev/null +++ b/libres/lib/job_queue/tests/job_program_output.cpp @@ -0,0 +1,35 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'job_program.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 +int main( int argc , char ** argv) { + int sleep_time; + util_sscanf_int(argv[2], &sleep_time); + sleep(sleep_time); + + char * filename = util_alloc_filename(argv[1], "OK", "status"); + + if (util_file_exists(argv[1])) { + FILE * file = util_fopen(filename, "w"); + fprintf(file, "All good"); + fclose(file); + exit(0); + } else + exit(1); +} diff --git a/libres/lib/job_queue/tests/job_queue_driver_test.cpp b/libres/lib/job_queue/tests/job_queue_driver_test.cpp new file mode 100644 index 00000000000..5f5083010b9 --- /dev/null +++ b/libres/lib/job_queue/tests/job_queue_driver_test.cpp @@ -0,0 +1,195 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'job_queue_test.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 + + +void job_queue_set_driver_(job_driver_type driver_type) { + job_queue_type * queue = job_queue_alloc(10, "OK", "STATUS", "ERROR"); + queue_driver_type * driver = queue_driver_alloc(driver_type); + test_assert_false(job_queue_has_driver(queue)); + + job_queue_set_driver(queue, driver); + test_assert_true(job_queue_has_driver(queue)); + + job_queue_free(queue); + queue_driver_free(driver); + + +} + +void set_option_max_running_max_running_value_set() { + queue_driver_type * driver_torque = queue_driver_alloc(TORQUE_DRIVER); + test_assert_true(queue_driver_set_option(driver_torque, MAX_RUNNING, "42")); + test_assert_string_equal("42", (const char *) queue_driver_get_option(driver_torque, MAX_RUNNING)); + queue_driver_free(driver_torque); + + + queue_driver_type * driver_lsf = queue_driver_alloc(LSF_DRIVER); + test_assert_true(queue_driver_set_option(driver_lsf, MAX_RUNNING, "72")); + test_assert_string_equal("72", (const char *) queue_driver_get_option(driver_lsf, MAX_RUNNING)); + queue_driver_free(driver_lsf); +} + +void set_option_max_running_max_running_option_set() { + queue_driver_type * driver_torque = queue_driver_alloc(TORQUE_DRIVER); + test_assert_true(queue_driver_set_option(driver_torque, MAX_RUNNING, "42")); + test_assert_string_equal("42", (const char *) queue_driver_get_option(driver_torque, MAX_RUNNING)); + queue_driver_free(driver_torque); + +} + +void set_option_invalid_option_returns_false() { + queue_driver_type * driver_torque = queue_driver_alloc(TORQUE_DRIVER); + test_assert_false(queue_driver_set_option(driver_torque, "MAKS_RUNNING", "42")); + queue_driver_free(driver_torque); +} + +void set_option_invalid_value_returns_false() { + queue_driver_type * driver_torque = queue_driver_alloc(TORQUE_DRIVER); + test_assert_false(queue_driver_set_option(driver_torque, "MAX_RUNNING", "2a")); + queue_driver_free(driver_torque); +} + +void set_option_valid_on_specific_driver_returns_true() { + queue_driver_type * driver_torque = queue_driver_alloc(TORQUE_DRIVER); + test_assert_true(queue_driver_set_option(driver_torque, TORQUE_NUM_CPUS_PER_NODE, "33")); + test_assert_string_equal("33", (const char *) queue_driver_get_option(driver_torque, TORQUE_NUM_CPUS_PER_NODE)); + queue_driver_free(driver_torque); +} + +void get_driver_option_lists() { + //Torque driver option list + { + queue_driver_type * driver_torque = queue_driver_alloc(TORQUE_DRIVER); + stringlist_type * option_list = stringlist_alloc_new(); + queue_driver_init_option_list(driver_torque, option_list); + + test_assert_true(stringlist_contains(option_list, MAX_RUNNING)); + test_assert_true(stringlist_contains(option_list, TORQUE_QSUB_CMD)); + test_assert_true(stringlist_contains(option_list, TORQUE_QSTAT_CMD)); + test_assert_true(stringlist_contains(option_list, TORQUE_QDEL_CMD)); + test_assert_true(stringlist_contains(option_list, TORQUE_QUEUE)); + test_assert_true(stringlist_contains(option_list, TORQUE_NUM_CPUS_PER_NODE)); + test_assert_true(stringlist_contains(option_list, TORQUE_NUM_NODES)); + test_assert_true(stringlist_contains(option_list, TORQUE_KEEP_QSUB_OUTPUT)); + test_assert_true(stringlist_contains(option_list, TORQUE_CLUSTER_LABEL)); + + stringlist_free(option_list); + queue_driver_free(driver_torque); + } + + //Local driver option list (only general queue_driver options) + { + queue_driver_type * driver_local = queue_driver_alloc(LOCAL_DRIVER); + stringlist_type * option_list = stringlist_alloc_new(); + queue_driver_init_option_list(driver_local, option_list); + + test_assert_true(stringlist_contains(option_list, MAX_RUNNING)); + + stringlist_free(option_list); + queue_driver_free(driver_local); + } + + //Lsf driver option list + { + queue_driver_type * driver_lsf = queue_driver_alloc(LSF_DRIVER); + stringlist_type * option_list = stringlist_alloc_new(); + queue_driver_init_option_list(driver_lsf, option_list); + + test_assert_true(stringlist_contains(option_list, MAX_RUNNING)); + test_assert_true(stringlist_contains(option_list, LSF_QUEUE)); + test_assert_true(stringlist_contains(option_list, LSF_RESOURCE)); + test_assert_true(stringlist_contains(option_list, LSF_SERVER)); + test_assert_true(stringlist_contains(option_list, LSF_RSH_CMD)); + test_assert_true(stringlist_contains(option_list, LSF_LOGIN_SHELL)); + test_assert_true(stringlist_contains(option_list, LSF_BSUB_CMD)); + test_assert_true(stringlist_contains(option_list, LSF_BJOBS_CMD)); + test_assert_true(stringlist_contains(option_list, LSF_BKILL_CMD)); + + stringlist_free(option_list); + queue_driver_free(driver_lsf); + } + + //Rsh driver option list + { + queue_driver_type * driver_rsh = queue_driver_alloc(RSH_DRIVER); + stringlist_type * option_list = stringlist_alloc_new(); + queue_driver_init_option_list(driver_rsh, option_list); + + test_assert_true(stringlist_contains(option_list, MAX_RUNNING)); + test_assert_true(stringlist_contains(option_list, RSH_HOST)); + test_assert_true(stringlist_contains(option_list, RSH_HOSTLIST)); + test_assert_true(stringlist_contains(option_list, RSH_CMD)); + test_assert_true(stringlist_contains(option_list, RSH_CLEAR_HOSTLIST)); + + stringlist_free(option_list); + queue_driver_free(driver_rsh); + } + + + //SLurm driver option list + { + queue_driver_type * driver_slurm = queue_driver_alloc(SLURM_DRIVER); + stringlist_type * option_list = stringlist_alloc_new(); + queue_driver_init_option_list(driver_slurm, option_list); + + stringlist_fprintf(option_list, ", ", stdout); + test_assert_true(stringlist_contains(option_list, MAX_RUNNING)); + test_assert_true(stringlist_contains(option_list, SLURM_SBATCH_OPTION)); + test_assert_true(stringlist_contains(option_list, SLURM_SCONTROL_OPTION)); + test_assert_true(stringlist_contains(option_list, SLURM_SQUEUE_OPTION)); + test_assert_true(stringlist_contains(option_list, SLURM_SCANCEL_OPTION)); + test_assert_true(stringlist_contains(option_list, SLURM_PARTITION_OPTION)); + test_assert_true(stringlist_contains(option_list, SLURM_SQUEUE_TIMEOUT_OPTION)); + test_assert_true(stringlist_contains(option_list, SLURM_MAX_RUNTIME_OPTION)); + test_assert_true(stringlist_contains(option_list, SLURM_MEMORY_OPTION)); + test_assert_true(stringlist_contains(option_list, SLURM_MEMORY_PER_CPU_OPTION)); + + stringlist_free(option_list); + queue_driver_free(driver_slurm); + + } +} + +int main(int argc, char ** argv) { + job_queue_set_driver_(LSF_DRIVER); + job_queue_set_driver_(LOCAL_DRIVER); + job_queue_set_driver_(RSH_DRIVER); + job_queue_set_driver_(TORQUE_DRIVER); + job_queue_set_driver_(SLURM_DRIVER); + + set_option_max_running_max_running_value_set(); + set_option_max_running_max_running_option_set(); + set_option_invalid_option_returns_false(); + set_option_invalid_value_returns_false(); + + set_option_valid_on_specific_driver_returns_true(); + get_driver_option_lists(); + + exit(0); +} diff --git a/libres/lib/job_queue/tests/job_queue_stress_task.cpp b/libres/lib/job_queue/tests/job_queue_stress_task.cpp new file mode 100644 index 00000000000..b5bf7957533 --- /dev/null +++ b/libres/lib/job_queue/tests/job_queue_stress_task.cpp @@ -0,0 +1,58 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'job_queue_test.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 + + +/* + This is a small test job used by the job_queue_stress_test. The job + does the following: + + 0. Chadir to runpath + 1. Create the file @runfile. + 2. Wait with usleep( @usleep_time ). + 3. Remove the @runfile. + 4. Create new file @OK_file + 5. exit. +*/ + +int main(int argc, char ** argv) { + const char * runpath = argv[1]; + const char * runfile = argv[2]; + const char * OK_file = argv[3]; + + int usleep_time; + + util_chdir( runpath ); + util_sscanf_int( argv[4] , &usleep_time ); + { + FILE * stream = util_fopen( runfile , "w"); + fprintf(stream , "Running ... \n"); + fclose( stream ); + } + usleep( usleep_time ); + util_unlink_existing(runfile); + { + FILE * stream = util_fopen( OK_file , "w"); + fprintf(stream , "OK ... \n"); + fclose( stream ); + } + return 0; +} diff --git a/libres/lib/job_queue/tests/job_slurm_driver.cpp b/libres/lib/job_queue/tests/job_slurm_driver.cpp new file mode 100644 index 00000000000..71ef80a3fbe --- /dev/null +++ b/libres/lib/job_queue/tests/job_slurm_driver.cpp @@ -0,0 +1,65 @@ +/* + Copyright (C) 2020 Equinor ASA, Norway. + + The file 'slurm_driver.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 + + +void test_option(slurm_driver_type * driver , const char * option , const char * value) { + test_assert_true( slurm_driver_set_option( driver , option , value)); + test_assert_string_equal((const char *) slurm_driver_get_option( driver , option) , value); +} + +void test_host_options(slurm_driver_type * driver, const char * option) { + test_assert_true(slurm_driver_set_option(driver, option, "host1")); + test_assert_string_equal((const char *) slurm_driver_get_option( driver , option) , "host1"); + + test_assert_true(slurm_driver_set_option(driver, option, "host2")); + test_assert_string_equal((const char *) slurm_driver_get_option( driver , option) , "host1,host2"); + + test_assert_true(slurm_driver_set_option(driver, option, "host2 host3,host4")); + test_assert_string_equal((const char *) slurm_driver_get_option( driver , option) , "host1,host2,host3,host4"); +} + + +void test_options() { + slurm_driver_type * driver = (slurm_driver_type *) slurm_driver_alloc(); + test_option(driver, SLURM_PARTITION_OPTION, "my_partition"); + test_option(driver, SLURM_SBATCH_OPTION, "my_funny_sbatch"); + test_option(driver, SLURM_SCANCEL_OPTION, "my_funny_scancel"); + test_option(driver, SLURM_SQUEUE_OPTION, "my_funny_squeue"); + test_option(driver, SLURM_SCONTROL_OPTION, "my_funny_scontrol"); + test_option(driver, SLURM_SQUEUE_TIMEOUT_OPTION, "11"); + test_option(driver, SLURM_MAX_RUNTIME_OPTION, "11"); + test_option(driver, SLURM_MEMORY_OPTION, "100mb"); + test_option(driver, SLURM_MEMORY_PER_CPU_OPTION, "1000gb"); + test_assert_false( slurm_driver_set_option(driver, "SLURM_SQUEUE_TIMEOUT_OPTION", "NOT_INTEGER")); + test_assert_false( slurm_driver_set_option(driver, "NO_SUCH_OPTION", "Value")); + test_host_options(driver, SLURM_INCLUDE_HOST_OPTION); + test_host_options(driver, SLURM_EXCLUDE_HOST_OPTION); + slurm_driver_free( driver ); +} + + +int main( int argc , char ** argv) { + test_options(); + exit(0); +} diff --git a/libres/lib/job_queue/tests/job_slurm_runtest.cpp b/libres/lib/job_queue/tests/job_slurm_runtest.cpp new file mode 100644 index 00000000000..5461748e4d6 --- /dev/null +++ b/libres/lib/job_queue/tests/job_slurm_runtest.cpp @@ -0,0 +1,122 @@ +/* + Copyright (C) 2020 Equinor ASA, Norway. + + The file 'job_slurm_runtest.cpp' 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 + +void make_sleep_job(const char * fname, int sleep_time) { + FILE * stream = util_fopen(fname, "w"); + fprintf(stream, "sleep %d \n", sleep_time); + fclose(stream); + + mode_t fmode = S_IRWXU; + chmod( fname, fmode); +} + + +void make_failed_job(const char * fname, int sleep_time) { + FILE * stream = util_fopen(fname, "w"); + fprintf(stream, "sleep %d \n", sleep_time); + fprintf(stream, "exit 1\n"); + fclose(stream); + + mode_t fmode = S_IRWXU; + chmod( fname, fmode); +} + + + +void run(double squeue_timeout) { + ecl::util::TestArea ta("slurm_submit", true); + queue_driver_type * driver = queue_driver_alloc_slurm(); + std::vector jobs; + const char * long_cmd = util_alloc_abs_path("long_run.sh"); + const char * ok_cmd = util_alloc_abs_path("ok_run.sh"); + const char * fail_cmd = util_alloc_abs_path("failed_run.sh"); + int num_jobs = 6; + + make_sleep_job(long_cmd, 10); + make_sleep_job(ok_cmd, 1); + make_failed_job(fail_cmd, 1); + auto squeue_timeout_string = std::to_string( squeue_timeout ); + queue_driver_set_option(driver, SLURM_SQUEUE_TIMEOUT_OPTION, squeue_timeout_string.c_str()); + + for (int i = 0; i < num_jobs; i++) { + std::string run_path = ta.test_cwd() + "/" + std::to_string(i); + std::string job_name = "job" + std::to_string(i); + util_make_path(run_path.c_str()); + if (i == 0) + jobs.push_back( queue_driver_submit_job(driver, long_cmd, 1, run_path.c_str(), job_name.c_str(), 0, nullptr)); + else if (i == num_jobs - 1) + jobs.push_back( queue_driver_submit_job(driver, fail_cmd, 1, run_path.c_str(), job_name.c_str(), 0, nullptr)); + else + jobs.push_back( queue_driver_submit_job(driver, ok_cmd, 1, run_path.c_str(), job_name.c_str(), 0, nullptr)); + } + + + while (true) { + int active_count = 0; + for (auto * job_ptr : jobs) { + auto status = queue_driver_get_status(driver, job_ptr); + if (status == JOB_QUEUE_RUNNING || status == JOB_QUEUE_PENDING || status == JOB_QUEUE_WAITING) + active_count += 1; + } + + if (active_count == 0) + break; + + auto * long_job = jobs[0]; + auto long_status = queue_driver_get_status(driver, long_job); + if (long_status != JOB_QUEUE_IS_KILLED) + queue_driver_kill_job( driver, long_job ); + + usleep(100000); + } + + + for (int i = 0; i < num_jobs; i++) { + auto * job_ptr = jobs[i]; + if (i == 0) + test_assert_int_equal( queue_driver_get_status(driver, job_ptr), JOB_QUEUE_IS_KILLED ); + else if (i == num_jobs - 1) + test_assert_int_equal( queue_driver_get_status(driver, job_ptr), JOB_QUEUE_EXIT ); + else + test_assert_int_equal( queue_driver_get_status(driver, job_ptr), JOB_QUEUE_DONE ); + } + + for (auto * job_ptr : jobs) + queue_driver_free_job(driver, job_ptr); + + queue_driver_free(driver); +} + + +int main( int argc , char ** argv) { + run(0); + run(2); + exit(0); +} diff --git a/libres/lib/job_queue/tests/job_slurm_submit.cpp b/libres/lib/job_queue/tests/job_slurm_submit.cpp new file mode 100644 index 00000000000..f54dfac7666 --- /dev/null +++ b/libres/lib/job_queue/tests/job_slurm_submit.cpp @@ -0,0 +1,72 @@ +/* + Copyright (C) 2020 Equinor ASA, Norway. + + The file 'job_slurm_runtest.cpp' 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 + +void make_sleep_job(const char * fname, int sleep_time) { + FILE * stream = util_fopen(fname, "w"); + fprintf(stream, "sleep %d \n", sleep_time); + fclose(stream); + + mode_t fmode = S_IRWXU; + chmod( fname, fmode); +} + + + +void submit_job(queue_driver_type * driver, const ecl::util::TestArea& ta, const std::string& job_name, const char* cmd, bool expect_fail) { + std::string run_path = ta.test_cwd() + "/" + job_name; + util_make_path(run_path.c_str()); + auto job = queue_driver_submit_job(driver, cmd, 1, run_path.c_str(), job_name.c_str(), 0, nullptr); + if (expect_fail) + test_assert_NULL( job ); + else { + test_assert_not_NULL( job ); + queue_driver_kill_job(driver, job); + queue_driver_free_job(driver, job); + } +} + + + +void run() { + ecl::util::TestArea ta("slurm_submit", true); + queue_driver_type * driver = queue_driver_alloc_slurm(); + const char * cmd = util_alloc_abs_path("cmd.sh"); + + make_sleep_job(cmd, 10); + submit_job(driver, ta, "JOB1", cmd, false); + queue_driver_set_option(driver, SLURM_PARTITION_OPTION, "invalid_partition"); + submit_job(driver, ta, "JOB1", cmd, true); + + queue_driver_free(driver); +} + + +int main( int argc , char ** argv) { + run(); +} diff --git a/libres/lib/job_queue/tests/job_status_test.cpp b/libres/lib/job_queue/tests/job_status_test.cpp new file mode 100644 index 00000000000..db19f752aac --- /dev/null +++ b/libres/lib/job_queue/tests/job_status_test.cpp @@ -0,0 +1,201 @@ +/* + Copyright (C) 2015 Equinor ASA, Norway. + + The file 'job_status_test.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 + + +void call_get_status( void * arg ) { + job_queue_status_type * job_status = job_queue_status_safe_cast( arg ); + job_queue_status_get_count( job_status , pow(2, JOB_QUEUE_MAX_STATE)); // This enum value is completly missing; should give util_abort. +} + + + +void test_create() { + job_queue_status_type * status = job_queue_status_alloc(); + test_assert_true( job_queue_status_is_instance( status )); + test_assert_int_equal( job_queue_status_get_count( status , JOB_QUEUE_DONE ) , 0 ); + test_assert_util_abort( "job_queue_status_get_count" , call_get_status , status ); + job_queue_status_free( status ); +} + + +void * add_sim( void * arg ) { + job_queue_status_type * job_status = job_queue_status_safe_cast( arg ); + job_queue_status_inc( job_status , JOB_QUEUE_WAITING ); + return NULL; +} + + +void * user_exit( void * arg ) { + job_queue_status_type * job_status = job_queue_status_safe_cast( arg ); + job_queue_status_transition( job_status , JOB_QUEUE_WAITING , JOB_QUEUE_DO_KILL); + return NULL; +} + + +void * user_done( void * arg ) { + job_queue_status_type * job_status = job_queue_status_safe_cast( arg ); + job_queue_status_transition( job_status , JOB_QUEUE_WAITING , JOB_QUEUE_DONE); + return NULL; +} + + + +void test_update() { + int N = 15000; + pthread_t * thread_list = (pthread_t *) util_malloc( 2*N*sizeof * thread_list); + int num_exit_threads = 0; + int num_done_threads = 0; + job_queue_status_type * status = job_queue_status_alloc(); + + test_assert_int_equal( 0 , job_queue_status_get_total_count( status )); + for (int i=0; i < 2*N; i++) + add_sim( status ); + test_assert_int_equal( 2*N , job_queue_status_get_count( status , JOB_QUEUE_WAITING )); + test_assert_int_equal( 2*N , job_queue_status_get_count( status , JOB_QUEUE_WAITING + JOB_QUEUE_RUNNING + JOB_QUEUE_DONE)); + + { + int i = 0; + while (true) { + int thread_status; + + if ((i % 2) == 0) { + thread_status = pthread_create( &thread_list[i] , NULL , user_exit , status ); + if (thread_status == 0) + num_exit_threads++; + else + break; + } else { + thread_status = pthread_create( &thread_list[i] , NULL , user_done , status ); + if (thread_status == 0) + num_done_threads++; + else + break; + } + + i++; + if (i == N) + break; + } + } + if ((num_done_threads + num_exit_threads) == 0) { + fprintf(stderr, "Hmmm - not a single thread created - very suspicious \n"); + exit(1); + } + + for (int i=0; i < num_done_threads + num_exit_threads; i++) + pthread_join( thread_list[i] , NULL ); + + test_assert_int_equal( 2*N - num_done_threads - num_exit_threads , job_queue_status_get_count( status , JOB_QUEUE_WAITING )); + test_assert_int_equal( num_exit_threads , job_queue_status_get_count( status , JOB_QUEUE_DO_KILL )); + test_assert_int_equal( num_done_threads , job_queue_status_get_count( status , JOB_QUEUE_DONE )); + + test_assert_int_equal( num_exit_threads + num_done_threads , job_queue_status_get_count( status , JOB_QUEUE_DO_KILL + JOB_QUEUE_DONE)); + + test_assert_int_equal( 2*N , job_queue_status_get_total_count( status )); + test_assert_int_equal( 2*N , job_queue_status_get_count( status , 2*JOB_QUEUE_DO_KILL_NODE_FAILURE - 1)); + job_queue_status_free( status ); +} + + + +/* + The job_queue_status_inc( ) and the job_queue_status_get_count( ) + functions use two different and independent implementations + internally; that is the reason for this seemingly quite trivial and + not-very-interesting test. +*/ + +void test_index() { + job_queue_status_type * status = job_queue_status_alloc(); + + job_queue_status_inc( status, JOB_QUEUE_NOT_ACTIVE ); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_NOT_ACTIVE), 1); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_STATUS_ALL ), 1); + + job_queue_status_inc( status, JOB_QUEUE_WAITING ); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_WAITING), 1); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_STATUS_ALL ), 2); + + job_queue_status_inc( status, JOB_QUEUE_SUBMITTED ); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_SUBMITTED), 1); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_STATUS_ALL ), 3); + + job_queue_status_inc( status, JOB_QUEUE_PENDING ); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_PENDING), 1); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_STATUS_ALL ), 4); + + job_queue_status_inc( status, JOB_QUEUE_RUNNING ); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_RUNNING), 1); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_STATUS_ALL ), 5); + + job_queue_status_inc( status, JOB_QUEUE_DONE ); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_DONE), 1); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_STATUS_ALL ), 6); + + job_queue_status_inc( status, JOB_QUEUE_EXIT ); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_EXIT), 1); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_STATUS_ALL ), 7); + + job_queue_status_inc( status, JOB_QUEUE_IS_KILLED ); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_IS_KILLED), 1); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_STATUS_ALL ), 8); + + job_queue_status_inc( status, JOB_QUEUE_DO_KILL ); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_DO_KILL), 1); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_STATUS_ALL ), 9); + + job_queue_status_inc( status, JOB_QUEUE_SUCCESS ); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_SUCCESS), 1); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_STATUS_ALL ), 10); + + job_queue_status_inc( status, JOB_QUEUE_RUNNING_DONE_CALLBACK ); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_RUNNING_DONE_CALLBACK), 1); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_STATUS_ALL ), 11); + + job_queue_status_inc( status, JOB_QUEUE_RUNNING_EXIT_CALLBACK ); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_RUNNING_EXIT_CALLBACK), 1); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_STATUS_ALL ), 12); + + job_queue_status_inc( status, JOB_QUEUE_STATUS_FAILURE ); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_STATUS_FAILURE), 1); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_STATUS_ALL ), 13); + + job_queue_status_inc( status, JOB_QUEUE_FAILED ); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_FAILED), 1); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_STATUS_ALL ), 14); + + job_queue_status_inc( status, JOB_QUEUE_DO_KILL_NODE_FAILURE ); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_DO_KILL_NODE_FAILURE), 1); + test_assert_int_equal( job_queue_status_get_count( status, JOB_QUEUE_STATUS_ALL ), 15); + + job_queue_status_free( status ); +} + +int main( int argc , char ** argv) { + util_install_signals(); + test_create(); + test_index(); + test_update(); +} diff --git a/libres/lib/job_queue/tests/job_torque_submit_test.cpp b/libres/lib/job_queue/tests/job_torque_submit_test.cpp new file mode 100644 index 00000000000..5efefb5ad56 --- /dev/null +++ b/libres/lib/job_queue/tests/job_torque_submit_test.cpp @@ -0,0 +1,115 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'job_torque_submit_test.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 assert_status( torque_driver_type * driver, torque_job_type * job, int status_mask) { + int torque_status = torque_driver_get_job_status(driver, job); + if ((torque_status & status_mask) == 0) + test_exit("Incorrect status:%d - expected overlap with: %d\n" , torque_status , status_mask); +} + + + +void test_submit(torque_driver_type * driver, const char * cmd) { + char * run_path = util_alloc_cwd(); + torque_job_type * job = (torque_job_type *) torque_driver_submit_job(driver, cmd, 1, run_path, "TEST-TORQUE-SUBMIT", 0, NULL); + + if (job != NULL) { + assert_status( driver , job, JOB_QUEUE_RUNNING + JOB_QUEUE_PENDING); + torque_driver_kill_job(driver, job); + printf("Waiting 2 seconds"); + for (int i = 0; i < 2; i++) { + printf("."); + fflush(stdout); + sleep(1); + } + printf("\n"); + + int torque_status = torque_driver_get_job_status(driver, job); + if (torque_status != JOB_QUEUE_EXIT && torque_status != JOB_QUEUE_DONE) { + exit(1); + test_exit("After kill of job, the status is %d, it should have been JOB_QUEUE_EXIT, which is %d\n", torque_status, JOB_QUEUE_EXIT); + } + } else { + exit(1); + test_exit("Function %s returned null-pointer to job, terminating test.", "torque_driver_submit_job"); + } + + free(run_path); + torque_driver_free_job(job); +} + +void test_submit_nocommand(torque_driver_type * driver) { + test_submit(driver, NULL); +} + + + + +void test_submit_failed_qstat(torque_driver_type * driver, const char * cmd) { + char * run_path = util_alloc_cwd(); + torque_job_type * job = (torque_job_type *) torque_driver_submit_job(driver, cmd, 1, run_path, "TEST-TORQUE-SUBMIT", 0, NULL); + + { + ecl::util::TestArea ta("torque"); + ta.copy_file( (const char *) torque_driver_get_option( driver , TORQUE_QSTAT_CMD )); + assert_status( driver , job , JOB_QUEUE_RUNNING + JOB_QUEUE_PENDING); + + { + char * qstat_cmd = util_alloc_abs_path( "qstat.local" ); + FILE * stream = util_fopen( qstat_cmd , "w"); + fprintf(stream , "#!/bin/sh\n"); + fprintf(stream , "echo XYZ - Error\n"); + fclose( stream ); + util_addmode_if_owner(qstat_cmd, S_IXUSR ); + torque_driver_set_option(driver, TORQUE_QSTAT_CMD, qstat_cmd); + free( qstat_cmd ); + } + + assert_status( driver , job , JOB_QUEUE_STATUS_FAILURE ); + } + + torque_driver_free_job(job); + free(run_path); +} + + + +int main(int argc, char ** argv) { + torque_driver_type * driver = (torque_driver_type *) torque_driver_alloc(); + if (argc == 1) { + test_submit_nocommand(driver); + } else if (argc == 2) { + test_submit(driver, argv[1]); + test_submit_failed_qstat( driver , argv[1] ); + } else { + printf("Only accepts zero or one arguments (the job script to run)\n"); + exit(1); + } + printf("Submit, status and kill OK\n"); + torque_driver_free(driver); + + exit(0); +} diff --git a/libres/lib/job_queue/tests/job_torque_test.cpp b/libres/lib/job_queue/tests/job_torque_test.cpp new file mode 100644 index 00000000000..b82cd56e6a6 --- /dev/null +++ b/libres/lib/job_queue/tests/job_torque_test.cpp @@ -0,0 +1,169 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'job_lsf_test.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 + +void test_option(torque_driver_type * driver, const char * option, const char * value) { + test_assert_true(torque_driver_set_option(driver, option, value)); + test_assert_string_equal((const char *) torque_driver_get_option(driver, option), value); +} + +void setoption_setalloptions_optionsset() { + torque_driver_type * driver = (torque_driver_type *) torque_driver_alloc(); + + test_option(driver, TORQUE_QSUB_CMD, "XYZaaa"); + test_option(driver, TORQUE_QSTAT_CMD, "xyZfff"); + test_option(driver, TORQUE_QDEL_CMD, "ZZyfff"); + test_option(driver, TORQUE_QUEUE, "superhigh"); + test_option(driver, TORQUE_NUM_CPUS_PER_NODE, "42"); + test_option(driver, TORQUE_NUM_NODES, "36"); + test_option(driver, TORQUE_KEEP_QSUB_OUTPUT, "1"); + test_option(driver, TORQUE_KEEP_QSUB_OUTPUT, "0"); + test_option(driver, TORQUE_CLUSTER_LABEL, "thecluster"); + test_option(driver, TORQUE_JOB_PREFIX_KEY, "coolJob"); + + test_assert_int_equal( 0 , torque_driver_get_submit_sleep(driver)); + test_assert_NULL( torque_driver_get_debug_stream(driver) ); + + test_assert_true( torque_driver_set_option( driver , TORQUE_SUBMIT_SLEEP , "0.25")); + test_assert_int_equal( 250000 , torque_driver_get_submit_sleep(driver)); + + char tmp_path[] = "/tmp/torque_debug_XXXXXX"; + // We do not strictly need the file, we are only interested in a path name + // tmpnam is however deprecated in favor of mkstemp, and no good substitute + // for tmpnam (with similar functionality) was found. + int fd = mkstemp(tmp_path); + + if (fd == -1) { + printf("Unable to create dummy log file"); + exit(1); + } + + close(fd); + unlink(tmp_path); + + test_assert_true( torque_driver_set_option( driver , TORQUE_DEBUG_OUTPUT , tmp_path)); + test_assert_not_NULL( torque_driver_get_debug_stream(driver) ); + + test_option(driver, TORQUE_QSUB_CMD , NULL); + test_option(driver, TORQUE_QSTAT_CMD , NULL); + test_option(driver, TORQUE_QDEL_CMD , NULL); + test_option(driver, TORQUE_QUEUE , NULL); + test_option(driver, TORQUE_CLUSTER_LABEL , NULL); + test_option(driver, TORQUE_JOB_PREFIX_KEY, NULL); + + // Setting NULL to numerical options should leave the value unchanged + torque_driver_set_option(driver, TORQUE_NUM_CPUS_PER_NODE, NULL); + torque_driver_set_option(driver, TORQUE_NUM_NODES , NULL); + torque_driver_set_option(driver, TORQUE_KEEP_QSUB_OUTPUT , NULL); + test_assert_string_equal( (const char *) torque_driver_get_option( driver, TORQUE_NUM_CPUS_PER_NODE ), "42"); + test_assert_string_equal( (const char *) torque_driver_get_option( driver, TORQUE_NUM_NODES ), "36"); + test_assert_string_equal( (const char *) torque_driver_get_option( driver, TORQUE_KEEP_QSUB_OUTPUT ), "0"); + + torque_driver_free(driver); +} + +void setoption_set_typed_options_wrong_format_returns_false() { + torque_driver_type * driver = (torque_driver_type *) torque_driver_alloc(); + test_assert_false(torque_driver_set_option(driver, TORQUE_NUM_CPUS_PER_NODE, "42.2")); + test_assert_false(torque_driver_set_option(driver, TORQUE_NUM_CPUS_PER_NODE, "fire")); + test_assert_false(torque_driver_set_option(driver, TORQUE_NUM_NODES, "42.2")); + test_assert_false(torque_driver_set_option(driver, TORQUE_NUM_NODES, "fire")); + test_assert_true(torque_driver_set_option(driver, TORQUE_KEEP_QSUB_OUTPUT, "true")); + test_assert_true(torque_driver_set_option(driver, TORQUE_KEEP_QSUB_OUTPUT, "1")); + test_assert_false(torque_driver_set_option(driver, TORQUE_KEEP_QSUB_OUTPUT, "ja")); + test_assert_false(torque_driver_set_option(driver, TORQUE_KEEP_QSUB_OUTPUT, "22")); + test_assert_false(torque_driver_set_option(driver, TORQUE_KEEP_QSUB_OUTPUT, "1.1")); + test_assert_false(torque_driver_set_option(driver, TORQUE_SUBMIT_SLEEP, "X45")); +} + +void getoption_nooptionsset_defaultoptionsreturned() { + torque_driver_type * driver = (torque_driver_type *) torque_driver_alloc(); + test_assert_string_equal((const char *) torque_driver_get_option(driver, TORQUE_QSUB_CMD), TORQUE_DEFAULT_QSUB_CMD); + test_assert_string_equal((const char *) torque_driver_get_option(driver, TORQUE_QSTAT_CMD), TORQUE_DEFAULT_QSTAT_CMD); + test_assert_string_equal((const char *) torque_driver_get_option(driver, TORQUE_QDEL_CMD), TORQUE_DEFAULT_QDEL_CMD); + test_assert_string_equal((const char *) torque_driver_get_option(driver, TORQUE_KEEP_QSUB_OUTPUT), "0"); + test_assert_string_equal((const char *) torque_driver_get_option(driver, TORQUE_NUM_CPUS_PER_NODE), "1"); + test_assert_string_equal((const char *) torque_driver_get_option(driver, TORQUE_NUM_NODES), "1"); + test_assert_string_equal((const char *) torque_driver_get_option(driver, TORQUE_CLUSTER_LABEL), NULL ); + test_assert_string_equal((const char *) torque_driver_get_option(driver, TORQUE_JOB_PREFIX_KEY), NULL); + + printf("Default options OK\n"); + torque_driver_free(driver); +} + +void create_submit_script_script_according_to_input() { + ecl::util::TestArea ta("submit_script"); + const char * script_filename = "qsub_script.sh"; + + { + const char ** args = (const char **) util_calloc(2, sizeof * args); + args[0] = "/tmp/jaja/"; + args[1] = "number2arg"; + torque_job_create_submit_script(script_filename, "job_program.py", 2, args); + free( args ); + } + + { + FILE* file_stream = util_fopen(script_filename, "r"); + bool at_eof = false; + + char * line = util_fscanf_alloc_line(file_stream, &at_eof); + test_assert_string_equal("#!/bin/sh", line); + free(line); + + line = util_fscanf_alloc_line(file_stream, &at_eof); + test_assert_string_equal("job_program.py /tmp/jaja/ number2arg", line); + free(line); + + line = util_fscanf_alloc_line(file_stream, &at_eof); + free(line); + test_assert_true(at_eof); + + fclose(file_stream); + } +} + + + +void test_parse_invalid( ) { + test_assert_int_equal( torque_driver_parse_status( "/file/does/not/exist" , NULL) , JOB_QUEUE_STATUS_FAILURE); + { + ecl::util::TestArea ta("submit"); + { + FILE * stream = util_fopen("qstat.stdout", "w"); + fclose( stream ); + } + test_assert_int_equal( torque_driver_parse_status( "qstat.stdout" , "a2345") , JOB_QUEUE_STATUS_FAILURE); + } +} + + +int main(int argc, char ** argv) { + getoption_nooptionsset_defaultoptionsreturned(); + setoption_setalloptions_optionsset(); + + setoption_set_typed_options_wrong_format_returns_false(); + create_submit_script_script_according_to_input(); + test_parse_invalid( ); + exit(0); +} diff --git a/libres/lib/job_queue/tests/job_workflow_test.cpp b/libres/lib/job_queue/tests/job_workflow_test.cpp new file mode 100644 index 00000000000..e979468845d --- /dev/null +++ b/libres/lib/job_queue/tests/job_workflow_test.cpp @@ -0,0 +1,185 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'job_workflow_test.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 + + +void create_workflow( const char * workflow_file , const char * tmp_file , int value) { + FILE * stream = util_fopen( workflow_file , "w"); + fprintf(stream , "CREATE_FILE %s %d\n" , tmp_file , value); + fprintf(stream , "READ_FILE %s\n" , tmp_file ); + fclose( stream ); + + printf("Have created:%s \n",workflow_file ); +} + + +void create_error_workflow( const char * workflow_file , const char * tmp_file , int value) { + FILE * stream = util_fopen( workflow_file , "w"); + fprintf(stream , "CREATE_FILE %s %d\n" , tmp_file , value); + fprintf(stream , "XREAD_FILE %s\n" , tmp_file ); + fclose( stream ); + + printf("Have created:%s \n",workflow_file ); +} + + +extern "C" { + // This symbol will be dlsym'd. + + void * read_file( void * self , const stringlist_type * args) { + printf("Running read_file \n"); + int * value = (int *) self; + FILE * stream = util_fopen(stringlist_iget(args , 0 ) , "r"); + int read_count = fscanf(stream , "%d" , value ); + fclose( stream ); + if (read_count == 1) { + int * return_value = (int *) util_malloc( sizeof * return_value ); + return_value[0] = value[0]; + + return return_value; + } else + return NULL; + } + +} + +static void create_exjob( const char * workflow , const char * bin_path) +{ + FILE * stream = util_fopen( workflow , "w"); + fprintf(stream , "EXECUTABLE \"%s/create_file\"\n" , bin_path); + fprintf(stream , "ARG_TYPE 1 INT\n"); + fprintf(stream , "MIN_ARG 2\n"); + fprintf(stream , "MAX_ARG 2\n"); + fclose(stream); +} + + +void test_has_job(const char * job) { + workflow_joblist_type * joblist = (workflow_joblist_type *) workflow_joblist_alloc(); + + test_assert_false( workflow_joblist_has_job( joblist , "NoNotThis")); + test_assert_true( workflow_joblist_add_job_from_file( joblist , "CREATE_FILE" , job) ); + test_assert_true( workflow_joblist_has_job( joblist , "CREATE_FILE")); + + workflow_joblist_free( joblist ); +} + + +int main( int argc , char ** argv) { + const char * exjob_file = "job"; + const char * bin_path = argv[1]; + const char * internal_workflow = argv[2]; + ecl::util::TestArea ta("workflo_test"); + + signal(SIGSEGV , util_abort_signal); + create_exjob( exjob_file , bin_path ); + test_has_job( exjob_file ); + { + + int int_value = rand(); + int read_value = 100; + workflow_joblist_type * joblist = (workflow_joblist_type *) workflow_joblist_alloc(); + + if (!workflow_joblist_add_job_from_file( joblist , "CREATE_FILE" , exjob_file)) { + remove( exjob_file ); + test_error_exit("Loading job CREATE_FILE failed\n"); + } else + remove( exjob_file ); + + if (!workflow_joblist_add_job_from_file( joblist , "READ_FILE" , internal_workflow)) + test_error_exit("Loading job READ_FILE failed\n"); + + { + config_parser_type * workflow_compiler = workflow_joblist_get_compiler( joblist ); + if (config_get_schema_size( workflow_compiler ) != 2) + test_error_exit("Config compiler - wrong size \n"); + } + + + { + const char * workflow_file = "workflow"; + const char * tmp_file = "fileX"; + workflow_type * workflow; + + create_workflow( workflow_file , tmp_file , int_value ); + workflow = workflow_alloc(workflow_file , joblist ); + unlink( workflow_file ); + + { + bool runOK; + runOK = workflow_run( workflow , &read_value , false , NULL); + if (runOK) { + if (int_value != read_value) + test_error_exit("Wrong numeric value read back \n"); + + test_assert_int_equal( workflow_get_stack_size( workflow ) , 2 ); + test_assert_not_NULL( workflow_iget_stack_ptr( workflow , 0 ) ); + test_assert_NULL( workflow_iget_stack_ptr( workflow , 1 ) ); + + { + void * return_value = workflow_iget_stack_ptr( workflow , 0 ); + int return_int = *((int *) return_value); + if (int_value != return_int) + test_error_exit("Wrong numeric value read back \n"); + + test_assert_not_NULL( workflow_pop_stack( workflow )); + test_assert_NULL( workflow_pop_stack( workflow )); + test_assert_int_equal( workflow_get_stack_size( workflow ) , 0 ); + + free( return_value ); + + } + } else { + unlink( tmp_file ); + test_error_exit("Workflow did not run\n"); + } + unlink( tmp_file ); + } + } + workflow_joblist_free( joblist ); + + } + { + workflow_joblist_type * joblist = workflow_joblist_alloc(); + const char * workflow_file = "workflow"; + const char * tmp_file = "fileX"; + int read_value; + int int_value = 100; + workflow_type * workflow; + + create_workflow( workflow_file , tmp_file , int_value ); + workflow = workflow_alloc(workflow_file , joblist ); + unlink( workflow_file ); + test_assert_false( workflow_run( workflow , &read_value , false , NULL) ); + test_assert_int_equal( workflow_get_stack_size( workflow ) , 0 ); + } + exit(0); +} diff --git a/libres/lib/job_queue/torque_driver.cpp b/libres/lib/job_queue/torque_driver.cpp new file mode 100644 index 00000000000..181602abd23 --- /dev/null +++ b/libres/lib/job_queue/torque_driver.cpp @@ -0,0 +1,587 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'torque_driver.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 + + +#define TORQUE_DRIVER_TYPE_ID 34873653 +#define TORQUE_JOB_TYPE_ID 12312312 + + +struct torque_driver_struct { + UTIL_TYPE_ID_DECLARATION; + char * queue_name; + char * qsub_cmd; + char * qstat_cmd; + char * qdel_cmd; + char * num_cpus_per_node_char; + char * job_prefix; + char * num_nodes_char; + bool keep_qsub_output; + int num_cpus_per_node; + int num_nodes; + char * cluster_label; + int submit_sleep; + FILE * debug_stream; +}; + +struct torque_job_struct { + UTIL_TYPE_ID_DECLARATION; + long int torque_jobnr; + char * torque_jobnr_char; +}; + +UTIL_SAFE_CAST_FUNCTION(torque_driver, TORQUE_DRIVER_TYPE_ID); + +static UTIL_SAFE_CAST_FUNCTION_CONST(torque_driver, TORQUE_DRIVER_TYPE_ID) +static UTIL_SAFE_CAST_FUNCTION(torque_job, TORQUE_JOB_TYPE_ID) + +void * torque_driver_alloc() { + torque_driver_type * torque_driver = (torque_driver_type*)util_malloc(sizeof * torque_driver); + UTIL_TYPE_ID_INIT(torque_driver, TORQUE_DRIVER_TYPE_ID); + + torque_driver->queue_name = NULL; + torque_driver->qsub_cmd = NULL; + torque_driver->qstat_cmd = NULL; + torque_driver->qdel_cmd = NULL; + torque_driver->num_cpus_per_node_char = NULL; + torque_driver->num_nodes_char = NULL; + torque_driver->keep_qsub_output = false; + torque_driver->num_cpus_per_node = 1; + torque_driver->num_nodes = 1; + torque_driver->cluster_label = NULL; + torque_driver->job_prefix = NULL; + torque_driver->debug_stream = NULL; + + torque_driver_set_option(torque_driver, TORQUE_QSUB_CMD, TORQUE_DEFAULT_QSUB_CMD); + torque_driver_set_option(torque_driver, TORQUE_QSTAT_CMD, TORQUE_DEFAULT_QSTAT_CMD); + torque_driver_set_option(torque_driver, TORQUE_QDEL_CMD, TORQUE_DEFAULT_QDEL_CMD); + torque_driver_set_option(torque_driver, TORQUE_NUM_CPUS_PER_NODE, "1"); + torque_driver_set_option(torque_driver, TORQUE_NUM_NODES, "1"); + torque_driver_set_option(torque_driver, TORQUE_SUBMIT_SLEEP, TORQUE_DEFAULT_SUBMIT_SLEEP); + + return torque_driver; +} + +static void torque_driver_set_debug_output(torque_driver_type * driver, const char * debug_file) { + if (driver->debug_stream) + fclose( driver->debug_stream ); + + if (debug_file) + driver->debug_stream = util_mkdir_fopen( debug_file , "w"); + else + driver->debug_stream = NULL; +} + + +static void torque_driver_set_qsub_cmd(torque_driver_type * driver, const char * qsub_cmd) { + driver->qsub_cmd = util_realloc_string_copy(driver->qsub_cmd, qsub_cmd); +} + +static void torque_driver_set_qstat_cmd(torque_driver_type * driver, const char * qstat_cmd) { + driver->qstat_cmd = util_realloc_string_copy(driver->qstat_cmd, qstat_cmd); +} + +static void torque_driver_set_qdel_cmd(torque_driver_type * driver, const char * qdel_cmd) { + driver->qdel_cmd = util_realloc_string_copy(driver->qdel_cmd, qdel_cmd); +} + +static void torque_driver_set_queue_name(torque_driver_type * driver, const char * queue_name) { + driver->queue_name = util_realloc_string_copy(driver->queue_name, queue_name); +} + +static bool torque_driver_set_submit_sleep(torque_driver_type * driver, const char* submit_sleep) { + double seconds_sleep; + if (util_sscanf_double( submit_sleep , &seconds_sleep)) { + driver->submit_sleep = (int) (seconds_sleep * 1000000); + return true; + } else + return false; +} + + +static bool torque_driver_set_num_nodes(torque_driver_type * driver, const char* num_nodes_char) { + int num_nodes = 0; + if (util_sscanf_int(num_nodes_char, &num_nodes)) { + driver->num_nodes = num_nodes; + driver->num_nodes_char = util_realloc_string_copy(driver->num_nodes_char, num_nodes_char); + return true; + } else { + return false; + } +} + +static bool torque_driver_set_keep_qsub_output(torque_driver_type * driver, const char* keep_output_bool_as_char) { + bool keep_output_parsed; + + if (util_sscanf_bool(keep_output_bool_as_char, &keep_output_parsed)) { + driver->keep_qsub_output = keep_output_parsed; + return true; + } else { + return false; + } +} + +static void torque_driver_set_job_prefix(torque_driver_type * driver, const char * job_prefix){ + driver->job_prefix = util_realloc_string_copy( driver->job_prefix , job_prefix); +} + +static void torque_driver_set_cluster_label(torque_driver_type * driver, const char* cluster_label) { + driver->cluster_label = util_realloc_string_copy(driver->cluster_label, cluster_label); +} + +static bool torque_driver_set_num_cpus_per_node(torque_driver_type * driver, const char* num_cpus_per_node_char) { + int num_cpus_per_node = 0; + if (util_sscanf_int(num_cpus_per_node_char, &num_cpus_per_node)) { + driver->num_cpus_per_node = num_cpus_per_node; + driver->num_cpus_per_node_char = util_realloc_string_copy(driver->num_cpus_per_node_char, num_cpus_per_node_char); + return true; + } else { + return false; + } +} + +bool torque_driver_set_option(void * __driver, const char * option_key, const void * value_) { + const char * value = (const char*)value_; + torque_driver_type * driver = torque_driver_safe_cast(__driver); + bool option_set = true; + { + if (strcmp(TORQUE_QSUB_CMD, option_key) == 0) + torque_driver_set_qsub_cmd(driver, value); + else if (strcmp(TORQUE_QSTAT_CMD, option_key) == 0) + torque_driver_set_qstat_cmd(driver, value); + else if (strcmp(TORQUE_QDEL_CMD, option_key) == 0) + torque_driver_set_qdel_cmd(driver, value); + else if (strcmp(TORQUE_QUEUE, option_key) == 0) + torque_driver_set_queue_name(driver, value); + else if (strcmp(TORQUE_NUM_CPUS_PER_NODE, option_key) == 0) + option_set = torque_driver_set_num_cpus_per_node(driver, value); + else if (strcmp(TORQUE_NUM_NODES, option_key) == 0) + option_set = torque_driver_set_num_nodes(driver, value); + else if (strcmp(TORQUE_KEEP_QSUB_OUTPUT, option_key) == 0) + option_set = torque_driver_set_keep_qsub_output(driver, value); + else if (strcmp(TORQUE_CLUSTER_LABEL, option_key) == 0) + torque_driver_set_cluster_label(driver, value); + else if (strcmp(TORQUE_JOB_PREFIX_KEY, option_key) == 0) + torque_driver_set_job_prefix(driver, value); + else if (strcmp(TORQUE_DEBUG_OUTPUT, option_key) == 0) + torque_driver_set_debug_output(driver, value); + else if (strcmp(TORQUE_SUBMIT_SLEEP, option_key) == 0) + option_set = torque_driver_set_submit_sleep(driver, value); + else + option_set = false; + } + return option_set; +} + +const void * torque_driver_get_option(const void * __driver, const char * option_key) { + const torque_driver_type * driver = torque_driver_safe_cast_const(__driver); + { + if (strcmp(TORQUE_QSUB_CMD, option_key) == 0) + return driver->qsub_cmd; + else if (strcmp(TORQUE_QSTAT_CMD, option_key) == 0) + return driver->qstat_cmd; + else if (strcmp(TORQUE_QDEL_CMD, option_key) == 0) + return driver->qdel_cmd; + else if (strcmp(TORQUE_QUEUE, option_key) == 0) + return driver->queue_name; + else if (strcmp(TORQUE_NUM_CPUS_PER_NODE, option_key) == 0) + return driver->num_cpus_per_node_char; + else if (strcmp(TORQUE_NUM_NODES, option_key) == 0) + return driver->num_nodes_char; + else if (strcmp(TORQUE_KEEP_QSUB_OUTPUT, option_key) == 0) + return driver->keep_qsub_output ? "1" : "0"; + else if (strcmp(TORQUE_CLUSTER_LABEL, option_key) == 0) + return driver->cluster_label; + else if(strcmp(TORQUE_JOB_PREFIX_KEY, option_key) == 0) + return driver->job_prefix; + else { + util_abort("%s: option_id:%s not recognized for TORQUE driver \n", __func__, option_key); + return NULL; + } + } +} + +void torque_driver_init_option_list(stringlist_type * option_list) { + stringlist_append_copy(option_list, TORQUE_QSUB_CMD); + stringlist_append_copy(option_list, TORQUE_QSTAT_CMD); + stringlist_append_copy(option_list, TORQUE_QDEL_CMD); + stringlist_append_copy(option_list, TORQUE_QUEUE); + stringlist_append_copy(option_list, TORQUE_NUM_CPUS_PER_NODE); + stringlist_append_copy(option_list, TORQUE_NUM_NODES); + stringlist_append_copy(option_list, TORQUE_KEEP_QSUB_OUTPUT); + stringlist_append_copy(option_list, TORQUE_CLUSTER_LABEL); + stringlist_append_copy(option_list, TORQUE_JOB_PREFIX_KEY); +} + +torque_job_type * torque_job_alloc() { + torque_job_type * job; + job = (torque_job_type*)util_malloc(sizeof * job); + job->torque_jobnr_char = NULL; + job->torque_jobnr = 0; + UTIL_TYPE_ID_INIT(job, TORQUE_JOB_TYPE_ID); + + return job; +} + +stringlist_type * torque_driver_alloc_cmd(torque_driver_type * driver, + const char * job_name, + const char * submit_script) { + + + stringlist_type * argv = stringlist_alloc_new(); + + if (driver->keep_qsub_output) { + stringlist_append_copy(argv, "-k"); + stringlist_append_copy(argv, "oe"); + } + + { + char * resource_string; + if (driver->cluster_label) + resource_string = util_alloc_sprintf("nodes=%d:%s:ppn=%d", driver->num_nodes, driver->cluster_label, driver->num_cpus_per_node); + else + resource_string = util_alloc_sprintf("nodes=%d:ppn=%d", driver->num_nodes, driver->num_cpus_per_node); + + stringlist_append_copy(argv, "-l"); + stringlist_append_copy(argv, resource_string); + free(resource_string); + } + + if (driver->queue_name != NULL) { + stringlist_append_copy(argv, "-q"); + stringlist_append_copy(argv, driver->queue_name); + } + + if (job_name != NULL) { + stringlist_append_copy(argv, "-N"); + stringlist_append_copy(argv, job_name); + } + + stringlist_append_copy(argv, submit_script); + + return argv; +} + +static void torque_debug(const torque_driver_type * driver , const char * fmt , ...) { + if (driver->debug_stream) { + { + va_list ap; + va_start(ap , fmt); + vfprintf(driver->debug_stream , fmt , ap ); + va_end(ap); + } + fprintf(driver->debug_stream , "\n"); + fsync( fileno(driver->debug_stream) ); + } +} + + +static int torque_job_parse_qsub_stdout(const torque_driver_type * driver, const char * stdout_file) { + int jobid; + { + FILE * stream = util_fopen(stdout_file, "r"); + char * jobid_string = util_fscanf_alloc_upto(stream, ".", false); + + torque_debug(driver, "Torque job ID string: '%s'", jobid_string); + + if (jobid_string == NULL || !util_sscanf_int(jobid_string, &jobid)) { + + char * file_content = util_fread_alloc_file_content(stdout_file, NULL); + fprintf(stderr, "Failed to get torque job id from file: %s \n", stdout_file); + fprintf(stderr, "qsub command : %s \n", driver->qsub_cmd); + fprintf(stderr, "File content: [%s]\n", file_content); + free(file_content); + util_exit("%s: \n", __func__); + } + free(jobid_string); + fclose(stream); + } + return jobid; +} + +void torque_job_create_submit_script(const char * script_filename, const char * submit_cmd, int argc, const char ** job_argv) { + if (submit_cmd == NULL) { + util_abort("%s: cannot create submit script, because there is no executing commmand specified.", __func__); + } + + FILE* script_file = util_fopen(script_filename, "w"); + fprintf(script_file, "#!/bin/sh\n"); + + fprintf(script_file, "%s", submit_cmd); + for (int i = 0; i < argc; i++) { + fprintf(script_file, " %s", job_argv[i]); + } + + fclose(script_file); +} + +static void torque_debug_spawn_status_info(torque_driver_type *driver, int status) { + if (WIFEXITED((status))) { + torque_debug(driver, "Torque spawn exited with status=%d", WEXITSTATUS((status))); + } else if (WIFSIGNALED((status))) { + torque_debug(driver, "Torque spawn killed by signal %d", WTERMSIG((status))); + } else if (WIFSTOPPED((status))) { + torque_debug(driver, "Torque spawn stopped by signal %d", WSTOPSIG((status))); + } else if (WIFCONTINUED((status))) { + torque_debug(driver, "Torque spawn continued"); + } else { + torque_debug(driver, "Torque spawn failed with unknown status code: %d", (status)); + } +} + +static int torque_driver_submit_shell_job(torque_driver_type * driver, + const char * run_path, + const char * job_name, + const char * submit_cmd, + int num_cpu, + int job_argc, + const char ** job_argv) { + + usleep( driver->submit_sleep ); + { + int job_id; + char * tmp_std_file = (char*)util_alloc_tmp_file("/tmp", "enkf-submit-std", true); + char * tmp_err_file = (char*)util_alloc_tmp_file("/tmp", "enkf-submit-err", true); + char * script_filename = (char*)util_alloc_filename(run_path, "qsub_script", "sh"); + + torque_debug(driver, "Setting up submit stdout target '%s' for '%s'", tmp_std_file, script_filename); + torque_debug(driver, "Setting up submit stderr target '%s' for '%s'", tmp_err_file, script_filename); + torque_job_create_submit_script(script_filename, submit_cmd, job_argc, job_argv); + { + int p_units_from_driver = driver->num_cpus_per_node * driver->num_nodes; + if (num_cpu > p_units_from_driver) { + util_abort("%s: Error in config, job's config requires %d processing units, but config says %s: %d, and %s: %d, which multiplied becomes: %d \n", + __func__, num_cpu, TORQUE_NUM_CPUS_PER_NODE, driver->num_cpus_per_node, TORQUE_NUM_NODES, driver->num_nodes, p_units_from_driver); + } + { + stringlist_type * remote_argv = torque_driver_alloc_cmd(driver, job_name, script_filename); + torque_debug(driver, "Submit arguments: %s", stringlist_alloc_joined_string(remote_argv, " ")); + char ** argv = stringlist_alloc_char_ref(remote_argv); + int status = util_spawn_blocking(driver->qsub_cmd, stringlist_get_size(remote_argv), (const char **) argv, tmp_std_file, tmp_err_file); + if (status != 0) { + torque_debug_spawn_status_info(driver, status); + } + free(argv); + stringlist_free(remote_argv); + } + } + + job_id = torque_job_parse_qsub_stdout(driver, tmp_std_file); + + util_unlink_existing(tmp_std_file); + util_unlink_existing(tmp_err_file); + free(tmp_std_file); + free(tmp_err_file); + + return job_id; + } +} + +void torque_job_free(torque_job_type * job) { + + free(job->torque_jobnr_char); + free(job); +} + +void torque_driver_free_job(void * __job) { + + torque_job_type * job = torque_job_safe_cast(__job); + torque_job_free(job); +} + +void * torque_driver_submit_job(void * __driver, + const char * submit_cmd, + int num_cpu, + const char * run_path, + const char * job_name, + int argc, + const char ** argv) { + torque_driver_type * driver = torque_driver_safe_cast(__driver); + torque_job_type * job = torque_job_alloc(); + + torque_debug( driver , "Submitting job in:%s" , run_path); + { + char * local_job_name = NULL; + if (driver->job_prefix) + local_job_name = util_alloc_sprintf("%s%s",driver->job_prefix, job_name); + else + local_job_name = util_alloc_string_copy( job_name ); + + job->torque_jobnr = torque_driver_submit_shell_job(driver, run_path, local_job_name, submit_cmd, num_cpu, argc, argv); + job->torque_jobnr_char = util_alloc_sprintf("%ld", job->torque_jobnr); + + torque_debug( driver , "Job:%s Id:%d" , run_path , job->torque_jobnr); + free(local_job_name); + } + + if (job->torque_jobnr > 0) + return job; + else { + /* + The submit failed - the queue system shall handle + NULL return values. + */ + torque_job_free(job); + return NULL; + } +} + +/** + Will return NULL if "something" fails; that again will be + translated to JOB_QUEUE_STATUS_FAILURE - which the queue layer will + just interpret as "No change in status". Possible failures are: + + 1. The file capturing stdout is not created. + 2. Can not extract the correct status string from the stdout file. + +*/ + +static job_status_type torque_driver_get_qstat_status(torque_driver_type * driver, const char * jobnr_char) { + char * tmp_file = (char*)util_alloc_tmp_file("/tmp", "enkf-qstat", true); + job_status_type status = JOB_QUEUE_STATUS_FAILURE; + + { + const char ** argv = (const char**)util_calloc(1, sizeof * argv); + argv[0] = jobnr_char; + + util_spawn_blocking(driver->qstat_cmd, 1, (const char **) argv, tmp_file, NULL); + free(argv); + } + + if (util_file_exists( tmp_file )) { + status = torque_driver_parse_status( tmp_file , jobnr_char); + unlink(tmp_file); + } else + fprintf(stderr, "No such file: %s - reading qstat status failed \n", tmp_file ); + + free(tmp_file); + + return status; +} + +job_status_type torque_driver_parse_status(const char * qstat_file, const char * jobnr_char) { + job_status_type status = JOB_QUEUE_STATUS_FAILURE; + + if (util_file_exists(qstat_file)) { + char * line = NULL; + { + FILE *stream = util_fopen(qstat_file, "r"); + bool at_eof = false; + util_fskip_lines(stream, 2); + line = util_fscanf_alloc_line(stream, &at_eof); + fclose(stream); + } + + if (line) { + char job_id_full_string[32]; + char string_status[2]; + + if (sscanf(line, "%s %*s %*s %*s %s %*s", job_id_full_string, string_status) == 2) { + const char *dotPtr = strchr(job_id_full_string, '.'); + int dotPosition = dotPtr - job_id_full_string; + + { + char* job_id_as_char_ptr = util_alloc_substring_copy(job_id_full_string, 0, dotPosition); + if (util_string_equal(job_id_as_char_ptr, jobnr_char)) { + + switch( string_status[0] ) { + case 'R': + status = JOB_QUEUE_RUNNING; + break; + + case 'E': + status = JOB_QUEUE_DONE; + break; + + case 'C': + status = JOB_QUEUE_DONE; + break; + + case 'H': + status = JOB_QUEUE_PENDING; + break; + case 'Q': + status = JOB_QUEUE_PENDING; + break; + + default: + break; + } + + free(job_id_as_char_ptr); + } + } + } + free(line); + } + } + if (status == JOB_QUEUE_STATUS_FAILURE) + fprintf(stderr,"** Warning: failed to get job status for job:%s from file:%s\n",jobnr_char , qstat_file ); + + return status; +} + + + +job_status_type torque_driver_get_job_status(void * __driver, void * __job) { + torque_driver_type * driver = torque_driver_safe_cast(__driver); + torque_job_type * job = torque_job_safe_cast(__job); + return torque_driver_get_qstat_status(driver, job->torque_jobnr_char); +} + + +void torque_driver_kill_job(void * __driver, void * __job) { + + torque_driver_type * driver = torque_driver_safe_cast(__driver); + torque_job_type * job = torque_job_safe_cast(__job); + util_spawn_blocking(driver->qdel_cmd, 1, (const char **) &job->torque_jobnr_char, NULL, NULL); +} + +void torque_driver_free(torque_driver_type * driver) { + torque_driver_set_debug_output(driver, NULL); + free(driver->queue_name); + free(driver->qdel_cmd); + free(driver->qstat_cmd); + free(driver->qsub_cmd); + free(driver->num_cpus_per_node_char); + free(driver->num_nodes_char); + if (driver->job_prefix) + free(driver->job_prefix); + + free(driver); +} + +void torque_driver_free__(void * __driver) { + torque_driver_type * driver = torque_driver_safe_cast(__driver); + torque_driver_free(driver); +} + +int torque_driver_get_submit_sleep( const torque_driver_type * driver ) { + return driver->submit_sleep; +} + +FILE * torque_driver_get_debug_stream( const torque_driver_type * driver ) { + return driver->debug_stream; +} diff --git a/libres/lib/job_queue/workflow.cpp b/libres/lib/job_queue/workflow.cpp new file mode 100644 index 00000000000..bfd1719d9b9 --- /dev/null +++ b/libres/lib/job_queue/workflow.cpp @@ -0,0 +1,270 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'workflow.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 + +#define CMD_TYPE_ID 66153 +#define WORKFLOW_TYPE_ID 6762081 +#define WORKFLOW_COMMENT_STRING "--" +#define WORKFLOW_INCLUDE "INCLUDE" + +typedef struct cmd_struct cmd_type; + +struct cmd_struct { + UTIL_TYPE_ID_DECLARATION; + const workflow_job_type * workflow_job; + stringlist_type * arglist; +}; + + + + +struct workflow_struct { + UTIL_TYPE_ID_DECLARATION; + time_t compile_time; + bool compiled; + char * src_file; + vector_type * cmd_list; + workflow_joblist_type * joblist; + config_error_type * last_error; + vector_type * stack; +}; + +/*****************************************************************/ + + +static cmd_type * cmd_alloc( const workflow_job_type * workflow_job , const stringlist_type * arglist) { + cmd_type * cmd = (cmd_type*)util_malloc( sizeof * cmd ); + UTIL_TYPE_ID_INIT(cmd , CMD_TYPE_ID ); + cmd->workflow_job = workflow_job; + cmd->arglist = stringlist_alloc_deep_copy( arglist ); + return cmd; +} + +static UTIL_SAFE_CAST_FUNCTION( cmd , CMD_TYPE_ID ); + +static void cmd_free( cmd_type * cmd ){ + stringlist_free( cmd->arglist ); + free( cmd ); +} + +static void cmd_free__( void * arg ) { + cmd_type * cmd = cmd_safe_cast( arg ); + cmd_free( cmd ); +} + +/*****************************************************************/ + +static void workflow_add_cmd( workflow_type * workflow , cmd_type * cmd ) { + vector_append_owned_ref( workflow->cmd_list , cmd , cmd_free__ ); +} + + +static void workflow_clear( workflow_type * workflow ) { + vector_clear( workflow->cmd_list ); +} + +static void workflow_store_error( workflow_type * workflow , const config_error_type * error) { + if (workflow->last_error) + config_error_free( workflow->last_error ); + + if (error) + workflow->last_error = config_error_alloc_copy( error ); + else + workflow->last_error = NULL; +} + + + + +bool workflow_try_compile( workflow_type * script , const subst_list_type * context) { + if (util_file_exists( script->src_file )) { + const char * src_file = script->src_file; + char * tmp_file = NULL; + bool update = false; + if (context != NULL) { + tmp_file = util_alloc_tmp_file("/tmp" , "ert-workflow" , false ); + update = subst_list_filter_file( context , script->src_file , tmp_file ); + if (update) { + script->compiled = false; + src_file = tmp_file; + } else { + remove( tmp_file ); + free( tmp_file ); + tmp_file = NULL; + } + } + + { + time_t src_mtime = util_file_mtime( script->src_file ); + if (script->compiled) { + if (util_difftime_seconds( src_mtime , script->compile_time ) > 0 ) + return true; + else { + // Script has been compiled succesfully, but then changed afterwards. + // We try to recompile; if that fails we are left with 'nothing'. + } + } + } + + { + // Try to compile + config_parser_type * config_compiler = workflow_joblist_get_compiler( script->joblist ); + script->compiled = false; + workflow_clear( script ); + { + config_content_type * content = config_parse( config_compiler , src_file , WORKFLOW_COMMENT_STRING , WORKFLOW_INCLUDE , NULL , NULL , CONFIG_UNRECOGNIZED_ERROR , true ); + + if (config_content_is_valid( content )) { + int cmd_line; + for (cmd_line = 0; cmd_line < config_content_get_size(content); cmd_line++) { + const config_content_node_type * node = config_content_iget_node( content , cmd_line ); + const char * jobname = config_content_node_get_kw( node ); + const workflow_job_type * job = workflow_joblist_get_job( script->joblist , jobname ); + cmd_type * cmd = cmd_alloc( job , config_content_node_get_stringlist( node )); + + workflow_add_cmd( script , cmd ); + } + script->compiled = true; + } else + workflow_store_error( script , config_content_get_errors( content )); + + config_content_free( content ); + } + } + + if (tmp_file != NULL) { + if (script->compiled) + remove( tmp_file ); + free( tmp_file ); + } + } + + // It is legal to remove the script after successfull compilation but + // then the context will not be applied at subsequent invocations. + return script->compiled; +} + + +bool workflow_run(workflow_type * workflow, void * self , bool verbose , const subst_list_type * context) { + vector_clear( workflow->stack ); + workflow_try_compile( workflow , context); + + if (workflow->compiled) { + int icmd; + for (icmd = 0; icmd < vector_get_size( workflow->cmd_list ); icmd++) { + const cmd_type * cmd = (const cmd_type*)vector_iget_const( workflow->cmd_list , icmd ); + void * return_value = workflow_job_run( cmd->workflow_job, self , verbose , cmd->arglist ); + vector_push_front_ref( workflow->stack , return_value ); + } + return true; + } else + return false; +} + +int workflow_get_stack_size( const workflow_type * workflow ) { + return vector_get_size( workflow->stack ); +} + + +void * workflow_iget_stack_ptr( const workflow_type * workflow , int index) { + return vector_iget( workflow->stack , index ); +} + + +void * workflow_pop_stack( workflow_type * workflow ) { + return vector_pop_front( workflow->stack); +} + + + +workflow_type * workflow_alloc( const char * src_file , workflow_joblist_type * joblist) { + workflow_type * script = (workflow_type*)util_malloc( sizeof * script ); + UTIL_TYPE_ID_INIT( script , WORKFLOW_TYPE_ID ); + + script->src_file = util_alloc_string_copy( src_file ); + script->joblist = joblist; + script->cmd_list = vector_alloc_new(); + script->compiled = false; + script->last_error = NULL; + script->stack = vector_alloc_new(); + + workflow_try_compile( script , NULL ); + return script; +} + + +static UTIL_SAFE_CAST_FUNCTION( workflow , WORKFLOW_TYPE_ID ) +UTIL_IS_INSTANCE_FUNCTION( workflow , WORKFLOW_TYPE_ID) + +void workflow_free( workflow_type * workflow ) { + free( workflow->src_file ); + vector_free( workflow->cmd_list ); + vector_free( workflow->stack ); + + if (workflow->last_error) + config_error_free( workflow->last_error ); + + free( workflow ); +} + + +void workflow_free__( void * arg ) { + workflow_type * workflow = workflow_safe_cast( arg ); + workflow_free( workflow ); +} + + +const config_error_type * workflow_get_last_error( const workflow_type * workflow) { + return workflow->last_error; +} + +int workflow_size(const workflow_type * workflow) { + return vector_get_size( workflow->cmd_list ); +} + +const workflow_job_type * workflow_iget_job( const workflow_type * workflow, int index) { + const cmd_type * cmd = (const cmd_type*)vector_iget_const( workflow->cmd_list , index ); + return cmd->workflow_job; +} + +stringlist_type * workflow_iget_arguments( const workflow_type * workflow, int index) { + const cmd_type * cmd = (const cmd_type*)vector_iget_const( workflow->cmd_list , index ); + return cmd->arglist; +} + +#ifdef __cplusplus +extern "C" { +#endif + +PY_USED const char * worflow_get_src_file(const workflow_type * workflow) { + return workflow->src_file; +} + +#ifdef __cplusplus +} +#endif diff --git a/libres/lib/job_queue/workflow_job.cpp b/libres/lib/job_queue/workflow_job.cpp new file mode 100644 index 00000000000..b670bef8d06 --- /dev/null +++ b/libres/lib/job_queue/workflow_job.cpp @@ -0,0 +1,391 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'workflow_job.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 + + +/* The default values are interepreted as no limit. */ +#define DEFAULT_INTERNAL false + +#define INTERNAL_KEY "INTERNAL" +#define MODULE_KEY "MODULE" +#define FUNCTION_KEY "FUNCTION" +#define SCRIPT_KEY "SCRIPT" + +#define NULL_STRING "NULL" + +#define WORKFLOW_JOB_TYPE_ID 614441 + + +struct workflow_job_struct { + UTIL_TYPE_ID_DECLARATION; + bool internal; + int min_arg; + int max_arg; + int_vector_type * arg_types; // Should contain values from the config_item_types enum in config.h. + char * executable; + char * internal_script_path; + char * module; + char * function; + char * name; + void * lib_handle; + workflow_job_ftype * dl_func; + bool valid; +}; + + +bool workflow_job_internal( const workflow_job_type * workflow_job ) { + return workflow_job->internal; +} + +const char * workflow_job_get_name( const workflow_job_type * workflow_job ) { + return workflow_job->name; +} + + +config_parser_type * workflow_job_alloc_config() { + config_parser_type * config = config_alloc(); + { + config_schema_item_type * item; + + item = config_add_schema_item( config , MIN_ARG_KEY , false ); + config_schema_item_set_argc_minmax( item , 1 , 1 ); + config_schema_item_iset_type( item , 0 , CONFIG_INT ); + + item = config_add_schema_item( config , MAX_ARG_KEY , false ); + config_schema_item_set_argc_minmax( item , 1 , 1 ); + config_schema_item_iset_type( item , 0 , CONFIG_INT ); + + item = config_add_schema_item( config , ARG_TYPE_KEY , false ); + config_schema_item_set_argc_minmax( item , 2 , 2 ); + config_schema_item_iset_type( item , 0 , CONFIG_INT ); + + stringlist_type * var_types = stringlist_alloc_new(); + stringlist_append_copy(var_types, JOB_STRING_TYPE); + stringlist_append_copy(var_types, JOB_INT_TYPE); + stringlist_append_copy(var_types, JOB_FLOAT_TYPE); + stringlist_append_copy(var_types, JOB_BOOL_TYPE); + config_schema_item_set_indexed_selection_set( item , 1 , var_types); + + /*****************************************************************/ + item = config_add_schema_item( config , EXECUTABLE_KEY , false ); + config_schema_item_set_argc_minmax( item , 1 , 1 ); + config_schema_item_iset_type( item , 0 , CONFIG_EXECUTABLE ); + + /*****************************************************************/ + item = config_add_schema_item( config , SCRIPT_KEY , false ); + config_schema_item_set_argc_minmax( item , 1 , 1 ); + config_schema_item_iset_type( item , 0 , CONFIG_PATH ); + + /*---------------------------------------------------------------*/ + + item = config_add_schema_item( config , FUNCTION_KEY , false ); + config_schema_item_set_argc_minmax( item , 1 , 1); + + item = config_add_schema_item( config , MODULE_KEY , false ); + config_schema_item_set_argc_minmax( item , 1 , 1); + /*****************************************************************/ + + item = config_add_schema_item( config , INTERNAL_KEY , false ); + config_schema_item_set_argc_minmax( item , 1 , 1); + config_schema_item_iset_type( item , 0 , CONFIG_BOOL); + } + return config; +} + + + +static UTIL_SAFE_CAST_FUNCTION(workflow_job , WORKFLOW_JOB_TYPE_ID ); + +void workflow_job_update_config_compiler( const workflow_job_type * workflow_job , config_parser_type * config_compiler ) { + config_schema_item_type * item = config_add_schema_item( config_compiler , workflow_job->name , false ); + /* + Ensure that the arg_types mapping is at least as large as the + max_arg value. The arg_type vector will be left padded with + CONFIG_STRING values. + */ + { + int iarg; + config_schema_item_set_argc_minmax( item , workflow_job->min_arg , workflow_job->max_arg ); + for (iarg = 0; iarg < int_vector_size( workflow_job->arg_types ); iarg++) + config_schema_item_iset_type( item , iarg , (config_item_types)int_vector_iget( workflow_job->arg_types , iarg )); + } +} + + +workflow_job_type * workflow_job_alloc( const char * name , bool internal ) { + workflow_job_type * workflow_job = (workflow_job_type*)util_malloc( sizeof * workflow_job ); + UTIL_TYPE_ID_INIT( workflow_job , WORKFLOW_JOB_TYPE_ID ); + workflow_job->internal = internal; // this can not be changed run-time. + workflow_job->min_arg = CONFIG_DEFAULT_ARG_MIN; + workflow_job->max_arg = CONFIG_DEFAULT_ARG_MAX; + workflow_job->arg_types = int_vector_alloc( 0 , CONFIG_STRING ); + + workflow_job->executable = NULL; + workflow_job->internal_script_path = NULL; + workflow_job->module = NULL; + workflow_job->function = NULL; + + if (name == NULL) + util_abort("%s: trying to create workflow_job with name == NULL - illegal\n",__func__); + else + workflow_job->name = util_alloc_string_copy( name ); + + workflow_job->valid = false; + + return workflow_job; +} + + +void workflow_job_set_executable( workflow_job_type * workflow_job , const char * executable ) { + workflow_job->executable = util_realloc_string_copy( workflow_job->executable , executable ); +} + +char* workflow_job_get_executable( workflow_job_type * workflow_job) { + return workflow_job->executable; +} + +void workflow_job_set_internal_script( workflow_job_type * workflow_job , const char * script_path ) { + workflow_job->internal_script_path = util_realloc_string_copy( workflow_job->internal_script_path , script_path ); +} + +char* workflow_job_get_internal_script_path( const workflow_job_type * workflow_job) { + return workflow_job->internal_script_path; +} + +bool workflow_job_is_internal_script( const workflow_job_type * workflow_job) { + return workflow_job->internal && workflow_job->internal_script_path != NULL; +} + +void workflow_job_set_module( workflow_job_type * workflow_job , const char * module) { + if (strcmp(module ,NULL_STRING) == 0) + module = NULL; + + workflow_job->module = util_realloc_string_copy( workflow_job->module , module ); +} + +char * workflow_job_get_module( workflow_job_type * workflow_job) { + return workflow_job->module; +} + +void workflow_job_set_function( workflow_job_type * workflow_job , const char * function) { + workflow_job->function = util_realloc_string_copy( workflow_job->function , function ); +} + +char * workflow_job_get_function( workflow_job_type * workflow_job) { + return workflow_job->function; +} + +void workflow_job_iset_argtype( workflow_job_type * workflow_job , int iarg , config_item_types type) { + if (type == CONFIG_STRING || type == CONFIG_INT || type == CONFIG_FLOAT || type == CONFIG_BOOL) + int_vector_iset( workflow_job->arg_types , iarg , type ); +} + +void workflow_job_set_min_arg( workflow_job_type * workflow_job , int min_arg) { + workflow_job->min_arg = min_arg; +} + +void workflow_job_set_max_arg( workflow_job_type * workflow_job , int max_arg) { + workflow_job->max_arg = max_arg; +} + +int workflow_job_get_min_arg( const workflow_job_type * workflow_job ) { + return workflow_job->min_arg; +} + +int workflow_job_get_max_arg( const workflow_job_type * workflow_job ) { + return workflow_job->max_arg; +} + +config_item_types workflow_job_iget_argtype( const workflow_job_type * workflow_job, int index) { + return (config_item_types)int_vector_safe_iget( workflow_job->arg_types , index ); +} + + + +static void workflow_job_iset_argtype_string( workflow_job_type * workflow_job , int iarg , const char * arg_type) { + config_item_types type = job_kw_get_type(arg_type); + if (type != CONFIG_INVALID) + workflow_job_iset_argtype( workflow_job , iarg , type ); +} + + +static void workflow_job_validate_internal( workflow_job_type * workflow_job ) { + if (workflow_job->executable == NULL) { + if ((workflow_job->internal_script_path == NULL) && (workflow_job->function != NULL)) { + workflow_job->lib_handle = dlopen( workflow_job->module , RTLD_NOW ); + if (workflow_job->lib_handle != NULL) { + workflow_job->dl_func = (workflow_job_ftype *) dlsym( workflow_job->lib_handle , workflow_job->function ); + if (workflow_job->dl_func != NULL) + workflow_job->valid = true; + else + fprintf(stderr,"Failed to load symbol:%s Error:%s \n",workflow_job->function , dlerror()); + } else { + if (workflow_job->module != NULL) + fprintf(stderr,"Failed to load module:%s Error:%s \n",workflow_job->module , dlerror()); + } + } else if ((workflow_job->internal_script_path != NULL) && (workflow_job->function == NULL)) { + workflow_job->valid = true; + } else { + fprintf(stderr, "Must have function != NULL or internal_script != NULL for internal jobs"); + } + } else { + fprintf(stderr, "Must have executable == NULL for internal jobs\n"); + } +} + + +static void workflow_job_validate_external( workflow_job_type * workflow_job ) { + if (workflow_job->executable != NULL) { + if (util_is_executable( workflow_job->executable ) && + (workflow_job->module == workflow_job->function) && + (workflow_job->module == NULL)) + workflow_job->valid = true; + } +} + + + +static void workflow_job_validate( workflow_job_type * workflow_job ) { + if (workflow_job->internal) + workflow_job_validate_internal( workflow_job ); + else + workflow_job_validate_external( workflow_job ); +} + + + + +workflow_job_type * workflow_job_config_alloc( const char * name , config_parser_type * config , const char * config_file) { + workflow_job_type * workflow_job = NULL; + config_content_type * content = config_parse( config , config_file , "--", NULL , NULL , NULL , CONFIG_UNRECOGNIZED_WARN , true); + if (config_content_is_valid( content )) { + bool internal = DEFAULT_INTERNAL; + if (config_content_has_item( content , INTERNAL_KEY)) + internal = config_content_iget_as_bool( content , INTERNAL_KEY , 0 , 0 ); + + { + workflow_job = workflow_job_alloc( name , internal ); + + if (config_content_has_item( content , MIN_ARG_KEY)) + workflow_job_set_min_arg( workflow_job , config_content_iget_as_int( content , MIN_ARG_KEY , 0 , 0 )); + + if (config_content_has_item( content , MAX_ARG_KEY)) + workflow_job_set_max_arg( workflow_job , config_content_iget_as_int( content , MAX_ARG_KEY , 0 , 0 )); + + { + int i; + for (i=0; i < config_content_get_occurences( content , ARG_TYPE_KEY); i++) { + int iarg = config_content_iget_as_int( content , ARG_TYPE_KEY , i , 0 ); + const char * arg_type = config_content_iget( content , ARG_TYPE_KEY , i , 1 ); + + workflow_job_iset_argtype_string( workflow_job , iarg , arg_type ); + } + } + + if (config_content_has_item( content , MODULE_KEY)) + workflow_job_set_module( workflow_job , config_content_get_value( content , MODULE_KEY)); // Could be a pure so name; or a full path ..... Like executable + + if (config_content_has_item( content , FUNCTION_KEY)) + workflow_job_set_function( workflow_job , config_content_get_value( content , FUNCTION_KEY)); + + if (config_content_has_item( content , EXECUTABLE_KEY)) + workflow_job_set_executable( workflow_job , config_content_get_value_as_executable( content , EXECUTABLE_KEY)); + + if (config_content_has_item( content , SCRIPT_KEY)) { + workflow_job_set_internal_script( workflow_job , config_content_get_value_as_abspath( content , SCRIPT_KEY)); + } + + workflow_job_validate( workflow_job ); + + if (!workflow_job->valid) { + workflow_job_free( workflow_job ); + workflow_job = NULL; + } + } + } + config_content_free( content ); + return workflow_job; +} + + + + +void workflow_job_free( workflow_job_type * workflow_job ) { + free( workflow_job->module ); + free( workflow_job->function ); + free( workflow_job->executable ); + int_vector_free( workflow_job->arg_types ); + free( workflow_job->internal_script_path ); + free( workflow_job->name ); + free( workflow_job ); +} + + +void workflow_job_free__( void * arg) { + workflow_job_type * workflow_job = workflow_job_safe_cast( arg ); + workflow_job_free( workflow_job ); +} + +/* + The workflow job can return an arbitrary (void *) pointer. It is the + calling scopes responsability to interpret this object correctly. If + the the workflow job allocates storage the calling scope must + discard it. +*/ + +static void * workflow_job_run_internal( const workflow_job_type * job, void * self , bool verbose , const stringlist_type * arg) { + return job->dl_func( self , arg ); +} + + +static void * workflow_job_run_external( const workflow_job_type * job, bool verbose , const stringlist_type * arg) { + char ** argv = stringlist_alloc_char_copy( arg ); + + util_spawn_blocking(job->executable, stringlist_get_size(arg), (const char **) argv, NULL, NULL); + + if (argv != NULL) { + int i; + for (i=0; i < stringlist_get_size( arg ); i++) + free( argv[i] ); + free( argv ); + } + return NULL; +} + +/* This is the old C way and will only be used from the TUI */ +void * workflow_job_run( const workflow_job_type * job, void * self , bool verbose , const stringlist_type * arg) { + if (job->internal) { + if (workflow_job_is_internal_script(job)) { + fprintf(stderr, "*** Can not run internal script workflow jobs using this method: workflow_job_run()\n"); + return NULL; + } else { + return workflow_job_run_internal( job, self, verbose, arg ); + } + } else { + return workflow_job_run_external( job, verbose, arg ); + } +} diff --git a/libres/lib/job_queue/workflow_joblist.cpp b/libres/lib/job_queue/workflow_joblist.cpp new file mode 100644 index 00000000000..5211b4023c2 --- /dev/null +++ b/libres/lib/job_queue/workflow_joblist.cpp @@ -0,0 +1,90 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'workflow_joblist.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 + + +struct workflow_joblist_struct { + config_parser_type * workflow_compiler; + config_parser_type * job_config; + + hash_type * joblist; +}; + + +workflow_joblist_type * workflow_joblist_alloc( ) { + workflow_joblist_type * joblist = (workflow_joblist_type*)util_malloc( sizeof * joblist ); + + joblist->job_config = workflow_job_alloc_config(); + joblist->workflow_compiler = config_alloc(); + joblist->joblist = hash_alloc(); + + return joblist; +} + + +void workflow_joblist_free( workflow_joblist_type * joblist) { + config_free( joblist->job_config ); + config_free( joblist->workflow_compiler ); + hash_free( joblist->joblist ); + free( joblist ); +} + + +const workflow_job_type * workflow_joblist_get_job( const workflow_joblist_type * joblist , const char * job_name) { + return (const workflow_job_type*)hash_get( joblist->joblist , job_name ); +} + + +void workflow_joblist_add_job( workflow_joblist_type * joblist , const workflow_job_type * job) { + hash_insert_hash_owned_ref( joblist->joblist , workflow_job_get_name( job ) , job , workflow_job_free__ ); + workflow_job_update_config_compiler( job , joblist->workflow_compiler ); +} + + +bool workflow_joblist_has_job( const workflow_joblist_type * joblist , const char * job_name) { + workflow_job_type * job = (workflow_job_type*)hash_safe_get(joblist->joblist, job_name); + return (NULL != job); +} + +bool workflow_joblist_add_job_from_file( workflow_joblist_type * joblist , const char * job_name , const char * config_file ) { + workflow_job_type * job = workflow_job_config_alloc( job_name , joblist->job_config , config_file ); + if (job) { + workflow_joblist_add_job( joblist , job ); + return true; + } else + return false; +} + + +config_parser_type * workflow_joblist_get_compiler( const workflow_joblist_type * joblist ) { + return joblist->workflow_compiler; +} + + +stringlist_type * workflow_joblist_get_job_names(const workflow_joblist_type * joblist) { + return hash_alloc_stringlist(joblist->joblist); +} diff --git a/libres/lib/makefile b/libres/lib/makefile new file mode 100644 index 00000000000..79b8e2f27fa --- /dev/null +++ b/libres/lib/makefile @@ -0,0 +1,74 @@ +include global_config +SDP_ROOT = $(shell get_sdp_root.py) +################################################################# +COMPILE_INCLUDE = -I$(LIBRMS_HOME)/src -I$(LIBECL_HOME)/include -I$(LIBUTIL_HOME)/include +INSTALL_INC_PATH = $(LIBRMS_HOME)/include +INSTALL_LIB_PATH = $(LIBRMS_HOME)/lib +################################################################# +OBJECTS = rms_file.o rms_util.o rms_tag.o rms_type.o rms_tagkey.o rms_stats.o rms_export.o +INC_FILES = rms_file.h rms_util.h rms_tag.h rms_type.h rms_tagkey.h rms_stats.h rms_export.h +LIB = librms.a + + +LOCAL: LIB + install -d $(INSTALL_INC_PATH) + install -d $(INSTALL_LIB_PATH) + install $(INC_FILES) $(INSTALL_INC_PATH) + install $(LIB) $(INSTALL_LIB_PATH) + +SDP_INSTALL: LIB BIN + install $(LIB) $(SDP_ROOT)/lib/lib$(LIB_ROOT).a + install $(INC_FILES) $(SDP_ROOT)/include + +LIB: $(LIB) + +clean: + rm -f *.o *~ + rm -f $(LIB) + rm -f $(INSTALL_LIB_PATH)/$(LIB) + rm -f $(INSTALL_INC_PATH)/*.h + +rebuild: clean LOCAL + +$(LIB): $(OBJECTS) + $(AR) $(ARFLAGS) $(LIB) $(OBJECTS) + + +%.o : %.c + $(CC) -c $(CFLAGS) $(COMPILE_INCLUDE) $(CPPFLAGS) $< -o $@ + + +new: + ../../Scripts/cdep.py all_include.h 0 + +include dependencies + +################################################################## +## Binaries +# +#BIN_FILES = rms.x rms_stat.x roff_tags.x rms_extract.x rms_setname.x +#BIN: $(BIN_FILES) +#BIN_FILES_SDP_INSTALL = roff_tags.x +# +#rms_test.o : rms_test.c +#rms_stat.o : rms_stat.c +#tag_list.o : tag_list.c +#rms_extract.o : rms_extract.c +#rms_setname.o : rms_setname.c +# +#roff_tags.x: tag_list.o $(LIB) +# $(CC) -$(MFLAG) $(LDFLAGS) tag_list.o -o roff_tags.x $(LIB_PATH) $(LIB_LINK) +# +#rms.x: rms_test.o $(LIB) +# $(CC) -$(MFLAG) $(LDFLAGS) rms_test.o -o rms.x $(LIB_PATH) $(LIB_LINK) +# +#rms_stat.x: rms_stat.o $(LIB) +# $(CC) -$(MFLAG) $(LDFLAGS) rms_stat.o -o rms_stat.x $(LIB_PATH) $(LIB_LINK) +# +#rms_extract.x: rms_extract.o $(LIB) +# $(CC) -$(MFLAG) $(LDFLAGS) rms_extract.o -o rms_extract.x $(LIB_PATH) $(LIB_LINK) +# +#rms_setname.x: rms_setname.o $(LIB) +# $(CC) -$(MFLAG) $(LDFLAGS) rms_setname.o -o rms_setname.x $(LIB_PATH) $(LIB_LINK) + + diff --git a/libres/lib/private-include/ext/json/cJSON.h b/libres/lib/private-include/ext/json/cJSON.h new file mode 100644 index 00000000000..9af08bb1ad2 --- /dev/null +++ b/libres/lib/private-include/ext/json/cJSON.h @@ -0,0 +1,286 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif + +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) +CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +#define CJSON_CDECL __cdecl +#define CJSON_STDCALL __stdcall + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type CJSON_STDCALL +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL +#endif +#else /* !__WINDOWS__ */ +#define CJSON_CDECL +#define CJSON_STDCALL + +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 7 +#define CJSON_VERSION_PATCH 8 + +#include + +/* cJSON Types: */ +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + +/* The cJSON structure: */ +typedef struct cJSON +{ + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *next; + struct cJSON *prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON *child; + + /* The type of the item, as above. */ + int type; + + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char *valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char *string; +} cJSON; + +typedef struct cJSON_Hooks +{ + /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ + void *(CJSON_CDECL *malloc_fn)(size_t sz); + void (CJSON_CDECL *free_fn)(void *ptr); +} cJSON_Hooks; + +typedef int cJSON_bool; + +/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_NESTING_LIMIT +#define CJSON_NESTING_LIMIT 1000 +#endif + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char*) cJSON_Version(void); + +/* Supply malloc, realloc and free functions to cJSON */ +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); + +/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); + +/* Render a cJSON entity to text for transfer/storage. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); +/* Delete a cJSON entity and all subentities. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *c); + +/* Returns the number of items in an array (or object). */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); +/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); +/* Get item "string" from object. Case insensitive. */ +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); + +/* Check if the item is a string and return its valuestring */ +CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); + +/* These calls create a cJSON item of the appropriate type. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); +/* raw json */ +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); + +/* Create a string where valuestring references a string so + * it will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); +/* Create an object/arrray that only references it's elements so + * they will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); + +/* These utilities create an Array of count items. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count); + +/* Append item to the specified array/object. */ +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); + +/* Remove/Detatch items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); + +/* Update array items. */ +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will +need to be released. With recurse!=0, it will duplicate any children connected to the item. +The item->next and ->prev pointers are always zero on return from Duplicate. */ +/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. + * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); + + +CJSON_PUBLIC(void) cJSON_Minify(char *json); + +/* Helper functions for creating and adding items to an object at the same time. + * They return the added item or NULL on failure. */ +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* helper for the cJSON_SetNumberValue macro */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); +#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) + +/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ +CJSON_PUBLIC(void *) cJSON_malloc(size_t size); +CJSON_PUBLIC(void) cJSON_free(void *object); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/libres/lib/readme.overview b/libres/lib/readme.overview new file mode 100644 index 00000000000..5b3c3dff524 --- /dev/null +++ b/libres/lib/readme.overview @@ -0,0 +1,55 @@ +The EnKF functionality is organized in xxx libraries with different +functionalities. The different libraries depend on eachother, and the +libraries must be built in correct order. The dependencies is as +follows: + +libhash : +libutil : libhash +libecl : libhash libutil +librms : libecl libutil libhash +libsched : libecl linutil libhash +libenkf : libecl libsched librm linutil libhash + + +libhash: This library implements the classes hash_type, set_type and + list_type. + +libutil: This library is a collection utility routines. Observe that + this library only implements routines, and not statefull + objects. + +libecl: This library implements functions for reading/writing ECLIPSE + restart/summary/init/grid files. + +libsched: This library implements a basic SCHEDULE file parser. + +librms: This library implements (basic) reader and writer for binary + RMS ROFF files. + +libenkf: This library implements various high level objects for EnKF + functionality. + +----------------------------------------------------------------- + +All the makefiles start with the statement: + +include "path_config" + +The file path_config is *not* under version control, this is on +purpose because every user can/should have a private confiiguration of +paths. The file path_config should define make-variables for the +location of all the libraries, this is an example of a valid +path-config file: + + LIBHASH_HOME = /h/a152128/EnKF/EnKF/libhash + LIBUTIL_HOME = /h/a152128/EnKF/EnKF/libutil + LIBSCHED_HOME = /h/a152128/EnKF/EnKF/libsched + LIBRMS_HOME = /h/a152128/EnKF/EnKF/librms + LIBECL_HOME = /h/a152128/EnKF/EnKF/libecl + LIBENKF_HOME = /h/a152128/EnKF/EnKF/libenkf + +In this example all libraries have a common path prefix, that is not +a requirement. + + + diff --git a/libres/lib/res_util/arg_pack.cpp b/libres/lib/res_util/arg_pack.cpp new file mode 100644 index 00000000000..57e29b6cabd --- /dev/null +++ b/libres/lib/res_util/arg_pack.cpp @@ -0,0 +1,486 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'arg_pack.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 + + +/** + This file implements an arg_pack structure which is a small + convienence utility to pack several arguments into one + argument. The generic use situtation is when calling functions like + e.g. pthread_create() which take one (void *) as argument. You can + then pack several arguments into one arg_pack instance, and then + unpack them at the other end. + + The content of the arg_pack is mainly inserted by appending, in + addition it is possible to insert new items by using _iset() + functions, however these functions will fail hard if the resulting + call sequence will lead to holes in the structure, i.e. + + arg_pack_type * arg_pack = arg_pack_alloc() + arg_pack_append_int( arg_pack , 1); + + When you take them out again that is done with indexed get. + + When elements are inserted into the arg_pack, they are inserted + with a (limited) type information (implictly given by the function + invoked to insert the argument), and the corresponding typed get + must be used to unpack the argument again afterwards. The + excepetion is with the function arg_pack_iget_adress() which can be + used to extract the reference of a scalar. + + + + Example: + -------- + + void some_function(const char * arg1 , int arg2 , double arg3) { + ..... + } + + + void some_function__(void * __arg_pack) { + arg_pack_type * arg_pack = arg_pack_safe_cast( __arg_pack ); + const char * arg1 = arg_pack_iget_ptr( arg_pack , 0); + int arg2 = arg_pack_iget_int( arg_pack , 1); + + some_function( arg1 , arg2 , arg3 ); + } + + + ..... + arg_pack_type * arg_pack = arg_pack_alloc(); + arg_pack_append_ptr(arg_pack , "ARG1"); + arg_pack_append_int(arg_pack , 1); + arg_pack_append_double(arg_pack , 3.14159265); + + pthread_create( , , some_function__ , arg_pack); + +*/ + + + + +#define ARG_PACK_TYPE_ID 668268 + + +typedef struct { + void * buffer; /* This is the actual content - can either point to a remote object, or to storage managed by the arg_pack instance. */ + node_ctype ctype; /* The type of the data which is stored. */ + arg_node_free_ftype * destructor; /* destructor called on buffer - can be NULL. */ + arg_node_copyc_ftype * copyc; /* copy constructor - will typically be NULL. */ +} arg_node_type; + + + +struct arg_pack_struct { + UTIL_TYPE_ID_DECLARATION; + int size; /* The number of arguments appended to this arg_pack instance. */ + int alloc_size; /* The number of nodes allocated to this arg_pack - will in general be greater than size. */ + bool locked; /* To insure against unwaranted modifictaions - you can explicitly lock the arg_pack instance. This only */ + arg_node_type **nodes; /* Vector of nodes */ +}; + + +/*****************************************************************/ +/* First comes the arg_node functions. These are all fully static.*/ + +static arg_node_type * arg_node_alloc_empty() { + arg_node_type * node = (arg_node_type*)util_malloc( sizeof * node ); + node->buffer = NULL; + node->destructor = NULL; + node->ctype = CTYPE_INVALID; + return node; +} + + +static void arg_node_realloc_buffer(arg_node_type * node , int new_size) { + node->buffer = util_realloc(node->buffer , new_size ); +} + + +static void __arg_node_assert_type(const arg_node_type * node , node_ctype arg_type) { + if (arg_type != node->ctype) + util_abort("%s: asked for type:\'%s\' inserted as:\'%s\' - aborting \n" , __func__ , node_ctype_name(arg_type) , node_ctype_name(node->ctype)); +} + + + +/*****************************************************************/ +#define ARG_NODE_GET_RETURN(type) \ +{ \ + type value; \ + memcpy(&value , node->buffer , sizeof value); \ + return value; \ +} + +// Used by IGET_TYPED macro +C_USED static int arg_node_get_int( const arg_node_type * node) { + __arg_node_assert_type( node , CTYPE_INT_VALUE ); + ARG_NODE_GET_RETURN( int ) +} + +C_USED static char arg_node_get_char( const arg_node_type * node) { + __arg_node_assert_type( node , CTYPE_CHAR_VALUE ); + ARG_NODE_GET_RETURN( char ) +} + +C_USED static double arg_node_get_double( const arg_node_type * node) { + __arg_node_assert_type( node , CTYPE_DOUBLE_VALUE ); + ARG_NODE_GET_RETURN( double ) +} + +C_USED static float arg_node_get_float( const arg_node_type * node) { + __arg_node_assert_type( node , CTYPE_FLOAT_VALUE ); + ARG_NODE_GET_RETURN( float ) +} + +C_USED static bool arg_node_get_bool( const arg_node_type * node) { + __arg_node_assert_type( node , CTYPE_BOOL_VALUE ); + ARG_NODE_GET_RETURN( bool ) +} + +C_USED static size_t arg_node_get_size_t( const arg_node_type * node) { + __arg_node_assert_type( node , CTYPE_SIZE_T_VALUE ); + ARG_NODE_GET_RETURN( size_t ) +} +#undef ARG_NODE_GET_RETURN + +/** + If the argument is inserted as a pointer, you must use get_ptr == + true, otherwise you must use get_ptr == false, and this will give + you the adress of the scalar. + + Observe that if you call XX_get_ptr() on a pointer which is still + owned by the arg_pack, you must be careful when freeing the + arg_pack, as that will delete the pointer you are using as well. +*/ + + +static void * arg_node_get_ptr(const arg_node_type * node , bool get_ptr) { + if (get_ptr) { + if (node->ctype != CTYPE_VOID_POINTER) + util_abort("%s: tried to get pointer from something not a pointer\n",__func__); + } else { + if (node->ctype == CTYPE_VOID_POINTER) + util_abort("%s: tried to get adress to something already a ponter\n",__func__); + } + return node->buffer; +} + + +static node_ctype arg_node_get_ctype( const arg_node_type * arg_node ) { + return arg_node->ctype; +} + +/*****************************************************************/ +/* SET functions. */ + + +#define ARG_NODE_SET(node , value) \ + arg_node_realloc_buffer(node , sizeof value); \ + memcpy(node->buffer , &value , sizeof value); \ + node->destructor = NULL; + +// Used by ISET_TYPED macro +C_USED static void arg_node_set_int( arg_node_type * node , int value) { + ARG_NODE_SET( node , value ); + node->ctype = CTYPE_INT_VALUE; +} + + +C_USED static void arg_node_set_char( arg_node_type * node , char value) { + ARG_NODE_SET( node , value ); + node->ctype = CTYPE_CHAR_VALUE; +} + + +C_USED static void arg_node_set_float( arg_node_type * node , float value) { + ARG_NODE_SET( node , value ); + node->ctype = CTYPE_FLOAT_VALUE; +} + + +C_USED static void arg_node_set_double( arg_node_type * node , double value) { + ARG_NODE_SET( node , value ); + node->ctype = CTYPE_DOUBLE_VALUE; +} + + +C_USED static void arg_node_set_bool( arg_node_type * node , bool value) { + ARG_NODE_SET( node , value ); + node->ctype = CTYPE_BOOL_VALUE; +} + + +C_USED static void arg_node_set_size_t( arg_node_type * node , size_t value) { + ARG_NODE_SET( node , value ); + node->ctype = CTYPE_SIZE_T_VALUE; +} + +#undef ARG_NODE_SET + + +static void arg_node_set_ptr(arg_node_type * node , const void * ptr , arg_node_copyc_ftype * copyc , arg_node_free_ftype * destructor) { + node->ctype = CTYPE_VOID_POINTER; + node->destructor = destructor; + node->copyc = copyc; + if (copyc != NULL) + node->buffer = copyc( ptr ); + else + node->buffer = (void *) ptr; +} + + + +/*****************************************************************/ + + +static void arg_node_clear(arg_node_type * node) { + if (node->ctype == CTYPE_VOID_POINTER) { + if (node->destructor != NULL) + node->destructor( node->buffer ); + /* When you have cleared - must not reuse the thing. */ + node->destructor = NULL; + node->buffer = NULL; + node->copyc = NULL; + } +} + + +static void arg_node_free(arg_node_type * node) { + arg_node_clear(node); + free(node->buffer); + free(node); +} + +/*****************************************************************/ +/* Ending node node functions - starting on functons for the whole pack. */ +/*****************************************************************/ + +UTIL_SAFE_CAST_FUNCTION( arg_pack , ARG_PACK_TYPE_ID) +UTIL_SAFE_CAST_FUNCTION_CONST( arg_pack , ARG_PACK_TYPE_ID) +UTIL_IS_INSTANCE_FUNCTION(arg_pack , ARG_PACK_TYPE_ID) + +static void __arg_pack_assert_index(const arg_pack_type * arg , int iarg) { + if (iarg < 0 || iarg >= arg->size) + util_abort("%s: arg_pack() object filled with %d arguments - %d invalid argument number - aborting \n",__func__ , arg->size , iarg); +} + + +static void arg_pack_realloc_nodes(arg_pack_type * arg_pack , int new_size) { + arg_pack->nodes = (arg_node_type**) util_realloc(arg_pack->nodes , new_size * sizeof * arg_pack->nodes ); + { + int i; + for (i = arg_pack->alloc_size; i < new_size; i++) + arg_pack->nodes[i] = arg_node_alloc_empty(); + } + arg_pack->alloc_size = new_size; +} + + +/** + The name of this function is QUITE MISLEADING; the function will + create a new node, with index @index, and return it possibly + freeing the existing with this index. If index == arg_pack->size a + new node will be created at the end of the arg_pack; if index > + arg_pack->size the function will fail hard. +*/ +static arg_node_type * arg_pack_iget_new_node( arg_pack_type * arg_pack , int index) { + if (index < 0 || index > arg_pack->size) + util_abort("%s: index:%d invalid. Valid interval: [0,%d) \n",__func__ , index , arg_pack->size); + { + if (index < arg_pack->size) { + arg_node_free( arg_pack->nodes[index] ); /* Free the existing current node. */ + arg_pack->nodes[index] = arg_node_alloc_empty( ); /* Allocate a new fresh instance. */ + } + + if (arg_pack->size == arg_pack->alloc_size) + arg_pack_realloc_nodes(arg_pack , 1 + arg_pack->alloc_size * 2); /* We have to grow the vector of nodes. */ + + if (index == arg_pack->size) + arg_pack->size++; /* We are asking for the first element beyond the current length of the vector, i.e. append. */ + return arg_pack->nodes[index]; + } +} + + + +static arg_node_type * arg_pack_get_append_node(arg_pack_type * arg_pack) { + if (arg_pack->locked) { + util_abort("%s: tryng to append to a locked arg_pack instance \n",__func__); + return NULL; + } + { + arg_node_type * new_node = arg_pack_iget_new_node( arg_pack , arg_pack->size ); + return new_node; + } +} + + +arg_pack_type * arg_pack_alloc() { + arg_pack_type * arg_pack = (arg_pack_type*)util_malloc(sizeof * arg_pack ); + UTIL_TYPE_ID_INIT( arg_pack , ARG_PACK_TYPE_ID); + arg_pack->nodes = NULL; + arg_pack->alloc_size = 0; + arg_pack->locked = false; + arg_pack_realloc_nodes(arg_pack , 4); + arg_pack_clear(arg_pack); + return arg_pack; +} + + +void arg_pack_free(arg_pack_type * arg_pack) { + int i; + + for (i=0; i < arg_pack->alloc_size; i++) + arg_node_free( arg_pack->nodes[i] ); + + free(arg_pack->nodes); + free(arg_pack); +} + + +void arg_pack_clear(arg_pack_type * arg_pack) { + if (arg_pack->locked) + util_abort("%s: arg_pack has been locked - abortng \n",__func__); + { + int i; + for ( i=0; i < arg_pack->size; i++) + arg_node_clear(arg_pack->nodes[i]); + arg_pack->size = 0; + } +} + + +/******************************************************************/ +/* Access functions: + + 1. Append + 2. iget + 3. iset (can NOT create holes in the vector) + +******************************************************************/ + +#define APPEND_TYPED(type) \ +void arg_pack_append_ ## type (arg_pack_type *pack , type value) { \ + arg_node_type * node = arg_pack_get_append_node( pack ); \ + arg_node_set_ ## type(node , value); \ +} + + +#define ISET_TYPED(type)\ +void arg_pack_iset_ ## type(arg_pack_type * pack, int index, type value) { \ + arg_node_type * node = arg_pack_iget_new_node( pack , index); \ + arg_node_set_ ## type(node , value); \ +} + + +#define IGET_TYPED(type)\ +type arg_pack_iget_ ## type(const arg_pack_type * pack, int index) { \ + __arg_pack_assert_index( pack , index); \ + { \ + arg_node_type * node = pack->nodes[index]; \ + return arg_node_get_ ## type ( node ); \ + } \ +} + + +APPEND_TYPED(int); +APPEND_TYPED(bool); +APPEND_TYPED(float); +APPEND_TYPED(double); +APPEND_TYPED(char); +APPEND_TYPED(size_t); + +IGET_TYPED(int); +IGET_TYPED(bool); +IGET_TYPED(float); +IGET_TYPED(double); +IGET_TYPED(char); +IGET_TYPED(size_t); + +ISET_TYPED(int); +ISET_TYPED(bool); +ISET_TYPED(float); +ISET_TYPED(double); +ISET_TYPED(char); +ISET_TYPED(size_t); + +#undef APPEND_TYPED +#undef IGET_TYPED +#undef ISET_TYPED + + +void * arg_pack_iget_ptr(const arg_pack_type * arg , int iarg) { + __arg_pack_assert_index(arg , iarg); + return arg_node_get_ptr(arg->nodes[iarg] , true); +} + + +const void * arg_pack_iget_const_ptr(const arg_pack_type * arg , int iarg) { + __arg_pack_assert_index(arg , iarg); + return arg_node_get_ptr(arg->nodes[iarg] , true); +} + + +void * arg_pack_iget_adress(const arg_pack_type * arg , int iarg) { + __arg_pack_assert_index(arg , iarg); + return arg_node_get_ptr(arg->nodes[iarg] , false); +} + + +/*****************************************************************/ + + +void arg_pack_iset_copy(arg_pack_type * arg_pack , int index , const void * ptr, arg_node_copyc_ftype * copyc , arg_node_free_ftype * freef) { + arg_node_type * node = arg_pack_iget_new_node( arg_pack , index ); + arg_node_set_ptr(node , ptr , copyc , freef); +} + + +void arg_pack_iset_ptr(arg_pack_type * arg_pack, int index , const void * ptr) { + arg_pack_iset_copy(arg_pack , index , ptr , NULL , NULL); +} + +void arg_pack_iset_owned_ptr(arg_pack_type * arg_pack, int index , void * ptr, arg_node_free_ftype * freef) { + arg_pack_iset_copy(arg_pack , index , ptr , NULL , freef ); +} + +void arg_pack_append_ptr(arg_pack_type * arg_pack, void * ptr) { + arg_pack_iset_ptr( arg_pack , arg_pack->size , ptr ); +} + +void arg_pack_append_const_ptr(arg_pack_type * arg_pack, const void * ptr) { + arg_pack_iset_ptr( arg_pack , arg_pack->size , ptr ); +} + + +void arg_pack_append_owned_ptr(arg_pack_type * arg_pack, void * ptr, arg_node_free_ftype * freef) { + arg_pack_iset_owned_ptr( arg_pack , arg_pack->size , ptr , freef); +} + +int arg_pack_size( const arg_pack_type * arg_pack ) { + return arg_pack->size; +} diff --git a/libres/lib/res_util/block_fs.cpp b/libres/lib/res_util/block_fs.cpp new file mode 100644 index 00000000000..b3b3c68b892 --- /dev/null +++ b/libres/lib/res_util/block_fs.cpp @@ -0,0 +1,1660 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'block_fs.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 + + +#define MOUNT_MAP_MAGIC_INT 8861290 +#define BLOCK_FS_TYPE_ID 7100652 +#define INDEX_MAGIC_INT 1213775 +#define INDEX_FORMAT_VERSION 1 + +// #define ENABLE_CACHE + + +/* + During mounting a significant part of the time is spent on filling + up the index hash table. By default a hash table is created with a + quite small size, and when initializing a large block_fs structure + it must be resized many times. By setting a default size with the + DEFAULT_INDEX_SIZE variable the hash table will immediately be + resized, avoiding some of the automatic calls to hash_resize. + + When the file system is loaded from an index a good size estimate + can be inferred directly from the index. +*/ + +#define DEFAULT_INDEX_SIZE 2048 + + + +/** + These should be bitwise "smart" - so it is possible + to go on a wild chase through a binary stream and look for them. +*/ + +#define NODE_IN_USE_BYTE 85 /* Binary(85) = 01010101 */ +#define NODE_FREE_BYTE 170 /* Binary(170) = 10101010 */ +#define WRITE_START__ 77162 + +static const int NODE_END_TAG = 16711935; /* Binary = 00000000111111110000000011111111 */ +static const int NODE_WRITE_ACTIVE_START = WRITE_START__; +static const int NODE_WRITE_ACTIVE_END = 776512; + + +typedef enum { + NODE_IN_USE = 1431655765, /* NODE_IN_USE_BYTE * ( 1 + 256 + 256**2 + 256**3) => Binary 01010101010101010101010101010101 */ + NODE_FREE = -1431655766, /* NODE_FREE_BYTE * ( 1 + 256 + 256**2 + 256**3) => Binary 10101010101010101010101010101010 */ + NODE_WRITE_ACTIVE = WRITE_START__, /* This */ + NODE_INVALID = 13 /* This should __never__ be written to disk */ +} node_status_type; + + +/** + The free_node_struct is used to implement a doubly linked list of + free nodes; i.e. holes in the file which are available for other use. +*/ +typedef struct file_node_struct file_node_type; +typedef struct free_node_struct free_node_type; + +struct free_node_struct { + free_node_type * next; + free_node_type * prev; + file_node_type * file_node; +}; + + + +/* + Datastructure representing one 'block' in the datafile. The block + can either refer to a file (status == NODE_IN_USE) or to an empty + slot in the datafile (status == NODE_FREE). +*/ + +struct file_node_struct{ + long int node_offset; /* The offset into the data_file of this node. NEVER Changed. */ + int data_offset; /* The offset from the node start to the start of actual data - i.e. data starts at absolute position: node_offset + data_offset. */ + int node_size; /* The size in bytes of this node - must be >= data_size. NEVER Changed. */ + int data_size; /* The size of the data stored in this node - in addition the node might need to store header information. */ + node_status_type status; /* This should be: NODE_IN_USE | NODE_FREE; in addition the disk can have NODE_WRITE_ACTIVE for incomplete writes. */ + +#ifdef ENABLE_CACHE + char * cache; + int cache_size; +#endif +}; + + +/** + data_size : manipulated in block_fs_fwrite__() and block_fs_insert_free_node(). + status : manipulated in block_fs_fwrite__() and block_fs_unlink_file__(); + data_offset : manipulated in block_fs_fwrite__() and block_fs_insert_free_node(). +*/ + + + + + + +struct block_fs_struct { + UTIL_TYPE_ID_DECLARATION; + char * mount_file; /* The full path to a file with some mount information - input to the mount routine. */ + char * path; + char * base_name; + + int version; /* A version number which is incremented each time the filesystem is defragmented - not implemented yet. */ + + char * data_file; + char * lock_file; + char * index_file; + + int data_fd; + FILE * data_stream; + + long int data_file_size; /* The total number of bytes in the data_file. */ + long int free_size; /* Size of 'holes' in the data file. */ + int block_size; /* The size of blocks in bytes. */ + int lock_fd; /* The file descriptor for the lock_file. Set to -1 if we do not have write access. */ + + pthread_mutex_t io_lock; /* Lock held during fread of the data file. */ + pthread_rwlock_t rw_lock; /* Read-write lock during all access to the fs. */ + + int num_free_nodes; + hash_type * index; /* THE HASH table of all the nodes/files which have been stored. */ + free_node_type * free_nodes; + vector_type * file_nodes; /* This vector owns all the file_node instances - the index and free_nodes structures + only contain pointers to the objects stored in this vector. */ + int write_count; /* This just counts the number of writes since the file system was mounted. */ + int max_cache_size; + size_t total_cache_size; + size_t max_total_cache_size; + float fragmentation_limit; /* If fragmentation (amount of wasted space) is above this limit - do a rotate. + fragmentation_limit == 1.0 : Never rotate. + fragmentation_limit == 0.0 : Rotate when one byte is wasted. */ + bool data_owner; + int fsync_interval; /* 0: never n: every nth iteration. */ +}; + +/*****************************************************************/ + +static void block_fs_rotate__( block_fs_type * block_fs ); + +UTIL_SAFE_CAST_FUNCTION( block_fs , BLOCK_FS_TYPE_ID ) + + +static inline void fseek__(FILE * stream , long int arg , int whence) { + if (fseek(stream , arg , whence) != 0) { + fprintf(stderr,"** Warning - seek:%ld failed %s(%d) \n",arg,strerror(errno) , errno); + util_abort("%S - aborting\n",__func__); + } +} + +static inline void block_fs_fseek(block_fs_type * block_fs , long offset) { + fseek__( block_fs->data_stream , offset , SEEK_SET ); +} + + +/*****************************************************************/ +/* file_node functions */ + + + + +/** + Observe that the two input arguments to this function should NEVER + change. They represent offset and size in the underlying data file, + and that is for ever fixed. +*/ + + +static file_node_type * file_node_alloc( node_status_type status , long int offset , int node_size) { + file_node_type * file_node = (file_node_type*)util_malloc( sizeof * file_node ); + + file_node->node_offset = offset; /* These should NEVER change. */ + file_node->node_size = node_size; /* ------------------------- */ + + file_node->data_size = 0; + file_node->data_offset = 0; + file_node->status = status; + +#ifdef ENABLE_CACHE + file_node->cache = NULL; + file_node->cache_size = 0; +#endif + + return file_node; +} + + +/** + This function is called from the functions exporting file_node + instances; the file_node instance will then have a correct filename + field immediately afterwards, but the normal block_fs functions + (read/write/unlink) do NOT update this field, so it can quickly go + out of sync. +*/ + + + + + + +#ifdef ENABLE_CACHE +static void file_node_read_from_cache( const file_node_type * file_node , void * ptr , size_t read_bytes) { + memcpy(ptr , file_node->cache , read_bytes); + /* + Could check: (ext_offset + file_node->cache_size <= read_bytes) - else + we are reading beyond the end of the cache. + */ +} + + +static void file_node_buffer_read_from_cache( const file_node_type * file_node , buffer_type * buffer ) { + buffer_fwrite( buffer , file_node->cache , 1 , file_node->cache_size ); +} + + +static void file_node_update_cache( file_node_type * file_node , int data_size , const void * data) { + if (data_size != file_node->cache_size) { + file_node->cache = util_realloc_copy( file_node->cache , data , data_size ); + file_node->cache_size = data_size; + } else + memcpy( file_node->cache , data , data_size); +} + + +static void file_node_clear_cache( file_node_type * file_node ) { + if (file_node->cache != NULL) { + file_node->cache_size = 0; + free(file_node->cache); + file_node->cache = NULL; + } +} +#endif + + +static void file_node_free( file_node_type * file_node ) { + free( file_node ); +#ifdef ENABLE_CACHE + free( file_node->cache ); +#endif +} + + +static void file_node_free__( void * file_node ) { + file_node_free( (file_node_type *) file_node ); +} + + + +static bool file_node_verify_end_tag( const file_node_type * file_node , FILE * stream ) { + int end_tag; + fseek__( stream , file_node->node_offset + file_node->node_size - sizeof NODE_END_TAG , SEEK_SET); + if (fread( &end_tag , sizeof end_tag , 1 , stream) == 1) { + if (end_tag == NODE_END_TAG) + return true; /* All hunkadory. */ + else + return false; + } else + return false; +} + + + +static file_node_type * file_node_fread_alloc( FILE * stream , char ** key) { + file_node_type * file_node = NULL; + node_status_type status; + long int node_offset = ftell( stream ); + if (fread( &status , sizeof status , 1 , stream) == 1) { + if ((status == NODE_IN_USE) || (status == NODE_FREE)) { + int node_size; + if (status == NODE_IN_USE) + *key = util_fread_realloc_string( *key , stream ); + else { + free( *key ); /* Explicitly set to NULL for free nodes. */ + *key = NULL; + } + + node_size = util_fread_int( stream ); + if (node_size <= 0) + status = NODE_INVALID; + /* + A case has occured with an invalid node with size 0. That + resulted in a deadlock, because the reader never got beyond + the broken node. We therefor explicitly check for this + condition. + */ + + file_node = file_node_alloc( status , node_offset , node_size ); + if (status == NODE_IN_USE) { + file_node->data_size = util_fread_int( stream ); + file_node->data_offset = ftell( stream ) - file_node->node_offset; + } + } else { + /* + We did not recognize the status identifier; the node will + eventually be marked as free. + */ + if (status != NODE_WRITE_ACTIVE) + status = NODE_INVALID; + file_node = file_node_alloc( status , node_offset , 0 ); + } + } + return file_node; +} + + +/** + Internal index layout: + + || + || + + /|\ + | + |<-------------------------------------------------------->| + | +node_offset offset + + The node_offset and offset values are not stored on disk, but rather + implicitly read with ftell() calls. +*/ + +/** + This function will write the node information to file, this + includes the NODE_END_TAG identifier which shoule be written to the + end of the node. +*/ + +static void file_node_fwrite( const file_node_type * file_node , const char * key , FILE * stream ) { + if (file_node->node_size == 0) + util_abort("%s: trying to write node with znode_offset , SEEK_SET); + util_fwrite_int( file_node->status , stream ); + if (file_node->status == NODE_IN_USE) + util_fwrite_string( key , stream ); + util_fwrite_int( file_node->node_size , stream ); + util_fwrite_int( file_node->data_size , stream ); + fseek__( stream , file_node->node_offset + file_node->node_size - sizeof NODE_END_TAG , SEEK_SET); + util_fwrite_int( NODE_END_TAG , stream ); + } +} + + +/** + This marks the start and end of the node with the integer tags: + NODE_WRITE_ACTIVE_START and NODE_WRITE_ACTIVE_END, signalling this + section in the data file is 'work in progress', and should be + discarded if the application aborts during the write. + + When the write is complete file_node_fwrite() should be called, + which will replace the NODE_WRITE_ACTIVE_START and + NODE_WRITE_ACTIVE_END tags with NODE_IN_USE and NODE_END_TAG + identifiers. +*/ + + +static void file_node_init_fwrite( const file_node_type * file_node , FILE * stream) { + fseek__( stream , file_node->node_offset , SEEK_SET ); + util_fwrite_int( NODE_WRITE_ACTIVE_START , stream ); + fseek__( stream , file_node->node_offset + file_node->node_size - sizeof NODE_END_TAG , SEEK_SET); + util_fwrite_int( NODE_WRITE_ACTIVE_END , stream ); +} + + +/** + Observe that header in this context include the size of the tail + marker NODE_END_TAG. +*/ + +static int file_node_header_size( const char * filename ) { + file_node_type * file_node; + return sizeof ( file_node->status ) + + sizeof ( file_node->node_size ) + + sizeof ( file_node->data_size ) + + sizeof ( NODE_END_TAG ) + sizeof(int) /* embedded by the util_fwrite_string routine */ + strlen(filename) + 1 /* \0 */; +} + + +static void file_node_set_data_offset( file_node_type * file_node, const char * filename ) { + file_node->data_offset = file_node_header_size( filename ) - sizeof( NODE_END_TAG ); +} + + + +static void file_node_dump_index( const file_node_type * file_node , FILE * index_stream) { + util_fwrite_int( file_node->status , index_stream ); + util_fwrite_long( file_node->node_offset , index_stream ); + util_fwrite_int( file_node->node_size , index_stream ); + util_fwrite_int( file_node->data_offset , index_stream ); + util_fwrite_int( file_node->data_size , index_stream ); +} + + + +/* +static file_node_type * file_node_index_fread_alloc( FILE * stream ) { + node_status_type status = util_fread_int( stream ); + long int node_offset = util_fread_long( stream ); + int node_size = util_fread_int( stream ); + { + file_node_type * file_node = file_node_alloc( status , node_offset , node_size ); + file_node->data_offset = util_fread_int( stream ); + file_node->data_size = util_fread_int( stream ); + + return file_node; + } +} +*/ + +static file_node_type * file_node_index_buffer_fread_alloc( buffer_type * buffer) { + node_status_type status = (node_status_type)buffer_fread_int( buffer ); + long int node_offset = buffer_fread_long( buffer ); + int node_size = buffer_fread_int( buffer ); + { + file_node_type * file_node = file_node_alloc( status , node_offset , node_size ); + + file_node->data_offset = buffer_fread_int( buffer ); + file_node->data_size = buffer_fread_int( buffer ); + + return file_node; + } +} + +/* file_node functions - end. */ +/*****************************************************************/ + +static free_node_type * free_node_alloc( file_node_type * file_node ) { + free_node_type * free_node = (free_node_type*)util_malloc( sizeof * free_node ); + + free_node->file_node = file_node; + free_node->next = NULL; + free_node->prev = NULL; + + return free_node; +} + + +static void free_node_free( free_node_type * free_node ) { + free( free_node ); +} + +static void free_node_free_list( free_node_type * head ) { + free_node_type * current = head; + free_node_type * next; + while (current != NULL) { + next = current->next; + free_node_free( current ); + current = next; + } +} + + + +/*****************************************************************/ +static inline void block_fs_aquire_wlock( block_fs_type * block_fs ) { + if (block_fs->data_owner) + pthread_rwlock_wrlock( &block_fs->rw_lock ); + else + util_abort("%s: tried to write to read only filesystem mounted at: %s \n",__func__ , block_fs->mount_file ); +} + + +static inline void block_fs_release_rwlock( block_fs_type * block_fs ) { + pthread_rwlock_unlock( &block_fs->rw_lock ); +} + + +static inline void block_fs_aquire_rlock( block_fs_type * block_fs ) { + pthread_rwlock_rdlock( &block_fs->rw_lock ); + /* + We just assume that the user does NOT write to the filesystem + with another instance; in that case we will go out of sync; + and things will probably fail badly. + */ +} + + + +static void block_fs_insert_index_node( block_fs_type * block_fs , const char * filename , const file_node_type * file_node) { + hash_insert_ref( block_fs->index , filename , file_node); +} + + +/** + Looks through the list of free nodes - looking for a node with + offset 'node_offset'. If no such node can be found, NULL will be + returned. +*/ + +static file_node_type * block_fs_lookup_free_node( const block_fs_type * block_fs , long int node_offset) { + free_node_type * current = block_fs->free_nodes; + while (current != NULL && (current->file_node->node_offset != node_offset)) + current = current->next; + + if (current == NULL) + return NULL; + else + return current->file_node; +} + + + +/** + Inserts a file_node instance in the linked list of free nodes. The + list is sorted in order of increasing node size. +*/ + +static void block_fs_insert_free_node( block_fs_type * block_fs , file_node_type * file_node ) { + free_node_type * _new = free_node_alloc( file_node ); + + /* Special case: starting with a empty list. */ + if (block_fs->free_nodes == NULL) { + _new->next = NULL; + _new->prev = NULL; + block_fs->free_nodes = _new; + } else { + free_node_type * current = block_fs->free_nodes; + free_node_type * prev = NULL; + + while ( current != NULL && (current->file_node->node_size < file_node->node_size)) { + prev = current; + current = current->next; + } + + if (current == NULL) { + /* + The new node should be added at the end of the list - i.e. it + will not have a next node. + */ + _new->next = NULL; + _new->prev = prev; + prev->next = _new; + } else { + /* + The new node should be placed BEFORE the current node. + */ + if (prev == NULL) { + /* The new node should become the new list head. */ + block_fs->free_nodes = _new; + _new->prev = NULL; + } else { + prev->next = _new; + _new->prev = prev; + } + current->prev = _new; + _new->next = current; + } + if (_new != NULL) if (_new->next == _new) util_abort("%s: broken LIST1 \n",__func__); + if (prev != NULL) if (prev->next == prev) util_abort("%s: broken LIST2 \n",__func__); + if (current != NULL) if (current->next == current) util_abort("%s: Broken LIST3 \n",__func__); + } + block_fs->num_free_nodes++; + block_fs->free_size += _new->file_node->node_size; +} + + +/** + Installing the new node AND updating file tail. +*/ + +static void block_fs_install_node(block_fs_type * block_fs , file_node_type * node) { + block_fs->data_file_size = util_size_t_max( block_fs->data_file_size , node->node_offset + node->node_size); /* Updating the total size of the file - i.e the next available offset. */ + vector_append_owned_ref( block_fs->file_nodes , node , file_node_free__ ); +} + + +static void block_fs_set_filenames( block_fs_type * block_fs ) { + char * data_ext = util_alloc_sprintf("data_%d" , block_fs->version ); + char * lock_ext = util_alloc_sprintf("lock_%d" , block_fs->version ); + const char * index_ext = "index"; + + free( block_fs->data_file ); + free( block_fs->lock_file ); + free( block_fs->index_file ); + + block_fs->data_file = util_alloc_filename( block_fs->path , block_fs->base_name , data_ext); + block_fs->lock_file = util_alloc_filename( block_fs->path , block_fs->base_name , lock_ext); + block_fs->index_file = util_alloc_filename( block_fs->path , block_fs->base_name , index_ext); + + free( data_ext ); + free( lock_ext ); +} + + + +/** + This function is called both when allocating a new block_fs + instance, and when an existing block_fs instance is 'rotated'. +*/ + + +static void block_fs_reinit( block_fs_type * block_fs ) { + block_fs->index = hash_alloc(); + block_fs->file_nodes = vector_alloc_new(); + block_fs->free_nodes = NULL; + block_fs->num_free_nodes = 0; + block_fs->write_count = 0; + block_fs->data_file_size = 0; + block_fs->free_size = 0; + block_fs->total_cache_size = 0; + block_fs_set_filenames( block_fs ); +} + + + + +static block_fs_type * block_fs_alloc_empty( const char * mount_file , + int block_size , + int max_cache_size, + float fragmentation_limit, + int fsync_interval , + bool read_only, + bool use_lockfile) { + block_fs_type * block_fs = (block_fs_type*)util_malloc( sizeof * block_fs ); + UTIL_TYPE_ID_INIT(block_fs , BLOCK_FS_TYPE_ID); + + block_fs->mount_file = util_alloc_string_copy( mount_file ); + block_fs->fsync_interval = fsync_interval; + block_fs->block_size = block_size; + block_fs->max_cache_size = max_cache_size; + block_fs->total_cache_size = 0; + block_fs->max_total_cache_size = 512 * 1024 * 1024; /* 512 MB */ + + block_fs->fragmentation_limit = fragmentation_limit; + util_alloc_file_components( mount_file , &block_fs->path , &block_fs->base_name, NULL ); + pthread_mutex_init( &block_fs->io_lock , NULL); + pthread_rwlock_init( &block_fs->rw_lock , NULL); + { + FILE * stream = util_fopen( mount_file , "r"); + int id = util_fread_int( stream ); + block_fs->version = util_fread_int( stream ); + fclose( stream ); + + if (id != MOUNT_MAP_MAGIC_INT) + util_abort("%s: The file:%s does not seem to be a valid block_fs mount map \n",__func__ , mount_file); + } + block_fs->data_file = NULL; + block_fs->lock_file = NULL; + block_fs->index_file = NULL; + block_fs_reinit( block_fs ); + + + { + bool lock_aquired = true; + + if (use_lockfile) { + lock_aquired = util_try_lockf( block_fs->lock_file , S_IWUSR + S_IWGRP , &block_fs->lock_fd); + + if (!lock_aquired) + fprintf(stderr," Another program has already opened filesystem read-write - this instance will be UNSYNCRONIZED read-only. Cross your fingers ....\n"); + } + + if (lock_aquired && (read_only == false)) + block_fs->data_owner = true; + else + block_fs->data_owner = false; + + } + return block_fs; +} + + +UTIL_IS_INSTANCE_FUNCTION(block_fs , BLOCK_FS_TYPE_ID); + + +static void block_fs_fwrite_mount_info__( const char * mount_file , int version) { + FILE * stream = util_fopen( mount_file , "w"); + util_fwrite_int( MOUNT_MAP_MAGIC_INT , stream ); + util_fwrite_int( version , stream ); + fclose( stream ); + } + + +/** + Will seek the datafile to the end of the current file_node. So that the next read will be "guaranteed" to + start at a new node. +*/ +static void block_fs_fseek_node_end(block_fs_type * block_fs , const file_node_type * file_node) { + block_fs_fseek( block_fs , file_node->node_offset + file_node->node_size); +} + +static void block_fs_fseek_node_data(block_fs_type * block_fs , const file_node_type * file_node) { + block_fs_fseek( block_fs , file_node->node_offset + file_node->data_offset ); +} + + + + +/** + This function will read through the datafile seeking for one of the + identifiers: NODE_IN_USE | NODE_FREE. If one of the valid status + identifiers is found the stream is repositioned at the beginning of + the valid node, so the calling scope can continue with a + + file_node = file_node_date_fread_alloc() + + call. If no valid status ID is found whatsover the data_stream + indicator is left at the end of the file; and the calling scope + will finish from there. +*/ +static bool block_fs_fseek_valid_node( block_fs_type * block_fs ) { + unsigned char byte; + int status; + while (true) { + if (fread(&byte , sizeof byte ,1 , block_fs->data_stream) == 1) { + if (byte == NODE_IN_USE_BYTE || byte == NODE_FREE_BYTE) { + long int pos = ftell( block_fs->data_stream ); + /* + OK - we found one interesting byte; let us try to read the + whole integer and see if we have hit any of the valid status identifiers. + */ + fseek__( block_fs->data_stream , -1 , SEEK_CUR); + if (fread(&status , sizeof status , 1 , block_fs->data_stream) == 1) { + if (status == NODE_IN_USE || status == NODE_FREE_BYTE) { + /* + OK - we have found a valid identifier. We reposition to + the start of this valid status id and return true. + */ + fseek__( block_fs->data_stream , -sizeof status , SEEK_CUR); + return true; + } else + /* + OK - this was not a valid id; we go back and continue + reading single bytes. + */ + block_fs_fseek( block_fs , pos ); + } else + break; /* EOF */ + } + } else + break; /* EOF */ + } + fseek__( block_fs->data_stream , 0 , SEEK_END); + return false; +} + + + + + +/** + The read-only open mode is only for the mount section, where the + data file is read in to load/verify the index. + + If the read_only open fails - the data_stream is set to NULL. If + the open succeeds the calling scope should close the stream before + calling this function again, with read_only == false. +*/ + +static void block_fs_open_data( block_fs_type * block_fs , bool read_write) { + if (read_write) { + /* Normal read-write open.- */ + if (util_file_exists( block_fs->data_file )) + block_fs->data_stream = util_fopen( block_fs->data_file , "r+"); + else + block_fs->data_stream = util_fopen( block_fs->data_file , "w+"); + } else { + /* read-only open. */ + if (util_file_exists( block_fs->data_file )) + block_fs->data_stream = util_fopen( block_fs->data_file , "r"); + else + block_fs->data_stream = NULL; + /* + If we ever try to dereference this pointer it will break + hard; but it should be stopped in hash_get() calls before the + data_stream is dereferenced anyway? + */ + } + if (block_fs->data_stream == NULL) + block_fs->data_fd = -1; + else + block_fs->data_fd = fileno( block_fs->data_stream ); +} + +#ifdef ENABLE_CACHE + +static void block_fs_clear_cache_node( block_fs_type * block_fs , file_node_type * node ) { + if (node->cache_size > 0) { + block_fs->total_cache_size -= node->cache_size; + file_node_clear_cache( node ); + } +} + +static void block_fs_update_cache_node( block_fs_type * block_fs, file_node_type * node, int data_size, const void * data) { + int delta_size = data_size - node->cache_size; + if (delta_size < 0) { + file_node_update_cache( node , data_size , data ); + block_fs->total_cache_size += delta_size; + } else { + if (((block_fs->total_cache_size + delta_size) <= block_fs->max_total_cache_size) && /* Check total cache usage */ + (data_size <= block_fs->max_cache_size)) { /* Chech cache size of this node */ + block_fs->total_cache_size += delta_size; + file_node_update_cache( node , data_size , data ); + } else + block_fs_clear_cache_node( block_fs , node ); + } +} + +/** + This function will load all the small (i.e. with size less than the + maximum cache size) nodes, and fill the cache. +*/ + +static void block_fs_preload( block_fs_type * block_fs ) { + if ((block_fs->max_cache_size > 0) && (block_fs->data_stream != NULL) && (block_fs->max_total_cache_size > 0)) { + void * buffer = util_malloc( block_fs->max_cache_size ); + hash_iter_type * index_iter = hash_iter_alloc( block_fs->index ); + + while (!hash_iter_is_complete( index_iter )) { + file_node_type * node = hash_iter_get_next_value( index_iter ); + if ((node->data_size < block_fs->max_cache_size) && /* Check the size of this node */ + (block_fs->total_cache_size + node->data_size < block_fs->max_total_cache_size)) { /* Check the total cache size */ + block_fs_fseek_node_data(block_fs , node); + util_fread( buffer , 1 , node->data_size , block_fs->data_stream , __func__); + block_fs_update_cache_node( block_fs , node , node->data_size , buffer ); + } + } + + hash_iter_free( index_iter ); + free( buffer ); + } +} +#else +static void block_fs_clear_cache_node( block_fs_type * block_fs , file_node_type * node ) { return; } +static void block_fs_update_cache_node( block_fs_type * block_fs, file_node_type * node, int data_size, const void * data) { return ;} +static void block_fs_preload( block_fs_type * block_fs ) { return; } +#endif + + + +/** + This function will 'fix' the nodes with offset in offset_list. The + fixing in this case means the following: + + 1. The node is updated in place on the file to become a free node. + 2. The node is added to the block_fs instance as a free node, which can + be recycled at a later stage. + + If the instance is not data owner (i.e. read-only) the function + will return immediately. +*/ + + +static void block_fs_fix_nodes( block_fs_type * block_fs , long_vector_type * offset_list ) { + if (block_fs->data_owner) { + fsync( block_fs->data_fd ); + { + char * key = NULL; + for (int inode = 0; inode < long_vector_size( offset_list ); inode++) { + bool new_node = false; + long int node_offset = long_vector_iget( offset_list , inode ); + file_node_type * file_node; + block_fs_fseek(block_fs , node_offset); + file_node = file_node_fread_alloc( block_fs->data_stream , &key ); + + if ((file_node->status == NODE_INVALID) || (file_node->status == NODE_WRITE_ACTIVE)) { + /* This node is really quite broken. */ + long int node_end; + block_fs_fseek_valid_node( block_fs ); + node_end = ftell( block_fs->data_stream ); + file_node->node_size = node_end - node_offset; + } + + file_node->status = NODE_FREE; + file_node->data_size = 0; + file_node->data_offset = 0; + if (block_fs_lookup_free_node( block_fs , node_offset) == NULL) { + /* The node is already on the free list - we just change some metadata. */ + new_node = true; + block_fs_install_node( block_fs , file_node ); + block_fs_insert_free_node( block_fs , file_node ); + } + + block_fs_fseek(block_fs , node_offset); + file_node_fwrite( file_node , NULL , block_fs->data_stream ); + if (!new_node) + file_node_free( file_node ); + } + free( key ); + } + fsync( block_fs->data_fd ); + } +} + + + +static void block_fs_build_index( block_fs_type * block_fs , long_vector_type * error_offset ) { + char * filename = NULL; + file_node_type * file_node; + + hash_resize( block_fs->index , DEFAULT_INDEX_SIZE ); + block_fs_fseek( block_fs , 0); + do { + file_node = file_node_fread_alloc( block_fs->data_stream , &filename ); + if (file_node != NULL) { + if ((file_node->status == NODE_INVALID) || (file_node->status == NODE_WRITE_ACTIVE)) { + if (file_node->status == NODE_INVALID) + fprintf(stderr,"** Warning:: invalid node found at offset:%ld in datafile:%s - data will be lost, node_size:%d\n", file_node->node_offset , block_fs->data_file , file_node->node_size); + else + fprintf(stderr,"** Warning:: file system was prematurely shut down while writing node in %s/%ld - will be discarded.\n",block_fs->data_file , file_node->node_offset); + + long_vector_append( error_offset , file_node->node_offset ); + file_node_free( file_node ); + block_fs_fseek_valid_node( block_fs ); + } else { + if (file_node_verify_end_tag(file_node, block_fs->data_stream)) { + block_fs_fseek_node_end(block_fs , file_node); + block_fs_install_node( block_fs , file_node ); + switch(file_node->status) { + case(NODE_IN_USE): + block_fs_insert_index_node(block_fs , filename , file_node); + break; + case(NODE_FREE): + block_fs_insert_free_node( block_fs , file_node ); + break; + default: + util_abort("%s: node status flag:%d not recognized - error in data file \n",__func__ , file_node->status); + } + } else { + /* + Could not find a valid END_TAG - indicating that + the filesystem was shut down during the write of + this node. This node will NOT be added to the + index. The node will be updated to become a free node. + */ + fprintf(stderr,"** Warning found node:%s at offset:%ld which was incomplete - discarded.\n",filename, file_node->node_offset); + long_vector_append( error_offset , file_node->node_offset ); + file_node_free( file_node ); + block_fs_fseek_valid_node( block_fs ); + } + } + } + } while (file_node != NULL); + free( filename ); +} + + +/** + Load an index for (slightly) faster mounting of the filesystem. The + function starts be reading a header and check if the current index + file is applicable. + + Will return true of the loading succedeed, and false if no index + was loaded. +*/ + + +static bool block_fs_load_index( block_fs_type * block_fs ) { + stat_type data_stat; + if (fstat( block_fs->data_fd , &data_stat) == 0) { + FILE * stream = fopen( block_fs->index_file , "r"); + if (stream != NULL) { + int id = util_fread_int( stream ); + int version = util_fread_int( stream ); + time_t index_mtime = util_fread_time_t( stream ); + + time_t data_mtime = data_stat.st_mtime; + fclose( stream ); + + if ((id == INDEX_MAGIC_INT) && /* This is indeed an index file. */ + (version == INDEX_FORMAT_VERSION) && /* The version on disk agrees with this version. */ + (index_mtime == data_mtime)) { /* The time stamp agrees with the time stamp of the data. */ + + /* Read the whole index file in one single read operation. */ + buffer_type * buffer = buffer_fread_alloc( block_fs->index_file ); + + buffer_fskip( buffer , sizeof( time_t ) + 2 * sizeof( int )); + /*1: Loading all the active nodes. */ + { + int num_active_nodes = buffer_fread_int( buffer ); + hash_resize( block_fs->index , num_active_nodes * 2 + 64); + + for (int i=0; i < num_active_nodes; i++) { + const char * filename = buffer_fread_string( buffer ); + file_node_type * file_node = file_node_index_buffer_fread_alloc( buffer ); + block_fs_install_node( block_fs , file_node); + block_fs_insert_index_node(block_fs , filename , file_node); + } + } + + /*2: Loading all the free nodes. */ + { + int num_free_nodes = buffer_fread_int( buffer ); + for (int i=0; i < num_free_nodes; i++) { + file_node_type * file_node = file_node_index_buffer_fread_alloc( buffer ); + block_fs_install_node( block_fs , file_node); + block_fs_insert_free_node(block_fs , file_node); + } + } + buffer_free( buffer ); + + return true; + } + } + } + /** No index was loaded - for whatever reason. */ + return false; +} + + +bool block_fs_is_readonly( const block_fs_type * bfs ) { + if (bfs->data_owner) + return false; + else + return true; +} + + + +block_fs_type * block_fs_mount( const char * mount_file , + int block_size , + int max_cache_size , + float fragmentation_limit , + int fsync_interval , + bool preload , + bool read_only, + bool use_lockfile) { + block_fs_type * block_fs; + { + + if (!util_file_exists(mount_file)) + /* This is a brand new filesystem - create the mount map first. */ + block_fs_fwrite_mount_info__( mount_file , 0 ); + { + long_vector_type * fix_nodes = long_vector_alloc(0 , 0); + block_fs = block_fs_alloc_empty( mount_file , block_size , max_cache_size , fragmentation_limit , fsync_interval , read_only, use_lockfile); + /* We build up the index & free_nodes_list based on the header/index information embedded in the datafile. */ + block_fs_open_data( block_fs , false ); + if (block_fs->data_stream != NULL) { + if (!block_fs_load_index( block_fs )) + block_fs_build_index( block_fs , fix_nodes ); + + fclose(block_fs->data_stream); + } + + block_fs_open_data( block_fs , block_fs->data_owner ); /* The data_stream is opened for reading AND writing (IFF we are data_owner - otherwise it is still read only) */ + block_fs_fix_nodes( block_fs , fix_nodes ); + long_vector_free( fix_nodes ); + } + } + if (preload) block_fs_preload( block_fs ); + return block_fs; +} + + + +static void block_fs_unlink_free_node( block_fs_type * block_fs , free_node_type * node) { + free_node_type * prev = node->prev; + free_node_type * next = node->next; + + if (prev == NULL) + /* Special case: popping off the head of the list. */ + block_fs->free_nodes = next; + else + prev->next = next; + + if (next != NULL) + next->prev = prev; + + block_fs->num_free_nodes--; + block_fs->free_size -= node->file_node->node_size; + free_node_free( node ); +} + + + +/** + This function first checks the free nodes if any of them can be + used, otherwise a new node is created. +*/ + +static file_node_type * block_fs_get_new_node( block_fs_type * block_fs , const char * filename , size_t min_size) { + + free_node_type * current = block_fs->free_nodes; + + while (current != NULL && (current->file_node->node_size < min_size)) { + current = current->next; + } + if (current != NULL) { + /* + Current points to a file_node which can be used. Before we return current we must: + + 1. Remove current from the free_nodes list. + 2. Add current to the index hash. + + */ + file_node_type * file_node = current->file_node; + block_fs_unlink_free_node( block_fs , current ); + + return file_node; + } else { + /* No usable nodes in the free nodes list - must allocate a brand new one. */ + + long int offset; + int node_size; + file_node_type * new_node; + + { + div_t d = div( min_size , block_fs->block_size ); + node_size = d.quot * block_fs->block_size; + if (d.rem) + node_size += block_fs->block_size; + } + + /* Must lock the total size here ... */ + offset = block_fs->data_file_size; + new_node = file_node_alloc(NODE_IN_USE , offset , node_size); + block_fs_install_node( block_fs , new_node ); /* <- This will update the total file size. */ + + return new_node; + } +} + + + + + +bool block_fs_has_file__( const block_fs_type * block_fs , const char * filename) { + return hash_has_key( block_fs->index , filename ); +} + + + +bool block_fs_has_file( block_fs_type * block_fs , const char * filename) { + bool has_file; + block_fs_aquire_rlock( block_fs ); + { + has_file = block_fs_has_file__( block_fs , filename ); + } + block_fs_release_rwlock( block_fs ); + return has_file; +} + + + + +static void block_fs_unlink_file__( block_fs_type * block_fs , const char * filename ) { + file_node_type * node = (file_node_type*)hash_pop( block_fs->index , filename ); + block_fs_clear_cache_node( block_fs , node ); + + node->status = NODE_FREE; + node->data_offset = 0; + node->data_size = 0; + if (block_fs->data_stream != NULL) { + fsync( block_fs->data_fd ); + block_fs_fseek(block_fs , node->node_offset); + file_node_fwrite( node , NULL , block_fs->data_stream ); + fsync( block_fs->data_fd ); + } + block_fs_insert_free_node( block_fs , node ); +} + +/** + Returns the fraction of unused space in the block_fs instance. +*/ +static double get_fragmentation( const block_fs_type * block_fs ) { + return block_fs->free_size * 1.0 / block_fs->data_file_size; +} + + +void block_fs_unlink_file( block_fs_type * block_fs , const char * filename) { + block_fs_aquire_wlock( block_fs ); + + block_fs_unlink_file__( block_fs , filename ); + if (get_fragmentation( block_fs ) > block_fs->fragmentation_limit) + block_fs_rotate__( block_fs ); + + block_fs_release_rwlock( block_fs ); +} + +/* + It seems it is not enough to call fsync(); must also issue this + funny fseek + ftell combination to ensure that all data is on + disk after an uncontrolled shutdown. + + Could possibly use fdatasync() to improve speed slightly? +*/ + +void block_fs_fsync( block_fs_type * block_fs ) { + if (block_fs->data_owner) { + //fdatasync( block_fs->data_fd ); + fsync( block_fs->data_fd ); + block_fs_fseek( block_fs , block_fs->data_file_size ); + ftell( block_fs->data_stream ); + } +} + + + + +/** + The single lowest-level write function: + + 3. seek to correct position. + 4. Write the data with util_fwrite() + + 7. increase the write_count + 8. set the data_size field of the node. + + Observe that when 'designing' this file-system the priority has + been on read-spead, one consequence of this is that all write + operations are sandwiched between two fsync() calls; that + guarantees that the read access (which should be the fast path) can + be without any calls to fsync(). + + Not necessary to lock - since all writes are protected by the + 'global' rwlock anyway. +*/ + + +static void block_fs_fwrite__(block_fs_type * block_fs , const char * filename , file_node_type * node , const void * ptr , int data_size) { + +#ifdef ENABLE_CACHE + if ((node->cache_size == data_size) && (memcmp( ptr , node->cache , data_size ) == 0)) + /* The current cache is identical to the data we are attempting to write - can leave immediately. */ + return; +#else + if (false) + return; +#endif + + else { + block_fs_fseek(block_fs , node->node_offset); + node->status = NODE_IN_USE; + node->data_size = data_size; + file_node_set_data_offset( node , filename ); + + /* This marks the node section in the datafile as write in progress with: NODE_WRITE_ACTIVE_START ... NODE_WRITE_ACTIVE_END */ + file_node_init_fwrite( node , block_fs->data_stream ); + + /* Writes the actual data content. */ + block_fs_fseek_node_data(block_fs , node); + util_fwrite( ptr , 1 , data_size , block_fs->data_stream , __func__); + + /* Writes the file node header data, including the NODE_END_TAG. */ + file_node_fwrite( node , filename , block_fs->data_stream ); + + block_fs_update_cache_node( block_fs , node , data_size , ptr); + block_fs->write_count++; + if (block_fs->fsync_interval && ((block_fs->write_count % block_fs->fsync_interval) == 0)) + block_fs_fsync( block_fs ); + + } +} + + + +static void block_fs_fwrite_file_unlocked(block_fs_type * block_fs , const char * filename , const void * ptr , size_t data_size) { + file_node_type * file_node; + bool new_node = true; + size_t min_size = data_size + file_node_header_size( filename ); + + if (block_fs_has_file__( block_fs , filename )) { + file_node = (file_node_type*)hash_get( block_fs->index , filename ); + if (file_node->node_size < min_size) { + /* + The current node is too small for the new content: + + 1. Remove the existing node, from the index and insert it + into the free_nodes list. + + 2. Get a new node. + + */ + block_fs_unlink_file__( block_fs , filename ); + file_node = block_fs_get_new_node( block_fs , filename , min_size ); + } else + new_node = false; /* We are reusing the existing node. */ + } else + file_node = block_fs_get_new_node( block_fs , filename , min_size ); + + + /* The actual writing ... */ + block_fs_fwrite__( block_fs , filename , file_node , ptr , data_size); + if (new_node) + block_fs_insert_index_node(block_fs , filename , file_node); +} + + + +void block_fs_fwrite_file(block_fs_type * block_fs , const char * filename , const void * ptr , size_t data_size) { + block_fs_aquire_wlock( block_fs ); + { + block_fs_fwrite_file_unlocked( block_fs , filename , ptr , data_size ); + + /* OKAY - this is going to take some time ... */ + if ((block_fs->free_size * 1.0 / block_fs->data_file_size) > block_fs->fragmentation_limit) + block_fs_rotate__( block_fs ); + + } + block_fs_release_rwlock( block_fs ); +} + +void block_fs_fwrite_buffer(block_fs_type * block_fs , const char * filename , const buffer_type * buffer) { + block_fs_fwrite_file( block_fs , filename , buffer_get_data( buffer ) , buffer_get_size( buffer )); +} + +/** + Reads the full content of 'filename' into the buffer. +*/ + +void block_fs_fread_realloc_buffer( block_fs_type * block_fs , const char * filename , buffer_type * buffer) { + block_fs_aquire_rlock( block_fs ); + { + file_node_type * node = (file_node_type*)hash_get( block_fs->index , filename); + + buffer_clear( buffer ); /* Setting: content_size = 0; pos = 0; */ + { + /* + Going low-level + */ + +#ifdef ENABLE_CACHE + if (node->cache != NULL) + file_node_buffer_read_from_cache( node , buffer ); + else +#else + if (true) +#endif + + { + pthread_mutex_lock( &block_fs->io_lock ); + block_fs_fseek_node_data(block_fs , node ); + buffer_stream_fread( buffer , node->data_size , block_fs->data_stream ); + //file_node_verify_end_tag( node , block_fs->data_stream ); + pthread_mutex_unlock( &block_fs->io_lock ); + } + + } + buffer_rewind( buffer ); /* Setting: pos = 0; */ + } + block_fs_release_rwlock( block_fs ); +} + +static void block_fs_dump_index( block_fs_type * block_fs ) { + if (block_fs->data_owner) { + struct stat stat_buffer; + int stat_return = stat(block_fs->data_file , &stat_buffer); + if (stat_return != 0) + return; + { + time_t data_mtime = stat_buffer.st_mtime; + FILE * index_stream = util_fopen( block_fs->index_file , "w"); + util_fwrite_int( INDEX_MAGIC_INT , index_stream ); + util_fwrite_int( INDEX_FORMAT_VERSION , index_stream ); + util_fwrite_time_t( data_mtime , index_stream ); + + /* 1: Dumping the hash table of active nodes. */ + { + hash_iter_type * index_iter = hash_iter_alloc( block_fs->index ); + + util_fwrite_int( hash_get_size( block_fs->index ) , index_stream); + while (!hash_iter_is_complete( index_iter )) { + const char * key = hash_iter_get_next_key( index_iter ); + const file_node_type * file_node = (const file_node_type*)hash_get( block_fs->index , key ); + + util_fwrite_string( key , index_stream); + file_node_dump_index( file_node , index_stream ); + } + hash_iter_free( index_iter ); + } + + /* 2: Dumping information about empty slots in the datafile. */ + util_fwrite_int( block_fs->num_free_nodes , index_stream ); + { + free_node_type * current = block_fs->free_nodes; + while ( current != NULL) { + file_node_dump_index( current->file_node , index_stream ); + current = current->next; + } + } + + fclose( index_stream ); + } + } +} + + +/** + Close/synchronize the open file descriptors and free all memory + related to the block_fs instance. + + If the boolean unlink_empty is set to true all the files will be + unlinked if the filesystem is empty. +*/ + +void block_fs_close( block_fs_type * block_fs , bool unlink_empty) { + block_fs_fsync( block_fs ); + + if (block_fs->data_owner) + block_fs_aquire_wlock( block_fs ); + + if (block_fs->data_stream != NULL) + fclose( block_fs->data_stream ); + + if (block_fs->data_owner) + block_fs_dump_index( block_fs ); + + if (block_fs->lock_fd > 0) { + close( block_fs->lock_fd ); /* Closing the lock_file file descriptor - and releasing the lock. */ + util_unlink_existing( block_fs->lock_file ); + } + + if (block_fs->data_owner) { + if ( unlink_empty && (hash_get_size( block_fs->index) == 0)) { + util_unlink_existing( block_fs->data_file ); + util_unlink_existing( block_fs->index_file ); + util_unlink_existing( block_fs->mount_file ); + } + block_fs_release_rwlock( block_fs ); + } + + free( block_fs->index_file ); + free( block_fs->lock_file ); + free( block_fs->base_name ); + free( block_fs->data_file ); + free( block_fs->path ); + free( block_fs->mount_file ); + + free_node_free_list( block_fs->free_nodes ); + hash_free( block_fs->index ); + vector_free( block_fs->file_nodes ); + free( block_fs ); +} + + + + + +/** + This function will 'rotate' the datafile to a new version which has + been defragmented, i.e. with no 'holes' in it. In the process the + datafile version number is increased with one. The function works + by using the regular block_fs read and write functions. + + Observe that the block_fs instance should hold the write lock when + entering this function. +*/ + +static void block_fs_rotate__( block_fs_type * block_fs ) { + /* + Write a updated mount map where the version info has been bumped + up with one; the new_fs will mount based on this mount_file. + */ + block_fs->version++; + block_fs_fwrite_mount_info__( block_fs->mount_file , block_fs->version ); + { + vector_type * old_nodes = block_fs->file_nodes; + hash_type * old_index = block_fs->index; + FILE * old_data_stream = block_fs->data_stream; + free_node_type * old_free_nodes = block_fs->free_nodes; + char * old_data_file = util_alloc_string_copy( block_fs->data_file ); + char * old_lock_file = util_alloc_string_copy( block_fs->lock_file ); + + block_fs_reinit( block_fs ); + /** + Now the block_fs pointers point to the new copy. Must use the + old_xxx pointers to access the existing. + */ + block_fs_open_data( block_fs , block_fs->data_owner ); + { + hash_iter_type * iter = hash_iter_alloc( old_index ); + buffer_type * buffer = buffer_alloc(1024); + + while (!hash_iter_is_complete( iter )) { + const char * key = hash_iter_get_next_key( iter ); + file_node_type * old_node = (file_node_type*)hash_get( old_index , key ); + buffer_clear( buffer ); + + /* Low level read of the old file. */ + fseek__( old_data_stream , old_node->node_offset + old_node->data_offset , SEEK_SET ); + buffer_stream_fread( buffer , old_node->data_size , old_data_stream ); + + block_fs_fwrite_file_unlocked( block_fs , key , buffer_get_data( buffer ) , buffer_get_size( buffer )); /* Normal write to the new file. */ + } + + buffer_free( buffer ); + hash_iter_free( iter ); + } + /* + OK - everything has been played over, and we should clean up the old fs: + + 1. Close the old data stream. + 2. Unlink the old lockfile. + 3. Delete the old data file. + 4. Delete the old list of free nodes. + 5. free() + + */ + fclose( old_data_stream ); + unlink( old_data_file ); + unlink( old_lock_file ); + free( old_lock_file ); + free( old_data_file ); + + free_node_free_list( old_free_nodes ); + hash_free( old_index ); + vector_free( old_nodes ); + } +} + + +/*****************************************************************/ +/* Functions related to 'ls' like functionality. */ +/*****************************************************************/ + + +/* + Help structure used for 'ls' like funtionality. +*/ + +struct user_file_node_struct { + const file_node_type * file_node; + char * filename; +}; + + +static user_file_node_type * user_file_node_alloc( const char * name , const file_node_type * file_node) { + user_file_node_type * user_node = (user_file_node_type*)util_malloc( sizeof * user_node ); + + user_node->filename = util_alloc_string_copy( name ); /* name can be NULL */ + user_node->file_node = file_node; + + return user_node; +} + + +static void user_file_node_free( user_file_node_type * node ) { + free( node->filename ); + free( node ); +} + +static void user_file_node_free__( void * node ) { + user_file_node_free( (user_file_node_type *) node ); +} + +static int offset_cmp( const void * arg1 , const void * arg2 ) { + const user_file_node_type * node1 = (user_file_node_type *) arg1; + const user_file_node_type * node2 = (user_file_node_type *) arg2; + + if (node1->file_node->node_offset > node2->file_node->node_offset) + return 1; + else + return -1; +} + + +static int string_cmp( const void * arg1 , const void * arg2 ) { + const user_file_node_type * node1 = (user_file_node_type *) arg1; + const user_file_node_type * node2 = (user_file_node_type *) arg2; + + if (node1->filename == NULL) + return 1; + else if (node2->filename == NULL) + return -1; + else + return strcmp( node1->filename , node2->filename ); +} + + +static bool pattern_match( const char * pattern , const char * string ) { + if (pattern == NULL) + return true; + else { + if (fnmatch( pattern , string , 0 ) == 0) + return true; + else + return false; + } +} + +/** + If pattern == NULL all files will be selected. Observe that the + returned vector contains pointers to the "real" file_node instances + - this has two consequences: + + 1. The calling scope should N O T change the elements in the + vector - that will lead to internal corruption of the block_fs + instance. + + 2. If normal read/write/unlink operations are performed on the + block_fs instance while the vector is held, the content of the + vector will go out of sync. + +*/ + +vector_type * block_fs_alloc_filelist( block_fs_type * block_fs , const char * pattern , block_fs_sort_type sort_mode , bool include_free_nodes ) { + vector_type * sort_vector = vector_alloc_new(); + + /* Inserting the nodes from the index. */ + block_fs_aquire_rlock( block_fs ); + { + hash_iter_type * iter = hash_iter_alloc( block_fs->index ); + while ( !hash_iter_is_complete( iter )) { + const char * key = hash_iter_get_next_key( iter ); + file_node_type * node = (file_node_type*)hash_get( block_fs->index , key ); + if (pattern_match( pattern , key )) { + user_file_node_type * unode = user_file_node_alloc( key , node ); + vector_append_owned_ref( sort_vector , unode , user_file_node_free__ ); + } + } + hash_iter_free( iter ); + } + block_fs_release_rwlock( block_fs ); + + if (pattern != NULL) + include_free_nodes = false; /* Doing fnmatch on free nodes makes no sense */ + + /* Inserting the free nodes - the holes. */ + if (include_free_nodes) { + free_node_type * current = block_fs->free_nodes; + while (current != NULL) { + user_file_node_type * unode = user_file_node_alloc( NULL , current->file_node ); + vector_append_owned_ref( sort_vector , unode , user_file_node_free__ ); + current = current->next; + } + } + + switch( sort_mode ) { + case(STRING_SORT): + vector_sort(sort_vector , string_cmp); + break; + case(OFFSET_SORT): + vector_sort(sort_vector , offset_cmp); + break; + case(NO_SORT): + break; + } + + return sort_vector; +} diff --git a/libres/lib/res_util/es_testdata.cpp b/libres/lib/res_util/es_testdata.cpp new file mode 100644 index 00000000000..ae4f8c78e0f --- /dev/null +++ b/libres/lib/res_util/es_testdata.cpp @@ -0,0 +1,298 @@ +/* + Copyright (C) 2019 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 + +#define ROW_MAJOR_STORAGE true + +namespace res { + +namespace { + +class pushd { +public: + pushd(const std::string& path, bool mkdir = false) { + if (!util_is_directory(path.c_str())) { + if (mkdir) + util_make_path(path.c_str()); + } + + if (!util_is_directory(path.c_str())) + throw std::invalid_argument("The path: " + path + " does not exist - can not proceed"); + + this->org_cwd = util_alloc_cwd(); + util_chdir(path.c_str()); + } + + + ~pushd() { + util_chdir(this->org_cwd); + free(this->org_cwd); + } + +private: + char * org_cwd; +}; + +matrix_type * alloc_load(const std::string& name, int rows, int columns) { + if (!util_file_exists(name.c_str())) + return NULL; + + FILE * stream = util_fopen(name.c_str(), "r"); + matrix_type * m = matrix_alloc(rows, columns); + matrix_fscanf_data(m, ROW_MAJOR_STORAGE, stream); + fclose(stream); + + return m; +} + +void save_matrix_data(const std::string& name, const matrix_type * m) { + FILE * stream = util_fopen(name.c_str(), "w"); + matrix_fprintf_data(m, ROW_MAJOR_STORAGE, stream); + fclose(stream); +} + + +std::array load_size() { + int active_ens_size, active_obs_size; + + FILE * stream = fopen("size", "r"); + if (!stream) + throw std::invalid_argument("Could not find file: size with ens_size, obs_size information in the test directory."); + + int read_count = fscanf(stream, "%d %d", &active_ens_size, &active_obs_size); + if (read_count != 2) + throw std::invalid_argument("Failed to read ens_size obs_size from size file"); + + fclose(stream); + + return {active_ens_size, active_obs_size}; +} + +void save_size(int ens_size, int obs_size) { + FILE * stream = util_fopen("size", "w"); + fprintf(stream, "%d %d\n", ens_size, obs_size); + fclose(stream); +} + +matrix_type * safe_copy(const matrix_type * m) { + if (m) + return matrix_alloc_copy(m); + + return nullptr; +} + + +void matrix_delete_row_column(matrix_type * m1, int row_column) { + matrix_delete_row(m1, row_column); + matrix_delete_column(m1, row_column); +} + + +} + + + + + +matrix_type * es_testdata::alloc_matrix(const std::string& fname, int rows, int columns) const { + pushd tmp_path(this->path); + + matrix_type * m = alloc_load(fname, rows, columns); + return m; +} + + +void es_testdata::save_matrix(const std::string& name, const matrix_type * m) const { + pushd tmp_path(this->path); + + FILE * stream = util_fopen(name.c_str(), "w"); + matrix_fprintf_data(m, ROW_MAJOR_STORAGE, stream); + fclose(stream); +} + +es_testdata::es_testdata(const matrix_type* S, const matrix_type * R, const matrix_type * dObs, const matrix_type *D , const matrix_type * E) + : S(safe_copy(S)), + R(safe_copy(R)), + dObs(safe_copy(dObs)), + D(safe_copy(D)), + E(safe_copy(E)), + active_ens_size(matrix_get_columns(S)), + active_obs_size(matrix_get_rows(S)), + obs_mask(bool_vector_alloc(active_obs_size, true)), + ens_mask(bool_vector_alloc(active_ens_size, true)) +{ +} + + +void es_testdata::deactivate_obs(int iobs) { + if (iobs >= bool_vector_size( this->obs_mask )) + throw std::invalid_argument("Obs number: " + std::to_string(iobs) + " out of reach"); + + if (bool_vector_iget(this->obs_mask, iobs)) { + bool_vector_iset(this->obs_mask, iobs, false); + + matrix_delete_row(this->dObs, iobs); + matrix_delete_row(this->S, iobs); + matrix_delete_row_column(this->R, iobs); + + if (this->E) + matrix_delete_row(this->E, iobs); + + if (this->D) + matrix_delete_row(this->D, iobs); + + this->active_obs_size -= 1; + } +} + +void es_testdata::deactivate_realization(int iens) { + if (iens >= bool_vector_size( this->ens_mask )) + throw std::invalid_argument("iRealization number: " + std::to_string(iens) + " out of reach"); + + if (bool_vector_iget(this->ens_mask, iens)) { + bool_vector_iset(this->ens_mask, iens, false); + + matrix_delete_column(this->S, iens); + + if (this->E) + matrix_delete_column(this->E, iens); + + if (this->D) + matrix_delete_column(this->D, iens); + + this->active_ens_size -= 1; + } +} + +es_testdata::es_testdata(const char * path) : + path(path), + S(nullptr), + E(nullptr), + R(nullptr), + D(nullptr), + dObs(nullptr) +{ + pushd tmp_path(this->path); + + auto size = load_size(); + this->active_ens_size = size[0]; + this->active_obs_size = size[1]; + + this->S = alloc_load("S", this->active_obs_size, this->active_ens_size); + this->E = alloc_load("E", this->active_obs_size, this->active_ens_size); + this->R = alloc_load("R", this->active_obs_size, this->active_obs_size); + this->D = alloc_load("D", this->active_obs_size, this->active_ens_size); + this->dObs = alloc_load("dObs", this->active_obs_size, 2); + this->obs_mask = bool_vector_alloc(this->active_obs_size, true); + this->ens_mask = bool_vector_alloc(this->active_ens_size, true); +} + + +es_testdata::~es_testdata() { + if (this->S) + matrix_free(this->S); + + if (this->E) + matrix_free(this->E); + + if (this->R) + matrix_free(this->R); + + if (this->D) + matrix_free(this->D); + + if (this->dObs) + matrix_free(this->dObs); + + bool_vector_free(this->obs_mask); + bool_vector_free(this->ens_mask); +} + + +void es_testdata::save(const std::string& path) const { + pushd tmp_path(path, true); + save_size(this->active_ens_size, this->active_obs_size); + + if (this->S) + save_matrix_data("S", S); + + if (this->E) + save_matrix_data("E", E); + + if (this->R) + save_matrix_data("R", R); + + if (this->D) + save_matrix_data("D", D); + + if (this->dObs) + save_matrix_data("dObs", dObs); +} + + +/* + This function will allocate a matrix based on data found on disk. The data on + disk is only the actual content of the matrix, in row_major order. Before the + matrix is constructed it is verified that the number of elements is a multiple + of this->active_ens_size. +*/ + +matrix_type * es_testdata::alloc_state(const std::string& name) const { + std::vector data; + { + pushd tmp_path(this->path); + FILE * stream = fopen(name.c_str(), "r"); + if (!stream) + throw std::invalid_argument("No such state matrix: " + this->path + "/" + name); + + while (true) { + double value; + int read_count = fscanf(stream, "%lg", &value); + if (read_count == 1) + data.push_back(value); + else + break; + } + + fclose(stream); + } + + if ((data.size() % this->active_ens_size) != 0) + throw std::invalid_argument("Number of elements in file with state informaton must be a multiple of ensemble_size: " + std::to_string(this->active_ens_size)); + + int state_size = data.size() / this->active_ens_size; + matrix_type * state = matrix_alloc(state_size, this->active_ens_size); + for (int is=0; is < state_size; is++) { + for (int iens=0; iens < this->active_ens_size; iens++) { + matrix_iset(state, is, iens, data[ iens + is * this->active_ens_size ]); + } + } + + return state; +} + +} diff --git a/libres/lib/res_util/log.cpp b/libres/lib/res_util/log.cpp new file mode 100644 index 00000000000..511bc5637c8 --- /dev/null +++ b/libres/lib/res_util/log.cpp @@ -0,0 +1,219 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file '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 +#include + +#include "ert/util/build_config.h" + +#ifdef HAVE_FSYNC +#include +#endif + +#ifdef HAVE_PTHREAD +#include +#endif + +#include + +#include + +struct log_struct { + char * filename; + FILE * stream; + int fd; + message_level_type log_level; + message_level_type log_level_stdout; + int msg_count; + bool stream_owner; +#ifdef HAVE_PTHREAD + pthread_mutex_t mutex; +#endif +}; + + + +static void log_delete_empty(const log_type * logh) { + if (!logh->filename) + return; + + if (!util_file_exists(logh->filename)) + return; + + if (util_file_size(logh->filename) == 0) + remove( logh->filename ); +} + + +const char * log_get_filename( const log_type * logh ) { + return logh->filename; +} + +int log_get_msg_count( const log_type * logh) { + return logh->msg_count; +} + +/** + * If an incoming message is below or equal to the configured log_level, it is included. So a high log_level will + * include more messages. + */ +void log_set_level( log_type * logh , message_level_type log_level) { + logh->log_level = log_level; +} + + + +log_type * log_open_stream(FILE * stream, message_level_type log_level) { + if (!stream) + return NULL; + + log_type * logh = (log_type *) util_malloc(sizeof * logh); + logh->msg_count = 0; + logh->log_level = log_level; + logh->log_level_stdout = LOG_ERROR; // non-configurable default + logh->stream = stream; + logh->fd = fileno( logh->stream ); + logh->filename = NULL; + logh->stream_owner = false; +#ifdef HAVE_PTHREAD + pthread_mutex_init( &logh->mutex , NULL ); +#endif + + return logh; +} + + +log_type * log_open_file(const char * filename , message_level_type log_level) { + if (!filename) + return NULL; + + { + char * path = util_split_alloc_dirname(filename); + if (path) { + + if (!util_is_directory(path)) { + if (!util_mkdir_p(path)) { + free(path); + return NULL; + } + } + + free(path); + } + } + + + FILE * stream = fopen( filename, "a+"); + if (!stream) + return NULL; + + + log_type * logh = log_open_stream(stream, log_level); + if (logh) { + logh->filename = util_alloc_string_copy(filename); + logh->stream_owner = true; + } + + return logh; +} + + +static bool log_include_message_stdout(const log_type *logh, + message_level_type message_level) { + return message_level >= logh->log_level_stdout; +} + +static bool log_include_message(const log_type *logh, message_level_type message_level) { + return message_level >= logh->log_level; +} + +void log_add_message_stream(FILE * stream, bool add_timestamp, message_level_type message_level, const char * message) { + struct tm time_fields; + time_t epoch_time; + + time(&epoch_time); + util_time_utc(&epoch_time , &time_fields); + + if (message_level >= LOG_CRITICAL) + fprintf(stream, "CRITICAL: "); + else if (message_level >= LOG_ERROR) + fprintf(stream, "ERROR: "); + else if (message_level >= LOG_WARNING) + fprintf(stream, "WARNING: "); + else if (message_level >= LOG_INFO) + fprintf(stream, "INFO: "); + else if (message_level >= LOG_DEBUG) + fprintf(stream, "DEBUG: "); + + if (add_timestamp) { + if (message) + fprintf(stream,"%02d/%02d - %02d:%02d:%02d %s\n",time_fields.tm_mday, time_fields.tm_mon + 1, time_fields.tm_hour , time_fields.tm_min , time_fields.tm_sec , message); + else + fprintf(stream,"%02d/%02d - %02d:%02d:%02d \n",time_fields.tm_mday, time_fields.tm_mon + 1, time_fields.tm_hour , time_fields.tm_min , time_fields.tm_sec); + } else + if (message) + fprintf(stream, "%s\n", message); + + +} + +void log_add_message(log_type *logh, + message_level_type message_level, + const char* message) { + if (log_include_message_stdout(logh, message_level)) + printf("%s\n", message); // temporary implementation of logging to terminal + + if (!log_include_message(logh, message_level)) + return; + + if (logh->stream == NULL) + util_abort("%s: logh->stream == NULL - must call log_reset_filename() first \n",__func__); + +#ifdef HAVE_PTHREAD + pthread_mutex_lock( &logh->mutex ); +#endif + + log_add_message_stream(logh->stream, true, message_level, message); + log_sync( logh ); + logh->msg_count++; + +#ifdef HAVE_PTHREAD + pthread_mutex_unlock( &logh->mutex ); +#endif +} + + +void log_sync(log_type * logh) { +#ifdef HAVE_FSYNC + fsync( logh->fd ); +#endif + fseek( logh->stream , 0 , SEEK_END ); +} + + + +void log_close( log_type * logh ) { + if (logh->stream_owner) + fclose( logh->stream ); /* This closes BOTH the FILE * stream and the integer file descriptor. */ + log_delete_empty( logh ); + free( logh->filename ); + free( logh ); +} diff --git a/libres/lib/res_util/matrix.cpp b/libres/lib/res_util/matrix.cpp new file mode 100644 index 00000000000..a20b7cf6132 --- /dev/null +++ b/libres/lib/res_util/matrix.cpp @@ -0,0 +1,1610 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'matrix.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 + +#ifdef __APPLE__ + #include +#else + #include +#endif + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +/** + This is V E R Y S I M P L E matrix implementation. It is not + designed to be fast/efficient or anything. It is purely a minor + support functionality for the enkf program, and should N O T be + considered as general matrix functionality. +*/ + + + +/** + Many of matrix functions can potentially involve laaarge amounts of + memory. The functions: + + o matrix_alloc(), matrix_resize() and matrix_alloc_copy() will + abort with util_abort() if the memory requirements can not be + satisfied. + + So the expression "safe" should be interpreted as 'can not abort' - + however the responsability of the calling scope is greater when it + comes to using these functions - things can surely blow up! +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#define MATRIX_TYPE_ID 712108 + +/*#define GET_INDEX(m,i,j) (m->row_stride * (i) + m->column_stride * (j))*/ + +/* + This GET_INDEX function has been forcely inlined for performance. +*/ +/*static size_t GET_INDEX( const matrix_type * m , size_t i , size_t j) { + return m->row_stride *i + m->column_stride *j; +}*/ + +static size_t MATRIX_DATA_SIZE( const matrix_type * m) { + size_t col = m->columns; + size_t stride = m->column_stride; + + return col*stride; +} + + + + +static void matrix_init_header(matrix_type * matrix , int rows , int columns , int row_stride , int column_stride) { + + if (!((column_stride * columns <= row_stride) || (row_stride * rows <= column_stride))) + util_abort("%s: invalid stride combination \n",__func__); + + matrix->data_size = 0; + matrix->alloc_rows = rows; + matrix->alloc_columns = columns; + matrix->row_stride = row_stride; + matrix->column_stride = column_stride; + + matrix_full_size( matrix ); +} + + +/** + This is the low-level function allocating storage. If the input + flag 'safe_mode' is equal to true, the function will return NULL if + the allocation fails, otherwise the function will abort() if the + allocation fails. + + Before returning all elements will be initialized to zero. + + 1. It is based on first free() of the original pointer, and then + subsequently calling malloc() to get new storage. This is to + avoid prohibitive temporary memory requirements during the + realloc() call. + + 2. If the malloc() fails the function will return NULL, i.e. you + will NOT keep the original data pointer. I.e. in this case the + matrix will be invalid. It is the responsability of the calling + scope to do the right thing. + + 3. realloc() functionality - i.e. keeping the original content of + the matrix is implemented at higher level. The memory layout of + the matrix will in general change anyway; so the promise made + by realloc() is not very interesting. +*/ + +static void matrix_realloc_data__( matrix_type * matrix , bool safe_mode ) { + if (matrix->data_owner) { + size_t data_size = MATRIX_DATA_SIZE( matrix ); + if (matrix->data_size == data_size) return; + if (matrix->data != NULL) + free(matrix->data); + + if (safe_mode) { + /* + If safe_mode == true it is 'OK' to fail in the allocation, + otherwise we use util_malloc() which will abort if the memory + is not available. + */ + matrix->data = (double*)malloc( sizeof * matrix->data * data_size ); + } else + matrix->data = (double*)util_malloc( sizeof * matrix->data * data_size ); + + + /* Initializing matrix content to zero. */ + if (matrix->data != NULL) { + size_t i; + for (i = 0; i < data_size; i++) + matrix->data[i] = 0; + } else + data_size = 0; + + /** + Observe that if the allocation failed the matrix will + be returned with data == NULL, and data_size == 0. + */ + matrix->data_size = data_size; + } else + util_abort("%s: can not manipulate memory when is not data owner\n",__func__); +} + + +UTIL_SAFE_CAST_FUNCTION( matrix , MATRIX_TYPE_ID ) + +/** + The matrix objecty is NOT ready for use after this function. +*/ +static matrix_type * matrix_alloc_empty( ) { + matrix_type * matrix = (matrix_type*)util_malloc( sizeof * matrix ); + UTIL_TYPE_ID_INIT( matrix , MATRIX_TYPE_ID ); + matrix->name = NULL; + return matrix; +} + +/* + The freshly allocated matrix is explicitly initialized to zero. If + the variable safe_mode equals true the function will return NULL if + the allocation of data fails, otherwise it will abort() if the + allocation fails. +*/ +static matrix_type * matrix_alloc_with_stride(int rows , int columns , int row_stride , int column_stride, bool safe_mode) { + matrix_type * matrix = NULL; + if ((rows > 0) && (columns > 0)) { + matrix = matrix_alloc_empty(); + matrix->data = NULL; + matrix->data_size = 0; + matrix_init_header( matrix , rows , columns , row_stride , column_stride); + matrix->data_owner = true; + matrix_realloc_data__( matrix , safe_mode ); + if (safe_mode) { + if (matrix->data == NULL) { + /* Allocation failed - we return NULL */ + matrix_free(matrix); + matrix = NULL; + } + } + } + return matrix; +} + + +void matrix_set_name( matrix_type * matrix , const char * name) { + matrix->name = util_realloc_string_copy( matrix->name , name ); +} + + +/** + This function will allocate a matrix object where the data is + shared with the 'src' matrix. A matrix allocated in this way can be + used with all the matrix_xxx functions, but you should be careful + when exporting the data pointer to e.g. lapack routines. +*/ + +matrix_type * matrix_alloc_shared(const matrix_type * src , int row , int column , int rows , int columns) { + if (((row + rows) > src->rows) || ((column + columns) > src->columns)) + util_abort("%s: Invalid matrix subsection src:[%d,%d] Offset:[%d,%d] SubSize:[%d,%d] \n", + __func__, + src->rows , src->columns, + row,column, + rows,columns); + + { + matrix_type * matrix = matrix_alloc_empty(); + + matrix_init_header( matrix , rows , columns , src->row_stride , src->column_stride); + matrix->data = &src->data[ GET_INDEX(src , row , column) ]; + matrix->data_owner = false; + + return matrix; + } +} + + +/*****************************************************************/ + +static matrix_type * matrix_alloc__(int rows, int columns , bool safe_mode) { + return matrix_alloc_with_stride( rows , columns , 1 , rows , safe_mode ); /* Must be the stride (1,rows) to use the lapack routines. */ +} + +matrix_type * matrix_alloc(int rows, int columns) { + return matrix_alloc__( rows , columns , false ); +} + +matrix_type * matrix_alloc_identity(int dim) { + if (dim < 1) + util_abort("%s: identity matrix must have positive size. \n",__func__); + + matrix_type * idty = matrix_alloc(dim, dim); + for (int i = 0; i < dim; ++i) + matrix_iset(idty, i, i, 1); + return idty; +} + +/*****************************************************************/ + +/** + Will not respect strides - that is considered low level data + layout. +*/ +static matrix_type * matrix_alloc_copy__( const matrix_type * src , bool safe_mode) { + matrix_type * copy = matrix_alloc__( matrix_get_rows( src ), matrix_get_columns( src ) , safe_mode); + if (copy != NULL) + matrix_assign(copy , src); + return copy; +} + + +matrix_type * matrix_alloc_copy(const matrix_type * src) { + return matrix_alloc_copy__(src , false ); +} + +matrix_type * matrix_alloc_column_compressed_copy(const matrix_type * src, const bool_vector_type * mask) { + if (bool_vector_size( mask ) != matrix_get_columns( src )) + util_abort("%s: size mismatch. Src matrix has %d rows mask has:%d elements\n", __func__ , matrix_get_rows( src ) , bool_vector_size( mask )); + { + int target_columns = bool_vector_count_equal( mask , true ); + matrix_type * target = matrix_alloc( matrix_get_rows( src ) , target_columns ); + + matrix_column_compressed_memcpy( target , src , mask ); + return target; + } +} + + +void matrix_column_compressed_memcpy(matrix_type * target, const matrix_type * src, const bool_vector_type * mask) { + if (bool_vector_count_equal( mask , true ) != matrix_get_columns( target )) + util_abort("%s: size mismatch. \n",__func__); + + if (bool_vector_size( mask ) != matrix_get_columns( src)) + util_abort("%s: size mismatch. \n",__func__); + + { + int target_col = 0; + int src_col; + for (src_col = 0; src_col < bool_vector_size( mask ); src_col++) { + if (bool_vector_iget( mask , src_col)) { + matrix_copy_column( target , src , target_col , src_col); + target_col++; + } + } + } +} + + + +matrix_type * matrix_realloc_copy(matrix_type * T , const matrix_type * src) { + if (T == NULL) + return matrix_alloc_copy( src ); + else { + matrix_resize( T , src->rows , src->columns , false ); + matrix_assign( T , src ); + return T; + } +} + +void matrix_copy_block( matrix_type * target_matrix , int target_row , int target_column , int rows , int columns, + const matrix_type * src_matrix , int src_row , int src_column) { + matrix_type * target_view = matrix_alloc_shared(target_matrix , target_row , target_column , rows , columns); + matrix_type * src_view = matrix_alloc_shared( src_matrix , src_row , src_column , rows , columns); + matrix_assign( target_view , src_view ); + matrix_free( target_view ); + matrix_free( src_view ); +} + +matrix_type * matrix_alloc_sub_copy( const matrix_type * src , int row_offset , int column_offset , int rows, int columns) { + matrix_type * copy = matrix_alloc( rows, columns ); + matrix_copy_block( copy , 0 , 0 , rows , columns , src , row_offset , column_offset ); + return copy; +} + + +/*****************************************************************/ + +static bool matrix_resize__(matrix_type * matrix , int rows , int columns , bool copy_content , bool safe_mode) { + if (!matrix->data_owner) + util_abort("%s: sorry - can not resize shared matrizes. \n",__func__); + { + bool resize_OK = true; + + if ((rows != matrix->rows) || (columns != matrix->columns)) { + int copy_rows = util_int_min( rows , matrix->rows ); + int copy_columns = util_int_min( columns , matrix->columns); + matrix_type * copy_view = NULL; + matrix_type * copy = NULL; + + if (copy_content) { + copy_view = matrix_alloc_shared( matrix , 0 , 0 , copy_rows , copy_columns); /* This is the part of the old matrix which should be copied over to the new. */ + copy = matrix_alloc_copy__( copy_view , safe_mode ); /* Now copy contains the part of the old matrix which should be copied over - with private storage. */ + } + { + int old_rows , old_columns, old_row_stride , old_column_stride; + matrix_get_dims( matrix , &old_rows , &old_columns , &old_row_stride , &old_column_stride); /* Storing the old header information - in case the realloc() fails. */ + + matrix_init_header(matrix , rows , columns , 1 , rows); /* Resetting the header for the matrix */ + matrix_realloc_data__(matrix , safe_mode); + if (matrix->data != NULL) { /* Realloc succeeded */ + if (copy_content) { + matrix_type * target_view = matrix_alloc_shared(matrix , 0 , 0 , copy_rows , copy_columns); + matrix_assign( target_view , copy); + matrix_free( target_view ); + } + } else { + /* Failed to realloc new storage; RETURNING AN INVALID MATRIX */ + matrix_init_header(matrix , old_rows , old_columns , old_row_stride , old_column_stride); + resize_OK = false; + } + } + + if (copy_content) { + matrix_free(copy_view); + matrix_free(copy); + } + } + return resize_OK; + } +} + + +/** + If copy content is true the content of the old matrix is carried + over to the new one, otherwise the new matrix is cleared. + + Will always return true (or abort). +*/ +bool matrix_resize(matrix_type * matrix , int rows , int columns , bool copy_content) { + return matrix_resize__(matrix , rows , columns , copy_content , false); +} + + +/** + This function will reduce the size of the matrix. It will only + affect the headers, and not touch the actual memory of the matrix. +*/ + +void matrix_shrink_header(matrix_type * matrix , int rows , int columns) { + + if (rows <= matrix->rows) + matrix->rows = rows; + + if (columns <= matrix->columns) + matrix->columns = columns; + +} + + +void matrix_full_size( matrix_type * matrix ) { + matrix->rows = matrix->alloc_rows; + matrix->columns = matrix->alloc_columns; +} + + +/*****************************************************************/ + +static void matrix_free_content(matrix_type * matrix) { + if (matrix->data_owner) + free(matrix->data); + free( matrix->name ); +} + +void matrix_free(matrix_type * matrix) { + matrix_free_content( matrix ); + free(matrix); +} + + +void matrix_safe_free( matrix_type * matrix ) { + if (matrix != NULL) + matrix_free( matrix ); +} + + +/*****************************************************************/ +void matrix_pretty_fprint_submat(const matrix_type * matrix , const char * name , const char * fmt , FILE * stream, int m, int M, int n, int N) { + int i,j; + + if (m<0 || m>M || M >= matrix->rows || n<0 || n>N || N >= matrix->columns) + util_abort("%s: matrix:%s not compatible with print subdimensions. \n",__func__ , matrix->name); + + fprintf(stream , "%s =\n" , name); + for (i=m; i <= M; i++) { + fprintf(stream , " ["); + for (j=n; j <= N; j++) + fprintf(stream , fmt , matrix_iget(matrix , i,j)); + fprintf(stream , "]\n"); + } +} +/*****************************************************************/ + +void matrix_pretty_fprint(const matrix_type * matrix , const char * name , const char * fmt , FILE * stream) { + int i,j; + for (i=0; i < matrix->rows; i++) { + + if (i == (matrix->rows / 2)) + fprintf(stream , "%s =" , name); + else { + int l; + for (l = 0; l < strlen(name) + 2; l++) + fprintf(stream , " "); + } + + fprintf(stream , " ["); + for (j=0; j < matrix->columns; j++) + fprintf(stream , fmt , matrix_iget(matrix , i,j)); + fprintf(stream , "]\n"); + } +} + + +void matrix_pretty_print(const matrix_type * matrix , const char * name , const char * fmt) { + matrix_pretty_fprint(matrix , name , fmt , stdout ); +} + + +void matrix_fprintf( const matrix_type * matrix , const char * fmt , FILE * stream ) { + int i,j; + for (i=0; i < matrix->rows; i++) { + for (j=0; j < matrix->columns; j++) + fprintf(stream , fmt , matrix_iget( matrix , i , j)); + fprintf(stream , "\n"); + } +} + + + +void matrix_dump_csv( const matrix_type * matrix ,const char * filename) { + FILE * stream = util_fopen(filename , "w"); + for (int i=0; i < matrix->rows; i++) { + for (int j=0; j < matrix->columns - 1; j++) + fprintf(stream , "%g, " , matrix_iget( matrix , i , j)); + fprintf(stream , "%g\n" , matrix_iget( matrix , i , matrix->columns - 1)); + } + fclose( stream ); +} + + +void matrix_fwrite(const matrix_type * matrix , FILE * stream) { + util_fwrite_int( matrix->rows , stream ); + util_fwrite_int( matrix->columns , stream ); + + if (matrix->column_stride == matrix->rows) + util_fwrite( matrix->data , sizeof * matrix->data , matrix->columns * matrix->rows , stream , __func__); + else { + int column; + for (column=0; column < matrix->columns; column++) { + if (matrix->row_stride == 1) { + const double * column_data = &matrix->data[ column * matrix->column_stride ]; + util_fwrite( column_data , sizeof * column_data , matrix->rows , stream , __func__); + } else { + int row; + for (row=0; row < matrix->rows; row++) + util_fwrite_double( matrix->data[ GET_INDEX( matrix , row , column )] , stream); + } + } + } +} + + +void matrix_fread(matrix_type * matrix , FILE * stream) { + int rows = util_fread_int( stream ); + int columns = util_fread_int( stream ); + + matrix_resize( matrix , rows , columns , false); + if (matrix->column_stride == matrix->rows) + util_fread( matrix->data , sizeof * matrix->data , matrix->columns * matrix->rows , stream , __func__); + else { + int column; + for (column=0; column < matrix->columns; column++) { + if (matrix->row_stride == 1) { + double * column_data = &matrix->data[ column * matrix->column_stride ]; + util_fread( column_data , sizeof * column_data , matrix->rows , stream , __func__); + } else { + int row; + for (row=0; row < matrix->rows; row++) + matrix->data[ GET_INDEX( matrix , row , column )] = util_fread_double( stream ); + } + } + } +} + +matrix_type * matrix_fread_alloc(FILE * stream) { + matrix_type * matrix = matrix_alloc(1,1); + matrix_fread(matrix , stream); + return matrix; +} + + +/** + [ a11 a12 ] + [ a21 a22 ] + + + + + row_major_order == true + ----------------------- + a_11 + a_12 + a_21 + a_22 + + + row_major_order == false + ----------------------- + a_11 + a_12 + a_21 + a_22 + + + The @orw_major_order parameter ONLY affects the layout on the file, + and NOT the memory layout of the matrix. +*/ + + +static void __fscanf_and_set( matrix_type * matrix , int row , int col , FILE * stream) { + double value; + if (fscanf(stream , "%lg" , &value) == 1) + matrix_iset( matrix , row , col , value ); + else + util_abort("%s: reading of matrix failed at row:%d col:%d \n",__func__ , row , col); +} + + +void matrix_fscanf_data( matrix_type * matrix , bool row_major_order , FILE * stream ) { + int row,col; + if (row_major_order) { + for (row = 0; row < matrix->rows; row++) { + for (col = 0; col < matrix->columns; col++) { + __fscanf_and_set( matrix , row , col ,stream); + } + } + } else { + for (col = 0; col < matrix->columns; col++) { + for (row = 0; row < matrix->rows; row++) { + __fscanf_and_set( matrix , row , col , stream); + } + } + } +} + +/* + If the matrix is printed in row_major_order it is printed so that it looks + visually like a matrix; i.e. with one row on each line in the file. When it is + written with row_major_order == false it is just written with one number on + each line. + + As long as the matrix is loaded again with the matrix_fscanf_data() - or a + similar function which is not line oriented, the presence of newlines make no + difference anyway. +*/ + +void matrix_fprintf_data( const matrix_type * matrix , bool row_major_order, FILE * stream ) { + int i,j; + if (row_major_order) { + for (i=0; i < matrix->rows; i++) { + for (j=0; j < matrix->columns; j++) + fprintf(stream , "%lg ", matrix_iget( matrix , i , j)); + fprintf(stream , "\n"); + } + } else { + for (j=0; j < matrix->columns; j++) + for (i=0; i < matrix->rows; i++) { + fprintf(stream , "%lg\n" , matrix_iget( matrix , i , j)); + } + } +} + + + +/*****************************************************************/ +/* Functions which manipulate one element in the matrix. */ + +static void matrix_assert_ij( const matrix_type * matrix , int i , int j) { + if ((i < 0) || (i >= matrix->rows) || (j < 0) || (j >= matrix->columns)) + util_abort("%s: (i,j) = (%d,%d) invalid. Matrix size: %d x %d \n",__func__ , i,j,matrix->rows , matrix->columns); +} + + +static void matrix_assert_equal_rows( const matrix_type * m1 , const matrix_type * m2) { + if (m1->rows != m2->rows) + util_abort("%s: size mismatch in binary matrix operation %d %d \n",__func__ , m1->rows , m2->rows); +} + + +static void matrix_assert_equal_columns( const matrix_type * m1 , const matrix_type * m2) { + if (m1->columns != m2->columns) + util_abort("%s: size mismatch in binary matrix operation %d %d \n",__func__ , m1->columns , m2->columns); +} + + +void matrix_iset(matrix_type * matrix , int i , int j, double value) { + matrix->data[ GET_INDEX(matrix , i,j) ] = value; +} + + + +void matrix_iset_safe(matrix_type * matrix , int i , int j, double value) { + matrix_assert_ij( matrix , i , j ); + matrix_iset( matrix , i , j , value ); +} + + +double matrix_iget(const matrix_type * matrix , int i , int j) { + return matrix->data[ GET_INDEX(matrix , i, j) ]; +} + + +double matrix_iget_safe(const matrix_type * matrix , int i , int j) { + matrix_assert_ij( matrix , i , j ); + return matrix_iget( matrix , i , j ); +} + + +void matrix_iadd(matrix_type * matrix , int i , int j , double value) { + matrix->data[ GET_INDEX(matrix , i,j) ] += value; +} + + +void matrix_isub(matrix_type * matrix , int i , int j , double value) { + matrix->data[ GET_INDEX(matrix , i,j) ] -= value; +} + + +void matrix_imul(matrix_type * matrix , int i , int j , double value) { + matrix->data[ GET_INDEX(matrix , i,j) ] *= value; +} + + +/*****************************************************************/ +/* One scalar operating on all the elements in the matrix */ + +void matrix_set(matrix_type * matrix, double value) { + int i,j; + for (j=0; j < matrix->columns; j++) + for (i=0; i < matrix->rows; i++) + matrix_iset(matrix , i , j , value); +} + + +void matrix_shift(matrix_type * matrix, double value) { + int i,j; + for (j=0; j < matrix->columns; j++) + for (i=0; i < matrix->rows; i++) + matrix_iadd(matrix , i , j , value); +} + + +void matrix_scale(matrix_type * matrix, double value) { + int i,j; + for (j=0; j < matrix->columns; j++) + for (i=0; i < matrix->rows; i++) + matrix_imul(matrix , i , j , value); +} + +/*****************************************************************/ +/* Functions working on rows & columns */ + +void matrix_set_many_on_column(matrix_type * matrix , int row_offset , int elements , const double * data , int column) { + if ((row_offset + elements) <= matrix->rows) { + if (matrix->row_stride == 1) /* Memory is continous ... */ + memcpy( &matrix->data[ GET_INDEX( matrix , row_offset , column) ] , data , elements * sizeof * data); + else { + int i; + for (i = 0; i < elements; i++) + matrix->data[ row_offset + GET_INDEX( matrix , i , column) ] = data[i]; + } + } else + util_abort("%s: range violation \n" , __func__); +} + +void matrix_set_column(matrix_type * matrix , const double * data , int column) { + matrix_set_many_on_column( matrix , 0 , matrix->rows , data , column ); +} + + +void matrix_set_const_column(matrix_type * matrix , const double value , int column) { + int row; + for (row = 0; row < matrix->rows; row++) + matrix->data[ GET_INDEX( matrix , row , column) ] = value; +} + + +void matrix_copy_column(matrix_type * target_matrix, const matrix_type * src_matrix , int target_column, int src_column) { + matrix_assert_equal_rows( target_matrix , src_matrix ); + { + int row; + for(row = 0; row < target_matrix->rows; row++) + target_matrix->data[ GET_INDEX( target_matrix, row , target_column)] = src_matrix->data[ GET_INDEX( src_matrix, row, src_column)]; + } +} + + +void matrix_scale_column(matrix_type * matrix , int column , double scale_factor) { + int row; + for (row = 0; row < matrix->rows; row++) + matrix->data[ GET_INDEX( matrix , row , column) ] *= scale_factor; +} + +void matrix_scale_row(matrix_type * matrix , int row , double scale_factor) { + int column; + for (column = 0; column < matrix->columns; column++) + matrix->data[ GET_INDEX( matrix , row , column) ] *= scale_factor; +} + +void matrix_copy_row(matrix_type * target_matrix, const matrix_type * src_matrix , int target_row, int src_row) { + matrix_assert_equal_columns( target_matrix , src_matrix ); + { + int col; + for(col = 0; col < target_matrix->columns; col++) + target_matrix->data[ GET_INDEX( target_matrix , target_row , col)] = src_matrix->data[ GET_INDEX( src_matrix, src_row, col)]; + } +} + + +/*****************************************************************/ +/* Functions for dot products between rows/columns in matrices. */ + +double matrix_column_column_dot_product(const matrix_type * m1 , int col1 , const matrix_type * m2 , int col2) { + if (m1->rows != m2->rows) + util_abort("%s: size mismatch \n",__func__); + + if (col1 >= m1->columns || col2 >= m2->columns) + util_abort("%s: size mismatch \n",__func__); + { + int row; + double sum = 0; + for( row = 0; row < m1->rows; row++) + sum += m1->data[ GET_INDEX(m1 , row , col1) ] * m2->data[ GET_INDEX(m2, row , col2) ]; + + return sum; + } +} + + +double matrix_row_column_dot_product(const matrix_type * m1 , int row1 , const matrix_type * m2 , int col2) { + if (m1->columns != m2->rows) + util_abort("%s: size mismatch: m1:[%d,%d] m2:[%d,%d] \n",__func__ , matrix_get_rows( m1 ) , matrix_get_columns( m1 ) , matrix_get_rows( m2 ) , matrix_get_columns( m2 )); + + { + int k; + double sum = 0; + for( k = 0; k < m1->columns; k++) + sum += m1->data[ GET_INDEX(m1 , row1 , k) ] * m2->data[ GET_INDEX(m2, k , col2) ]; + + return sum; + } +} + + + + +/*****************************************************************/ +/* Matrix - matrix operations */ + + +/* Implements assignement: A = B */ +void matrix_assign(matrix_type * A , const matrix_type * B) { + if ((A->rows == B->rows) && (A->columns == B->columns)) { + int i,j; + + if (A->row_stride == B->row_stride) { + if (A->columns == A->row_stride) /** Memory is just one continous block */ + memcpy( A->data , B->data , A->rows * A->columns * sizeof * A->data); + else { + /* Copying columns of data */ + for (j = 0; j < A->columns; j++) + memcpy( &A->data[ GET_INDEX(A , 0 , j)] , &B->data[ GET_INDEX(B , 0 , j) ] , A->rows * sizeof * A->data); + } + } else { + /* Copying element by element */ + for (j = 0; j < A->columns; j++) + for (i=0; i < A->rows; i++) + A->data[ GET_INDEX(A,i,j) ] = B->data[ GET_INDEX(B,i,j) ]; + } + } else + util_abort("%s: size mismatch A:[%d,%d] B:[%d,%d] \n",__func__ , A->rows , A->columns , B->rows , B->columns); +} + + + +void matrix_inplace_sub_column(matrix_type * A , const matrix_type * B, int colA , int colB) { + if ((A->rows == B->rows) && + (colA < A->columns) && + (colB < B->columns)) { + int row; + + for (row = 0; row < A->rows; row++) + A->data[ GET_INDEX(A , row , colA)] -= B->data[ GET_INDEX(B , row , colB)]; + + } else + util_abort("%s: size mismatch \n",__func__); +} + +void matrix_inplace_add_column(matrix_type * A , const matrix_type * B, int colA , int colB) { + if ((A->rows == B->rows) && + (colA < A->columns) && + (colB < B->columns)) { + int row; + + for (row = 0; row < A->rows; row++) + A->data[ GET_INDEX(A , row , colA)] += B->data[ GET_INDEX(B , row , colB)]; + + } else + util_abort("%s: size mismatch \n",__func__); +} + +/* Updates matrix A by adding in matrix B - elementwise. */ +void matrix_inplace_add(matrix_type * A , const matrix_type * B) { + if ((A->rows == B->rows) && (A->columns == B->columns)) { + int i,j; + + for (j = 0; j < A->columns; j++) + for (i=0; i < A->rows; i++) + A->data[ GET_INDEX(A,i,j) ] += B->data[ GET_INDEX(B,i,j) ]; + + } else + util_abort("%s: size mismatch \n",__func__); +} + + +/* Updates matrix A by subtracting matrix B - elementwise. */ +void matrix_inplace_sub(matrix_type * A , const matrix_type * B) { + if ((A->rows == B->rows) && (A->columns == B->columns)) { + int i,j; + + for (j = 0; j < A->columns; j++) + for (i=0; i < A->rows; i++) + A->data[ GET_INDEX(A,i,j) ] -= B->data[ GET_INDEX(B,i,j) ]; + + } else + util_abort("%s: size mismatch A:[%d,%d] B:[%d,%d]\n",__func__ , + matrix_get_rows(A), + matrix_get_columns(A), + matrix_get_rows(B), + matrix_get_columns(B)); +} + + +/* Does the matrix operation: + + A = B - C + + elementwise. +*/ +void matrix_sub(matrix_type * A , const matrix_type * B , const matrix_type * C) { + if ((A->rows == B->rows) && (A->columns == B->columns) && (A->rows == C->rows) && (A->columns == C->columns)) { + int i,j; + + for (j = 0; j < A->columns; j++) + for (i=0; i < A->rows; i++) + A->data[ GET_INDEX(A,i,j) ] = B->data[ GET_INDEX(B,i,j) ] - C->data[ GET_INDEX(B,i,j) ]; + + } else + util_abort("%s: size mismatch \n",__func__); +} + + +/** + Observe that A and T should not overlap, i.e. the call + + matrix_transpose(X , X) + + will fail in mysterious ways. +*/ + +void matrix_transpose(const matrix_type * A , matrix_type * T) { + if ((A->columns == T->rows) && (A->rows == T->columns)) { + int i,j; + for (i=0; i < A->rows; i++) { + for (j=0; j < A->columns; j++) { + size_t src_index = GET_INDEX(A , i , j ); + size_t target_index = GET_INDEX(T , j , i ); + + T->data[ target_index ] = A->data[ src_index ]; + } + } + } else + util_abort("%s: size mismatch\n",__func__); +} + + +void matrix_inplace_transpose(matrix_type * A ) { + matrix_type * B = matrix_alloc_transpose( A ); + matrix_free_content( A ); + memcpy( A , B , sizeof * A ); + free( B ); +} + + + +matrix_type * matrix_alloc_transpose( const matrix_type * A) { + matrix_type * B = matrix_alloc( matrix_get_columns( A ) , matrix_get_rows( A )); + matrix_transpose( A , B ); + return B; +} + + + +/** + For this function to work the following must be satisfied: + + columns in A == rows in B == columns in B + + For general matrix multiplactions where A = B * C all have + different dimensions you can use matrix_matmul() (which calls the + BLAS routine dgemm()); +*/ + + +void matrix_inplace_matmul(matrix_type * A, const matrix_type * B) { + if ((A->columns == B->rows) && (B->rows == B->columns)) { + double * tmp = (double*)util_malloc( sizeof * A->data * A->columns ); + int i,j,k; + + for (i=0; i < A->rows; i++) { + + /* Clearing the tmp vector */ + for (k=0; k < B->rows; k++) + tmp[k] = 0; + + for (j=0; j < B->rows; j++) { + double scalar_product = 0; + for (k=0; k < A->columns; k++) + scalar_product += A->data[ GET_INDEX(A,i,k) ] * B->data[ GET_INDEX(B,k,j) ]; + + /* Assign first to tmp[j] */ + tmp[j] = scalar_product; + } + for (j=0; j < A->columns; j++) + A->data[ GET_INDEX(A , i, j) ] = tmp[j]; + } + free(tmp); + } else + util_abort("%s: size mismatch: A:[%d,%d] B:[%d,%d]\n",__func__ , matrix_get_rows(A) , matrix_get_columns(A) , matrix_get_rows(B) , matrix_get_columns(B)); +} + +/*****************************************************************/ +/* If the current build has a thread_pool implementation enabled a + proper matrix_implace_matmul_mt() function will be built, otherwise + only the serial version matrix_inplace_matmul() will be used. */ + + +#ifdef ERT_HAVE_THREAD_POOL + +static void * matrix_inplace_matmul_mt__(void * arg) { + + arg_pack_type * arg_pack = arg_pack_safe_cast( arg ); + int row_offset = arg_pack_iget_int( arg_pack , 0 ); + int rows = arg_pack_iget_int( arg_pack , 1 ); + matrix_type * A = (matrix_type*) arg_pack_iget_ptr( arg_pack , 2 ); + const matrix_type * B = (const matrix_type*)arg_pack_iget_const_ptr( arg_pack , 3 ); + + matrix_type * A_view = matrix_alloc_shared( A , row_offset , 0 , rows , matrix_get_columns( A )); + matrix_inplace_matmul( A_view , B ); + matrix_free( A_view ); + return NULL; +} + +/** + Observe that the calling scope is responsible for passing a + thread_pool in suitable state to this function. This implies one of + the following: + + 1. The thread_pool is newly created, with the @start_queue + argument set to false. + + 2. The thread_pool has been joined __without__ an interevening + call to thread_pool_restart(). + + If the thread_pool has not been correctly prepared, according to + this specification, it will be crash and burn. +*/ + +void matrix_inplace_matmul_mt2(matrix_type * A, const matrix_type * B , thread_pool_type * thread_pool){ + int num_threads = thread_pool_get_max_running( thread_pool ); + arg_pack_type ** arglist = (arg_pack_type**)util_malloc( num_threads * sizeof * arglist ); + int it; + thread_pool_restart( thread_pool ); + { + int rows = matrix_get_rows( A ) / num_threads; + int rows_mod = matrix_get_rows( A ) % num_threads; + int row_offset = 0; + + for (it = 0; it < num_threads; it++) { + int row_size; + arglist[it] = arg_pack_alloc(); + row_size = rows; + if (it < rows_mod) + row_size += 1; + + arg_pack_append_int(arglist[it] , row_offset ); + arg_pack_append_int(arglist[it] , row_size ); + arg_pack_append_ptr(arglist[it] , A ); + arg_pack_append_const_ptr(arglist[it] , B ); + + thread_pool_add_job( thread_pool , matrix_inplace_matmul_mt__ , arglist[it]); + row_offset += row_size; + } + } + thread_pool_join( thread_pool ); + + for (it = 0; it < num_threads; it++) + arg_pack_free( arglist[it] ); + free( arglist ); +} + +void matrix_inplace_matmul_mt1(matrix_type * A, const matrix_type * B , int num_threads){ + thread_pool_type * thread_pool = thread_pool_alloc( num_threads , false ); + matrix_inplace_matmul_mt2( A , B , thread_pool ); + thread_pool_free( thread_pool ); +} + +#else + +void matrix_inplace_matmul_mt1(matrix_type * A, const matrix_type * B , int num_threads){ + matrix_inplace_matmul( A , B ); +} + +#endif + + + + +/*****************************************************************/ +/* Row/column functions */ + +double matrix_get_row_sum(const matrix_type * matrix , int row) { + double sum = 0; + int j; + for (j=0; j < matrix->columns; j++) + sum += matrix->data[ GET_INDEX( matrix , row , j ) ]; + return sum; +} + + +double matrix_get_column_sum(const matrix_type * matrix , int column) { + double sum = 0; + int i; + for (i=0; i < matrix->rows; i++) + sum += matrix->data[ GET_INDEX( matrix , i , column ) ]; + return sum; +} + + +double matrix_get_column_abssum(const matrix_type * matrix , int column) { + double sum = 0; + int i; + for (i=0; i < matrix->rows; i++) + sum += std::abs( matrix->data[ GET_INDEX( matrix , i , column ) ] ); + return sum; +} + + +/** + Return the sum of the squares on column. +*/ +double matrix_get_column_sum2(const matrix_type * matrix , int column) { + double sum2 = 0; + int i; + for ( i=0; i < matrix->rows; i++) { + double m = matrix->data[ GET_INDEX( matrix , i , column ) ]; + sum2 += m*m; + } + return sum2; +} + +void matrix_shift_row(matrix_type * matrix , int row , double shift) { + int j; + for ( j=0; j < matrix->columns; j++) + matrix->data[ GET_INDEX( matrix , row , j ) ] += shift; +} + + +void matrix_set_row(matrix_type * matrix , const double * data , int row) { + if (row < 0 || row >= matrix->rows) + throw std::invalid_argument("Invalid row index"); + + for (int j=0; j < matrix->columns; j++) + matrix->data[ GET_INDEX( matrix , row , j ) ] = data[j]; +} + + +/** + For each row in the matrix we will do the operation + + R -> R - +*/ + +void matrix_subtract_row_mean(matrix_type * matrix) { + int i; + for ( i=0; i < matrix->rows; i++) { + double row_mean = matrix_get_row_sum(matrix , i) / matrix->columns; + matrix_shift_row( matrix , i , -row_mean); + } +} + +void matrix_subtract_and_store_row_mean(matrix_type * matrix, matrix_type * row_mean) { + int i; + for ( i=0; i < matrix->rows; i++) { + double mean = matrix_get_row_sum(matrix , i) / matrix->columns; + matrix_shift_row( matrix , i , -mean); + matrix_iset(row_mean , i , 0, mean ); + } +} + +void matrix_imul_col( matrix_type * matrix , int column , double factor) { + int i; + for ( i=0; i < matrix->rows; i++) + matrix_imul( matrix , i , column , factor ); +} + + +/*****************************************************************/ +/** + This function will return the double data pointer of the matrix, + when you use this explicitly you ARE ON YOUR OWN. +*/ + +double * matrix_get_data(const matrix_type * matrix) { + return matrix->data; +} + +/** + The query functions below can be used to ask for the dimensions & + strides of the matrix. +*/ + +int matrix_get_rows(const matrix_type * matrix) { + return matrix->rows; +} + +int matrix_get_columns(const matrix_type * matrix) { + return matrix->columns; +} + +int matrix_get_column_stride(const matrix_type * matrix) { + return matrix->column_stride; +} + + +void matrix_get_dims(const matrix_type * matrix , int * rows , int * columns , int * row_stride , int * column_stride) { + + *rows = matrix->rows; + *columns = matrix->columns; + *row_stride = matrix->row_stride; + *column_stride = matrix->column_stride; + +} + + +bool matrix_is_quadratic(const matrix_type * matrix) { + if (matrix->rows == matrix->columns) + return true; + else + return false; +} + +/** + Goes through all the elements in the matrix - and return true if they are all finite. +*/ + +#ifdef ERT_HAVE_ISFINITE + +bool matrix_is_finite(const matrix_type * matrix) { + int i,j; + for (i = 0; i < matrix->rows; i++) + for (j =0; j< matrix->columns; j++) + if ( !std::isfinite( matrix->data[ GET_INDEX( matrix , i , j) ])) { + printf("%s(%d,%d) = %g \n",matrix->name , i,j,matrix->data[ GET_INDEX( matrix , i , j) ]); + return false; + } + + return true; +} + +void matrix_assert_finite( const matrix_type * matrix ) { + if (!matrix_is_finite( matrix )) { + if ((matrix->rows * matrix->columns) < 400) + matrix_pretty_fprint( matrix , matrix->name , " %6.3f" , stdout); + + util_abort("%s: matrix:%s is not finite. \n",__func__ , matrix->name); + } +} + +#endif + +/** + Return true if the two matrices m1 and m2 are equal. The equality + test is based on element-by-element memcmp() comparison, i.e. the + there is ZERO numerical tolerance in the comparison. + + If the two matrices do not have equal dimension false is returned. +*/ + +bool matrix_equal( const matrix_type * m1 , const matrix_type * m2) { + if (! ((m1->rows == m2->rows) && (m1->columns == m2->columns))) + return false; + { + int i,j; + for (i=0; i < m1->rows; i++) { + for (j=0; j < m1->columns; j++) { + int index1 = GET_INDEX(m1 , i , j); + int index2 = GET_INDEX(m2 , i , j); + double d1 = m1->data[ index1 ]; + double d2 = m2->data[ index2 ]; + + if (d1 != d2) + return false; + } + } + } + + /** OK - we came all the way through - they are equal. */ + return true; +} + + +/* + Returns true if the two matrices m1 and m2 are almost equal. + The equality-test applies an element-by-element comparison + whether the absolute value of the difference is less than a + user-specified tolerance. This routine is useful for testing + two different numerical algorithms that should give the same + result but not necessarily to machine precision. If the two + matrices do not have equal dimension false is returned. +*/ + +bool matrix_similar( const matrix_type * m1 , const matrix_type * m2, double epsilon) { + if (! ((m1->rows == m2->rows) && (m1->columns == m2->columns))) + return false; + { + int i,j; + for (i=0; i < m1->rows; i++) { + for (j=0; j < m1->columns; j++) { + int index1 = GET_INDEX(m1 , i , j); + int index2 = GET_INDEX(m2 , i , j); + double d1 = m1->data[ index1 ]; + double d2 = m2->data[ index2 ]; + + if (std::abs(d1 - d2) > epsilon) + return false; + } + } + } + + /** OK - we came all the way through - they are almost equal. */ + return true; +} + +bool matrix_columns_equal( const matrix_type * m1 , int col1 , const matrix_type * m2 , int col2) { + if (m1->rows != m2->rows) + return false; + + { + int row; + for (row=0; row < m1->rows; row++) { + if (memcmp( &m1->data[ GET_INDEX(m1 , row , col1)] , &m2->data[ GET_INDEX(m2 , row , col2)] , sizeof * m1->data) != 0) + return false; + } + } + + return true; +} + + +/*****************************************************************/ +/* Various special matrices */ + + +/** + Will set the diagonal elements in matrix to the values in diag, + and all remaining elements to zero. Assumes that matrix is + rectangular. +*/ + +void matrix_diag_set(matrix_type * matrix , const double * diag) { + if (matrix->rows == matrix->columns) { + int i; + matrix_set(matrix , 0); + for ( i=0; i < matrix->rows; i++) + matrix->data[ GET_INDEX(matrix , i , i) ] = diag[i]; + } else + util_abort("%s: size mismatch \n",__func__); +} + + +/** + Will set the scalar @value on all the diagonal elements of the + matrix; all off-diagonal elements are explicitly set to zero. +*/ + +void matrix_diag_set_scalar(matrix_type * matrix , double value) { + if (matrix->rows == matrix->columns) { + int i; + matrix_set(matrix , 0); + for ( i=0; i < matrix->rows; i++) + matrix->data[ GET_INDEX(matrix , i , i) ] = value; + } else + util_abort("%s: size mismatch \n",__func__); +} + + +void matrix_scalar_set( matrix_type * matrix , double value) { + int i,j; + for (j=0; j < matrix->columns; j++) + for (i=0; i < matrix->rows; i++) + matrix->data[ GET_INDEX(matrix , i , j) ] = value; +} + + + +/** + Fills the matrix with uniformly distributed random numbers in + [0,1). +*/ + +void matrix_random_init(matrix_type * matrix , rng_type * rng) { + int i,j; + for (j=0; j < matrix->columns; j++) + for (i=0; i < matrix->rows; i++) + matrix->data[ GET_INDEX(matrix , i , j) ] = rng_get_double( rng ); +} + + +void matrix_inplace_diag_sqrt(matrix_type *Cd) +{ + int nrows = Cd->rows; + + if (Cd->rows != Cd->columns) { + util_abort("%s: size mismatch \n",__func__); + } + else{ + int i; + for ( i=0; idata[GET_INDEX(Cd , i , i)] = sqrt(Cd->data[GET_INDEX(Cd , i , i)]); + } + } +} + +void matrix_delete_column(matrix_type * m1, int column) { + if (column < 0 || column >= matrix_get_columns(m1)) + throw std::invalid_argument("Invalid column" + std::to_string(column)); + + matrix_type * m2 = matrix_alloc(matrix_get_rows(m1), matrix_get_columns(m1) - 1); + if (column > 0) + matrix_copy_block(m2, 0, 0, + matrix_get_rows(m2), column, + m1, 0, 0); + + if (column < (matrix_get_columns(m1) - 1)) + matrix_copy_block(m2, 0, column, + matrix_get_rows(m2), matrix_get_columns(m2) - column, + m1, 0, column + 1); + + matrix_resize(m1, matrix_get_rows(m2), matrix_get_columns(m2), false); + matrix_assign(m1, m2); + matrix_free(m2); +} + + +void matrix_delete_row(matrix_type * m1, int row) { + if (row < 0 || row >= matrix_get_rows(m1)) + throw std::invalid_argument("Invalid row" + std::to_string(row)); + + matrix_type * m2 = matrix_alloc(matrix_get_rows(m1) - 1, matrix_get_columns(m1)); + if (row > 0) + matrix_copy_block(m2, 0, 0, + row, matrix_get_columns(m2), + m1, 0, 0); + + if (row < (matrix_get_rows(m1) - 1)) + matrix_copy_block(m2, row, 0, + matrix_get_rows(m2) - row, matrix_get_columns(m2), + m1, row + 1, 0); + + matrix_resize(m1, matrix_get_rows(m2), matrix_get_columns(m2), false); + matrix_assign(m1, m2); + matrix_free(m2); +} + + + +double matrix_trace(const matrix_type *matrix) { + + int nrows = matrix->rows; + double sum = 0; + + if (matrix->rows != matrix->columns) { + util_abort("%s: matrix is not square \n",__func__); + } + else{ + int i; + for ( i=0; idata[GET_INDEX(matrix , i , i)]; + } + } + return sum; +} + + +bool matrix_check_dims( const matrix_type * m , int rows , int columns) { + if (m) { + if ((m->rows == rows) && (m->columns == columns)) + return true; + else + return false; + } else { + util_abort("%s: internal error - trying to dereference NULL matrix pointer \n",__func__); + return false; + } +} + + +double matrix_diag_std(const matrix_type * Sk,double mean) +{ + + if (Sk->rows != Sk->columns) { + util_abort("%s: matrix is not square \n",__func__); + return 0; + } + else{ + int nrows = Sk->rows; + double std = 0; + int i; + + for ( i=0; idata[GET_INDEX(Sk , i , i)] - mean; + std += d*d; + } + + + std = sqrt(std / nrows); + return std; + } +} + +/** + The matrix_det3() and matrix_det4() are explicit implementations of + the determinant of a 3x3 and 4x4 matrices. The ecl_grid class uses + these determinants determine whether a point is inside a cell. By + using this explicit implementation the ecl_grid library has no + LAPACK dependency. +*/ + +double matrix_det2( const matrix_type * A) { + if ((A->rows == 2) && (A->columns == 2)) { + double a00 = A->data[GET_INDEX(A,0,0)]; + double a01 = A->data[GET_INDEX(A,0,1)]; + double a10 = A->data[GET_INDEX(A,1,0)]; + double a11 = A->data[GET_INDEX(A,1,1)]; + + return a00 * a11 - a10 * a01; + } else { + util_abort("%s: hardcoded for 2x2 matrices A is: %d x %d \n",__func__, A->rows , A->columns); + return 0; + } +} + +double matrix_det3( const matrix_type * A) { + if ((A->rows == 3) && (A->columns == 3)) { + double a = A->data[GET_INDEX(A,0,0)]; + double b = A->data[GET_INDEX(A,0,1)]; + double c = A->data[GET_INDEX(A,0,2)]; + + double d = A->data[GET_INDEX(A,1,0)]; + double e = A->data[GET_INDEX(A,1,1)]; + double f = A->data[GET_INDEX(A,1,2)]; + + double g = A->data[GET_INDEX(A,2,0)]; + double h = A->data[GET_INDEX(A,2,1)]; + double i = A->data[GET_INDEX(A,2,2)]; + + return a*e*i + b*f*g + c*d*h - c*e*g - b*d*i - a*f*h; + } else { + util_abort("%s: hardcoded for 3x3 matrices A is: %d x %d \n",__func__, A->rows , A->columns); + return 0; + } +} + + +double matrix_det4( const matrix_type * A) { + if ((A->rows == 4) && (A->columns == 4)) { + double a00 = A->data[GET_INDEX(A,0,0)]; + double a01 = A->data[GET_INDEX(A,0,1)]; + double a02 = A->data[GET_INDEX(A,0,2)]; + double a03 = A->data[GET_INDEX(A,0,3)]; + double a10 = A->data[GET_INDEX(A,1,0)]; + double a11 = A->data[GET_INDEX(A,1,1)]; + double a12 = A->data[GET_INDEX(A,1,2)]; + double a13 = A->data[GET_INDEX(A,1,3)]; + double a20 = A->data[GET_INDEX(A,2,0)]; + double a21 = A->data[GET_INDEX(A,2,1)]; + double a22 = A->data[GET_INDEX(A,2,2)]; + double a23 = A->data[GET_INDEX(A,2,3)]; + double a30 = A->data[GET_INDEX(A,3,0)]; + double a31 = A->data[GET_INDEX(A,3,1)]; + double a32 = A->data[GET_INDEX(A,3,2)]; + double a33 = A->data[GET_INDEX(A,3,3)]; + + /* + double det = (a00*(a11*(a22*a33 - a23*a32)-a12*(a21*a33 - a23*a31)+a13*(a21*a32 - a22*a31)) - + a01*(a10*(a22*a33 - a23*a32)-a12*(a20*a33 - a23*a30)+a13*(a20*a32 - a22*a30)) + + a02*(a10*(a21*a33 - a23*a31)-a11*(a20*a33 - a23*a30)+a13*(a20*a31 - a21*a30)) - + a03*(a10*(a21*a32 - a22*a31)-a11*(a20*a32 - a22*a30)+a12*(a20*a31 - a21*a30))); + */ + double det = 0; + + { + double factors[24] = { a00*a12*a23*a31, + a00*a13*a21*a32, + a00*a11*a22*a33, + a01*a10*a23*a32, + a01*a12*a20*a33, + a01*a13*a22*a30, + a02*a10*a21*a33, + a02*a11*a23*a30, + a02*a13*a20*a31, + a03*a10*a22*a31, + a03*a11*a20*a32, + a03*a12*a21*a30 + -a02*a13*a21*a30, + -a03*a10*a21*a32, + -a03*a11*a22*a30, + -a03*a12*a20*a31, + -a00*a11*a23*a32, + -a00*a12*a21*a33, + -a00*a13*a22*a31, + -a01*a10*a22*a33, + -a01*a12*a23*a30, + -a01*a13*a20*a32, + -a02*a10*a23*a31, + -a02*a11*a20*a33}; + int i; + + for (i = 0; i < 12; i++) + det += (factors[i] + factors[i + 12]); + } + + return det; + } else { + util_abort("%s: hardcoded for 4x4 matrices A is: %d x %d \n",__func__, A->rows , A->columns); + return 0; + } +} + + +#ifdef __cplusplus +} +#endif diff --git a/libres/lib/res_util/matrix_blas.cpp b/libres/lib/res_util/matrix_blas.cpp new file mode 100644 index 00000000000..15286cef263 --- /dev/null +++ b/libres/lib/res_util/matrix_blas.cpp @@ -0,0 +1,254 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'matrix_blas.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 + +#ifdef __cplusplus +extern "C" { +#endif + +/*****************************************************************/ +void dgemm_(char * , char * , int * , int * , int * , double * , double * , int * , double * , int * , double * , double * , int *); +void dgemv_(char * , int * , int * , double * , double * , int * , const double * , int * , double * , double * , int * ); +/*****************************************************************/ + + + +/** + y = alpha * op(A)*x + beta * y + + alpha,beta: scalars + x,y : vectors + A : matrix + + + x and y are entered as (double * ). +*/ + + +void matrix_dgemv(const matrix_type * A , const double *x , double * y, bool transA , double alpha , double beta) { + int m = matrix_get_rows( A ); + int n = matrix_get_columns( A ); + int lda = matrix_get_column_stride( A ); + int incx = 1; + int incy = 1; + + char transA_c; + if (transA) + transA_c = 'T'; + else + transA_c = 'N'; + + dgemv_(&transA_c , &m , &n , &alpha , matrix_get_data( A ) , &lda , x , &incx , &beta , y , &incy); +} + + +static void dgemm_debug(const matrix_type *C , const matrix_type *A , const matrix_type * B , bool transA, bool transB) { + printf("\nC = [%d , %d]\n",matrix_get_rows( C ) , matrix_get_columns(C)); + + printf("A: [%d , %d]", matrix_get_rows( A ) , matrix_get_columns(A)); + if (transA) + printf("^T"); + + printf("\nB: [%d , %d]", matrix_get_rows( B ) , matrix_get_columns(B)); + if (transB) + printf("^T"); + + printf("\n\n"); + printf("[%d ,%d] = ",matrix_get_rows( C ) , matrix_get_columns(C)); + if (transA) + printf("[%d ,%d] x ",matrix_get_rows( A ) , matrix_get_columns(A)); + else + printf("[%d ,%d] x ",matrix_get_columns( A ) , matrix_get_rows(A)); + + + if (transB) + printf("[%d ,%d]\n",matrix_get_rows( B ) , matrix_get_columns(B)); + else + printf("[%d ,%d]\n",matrix_get_columns( B ) , matrix_get_rows(B)); + + +} + + + +/** + C = alpha * op(A) * op(B) + beta * C + + op(·) can either be unity or Transpose. +*/ + +void matrix_dgemm(matrix_type *C , const matrix_type *A , const matrix_type * B , bool transA, bool transB , double alpha , double beta) { + int m = matrix_get_rows( C ); + int n = matrix_get_columns( C ); + int lda = matrix_get_column_stride( A ); + int ldb = matrix_get_column_stride( B ); + int ldc = matrix_get_column_stride( C ); + char transA_c; + char transB_c; + int k , innerA, innerB , outerA , outerB; + + if (transA) + k = matrix_get_rows( A ); + else + k = matrix_get_columns( A ); + + + if (transA) { + innerA = matrix_get_rows(A); + outerA = matrix_get_columns(A); + transA_c = 'T'; + } else { + innerA = matrix_get_columns(A); + outerA = matrix_get_rows(A); + transA_c = 'N'; + } + + + if (transB) { + innerB = matrix_get_columns( B ); + outerB = matrix_get_rows( B ); + transB_c = 'T'; + } else { + transB_c = 'N'; + innerB = matrix_get_rows( B ); + outerB = matrix_get_columns( B ); + } + + /* + This is the dimension check which must pass: + + -------------------------------------------------- + A | B | Columns(A) = Rows(B) + Trans(A) | Trans(B) | Rows(A) = Columns(B) + A | Trans(B) | Columns(A) = Columns(B) + Trans(A) | B | Rows(A) = Rows(B) + -------------------------------------------------- + + -------------------------------------------------- + A | Rows(A) = Rows(C) + Trans(A) | Columns(A) = Rows(C) + B | Columns(B) = Columns(C) + Trans(B) | Rows(B) = Columns(B) + -------------------------------------------------- + + */ + + if (innerA != innerB) { + dgemm_debug(C,A,B,transA , transB); + util_abort("%s: matrix size mismatch between A and B \n", __func__); + } + + + if (outerA != matrix_get_rows( C )) { + dgemm_debug(C,A,B,transA , transB); + printf("outerA:%d rows(C):%d \n",outerA , matrix_get_rows( C )); + util_abort("%s: matrix size mismatch between A and C \n",__func__); + } + + + if (outerB != matrix_get_columns( C )) { + dgemm_debug(C,A,B,transA , transB); + util_abort("%s: matrix size mismatch between B and C \n",__func__); + } + + if (ldc < util_int_max(1 , m)) { + dgemm_debug(C,A,B,transA , transB); + fprintf(stderr,"Tried to capture blas message: \"** On entry to DGEMM parameter 13 had an illegal value\"\n"); + fprintf(stderr,"m:%d ldc:%d ldc should be >= max(1,%d) \n",m,ldc,m); + util_abort("%s: invalid value for ldc\n",__func__); + } + + + dgemm_(&transA_c , // 1 + &transB_c , // 2 + &m , // 3 + &n , // 4 + &k , // 5 + &alpha , // 6 + matrix_get_data( A ) , // 7 + &lda , // 8 + matrix_get_data( B ) , // 9 + &ldb , // 10 + &beta , // 11 + matrix_get_data( C ) , // 12 + &ldc); // 13 +} + + + +void matrix_matmul_with_transpose(matrix_type * C, const matrix_type * A , const matrix_type * B , bool transA , bool transB) { + matrix_dgemm( C , A , B , transA , transB , 1 , 0); +} + + +/* + This function does a general matrix multiply of A * B, and stores + the result in C. +*/ + +void matrix_matmul(matrix_type * C, const matrix_type * A , const matrix_type * B) { + matrix_dgemm( C , A , B , false , false , 1 , 0); +} + + +/** + Allocates new matrix C = A·B +*/ + +matrix_type * matrix_alloc_matmul(const matrix_type * A, const matrix_type * B) { + matrix_type * C = matrix_alloc( matrix_get_rows( A ) , matrix_get_columns( B )); + matrix_matmul( C , A , B ); + return C; +} + + + + + +/*****************************************************************/ +/** + Will calculate the Gram matrix: G = X'*X +*/ + +void matrix_gram_set( const matrix_type * X , matrix_type * G, bool col) { + int G_rows = matrix_get_rows( G ); + int G_cols = matrix_get_columns( G ); + int X_rows = matrix_get_rows( X ); + int X_cols = matrix_get_columns( X ); + if (col) { + // Calculate X' · X + if ((G_rows == G_cols) && (X_cols == G_rows)) + matrix_dgemm( G , X , X , true , false , 1 , 0); + else + util_abort("%s: dimension mismatch \n",__func__); + } else { + // Calculate X · X' + if ((G_rows == G_cols) && (X_rows == G_rows)) + matrix_dgemm( G , X , X , false , true , 1 , 0); + else + util_abort("%s: dimension mismatch \n",__func__); + } +} + +#ifdef __cplusplus +} +#endif diff --git a/libres/lib/res_util/matrix_lapack.cpp b/libres/lib/res_util/matrix_lapack.cpp new file mode 100644 index 00000000000..89523c00bd4 --- /dev/null +++ b/libres/lib/res_util/matrix_lapack.cpp @@ -0,0 +1,693 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'matrix_lapack.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 + +#ifdef __cplusplus +extern "C" { +#endif + +/** + The external lapack routines +*/ +/*****************************************************************/ +void dgesv_(int * n, + int * nrhs, + double * A, + int * lda, + long int * ipivot, + double * B, + int * ldb, + int * info); +void dgesvx_(char * fact, + char * trans, + int * n, + int * nrhs, + double * A, + int * lda, + double * AF, + int * ldaf, + long int * ipivot, + char * equed, + double * R, + double * C, + double * B, + int * ldb, + double * X, + int * ldx, + double * rcond, + double * ferr, + double * berr, + double * work, + long int * iwork, + int * info); +void dgesvd_(char * jobu, + char * jobvt, + int * m, + int * n, + double * A, + int * lda, + double * S, + double * U, + int * ldu, + double * VT, + int * ldvt, + double * work, + int * worksize, + int * info); +void dsyevx_(char * jobz, + char * range, + char * uplo, + int *n, + double * A, + int * lda, + double * vl, + double * vu, + int * il, + int * iu, + double * abstol, + int * m, + double * w, + double *z, + int * ldz, + double * work, + int * lwork, + int * iwork, + int * ifail, + int * info); +void dgeqrf_(int * m, + int * n, + double * A, + int * lda, + double * tau, + double * work, + int * lwork, + int * info); +void dorgqr_(int * m, + int * n, + int * k, + double * A, + int * lda, + double * tau, + double * work, + int * lwork, + int * info); +void dgetrf_(int * M, + int * n, + double * A, + int * lda, + int * ipiv, + int * info); +void dgedi_(double * A, + int * lda, + int * n, + int * ipiv, + double * det, + double * work, + int * job); +void dgetri_(int * n, + double * A, + int * lda, + int * ipiv, + double * work, + int * work_size, + int * info); +/*****************************************************************/ + + + + + + +/** + This file implements (very thin) interfaces for the matrix_type to + some lapack functions. The file does not contain any data + structures, only functions. +*/ + + + +static void matrix_lapack_assert_fortran_layout( const matrix_type * matrix ) { + int rows, columns, row_stride , column_stride; + matrix_get_dims( matrix , &rows , &columns , &row_stride , &column_stride); + if (!( (column_stride >= rows) && (row_stride == 1))) + util_abort("%s: lapack routines require Fortran layout of memory - aborting \n",__func__); +} + + +static void matrix_lapack_assert_square(const matrix_type * matrix) { + matrix_lapack_assert_fortran_layout(matrix ); + { + int rows, columns, row_stride , column_stride; + matrix_get_dims( matrix , &rows , &columns , &row_stride , &column_stride); + if (rows != columns) + util_abort("%s: must have square matrices \n",__func__); + } +} + + +/*****************************************************************/ +/** + Solves the linear equations Ax = B. The solution is stored in B on + return. +*/ + + +void matrix_dgesv(matrix_type * A , matrix_type * B) { + matrix_lapack_assert_square( A ); + matrix_lapack_assert_fortran_layout( B ); + { + int n = matrix_get_rows( A ); + int lda = matrix_get_column_stride( A ); + int ldb = matrix_get_column_stride( B ); + int nrhs = matrix_get_columns( B ); + long int * ipivot = (long int*)util_calloc( n , sizeof * ipivot ); + int info; + + dgesv_(&n , &nrhs , matrix_get_data( A ) , &lda , ipivot , matrix_get_data( B ), &ldb , &info); + if (info != 0) + util_abort("%s: low level lapack routine: dgesv() failed with info:%d \n",__func__ , info); + free(ipivot); + } +} + + +void matrix_dgesvx(matrix_type * A , matrix_type * B, double * rcond) { + matrix_lapack_assert_square( A ); + matrix_lapack_assert_fortran_layout( B ); + { + int n = matrix_get_rows( A ); + int lda = matrix_get_column_stride( A ); + int ldb = matrix_get_column_stride( B ); + int nrhs = matrix_get_columns( B ); + int info; + int i,j; + + int ldx = ldb; + int ldaf = n; + char trans='N'; + char fact='N'; + char equed='N'; + + double * X = (double*)util_calloc( nrhs*n , sizeof * X ); + double * AF = (double*)util_calloc( n*n , sizeof * AF ); + double * C = (double*)util_calloc( n , sizeof * C ); + double * R = (double*)util_calloc( n , sizeof * R ); + double * work = (double*)util_calloc( 4*n , sizeof * work ); + double * ferr = (double*)util_calloc( nrhs , sizeof * ferr ); + double * berr = (double*)util_calloc( nrhs , sizeof * berr ); + long int * ipivot = (long int*)util_calloc( n , sizeof * ipivot ); + long int * iwork = (long int*)util_calloc( n , sizeof * iwork ); + + + dgesvx_(&fact, &trans , &n, &nrhs, matrix_get_data( A ), &lda, AF, &ldaf, ipivot, &equed, R, C, matrix_get_data( B ), &ldb, X, &ldx, rcond, ferr, berr, work, iwork, &info); + if (info != 0) + util_abort("%s: low level lapack routine: dgesvx() failed with info:%d \n",__func__ , info); + + for (j=0; j < B->columns; j++) + for (i=0; i < B->rows; i++) + matrix_iset(B , i , j , X[j*B->rows+i]); + + free(X); + free(AF); + free(C); + free(R); + free(work); + free(ferr); + free(berr); + free(ipivot); + free(iwork); + } +} + + +/*****************************************************************/ +/** + Singular Value Decomposition +*/ + + +/** + This little function translates between an integer identifier + (i.e. and enum instance) to one of the characters used by the low + level lapack routine to indicate how the singular vectors should be + returned to the calling scope. + + The meaning of the different enum values is documented in the enum + definition in the header file matrix_lapack.h. +*/ + +static char dgesvd_get_vector_job( dgesvd_vector_enum vector_job) { + char job = 'X'; + switch (vector_job) { + case(DGESVD_ALL): + job = 'A'; + break; + case(DGESVD_MIN_RETURN): + job = 'S'; + break; + case(DGESVD_MIN_OVERWRITE): + job = 'O'; + break; + case(DGESVD_NONE): + job = 'N'; + break; + default: + util_abort("%s: internal error - unrecognized code:%d \n",vector_job); + } + return job; +} + + + +/** + If jobu == DGSEVD_NONE the U matrix can be NULL, same for jobvt. +*/ + +void matrix_dgesvd(dgesvd_vector_enum jobu , dgesvd_vector_enum jobvt , matrix_type * A , double * S , matrix_type * U , matrix_type * VT) { + char _jobu = dgesvd_get_vector_job( jobu ); + char _jobvt = dgesvd_get_vector_job( jobvt ); + int m = matrix_get_rows( A ); + int n = matrix_get_columns( A ); + int lda = matrix_get_column_stride( A ); + int ldu, ldvt; + double * VT_data , *U_data; + int info = 0; + int min_worksize = util_int_max(3* util_int_min(m , n) + util_int_max(m , n) , 5 * util_int_min(m , n)); + double * work; + int worksize; + + + if (U == NULL) { + ldu = 1; + U_data = NULL; + if (jobu != DGESVD_NONE) + util_abort("%s: internal error \n",__func__); + } else { + ldu = matrix_get_column_stride( U ); + U_data = matrix_get_data( U ); + if (jobu == DGESVD_NONE) + util_abort("%s: internal error \n",__func__); + } + + if (VT == NULL) { + ldvt = 1; /* Will fail if set to zero */ + VT_data = NULL; + if (jobvt != DGESVD_NONE) + util_abort("%s: internal error \n",__func__); + } else { + ldvt = matrix_get_column_stride( VT ); + VT_data = matrix_get_data( VT ); + if (jobvt == DGESVD_NONE) + util_abort("%s: internal error \n",__func__); + } + + /* + Query the routine for optimal worksize. + */ + + work = (double*)util_calloc( 1 , sizeof * work ); + worksize = -1; + dgesvd_(&_jobu , /* 1 */ + &_jobvt , /* 2 */ + &m , /* 3 */ + &n , /* 4 */ + matrix_get_data( A ) , /* 5 */ + &lda , /* 6 */ + S , /* 7 */ + U_data , /* 8 */ + &ldu , /* 9 */ + VT_data , /* 10 */ + &ldvt , /* 11 */ + work , /* 12 */ + &worksize , /* 13 */ + &info); /* 14 */ + + + /* Try to allocate optimal worksize. */ + worksize = (int) work[0]; + double * tmp = (double*)realloc( work , sizeof * work * worksize ); + if (tmp == NULL) { + /* Could not allocate optimal worksize - settle for the minimum. This can not fail. */ + worksize = min_worksize; + free(work); + work = (double*)util_calloc( worksize , sizeof * work ); + }else{ + work = tmp; /* The request for optimal worksize succeeded */ + } + + dgesvd_(&_jobu , &_jobvt , &m , &n , matrix_get_data( A ) , &lda , S , U_data , &ldu , VT_data , &ldvt , work , &worksize , &info); + free( work ); +} + + + +/******************************************************************/ +/* Eigenvalues of a symmetric matrix */ +/* Return value is the number of eigenvalues found. */ +/******************************************************************/ + +int matrix_dsyevx(bool compute_eig_vectors , + dsyevx_eig_enum which_values , /* DSYEVX | DSYEVX_VALUE_INTERVAL | DSYEVX_INDEX_INTERVAL */ + dsyevx_uplo_enum uplo, + matrix_type * A , /* The input matrix - is modified by the dsyevx() function. */ + double VL , /* Lower limit when using DSYEVX_VALUE_INTERVAL */ + double VU , /* Upper limit when using DSYEVX_VALUE_INTERVAL */ + int IL , /* Lower index when using DSYEVX_INDEX_INTERVAL */ + int IU , /* Upper index when using DSYEVX_INDEX_INTERVAL */ + double *eig_values , /* The calcualated eigenvalues */ + matrix_type * Z ) { /* The eigenvectors as columns vectors */ + + int lda = matrix_get_column_stride( A ); + int n = matrix_get_rows( A ); + char jobz; + char range; + char uplo_c; + + if (compute_eig_vectors) + jobz = 'V'; + else + jobz = 'N'; + + switch(which_values) { + case(DSYEVX_ALL): + range = 'A'; + break; + case(DSYEVX_VALUE_INTERVAL): + range = 'V'; + break; + case(DSYEVX_INDEX_INTERVAL): + range = 'I'; + break; + default: + util_abort("%s: internal error \n",__func__); + } + + if (uplo == DSYEVX_AUPPER) + uplo_c = 'U'; + else if (uplo == DSYEVX_ALOWER) + uplo_c = 'L'; + else + util_abort("%s: internal error \n",__func__); + + + if (!matrix_is_quadratic( A )) + util_abort("%s: matrix A must be quadratic \n",__func__); + + { + int num_eigenvalues , ldz, info , worksize; + int * ifail = (int*) util_calloc( n , sizeof * ifail ); + int * iwork = (int*) util_calloc( 5 * n , sizeof * iwork ); + double * work = (double*)util_calloc( 1 , sizeof * work ); + double * z_data; + double abstol = 0.0; /* SHopuld */ + + + if (compute_eig_vectors) { + ldz = matrix_get_column_stride( Z ); + z_data = matrix_get_data( Z ); + } else { + /* In this case we can accept that Z == NULL */ + ldz = 1; + z_data = NULL; + } + + /* First call to determine optimal worksize. */ + worksize = -1; + info = 0; + dsyevx_( &jobz, /* 1 */ + &range, /* 2 */ + &uplo_c, /* 3 */ + &n, /* 4 */ + matrix_get_data( A ), /* 5 */ + &lda , /* 6 */ + &VL , /* 7 */ + &VU , /* 8 */ + &IL , /* 9 */ + &IU , /* 10 */ + &abstol , /* 11 */ + &num_eigenvalues , /* 12 */ + eig_values , /* 13 */ + z_data , /* 14 */ + &ldz , /* 15 */ + work , /* 16 */ + &worksize , /* 17 */ + iwork , /* 18 */ + ifail , /* 19 */ + &info); /* 20 */ + + + worksize = (int) work[0]; + { + double * tmp = (double*)realloc(work , sizeof * work * worksize ); + if (tmp == NULL) { + /* + OK - we could not get the optimal worksize, + try again with the minimum. + */ + worksize = 8 * n; + work = (double*)util_realloc(work , sizeof * work * worksize ); + } else + work = tmp; /* The request for optimal worksize succeeded */ + } + /* Second call: do the job */ + info = 0; + dsyevx_( &jobz, + &range, + &uplo_c, + &n, + matrix_get_data( A ), + &lda , + &VL , + &VU , + &IL , + &IU , + &abstol , + &num_eigenvalues , + eig_values , + z_data , + &ldz , + work , + &worksize , + iwork , + ifail , + &info); + + free( ifail ); + free( work ); + free( iwork ); + return num_eigenvalues; + } +} + +/*****************************************************************/ +/* Function to compute QR factorization withe the routine dgeqrf */ + +void matrix_dgeqrf(matrix_type * A , double * tau) { + int lda = matrix_get_column_stride( A ); + int m = matrix_get_rows( A ); + int n = matrix_get_columns( A ); + double * work = (double*)util_calloc(1 , sizeof * work ); + int worksize; + int info; + + + /* Determine optimal worksize. */ + worksize = -1; + dgeqrf_(&m , &n , matrix_get_data( A ), &lda , tau , work , &worksize , &info); + if (info != 0) + util_abort("%s: dgerqf routine failed with info:%d \n",__func__ , info); + worksize = ( int ) work[0]; + { + double * tmp = (double*)realloc(work , sizeof * work * worksize ); + if (tmp == NULL) { + /* + OK - we could not get the optimal worksize, + try again with the minimum. + */ + worksize = n; + work = (double*)util_realloc(work , sizeof * work * worksize ); + } else + work = tmp; /* The request for optimal worksize succeeded */ + } + + + /* Second call - do the actual computation. */ + dgeqrf_(&m , &n , matrix_get_data( A ), &lda , tau , work , &worksize , &info); + if (info != 0) + util_abort("%s: dgerqf routine failed with info:%d \n",__func__ , info); + free( work ); +} + + +/** + Typically to be used after the matrix_dgeqrf() function to construct a orthormal matrix. +*/ + +void matrix_dorgqr(matrix_type * A , double * tau, int num_reflectors) { /* num_reflectors == length of tau. */ + int lda = matrix_get_column_stride( A ); + int m = matrix_get_rows( A ); + int n = matrix_get_columns( A ); + double * work = (double*)util_malloc(sizeof * work ); + int worksize; + int info; + + + /* Determine optimal worksize. */ + worksize = -1; + dorgqr_(&m , &n , &num_reflectors , matrix_get_data( A ), &lda , tau , work , &worksize , &info); + if (info != 0) + util_abort("%s: dorgqf routine failed with info:%d \n",__func__ , info); + worksize = ( int ) work[0]; + { + double * tmp = (double*)realloc(work , sizeof * work * worksize ); + if (tmp == NULL) { + /* + OK - we could not get the optimal worksize, + try again with the minimum. + */ + worksize = n; + work = (double*)util_realloc(work , sizeof * work * worksize ); + } else + work = tmp; /* The request for optimal worksize succeeded */ + } + + + /* Second call - do the actual computation. */ + dorgqr_(&m , &n , &num_reflectors , matrix_get_data( A ), &lda , tau , work , &worksize , &info); + if (info != 0) + util_abort("%s: dorqf routine failed with info:%d \n",__func__ , info); + free( work ); +} + +/*****************************************************************/ + +/*****************************************************************/ +/* Factorization */ + +/* Currently only used as 'support' function for the matrix_det function. */ +static void matrix_dgetrf__( matrix_type * A, int * ipiv, int * info) { + int lda = matrix_get_column_stride( A ); + int m = matrix_get_rows( A ); + int n = matrix_get_columns( A ); + + dgetrf_( &m , &n , matrix_get_data( A ) , &lda , ipiv , info); +} + + +/** + Calculated the determinant of A. The matrix content will be + destroyed. +*/ + +double matrix_det( matrix_type *A ) { + matrix_lapack_assert_square( A ); + { + + int dgetrf_info; + double det = 1; + double det_scale = 0; + int n = matrix_get_columns( A ); + int * ipiv = (int*)util_malloc( n * sizeof * ipiv ); + matrix_dgetrf__( A , ipiv , &dgetrf_info ); + { + int i; + for (i=0; i < n; i++) { + det *= matrix_iget(A , i , i); + if (det == 0) return 0; + + if (ipiv[i] != (i + 1)) /* A permutation has taken place. */ + det *= -1; + + + /* Try to avoid overflow/underflow by factoring out the order of magnitude. */ + while (std::abs(det) > 10.0) { + det /= 10; + det_scale += 1; + } + + while (std::abs(det) < 1.0) { + det *= 10; + det_scale -= 1; + } + } + } + + free( ipiv ); + return det * pow(10 , det_scale ); + } +} + + + + + +/*****************************************************************/ +/* The matrix will be inverted in-place, the inversion is based on LU + factorisation in the routine matrix_dgetrf__( ). + + The return value: + + =0 : Success + >0 : Singular matrix + <0 : Invalid input +*/ + + + + +int matrix_inv( matrix_type * A ) { + matrix_lapack_assert_square( A ); + { + int dgetrf_info; + int info; + int n = matrix_get_columns( A ); + int * ipiv = (int*)util_malloc( n * sizeof * ipiv ); + matrix_dgetrf__( A , ipiv , &dgetrf_info ); + { + int lda = matrix_get_column_stride( A ); + double * work = (double*)util_malloc( sizeof * work ); + int work_size; + + /* First call: determine optimal worksize: */ + work_size = -1; + dgetri_( &n , matrix_get_data( A ), &lda , ipiv , work , &work_size , &info); + + if (info == 0) { + work_size = (int) work[0]; + work = (double*)util_realloc( work , sizeof * work * work_size ); + dgetri_( &n , matrix_get_data( A ), &lda , ipiv , work , &work_size , &info); + } else + util_abort("%s: dgetri_ returned info:%d \n",__func__ , info); + + free( work ); + } + free( ipiv ); + return info; + } +} + + +#ifdef __cplusplus +} +#endif diff --git a/libres/lib/res_util/matrix_stat.cpp b/libres/lib/res_util/matrix_stat.cpp new file mode 100644 index 00000000000..1c393725e6d --- /dev/null +++ b/libres/lib/res_util/matrix_stat.cpp @@ -0,0 +1,108 @@ +/* + Copyright (C) 2015 Equinor ASA, Norway. + + The file 'matrix_stat.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 + + +llsq_result_enum matrix_stat_llsq_estimate( matrix_type * beta , const matrix_type * X0 , const matrix_type * Y0 , const matrix_type * S) { + if (matrix_get_rows( beta ) != matrix_get_columns( X0 )) + return LLSQ_INVALID_DIM; + + if (matrix_get_rows( X0 ) != matrix_get_rows( Y0 )) + return LLSQ_INVALID_DIM; + + if (S && matrix_get_rows( S ) != matrix_get_rows( Y0 )) + return LLSQ_INVALID_DIM; + + if (matrix_get_rows(beta) > matrix_get_rows( X0 )) + return LLSQ_UNDETERMINED; + + { + int num_data = matrix_get_rows( X0 ); + int num_var = matrix_get_columns( X0 ); + matrix_type * XX = matrix_alloc( num_var , num_var ); + matrix_type * A = matrix_alloc( num_var , num_data ); + + matrix_type * X,*Y; + + if (S == NULL) { + X = (matrix_type *) X0; + Y = (matrix_type *) Y0; + } else { + X = matrix_alloc_copy( X0 ); + Y = matrix_alloc_copy( Y0 ); + + { + int row,col; + for (row = 0; row < matrix_get_rows( X0 ); row++) { + double sigma = matrix_iget(S , row , 0); + double weigth = 1.0 / (sigma * sigma ); + + for (col = 0; col < matrix_get_columns( X0 ); col++) + matrix_imul( X , row , col , weigth ); + + matrix_imul( Y , row , col , weigth ); + } + } + } + + matrix_matmul_with_transpose( XX , X , X , true , false ); + matrix_inv( XX ); + matrix_matmul_with_transpose( A , XX , X , false , true ); + matrix_matmul(beta , A , Y); + + matrix_free( XX ); + matrix_free( A ); + if (S) { + matrix_free( X ); + matrix_free( Y ); + } + } + + return LLSQ_SUCCESS; +} + + + + +llsq_result_enum matrix_stat_polyfit( matrix_type * beta , const matrix_type * X0 , const matrix_type * Y0 , const matrix_type * S) { + int num_data = matrix_get_rows( X0 ); + int num_var = matrix_get_rows( beta ); + llsq_result_enum result; + matrix_type * X = matrix_alloc( num_data , num_var ); + int row,col; + + for (row = 0; row < matrix_get_rows( X0 ); row++) { + double x1 = matrix_iget( X0 , row , 0 ); + double xp = 1; + for (col = 0; col < num_var; col++) { + matrix_iset(X , row , col , xp); + xp *= x1; + } + } + + result = matrix_stat_llsq_estimate( beta , X , Y0 , S); + matrix_free( X ); + return result; +} + diff --git a/libres/lib/res_util/path_fmt.cpp b/libres/lib/res_util/path_fmt.cpp new file mode 100644 index 00000000000..7c33fa4e297 --- /dev/null +++ b/libres/lib/res_util/path_fmt.cpp @@ -0,0 +1,295 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'path_fmt.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 "ert/util/build_config.hpp" +#include +#include + +#include + +/** +The basic idea of the path_fmt_type is that it should be possible for +a user to specify an arbirtrary path *WITH* embedded format +strings. It is implemented with the the help av variable length +argument lists. This has the following disadvantages: + + o The code gets ugly - really ugly. + + o It is difficult to provide type-safety on user input. + +Example: + + +path_fmt_type * path_fmt = path_fmt_alloc_directory_fmt("/tmp/ECLIPSE/%s/Run-%d"); + + +Here we have allocated a path_fmt instance which will require two +additional arguments when a full path is created, a string for the +"%s" placeholder and an integer for the %d placeholder: + +char * path = path_fmt_alloc(path_fmt , "BaseCase" , 67); + +=> path = /tmp/ECLIPSE/Basecase/Run-67 + +*/ + +#define PATH_FMT_ID 7519200 + + +struct path_fmt_struct { + UTIL_TYPE_ID_DECLARATION; + char *fmt; + char *file_fmt; + bool is_directory; +}; + + +static UTIL_SAFE_CAST_FUNCTION( path_fmt , PATH_FMT_ID) + +void path_fmt_reset_fmt(path_fmt_type * path , const char * fmt) { + path->fmt = util_realloc_string_copy(path->fmt , fmt); + if (path->is_directory) + path->file_fmt = util_alloc_sprintf("%s/%%s" , fmt); +} + + + +static path_fmt_type * path_fmt_alloc__(const char * fmt , bool is_directory) { + path_fmt_type * path = (path_fmt_type*)util_malloc(sizeof * path ); + UTIL_TYPE_ID_INIT(path , PATH_FMT_ID); + path->fmt = NULL; + path->file_fmt = NULL; + path->is_directory = is_directory; + + path_fmt_reset_fmt(path , fmt); + return path; +} + + +/** + + This function is used to allocate a path_fmt instance which is + intended to hold a directory, if the second argument is true, the + resulting directory will be automatically created when + path_fmt_alloc_path() is later invoked. + + Example: + ------- + path_fmt_type * path_fmt = path_fmt_alloc_directory_fmt("/tmp/scratch/member%d/%d.%d" , true); + .... + .... + char * path = path_fmt_alloc_path(path_fmt , 10 , 12 , 15); + char * file = path_fmt_alloc_file(path_fmt , 8 , 12 , 17, "SomeFile"); + + After the two last function calls we will have: + + o path = "/tmp/scratch/member10/12.15" - and this directory has + been created. + + o file = "/tmp/scratch/member8/12.17/SomeFile - and the directory + /tmp/scratch/member8/12.17 has been created. + + + Observe that the functionality is implemented with the help av + variable length argument lists, and **NO** checking of argument list + versus format string is performed. +*/ + + +path_fmt_type * path_fmt_alloc_directory_fmt(const char * fmt) { + return path_fmt_alloc__(fmt , true); +} + + + +/* + Most general. Can afterwards be used to allocate strings + representing both directories and files. +*/ + +path_fmt_type * path_fmt_alloc_path_fmt(const char * fmt) { + return path_fmt_alloc__(fmt , false ); +} + + +char * path_fmt_alloc_path_va(const path_fmt_type * path ,bool auto_mkdir, va_list ap) { + char * new_path = util_alloc_sprintf_va(path->fmt , ap ); + if (auto_mkdir) + if (! util_is_directory(new_path) ) + util_make_path(new_path); + return new_path; +} + + +char * path_fmt_alloc_path(const path_fmt_type * path , bool auto_mkdir , ...) { + char * new_path; + va_list ap; + va_start(ap , auto_mkdir); + new_path = path_fmt_alloc_path_va(path ,auto_mkdir , ap); + va_end(ap); + return new_path; +} + + + +/** + This function is used to allocate a filename (full path) from a + path_fmt instance: + + Eaxample: + + path_fmt_type * path_fmt = path_fmt_alloc_directory("/tmp/path%d/X.%02d"); + char * file = path_fmt_alloc_file(path_fmt , 100 , 78 , "SomeFile.txt") + + This will allocate the filename: /tmp/path100/X.78/SomeFile.txt; if + it does not already exist, the underlying directory will be + created. Observe that there is nothing special about the filename + argument (i.e. 'SomeFile.txt' in the current example), it is just + the last argument to the path_fmt_alloc_file() function call - + however it must be a string; i.e. if you are making a purely numeric + filename you must convert to a string. + + Observe that the handling of the variable length argument lists gets + seriously ugly. + + ----------------------------------------------------------------- + + If auto_mkdir == true the function behaves in two different ways + depending on whether the path_instance was allocated as a directory + or as a path: + + * [Directory]: When the path_fmt instance was allocated as a + directory, "/%s" format decriptor will be appended to the format. + + * [Path]: The resulting string will be split on "/", and the path + component will be created. +*/ + + +char * path_fmt_alloc_file(const path_fmt_type * path , bool auto_mkdir , ...) { + if (path->is_directory) { + char * filename; + va_list tmp_va , ap; + va_start(ap , auto_mkdir); + UTIL_VA_COPY(tmp_va , ap); + filename = util_alloc_sprintf_va( path->file_fmt , tmp_va ); + if (auto_mkdir) { + const char * __path = util_alloc_sprintf_va( path->fmt , tmp_va ); + if (! util_is_directory(__path)) + util_make_path( __path ); + free((char *) __path ); + } + va_end(ap); + return filename; + } else { + + char * filename; + va_list tmp_va , ap; + va_start(ap , auto_mkdir); + UTIL_VA_COPY(tmp_va , ap); + filename = util_alloc_sprintf_va( path->fmt , tmp_va ); + if (auto_mkdir) { + char * __path; + util_alloc_file_components(filename , &__path , NULL , NULL); + util_make_path(__path); + free(__path); + } + va_end(ap); + + return filename; + } +} + + + +/** + This function is used to assert that the format in a path_fmt + instance is according to specification. What is checked is that the + format string contains %d, %lfg and %s in as specified in the + input_types vector. + + Observe that %s is mapped to void_pointer - as the node_ctype does not + have typed pointers. +*/ + + +/* +void path_fmt_assert_fmt(const path_fmt_type * path , int num_input , const node_ctype * input_types) { + int input_nr = 0; + int char_nr = 0; + do { + if (path->fmt[char_nr] == '%') { + + } + } +} +*/ + +/* + fmt == NULL + ----------- + The function will return NULL, possibly freeing the incoming + path_fmt instance if that is different from NULL. + + + fmt != NULL + ----------- + The current path_fmt instance will be updated, or a new created. The + new/updated path_fmt instance is returned. +*/ + +path_fmt_type * path_fmt_realloc_path_fmt( path_fmt_type * path_fmt, const char * fmt ) { + if (fmt == NULL) { + if (path_fmt != NULL) + path_fmt_free( path_fmt ); + return NULL; + } else { + if (path_fmt == NULL) + return path_fmt_alloc_path_fmt( fmt ); + else { + path_fmt_reset_fmt( path_fmt , fmt ); + return path_fmt; + } + } +} + + +const char * path_fmt_get_fmt(const path_fmt_type * path) { + if (path == NULL) + return NULL; + else + return path->fmt; +} + + +void path_fmt_free(path_fmt_type * path) { + free(path->fmt); + if (path->is_directory) + free(path->file_fmt); + free(path); +} + + +void path_fmt_free__( void * arg ) { + path_fmt_type * path_fmt = path_fmt_safe_cast( arg ); + path_fmt_free( path_fmt ); +} diff --git a/libres/lib/res_util/regression.cpp b/libres/lib/res_util/regression.cpp new file mode 100644 index 00000000000..d62bf34d832 --- /dev/null +++ b/libres/lib/res_util/regression.cpp @@ -0,0 +1,74 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'regression.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 + + +/** + Performs an ordinary least squares estimation of the parameter + vector beta. + + beta = inv(X' . X) . X' . y +*/ + +void regression_augmented_OLS( const matrix_type * X , const matrix_type * Y , const matrix_type* Z, matrix_type * beta) { + /* + Solves the following especial augmented regression problem: + + [Y ; 0] = [X ; Z] beta + epsilon + + where 0 is the zero matrix of same size as Y. + + The solution to this OLS is: + + inv(X'X + Z'Z) * X' * Y + + The semicolon denotes row concatenation and the apostrophe the transpose. + + */ + int nvar = matrix_get_columns( X ); + matrix_type * Xt = matrix_alloc_transpose( X ); + matrix_type * Xinv = matrix_alloc( nvar , nvar); + matrix_matmul( Xinv , Xt , X ); + + matrix_type * Zt = matrix_alloc_transpose( Z ); + matrix_type * ZtZ = matrix_alloc( nvar , nvar); + matrix_matmul( ZtZ , Zt , Z ); + + // Xinv <- X'X + Z'Z + matrix_inplace_add(Xinv, ZtZ); + + // Sometimes the inversion fails - add a small regularization to diagonal + for (int i = 0; i < nvar; ++i) + matrix_iadd(Xinv, i, i, 1e-10); + + matrix_inv( Xinv ); // Xinv is always invertible + { + matrix_type * tmp = matrix_alloc_matmul( Xinv , Xt ); + matrix_matmul( beta , tmp , Y ); + matrix_free( tmp ); + } + + matrix_free( Xt ); + matrix_free( Xinv ); + matrix_free( Zt ); + matrix_free( ZtZ ); +} diff --git a/libres/lib/res_util/res_env.cpp b/libres/lib/res_util/res_env.cpp new file mode 100644 index 00000000000..919f54d4fd0 --- /dev/null +++ b/libres/lib/res_util/res_env.cpp @@ -0,0 +1,330 @@ +/* + Copyright (C) 2018 Equinor ASA, Norway. + + The file '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 +#include +#include + +#define PATHVAR_SPLIT ":" + +void res_env_unsetenv( const char * variable ) { + unsetenv( variable ); +} + +void res_env_setenv( const char * variable , const char * value) { + int overwrite = 1; + setenv( variable , value , overwrite ); +} + +/** + Will return a NULL terminated list char ** of the paths in the PATH + variable. +*/ + +char ** res_env_alloc_PATH_list() { + char ** path_list = NULL; + char * path_env = getenv("PATH"); + if (path_env != NULL) { + int path_size; + + util_split_string(path_env , PATHVAR_SPLIT , &path_size , &path_list); + path_list = (char**)util_realloc( path_list , (path_size + 1) * sizeof * path_list); + path_list[path_size] = NULL; + } else { + path_list = (char**)util_malloc( sizeof * path_list); + path_list[0] = NULL; + } + return path_list; +} + +/** + This function searches through the content of the (currently set) + PATH variable, and allocates a string containing the full path + (first match) to the executable given as input. + + * If the entered executable already is an absolute path, a copy of + the input is returned *WITHOUT* consulting the PATH variable (or + checking that it exists). + + * If the executable starts with "./" getenv("PWD") is prepended. + + * If the executable is not found in the PATH list NULL is returned. +*/ + + +char * res_env_alloc_PATH_executable(const char * executable) { + if (util_is_abs_path(executable)) { + if (util_is_executable(executable)) + return util_alloc_string_copy(executable); + else + return NULL; + } else if (strncmp(executable , "./" , 2) == 0) { + char * cwd = util_alloc_cwd(); + char * path = util_alloc_filename(cwd , &executable[2] , NULL); + + /* The program has been invoked as ./xxxx */ + if (!(util_is_file(path) && util_is_executable( path ))) { + free( path ); + path = NULL; + } + free( cwd ); + + return path; + } else { + char * full_path = NULL; + char ** path_list = res_env_alloc_PATH_list(); + int ipath = 0; + + while (true) { + if (path_list[ipath] != NULL) { + char * current_attempt = util_alloc_filename(path_list[ipath] , executable , NULL); + + if ( util_is_file( current_attempt ) && util_is_executable( current_attempt )) { + full_path = current_attempt; + break; + } else { + free(current_attempt); + ipath++; + } + } else + break; + } + + util_free_NULL_terminated_stringlist(path_list); + return full_path; + } +} + + + + + +/** + This function updates an environment variable representing a path, + before actually updating the environment variable the current value + is checked, and the following rules apply: + + 1. If @append == true, and @value is already included in the + environment variable; nothing is done. + + 2. If @append == false, and the variable already starts with + @value, nothing is done. + + A pointer to the updated(?) environment variable is returned. +*/ + +const char * res_env_update_path_var(const char * variable, const char * value, bool append) { + const char * current_value = getenv( variable ); + if (current_value == NULL) + /* The (path) variable is not currently set. */ + res_env_setenv( variable , value ); + else { + bool update = true; + + { + char ** path_list; + int num_path; + util_split_string( current_value , ":" , &num_path , &path_list); + if (append) { + int i; + for (i = 0; i < num_path; i++) { + if (util_string_equal( path_list[i] , value)) + update = false; /* The environment variable already contains @value - no point in appending it at the end. */ + } + } else { + if (util_string_equal( path_list[0] , value)) + update = false; /* The environment variable already starts with @value. */ + } + util_free_stringlist( path_list , num_path ); + } + + if (update) { + char * new_value; + if (append) + new_value = util_alloc_sprintf("%s:%s" , current_value , value); + else + new_value = util_alloc_sprintf("%s:%s" , value , current_value); + res_env_setenv( variable , new_value ); + free( new_value ); + } + + } + return getenv( variable ); +} + + + +/** + This is a thin wrapper around the setenv() call, with the twist + that all $VAR expressions in the @value parameter are replaced with + getenv() calls, so that the function call: + + res_env_setenv("PATH" , "$HOME/bin:$PATH") + + Should work as in the shell. If the variables referred to with ${} + in @value do not exist the literal string, i.e. '$HOME' is + retained. + + If @value == NULL a call to unsetenv( @variable ) will be issued. +*/ + +const char * res_env_interp_setenv( const char * variable , const char * value) { + char * interp_value = res_env_alloc_envvar( value ); + if (interp_value != NULL) { + res_env_setenv( variable , interp_value); + free( interp_value ); + } else + res_env_unsetenv( variable ); + + return getenv( variable ); +} + + + +/** + This function will take a string as input, and then replace all if + $VAR expressions with the corresponding environment variable. If + the environament variable VAR is not set, the string literal $VAR + is retained. The return value is a newly allocated string. + + If the input value is NULL - the function will just return NULL; +*/ + + +char * res_env_alloc_envvar( const char * value ) { + if (value == NULL) + return NULL; + else { + buffer_type * buffer = buffer_alloc( 1024 ); /* Start by filling up a buffer instance with + the current content of @value. */ + buffer_fwrite_char_ptr( buffer , value ); + buffer_rewind( buffer ); + + + while (true) { + if (buffer_strchr( buffer , '$')) { + const char * data = (const char*)buffer_get_data( buffer ); + int offset = buffer_get_offset( buffer ) + 1; /* Points at the first character following the '$' */ + int var_length = 0; + + /* Find the length of the variable name */ + while (true) { + char c; + c = data[offset + var_length]; + if (!(isalnum( c ) || c == '_')) /* Any character which is NOT in the set [a-Z,0-9_] marks the end of the variable. */ + break; + + if (c == '\0') /* The end of the string. */ + break; + + var_length += 1; + } + + { + char * var_name = util_alloc_substring_copy( data , offset - 1 , var_length + 1); /* Include the leading $ */ + const char * var_value = getenv( &var_name[1] ); + + if (var_value != NULL) + buffer_search_replace( buffer , var_name , var_value); /* The actual string replacement. */ + else + buffer_fseek( buffer , var_length , SEEK_CUR ); /* The variable is not defined, and we leave the $name. */ + + free( var_name ); + } + } else break; /* No more $ to replace */ + } + + + buffer_shrink_to_fit( buffer ); + { + char * expanded_value = (char*)buffer_get_data( buffer ); + buffer_free_container( buffer ); + return expanded_value; + } + } +} + +/** + This function will allocate a string copy of the env_index'th + occurence of an embedded environment variable from the input + string. + + An environment variable is defined as follows: + + 1. It starts with '$'. + 2. It ends with a characeter NOT in the set [a-Z,0-9,_]. + + The function will return environment variable number 'env_index'. If + no such environment variable can be found in the string the + function will return NULL. + + Observe that the returned string will start with '$'. This is to + simplify subsequent calls to util_string_replace_XXX() functions, + however &ret_value[1] must be used in the subsequent getenv() call: + + { + char * env_var = res_env_isscanf_alloc_envvar( s , 0 ); + if (env_var != NULL) { + const char * env_value = getenv( &env_var[1] ); // Skip the leading '$'. + if (env_value != NULL) + util_string_replace_inplace( s , env_value ); + else + fprintf(stderr,"** Warning: environment variable: \'%s\' is not defined \n", env_var); + free( env_var ); + } + } + + +*/ + +char * res_env_isscanf_alloc_envvar( const char * string , int env_index ) { + int env_count = 0; + const char * offset = string; + const char * env_ptr; + do { + env_ptr = strchr( offset , '$' ); + offset = &env_ptr[1]; + env_count++; + } while ((env_count <= env_index) && (env_ptr != NULL)); + + if (env_ptr != NULL) { + /* + We found an environment variable we are interested in. Find the + end of this variable and return a copy. + */ + int length = 1; + bool cont = true; + do { + + if ( !( isalnum(env_ptr[length]) || env_ptr[length] == '_' )) + cont = false; + else + length++; + if (length == strlen( env_ptr )) + cont = false; + } while (cont); + + return util_alloc_substring_copy( env_ptr , 0 , length ); + } else + return NULL; /* Could not find any env variable occurences. */ +} + diff --git a/libres/lib/res_util/res_log.cpp b/libres/lib/res_util/res_log.cpp new file mode 100644 index 00000000000..f3dcbe220a0 --- /dev/null +++ b/libres/lib/res_util/res_log.cpp @@ -0,0 +1,195 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'res_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. +*/ + +/* + * TODO: + * + * Should we have these as macros which can then insert the + * filename/line-number in the message? + */ + +#include +#include + +#include + +#include +#include + + +static FILE * DEFAULT_STREAM = stdout; +static log_type * logh = NULL; /* Handle to an open log file. */ + + + +static void res_log_init_stream(message_level_type log_level, + FILE * stream) { + if (logh) + log_close(logh); + + logh = log_open_stream(stream, log_level); + if (!logh) + fprintf(stderr,"Could not open stderr log stream\n"); +} + + +/** + * Initializes the log with default filename and default log level. + */ +static void res_log_init_default(){ + res_log_init_stream(DEFAULT_LOG_LEVEL, DEFAULT_STREAM); +} + + +/** + * Adding a message with a given message_level. + * + * A higher message_level means "more important", as only messages with + * message_level above the configured log_level will be included. + */ +void res_log_add_message(message_level_type message_level, + const char* message) { + if (!logh) + res_log_init_default(); + + /* We have not managed to open log handle, if the message is critical we write to stderr. */ + if (!logh) { + if (message_level >= LOG_ERROR ) + log_add_message_stream(stderr, message_level, (message_level_type)false, message); + return; + } + + + log_add_message(logh, message_level, message); +} + + +static void res_log_va(message_level_type level, const char * fmt, va_list args) { + char * message = util_alloc_sprintf_va(fmt, args); + res_log_add_message(level, message); + free(message); +} + + +void res_log_debug(const char* msg) { + res_log_add_message(LOG_DEBUG, msg); +} + +void res_log_info(const char* msg) { + res_log_add_message(LOG_INFO, msg); +} + +void res_log_warning(const char* msg) { + res_log_add_message(LOG_WARNING, msg); +} + +void res_log_error(const char* msg) { + res_log_add_message(LOG_ERROR, msg); +} + +void res_log_critical(const char* msg) { + res_log_add_message(LOG_CRITICAL, msg); +} + + +void res_log_fdebug(const char * fmt, ...) { + va_list ap; + va_start(ap, fmt); + res_log_va(LOG_DEBUG, fmt, ap); + va_end(ap); +} + +void res_log_finfo(const char * fmt, ...) { + va_list ap; + va_start(ap, fmt); + res_log_va(LOG_INFO, fmt, ap); + va_end(ap); +} + +void res_log_fwarning(const char * fmt, ...) { + va_list ap; + va_start(ap, fmt); + res_log_va(LOG_WARNING, fmt, ap); + va_end(ap); +} + +void res_log_ferror(const char * fmt, ...) { + va_list ap; + va_start(ap, fmt); + res_log_va(LOG_ERROR, fmt, ap); + va_end(ap); +} + +void res_log_fcritical(const char * fmt, ...) { + va_list ap; + va_start(ap, fmt); + res_log_va(LOG_CRITICAL, fmt, ap); + va_end(ap); +} + + +/** + * The logging uses log_level to determine if an incoming message is to be + * included in the log. + * + * A high log_level setting will include more messages. + * + * if log_file_name=NULL the logger will write to DEFAULT_STREAM (i.e. stdout). + */ +bool res_log_init_log(message_level_type log_level, + const char * log_file_name, + bool verbose) { + // If a log is already open, close it + if(logh) + log_close(logh); + + if(log_file_name) + logh = log_open_file(log_file_name , log_level ); + else + logh = log_open_stream( DEFAULT_STREAM, log_level); + + if (!logh) { + fprintf(stderr,"Failed to open log handle to %s\n", log_file_name); + return false; + } + + + if (verbose && log_file_name) + printf("Activity will be logged to %s \n",log_get_filename( logh )); + + log_add_message(logh, LOG_INFO, "ert configuration loaded"); + return true; +} + + +void res_log_close() { + if (logh) { + log_add_message(logh, (message_level_type)false, + "Exiting ert application normally - all is fine(?)"); + log_close( logh ); + } + logh = NULL; +} + +const char * res_log_get_filename() { + if (logh) + return log_get_filename(logh); + else + return NULL; +} + diff --git a/libres/lib/res_util/res_portability.cpp b/libres/lib/res_util/res_portability.cpp new file mode 100644 index 00000000000..19150972a3d --- /dev/null +++ b/libres/lib/res_util/res_portability.cpp @@ -0,0 +1,34 @@ +/* + Copyright (C) 2018 Equinor ASA, Norway. + + The file 'res_portability.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 + +void res_yield() { +#ifdef HAVE_YIELD_NP + pthread_yield_np(); +#else + #ifdef HAVE_YIELD + pthread_yield(); + #else + util_usleep(1000); + #endif +#endif +} + diff --git a/libres/lib/res_util/res_version.cpp b/libres/lib/res_util/res_version.cpp new file mode 100644 index 00000000000..82daec514a5 --- /dev/null +++ b/libres/lib/res_util/res_version.cpp @@ -0,0 +1,41 @@ +#include + +#include + +#define xstr(s) #s +#define str(s) xstr(s) + +const char* res_version_get_git_commit() { + #ifdef GIT_COMMIT + return str(GIT_COMMIT); + #else + return "Unknown git commit hash"; + #endif +} + +const char* res_version_get_build_time() { + #ifdef COMPILE_TIME_STAMP + return COMPILE_TIME_STAMP; + #else + return "Unknown build time"; + #endif +} + +int res_version_get_major_version() { + return RES_VERSION_MAJOR; +} + + +int res_version_get_minor_version() { + return RES_VERSION_MINOR; +} + + +const char * res_version_get_micro_version() { + return str(RES_VERSION_MICRO); +} + + +bool res_version_is_devel_version() { + return util_sscanf_int(str(RES_VERSION_MICRO), NULL); +} diff --git a/libres/lib/res_util/subst_func.cpp b/libres/lib/res_util/subst_func.cpp new file mode 100644 index 00000000000..30119c5a98f --- /dev/null +++ b/libres/lib/res_util/subst_func.cpp @@ -0,0 +1,199 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'subst_func.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 SUBST_FUNC_TYPE_ID 646781 +#define SUBST_FUNC_POOL_TYPE_ID 7641 + +struct subst_func_pool_struct { + UTIL_TYPE_ID_DECLARATION; + hash_type * func_table; +}; + + + +struct subst_func_struct { + UTIL_TYPE_ID_DECLARATION; + subst_func_ftype * func; + char * name; + char * doc_string; /* doc_string for this function - can be NULL. */ + bool vararg; + int argc_min; + int argc_max; + void * arg; /* 100% unmanaged void argument passed in from the construction. */ +}; + + + +char * subst_func_eval( const subst_func_type * subst_func , const stringlist_type * args) { + if (!subst_func->vararg) { + /* Checking that we have the right number of arguments. */ + int argc = stringlist_get_size( args ); + if (argc < subst_func->argc_min || argc > subst_func->argc_max) { + fprintf(stderr,"Fatal error when appying function:%s - got %d arguments: [",subst_func->name , argc); + stringlist_fprintf(args , " " , stderr); + fprintf(stderr,"] expected %d-%d arguments.\n", subst_func->argc_min , subst_func->argc_max); + util_abort("%s: Fatal error - aborting \n",__func__); + } + } + printf("Running:%s \n",subst_func->name); + return subst_func->func( args , subst_func->arg ); +} + + +subst_func_type * subst_func_alloc( const char * func_name , const char * doc_string , subst_func_ftype * func , bool vararg, int argc_min , int argc_max , void * arg) { + subst_func_type * subst_func = (subst_func_type*)util_malloc( sizeof * subst_func ); + UTIL_TYPE_ID_INIT( subst_func , SUBST_FUNC_TYPE_ID ); + subst_func->func = func; + subst_func->name = util_alloc_string_copy( func_name ); + subst_func->vararg = vararg; + subst_func->argc_min = argc_min; + subst_func->argc_max = argc_max; + subst_func->doc_string = util_alloc_string_copy( doc_string ); + subst_func->arg = arg; + return subst_func; +} + + +void subst_func_free( subst_func_type * subst_func ) { + free( subst_func->doc_string ); + free( subst_func->name ); + free( subst_func ); +} + + +UTIL_SAFE_CAST_FUNCTION( subst_func , SUBST_FUNC_TYPE_ID); + +static void subst_func_free__( void * arg ) { + subst_func_free( subst_func_safe_cast( arg )); +} + + + + +/*****************************************************************/ + +UTIL_IS_INSTANCE_FUNCTION( subst_func_pool , SUBST_FUNC_POOL_TYPE_ID); + +subst_func_pool_type * subst_func_pool_alloc( ) { + subst_func_pool_type * pool = (subst_func_pool_type*)util_malloc( sizeof * pool ); + UTIL_TYPE_ID_INIT( pool , SUBST_FUNC_POOL_TYPE_ID ); + pool->func_table = hash_alloc(); + return pool; +} + + + +void subst_func_pool_free( subst_func_pool_type * pool ) { + hash_free( pool->func_table ); + free( pool ); +} + + +void subst_func_pool_add_func( subst_func_pool_type * pool , const char * func_name , const char * doc_string , subst_func_ftype * func , bool vararg, int argc_min , int argc_max , void * arg) { + subst_func_type * subst_func = subst_func_alloc( func_name , doc_string , func , vararg , argc_min , argc_max , arg); + hash_insert_hash_owned_ref( pool->func_table , func_name , subst_func , subst_func_free__); +} + + +subst_func_type * subst_func_pool_get_func( const subst_func_pool_type * pool , const char * func_name ) { + return (subst_func_type*)hash_get( pool->func_table , func_name ); +} + +bool subst_func_pool_has_func( const subst_func_pool_type * pool , const char * func_name ) { + return hash_has_key( pool->func_table , func_name ); +} + + +/*****************************************************************/ + + +char * subst_func_exp( const stringlist_type * args , void * ext_arg) { + double arg; + if (util_sscanf_double( stringlist_iget(args , 0 ) , &arg)) + return util_alloc_sprintf("%g" , exp(arg)); + else + return NULL; +} + + +char * subst_func_log( const stringlist_type * args , void * ext_arg) { + double arg; + if (util_sscanf_double( stringlist_iget(args , 0 ) , &arg)) + return util_alloc_sprintf("%g" , log(arg)); + else + return NULL; +} + + +char * subst_func_pow10( const stringlist_type * args , void * ext_arg) { + double arg; + if (util_sscanf_double( stringlist_iget(args , 0 ) , &arg)) + return util_alloc_sprintf("%g" , pow(10 , arg)); + else + return NULL; +} + + + +char * subst_func_add( const stringlist_type * args , void * ext_arg) { + double sum = 0; + bool OK = true; + int index; + for (index = 0; index < stringlist_get_size( args ); index++) { + double term; + if (util_sscanf_double( stringlist_iget(args , index ) , &term)) + sum += term; + else + OK = false; + } + + if (OK) + return util_alloc_sprintf("%g" , sum); + else + return NULL; +} + + +char * subst_func_mul( const stringlist_type * args , void * ext_arg) { + double product = 0; + bool OK = true; + int index; + for (index = 0; index < stringlist_get_size( args ); index++) { + double factor; + if (util_sscanf_double( stringlist_iget(args , index ) , &factor)) + product *= factor; + else + OK = false; + } + + + if (OK) + return util_alloc_sprintf("%g" , product); + else + return NULL; +} diff --git a/libres/lib/res_util/subst_list.cpp b/libres/lib/res_util/subst_list.cpp new file mode 100644 index 00000000000..f26b53e6376 --- /dev/null +++ b/libres/lib/res_util/subst_list.cpp @@ -0,0 +1,846 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'subst_list.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 + +/** + This file implements a small support struct for search-replace + operations, along with wrapped calls to util_string_replace_inplace(). + + Substitutions can be carried out on files and string in memory (char * + with \0 termination); and the operations can be carried out inplace, or + in a filtering mode where a new file/string is created. + + + Usage + ===== + 1. Start with allocating a subst_list instance with subst_list_alloc(). + + 2. Insert key,value pairs to for search-replace with the functions + + * subst_list_insert_ref(subst_list , key , value); + * subst_list_insert_owned_ref(subst_list , key , value); + * subst_list_insert_copy(subst_list , key , value ); + + The difference between these functions is who is owning the memory + pointed to by the value pointer. + + 3. Do the actual search-replace operation on a file or memory buffer: + + * subst_list_filter_file() : Does search-replace on a file. + * subst_list_update_string() : Does search-replace on a buffer. + + 4. Free the subst_list and go home. + + Internally the (key,value) pairs used for substitutions are stored in a + vector, preserving insert order. If you insert the cascade + + ("A","B") + ("B","C") + ..... + ("Y","Z") + + You will eventually end up with a string where all capital letters have + been transformed to 'Z'. +*/ + + +typedef enum { SUBST_DEEP_COPY = 1, + SUBST_MANAGED_REF = 2, + SUBST_SHARED_REF = 3} subst_insert_type; /* Mode used in the subst_list_insert__() function */ + +#define SUBST_LIST_TYPE_ID 6614320 + +struct subst_list_struct { + UTIL_TYPE_ID_DECLARATION; + const subst_list_type * parent; /* A parent subst_list instance - can be NULL - no destructor is called for the parent. */ + vector_type * string_data; /* The string substitutions we should do. */ + vector_type * func_data; /* The functions we support. */ + const subst_func_pool_type * func_pool; /* NOT owned by the subst_list instance - can be NULL */ + hash_type * map; +}; + + + +typedef struct { + subst_func_type * func; /* Pointer to the real subst_func_type - implemented in subst_func.c */ + char * name; /* The name the function is recognized as - in this substitution context. */ +} subst_list_func_type; + + + + +/* + The subst_list type is implemented as a hash of subst_list_node + instances. This node type is not exported out of this file scope at + all. +*/ + +typedef struct { + bool value_owner; /* Wether the memory pointed to by value should bee freed.*/ + char * value; + char * key; + char * doc_string; /* A doc_string of this substitution - only for documentation - can be NULL. */ +} subst_list_string_type; + + + +/*****************************************************************/ +/* Here comes functions implementing the small + subst_list_string_type */ + +/** Allocates an empty instance with no values. */ +static subst_list_string_type * subst_list_string_alloc(const char * key) { + subst_list_string_type * node = (subst_list_string_type*)util_malloc(sizeof * node ); + node->value_owner = false; + node->value = NULL; + node->doc_string = NULL; + node->key = util_alloc_string_copy( key ); + return node; +} + + +static void subst_list_string_free_content( subst_list_string_type * node ) { + if (node->value_owner) + free( node->value ); +} + + +static void subst_list_string_free(subst_list_string_type * node) { + subst_list_string_free_content( node ); + free( node->doc_string ); + free(node->key); + free(node); +} + + +static void subst_list_string_free__(void * node) { + subst_list_string_free( (subst_list_string_type *) node ); +} + + + + +/** + input_value can be NULL. +*/ +static void subst_list_string_set_value(subst_list_string_type * node, const char * input_value , const char * doc_string , subst_insert_type insert_mode) { + subst_list_string_free_content( node ); + { + char * value; + if (insert_mode == SUBST_DEEP_COPY) + value = util_alloc_string_copy(input_value); + else + value = (char *) input_value; + + if (insert_mode == SUBST_SHARED_REF) + node->value_owner = false; + else + node->value_owner = true; + + node->value = value; + } + + if (doc_string != NULL) + node->doc_string = util_realloc_string_copy( node->doc_string , doc_string ); +} + +/*****************************************************************/ +/** + When arriving at this function the main subst scope should already have verified that + the requested function is available in the function pool. +*/ + +static subst_list_func_type * subst_list_func_alloc( const char * func_name, subst_func_type * func) { + subst_list_func_type * subst_func = (subst_list_func_type*)util_malloc( sizeof * subst_func ); + subst_func->name = util_alloc_string_copy( func_name ); + subst_func->func = func; + return subst_func; +} + + +static void subst_list_func_free( subst_list_func_type * subst_func ) { + free( subst_func->name ); + free( subst_func ); +} + + +static void subst_list_func_free__(void * node) { + subst_list_func_free( (subst_list_func_type *) node ); +} + +static char * subst_list_func_eval( const subst_list_func_type * subst_func , const stringlist_type * arglist) { + return subst_func_eval(subst_func->func , arglist ); +} + +/*****************************************************************/ + +/** + Find the node corresponding to 'key' - returning NULL if it is not found. +*/ + +static subst_list_string_type * subst_list_get_string_node(const subst_list_type * subst_list , const char * key) { + subst_list_string_type * node = NULL; + int index = 0; + + /* Linear search ... */ /*Should use map*/ + while ((index < vector_get_size(subst_list->string_data)) && (node == NULL)) { + subst_list_string_type * inode = (subst_list_string_type*)vector_iget( subst_list->string_data , index); + + if (strcmp(inode->key , key) == 0) /* Found it */ + node = inode; + else + index++; + } + + return node; +} + + + +static subst_list_string_type * subst_list_insert_new_node(subst_list_type * subst_list , const char * key , bool append) { + subst_list_string_type * new_node = subst_list_string_alloc(key); + + if (append) + vector_append_owned_ref( subst_list->string_data , new_node , subst_list_string_free__ ); + else + vector_insert_owned_ref( subst_list->string_data , 0 , new_node , subst_list_string_free__ ); + + hash_insert_ref( subst_list->map , key , new_node ); + return new_node; +} + +/*****************************************************************/ + +UTIL_IS_INSTANCE_FUNCTION( subst_list , SUBST_LIST_TYPE_ID ) + + +/** + Observe that this function sets both the subst parent, and the pool + of available functions. If this is call is repeated it is possible + to create a weird config with dangling function pointers - that is + a somewhat contrived and pathological use case, and not checked + for. +*/ + +void subst_list_set_parent( subst_list_type * subst_list , const subst_list_type * parent) { + subst_list->parent = parent; + if (parent != NULL) + subst_list->func_pool = subst_list->parent->func_pool; +} + + +bool subst_list_has_key( const subst_list_type * subst_list , const char * key) { + return hash_has_key( subst_list->map , key ); +} + + +/* + The input argument is currently only (void *), runtime it will be + checked whether it is of type subst_list_type, in which case it is + interpreted as a parent instance, if it is of type + subst_func_pool_type it is interpreted as a func_pool instance, + otherwise the function will fail hard. + + If the the input argument is a subst_list parent, the func_pool of + the parent is used also for the newly allocated subst_list + instance. +*/ + + +subst_list_type * subst_list_alloc(const void * input_arg) { + subst_list_type * subst_list = (subst_list_type*)util_malloc(sizeof * subst_list ); + UTIL_TYPE_ID_INIT( subst_list , SUBST_LIST_TYPE_ID); + subst_list->parent = NULL; + subst_list->func_pool = NULL; + subst_list->map = hash_alloc(); + subst_list->string_data = vector_alloc_new(); + subst_list->func_data = vector_alloc_new(); + + if (input_arg != NULL) { + if (subst_list_is_instance( input_arg )) + subst_list_set_parent( subst_list , (const subst_list_type*)input_arg ); + else if (subst_func_pool_is_instance( input_arg )) + subst_list->func_pool = (const subst_func_pool_type*)input_arg; + else + util_abort("%s: run_time cast failed - invalid type on input argument.\n",__func__); + } + + return subst_list; +} + + +/** + The semantics of the doc_string string is as follows: + + 1. If doc_string is different from NULL the doc_string is stored in the node. + + 2. If a NULL value follows a non-NULL value (i.e. the substitution + is updated) the doc_string is not changed. + + The idea is that the doc_string must just be included the first + time a (key,value) pair is added. On subsequent updates of the + value, the doc_string string can be NULL. +*/ + + +static void subst_list_insert__(subst_list_type * subst_list , const char * key , const char * value , const char * doc_string, bool append , + subst_insert_type insert_mode) { + subst_list_string_type * node = subst_list_get_string_node(subst_list , key); + + if (node == NULL) /* Did not have the node. */ + node = subst_list_insert_new_node(subst_list , key ,append); + subst_list_string_set_value(node , value , doc_string , insert_mode); +} + + +/** + There are three different functions for inserting a key-value pair + in the subst_list instance. The difference between the three is in + which scope takes/has ownership of 'value'. The alternatives are: + + subst_list_insert_ref: In this case the calling scope has full + ownership of value, and is consquently responsible for freeing + it, and ensuring that it stays a valid pointer for the subst_list + instance. Probably the most natural function to use when used + with static storage, i.e. typically string literals. + + subst_list_insert_owned_ref: In this case the subst_list takes + ownership of the value reference, in the sense that it will + free it when it is done. + + subst_list_insert_copy: In this case the subst_list takes a copy + of value and inserts it. Meaning that the substs_list instance + takes repsonibility of freeing, _AND_ the calling scope is free + to do whatever it wants with the value pointer. + +*/ + +void subst_list_append_owned_ref(subst_list_type * subst_list , const char * key , const char * value, const char * doc_string) { + subst_list_insert__(subst_list , key , value , doc_string , true , SUBST_MANAGED_REF); +} + +void subst_list_append_copy(subst_list_type * subst_list , const char * key , const char * value, const char * doc_string) { + subst_list_insert__(subst_list , key , value , doc_string , true , SUBST_DEEP_COPY); +} + +void subst_list_prepend_ref(subst_list_type * subst_list , const char * key , const char * value, const char * doc_string) { + subst_list_insert__(subst_list , key , value , doc_string , false , SUBST_SHARED_REF); +} + +void subst_list_prepend_owned_ref(subst_list_type * subst_list , const char * key , const char * value, const char * doc_string) { + subst_list_insert__(subst_list , key , value , doc_string , false , SUBST_MANAGED_REF); +} + +void subst_list_prepend_copy(subst_list_type * subst_list , const char * key , const char * value, const char * doc_string) { + subst_list_insert__(subst_list , key , value , doc_string , false , SUBST_DEEP_COPY); +} + + + +/** + This function will install the function @func_name from the current + subst_func_pool, it will be made available to this subst_list + instance with the function name @local_func_name. If @func_name is + not available, the function will fail hard. +*/ + +void subst_list_insert_func(subst_list_type * subst_list , const char * func_name , const char * local_func_name) { + + if (subst_list->func_pool != NULL && subst_func_pool_has_func( subst_list->func_pool , func_name )) { + subst_list_func_type * subst_func = subst_list_func_alloc( local_func_name , subst_func_pool_get_func( subst_list->func_pool , func_name )); + vector_append_owned_ref( subst_list->func_data , subst_func , subst_list_func_free__ ); + } else + util_abort("%s: function:%s not available \n",__func__ , func_name); +} + + +void subst_list_clear( subst_list_type * subst_list ) { + vector_clear( subst_list->string_data ); +} + + +void subst_list_free(subst_list_type * subst_list) { + vector_free( subst_list->string_data ); + vector_free( subst_list->func_data ); + hash_free( subst_list->map ); + free(subst_list); +} + + +/*****************************************************************/ +/* + Below comes many different functions for doing the actual updating + the functions differ in the form of the input and output. At the + lowest level, is the function + + subst_list_uppdate_buffer() + + which will update a buffer instance. This function again will call + two separate functions for pure string substitutions and for + function evaluation. + + The update replace functions will first apply all the string + substitutions for this particular instance, and afterwards calling + all the string substititions of the parent (recursively); the same + applies to the function replacements. +*/ + +/*****************************************************************/ + + +/** + Updates the buffer inplace with all the string substitutions in the + subst_list. This is the lowest level function, which does *NOT* + consider the parent pointer. +*/ +static bool subst_list_replace_strings__(const subst_list_type * subst_list , buffer_type * buffer) { + int index; + bool global_match = false; + for (index = 0; index < vector_get_size( subst_list->string_data ); index++) { + const subst_list_string_type * node = (const subst_list_string_type*)vector_iget_const( subst_list->string_data , index ); + if (node->value != NULL) { + bool match; + buffer_rewind( buffer ); + do { + match = buffer_search_replace( buffer , node->key , node->value); + if (match) + global_match = true; + } while (match); + } + } + return global_match; +} + + + + +/** + Updates the buffer inplace by evaluationg all the string functions + in the subst_list. Last performing all the replacements in the + parent. + + The rules for function evaluation are as follows: + + 1. Every function MUST have a '()', IMMEDIATELY following the + function name, this also applies to functions which do not have + any arguments. + + 2. The function parser is quite primitive, and can (at least + currently) not handle nested functions, i.e. + + __SUM__( __SUM__(1 ,2 ) , 3) + + will fail. + + 3. If the function evaluation fails; typicall because of wrong + type/number of arguments the buffer will not updated. + + + 4. The functions will return a freshly allocated (char *) pointer, + or NULL if the evaluation fails, and the input value is a list + of strings extracted from the parsing context - i.e. the + function is in no way limited to numeric functions like sum() + and exp(). + +*/ + +static bool subst_list_eval_funcs____(const subst_list_type * subst_list , const basic_parser_type * parser , buffer_type * buffer) { + bool global_match = false; + int index; + for (index = 0; index < vector_get_size( subst_list->func_data); index++) { + const subst_list_func_type * subst_func = (const subst_list_func_type*)vector_iget_const( subst_list->func_data , index ); + const char * func_name = subst_func->name; + + bool match; + buffer_rewind( buffer ); + do { + size_t match_pos; + match = buffer_strstr( buffer , func_name ); + match_pos = buffer_get_offset( buffer ); + + if (match) { + bool update = false; + char * arg_start = (char*)buffer_get_data( buffer ); + arg_start += buffer_get_offset( buffer ) + strlen( func_name ); + + if (arg_start[0] == '(') { /* We require that an opening paren follows immediately behind the function name. */ + char * arg_end = strchr( arg_start , ')'); + if (arg_end != NULL) { + /* OK - we found an enclosing () pair. */ + char * arg_content = util_alloc_substring_copy( arg_start, 1 , arg_end - arg_start - 1); + stringlist_type * arg_list = basic_parser_tokenize_buffer( parser , arg_content , true); + char * func_eval = subst_list_func_eval( subst_func , arg_list ); + int old_len = strlen(func_name) + strlen( arg_content) + 2; + + if (func_eval != NULL) { + buffer_memshift( buffer , match_pos + old_len , strlen( func_eval ) - old_len); + buffer_fwrite( buffer , func_eval , strlen( func_eval ) , sizeof * func_eval ); + free( func_eval ); + update = true; + global_match = true; + } + + free( arg_content ); + stringlist_free( arg_list ); + } + } + if (!update) + buffer_fseek( buffer , match_pos + strlen( func_name ) , SEEK_SET); + } + } while (match); + } + if (subst_list->parent != NULL) + global_match = (subst_list_eval_funcs____( subst_list->parent , parser , buffer ) || global_match); + return global_match; +} + + +static bool subst_list_eval_funcs__(const subst_list_type * subst_list , buffer_type * buffer) { + basic_parser_type * parser = basic_parser_alloc( "," , "\"\'" , NULL , " \t" , NULL , NULL ); + bool match = subst_list_eval_funcs____( subst_list , parser , buffer ); + + basic_parser_free( parser ); + return match; +} + + + +/** + Should we evaluate the parent first (i.e. top down), or this + instance first and then subsequently the parent (i.e. bottom + up). The problem is with inherited defintions: + + Inherited defintions + -------------------- + + In this situation we have defined a (key,value) substitution, + where the value depends on a value following afterwards: + + ("" , "/tmp/run/") + ("" , "Test4") + + I.e. first is replaced with "/tmp/run/" and then + subsequently "" is replaced with "Test4". A typical use + case here is that the common definition of "" is in the + parent, and consequently parent should run first (i.e. top + down). + + However, in other cases the order of defintion might very well be + opposite, i.e. with "" first and then things will blow up: + + 1. : Not found + 2. -> /tmp/run/ + + and, the final will not be resolved. I.e. there is no + obvious 'right' way to do it. + + + + Overriding defaults + ------------------- + + The parent has defined: + + ("" , "/tmp/run/default") + + But for a particular instance we would like to overwrite + with another definition: + + ("" , "/tmp/run/special_case") + + This will require evaluating the bottom first, i.e. a bottom up + approach. + + + Currently the implementation is purely top down, the latter case + above is not supported. The actual implementation here is in terms + of recursion, the low level function doing the stuff is + subst_list_replace_strings__() which is not recursive. +*/ + +static bool subst_list_replace_strings( const subst_list_type * subst_list , buffer_type * buffer ) { + bool match = false; + if (subst_list->parent != NULL) + match = subst_list_replace_strings( subst_list->parent , buffer ); + + /* The actual string replace */ + match = (subst_list_replace_strings__( subst_list , buffer ) || match); + return match; +} + + +/* + This function updates a buffer instance inplace with all the + substitutions in the subst_list. + + This is the common low-level function employed by all the the + subst_update_xxx() functions. Observe that it is a hard assumption + that the buffer has a \0 terminated string. +*/ + + +bool subst_list_update_buffer( const subst_list_type * subst_list , buffer_type * buffer ) { + bool match1 = subst_list_replace_strings( subst_list , buffer ); + bool match2 = subst_list_eval_funcs__( subst_list , buffer ); + return (match1 || match2); // Funny construction to ensure to avoid fault short circuit. +} + + +/** + This function reads the content of a file, and writes a new file + where all substitutions in subst_list have been performed. Observe + that target_file and src_file *CAN* point to the same file, in + which case this will amount to an inplace update. In that case a + backup file is written, and held, during the execution of the + function. + + Observe that @target_file can contain a path component, that + component will be created if it does not exist. +*/ + + +bool subst_list_filter_file(const subst_list_type * subst_list , const char * src_file , const char * target_file) { + bool match; + char * backup_file = NULL; + buffer_type * buffer = buffer_fread_alloc( src_file ); + buffer_fseek( buffer , 0 , SEEK_END ); /* Ensure that the buffer is a \0 terminated string. */ + buffer_fwrite_char( buffer , '\0'); + + /*****************************************************************/ + /* Backup file ?? */ + if (util_same_file(src_file , target_file)) { + char * backup_prefix = util_alloc_sprintf("%s-%s" , src_file , __func__); + backup_file = util_alloc_tmp_file("/tmp" , backup_prefix , false); + free(backup_prefix); + } + + + /* Writing backup file */ + if (backup_file != NULL) { + FILE * stream = util_fopen(backup_file , "w"); + buffer_stream_fwrite_n( buffer , 0 , -1 , stream ); /* -1: Do not write the trailing \0. */ + fclose(stream); + } + + + /* Doing the actual update */ + match = subst_list_update_buffer(subst_list , buffer); + + + /* Writing updated file */ + { + FILE * stream = util_mkdir_fopen(target_file , "w"); + buffer_stream_fwrite_n( buffer , 0 , -1 , stream ); /* -1: Do not write the trailing \0. */ + fclose(stream); + } + + /* OK - all went hunka dory - unlink the backup file and leave the building. */ + if (backup_file != NULL) { + remove( backup_file ); + free( backup_file ); + } + buffer_free( buffer ); + return match; +} + + +/** + This function does search-replace on string instance inplace. +*/ +bool subst_list_update_string(const subst_list_type * subst_list , char ** string) { + buffer_type * buffer = buffer_alloc_private_wrapper( *string , strlen( *string ) + 1); + bool match = subst_list_update_buffer(subst_list , buffer); + *string = (char*)buffer_get_data( buffer ); + buffer_free_container( buffer ); + + return match; +} + + + +/** + This function allocates a new string where the search-replace + operation has been performed. +*/ + +char * subst_list_alloc_filtered_string(const subst_list_type * subst_list , const char * string) { + char * filtered_string = util_alloc_string_copy( string ); + if (subst_list) + subst_list_update_string( subst_list , &filtered_string ); + return filtered_string; +} + + +/*****************************************************************/ +/* End of update/apply functions */ +/*****************************************************************/ + + + + +/** + This allocates a new subst_list instance, the copy process is deep, + in the sense that all srings inserted in the new subst_list + instance have their own storage, irrespective of the ownership in + the original subst_list instance. +*/ + +subst_list_type * subst_list_alloc_deep_copy(const subst_list_type * src) { + subst_list_type * copy; + if (src->parent != NULL) + copy = subst_list_alloc( src->parent ); + else + copy = subst_list_alloc( src->func_pool); + + { + int index; + for (index = 0; index < vector_get_size( src->string_data ); index++) { + const subst_list_string_type * node = (const subst_list_string_type*)vector_iget_const( src->string_data , index ); + subst_list_insert__( copy , node->key , node->value , node->doc_string , true , SUBST_DEEP_COPY); + } + + for (index = 0; index < vector_get_size( src->func_data ); index++) { + const subst_list_func_type * src_node = (const subst_list_func_type*)vector_iget_const( src->func_data , index ); + subst_list_func_type * copy_node = subst_list_func_alloc( src_node->name , src_node->func ); + vector_append_owned_ref( copy->func_data , copy_node , subst_list_func_free__ ); + } + + } + return copy; +} + + +int subst_list_get_size( const subst_list_type * subst_list) { + return vector_get_size(subst_list->string_data); +} + + +const char * subst_list_iget_key( const subst_list_type * subst_list , int index) { + if (index < vector_get_size(subst_list->string_data)) { + const subst_list_string_type * node = (const subst_list_string_type*)vector_iget_const( subst_list->string_data , index ); + return node->key; + } else { + util_abort("%s: index:%d to large \n",__func__ , index); + return NULL; + } +} + + +const char * subst_list_iget_value( const subst_list_type * subst_list , int index) { + if (index < vector_get_size(subst_list->string_data)) { + const subst_list_string_type * node = (const subst_list_string_type*)vector_iget_const( subst_list->string_data , index ); + return node->value; + } else { + util_abort("%s: index:%d to large \n",__func__ , index); + return NULL; + } +} + +const char * subst_list_get_value( const subst_list_type * subst_list , const char * key) { + const subst_list_string_type * node = (const subst_list_string_type*)hash_get( subst_list->map , key ); + return node->value; +} + + + +const char * subst_list_get_doc_string( const subst_list_type * subst_list , const char * key) { + const subst_list_string_type * node = (const subst_list_string_type*)hash_get( subst_list->map , key ); + return node->doc_string; +} + + +void subst_list_fprintf(const subst_list_type * subst_list , FILE * stream) { + int index; + for (index=0; index < vector_get_size( subst_list->string_data ); index++) { + const subst_list_string_type * node = (const subst_list_string_type*)vector_iget_const( subst_list->string_data , index ); + fprintf(stream , "%s = %s\n" , node->key , node->value); + } +} + + +/** Will loose tagging .... */ +int subst_list_add_from_string( subst_list_type * subst_list , const char * arg_string, bool append) { + int error_count = 0; + if (arg_string != NULL) { + char ** key_value_list; + int num_arg, iarg; + + util_split_string(arg_string , "," , &num_arg , &key_value_list); + for (iarg = 0; iarg < num_arg; iarg++) { + if (strchr(key_value_list[iarg] , '=') == NULL) + //util_abort("%s: could not find \'=\' in argument string:%s \n",__func__ , key_value_list[iarg]); + /* + Could not find '=' in the argument string, this argument will + be ignored, and the error_count will be increased by one. + */ + error_count += 1; + else { + char * key , * value; + char * tmp = key_value_list[iarg]; + int arg_length , value_length; + while (isspace(*tmp)) /* Skipping initial space */ + tmp++; + + arg_length = strcspn(tmp , " ="); + key = util_alloc_substring_copy(tmp , 0 , arg_length); + tmp += arg_length; + while ((*tmp == ' ') || (*tmp == '=')) + tmp++; + + value_length = strcspn(tmp , " "); + value = util_alloc_substring_copy( tmp , 0 , value_length); + + /* Setting the argument */ + if (append) + subst_list_append_copy( subst_list , key , value , NULL); + else + subst_list_prepend_copy( subst_list , key , value , NULL); + + free(key); + free(value); + tmp += value_length; + + + /* Accept only trailing space - any other character indicates a failed parsing. */ + while (*tmp != '\0') { + if (!isspace(*tmp)) + util_abort("%s: something wrong with:%s - spaces are not allowed in key or value part.\n",__func__ , key_value_list[iarg]); + tmp++; + } + } + } + util_free_stringlist(key_value_list , num_arg); + } + return error_count; +} + + + +/*****************************************************************/ diff --git a/libres/lib/res_util/template.cpp b/libres/lib/res_util/template.cpp new file mode 100644 index 00000000000..b322c71ba2a --- /dev/null +++ b/libres/lib/res_util/template.cpp @@ -0,0 +1,224 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'template.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 + +/** + Iff the template is set up with internaliz_template == false the + template content is loaded at instantiation time, and in that case + the name of the template file can contain substitution characters - + i.e. in this case different instance can use different source + templates. + + To avoid race issues this function does not set actually update the + state of the template object. +*/ + +static char * template_load( const template_type * _template , const subst_list_type * ext_arg_list) { + int buffer_size; + char * template_file = util_alloc_string_copy( _template->template_file ); + char * template_buffer; + + subst_list_update_string( _template->arg_list , &template_file); + if (ext_arg_list != NULL) + subst_list_update_string( ext_arg_list , &template_file); + + template_buffer = util_fread_alloc_file_content( template_file , &buffer_size ); + free( template_file ); + + return template_buffer; +} + + + +void template_set_template_file( template_type * _template , const char * template_file) { + _template->template_file = util_realloc_string_copy( _template->template_file , template_file ); + if (_template->internalize_template) { + free( _template->template_buffer ); + _template->template_buffer = template_load( _template , NULL ); + } +} + +/** This will not instantiate */ +const char * template_get_template_file( const template_type * _template ) { + return _template->template_file; +} + + + +/** + This function allocates a template object based on the source file + 'template_file'. If @internalize_template is true the template + content will be read and internalized at boot time, otherwise that + is deferred to template instantiation time (in which case the + template file can change dynamically). +*/ + + +template_type * template_alloc( const char * template_file , bool internalize_template , subst_list_type * parent_subst) { + template_type * _template = (template_type*)util_malloc( sizeof * _template ); + UTIL_TYPE_ID_INIT(_template , TEMPLATE_TYPE_ID); + _template->arg_list = subst_list_alloc( parent_subst ); + _template->template_buffer = NULL; + _template->template_file = NULL; + _template->internalize_template = internalize_template; + _template->arg_string = NULL; + template_set_template_file( _template , template_file ); + +#ifdef ERT_HAVE_REGEXP + template_init_loop_regexp( _template ); +#endif + return _template; +} + + + + + + + +void template_free( template_type * _template ) { + subst_list_free( _template->arg_list ); + free( _template->template_file ); + free( _template->template_buffer ); + free( _template->arg_string ); + +#ifdef ERT_HAVE_REGEXP + regfree( &_template->start_regexp ); + regfree( &_template->end_regexp ); +#endif + + free( _template ); +} + + + +/** + This function will create the file @__target_file based on the + template instance. Before the target file is written all the + internal substitutions and then subsequently the subsititutions in + @arg_list will be performed. The input @arg_list can be NULL - in + which case this is more like copy operation. + + Observe that: + + 1. Substitutions will be performed on @__target_file + + 2. @__target_file can contain path components. + + 3. If internalize_template == false subsititions will be performed + on the filename of the file with template content. + + 4. If the parameter @override_symlink is true the function will + have the following behaviour: + + If the target_file already exists as a symbolic link, the + symbolic link will be removed prior to creating the instance, + ensuring that a remote file is not updated. + +*/ + + + +void template_instantiate( const template_type * template_ , const char * __target_file , const subst_list_type * arg_list , bool override_symlink) { + char * target_file = util_alloc_string_copy( __target_file ); + + /* Finding the name of the target file. */ + subst_list_update_string( template_->arg_list , &target_file); + if (arg_list != NULL) subst_list_update_string( arg_list , &target_file ); + + { + char * char_buffer; + /* Loading the template - possibly expanding keys in the filename */ + if (template_->internalize_template) + char_buffer = util_alloc_string_copy( template_->template_buffer); + else + char_buffer = template_load( template_ , arg_list ); + + /* Substitutions on the content. */ + subst_list_update_string( template_->arg_list , &char_buffer ); + if (arg_list != NULL) subst_list_update_string( arg_list , &char_buffer ); + + +#ifdef ERT_HAVE_REGEXP + { + buffer_type * buffer = buffer_alloc_private_wrapper( char_buffer , strlen( char_buffer ) + 1); + template_eval_loops( template_ , buffer ); + char_buffer = (char*)buffer_get_data( buffer ); + buffer_free_container( buffer ); + } +#endif + + /* + Check if target file already exists as a symlink, + and remove it if override_symlink is true. + */ + if (override_symlink) { + if (util_is_link( target_file )) + remove( target_file ); + } + + /* Write the content out. */ + { + FILE * stream = util_mkdir_fopen( target_file , "w"); + fprintf(stream , "%s" , char_buffer); + fclose( stream ); + } + free( char_buffer ); + } + + free( target_file ); +} + + +/** + Add an internal key_value pair. This substitution will be performed + before the internal substitutions. +*/ +void template_add_arg( template_type * _template , const char * key , const char * value ) { + subst_list_append_copy( _template->arg_list , key , value , NULL /* No doc_string */); +} + +subst_list_type * template_get_args_list(template_type * _template) { + return _template->arg_list; +} + + +void template_clear_args( template_type * _template ) { + subst_list_clear( _template->arg_list ); +} + + +int template_add_args_from_string( template_type * _template , const char * arg_string) { + return subst_list_add_from_string( _template->arg_list , arg_string , true); +} + + +/*****************************************************************/ + + diff --git a/libres/lib/res_util/template_loop.cpp b/libres/lib/res_util/template_loop.cpp new file mode 100644 index 00000000000..0d5da591daf --- /dev/null +++ b/libres/lib/res_util/template_loop.cpp @@ -0,0 +1,290 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'template_loop.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 + +#define END_REGEXP "[{]%[[:space:]]+endfor[[:space:]]+%[}]" +#define LOOP_REGEXP "[{]%[[:space:]]+for[[:space:]]+([$]?[[:alpha:]][[:alnum:]]*)[[:space:]]+in[[:space:]]+[[]([^]]*)[]][[:space:]]+%[}]" + +#define LOOP_OPTIONS REG_EXTENDED +#define END_OPTIONS REG_EXTENDED + +#define DOLLAR '$' +/* + This file implements a simple looping construct in the templates. The support + is strongly based on the POSIX regexp functionality; and this file will not + be compiled unless that support is present. +*/ + +struct loop_struct { + int opentag_offset; + int opentag_length; + + int endtag_offset; + int endtag_length; + + int body_offset; + int body_length; + + bool replace_substring; + int var_length; + char * loop_var; + stringlist_type * items; +}; + +static void regcompile(regex_t * reg , const char * reg_string , int flags) { + int error = regcomp(reg , reg_string , flags); + if (error) { + int error_size = 256; + char error_buffer[256]; + regerror(error , reg , error_buffer , error_size); + util_exit("Compile of %s failed: %s \n",LOOP_REGEXP, error_buffer); + } +} + + + + +static loop_type * loop_alloc( const char * buffer , int global_offset , regmatch_t tag_offset , regmatch_t __var_offset , regmatch_t __items_offset) { + loop_type * loop = (loop_type*)util_malloc( sizeof * loop); + + loop->opentag_offset = global_offset + tag_offset.rm_so; + loop->opentag_length = tag_offset.rm_eo - tag_offset.rm_so; + + loop->endtag_offset = -1; + loop->endtag_length = -1; + + loop->body_offset = loop->opentag_offset + loop->opentag_length; + loop->body_length = -1; + { + int var_offset = global_offset + __var_offset.rm_so; + int var_length = __var_offset.rm_eo - __var_offset.rm_so; + loop->loop_var = util_alloc_substring_copy( buffer , var_offset , var_length ); + loop->var_length = strlen( loop->loop_var ); + if (loop->loop_var[0] == DOLLAR) + loop->replace_substring = true; + else + loop->replace_substring = false; + } + { + int items_offset = global_offset + __items_offset.rm_so; + int items_length = __items_offset.rm_eo - __items_offset.rm_so; + char * items_string = util_alloc_substring_copy( buffer , items_offset , items_length ); + basic_parser_type * parser = basic_parser_alloc("," , NULL , NULL , NULL , NULL , NULL); // Does not tolerate spaces around the splitting "," + + loop->items = basic_parser_tokenize_buffer( parser , items_string , false); + + basic_parser_free( parser ); + free( items_string ); + } + return loop; +} + +static void loop_set_endmatch( loop_type * loop , int global_offset , regmatch_t end_offset) { + loop->endtag_offset = global_offset + end_offset.rm_so; + loop->endtag_length = end_offset.rm_eo - end_offset.rm_so; + loop->body_length = loop->endtag_offset - loop->opentag_offset - loop->opentag_length; +} + + +static void loop_free( loop_type * loop ) { + free( loop->loop_var ); + stringlist_free( loop->items ); + free( loop ); +} + + + + +static void replace_1var( buffer_type * var_expansion , int shift , int write_offset , int shift_offset , const char * value) { + buffer_memshift( var_expansion , shift_offset , shift ); + buffer_fseek( var_expansion , write_offset , SEEK_SET ); + buffer_fwrite( var_expansion , value , strlen(value) , 1); +} + + +static void loop_eval( const loop_type * loop , const char * body , buffer_type * var_expansion , int ivar) { + buffer_clear( var_expansion ); + buffer_fwrite_char_ptr( var_expansion , body ); + { + const char * value = stringlist_iget( loop->items , ivar ); + int value_length = strlen( value ); + int shift = value_length - loop->var_length; + int search_offset = 0; + + + while (true) { + char * data = (char*)buffer_get_data( var_expansion ); + char * match_ptr = strstr( &data[search_offset] , loop->loop_var ); + + if (match_ptr == NULL) + break; + else { + + /* Check that the match is either at the very start of the + string, or alternatively NOT preceeded by alphanumeric + character. If the variable starts with a '$' we ignore this + test. + */ + if (!loop->replace_substring) { + if (match_ptr != &data[search_offset]) { char + pre_char = match_ptr[-1]; if (isalnum( pre_char )) { + search_offset = match_ptr - data + 1; + + if (search_offset >= strlen(data)) + break; + else + continue; + } + } + + + /* + Check that the match is at the very end of the string, or + alternatively followed by a NON alphanumeric character. */ + if (strlen(match_ptr) > loop->var_length) { + char end_char = match_ptr[ loop->var_length ]; + if (isalnum( end_char )) + break; + } + } + + /* OK - this is a valid match; update the string buffer. */ + { + int write_offset = match_ptr - data; + int shift_offset = write_offset + loop->var_length; + + replace_1var( var_expansion , shift , write_offset , shift_offset , value ); + search_offset = write_offset + loop->var_length; + } + } + } + } +} + + +int template_eval_loop( const template_type * _template , buffer_type * buffer , int global_offset , loop_type * loop) { + int flags = 0; + int NMATCH = 3; + regmatch_t match_list_loop[NMATCH]; + regmatch_t match_list_end[NMATCH]; + int search_offset = loop->opentag_offset + loop->opentag_length; + { + int end_match , loop_match; + { + char * search_data = (char*)buffer_get_data( buffer ); // This pointer must be refetched because the buffer can realloc and move it. + loop_match = regexec( &_template->start_regexp , &search_data[search_offset] , NMATCH , match_list_loop , flags ); + end_match = regexec( &_template->end_regexp , &search_data[search_offset] , NMATCH , match_list_end , flags ); + } + + if (end_match == REG_NOMATCH) + return -1; // This for loop does not have a matching {% endfor %} + + if (loop_match == 0) { + if (match_list_loop[0].rm_so < match_list_end[0].rm_so) { + // This is a nested sub loop - evaluate that recursively. + char * search_data = (char*)buffer_get_data( buffer ); + loop_type * sub_loop = loop_alloc( search_data , search_offset , match_list_loop[0] , match_list_loop[1] , match_list_loop[2]); + global_offset = template_eval_loop( _template , buffer , global_offset , sub_loop ); + } + } + + /*****************************************************************/ + /* We have completed the sub loops; the buffer has (possibly) + changed so we must search for the end again - to get the right + offset. */ + { + char * search_data = (char*)buffer_get_data( buffer ); + search_offset = global_offset; + end_match = regexec( &_template->end_regexp , &search_data[search_offset] , NMATCH , match_list_end , flags ); + if (end_match == REG_NOMATCH) + util_exit("Fatal error - have lost a {% endfor %} marker \n"); + } + + /* This loop is the inner loop - expand it. */ + loop_set_endmatch( loop , search_offset , match_list_end[0]); + { + buffer_type * loop_expansion = buffer_alloc( loop->body_length * stringlist_get_size( loop->items) ); + { + char * src_data = (char*)buffer_get_data( buffer ); + char * body = util_alloc_substring_copy( src_data , loop->body_offset , loop->body_length ); + buffer_type * var_expansion = buffer_alloc( 0 ); + int ivar; + + for (ivar =0; ivar < stringlist_get_size( loop->items ); ivar++) { + loop_eval(loop , body , var_expansion , ivar ); + buffer_strcat( loop_expansion , (const char*)buffer_get_data( var_expansion )); + } + + buffer_free( var_expansion ); + free( body ); + } + { + int tag_length = loop->endtag_offset + loop->endtag_length - loop->opentag_offset; + int offset = loop->endtag_offset + loop->endtag_length; + int shift = buffer_get_string_size( loop_expansion ) - tag_length; + + buffer_memshift( buffer , offset , shift ); + buffer_fseek( buffer , loop->opentag_offset , SEEK_SET ); + buffer_fwrite( buffer , buffer_get_data( loop_expansion ) , 1 , buffer_get_string_size( loop_expansion )); + + global_offset = loop->opentag_offset + buffer_get_string_size( loop_expansion ); + } + buffer_free( loop_expansion ); + } + loop_free( loop ); + } + return global_offset; +} + + + + +void template_init_loop_regexp( template_type * _template ) { + regcompile( &_template->start_regexp , LOOP_REGEXP , LOOP_OPTIONS ); + regcompile( &_template->end_regexp , END_REGEXP , END_OPTIONS ); +} + + +void template_eval_loops( const template_type * _template , buffer_type * buffer ) { + int NMATCH = 10; + regmatch_t match_list[NMATCH]; + { + int global_offset = 0; + while( true ) { + char * src_data = (char*)buffer_get_data( buffer ); + int match = regexec(&_template->start_regexp , &src_data[global_offset] , NMATCH , match_list , 0); + if (match == 0) { + loop_type * loop = loop_alloc( src_data , global_offset , match_list[0] , match_list[1] , match_list[2]); + global_offset = template_eval_loop( _template , buffer , global_offset , loop ); + if (global_offset < 0) { + fprintf(stderr,"** Warning ** : found {%% for .... %%} loop construct without mathcing {%% endfor %%}\n"); + break; + } + } else if (match == REG_NOMATCH) + break; + } + } +} diff --git a/libres/lib/res_util/tests/ert_util_PATH_test.cpp b/libres/lib/res_util/tests/ert_util_PATH_test.cpp new file mode 100644 index 00000000000..60639c2014b --- /dev/null +++ b/libres/lib/res_util/tests/ert_util_PATH_test.cpp @@ -0,0 +1,60 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'ert_util_PATH_test.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 + + +int main(int argc , char ** argv) { + unsetenv("PATH"); + { + char ** path_list = util_alloc_PATH_list(); + if (path_list[0] != NULL) + test_error_exit("Failed on empty PATH\n"); + + util_free_NULL_terminated_stringlist( path_list ); + } + + + setenv("PATH" , "/usr/bin:/bin:/usr/local/bin" , 1); + { + char ** path_list = util_alloc_PATH_list(); + if (strcmp(path_list[0] , "/usr/bin") != 0) + test_error_exit("Failed on first path element\n"); + + if (strcmp(path_list[1] , "/bin") != 0) + test_error_exit("Failed on second path element\n"); + + if (strcmp(path_list[2] , "/usr/local/bin") != 0) + test_error_exit("Failed on third path element\n"); + + if (path_list[3] != NULL) + test_error_exit("Failed termination \n"); + + util_free_NULL_terminated_stringlist( path_list ); + } + + + exit(0); +} diff --git a/libres/lib/res_util/tests/ert_util_arg_pack.cpp b/libres/lib/res_util/tests/ert_util_arg_pack.cpp new file mode 100644 index 00000000000..42a4c111b13 --- /dev/null +++ b/libres/lib/res_util/tests/ert_util_arg_pack.cpp @@ -0,0 +1,47 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'ert_util_arg_pack.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 + + + +/* + This test must be compiled with -Werror; an important part of the test + is that we should get no warnings related to the const pointers. +*/ + +int main( int argc , char ** argv) { + const char * ptr1 = "Pointer1"; + const char * ptr2 = "Pointer2"; + + arg_pack_type * arg_pack = arg_pack_alloc(); + test_assert_int_equal( 0 , arg_pack_size( arg_pack )); + arg_pack_append_const_ptr( arg_pack , ptr1 ); + arg_pack_append_const_ptr( arg_pack , ptr2 ); + + test_assert_int_equal( 2 , arg_pack_size( arg_pack )); + test_assert_ptr_equal( ptr1 , arg_pack_iget_const_ptr( arg_pack , 0 )); + test_assert_ptr_equal( ptr2 , arg_pack_iget_const_ptr( arg_pack , 1 )); + + + + exit(0); +} diff --git a/libres/lib/res_util/tests/ert_util_block_fs.cpp b/libres/lib/res_util/tests/ert_util_block_fs.cpp new file mode 100644 index 00000000000..e49e7f4e56c --- /dev/null +++ b/libres/lib/res_util/tests/ert_util_block_fs.cpp @@ -0,0 +1,105 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'ert_util_block_fs.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_assert_util_abort(const char * function_name , void call_func (void *) , void * arg); + + +void violating_fwrite( void * arg ) { + block_fs_type * bfs = block_fs_safe_cast( arg ); + block_fs_fwrite_file( bfs , "name" , NULL , 100 ); +} + + +void test_readonly( ) { + ecl::util::TestArea ta("readonly"); + block_fs_type * bfs = block_fs_mount( "test.mnt" , 1000 , 10000 , 0.67 , 10 , true , true , false ); + test_assert_true( block_fs_is_readonly( bfs )); + test_assert_util_abort("block_fs_aquire_wlock" , violating_fwrite , bfs ); + block_fs_close(bfs , true); +} + + +void createFS1() { + pid_t pid = fork(); + + if (pid == 0) { + block_fs_type * bfs = block_fs_mount( "test.mnt" , 1000 , 10000 , 0.67 , 10 , true , false , true ); + test_assert_false( block_fs_is_readonly( bfs ) ); + test_assert_true( util_file_exists("test.lock_0")); + { + int total_sleep = 0; + while (true) { + if (util_file_exists( "stop")) { + unlink("stop"); + break; + } + + usleep(1000); + total_sleep += 1000; + if (total_sleep > 1000000 * 5) { + fprintf(stderr,"Test failure - never receieved \"stop\" file from parent process \n"); + break; + } + } + } + block_fs_close( bfs , false ); + exit(0); + } + usleep(10000); +} + + + +void test_lock_conflict() { + ecl::util::TestArea ta("lockfile"); + createFS1(); + while (true) { + if (util_file_exists("test.lock_0")) + break; + } + + { + block_fs_type * bfs = block_fs_mount( "test.mnt" , 1000 , 10000 , 0.67 , 10 , true , false , true ); + test_assert_true( block_fs_is_readonly( bfs ) ); + } + { + FILE * stream = util_fopen("stop" , "w"); + fclose( stream ); + } + + while (util_file_exists( "stop")) { + usleep( 1000 ); + } +} + + + + +int main(int argc , char ** argv) { + test_readonly(); + test_lock_conflict(); + exit(0); +} diff --git a/libres/lib/res_util/tests/ert_util_logh.cpp b/libres/lib/res_util/tests/ert_util_logh.cpp new file mode 100644 index 00000000000..87164601c83 --- /dev/null +++ b/libres/lib/res_util/tests/ert_util_logh.cpp @@ -0,0 +1,123 @@ +/* + Copyright (C) 20LOG_DEBUG3 Equinor ASA, Norway. + + The file 'ert_util_logh.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 LOG_FILE "log.txt" + + +void test_open() { + ecl::util::TestArea ta("test_open"); + { + log_type * logh = log_open_file( LOG_FILE , LOG_DEBUG); + test_assert_not_NULL(logh); + log_close( logh ); + } + + { + log_type * logh = log_open_file( LOG_FILE , LOG_DEBUG ); + log_add_message( logh , LOG_DEBUG , "Message"); + test_assert_int_equal( 1 , log_get_msg_count( logh )); + log_close( logh ); + } +} + + +void test_delete_empty() { + ecl::util::TestArea ta("delete"); + { + log_type * logh = log_open_file( LOG_FILE , LOG_DEBUG ); + test_assert_not_NULL(logh); + log_close( logh ); + + test_assert_false( util_file_exists( LOG_FILE )); + } + + { + log_type * logh = log_open_file( LOG_FILE , LOG_DEBUG); + log_close( logh ); + + test_assert_false( util_file_exists( LOG_FILE )); + } + + { + log_type * logh = log_open_file( LOG_FILE , LOG_DEBUG ); + log_add_message( logh , LOG_DEBUG , "Message"); + log_close( logh ); + test_assert_true( util_file_exists( LOG_FILE )); + + logh = log_open_file( LOG_FILE , LOG_DEBUG ); + log_close( logh ); + test_assert_true( util_file_exists( LOG_FILE )); + } +} + + +/* + Invalid input - return NULL. +*/ +void test_invalid_input() { + ecl::util::TestArea ta("invalic_iinput"); + test_assert_NULL( log_open_file( NULL, LOG_DEBUG)); + + util_mkdir_p("read_only"); + chmod("read_only", 0500); + test_assert_NULL( log_open_file( "read_only/log.txt", LOG_DEBUG)); +} + + +/* + Someone else deletes the file before closing - that should not kill the thing. +*/ + +void test_file_deleted() { + log_type * logh = log_open_file( LOG_FILE , LOG_DEBUG ); + log_add_message( logh , LOG_DEBUG , "Message"); + util_unlink( LOG_FILE ); + test_assert_false( util_file_exists( LOG_FILE )); + log_close( logh ); + test_assert_false( util_file_exists( LOG_FILE )); +} + + +void test_stream_open() { + ecl::util::TestArea ta("stream_open"); + FILE * stream = util_fopen("log_file.txt", "w"); + { + log_type * logh = log_open_stream( stream , LOG_DEBUG); + log_add_message(logh, LOG_DEBUG, "Message"); + log_close(logh); + } + fputs("Can still write to stream \n", stream); + fclose(stream); +} + + +int main(int argc , char ** argv) { + test_open(); + test_delete_empty(); + test_file_deleted( ); + test_invalid_input(); + test_stream_open( ); + exit(0); +} diff --git a/libres/lib/res_util/tests/ert_util_matrix.cpp b/libres/lib/res_util/tests/ert_util_matrix.cpp new file mode 100644 index 00000000000..61dcaacedb9 --- /dev/null +++ b/libres/lib/res_util/tests/ert_util_matrix.cpp @@ -0,0 +1,397 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'ert_util_matrix.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 + + +void test_resize() { + matrix_type * m1 = matrix_alloc(5,5); + matrix_type * m2 = matrix_alloc(5,5); + rng_type * rng = rng_alloc( MZRAN , INIT_DEFAULT ); + + matrix_random_init( m1 , rng ); + matrix_assign( m2 , m1 ); + + test_assert_true( matrix_equal( m1 , m2 )); + matrix_resize( m1 , 5 , 5 , false ); + test_assert_true( matrix_equal( m1 , m2 )); + matrix_resize( m1 , 5 , 5 , true ); + test_assert_true( matrix_equal( m1 , m2 )); + + rng_free( rng ); + matrix_free( m1 ); + matrix_free( m2 ); +} + + +void test_column_equal() { + matrix_type * m1 = matrix_alloc(5,5); + matrix_type * m2 = matrix_alloc(5,5); + matrix_type * m3 = matrix_alloc(6,5); + rng_type * rng = rng_alloc( MZRAN , INIT_DEFAULT ); + + matrix_random_init( m1 , rng ); + matrix_assign( m2 , m1 ); + + test_assert_true( matrix_columns_equal( m1 , 2 , m2 , 2 )); + test_assert_false( matrix_columns_equal( m1 , 2 , m2 , 3 )); + test_assert_false( matrix_columns_equal( m1 , 2 , m3 , 3 )); + + rng_free( rng ); + matrix_free( m1 ); + matrix_free( m2 ); + matrix_free( m3 ); +} + + + + +void test_create_invalid() { + test_assert_NULL( matrix_alloc(0, 100)); + test_assert_NULL( matrix_alloc(100, 0)); + test_assert_NULL( matrix_alloc(0, 0)); + test_assert_NULL( matrix_alloc(-1, -1)); +} + + + +void test_dims() { + const int rows = 10; + const int columns = 13; + matrix_type * m = matrix_alloc(rows , columns); + + test_assert_true( matrix_check_dims(m , rows , columns)); + test_assert_false( matrix_check_dims(m , rows + 1 , columns)); + test_assert_false( matrix_check_dims(m , rows , columns + 1)); + + matrix_free( m ); +} + + + + +void test_readwrite() { + ecl::util::TestArea test_area("matrix_test"); + { + rng_type * rng = rng_alloc(MZRAN , INIT_DEV_URANDOM ); + matrix_type * m1 = matrix_alloc(3 , 3); + matrix_type * m2 = matrix_alloc(3 , 3); + matrix_random_init( m1 , rng ); + matrix_assign(m2 , m1); + + test_assert_true( matrix_equal( m1 , m2 ) ); + { + FILE * stream = util_fopen("m1" , "w"); + matrix_fwrite( m1 , stream ); + fclose( stream ); + } + matrix_random_init( m1 , rng ); + test_assert_false( matrix_equal( m1 , m2 ) ); + { + FILE * stream = util_fopen("m1" , "r"); + matrix_free( m1 ); + m1 = matrix_alloc(1,1); + printf("-----------------------------------------------------------------\n"); + matrix_fread( m1 , stream ); + test_assert_int_equal( matrix_get_rows(m1) , matrix_get_rows( m2)); + test_assert_int_equal( matrix_get_columns(m1) , matrix_get_columns( m2)); + fseek( stream , 0 , SEEK_SET); + { + matrix_type * m3 = matrix_fread_alloc( stream ); + test_assert_true( matrix_equal( m2 , m3 )); + matrix_free( m3 ); + } + fclose( stream ); + } + test_assert_true( matrix_equal( m1 , m2 ) ); + + matrix_free( m2 ); + matrix_free( m1 ); + rng_free( rng ); + } +} + + +void test_diag_std() { + const int N = 25; + double_vector_type * data = double_vector_alloc( 0,0); + rng_type * rng = rng_alloc(MZRAN , INIT_DEV_URANDOM ); + matrix_type * m = matrix_alloc( N , N ); + double sum1 = 0; + double sum2 = 0; + int i; + + for (i=0; i < N; i++) { + double R = rng_get_double( rng ); + matrix_iset(m , i , i , R); + double_vector_iset( data , i , R ); + + sum1 += R; + sum2 += R*R; + } + { + double mean = sum1 / N; + double std = sqrt( sum2 / N - mean * mean ); + + test_assert_double_equal( std , matrix_diag_std( m , mean )); + test_assert_double_equal( statistics_std( data ) , matrix_diag_std( m , mean )); + test_assert_double_equal( statistics_mean( data ) , mean ); + } + matrix_free( m ); + rng_free( rng ); +} + + + +void test_masked_copy() { + const int N = 25; + bool_vector_type * mask = bool_vector_alloc(N , true); + rng_type * rng = rng_alloc(MZRAN , INIT_DEV_URANDOM ); + matrix_type * m1 = matrix_alloc( N , N ); + matrix_random_init( m1 , rng ); + + bool_vector_iset( mask , 0 , false ); + bool_vector_iset( mask , 10 , false ); + + { + matrix_type * m2 = matrix_alloc_column_compressed_copy( m1 , mask ); + matrix_type * m3 = matrix_alloc( N , N - 2 ); + + test_assert_int_equal( matrix_get_rows( m1 ) , matrix_get_rows( m2 )); + test_assert_int_equal( matrix_get_columns( m1 ) , matrix_get_columns( m2 ) + 2); + + matrix_column_compressed_memcpy( m3 , m1 , mask ); + { + int src_col; + int target_col = 0; + for (src_col = 0; src_col < N; src_col++) { + if (bool_vector_iget( mask , src_col)) { + test_assert_true( matrix_columns_equal( m1 , src_col , m3 , target_col )); + target_col++; + } + } + } + + test_assert_true( matrix_equal( m2 , m3 )); + matrix_free( m3 ); + matrix_free( m2 ); + } + + matrix_free( m1 ); + rng_free( rng ); +} + + +void test_inplace_sub_column() { + const int N = 25; + rng_type * rng = rng_alloc(MZRAN , INIT_DEV_URANDOM ); + matrix_type * m1 = matrix_alloc( N , N ); + matrix_type * m2 = matrix_alloc( N , N ); + + matrix_random_init( m1 , rng ); + matrix_assign( m2 , m1 ); + matrix_inplace_sub_column( m1 , m2 , 0 , 0 ); + { + int row; + for (row = 0; row < N; row++) { + double diff = matrix_iget( m1 , row , 0); + test_assert_true( std::abs( diff ) < 1e-6); + } + } +} + + + +void test_data() { + ecl::util::TestArea("matrix_data"); + int rows = 11; + int columns = 7; + matrix_type * m1 = matrix_alloc(rows, columns); + double value = 0.0; + for (int i=0; i < rows; i++) { + for (int j=0; j < columns; j++) { + matrix_iset(m1, i, j, value); + value += 1; + } + } + { + FILE * stream = util_fopen("row_major.txt", "w"); + matrix_fprintf_data(m1, true, stream); + fclose(stream); + } + { + FILE * stream = util_fopen("column_major.txt", "w"); + matrix_fprintf_data(m1, false, stream); + fclose(stream); + } + { + FILE * stream = util_fopen("row_major.txt", "r"); + matrix_type * m2 = matrix_alloc(rows, columns); + matrix_fscanf_data(m2, true, stream); + test_assert_true(matrix_equal(m1,m2)); + matrix_free(m2); + fclose(stream); + } + { + FILE * stream = util_fopen("column_major.txt", "r"); + matrix_type * m2 = matrix_alloc(rows, columns); + matrix_fscanf_data(m2, false, stream); + test_assert_true(matrix_equal(m1,m2)); + matrix_free(m2); + fclose(stream); + } + matrix_free(m1); +} + +namespace { + +matrix_type * alloc_column_matrix(int num_row, int num_col) { + matrix_type * m = matrix_alloc(num_row, num_col); + for (int row = 0; row < matrix_get_rows(m); row++) { + for (int col = 0; col < matrix_get_columns(m); col++) { + matrix_iset(m, row, col, col * 1.0); + } + } + return m; +} + +} + +void test_delete_column() { + int num_col = 10; + int num_row = 10; + matrix_type * m = alloc_column_matrix(num_row, num_col); + test_assert_throw( matrix_delete_column(m, matrix_get_columns(m)), std::invalid_argument); + + matrix_delete_column(m, matrix_get_columns(m) - 1); + test_assert_int_equal( matrix_get_columns(m), num_col - 1); + for (int row = 0; row < matrix_get_rows(m); row++) { + for (int col = 0; col < num_col < 1; col ++) + test_assert_double_equal( matrix_iget(m, row, col), col * 1.0 ); + } + + matrix_delete_column(m, 0); + test_assert_int_equal( matrix_get_columns(m), num_col - 2); + for (int row = 0; row < matrix_get_rows(m); row++) { + for (int col = 0; col < num_col < 1; col ++) + test_assert_double_equal( matrix_iget(m, row, col), 1 + col * 1.0 ); + } + + matrix_delete_column(m, 3); + test_assert_int_equal( matrix_get_columns(m), num_col - 3); + for (int row = 0; row < matrix_get_rows(m); row++) { + for (int col = 0; col < num_col < 1; col ++) { + int col_value = col + 1 + (col >= 3 ? 1 : 0); + test_assert_double_equal( matrix_iget(m, row, col), col_value); + } + } + matrix_free(m); +} + + + +void test_delete_row() { + int num_col = 10; + int num_row = 10; + matrix_type * m = alloc_column_matrix(num_row, num_col); + test_assert_throw( matrix_delete_row(m, matrix_get_rows(m)), std::invalid_argument); + + matrix_delete_row(m, matrix_get_rows(m) - 1); + test_assert_int_equal( matrix_get_rows(m), num_row - 1); + for (int row = 0; row < matrix_get_rows(m); row++) { + for (int col = 0; col < num_col < 1; col ++) + test_assert_double_equal( matrix_iget(m, row, col), row * 1.0 ); + } + + matrix_delete_row(m, 0); + test_assert_int_equal( matrix_get_rows(m), num_row - 2); + for (int row = 0; row < matrix_get_rows(m); row++) { + for (int col = 0; col < num_col < 1; col ++) + test_assert_double_equal( matrix_iget(m, row, col), 1 + row * 1.0 ); + } + + matrix_delete_row(m, 3); + test_assert_int_equal( matrix_get_rows(m), num_row - 3); + for (int row = 0; row < matrix_get_rows(m); row++) { + for (int col = 0; col < num_col < 1; col ++) { + int row_value = row + 1 + (row >= 3 ? 1 : 0); + test_assert_double_equal( matrix_iget(m, row, col), row_value); + } + } + + matrix_free(m); +} + + +void test_set_row() { + const int num_col = 16; + const int num_row = 10; + std::vector row(num_col); + matrix_type * m = matrix_alloc(num_row, num_col); + + test_assert_throw( matrix_set_row(m, row.data(), 100), std::invalid_argument); + + std::iota(row.begin(), row.end(), 0); + { + int r = 7; + matrix_set_row(m, row.data(), r); + + for (int col = 0; col < num_col; col++) + test_assert_double_equal( row[col], matrix_iget(m, r, col)); + } + + matrix_free(m); +} + + + + + + + +int main( int argc , char ** argv) { + test_create_invalid(); + test_resize(); + test_column_equal(); + test_dims(); + + test_readwrite(); + test_diag_std(); + test_masked_copy(); + test_inplace_sub_column(); + test_data(); + test_delete_column(); + test_delete_row(); + test_set_row(); + exit(0); +} diff --git a/libres/lib/res_util/tests/ert_util_matrix_lapack.cpp b/libres/lib/res_util/tests/ert_util_matrix_lapack.cpp new file mode 100644 index 00000000000..6dec558d0bd --- /dev/null +++ b/libres/lib/res_util/tests/ert_util_matrix_lapack.cpp @@ -0,0 +1,159 @@ +/* + Copyright (C) 2015 Equinor ASA, Norway. + + The file 'ert_util_matrix_lapack.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 + + + + +void test_det4() { + matrix_type * m = matrix_alloc(4 , 4 ); + rng_type * rng = rng_alloc(MZRAN , INIT_DEV_URANDOM ); + for (int i=0; i < 10; i++) { + matrix_random_init( m , rng ); + { + double det4 = matrix_det4( m ); + double det = matrix_det( m ); + + test_assert_double_equal( det , det4 ); + } + } + + matrix_free( m ); + rng_free( rng ); +} + + +void test_det3() { + matrix_type * m = matrix_alloc(3 , 3 ); + rng_type * rng = rng_alloc(MZRAN , INIT_DEV_URANDOM ); + matrix_random_init( m , rng ); + + { + double det3 = matrix_det3( m ); + double det = matrix_det( m ); + + test_assert_double_equal( det , det3 ); + } + + matrix_free( m ); + rng_free( rng ); +} + + +void test_det2() { + matrix_type * m = matrix_alloc(2,2); + rng_type * rng = rng_alloc(MZRAN , INIT_DEV_URANDOM ); + matrix_random_init( m , rng ); + { + double det2 = matrix_det2( m ); + double det = matrix_det( m ); + + test_assert_double_equal( det , det2 ); + } + matrix_free( m ); + rng_free( rng ); +} + +void test_dgesvx() { + matrix_type * m1 = matrix_alloc(3 , 3 ); + matrix_type * m2 = matrix_alloc(3 , 3 ); + matrix_type * b1 = matrix_alloc(3 , 5 ); + matrix_type * b2 = matrix_alloc(3 , 5 ); + matrix_type * b3 = matrix_alloc(3 , 5 ); + rng_type * rng = rng_alloc(MZRAN , INIT_DEV_URANDOM ); + double rcond; + double epsilon=0.0000000001; + double diag=1.0; + { + matrix_diag_set_scalar(m1, diag); + matrix_random_init( b1 , rng ); + matrix_assign(b2 , b1); + matrix_dgesv( m1 , b1 ); + test_assert_true( matrix_similar( b1 , b2 , epsilon ) ); + + matrix_diag_set_scalar(m1, diag); + matrix_random_init( b1 , rng ); + matrix_assign(b2 , b1); + matrix_dgesvx( m1 , b1, &rcond ); + test_assert_true( matrix_similar( b1 , b2 , epsilon ) ); + + + matrix_random_init( m1 , rng ); + matrix_assign(m2 , m1); + + matrix_random_init( b1 , rng ); + matrix_assign(b2 , b1); + + matrix_dgesv( m1 , b1 ); + matrix_dgesvx( m2 , b2, &rcond ); + + + matrix_sub(b3,b2,b1); + matrix_pretty_fprint_submat(b3,"b3","%.6f ",stdout,0,2,0,4) ; + + test_assert_true( matrix_similar( b1 , b2 , epsilon ) ); + + } + + matrix_free( m1 ); + matrix_free( m2 ); + matrix_free( b1 ); + matrix_free( b2 ); + matrix_free( b3 ); + rng_free( rng ); +} + +void test_matrix_similar() { + rng_type * rng = rng_alloc(MZRAN , INIT_DEV_URANDOM ); + matrix_type * m1 = matrix_alloc( 3 , 3 ); + matrix_type * m2 = matrix_alloc( 3 , 3 ); + double epsilon=0.0000000001; + + matrix_random_init( m1 , rng ); + matrix_assign( m2 , m1 ); + + test_assert_true( matrix_similar( m1 , m2, epsilon)); + + matrix_iadd(m2,2,2,0.5*epsilon); + test_assert_true( matrix_similar( m1 , m2, epsilon)); + + matrix_iadd(m2,2,2,epsilon); + test_assert_true( !matrix_similar( m1 , m2, epsilon)); + + matrix_free(m2); + matrix_free(m1); + rng_free(rng); +} + + +int main( int argc , char ** argv) { + test_det2(); + test_det3(); + test_det4(); + test_dgesvx(); + test_matrix_similar(); + exit(0); +} diff --git a/libres/lib/res_util/tests/ert_util_matrix_stat.cpp b/libres/lib/res_util/tests/ert_util_matrix_stat.cpp new file mode 100644 index 00000000000..7e22943fcb3 --- /dev/null +++ b/libres/lib/res_util/tests/ert_util_matrix_stat.cpp @@ -0,0 +1,169 @@ +/* + Copyright (C) 2015 Equinor ASA, Norway. + + The file 'ert_util_matrix_stat.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 + + + +void test_invalid_dimensions() { + matrix_type * X = matrix_alloc(10,2); + matrix_type * Y = matrix_alloc(11,1); + matrix_type * S = matrix_alloc(10,1); + matrix_type * beta = matrix_alloc(2,1); + + test_assert_true( matrix_stat_llsq_estimate( beta , X , Y , S ) == LLSQ_INVALID_DIM ); + test_assert_true( matrix_stat_llsq_estimate( beta , X , Y , NULL ) == LLSQ_INVALID_DIM ); + + matrix_resize(beta , 4 , 1 , false ); + matrix_resize(Y , 3 , 1 , false ); + matrix_resize(X , 3 , 4 , false ); + test_assert_true( matrix_stat_llsq_estimate( beta , X , Y , NULL ) == LLSQ_UNDETERMINED ); + + matrix_free( Y ); + matrix_free( S ); + matrix_free( beta ); + matrix_free( X ); +} + + +void test_no_sigma() { + const double A = 4.00; + const double B = -2; + const double C = 0.25; + + const double xmin = 0; + const double xmax = 1; + int size = 20; + int P = 3; + matrix_type * X = matrix_alloc(size,P); + matrix_type * Y = matrix_alloc(size,1); + matrix_type * beta = matrix_alloc(P,1); + + int i; + for (i = 0; i < size; i++) { + double x = xmin + i * (xmax - xmin) / (size - 1); + + double y = A + B*x + C*x*x; + matrix_iset( X , i , 0 , 1 ); + matrix_iset( X , i , 1 , x ); + matrix_iset( X , i , 2 , x*x ); + + matrix_iset( Y , i , 0 , y); + } + + test_assert_true( matrix_stat_llsq_estimate( beta , X , Y , NULL ) == LLSQ_SUCCESS ); + + test_assert_double_equal( A , matrix_iget( beta , 0 , 0 )); + test_assert_double_equal( B , matrix_iget( beta , 1 , 0 )); + test_assert_double_equal( C , matrix_iget( beta , 2 , 0 )); + + + matrix_free( Y ); + matrix_free( beta ); + matrix_free( X ); +} + + +void test_with_sigma() { + const double A = 4.00; + const double B = -2; + const double C = 0.25; + + const double xmin = 0; + const double xmax = 1; + int size = 20; + int P = 3; + matrix_type * X = matrix_alloc(size,P); + matrix_type * Y = matrix_alloc(size,1); + matrix_type * beta = matrix_alloc(P,1); + matrix_type * S = matrix_alloc(size,1); + + int i; + for (i = 0; i < size; i++) { + double x = xmin + i * (xmax - xmin) / (size - 1); + + double y = A + B*x + C*x*x; + matrix_iset( X , i , 0 , 1 ); + matrix_iset( X , i , 1 , x ); + matrix_iset( X , i , 2 , x*x ); + + matrix_iset( Y , i , 0 , y); + matrix_iset( S , i , 0 , 1); + } + + test_assert_true( matrix_stat_llsq_estimate( beta , X , Y , S ) == LLSQ_SUCCESS ); + + test_assert_double_equal( A , matrix_iget( beta , 0 , 0 )); + test_assert_double_equal( B , matrix_iget( beta , 1 , 0 )); + test_assert_double_equal( C , matrix_iget( beta , 2 , 0 )); + + matrix_free( S ); + matrix_free( Y ); + matrix_free( beta ); + matrix_free( X ); +} + + +void test_polyfit() { + const double A = 4.00; + const double B = -2; + const double C = 0.25; + + const double xmin = 0; + const double xmax = 1; + int size = 20; + int P = 3; + matrix_type * X = matrix_alloc(size,1); + matrix_type * Y = matrix_alloc(size,1); + matrix_type * beta = matrix_alloc(P,1); + + int i; + for (i = 0; i < size; i++) { + double x = xmin + i * (xmax - xmin) / (size - 1); + + double y = A + B*x + C*x*x; + matrix_iset( X , i , 0 , x ); + matrix_iset( Y , i , 0 , y); + } + + test_assert_true( matrix_stat_polyfit( beta , X , Y , NULL) == LLSQ_SUCCESS ); + + test_assert_double_equal( A , matrix_iget( beta , 0 , 0 )); + test_assert_double_equal( B , matrix_iget( beta , 1 , 0 )); + test_assert_double_equal( C , matrix_iget( beta , 2 , 0 )); + + matrix_free( Y ); + matrix_free( beta ); + matrix_free( X ); +} + + + + + +int main() { + util_install_signals(); + test_invalid_dimensions(); + test_no_sigma(); + test_with_sigma(); + test_polyfit(); + exit(0); +} diff --git a/libres/lib/res_util/tests/ert_util_subst_list.cpp b/libres/lib/res_util/tests/ert_util_subst_list.cpp new file mode 100644 index 00000000000..a3e4275452b --- /dev/null +++ b/libres/lib/res_util/tests/ert_util_subst_list.cpp @@ -0,0 +1,92 @@ +/* + Copyright (C) 2015 Equinor ASA, Norway. + + The file 'ert_util_subst_list.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 + + +void test_create() { + subst_list_type * subst_list = subst_list_alloc( NULL ); + test_assert_true( subst_list_is_instance( subst_list ) ); + subst_list_free( subst_list ); +} + + +void test_filter_file1() { + subst_list_type * subst_list = subst_list_alloc( NULL ); + ecl::util::TestArea ta("filter_:file"); + { + FILE * stream = util_fopen("template" , "w"); + fprintf(stream , "\n\n\n\n"); + fclose(stream); + } + subst_list_append_copy( subst_list , "" , "Value1" , NULL); + subst_list_append_copy( subst_list , "" , "Value2" , NULL); + subst_list_append_copy( subst_list , "" , "Value3" , NULL); + subst_list_append_copy( subst_list , "" , "Value4" , NULL); + + subst_list_filter_file( subst_list , "template" , "target"); + + { + FILE * stream = util_fopen("target" , "r"); + char s1[128],s2[128],s3[128],s4[128]; + + test_assert_int_equal( 4 , fscanf( stream , "%s %s %s %s" , s1,s2,s3,s4)); + fclose(stream); + + test_assert_string_equal( s1 , "Value1"); + test_assert_string_equal( s2 , "Value2"); + test_assert_string_equal( s3 , "Value3"); + test_assert_string_equal( s4 , "Value4"); + } + subst_list_free( subst_list ); +} + + +void test_filter_file2() { + subst_list_type * subst_list = subst_list_alloc( NULL ); + ecl::util::TestArea ta("filetr2"); + { + FILE * stream = util_fopen("template" , "w"); + fprintf(stream , "MAGIC_PRINT magic-list.txt __MAGIC__"); + fclose(stream); + } + + subst_list_append_copy( subst_list , "__MAGIC__" , "MagicAllTheWayToWorkFlow" , NULL); + subst_list_append_copy( subst_list , "" , "SUPERcase" , NULL); + subst_list_append_copy( subst_list , "" , "default" , NULL); + subst_list_filter_file( subst_list , "template" , "target"); + + { + char * target_string = util_fread_alloc_file_content( "target" , NULL ); + test_assert_string_equal( target_string , "MAGIC_PRINT magic-list.txt default MagicAllTheWayToWorkFlow"); + free( target_string ); + } + subst_list_free( subst_list ); +} + + + + +int main(int argc , char ** argv) { + test_create(); + test_filter_file1(); + test_filter_file2(); +} diff --git a/libres/lib/res_util/tests/ert_util_ui_return.cpp b/libres/lib/res_util/tests/ert_util_ui_return.cpp new file mode 100644 index 00000000000..7b890179e3f --- /dev/null +++ b/libres/lib/res_util/tests/ert_util_ui_return.cpp @@ -0,0 +1,97 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'enkf_ui_return_type.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 + + + + +void test_create() { + ui_return_status_enum status = UI_RETURN_OK; + ui_return_type * ui_return = ui_return_alloc(status); + test_assert_true( ui_return_is_instance( ui_return )); + test_assert_int_equal( status , ui_return_get_status(ui_return)); + ui_return_free( ui_return ); +} + + +void test_default() { + ui_return_status_enum status = UI_RETURN_OK; + ui_return_type * ui_return = ui_return_alloc(status); + + test_assert_int_equal( 0 , ui_return_get_error_count(ui_return)); + test_assert_NULL( ui_return_get_first_error( ui_return)); + test_assert_NULL( ui_return_get_last_error( ui_return)); + test_assert_NULL( ui_return_get_help(ui_return)); + ui_return_free( ui_return); +} + +void test_errors_inconsistent() { + ui_return_status_enum status = UI_RETURN_OK; + ui_return_type * ui_return = ui_return_alloc(status); + + test_assert_int_equal( 0 , ui_return_get_error_count(ui_return)); + test_assert_false( ui_return_add_error( ui_return , "ERROR1")); + test_assert_int_equal( 0 , ui_return_get_error_count(ui_return)); + ui_return_free( ui_return); +} + + +void test_errors_consistent() { + ui_return_status_enum status = UI_RETURN_FAIL; + ui_return_type * ui_return = ui_return_alloc(status); + + test_assert_int_equal( 0 , ui_return_get_error_count(ui_return)); + test_assert_true( ui_return_add_error( ui_return , "ERROR1")); + test_assert_int_equal(1, ui_return_get_error_count(ui_return)); + test_assert_string_equal("ERROR1", ui_return_get_first_error(ui_return)); + test_assert_string_equal("ERROR1", ui_return_get_last_error(ui_return)); + + test_assert_true(ui_return_add_error(ui_return, "ERROR2")); + test_assert_int_equal(2, ui_return_get_error_count(ui_return)); + test_assert_string_equal("ERROR1", ui_return_get_first_error(ui_return)); + test_assert_string_equal("ERROR2", ui_return_get_last_error(ui_return)); + + test_assert_string_equal("ERROR1" , ui_return_iget_error(ui_return , 0)); + test_assert_string_equal("ERROR2" , ui_return_iget_error(ui_return , 1)); + + ui_return_free( ui_return); +} + + +void test_help() { + ui_return_type * ui_return = ui_return_alloc(UI_RETURN_OK); + + ui_return_add_help(ui_return , "HELP1"); + test_assert_string_equal( "HELP1" , ui_return_get_help(ui_return)); + + ui_return_add_help(ui_return , "HELP2"); + test_assert_string_equal( "HELP1 HELP2" , ui_return_get_help(ui_return)); + + ui_return_free( ui_return); +} + +int main(int argc , char ** argv) { + test_create(); + test_default(); + test_errors_inconsistent(); + exit(0); +} diff --git a/libres/lib/res_util/tests/es_testdata.cpp b/libres/lib/res_util/tests/es_testdata.cpp new file mode 100644 index 00000000000..6e5b05d383d --- /dev/null +++ b/libres/lib/res_util/tests/es_testdata.cpp @@ -0,0 +1,227 @@ +/* + Copyright (C) 2019 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 + + + +res::es_testdata make_testdata(int ens_size, int obs_size) { + matrix_type * S = matrix_alloc(obs_size, ens_size); + matrix_type * E = matrix_alloc(obs_size, ens_size); + matrix_type * R = matrix_alloc(obs_size, obs_size); + matrix_type * D = matrix_alloc(obs_size, ens_size); + matrix_type * dObs = matrix_alloc(obs_size, 2); + + { + double v = 0; + for (int i=0; i < matrix_get_rows(S); i++) { + for (int j=0; j < matrix_get_columns(S); j++) { + matrix_iset(S, i, j, v); + matrix_iset(E, i, j, v); + matrix_iset(D, i, j, v); + v += 1; + } + } + + v = 0; + for (int i=0; i < matrix_get_rows(dObs); i++) { + for (int j=0; j < matrix_get_columns(dObs); j++) { + matrix_iset(dObs, i, j, v); + v += 1; + } + } + + v = 0; + for (int i=0; i < matrix_get_rows(R); i++) { + for (int j=0; j < matrix_get_columns(R); j++) { + matrix_iset(R, i, j, v); + v += 1; + } + } + + + + } + res::es_testdata td(S,R,dObs,D,E); + + matrix_free(S); + matrix_free(R); + matrix_free(dObs); + matrix_free(D); + matrix_free(E); + + return td; +} + +void test_deactivate() { + int ens_size = 10; + int obs_size = 7; + int del_iobs = 3; + int del_iens = 7; + res::es_testdata td1 = make_testdata(ens_size, obs_size); + res::es_testdata td2 = make_testdata(ens_size, obs_size); + + + + test_assert_throw(td1.deactivate_obs(10), std::invalid_argument); + td1.deactivate_obs(del_iobs); + + for (int i1=0; i1 < matrix_get_rows(td1.R); i1++) { + int i2 = i1 + (i1 >= del_iobs ? 1 : 0); + for (int j1=0; j1 < matrix_get_columns(td1.R); j1++) { + int j2 = j1 + (j1 >= del_iobs ? 1 : 0); + + test_assert_double_equal(matrix_iget(td1.R, i1, j1), matrix_iget(td2.R, i2, j2)); + } + + test_assert_double_equal(matrix_iget(td1.dObs, i1, 0), matrix_iget(td2.dObs, i2, 0)); + test_assert_double_equal(matrix_iget(td1.dObs, i1, 1), matrix_iget(td2.dObs, i2, 1)); + for (int j1=0; j1 < matrix_get_columns(td1.S); j1++) { + int j2 = j1; + test_assert_double_equal(matrix_iget(td1.S, i1, j1), matrix_iget(td2.S, i2, j2)); + + if (td1.E) + test_assert_double_equal(matrix_iget(td1.E, i1, j1), matrix_iget(td2.E, i2, j2)); + + if (td1.D) + test_assert_double_equal(matrix_iget(td1.D, i1, j1), matrix_iget(td2.D, i2, j2)); + } + } + + td1.deactivate_realization(del_iens); + + for (int i1=0; i1 < matrix_get_rows(td1.S); i1++) { + int i2 = i1 + (i1 >= del_iobs ? 1 : 0); + + for (int j1=0; j1 < matrix_get_columns(td1.S); j1++) { + int j2 = j1 + (j1 >= del_iens ? 1 : 0); + + test_assert_double_equal(matrix_iget(td1.S, i1, j1), matrix_iget(td2.S, i2, j2)); + + if (td1.E) + test_assert_double_equal(matrix_iget(td1.E, i1, j1), matrix_iget(td2.E, i2, j2)); + + if (td1.D) + test_assert_double_equal(matrix_iget(td1.D, i1, j1), matrix_iget(td2.D, i2, j2)); + } + } + + test_assert_int_equal(td1.active_ens_size, ens_size - 1); + test_assert_int_equal(td1.active_obs_size, obs_size - 1); +} + + +void test_basic() { + ecl::util::TestArea work_area("es_testdata"); + int ens_size = 10; + int obs_size = 7; + res::es_testdata td1 = make_testdata(ens_size, obs_size); + td1.save("path/sub/path"); + res::es_testdata td2("path/sub/path"); + test_assert_true( matrix_equal(td1.S, td2.S) ); + + test_assert_int_equal( bool_vector_size(td1.obs_mask), obs_size ); + test_assert_int_equal( bool_vector_count_equal(td1.obs_mask, true), obs_size ); + + test_assert_int_equal( bool_vector_size(td1.ens_mask), ens_size ); + test_assert_int_equal( bool_vector_count_equal(td1.ens_mask, true), ens_size ); +} + + +void test_size_problems() { + ecl::util::TestArea work_area("es_testdata"); + int ens_size = 10; + int obs_size = 7; + { + res::es_testdata td1 = make_testdata(ens_size, obs_size); + td1.save("path"); + } + unlink("path/size"); + test_assert_throw( res::es_testdata("path"), std::invalid_argument ); + { + FILE * fp = util_fopen("path/size", "w"); + fprintf(fp, "%d\n", ens_size); + fclose(fp); + } + test_assert_throw( res::es_testdata("path"), std::invalid_argument ); +} + +void test_load_state() { + ecl::util::TestArea work_area("es_testdata"); + int ens_size = 10; + int obs_size = 7; + res::es_testdata td0 = make_testdata(ens_size, obs_size); + td0.save("PATH"); + + res::es_testdata td("PATH"); + td.deactivate_realization(5); + int active_ens_size = td.active_ens_size; + + test_assert_throw( td.alloc_state("DOES_NOT_EXIST"), std::invalid_argument); + + { + int invalid_size = 10 * active_ens_size + active_ens_size / 2; + FILE * stream = util_fopen("PATH/A0", "w"); + for (int i = 0; i < invalid_size; i++) + fprintf(stream, "%d\n", i); + fclose(stream); + test_assert_throw( td.alloc_state("A0"), std::invalid_argument); + } + { + int state_size = 7; + int valid_size = state_size * active_ens_size; + FILE * stream = util_fopen("PATH/A1", "w"); + double value = 0; + for (int row=0; row < state_size; row++) { + for (int iens = 0; iens < active_ens_size; iens++) { + fprintf(stream, "%lg ", value); + value++; + } + fputs("\n", stream); + } + fclose(stream); + + matrix_type * A1 = td.alloc_state("A1"); + test_assert_int_equal(matrix_get_rows(A1), state_size); + test_assert_int_equal(matrix_get_columns(A1), active_ens_size); + + value = 0; + for (int row=0; row < state_size; row++) { + for (int iens = 0; iens < active_ens_size; iens++) { + test_assert_double_equal(matrix_iget(A1, row, iens), value); + value++; + } + } + + td.save_matrix("A2", A1); + matrix_type * A2 = td.alloc_matrix("A2", state_size, active_ens_size); + test_assert_true( matrix_equal(A1,A2) ); + + matrix_free(A1); + matrix_free(A2); + } +} + +int main() { + test_basic(); + test_load_state(); + test_deactivate(); + test_size_problems(); +} diff --git a/libres/lib/res_util/tests/res_util_PATH.cpp b/libres/lib/res_util/tests/res_util_PATH.cpp new file mode 100644 index 00000000000..adecbd8f5d6 --- /dev/null +++ b/libres/lib/res_util/tests/res_util_PATH.cpp @@ -0,0 +1,58 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'ert_util_PATH_test.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 + + +int main(int argc , char ** argv) { + unsetenv("PATH"); + { + char ** path_list = res_env_alloc_PATH_list(); + if (path_list[0] != NULL) + test_error_exit("Failed on empty PATH\n"); + + util_free_NULL_terminated_stringlist( path_list ); + } + + + setenv("PATH" , "/usr/bin:/bin:/usr/local/bin" , 1); + { + char ** path_list = res_env_alloc_PATH_list(); + if (strcmp(path_list[0] , "/usr/bin") != 0) + test_error_exit("Failed on first path element\n"); + + if (strcmp(path_list[1] , "/bin") != 0) + test_error_exit("Failed on second path element\n"); + + if (strcmp(path_list[2] , "/usr/local/bin") != 0) + test_error_exit("Failed on third path element\n"); + + if (path_list[3] != NULL) + test_error_exit("Failed termination \n"); + + util_free_NULL_terminated_stringlist( path_list ); + } + + + exit(0); +} diff --git a/libres/lib/res_util/tests/test_thread_pool.cpp b/libres/lib/res_util/tests/test_thread_pool.cpp new file mode 100644 index 00000000000..36adfef4d05 --- /dev/null +++ b/libres/lib/res_util/tests/test_thread_pool.cpp @@ -0,0 +1,70 @@ +/* + Copyright (C) 2015 Equinor ASA, Norway. + + The file 'test_thread_pool.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 + + +pthread_mutex_t lock; + + +void create_and_destroy() { + int run_size = 10; + thread_pool_type * tp = thread_pool_alloc( run_size , true ); + + thread_pool_join( tp ); + thread_pool_free( tp ); +} + + +void * inc(void * arg) { + int * int_arg = (int *) arg; + pthread_mutex_lock( &lock ); + int_arg[0]++; + pthread_mutex_unlock( &lock ); + return NULL; +} + + +void run() { + int run_size = 10; + int job_size = 1000; + int value = 0; + thread_pool_type * tp = thread_pool_alloc( run_size , true ); + + pthread_mutex_init(&lock , NULL); + for (int i=0; i < job_size; i++) + thread_pool_add_job( tp , inc , &value ); + + thread_pool_join( tp ); + thread_pool_free( tp ); + test_assert_int_equal( job_size , value ); + pthread_mutex_destroy( &lock ); +} + + + + + + +int main( int argc , char ** argv) { + create_and_destroy(); + run(); +} diff --git a/libres/lib/res_util/thread_pool.cpp b/libres/lib/res_util/thread_pool.cpp new file mode 100644 index 00000000000..c201c08939a --- /dev/null +++ b/libres/lib/res_util/thread_pool.cpp @@ -0,0 +1,479 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'thread_pool.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 + + +/** + This file implements a small thread_pool object based on + pthread_create() function calls. The characetristics of this + implementation is as follows: + + 1. The jobs are mangaged by a separate thread - dispatch_thread. + 2. The new jobs are just appended to the queue, the + dispatch_thread sees them in the queue and dispatches them. + 3. The dispatch thread manages a list of thread_pool_job_slot_type + instances - one slot for each actually running job. + + Example + ------- + + 1. Start with creating a thread pool object. The arguments to the + allocater are the (maximum) number of concurrently running + threads and a boolean flag of whether the queue should start + immediately (that is in general the case). + + thread_pool_type * tp = thread_pool_alloc( NUM_THREADS , immediate_start); + + + 2. Add the jobs you want to run: + + thread_pool_add_job( tp , some_function , argument_to_some_function ); + + Here the prototype for the function which is being run is + + void * (some_func) (void *); + + I.e. it expects a (void *) input pointer, and also returns a + (void *) pointer as output. The thread pool implementation does + not touch the input and output of some_function. + + + 3. When all the jobs have been added you inform the thread pool of + that by calling: + + thread_pool_join( tp ); + + This function will not return before all the added jobs have run + to completion. + + + 4. Optional: If you want to get the return value from the function + you supplied, you can use: + + thread_pool_iget_return_value( tp , index ); + + To get the return value from function nr index. + + + 5. Optional: The thread pool will probably mainly be used only once, + but after a join it is possible to reuse a thread pool, but then + you MUST call thread_pool_restart() before adding jobs again. + + + 6. When you are really finished: thread_pool_free( tp ); + +*/ + + +typedef void * (start_func_ftype) (void *) ; + + +/** + Internal struct which is used as queue node. +*/ +typedef struct { + thread_pool_type * pool; /* A back-reference to the thread_pool holding the queue. */ + int slot_index; /* The index in the space [0,max_running) of the job slot where this job is running. */ + int queue_index; /* The index of the current tp_arg in the queue. */ + void * func_arg; /* The arguments to this job - supplied by the calling scope. */ + start_func_ftype * func; /* The function to call - supplied by the calling scope. */ + void * return_value; +} thread_pool_arg_type; + + + +/** + Internal struct used to keep track of the job slots. +*/ +typedef struct { + pthread_t thread; /* The thread variable currently (or more correct:last) running. */ + int run_count; /* The number of times this slot has been used - just to check whether the slot has been used AT ALL when/if joining. */ + bool running; /* Is the job_slot running now?? */ +} thread_pool_job_slot_type; + + + + +#define THREAD_POOL_TYPE_ID 71443207 +struct thread_pool_struct { + UTIL_TYPE_ID_DECLARATION; + thread_pool_arg_type * queue; /* The jobs to be executed are appended in this vector. */ + int queue_index; /* The index of the next job to run. */ + int queue_size; /* The number of jobs in the queue - including those which are complete. [Should be protected / atomic / ... ] */ + int queue_alloc_size; /* The allocated size of the queue. */ + + int max_running; /* The max number of concurrently running jobs. */ + bool join; /* Flag set by the main thread to inform the dispatch thread that joining should start. */ + bool accepting_jobs; /* True|False whether the dispatch thread is running. */ + + thread_pool_job_slot_type * job_slots; /* A vector to @max_running job slots, each slot can be reused several times.*/ + pthread_t dispatch_thread; + pthread_rwlock_t queue_lock; +}; + + +static UTIL_SAFE_CAST_FUNCTION( thread_pool , THREAD_POOL_TYPE_ID ) + + +/** + This function will grow the queue. It is called by the main thread + (i.e. the context of the calling scope), and the queue is read by + the dispatch_thread - i.e. access to the queue must be protected by + rwlock. +*/ + +static void thread_pool_resize_queue( thread_pool_type * pool, int queue_length ) { + pthread_rwlock_wrlock( &pool->queue_lock ); + { + pool->queue = (thread_pool_arg_type*)util_realloc( pool->queue , queue_length * sizeof * pool->queue ); + pool->queue_alloc_size = queue_length; + } + pthread_rwlock_unlock( &pool->queue_lock ); +} + + +/** + This function updates an element in the queue, the function is + called by the executing threads, on the same time the main thread + might be resizing the thread, we therefor take a read lock during + execution of this function. (Write lock is not necessary because we + will not change the queue pointer itself, only something it points + to.) +*/ + +static void thread_pool_iset_return_value( thread_pool_type * pool , int index , void * return_value) { + pthread_rwlock_rdlock( &pool->queue_lock ); + { + pool->queue[ index ].return_value = return_value; + } + pthread_rwlock_unlock( &pool->queue_lock ); +} + + +/** + The pthread_create() call which this is all about, does not start + the user supplied function. Instead it will start an instance of + this function, which will do some housekeeping before calling the + user supplied function. +*/ + +static void * thread_pool_start_job( void * arg ) { + thread_pool_arg_type * tp_arg = (thread_pool_arg_type * ) arg; + thread_pool_type * tp = tp_arg->pool; + int slot_index = tp_arg->slot_index; + void * func_arg = tp_arg->func_arg; + start_func_ftype * func = tp_arg->func; + void * return_value; + + + return_value = func( func_arg ); /* Starting the real external function */ + tp->job_slots[ slot_index ].running = false; /* We mark the job as completed. */ + free( arg ); + + if (return_value != NULL) + thread_pool_iset_return_value( tp , tp_arg->queue_index , return_value); + + return NULL; +} + + + +/** + This function is run by the dispatch_thread. The thread will keep + an eye on the queue, and dispatch new jobs when there are free + slots available. +*/ + +static void * thread_pool_main_loop( void * arg ) { + thread_pool_type * tp = thread_pool_safe_cast( arg ); + { + const int usleep_init = 1000; /* The sleep time when there are free slots available - but no jobs wanting to run. */ + int internal_offset = 0; /* Keep track of the (index of) the last job slot fired off - minor time saving. */ + while (true) { + if (tp->queue_size > tp->queue_index) { + /* + There are jobs in the queue which would like to run - + let us see if we can find a slot for them. + */ + int counter = 0; + bool slot_found = false; + do { + int slot_index = (counter + internal_offset) % tp->max_running; + thread_pool_job_slot_type * job_slot = &tp->job_slots[ slot_index ]; + if (!job_slot->running) { + /* OK thread[slot_index] is ready to take this job.*/ + thread_pool_arg_type * tp_arg; + + /* + The queue might be updated by the main thread - we must + take a copy of the node we are interested in. + */ + pthread_rwlock_rdlock( &tp->queue_lock ); + tp_arg = (thread_pool_arg_type*)util_alloc_copy( &tp->queue[ tp->queue_index ] , sizeof * tp_arg ); + pthread_rwlock_unlock( &tp->queue_lock ); + + tp_arg->slot_index = slot_index; + job_slot->running = true; + /* + Here is the actual pthread_create() call creating an + additional running thread. + */ + + /*Cleanup of previous run threads. Needed to avoid memory leak*/ + if (job_slot->run_count > 0) + pthread_join(job_slot->thread, NULL); + + pthread_create( &job_slot->thread , NULL , thread_pool_start_job , tp_arg ); + job_slot->run_count += 1; + tp->queue_index++; + internal_offset += (counter + 1); + slot_found = true; + } else + counter++; + } while (!slot_found && (counter < tp->max_running)); + + if (!slot_found) { + res_yield(); + } + } else + util_usleep(usleep_init); /* There are no jobs wanting to run. */ + + /*****************************************************************/ + /* + We exit explicitly from this loop when both conditions apply: + + 1. tp->join == true : The calling scope has signaled that it will not submit more jobs. + 2. tp->queue_size == tp->queue_index : This function has submitted all the jobs in the queue. + */ + if ((tp->join) && (tp->queue_size == tp->queue_index)) + break; + } /* End of while() loop */ + } + + /* + There are no more jobs in the queue, and the main scope has + signaled that join should start. Observe that we join only the + jobs corresponding to explicitly running job_slots; when a job + slot is used multiple times the first jobs run in the job_slot + will not be explicitly joined. + */ + { + int i; + for (i=0; i < tp->max_running; i++) { + thread_pool_job_slot_type job_slot = tp->job_slots[i]; + if (job_slot.run_count > 0) + pthread_join( job_slot.thread , NULL ); + } + } + /* When we are here all the jobs have completed. */ + return NULL; +} + + + + +/** + This function initializes a couple of counters, and starts up the + dispatch thread. If the thread_pool should be reused after a join, + this function must be called before adding new jobs. + + The functions thread_pool_restart() and thread_pool_join() should + be joined up like open/close and malloc/free combinations. +*/ + +void thread_pool_restart( thread_pool_type * tp ) { + if (tp->accepting_jobs) + util_abort("%s: fatal error - tried restart already running thread pool\n",__func__); + { + tp->join = false; + tp->queue_index = 0; + tp->queue_size = 0; + { + int i; + for (i=0; i < tp->max_running; i++) { + tp->job_slots[i].run_count = 0; + tp->job_slots[i].running = false; + } + } + + /* Starting the dispatch thread. */ + pthread_create( &tp->dispatch_thread , NULL , thread_pool_main_loop , tp ); + tp->accepting_jobs = true; + } +} + + + +/** + This function is called by the calling scope when all the jobs have + been submitted, and we just wait for them to complete. + + This function just sets the join switch to true - this again tells + the dispatch_thread to start the join process on the worker + threads. +*/ + +void thread_pool_join(thread_pool_type * pool) { + pool->join = true; /* Signals to the main thread that joining can start. */ + if (pool->max_running > 0) { + pthread_join( pool->dispatch_thread , NULL ); /* Wait for the main thread to complete. */ + pool->accepting_jobs = false; + } +} + +/* + This will try to join the thread; if the manager thread has not + completed within @timeout_seconds the function will return false. If + the join fails the queue will be reset in a non-joining state and it + will be open for more jobs. Probably not in a 100% sane state. +*/ + +bool thread_pool_try_join(thread_pool_type * pool, int timeout_seconds) { + bool join_ok = true; + + pool->join = true; /* Signals to the main thread that joining can start. */ + if (pool->max_running > 0) { + time_t timeout_time = time( NULL ); + util_inplace_forward_seconds_utc(&timeout_time , timeout_seconds ); + +#ifdef HAVE_TIMEDJOIN + + struct timespec ts; + ts.tv_sec = timeout_time; + ts.tv_nsec = 0; + + { + int join_return = pthread_timedjoin_np( pool->dispatch_thread , NULL , &ts); /* Wait for the main thread to complete. */ + if (join_return == 0) + pool->accepting_jobs = false; + else { + pool->join = false; + join_ok = false; + } + } + +#else + + while(true) { + if (pthread_kill(pool->dispatch_thread, 0) == 0){ + res_yield(); + } else { + pthread_join(pool->dispatch_thread, NULL); + pool->accepting_jobs = false; + break; + } + + time_t now = time(NULL); + + if(util_difftime_seconds(now, timeout_time) <= 0) { + join_ok = false; + break; + } + } + +#endif + + + + } + return join_ok; +} + + + + +/** + max_running is the maximum number of concurrent threads. If + @start_queue is true the dispatch thread will start immediately. If + the function is called with @start_queue == false you must first + call thread_pool_restart() BEFORE you can start adding jobs. +*/ + +thread_pool_type * thread_pool_alloc(int max_running , bool start_queue) { + thread_pool_type * pool = (thread_pool_type*)util_malloc( sizeof *pool ); + UTIL_TYPE_ID_INIT( pool , THREAD_POOL_TYPE_ID ); + pool->job_slots = (thread_pool_job_slot_type*)util_calloc( max_running , sizeof * pool->job_slots ); + pool->max_running = max_running; + pool->queue = NULL; + pool->accepting_jobs = false; + pthread_rwlock_init( &pool->queue_lock , NULL); + thread_pool_resize_queue( pool , 32 ); + if (start_queue) + thread_pool_restart( pool ); + return pool; +} + + + +void thread_pool_add_job(thread_pool_type * pool , start_func_ftype * start_func , void * func_arg ) { + if (pool->max_running == 0) /* Blocking non-threaded mode: */ + start_func( func_arg ); + else { + if (pool->accepting_jobs) { + if (pool->queue_size == pool->queue_alloc_size) + thread_pool_resize_queue( pool , pool->queue_alloc_size * 2); + + /* + The new job is added to the queue - the main thread is watching + the queue and will pick up the new job. + */ + { + int queue_index = pool->queue_size; + + pool->queue[ queue_index ].pool = pool; + pool->queue[ queue_index ].func_arg = func_arg; + pool->queue[ queue_index ].func = start_func; + pool->queue[ queue_index ].return_value = NULL; + pool->queue[ queue_index ].queue_index = queue_index; + } + pool->queue_size++; /* <- This is shared between this thread and the dispatch thread */ + } else + util_abort("%s: thread_pool is not running - restart with thread_pool_restart()?? \n",__func__); + } +} + + + +/* + Observe that this function does not join the worker threads, + i.e. you should call thread_pool_join() first (otherwise the thing + will go up in flames). +*/ + + +void thread_pool_free(thread_pool_type * pool) { + free( pool->job_slots ); + free( pool->queue ); + free(pool); +} + +int thread_pool_get_max_running( const thread_pool_type * pool ) { + return pool->max_running; +} diff --git a/libres/lib/res_util/ui_return.cpp b/libres/lib/res_util/ui_return.cpp new file mode 100644 index 00000000000..cc8cdc462bd --- /dev/null +++ b/libres/lib/res_util/ui_return.cpp @@ -0,0 +1,116 @@ +/* + + * + * Created on: Aug 28, 2013 + * Author: joaho + */ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'ui_return.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 UI_RETURN_TYPE_ID 6122209 + +struct ui_return_struct { + UTIL_TYPE_ID_DECLARATION; + ui_return_status_enum status; + stringlist_type * error_list; + char * help_text; +}; + + +UTIL_IS_INSTANCE_FUNCTION(ui_return , UI_RETURN_TYPE_ID) + + +ui_return_type * ui_return_alloc(ui_return_status_enum status) { + ui_return_type * ui_return = (ui_return_type*)util_malloc( sizeof * ui_return ); + UTIL_TYPE_ID_INIT(ui_return , UI_RETURN_TYPE_ID); + ui_return->status = status; + ui_return->help_text = NULL; + ui_return->error_list = stringlist_alloc_new(); + return ui_return; +} + + +void ui_return_free( ui_return_type * ui_return ) { + stringlist_free(ui_return->error_list); + free( ui_return->help_text); + free( ui_return); +} + + +ui_return_status_enum ui_return_get_status(const ui_return_type * ui_return) { + return ui_return->status; +} + + +bool ui_return_add_error(ui_return_type *ui_return , const char * error_msg) { + if (ui_return->status != UI_RETURN_OK) + stringlist_append_copy( ui_return->error_list , error_msg); + + return (ui_return->status != UI_RETURN_OK); +} + + +int ui_return_get_error_count( const ui_return_type * ui_return ) { + return stringlist_get_size( ui_return->error_list ); +} + + +const char * ui_return_get_first_error( const ui_return_type * ui_return) { + if (stringlist_get_size(ui_return->error_list)) + return stringlist_front( ui_return->error_list); + else + return NULL; +} + + +const char * ui_return_get_last_error( const ui_return_type * ui_return) { + if (stringlist_get_size(ui_return->error_list)) + return stringlist_back( ui_return->error_list); + else + return NULL; +} + +const char * ui_return_iget_error( const ui_return_type * ui_return , int index) { + return stringlist_iget(ui_return->error_list , index); +} + + +const char * ui_return_get_help(const ui_return_type * ui_return) { + return ui_return->help_text; +} + + +void ui_return_add_help(ui_return_type * ui_return, const char * help_text) { + if (ui_return->help_text) { + int new_length = strlen(ui_return->help_text) + strlen(help_text) + 1 + 1; + ui_return->help_text = (char*)util_realloc(ui_return->help_text , new_length * sizeof * ui_return->help_text); + + strcat(ui_return->help_text , " "); + strcat(ui_return->help_text , help_text); + } else + ui_return->help_text = util_alloc_string_copy( help_text); +} + + diff --git a/libres/lib/res_util/util_env.cpp b/libres/lib/res_util/util_env.cpp new file mode 100644 index 00000000000..e2f32149a95 --- /dev/null +++ b/libres/lib/res_util/util_env.cpp @@ -0,0 +1,279 @@ +/* + Copyright (C) 2012 Equinor ASA, Norway. + + The file 'util_env.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 "ert/util/build_config.hpp" +#include +#include +#include + +#ifdef HAVE_POSIX_SETENV +#define PATHVAR_SPLIT ":" + +void util_unsetenv( const char * variable ) { + unsetenv( variable ); +} + +void util_setenv( const char * variable , const char * value) { + int overwrite = 1; + setenv( variable , value , overwrite ); +} + +#else + +#include + +#define PATHVAR_SPLIT ";" +void util_setenv( const char * variable , const char * value) { + SetEnvironmentVariable( variable , NULL ); +} + +void util_unsetenv( const char * variable ) { + util_setenv( variable , NULL ); +} +#endif + +/** + Will return a NULL terminated list char ** of the paths in the PATH + variable. +*/ + +char ** util_alloc_PATH_list() { + char ** path_list = NULL; + char * path_env = getenv("PATH"); + if (path_env != NULL) { + int path_size; + + util_split_string(path_env , PATHVAR_SPLIT , &path_size , &path_list); + path_list = (char**)util_realloc( path_list , (path_size + 1) * sizeof * path_list); + path_list[path_size] = NULL; + } else { + path_list = (char**)util_malloc( sizeof * path_list); + path_list[0] = NULL; + } + return path_list; +} + +/** + This function searches through the content of the (currently set) + PATH variable, and allocates a string containing the full path + (first match) to the executable given as input. + + * If the entered executable already is an absolute path, a copy of + the input is returned *WITHOUT* consulting the PATH variable (or + checking that it exists). + + * If the executable starts with "./" getenv("PWD") is prepended. + + * If the executable is not found in the PATH list NULL is returned. +*/ + + +char * util_alloc_PATH_executable(const char * executable) { + if (util_is_abs_path(executable)) { + if (util_is_executable(executable)) + return util_alloc_string_copy(executable); + else + return NULL; + } else if (strncmp(executable , "./" , 2) == 0) { + char * cwd = util_alloc_cwd(); + char * path = util_alloc_filename(cwd , &executable[2] , NULL); + + /* The program has been invoked as ./xxxx */ + if (!(util_is_file(path) && util_is_executable( path ))) { + free( path ); + path = NULL; + } + free( cwd ); + + return path; + } else { + char * full_path = NULL; + char ** path_list = util_alloc_PATH_list(); + int ipath = 0; + + while (true) { + if (path_list[ipath] != NULL) { + char * current_attempt = util_alloc_filename(path_list[ipath] , executable , NULL); + + if ( util_is_file( current_attempt ) && util_is_executable( current_attempt )) { + full_path = current_attempt; + break; + } else { + free(current_attempt); + ipath++; + } + } else + break; + } + + util_free_NULL_terminated_stringlist(path_list); + return full_path; + } +} + + + + + +/** + This function updates an environment variable representing a path, + before actually updating the environment variable the current value + is checked, and the following rules apply: + + 1. If @append == true, and @value is already included in the + environment variable; nothing is done. + + 2. If @append == false, and the variable already starts with + @value, nothing is done. + + A pointer to the updated(?) environment variable is returned. +*/ + +const char * util_update_path_var(const char * variable, const char * value, bool append) { + const char * current_value = getenv( variable ); + if (current_value == NULL) + /* The (path) variable is not currently set. */ + util_setenv( variable , value ); + else { + bool update = true; + + { + char ** path_list; + int num_path; + util_split_string( current_value , ":" , &num_path , &path_list); + if (append) { + int i; + for (i = 0; i < num_path; i++) { + if (util_string_equal( path_list[i] , value)) + update = false; /* The environment variable already contains @value - no point in appending it at the end. */ + } + } else { + if (util_string_equal( path_list[0] , value)) + update = false; /* The environment variable already starts with @value. */ + } + util_free_stringlist( path_list , num_path ); + } + + if (update) { + char * new_value; + if (append) + new_value = util_alloc_sprintf("%s:%s" , current_value , value); + else + new_value = util_alloc_sprintf("%s:%s" , value , current_value); + util_setenv( variable , new_value ); + free( new_value ); + } + + } + return getenv( variable ); +} + + + +/** + This is a thin wrapper around the setenv() call, with the twist + that all $VAR expressions in the @value parameter are replaced with + getenv() calls, so that the function call: + + util_setenv("PATH" , "$HOME/bin:$PATH") + + Should work as in the shell. If the variables referred to with ${} + in @value do not exist the literal string, i.e. '$HOME' is + retained. + + If @value == NULL a call to unsetenv( @variable ) will be issued. +*/ + +const char * util_interp_setenv( const char * variable , const char * value) { + char * interp_value = util_alloc_envvar( value ); + if (interp_value != NULL) { + util_setenv( variable , interp_value); + free( interp_value ); + } else + util_unsetenv( variable ); + + return getenv( variable ); +} + + + +/** + This function will take a string as input, and then replace all if + $VAR expressions with the corresponding environment variable. If + the environament variable VAR is not set, the string literal $VAR + is retained. The return value is a newly allocated string. + + If the input value is NULL - the function will just return NULL; +*/ + + +char * util_alloc_envvar( const char * value ) { + if (value == NULL) + return NULL; + else { + buffer_type * buffer = buffer_alloc( 1024 ); /* Start by filling up a buffer instance with + the current content of @value. */ + buffer_fwrite_char_ptr( buffer , value ); + buffer_rewind( buffer ); + + + while (true) { + if (buffer_strchr( buffer , '$')) { + const char * data = (const char*)buffer_get_data( buffer ); + int offset = buffer_get_offset( buffer ) + 1; /* Points at the first character following the '$' */ + int var_length = 0; + + /* Find the length of the variable name */ + while (true) { + char c; + c = data[offset + var_length]; + if (!(isalnum( c ) || c == '_')) /* Any character which is NOT in the set [a-Z,0-9_] marks the end of the variable. */ + break; + + if (c == '\0') /* The end of the string. */ + break; + + var_length += 1; + } + + { + char * var_name = util_alloc_substring_copy( data , offset - 1 , var_length + 1); /* Include the leading $ */ + const char * var_value = getenv( &var_name[1] ); + + if (var_value != NULL) + buffer_search_replace( buffer , var_name , var_value); /* The actual string replacement. */ + else + buffer_fseek( buffer , var_length , SEEK_CUR ); /* The variable is not defined, and we leave the $name. */ + + free( var_name ); + } + } else break; /* No more $ to replace */ + } + + + buffer_shrink_to_fit( buffer ); + { + char * expanded_value = (char*)buffer_get_data( buffer ); + buffer_free_container( buffer ); + return expanded_value; + } + } +} diff --git a/libres/lib/res_util/util_printf.cpp b/libres/lib/res_util/util_printf.cpp new file mode 100644 index 00000000000..320671f448c --- /dev/null +++ b/libres/lib/res_util/util_printf.cpp @@ -0,0 +1,65 @@ +/* + Copyright (C) 2018 Equinor ASA, Norway. + + The file 'util_printf.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 + +void util_fprintf_string(const char * s , int width_ , string_alignement_type alignement , FILE * stream) { + char fmt[32]; + size_t i; + size_t width = width_; + if (alignement == left_pad) { + i = 0; + if (width > strlen(s)) { + for (i=0; i < (width - strlen(s)); i++) + fputc(' ' , stream); + } + fprintf(stream , "%s", s); + } else if (alignement == right_pad) { + sprintf(fmt , "%%-%lus" , width); + fprintf(stream , fmt , s); + } else { + int total_pad = width - strlen(s); + int front_pad = total_pad / 2; + int back_pad = total_pad - front_pad; + int i; + util_fprintf_string(s , front_pad + strlen(s) , left_pad , stream); + for (i=0; i < back_pad; i++) + fputc(' ' , stream); + } +} + + +void util_fprintf_double(double value , int width , int decimals , char base_fmt , FILE * stream) { + char * fmt = util_alloc_sprintf("%c%d.%d%c" , '%' , width , decimals , base_fmt); + fprintf(stream , fmt , value); + free(fmt); +} + + +void util_fprintf_int(int value , int width , FILE * stream) { + char fmt[32]; + sprintf(fmt , "%%%dd" , width); + fprintf(stream , fmt , value); +} + + diff --git a/libres/lib/rms/rms_file.cpp b/libres/lib/rms/rms_file.cpp new file mode 100644 index 00000000000..78a7174dd16 --- /dev/null +++ b/libres/lib/rms/rms_file.cpp @@ -0,0 +1,314 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'rms_file.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 + +/*****************************************************************/ +static const char * rms_ascii_header = "roff-asc"; +static const char * rms_binary_header = "roff-bin"; + +static const char * rms_comment1 = "ROFF file"; +static const char * rms_comment2 = "Creator: RMS - Reservoir Modelling System, version 8.1"; + + +struct rms_file_struct { + char * filename; + bool endian_convert; + bool fmt_file; + hash_type * type_map; + vector_type * tag_list; + FILE * stream; +}; + + + +/*****************************************************************/ +/* Pure roff routines */ + +static bool rms_fmt_file(const rms_file_type *rms_file) { + char filetype[9]; + rms_util_fread_string( filetype , 9 , rms_file->stream); + + if (strncmp(filetype , rms_binary_header , 8) == 0) + return false; + if (strncmp(filetype , rms_ascii_header , 8) == 0) + return true; + + fprintf(stderr,"%s: header : %8s not recognized in file: %s - aborting \n",__func__ , filetype , rms_file->filename); + abort(); + return false; // will not happen +} + + +static void rms_file_add_tag(rms_file_type *rms_file , const rms_tag_type *tag) { + vector_append_owned_ref(rms_file->tag_list , tag , rms_tag_free__ ); +} + + +rms_tag_type * rms_file_get_tag_ref(const rms_file_type *rms_file , + const char *tagname , + const char *keyname , + const char *keyvalue, bool abort_on_error) { + + rms_tag_type *return_tag = NULL; + + int size = vector_get_size( rms_file->tag_list ); + for (int index = 0; index < size; ++index) { + rms_tag_type *tag = (rms_tag_type*)vector_iget( rms_file->tag_list , index ); + if (rms_tag_name_eq(tag , tagname , keyname , keyvalue)) { + return_tag = tag; + break; + } + } + + if (return_tag == NULL && abort_on_error) { + if (keyname != NULL && keyvalue != NULL) + fprintf(stderr,"%s: failed to find tag:%s with key:%s=%s in file:%s - aborting \n",__func__ , tagname , keyname , keyvalue , rms_file->filename); + else + fprintf(stderr,"%s: failed to find tag:%s in file:%s - aborting \n",__func__ , tagname , rms_file->filename); + } + return return_tag; +} + + +/** + This function allocates and rms_file_type * handle, but it does + not load the file content. +*/ +rms_file_type * rms_file_alloc(const char *filename, bool fmt_file) { + rms_file_type *rms_file = (rms_file_type*)malloc(sizeof *rms_file); + rms_file->endian_convert = false; + rms_file->type_map = hash_alloc(); + rms_file->tag_list = vector_alloc_new(); + + hash_insert_hash_owned_ref(rms_file->type_map , "byte" , rms_type_alloc(rms_byte_type , 1) , rms_type_free); + hash_insert_hash_owned_ref(rms_file->type_map , "bool" , rms_type_alloc(rms_bool_type, 1) , rms_type_free); + hash_insert_hash_owned_ref(rms_file->type_map , "int" , rms_type_alloc(rms_int_type , 4) , rms_type_free); + hash_insert_hash_owned_ref(rms_file->type_map , "float" , rms_type_alloc(rms_float_type , 4) , rms_type_free); + hash_insert_hash_owned_ref(rms_file->type_map , "double" , rms_type_alloc(rms_double_type , 8) , rms_type_free); + + hash_insert_hash_owned_ref(rms_file->type_map , "char" , rms_type_alloc(rms_char_type , -1) , rms_type_free); /* Char are a f*** mix of vector and scalar */ + + rms_file->filename = NULL; + rms_file->stream = NULL; + rms_file_set_filename(rms_file , filename , fmt_file); + return rms_file; +} + + +void rms_file_set_filename(rms_file_type * rms_file , const char *filename , bool fmt_file) { + rms_file->filename = util_realloc_string_copy(rms_file->filename , filename); + rms_file->fmt_file = fmt_file; +} + + +void rms_file_free_data(rms_file_type * rms_file) { + vector_clear( rms_file->tag_list ); +} + + +void rms_file_free(rms_file_type * rms_file) { + rms_file_free_data(rms_file); + vector_free( rms_file->tag_list ); + hash_free(rms_file->type_map); + free(rms_file->filename); + free(rms_file); +} + + +static int rms_file_get_dim(const rms_tag_type *tag , const char *dim_name) { + rms_tagkey_type *key = rms_tag_get_key(tag , dim_name); + if (key == NULL) { + fprintf(stderr,"%s: failed to find tagkey:%s aborting \n" , __func__ , dim_name); + abort(); + } + return * (int *) rms_tagkey_get_data_ref(key); +} + + +rms_tag_type * rms_file_get_dim_tag_ref(const rms_file_type * rms_file) { + return rms_file_get_tag_ref(rms_file , "dimensions" , NULL , NULL , true); +} + + +void rms_file_get_dims(const rms_file_type * rms_file , int * dims) { + rms_tag_type *tag = rms_file_get_dim_tag_ref(rms_file); + dims[0] = rms_file_get_dim(tag , "nX"); + dims[1] = rms_file_get_dim(tag , "nY"); + dims[2] = rms_file_get_dim(tag , "nZ"); +} + + +FILE * rms_file_get_FILE(const rms_file_type * rms_file) { + return rms_file->stream; +} + + +static void rms_file_init_fread(rms_file_type * rms_file) { + + rms_file->fmt_file = rms_fmt_file( rms_file ); + if (rms_file->fmt_file) { + fprintf(stderr,"%s only binary files implemented - aborting \n",__func__); + abort(); + } + /* Skipping two comment lines ... */ + rms_util_fskip_string(rms_file->stream); + rms_util_fskip_string(rms_file->stream); + { + bool eof_tag; + rms_tag_type * filedata_tag = rms_tag_fread_alloc(rms_file->stream, + rms_file->type_map, + rms_file->endian_convert, + &eof_tag); + rms_tagkey_type * byteswap_key = rms_tag_get_key(filedata_tag , "byteswaptest"); + if (byteswap_key == NULL) { + fprintf(stderr,"%s: failed to find filedata/byteswaptest - aborting \n", __func__); + abort(); + } + int byteswap_value = *( int *) rms_tagkey_get_data_ref(byteswap_key); + if (byteswap_value == 1) + rms_file->endian_convert = false; + else + rms_file->endian_convert = true; + rms_tag_free(filedata_tag); + } +} + + +rms_tag_type * rms_file_fread_alloc_tag(rms_file_type * rms_file, + const char *tagname, + const char *keyname, + const char *keyvalue) { + rms_tag_type * tag = NULL; + rms_file_fopen_r(rms_file); + + long int start_pos = util_ftell(rms_file->stream); + fseek(rms_file->stream , 0 , SEEK_SET); + rms_file_init_fread(rms_file); + while (true) { + bool eof_tag = false; // will be set by rms_tag + rms_tag_type * tmp_tag = rms_tag_fread_alloc(rms_file->stream, + rms_file->type_map, + rms_file->endian_convert, + &eof_tag); + + if (!rms_tag_name_eq(tmp_tag, tagname, keyname, keyvalue)) { + rms_tag_free(tmp_tag); + continue; + } + tag = tmp_tag; + break; + } + + if (tag == NULL) { + fseek(rms_file->stream , start_pos , SEEK_SET); + util_abort("%s: could not find tag: \"%s\" (with %s=%s) in file:%s - aborting.\n", + __func__, + tagname, + keyname, + keyvalue, + rms_file->filename); + } + + rms_file_fclose(rms_file); + return tag; +} + + +FILE * rms_file_fopen_r(rms_file_type *rms_file) { + rms_file->stream = util_fopen(rms_file->filename , "r"); + return rms_file->stream; +} + + +FILE * rms_file_fopen_w(rms_file_type *rms_file) { + rms_file->stream = util_mkdir_fopen(rms_file->filename , "w"); + return rms_file->stream; +} + +void rms_file_fclose(rms_file_type * rms_file) { + fclose(rms_file->stream); + rms_file->stream = NULL; +} + + +rms_tagkey_type * rms_file_fread_alloc_data_tagkey(rms_file_type * rms_file, + const char *tagname, + const char *keyname, + const char *keyvalue) { + rms_tag_type * tag = rms_file_fread_alloc_tag(rms_file, + tagname, + keyname, + keyvalue); + if (tag == NULL) + return NULL; + + rms_tagkey_type *tagkey = rms_tagkey_copyc(rms_tag_get_key(tag, "data")); + rms_tag_free(tag); + return tagkey; +} + + +/*static */ +void rms_file_init_fwrite(const rms_file_type * rms_file , const char * filetype) { + if (!rms_file->fmt_file) + rms_util_fwrite_string(rms_binary_header , rms_file->stream); + else { + fprintf(stderr,"%s: Sorry only binary writes implemented ... \n",__func__); + rms_util_fwrite_string(rms_ascii_header , rms_file->stream); + } + + rms_util_fwrite_comment(rms_comment1 , rms_file->stream); + rms_util_fwrite_comment(rms_comment2 , rms_file->stream); + rms_tag_fwrite_filedata(filetype , rms_file->stream); +} + + +void rms_file_complete_fwrite(const rms_file_type * rms_file) { + rms_tag_fwrite_eof(rms_file->stream); +} + + +bool rms_file_is_roff(FILE * stream) { + const int len = strlen(rms_comment1); + char *header = (char*)malloc(strlen(rms_comment1) + 1); + const long int current_pos = util_ftell(stream); + bool roff_file = false; + + /* Skipping #roff-bin#0# WILL Fail with formatted files */ + fseek(stream, 1 + 1 + 8, SEEK_CUR); + + + rms_util_fread_string(header , len+1 , stream); + if (strncmp(rms_comment1 , header , len) == 0) + roff_file = true; + + fseek(stream , current_pos , SEEK_SET); + free(header); + return roff_file; +} diff --git a/libres/lib/rms/rms_tag.cpp b/libres/lib/rms/rms_tag.cpp new file mode 100644 index 00000000000..f89bfa40ca0 --- /dev/null +++ b/libres/lib/rms/rms_tag.cpp @@ -0,0 +1,284 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'rms_tag.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 + +static const char * rms_eof_tag = "eof"; +static const char * rms_starttag_string = "tag"; +static const char * rms_endtag_string = "endtag"; + + +#define SHARED 0 +#define OWNED_REF 1 +#define COPY 2 + +#define RMS_TAG_TYPE_ID 4431296 + +struct rms_tag_struct { + UTIL_TYPE_ID_DECLARATION; + char * name; + vector_type * key_list; + hash_type * key_hash; /* Hash of tagkey instances */ +}; + +/*****************************************************************/ + + +rms_tag_type * rms_tag_alloc(const char * name) { + rms_tag_type *tag = (rms_tag_type*)malloc(sizeof *tag); + UTIL_TYPE_ID_INIT( tag , RMS_TAG_TYPE_ID ) + tag->name = NULL; + tag->key_hash = hash_alloc(); + tag->key_list = vector_alloc_new(); + if (name != NULL) + tag->name = util_alloc_string_copy(name); + return tag; +} + + +static UTIL_SAFE_CAST_FUNCTION( rms_tag , RMS_TAG_TYPE_ID ) + + +void rms_tag_free(rms_tag_type *tag) { + free(tag->name); + hash_free(tag->key_hash); + vector_free(tag->key_list); + free(tag); +} + +void rms_tag_free__(void * arg) { + rms_tag_type * tag = rms_tag_safe_cast( arg ); + rms_tag_free( tag ); +} + + +const char * rms_tag_get_name(const rms_tag_type *tag) { + return tag->name; +} + + + +/*static*/ +void rms_tag_fread_header(rms_tag_type *tag, FILE *stream, bool *eof_tag) { + char *buffer; + *eof_tag = false; + buffer = (char*)util_calloc( 4 , sizeof * buffer); + if (!rms_util_fread_string(buffer , 4 , stream )) + util_abort("%s: not at tag - header aborting \n",__func__); + + if (strcmp(buffer , rms_starttag_string) != 0) + util_abort("%s: not at tag - header aborting \n",__func__); + + char *tmp = (char*)util_calloc(rms_util_fread_strlen(stream) + 1, sizeof * tmp); + rms_util_fread_string(tmp , 0 , stream); + tag->name = tmp; + if (strcmp(tag->name , rms_eof_tag) == 0) + *eof_tag = true; + + free(buffer); +} + + + +/** + This function does a "two-level" comparison. + + 1. tag->name is compared with tagname. + 2. Iff test number one succeeds we go further to step2. + The second will always suceed if tagkey_name == NULL. +*/ +bool rms_tag_name_eq(const rms_tag_type *tag, + const char *tagname, + const char *tagkey_name, + const char *keyvalue) { + + if (strcmp(tag->name , tagname) != 0) + return false; + + if (tagkey_name == NULL || keyvalue == NULL) + return true; + + if (hash_has_key(tag->key_hash , tagkey_name)) { + const rms_tagkey_type *tagkey = (const rms_tagkey_type*)hash_get(tag->key_hash, tagkey_name); + return rms_tagkey_char_eq(tagkey, keyvalue); + } + + return false; +} + + +rms_tagkey_type * rms_tag_get_key(const rms_tag_type *tag, + const char *keyname) { + if (hash_has_key(tag->key_hash , keyname)) + return (rms_tagkey_type*)hash_get(tag->key_hash, keyname); + return NULL; +} + + +const char * rms_tag_get_namekey_name(const rms_tag_type * tag) { + rms_tagkey_type * name_key = rms_tag_get_key(tag , "name"); + if (name_key == NULL) + util_abort("%s: no name tagkey defined for this tag - aborting \n", + __func__); + + return (const char*)rms_tagkey_get_data_ref(name_key); +} + + +void rms_tag_add_tagkey(rms_tag_type *tag, + const rms_tagkey_type *tagkey, + int mem_mode) { + rms_tagkey_type * tagkey_copy; + + switch (mem_mode) { + case(COPY): + tagkey_copy = rms_tagkey_copyc(tagkey); + vector_append_owned_ref( tag->key_list , tagkey_copy , rms_tagkey_free_ ); + hash_insert_ref(tag->key_hash , rms_tagkey_get_name(tagkey_copy) , tagkey_copy); + break; + case(OWNED_REF): + vector_append_owned_ref( tag->key_list , tagkey , rms_tagkey_free_ ); + hash_insert_ref(tag->key_hash , rms_tagkey_get_name(tagkey) , tagkey); + break; + case(SHARED): + vector_append_ref( tag->key_list , tagkey ); + hash_insert_ref(tag->key_hash , rms_tagkey_get_name(tagkey) , tagkey); + break; + } +} + + + +static bool rms_tag_at_endtag(FILE *stream) { + const int init_pos = util_ftell(stream); + bool at_endtag = false; + char tag[7]; + if (rms_util_fread_string(tag , 7 , stream)) { + if (strcmp(tag , rms_endtag_string) == 0) + at_endtag = true; + else + at_endtag = false; + } else + at_endtag = false; + + if (!at_endtag) + fseek(stream , init_pos , SEEK_SET); + return at_endtag; +} + + +void rms_fread_tag(rms_tag_type *tag, + FILE *stream, + hash_type *type_map, + bool endian_convert, + bool *at_eof) { + rms_tag_fread_header(tag , stream , at_eof); + if (*at_eof) + return; + + while (! rms_tag_at_endtag(stream)) { + rms_tagkey_type *tagkey = rms_tagkey_alloc_empty(endian_convert); + rms_tagkey_load(tagkey , endian_convert , stream , type_map); + rms_tag_add_tagkey(tag , tagkey , COPY); + rms_tagkey_free(tagkey); + } +} + + + +rms_tag_type * rms_tag_fread_alloc(FILE *stream, + hash_type *type_map, + bool endian_convert, + bool *at_eof) { + rms_tag_type *tag = rms_tag_alloc(NULL); + rms_fread_tag(tag , stream , type_map , endian_convert , at_eof); + return tag; +} + + + +void rms_tag_fwrite(const rms_tag_type * tag , FILE * stream) { + rms_util_fwrite_string("tag" , stream); + rms_util_fwrite_string(tag->name , stream); + + int size = vector_get_size(tag->key_list); + for (int i = 0; i < size; i++) { + const rms_tagkey_type * tagkey = (const rms_tagkey_type*)vector_iget_const( tag->key_list , i ); + rms_tagkey_fwrite( tagkey , stream); + } + + rms_util_fwrite_string("endtag" , stream); +} + + +void rms_tag_fwrite_eof(FILE *stream) { + rms_tag_type * tag = rms_tag_alloc("eof"); + rms_tag_fwrite(tag , stream); + rms_tag_free(tag); +} + + +void rms_tag_fwrite_filedata(const char * filetype, FILE *stream) { + rms_tag_type * tag = rms_tag_alloc("filedata"); + + rms_tag_add_tagkey(tag , rms_tagkey_alloc_byteswap() , OWNED_REF); + rms_tag_add_tagkey(tag , rms_tagkey_alloc_filetype(filetype) , OWNED_REF); + rms_tag_add_tagkey(tag , rms_tagkey_alloc_creationDate() , OWNED_REF); + + rms_tag_fwrite(tag , stream); + rms_tag_free(tag); +} + + +rms_tag_type * rms_tag_alloc_dimensions(int nX , int nY , int nZ) { + rms_tag_type * tag = rms_tag_alloc("dimensions"); + + rms_tag_add_tagkey(tag , rms_tagkey_alloc_dim("nX", nX) , OWNED_REF); + rms_tag_add_tagkey(tag , rms_tagkey_alloc_dim("nY", nY) , OWNED_REF); + rms_tag_add_tagkey(tag , rms_tagkey_alloc_dim("nZ", nZ) , OWNED_REF); + + return tag; +} + + +void rms_tag_fwrite_dimensions(int nX , int nY , int nZ , FILE *stream) { + rms_tag_type * tag = rms_tag_alloc_dimensions(nX , nY , nZ); + rms_tag_fwrite(tag , stream); + rms_tag_free(tag); +} + + +void rms_tag_fwrite_parameter(const char *param_name, + const rms_tagkey_type *data_key, + FILE *stream) { + rms_tag_type * tag = rms_tag_alloc("parameter"); + + rms_tag_add_tagkey(tag, rms_tagkey_alloc_parameter_name(param_name), OWNED_REF); + rms_tag_add_tagkey(tag, data_key, SHARED); + rms_tag_fwrite(tag , stream); + rms_tag_free(tag); + +} diff --git a/libres/lib/rms/rms_tagkey.cpp b/libres/lib/rms/rms_tagkey.cpp new file mode 100644 index 00000000000..15f48a23f94 --- /dev/null +++ b/libres/lib/rms/rms_tagkey.cpp @@ -0,0 +1,518 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'rms_tagkey.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 + +static const char * rms_array_string = "array"; + +/*(static const char rms_type_names[6][7] = {{"char\0"}, + {"float\0"}, + {"double\0"}, + {"bool\0"}, + {"byte\0"}, + {"int\0"}};*/ + +static const char * rms_type_names[6] = {"char", + "float", + "double", + "bool", + "byte", + "int"}; + +static const int rms_type_size[6] = {1 , 4 , 8 , 1 , 1 , 4}; /* */ + + +struct rms_tagkey_struct { + int size; + int sizeof_ctype; + int data_size; + int alloc_size; + rms_type_enum rms_type; + char *name; + void *data; + bool endian_convert; + bool shared_data; +}; + + +/*****************************************************************/ + + + +/*****************************************************************/ + + +static void rms_tagkey_assert_fnum(const rms_tagkey_type * tagkey) { + if (!(tagkey->rms_type == rms_float_type || tagkey->rms_type == rms_double_type)) { + fprintf(stderr,"%s: tried to perform numerical operataion on rms_type: %s invalid/not implemented\n",__func__ , rms_type_names[tagkey->rms_type]); + abort(); + } +} + +static void rms_tagkey_assert_fnum2(const rms_tagkey_type * tagkey1 , const rms_tagkey_type *tagkey2) { + rms_tagkey_assert_fnum(tagkey1); + rms_tagkey_assert_fnum(tagkey2); + if (tagkey1->size != tagkey2->size || tagkey1->rms_type != tagkey2->rms_type) { + fprintf(stderr,"%s: tried to combine tagkey with different size/type - aborting \n",__func__); + abort(); + } +} + + +void rms_tagkey_clear(rms_tagkey_type * tagkey) { + int i; + rms_tagkey_assert_fnum(tagkey); + switch (tagkey->rms_type) { + case(rms_double_type): + { + double *tmp = (double *) tagkey->data; + for (i=0; i < tagkey->size; i++) + tmp[i] = 0; + } + break; + case(rms_float_type): + { + float *tmp = (float *) tagkey->data; + for (i=0; i < tagkey->size; i++) + tmp[i] = 0; + } + break; + default: + fprintf(stderr,"%s: only implemented for rms_double_type and rms_float_type - aborting \n",__func__); + abort(); + } +} + + +void rms_tagkey_inplace_sqr(rms_tagkey_type * tagkey) { + int i; + rms_tagkey_assert_fnum(tagkey); + switch (tagkey->rms_type) { + case(rms_double_type): + { + double *tmp = (double *) tagkey->data; + for (i=0; i < tagkey->size; i++) + tmp[i] *= tmp[i]; + } + break; + + case(rms_float_type): + { + float *tmp = (float *) tagkey->data; + for (i=0; i < tagkey->size; i++) + tmp[i] *= tmp[i]; + } + break; + + default: + fprintf(stderr,"%s: only implemented for rms_double_type and rms_float_type - aborting \n",__func__); + abort(); + } +} + + +void rms_tagkey_inplace_log10(rms_tagkey_type * tagkey) { + int i; + rms_tagkey_assert_fnum(tagkey); + switch (tagkey->rms_type) { + case(rms_double_type): + { + double *tmp = (double *) tagkey->data; + for (i=0; i < tagkey->size; i++) + tmp[i] = log10(tmp[i]); + } + break; + + case(rms_float_type): + { + float *tmp = (float *) tagkey->data; + for (i=0; i < tagkey->size; i++) + tmp[i] = log10(tmp[i]); + } + break; + + default: + fprintf(stderr,"%s: only implemented for rms_double_type and rms_float_type - aborting \n",__func__); + abort(); + } +} + + +void rms_tagkey_inplace_sqrt(rms_tagkey_type * tagkey) { + int i; + rms_tagkey_assert_fnum(tagkey); + switch (tagkey->rms_type) { + case(rms_double_type): + { + double *tmp = (double *) tagkey->data; + for (i=0; i < tagkey->size; i++) + tmp[i] = sqrt(util_double_max(0.0 , tmp[i])); + } + break; + + case(rms_float_type): + { + float *tmp = (float *) tagkey->data; + for (i=0; i < tagkey->size; i++) + tmp[i] = sqrtf(util_float_max(0.0 , tmp[i])); + } + break; + + default: + fprintf(stderr,"%s: only implemented for rms_double_type and rms_float_type - aborting \n",__func__); + abort(); + } +} + + +void rms_tagkey_inplace_add_scaled(rms_tagkey_type * tagkey , const rms_tagkey_type *delta, double factor) { + int i; + rms_tagkey_assert_fnum2(tagkey , delta); + switch (tagkey->rms_type) { + case(rms_double_type): + { + double *tmp1 = (double *) tagkey->data; + const double *tmp2 = (const double *) delta->data; + for (i=0; i < tagkey->size; i++) + tmp1[i] += tmp2[i] * factor; + } + break; + + case(rms_float_type): + { + float *tmp1 = (float *) tagkey->data; + const float *tmp2 = (const float *) delta->data; + for (i=0; i < tagkey->size; i++) + tmp1[i] += tmp2[i] * factor; + } + break; + + default: + fprintf(stderr,"%s: only implemented for rms_double_type and rms_float_type - aborting \n",__func__); + abort(); + } +} + +void rms_tagkey_free_(void *_tagkey) { + rms_tagkey_type * tagkey = (rms_tagkey_type *) _tagkey; + rms_tagkey_free(tagkey); +} + + +static void rms_tagkey_alloc_data(rms_tagkey_type *tagkey) { + if (!tagkey->shared_data) { + + if (tagkey->data_size > tagkey->alloc_size) { + void *tmp = realloc(tagkey->data , tagkey->data_size); + if (tmp == NULL) { + fprintf(stderr,"%s: failed to allocate: %d bytes of storage - aborting \n",__func__ , tagkey->data_size); + abort(); + } + tagkey->data = tmp; + tagkey->alloc_size = tagkey->data_size; + + } + } +} + + + + +rms_tagkey_type * rms_tagkey_copyc(const rms_tagkey_type *tagkey) { + rms_tagkey_type *new_tagkey = rms_tagkey_alloc_empty(tagkey->endian_convert); + + new_tagkey->alloc_size = 0; + new_tagkey->size = tagkey->size; + new_tagkey->sizeof_ctype = tagkey->sizeof_ctype; + new_tagkey->data_size = tagkey->data_size; + new_tagkey->rms_type = tagkey->rms_type; + new_tagkey->data = NULL; + new_tagkey->shared_data = tagkey->shared_data; + + rms_tagkey_alloc_data(new_tagkey); + memcpy(new_tagkey->data , tagkey->data , tagkey->data_size); + new_tagkey->name = util_alloc_string_copy(tagkey->name); + return new_tagkey; +} + + +static void rms_tagkey_set_data_size(rms_tagkey_type *tagkey , FILE *stream , int strlen) { + + if (tagkey->rms_type == rms_char_type) { + if (stream != NULL) { + const long int init_pos = util_ftell(stream); + int i; + for (i=0; i < tagkey->size; i++) + rms_util_fskip_string(stream); + tagkey->data_size = util_ftell(stream) - init_pos; + fseek(stream , init_pos , SEEK_SET); + } else + tagkey->data_size = strlen + 1; + } else + tagkey->data_size = tagkey->size * tagkey->sizeof_ctype; +} + + + +static void rms_tagkey_fread_data(rms_tagkey_type *tagkey , bool endian_convert , FILE *stream) { + if (tagkey->alloc_size < tagkey->data_size) { + fprintf(stderr,"%s: fatal error buffer to small - aborting \n",__func__); + abort(); + } + + int bytes_read = fread(tagkey->data , 1 , tagkey->data_size , stream); + if (bytes_read != tagkey->data_size) { + fprintf(stderr,"%s: failed to read %d bytes - premature EOF? \n",__func__ , tagkey->data_size); + fprintf(stderr,"%s: tagkey: %s \n",__func__ , tagkey->name); + abort(); + } + if (endian_convert) + if (tagkey->sizeof_ctype > 1) + util_endian_flip_vector(tagkey->data , tagkey->sizeof_ctype , tagkey->size); +} + +void rms_tagkey_set_data(rms_tagkey_type * tagkey , const void * data) { + if (tagkey->shared_data) + tagkey->data = (void *) data; + else + memcpy(tagkey->data , data , tagkey->data_size); +} + + + + +static void rms_fread_tagkey_header(rms_tagkey_type *tagkey , FILE *stream, hash_type *type_map) { + bool is_array; + char type_string[7]; + + rms_util_fread_string(type_string , 7 , stream); + if (strcmp(type_string , rms_array_string) == 0) { + is_array = true; + rms_util_fread_string(type_string , 7 , stream); + } else + is_array = false; + + { + __rms_type * rms_t = (__rms_type*)hash_get(type_map , type_string); + tagkey->rms_type = rms_t->rms_type; + tagkey->sizeof_ctype = rms_t->sizeof_ctype; + } + + tagkey->name = (char*)realloc(tagkey->name , rms_util_fread_strlen(stream) + 1); + + rms_util_fread_string(tagkey->name , 0 , stream); + if (is_array) + fread(&tagkey->size , 1 , sizeof tagkey->size, stream); + else + tagkey->size = 1; + rms_tagkey_set_data_size(tagkey , stream , -1); +} + + + +static void rms_fread_realloc_tagkey(rms_tagkey_type *tagkey , bool endian_convert , FILE *stream , hash_type *type_map) { + + rms_fread_tagkey_header(tagkey , stream , type_map); + rms_tagkey_alloc_data(tagkey); + rms_tagkey_fread_data(tagkey , endian_convert , stream); + +} + + + +static void rms_tagkey_fwrite_data(const rms_tagkey_type * tagkey , FILE *stream) { + int elm = fwrite(tagkey->data , 1 , tagkey->data_size , stream); + if (elm != tagkey->data_size) { + fprintf(stderr,"%s: failed to write %d bytes to file [tagkey:%s] - aborting \n",__func__ , tagkey->data_size , tagkey->name); + abort(); + } +} + + +void rms_tagkey_fwrite(const rms_tagkey_type * tagkey , FILE *stream) { + if (tagkey->size > 1) + rms_util_fwrite_string("array" , stream); + rms_util_fwrite_string(rms_type_names[tagkey->rms_type] , stream); + rms_util_fwrite_string(tagkey->name , stream); + if (tagkey->size > 1) { + fwrite(&tagkey->size , sizeof tagkey->size , 1 , stream); + rms_util_fwrite_newline(stream); + } + rms_tagkey_fwrite_data(tagkey , stream); +} + +const char * rms_tagkey_get_name(const rms_tagkey_type *tagkey) { + return tagkey->name; +} + +void * rms_tagkey_get_data_ref(const rms_tagkey_type *tagkey) { + return tagkey->data; +} + + +void rms_tagkey_load(rms_tagkey_type *tagkey , bool endian_convert , FILE *stream, hash_type *type_map) { + rms_fread_realloc_tagkey(tagkey , endian_convert , stream , type_map); +} + + +bool rms_tagkey_char_eq(const rms_tagkey_type *tagkey , const char *keyvalue) { + bool eq = false; + if (tagkey->rms_type == rms_char_type) { + if (strcmp(keyvalue , (const char*)tagkey->data) == 0) + eq = true; + } + return eq; +} + + +rms_tagkey_type * rms_tagkey_alloc_empty(bool endian_convert) { + + rms_tagkey_type *tagkey = (rms_tagkey_type*)malloc(sizeof *tagkey); + tagkey->alloc_size = 0; + tagkey->data_size = 0; + tagkey->size = 0; + tagkey->name = NULL; + tagkey->data = NULL; + tagkey->endian_convert = endian_convert; + tagkey->shared_data = false; + + return tagkey; + +} + + +static rms_tagkey_type * rms_tagkey_alloc_initialized(const char * name , int size , rms_type_enum rms_type , bool endian_convert) { + rms_tagkey_type *tagkey = rms_tagkey_alloc_empty(endian_convert); + tagkey->size = size; + tagkey->rms_type = rms_type; + tagkey->sizeof_ctype = rms_type_size[rms_type]; + tagkey->data_size = tagkey->size * tagkey->sizeof_ctype; + tagkey->name = util_alloc_string_copy(name); + return tagkey; +} + + +rms_tagkey_type * rms_tagkey_alloc_complete(const char * name , int size , rms_type_enum rms_type , const void * data , bool shared_data) { + rms_tagkey_type * tag = rms_tagkey_alloc_initialized(name , size , rms_type , false); + tag->shared_data = shared_data; + + rms_tagkey_alloc_data(tag); + rms_tagkey_set_data(tag , data); + + return tag; +} + + +rms_type_enum rms_tagkey_get_rms_type(const rms_tagkey_type * key) { + return key->rms_type; +} + +ecl_data_type rms_tagkey_get_ecl_data_type(const rms_tagkey_type * key) { + switch (key->rms_type) { + case(rms_float_type): + return ECL_FLOAT; + case(rms_double_type): + return ECL_DOUBLE; + case(rms_int_type): + return ECL_INT; + default: + util_abort("%s: sorry rms_type: %d not implemented - aborting \n",__func__ , key->rms_type); + return ECL_INT; /* Dummy */ + } +} + + +void rms_tagkey_free(rms_tagkey_type *tagkey) { + if (tagkey->name != NULL) free(tagkey->name); + + if (!tagkey->shared_data) + if (tagkey->data != NULL) free(tagkey->data); + + free(tagkey); +} + + +rms_tagkey_type * rms_tagkey_alloc_byteswap() { + rms_tagkey_type *tagkey = rms_tagkey_alloc_initialized("byteswaptest" , 1 , rms_int_type , false); + rms_tagkey_alloc_data(tagkey); + ((int *) tagkey->data)[0] = 1; + return tagkey; +} + + +rms_tagkey_type * rms_tagkey_alloc_filetype(const char * filetype) { + rms_tagkey_type *tagkey = rms_tagkey_alloc_initialized("filetype" , 1 , rms_char_type , false); + rms_tagkey_set_data_size(tagkey , NULL , strlen(filetype)); + rms_tagkey_alloc_data(tagkey); + sprintf((char*)tagkey->data , "%s" , filetype); + return tagkey; +} + + +rms_tagkey_type * rms_tagkey_alloc_parameter_name(const char * parameter_name) { + rms_tagkey_type *tagkey = rms_tagkey_alloc_initialized("name" , 1 , rms_char_type , false); + rms_tagkey_set_data_size(tagkey , NULL , strlen(parameter_name)); + rms_tagkey_alloc_data(tagkey); + sprintf((char*)tagkey->data , "%s" , parameter_name); + return tagkey; +} + + +rms_tagkey_type * rms_tagkey_alloc_creationDate() { + const int len = strlen("08/05/2007 08:31:39"); + struct tm ts; + time_t now; + rms_tagkey_type *tagkey = rms_tagkey_alloc_initialized("creationDate" , 1 , rms_char_type , false); + + now = time(NULL); + localtime_r(&now , &ts); + + rms_tagkey_set_data_size(tagkey , NULL , len); + rms_tagkey_alloc_data(tagkey); + sprintf((char*)tagkey->data , "%02d/%02d/%4d %02d:%02d:%02d" , + ts.tm_mday, + ts.tm_mon, + ts.tm_year + 1900, + ts.tm_hour, + ts.tm_min, + ts.tm_sec); + + return tagkey; +} + + +rms_tagkey_type * rms_tagkey_alloc_dim(const char * dim, int value) { + rms_tagkey_type *tagkey = rms_tagkey_alloc_initialized(dim , 1 , rms_int_type , false); + rms_tagkey_alloc_data(tagkey); + ((int *) tagkey->data)[0] = value; + return tagkey; +} + + +int rms_tagkey_get_size(const rms_tagkey_type * tagkey) { return tagkey->size; } diff --git a/libres/lib/rms/rms_type.cpp b/libres/lib/rms/rms_type.cpp new file mode 100644 index 00000000000..f493cb9cc63 --- /dev/null +++ b/libres/lib/rms/rms_type.cpp @@ -0,0 +1,46 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'rms_type.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 + +/*****************************************************************/ +/* A microscopic (purely internal) type object only used + for storing the hash type_map */ +/*****************************************************************/ + + + + +void rms_type_free(void *rms_t) { + free( (__rms_type *) rms_t); +} + + +static __rms_type * rms_type_set(__rms_type *rms_t , rms_type_enum rms_type , int sizeof_ctype) { + rms_t->rms_type = rms_type; + rms_t->sizeof_ctype = sizeof_ctype; + return rms_t; +} + + +__rms_type * rms_type_alloc(rms_type_enum rms_type, int sizeof_ctype) { + __rms_type *rms_t = (__rms_type*)malloc(sizeof *rms_t); + rms_type_set(rms_t , rms_type , sizeof_ctype); + return rms_t; +} diff --git a/libres/lib/rms/rms_util.cpp b/libres/lib/rms/rms_util.cpp new file mode 100644 index 00000000000..44e9322c94c --- /dev/null +++ b/libres/lib/rms/rms_util.cpp @@ -0,0 +1,139 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + + The file 'rms_util.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 + + +/* + This translates from the RMS data layout to "Fortan / ECLIPSE" data + layout. + + RMS: k index is running fastest *AND* backwards. + F90: i is running fastest, and k is running the 'normal' way. + + This function should be *THE ONLY* place in the code where explicit mention + is made to the RMS ordering sequence. +*/ + + +int rms_util_global_index_from_eclipse_ijk(int nx, int ny , int nz , int i , int j , int k) { + return i*ny*nz + j*nz + (nz - k - 1); +} + + +void rms_util_translate_undef(void * _data , int size , int sizeof_ctype , const void * old_undef , const void * new_undef) { + char * data = (char *) _data; + int i; + for (i=0; i < size; i++) { + if (memcmp( &data[i*sizeof_ctype] , old_undef , sizeof_ctype) == 0) + memcpy( &data[i*sizeof_ctype] , new_undef , sizeof_ctype); + } +} + + +void rms_util_fskip_string(FILE *stream) { + char c; + bool cont = true; + while (cont) { + fread(&c , 1 , 1 , stream); + if (c == 0) + cont = false; + } +} + + +int rms_util_fread_strlen(FILE *stream) { + long int init_pos = util_ftell(stream); + int len; + rms_util_fskip_string(stream); + len = util_ftell(stream) - init_pos; + fseek(stream , init_pos , SEEK_SET); + return len; +} + + +/* + max_length *includes* the trailing \0. +*/ +bool rms_util_fread_string(char *string , int max_length , FILE *stream) { + bool read_ok = true; + bool cont = true; + long int init_pos = util_ftell(stream); + int pos = 0; + while (cont) { + fread(&string[pos] , sizeof *string , 1 , stream); + if (string[pos] == 0) { + read_ok = true; + cont = false; + } else { + pos++; + if (max_length > 0) { + if (pos == max_length) { + read_ok = false; + fseek(stream , init_pos , SEEK_SET); + cont = false; + } + } + } + } + + return read_ok; +} + + +void rms_util_fwrite_string(const char * string , FILE *stream) { + fwrite(string , sizeof * string , strlen(string) , stream); + fputc('\0' , stream); +} + +void rms_util_fwrite_comment(const char * comment , FILE *stream) { + fputc('#' , stream); + fwrite(comment , sizeof * comment , strlen(comment) , stream); + fputc('#' , stream); + fputc('\0' , stream); +} + + +void rms_util_fwrite_newline(FILE *stream) { + return; +} + + +rms_type_enum rms_util_convert_ecl_type(ecl_data_type data_type) { + rms_type_enum rms_type = rms_int_type; /* Shut up the compiler */ + switch (ecl_type_get_type(data_type)) { + case(ECL_INT_TYPE): + rms_type = rms_int_type; + break; + case(ECL_FLOAT_TYPE): + rms_type = rms_float_type; + break; + case(ECL_DOUBLE_TYPE): + rms_type = rms_double_type; + break; + default: + util_abort("%s: Conversion ecl_type -> rms_type not supported for ecl_type:%s \n",__func__ , ecl_type_alloc_name(data_type)); + } + return rms_type; +} diff --git a/libres/lib/rms/tests/rms_file_test.cpp b/libres/lib/rms/tests/rms_file_test.cpp new file mode 100644 index 00000000000..48dd76fb5b7 --- /dev/null +++ b/libres/lib/rms/tests/rms_file_test.cpp @@ -0,0 +1,53 @@ +/* + Copyright (C) 2014 Equinor ASA, Norway. + + The file 'rms_file_test.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 + + + +void test_rms_file_fread_alloc_data_tag(rms_file_type * rms_file) { + rms_tag_type * parameter_tag = rms_file_fread_alloc_tag(rms_file , "parameter" , NULL , NULL); + test_assert_not_NULL(parameter_tag); + test_assert_string_equal("parameter", rms_tag_get_name(parameter_tag)); + rms_tag_free(parameter_tag); +} + + +void test_rms_file_fread_alloc_data_tagkey(rms_file_type *rms_file) { + rms_tagkey_type * name_tagkey = rms_file_fread_alloc_data_tagkey(rms_file , "parameter" , NULL , NULL); + test_assert_not_NULL(name_tagkey); + test_assert_int_equal(rms_float_type, rms_tagkey_get_rms_type(name_tagkey)); + rms_tagkey_free(name_tagkey); +} + + +int main(int argc , char ** argv) { + const char * filename = argv[1]; + rms_file_type * rms_file = rms_file_alloc(filename , false); + test_assert_not_NULL(rms_file); + + test_rms_file_fread_alloc_data_tag(rms_file); + test_rms_file_fread_alloc_data_tagkey(rms_file); + + rms_file_free(rms_file); + exit(0); +} diff --git a/libres/lib/sched/history.cpp b/libres/lib/sched/history.cpp new file mode 100644 index 00000000000..ec4beebbb44 --- /dev/null +++ b/libres/lib/sched/history.cpp @@ -0,0 +1,195 @@ +/* + Copyright (C) 2011 Equinor ASA, Norway. + The file 'history.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 + + +#define HISTORY_TYPE_ID 66143109 + +struct history_struct{ + UTIL_TYPE_ID_DECLARATION; + const ecl_sum_type * refcase; /* ecl_sum instance used when the data are taken from a summary instance. Observe that this is NOT owned by history instance.*/ + history_source_type source; +}; + + +history_source_type history_get_source_type( const char * string_source ) { + history_source_type source_type = HISTORY_SOURCE_INVALID; + + if (strcmp( string_source , "REFCASE_SIMULATED") == 0) + source_type = REFCASE_SIMULATED; + else if (strcmp( string_source , "REFCASE_HISTORY") == 0) + source_type = REFCASE_HISTORY; + else + util_abort("%s: Sorry source:%s not recognized\n",__func__ , string_source); + + return source_type; +} + + +const char * history_get_source_string( history_source_type history_source ) { + switch( history_source ) { + case( REFCASE_SIMULATED ): + return "REFCASE_SIMULATED"; + break; + case(REFCASE_HISTORY ): + return "REFCASE_HISTORY"; + break; + default: + util_abort("%s: Internal inconsistency in refcase \n",__func__); + return NULL; + } +} + + +UTIL_IS_INSTANCE_FUNCTION( history , HISTORY_TYPE_ID ) + + +static history_type * history_alloc_empty( ) +{ + history_type * history = (history_type*)util_malloc(sizeof * history); + UTIL_TYPE_ID_INIT( history , HISTORY_TYPE_ID ); + history->refcase = NULL; + return history; +} + + + +/******************************************************************/ +// Exported functions for manipulating history_type. Acess functions further below. + + +void history_free(history_type * history) +{ + free(history); +} + + +history_type * history_alloc_from_refcase(const ecl_sum_type * refcase , bool use_h_keywords) { + history_type * history = history_alloc_empty( ); + + history->refcase = refcase; /* This function does not really do anthing - it just sets the ecl_sum field of the history instance. */ + if (use_h_keywords) + history->source = REFCASE_HISTORY; + else + history->source = REFCASE_SIMULATED; + + return history; +} + + +/******************************************************************/ +// Exported functions for accessing history_type. + + +history_source_type history_get_source(const history_type * history) { + return history->source; +} + + +int history_get_last_restart(const history_type * history) { + return ecl_sum_get_last_report_step( history->refcase); +} + + +bool history_init_ts( const history_type * history , const char * summary_key , double_vector_type * value, bool_vector_type * valid) { + bool initOK = false; + + double_vector_reset( value ); + bool_vector_reset( valid ); + bool_vector_set_default( valid , false); + + char * local_key; + if (history->source == REFCASE_HISTORY) { + /* Must create a new key with 'H' for historical values. */ + const ecl_smspec_type * smspec = ecl_sum_get_smspec( history->refcase ); + const char * join_string = ecl_smspec_get_join_string( smspec ); + ecl_smspec_var_type var_type = ecl_smspec_identify_var_type( summary_key ); + + if ((var_type == ECL_SMSPEC_WELL_VAR) || (var_type == ECL_SMSPEC_GROUP_VAR)) + local_key = util_alloc_sprintf( "%sH%s%s" , + ecl_sum_get_keyword( history->refcase , summary_key ) , + join_string , + ecl_sum_get_wgname( history->refcase , summary_key )); + else if (var_type == ECL_SMSPEC_FIELD_VAR) + local_key = util_alloc_sprintf( "%sH" , ecl_sum_get_keyword( history->refcase , summary_key )); + else + local_key = NULL; // If we try to get historical values of e.g. Region quantities it will fail. + } else + local_key = (char *) summary_key; + + if (local_key) { + if (ecl_sum_has_general_var( history->refcase , local_key )) { + for (int tstep = 0; tstep <= history_get_last_restart(history); tstep++) { + if (ecl_sum_has_report_step(history->refcase, tstep)) { + int time_index = ecl_sum_iget_report_end( history->refcase , tstep ); + double_vector_iset( value , tstep , ecl_sum_get_general_var( history->refcase , time_index , local_key )); + bool_vector_iset( valid , tstep , true ); + } else + bool_vector_iset( valid , tstep , false ); /* Did not have this report step */ + } + initOK = true; + } + + if (history->source == REFCASE_HISTORY) + free( local_key ); + } + return initOK; +} + + +time_t history_get_start_time( const history_type * history ) { + return ecl_sum_get_start_time( history->refcase ); +} + +/* Uncertain about the first node - offset problems +++ ?? + Changed to use node_end_time() at svn ~ 2850 + + Changed to sched_history at svn ~2940 +*/ +time_t history_get_time_t_from_restart_nr( const history_type * history , int restart_nr) { + if (restart_nr == 0) + return ecl_sum_get_start_time( history->refcase ); + else + return ecl_sum_get_report_time( history->refcase , restart_nr ); +} + + +int history_get_restart_nr_from_time_t( const history_type * history , time_t time) { + if (time == history_get_start_time( history )) + return 0; + else { + int report_step = ecl_sum_get_report_step_from_time( history->refcase , time ); + if (report_step >= 1) + return report_step; + else { + int mday,year,month; + util_set_date_values_utc( time , &mday , &month , &year); + util_abort("%s: Date: %02d/%02d/%04d does not cooincide with any report time. Aborting.\n", __func__ , mday , month , year); + return -1; + } + } +} diff --git a/libres/lib/sched/tests/sched_history_summary.cpp b/libres/lib/sched/tests/sched_history_summary.cpp new file mode 100644 index 00000000000..33cff72ab01 --- /dev/null +++ b/libres/lib/sched/tests/sched_history_summary.cpp @@ -0,0 +1,70 @@ +/* + Copyright (C) 2013 Equinor ASA, Norway. + + The file 'sched_history_summary.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 + + + +int main(int argc, char **argv) { + char * sum_case = argv[1]; + ecl_sum_type * refcase = ecl_sum_fread_alloc_case( sum_case , ":" ); + history_type * hist_h = history_alloc_from_refcase( refcase , true ); + history_type * hist_sim = history_alloc_from_refcase( refcase , false ); + + test_assert_true( history_is_instance( hist_h ) ); + test_assert_true( history_is_instance( hist_sim ) ); + test_assert_int_equal( history_get_last_restart( hist_sim ) , ecl_sum_get_last_report_step( refcase ) ); + test_assert_int_equal( history_get_last_restart( hist_h ) , ecl_sum_get_last_report_step( refcase ) ); + + { + double_vector_type * value_sim = double_vector_alloc(0 , 0); + double_vector_type * value_h = double_vector_alloc(0 , 0); + bool_vector_type * valid_sim = bool_vector_alloc( 0 , false ); + bool_vector_type * valid_h = bool_vector_alloc( 0 , false ); + + test_assert_true( history_init_ts( hist_sim , "FOPT" , value_sim , valid_sim )); + test_assert_true( history_init_ts( hist_h , "FOPT" , value_h , valid_h )); + { + int step; + for (step = 1; step < ecl_sum_get_last_report_step( refcase ); step++) { + test_assert_true( bool_vector_iget( valid_sim , step )); + test_assert_true( bool_vector_iget( valid_h , step )); + { + int time_index = ecl_sum_iget_report_end( refcase , step ); + test_assert_double_equal( ecl_sum_get_general_var( refcase , time_index , "FOPT" ) , double_vector_iget( value_sim , step )); + test_assert_double_equal( ecl_sum_get_general_var( refcase , time_index , "FOPTH" ) , double_vector_iget( value_h , step )); + } + } + } + bool_vector_free( valid_sim ); + bool_vector_free( valid_h ); + + double_vector_free( value_sim ); + double_vector_free( value_h ); + } + + + history_free( hist_h ); + history_free( hist_sim ); + ecl_sum_free( refcase ); + exit(0); +} diff --git a/libres/test-data/README.txt b/libres/test-data/README.txt new file mode 100644 index 00000000000..182de174d36 --- /dev/null +++ b/libres/test-data/README.txt @@ -0,0 +1,12 @@ +This directory is meant as a holding place for test data for the ERT +project. The local/ directory should contain test-data which is +checked in an distributed as part of the solution. + +In addition many of the tests expect to find a Equinor/ subdirectory +in the current directory. This directory should link to Equinor +internal test data. This data is currently located on the enkf disk in +Bergen; before you can start using the Equinor specific test data you +must add the following symlink: + + ln -s /ErtTestData Equinor + diff --git a/libres/test-data/local/batch_sim/batch_sim.ert b/libres/test-data/local/batch_sim/batch_sim.ert new file mode 100644 index 00000000000..c43a6396d19 --- /dev/null +++ b/libres/test-data/local/batch_sim/batch_sim.ert @@ -0,0 +1,21 @@ +QUEUE_SYSTEM LOCAL +QUEUE_OPTION LOCAL MAX_RUNNING 2 + +NUM_REALIZATIONS 25 + +DEFINE storage/ + +RUNPATH /runpath//realisation-%d/iter-%d +ENSPATH /ensemble +JOBNAME SNAKE_OIL_FIELD + +INSTALL_JOB SQUARE_PARAMS jobs/SQUARE_PARAMS + +LOAD_WORKFLOW workflows/REALIZATION_NUMBER_WORKFLOW +HOOK_WORKFLOW REALIZATION_NUMBER_WORKFLOW PRE_SIMULATION +LOAD_WORKFLOW_JOB workflows/jobs/REALIZATION_NUMBER + +FORWARD_MODEL SQUARE_PARAMS +LOG_LEVEL INFO + + diff --git a/libres/test-data/local/batch_sim/batch_sim_sleep_and_fail.ert b/libres/test-data/local/batch_sim/batch_sim_sleep_and_fail.ert new file mode 100644 index 00000000000..728f680c040 --- /dev/null +++ b/libres/test-data/local/batch_sim/batch_sim_sleep_and_fail.ert @@ -0,0 +1,15 @@ +QUEUE_SYSTEM LOCAL +QUEUE_OPTION LOCAL MAX_RUNNING 10 + +NUM_REALIZATIONS 10 + +RUNPATH runpath/realisation-%d/iter-%d +JOBNAME SNAKE_OIL_FIELD + +INSTALL_JOB SQUARE_PARAMS jobs/SQUARE_PARAMS +INSTALL_JOB SLEEP jobs/SLEEP +INSTALL_JOB ODD_FAIL jobs/ODD_FAIL + +SIMULATION_JOB SQUARE_PARAMS +SIMULATION_JOB SLEEP +SIMULATION_JOB ODD_FAIL diff --git a/libres/test-data/local/batch_sim/jobs/ODD_FAIL b/libres/test-data/local/batch_sim/jobs/ODD_FAIL new file mode 100644 index 00000000000..cbfbf0d58c9 --- /dev/null +++ b/libres/test-data/local/batch_sim/jobs/ODD_FAIL @@ -0,0 +1 @@ +EXECUTABLE odd_fail.py diff --git a/libres/test-data/local/batch_sim/jobs/SLEEP b/libres/test-data/local/batch_sim/jobs/SLEEP new file mode 100644 index 00000000000..f86ab647834 --- /dev/null +++ b/libres/test-data/local/batch_sim/jobs/SLEEP @@ -0,0 +1 @@ +EXECUTABLE super_sleep.sh diff --git a/libres/test-data/local/batch_sim/jobs/SQUARE_PARAMS b/libres/test-data/local/batch_sim/jobs/SQUARE_PARAMS new file mode 100644 index 00000000000..fcd6402c70d --- /dev/null +++ b/libres/test-data/local/batch_sim/jobs/SQUARE_PARAMS @@ -0,0 +1,4 @@ +STDOUT square_params.stdout +STDERR square_params.stderr + +EXECUTABLE square_params.py \ No newline at end of file diff --git a/libres/test-data/local/batch_sim/jobs/odd_fail.py b/libres/test-data/local/batch_sim/jobs/odd_fail.py new file mode 100755 index 00000000000..0fc9648a728 --- /dev/null +++ b/libres/test-data/local/batch_sim/jobs/odd_fail.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python + +import sys + +if __name__ == "__main__": + iens = int(sys.argv[1]) + sys.exit(iens % 2) diff --git a/libres/test-data/local/batch_sim/jobs/square_params.py b/libres/test-data/local/batch_sim/jobs/square_params.py new file mode 100644 index 00000000000..7a6cfe294cd --- /dev/null +++ b/libres/test-data/local/batch_sim/jobs/square_params.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python +import sys +import json +import random +import time + +keys = ["W1", "W2", "W3"] + + +def square_file(input_file, output_file): + with open(input_file) as fin: + data0 = json.load(fin) + out = [] + for key in keys: + data1 = data0[key] + try: + out.extend([str(v ** 2) for _, v in data1.items()]) + except AttributeError: + out.append(str(data1 ** 2)) + with open(output_file, "w") as fout: + fout.write("\n".join(out)) + + +if __name__ == "__main__": + square_file("WELL_ORDER.json", "ORDER_0") + time.sleep(random.randint(0, 2)) + square_file("WELL_ON_OFF.json", "ON_OFF_0") + time.sleep(random.randint(0, 2)) diff --git a/libres/test-data/local/batch_sim/jobs/super_sleep.sh b/libres/test-data/local/batch_sim/jobs/super_sleep.sh new file mode 100755 index 00000000000..52c5ca5ada7 --- /dev/null +++ b/libres/test-data/local/batch_sim/jobs/super_sleep.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +let sleep_interval=$1/2 +sleep $sleep_interval diff --git a/libres/test-data/local/batch_sim/sleepy_time.ert b/libres/test-data/local/batch_sim/sleepy_time.ert new file mode 100644 index 00000000000..d385b6971dc --- /dev/null +++ b/libres/test-data/local/batch_sim/sleepy_time.ert @@ -0,0 +1,11 @@ +QUEUE_SYSTEM LOCAL +QUEUE_OPTION LOCAL MAX_RUNNING 10 + +NUM_REALIZATIONS 10 + +RUNPATH runpath/realisation-%d/iter-%d +JOBNAME OLE_LUKKOYE + +INSTALL_JOB SLEEP jobs/SLEEP + +SIMULATION_JOB SLEEP 2 diff --git a/libres/test-data/local/batch_sim/workflows/REALIZATION_NUMBER_WORKFLOW b/libres/test-data/local/batch_sim/workflows/REALIZATION_NUMBER_WORKFLOW new file mode 100644 index 00000000000..2d2c67a2a18 --- /dev/null +++ b/libres/test-data/local/batch_sim/workflows/REALIZATION_NUMBER_WORKFLOW @@ -0,0 +1 @@ +REALIZATION_NUMBER diff --git a/libres/test-data/local/batch_sim/workflows/jobs/REALIZATION_NUMBER b/libres/test-data/local/batch_sim/workflows/jobs/REALIZATION_NUMBER new file mode 100644 index 00000000000..6b92b1dc25b --- /dev/null +++ b/libres/test-data/local/batch_sim/workflows/jobs/REALIZATION_NUMBER @@ -0,0 +1,4 @@ +INTERNAL FALSE +EXECUTABLE realization_number.py +MIN_ARG 1 +MAX_ARG 1 diff --git a/libres/test-data/local/batch_sim/workflows/jobs/realization_number.py b/libres/test-data/local/batch_sim/workflows/jobs/realization_number.py new file mode 100755 index 00000000000..1d8b37ea14a --- /dev/null +++ b/libres/test-data/local/batch_sim/workflows/jobs/realization_number.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +import os.path +import argparse +import re + +TARGET_FILE = "realization.number" +REGEX = "realisation-(\d+)" + + +def add_file_to_realization_runpaths(runpath_file): + with open(runpath_file, "r") as fh: + runpath_file_lines = fh.readlines() + + for line in runpath_file_lines: + realization_path = line.split()[1] + with open(os.path.join(realization_path, TARGET_FILE), "w") as fh: + realization_nr = re.findall(r"realisation-(\d+)", realization_path) + fh.write("{}\n".format(realization_nr[0])) + + +def job_parser(): + description = """A workflow job that, if the komodo version is set, adds a file with the current Komodo version to the runpath of each realization.""" + parser = argparse.ArgumentParser(description=description) + parser.add_argument( + "runpath_file", + type=str, + help="Path to the file containing the runpath of all the realizations.", + ) + return parser + + +if __name__ == "__main__": + parser = job_parser() + args = parser.parse_args() + add_file_to_realization_runpaths(args.runpath_file) diff --git a/libres/test-data/local/config/gen_data/REFCASE.SMSPEC b/libres/test-data/local/config/gen_data/REFCASE.SMSPEC new file mode 100644 index 00000000000..04373c55f97 Binary files /dev/null and b/libres/test-data/local/config/gen_data/REFCASE.SMSPEC differ diff --git a/libres/test-data/local/config/gen_data/REFCASE.UNSMRY b/libres/test-data/local/config/gen_data/REFCASE.UNSMRY new file mode 100644 index 00000000000..426c9beb6cb Binary files /dev/null and b/libres/test-data/local/config/gen_data/REFCASE.UNSMRY differ diff --git a/libres/test-data/local/config/gen_data/config b/libres/test-data/local/config/gen_data/config new file mode 100644 index 00000000000..64f533aca19 --- /dev/null +++ b/libres/test-data/local/config/gen_data/config @@ -0,0 +1,8 @@ +JOBNAME Job%d +RUNPATH /tmp/simulations/run%d +NUM_REALIZATIONS 1 + +GEN_DATA DATA INPUT_FORMAT:ASCII RESULT_FILE:Result%d REPORT_STEPS:10,20 + +OBS_CONFIG observations +REFCASE REFCASE \ No newline at end of file diff --git a/libres/test-data/local/config/gen_data/gen_obs_data b/libres/test-data/local/config/gen_data/gen_obs_data new file mode 100644 index 00000000000..94ebaf90016 --- /dev/null +++ b/libres/test-data/local/config/gen_data/gen_obs_data @@ -0,0 +1,4 @@ +1 +2 +3 +4 diff --git a/libres/test-data/local/config/gen_data/observations b/libres/test-data/local/config/gen_data/observations new file mode 100644 index 00000000000..28b4e99cd21 --- /dev/null +++ b/libres/test-data/local/config/gen_data/observations @@ -0,0 +1,19 @@ +GENERAL_OBSERVATION GEN_OBS10 { + DATA = DATA; + RESTART = 10; + OBS_FILE = "gen_obs_data"; +}; + + +GENERAL_OBSERVATION GEN_OBS20 { + DATA = DATA; + RESTART = 20; + OBS_FILE = "gen_obs_data"; +}; + + +GENERAL_OBSERVATION GEN_OBS30 { + DATA = DATA; + RESTART = 30; + OBS_FILE = "gen_obs_data"; +}; diff --git a/libres/test-data/local/config/simple_config b/libres/test-data/local/config/simple_config new file mode 100644 index 00000000000..b1edca7fc21 --- /dev/null +++ b/libres/test-data/local/config/simple_config @@ -0,0 +1,183 @@ +QUEUE_OPTION LSF LSF_LOGIN_SHELL /bin/csh + +DEFINE __NAME__ EXAMPLE_01_BASE +DEFINE __INCLUDE_PATH__ /private/joaho/ERT/git/ert/Gurbat +DEFINE __GRID__ __NAME__.EGRID +DEFINE MULTIR_FILE MULTIR.INC + +JOB_SCRIPT /private/joaho/ERT/Equinor/etc/ERT/Scripts/job_dispatch.py + + +-- The ECLIPSE data files which are used as init. + +GRID __GRID__ +SCHEDULE_FILE target.SCH + + +--SCHEDULE_PREDICTION_FILE prediction.sch + +INSTALL_JOB RMS_BATCH /private/joaho/ERT/Equinor/etc/ERT/Config/jobs/resmod/RMS_BATCH_TEST +INSTALL_JOB ECLIPSE_TEST ECLIPSE_TEST +INSTALL_JOB AVGP jobs/AVGP +--INSTALL_JOB RMS_TEST /private/joaho/ERT/Equinor/etc/ERT/Config/jobs/resmod/RMS_BATCH +SETENV MANY_PATH $PATH:$LD_LIBRARY_PATH:$MANPATH:/lib + + +--SCHEDULE_PREDICTION_FILE prediction2.schi + +DATA_FILE Example_01_base.data + +LICENSE_PATH /tmp/BJARNE_PATH + +--SETENV ERT_LINK_LSF True +--LSF_SERVER be-grid01 + +-- Where do you want to store things + + +RUNPATH /scratch/ert/joaho/Run1/Xrun%d + + + + + +ENSPATH /tmp/Gurbat/104BlockFS +ECLBASE ECL_%04d + +-- How to simulate + + +INSTALL_JOB PVT jobs/PVT +INSTALL_JOB NULL jobs/NULL + +INSTALL_JOB RMS_TEST /private/joaho/ERT/Equinor/etc/ERT/Config/jobs/resmod/RUN_RMS_20XX +INSTALL_JOB PRESSURE33 jobs/Pressure33 + +--FORWARD_MODEL RMS_BATCH(=2012.0.1, =/rms/rms_project_2012 , =MAIN_WORKFLOW) +FORWARD_MODEL ECLIPSE100_2009.1 +--FORWARD_MODEL PRESSURE33 +--FORWARD_MODEL RMS_TEST( = , = , = 2011.0.2, =/rms/rms_project_2012 , =/d/proj/bg/ior_fsenter/GRM_TDP/Gimle/work/tfen/r0004/models/eclipse/refcase/, =Forward_Seismic,=SEISMIC_FORWARD_TARGET.INC) + +--FORWARD_MODEL AVGP +--FORWARD_MODEL NULL + + +UMASK 0 +MAX_SUBMIT 10 +MAX_RESAMPLE 1 + +-- The ensemble +NUM_REALIZATIONS 25 + +IMAGE_TYPE png + +RUN_TEMPLATE Template1 File1 File:1 +RUN_TEMPLATE Template1 File2 File:2 + + +INSTALL_JOB PRESSURE100 PRESSURE100 + + +GEN_KW MULTFLT FAULT_TEMPLATE MULTFLT.INC Config/MULTFLT.txt +GEN_KW GRID_PARAMS GRID_PARAM_TEMPLATE GRID_PARAMS.INC Config/GRID_PARAMS.txt --MIN_STD:Config/GRID_PARAMS.min_std +GEN_KW FLUID_PARAMS FLUID_PARAM_TEMPLATE FLUID_PARAMS.INC Config/FLUID_PARAMS.txt --MIN_STD:Config/FLUID_PARAMS.min_std + +GEN_PARAM GP GP.txt INIT_FILES:GP/GP.txt INPUT_FORMAT:ASCII OUTPUT_FORMAT:ASCII + + + + +ENKF_RERUN FALSE +RERUN_START 0 + +INSTALL_JOB SGOF_TAB jobs/SGOF_TAB + + + +ENKF_MERGE_OBSERVATIONS TRUE +--ENKF_SCHED_FILE sched_config + + +ENKF_ALPHA 1.50 +----------------------------------------------------------------- +IMAGE_VIEWER /tmp/echo.sh +IMAGE_VIEWER /usr/bin/display + + +QUEUE_SYSTEM LSF +QUEUE_SYSTEM LOCAL +QUEUE_SYSTEM RSH + +--LSF_QUEUE SUPERKO + +RSH_HOST be-lx655082:2 be-lx633214:2 + + + +LOG_LEVEL INFO +LOG_FILE log.txt +UPDATE_LOG_PATH UP + +KEEP_RUNPATH 0 - 9 +DATA_KW __INCLUDE_PATH__ +DATA_KW WhatEver SomeThing + + + + +-- Quantities to estimate on + +HISTORY_SOURCE REFCASE_HISTORY +REFCASE __NAME__.DATA + +--EQUIL EQUIL EQUIL.INC equil_config + +RUN_TEMPLATE GEN_DATA_TEMPLATE GEN_DATA_FILE +--GEN_DATA PRESSURE100 INPUT_FORMAT:ASCII RESULT_FILE:PRESSURE%04d + + + +--ANALYSIS_LOAD RML_ENKF /project/res/x86_64_RH_5/lib/rml_enkf.so +--ANALYSIS_LOAD RML_ENKF rml_enkf.so +ANALYSIS_LOAD RML_ENKF /private/joaho/ERT/NR/libanalysis/src/rml_enkf.so + +--ANALYSIS_LOAD FWD_ENKF /private/joaho/ERT/NR/tmp-build/libanalysis/src/sqrt_enkf.so +--ANALYSIS_LOAD SFWD_ENKF /private/joaho/ERT/NR/tmp-build/libanalysis/src/std_enkf.so +ANALYSIS_SET_VAR STD_ENKF ENKF_TRUNCATION 0.95 +--ANALYSIS_SET_VAR SQRT_ENKF ENKF_TRUNCATION 0.99 +ANALYSIS_SET_VAR STD_ENKF ENKF_NCOMP 2 +ANALYSIS_SET_VAR STD_ENKF ENKF_TRUNCATION 0.98 +ANALYSIS_SELECT STD_ENKF + +ANALYSIS_LOAD TEST_ENKF /private/joaho/ERT/NR/libanalysis/src/test_enkf.so + + +QUEUE_OPTION LSF LSF_LOGIN_SHELL /bin/csh + + +FIELD PRESSURE DYNAMIC +FIELD SWAT DYNAMIC MIN:0 MAX:1 +FIELD SGAS DYNAMIC MIN:0 MAX:1 +FIELD RS DYNAMIC MIN:0 +FIELD RV DYNAMIC MIN:0.0034 + +--CONTAINER CXX PRESSURE SWAT + + +--SURFACE TOP OUTPUT_FILE:surf.irap INIT_FILES:Surfaces/d_BCU_%d.irap BASE_SURFACE:Surfaces/d_BCU_0.irap + +-- Observations +OBS_CONFIG observations + + +--SUMMARY F* RPR:* + +PLOT_PATH plots + + +IMAGE_VIEWER /tmp/noplot.sh + +----------------------------------------------------------------- + +WORKFLOW_JOB_DIRECTORY WorkflowJobs +LOAD_WORKFLOW Workflows/PLOT_AND_EXIT diff --git a/libres/test-data/local/config/simulation_batch/config.ert b/libres/test-data/local/config/simulation_batch/config.ert new file mode 100644 index 00000000000..3ef09ded640 --- /dev/null +++ b/libres/test-data/local/config/simulation_batch/config.ert @@ -0,0 +1,14 @@ +NUM_REALIZATIONS 2 +QUEUE_SYSTEM LOCAL +JOBNAME EVEREST_%d + +INSTALL_JOB COPY_EXT_PARAM jobs/COPY_EXT_PARAM + +FORWARD_MODEL COPY_EXT_PARAM +LOG_LEVEL INFO + + +-- This datafile is used in a test where the GEN_DATA and EXT_PARAM +-- nodes are added programatically from the test code. Observe that +-- significant hardcoded assumptions exist for this config file, the +-- forward model code and the test code. \ No newline at end of file diff --git a/libres/test-data/local/config/simulation_batch/jobs/COPY_EXT_PARAM b/libres/test-data/local/config/simulation_batch/jobs/COPY_EXT_PARAM new file mode 100644 index 00000000000..c6e965c5e8e --- /dev/null +++ b/libres/test-data/local/config/simulation_batch/jobs/COPY_EXT_PARAM @@ -0,0 +1,3 @@ +EXECUTABLE copy_ext_param_script +STDERR copy_ext_param.stderr +STDOUT copy_ext_param.stdout diff --git a/libres/test-data/local/config/simulation_batch/jobs/copy_ext_param_script b/libres/test-data/local/config/simulation_batch/jobs/copy_ext_param_script new file mode 100755 index 00000000000..ed061b9d615 --- /dev/null +++ b/libres/test-data/local/config/simulation_batch/jobs/copy_ext_param_script @@ -0,0 +1,18 @@ +#!/usr/bin/env python +import sys +import json + +# This forward model job is quite funny, and hardcodes significant +# interaction with the the EXT_PARAM and GEN_DATA nodes added in the +# test_everest.py test. + +order = json.load(open("WELL_ORDER.json")) +inj = json.load(open("WELL_INJECTION.json")) + +with open("order_0", "w") as f: + for key in ["W1", "W2", "W3"]: + f.write("%g\n" % order[key]) + +with open("injection_0", "w") as f: + for key in ["W1", "W4"]: + f.write("%g\n" % inj[key]) diff --git a/libres/test-data/local/config/workflows/config b/libres/test-data/local/config/workflows/config new file mode 100644 index 00000000000..7ea9275d834 --- /dev/null +++ b/libres/test-data/local/config/workflows/config @@ -0,0 +1,10 @@ +DEFINE __NAME__ EXAMPLE_01_BASE +DEFINE __GRID__ __NAME__.EGRID +DEFINE MULTIR_FILE MULTIR.INC + + +DEFINE __MAGIC__ MagicAllTheWayToWorkFlow +NUM_REALIZATIONS 25 +LOAD_WORKFLOW_JOB workflowjobs/MAGIC_PRINT +LOAD_WORKFLOW workflows/MAGIC_PRINT +DATA_KW $PWD/include diff --git a/libres/test-data/local/config/workflows/workflowjobs/MAGIC_PRINT b/libres/test-data/local/config/workflows/workflowjobs/MAGIC_PRINT new file mode 100644 index 00000000000..d57a26a37d4 --- /dev/null +++ b/libres/test-data/local/config/workflows/workflowjobs/MAGIC_PRINT @@ -0,0 +1,3 @@ +INTERNAL False +EXECUTABLE bin/magic_print.py +MIN_ARG 1 diff --git a/libres/test-data/local/config/workflows/workflowjobs/bin/magic_print.py b/libres/test-data/local/config/workflows/workflowjobs/bin/magic_print.py new file mode 100755 index 00000000000..8d13045a8a6 --- /dev/null +++ b/libres/test-data/local/config/workflows/workflowjobs/bin/magic_print.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python +import sys + +fileH = open(sys.argv[1], "w") +for arg in sys.argv[2:]: + fileH.write("%s\n" % arg) +fileH.close() diff --git a/libres/test-data/local/config/workflows/workflows/MAGIC_PRINT b/libres/test-data/local/config/workflows/workflows/MAGIC_PRINT new file mode 100644 index 00000000000..2d64d86805a --- /dev/null +++ b/libres/test-data/local/config/workflows/workflows/MAGIC_PRINT @@ -0,0 +1 @@ +MAGIC_PRINT magic-list.txt __MAGIC__ diff --git a/libres/test-data/local/configuration_tests/FAULT_TEMPLATE b/libres/test-data/local/configuration_tests/FAULT_TEMPLATE new file mode 100644 index 00000000000..de1f12ca5b5 --- /dev/null +++ b/libres/test-data/local/configuration_tests/FAULT_TEMPLATE @@ -0,0 +1 @@ + diff --git a/libres/test-data/local/configuration_tests/MULTFLT.TXT b/libres/test-data/local/configuration_tests/MULTFLT.TXT new file mode 100644 index 00000000000..c193c485289 --- /dev/null +++ b/libres/test-data/local/configuration_tests/MULTFLT.TXT @@ -0,0 +1 @@ +MULTFLT NORMAL 0 1 diff --git a/libres/test-data/local/configuration_tests/ecl_config.ert b/libres/test-data/local/configuration_tests/ecl_config.ert new file mode 100644 index 00000000000..189f37fc5d0 --- /dev/null +++ b/libres/test-data/local/configuration_tests/ecl_config.ert @@ -0,0 +1,17 @@ +ECLBASE input/-%d +DATA_FILE input/SPE1.DATA +REFCASE input/SNAKE_OIL_FIELD +GRID input/CASE.EGRID +END_DATE 10/10/2010 +SCHEDULE_PREDICTION_FILE input/schedule.sch + +RUNPATH /tmp/simulations/run%d +NUM_REALIZATIONS 10 +MIN_REALIZATIONS 10 + +QUEUE_SYSTEM LOCAL +QUEUE_OPTION LOCAL MAX_RUNNING 50 + +-- This is not strictly necessary, a sensible default will be used if not given. +ENSPATH Ensemble + diff --git a/libres/test-data/local/configuration_tests/ensemble_config.ert b/libres/test-data/local/configuration_tests/ensemble_config.ert new file mode 100644 index 00000000000..cd3fa139d0d --- /dev/null +++ b/libres/test-data/local/configuration_tests/ensemble_config.ert @@ -0,0 +1,23 @@ +JOBNAME Job%d +RUNPATH /tmp/simulations/run%d +NUM_REALIZATIONS 10 +MIN_REALIZATIONS 10 + +GEN_PARAM GP GP.txt INIT_FILES:GP/GP.txt INPUT_FORMAT:ASCII OUTPUT_FORMAT:ASCII + +GEN_DATA SNAKE_OIL_OPR_DIFF INPUT_FORMAT:ASCII RESULT_FILE:snake_oil_opr_diff_%d.txt REPORT_STEPS:199 + +GEN_DATA SNAKE_OIL_GPR_DIFF INPUT_FORMAT:ASCII RESULT_FILE:snake_oil_gpr_diff_%d.txt REPORT_STEPS:199 + +GEN_KW MULTFLT FAULT_TEMPLATE MULTFLT.INC MULTFLT.TXT + +SURFACE TOP INIT_FILES:configuration_tests/surface/small.irap OUTPUT_FILE:configuration_tests/surface/small_out.irap BASE_SURFACE:configuration_tests/surface/small.irap + +GRID grid/CASE.EGRID + +FIELD PERMX PARAMETER permx.grdcel INIT_FILES:fields/permx%d.grdecl + +SCHEDULE_PREDICTION_FILE input/schedule.sch +CONTAINER CXX PERMX MULTFLT + +SUMMARY WOPR:OP_1 diff --git a/libres/test-data/local/configuration_tests/grid/CASE.EGRID b/libres/test-data/local/configuration_tests/grid/CASE.EGRID new file mode 100644 index 00000000000..33da9f7a70e Binary files /dev/null and b/libres/test-data/local/configuration_tests/grid/CASE.EGRID differ diff --git a/libres/test-data/local/configuration_tests/input/CASE.EGRID b/libres/test-data/local/configuration_tests/input/CASE.EGRID new file mode 100644 index 00000000000..33da9f7a70e Binary files /dev/null and b/libres/test-data/local/configuration_tests/input/CASE.EGRID differ diff --git a/libres/test-data/local/configuration_tests/input/SPE1.DATA b/libres/test-data/local/configuration_tests/input/SPE1.DATA new file mode 100644 index 00000000000..619d4397668 --- /dev/null +++ b/libres/test-data/local/configuration_tests/input/SPE1.DATA @@ -0,0 +1,419 @@ +-- This reservoir simulation deck is made available under the Open Database +-- License: http://opendatacommons.org/licenses/odbl/1.0/. Any rights in +-- individual contents of the database are licensed under the Database Contents +-- License: http://opendatacommons.org/licenses/dbcl/1.0/ + +-- Copyright (C) 2015 Equinor + +-- This simulation is based on the data given in +-- 'Comparison of Solutions to a Three-Dimensional +-- Black-Oil Reservoir Simulation Problem' by Aziz S. Odeh, +-- Journal of Petroleum Technology, January 1981 + +--------------------------------------------------------------------------- +------------------------ SPE1 - CASE 2 ------------------------------------ +--------------------------------------------------------------------------- + +RUNSPEC +-- ------------------------------------------------------------------------- + +TITLE + SPE1 - CASE 2 + +DIMENS + 10 10 3 / + +-- The number of equilibration regions is inferred from the EQLDIMS +-- keyword. +EQLDIMS +/ + +-- The number of PVTW tables is inferred from the TABDIMS keyword; +-- when no data is included in the keyword the default values are used. +TABDIMS +/ + + +OIL +GAS +WATER +DISGAS +-- As seen from figure 4 in Odeh, GOR is increasing with time, +-- which means that dissolved gas is present + + +FIELD + +START + 1 'JAN' 2015 / + +WELLDIMS +-- Item 1: maximum number of wells in the model +-- - there are two wells in the problem; injector and producer +-- Item 2: maximum number of grid blocks connected to any one well +-- - must be one as the wells are located at specific grid blocks +-- Item 3: maximum number of groups in the model +-- - we are dealing with only one 'group' +-- Item 4: maximum number of wells in any one group +-- - there must be two wells in a group as there are two wells in total + 2 1 1 2 / + +UNIFOUT + +GRID +-- ------------------------------------------------------------------------- +NOECHO + +DX +-- There are in total 300 cells with length 1000ft in x-direction + 300*1000 / +DY +-- There are in total 300 cells with length 1000ft in y-direction + 300*1000 / +DZ +-- The layers are 20, 30 and 50 ft thick, in each layer there are 100 cells + 100*20 100*30 100*50 / + +TOPS +-- The depth of the top of each grid block + 100*8325 / + +PORO +-- Constant porosity of 0.3 throughout all 300 grid cells + 300*0.3 / + +PERMX +-- The layers have perm. 500mD, 50mD and 200mD, respectively. + 100*500 100*50 100*200 / + +PERMY +-- Equal to PERMX + 100*500 100*50 100*200 / + +PERMZ +-- Cannot find perm. in z-direction in Odeh's paper +-- For the time being, we will assume PERMZ equal to PERMX and PERMY: + 100*500 100*50 100*200 / +ECHO + +PROPS +-- ------------------------------------------------------------------------- + +PVTW +-- Item 1: pressure reference (psia) +-- Item 2: water FVF (rb per bbl or rb per stb) +-- Item 3: water compressibility (psi^{-1}) +-- Item 4: water viscosity (cp) +-- Item 5: water 'viscosibility' (psi^{-1}) + +-- Using values from Norne: +-- In METRIC units: +-- 277.0 1.038 4.67E-5 0.318 0.0 / +-- In FIELD units: + 4017.55 1.038 3.22E-6 0.318 0.0 / + +ROCK +-- Item 1: reference pressure (psia) +-- Item 2: rock compressibility (psi^{-1}) + +-- Using values from table 1 in Odeh: + 14.7 3E-6 / + +SWOF +-- Column 1: water saturation +-- - this has been set to (almost) equally spaced values from 0.12 to 1 +-- Column 2: water relative permeability +-- - generated from the Corey-type approx. formula +-- the coeffisient is set to 10e-5, S_{orw}=0 and S_{wi}=0.12 +-- Column 3: oil relative permeability when only oil and water are present +-- - we will use the same values as in column 3 in SGOF. +-- This is not really correct, but since only the first +-- two values are of importance, this does not really matter +-- Column 4: corresponding water-oil capillary pressure (psi) + +0.12 0 1 0 +0.18 4.64876033057851E-008 1 0 +0.24 0.000000186 0.997 0 +0.3 4.18388429752066E-007 0.98 0 +0.36 7.43801652892562E-007 0.7 0 +0.42 1.16219008264463E-006 0.35 0 +0.48 1.67355371900826E-006 0.2 0 +0.54 2.27789256198347E-006 0.09 0 +0.6 2.97520661157025E-006 0.021 0 +0.66 3.7654958677686E-006 0.01 0 +0.72 4.64876033057851E-006 0.001 0 +0.78 0.000005625 0.0001 0 +0.84 6.69421487603306E-006 0 0 +0.91 8.05914256198347E-006 0 0 +1 0.00001 0 0 / + + +SGOF +-- Column 1: gas saturation +-- Column 2: gas relative permeability +-- Column 3: oil relative permeability when oil, gas and connate water are present +-- Column 4: oil-gas capillary pressure (psi) +-- - stated to be zero in Odeh's paper + +-- Values in column 1-3 are taken from table 3 in Odeh's paper: +0 0 1 0 +0.001 0 1 0 +0.02 0 0.997 0 +0.05 0.005 0.980 0 +0.12 0.025 0.700 0 +0.2 0.075 0.350 0 +0.25 0.125 0.200 0 +0.3 0.190 0.090 0 +0.4 0.410 0.021 0 +0.45 0.60 0.010 0 +0.5 0.72 0.001 0 +0.6 0.87 0.0001 0 +0.7 0.94 0.000 0 +0.85 0.98 0.000 0 +0.88 0.984 0.000 0 / +--1.00 1.0 0.000 0 / +-- Warning from Eclipse: first sat. value in SWOF + last sat. value in SGOF +-- must not be greater than 1, but Eclipse still runs +-- Flow needs the sum to be excactly 1 so I added a row with gas sat. = 0.88 +-- The corresponding krg value was estimated by assuming linear rel. between +-- gas sat. and krw. between gas sat. 0.85 and 1.00 (the last two values given) + +DENSITY +-- Density (lb per ft³) at surface cond. of +-- oil, water and gas, respectively (in that order) + +-- Using values from Norne: +-- In METRIC units: +-- 859.5 1033.0 0.854 / +-- In FIELD units: + 53.66 64.49 0.0533 / + +PVDG +-- Column 1: gas phase pressure (psia) +-- Column 2: gas formation volume factor (rb per Mscf) +-- - in Odeh's paper the units are said to be given in rb per bbl, +-- but this is assumed to be a mistake: FVF-values in Odeh's paper +-- are given in rb per scf, not rb per bbl. This will be in +-- agreement with conventions +-- Column 3: gas viscosity (cP) + +-- Using values from lower right table in Odeh's table 2: +14.700 166.666 0.008000 +264.70 12.0930 0.009600 +514.70 6.27400 0.011200 +1014.7 3.19700 0.014000 +2014.7 1.61400 0.018900 +2514.7 1.29400 0.020800 +3014.7 1.08000 0.022800 +4014.7 0.81100 0.026800 +5014.7 0.64900 0.030900 +9014.7 0.38600 0.047000 / + +PVTO +-- Column 1: dissolved gas-oil ratio (Mscf per stb) +-- Column 2: bubble point pressure (psia) +-- Column 3: oil FVF for saturated oil (rb per stb) +-- Column 4: oil viscosity for saturated oil (cP) + +-- Use values from top left table in Odeh's table 2: +0.0010 14.7 1.0620 1.0400 / +0.0905 264.7 1.1500 0.9750 / +0.1800 514.7 1.2070 0.9100 / +0.3710 1014.7 1.2950 0.8300 / +0.6360 2014.7 1.4350 0.6950 / +0.7750 2514.7 1.5000 0.6410 / +0.9300 3014.7 1.5650 0.5940 / +1.2700 4014.7 1.6950 0.5100 + 9014.7 1.5790 0.7400 / +1.6180 5014.7 1.8270 0.4490 + 9014.7 1.7370 0.6310 / +-- It is required to enter data for undersaturated oil for the highest GOR +-- (i.e. the last row) in the PVTO table. +-- In order to fulfill this requirement, values for oil FVF and viscosity +-- at 9014.7psia and GOR=1.618 for undersaturated oil have been approximated: +-- It has been assumed that there is a linear relation between the GOR +-- and the FVF when keeping the pressure constant at 9014.7psia. +-- From Odeh we know that (at 9014.7psia) the FVF is 2.357 at GOR=2.984 +-- for saturated oil and that the FVF is 1.579 at GOR=1.27 for undersaturated oil, +-- so it is possible to use the assumption described above. +-- An equivalent approximation for the viscosity has been used. +/ + +SOLUTION +-- ------------------------------------------------------------------------- + +EQUIL +-- Item 1: datum depth (ft) +-- Item 2: pressure at datum depth (psia) +-- - Odeh's table 1 says that initial reservoir pressure is +-- 4800 psi at 8400ft, which explains choice of item 1 and 2 +-- Item 3: depth of water-oil contact (ft) +-- - chosen to be directly under the reservoir +-- Item 4: oil-water capillary pressure at the water oil contact (psi) +-- - given to be 0 in Odeh's paper +-- Item 5: depth of gas-oil contact (ft) +-- - chosen to be directly above the reservoir +-- Item 6: gas-oil capillary pressure at gas-oil contact (psi) +-- - given to be 0 in Odeh's paper +-- Item 7: RSVD-table +-- Item 8: RVVD-table +-- Item 9: Set to 0 as this is the only value supported by OPM + +-- Item #: 1 2 3 4 5 6 7 8 9 + 8400 4800 8450 0 8300 0 1 0 0 / + +RSVD +-- Dissolved GOR is initially constant with depth through the reservoir. +-- The reason is that the initial reservoir pressure given is higher +---than the bubble point presssure of 4014.7psia, meaning that there is no +-- free gas initially present. +8300 1.270 +8450 1.270 / + +SUMMARY +-- ------------------------------------------------------------------------- + +-- 1a) Oil rate vs time +FOPR +-- Field Oil Production Rate + +-- 1b) GOR vs time +WGOR +-- Well Gas-Oil Ratio + 'PROD' +/ +-- Using FGOR instead of WGOR:PROD results in the same graph +FGOR + +-- 2a) Pressures of the cell where the injector and producer are located +BPR +1 1 1 / +10 10 3 / +/ + +-- 2b) Gas saturation at grid points given in Odeh's paper +BGSAT +1 1 1 / +1 1 2 / +1 1 3 / +10 1 1 / +10 1 2 / +10 1 3 / +10 10 1 / +10 10 2 / +10 10 3 / +/ + +-- In order to compare Eclipse with Flow: +WBHP + 'INJ' + 'PROD' +/ +WGIR + 'INJ' + 'PROD' +/ +WGIT + 'INJ' + 'PROD' +/ +WGPR + 'INJ' + 'PROD' +/ +WGPT + 'INJ' + 'PROD' +/ +WOIR + 'INJ' + 'PROD' +/ +WOIT + 'INJ' + 'PROD' +/ +WOPR + 'INJ' + 'PROD' +/ +WOPT + 'INJ' + 'PROD' +/ +WWIR + 'INJ' + 'PROD' +/ +WWIT + 'INJ' + 'PROD' +/ +WWPR + 'INJ' + 'PROD' +/ +WWPT + 'INJ' + 'PROD' +/ +SCHEDULE +-- ------------------------------------------------------------------------- +RPTSCHED + 'PRES' 'SGAS' 'RS' 'WELLS' / + +RPTRST + 'BASIC=1' / + + +-- If no resolution (i.e. case 1), the two following lines must be added: +--DRSDT +-- 0 / +-- Since this is Case 2, the two lines above have been commented out. +-- if DRSDT is set to 0, GOR cannot rise and free gas does not +-- dissolve in undersaturated oil -> constant bubble point pressure + +WELSPECS +-- Item #: 1 2 3 4 5 6 + 'PROD' 'G1' 10 10 8400 'OIL' / + 'INJ' 'G1' 1 1 8335 'GAS' / +/ +-- Coordinates in item 3-4 are retrieved from Odeh's figure 1 and 2 +-- Note that the depth at the midpoint of the well grid blocks +-- has been used as reference depth for bottom hole pressure in item 5 + +COMPDAT +-- Item #: 1 2 3 4 5 6 7 8 9 + 'PROD' 10 10 3 3 'OPEN' 1* 1* 0.5 / + 'INJ' 1 1 1 1 'OPEN' 1* 1* 0.5 / +/ +-- Coordinates in item 2-5 are retreived from Odeh's figure 1 and 2 +-- Item 9 is the well bore internal diameter, +-- the radius is given to be 0.25ft in Odeh's paper + + +WCONPROD +-- Item #:1 2 3 4 5 9 + 'PROD' 'OPEN' 'ORAT' 20000 4* 1000 / +/ +-- It is stated in Odeh's paper that the maximum oil prod. rate +-- is 20 000stb per day which explains the choice of value in item 4. +-- The items > 4 are defaulted with the exception of item 9, +-- the BHP lower limit, which is given to be 1000psia in Odeh's paper + +WCONINJE +-- Item #:1 2 3 4 5 6 7 + 'INJ' 'GAS' 'OPEN' 'RATE' 100000 1* 9014 / +/ +-- Stated in Odeh that gas inj. rate (item 5) is 100MMscf per day +-- BHP upper limit (item 7) should not be exceeding the highest +-- pressure in the PVT table=9014.7psia (default is 100 000psia) + +TSTEP +--Advance the simulater once a month for TEN years: +31 28 31 30 31 30 31 31 30 31 30 31 / + +--Advance the simulator once a year for TEN years: +--10*365 / + +END diff --git a/libres/test-data/local/configuration_tests/input/jobs/SNAKE_OIL_DIFF b/libres/test-data/local/configuration_tests/input/jobs/SNAKE_OIL_DIFF new file mode 100644 index 00000000000..98a867d9594 --- /dev/null +++ b/libres/test-data/local/configuration_tests/input/jobs/SNAKE_OIL_DIFF @@ -0,0 +1,4 @@ +STDOUT snake_oil_diff.stdout +STDERR snake_oil_diff.stderr + +EXECUTABLE snake_oil_diff.py \ No newline at end of file diff --git a/libres/test-data/local/configuration_tests/input/jobs/SNAKE_OIL_NPV b/libres/test-data/local/configuration_tests/input/jobs/SNAKE_OIL_NPV new file mode 100644 index 00000000000..887830c756f --- /dev/null +++ b/libres/test-data/local/configuration_tests/input/jobs/SNAKE_OIL_NPV @@ -0,0 +1,4 @@ +STDOUT snake_oil_npv.stdout +STDERR snake_oil_npv.stderr + +EXECUTABLE snake_oil_npv.py \ No newline at end of file diff --git a/libres/test-data/local/configuration_tests/input/jobs/SNAKE_OIL_SIMULATOR b/libres/test-data/local/configuration_tests/input/jobs/SNAKE_OIL_SIMULATOR new file mode 100644 index 00000000000..b4b7f9928fd --- /dev/null +++ b/libres/test-data/local/configuration_tests/input/jobs/SNAKE_OIL_SIMULATOR @@ -0,0 +1,4 @@ +STDOUT snake_oil.stdout +STDERR snake_oil.stderr + +EXECUTABLE snake_oil_simulator.py \ No newline at end of file diff --git a/libres/test-data/local/configuration_tests/input/jobs/snake_oil_diff.py b/libres/test-data/local/configuration_tests/input/jobs/snake_oil_diff.py new file mode 100755 index 00000000000..cdcf98cd7d8 --- /dev/null +++ b/libres/test-data/local/configuration_tests/input/jobs/snake_oil_diff.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +from ecl.summary import EclSum + + +def writeDiff(filename, ecl_sum, key1, key2): + with open(filename, "w") as f: + for v1, v2 in zip(ecl_sum.numpy_vector(key1), ecl_sum.numpy_vector(key2)): + diff = v1 - v2 + f.write("%f\n" % diff) + + +if __name__ == "__main__": + ecl_sum = EclSum("SNAKE_OIL_FIELD") + + report_step = 199 + writeDiff( + "snake_oil_opr_diff_%d.txt" % report_step, ecl_sum, "WOPR:OP1", "WOPR:OP2" + ) + writeDiff( + "snake_oil_wpr_diff_%d.txt" % report_step, ecl_sum, "WWPR:OP1", "WWPR:OP2" + ) + writeDiff( + "snake_oil_gpr_diff_%d.txt" % report_step, ecl_sum, "WGPR:OP1", "WGPR:OP2" + ) diff --git a/libres/test-data/local/configuration_tests/input/jobs/snake_oil_npv.py b/libres/test-data/local/configuration_tests/input/jobs/snake_oil_npv.py new file mode 100755 index 00000000000..e1f4af7fa8b --- /dev/null +++ b/libres/test-data/local/configuration_tests/input/jobs/snake_oil_npv.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python +from ecl.summary import EclSum + +OIL_PRICES = { + "2010-01-01": 78.33, + "2010-02-01": 76.39, + "2010-03-01": 81.20, + "2010-04-01": 84.29, + "2010-05-01": 73.74, + "2010-06-01": 75.34, + "2010-07-01": 76.32, + "2010-08-01": 76.60, + "2010-09-01": 75.24, + "2010-10-01": 81.89, + "2010-11-01": 84.25, + "2010-12-01": 89.15, + "2011-01-01": 89.17, + "2011-02-01": 88.58, + "2011-03-01": 102.86, + "2011-04-01": 109.53, + "2011-05-01": 100.90, + "2011-06-01": 96.26, + "2011-07-01": 97.30, + "2011-08-01": 86.33, + "2011-09-01": 85.52, + "2011-10-01": 86.32, + "2011-11-01": 97.16, + "2011-12-01": 98.56, + "2012-01-01": 100.27, + "2012-02-01": 102.20, + "2012-03-01": 106.16, + "2012-04-01": 103.32, + "2012-05-01": 94.65, + "2012-06-01": 82.30, + "2012-07-01": 87.90, + "2012-08-01": 94.13, + "2012-09-01": 94.51, + "2012-10-01": 89.49, + "2012-11-01": 86.53, + "2012-12-01": 87.86, + "2013-01-01": 94.76, + "2013-02-01": 95.31, + "2013-03-01": 92.94, + "2013-04-01": 92.02, + "2013-05-01": 94.51, + "2013-06-01": 95.77, + "2013-07-01": 104.67, + "2013-08-01": 106.57, + "2013-09-01": 106.29, + "2013-10-01": 100.54, + "2013-11-01": 93.86, + "2013-12-01": 97.63, + "2014-01-01": 94.62, + "2014-02-01": 100.82, + "2014-03-01": 100.80, + "2014-04-01": 102.07, + "2014-05-01": 102.18, + "2014-06-01": 105.79, + "2014-07-01": 103.59, + "2014-08-01": 96.54, + "2014-09-01": 93.21, + "2014-10-01": 84.40, + "2014-11-01": 75.79, + "2014-12-01": 59.29, + "2015-01-01": 47.22, + "2015-02-01": 50.58, + "2015-03-01": 47.82, + "2015-04-01": 54.45, + "2015-05-01": 59.27, + "2015-06-01": 59.82, + "2015-07-01": 50.90, + "2015-08-01": 42.87, + "2015-09-01": 45.48, +} + +if __name__ == "__main__": + ecl_sum = EclSum("SNAKE_OIL_FIELD") + start_time = ecl_sum.getStartTime() + date_ranges = ecl_sum.timeRange(start_time, interval="1M") + production_sums = ecl_sum.blockedProduction("FOPT", date_ranges) + + npv = 0.0 + for index in range(0, len(date_ranges) - 1): + date = date_ranges[index + 1] # end of period + production_sum = production_sums[index] + + oil_price = OIL_PRICES[date.date().strftime("%Y-%m-%d")] + + production_value = oil_price * production_sum + npv += production_value + + with open("snake_oil_npv.txt", "w") as output_file: + output_file.write("NPV %s\n" % npv) + + if npv < 80000: + rating = "POOR" + elif 80000 <= npv < 100000: + rating = "AVERAGE" + elif 100000 <= npv < 120000: + rating = "GOOD" + else: + rating = "EXCELLENT" + + output_file.write("RATING %s\n" % rating) diff --git a/libres/test-data/local/configuration_tests/input/jobs/snake_oil_simulator.py b/libres/test-data/local/configuration_tests/input/jobs/snake_oil_simulator.py new file mode 100755 index 00000000000..79432de73cf --- /dev/null +++ b/libres/test-data/local/configuration_tests/input/jobs/snake_oil_simulator.py @@ -0,0 +1,197 @@ +#!/usr/bin/env python +from datetime import datetime +import os +import sys + +from ecl.summary import EclSum, EclSumTStep +from ecl.util.test import ExtendedTestCase + +print(sys.path) +print(os.environ["PYTHONPATH"]) +import res + +from res.test.synthesizer import OilSimulator + + +def globalIndex(i, j, k, nx=10, ny=10, nz=10): + return i + nx * (j - 1) + nx * ny * (k - 1) + + +def readParameters(filename): + params = {} + with open(filename, "r") as f: + for line in f: + key, value = line.split(":", 1) + params[key] = value.strip() + + return params + + +def runSimulator(simulator, history_simulator, time_step_count): + """@rtype: EclSum""" + ecl_sum = EclSum.writer("SNAKE_OIL_FIELD", datetime(2010, 1, 1), 10, 10, 10) + + ecl_sum.addVariable("FOPT") + ecl_sum.addVariable("FOPR") + ecl_sum.addVariable("FGPT") + ecl_sum.addVariable("FGPR") + ecl_sum.addVariable("FWPT") + ecl_sum.addVariable("FWPR") + ecl_sum.addVariable("FGOR") + ecl_sum.addVariable("FWCT") + + ecl_sum.addVariable("FOPTH") + ecl_sum.addVariable("FOPRH") + ecl_sum.addVariable("FGPTH") + ecl_sum.addVariable("FGPRH") + ecl_sum.addVariable("FWPTH") + ecl_sum.addVariable("FWPRH") + ecl_sum.addVariable("FGORH") + ecl_sum.addVariable("FWCTH") + + ecl_sum.addVariable("WOPR", wgname="OP1") + ecl_sum.addVariable("WOPR", wgname="OP2") + ecl_sum.addVariable("WWPR", wgname="OP1") + ecl_sum.addVariable("WWPR", wgname="OP2") + ecl_sum.addVariable("WGPR", wgname="OP1") + ecl_sum.addVariable("WGPR", wgname="OP2") + ecl_sum.addVariable("WGOR", wgname="OP1") + ecl_sum.addVariable("WGOR", wgname="OP2") + ecl_sum.addVariable("WWCT", wgname="OP1") + ecl_sum.addVariable("WWCT", wgname="OP2") + + ecl_sum.addVariable("WOPRH", wgname="OP1") + ecl_sum.addVariable("WOPRH", wgname="OP2") + ecl_sum.addVariable("WWPRH", wgname="OP1") + ecl_sum.addVariable("WWPRH", wgname="OP2") + ecl_sum.addVariable("WGPRH", wgname="OP1") + ecl_sum.addVariable("WGPRH", wgname="OP2") + ecl_sum.addVariable("WGORH", wgname="OP1") + ecl_sum.addVariable("WGORH", wgname="OP2") + ecl_sum.addVariable("WWCTH", wgname="OP1") + ecl_sum.addVariable("WWCTH", wgname="OP2") + + ecl_sum.addVariable("BPR", num=globalIndex(5, 5, 5)) + ecl_sum.addVariable("BPR", num=globalIndex(1, 3, 8)) + + time_map = [] + mini_step_count = 10 + total_step_count = time_step_count * mini_step_count + + for report_step in range(time_step_count): + for mini_step in range(mini_step_count): + t_step = ecl_sum.addTStep( + report_step + 1, sim_days=report_step * mini_step_count + mini_step + ) + + time_map.append(t_step.getSimTime().datetime().strftime("%d/%m/%Y")) + + simulator.step(scale=1.0 / total_step_count) + history_simulator.step(scale=1.0 / total_step_count) + + t_step["FOPR"] = simulator.fopr() + t_step["FOPT"] = simulator.fopt() + t_step["FGPR"] = simulator.fgpr() + t_step["FGPT"] = simulator.fgpt() + t_step["FWPR"] = simulator.fwpr() + t_step["FWPT"] = simulator.fwpt() + t_step["FGOR"] = simulator.fgor() + t_step["FWCT"] = simulator.fwct() + + t_step["WOPR:OP1"] = simulator.opr("OP1") + t_step["WOPR:OP2"] = simulator.opr("OP2") + + t_step["WGPR:OP1"] = simulator.gpr("OP1") + t_step["WGPR:OP2"] = simulator.gpr("OP2") + + t_step["WWPR:OP1"] = simulator.wpr("OP1") + t_step["WWPR:OP2"] = simulator.wpr("OP2") + + t_step["WGOR:OP1"] = simulator.gor("OP1") + t_step["WGOR:OP2"] = simulator.gor("OP2") + + t_step["WWCT:OP1"] = simulator.wct("OP1") + t_step["WWCT:OP2"] = simulator.wct("OP2") + + t_step["BPR:5,5,5"] = simulator.bpr("5,5,5") + t_step["BPR:1,3,8"] = simulator.bpr("1,3,8") + + t_step["FOPRH"] = history_simulator.fopr() + t_step["FOPTH"] = history_simulator.fopt() + t_step["FGPRH"] = history_simulator.fgpr() + t_step["FGPTH"] = history_simulator.fgpt() + t_step["FWPRH"] = history_simulator.fwpr() + t_step["FWPTH"] = history_simulator.fwpt() + t_step["FGORH"] = history_simulator.fgor() + t_step["FWCTH"] = history_simulator.fwct() + + t_step["WOPRH:OP1"] = history_simulator.opr("OP1") + t_step["WOPRH:OP2"] = history_simulator.opr("OP2") + + t_step["WGPRH:OP1"] = history_simulator.gpr("OP1") + t_step["WGPRH:OP2"] = history_simulator.gpr("OP2") + + t_step["WWPRH:OP1"] = history_simulator.wpr("OP1") + t_step["WWPRH:OP2"] = history_simulator.wpr("OP2") + + t_step["WGORH:OP1"] = history_simulator.gor("OP1") + t_step["WGORH:OP2"] = history_simulator.gor("OP2") + + t_step["WWCTH:OP1"] = history_simulator.wct("OP1") + t_step["WWCTH:OP2"] = history_simulator.wct("OP2") + + return ecl_sum, time_map + + +def roundedInt(value): + return int(round(float(value))) + + +if __name__ == "__main__": + seed = int(readParameters("seed.txt")["SEED"]) + parameters = readParameters("snake_oil_params.txt") + + op1_divergence_scale = float(parameters["OP1_DIVERGENCE_SCALE"]) + op2_divergence_scale = float(parameters["OP2_DIVERGENCE_SCALE"]) + op1_persistence = float(parameters["OP1_PERSISTENCE"]) + op2_persistence = float(parameters["OP2_PERSISTENCE"]) + op1_offset = float(parameters["OP1_OFFSET"]) + op2_offset = float(parameters["OP2_OFFSET"]) + bpr_138_persistence = float(parameters["BPR_138_PERSISTENCE"]) + bpr_555_persistence = float(parameters["BPR_555_PERSISTENCE"]) + + op1_octaves = roundedInt(parameters["OP1_OCTAVES"]) + op2_octaves = roundedInt(parameters["OP2_OCTAVES"]) + + simulator = OilSimulator() + simulator.addWell( + "OP1", + seed * 997, + persistence=op1_persistence, + octaves=op1_octaves, + divergence_scale=op1_divergence_scale, + offset=op1_offset, + ) + simulator.addWell( + "OP2", + seed * 13, + persistence=op2_persistence, + octaves=op2_octaves, + divergence_scale=op2_divergence_scale, + offset=op2_offset, + ) + simulator.addBlock("5,5,5", seed * 37, persistence=bpr_555_persistence) + simulator.addBlock("1,3,8", seed * 31, persistence=bpr_138_persistence) + + history_simulator = OilSimulator() + history_simulator.addWell("OP1", 222118781) + history_simulator.addWell("OP2", 118116362) + + report_step_count = 200 + ecl_sum, time_map = runSimulator(simulator, history_simulator, report_step_count) + + ecl_sum.fwrite() + + with open("time_map.txt", "w") as f: + for t in time_map: + f.write("%s\n" % t) diff --git a/libres/test-data/local/configuration_tests/input/observations/observations.txt b/libres/test-data/local/configuration_tests/input/observations/observations.txt new file mode 100644 index 00000000000..483c1d59ab3 --- /dev/null +++ b/libres/test-data/local/configuration_tests/input/observations/observations.txt @@ -0,0 +1,56 @@ +HISTORY_OBSERVATION FOPR; + +SUMMARY_OBSERVATION WOPR_OP1_9 +{ + VALUE = 0.1; + ERROR = 0.05; + RESTART = 9; + KEY = WOPR:OP1; +}; + +SUMMARY_OBSERVATION WOPR_OP1_36 +{ + VALUE = 0.7; + ERROR = 0.07; + RESTART = 36; + KEY = WOPR:OP1; +}; + +SUMMARY_OBSERVATION WOPR_OP1_72 +{ + VALUE = 0.5; + ERROR = 0.05; + RESTART = 72; + KEY = WOPR:OP1; +}; + +SUMMARY_OBSERVATION WOPR_OP1_108 +{ + VALUE = 0.3; + ERROR = 0.075; + RESTART = 108; + KEY = WOPR:OP1; +}; + +SUMMARY_OBSERVATION WOPR_OP1_144 +{ + VALUE = 0.2; + ERROR = 0.035; + RESTART = 144; + KEY = WOPR:OP1; +}; + +SUMMARY_OBSERVATION WOPR_OP1_190 +{ + VALUE = 0.015; + ERROR = 0.01; + RESTART = 190; + KEY = WOPR:OP1; +}; + +GENERAL_OBSERVATION WPR_DIFF_1 { + DATA = SNAKE_OIL_WPR_DIFF; + INDEX_LIST = 400,800,1200,1800; + RESTART = 199; + OBS_FILE = wpr_diff_obs.txt; +}; diff --git a/libres/test-data/local/configuration_tests/input/observations/wpr_diff_obs.txt b/libres/test-data/local/configuration_tests/input/observations/wpr_diff_obs.txt new file mode 100644 index 00000000000..4aee6d7ef7c --- /dev/null +++ b/libres/test-data/local/configuration_tests/input/observations/wpr_diff_obs.txt @@ -0,0 +1,4 @@ +0.0 0.1 +0.1 0.2 +0.2 0.15 +0.0 0.05 diff --git a/libres/test-data/local/configuration_tests/input/refcase/SNAKE_OIL_FIELD.SMSPEC b/libres/test-data/local/configuration_tests/input/refcase/SNAKE_OIL_FIELD.SMSPEC new file mode 100644 index 00000000000..c5aa629876e Binary files /dev/null and b/libres/test-data/local/configuration_tests/input/refcase/SNAKE_OIL_FIELD.SMSPEC differ diff --git a/libres/test-data/local/configuration_tests/input/refcase/SNAKE_OIL_FIELD.UNSMRY b/libres/test-data/local/configuration_tests/input/refcase/SNAKE_OIL_FIELD.UNSMRY new file mode 100644 index 00000000000..854f523fa7e Binary files /dev/null and b/libres/test-data/local/configuration_tests/input/refcase/SNAKE_OIL_FIELD.UNSMRY differ diff --git a/libres/test-data/local/configuration_tests/input/refcase/refcase_readme.txt b/libres/test-data/local/configuration_tests/input/refcase/refcase_readme.txt new file mode 100644 index 00000000000..a3d0fe60589 --- /dev/null +++ b/libres/test-data/local/configuration_tests/input/refcase/refcase_readme.txt @@ -0,0 +1 @@ +To create a refcase run the snake_oil_simulator.py job with the this as working directory. \ No newline at end of file diff --git a/libres/test-data/local/configuration_tests/input/refcase/seed.txt b/libres/test-data/local/configuration_tests/input/refcase/seed.txt new file mode 100644 index 00000000000..0009f6e89a5 --- /dev/null +++ b/libres/test-data/local/configuration_tests/input/refcase/seed.txt @@ -0,0 +1 @@ +SEED:268776 \ No newline at end of file diff --git a/libres/test-data/local/configuration_tests/input/refcase/snake_oil_params.txt b/libres/test-data/local/configuration_tests/input/refcase/snake_oil_params.txt new file mode 100644 index 00000000000..3868522924c --- /dev/null +++ b/libres/test-data/local/configuration_tests/input/refcase/snake_oil_params.txt @@ -0,0 +1,10 @@ +OP1_PERSISTENCE:0.15 +OP1_OCTAVES:4 +OP1_DIVERGENCE_SCALE:0.5 +OP1_OFFSET:0.0 +OP2_PERSISTENCE:0.25 +OP2_OCTAVES:7.0 +OP2_DIVERGENCE_SCALE:1.0 +OP2_OFFSET:0.0 +BPR_555_PERSISTENCE:0.25 +BPR_138_PERSISTENCE:0.35 diff --git a/libres/test-data/local/configuration_tests/input/refcase/time_map.txt b/libres/test-data/local/configuration_tests/input/refcase/time_map.txt new file mode 100644 index 00000000000..d54b4293aec --- /dev/null +++ b/libres/test-data/local/configuration_tests/input/refcase/time_map.txt @@ -0,0 +1,2000 @@ +01/01/2010 +02/01/2010 +03/01/2010 +04/01/2010 +05/01/2010 +06/01/2010 +07/01/2010 +08/01/2010 +09/01/2010 +10/01/2010 +11/01/2010 +12/01/2010 +13/01/2010 +14/01/2010 +15/01/2010 +16/01/2010 +17/01/2010 +18/01/2010 +19/01/2010 +20/01/2010 +21/01/2010 +22/01/2010 +23/01/2010 +24/01/2010 +25/01/2010 +26/01/2010 +27/01/2010 +28/01/2010 +29/01/2010 +30/01/2010 +31/01/2010 +01/02/2010 +02/02/2010 +03/02/2010 +04/02/2010 +05/02/2010 +06/02/2010 +07/02/2010 +08/02/2010 +09/02/2010 +10/02/2010 +11/02/2010 +12/02/2010 +13/02/2010 +14/02/2010 +15/02/2010 +16/02/2010 +17/02/2010 +18/02/2010 +19/02/2010 +20/02/2010 +21/02/2010 +22/02/2010 +23/02/2010 +24/02/2010 +25/02/2010 +26/02/2010 +27/02/2010 +28/02/2010 +01/03/2010 +02/03/2010 +03/03/2010 +04/03/2010 +05/03/2010 +06/03/2010 +07/03/2010 +08/03/2010 +09/03/2010 +10/03/2010 +11/03/2010 +12/03/2010 +13/03/2010 +14/03/2010 +15/03/2010 +16/03/2010 +17/03/2010 +18/03/2010 +19/03/2010 +20/03/2010 +21/03/2010 +22/03/2010 +23/03/2010 +24/03/2010 +25/03/2010 +26/03/2010 +27/03/2010 +28/03/2010 +29/03/2010 +30/03/2010 +31/03/2010 +01/04/2010 +02/04/2010 +03/04/2010 +04/04/2010 +05/04/2010 +06/04/2010 +07/04/2010 +08/04/2010 +09/04/2010 +10/04/2010 +11/04/2010 +12/04/2010 +13/04/2010 +14/04/2010 +15/04/2010 +16/04/2010 +17/04/2010 +18/04/2010 +19/04/2010 +20/04/2010 +21/04/2010 +22/04/2010 +23/04/2010 +24/04/2010 +25/04/2010 +26/04/2010 +27/04/2010 +28/04/2010 +29/04/2010 +30/04/2010 +01/05/2010 +02/05/2010 +03/05/2010 +04/05/2010 +05/05/2010 +06/05/2010 +07/05/2010 +08/05/2010 +09/05/2010 +10/05/2010 +11/05/2010 +12/05/2010 +13/05/2010 +14/05/2010 +15/05/2010 +16/05/2010 +17/05/2010 +18/05/2010 +19/05/2010 +20/05/2010 +21/05/2010 +22/05/2010 +23/05/2010 +24/05/2010 +25/05/2010 +26/05/2010 +27/05/2010 +28/05/2010 +29/05/2010 +30/05/2010 +31/05/2010 +01/06/2010 +02/06/2010 +03/06/2010 +04/06/2010 +05/06/2010 +06/06/2010 +07/06/2010 +08/06/2010 +09/06/2010 +10/06/2010 +11/06/2010 +12/06/2010 +13/06/2010 +14/06/2010 +15/06/2010 +16/06/2010 +17/06/2010 +18/06/2010 +19/06/2010 +20/06/2010 +21/06/2010 +22/06/2010 +23/06/2010 +24/06/2010 +25/06/2010 +26/06/2010 +27/06/2010 +28/06/2010 +29/06/2010 +30/06/2010 +01/07/2010 +02/07/2010 +03/07/2010 +04/07/2010 +05/07/2010 +06/07/2010 +07/07/2010 +08/07/2010 +09/07/2010 +10/07/2010 +11/07/2010 +12/07/2010 +13/07/2010 +14/07/2010 +15/07/2010 +16/07/2010 +17/07/2010 +18/07/2010 +19/07/2010 +20/07/2010 +21/07/2010 +22/07/2010 +23/07/2010 +24/07/2010 +25/07/2010 +26/07/2010 +27/07/2010 +28/07/2010 +29/07/2010 +30/07/2010 +31/07/2010 +01/08/2010 +02/08/2010 +03/08/2010 +04/08/2010 +05/08/2010 +06/08/2010 +07/08/2010 +08/08/2010 +09/08/2010 +10/08/2010 +11/08/2010 +12/08/2010 +13/08/2010 +14/08/2010 +15/08/2010 +16/08/2010 +17/08/2010 +18/08/2010 +19/08/2010 +20/08/2010 +21/08/2010 +22/08/2010 +23/08/2010 +24/08/2010 +25/08/2010 +26/08/2010 +27/08/2010 +28/08/2010 +29/08/2010 +30/08/2010 +31/08/2010 +01/09/2010 +02/09/2010 +03/09/2010 +04/09/2010 +05/09/2010 +06/09/2010 +07/09/2010 +08/09/2010 +09/09/2010 +10/09/2010 +11/09/2010 +12/09/2010 +13/09/2010 +14/09/2010 +15/09/2010 +16/09/2010 +17/09/2010 +18/09/2010 +19/09/2010 +20/09/2010 +21/09/2010 +22/09/2010 +23/09/2010 +24/09/2010 +25/09/2010 +26/09/2010 +27/09/2010 +28/09/2010 +29/09/2010 +30/09/2010 +01/10/2010 +02/10/2010 +03/10/2010 +04/10/2010 +05/10/2010 +06/10/2010 +07/10/2010 +08/10/2010 +09/10/2010 +10/10/2010 +11/10/2010 +12/10/2010 +13/10/2010 +14/10/2010 +15/10/2010 +16/10/2010 +17/10/2010 +18/10/2010 +19/10/2010 +20/10/2010 +21/10/2010 +22/10/2010 +23/10/2010 +24/10/2010 +25/10/2010 +26/10/2010 +27/10/2010 +28/10/2010 +29/10/2010 +30/10/2010 +31/10/2010 +01/11/2010 +02/11/2010 +03/11/2010 +04/11/2010 +05/11/2010 +06/11/2010 +07/11/2010 +08/11/2010 +09/11/2010 +10/11/2010 +11/11/2010 +12/11/2010 +13/11/2010 +14/11/2010 +15/11/2010 +16/11/2010 +17/11/2010 +18/11/2010 +19/11/2010 +20/11/2010 +21/11/2010 +22/11/2010 +23/11/2010 +24/11/2010 +25/11/2010 +26/11/2010 +27/11/2010 +28/11/2010 +29/11/2010 +30/11/2010 +01/12/2010 +02/12/2010 +03/12/2010 +04/12/2010 +05/12/2010 +06/12/2010 +07/12/2010 +08/12/2010 +09/12/2010 +10/12/2010 +11/12/2010 +12/12/2010 +13/12/2010 +14/12/2010 +15/12/2010 +16/12/2010 +17/12/2010 +18/12/2010 +19/12/2010 +20/12/2010 +21/12/2010 +22/12/2010 +23/12/2010 +24/12/2010 +25/12/2010 +26/12/2010 +27/12/2010 +28/12/2010 +29/12/2010 +30/12/2010 +31/12/2010 +01/01/2011 +02/01/2011 +03/01/2011 +04/01/2011 +05/01/2011 +06/01/2011 +07/01/2011 +08/01/2011 +09/01/2011 +10/01/2011 +11/01/2011 +12/01/2011 +13/01/2011 +14/01/2011 +15/01/2011 +16/01/2011 +17/01/2011 +18/01/2011 +19/01/2011 +20/01/2011 +21/01/2011 +22/01/2011 +23/01/2011 +24/01/2011 +25/01/2011 +26/01/2011 +27/01/2011 +28/01/2011 +29/01/2011 +30/01/2011 +31/01/2011 +01/02/2011 +02/02/2011 +03/02/2011 +04/02/2011 +05/02/2011 +06/02/2011 +07/02/2011 +08/02/2011 +09/02/2011 +10/02/2011 +11/02/2011 +12/02/2011 +13/02/2011 +14/02/2011 +15/02/2011 +16/02/2011 +17/02/2011 +18/02/2011 +19/02/2011 +20/02/2011 +21/02/2011 +22/02/2011 +23/02/2011 +24/02/2011 +25/02/2011 +26/02/2011 +27/02/2011 +28/02/2011 +01/03/2011 +02/03/2011 +03/03/2011 +04/03/2011 +05/03/2011 +06/03/2011 +07/03/2011 +08/03/2011 +09/03/2011 +10/03/2011 +11/03/2011 +12/03/2011 +13/03/2011 +14/03/2011 +15/03/2011 +16/03/2011 +17/03/2011 +18/03/2011 +19/03/2011 +20/03/2011 +21/03/2011 +22/03/2011 +23/03/2011 +24/03/2011 +25/03/2011 +26/03/2011 +27/03/2011 +28/03/2011 +29/03/2011 +30/03/2011 +31/03/2011 +01/04/2011 +02/04/2011 +03/04/2011 +04/04/2011 +05/04/2011 +06/04/2011 +07/04/2011 +08/04/2011 +09/04/2011 +10/04/2011 +11/04/2011 +12/04/2011 +13/04/2011 +14/04/2011 +15/04/2011 +16/04/2011 +17/04/2011 +18/04/2011 +19/04/2011 +20/04/2011 +21/04/2011 +22/04/2011 +23/04/2011 +24/04/2011 +25/04/2011 +26/04/2011 +27/04/2011 +28/04/2011 +29/04/2011 +30/04/2011 +01/05/2011 +02/05/2011 +03/05/2011 +04/05/2011 +05/05/2011 +06/05/2011 +07/05/2011 +08/05/2011 +09/05/2011 +10/05/2011 +11/05/2011 +12/05/2011 +13/05/2011 +14/05/2011 +15/05/2011 +16/05/2011 +17/05/2011 +18/05/2011 +19/05/2011 +20/05/2011 +21/05/2011 +22/05/2011 +23/05/2011 +24/05/2011 +25/05/2011 +26/05/2011 +27/05/2011 +28/05/2011 +29/05/2011 +30/05/2011 +31/05/2011 +01/06/2011 +02/06/2011 +03/06/2011 +04/06/2011 +05/06/2011 +06/06/2011 +07/06/2011 +08/06/2011 +09/06/2011 +10/06/2011 +11/06/2011 +12/06/2011 +13/06/2011 +14/06/2011 +15/06/2011 +16/06/2011 +17/06/2011 +18/06/2011 +19/06/2011 +20/06/2011 +21/06/2011 +22/06/2011 +23/06/2011 +24/06/2011 +25/06/2011 +26/06/2011 +27/06/2011 +28/06/2011 +29/06/2011 +30/06/2011 +01/07/2011 +02/07/2011 +03/07/2011 +04/07/2011 +05/07/2011 +06/07/2011 +07/07/2011 +08/07/2011 +09/07/2011 +10/07/2011 +11/07/2011 +12/07/2011 +13/07/2011 +14/07/2011 +15/07/2011 +16/07/2011 +17/07/2011 +18/07/2011 +19/07/2011 +20/07/2011 +21/07/2011 +22/07/2011 +23/07/2011 +24/07/2011 +25/07/2011 +26/07/2011 +27/07/2011 +28/07/2011 +29/07/2011 +30/07/2011 +31/07/2011 +01/08/2011 +02/08/2011 +03/08/2011 +04/08/2011 +05/08/2011 +06/08/2011 +07/08/2011 +08/08/2011 +09/08/2011 +10/08/2011 +11/08/2011 +12/08/2011 +13/08/2011 +14/08/2011 +15/08/2011 +16/08/2011 +17/08/2011 +18/08/2011 +19/08/2011 +20/08/2011 +21/08/2011 +22/08/2011 +23/08/2011 +24/08/2011 +25/08/2011 +26/08/2011 +27/08/2011 +28/08/2011 +29/08/2011 +30/08/2011 +31/08/2011 +01/09/2011 +02/09/2011 +03/09/2011 +04/09/2011 +05/09/2011 +06/09/2011 +07/09/2011 +08/09/2011 +09/09/2011 +10/09/2011 +11/09/2011 +12/09/2011 +13/09/2011 +14/09/2011 +15/09/2011 +16/09/2011 +17/09/2011 +18/09/2011 +19/09/2011 +20/09/2011 +21/09/2011 +22/09/2011 +23/09/2011 +24/09/2011 +25/09/2011 +26/09/2011 +27/09/2011 +28/09/2011 +29/09/2011 +30/09/2011 +01/10/2011 +02/10/2011 +03/10/2011 +04/10/2011 +05/10/2011 +06/10/2011 +07/10/2011 +08/10/2011 +09/10/2011 +10/10/2011 +11/10/2011 +12/10/2011 +13/10/2011 +14/10/2011 +15/10/2011 +16/10/2011 +17/10/2011 +18/10/2011 +19/10/2011 +20/10/2011 +21/10/2011 +22/10/2011 +23/10/2011 +24/10/2011 +25/10/2011 +26/10/2011 +27/10/2011 +28/10/2011 +29/10/2011 +30/10/2011 +31/10/2011 +01/11/2011 +02/11/2011 +03/11/2011 +04/11/2011 +05/11/2011 +06/11/2011 +07/11/2011 +08/11/2011 +09/11/2011 +10/11/2011 +11/11/2011 +12/11/2011 +13/11/2011 +14/11/2011 +15/11/2011 +16/11/2011 +17/11/2011 +18/11/2011 +19/11/2011 +20/11/2011 +21/11/2011 +22/11/2011 +23/11/2011 +24/11/2011 +25/11/2011 +26/11/2011 +27/11/2011 +28/11/2011 +29/11/2011 +30/11/2011 +01/12/2011 +02/12/2011 +03/12/2011 +04/12/2011 +05/12/2011 +06/12/2011 +07/12/2011 +08/12/2011 +09/12/2011 +10/12/2011 +11/12/2011 +12/12/2011 +13/12/2011 +14/12/2011 +15/12/2011 +16/12/2011 +17/12/2011 +18/12/2011 +19/12/2011 +20/12/2011 +21/12/2011 +22/12/2011 +23/12/2011 +24/12/2011 +25/12/2011 +26/12/2011 +27/12/2011 +28/12/2011 +29/12/2011 +30/12/2011 +31/12/2011 +01/01/2012 +02/01/2012 +03/01/2012 +04/01/2012 +05/01/2012 +06/01/2012 +07/01/2012 +08/01/2012 +09/01/2012 +10/01/2012 +11/01/2012 +12/01/2012 +13/01/2012 +14/01/2012 +15/01/2012 +16/01/2012 +17/01/2012 +18/01/2012 +19/01/2012 +20/01/2012 +21/01/2012 +22/01/2012 +23/01/2012 +24/01/2012 +25/01/2012 +26/01/2012 +27/01/2012 +28/01/2012 +29/01/2012 +30/01/2012 +31/01/2012 +01/02/2012 +02/02/2012 +03/02/2012 +04/02/2012 +05/02/2012 +06/02/2012 +07/02/2012 +08/02/2012 +09/02/2012 +10/02/2012 +11/02/2012 +12/02/2012 +13/02/2012 +14/02/2012 +15/02/2012 +16/02/2012 +17/02/2012 +18/02/2012 +19/02/2012 +20/02/2012 +21/02/2012 +22/02/2012 +23/02/2012 +24/02/2012 +25/02/2012 +26/02/2012 +27/02/2012 +28/02/2012 +29/02/2012 +01/03/2012 +02/03/2012 +03/03/2012 +04/03/2012 +05/03/2012 +06/03/2012 +07/03/2012 +08/03/2012 +09/03/2012 +10/03/2012 +11/03/2012 +12/03/2012 +13/03/2012 +14/03/2012 +15/03/2012 +16/03/2012 +17/03/2012 +18/03/2012 +19/03/2012 +20/03/2012 +21/03/2012 +22/03/2012 +23/03/2012 +24/03/2012 +25/03/2012 +26/03/2012 +27/03/2012 +28/03/2012 +29/03/2012 +30/03/2012 +31/03/2012 +01/04/2012 +02/04/2012 +03/04/2012 +04/04/2012 +05/04/2012 +06/04/2012 +07/04/2012 +08/04/2012 +09/04/2012 +10/04/2012 +11/04/2012 +12/04/2012 +13/04/2012 +14/04/2012 +15/04/2012 +16/04/2012 +17/04/2012 +18/04/2012 +19/04/2012 +20/04/2012 +21/04/2012 +22/04/2012 +23/04/2012 +24/04/2012 +25/04/2012 +26/04/2012 +27/04/2012 +28/04/2012 +29/04/2012 +30/04/2012 +01/05/2012 +02/05/2012 +03/05/2012 +04/05/2012 +05/05/2012 +06/05/2012 +07/05/2012 +08/05/2012 +09/05/2012 +10/05/2012 +11/05/2012 +12/05/2012 +13/05/2012 +14/05/2012 +15/05/2012 +16/05/2012 +17/05/2012 +18/05/2012 +19/05/2012 +20/05/2012 +21/05/2012 +22/05/2012 +23/05/2012 +24/05/2012 +25/05/2012 +26/05/2012 +27/05/2012 +28/05/2012 +29/05/2012 +30/05/2012 +31/05/2012 +01/06/2012 +02/06/2012 +03/06/2012 +04/06/2012 +05/06/2012 +06/06/2012 +07/06/2012 +08/06/2012 +09/06/2012 +10/06/2012 +11/06/2012 +12/06/2012 +13/06/2012 +14/06/2012 +15/06/2012 +16/06/2012 +17/06/2012 +18/06/2012 +19/06/2012 +20/06/2012 +21/06/2012 +22/06/2012 +23/06/2012 +24/06/2012 +25/06/2012 +26/06/2012 +27/06/2012 +28/06/2012 +29/06/2012 +30/06/2012 +01/07/2012 +02/07/2012 +03/07/2012 +04/07/2012 +05/07/2012 +06/07/2012 +07/07/2012 +08/07/2012 +09/07/2012 +10/07/2012 +11/07/2012 +12/07/2012 +13/07/2012 +14/07/2012 +15/07/2012 +16/07/2012 +17/07/2012 +18/07/2012 +19/07/2012 +20/07/2012 +21/07/2012 +22/07/2012 +23/07/2012 +24/07/2012 +25/07/2012 +26/07/2012 +27/07/2012 +28/07/2012 +29/07/2012 +30/07/2012 +31/07/2012 +01/08/2012 +02/08/2012 +03/08/2012 +04/08/2012 +05/08/2012 +06/08/2012 +07/08/2012 +08/08/2012 +09/08/2012 +10/08/2012 +11/08/2012 +12/08/2012 +13/08/2012 +14/08/2012 +15/08/2012 +16/08/2012 +17/08/2012 +18/08/2012 +19/08/2012 +20/08/2012 +21/08/2012 +22/08/2012 +23/08/2012 +24/08/2012 +25/08/2012 +26/08/2012 +27/08/2012 +28/08/2012 +29/08/2012 +30/08/2012 +31/08/2012 +01/09/2012 +02/09/2012 +03/09/2012 +04/09/2012 +05/09/2012 +06/09/2012 +07/09/2012 +08/09/2012 +09/09/2012 +10/09/2012 +11/09/2012 +12/09/2012 +13/09/2012 +14/09/2012 +15/09/2012 +16/09/2012 +17/09/2012 +18/09/2012 +19/09/2012 +20/09/2012 +21/09/2012 +22/09/2012 +23/09/2012 +24/09/2012 +25/09/2012 +26/09/2012 +27/09/2012 +28/09/2012 +29/09/2012 +30/09/2012 +01/10/2012 +02/10/2012 +03/10/2012 +04/10/2012 +05/10/2012 +06/10/2012 +07/10/2012 +08/10/2012 +09/10/2012 +10/10/2012 +11/10/2012 +12/10/2012 +13/10/2012 +14/10/2012 +15/10/2012 +16/10/2012 +17/10/2012 +18/10/2012 +19/10/2012 +20/10/2012 +21/10/2012 +22/10/2012 +23/10/2012 +24/10/2012 +25/10/2012 +26/10/2012 +27/10/2012 +28/10/2012 +29/10/2012 +30/10/2012 +31/10/2012 +01/11/2012 +02/11/2012 +03/11/2012 +04/11/2012 +05/11/2012 +06/11/2012 +07/11/2012 +08/11/2012 +09/11/2012 +10/11/2012 +11/11/2012 +12/11/2012 +13/11/2012 +14/11/2012 +15/11/2012 +16/11/2012 +17/11/2012 +18/11/2012 +19/11/2012 +20/11/2012 +21/11/2012 +22/11/2012 +23/11/2012 +24/11/2012 +25/11/2012 +26/11/2012 +27/11/2012 +28/11/2012 +29/11/2012 +30/11/2012 +01/12/2012 +02/12/2012 +03/12/2012 +04/12/2012 +05/12/2012 +06/12/2012 +07/12/2012 +08/12/2012 +09/12/2012 +10/12/2012 +11/12/2012 +12/12/2012 +13/12/2012 +14/12/2012 +15/12/2012 +16/12/2012 +17/12/2012 +18/12/2012 +19/12/2012 +20/12/2012 +21/12/2012 +22/12/2012 +23/12/2012 +24/12/2012 +25/12/2012 +26/12/2012 +27/12/2012 +28/12/2012 +29/12/2012 +30/12/2012 +31/12/2012 +01/01/2013 +02/01/2013 +03/01/2013 +04/01/2013 +05/01/2013 +06/01/2013 +07/01/2013 +08/01/2013 +09/01/2013 +10/01/2013 +11/01/2013 +12/01/2013 +13/01/2013 +14/01/2013 +15/01/2013 +16/01/2013 +17/01/2013 +18/01/2013 +19/01/2013 +20/01/2013 +21/01/2013 +22/01/2013 +23/01/2013 +24/01/2013 +25/01/2013 +26/01/2013 +27/01/2013 +28/01/2013 +29/01/2013 +30/01/2013 +31/01/2013 +01/02/2013 +02/02/2013 +03/02/2013 +04/02/2013 +05/02/2013 +06/02/2013 +07/02/2013 +08/02/2013 +09/02/2013 +10/02/2013 +11/02/2013 +12/02/2013 +13/02/2013 +14/02/2013 +15/02/2013 +16/02/2013 +17/02/2013 +18/02/2013 +19/02/2013 +20/02/2013 +21/02/2013 +22/02/2013 +23/02/2013 +24/02/2013 +25/02/2013 +26/02/2013 +27/02/2013 +28/02/2013 +01/03/2013 +02/03/2013 +03/03/2013 +04/03/2013 +05/03/2013 +06/03/2013 +07/03/2013 +08/03/2013 +09/03/2013 +10/03/2013 +11/03/2013 +12/03/2013 +13/03/2013 +14/03/2013 +15/03/2013 +16/03/2013 +17/03/2013 +18/03/2013 +19/03/2013 +20/03/2013 +21/03/2013 +22/03/2013 +23/03/2013 +24/03/2013 +25/03/2013 +26/03/2013 +27/03/2013 +28/03/2013 +29/03/2013 +30/03/2013 +31/03/2013 +01/04/2013 +02/04/2013 +03/04/2013 +04/04/2013 +05/04/2013 +06/04/2013 +07/04/2013 +08/04/2013 +09/04/2013 +10/04/2013 +11/04/2013 +12/04/2013 +13/04/2013 +14/04/2013 +15/04/2013 +16/04/2013 +17/04/2013 +18/04/2013 +19/04/2013 +20/04/2013 +21/04/2013 +22/04/2013 +23/04/2013 +24/04/2013 +25/04/2013 +26/04/2013 +27/04/2013 +28/04/2013 +29/04/2013 +30/04/2013 +01/05/2013 +02/05/2013 +03/05/2013 +04/05/2013 +05/05/2013 +06/05/2013 +07/05/2013 +08/05/2013 +09/05/2013 +10/05/2013 +11/05/2013 +12/05/2013 +13/05/2013 +14/05/2013 +15/05/2013 +16/05/2013 +17/05/2013 +18/05/2013 +19/05/2013 +20/05/2013 +21/05/2013 +22/05/2013 +23/05/2013 +24/05/2013 +25/05/2013 +26/05/2013 +27/05/2013 +28/05/2013 +29/05/2013 +30/05/2013 +31/05/2013 +01/06/2013 +02/06/2013 +03/06/2013 +04/06/2013 +05/06/2013 +06/06/2013 +07/06/2013 +08/06/2013 +09/06/2013 +10/06/2013 +11/06/2013 +12/06/2013 +13/06/2013 +14/06/2013 +15/06/2013 +16/06/2013 +17/06/2013 +18/06/2013 +19/06/2013 +20/06/2013 +21/06/2013 +22/06/2013 +23/06/2013 +24/06/2013 +25/06/2013 +26/06/2013 +27/06/2013 +28/06/2013 +29/06/2013 +30/06/2013 +01/07/2013 +02/07/2013 +03/07/2013 +04/07/2013 +05/07/2013 +06/07/2013 +07/07/2013 +08/07/2013 +09/07/2013 +10/07/2013 +11/07/2013 +12/07/2013 +13/07/2013 +14/07/2013 +15/07/2013 +16/07/2013 +17/07/2013 +18/07/2013 +19/07/2013 +20/07/2013 +21/07/2013 +22/07/2013 +23/07/2013 +24/07/2013 +25/07/2013 +26/07/2013 +27/07/2013 +28/07/2013 +29/07/2013 +30/07/2013 +31/07/2013 +01/08/2013 +02/08/2013 +03/08/2013 +04/08/2013 +05/08/2013 +06/08/2013 +07/08/2013 +08/08/2013 +09/08/2013 +10/08/2013 +11/08/2013 +12/08/2013 +13/08/2013 +14/08/2013 +15/08/2013 +16/08/2013 +17/08/2013 +18/08/2013 +19/08/2013 +20/08/2013 +21/08/2013 +22/08/2013 +23/08/2013 +24/08/2013 +25/08/2013 +26/08/2013 +27/08/2013 +28/08/2013 +29/08/2013 +30/08/2013 +31/08/2013 +01/09/2013 +02/09/2013 +03/09/2013 +04/09/2013 +05/09/2013 +06/09/2013 +07/09/2013 +08/09/2013 +09/09/2013 +10/09/2013 +11/09/2013 +12/09/2013 +13/09/2013 +14/09/2013 +15/09/2013 +16/09/2013 +17/09/2013 +18/09/2013 +19/09/2013 +20/09/2013 +21/09/2013 +22/09/2013 +23/09/2013 +24/09/2013 +25/09/2013 +26/09/2013 +27/09/2013 +28/09/2013 +29/09/2013 +30/09/2013 +01/10/2013 +02/10/2013 +03/10/2013 +04/10/2013 +05/10/2013 +06/10/2013 +07/10/2013 +08/10/2013 +09/10/2013 +10/10/2013 +11/10/2013 +12/10/2013 +13/10/2013 +14/10/2013 +15/10/2013 +16/10/2013 +17/10/2013 +18/10/2013 +19/10/2013 +20/10/2013 +21/10/2013 +22/10/2013 +23/10/2013 +24/10/2013 +25/10/2013 +26/10/2013 +27/10/2013 +28/10/2013 +29/10/2013 +30/10/2013 +31/10/2013 +01/11/2013 +02/11/2013 +03/11/2013 +04/11/2013 +05/11/2013 +06/11/2013 +07/11/2013 +08/11/2013 +09/11/2013 +10/11/2013 +11/11/2013 +12/11/2013 +13/11/2013 +14/11/2013 +15/11/2013 +16/11/2013 +17/11/2013 +18/11/2013 +19/11/2013 +20/11/2013 +21/11/2013 +22/11/2013 +23/11/2013 +24/11/2013 +25/11/2013 +26/11/2013 +27/11/2013 +28/11/2013 +29/11/2013 +30/11/2013 +01/12/2013 +02/12/2013 +03/12/2013 +04/12/2013 +05/12/2013 +06/12/2013 +07/12/2013 +08/12/2013 +09/12/2013 +10/12/2013 +11/12/2013 +12/12/2013 +13/12/2013 +14/12/2013 +15/12/2013 +16/12/2013 +17/12/2013 +18/12/2013 +19/12/2013 +20/12/2013 +21/12/2013 +22/12/2013 +23/12/2013 +24/12/2013 +25/12/2013 +26/12/2013 +27/12/2013 +28/12/2013 +29/12/2013 +30/12/2013 +31/12/2013 +01/01/2014 +02/01/2014 +03/01/2014 +04/01/2014 +05/01/2014 +06/01/2014 +07/01/2014 +08/01/2014 +09/01/2014 +10/01/2014 +11/01/2014 +12/01/2014 +13/01/2014 +14/01/2014 +15/01/2014 +16/01/2014 +17/01/2014 +18/01/2014 +19/01/2014 +20/01/2014 +21/01/2014 +22/01/2014 +23/01/2014 +24/01/2014 +25/01/2014 +26/01/2014 +27/01/2014 +28/01/2014 +29/01/2014 +30/01/2014 +31/01/2014 +01/02/2014 +02/02/2014 +03/02/2014 +04/02/2014 +05/02/2014 +06/02/2014 +07/02/2014 +08/02/2014 +09/02/2014 +10/02/2014 +11/02/2014 +12/02/2014 +13/02/2014 +14/02/2014 +15/02/2014 +16/02/2014 +17/02/2014 +18/02/2014 +19/02/2014 +20/02/2014 +21/02/2014 +22/02/2014 +23/02/2014 +24/02/2014 +25/02/2014 +26/02/2014 +27/02/2014 +28/02/2014 +01/03/2014 +02/03/2014 +03/03/2014 +04/03/2014 +05/03/2014 +06/03/2014 +07/03/2014 +08/03/2014 +09/03/2014 +10/03/2014 +11/03/2014 +12/03/2014 +13/03/2014 +14/03/2014 +15/03/2014 +16/03/2014 +17/03/2014 +18/03/2014 +19/03/2014 +20/03/2014 +21/03/2014 +22/03/2014 +23/03/2014 +24/03/2014 +25/03/2014 +26/03/2014 +27/03/2014 +28/03/2014 +29/03/2014 +30/03/2014 +31/03/2014 +01/04/2014 +02/04/2014 +03/04/2014 +04/04/2014 +05/04/2014 +06/04/2014 +07/04/2014 +08/04/2014 +09/04/2014 +10/04/2014 +11/04/2014 +12/04/2014 +13/04/2014 +14/04/2014 +15/04/2014 +16/04/2014 +17/04/2014 +18/04/2014 +19/04/2014 +20/04/2014 +21/04/2014 +22/04/2014 +23/04/2014 +24/04/2014 +25/04/2014 +26/04/2014 +27/04/2014 +28/04/2014 +29/04/2014 +30/04/2014 +01/05/2014 +02/05/2014 +03/05/2014 +04/05/2014 +05/05/2014 +06/05/2014 +07/05/2014 +08/05/2014 +09/05/2014 +10/05/2014 +11/05/2014 +12/05/2014 +13/05/2014 +14/05/2014 +15/05/2014 +16/05/2014 +17/05/2014 +18/05/2014 +19/05/2014 +20/05/2014 +21/05/2014 +22/05/2014 +23/05/2014 +24/05/2014 +25/05/2014 +26/05/2014 +27/05/2014 +28/05/2014 +29/05/2014 +30/05/2014 +31/05/2014 +01/06/2014 +02/06/2014 +03/06/2014 +04/06/2014 +05/06/2014 +06/06/2014 +07/06/2014 +08/06/2014 +09/06/2014 +10/06/2014 +11/06/2014 +12/06/2014 +13/06/2014 +14/06/2014 +15/06/2014 +16/06/2014 +17/06/2014 +18/06/2014 +19/06/2014 +20/06/2014 +21/06/2014 +22/06/2014 +23/06/2014 +24/06/2014 +25/06/2014 +26/06/2014 +27/06/2014 +28/06/2014 +29/06/2014 +30/06/2014 +01/07/2014 +02/07/2014 +03/07/2014 +04/07/2014 +05/07/2014 +06/07/2014 +07/07/2014 +08/07/2014 +09/07/2014 +10/07/2014 +11/07/2014 +12/07/2014 +13/07/2014 +14/07/2014 +15/07/2014 +16/07/2014 +17/07/2014 +18/07/2014 +19/07/2014 +20/07/2014 +21/07/2014 +22/07/2014 +23/07/2014 +24/07/2014 +25/07/2014 +26/07/2014 +27/07/2014 +28/07/2014 +29/07/2014 +30/07/2014 +31/07/2014 +01/08/2014 +02/08/2014 +03/08/2014 +04/08/2014 +05/08/2014 +06/08/2014 +07/08/2014 +08/08/2014 +09/08/2014 +10/08/2014 +11/08/2014 +12/08/2014 +13/08/2014 +14/08/2014 +15/08/2014 +16/08/2014 +17/08/2014 +18/08/2014 +19/08/2014 +20/08/2014 +21/08/2014 +22/08/2014 +23/08/2014 +24/08/2014 +25/08/2014 +26/08/2014 +27/08/2014 +28/08/2014 +29/08/2014 +30/08/2014 +31/08/2014 +01/09/2014 +02/09/2014 +03/09/2014 +04/09/2014 +05/09/2014 +06/09/2014 +07/09/2014 +08/09/2014 +09/09/2014 +10/09/2014 +11/09/2014 +12/09/2014 +13/09/2014 +14/09/2014 +15/09/2014 +16/09/2014 +17/09/2014 +18/09/2014 +19/09/2014 +20/09/2014 +21/09/2014 +22/09/2014 +23/09/2014 +24/09/2014 +25/09/2014 +26/09/2014 +27/09/2014 +28/09/2014 +29/09/2014 +30/09/2014 +01/10/2014 +02/10/2014 +03/10/2014 +04/10/2014 +05/10/2014 +06/10/2014 +07/10/2014 +08/10/2014 +09/10/2014 +10/10/2014 +11/10/2014 +12/10/2014 +13/10/2014 +14/10/2014 +15/10/2014 +16/10/2014 +17/10/2014 +18/10/2014 +19/10/2014 +20/10/2014 +21/10/2014 +22/10/2014 +23/10/2014 +24/10/2014 +25/10/2014 +26/10/2014 +27/10/2014 +28/10/2014 +29/10/2014 +30/10/2014 +31/10/2014 +01/11/2014 +02/11/2014 +03/11/2014 +04/11/2014 +05/11/2014 +06/11/2014 +07/11/2014 +08/11/2014 +09/11/2014 +10/11/2014 +11/11/2014 +12/11/2014 +13/11/2014 +14/11/2014 +15/11/2014 +16/11/2014 +17/11/2014 +18/11/2014 +19/11/2014 +20/11/2014 +21/11/2014 +22/11/2014 +23/11/2014 +24/11/2014 +25/11/2014 +26/11/2014 +27/11/2014 +28/11/2014 +29/11/2014 +30/11/2014 +01/12/2014 +02/12/2014 +03/12/2014 +04/12/2014 +05/12/2014 +06/12/2014 +07/12/2014 +08/12/2014 +09/12/2014 +10/12/2014 +11/12/2014 +12/12/2014 +13/12/2014 +14/12/2014 +15/12/2014 +16/12/2014 +17/12/2014 +18/12/2014 +19/12/2014 +20/12/2014 +21/12/2014 +22/12/2014 +23/12/2014 +24/12/2014 +25/12/2014 +26/12/2014 +27/12/2014 +28/12/2014 +29/12/2014 +30/12/2014 +31/12/2014 +01/01/2015 +02/01/2015 +03/01/2015 +04/01/2015 +05/01/2015 +06/01/2015 +07/01/2015 +08/01/2015 +09/01/2015 +10/01/2015 +11/01/2015 +12/01/2015 +13/01/2015 +14/01/2015 +15/01/2015 +16/01/2015 +17/01/2015 +18/01/2015 +19/01/2015 +20/01/2015 +21/01/2015 +22/01/2015 +23/01/2015 +24/01/2015 +25/01/2015 +26/01/2015 +27/01/2015 +28/01/2015 +29/01/2015 +30/01/2015 +31/01/2015 +01/02/2015 +02/02/2015 +03/02/2015 +04/02/2015 +05/02/2015 +06/02/2015 +07/02/2015 +08/02/2015 +09/02/2015 +10/02/2015 +11/02/2015 +12/02/2015 +13/02/2015 +14/02/2015 +15/02/2015 +16/02/2015 +17/02/2015 +18/02/2015 +19/02/2015 +20/02/2015 +21/02/2015 +22/02/2015 +23/02/2015 +24/02/2015 +25/02/2015 +26/02/2015 +27/02/2015 +28/02/2015 +01/03/2015 +02/03/2015 +03/03/2015 +04/03/2015 +05/03/2015 +06/03/2015 +07/03/2015 +08/03/2015 +09/03/2015 +10/03/2015 +11/03/2015 +12/03/2015 +13/03/2015 +14/03/2015 +15/03/2015 +16/03/2015 +17/03/2015 +18/03/2015 +19/03/2015 +20/03/2015 +21/03/2015 +22/03/2015 +23/03/2015 +24/03/2015 +25/03/2015 +26/03/2015 +27/03/2015 +28/03/2015 +29/03/2015 +30/03/2015 +31/03/2015 +01/04/2015 +02/04/2015 +03/04/2015 +04/04/2015 +05/04/2015 +06/04/2015 +07/04/2015 +08/04/2015 +09/04/2015 +10/04/2015 +11/04/2015 +12/04/2015 +13/04/2015 +14/04/2015 +15/04/2015 +16/04/2015 +17/04/2015 +18/04/2015 +19/04/2015 +20/04/2015 +21/04/2015 +22/04/2015 +23/04/2015 +24/04/2015 +25/04/2015 +26/04/2015 +27/04/2015 +28/04/2015 +29/04/2015 +30/04/2015 +01/05/2015 +02/05/2015 +03/05/2015 +04/05/2015 +05/05/2015 +06/05/2015 +07/05/2015 +08/05/2015 +09/05/2015 +10/05/2015 +11/05/2015 +12/05/2015 +13/05/2015 +14/05/2015 +15/05/2015 +16/05/2015 +17/05/2015 +18/05/2015 +19/05/2015 +20/05/2015 +21/05/2015 +22/05/2015 +23/05/2015 +24/05/2015 +25/05/2015 +26/05/2015 +27/05/2015 +28/05/2015 +29/05/2015 +30/05/2015 +31/05/2015 +01/06/2015 +02/06/2015 +03/06/2015 +04/06/2015 +05/06/2015 +06/06/2015 +07/06/2015 +08/06/2015 +09/06/2015 +10/06/2015 +11/06/2015 +12/06/2015 +13/06/2015 +14/06/2015 +15/06/2015 +16/06/2015 +17/06/2015 +18/06/2015 +19/06/2015 +20/06/2015 +21/06/2015 +22/06/2015 +23/06/2015 diff --git a/libres/test-data/local/configuration_tests/input/schedule.sch b/libres/test-data/local/configuration_tests/input/schedule.sch new file mode 100644 index 00000000000..e47abc0f60e --- /dev/null +++ b/libres/test-data/local/configuration_tests/input/schedule.sch @@ -0,0 +1,179 @@ +RPTRST + BASIC=3 FREQ=3 / + +DATES + 1 'JAN' 2000 / --ADDED +/ + + +--start files/welopen.tmpl +WELOPEN + 'INJECT5' 'OPEN' / +/ + +--end files/welopen.tmpl + + +--start files/welopen.tmpl +WELOPEN + 'INJECT7' 'OPEN' / +/ + +--end files/welopen.tmpl + + +--start files/welopen.tmpl +WELOPEN + 'PROD3' 'OPEN' / +/ + +--end files/welopen.tmpl + + +--start files/welopen.tmpl +WELOPEN + 'INJECT3' 'OPEN' / +/ + +--end files/welopen.tmpl + + +--start files/welopen.tmpl +WELOPEN + 'INJECT1' 'OPEN' / +/ + +--end files/welopen.tmpl + + +--start files/welopen.tmpl +WELOPEN + 'PROD1' 'OPEN' / +/ + +--end files/welopen.tmpl + +DATES + 30 'MAY' 2014 / +/ + +DATES + 28 'AUG' 2014 / +/ + + +DATES + 26 'NOV' 2014 / +/ + +DATES + 24 'FEB' 2015 / +/ + +DATES + 25 'MAY' 2015 / +/ + +DATES + 23 'AUG' 2015 / +/ + +DATES + 21 'NOV' 2015 / +/ + +DATES + 19 'FEB' 2016 / +/ + +DATES + 19 'MAY' 2016 / +/ + + +-- this END limits the life cycle period to 5 years instead of 10 +END + + +DATES + 17 'AUG' 2016 / +/ + +DATES + 15 'NOV' 2016 / +/ + +DATES + 13 'FEB' 2017 / +/ + +DATES + 14 'MAY' 2017 / +/ + +DATES + 12 'AUG' 2017 / +/ + +DATES + 10 'NOV' 2017 / +/ + +DATES + 8 'FEB' 2018 / +/ + +DATES + 9 'MAY' 2018 / +/ + +DATES + 7 'AUG' 2018 / +/ + +DATES + 5 'NOV' 2018 / +/ + +DATES + 3 'FEB' 2019 / +/ + +DATES + 4 'MAY' 2019 / +/ + +DATES + 2 'AUG' 2019 / +/ + +DATES + 31 'OCT' 2019 / +/ + +DATES + 29 'JAN' 2020 / +/ + +DATES + 28 'APR' 2020 / +/ + +DATES + 27 'JUL' 2020 / +/ + +DATES + 25 'OCT' 2020 / +/ + +DATES + 23 'JAN' 2021 / +/ + +DATES + 23 'APR' 2021 / +/ + + +END \ No newline at end of file diff --git a/libres/test-data/local/configuration_tests/model_config.ert b/libres/test-data/local/configuration_tests/model_config.ert new file mode 100644 index 00000000000..8a68ebef427 --- /dev/null +++ b/libres/test-data/local/configuration_tests/model_config.ert @@ -0,0 +1,28 @@ +REFCASE input/refcase/SNAKE_OIL_FIELD +END_DATE 10/10/2010 +MIN_REALIZATIONS 10 +QUEUE_SYSTEM LOCAL +QUEUE_OPTION LOCAL MAX_RUNNING 50 + +INSTALL_JOB SNAKE_OIL_SIMULATOR input/jobs/SNAKE_OIL_SIMULATOR +INSTALL_JOB SNAKE_OIL_NPV input/jobs/SNAKE_OIL_NPV +INSTALL_JOB SNAKE_OIL_DIFF input/jobs/SNAKE_OIL_DIFF + +-- ModelConfig specific input + +MAX_RESAMPLE 1 +JOBNAME model_config_test +RUNPATH /tmp/simulations/run%d +NUM_REALIZATIONS 10 +ENSPATH Ensemble + +TIME_MAP input/refcase/time_map.txt +OBS_CONFIG input/observations/observations.txt +HISTORY_SOURCE REFCASE_HISTORY + +GEN_KW_EXPORT_NAME parameter_test.json + +FORWARD_MODEL COPY_FILE(=input/schedule.sch, =output/schedule_copy.sch) +FORWARD_MODEL SNAKE_OIL_SIMULATOR +FORWARD_MODEL SNAKE_OIL_NPV +FORWARD_MODEL SNAKE_OIL_DIFF diff --git a/libres/test-data/local/configuration_tests/sched_file_as_history_source.ert b/libres/test-data/local/configuration_tests/sched_file_as_history_source.ert new file mode 100644 index 00000000000..7fb28eb157e --- /dev/null +++ b/libres/test-data/local/configuration_tests/sched_file_as_history_source.ert @@ -0,0 +1,30 @@ +-- Note that this is copied from model_config.ert +-- We replace REFCASE with SCHEDULE_FILE and then change the +-- HISTORY_SOURCE to be SCHEDULE. +END_DATE 10/10/2010 +MIN_REALIZATIONS 10 +QUEUE_SYSTEM LOCAL +QUEUE_OPTION LOCAL MAX_RUNNING 50 + +INSTALL_JOB SNAKE_OIL_SIMULATOR input/jobs/SNAKE_OIL_SIMULATOR +INSTALL_JOB SNAKE_OIL_NPV input/jobs/SNAKE_OIL_NPV +INSTALL_JOB SNAKE_OIL_DIFF input/jobs/SNAKE_OIL_DIFF + +-- ModelConfig specific input + +MAX_RESAMPLE 1 +JOBNAME model_config_test +RUNPATH /tmp/simulations/run%d +NUM_REALIZATIONS 10 +ENSPATH Ensemble + +TIME_MAP input/refcase/time_map.txt +OBS_CONFIG input/observations/observations.txt +HISTORY_SOURCE SCHEDULE + +GEN_KW_EXPORT_NAME parameter_test.json + +FORWARD_MODEL COPY_FILE(=input/schedule.sch, =output/schedule_copy.sch) +FORWARD_MODEL SNAKE_OIL_SIMULATOR +FORWARD_MODEL SNAKE_OIL_NPV +FORWARD_MODEL SNAKE_OIL_DIFF diff --git a/libres/test-data/local/configuration_tests/surface/small.irap b/libres/test-data/local/configuration_tests/surface/small.irap new file mode 100644 index 00000000000..4817d3295c0 --- /dev/null +++ b/libres/test-data/local/configuration_tests/surface/small.irap @@ -0,0 +1,104 @@ + -996 20 50.000000 50.000000 + 463325.562500 465725.562500 7336963.500000 7340863.500000 + 30 -65.000000 463325.562500 7336963.500000 + 0 0 0 0 0 0 0 + 0.0051 0.0017 -0.0101 -0.0126 -0.0078 0.0019 + 0.0002 -0.0039 -0.0100 -0.0134 -0.0076 -0.0017 + 0.0014 0.0045 0.0058 -0.0002 -0.0029 -0.0023 + 0.0048 0.0063 0.0107 0.0043 -0.0003 0.0044 + 0.0009 0.0036 0.0033 0.0055 0.0174 0.0168 + 0.0142 0.0073 0.0063 0.0113 0.0083 0.0050 + 0.0092 0.0054 0.0037 0.0034 0.0057 0.0034 + -0.0031 0.0034 0.0035 -0.0119 0.0004 0.0101 + 0.0218 -0.0013 -0.0026 -0.0042 -0.0107 -0.0079 + 0.0000 0.0034 -0.0032 -0.0111 -0.0106 -0.0028 + 0.0015 0.0050 0.0028 -0.0037 -0.0079 -0.0036 + 0.0047 -0.0039 0.0088 0.0091 0.0013 -0.0072 + -0.0075 -0.0027 0.0003 -0.0008 -0.0049 0.0036 + 0.0079 0.0021 0.0085 0.0151 0.0135 0.0112 + 0.0139 0.0176 0.0081 0.0041 0.0083 0.0077 + 0.0023 -0.0049 0.0042 -0.0074 -0.0134 -0.0015 + 0.0173 0.0204 -0.0083 -0.0086 -0.0008 -0.0014 + 0.0000 -0.0002 -0.0005 -0.0024 -0.0070 -0.0145 + -0.0050 0.0015 0.0019 -0.0029 -0.0121 -0.0136 + -0.0050 0.0054 0.0013 0.0056 0.0063 -0.0000 + -0.0084 -0.0124 -0.0023 -0.0012 -0.0062 -0.0078 + 0.0006 0.0045 0.0048 0.0084 0.0124 0.0145 + 0.0173 0.0205 0.0130 0.0071 0.0102 0.0179 + 0.0126 0.0054 -0.0094 0.0025 -0.0025 -0.0166 + -0.0021 0.0228 0.0183 -0.0099 -0.0111 -0.0123 + -0.0120 -0.0098 -0.0070 -0.0053 -0.0030 -0.0048 + -0.0051 -0.0031 0.0009 0.0004 -0.0016 -0.0107 + -0.0129 -0.0066 0.0036 0.0048 0.0015 0.0012 + 0.0030 -0.0055 -0.0066 -0.0023 -0.0030 -0.0007 + 0.0027 0.0078 0.0086 0.0070 0.0101 0.0135 + 0.0124 0.0216 0.0161 0.0074 0.0087 0.0149 + 0.0161 0.0131 0.0078 -0.0079 0.0048 -0.0069 + -0.0132 -0.0016 0.0319 0.0240 -0.0114 -0.0103 + -0.0131 -0.0154 -0.0105 -0.0060 -0.0094 -0.0076 + -0.0022 -0.0014 -0.0002 -0.0002 -0.0023 -0.0033 + -0.0097 -0.0033 -0.0019 0.0055 0.0001 -0.0007 + -0.0086 -0.0082 -0.0063 0.0029 0.0072 0.0071 + 0.0061 0.0140 0.0178 0.0160 0.0136 0.0152 + 0.0189 0.0182 0.0220 0.0201 0.0153 0.0110 + 0.0087 0.0073 0.0117 0.0069 -0.0149 0.0080 + -0.0062 -0.0185 -0.0008 0.0237 0.0083 -0.0104 + -0.0123 -0.0118 -0.0106 -0.0049 0.0025 0.0011 + -0.0009 0.0021 0.0013 0.0016 0.0014 -0.0005 + 0.0006 -0.0033 0.0024 0.0074 0.0009 -0.0040 + -0.0075 -0.0120 -0.0106 0.0043 0.0142 0.0162 + 0.0131 0.0128 0.0202 0.0246 0.0149 0.0068 + 0.0065 0.0058 0.0140 0.0269 0.0311 0.0138 + 0.0024 -0.0052 -0.0062 0.0026 0.0004 -0.0118 + 0.0003 0.0001 -0.0139 0.0065 0.0256 0.0177 + -0.0092 -0.0075 -0.0133 -0.0097 -0.0030 0.0077 + 0.0043 0.0046 -0.0011 -0.0014 0.0012 -0.0021 + -0.0009 -0.0016 0.0036 0.0069 -0.0023 0.0039 + -0.0109 -0.0086 -0.0060 0.0022 0.0148 0.0182 + 0.0134 0.0144 0.0187 0.0197 0.0181 0.0135 + 0.0053 0.0043 0.0135 0.0246 0.0226 0.0140 + 0.0025 -0.0046 -0.0036 -0.0043 -0.0081 0.0108 + -0.0009 -0.0077 -0.0014 -0.0134 0.0091 0.0187 + 0.0182 -0.0004 0.0013 -0.0078 -0.0107 -0.0033 + 0.0008 0.0026 0.0038 0.0007 -0.0001 -0.0023 + -0.0062 -0.0085 -0.0004 0.0099 0.0097 0.0007 + 0.0059 -0.0091 -0.0084 0.0068 0.0153 0.0180 + 0.0133 0.0103 0.0102 0.0142 0.0165 0.0134 + 0.0056 0.0044 0.0037 0.0110 0.0147 0.0119 + 0.0049 0.0008 0.0035 0.0070 -0.0025 -0.0037 + 0.0227 -0.0061 -0.0042 0.0001 -0.0122 0.0085 + 0.0154 0.0151 0.0007 -0.0031 -0.0067 -0.0080 + 0.0023 0.0018 -0.0027 -0.0006 -0.0036 -0.0071 + -0.0089 -0.0072 -0.0072 0.0063 0.0126 0.0161 + 0.0070 -0.0027 -0.0115 0.0002 0.0077 0.0122 + 0.0080 0.0112 0.0143 0.0152 0.0141 0.0140 + 0.0136 0.0111 0.0063 0.0054 0.0067 0.0074 + 0.0109 0.0138 0.0088 0.0088 0.0118 0.0027 + 0.0033 0.0045 -0.0119 0.0004 -0.0078 -0.0018 + 0.0051 0.0055 0.0033 0.0039 -0.0014 -0.0043 + 0.0001 0.0069 -0.0034 -0.0046 -0.0055 -0.0064 + -0.0059 -0.0017 0.0065 0.0068 0.0088 0.0131 + 0.0041 0.0010 -0.0020 0.0034 0.0068 0.0054 + 0.0080 0.0107 0.0155 0.0135 0.0169 0.0161 + 0.0094 0.0124 0.0138 0.0103 0.0079 0.0055 + 0.0060 0.0122 0.0193 0.0169 0.0227 0.0157 + 0.0098 0.0089 -0.0063 -0.0101 -0.0043 -0.0097 + 0.0206 0.0057 0.0016 0.0022 0.0037 0.0029 + 0.0045 0.0092 0.0073 -0.0041 -0.0055 -0.0103 + -0.0107 -0.0068 -0.0028 -0.0004 0.0007 0.0017 + -0.0022 -0.0001 0.0007 -0.0030 0.0064 0.0025 + 0.0045 0.0054 0.0060 0.0132 0.0164 0.0233 + 0.0204 0.0169 0.0172 0.0172 0.0148 0.0136 + 0.0143 0.0147 0.0150 0.0165 0.0156 0.0219 + 0.0157 0.0071 0.0103 -0.0051 -0.0107 -0.0103 + 0.0068 0.0203 0.0130 0.0104 0.0098 0.0038 + 0.0046 0.0099 0.0131 0.0117 -0.0061 -0.0010 + -0.0050 0.0006 0.0028 0.0034 0.0042 0.0030 + 0.0032 -0.0036 -0.0011 0.0054 -0.0062 0.0077 + 0.0073 0.0083 0.0079 0.0095 0.0091 0.0136 + 0.0198 0.0228 0.0248 0.0200 0.0164 0.0180 + 0.0182 0.0207 0.0211 0.0157 0.0134 0.0137 + 0.0103 0.0108 0.0148 0.0091 -0.0106 -0.0164 + -0.0062 0.0124 0.0180 0.0156 0.0117 0.0092 + 0.0060 0.0059 0.0045 0.0056 0.0058 -0.0049 + 0.0024 0.0080 0.0086 0.0108 -0.0004 -0.0023 diff --git a/libres/test-data/local/eclipse/SPE1.DATA b/libres/test-data/local/eclipse/SPE1.DATA new file mode 100644 index 00000000000..619d4397668 --- /dev/null +++ b/libres/test-data/local/eclipse/SPE1.DATA @@ -0,0 +1,419 @@ +-- This reservoir simulation deck is made available under the Open Database +-- License: http://opendatacommons.org/licenses/odbl/1.0/. Any rights in +-- individual contents of the database are licensed under the Database Contents +-- License: http://opendatacommons.org/licenses/dbcl/1.0/ + +-- Copyright (C) 2015 Equinor + +-- This simulation is based on the data given in +-- 'Comparison of Solutions to a Three-Dimensional +-- Black-Oil Reservoir Simulation Problem' by Aziz S. Odeh, +-- Journal of Petroleum Technology, January 1981 + +--------------------------------------------------------------------------- +------------------------ SPE1 - CASE 2 ------------------------------------ +--------------------------------------------------------------------------- + +RUNSPEC +-- ------------------------------------------------------------------------- + +TITLE + SPE1 - CASE 2 + +DIMENS + 10 10 3 / + +-- The number of equilibration regions is inferred from the EQLDIMS +-- keyword. +EQLDIMS +/ + +-- The number of PVTW tables is inferred from the TABDIMS keyword; +-- when no data is included in the keyword the default values are used. +TABDIMS +/ + + +OIL +GAS +WATER +DISGAS +-- As seen from figure 4 in Odeh, GOR is increasing with time, +-- which means that dissolved gas is present + + +FIELD + +START + 1 'JAN' 2015 / + +WELLDIMS +-- Item 1: maximum number of wells in the model +-- - there are two wells in the problem; injector and producer +-- Item 2: maximum number of grid blocks connected to any one well +-- - must be one as the wells are located at specific grid blocks +-- Item 3: maximum number of groups in the model +-- - we are dealing with only one 'group' +-- Item 4: maximum number of wells in any one group +-- - there must be two wells in a group as there are two wells in total + 2 1 1 2 / + +UNIFOUT + +GRID +-- ------------------------------------------------------------------------- +NOECHO + +DX +-- There are in total 300 cells with length 1000ft in x-direction + 300*1000 / +DY +-- There are in total 300 cells with length 1000ft in y-direction + 300*1000 / +DZ +-- The layers are 20, 30 and 50 ft thick, in each layer there are 100 cells + 100*20 100*30 100*50 / + +TOPS +-- The depth of the top of each grid block + 100*8325 / + +PORO +-- Constant porosity of 0.3 throughout all 300 grid cells + 300*0.3 / + +PERMX +-- The layers have perm. 500mD, 50mD and 200mD, respectively. + 100*500 100*50 100*200 / + +PERMY +-- Equal to PERMX + 100*500 100*50 100*200 / + +PERMZ +-- Cannot find perm. in z-direction in Odeh's paper +-- For the time being, we will assume PERMZ equal to PERMX and PERMY: + 100*500 100*50 100*200 / +ECHO + +PROPS +-- ------------------------------------------------------------------------- + +PVTW +-- Item 1: pressure reference (psia) +-- Item 2: water FVF (rb per bbl or rb per stb) +-- Item 3: water compressibility (psi^{-1}) +-- Item 4: water viscosity (cp) +-- Item 5: water 'viscosibility' (psi^{-1}) + +-- Using values from Norne: +-- In METRIC units: +-- 277.0 1.038 4.67E-5 0.318 0.0 / +-- In FIELD units: + 4017.55 1.038 3.22E-6 0.318 0.0 / + +ROCK +-- Item 1: reference pressure (psia) +-- Item 2: rock compressibility (psi^{-1}) + +-- Using values from table 1 in Odeh: + 14.7 3E-6 / + +SWOF +-- Column 1: water saturation +-- - this has been set to (almost) equally spaced values from 0.12 to 1 +-- Column 2: water relative permeability +-- - generated from the Corey-type approx. formula +-- the coeffisient is set to 10e-5, S_{orw}=0 and S_{wi}=0.12 +-- Column 3: oil relative permeability when only oil and water are present +-- - we will use the same values as in column 3 in SGOF. +-- This is not really correct, but since only the first +-- two values are of importance, this does not really matter +-- Column 4: corresponding water-oil capillary pressure (psi) + +0.12 0 1 0 +0.18 4.64876033057851E-008 1 0 +0.24 0.000000186 0.997 0 +0.3 4.18388429752066E-007 0.98 0 +0.36 7.43801652892562E-007 0.7 0 +0.42 1.16219008264463E-006 0.35 0 +0.48 1.67355371900826E-006 0.2 0 +0.54 2.27789256198347E-006 0.09 0 +0.6 2.97520661157025E-006 0.021 0 +0.66 3.7654958677686E-006 0.01 0 +0.72 4.64876033057851E-006 0.001 0 +0.78 0.000005625 0.0001 0 +0.84 6.69421487603306E-006 0 0 +0.91 8.05914256198347E-006 0 0 +1 0.00001 0 0 / + + +SGOF +-- Column 1: gas saturation +-- Column 2: gas relative permeability +-- Column 3: oil relative permeability when oil, gas and connate water are present +-- Column 4: oil-gas capillary pressure (psi) +-- - stated to be zero in Odeh's paper + +-- Values in column 1-3 are taken from table 3 in Odeh's paper: +0 0 1 0 +0.001 0 1 0 +0.02 0 0.997 0 +0.05 0.005 0.980 0 +0.12 0.025 0.700 0 +0.2 0.075 0.350 0 +0.25 0.125 0.200 0 +0.3 0.190 0.090 0 +0.4 0.410 0.021 0 +0.45 0.60 0.010 0 +0.5 0.72 0.001 0 +0.6 0.87 0.0001 0 +0.7 0.94 0.000 0 +0.85 0.98 0.000 0 +0.88 0.984 0.000 0 / +--1.00 1.0 0.000 0 / +-- Warning from Eclipse: first sat. value in SWOF + last sat. value in SGOF +-- must not be greater than 1, but Eclipse still runs +-- Flow needs the sum to be excactly 1 so I added a row with gas sat. = 0.88 +-- The corresponding krg value was estimated by assuming linear rel. between +-- gas sat. and krw. between gas sat. 0.85 and 1.00 (the last two values given) + +DENSITY +-- Density (lb per ft³) at surface cond. of +-- oil, water and gas, respectively (in that order) + +-- Using values from Norne: +-- In METRIC units: +-- 859.5 1033.0 0.854 / +-- In FIELD units: + 53.66 64.49 0.0533 / + +PVDG +-- Column 1: gas phase pressure (psia) +-- Column 2: gas formation volume factor (rb per Mscf) +-- - in Odeh's paper the units are said to be given in rb per bbl, +-- but this is assumed to be a mistake: FVF-values in Odeh's paper +-- are given in rb per scf, not rb per bbl. This will be in +-- agreement with conventions +-- Column 3: gas viscosity (cP) + +-- Using values from lower right table in Odeh's table 2: +14.700 166.666 0.008000 +264.70 12.0930 0.009600 +514.70 6.27400 0.011200 +1014.7 3.19700 0.014000 +2014.7 1.61400 0.018900 +2514.7 1.29400 0.020800 +3014.7 1.08000 0.022800 +4014.7 0.81100 0.026800 +5014.7 0.64900 0.030900 +9014.7 0.38600 0.047000 / + +PVTO +-- Column 1: dissolved gas-oil ratio (Mscf per stb) +-- Column 2: bubble point pressure (psia) +-- Column 3: oil FVF for saturated oil (rb per stb) +-- Column 4: oil viscosity for saturated oil (cP) + +-- Use values from top left table in Odeh's table 2: +0.0010 14.7 1.0620 1.0400 / +0.0905 264.7 1.1500 0.9750 / +0.1800 514.7 1.2070 0.9100 / +0.3710 1014.7 1.2950 0.8300 / +0.6360 2014.7 1.4350 0.6950 / +0.7750 2514.7 1.5000 0.6410 / +0.9300 3014.7 1.5650 0.5940 / +1.2700 4014.7 1.6950 0.5100 + 9014.7 1.5790 0.7400 / +1.6180 5014.7 1.8270 0.4490 + 9014.7 1.7370 0.6310 / +-- It is required to enter data for undersaturated oil for the highest GOR +-- (i.e. the last row) in the PVTO table. +-- In order to fulfill this requirement, values for oil FVF and viscosity +-- at 9014.7psia and GOR=1.618 for undersaturated oil have been approximated: +-- It has been assumed that there is a linear relation between the GOR +-- and the FVF when keeping the pressure constant at 9014.7psia. +-- From Odeh we know that (at 9014.7psia) the FVF is 2.357 at GOR=2.984 +-- for saturated oil and that the FVF is 1.579 at GOR=1.27 for undersaturated oil, +-- so it is possible to use the assumption described above. +-- An equivalent approximation for the viscosity has been used. +/ + +SOLUTION +-- ------------------------------------------------------------------------- + +EQUIL +-- Item 1: datum depth (ft) +-- Item 2: pressure at datum depth (psia) +-- - Odeh's table 1 says that initial reservoir pressure is +-- 4800 psi at 8400ft, which explains choice of item 1 and 2 +-- Item 3: depth of water-oil contact (ft) +-- - chosen to be directly under the reservoir +-- Item 4: oil-water capillary pressure at the water oil contact (psi) +-- - given to be 0 in Odeh's paper +-- Item 5: depth of gas-oil contact (ft) +-- - chosen to be directly above the reservoir +-- Item 6: gas-oil capillary pressure at gas-oil contact (psi) +-- - given to be 0 in Odeh's paper +-- Item 7: RSVD-table +-- Item 8: RVVD-table +-- Item 9: Set to 0 as this is the only value supported by OPM + +-- Item #: 1 2 3 4 5 6 7 8 9 + 8400 4800 8450 0 8300 0 1 0 0 / + +RSVD +-- Dissolved GOR is initially constant with depth through the reservoir. +-- The reason is that the initial reservoir pressure given is higher +---than the bubble point presssure of 4014.7psia, meaning that there is no +-- free gas initially present. +8300 1.270 +8450 1.270 / + +SUMMARY +-- ------------------------------------------------------------------------- + +-- 1a) Oil rate vs time +FOPR +-- Field Oil Production Rate + +-- 1b) GOR vs time +WGOR +-- Well Gas-Oil Ratio + 'PROD' +/ +-- Using FGOR instead of WGOR:PROD results in the same graph +FGOR + +-- 2a) Pressures of the cell where the injector and producer are located +BPR +1 1 1 / +10 10 3 / +/ + +-- 2b) Gas saturation at grid points given in Odeh's paper +BGSAT +1 1 1 / +1 1 2 / +1 1 3 / +10 1 1 / +10 1 2 / +10 1 3 / +10 10 1 / +10 10 2 / +10 10 3 / +/ + +-- In order to compare Eclipse with Flow: +WBHP + 'INJ' + 'PROD' +/ +WGIR + 'INJ' + 'PROD' +/ +WGIT + 'INJ' + 'PROD' +/ +WGPR + 'INJ' + 'PROD' +/ +WGPT + 'INJ' + 'PROD' +/ +WOIR + 'INJ' + 'PROD' +/ +WOIT + 'INJ' + 'PROD' +/ +WOPR + 'INJ' + 'PROD' +/ +WOPT + 'INJ' + 'PROD' +/ +WWIR + 'INJ' + 'PROD' +/ +WWIT + 'INJ' + 'PROD' +/ +WWPR + 'INJ' + 'PROD' +/ +WWPT + 'INJ' + 'PROD' +/ +SCHEDULE +-- ------------------------------------------------------------------------- +RPTSCHED + 'PRES' 'SGAS' 'RS' 'WELLS' / + +RPTRST + 'BASIC=1' / + + +-- If no resolution (i.e. case 1), the two following lines must be added: +--DRSDT +-- 0 / +-- Since this is Case 2, the two lines above have been commented out. +-- if DRSDT is set to 0, GOR cannot rise and free gas does not +-- dissolve in undersaturated oil -> constant bubble point pressure + +WELSPECS +-- Item #: 1 2 3 4 5 6 + 'PROD' 'G1' 10 10 8400 'OIL' / + 'INJ' 'G1' 1 1 8335 'GAS' / +/ +-- Coordinates in item 3-4 are retrieved from Odeh's figure 1 and 2 +-- Note that the depth at the midpoint of the well grid blocks +-- has been used as reference depth for bottom hole pressure in item 5 + +COMPDAT +-- Item #: 1 2 3 4 5 6 7 8 9 + 'PROD' 10 10 3 3 'OPEN' 1* 1* 0.5 / + 'INJ' 1 1 1 1 'OPEN' 1* 1* 0.5 / +/ +-- Coordinates in item 2-5 are retreived from Odeh's figure 1 and 2 +-- Item 9 is the well bore internal diameter, +-- the radius is given to be 0.25ft in Odeh's paper + + +WCONPROD +-- Item #:1 2 3 4 5 9 + 'PROD' 'OPEN' 'ORAT' 20000 4* 1000 / +/ +-- It is stated in Odeh's paper that the maximum oil prod. rate +-- is 20 000stb per day which explains the choice of value in item 4. +-- The items > 4 are defaulted with the exception of item 9, +-- the BHP lower limit, which is given to be 1000psia in Odeh's paper + +WCONINJE +-- Item #:1 2 3 4 5 6 7 + 'INJ' 'GAS' 'OPEN' 'RATE' 100000 1* 9014 / +/ +-- Stated in Odeh that gas inj. rate (item 5) is 100MMscf per day +-- BHP upper limit (item 7) should not be exceeding the highest +-- pressure in the PVT table=9014.7psia (default is 100 000psia) + +TSTEP +--Advance the simulater once a month for TEN years: +31 28 31 30 31 30 31 31 30 31 30 31 / + +--Advance the simulator once a year for TEN years: +--10*365 / + +END diff --git a/libres/test-data/local/eclipse/SPE1_ERROR.DATA b/libres/test-data/local/eclipse/SPE1_ERROR.DATA new file mode 100644 index 00000000000..362aa9015ff --- /dev/null +++ b/libres/test-data/local/eclipse/SPE1_ERROR.DATA @@ -0,0 +1,419 @@ +-- This reservoir simulation deck is made available under the Open Database +-- License: http://opendatacommons.org/licenses/odbl/1.0/. Any rights in +-- individual contents of the database are licensed under the Database Contents +-- License: http://opendatacommons.org/licenses/dbcl/1.0/ + +-- Copyright (C) 2015 Equinor + +-- This simulation is based on the data given in +-- 'Comparison of Solutions to a Three-Dimensional +-- Black-Oil Reservoir Simulation Problem' by Aziz S. Odeh, +-- Journal of Petroleum Technology, January 1981 + +--------------------------------------------------------------------------- +------------------------ SPE1 - CASE 2 ------------------------------------ +--------------------------------------------------------------------------- + +RUNSPEC +-- ------------------------------------------------------------------------- + +TITLE + SPE1 - CASE 2 + +DIMENS + 10 10 3 / + +-- The number of equilibration regions is inferred from the EQLDIMS +-- keyword. +EQLDIMS +/ + +-- The number of PVTW tables is inferred from the TABDIMS keyword; +-- when no data is included in the keyword the default values are used. +TABDIMS +/ + + +OIL +GAS +WATER +DISGAS +-- As seen from figure 4 in Odeh, GOR is increasing with time, +-- which means that dissolved gas is present + + +FIELD + +START + 1 'JAN' 2015 / + +WELLDIMS +-- Item 1: maximum number of wells in the model +-- - there are two wells in the problem; injector and producer +-- Item 2: maximum number of grid blocks connected to any one well +-- - must be one as the wells are located at specific grid blocks +-- Item 3: maximum number of groups in the model +-- - we are dealing with only one 'group' +-- Item 4: maximum number of wells in any one group +-- - there must be two wells in a group as there are two wells in total + 2 1 1 2 / + +UNIFOUT + +GRID +-- ------------------------------------------------------------------------- +NOECHO + +DX +-- There are in total 300 cells with length 1000ft in x-direction + 300*1000 / +DY +-- There are in total 300 cells with length 1000ft in y-direction + 300*1000 / +DZ +-- The layers are 20, 30 and 50 ft thick, in each layer there are 100 cells + 100*20 100*30 100*50 / + +TOPS +-- The depth of the top of each grid block + 100*8325 / + +PORO +-- Constant porosity of 0.3 throughout all 300 grid cells + 300*0.3 / + +PERMX +-- The layers have perm. 500mD, 50mD and 200mD, respectively. + 100*500 100*50 100*200 / + +PERMY +-- Equal to PERMX + 100*500 100*50 100*200 / + +PERMZ +-- Cannot find perm. in z-direction in Odeh's paper +-- For the time being, we will assume PERMZ equal to PERMX and PERMY: + 100*500 100*50 100*200 / +ECHO + +PROPS +-- ------------------------------------------------------------------------- + +PVTW +-- Item 1: pressure reference (psia) +-- Item 2: water FVF (rb per bbl or rb per stb) +-- Item 3: water compressibility (psi^{-1}) +-- Item 4: water viscosity (cp) +-- Item 5: water 'viscosibility' (psi^{-1}) + +-- Using values from Norne: +-- In METRIC units: +-- 277.0 1.038 4.67E-5 0.318 0.0 / +-- In FIELD units: + 4017.55 1.038 3.22E-6 0.318 0.0 / + +ROCK +-- Item 1: reference pressure (psia) +-- Item 2: rock compressibility (psi^{-1}) + +-- Using values from table 1 in Odeh: + 14.7 3E-6 / + +SWOF +-- Column 1: water saturation +-- - this has been set to (almost) equally spaced values from 0.12 to 1 +-- Column 2: water relative permeability +-- - generated from the Corey-type approx. formula +-- the coeffisient is set to 10e-5, S_{orw}=0 and S_{wi}=0.12 +-- Column 3: oil relative permeability when only oil and water are present +-- - we will use the same values as in column 3 in SGOF. +-- This is not really correct, but since only the first +-- two values are of importance, this does not really matter +-- Column 4: corresponding water-oil capillary pressure (psi) + +0.12 0 1 0 +0.18 4.64876033057851E-008 1 0 +0.24 0.000000186 0.997 0 +0.3 4.18388429752066E-007 0.98 0 +0.36 7.43801652892562E-007 0.7 0 +0.42 1.16219008264463E-006 0.35 0 +0.48 1.67355371900826E-006 0.2 0 +0.54 2.27789256198347E-006 0.09 0 +0.6 2.97520661157025E-006 0.021 0 +0.66 3.7654958677686E-006 0.01 0 +0.72 4.64876033057851E-006 0.001 0 +0.78 0.000005625 0.0001 0 +0.84 6.69421487603306E-006 0 0 +0.91 8.05914256198347E-006 0 0 +1 0.00001 0 0 / + + +SGOF +-- Column 1: gas saturation +-- Column 2: gas relative permeability +-- Column 3: oil relative permeability when oil, gas and connate water are present +-- Column 4: oil-gas capillary pressure (psi) +-- - stated to be zero in Odeh's paper + +-- Values in column 1-3 are taken from table 3 in Odeh's paper: +0 0 1 0 +0.001 0 1 0 +0.02 0 0.997 0 +0.05 0.005 0.980 0 +0.12 0.025 0.700 0 +0.2 0.075 0.350 0 +0.25 0.125 0.200 0 +0.3 0.190 0.090 0 +0.4 0.410 0.021 0 +0.45 0.60 0.010 0 +0.5 0.72 0.001 0 +0.6 0.87 0.0001 0 +0.7 0.94 0.000 0 +0.85 0.98 0.000 0 +0.88 0.984 0.000 0 / +--1.00 1.0 0.000 0 / +-- Warning from Eclipse: first sat. value in SWOF + last sat. value in SGOF +-- must not be greater than 1, but Eclipse still runs +-- Flow needs the sum to be excactly 1 so I added a row with gas sat. = 0.88 +-- The corresponding krg value was estimated by assuming linear rel. between +-- gas sat. and krw. between gas sat. 0.85 and 1.00 (the last two values given) + +DENSITY +-- Density (lb per ft³) at surface cond. of +-- oil, water and gas, respectively (in that order) + +-- Using values from Norne: +-- In METRIC units: +-- 859.5 1033.0 0.854 / +-- In FIELD units: + 53.66 64.49 0.0533 / + +PVDG +-- Column 1: gas phase pressure (psia) +-- Column 2: gas formation volume factor (rb per Mscf) +-- - in Odeh's paper the units are said to be given in rb per bbl, +-- but this is assumed to be a mistake: FVF-values in Odeh's paper +-- are given in rb per scf, not rb per bbl. This will be in +-- agreement with conventions +-- Column 3: gas viscosity (cP) + +-- Using values from lower right table in Odeh's table 2: +14.700 166.666 0.008000 +264.70 12.0930 0.009600 +514.70 6.27400 0.011200 +1014.7 3.19700 0.014000 +2014.7 1.61400 0.018900 +2514.7 1.29400 0.020800 +3014.7 1.08000 0.022800 +4014.7 0.81100 0.026800 +5014.7 0.64900 0.030900 +9014.7 0.38600 0.047000 / + +PVTO +-- Column 1: dissolved gas-oil ratio (Mscf per stb) +-- Column 2: bubble point pressure (psia) +-- Column 3: oil FVF for saturated oil (rb per stb) +-- Column 4: oil viscosity for saturated oil (cP) + +-- Use values from top left table in Odeh's table 2: +0.0010 14.7 1.0620 1.0400 / +0.0905 264.7 1.1500 0.9750 / +0.1800 514.7 1.2070 0.9100 / +0.3710 1014.7 1.2950 0.8300 / +0.6360 2014.7 1.4350 0.6950 / +0.7750 2514.7 1.5000 0.6410 / +0.9300 3014.7 1.5650 0.5940 / +1.2700 4014.7 1.6950 0.5100 + 9014.7 1.5790 0.7400 / +1.6180 5014.7 1.8270 0.4490 + 9014.7 1.7370 0.6310 / +-- It is required to enter data for undersaturated oil for the highest GOR +-- (i.e. the last row) in the PVTO table. +-- In order to fulfill this requirement, values for oil FVF and viscosity +-- at 9014.7psia and GOR=1.618 for undersaturated oil have been approximated: +-- It has been assumed that there is a linear relation between the GOR +-- and the FVF when keeping the pressure constant at 9014.7psia. +-- From Odeh we know that (at 9014.7psia) the FVF is 2.357 at GOR=2.984 +-- for saturated oil and that the FVF is 1.579 at GOR=1.27 for undersaturated oil, +-- so it is possible to use the assumption described above. +-- An equivalent approximation for the viscosity has been used. +/ + +SOLUTION +-- ------------------------------------------------------------------------- + +EQUIL +-- Item 1: datum depth (ft) +-- Item 2: pressure at datum depth (psia) +-- - Odeh's table 1 says that initial reservoir pressure is +-- 4800 psi at 8400ft, which explains choice of item 1 and 2 +-- Item 3: depth of water-oil contact (ft) +-- - chosen to be directly under the reservoir +-- Item 4: oil-water capillary pressure at the water oil contact (psi) +-- - given to be 0 in Odeh's paper +-- Item 5: depth of gas-oil contact (ft) +-- - chosen to be directly above the reservoir +-- Item 6: gas-oil capillary pressure at gas-oil contact (psi) +-- - given to be 0 in Odeh's paper +-- Item 7: RSVD-table +-- Item 8: RVVD-table +-- Item 9: Set to 0 as this is the only value supported by OPM + +-- Item #: 1 2 3 4 5 6 7 8 9 + 8400 4800 8450 0 8300 0 1 0 0 / + +RSVD +-- Dissolved GOR is initially constant with depth through the reservoir. +-- The reason is that the initial reservoir pressure given is higher +---than the bubble point presssure of 4014.7psia, meaning that there is no +-- free gas initially present. +8300 1.270 +8450 1.270 / + +SUMMARY +-- ------------------------------------------------------------------------- + +-- 1a) Oil rate vs time +FOPR +-- Field Oil Production Rate + +-- 1b) GOR vs time +WGOR +-- Well Gas-Oil Ratio + 'PROD' +/ +-- Using FGOR instead of WGOR:PROD results in the same graph +FGOR + +-- 2a) Pressures of the cell where the injector and producer are located +BPR +1 1 1 / +10 10 3 / +/ + +-- 2b) Gas saturation at grid points given in Odeh's paper +BGSAT +1 1 1 / +1 1 2 / +1 1 3 / +10 1 1 / +10 1 2 / +10 1 3 / +10 10 1 / +10 10 2 / +10 10 3 / +/ + +-- In order to compare Eclipse with Flow: +WBHP + 'INJ' + 'PROD' +/ +WGIR + 'INJ' + 'PROD' +/ +WGIT + 'INJ' + 'PROD' +/ +WGPR + 'INJ' + 'PROD' +/ +WGPT + 'INJ' + 'PROD' +/ +WOIR + 'INJ' + 'PROD' +/ +WOIT + 'INJ' + 'PROD' +/ +WOPR + 'INJ' + 'PROD' +/ +WOPT + 'INJ' + 'PROD' +/ +WWIR + 'INJ' + 'PROD' +/ +WWIT + 'INJ' + 'PROD' +/ +WWPR + 'INJ' + 'PROD' +/ +WWPT + 'INJ' + 'PROD' +/ +SCHEDULE +-- ------------------------------------------------------------------------- +RPTSCHED + 'PRES' 'SGAS' 'RS' 'WELLS' / + +RPTRST + 'BASIC=1' / + + +-- If no resolution (i.e. case 1), the two following lines must be added: +--DRSDT +-- 0 / +-- Since this is Case 2, the two lines above have been commented out. +-- if DRSDT is set to 0, GOR cannot rise and free gas does not +-- dissolve in undersaturated oil -> constant bubble point pressure + +WELSPECS +-- Item #: 1 2 3 4 5 6 + 'PROD' 'G1' 10 10 8400 'OIL' / + 'INJ' 'G1' 1 1 8335 'GAS' / +/ +-- Coordinates in item 3-4 are retrieved from Odeh's figure 1 and 2 +-- Note that the depth at the midpoint of the well grid blocks +-- has been used as reference depth for bottom hole pressure in item 5 + +COMPDAT +-- Item #: 1 2 3 4 5 6 7 8 9 + 'PROD' 10 10 3 3 'OPEN' 1* 1* 0.5 / + 'INJ' 1 1 1 1 'OPEN' 1* 1* 0.5 / +/ +-- Coordinates in item 2-5 are retreived from Odeh's figure 1 and 2 +-- Item 9 is the well bore internal diameter, +-- the radius is given to be 0.25ft in Odeh's paper + + +WCONPROD +-- Item #:1 2 3 4 5 9 + 'PROD' 'OPEN' 'ORAT' 20000 4* 1000 / +/ +-- It is stated in Odeh's paper that the maximum oil prod. rate +-- is 20 000stb per day which explains the choice of value in item 4. +-- The items > 4 are defaulted with the exception of item 9, +-- the BHP lower limit, which is given to be 1000psia in Odeh's paper + +WCONINJE +-- Item #:1 2 3 4 5 6 7 + 'INJX' 'GAS' 'OPEN' 'RATE' 100000 1* 9014 / +/ +-- Stated in Odeh that gas inj. rate (item 5) is 100MMscf per day +-- BHP upper limit (item 7) should not be exceeding the highest +-- pressure in the PVT table=9014.7psia (default is 100 000psia) + +TSTEP +--Advance the simulater once a month for TEN years: +31 28 31 30 31 30 31 31 30 31 30 31 / + +--Advance the simulator once a year for TEN years: +--10*365 / + +END diff --git a/libres/test-data/local/eclipse/SPE1_PARALLELL.DATA b/libres/test-data/local/eclipse/SPE1_PARALLELL.DATA new file mode 100644 index 00000000000..83114d59dfe --- /dev/null +++ b/libres/test-data/local/eclipse/SPE1_PARALLELL.DATA @@ -0,0 +1,425 @@ +-- This reservoir simulation deck is made available under the Open Database +-- License: http://opendatacommons.org/licenses/odbl/1.0/. Any rights in +-- individual contents of the database are licensed under the Database Contents +-- License: http://opendatacommons.org/licenses/dbcl/1.0/ + +-- Copyright (C) 2015 Equinor + +-- This simulation is based on the data given in +-- 'Comparison of Solutions to a Three-Dimensional +-- Black-Oil Reservoir Simulation Problem' by Aziz S. Odeh, +-- Journal of Petroleum Technology, January 1981 + +--------------------------------------------------------------------------- +------------------------ SPE1 - CASE 2 ------------------------------------ +--------------------------------------------------------------------------- + +RUNSPEC +-- ------------------------------------------------------------------------- + +TITLE + SPE1 - CASE 2 + +PARALLEL + 2 DISTRIBUTED / + + + + +DIMENS + 10 10 3 / + +-- The number of equilibration regions is inferred from the EQLDIMS +-- keyword. +EQLDIMS +/ + +-- The number of PVTW tables is inferred from the TABDIMS keyword; +-- when no data is included in the keyword the default values are used. +TABDIMS +/ + + +OIL +GAS +WATER +DISGAS +-- As seen from figure 4 in Odeh, GOR is increasing with time, +-- which means that dissolved gas is present + + +FIELD + +START + 1 'JAN' 2015 / + +WELLDIMS +-- Item 1: maximum number of wells in the model +-- - there are two wells in the problem; injector and producer +-- Item 2: maximum number of grid blocks connected to any one well +-- - must be one as the wells are located at specific grid blocks +-- Item 3: maximum number of groups in the model +-- - we are dealing with only one 'group' +-- Item 4: maximum number of wells in any one group +-- - there must be two wells in a group as there are two wells in total + 2 1 1 2 / + +UNIFOUT + +GRID +-- ------------------------------------------------------------------------- +NOECHO + +DX +-- There are in total 300 cells with length 1000ft in x-direction + 300*1000 / +DY +-- There are in total 300 cells with length 1000ft in y-direction + 300*1000 / +DZ +-- The layers are 20, 30 and 50 ft thick, in each layer there are 100 cells + 100*20 100*30 100*50 / + +TOPS +-- The depth of the top of each grid block + 100*8325 / + +PORO +-- Constant porosity of 0.3 throughout all 300 grid cells + 300*0.3 / + +PERMX +-- The layers have perm. 500mD, 50mD and 200mD, respectively. + 100*500 100*50 100*200 / + +PERMY +-- Equal to PERMX + 100*500 100*50 100*200 / + +PERMZ +-- Cannot find perm. in z-direction in Odeh's paper +-- For the time being, we will assume PERMZ equal to PERMX and PERMY: + 100*500 100*50 100*200 / +ECHO + +PROPS +-- ------------------------------------------------------------------------- + +PVTW +-- Item 1: pressure reference (psia) +-- Item 2: water FVF (rb per bbl or rb per stb) +-- Item 3: water compressibility (psi^{-1}) +-- Item 4: water viscosity (cp) +-- Item 5: water 'viscosibility' (psi^{-1}) + +-- Using values from Norne: +-- In METRIC units: +-- 277.0 1.038 4.67E-5 0.318 0.0 / +-- In FIELD units: + 4017.55 1.038 3.22E-6 0.318 0.0 / + +ROCK +-- Item 1: reference pressure (psia) +-- Item 2: rock compressibility (psi^{-1}) + +-- Using values from table 1 in Odeh: + 14.7 3E-6 / + +SWOF +-- Column 1: water saturation +-- - this has been set to (almost) equally spaced values from 0.12 to 1 +-- Column 2: water relative permeability +-- - generated from the Corey-type approx. formula +-- the coeffisient is set to 10e-5, S_{orw}=0 and S_{wi}=0.12 +-- Column 3: oil relative permeability when only oil and water are present +-- - we will use the same values as in column 3 in SGOF. +-- This is not really correct, but since only the first +-- two values are of importance, this does not really matter +-- Column 4: corresponding water-oil capillary pressure (psi) + +0.12 0 1 0 +0.18 4.64876033057851E-008 1 0 +0.24 0.000000186 0.997 0 +0.3 4.18388429752066E-007 0.98 0 +0.36 7.43801652892562E-007 0.7 0 +0.42 1.16219008264463E-006 0.35 0 +0.48 1.67355371900826E-006 0.2 0 +0.54 2.27789256198347E-006 0.09 0 +0.6 2.97520661157025E-006 0.021 0 +0.66 3.7654958677686E-006 0.01 0 +0.72 4.64876033057851E-006 0.001 0 +0.78 0.000005625 0.0001 0 +0.84 6.69421487603306E-006 0 0 +0.91 8.05914256198347E-006 0 0 +1 0.00001 0 0 / + + +SGOF +-- Column 1: gas saturation +-- Column 2: gas relative permeability +-- Column 3: oil relative permeability when oil, gas and connate water are present +-- Column 4: oil-gas capillary pressure (psi) +-- - stated to be zero in Odeh's paper + +-- Values in column 1-3 are taken from table 3 in Odeh's paper: +0 0 1 0 +0.001 0 1 0 +0.02 0 0.997 0 +0.05 0.005 0.980 0 +0.12 0.025 0.700 0 +0.2 0.075 0.350 0 +0.25 0.125 0.200 0 +0.3 0.190 0.090 0 +0.4 0.410 0.021 0 +0.45 0.60 0.010 0 +0.5 0.72 0.001 0 +0.6 0.87 0.0001 0 +0.7 0.94 0.000 0 +0.85 0.98 0.000 0 +0.88 0.984 0.000 0 / +--1.00 1.0 0.000 0 / +-- Warning from Eclipse: first sat. value in SWOF + last sat. value in SGOF +-- must not be greater than 1, but Eclipse still runs +-- Flow needs the sum to be excactly 1 so I added a row with gas sat. = 0.88 +-- The corresponding krg value was estimated by assuming linear rel. between +-- gas sat. and krw. between gas sat. 0.85 and 1.00 (the last two values given) + +DENSITY +-- Density (lb per ft³) at surface cond. of +-- oil, water and gas, respectively (in that order) + +-- Using values from Norne: +-- In METRIC units: +-- 859.5 1033.0 0.854 / +-- In FIELD units: + 53.66 64.49 0.0533 / + +PVDG +-- Column 1: gas phase pressure (psia) +-- Column 2: gas formation volume factor (rb per Mscf) +-- - in Odeh's paper the units are said to be given in rb per bbl, +-- but this is assumed to be a mistake: FVF-values in Odeh's paper +-- are given in rb per scf, not rb per bbl. This will be in +-- agreement with conventions +-- Column 3: gas viscosity (cP) + +-- Using values from lower right table in Odeh's table 2: +14.700 166.666 0.008000 +264.70 12.0930 0.009600 +514.70 6.27400 0.011200 +1014.7 3.19700 0.014000 +2014.7 1.61400 0.018900 +2514.7 1.29400 0.020800 +3014.7 1.08000 0.022800 +4014.7 0.81100 0.026800 +5014.7 0.64900 0.030900 +9014.7 0.38600 0.047000 / + +PVTO +-- Column 1: dissolved gas-oil ratio (Mscf per stb) +-- Column 2: bubble point pressure (psia) +-- Column 3: oil FVF for saturated oil (rb per stb) +-- Column 4: oil viscosity for saturated oil (cP) + +-- Use values from top left table in Odeh's table 2: +0.0010 14.7 1.0620 1.0400 / +0.0905 264.7 1.1500 0.9750 / +0.1800 514.7 1.2070 0.9100 / +0.3710 1014.7 1.2950 0.8300 / +0.6360 2014.7 1.4350 0.6950 / +0.7750 2514.7 1.5000 0.6410 / +0.9300 3014.7 1.5650 0.5940 / +1.2700 4014.7 1.6950 0.5100 + 9014.7 1.5790 0.7400 / +1.6180 5014.7 1.8270 0.4490 + 9014.7 1.7370 0.6310 / +-- It is required to enter data for undersaturated oil for the highest GOR +-- (i.e. the last row) in the PVTO table. +-- In order to fulfill this requirement, values for oil FVF and viscosity +-- at 9014.7psia and GOR=1.618 for undersaturated oil have been approximated: +-- It has been assumed that there is a linear relation between the GOR +-- and the FVF when keeping the pressure constant at 9014.7psia. +-- From Odeh we know that (at 9014.7psia) the FVF is 2.357 at GOR=2.984 +-- for saturated oil and that the FVF is 1.579 at GOR=1.27 for undersaturated oil, +-- so it is possible to use the assumption described above. +-- An equivalent approximation for the viscosity has been used. +/ + +SOLUTION +-- ------------------------------------------------------------------------- + +EQUIL +-- Item 1: datum depth (ft) +-- Item 2: pressure at datum depth (psia) +-- - Odeh's table 1 says that initial reservoir pressure is +-- 4800 psi at 8400ft, which explains choice of item 1 and 2 +-- Item 3: depth of water-oil contact (ft) +-- - chosen to be directly under the reservoir +-- Item 4: oil-water capillary pressure at the water oil contact (psi) +-- - given to be 0 in Odeh's paper +-- Item 5: depth of gas-oil contact (ft) +-- - chosen to be directly above the reservoir +-- Item 6: gas-oil capillary pressure at gas-oil contact (psi) +-- - given to be 0 in Odeh's paper +-- Item 7: RSVD-table +-- Item 8: RVVD-table +-- Item 9: Set to 0 as this is the only value supported by OPM + +-- Item #: 1 2 3 4 5 6 7 8 9 + 8400 4800 8450 0 8300 0 1 0 0 / + +RSVD +-- Dissolved GOR is initially constant with depth through the reservoir. +-- The reason is that the initial reservoir pressure given is higher +---than the bubble point presssure of 4014.7psia, meaning that there is no +-- free gas initially present. +8300 1.270 +8450 1.270 / + +SUMMARY +-- ------------------------------------------------------------------------- + +-- 1a) Oil rate vs time +FOPR +-- Field Oil Production Rate + +-- 1b) GOR vs time +WGOR +-- Well Gas-Oil Ratio + 'PROD' +/ +-- Using FGOR instead of WGOR:PROD results in the same graph +FGOR + +-- 2a) Pressures of the cell where the injector and producer are located +BPR +1 1 1 / +10 10 3 / +/ + +-- 2b) Gas saturation at grid points given in Odeh's paper +BGSAT +1 1 1 / +1 1 2 / +1 1 3 / +10 1 1 / +10 1 2 / +10 1 3 / +10 10 1 / +10 10 2 / +10 10 3 / +/ + +-- In order to compare Eclipse with Flow: +WBHP + 'INJ' + 'PROD' +/ +WGIR + 'INJ' + 'PROD' +/ +WGIT + 'INJ' + 'PROD' +/ +WGPR + 'INJ' + 'PROD' +/ +WGPT + 'INJ' + 'PROD' +/ +WOIR + 'INJ' + 'PROD' +/ +WOIT + 'INJ' + 'PROD' +/ +WOPR + 'INJ' + 'PROD' +/ +WOPT + 'INJ' + 'PROD' +/ +WWIR + 'INJ' + 'PROD' +/ +WWIT + 'INJ' + 'PROD' +/ +WWPR + 'INJ' + 'PROD' +/ +WWPT + 'INJ' + 'PROD' +/ +SCHEDULE +-- ------------------------------------------------------------------------- +RPTSCHED + 'PRES' 'SGAS' 'RS' 'WELLS' / + +RPTRST + 'BASIC=1' / + + +-- If no resolution (i.e. case 1), the two following lines must be added: +--DRSDT +-- 0 / +-- Since this is Case 2, the two lines above have been commented out. +-- if DRSDT is set to 0, GOR cannot rise and free gas does not +-- dissolve in undersaturated oil -> constant bubble point pressure + +WELSPECS +-- Item #: 1 2 3 4 5 6 + 'PROD' 'G1' 10 10 8400 'OIL' / + 'INJ' 'G1' 1 1 8335 'GAS' / +/ +-- Coordinates in item 3-4 are retrieved from Odeh's figure 1 and 2 +-- Note that the depth at the midpoint of the well grid blocks +-- has been used as reference depth for bottom hole pressure in item 5 + +COMPDAT +-- Item #: 1 2 3 4 5 6 7 8 9 + 'PROD' 10 10 3 3 'OPEN' 1* 1* 0.5 / + 'INJ' 1 1 1 1 'OPEN' 1* 1* 0.5 / +/ +-- Coordinates in item 2-5 are retreived from Odeh's figure 1 and 2 +-- Item 9 is the well bore internal diameter, +-- the radius is given to be 0.25ft in Odeh's paper + + +WCONPROD +-- Item #:1 2 3 4 5 9 + 'PROD' 'OPEN' 'ORAT' 20000 4* 1000 / +/ +-- It is stated in Odeh's paper that the maximum oil prod. rate +-- is 20 000stb per day which explains the choice of value in item 4. +-- The items > 4 are defaulted with the exception of item 9, +-- the BHP lower limit, which is given to be 1000psia in Odeh's paper + +WCONINJE +-- Item #:1 2 3 4 5 6 7 + 'INJ' 'GAS' 'OPEN' 'RATE' 100000 1* 9014 / +/ +-- Stated in Odeh that gas inj. rate (item 5) is 100MMscf per day +-- BHP upper limit (item 7) should not be exceeding the highest +-- pressure in the PVT table=9014.7psia (default is 100 000psia) + +TSTEP +--Advance the simulater once a month for TEN years: +31 28 31 30 31 30 31 31 30 31 30 31 / + +--Advance the simulator once a year for TEN years: +--10*365 / + +END diff --git a/libres/test-data/local/eclipse/parse/ERROR.PRT b/libres/test-data/local/eclipse/parse/ERROR.PRT new file mode 100644 index 00000000000..43ed16fbbea --- /dev/null +++ b/libres/test-data/local/eclipse/parse/ERROR.PRT @@ -0,0 +1,361 @@ + + + + $$$$$ $$$ $ $$$ $$$$ $$$ $$$$$ $ $$$ $$$ + $ $ $ $ $ $ $ $ $ $ $$ $ $ $ $ + $ $ $ $ $ $ $ $ $ $ $ $ $ + $$$$ $ $ $ $$$$ $$$ $$$$ $ $ $ $ $ + $ $ $ $ $ $ $ $ $ $ $ $ + $ $ $ $ $ $ $ $ $ $ $ $ $ $ + $$$$$ $$$ $$$$$ $$$ $ $$$ $$$$$ $$$ $$$ $$$ + + + + $$$$$ $$$ $ $$$ $$$$ $$$ $$$$$ $ $$$ $$$ + $ $ $ $ $ $ $ $ $ $ $$ $ $ $ $ + $ $ $ $ $ $ $ $ $ $ $ $ $ + $$$$ $ $ $ $$$$ $$$ $$$$ $ $ $ $ $ + $ $ $ $ $ $ $ $ $ $ $ $ + $ $ $ $ $ $ $ $ $ $ $ $ $ $ + $$$$$ $$$ $$$$$ $$$ $ $$$ $$$$$ $$$ $$$ $$$ + + + + $$$$$ $$$ $ $$$ $$$$ $$$ $$$$$ $ $$$ $$$ + $ $ $ $ $ $ $ $ $ $ $$ $ $ $ $ + $ $ $ $ $ $ $ $ $ $ $ $ $ + $$$$ $ $ $ $$$$ $$$ $$$$ $ $ $ $ $ + $ $ $ $ $ $ $ $ $ $ $ $ + $ $ $ $ $ $ $ $ $ $ $ $ $ $ + $$$$$ $$$ $$$$$ $$$ $ $$$ $$$$$ $$$ $$$ $$$ + + + + $$$$$ $$$ $ $$$ $$$$ $$$ $$$$$ $ $$$ $$$ + $ $ $ $ $ $ $ $ $ $ $$ $ $ $ $ + $ $ $ $ $ $ $ $ $ $ $ $ $ + $$$$ $ $ $ $$$$ $$$ $$$$ $ $ $ $ $ + $ $ $ $ $ $ $ $ $ $ $ $ + $ $ $ $ $ $ $ $ $ $ $ $ $ $ + $$$$$ $$$ $$$$$ $$$ $ $$$ $$$$$ $$$ $$$ $$$ + +Proprietary Notice +This application contains the confidential and proprietary trade secrets of Schlumberger and may not be copied or stored in an +information retrieval system, transferred, used, distributed, translated, or retransmitted in any form or by any means, electron +or mechanical, in whole or in part, without the express written permission of the copyright owner. + +ECLIPSE is a mark of Schlumberger. Copyright (c) 1981-2014 Schlumberger. All rights reserved. + +Version = 2014.2 +Arch = Linux X86_64 (64 bit, LINUX_REL5IL1364OPT) + +Annex | Modification date | Update date | baseline +===== | ================= | =========== | ======== +ECLIPSE | 10-NOV-2014 15:48:03 | 11-NOV-2014 07:23:12 | 2014.2_507827 +petlast | 29-FEB-2012 11:48:27 | 11-NOV-2014 07:23:15 | 2014.2_507827 +system | 09-OCT-2014 10:49:54 | 11-NOV-2014 07:23:16 | 2014.2_507827 +tpb | 17-JUL-2013 16:28:58 | 11-NOV-2014 07:23:16 | 2014.2_507827 +utility | 28-OCT-2014 11:34:04 | 11-NOV-2014 07:23:16 | 2014.2_507827 + + HOST be-lcg01-01-08 USER joaho ARCH + +1 + + ******** ECHO OF INPUT DATA FOR RUN ECL-0 + + 0: -- SUPERCase + 0: -- + 0: ----------------------------------------------------------------- + 0: ----------------------------------------------------------------- + 0: -- This is the DATA file used by EnKF to run ECLIPSE. It is nearly a 100% + 0: -- normal ECLIPSE DATA file, but there have been som modifications to use + 0: -- it with EnkF. There are essentially three types of modifications: + 0: -- + 0: -- * The parameters we want to update/estimate with EnKF are separated + 0: -- out in separate files, which are included into this file. + 0: -- + 0: -- * There are som 'magic strings' looking like this: . These are + 0: -- replaced withother content by EnKF before the simulations starts - + 0: -- observe that the 'magic strings' never represent parameters to + 0: -- update. + 0: -- + 0: -- * There are some special ECLIPSE keywords which must/must not be + 0: -- present in the DATA file. + 0: -- + 0: -- All the places[1] where the DATA file has been update for EnKF are marked + 0: -- with EnKF. + 0: -- + 0: -- [1]: There are many places with the string /private/joaho/ERT/git/Gurbat - that is + 0: -- not essential EnKF - just convenience to get EnKF to insert the + 0: -- include path. The value of INCLUDE_PATH must be set with DATA_KW in + 0: -- the main configuration file. + 0: ----------------------------------------------------------------- + 0: ----------------------------------------------------------------- + 0: + 0: + 0: ----------------------------------------------------------------------------------------- + 0: ----------------------------------------------------------------------------------------- + 0: ----------------------------------------------------------------------------------------- + 0: -- Reservoir Simulation Model Building Basic Principles: Generic Reservoir + 0: -- by Gurbat S. Agaev, 26.06.2006 + 0: -- + 0: -- Objective: Explain simulation modelling steps + 0: -- 3-phase model + 0: -- Geological Grid - 120 x 200 x 42 + 0: ----------------------------------------------------------------------------------------- + 0: -- + 0: -- + 0: -- ************************************************************************************** + 0: -- In this section simulation run specification is given + 0: -- ************************************************************************************** + 0: -- + 0: ----------------------------------------------------------------------------------------- + 0: -- ************************************************************************************** + 0: RUNSPEC + 0: -- ************************************************************************************** + 0: -- Simulation run title + 0: TITLE + 0: Reservoir Simulation Model Building Basic Principles + 0: -- + 0: -- -------------------------------------------------------------------------------------- + 0: -- Simulation grid dimension (Imax, Jmax, Kmax) + 0: DIMENS + 0: 40 64 14 / + 0: -- + 0: -- -------------------------------------------------------------------------------------- + 0: -- Big Model + 0: --BIGMODEL + 0: -- + 0: -- -------------------------------------------------------------------------------------- + 0: -- Simulation run start + 0: START + 0: ---- + 0: 1 JAN 2000 / + 0: -- + 0: -- -------------------------------------------------------------------------------------- + 0: -- Fluid phases present + 0: OIL + 0: GAS + 0: WATER + 0: DISGAS + 0: VAPOIL + 0: -- + 0: -- -------------------------------------------------------------------------------------- + 0: -- Measurement unit used + 0: METRIC + 0: -- + 0: -- -------------------------------------------------------------------------------------- + 0: --Options to process grid data + 0: --If MULTX-, MULTY- and MULTZ- are used, set first parameter= 'YES' + 0: GRIDOPTS + 0: -- MULTNUM? NRMULT + 0: 'YES' 1* / + 0: -- + 0: -- -------------------------------------------------------------------------------------- + 0: --Use saturation table end-point scaling + 0: ENDSCALE + 0: 'NODIR' 'REVERS' 1 20 / + 0: -- + 0: -- -------------------------------------------------------------------------------------- + 0: --Options for equilibration + 0: EQLOPTS + 0: 'QUIESC' / + 0: -- + 0: -- -------------------------------------------------------------------------------------- + 0: -- Enables the rock compaction option + 0: --ROCKCOMP + 0: -- 'HYSTER' 1 / + 0: -- + 0: -- -------------------------------------------------------------------------------------- + 0: -- Options for directional and hysteretic relative permeabilities + 0: -- (see EHYSTR keyword in PROPS Section) + 0: --SATOPTS + 0: -- 'HYSTER' / + 0: -- + 0: -- -------------------------------------------------------------------------------------- + 0: -- + 0: --Table dimensions + 0: TABDIMS + 0: -- NTSFUN NTPVT NSSFUN NPPVT NTFIP NRPVT + 0: 1 1 30 24 10 20 / + 0: -- + 0: -- -------------------------------------------------------------------------------------- + 0: -- Dimensions for equilibration tables + 0: EQLDIMS + 0: 2 100 20 / + 0: -- + 0: -- -------------------------------------------------------------------------------------- + 0: --Regions dimension data + 0: REGDIMS + 0: -- NTFIP NMFIPR NRFREG NTFREG + 0: 2 2 0 3 / + 0: -- + 0: -- -------------------------------------------------------------------------------------- + 0: --Dimensions for fault data + 0: FAULTDIM + 0: 1300 / + 0: -- + 0: -- -------------------------------------------------------------------------------------- + 0: --Dimension for well dat + 0: + 0: WELLDIMS + 0: 230 120 50 80 / + 0: + 0: -- + 0: -- -------------------------------------------------------------------------------------- + 0: --Production well VFP table dimension + 0: VFPPDIMS + 0: 20 20 15 15 15 50 / + 0: -- + 0: -- -------------------------------------------------------------------------------------- + 0: --If injection well VFP data is required, they should be specified here first + 0: VFPIDIMS + 0: 10 2 5 / + 0: -- + 0: -- -------------------------------------------------------------------------------------- + 0: --Dimensions and options for tracers + 0: --TRACERS + 0: --NOTRAC NWTRAC NGTRAC NETRAC DIFF/NODIFF + 0: -- 0 3 0 0 'NODIFF' / + 0: -- + 0: -- -------------------------------------------------------------------------------------- + 0: --Summary file dimensions + 0: SMRYDIMS + 0: 15000 / + 0: -- + 0: -- -------------------------------------------------------------------------------------- + 0: --Number of iterations to update well targets + 0: NUPCOL + 0: 4 / + 0: -- + 0: -- -------------------------------------------------------------------------------------- + 0: --Linear solver stack size + 0: NSTACK + 0: 25 / + 0: -- + 0: -- -------------------------------------------------------------------------------------- + 0: -- Input and output files format + 0: -- + 0: -- -------------------------------------------------------------------------------------- + 0: --Activate "Data Check Only" option + 0: --NOSIM + 0: -- + 0: -- + 0: + 0: + 0: -- + 0: -- + 0: -- ************************************************************************************** + 0: -- In this section simulation grid and static reservoir parameters are given + 0: -- ************************************************************************************** + 0: -- + 0: ----------------------------------------------------------------------------------------- + 0: -- ************************************************************************************** + 0: + 0: + 0: GRID + 0: -- ************************************************************************************** + 0: ----------------------------------------------------------------------------------------- + 0: -- + 0: --Disable echoing of the input file + 0: NOECHO + + @-- ERROR AT TIME 0.0 DAYS ( 1-JAN-0): + @ UNABLE TO OPEN INCLUDED FILE + @ /private/joaho/ERT/git/Gurbat/XXexample_grid_sim.GRDECL + @ SYSTEM ERROR CODE IS 29 + + ******** START OF INCLUDED FILE /private/joaho/ERT/git/Gurbat/example_fluxnum_sim.GRDECL + + + ******** END OF INCLUDED FILE + + + ******** START OF INCLUDED FILE /private/joaho/ERT/git/Gurbat/example_faults_sim.GRDECL + + + ******** END OF INCLUDED FILE + + + ******** START OF INCLUDED FILE /private/joaho/ERT/git/Gurbat/example_poro.GRDECL + + + ******** END OF INCLUDED FILE + + + ******** START OF INCLUDED FILE /private/joaho/ERT/git/Gurbat/example_permx.GRDECL + + + ******** END OF INCLUDED FILE + + + ******** START OF INCLUDED FILE MULTFLT.INC + + + ******** END OF INCLUDED FILE + + + ******** START OF INCLUDED FILE GRID_PARAMS.INC + + + ******** END OF INCLUDED FILE + + + ******** START OF INCLUDED FILE FLUID_PARAMS.INC + + + ******** END OF INCLUDED FILE + + + ******** START OF INCLUDED FILE /private/joaho/ERT/git/Gurbat/example_relperm.relperm + + + ******** END OF INCLUDED FILE + + + ******** START OF INCLUDED FILE /private/joaho/ERT/git/Gurbat/example_pvt.txt + + + ******** END OF INCLUDED FILE + + + ******** START OF INCLUDED FILE /private/joaho/ERT/git/Gurbat/example_eqlnum.GRDECL + + + ******** END OF INCLUDED FILE + + + ******** START OF INCLUDED FILE /private/joaho/ERT/git/Gurbat/example_fipnum.GRDECL + + + ******** END OF INCLUDED FILE + + + ******** START OF INCLUDED FILE /private/joaho/ERT/git/Gurbat/example_summary.txt + + + ******** END OF INCLUDED FILE + + + ******** START OF INCLUDED FILE target.SCH + + + ******** END OF INCLUDED FILE + + + ******** END OF INPUT DATA FOR RUN ECL-0 + + @-- ERROR AT TIME 0.0 DAYS ( 1-JAN-0): + @ INCLUDE FILES MISSING. + -1020 Mbytes of storage required + No active cells found + 75 Mbytes (image size) + + Error summary + Comments 0 + Warnings 0 + Problems 0 + Errors 2 + Bugs 0 + Final cpu 0.03 elapsed 0.21 + Total number of time steps forced to be accepted 0 diff --git a/libres/test-data/local/geometry/pol11.xyz b/libres/test-data/local/geometry/pol11.xyz new file mode 100644 index 00000000000..2d173ae49e7 --- /dev/null +++ b/libres/test-data/local/geometry/pol11.xyz @@ -0,0 +1,15 @@ +390271.843750 6606121.334396 1441.942627 +390198.022949 6606263.742462 1431.541504 +390088.572021 6606321.336670 1428.264648 +389889.315430 6606363.343536 1429.486084 +389733.476807 6606420.797058 1425.822632 +389571.975586 6606407.514496 1423.813232 +389554.051514 6606372.157379 1426.375854 +389686.750977 6606220.452850 1429.880615 +389740.487549 6606153.455872 1429.914062 +389764.510010 6606117.441467 1429.094849 +389683.440430 6606035.770203 1421.569092 +389714.931396 6605935.752983 1424.135864 +389789.263184 6605784.945099 1446.627808 +999.000000 999.000000 999.000000 +1 2 3 \ No newline at end of file diff --git a/libres/test-data/local/geometry/pol8.xyz b/libres/test-data/local/geometry/pol8.xyz new file mode 100644 index 00000000000..b5808a6e0d8 --- /dev/null +++ b/libres/test-data/local/geometry/pol8.xyz @@ -0,0 +1,22 @@ +396202.413086 6606091.935028 1542.620972 +396645.891113 6606123.317261 1539.354736 +397240.054443 6605939.969238 1537.357056 +397610.076172 6605656.411438 1536.399902 +397868.064453 6604885.903809 1561.070068 +398026.336914 6604489.468872 1559.005859 +398316.978516 6603905.551758 1553.967041 +398432.308594 6603433.575195 1536.177246 +398449.528809 6602961.209473 1542.235718 +397890.646973 6602644.205811 1520.076294 +397346.204346 6603057.528320 1508.330200 +397066.059082 6603224.013672 1518.558228 +396678.629883 6603318.616211 1534.576416 +396396.212158 6603663.201172 1546.758179 +396367.576416 6604516.860718 1546.737793 +396214.364990 6605134.893372 1548.336060 +396155.645264 6605262.520691 1548.300781 +395988.849121 6605539.834412 1552.912964 +395980.233887 6605551.104919 1551.782959 +396056.314697 6605835.119461 1541.126465 +396202.413086 6606091.935028 1542.620972 +999.000000 999.000000 999.000000 diff --git a/libres/test-data/local/geometry/pol8_noend.xyz b/libres/test-data/local/geometry/pol8_noend.xyz new file mode 100644 index 00000000000..60e3a7551e7 --- /dev/null +++ b/libres/test-data/local/geometry/pol8_noend.xyz @@ -0,0 +1,22 @@ +396202.413086 6606091.935028 0 +396645.891113 6606123.317261 0 +397240.054443 6605939.969238 0 +397610.076172 6605656.411438 0 +397868.064453 6604885.903809 0 +398026.336914 6604489.468872 0 +398316.978516 6603905.551758 0 +398432.308594 6603433.575195 0 +398449.528809 6602961.209473 0 +397890.646973 6602644.205811 0 +397346.204346 6603057.528320 0 +397066.059082 6603224.013672 0 +396678.629883 6603318.616211 0 +396396.212158 6603663.201172 0 +396367.576416 6604516.860718 0 +396214.364990 6605134.893372 0 +396155.645264 6605262.520691 0 +395988.849121 6605539.834412 0 +395980.233887 6605551.104919 0 +396056.314697 6605835.119461 0 + + diff --git a/libres/test-data/local/geometry/surface/long_ascii.irap b/libres/test-data/local/geometry/surface/long_ascii.irap new file mode 100644 index 00000000000..70c753315ac --- /dev/null +++ b/libres/test-data/local/geometry/surface/long_ascii.irap @@ -0,0 +1,650 @@ + -996 79 50.000000 50.000000 + 463325.562500 465725.562500 7336963.500000 7340863.500000 + 49 -65.000000 463325.562500 7336963.500000 + 0 0 0 0 0 0 0 + 0.0051 0.0017 -0.0101 -0.0126 -0.0078 0.0019 + 0.0002 -0.0039 -0.0100 -0.0134 -0.0076 -0.0017 + 0.0014 0.0045 0.0058 -0.0002 -0.0029 -0.0023 + 0.0048 0.0063 0.0107 0.0043 -0.0003 0.0044 + 0.0009 0.0036 0.0033 0.0055 0.0174 0.0168 + 0.0142 0.0073 0.0063 0.0113 0.0083 0.0050 + 0.0092 0.0054 0.0037 0.0034 0.0057 0.0034 + -0.0031 0.0034 0.0035 -0.0119 0.0004 0.0101 + 0.0218 -0.0013 -0.0026 -0.0042 -0.0107 -0.0079 + 0.0000 0.0034 -0.0032 -0.0111 -0.0106 -0.0028 + 0.0015 0.0050 0.0028 -0.0037 -0.0079 -0.0036 + 0.0047 -0.0039 0.0088 0.0091 0.0013 -0.0072 + -0.0075 -0.0027 0.0003 -0.0008 -0.0049 0.0036 + 0.0079 0.0021 0.0085 0.0151 0.0135 0.0112 + 0.0139 0.0176 0.0081 0.0041 0.0083 0.0077 + 0.0023 -0.0049 0.0042 -0.0074 -0.0134 -0.0015 + 0.0173 0.0204 -0.0083 -0.0086 -0.0008 -0.0014 + 0.0000 -0.0002 -0.0005 -0.0024 -0.0070 -0.0145 + -0.0050 0.0015 0.0019 -0.0029 -0.0121 -0.0136 + -0.0050 0.0054 0.0013 0.0056 0.0063 -0.0000 + -0.0084 -0.0124 -0.0023 -0.0012 -0.0062 -0.0078 + 0.0006 0.0045 0.0048 0.0084 0.0124 0.0145 + 0.0173 0.0205 0.0130 0.0071 0.0102 0.0179 + 0.0126 0.0054 -0.0094 0.0025 -0.0025 -0.0166 + -0.0021 0.0228 0.0183 -0.0099 -0.0111 -0.0123 + -0.0120 -0.0098 -0.0070 -0.0053 -0.0030 -0.0048 + -0.0051 -0.0031 0.0009 0.0004 -0.0016 -0.0107 + -0.0129 -0.0066 0.0036 0.0048 0.0015 0.0012 + 0.0030 -0.0055 -0.0066 -0.0023 -0.0030 -0.0007 + 0.0027 0.0078 0.0086 0.0070 0.0101 0.0135 + 0.0124 0.0216 0.0161 0.0074 0.0087 0.0149 + 0.0161 0.0131 0.0078 -0.0079 0.0048 -0.0069 + -0.0132 -0.0016 0.0319 0.0240 -0.0114 -0.0103 + -0.0131 -0.0154 -0.0105 -0.0060 -0.0094 -0.0076 + -0.0022 -0.0014 -0.0002 -0.0002 -0.0023 -0.0033 + -0.0097 -0.0033 -0.0019 0.0055 0.0001 -0.0007 + -0.0086 -0.0082 -0.0063 0.0029 0.0072 0.0071 + 0.0061 0.0140 0.0178 0.0160 0.0136 0.0152 + 0.0189 0.0182 0.0220 0.0201 0.0153 0.0110 + 0.0087 0.0073 0.0117 0.0069 -0.0149 0.0080 + -0.0062 -0.0185 -0.0008 0.0237 0.0083 -0.0104 + -0.0123 -0.0118 -0.0106 -0.0049 0.0025 0.0011 + -0.0009 0.0021 0.0013 0.0016 0.0014 -0.0005 + 0.0006 -0.0033 0.0024 0.0074 0.0009 -0.0040 + -0.0075 -0.0120 -0.0106 0.0043 0.0142 0.0162 + 0.0131 0.0128 0.0202 0.0246 0.0149 0.0068 + 0.0065 0.0058 0.0140 0.0269 0.0311 0.0138 + 0.0024 -0.0052 -0.0062 0.0026 0.0004 -0.0118 + 0.0003 0.0001 -0.0139 0.0065 0.0256 0.0177 + -0.0092 -0.0075 -0.0133 -0.0097 -0.0030 0.0077 + 0.0043 0.0046 -0.0011 -0.0014 0.0012 -0.0021 + -0.0009 -0.0016 0.0036 0.0069 -0.0023 0.0039 + -0.0109 -0.0086 -0.0060 0.0022 0.0148 0.0182 + 0.0134 0.0144 0.0187 0.0197 0.0181 0.0135 + 0.0053 0.0043 0.0135 0.0246 0.0226 0.0140 + 0.0025 -0.0046 -0.0036 -0.0043 -0.0081 0.0108 + -0.0009 -0.0077 -0.0014 -0.0134 0.0091 0.0187 + 0.0182 -0.0004 0.0013 -0.0078 -0.0107 -0.0033 + 0.0008 0.0026 0.0038 0.0007 -0.0001 -0.0023 + -0.0062 -0.0085 -0.0004 0.0099 0.0097 0.0007 + 0.0059 -0.0091 -0.0084 0.0068 0.0153 0.0180 + 0.0133 0.0103 0.0102 0.0142 0.0165 0.0134 + 0.0056 0.0044 0.0037 0.0110 0.0147 0.0119 + 0.0049 0.0008 0.0035 0.0070 -0.0025 -0.0037 + 0.0227 -0.0061 -0.0042 0.0001 -0.0122 0.0085 + 0.0154 0.0151 0.0007 -0.0031 -0.0067 -0.0080 + 0.0023 0.0018 -0.0027 -0.0006 -0.0036 -0.0071 + -0.0089 -0.0072 -0.0072 0.0063 0.0126 0.0161 + 0.0070 -0.0027 -0.0115 0.0002 0.0077 0.0122 + 0.0080 0.0112 0.0143 0.0152 0.0141 0.0140 + 0.0136 0.0111 0.0063 0.0054 0.0067 0.0074 + 0.0109 0.0138 0.0088 0.0088 0.0118 0.0027 + 0.0033 0.0045 -0.0119 0.0004 -0.0078 -0.0018 + 0.0051 0.0055 0.0033 0.0039 -0.0014 -0.0043 + 0.0001 0.0069 -0.0034 -0.0046 -0.0055 -0.0064 + -0.0059 -0.0017 0.0065 0.0068 0.0088 0.0131 + 0.0041 0.0010 -0.0020 0.0034 0.0068 0.0054 + 0.0080 0.0107 0.0155 0.0135 0.0169 0.0161 + 0.0094 0.0124 0.0138 0.0103 0.0079 0.0055 + 0.0060 0.0122 0.0193 0.0169 0.0227 0.0157 + 0.0098 0.0089 -0.0063 -0.0101 -0.0043 -0.0097 + 0.0206 0.0057 0.0016 0.0022 0.0037 0.0029 + 0.0045 0.0092 0.0073 -0.0041 -0.0055 -0.0103 + -0.0107 -0.0068 -0.0028 -0.0004 0.0007 0.0017 + -0.0022 -0.0001 0.0007 -0.0030 0.0064 0.0025 + 0.0045 0.0054 0.0060 0.0132 0.0164 0.0233 + 0.0204 0.0169 0.0172 0.0172 0.0148 0.0136 + 0.0143 0.0147 0.0150 0.0165 0.0156 0.0219 + 0.0157 0.0071 0.0103 -0.0051 -0.0107 -0.0103 + 0.0068 0.0203 0.0130 0.0104 0.0098 0.0038 + 0.0046 0.0099 0.0131 0.0117 -0.0061 -0.0010 + -0.0050 0.0006 0.0028 0.0034 0.0042 0.0030 + 0.0032 -0.0036 -0.0011 0.0054 -0.0062 0.0077 + 0.0073 0.0083 0.0079 0.0095 0.0091 0.0136 + 0.0198 0.0228 0.0248 0.0200 0.0164 0.0180 + 0.0182 0.0207 0.0211 0.0157 0.0134 0.0137 + 0.0103 0.0108 0.0148 0.0091 -0.0106 -0.0164 + -0.0062 0.0124 0.0180 0.0156 0.0117 0.0092 + 0.0060 0.0059 0.0045 0.0056 0.0058 -0.0049 + 0.0024 0.0080 0.0086 0.0108 -0.0004 -0.0023 + -0.0025 0.0011 -0.0033 0.0008 0.0015 0.0004 + -0.0017 -0.0004 0.0065 0.0083 0.0096 0.0086 + 0.0080 0.0145 0.0229 0.0215 0.0180 0.0136 + 0.0104 0.0091 0.0075 0.0027 -0.0008 0.0042 + 0.0111 0.0113 0.0096 0.0066 0.0046 -0.0076 + -0.0138 0.0052 0.0151 0.0164 0.0124 0.0108 + 0.0164 0.0006 0.0045 0.0040 -0.0021 -0.0009 + 0.0051 -0.0006 -0.0005 0.0041 0.0072 0.0003 + -0.0030 -0.0076 -0.0042 -0.0024 0.0076 0.0009 + 0.0013 -0.0046 0.0041 0.0022 0.0007 0.0089 + 0.0126 0.0075 0.0083 0.0118 0.0183 0.0095 + 0.0036 0.0014 0.0016 -0.0018 -0.0046 -0.0043 + -0.0012 0.0036 0.0090 0.0121 0.0025 0.0026 + -0.0060 -0.0085 0.0104 0.0147 0.0157 0.0089 + 0.0085 0.0195 0.0003 0.0088 0.0101 0.0022 + 0.0047 0.0006 -0.0055 -0.0059 -0.0015 -0.0029 + -0.0011 -0.0006 -0.0025 -0.0016 0.0006 0.0004 + 0.0023 0.0088 0.0006 0.0034 -0.0010 0.0002 + 0.0094 0.0220 0.0156 0.0087 0.0137 0.0124 + 0.0039 -0.0005 0.0031 0.0070 0.0058 0.0000 + -0.0016 -0.0018 -0.0036 -0.0009 0.0072 0.0020 + 0.0013 -0.0031 -0.0085 0.0036 0.0114 0.0101 + 0.0075 0.0064 0.0085 0.0024 0.0093 0.0117 + 0.0028 0.0023 -0.0015 -0.0055 -0.0075 -0.0053 + -0.0079 -0.0097 -0.0076 -0.0034 0.0016 0.0026 + -0.0047 -0.0065 0.0151 -0.0032 -0.0033 -0.0009 + 0.0023 0.0137 0.0188 0.0170 0.0128 0.0096 + 0.0096 0.0159 0.0107 0.0076 0.0049 -0.0001 + -0.0029 -0.0007 0.0025 -0.0010 -0.0044 -0.0032 + 0.0003 0.0121 -0.0084 -0.0086 0.0012 0.0101 + 0.0118 0.0055 -0.0000 0.0041 0.0100 0.0114 + 0.0060 -0.0007 -0.0014 0.0052 -0.0078 -0.0068 + -0.0022 -0.0034 -0.0065 -0.0090 -0.0009 0.0074 + 0.0033 -0.0015 -0.0043 0.0192 0.0035 -0.0026 + -0.0001 0.0061 0.0095 0.0118 0.0135 0.0139 + 0.0129 0.0045 0.0121 0.0211 0.0091 -0.0005 + -0.0059 -0.0070 -0.0019 0.0053 0.0121 0.0039 + -0.0003 0.0030 0.0089 -0.0181 -0.0106 -0.0025 + 0.0113 0.0107 0.0050 0.0021 0.0014 0.0018 + -0.0007 0.0038 0.0001 -0.0052 0.0005 -0.0102 + -0.0022 0.0077 0.0122 0.0091 0.0009 0.0008 + 0.0078 0.0023 -0.0042 0.0019 0.0064 -0.0013 + 0.0025 0.0023 0.0043 0.0082 0.0090 0.0096 + 0.0137 0.0155 0.0114 0.0096 0.0145 0.0106 + 0.0029 -0.0012 -0.0049 0.0004 0.0084 0.0141 + 0.0073 0.0052 0.0098 0.0086 -0.0169 -0.0136 + 0.0043 0.0027 -0.0045 -0.0085 -0.0125 -0.0060 + 0.0013 0.0018 -0.0017 -0.0084 0.0017 -0.0052 + -0.0074 -0.0036 0.0113 0.0218 0.0227 0.0140 + 0.0008 0.0056 0.0072 0.0055 0.0034 -0.0021 + 0.0043 0.0100 0.0100 0.0102 0.0089 0.0108 + 0.0108 0.0127 0.0182 0.0143 0.0105 0.0099 + 0.0041 -0.0018 -0.0028 -0.0018 0.0037 0.0094 + 0.0119 0.0117 0.0110 0.0123 0.0063 -0.0058 + -0.0222 0.0036 -0.0002 -0.0181 -0.0173 -0.0027 + 0.0046 0.0096 0.0136 -0.0014 -0.0006 -0.0006 + -0.0035 0.0013 0.0088 0.0125 0.0167 0.0255 + 0.0175 0.0077 0.0013 0.0036 0.0032 -0.0032 + -0.0057 0.0061 0.0107 0.0131 0.0125 0.0039 + 0.0051 0.0067 0.0089 0.0120 0.0057 -0.0024 + 0.0002 0.0023 -0.0053 -0.0062 -0.0024 0.0067 + 0.0152 0.0178 0.0148 0.0129 0.0160 0.0056 + 0.0082 -0.0148 0.0060 -0.0035 -0.0126 -0.0114 + 0.0024 0.0059 0.0063 0.0067 -0.0007 0.0019 + -0.0018 -0.0008 0.0036 0.0047 0.0067 0.0104 + 0.0157 0.0160 0.0025 0.0022 0.0026 -0.0071 + -0.0067 -0.0009 0.0029 0.0077 0.0120 0.0105 + 0.0038 0.0029 0.0063 0.0097 0.0057 -0.0035 + -0.0083 -0.0029 0.0076 0.0037 -0.0048 -0.0068 + 0.0033 0.0123 0.0192 0.0203 0.0208 0.0186 + 0.0120 0.0041 -0.0150 0.0060 -0.0009 -0.0070 + 0.0011 0.0069 0.0041 0.0078 0.0042 0.0005 + -0.0042 -0.0195 0.0011 0.0018 0.0010 0.0070 + 0.0048 0.0037 -0.0021 0.0010 0.0011 -0.0075 + -0.0075 -0.0020 0.0084 0.0060 0.0003 0.0033 + 0.0035 0.0015 0.0006 0.0012 0.0021 0.0026 + 0.0011 -0.0021 -0.0028 0.0038 0.0120 0.0106 + -0.0036 -0.0005 0.0163 0.0177 0.0214 0.0293 + 0.0191 0.0124 0.0082 -0.0113 0.0026 -0.0058 + -0.0104 0.0114 0.0132 0.0088 -0.0026 -0.0046 + -0.0041 -0.0111 0.0026 0.0099 0.0114 0.0159 + 0.0132 0.0024 0.0040 -0.0018 -0.0022 -0.0049 + -0.0058 -0.0011 0.0039 0.0051 0.0058 0.0013 + -0.0029 -0.0006 0.0002 -0.0010 -0.0056 -0.0039 + -0.0021 0.0018 0.0007 -0.0013 0.0028 0.0131 + 0.0168 0.0080 0.0006 0.0072 0.0082 0.0050 + 0.0126 0.0205 0.0206 0.0068 -0.0036 -0.0038 + -0.0053 -0.0113 0.0114 0.0107 0.0122 -0.0031 + -0.0040 -0.0050 -0.0053 0.0118 0.0138 0.0123 + 0.0127 0.0077 0.0062 0.0024 -0.0035 -0.0006 + -0.0017 -0.0005 0.0037 0.0045 0.0065 0.0044 + 0.0022 -0.0011 -0.0001 0.0038 0.0031 -0.0023 + -0.0041 -0.0067 -0.0044 0.0009 0.0073 0.0103 + 0.0058 0.0054 0.0080 0.0014 0.0007 -0.0072 + -0.0080 0.0005 0.0170 0.0197 0.0067 -0.0003 + -0.0069 -0.0096 -0.0069 0.0057 0.0098 0.0186 + -0.0046 -0.0071 -0.0041 -0.0063 -0.0005 0.0072 + 0.0082 0.0096 0.0047 0.0085 0.0016 0.0028 + 0.0009 0.0025 0.0067 0.0074 0.0080 0.0097 + 0.0087 0.0037 -0.0009 -0.0055 -0.0031 0.0022 + 0.0072 -0.0017 -0.0077 -0.0098 -0.0022 0.0003 + -0.0035 -0.0118 -0.0160 -0.0141 -0.0119 -0.0088 + -0.0072 -0.0069 -0.0031 0.0116 0.0185 0.0028 + -0.0092 -0.0124 -0.0068 -0.0034 0.0071 0.0126 + 0.0170 -0.0014 -0.0023 -0.0003 -0.0031 0.0038 + 0.0100 0.0007 -0.0014 -0.0000 0.0009 -0.0008 + -0.0019 0.0075 -0.0021 0.0028 0.0074 0.0092 + 0.0111 0.0083 0.0016 -0.0050 -0.0084 -0.0052 + -0.0016 0.0156 0.0159 0.0034 -0.0088 -0.0039 + 0.0010 0.0009 -0.0004 -0.0025 -0.0036 -0.0057 + -0.0048 -0.0037 -0.0028 0.0006 0.0066 0.0165 + -0.0064 -0.0089 -0.0176 -0.0053 0.0001 0.0029 + 0.0100 0.0197 -0.0027 0.0010 0.0006 0.0032 + 0.0065 0.0089 0.0014 -0.0008 0.0010 -0.0013 + 0.0014 0.0040 0.0043 0.0096 0.0052 0.0039 + 0.0022 -0.0007 0.0010 0.0028 0.0071 0.0073 + 0.0034 0.0025 0.0142 0.0222 0.0160 0.0093 + 0.0012 0.0036 0.0059 0.0043 0.0013 -0.0046 + -0.0034 -0.0116 -0.0139 -0.0090 -0.0009 0.0094 + 0.0095 -0.0030 -0.0123 -0.0132 -0.0048 0.0053 + 0.0103 0.0085 0.0126 -0.0088 -0.0046 -0.0030 + 0.0085 0.0110 0.0080 0.0007 -0.0049 0.0074 + 0.0114 0.0012 -0.0004 0.0021 0.0026 0.0024 + 0.0009 0.0112 0.0157 0.0124 0.0087 0.0088 + 0.0112 0.0075 0.0054 0.0116 0.0149 0.0137 + 0.0140 0.0092 0.0014 -0.0002 -0.0042 -0.0118 + -0.0202 -0.0257 -0.0268 -0.0258 -0.0199 -0.0100 + 0.0087 0.0042 -0.0058 -0.0125 -0.0071 -0.0036 + 0.0062 0.0112 0.0115 0.0196 -0.0063 -0.0065 + -0.0015 0.0101 0.0118 0.0035 0.0048 -0.0063 + 0.0008 0.0035 -0.0051 -0.0038 -0.0016 -0.0054 + 0.0026 0.0086 0.0120 0.0215 0.0176 0.0129 + 0.0086 0.0080 0.0129 0.0126 0.0113 0.0159 + 0.0148 0.0049 -0.0067 -0.0168 -0.0177 -0.0218 + -0.0329 -0.0538 -0.0566 -0.0497 -0.0360 -0.0254 + -0.0103 0.0062 0.0091 0.0058 -0.0088 0.0012 + 0.0004 0.0046 0.0113 0.0161 0.0077 0.0030 + 0.0026 0.0009 0.0073 0.0106 0.0056 0.0074 + -0.0018 -0.0048 0.0014 0.0051 0.0011 0.0036 + -0.0031 0.0026 0.0078 0.0080 0.0106 0.0181 + 0.0199 0.0152 0.0135 0.0182 0.0150 0.0098 + 0.0060 0.0051 0.0038 -0.0049 -0.0117 -0.0211 + -0.0267 -0.0388 -0.0553 -0.0684 -0.0476 -0.0372 + -0.0258 -0.0131 0.0065 0.0117 0.0083 -0.0081 + -0.0099 0.0169 0.0102 0.0018 -0.0083 -0.0078 + 0.0048 0.0039 0.0084 0.0133 0.0119 0.0043 + -0.0034 -0.0064 -0.0039 -0.0024 0.0055 -0.0020 + 0.0026 0.0018 0.0068 0.0057 0.0056 0.0087 + 0.0108 0.0135 0.0134 0.0117 0.0143 0.0169 + 0.0081 0.0039 -0.0012 -0.0046 -0.0018 -0.0061 + -0.0187 -0.0267 -0.0306 -0.0442 -0.0524 -0.0412 + -0.0312 -0.0218 -0.0099 0.0135 0.0035 0.0029 + -0.0160 -0.0133 -0.0057 -0.0112 -0.0065 -0.0007 + 0.0018 0.0106 0.0103 0.0089 0.0091 0.0106 + 0.0059 0.0008 -0.0054 -0.0056 0.0063 -0.0069 + 0.0027 0.0026 0.0022 0.0013 0.0031 0.0084 + 0.0179 0.0161 0.0110 0.0095 0.0065 0.0108 + 0.0132 0.0084 0.0045 -0.0010 -0.0076 -0.0056 + -0.0058 -0.0146 -0.0223 -0.0192 -0.0215 -0.0322 + -0.0221 -0.0194 -0.0237 -0.0116 0.0045 -0.0005 + 0.0014 -0.0006 0.0028 -0.0027 -0.0075 -0.0015 + 0.0114 0.0077 0.0226 0.0159 0.0133 0.0073 + 0.0085 0.0089 0.0144 -0.0021 -0.0012 0.0065 + 0.0015 0.0064 0.0036 0.0001 -0.0043 -0.0019 + 0.0085 0.0165 0.0250 0.0195 0.0101 0.0032 + 0.0054 0.0083 0.0062 0.0031 -0.0008 -0.0010 + -0.0040 -0.0059 -0.0143 -0.0086 -0.0108 -0.0106 + -0.0044 -0.0090 -0.0068 -0.0060 -0.0093 0.0021 + 0.0041 0.0035 0.0062 0.0033 0.0013 0.0013 + 0.0003 0.0040 0.0042 0.0268 0.0303 0.0133 + 0.0076 0.0038 0.0061 0.0057 -0.0026 -0.0029 + 0.0058 0.0037 0.0080 0.0054 0.0021 -0.0018 + -0.0029 0.0013 0.0059 0.0092 0.0113 0.0054 + 0.0027 0.0027 0.0015 0.0015 0.0042 0.0039 + 0.0026 0.0013 -0.0002 -0.0006 -0.0106 -0.0132 + -0.0110 -0.0151 -0.0188 -0.0085 0.0013 -0.0058 + 0.0000 0.0013 0.0116 0.0061 0.0051 0.0047 + 0.0038 0.0079 0.0049 0.0036 0.0160 0.0204 + 0.0049 -0.0020 0.0017 0.0066 0.0081 0.0012 + -0.0048 0.0016 -0.0028 0.0015 0.0021 -0.0013 + -0.0004 0.0010 -0.0023 -0.0071 -0.0031 0.0027 + 0.0038 0.0058 0.0085 0.0040 -0.0012 0.0047 + 0.0044 -0.0008 -0.0023 -0.0020 -0.0090 -0.0149 + -0.0182 -0.0158 -0.0129 -0.0184 -0.0194 -0.0021 + -0.0057 0.0021 -0.0084 0.0077 0.0080 0.0163 + 0.0080 0.0017 0.0049 0.0044 -0.0029 0.0166 + 0.0137 0.0071 -0.0014 -0.0004 0.0111 0.0133 + -0.0000 0.0017 -0.0026 0.0006 0.0022 0.0010 + -0.0057 -0.0076 -0.0059 -0.0033 -0.0044 -0.0027 + 0.0019 0.0054 0.0072 0.0102 0.0078 0.0041 + -0.0037 -0.0150 -0.0117 -0.0146 -0.0189 -0.0184 + -0.0180 -0.0265 -0.0268 -0.0190 -0.0139 -0.0135 + -0.0061 -0.0071 -0.0026 -0.0004 0.0050 0.0130 + 0.0154 0.0073 0.0047 0.0025 0.0096 0.0085 + 0.0087 0.0104 0.0096 0.0025 0.0033 0.0142 + 0.0130 -0.0024 0.0010 -0.0060 0.0044 0.0049 + 0.0011 -0.0018 -0.0058 -0.0063 0.0019 0.0059 + 0.0004 -0.0013 -0.0003 0.0059 -0.0015 -0.0102 + -0.0147 -0.0267 -0.0099 -0.0124 -0.0210 -0.0227 + -0.0224 -0.0156 -0.0212 -0.0286 -0.0224 -0.0114 + -0.0064 -0.0066 -0.0111 -0.0054 0.0029 0.0073 + 0.0124 0.0126 0.0089 0.0106 0.0096 0.0094 + 0.0087 0.0102 0.0027 0.0052 0.0036 0.0081 + 0.0115 0.0111 -0.0041 0.0002 -0.0039 0.0006 + 0.0023 0.0011 0.0009 0.0028 -0.0022 0.0000 + 0.0083 0.0039 -0.0029 -0.0098 -0.0180 -0.0228 + -0.0239 -0.0241 -0.0222 -0.0166 -0.0139 -0.0152 + -0.0199 -0.0213 -0.0186 -0.0196 -0.0313 -0.0258 + -0.0154 -0.0050 -0.0042 -0.0147 -0.0059 0.0027 + 0.0050 0.0050 0.0019 0.0011 0.0089 0.0137 + 0.0126 0.0127 0.0130 0.0026 0.0009 0.0075 + 0.0125 0.0115 0.0116 0.0061 -0.0058 0.0018 + -0.0005 0.0009 0.0002 -0.0004 -0.0020 -0.0012 + -0.0037 -0.0038 -0.0019 -0.0064 -0.0064 -0.0135 + -0.0259 -0.0224 -0.0215 -0.0223 -0.0198 -0.0141 + -0.0087 -0.0112 -0.0131 -0.0195 -0.0212 -0.0212 + -0.0222 -0.0130 -0.0123 -0.0119 -0.0059 -0.0019 + 0.0042 -0.0017 0.0003 -0.0008 -0.0081 0.0041 + 0.0129 0.0149 0.0183 0.0247 0.0157 0.0025 + 0.0050 0.0084 0.0101 0.0108 0.0032 -0.0091 + -0.0012 0.0033 0.0034 -0.0025 -0.0047 -0.0082 + -0.0081 -0.0070 -0.0076 -0.0114 -0.0101 -0.0123 + -0.0127 -0.0211 -0.0192 -0.0127 -0.0123 -0.0145 + -0.0156 -0.0064 -0.0001 0.0058 -0.0044 -0.0122 + -0.0167 -0.0139 -0.0092 -0.0106 -0.0131 -0.0077 + 0.0053 0.0076 0.0022 0.0008 -0.0015 -0.0032 + 0.0062 0.0115 0.0083 0.0112 0.0157 0.0131 + 0.0075 0.0029 0.0055 0.0077 0.0067 0.0097 + -0.0080 0.0056 0.0020 -0.0040 -0.0123 -0.0152 + -0.0150 -0.0123 -0.0081 -0.0073 -0.0089 -0.0040 + -0.0126 -0.0120 -0.0103 -0.0052 -0.0086 -0.0112 + -0.0135 -0.0143 -0.0090 0.0016 0.0008 -0.0018 + -0.0029 -0.0066 -0.0077 -0.0070 -0.0065 -0.0063 + -0.0125 0.0030 0.0057 -0.0036 -0.0023 -0.0046 + -0.0016 0.0124 0.0186 0.0089 0.0120 0.0100 + 0.0096 0.0060 0.0035 0.0017 0.0023 0.0146 + 0.0095 -0.0033 0.0007 0.0017 0.0004 -0.0103 + -0.0160 -0.0154 -0.0151 -0.0136 -0.0100 -0.0013 + 0.0007 -0.0083 -0.0119 0.0065 0.0074 0.0028 + -0.0099 -0.0096 -0.0064 -0.0001 0.0057 0.0067 + 0.0038 -0.0011 -0.0064 -0.0083 -0.0055 -0.0096 + -0.0099 -0.0030 0.0033 0.0033 -0.0040 -0.0016 + -0.0061 -0.0005 0.0133 0.0238 0.0128 0.0113 + 0.0048 0.0013 -0.0069 -0.0027 0.0027 0.0008 + 0.0078 0.0074 -0.0024 -0.0082 0.0001 0.0022 + -0.0041 -0.0067 -0.0058 -0.0133 -0.0180 -0.0179 + -0.0069 -0.0042 -0.0129 -0.0133 0.0049 0.0023 + -0.0076 -0.0131 -0.0100 -0.0024 0.0031 0.0001 + -0.0010 0.0036 0.0050 -0.0080 -0.0104 -0.0031 + -0.0127 -0.0151 -0.0062 0.0088 0.0006 -0.0076 + -0.0053 -0.0042 -0.0008 0.0045 0.0087 0.0089 + 0.0030 0.0045 0.0038 0.0021 0.0009 0.0035 + -0.0006 0.0105 0.0053 -0.0018 0.0026 -0.0059 + -0.0038 -0.0078 -0.0110 -0.0159 -0.0210 -0.0281 + -0.0207 -0.0135 -0.0116 -0.0122 -0.0137 0.0016 + -0.0013 -0.0098 -0.0125 -0.0077 0.0002 0.0086 + 0.0007 -0.0010 0.0064 0.0058 -0.0030 -0.0011 + -0.0010 -0.0072 -0.0133 -0.0075 0.0067 0.0063 + -0.0085 -0.0068 -0.0013 -0.0076 0.0006 0.0010 + -0.0028 -0.0020 0.0135 0.0122 0.0101 0.0071 + 0.0028 -0.0028 0.0088 0.0083 0.0046 -0.0041 + 0.0001 -0.0012 -0.0095 -0.0199 -0.0233 -0.0266 + -0.0251 -0.0176 -0.0061 -0.0052 -0.0053 -0.0063 + 0.0035 0.0012 -0.0014 -0.0024 -0.0016 -0.0031 + -0.0018 0.0020 0.0039 0.0056 0.0020 0.0025 + -0.0006 0.0033 0.0101 -0.0036 -0.0014 0.0092 + 0.0008 -0.0097 -0.0127 -0.0059 -0.0087 -0.0037 + 0.0078 0.0051 -0.0011 0.0153 0.0156 0.0132 + 0.0109 0.0076 0.0001 0.0087 0.0089 0.0028 + -0.0032 0.0023 -0.0021 -0.0087 -0.0167 -0.0201 + -0.0150 -0.0172 -0.0125 -0.0051 -0.0037 0.0027 + 0.0094 0.0119 0.0085 0.0065 0.0049 -0.0064 + -0.0120 -0.0103 0.0001 0.0038 0.0037 -0.0002 + 0.0012 -0.0026 0.0120 0.0068 0.0070 -0.0072 + -0.0084 -0.0159 -0.0057 -0.0051 -0.0082 -0.0150 + -0.0059 0.0023 0.0036 0.0002 0.0161 0.0141 + 0.0109 0.0102 0.0093 0.0047 0.0041 0.0038 + 0.0021 0.0041 0.0055 -0.0027 -0.0121 -0.0176 + -0.0166 -0.0154 -0.0187 -0.0128 -0.0047 -0.0036 + 0.0070 0.0128 0.0073 0.0045 0.0043 0.0016 + -0.0058 -0.0097 -0.0053 0.0027 0.0026 -0.0009 + -0.0047 -0.0023 0.0032 0.0072 0.0085 -0.0045 + -0.0073 0.0022 0.0054 0.0009 -0.0002 -0.0057 + -0.0078 0.0077 -0.0039 -0.0040 -0.0086 0.0100 + 0.0138 0.0062 0.0085 0.0110 0.0081 0.0069 + 0.0165 0.0069 0.0009 0.0140 -0.0074 -0.0148 + -0.0133 -0.0112 -0.0128 -0.0128 -0.0058 -0.0111 + -0.0041 0.0096 0.0069 -0.0038 -0.0039 -0.0019 + -0.0045 -0.0041 -0.0073 -0.0039 0.0030 0.0003 + -0.0077 -0.0124 -0.0007 0.0037 0.0016 0.0069 + -0.0039 -0.0135 -0.0025 0.0071 0.0070 -0.0015 + 0.0082 0.0061 -0.0022 -0.0043 -0.0085 -0.0055 + 0.0007 -0.0037 0.0036 0.0106 0.0079 0.0095 + 0.0074 0.0112 0.0106 -0.0036 0.0081 -0.0105 + -0.0127 -0.0037 -0.0006 -0.0074 -0.0088 -0.0039 + -0.0101 -0.0097 -0.0031 0.0037 -0.0052 -0.0106 + -0.0072 -0.0037 -0.0063 -0.0077 -0.0054 -0.0029 + -0.0104 -0.0125 -0.0066 -0.0018 0.0006 0.0037 + 0.0055 0.0004 -0.0094 -0.0038 0.0035 0.0060 + -0.0040 0.0069 0.0082 0.0046 -0.0174 -0.0185 + -0.0115 -0.0050 -0.0028 0.0065 0.0104 0.0098 + 0.0109 0.0086 0.0043 0.0029 -0.0043 0.0033 + -0.0027 -0.0085 -0.0016 0.0046 -0.0009 -0.0026 + -0.0104 -0.0110 -0.0108 -0.0037 0.0035 -0.0001 + -0.0097 -0.0070 -0.0001 0.0057 0.0014 -0.0025 + -0.0092 -0.0158 -0.0054 0.0029 -0.0055 -0.0005 + 0.0008 -0.0016 0.0002 -0.0058 0.0043 0.0054 + 0.0025 -0.0038 -0.0011 0.0096 0.0157 -0.0160 + -0.0170 -0.0162 -0.0017 0.0027 0.0166 0.0125 + 0.0122 0.0134 0.0108 0.0029 -0.0036 -0.0071 + -0.0152 0.0059 -0.0088 -0.0075 0.0001 -0.0022 + -0.0027 -0.0041 -0.0045 0.0015 -0.0006 0.0031 + 0.0107 -0.0008 -0.0038 0.0000 -0.0077 -0.0088 + -0.0122 -0.0117 -0.0097 -0.0004 -0.0022 0.0012 + -0.0013 0.0021 -0.0013 -0.0054 -0.0060 0.0105 + 0.0123 0.0022 -0.0031 -0.0038 0.0058 -0.0023 + -0.0038 -0.0019 -0.0044 0.0042 0.0081 0.0161 + 0.0152 0.0109 0.0145 0.0099 -0.0002 -0.0104 + -0.0029 -0.0047 0.0060 0.0002 0.0076 0.0011 + -0.0006 -0.0114 -0.0091 -0.0010 -0.0022 -0.0006 + 0.0063 0.0092 -0.0035 -0.0053 -0.0043 -0.0073 + -0.0112 -0.0098 -0.0098 0.0007 0.0009 -0.0051 + 0.0056 0.0006 0.0076 -0.0044 -0.0132 -0.0070 + 0.0091 0.0109 0.0109 -0.0100 -0.0043 -0.0024 + -0.0049 0.0030 0.0003 0.0120 0.0097 0.0070 + 0.0076 0.0107 0.0136 0.0168 0.0147 -0.0006 + -0.0124 -0.0052 -0.0038 0.0017 0.0103 0.0097 + 0.0028 -0.0081 -0.0014 0.0056 0.0120 0.0142 + 0.0096 0.0065 0.0042 0.0028 -0.0040 0.0020 + 0.0053 0.0001 -0.0058 -0.0135 -0.0090 -0.0049 + -0.0041 -0.0065 -0.0093 0.0262 -0.0026 -0.0126 + -0.0117 -0.0004 0.0091 0.0058 -0.0031 -0.0104 + -0.0070 0.0047 0.0148 0.0066 0.0063 0.0081 + 0.0033 0.0007 0.0062 0.0087 0.0154 0.0158 + 0.0027 -0.0144 -0.0045 -0.0001 -0.0009 0.0004 + 0.0021 -0.0044 -0.0081 0.0007 0.0094 0.0148 + 0.0083 0.0060 0.0008 -0.0013 0.0016 0.0181 + 0.0059 0.0067 -0.0025 -0.0143 -0.0147 -0.0121 + -0.0044 -0.0008 -0.0119 -0.0099 0.0153 0.0006 + -0.0096 -0.0134 -0.0061 0.0071 -0.0008 0.0009 + -0.0064 -0.0024 0.0046 0.0165 0.0141 0.0022 + 0.0056 0.0030 -0.0060 -0.0049 0.0075 0.0051 + 0.0100 -0.0003 -0.0112 -0.0060 -0.0020 -0.0005 + -0.0058 -0.0148 -0.0056 0.0010 0.0004 0.0017 + 0.0056 0.0044 -0.0021 -0.0023 0.0060 0.0028 + 0.0102 0.0011 0.0037 -0.0010 -0.0062 0.0009 + 0.0029 0.0062 0.0016 -0.0060 0.0130 0.0141 + 0.0031 -0.0076 -0.0079 -0.0012 0.0034 0.0035 + 0.0082 0.0029 0.0125 0.0165 0.0164 0.0104 + -0.0007 0.0019 0.0076 -0.0017 -0.0060 -0.0037 + -0.0061 0.0006 0.0009 -0.0042 -0.0054 -0.0012 + -0.0043 -0.0033 -0.0084 -0.0026 0.0001 0.0010 + -0.0004 -0.0066 -0.0061 -0.0013 0.0107 0.0126 + 0.0174 0.0072 -0.0060 -0.0014 -0.0009 -0.0018 + 0.0028 0.0126 0.0169 0.0046 -0.0079 0.0127 + 0.0008 0.0002 -0.0133 -0.0036 0.0026 0.0104 + 0.0129 0.0116 0.0058 0.0147 0.0164 0.0101 + -0.0039 -0.0133 0.0060 0.0020 0.0001 -0.0063 + -0.0051 0.0052 0.0028 0.0015 0.0030 -0.0015 + -0.0049 -0.0092 -0.0060 0.0024 0.0063 -0.0018 + -0.0052 -0.0095 0.0006 0.0056 -0.0057 0.0031 + 0.0156 0.0076 -0.0020 -0.0042 -0.0047 -0.0029 + -0.0014 -0.0011 0.0127 0.0236 0.0142 0.0089 + -0.0045 -0.0088 -0.0037 -0.0138 0.0016 0.0068 + 0.0116 0.0097 0.0131 0.0109 0.0097 0.0054 + -0.0047 -0.0111 -0.0109 0.0033 -0.0057 -0.0053 + -0.0035 -0.0012 -0.0028 0.0010 -0.0014 -0.0016 + -0.0020 -0.0135 -0.0118 -0.0062 0.0160 0.0072 + -0.0063 -0.0072 -0.0002 0.0101 -0.0013 -0.0047 + 0.0031 0.0057 -0.0069 -0.0094 -0.0017 0.0023 + 0.0020 0.0022 0.0017 0.0112 0.0151 0.0029 + -0.0057 -0.0139 -0.0103 -0.0079 -0.0134 0.0033 + 0.0064 0.0113 0.0120 0.0134 0.0072 -0.0018 + -0.0043 -0.0075 -0.0122 -0.0079 0.0005 -0.0021 + -0.0004 -0.0043 -0.0032 -0.0013 -0.0028 -0.0035 + -0.0030 -0.0002 -0.0105 -0.0196 -0.0018 0.0104 + 0.0009 -0.0011 -0.0034 -0.0040 -0.0007 -0.0077 + -0.0114 -0.0095 -0.0046 -0.0062 -0.0076 -0.0010 + 0.0046 0.0031 0.0001 0.0016 0.0025 -0.0023 + -0.0087 -0.0124 -0.0148 -0.0120 -0.0162 -0.0156 + -0.0006 0.0074 0.0082 0.0084 0.0216 0.0108 + -0.0001 -0.0078 -0.0156 -0.0099 -0.0011 -0.0002 + -0.0052 -0.0018 -0.0013 -0.0016 -0.0037 -0.0044 + -0.0049 -0.0053 -0.0046 -0.0237 -0.0183 -0.0054 + 0.0035 0.0001 -0.0052 -0.0067 -0.0144 -0.0111 + -0.0180 -0.0146 -0.0024 -0.0027 -0.0048 -0.0013 + -0.0012 0.0024 -0.0099 -0.0075 -0.0051 -0.0047 + -0.0071 -0.0245 -0.0210 -0.0169 -0.0228 -0.0235 + -0.0095 0.0102 -0.0033 -0.0021 0.0041 0.0077 + 0.0026 -0.0011 -0.0131 -0.0114 -0.0071 0.0014 + 0.0039 0.0040 0.0015 0.0007 0.0048 0.0006 + -0.0059 0.0022 0.0069 0.0061 0.0041 0.0051 + 0.0044 0.0004 -0.0071 -0.0180 -0.0078 -0.0052 + 0.0033 -0.0057 -0.0061 0.0023 0.0040 -0.0033 + -0.0031 -0.0010 -0.0025 -0.0111 -0.0092 -0.0053 + -0.0038 -0.0073 -0.0156 -0.0161 -0.0185 -0.0226 + -0.0216 -0.0053 0.0068 -0.0004 -0.0010 -0.0002 + -0.0023 -0.0072 -0.0053 -0.0089 -0.0087 -0.0030 + 0.0038 0.0086 0.0058 0.0050 0.0099 0.0176 + 0.0099 0.0008 0.0039 0.0087 0.0040 0.0028 + 0.0019 -0.0024 -0.0086 -0.0146 -0.0104 -0.0034 + 0.0017 0.0063 0.0122 0.0051 -0.0028 -0.0029 + -0.0039 0.0026 0.0035 0.0037 0.0043 0.0092 + 0.0075 0.0061 -0.0037 -0.0036 -0.0056 -0.0153 + -0.0218 -0.0158 -0.0063 0.0003 0.0035 0.0040 + 0.0002 -0.0126 -0.0125 -0.0078 -0.0084 -0.0056 + 0.0032 0.0075 0.0053 0.0054 0.0030 -0.0049 + 0.0008 -0.0025 -0.0009 0.0021 0.0123 0.0135 + -0.0030 -0.0071 -0.0116 -0.0165 -0.0132 -0.0056 + 0.0022 0.0016 -0.0006 0.0063 0.0025 -0.0018 + -0.0068 -0.0042 -0.0047 0.0034 0.0067 0.0062 + 0.0097 0.0121 0.0117 -0.0009 -0.0152 -0.0114 + -0.0148 -0.0138 -0.0079 -0.0052 -0.0029 -0.0003 + 0.0065 -0.0057 -0.0186 -0.0150 -0.0040 0.0017 + 0.0060 0.0136 0.0143 0.0132 0.0140 0.0120 + 0.0006 -0.0079 -0.0070 -0.0023 0.0002 0.0048 + 0.0167 0.0007 -0.0088 -0.0055 -0.0011 -0.0079 + -0.0102 -0.0051 -0.0060 -0.0102 -0.0067 -0.0007 + 0.0001 -0.0048 -0.0024 0.0072 0.0024 -0.0034 + -0.0047 0.0039 0.0093 0.0055 -0.0131 -0.0172 + -0.0148 -0.0138 -0.0102 -0.0015 -0.0015 -0.0036 + 0.0053 0.0028 -0.0159 -0.0208 -0.0046 0.0057 + 0.0082 0.0147 0.0157 0.0116 0.0165 0.0141 + 0.0175 0.0102 -0.0032 -0.0065 -0.0056 -0.0029 + 0.0059 0.0181 -0.0071 -0.0057 0.0019 0.0090 + 0.0043 -0.0040 -0.0144 -0.0145 -0.0241 -0.0204 + 0.0025 0.0072 0.0048 -0.0004 -0.0021 -0.0060 + -0.0140 -0.0114 -0.0019 -0.0000 -0.0025 -0.0183 + -0.0161 -0.0119 -0.0087 -0.0043 0.0009 -0.0000 + -0.0021 0.0020 -0.0007 -0.0068 -0.0041 0.0007 + 0.0092 0.0134 0.0223 0.0167 0.0059 0.0174 + 0.0155 0.0162 0.0202 0.0063 -0.0038 -0.0013 + 0.0001 0.0026 0.0018 0.0062 0.0039 0.0052 + 0.0071 0.0074 0.0024 -0.0080 -0.0109 -0.0148 + -0.0064 0.0075 0.0135 0.0101 0.0030 -0.0104 + -0.0070 -0.0063 -0.0084 -0.0004 0.0033 -0.0059 + -0.0197 -0.0189 -0.0107 -0.0037 0.0010 -0.0027 + -0.0065 -0.0040 -0.0035 -0.0037 -0.0011 -0.0030 + 0.0011 0.0079 0.0160 0.0125 0.0053 0.0033 + 0.0140 0.0147 0.0123 0.0171 0.0117 0.0042 + 0.0021 0.0001 -0.0063 -0.0061 0.0009 0.0083 + 0.0091 0.0014 0.0111 0.0052 -0.0060 -0.0147 + -0.0154 -0.0045 0.0081 0.0131 0.0077 -0.0053 + -0.0162 -0.0026 0.0011 0.0022 0.0018 0.0019 + -0.0157 -0.0195 -0.0170 -0.0038 0.0054 0.0009 + -0.0025 -0.0038 -0.0064 -0.0092 -0.0060 0.0004 + 0.0049 0.0132 0.0196 0.0126 0.0116 0.0108 + 0.0093 0.0099 0.0090 0.0073 0.0070 0.0022 + -0.0001 -0.0001 -0.0053 -0.0071 -0.0122 0.0126 + 0.0168 0.0016 0.0059 0.0045 0.0013 -0.0068 + -0.0097 -0.0166 0.0068 0.0054 0.0046 -0.0032 + -0.0142 -0.0116 -0.0011 0.0048 0.0076 0.0012 + -0.0053 -0.0253 -0.0212 -0.0148 -0.0033 0.0063 + 0.0001 -0.0043 -0.0078 -0.0011 -0.0051 -0.0064 + 0.0049 0.0170 0.0199 0.0111 0.0041 0.0104 + 0.0146 0.0190 0.0032 0.0071 0.0131 0.0041 + -0.0006 0.0009 0.0086 0.0052 -0.0008 -0.0051 + 0.0044 0.0074 -0.0002 0.0014 0.0007 0.0025 + 0.0045 0.0012 0.0027 0.0002 -0.0012 -0.0081 + -0.0112 -0.0021 0.0014 0.0071 0.0129 0.0110 + 0.0079 -0.0053 -0.0142 -0.0124 -0.0064 0.0041 + 0.0051 -0.0015 -0.0006 -0.0041 0.0046 -0.0042 + 0.0032 0.0116 0.0083 0.0031 0.0060 -0.0014 + 0.0029 0.0071 0.0169 0.0017 0.0073 0.0078 + -0.0052 -0.0006 0.0044 0.0195 0.0116 0.0000 + 0.0019 0.0054 0.0006 0.0085 0.0024 0.0014 + -0.0031 0.0014 -0.0016 -0.0093 -0.0127 -0.0124 + -0.0143 0.0010 -0.0024 -0.0010 0.0074 0.0171 + 0.0072 0.0048 -0.0003 -0.0144 -0.0130 0.0059 + 0.0111 0.0060 -0.0012 -0.0053 -0.0080 -0.0017 + 0.0040 0.0093 0.0159 0.0037 -0.0054 -0.0048 + 0.0045 0.0127 0.0097 0.0127 -0.0090 -0.0022 + 0.0078 -0.0043 -0.0035 0.0017 0.0068 0.0092 + -0.0042 -0.0034 -0.0002 0.0102 0.0208 0.0205 + 0.0102 0.0014 -0.0091 -0.0125 -0.0127 -0.0120 + -0.0128 -0.0069 0.0023 -0.0038 -0.0068 0.0082 + 0.0231 0.0191 0.0109 0.0080 -0.0001 -0.0048 + 0.0057 0.0083 0.0038 -0.0021 -0.0045 -0.0085 + -0.0029 0.0038 0.0121 0.0148 0.0037 -0.0066 + -0.0059 -0.0004 0.0038 0.0052 0.0034 -0.0104 + -0.0041 -0.0041 -0.0046 0.0022 0.0096 0.0027 + -0.0054 -0.0002 0.0023 -0.0066 0.0193 0.0259 + 0.0211 0.0188 0.0121 0.0024 -0.0003 0.0041 + -0.0037 -0.0084 -0.0014 -0.0008 -0.0040 0.0012 + 0.0151 0.0240 0.0196 0.0101 0.0072 0.0052 + 0.0088 0.0017 0.0003 0.0031 0.0040 0.0012 + -0.0029 0.0025 0.0047 0.0126 0.0080 -0.0030 + -0.0086 -0.0045 -0.0028 -0.0010 0.0034 -0.0029 + -0.0120 -0.0112 -0.0153 -0.0128 0.0006 0.0114 + 0.0042 0.0002 0.0036 0.0047 0.0073 0.0198 + 0.0185 0.0092 0.0096 0.0122 0.0045 0.0034 + 0.0112 0.0083 0.0056 0.0028 -0.0070 -0.0029 + 0.0028 0.0171 0.0127 0.0146 0.0027 0.0004 + 0.0035 0.0068 0.0048 0.0022 0.0054 0.0073 + 0.0036 -0.0000 0.0032 -0.0006 0.0008 -0.0004 + -0.0026 0.0013 0.0016 0.0023 0.0048 0.0008 + -0.0112 -0.0052 -0.0057 -0.0123 -0.0162 -0.0055 + 0.0026 -0.0010 -0.0041 -0.0077 -0.0043 0.0095 + 0.0180 0.0117 0.0035 -0.0008 0.0072 0.0116 + 0.0081 0.0066 0.0078 0.0096 0.0077 -0.0018 + -0.0163 -0.0007 0.0219 0.0143 0.0065 -0.0014 + -0.0026 -0.0028 0.0004 0.0039 0.0041 0.0063 + 0.0102 0.0048 0.0050 0.0050 0.0019 -0.0001 + -0.0009 0.0003 0.0062 0.0104 0.0073 0.0014 + -0.0148 -0.0176 0.0042 -0.0051 -0.0095 -0.0131 + -0.0160 -0.0100 -0.0069 0.0005 -0.0025 -0.0047 + 0.0049 0.0061 0.0036 -0.0000 0.0023 0.0092 + 0.0124 0.0051 0.0009 0.0020 -0.0032 0.0192 + 0.0075 -0.0101 0.0035 0.0138 0.0094 0.0049 + -0.0065 -0.0087 -0.0111 -0.0074 -0.0044 -0.0017 + 0.0011 0.0034 0.0052 0.0101 0.0070 0.0002 + -0.0006 -0.0021 -0.0008 0.0085 0.0128 0.0052 + -0.0072 -0.0174 -0.0211 -0.0107 -0.0097 -0.0047 + -0.0018 -0.0041 -0.0052 -0.0037 0.0043 0.0021 + 0.0037 0.0068 0.0012 -0.0048 -0.0044 0.0044 + 0.0065 0.0049 -0.0041 -0.0052 -0.0067 -0.0090 + 0.0097 0.0102 -0.0100 -0.0024 0.0005 0.0064 + 0.0016 -0.0076 -0.0083 -0.0116 -0.0105 -0.0120 + -0.0077 0.0012 0.0087 0.0177 0.0060 0.0019 + -0.0029 -0.0061 -0.0030 -0.0015 0.0012 0.0060 + 0.0025 -0.0056 -0.0129 -0.0105 -0.0078 -0.0051 + -0.0011 0.0044 0.0051 -0.0042 0.0062 0.0034 + 0.0048 0.0068 0.0022 -0.0011 -0.0046 -0.0040 + 0.0005 -0.0014 -0.0029 0.0004 -0.0022 -0.0057 + -0.0019 0.0042 -0.0012 -0.0154 -0.0109 -0.0046 + 0.0023 0.0009 -0.0078 -0.0127 -0.0137 -0.0086 + -0.0092 -0.0025 0.0064 0.0140 0.0157 0.0059 + -0.0027 -0.0023 -0.0032 -0.0013 -0.0007 0.0023 + -0.0011 -0.0045 -0.0102 -0.0088 -0.0034 -0.0117 + -0.0109 -0.0064 -0.0002 0.0032 -0.0073 -0.0097 + 0.0019 0.0092 0.0086 -0.0027 -0.0074 -0.0071 + -0.0120 -0.0046 -0.0043 -0.0018 0.0017 0.0077 + 0.0027 -0.0023 0.0036 -0.0100 -0.0244 -0.0128 + 0.0095 0.0054 -0.0024 -0.0024 -0.0076 -0.0132 + -0.0086 -0.0092 0.0039 0.0059 0.0072 0.0078 + 0.0036 -0.0007 0.0027 0.0047 0.0031 0.0029 + -0.0015 -0.0090 -0.0039 -0.0052 -0.0016 -0.0029 + -0.0149 -0.0185 -0.0116 -0.0062 -0.0015 -0.0090 + -0.0156 -0.0063 0.0001 0.0006 -0.0061 -0.0056 + -0.0088 -0.0125 -0.0075 -0.0028 0.0019 0.0044 + 0.0055 0.0016 -0.0086 -0.0041 -0.0112 -0.0190 + 0.0013 0.0121 0.0054 0.0003 0.0006 -0.0054 + -0.0067 -0.0036 -0.0065 -0.0022 0.0010 0.0022 + 0.0027 0.0033 0.0062 0.0096 0.0112 0.0086 + 0.0012 -0.0017 -0.0091 -0.0099 -0.0073 -0.0018 + -0.0014 0.92314 0.234 0.234 diff --git a/libres/test-data/local/geometry/surface/short_ascii.irap b/libres/test-data/local/geometry/surface/short_ascii.irap new file mode 100644 index 00000000000..c8ce71d91bf --- /dev/null +++ b/libres/test-data/local/geometry/surface/short_ascii.irap @@ -0,0 +1,614 @@ + -996 79 50.000000 50.000000 + 463325.562500 465725.562500 7336963.500000 7340863.500000 + 49 -65.000000 463325.562500 7336963.500000 + 0 0 0 0 0 0 0 + 0.0051 0.0017 -0.0101 -0.0126 -0.0078 0.0019 + 0.0002 -0.0039 -0.0100 -0.0134 -0.0076 -0.0017 + 0.0014 0.0045 0.0058 -0.0002 -0.0029 -0.0023 + 0.0048 0.0063 0.0107 0.0043 -0.0003 0.0044 + 0.0009 0.0036 0.0033 0.0055 0.0174 0.0168 + 0.0142 0.0073 0.0063 0.0113 0.0083 0.0050 + 0.0092 0.0054 0.0037 0.0034 0.0057 0.0034 + -0.0031 0.0034 0.0035 -0.0119 0.0004 0.0101 + 0.0218 -0.0013 -0.0026 -0.0042 -0.0107 -0.0079 + 0.0000 0.0034 -0.0032 -0.0111 -0.0106 -0.0028 + 0.0015 0.0050 0.0028 -0.0037 -0.0079 -0.0036 + 0.0047 -0.0039 0.0088 0.0091 0.0013 -0.0072 + -0.0075 -0.0027 0.0003 -0.0008 -0.0049 0.0036 + 0.0079 0.0021 0.0085 0.0151 0.0135 0.0112 + 0.0139 0.0176 0.0081 0.0041 0.0083 0.0077 + 0.0023 -0.0049 0.0042 -0.0074 -0.0134 -0.0015 + 0.0173 0.0204 -0.0083 -0.0086 -0.0008 -0.0014 + 0.0000 -0.0002 -0.0005 -0.0024 -0.0070 -0.0145 + -0.0050 0.0015 0.0019 -0.0029 -0.0121 -0.0136 + -0.0050 0.0054 0.0013 0.0056 0.0063 -0.0000 + -0.0084 -0.0124 -0.0023 -0.0012 -0.0062 -0.0078 + 0.0006 0.0045 0.0048 0.0084 0.0124 0.0145 + 0.0173 0.0205 0.0130 0.0071 0.0102 0.0179 + 0.0126 0.0054 -0.0094 0.0025 -0.0025 -0.0166 + -0.0021 0.0228 0.0183 -0.0099 -0.0111 -0.0123 + -0.0120 -0.0098 -0.0070 -0.0053 -0.0030 -0.0048 + -0.0051 -0.0031 0.0009 0.0004 -0.0016 -0.0107 + -0.0129 -0.0066 0.0036 0.0048 0.0015 0.0012 + 0.0030 -0.0055 -0.0066 -0.0023 -0.0030 -0.0007 + 0.0027 0.0078 0.0086 0.0070 0.0101 0.0135 + 0.0124 0.0216 0.0161 0.0074 0.0087 0.0149 + 0.0161 0.0131 0.0078 -0.0079 0.0048 -0.0069 + -0.0132 -0.0016 0.0319 0.0240 -0.0114 -0.0103 + -0.0131 -0.0154 -0.0105 -0.0060 -0.0094 -0.0076 + -0.0022 -0.0014 -0.0002 -0.0002 -0.0023 -0.0033 + -0.0097 -0.0033 -0.0019 0.0055 0.0001 -0.0007 + -0.0086 -0.0082 -0.0063 0.0029 0.0072 0.0071 + 0.0061 0.0140 0.0178 0.0160 0.0136 0.0152 + 0.0189 0.0182 0.0220 0.0201 0.0153 0.0110 + 0.0087 0.0073 0.0117 0.0069 -0.0149 0.0080 + -0.0062 -0.0185 -0.0008 0.0237 0.0083 -0.0104 + -0.0123 -0.0118 -0.0106 -0.0049 0.0025 0.0011 + -0.0009 0.0021 0.0013 0.0016 0.0014 -0.0005 + 0.0006 -0.0033 0.0024 0.0074 0.0009 -0.0040 + -0.0075 -0.0120 -0.0106 0.0043 0.0142 0.0162 + 0.0131 0.0128 0.0202 0.0246 0.0149 0.0068 + 0.0065 0.0058 0.0140 0.0269 0.0311 0.0138 + 0.0024 -0.0052 -0.0062 0.0026 0.0004 -0.0118 + 0.0003 0.0001 -0.0139 0.0065 0.0256 0.0177 + -0.0092 -0.0075 -0.0133 -0.0097 -0.0030 0.0077 + 0.0043 0.0046 -0.0011 -0.0014 0.0012 -0.0021 + -0.0009 -0.0016 0.0036 0.0069 -0.0023 0.0039 + -0.0109 -0.0086 -0.0060 0.0022 0.0148 0.0182 + 0.0134 0.0144 0.0187 0.0197 0.0181 0.0135 + 0.0053 0.0043 0.0135 0.0246 0.0226 0.0140 + 0.0025 -0.0046 -0.0036 -0.0043 -0.0081 0.0108 + -0.0009 -0.0077 -0.0014 -0.0134 0.0091 0.0187 + 0.0182 -0.0004 0.0013 -0.0078 -0.0107 -0.0033 + 0.0008 0.0026 0.0038 0.0007 -0.0001 -0.0023 + -0.0062 -0.0085 -0.0004 0.0099 0.0097 0.0007 + 0.0059 -0.0091 -0.0084 0.0068 0.0153 0.0180 + 0.0133 0.0103 0.0102 0.0142 0.0165 0.0134 + 0.0056 0.0044 0.0037 0.0110 0.0147 0.0119 + 0.0049 0.0008 0.0035 0.0070 -0.0025 -0.0037 + 0.0227 -0.0061 -0.0042 0.0001 -0.0122 0.0085 + 0.0154 0.0151 0.0007 -0.0031 -0.0067 -0.0080 + 0.0023 0.0018 -0.0027 -0.0006 -0.0036 -0.0071 + -0.0089 -0.0072 -0.0072 0.0063 0.0126 0.0161 + 0.0070 -0.0027 -0.0115 0.0002 0.0077 0.0122 + 0.0080 0.0112 0.0143 0.0152 0.0141 0.0140 + 0.0136 0.0111 0.0063 0.0054 0.0067 0.0074 + 0.0109 0.0138 0.0088 0.0088 0.0118 0.0027 + 0.0033 0.0045 -0.0119 0.0004 -0.0078 -0.0018 + 0.0051 0.0055 0.0033 0.0039 -0.0014 -0.0043 + 0.0001 0.0069 -0.0034 -0.0046 -0.0055 -0.0064 + -0.0059 -0.0017 0.0065 0.0068 0.0088 0.0131 + 0.0041 0.0010 -0.0020 0.0034 0.0068 0.0054 + 0.0080 0.0107 0.0155 0.0135 0.0169 0.0161 + 0.0094 0.0124 0.0138 0.0103 0.0079 0.0055 + 0.0060 0.0122 0.0193 0.0169 0.0227 0.0157 + 0.0098 0.0089 -0.0063 -0.0101 -0.0043 -0.0097 + 0.0206 0.0057 0.0016 0.0022 0.0037 0.0029 + 0.0045 0.0092 0.0073 -0.0041 -0.0055 -0.0103 + -0.0107 -0.0068 -0.0028 -0.0004 0.0007 0.0017 + -0.0022 -0.0001 0.0007 -0.0030 0.0064 0.0025 + 0.0045 0.0054 0.0060 0.0132 0.0164 0.0233 + 0.0204 0.0169 0.0172 0.0172 0.0148 0.0136 + 0.0143 0.0147 0.0150 0.0165 0.0156 0.0219 + 0.0157 0.0071 0.0103 -0.0051 -0.0107 -0.0103 + 0.0068 0.0203 0.0130 0.0104 0.0098 0.0038 + 0.0046 0.0099 0.0131 0.0117 -0.0061 -0.0010 + -0.0050 0.0006 0.0028 0.0034 0.0042 0.0030 + 0.0032 -0.0036 -0.0011 0.0054 -0.0062 0.0077 + 0.0073 0.0083 0.0079 0.0095 0.0091 0.0136 + 0.0198 0.0228 0.0248 0.0200 0.0164 0.0180 + 0.0182 0.0207 0.0211 0.0157 0.0134 0.0137 + 0.0103 0.0108 0.0148 0.0091 -0.0106 -0.0164 + -0.0062 0.0124 0.0180 0.0156 0.0117 0.0092 + 0.0060 0.0059 0.0045 0.0056 0.0058 -0.0049 + 0.0024 0.0080 0.0086 0.0108 -0.0004 -0.0023 + -0.0025 0.0011 -0.0033 0.0008 0.0015 0.0004 + -0.0017 -0.0004 0.0065 0.0083 0.0096 0.0086 + 0.0080 0.0145 0.0229 0.0215 0.0180 0.0136 + 0.0104 0.0091 0.0075 0.0027 -0.0008 0.0042 + 0.0111 0.0113 0.0096 0.0066 0.0046 -0.0076 + -0.0138 0.0052 0.0151 0.0164 0.0124 0.0108 + 0.0164 0.0006 0.0045 0.0040 -0.0021 -0.0009 + 0.0051 -0.0006 -0.0005 0.0041 0.0072 0.0003 + -0.0030 -0.0076 -0.0042 -0.0024 0.0076 0.0009 + 0.0013 -0.0046 0.0041 0.0022 0.0007 0.0089 + 0.0126 0.0075 0.0083 0.0118 0.0183 0.0095 + 0.0036 0.0014 0.0016 -0.0018 -0.0046 -0.0043 + -0.0012 0.0036 0.0090 0.0121 0.0025 0.0026 + -0.0060 -0.0085 0.0104 0.0147 0.0157 0.0089 + 0.0085 0.0195 0.0003 0.0088 0.0101 0.0022 + 0.0047 0.0006 -0.0055 -0.0059 -0.0015 -0.0029 + -0.0011 -0.0006 -0.0025 -0.0016 0.0006 0.0004 + 0.0023 0.0088 0.0006 0.0034 -0.0010 0.0002 + 0.0094 0.0220 0.0156 0.0087 0.0137 0.0124 + 0.0039 -0.0005 0.0031 0.0070 0.0058 0.0000 + -0.0016 -0.0018 -0.0036 -0.0009 0.0072 0.0020 + 0.0013 -0.0031 -0.0085 0.0036 0.0114 0.0101 + 0.0075 0.0064 0.0085 0.0024 0.0093 0.0117 + 0.0028 0.0023 -0.0015 -0.0055 -0.0075 -0.0053 + -0.0079 -0.0097 -0.0076 -0.0034 0.0016 0.0026 + -0.0047 -0.0065 0.0151 -0.0032 -0.0033 -0.0009 + 0.0023 0.0137 0.0188 0.0170 0.0128 0.0096 + 0.0096 0.0159 0.0107 0.0076 0.0049 -0.0001 + -0.0029 -0.0007 0.0025 -0.0010 -0.0044 -0.0032 + 0.0003 0.0121 -0.0084 -0.0086 0.0012 0.0101 + 0.0118 0.0055 -0.0000 0.0041 0.0100 0.0114 + 0.0060 -0.0007 -0.0014 0.0052 -0.0078 -0.0068 + -0.0022 -0.0034 -0.0065 -0.0090 -0.0009 0.0074 + 0.0033 -0.0015 -0.0043 0.0192 0.0035 -0.0026 + -0.0001 0.0061 0.0095 0.0118 0.0135 0.0139 + 0.0129 0.0045 0.0121 0.0211 0.0091 -0.0005 + -0.0059 -0.0070 -0.0019 0.0053 0.0121 0.0039 + -0.0003 0.0030 0.0089 -0.0181 -0.0106 -0.0025 + 0.0113 0.0107 0.0050 0.0021 0.0014 0.0018 + -0.0007 0.0038 0.0001 -0.0052 0.0005 -0.0102 + -0.0022 0.0077 0.0122 0.0091 0.0009 0.0008 + 0.0078 0.0023 -0.0042 0.0019 0.0064 -0.0013 + 0.0025 0.0023 0.0043 0.0082 0.0090 0.0096 + 0.0137 0.0155 0.0114 0.0096 0.0145 0.0106 + 0.0029 -0.0012 -0.0049 0.0004 0.0084 0.0141 + 0.0073 0.0052 0.0098 0.0086 -0.0169 -0.0136 + 0.0043 0.0027 -0.0045 -0.0085 -0.0125 -0.0060 + 0.0013 0.0018 -0.0017 -0.0084 0.0017 -0.0052 + -0.0074 -0.0036 0.0113 0.0218 0.0227 0.0140 + 0.0008 0.0056 0.0072 0.0055 0.0034 -0.0021 + 0.0043 0.0100 0.0100 0.0102 0.0089 0.0108 + 0.0108 0.0127 0.0182 0.0143 0.0105 0.0099 + 0.0041 -0.0018 -0.0028 -0.0018 0.0037 0.0094 + 0.0119 0.0117 0.0110 0.0123 0.0063 -0.0058 + -0.0222 0.0036 -0.0002 -0.0181 -0.0173 -0.0027 + 0.0046 0.0096 0.0136 -0.0014 -0.0006 -0.0006 + -0.0035 0.0013 0.0088 0.0125 0.0167 0.0255 + 0.0175 0.0077 0.0013 0.0036 0.0032 -0.0032 + -0.0057 0.0061 0.0107 0.0131 0.0125 0.0039 + 0.0051 0.0067 0.0089 0.0120 0.0057 -0.0024 + 0.0002 0.0023 -0.0053 -0.0062 -0.0024 0.0067 + 0.0152 0.0178 0.0148 0.0129 0.0160 0.0056 + 0.0082 -0.0148 0.0060 -0.0035 -0.0126 -0.0114 + 0.0024 0.0059 0.0063 0.0067 -0.0007 0.0019 + -0.0018 -0.0008 0.0036 0.0047 0.0067 0.0104 + 0.0157 0.0160 0.0025 0.0022 0.0026 -0.0071 + -0.0067 -0.0009 0.0029 0.0077 0.0120 0.0105 + 0.0038 0.0029 0.0063 0.0097 0.0057 -0.0035 + -0.0083 -0.0029 0.0076 0.0037 -0.0048 -0.0068 + 0.0033 0.0123 0.0192 0.0203 0.0208 0.0186 + 0.0120 0.0041 -0.0150 0.0060 -0.0009 -0.0070 + 0.0011 0.0069 0.0041 0.0078 0.0042 0.0005 + -0.0042 -0.0195 0.0011 0.0018 0.0010 0.0070 + 0.0048 0.0037 -0.0021 0.0010 0.0011 -0.0075 + -0.0075 -0.0020 0.0084 0.0060 0.0003 0.0033 + 0.0035 0.0015 0.0006 0.0012 0.0021 0.0026 + 0.0011 -0.0021 -0.0028 0.0038 0.0120 0.0106 + -0.0036 -0.0005 0.0163 0.0177 0.0214 0.0293 + 0.0191 0.0124 0.0082 -0.0113 0.0026 -0.0058 + -0.0104 0.0114 0.0132 0.0088 -0.0026 -0.0046 + -0.0041 -0.0111 0.0026 0.0099 0.0114 0.0159 + 0.0132 0.0024 0.0040 -0.0018 -0.0022 -0.0049 + -0.0058 -0.0011 0.0039 0.0051 0.0058 0.0013 + -0.0029 -0.0006 0.0002 -0.0010 -0.0056 -0.0039 + -0.0021 0.0018 0.0007 -0.0013 0.0028 0.0131 + 0.0168 0.0080 0.0006 0.0072 0.0082 0.0050 + 0.0126 0.0205 0.0206 0.0068 -0.0036 -0.0038 + -0.0053 -0.0113 0.0114 0.0107 0.0122 -0.0031 + -0.0040 -0.0050 -0.0053 0.0118 0.0138 0.0123 + 0.0127 0.0077 0.0062 0.0024 -0.0035 -0.0006 + -0.0017 -0.0005 0.0037 0.0045 0.0065 0.0044 + 0.0022 -0.0011 -0.0001 0.0038 0.0031 -0.0023 + -0.0041 -0.0067 -0.0044 0.0009 0.0073 0.0103 + 0.0058 0.0054 0.0080 0.0014 0.0007 -0.0072 + -0.0080 0.0005 0.0170 0.0197 0.0067 -0.0003 + -0.0069 -0.0096 -0.0069 0.0057 0.0098 0.0186 + -0.0046 -0.0071 -0.0041 -0.0063 -0.0005 0.0072 + 0.0082 0.0096 0.0047 0.0085 0.0016 0.0028 + 0.0009 0.0025 0.0067 0.0074 0.0080 0.0097 + 0.0087 0.0037 -0.0009 -0.0055 -0.0031 0.0022 + 0.0072 -0.0017 -0.0077 -0.0098 -0.0022 0.0003 + -0.0035 -0.0118 -0.0160 -0.0141 -0.0119 -0.0088 + -0.0072 -0.0069 -0.0031 0.0116 0.0185 0.0028 + -0.0092 -0.0124 -0.0068 -0.0034 0.0071 0.0126 + 0.0170 -0.0014 -0.0023 -0.0003 -0.0031 0.0038 + 0.0100 0.0007 -0.0014 -0.0000 0.0009 -0.0008 + -0.0019 0.0075 -0.0021 0.0028 0.0074 0.0092 + 0.0111 0.0083 0.0016 -0.0050 -0.0084 -0.0052 + -0.0016 0.0156 0.0159 0.0034 -0.0088 -0.0039 + 0.0010 0.0009 -0.0004 -0.0025 -0.0036 -0.0057 + -0.0048 -0.0037 -0.0028 0.0006 0.0066 0.0165 + -0.0064 -0.0089 -0.0176 -0.0053 0.0001 0.0029 + 0.0100 0.0197 -0.0027 0.0010 0.0006 0.0032 + 0.0065 0.0089 0.0014 -0.0008 0.0010 -0.0013 + 0.0014 0.0040 0.0043 0.0096 0.0052 0.0039 + 0.0022 -0.0007 0.0010 0.0028 0.0071 0.0073 + 0.0034 0.0025 0.0142 0.0222 0.0160 0.0093 + 0.0012 0.0036 0.0059 0.0043 0.0013 -0.0046 + -0.0034 -0.0116 -0.0139 -0.0090 -0.0009 0.0094 + 0.0095 -0.0030 -0.0123 -0.0132 -0.0048 0.0053 + 0.0103 0.0085 0.0126 -0.0088 -0.0046 -0.0030 + 0.0085 0.0110 0.0080 0.0007 -0.0049 0.0074 + 0.0114 0.0012 -0.0004 0.0021 0.0026 0.0024 + 0.0009 0.0112 0.0157 0.0124 0.0087 0.0088 + 0.0112 0.0075 0.0054 0.0116 0.0149 0.0137 + 0.0140 0.0092 0.0014 -0.0002 -0.0042 -0.0118 + -0.0202 -0.0257 -0.0268 -0.0258 -0.0199 -0.0100 + 0.0087 0.0042 -0.0058 -0.0125 -0.0071 -0.0036 + 0.0062 0.0112 0.0115 0.0196 -0.0063 -0.0065 + -0.0015 0.0101 0.0118 0.0035 0.0048 -0.0063 + 0.0008 0.0035 -0.0051 -0.0038 -0.0016 -0.0054 + 0.0026 0.0086 0.0120 0.0215 0.0176 0.0129 + 0.0086 0.0080 0.0129 0.0126 0.0113 0.0159 + 0.0148 0.0049 -0.0067 -0.0168 -0.0177 -0.0218 + -0.0329 -0.0538 -0.0566 -0.0497 -0.0360 -0.0254 + -0.0103 0.0062 0.0091 0.0058 -0.0088 0.0012 + 0.0004 0.0046 0.0113 0.0161 0.0077 0.0030 + 0.0026 0.0009 0.0073 0.0106 0.0056 0.0074 + -0.0018 -0.0048 0.0014 0.0051 0.0011 0.0036 + -0.0031 0.0026 0.0078 0.0080 0.0106 0.0181 + 0.0199 0.0152 0.0135 0.0182 0.0150 0.0098 + 0.0060 0.0051 0.0038 -0.0049 -0.0117 -0.0211 + -0.0267 -0.0388 -0.0553 -0.0684 -0.0476 -0.0372 + -0.0258 -0.0131 0.0065 0.0117 0.0083 -0.0081 + -0.0099 0.0169 0.0102 0.0018 -0.0083 -0.0078 + 0.0048 0.0039 0.0084 0.0133 0.0119 0.0043 + -0.0034 -0.0064 -0.0039 -0.0024 0.0055 -0.0020 + 0.0026 0.0018 0.0068 0.0057 0.0056 0.0087 + 0.0108 0.0135 0.0134 0.0117 0.0143 0.0169 + 0.0081 0.0039 -0.0012 -0.0046 -0.0018 -0.0061 + -0.0187 -0.0267 -0.0306 -0.0442 -0.0524 -0.0412 + -0.0312 -0.0218 -0.0099 0.0135 0.0035 0.0029 + -0.0160 -0.0133 -0.0057 -0.0112 -0.0065 -0.0007 + 0.0018 0.0106 0.0103 0.0089 0.0091 0.0106 + 0.0059 0.0008 -0.0054 -0.0056 0.0063 -0.0069 + 0.0027 0.0026 0.0022 0.0013 0.0031 0.0084 + 0.0179 0.0161 0.0110 0.0095 0.0065 0.0108 + 0.0132 0.0084 0.0045 -0.0010 -0.0076 -0.0056 + -0.0058 -0.0146 -0.0223 -0.0192 -0.0215 -0.0322 + -0.0221 -0.0194 -0.0237 -0.0116 0.0045 -0.0005 + 0.0014 -0.0006 0.0028 -0.0027 -0.0075 -0.0015 + 0.0114 0.0077 0.0226 0.0159 0.0133 0.0073 + 0.0085 0.0089 0.0144 -0.0021 -0.0012 0.0065 + 0.0015 0.0064 0.0036 0.0001 -0.0043 -0.0019 + 0.0085 0.0165 0.0250 0.0195 0.0101 0.0032 + 0.0054 0.0083 0.0062 0.0031 -0.0008 -0.0010 + -0.0040 -0.0059 -0.0143 -0.0086 -0.0108 -0.0106 + -0.0044 -0.0090 -0.0068 -0.0060 -0.0093 0.0021 + 0.0041 0.0035 0.0062 0.0033 0.0013 0.0013 + 0.0003 0.0040 0.0042 0.0268 0.0303 0.0133 + 0.0076 0.0038 0.0061 0.0057 -0.0026 -0.0029 + 0.0058 0.0037 0.0080 0.0054 0.0021 -0.0018 + -0.0029 0.0013 0.0059 0.0092 0.0113 0.0054 + 0.0027 0.0027 0.0015 0.0015 0.0042 0.0039 + 0.0026 0.0013 -0.0002 -0.0006 -0.0106 -0.0132 + -0.0110 -0.0151 -0.0188 -0.0085 0.0013 -0.0058 + 0.0000 0.0013 0.0116 0.0061 0.0051 0.0047 + 0.0038 0.0079 0.0049 0.0036 0.0160 0.0204 + 0.0049 -0.0020 0.0017 0.0066 0.0081 0.0012 + -0.0048 0.0016 -0.0028 0.0015 0.0021 -0.0013 + -0.0004 0.0010 -0.0023 -0.0071 -0.0031 0.0027 + 0.0038 0.0058 0.0085 0.0040 -0.0012 0.0047 + 0.0044 -0.0008 -0.0023 -0.0020 -0.0090 -0.0149 + -0.0182 -0.0158 -0.0129 -0.0184 -0.0194 -0.0021 + -0.0057 0.0021 -0.0084 0.0077 0.0080 0.0163 + 0.0080 0.0017 0.0049 0.0044 -0.0029 0.0166 + 0.0137 0.0071 -0.0014 -0.0004 0.0111 0.0133 + -0.0000 0.0017 -0.0026 0.0006 0.0022 0.0010 + -0.0057 -0.0076 -0.0059 -0.0033 -0.0044 -0.0027 + 0.0019 0.0054 0.0072 0.0102 0.0078 0.0041 + -0.0037 -0.0150 -0.0117 -0.0146 -0.0189 -0.0184 + -0.0180 -0.0265 -0.0268 -0.0190 -0.0139 -0.0135 + -0.0061 -0.0071 -0.0026 -0.0004 0.0050 0.0130 + 0.0154 0.0073 0.0047 0.0025 0.0096 0.0085 + 0.0087 0.0104 0.0096 0.0025 0.0033 0.0142 + 0.0130 -0.0024 0.0010 -0.0060 0.0044 0.0049 + 0.0011 -0.0018 -0.0058 -0.0063 0.0019 0.0059 + 0.0004 -0.0013 -0.0003 0.0059 -0.0015 -0.0102 + -0.0147 -0.0267 -0.0099 -0.0124 -0.0210 -0.0227 + -0.0224 -0.0156 -0.0212 -0.0286 -0.0224 -0.0114 + -0.0064 -0.0066 -0.0111 -0.0054 0.0029 0.0073 + 0.0124 0.0126 0.0089 0.0106 0.0096 0.0094 + 0.0087 0.0102 0.0027 0.0052 0.0036 0.0081 + 0.0115 0.0111 -0.0041 0.0002 -0.0039 0.0006 + 0.0023 0.0011 0.0009 0.0028 -0.0022 0.0000 + 0.0083 0.0039 -0.0029 -0.0098 -0.0180 -0.0228 + -0.0239 -0.0241 -0.0222 -0.0166 -0.0139 -0.0152 + -0.0199 -0.0213 -0.0186 -0.0196 -0.0313 -0.0258 + -0.0154 -0.0050 -0.0042 -0.0147 -0.0059 0.0027 + 0.0050 0.0050 0.0019 0.0011 0.0089 0.0137 + 0.0126 0.0127 0.0130 0.0026 0.0009 0.0075 + 0.0125 0.0115 0.0116 0.0061 -0.0058 0.0018 + -0.0005 0.0009 0.0002 -0.0004 -0.0020 -0.0012 + -0.0037 -0.0038 -0.0019 -0.0064 -0.0064 -0.0135 + -0.0259 -0.0224 -0.0215 -0.0223 -0.0198 -0.0141 + -0.0087 -0.0112 -0.0131 -0.0195 -0.0212 -0.0212 + -0.0222 -0.0130 -0.0123 -0.0119 -0.0059 -0.0019 + 0.0042 -0.0017 0.0003 -0.0008 -0.0081 0.0041 + 0.0129 0.0149 0.0183 0.0247 0.0157 0.0025 + 0.0050 0.0084 0.0101 0.0108 0.0032 -0.0091 + -0.0012 0.0033 0.0034 -0.0025 -0.0047 -0.0082 + -0.0081 -0.0070 -0.0076 -0.0114 -0.0101 -0.0123 + -0.0127 -0.0211 -0.0192 -0.0127 -0.0123 -0.0145 + -0.0156 -0.0064 -0.0001 0.0058 -0.0044 -0.0122 + -0.0167 -0.0139 -0.0092 -0.0106 -0.0131 -0.0077 + 0.0053 0.0076 0.0022 0.0008 -0.0015 -0.0032 + 0.0062 0.0115 0.0083 0.0112 0.0157 0.0131 + 0.0075 0.0029 0.0055 0.0077 0.0067 0.0097 + -0.0080 0.0056 0.0020 -0.0040 -0.0123 -0.0152 + -0.0150 -0.0123 -0.0081 -0.0073 -0.0089 -0.0040 + -0.0126 -0.0120 -0.0103 -0.0052 -0.0086 -0.0112 + -0.0135 -0.0143 -0.0090 0.0016 0.0008 -0.0018 + -0.0029 -0.0066 -0.0077 -0.0070 -0.0065 -0.0063 + -0.0125 0.0030 0.0057 -0.0036 -0.0023 -0.0046 + -0.0016 0.0124 0.0186 0.0089 0.0120 0.0100 + 0.0096 0.0060 0.0035 0.0017 0.0023 0.0146 + 0.0095 -0.0033 0.0007 0.0017 0.0004 -0.0103 + -0.0160 -0.0154 -0.0151 -0.0136 -0.0100 -0.0013 + 0.0007 -0.0083 -0.0119 0.0065 0.0074 0.0028 + -0.0099 -0.0096 -0.0064 -0.0001 0.0057 0.0067 + 0.0038 -0.0011 -0.0064 -0.0083 -0.0055 -0.0096 + -0.0099 -0.0030 0.0033 0.0033 -0.0040 -0.0016 + -0.0061 -0.0005 0.0133 0.0238 0.0128 0.0113 + 0.0048 0.0013 -0.0069 -0.0027 0.0027 0.0008 + 0.0078 0.0074 -0.0024 -0.0082 0.0001 0.0022 + -0.0041 -0.0067 -0.0058 -0.0133 -0.0180 -0.0179 + -0.0069 -0.0042 -0.0129 -0.0133 0.0049 0.0023 + -0.0076 -0.0131 -0.0100 -0.0024 0.0031 0.0001 + -0.0010 0.0036 0.0050 -0.0080 -0.0104 -0.0031 + -0.0127 -0.0151 -0.0062 0.0088 0.0006 -0.0076 + -0.0053 -0.0042 -0.0008 0.0045 0.0087 0.0089 + 0.0030 0.0045 0.0038 0.0021 0.0009 0.0035 + -0.0006 0.0105 0.0053 -0.0018 0.0026 -0.0059 + -0.0038 -0.0078 -0.0110 -0.0159 -0.0210 -0.0281 + -0.0207 -0.0135 -0.0116 -0.0122 -0.0137 0.0016 + -0.0013 -0.0098 -0.0125 -0.0077 0.0002 0.0086 + 0.0007 -0.0010 0.0064 0.0058 -0.0030 -0.0011 + -0.0010 -0.0072 -0.0133 -0.0075 0.0067 0.0063 + -0.0085 -0.0068 -0.0013 -0.0076 0.0006 0.0010 + -0.0028 -0.0020 0.0135 0.0122 0.0101 0.0071 + 0.0028 -0.0028 0.0088 0.0083 0.0046 -0.0041 + 0.0001 -0.0012 -0.0095 -0.0199 -0.0233 -0.0266 + -0.0251 -0.0176 -0.0061 -0.0052 -0.0053 -0.0063 + 0.0035 0.0012 -0.0014 -0.0024 -0.0016 -0.0031 + -0.0018 0.0020 0.0039 0.0056 0.0020 0.0025 + -0.0006 0.0033 0.0101 -0.0036 -0.0014 0.0092 + 0.0008 -0.0097 -0.0127 -0.0059 -0.0087 -0.0037 + 0.0078 0.0051 -0.0011 0.0153 0.0156 0.0132 + 0.0109 0.0076 0.0001 0.0087 0.0089 0.0028 + -0.0032 0.0023 -0.0021 -0.0087 -0.0167 -0.0201 + -0.0150 -0.0172 -0.0125 -0.0051 -0.0037 0.0027 + 0.0094 0.0119 0.0085 0.0065 0.0049 -0.0064 + -0.0120 -0.0103 0.0001 0.0038 0.0037 -0.0002 + 0.0012 -0.0026 0.0120 0.0068 0.0070 -0.0072 + -0.0084 -0.0159 -0.0057 -0.0051 -0.0082 -0.0150 + -0.0059 0.0023 0.0036 0.0002 0.0161 0.0141 + 0.0109 0.0102 0.0093 0.0047 0.0041 0.0038 + 0.0021 0.0041 0.0055 -0.0027 -0.0121 -0.0176 + -0.0166 -0.0154 -0.0187 -0.0128 -0.0047 -0.0036 + 0.0070 0.0128 0.0073 0.0045 0.0043 0.0016 + -0.0058 -0.0097 -0.0053 0.0027 0.0026 -0.0009 + -0.0047 -0.0023 0.0032 0.0072 0.0085 -0.0045 + -0.0073 0.0022 0.0054 0.0009 -0.0002 -0.0057 + -0.0078 0.0077 -0.0039 -0.0040 -0.0086 0.0100 + 0.0138 0.0062 0.0085 0.0110 0.0081 0.0069 + 0.0165 0.0069 0.0009 0.0140 -0.0074 -0.0148 + -0.0133 -0.0112 -0.0128 -0.0128 -0.0058 -0.0111 + -0.0041 0.0096 0.0069 -0.0038 -0.0039 -0.0019 + -0.0045 -0.0041 -0.0073 -0.0039 0.0030 0.0003 + -0.0077 -0.0124 -0.0007 0.0037 0.0016 0.0069 + -0.0039 -0.0135 -0.0025 0.0071 0.0070 -0.0015 + 0.0082 0.0061 -0.0022 -0.0043 -0.0085 -0.0055 + 0.0007 -0.0037 0.0036 0.0106 0.0079 0.0095 + 0.0074 0.0112 0.0106 -0.0036 0.0081 -0.0105 + -0.0127 -0.0037 -0.0006 -0.0074 -0.0088 -0.0039 + -0.0101 -0.0097 -0.0031 0.0037 -0.0052 -0.0106 + -0.0072 -0.0037 -0.0063 -0.0077 -0.0054 -0.0029 + -0.0104 -0.0125 -0.0066 -0.0018 0.0006 0.0037 + 0.0055 0.0004 -0.0094 -0.0038 0.0035 0.0060 + -0.0040 0.0069 0.0082 0.0046 -0.0174 -0.0185 + -0.0115 -0.0050 -0.0028 0.0065 0.0104 0.0098 + 0.0109 0.0086 0.0043 0.0029 -0.0043 0.0033 + -0.0027 -0.0085 -0.0016 0.0046 -0.0009 -0.0026 + -0.0104 -0.0110 -0.0108 -0.0037 0.0035 -0.0001 + -0.0097 -0.0070 -0.0001 0.0057 0.0014 -0.0025 + -0.0092 -0.0158 -0.0054 0.0029 -0.0055 -0.0005 + 0.0008 -0.0016 0.0002 -0.0058 0.0043 0.0054 + 0.0025 -0.0038 -0.0011 0.0096 0.0157 -0.0160 + -0.0170 -0.0162 -0.0017 0.0027 0.0166 0.0125 + 0.0122 0.0134 0.0108 0.0029 -0.0036 -0.0071 + -0.0152 0.0059 -0.0088 -0.0075 0.0001 -0.0022 + -0.0027 -0.0041 -0.0045 0.0015 -0.0006 0.0031 + 0.0107 -0.0008 -0.0038 0.0000 -0.0077 -0.0088 + -0.0122 -0.0117 -0.0097 -0.0004 -0.0022 0.0012 + -0.0013 0.0021 -0.0013 -0.0054 -0.0060 0.0105 + 0.0123 0.0022 -0.0031 -0.0038 0.0058 -0.0023 + -0.0038 -0.0019 -0.0044 0.0042 0.0081 0.0161 + 0.0152 0.0109 0.0145 0.0099 -0.0002 -0.0104 + -0.0029 -0.0047 0.0060 0.0002 0.0076 0.0011 + -0.0006 -0.0114 -0.0091 -0.0010 -0.0022 -0.0006 + 0.0063 0.0092 -0.0035 -0.0053 -0.0043 -0.0073 + -0.0112 -0.0098 -0.0098 0.0007 0.0009 -0.0051 + 0.0056 0.0006 0.0076 -0.0044 -0.0132 -0.0070 + 0.0091 0.0109 0.0109 -0.0100 -0.0043 -0.0024 + -0.0049 0.0030 0.0003 0.0120 0.0097 0.0070 + 0.0076 0.0107 0.0136 0.0168 0.0147 -0.0006 + -0.0124 -0.0052 -0.0038 0.0017 0.0103 0.0097 + 0.0028 -0.0081 -0.0014 0.0056 0.0120 0.0142 + 0.0096 0.0065 0.0042 0.0028 -0.0040 0.0020 + 0.0053 0.0001 -0.0058 -0.0135 -0.0090 -0.0049 + -0.0041 -0.0065 -0.0093 0.0262 -0.0026 -0.0126 + -0.0117 -0.0004 0.0091 0.0058 -0.0031 -0.0104 + -0.0070 0.0047 0.0148 0.0066 0.0063 0.0081 + 0.0033 0.0007 0.0062 0.0087 0.0154 0.0158 + 0.0027 -0.0144 -0.0045 -0.0001 -0.0009 0.0004 + 0.0021 -0.0044 -0.0081 0.0007 0.0094 0.0148 + 0.0083 0.0060 0.0008 -0.0013 0.0016 0.0181 + 0.0059 0.0067 -0.0025 -0.0143 -0.0147 -0.0121 + -0.0044 -0.0008 -0.0119 -0.0099 0.0153 0.0006 + -0.0096 -0.0134 -0.0061 0.0071 -0.0008 0.0009 + -0.0064 -0.0024 0.0046 0.0165 0.0141 0.0022 + 0.0056 0.0030 -0.0060 -0.0049 0.0075 0.0051 + 0.0100 -0.0003 -0.0112 -0.0060 -0.0020 -0.0005 + -0.0058 -0.0148 -0.0056 0.0010 0.0004 0.0017 + 0.0056 0.0044 -0.0021 -0.0023 0.0060 0.0028 + 0.0102 0.0011 0.0037 -0.0010 -0.0062 0.0009 + 0.0029 0.0062 0.0016 -0.0060 0.0130 0.0141 + 0.0031 -0.0076 -0.0079 -0.0012 0.0034 0.0035 + 0.0082 0.0029 0.0125 0.0165 0.0164 0.0104 + -0.0007 0.0019 0.0076 -0.0017 -0.0060 -0.0037 + -0.0061 0.0006 0.0009 -0.0042 -0.0054 -0.0012 + -0.0043 -0.0033 -0.0084 -0.0026 0.0001 0.0010 + -0.0004 -0.0066 -0.0061 -0.0013 0.0107 0.0126 + 0.0174 0.0072 -0.0060 -0.0014 -0.0009 -0.0018 + 0.0028 0.0126 0.0169 0.0046 -0.0079 0.0127 + 0.0008 0.0002 -0.0133 -0.0036 0.0026 0.0104 + 0.0129 0.0116 0.0058 0.0147 0.0164 0.0101 + -0.0039 -0.0133 0.0060 0.0020 0.0001 -0.0063 + -0.0051 0.0052 0.0028 0.0015 0.0030 -0.0015 + -0.0049 -0.0092 -0.0060 0.0024 0.0063 -0.0018 + -0.0052 -0.0095 0.0006 0.0056 -0.0057 0.0031 + 0.0156 0.0076 -0.0020 -0.0042 -0.0047 -0.0029 + -0.0014 -0.0011 0.0127 0.0236 0.0142 0.0089 + -0.0045 -0.0088 -0.0037 -0.0138 0.0016 0.0068 + 0.0116 0.0097 0.0131 0.0109 0.0097 0.0054 + -0.0047 -0.0111 -0.0109 0.0033 -0.0057 -0.0053 + -0.0035 -0.0012 -0.0028 0.0010 -0.0014 -0.0016 + -0.0020 -0.0135 -0.0118 -0.0062 0.0160 0.0072 + -0.0063 -0.0072 -0.0002 0.0101 -0.0013 -0.0047 + 0.0031 0.0057 -0.0069 -0.0094 -0.0017 0.0023 + 0.0020 0.0022 0.0017 0.0112 0.0151 0.0029 + -0.0057 -0.0139 -0.0103 -0.0079 -0.0134 0.0033 + 0.0064 0.0113 0.0120 0.0134 0.0072 -0.0018 + -0.0043 -0.0075 -0.0122 -0.0079 0.0005 -0.0021 + -0.0004 -0.0043 -0.0032 -0.0013 -0.0028 -0.0035 + -0.0030 -0.0002 -0.0105 -0.0196 -0.0018 0.0104 + 0.0009 -0.0011 -0.0034 -0.0040 -0.0007 -0.0077 + -0.0114 -0.0095 -0.0046 -0.0062 -0.0076 -0.0010 + 0.0046 0.0031 0.0001 0.0016 0.0025 -0.0023 + -0.0087 -0.0124 -0.0148 -0.0120 -0.0162 -0.0156 + -0.0006 0.0074 0.0082 0.0084 0.0216 0.0108 + -0.0001 -0.0078 -0.0156 -0.0099 -0.0011 -0.0002 + -0.0052 -0.0018 -0.0013 -0.0016 -0.0037 -0.0044 + -0.0049 -0.0053 -0.0046 -0.0237 -0.0183 -0.0054 + 0.0035 0.0001 -0.0052 -0.0067 -0.0144 -0.0111 + -0.0180 -0.0146 -0.0024 -0.0027 -0.0048 -0.0013 + -0.0012 0.0024 -0.0099 -0.0075 -0.0051 -0.0047 + -0.0071 -0.0245 -0.0210 -0.0169 -0.0228 -0.0235 + -0.0095 0.0102 -0.0033 -0.0021 0.0041 0.0077 + 0.0026 -0.0011 -0.0131 -0.0114 -0.0071 0.0014 + 0.0039 0.0040 0.0015 0.0007 0.0048 0.0006 + -0.0059 0.0022 0.0069 0.0061 0.0041 0.0051 + 0.0044 0.0004 -0.0071 -0.0180 -0.0078 -0.0052 + 0.0033 -0.0057 -0.0061 0.0023 0.0040 -0.0033 + -0.0031 -0.0010 -0.0025 -0.0111 -0.0092 -0.0053 + -0.0038 -0.0073 -0.0156 -0.0161 -0.0185 -0.0226 + -0.0216 -0.0053 0.0068 -0.0004 -0.0010 -0.0002 + -0.0023 -0.0072 -0.0053 -0.0089 -0.0087 -0.0030 + 0.0038 0.0086 0.0058 0.0050 0.0099 0.0176 + 0.0099 0.0008 0.0039 0.0087 0.0040 0.0028 + 0.0019 -0.0024 -0.0086 -0.0146 -0.0104 -0.0034 + 0.0017 0.0063 0.0122 0.0051 -0.0028 -0.0029 + -0.0039 0.0026 0.0035 0.0037 0.0043 0.0092 + 0.0075 0.0061 -0.0037 -0.0036 -0.0056 -0.0153 + -0.0218 -0.0158 -0.0063 0.0003 0.0035 0.0040 + 0.0002 -0.0126 -0.0125 -0.0078 -0.0084 -0.0056 + 0.0032 0.0075 0.0053 0.0054 0.0030 -0.0049 + 0.0008 -0.0025 -0.0009 0.0021 0.0123 0.0135 + -0.0030 -0.0071 -0.0116 -0.0165 -0.0132 -0.0056 + 0.0022 0.0016 -0.0006 0.0063 0.0025 -0.0018 + -0.0068 -0.0042 -0.0047 0.0034 0.0067 0.0062 + 0.0097 0.0121 0.0117 -0.0009 -0.0152 -0.0114 + -0.0148 -0.0138 -0.0079 -0.0052 -0.0029 -0.0003 + 0.0065 -0.0057 -0.0186 -0.0150 -0.0040 0.0017 + 0.0060 0.0136 0.0143 0.0132 0.0140 0.0120 + 0.0006 -0.0079 -0.0070 -0.0023 0.0002 0.0048 + 0.0167 0.0007 -0.0088 -0.0055 -0.0011 -0.0079 + -0.0102 -0.0051 -0.0060 -0.0102 -0.0067 -0.0007 + 0.0001 -0.0048 -0.0024 0.0072 0.0024 -0.0034 + -0.0047 0.0039 0.0093 0.0055 -0.0131 -0.0172 + -0.0148 -0.0138 -0.0102 -0.0015 -0.0015 -0.0036 + 0.0053 0.0028 -0.0159 -0.0208 -0.0046 0.0057 + 0.0082 0.0147 0.0157 0.0116 0.0165 0.0141 + 0.0175 0.0102 -0.0032 -0.0065 -0.0056 -0.0029 + 0.0059 0.0181 -0.0071 -0.0057 0.0019 0.0090 + 0.0043 -0.0040 -0.0144 -0.0145 -0.0241 -0.0204 + 0.0025 0.0072 0.0048 -0.0004 -0.0021 -0.0060 + -0.0140 -0.0114 -0.0019 -0.0000 -0.0025 -0.0183 + -0.0161 -0.0119 -0.0087 -0.0043 0.0009 -0.0000 + -0.0021 0.0020 -0.0007 -0.0068 -0.0041 0.0007 + 0.0092 0.0134 0.0223 0.0167 0.0059 0.0174 + 0.0155 0.0162 0.0202 0.0063 -0.0038 -0.0013 + 0.0001 0.0026 0.0018 0.0062 0.0039 0.0052 + 0.0071 0.0074 0.0024 -0.0080 -0.0109 -0.0148 + -0.0064 0.0075 0.0135 0.0101 0.0030 -0.0104 + -0.0070 -0.0063 -0.0084 -0.0004 0.0033 -0.0059 + -0.0197 -0.0189 -0.0107 -0.0037 0.0010 -0.0027 + -0.0065 -0.0040 -0.0035 -0.0037 -0.0011 -0.0030 + 0.0011 0.0079 0.0160 0.0125 0.0053 0.0033 + 0.0140 0.0147 0.0123 0.0171 0.0117 0.0042 + 0.0021 0.0001 -0.0063 -0.0061 0.0009 0.0083 + 0.0091 0.0014 0.0111 0.0052 -0.0060 -0.0147 + -0.0154 -0.0045 0.0081 0.0131 0.0077 -0.0053 + -0.0162 -0.0026 0.0011 0.0022 0.0018 0.0019 + -0.0157 -0.0195 -0.0170 -0.0038 0.0054 0.0009 + -0.0025 -0.0038 -0.0064 -0.0092 -0.0060 0.0004 + 0.0049 0.0132 0.0196 0.0126 0.0116 0.0108 + 0.0093 0.0099 0.0090 0.0073 0.0070 0.0022 + -0.0001 -0.0001 -0.0053 -0.0071 -0.0122 0.0126 + 0.0168 0.0016 0.0059 0.0045 0.0013 -0.0068 + -0.0097 -0.0166 0.0068 0.0054 0.0046 -0.0032 + -0.0142 -0.0116 -0.0011 0.0048 0.0076 0.0012 + -0.0053 -0.0253 -0.0212 -0.0148 -0.0033 0.0063 + 0.0001 -0.0043 -0.0078 -0.0011 -0.0051 -0.0064 + 0.0049 0.0170 0.0199 0.0111 0.0041 0.0104 + 0.0146 0.0190 0.0032 0.0071 0.0131 0.0041 + -0.0006 0.0009 0.0086 0.0052 -0.0008 -0.0051 + 0.0044 0.0074 -0.0002 0.0014 0.0007 0.0025 + 0.0045 0.0012 0.0027 0.0002 -0.0012 -0.0081 + -0.0112 -0.0021 0.0014 0.0071 0.0129 0.0110 + 0.0079 -0.0053 -0.0142 -0.0124 -0.0064 0.0041 + 0.0051 -0.0015 -0.0006 -0.0041 0.0046 -0.0042 + 0.0032 0.0116 0.0083 0.0031 0.0060 -0.0014 + 0.0029 0.0071 0.0169 0.0017 0.0073 0.0078 + -0.0052 -0.0006 0.0044 0.0195 0.0116 0.0000 + 0.0019 0.0054 0.0006 0.0085 0.0024 0.0014 + -0.0031 0.0014 -0.0016 -0.0093 -0.0127 -0.0124 + -0.0143 0.0010 -0.0024 -0.0010 0.0074 0.0171 + 0.0072 0.0048 -0.0003 -0.0144 -0.0130 0.0059 + 0.0111 0.0060 -0.0012 -0.0053 -0.0080 -0.0017 + 0.0040 0.0093 0.0159 0.0037 -0.0054 -0.0048 + 0.0045 0.0127 0.0097 0.0127 -0.0090 -0.0022 + 0.0078 -0.0043 -0.0035 0.0017 0.0068 0.0092 + -0.0042 -0.0034 -0.0002 0.0102 0.0208 0.0205 + 0.0102 0.0014 -0.0091 -0.0125 -0.0127 -0.0120 + -0.0128 -0.0069 0.0023 -0.0038 -0.0068 0.0082 + 0.0231 0.0191 0.0109 0.0080 -0.0001 -0.0048 + 0.0057 0.0083 0.0038 -0.0021 -0.0045 -0.0085 + -0.0029 0.0038 0.0121 0.0148 0.0037 -0.0066 + -0.0059 -0.0004 0.0038 0.0052 0.0034 -0.0104 + -0.0041 -0.0041 -0.0046 0.0022 0.0096 0.0027 + -0.0054 -0.0002 0.0023 -0.0066 0.0193 0.0259 + 0.0211 0.0188 0.0121 0.0024 -0.0003 0.0041 + -0.0037 -0.0084 -0.0014 -0.0008 -0.0040 0.0012 + 0.0151 0.0240 0.0196 0.0101 0.0072 0.0052 + 0.0088 0.0017 0.0003 0.0031 0.0040 0.0012 + -0.0029 0.0025 0.0047 0.0126 0.0080 -0.0030 + -0.0086 -0.0045 -0.0028 -0.0010 0.0034 -0.0029 + -0.0120 -0.0112 -0.0153 -0.0128 0.0006 0.0114 + 0.0042 0.0002 0.0036 0.0047 0.0073 0.0198 + 0.0185 0.0092 0.0096 0.0122 0.0045 0.0034 + 0.0112 0.0083 0.0056 0.0028 -0.0070 -0.0029 + 0.0028 0.0171 0.0127 0.0146 0.0027 0.0004 + 0.0035 0.0068 0.0048 0.0022 0.0054 0.0073 + 0.0036 -0.0000 0.0032 -0.0006 0.0008 -0.0004 + -0.0026 0.0013 0.0016 0.0023 0.0048 0.0008 + -0.0112 -0.0052 -0.0057 -0.0123 -0.0162 -0.0055 + 0.0026 -0.0010 -0.0041 -0.0077 -0.0043 0.0095 + 0.0180 0.0117 0.0035 -0.0008 0.0072 0.0116 + 0.0081 0.0066 0.0078 0.0096 0.0077 -0.0018 + -0.0163 -0.0007 0.0219 0.0143 0.0065 -0.0014 + -0.0026 -0.0028 0.0004 0.0039 0.0041 0.0063 + 0.0102 0.0048 0.0050 0.0050 0.0019 -0.0001 + -0.0009 0.0003 0.0062 0.0104 0.0073 0.0014 + -0.0148 -0.0176 0.0042 -0.0051 -0.0095 -0.0131 + -0.0160 -0.0100 -0.0069 0.0005 -0.0025 -0.0047 + 0.0049 0.0061 0.0036 -0.0000 0.0023 0.0092 + 0.0124 0.0051 0.0009 0.0020 -0.0032 0.0192 + 0.0075 -0.0101 0.0035 0.0138 0.0094 0.0049 + -0.0065 -0.0087 -0.0111 -0.0074 -0.0044 -0.0017 diff --git a/libres/test-data/local/geometry/surface/valid2_ascii.irap b/libres/test-data/local/geometry/surface/valid2_ascii.irap new file mode 100644 index 00000000000..7c1c611ebce --- /dev/null +++ b/libres/test-data/local/geometry/surface/valid2_ascii.irap @@ -0,0 +1,650 @@ + -996 79 60.000000 50.000000 + 463325.562500 465725.562500 7336963.500000 7340863.500000 + 49 -65.000000 463325.562500 7336963.500000 + 0 0 0 0 0 0 0 + 0.0051 0.0017 -0.0101 -0.0126 -0.0078 0.0019 + 0.0002 -0.0039 -0.0100 -0.0134 -0.0076 -0.0017 + 0.0014 0.0045 0.0058 -0.0002 -0.0029 -0.0023 + 0.0048 0.0063 0.0107 0.0043 -0.0003 0.0044 + 0.0009 0.0036 0.0033 0.0055 0.0174 0.0168 + 0.0142 0.0073 0.0063 0.0113 0.0083 0.0050 + 0.0092 0.0054 0.0037 0.0034 0.0057 0.0034 + -0.0031 0.0034 0.0035 -0.0119 0.0004 0.0101 + 0.0218 -0.0013 -0.0026 -0.0042 -0.0107 -0.0079 + 0.0000 0.0034 -0.0032 -0.0111 -0.0106 -0.0028 + 0.0015 0.0050 0.0028 -0.0037 -0.0079 -0.0036 + 0.0047 -0.0039 0.0088 0.0091 0.0013 -0.0072 + -0.0075 -0.0027 0.0003 -0.0008 -0.0049 0.0036 + 0.0079 0.0021 0.0085 0.0151 0.0135 0.0112 + 0.0139 0.0176 0.0081 0.0041 0.0083 0.0077 + 0.0023 -0.0049 0.0042 -0.0074 -0.0134 -0.0015 + 0.0173 0.0204 -0.0083 -0.0086 -0.0008 -0.0014 + 0.0000 -0.0002 -0.0005 -0.0024 -0.0070 -0.0145 + -0.0050 0.0015 0.0019 -0.0029 -0.0121 -0.0136 + -0.0050 0.0054 0.0013 0.0056 0.0063 -0.0000 + -0.0084 -0.0124 -0.0023 -0.0012 -0.0062 -0.0078 + 0.0006 0.0045 0.0048 0.0084 0.0124 0.0145 + 0.0173 0.0205 0.0130 0.0071 0.0102 0.0179 + 0.0126 0.0054 -0.0094 0.0025 -0.0025 -0.0166 + -0.0021 0.0228 0.0183 -0.0099 -0.0111 -0.0123 + -0.0120 -0.0098 -0.0070 -0.0053 -0.0030 -0.0048 + -0.0051 -0.0031 0.0009 0.0004 -0.0016 -0.0107 + -0.0129 -0.0066 0.0036 0.0048 0.0015 0.0012 + 0.0030 -0.0055 -0.0066 -0.0023 -0.0030 -0.0007 + 0.0027 0.0078 0.0086 0.0070 0.0101 0.0135 + 0.0124 0.0216 0.0161 0.0074 0.0087 0.0149 + 0.0161 0.0131 0.0078 -0.0079 0.0048 -0.0069 + -0.0132 -0.0016 0.0319 0.0240 -0.0114 -0.0103 + -0.0131 -0.0154 -0.0105 -0.0060 -0.0094 -0.0076 + -0.0022 -0.0014 -0.0002 -0.0002 -0.0023 -0.0033 + -0.0097 -0.0033 -0.0019 0.0055 0.0001 -0.0007 + -0.0086 -0.0082 -0.0063 0.0029 0.0072 0.0071 + 0.0061 0.0140 0.0178 0.0160 0.0136 0.0152 + 0.0189 0.0182 0.0220 0.0201 0.0153 0.0110 + 0.0087 0.0073 0.0117 0.0069 -0.0149 0.0080 + -0.0062 -0.0185 -0.0008 0.0237 0.0083 -0.0104 + -0.0123 -0.0118 -0.0106 -0.0049 0.0025 0.0011 + -0.0009 0.0021 0.0013 0.0016 0.0014 -0.0005 + 0.0006 -0.0033 0.0024 0.0074 0.0009 -0.0040 + -0.0075 -0.0120 -0.0106 0.0043 0.0142 0.0162 + 0.0131 0.0128 0.0202 0.0246 0.0149 0.0068 + 0.0065 0.0058 0.0140 0.0269 0.0311 0.0138 + 0.0024 -0.0052 -0.0062 0.0026 0.0004 -0.0118 + 0.0003 0.0001 -0.0139 0.0065 0.0256 0.0177 + -0.0092 -0.0075 -0.0133 -0.0097 -0.0030 0.0077 + 0.0043 0.0046 -0.0011 -0.0014 0.0012 -0.0021 + -0.0009 -0.0016 0.0036 0.0069 -0.0023 0.0039 + -0.0109 -0.0086 -0.0060 0.0022 0.0148 0.0182 + 0.0134 0.0144 0.0187 0.0197 0.0181 0.0135 + 0.0053 0.0043 0.0135 0.0246 0.0226 0.0140 + 0.0025 -0.0046 -0.0036 -0.0043 -0.0081 0.0108 + -0.0009 -0.0077 -0.0014 -0.0134 0.0091 0.0187 + 0.0182 -0.0004 0.0013 -0.0078 -0.0107 -0.0033 + 0.0008 0.0026 0.0038 0.0007 -0.0001 -0.0023 + -0.0062 -0.0085 -0.0004 0.0099 0.0097 0.0007 + 0.0059 -0.0091 -0.0084 0.0068 0.0153 0.0180 + 0.0133 0.0103 0.0102 0.0142 0.0165 0.0134 + 0.0056 0.0044 0.0037 0.0110 0.0147 0.0119 + 0.0049 0.0008 0.0035 0.0070 -0.0025 -0.0037 + 0.0227 -0.0061 -0.0042 0.0001 -0.0122 0.0085 + 0.0154 0.0151 0.0007 -0.0031 -0.0067 -0.0080 + 0.0023 0.0018 -0.0027 -0.0006 -0.0036 -0.0071 + -0.0089 -0.0072 -0.0072 0.0063 0.0126 0.0161 + 0.0070 -0.0027 -0.0115 0.0002 0.0077 0.0122 + 0.0080 0.0112 0.0143 0.0152 0.0141 0.0140 + 0.0136 0.0111 0.0063 0.0054 0.0067 0.0074 + 0.0109 0.0138 0.0088 0.0088 0.0118 0.0027 + 0.0033 0.0045 -0.0119 0.0004 -0.0078 -0.0018 + 0.0051 0.0055 0.0033 0.0039 -0.0014 -0.0043 + 0.0001 0.0069 -0.0034 -0.0046 -0.0055 -0.0064 + -0.0059 -0.0017 0.0065 0.0068 0.0088 0.0131 + 0.0041 0.0010 -0.0020 0.0034 0.0068 0.0054 + 0.0080 0.0107 0.0155 0.0135 0.0169 0.0161 + 0.0094 0.0124 0.0138 0.0103 0.0079 0.0055 + 0.0060 0.0122 0.0193 0.0169 0.0227 0.0157 + 0.0098 0.0089 -0.0063 -0.0101 -0.0043 -0.0097 + 0.0206 0.0057 0.0016 0.0022 0.0037 0.0029 + 0.0045 0.0092 0.0073 -0.0041 -0.0055 -0.0103 + -0.0107 -0.0068 -0.0028 -0.0004 0.0007 0.0017 + -0.0022 -0.0001 0.0007 -0.0030 0.0064 0.0025 + 0.0045 0.0054 0.0060 0.0132 0.0164 0.0233 + 0.0204 0.0169 0.0172 0.0172 0.0148 0.0136 + 0.0143 0.0147 0.0150 0.0165 0.0156 0.0219 + 0.0157 0.0071 0.0103 -0.0051 -0.0107 -0.0103 + 0.0068 0.0203 0.0130 0.0104 0.0098 0.0038 + 0.0046 0.0099 0.0131 0.0117 -0.0061 -0.0010 + -0.0050 0.0006 0.0028 0.0034 0.0042 0.0030 + 0.0032 -0.0036 -0.0011 0.0054 -0.0062 0.0077 + 0.0073 0.0083 0.0079 0.0095 0.0091 0.0136 + 0.0198 0.0228 0.0248 0.0200 0.0164 0.0180 + 0.0182 0.0207 0.0211 0.0157 0.0134 0.0137 + 0.0103 0.0108 0.0148 0.0091 -0.0106 -0.0164 + -0.0062 0.0124 0.0180 0.0156 0.0117 0.0092 + 0.0060 0.0059 0.0045 0.0056 0.0058 -0.0049 + 0.0024 0.0080 0.0086 0.0108 -0.0004 -0.0023 + -0.0025 0.0011 -0.0033 0.0008 0.0015 0.0004 + -0.0017 -0.0004 0.0065 0.0083 0.0096 0.0086 + 0.0080 0.0145 0.0229 0.0215 0.0180 0.0136 + 0.0104 0.0091 0.0075 0.0027 -0.0008 0.0042 + 0.0111 0.0113 0.0096 0.0066 0.0046 -0.0076 + -0.0138 0.0052 0.0151 0.0164 0.0124 0.0108 + 0.0164 0.0006 0.0045 0.0040 -0.0021 -0.0009 + 0.0051 -0.0006 -0.0005 0.0041 0.0072 0.0003 + -0.0030 -0.0076 -0.0042 -0.0024 0.0076 0.0009 + 0.0013 -0.0046 0.0041 0.0022 0.0007 0.0089 + 0.0126 0.0075 0.0083 0.0118 0.0183 0.0095 + 0.0036 0.0014 0.0016 -0.0018 -0.0046 -0.0043 + -0.0012 0.0036 0.0090 0.0121 0.0025 0.0026 + -0.0060 -0.0085 0.0104 0.0147 0.0157 0.0089 + 0.0085 0.0195 0.0003 0.0088 0.0101 0.0022 + 0.0047 0.0006 -0.0055 -0.0059 -0.0015 -0.0029 + -0.0011 -0.0006 -0.0025 -0.0016 0.0006 0.0004 + 0.0023 0.0088 0.0006 0.0034 -0.0010 0.0002 + 0.0094 0.0220 0.0156 0.0087 0.0137 0.0124 + 0.0039 -0.0005 0.0031 0.0070 0.0058 0.0000 + -0.0016 -0.0018 -0.0036 -0.0009 0.0072 0.0020 + 0.0013 -0.0031 -0.0085 0.0036 0.0114 0.0101 + 0.0075 0.0064 0.0085 0.0024 0.0093 0.0117 + 0.0028 0.0023 -0.0015 -0.0055 -0.0075 -0.0053 + -0.0079 -0.0097 -0.0076 -0.0034 0.0016 0.0026 + -0.0047 -0.0065 0.0151 -0.0032 -0.0033 -0.0009 + 0.0023 0.0137 0.0188 0.0170 0.0128 0.0096 + 0.0096 0.0159 0.0107 0.0076 0.0049 -0.0001 + -0.0029 -0.0007 0.0025 -0.0010 -0.0044 -0.0032 + 0.0003 0.0121 -0.0084 -0.0086 0.0012 0.0101 + 0.0118 0.0055 -0.0000 0.0041 0.0100 0.0114 + 0.0060 -0.0007 -0.0014 0.0052 -0.0078 -0.0068 + -0.0022 -0.0034 -0.0065 -0.0090 -0.0009 0.0074 + 0.0033 -0.0015 -0.0043 0.0192 0.0035 -0.0026 + -0.0001 0.0061 0.0095 0.0118 0.0135 0.0139 + 0.0129 0.0045 0.0121 0.0211 0.0091 -0.0005 + -0.0059 -0.0070 -0.0019 0.0053 0.0121 0.0039 + -0.0003 0.0030 0.0089 -0.0181 -0.0106 -0.0025 + 0.0113 0.0107 0.0050 0.0021 0.0014 0.0018 + -0.0007 0.0038 0.0001 -0.0052 0.0005 -0.0102 + -0.0022 0.0077 0.0122 0.0091 0.0009 0.0008 + 0.0078 0.0023 -0.0042 0.0019 0.0064 -0.0013 + 0.0025 0.0023 0.0043 0.0082 0.0090 0.0096 + 0.0137 0.0155 0.0114 0.0096 0.0145 0.0106 + 0.0029 -0.0012 -0.0049 0.0004 0.0084 0.0141 + 0.0073 0.0052 0.0098 0.0086 -0.0169 -0.0136 + 0.0043 0.0027 -0.0045 -0.0085 -0.0125 -0.0060 + 0.0013 0.0018 -0.0017 -0.0084 0.0017 -0.0052 + -0.0074 -0.0036 0.0113 0.0218 0.0227 0.0140 + 0.0008 0.0056 0.0072 0.0055 0.0034 -0.0021 + 0.0043 0.0100 0.0100 0.0102 0.0089 0.0108 + 0.0108 0.0127 0.0182 0.0143 0.0105 0.0099 + 0.0041 -0.0018 -0.0028 -0.0018 0.0037 0.0094 + 0.0119 0.0117 0.0110 0.0123 0.0063 -0.0058 + -0.0222 0.0036 -0.0002 -0.0181 -0.0173 -0.0027 + 0.0046 0.0096 0.0136 -0.0014 -0.0006 -0.0006 + -0.0035 0.0013 0.0088 0.0125 0.0167 0.0255 + 0.0175 0.0077 0.0013 0.0036 0.0032 -0.0032 + -0.0057 0.0061 0.0107 0.0131 0.0125 0.0039 + 0.0051 0.0067 0.0089 0.0120 0.0057 -0.0024 + 0.0002 0.0023 -0.0053 -0.0062 -0.0024 0.0067 + 0.0152 0.0178 0.0148 0.0129 0.0160 0.0056 + 0.0082 -0.0148 0.0060 -0.0035 -0.0126 -0.0114 + 0.0024 0.0059 0.0063 0.0067 -0.0007 0.0019 + -0.0018 -0.0008 0.0036 0.0047 0.0067 0.0104 + 0.0157 0.0160 0.0025 0.0022 0.0026 -0.0071 + -0.0067 -0.0009 0.0029 0.0077 0.0120 0.0105 + 0.0038 0.0029 0.0063 0.0097 0.0057 -0.0035 + -0.0083 -0.0029 0.0076 0.0037 -0.0048 -0.0068 + 0.0033 0.0123 0.0192 0.0203 0.0208 0.0186 + 0.0120 0.0041 -0.0150 0.0060 -0.0009 -0.0070 + 0.0011 0.0069 0.0041 0.0078 0.0042 0.0005 + -0.0042 -0.0195 0.0011 0.0018 0.0010 0.0070 + 0.0048 0.0037 -0.0021 0.0010 0.0011 -0.0075 + -0.0075 -0.0020 0.0084 0.0060 0.0003 0.0033 + 0.0035 0.0015 0.0006 0.0012 0.0021 0.0026 + 0.0011 -0.0021 -0.0028 0.0038 0.0120 0.0106 + -0.0036 -0.0005 0.0163 0.0177 0.0214 0.0293 + 0.0191 0.0124 0.0082 -0.0113 0.0026 -0.0058 + -0.0104 0.0114 0.0132 0.0088 -0.0026 -0.0046 + -0.0041 -0.0111 0.0026 0.0099 0.0114 0.0159 + 0.0132 0.0024 0.0040 -0.0018 -0.0022 -0.0049 + -0.0058 -0.0011 0.0039 0.0051 0.0058 0.0013 + -0.0029 -0.0006 0.0002 -0.0010 -0.0056 -0.0039 + -0.0021 0.0018 0.0007 -0.0013 0.0028 0.0131 + 0.0168 0.0080 0.0006 0.0072 0.0082 0.0050 + 0.0126 0.0205 0.0206 0.0068 -0.0036 -0.0038 + -0.0053 -0.0113 0.0114 0.0107 0.0122 -0.0031 + -0.0040 -0.0050 -0.0053 0.0118 0.0138 0.0123 + 0.0127 0.0077 0.0062 0.0024 -0.0035 -0.0006 + -0.0017 -0.0005 0.0037 0.0045 0.0065 0.0044 + 0.0022 -0.0011 -0.0001 0.0038 0.0031 -0.0023 + -0.0041 -0.0067 -0.0044 0.0009 0.0073 0.0103 + 0.0058 0.0054 0.0080 0.0014 0.0007 -0.0072 + -0.0080 0.0005 0.0170 0.0197 0.0067 -0.0003 + -0.0069 -0.0096 -0.0069 0.0057 0.0098 0.0186 + -0.0046 -0.0071 -0.0041 -0.0063 -0.0005 0.0072 + 0.0082 0.0096 0.0047 0.0085 0.0016 0.0028 + 0.0009 0.0025 0.0067 0.0074 0.0080 0.0097 + 0.0087 0.0037 -0.0009 -0.0055 -0.0031 0.0022 + 0.0072 -0.0017 -0.0077 -0.0098 -0.0022 0.0003 + -0.0035 -0.0118 -0.0160 -0.0141 -0.0119 -0.0088 + -0.0072 -0.0069 -0.0031 0.0116 0.0185 0.0028 + -0.0092 -0.0124 -0.0068 -0.0034 0.0071 0.0126 + 0.0170 -0.0014 -0.0023 -0.0003 -0.0031 0.0038 + 0.0100 0.0007 -0.0014 -0.0000 0.0009 -0.0008 + -0.0019 0.0075 -0.0021 0.0028 0.0074 0.0092 + 0.0111 0.0083 0.0016 -0.0050 -0.0084 -0.0052 + -0.0016 0.0156 0.0159 0.0034 -0.0088 -0.0039 + 0.0010 0.0009 -0.0004 -0.0025 -0.0036 -0.0057 + -0.0048 -0.0037 -0.0028 0.0006 0.0066 0.0165 + -0.0064 -0.0089 -0.0176 -0.0053 0.0001 0.0029 + 0.0100 0.0197 -0.0027 0.0010 0.0006 0.0032 + 0.0065 0.0089 0.0014 -0.0008 0.0010 -0.0013 + 0.0014 0.0040 0.0043 0.0096 0.0052 0.0039 + 0.0022 -0.0007 0.0010 0.0028 0.0071 0.0073 + 0.0034 0.0025 0.0142 0.0222 0.0160 0.0093 + 0.0012 0.0036 0.0059 0.0043 0.0013 -0.0046 + -0.0034 -0.0116 -0.0139 -0.0090 -0.0009 0.0094 + 0.0095 -0.0030 -0.0123 -0.0132 -0.0048 0.0053 + 0.0103 0.0085 0.0126 -0.0088 -0.0046 -0.0030 + 0.0085 0.0110 0.0080 0.0007 -0.0049 0.0074 + 0.0114 0.0012 -0.0004 0.0021 0.0026 0.0024 + 0.0009 0.0112 0.0157 0.0124 0.0087 0.0088 + 0.0112 0.0075 0.0054 0.0116 0.0149 0.0137 + 0.0140 0.0092 0.0014 -0.0002 -0.0042 -0.0118 + -0.0202 -0.0257 -0.0268 -0.0258 -0.0199 -0.0100 + 0.0087 0.0042 -0.0058 -0.0125 -0.0071 -0.0036 + 0.0062 0.0112 0.0115 0.0196 -0.0063 -0.0065 + -0.0015 0.0101 0.0118 0.0035 0.0048 -0.0063 + 0.0008 0.0035 -0.0051 -0.0038 -0.0016 -0.0054 + 0.0026 0.0086 0.0120 0.0215 0.0176 0.0129 + 0.0086 0.0080 0.0129 0.0126 0.0113 0.0159 + 0.0148 0.0049 -0.0067 -0.0168 -0.0177 -0.0218 + -0.0329 -0.0538 -0.0566 -0.0497 -0.0360 -0.0254 + -0.0103 0.0062 0.0091 0.0058 -0.0088 0.0012 + 0.0004 0.0046 0.0113 0.0161 0.0077 0.0030 + 0.0026 0.0009 0.0073 0.0106 0.0056 0.0074 + -0.0018 -0.0048 0.0014 0.0051 0.0011 0.0036 + -0.0031 0.0026 0.0078 0.0080 0.0106 0.0181 + 0.0199 0.0152 0.0135 0.0182 0.0150 0.0098 + 0.0060 0.0051 0.0038 -0.0049 -0.0117 -0.0211 + -0.0267 -0.0388 -0.0553 -0.0684 -0.0476 -0.0372 + -0.0258 -0.0131 0.0065 0.0117 0.0083 -0.0081 + -0.0099 0.0169 0.0102 0.0018 -0.0083 -0.0078 + 0.0048 0.0039 0.0084 0.0133 0.0119 0.0043 + -0.0034 -0.0064 -0.0039 -0.0024 0.0055 -0.0020 + 0.0026 0.0018 0.0068 0.0057 0.0056 0.0087 + 0.0108 0.0135 0.0134 0.0117 0.0143 0.0169 + 0.0081 0.0039 -0.0012 -0.0046 -0.0018 -0.0061 + -0.0187 -0.0267 -0.0306 -0.0442 -0.0524 -0.0412 + -0.0312 -0.0218 -0.0099 0.0135 0.0035 0.0029 + -0.0160 -0.0133 -0.0057 -0.0112 -0.0065 -0.0007 + 0.0018 0.0106 0.0103 0.0089 0.0091 0.0106 + 0.0059 0.0008 -0.0054 -0.0056 0.0063 -0.0069 + 0.0027 0.0026 0.0022 0.0013 0.0031 0.0084 + 0.0179 0.0161 0.0110 0.0095 0.0065 0.0108 + 0.0132 0.0084 0.0045 -0.0010 -0.0076 -0.0056 + -0.0058 -0.0146 -0.0223 -0.0192 -0.0215 -0.0322 + -0.0221 -0.0194 -0.0237 -0.0116 0.0045 -0.0005 + 0.0014 -0.0006 0.0028 -0.0027 -0.0075 -0.0015 + 0.0114 0.0077 0.0226 0.0159 0.0133 0.0073 + 0.0085 0.0089 0.0144 -0.0021 -0.0012 0.0065 + 0.0015 0.0064 0.0036 0.0001 -0.0043 -0.0019 + 0.0085 0.0165 0.0250 0.0195 0.0101 0.0032 + 0.0054 0.0083 0.0062 0.0031 -0.0008 -0.0010 + -0.0040 -0.0059 -0.0143 -0.0086 -0.0108 -0.0106 + -0.0044 -0.0090 -0.0068 -0.0060 -0.0093 0.0021 + 0.0041 0.0035 0.0062 0.0033 0.0013 0.0013 + 0.0003 0.0040 0.0042 0.0268 0.0303 0.0133 + 0.0076 0.0038 0.0061 0.0057 -0.0026 -0.0029 + 0.0058 0.0037 0.0080 0.0054 0.0021 -0.0018 + -0.0029 0.0013 0.0059 0.0092 0.0113 0.0054 + 0.0027 0.0027 0.0015 0.0015 0.0042 0.0039 + 0.0026 0.0013 -0.0002 -0.0006 -0.0106 -0.0132 + -0.0110 -0.0151 -0.0188 -0.0085 0.0013 -0.0058 + 0.0000 0.0013 0.0116 0.0061 0.0051 0.0047 + 0.0038 0.0079 0.0049 0.0036 0.0160 0.0204 + 0.0049 -0.0020 0.0017 0.0066 0.0081 0.0012 + -0.0048 0.0016 -0.0028 0.0015 0.0021 -0.0013 + -0.0004 0.0010 -0.0023 -0.0071 -0.0031 0.0027 + 0.0038 0.0058 0.0085 0.0040 -0.0012 0.0047 + 0.0044 -0.0008 -0.0023 -0.0020 -0.0090 -0.0149 + -0.0182 -0.0158 -0.0129 -0.0184 -0.0194 -0.0021 + -0.0057 0.0021 -0.0084 0.0077 0.0080 0.0163 + 0.0080 0.0017 0.0049 0.0044 -0.0029 0.0166 + 0.0137 0.0071 -0.0014 -0.0004 0.0111 0.0133 + -0.0000 0.0017 -0.0026 0.0006 0.0022 0.0010 + -0.0057 -0.0076 -0.0059 -0.0033 -0.0044 -0.0027 + 0.0019 0.0054 0.0072 0.0102 0.0078 0.0041 + -0.0037 -0.0150 -0.0117 -0.0146 -0.0189 -0.0184 + -0.0180 -0.0265 -0.0268 -0.0190 -0.0139 -0.0135 + -0.0061 -0.0071 -0.0026 -0.0004 0.0050 0.0130 + 0.0154 0.0073 0.0047 0.0025 0.0096 0.0085 + 0.0087 0.0104 0.0096 0.0025 0.0033 0.0142 + 0.0130 -0.0024 0.0010 -0.0060 0.0044 0.0049 + 0.0011 -0.0018 -0.0058 -0.0063 0.0019 0.0059 + 0.0004 -0.0013 -0.0003 0.0059 -0.0015 -0.0102 + -0.0147 -0.0267 -0.0099 -0.0124 -0.0210 -0.0227 + -0.0224 -0.0156 -0.0212 -0.0286 -0.0224 -0.0114 + -0.0064 -0.0066 -0.0111 -0.0054 0.0029 0.0073 + 0.0124 0.0126 0.0089 0.0106 0.0096 0.0094 + 0.0087 0.0102 0.0027 0.0052 0.0036 0.0081 + 0.0115 0.0111 -0.0041 0.0002 -0.0039 0.0006 + 0.0023 0.0011 0.0009 0.0028 -0.0022 0.0000 + 0.0083 0.0039 -0.0029 -0.0098 -0.0180 -0.0228 + -0.0239 -0.0241 -0.0222 -0.0166 -0.0139 -0.0152 + -0.0199 -0.0213 -0.0186 -0.0196 -0.0313 -0.0258 + -0.0154 -0.0050 -0.0042 -0.0147 -0.0059 0.0027 + 0.0050 0.0050 0.0019 0.0011 0.0089 0.0137 + 0.0126 0.0127 0.0130 0.0026 0.0009 0.0075 + 0.0125 0.0115 0.0116 0.0061 -0.0058 0.0018 + -0.0005 0.0009 0.0002 -0.0004 -0.0020 -0.0012 + -0.0037 -0.0038 -0.0019 -0.0064 -0.0064 -0.0135 + -0.0259 -0.0224 -0.0215 -0.0223 -0.0198 -0.0141 + -0.0087 -0.0112 -0.0131 -0.0195 -0.0212 -0.0212 + -0.0222 -0.0130 -0.0123 -0.0119 -0.0059 -0.0019 + 0.0042 -0.0017 0.0003 -0.0008 -0.0081 0.0041 + 0.0129 0.0149 0.0183 0.0247 0.0157 0.0025 + 0.0050 0.0084 0.0101 0.0108 0.0032 -0.0091 + -0.0012 0.0033 0.0034 -0.0025 -0.0047 -0.0082 + -0.0081 -0.0070 -0.0076 -0.0114 -0.0101 -0.0123 + -0.0127 -0.0211 -0.0192 -0.0127 -0.0123 -0.0145 + -0.0156 -0.0064 -0.0001 0.0058 -0.0044 -0.0122 + -0.0167 -0.0139 -0.0092 -0.0106 -0.0131 -0.0077 + 0.0053 0.0076 0.0022 0.0008 -0.0015 -0.0032 + 0.0062 0.0115 0.0083 0.0112 0.0157 0.0131 + 0.0075 0.0029 0.0055 0.0077 0.0067 0.0097 + -0.0080 0.0056 0.0020 -0.0040 -0.0123 -0.0152 + -0.0150 -0.0123 -0.0081 -0.0073 -0.0089 -0.0040 + -0.0126 -0.0120 -0.0103 -0.0052 -0.0086 -0.0112 + -0.0135 -0.0143 -0.0090 0.0016 0.0008 -0.0018 + -0.0029 -0.0066 -0.0077 -0.0070 -0.0065 -0.0063 + -0.0125 0.0030 0.0057 -0.0036 -0.0023 -0.0046 + -0.0016 0.0124 0.0186 0.0089 0.0120 0.0100 + 0.0096 0.0060 0.0035 0.0017 0.0023 0.0146 + 0.0095 -0.0033 0.0007 0.0017 0.0004 -0.0103 + -0.0160 -0.0154 -0.0151 -0.0136 -0.0100 -0.0013 + 0.0007 -0.0083 -0.0119 0.0065 0.0074 0.0028 + -0.0099 -0.0096 -0.0064 -0.0001 0.0057 0.0067 + 0.0038 -0.0011 -0.0064 -0.0083 -0.0055 -0.0096 + -0.0099 -0.0030 0.0033 0.0033 -0.0040 -0.0016 + -0.0061 -0.0005 0.0133 0.0238 0.0128 0.0113 + 0.0048 0.0013 -0.0069 -0.0027 0.0027 0.0008 + 0.0078 0.0074 -0.0024 -0.0082 0.0001 0.0022 + -0.0041 -0.0067 -0.0058 -0.0133 -0.0180 -0.0179 + -0.0069 -0.0042 -0.0129 -0.0133 0.0049 0.0023 + -0.0076 -0.0131 -0.0100 -0.0024 0.0031 0.0001 + -0.0010 0.0036 0.0050 -0.0080 -0.0104 -0.0031 + -0.0127 -0.0151 -0.0062 0.0088 0.0006 -0.0076 + -0.0053 -0.0042 -0.0008 0.0045 0.0087 0.0089 + 0.0030 0.0045 0.0038 0.0021 0.0009 0.0035 + -0.0006 0.0105 0.0053 -0.0018 0.0026 -0.0059 + -0.0038 -0.0078 -0.0110 -0.0159 -0.0210 -0.0281 + -0.0207 -0.0135 -0.0116 -0.0122 -0.0137 0.0016 + -0.0013 -0.0098 -0.0125 -0.0077 0.0002 0.0086 + 0.0007 -0.0010 0.0064 0.0058 -0.0030 -0.0011 + -0.0010 -0.0072 -0.0133 -0.0075 0.0067 0.0063 + -0.0085 -0.0068 -0.0013 -0.0076 0.0006 0.0010 + -0.0028 -0.0020 0.0135 0.0122 0.0101 0.0071 + 0.0028 -0.0028 0.0088 0.0083 0.0046 -0.0041 + 0.0001 -0.0012 -0.0095 -0.0199 -0.0233 -0.0266 + -0.0251 -0.0176 -0.0061 -0.0052 -0.0053 -0.0063 + 0.0035 0.0012 -0.0014 -0.0024 -0.0016 -0.0031 + -0.0018 0.0020 0.0039 0.0056 0.0020 0.0025 + -0.0006 0.0033 0.0101 -0.0036 -0.0014 0.0092 + 0.0008 -0.0097 -0.0127 -0.0059 -0.0087 -0.0037 + 0.0078 0.0051 -0.0011 0.0153 0.0156 0.0132 + 0.0109 0.0076 0.0001 0.0087 0.0089 0.0028 + -0.0032 0.0023 -0.0021 -0.0087 -0.0167 -0.0201 + -0.0150 -0.0172 -0.0125 -0.0051 -0.0037 0.0027 + 0.0094 0.0119 0.0085 0.0065 0.0049 -0.0064 + -0.0120 -0.0103 0.0001 0.0038 0.0037 -0.0002 + 0.0012 -0.0026 0.0120 0.0068 0.0070 -0.0072 + -0.0084 -0.0159 -0.0057 -0.0051 -0.0082 -0.0150 + -0.0059 0.0023 0.0036 0.0002 0.0161 0.0141 + 0.0109 0.0102 0.0093 0.0047 0.0041 0.0038 + 0.0021 0.0041 0.0055 -0.0027 -0.0121 -0.0176 + -0.0166 -0.0154 -0.0187 -0.0128 -0.0047 -0.0036 + 0.0070 0.0128 0.0073 0.0045 0.0043 0.0016 + -0.0058 -0.0097 -0.0053 0.0027 0.0026 -0.0009 + -0.0047 -0.0023 0.0032 0.0072 0.0085 -0.0045 + -0.0073 0.0022 0.0054 0.0009 -0.0002 -0.0057 + -0.0078 0.0077 -0.0039 -0.0040 -0.0086 0.0100 + 0.0138 0.0062 0.0085 0.0110 0.0081 0.0069 + 0.0165 0.0069 0.0009 0.0140 -0.0074 -0.0148 + -0.0133 -0.0112 -0.0128 -0.0128 -0.0058 -0.0111 + -0.0041 0.0096 0.0069 -0.0038 -0.0039 -0.0019 + -0.0045 -0.0041 -0.0073 -0.0039 0.0030 0.0003 + -0.0077 -0.0124 -0.0007 0.0037 0.0016 0.0069 + -0.0039 -0.0135 -0.0025 0.0071 0.0070 -0.0015 + 0.0082 0.0061 -0.0022 -0.0043 -0.0085 -0.0055 + 0.0007 -0.0037 0.0036 0.0106 0.0079 0.0095 + 0.0074 0.0112 0.0106 -0.0036 0.0081 -0.0105 + -0.0127 -0.0037 -0.0006 -0.0074 -0.0088 -0.0039 + -0.0101 -0.0097 -0.0031 0.0037 -0.0052 -0.0106 + -0.0072 -0.0037 -0.0063 -0.0077 -0.0054 -0.0029 + -0.0104 -0.0125 -0.0066 -0.0018 0.0006 0.0037 + 0.0055 0.0004 -0.0094 -0.0038 0.0035 0.0060 + -0.0040 0.0069 0.0082 0.0046 -0.0174 -0.0185 + -0.0115 -0.0050 -0.0028 0.0065 0.0104 0.0098 + 0.0109 0.0086 0.0043 0.0029 -0.0043 0.0033 + -0.0027 -0.0085 -0.0016 0.0046 -0.0009 -0.0026 + -0.0104 -0.0110 -0.0108 -0.0037 0.0035 -0.0001 + -0.0097 -0.0070 -0.0001 0.0057 0.0014 -0.0025 + -0.0092 -0.0158 -0.0054 0.0029 -0.0055 -0.0005 + 0.0008 -0.0016 0.0002 -0.0058 0.0043 0.0054 + 0.0025 -0.0038 -0.0011 0.0096 0.0157 -0.0160 + -0.0170 -0.0162 -0.0017 0.0027 0.0166 0.0125 + 0.0122 0.0134 0.0108 0.0029 -0.0036 -0.0071 + -0.0152 0.0059 -0.0088 -0.0075 0.0001 -0.0022 + -0.0027 -0.0041 -0.0045 0.0015 -0.0006 0.0031 + 0.0107 -0.0008 -0.0038 0.0000 -0.0077 -0.0088 + -0.0122 -0.0117 -0.0097 -0.0004 -0.0022 0.0012 + -0.0013 0.0021 -0.0013 -0.0054 -0.0060 0.0105 + 0.0123 0.0022 -0.0031 -0.0038 0.0058 -0.0023 + -0.0038 -0.0019 -0.0044 0.0042 0.0081 0.0161 + 0.0152 0.0109 0.0145 0.0099 -0.0002 -0.0104 + -0.0029 -0.0047 0.0060 0.0002 0.0076 0.0011 + -0.0006 -0.0114 -0.0091 -0.0010 -0.0022 -0.0006 + 0.0063 0.0092 -0.0035 -0.0053 -0.0043 -0.0073 + -0.0112 -0.0098 -0.0098 0.0007 0.0009 -0.0051 + 0.0056 0.0006 0.0076 -0.0044 -0.0132 -0.0070 + 0.0091 0.0109 0.0109 -0.0100 -0.0043 -0.0024 + -0.0049 0.0030 0.0003 0.0120 0.0097 0.0070 + 0.0076 0.0107 0.0136 0.0168 0.0147 -0.0006 + -0.0124 -0.0052 -0.0038 0.0017 0.0103 0.0097 + 0.0028 -0.0081 -0.0014 0.0056 0.0120 0.0142 + 0.0096 0.0065 0.0042 0.0028 -0.0040 0.0020 + 0.0053 0.0001 -0.0058 -0.0135 -0.0090 -0.0049 + -0.0041 -0.0065 -0.0093 0.0262 -0.0026 -0.0126 + -0.0117 -0.0004 0.0091 0.0058 -0.0031 -0.0104 + -0.0070 0.0047 0.0148 0.0066 0.0063 0.0081 + 0.0033 0.0007 0.0062 0.0087 0.0154 0.0158 + 0.0027 -0.0144 -0.0045 -0.0001 -0.0009 0.0004 + 0.0021 -0.0044 -0.0081 0.0007 0.0094 0.0148 + 0.0083 0.0060 0.0008 -0.0013 0.0016 0.0181 + 0.0059 0.0067 -0.0025 -0.0143 -0.0147 -0.0121 + -0.0044 -0.0008 -0.0119 -0.0099 0.0153 0.0006 + -0.0096 -0.0134 -0.0061 0.0071 -0.0008 0.0009 + -0.0064 -0.0024 0.0046 0.0165 0.0141 0.0022 + 0.0056 0.0030 -0.0060 -0.0049 0.0075 0.0051 + 0.0100 -0.0003 -0.0112 -0.0060 -0.0020 -0.0005 + -0.0058 -0.0148 -0.0056 0.0010 0.0004 0.0017 + 0.0056 0.0044 -0.0021 -0.0023 0.0060 0.0028 + 0.0102 0.0011 0.0037 -0.0010 -0.0062 0.0009 + 0.0029 0.0062 0.0016 -0.0060 0.0130 0.0141 + 0.0031 -0.0076 -0.0079 -0.0012 0.0034 0.0035 + 0.0082 0.0029 0.0125 0.0165 0.0164 0.0104 + -0.0007 0.0019 0.0076 -0.0017 -0.0060 -0.0037 + -0.0061 0.0006 0.0009 -0.0042 -0.0054 -0.0012 + -0.0043 -0.0033 -0.0084 -0.0026 0.0001 0.0010 + -0.0004 -0.0066 -0.0061 -0.0013 0.0107 0.0126 + 0.0174 0.0072 -0.0060 -0.0014 -0.0009 -0.0018 + 0.0028 0.0126 0.0169 0.0046 -0.0079 0.0127 + 0.0008 0.0002 -0.0133 -0.0036 0.0026 0.0104 + 0.0129 0.0116 0.0058 0.0147 0.0164 0.0101 + -0.0039 -0.0133 0.0060 0.0020 0.0001 -0.0063 + -0.0051 0.0052 0.0028 0.0015 0.0030 -0.0015 + -0.0049 -0.0092 -0.0060 0.0024 0.0063 -0.0018 + -0.0052 -0.0095 0.0006 0.0056 -0.0057 0.0031 + 0.0156 0.0076 -0.0020 -0.0042 -0.0047 -0.0029 + -0.0014 -0.0011 0.0127 0.0236 0.0142 0.0089 + -0.0045 -0.0088 -0.0037 -0.0138 0.0016 0.0068 + 0.0116 0.0097 0.0131 0.0109 0.0097 0.0054 + -0.0047 -0.0111 -0.0109 0.0033 -0.0057 -0.0053 + -0.0035 -0.0012 -0.0028 0.0010 -0.0014 -0.0016 + -0.0020 -0.0135 -0.0118 -0.0062 0.0160 0.0072 + -0.0063 -0.0072 -0.0002 0.0101 -0.0013 -0.0047 + 0.0031 0.0057 -0.0069 -0.0094 -0.0017 0.0023 + 0.0020 0.0022 0.0017 0.0112 0.0151 0.0029 + -0.0057 -0.0139 -0.0103 -0.0079 -0.0134 0.0033 + 0.0064 0.0113 0.0120 0.0134 0.0072 -0.0018 + -0.0043 -0.0075 -0.0122 -0.0079 0.0005 -0.0021 + -0.0004 -0.0043 -0.0032 -0.0013 -0.0028 -0.0035 + -0.0030 -0.0002 -0.0105 -0.0196 -0.0018 0.0104 + 0.0009 -0.0011 -0.0034 -0.0040 -0.0007 -0.0077 + -0.0114 -0.0095 -0.0046 -0.0062 -0.0076 -0.0010 + 0.0046 0.0031 0.0001 0.0016 0.0025 -0.0023 + -0.0087 -0.0124 -0.0148 -0.0120 -0.0162 -0.0156 + -0.0006 0.0074 0.0082 0.0084 0.0216 0.0108 + -0.0001 -0.0078 -0.0156 -0.0099 -0.0011 -0.0002 + -0.0052 -0.0018 -0.0013 -0.0016 -0.0037 -0.0044 + -0.0049 -0.0053 -0.0046 -0.0237 -0.0183 -0.0054 + 0.0035 0.0001 -0.0052 -0.0067 -0.0144 -0.0111 + -0.0180 -0.0146 -0.0024 -0.0027 -0.0048 -0.0013 + -0.0012 0.0024 -0.0099 -0.0075 -0.0051 -0.0047 + -0.0071 -0.0245 -0.0210 -0.0169 -0.0228 -0.0235 + -0.0095 0.0102 -0.0033 -0.0021 0.0041 0.0077 + 0.0026 -0.0011 -0.0131 -0.0114 -0.0071 0.0014 + 0.0039 0.0040 0.0015 0.0007 0.0048 0.0006 + -0.0059 0.0022 0.0069 0.0061 0.0041 0.0051 + 0.0044 0.0004 -0.0071 -0.0180 -0.0078 -0.0052 + 0.0033 -0.0057 -0.0061 0.0023 0.0040 -0.0033 + -0.0031 -0.0010 -0.0025 -0.0111 -0.0092 -0.0053 + -0.0038 -0.0073 -0.0156 -0.0161 -0.0185 -0.0226 + -0.0216 -0.0053 0.0068 -0.0004 -0.0010 -0.0002 + -0.0023 -0.0072 -0.0053 -0.0089 -0.0087 -0.0030 + 0.0038 0.0086 0.0058 0.0050 0.0099 0.0176 + 0.0099 0.0008 0.0039 0.0087 0.0040 0.0028 + 0.0019 -0.0024 -0.0086 -0.0146 -0.0104 -0.0034 + 0.0017 0.0063 0.0122 0.0051 -0.0028 -0.0029 + -0.0039 0.0026 0.0035 0.0037 0.0043 0.0092 + 0.0075 0.0061 -0.0037 -0.0036 -0.0056 -0.0153 + -0.0218 -0.0158 -0.0063 0.0003 0.0035 0.0040 + 0.0002 -0.0126 -0.0125 -0.0078 -0.0084 -0.0056 + 0.0032 0.0075 0.0053 0.0054 0.0030 -0.0049 + 0.0008 -0.0025 -0.0009 0.0021 0.0123 0.0135 + -0.0030 -0.0071 -0.0116 -0.0165 -0.0132 -0.0056 + 0.0022 0.0016 -0.0006 0.0063 0.0025 -0.0018 + -0.0068 -0.0042 -0.0047 0.0034 0.0067 0.0062 + 0.0097 0.0121 0.0117 -0.0009 -0.0152 -0.0114 + -0.0148 -0.0138 -0.0079 -0.0052 -0.0029 -0.0003 + 0.0065 -0.0057 -0.0186 -0.0150 -0.0040 0.0017 + 0.0060 0.0136 0.0143 0.0132 0.0140 0.0120 + 0.0006 -0.0079 -0.0070 -0.0023 0.0002 0.0048 + 0.0167 0.0007 -0.0088 -0.0055 -0.0011 -0.0079 + -0.0102 -0.0051 -0.0060 -0.0102 -0.0067 -0.0007 + 0.0001 -0.0048 -0.0024 0.0072 0.0024 -0.0034 + -0.0047 0.0039 0.0093 0.0055 -0.0131 -0.0172 + -0.0148 -0.0138 -0.0102 -0.0015 -0.0015 -0.0036 + 0.0053 0.0028 -0.0159 -0.0208 -0.0046 0.0057 + 0.0082 0.0147 0.0157 0.0116 0.0165 0.0141 + 0.0175 0.0102 -0.0032 -0.0065 -0.0056 -0.0029 + 0.0059 0.0181 -0.0071 -0.0057 0.0019 0.0090 + 0.0043 -0.0040 -0.0144 -0.0145 -0.0241 -0.0204 + 0.0025 0.0072 0.0048 -0.0004 -0.0021 -0.0060 + -0.0140 -0.0114 -0.0019 -0.0000 -0.0025 -0.0183 + -0.0161 -0.0119 -0.0087 -0.0043 0.0009 -0.0000 + -0.0021 0.0020 -0.0007 -0.0068 -0.0041 0.0007 + 0.0092 0.0134 0.0223 0.0167 0.0059 0.0174 + 0.0155 0.0162 0.0202 0.0063 -0.0038 -0.0013 + 0.0001 0.0026 0.0018 0.0062 0.0039 0.0052 + 0.0071 0.0074 0.0024 -0.0080 -0.0109 -0.0148 + -0.0064 0.0075 0.0135 0.0101 0.0030 -0.0104 + -0.0070 -0.0063 -0.0084 -0.0004 0.0033 -0.0059 + -0.0197 -0.0189 -0.0107 -0.0037 0.0010 -0.0027 + -0.0065 -0.0040 -0.0035 -0.0037 -0.0011 -0.0030 + 0.0011 0.0079 0.0160 0.0125 0.0053 0.0033 + 0.0140 0.0147 0.0123 0.0171 0.0117 0.0042 + 0.0021 0.0001 -0.0063 -0.0061 0.0009 0.0083 + 0.0091 0.0014 0.0111 0.0052 -0.0060 -0.0147 + -0.0154 -0.0045 0.0081 0.0131 0.0077 -0.0053 + -0.0162 -0.0026 0.0011 0.0022 0.0018 0.0019 + -0.0157 -0.0195 -0.0170 -0.0038 0.0054 0.0009 + -0.0025 -0.0038 -0.0064 -0.0092 -0.0060 0.0004 + 0.0049 0.0132 0.0196 0.0126 0.0116 0.0108 + 0.0093 0.0099 0.0090 0.0073 0.0070 0.0022 + -0.0001 -0.0001 -0.0053 -0.0071 -0.0122 0.0126 + 0.0168 0.0016 0.0059 0.0045 0.0013 -0.0068 + -0.0097 -0.0166 0.0068 0.0054 0.0046 -0.0032 + -0.0142 -0.0116 -0.0011 0.0048 0.0076 0.0012 + -0.0053 -0.0253 -0.0212 -0.0148 -0.0033 0.0063 + 0.0001 -0.0043 -0.0078 -0.0011 -0.0051 -0.0064 + 0.0049 0.0170 0.0199 0.0111 0.0041 0.0104 + 0.0146 0.0190 0.0032 0.0071 0.0131 0.0041 + -0.0006 0.0009 0.0086 0.0052 -0.0008 -0.0051 + 0.0044 0.0074 -0.0002 0.0014 0.0007 0.0025 + 0.0045 0.0012 0.0027 0.0002 -0.0012 -0.0081 + -0.0112 -0.0021 0.0014 0.0071 0.0129 0.0110 + 0.0079 -0.0053 -0.0142 -0.0124 -0.0064 0.0041 + 0.0051 -0.0015 -0.0006 -0.0041 0.0046 -0.0042 + 0.0032 0.0116 0.0083 0.0031 0.0060 -0.0014 + 0.0029 0.0071 0.0169 0.0017 0.0073 0.0078 + -0.0052 -0.0006 0.0044 0.0195 0.0116 0.0000 + 0.0019 0.0054 0.0006 0.0085 0.0024 0.0014 + -0.0031 0.0014 -0.0016 -0.0093 -0.0127 -0.0124 + -0.0143 0.0010 -0.0024 -0.0010 0.0074 0.0171 + 0.0072 0.0048 -0.0003 -0.0144 -0.0130 0.0059 + 0.0111 0.0060 -0.0012 -0.0053 -0.0080 -0.0017 + 0.0040 0.0093 0.0159 0.0037 -0.0054 -0.0048 + 0.0045 0.0127 0.0097 0.0127 -0.0090 -0.0022 + 0.0078 -0.0043 -0.0035 0.0017 0.0068 0.0092 + -0.0042 -0.0034 -0.0002 0.0102 0.0208 0.0205 + 0.0102 0.0014 -0.0091 -0.0125 -0.0127 -0.0120 + -0.0128 -0.0069 0.0023 -0.0038 -0.0068 0.0082 + 0.0231 0.0191 0.0109 0.0080 -0.0001 -0.0048 + 0.0057 0.0083 0.0038 -0.0021 -0.0045 -0.0085 + -0.0029 0.0038 0.0121 0.0148 0.0037 -0.0066 + -0.0059 -0.0004 0.0038 0.0052 0.0034 -0.0104 + -0.0041 -0.0041 -0.0046 0.0022 0.0096 0.0027 + -0.0054 -0.0002 0.0023 -0.0066 0.0193 0.0259 + 0.0211 0.0188 0.0121 0.0024 -0.0003 0.0041 + -0.0037 -0.0084 -0.0014 -0.0008 -0.0040 0.0012 + 0.0151 0.0240 0.0196 0.0101 0.0072 0.0052 + 0.0088 0.0017 0.0003 0.0031 0.0040 0.0012 + -0.0029 0.0025 0.0047 0.0126 0.0080 -0.0030 + -0.0086 -0.0045 -0.0028 -0.0010 0.0034 -0.0029 + -0.0120 -0.0112 -0.0153 -0.0128 0.0006 0.0114 + 0.0042 0.0002 0.0036 0.0047 0.0073 0.0198 + 0.0185 0.0092 0.0096 0.0122 0.0045 0.0034 + 0.0112 0.0083 0.0056 0.0028 -0.0070 -0.0029 + 0.0028 0.0171 0.0127 0.0146 0.0027 0.0004 + 0.0035 0.0068 0.0048 0.0022 0.0054 0.0073 + 0.0036 -0.0000 0.0032 -0.0006 0.0008 -0.0004 + -0.0026 0.0013 0.0016 0.0023 0.0048 0.0008 + -0.0112 -0.0052 -0.0057 -0.0123 -0.0162 -0.0055 + 0.0026 -0.0010 -0.0041 -0.0077 -0.0043 0.0095 + 0.0180 0.0117 0.0035 -0.0008 0.0072 0.0116 + 0.0081 0.0066 0.0078 0.0096 0.0077 -0.0018 + -0.0163 -0.0007 0.0219 0.0143 0.0065 -0.0014 + -0.0026 -0.0028 0.0004 0.0039 0.0041 0.0063 + 0.0102 0.0048 0.0050 0.0050 0.0019 -0.0001 + -0.0009 0.0003 0.0062 0.0104 0.0073 0.0014 + -0.0148 -0.0176 0.0042 -0.0051 -0.0095 -0.0131 + -0.0160 -0.0100 -0.0069 0.0005 -0.0025 -0.0047 + 0.0049 0.0061 0.0036 -0.0000 0.0023 0.0092 + 0.0124 0.0051 0.0009 0.0020 -0.0032 0.0192 + 0.0075 -0.0101 0.0035 0.0138 0.0094 0.0049 + -0.0065 -0.0087 -0.0111 -0.0074 -0.0044 -0.0017 + 0.0011 0.0034 0.0052 0.0101 0.0070 0.0002 + -0.0006 -0.0021 -0.0008 0.0085 0.0128 0.0052 + -0.0072 -0.0174 -0.0211 -0.0107 -0.0097 -0.0047 + -0.0018 -0.0041 -0.0052 -0.0037 0.0043 0.0021 + 0.0037 0.0068 0.0012 -0.0048 -0.0044 0.0044 + 0.0065 0.0049 -0.0041 -0.0052 -0.0067 -0.0090 + 0.0097 0.0102 -0.0100 -0.0024 0.0005 0.0064 + 0.0016 -0.0076 -0.0083 -0.0116 -0.0105 -0.0120 + -0.0077 0.0012 0.0087 0.0177 0.0060 0.0019 + -0.0029 -0.0061 -0.0030 -0.0015 0.0012 0.0060 + 0.0025 -0.0056 -0.0129 -0.0105 -0.0078 -0.0051 + -0.0011 0.0044 0.0051 -0.0042 0.0062 0.0034 + 0.0048 0.0068 0.0022 -0.0011 -0.0046 -0.0040 + 0.0005 -0.0014 -0.0029 0.0004 -0.0022 -0.0057 + -0.0019 0.0042 -0.0012 -0.0154 -0.0109 -0.0046 + 0.0023 0.0009 -0.0078 -0.0127 -0.0137 -0.0086 + -0.0092 -0.0025 0.0064 0.0140 0.0157 0.0059 + -0.0027 -0.0023 -0.0032 -0.0013 -0.0007 0.0023 + -0.0011 -0.0045 -0.0102 -0.0088 -0.0034 -0.0117 + -0.0109 -0.0064 -0.0002 0.0032 -0.0073 -0.0097 + 0.0019 0.0092 0.0086 -0.0027 -0.0074 -0.0071 + -0.0120 -0.0046 -0.0043 -0.0018 0.0017 0.0077 + 0.0027 -0.0023 0.0036 -0.0100 -0.0244 -0.0128 + 0.0095 0.0054 -0.0024 -0.0024 -0.0076 -0.0132 + -0.0086 -0.0092 0.0039 0.0059 0.0072 0.0078 + 0.0036 -0.0007 0.0027 0.0047 0.0031 0.0029 + -0.0015 -0.0090 -0.0039 -0.0052 -0.0016 -0.0029 + -0.0149 -0.0185 -0.0116 -0.0062 -0.0015 -0.0090 + -0.0156 -0.0063 0.0001 0.0006 -0.0061 -0.0056 + -0.0088 -0.0125 -0.0075 -0.0028 0.0019 0.0044 + 0.0055 0.0016 -0.0086 -0.0041 -0.0112 -0.0190 + 0.0013 0.0121 0.0054 0.0003 0.0006 -0.0054 + -0.0067 -0.0036 -0.0065 -0.0022 0.0010 0.0022 + 0.0027 0.0033 0.0062 0.0096 0.0112 0.0086 + 0.0012 -0.0017 -0.0091 -0.0099 -0.0073 -0.0018 + -0.0014 diff --git a/libres/test-data/local/geometry/surface/valid_ascii.irap b/libres/test-data/local/geometry/surface/valid_ascii.irap new file mode 100755 index 00000000000..89e56212027 --- /dev/null +++ b/libres/test-data/local/geometry/surface/valid_ascii.irap @@ -0,0 +1,650 @@ + -996 79 50.000000 50.000000 + 463325.562500 465725.562500 7336963.500000 7340863.500000 + 49 -65.000000 463325.562500 7336963.500000 + 0 0 0 0 0 0 0 + 0.0051 0.0017 -0.0101 -0.0126 -0.0078 0.0019 + 0.0002 -0.0039 -0.0100 -0.0134 -0.0076 -0.0017 + 0.0014 0.0045 0.0058 -0.0002 -0.0029 -0.0023 + 0.0048 0.0063 0.0107 0.0043 -0.0003 0.0044 + 0.0009 0.0036 0.0033 0.0055 0.0174 0.0168 + 0.0142 0.0073 0.0063 0.0113 0.0083 0.0050 + 0.0092 0.0054 0.0037 0.0034 0.0057 0.0034 + -0.0031 0.0034 0.0035 -0.0119 0.0004 0.0101 + 0.0218 -0.0013 -0.0026 -0.0042 -0.0107 -0.0079 + 0.0000 0.0034 -0.0032 -0.0111 -0.0106 -0.0028 + 0.0015 0.0050 0.0028 -0.0037 -0.0079 -0.0036 + 0.0047 -0.0039 0.0088 0.0091 0.0013 -0.0072 + -0.0075 -0.0027 0.0003 -0.0008 -0.0049 0.0036 + 0.0079 0.0021 0.0085 0.0151 0.0135 0.0112 + 0.0139 0.0176 0.0081 0.0041 0.0083 0.0077 + 0.0023 -0.0049 0.0042 -0.0074 -0.0134 -0.0015 + 0.0173 0.0204 -0.0083 -0.0086 -0.0008 -0.0014 + 0.0000 -0.0002 -0.0005 -0.0024 -0.0070 -0.0145 + -0.0050 0.0015 0.0019 -0.0029 -0.0121 -0.0136 + -0.0050 0.0054 0.0013 0.0056 0.0063 -0.0000 + -0.0084 -0.0124 -0.0023 -0.0012 -0.0062 -0.0078 + 0.0006 0.0045 0.0048 0.0084 0.0124 0.0145 + 0.0173 0.0205 0.0130 0.0071 0.0102 0.0179 + 0.0126 0.0054 -0.0094 0.0025 -0.0025 -0.0166 + -0.0021 0.0228 0.0183 -0.0099 -0.0111 -0.0123 + -0.0120 -0.0098 -0.0070 -0.0053 -0.0030 -0.0048 + -0.0051 -0.0031 0.0009 0.0004 -0.0016 -0.0107 + -0.0129 -0.0066 0.0036 0.0048 0.0015 0.0012 + 0.0030 -0.0055 -0.0066 -0.0023 -0.0030 -0.0007 + 0.0027 0.0078 0.0086 0.0070 0.0101 0.0135 + 0.0124 0.0216 0.0161 0.0074 0.0087 0.0149 + 0.0161 0.0131 0.0078 -0.0079 0.0048 -0.0069 + -0.0132 -0.0016 0.0319 0.0240 -0.0114 -0.0103 + -0.0131 -0.0154 -0.0105 -0.0060 -0.0094 -0.0076 + -0.0022 -0.0014 -0.0002 -0.0002 -0.0023 -0.0033 + -0.0097 -0.0033 -0.0019 0.0055 0.0001 -0.0007 + -0.0086 -0.0082 -0.0063 0.0029 0.0072 0.0071 + 0.0061 0.0140 0.0178 0.0160 0.0136 0.0152 + 0.0189 0.0182 0.0220 0.0201 0.0153 0.0110 + 0.0087 0.0073 0.0117 0.0069 -0.0149 0.0080 + -0.0062 -0.0185 -0.0008 0.0237 0.0083 -0.0104 + -0.0123 -0.0118 -0.0106 -0.0049 0.0025 0.0011 + -0.0009 0.0021 0.0013 0.0016 0.0014 -0.0005 + 0.0006 -0.0033 0.0024 0.0074 0.0009 -0.0040 + -0.0075 -0.0120 -0.0106 0.0043 0.0142 0.0162 + 0.0131 0.0128 0.0202 0.0246 0.0149 0.0068 + 0.0065 0.0058 0.0140 0.0269 0.0311 0.0138 + 0.0024 -0.0052 -0.0062 0.0026 0.0004 -0.0118 + 0.0003 0.0001 -0.0139 0.0065 0.0256 0.0177 + -0.0092 -0.0075 -0.0133 -0.0097 -0.0030 0.0077 + 0.0043 0.0046 -0.0011 -0.0014 0.0012 -0.0021 + -0.0009 -0.0016 0.0036 0.0069 -0.0023 0.0039 + -0.0109 -0.0086 -0.0060 0.0022 0.0148 0.0182 + 0.0134 0.0144 0.0187 0.0197 0.0181 0.0135 + 0.0053 0.0043 0.0135 0.0246 0.0226 0.0140 + 0.0025 -0.0046 -0.0036 -0.0043 -0.0081 0.0108 + -0.0009 -0.0077 -0.0014 -0.0134 0.0091 0.0187 + 0.0182 -0.0004 0.0013 -0.0078 -0.0107 -0.0033 + 0.0008 0.0026 0.0038 0.0007 -0.0001 -0.0023 + -0.0062 -0.0085 -0.0004 0.0099 0.0097 0.0007 + 0.0059 -0.0091 -0.0084 0.0068 0.0153 0.0180 + 0.0133 0.0103 0.0102 0.0142 0.0165 0.0134 + 0.0056 0.0044 0.0037 0.0110 0.0147 0.0119 + 0.0049 0.0008 0.0035 0.0070 -0.0025 -0.0037 + 0.0227 -0.0061 -0.0042 0.0001 -0.0122 0.0085 + 0.0154 0.0151 0.0007 -0.0031 -0.0067 -0.0080 + 0.0023 0.0018 -0.0027 -0.0006 -0.0036 -0.0071 + -0.0089 -0.0072 -0.0072 0.0063 0.0126 0.0161 + 0.0070 -0.0027 -0.0115 0.0002 0.0077 0.0122 + 0.0080 0.0112 0.0143 0.0152 0.0141 0.0140 + 0.0136 0.0111 0.0063 0.0054 0.0067 0.0074 + 0.0109 0.0138 0.0088 0.0088 0.0118 0.0027 + 0.0033 0.0045 -0.0119 0.0004 -0.0078 -0.0018 + 0.0051 0.0055 0.0033 0.0039 -0.0014 -0.0043 + 0.0001 0.0069 -0.0034 -0.0046 -0.0055 -0.0064 + -0.0059 -0.0017 0.0065 0.0068 0.0088 0.0131 + 0.0041 0.0010 -0.0020 0.0034 0.0068 0.0054 + 0.0080 0.0107 0.0155 0.0135 0.0169 0.0161 + 0.0094 0.0124 0.0138 0.0103 0.0079 0.0055 + 0.0060 0.0122 0.0193 0.0169 0.0227 0.0157 + 0.0098 0.0089 -0.0063 -0.0101 -0.0043 -0.0097 + 0.0206 0.0057 0.0016 0.0022 0.0037 0.0029 + 0.0045 0.0092 0.0073 -0.0041 -0.0055 -0.0103 + -0.0107 -0.0068 -0.0028 -0.0004 0.0007 0.0017 + -0.0022 -0.0001 0.0007 -0.0030 0.0064 0.0025 + 0.0045 0.0054 0.0060 0.0132 0.0164 0.0233 + 0.0204 0.0169 0.0172 0.0172 0.0148 0.0136 + 0.0143 0.0147 0.0150 0.0165 0.0156 0.0219 + 0.0157 0.0071 0.0103 -0.0051 -0.0107 -0.0103 + 0.0068 0.0203 0.0130 0.0104 0.0098 0.0038 + 0.0046 0.0099 0.0131 0.0117 -0.0061 -0.0010 + -0.0050 0.0006 0.0028 0.0034 0.0042 0.0030 + 0.0032 -0.0036 -0.0011 0.0054 -0.0062 0.0077 + 0.0073 0.0083 0.0079 0.0095 0.0091 0.0136 + 0.0198 0.0228 0.0248 0.0200 0.0164 0.0180 + 0.0182 0.0207 0.0211 0.0157 0.0134 0.0137 + 0.0103 0.0108 0.0148 0.0091 -0.0106 -0.0164 + -0.0062 0.0124 0.0180 0.0156 0.0117 0.0092 + 0.0060 0.0059 0.0045 0.0056 0.0058 -0.0049 + 0.0024 0.0080 0.0086 0.0108 -0.0004 -0.0023 + -0.0025 0.0011 -0.0033 0.0008 0.0015 0.0004 + -0.0017 -0.0004 0.0065 0.0083 0.0096 0.0086 + 0.0080 0.0145 0.0229 0.0215 0.0180 0.0136 + 0.0104 0.0091 0.0075 0.0027 -0.0008 0.0042 + 0.0111 0.0113 0.0096 0.0066 0.0046 -0.0076 + -0.0138 0.0052 0.0151 0.0164 0.0124 0.0108 + 0.0164 0.0006 0.0045 0.0040 -0.0021 -0.0009 + 0.0051 -0.0006 -0.0005 0.0041 0.0072 0.0003 + -0.0030 -0.0076 -0.0042 -0.0024 0.0076 0.0009 + 0.0013 -0.0046 0.0041 0.0022 0.0007 0.0089 + 0.0126 0.0075 0.0083 0.0118 0.0183 0.0095 + 0.0036 0.0014 0.0016 -0.0018 -0.0046 -0.0043 + -0.0012 0.0036 0.0090 0.0121 0.0025 0.0026 + -0.0060 -0.0085 0.0104 0.0147 0.0157 0.0089 + 0.0085 0.0195 0.0003 0.0088 0.0101 0.0022 + 0.0047 0.0006 -0.0055 -0.0059 -0.0015 -0.0029 + -0.0011 -0.0006 -0.0025 -0.0016 0.0006 0.0004 + 0.0023 0.0088 0.0006 0.0034 -0.0010 0.0002 + 0.0094 0.0220 0.0156 0.0087 0.0137 0.0124 + 0.0039 -0.0005 0.0031 0.0070 0.0058 0.0000 + -0.0016 -0.0018 -0.0036 -0.0009 0.0072 0.0020 + 0.0013 -0.0031 -0.0085 0.0036 0.0114 0.0101 + 0.0075 0.0064 0.0085 0.0024 0.0093 0.0117 + 0.0028 0.0023 -0.0015 -0.0055 -0.0075 -0.0053 + -0.0079 -0.0097 -0.0076 -0.0034 0.0016 0.0026 + -0.0047 -0.0065 0.0151 -0.0032 -0.0033 -0.0009 + 0.0023 0.0137 0.0188 0.0170 0.0128 0.0096 + 0.0096 0.0159 0.0107 0.0076 0.0049 -0.0001 + -0.0029 -0.0007 0.0025 -0.0010 -0.0044 -0.0032 + 0.0003 0.0121 -0.0084 -0.0086 0.0012 0.0101 + 0.0118 0.0055 -0.0000 0.0041 0.0100 0.0114 + 0.0060 -0.0007 -0.0014 0.0052 -0.0078 -0.0068 + -0.0022 -0.0034 -0.0065 -0.0090 -0.0009 0.0074 + 0.0033 -0.0015 -0.0043 0.0192 0.0035 -0.0026 + -0.0001 0.0061 0.0095 0.0118 0.0135 0.0139 + 0.0129 0.0045 0.0121 0.0211 0.0091 -0.0005 + -0.0059 -0.0070 -0.0019 0.0053 0.0121 0.0039 + -0.0003 0.0030 0.0089 -0.0181 -0.0106 -0.0025 + 0.0113 0.0107 0.0050 0.0021 0.0014 0.0018 + -0.0007 0.0038 0.0001 -0.0052 0.0005 -0.0102 + -0.0022 0.0077 0.0122 0.0091 0.0009 0.0008 + 0.0078 0.0023 -0.0042 0.0019 0.0064 -0.0013 + 0.0025 0.0023 0.0043 0.0082 0.0090 0.0096 + 0.0137 0.0155 0.0114 0.0096 0.0145 0.0106 + 0.0029 -0.0012 -0.0049 0.0004 0.0084 0.0141 + 0.0073 0.0052 0.0098 0.0086 -0.0169 -0.0136 + 0.0043 0.0027 -0.0045 -0.0085 -0.0125 -0.0060 + 0.0013 0.0018 -0.0017 -0.0084 0.0017 -0.0052 + -0.0074 -0.0036 0.0113 0.0218 0.0227 0.0140 + 0.0008 0.0056 0.0072 0.0055 0.0034 -0.0021 + 0.0043 0.0100 0.0100 0.0102 0.0089 0.0108 + 0.0108 0.0127 0.0182 0.0143 0.0105 0.0099 + 0.0041 -0.0018 -0.0028 -0.0018 0.0037 0.0094 + 0.0119 0.0117 0.0110 0.0123 0.0063 -0.0058 + -0.0222 0.0036 -0.0002 -0.0181 -0.0173 -0.0027 + 0.0046 0.0096 0.0136 -0.0014 -0.0006 -0.0006 + -0.0035 0.0013 0.0088 0.0125 0.0167 0.0255 + 0.0175 0.0077 0.0013 0.0036 0.0032 -0.0032 + -0.0057 0.0061 0.0107 0.0131 0.0125 0.0039 + 0.0051 0.0067 0.0089 0.0120 0.0057 -0.0024 + 0.0002 0.0023 -0.0053 -0.0062 -0.0024 0.0067 + 0.0152 0.0178 0.0148 0.0129 0.0160 0.0056 + 0.0082 -0.0148 0.0060 -0.0035 -0.0126 -0.0114 + 0.0024 0.0059 0.0063 0.0067 -0.0007 0.0019 + -0.0018 -0.0008 0.0036 0.0047 0.0067 0.0104 + 0.0157 0.0160 0.0025 0.0022 0.0026 -0.0071 + -0.0067 -0.0009 0.0029 0.0077 0.0120 0.0105 + 0.0038 0.0029 0.0063 0.0097 0.0057 -0.0035 + -0.0083 -0.0029 0.0076 0.0037 -0.0048 -0.0068 + 0.0033 0.0123 0.0192 0.0203 0.0208 0.0186 + 0.0120 0.0041 -0.0150 0.0060 -0.0009 -0.0070 + 0.0011 0.0069 0.0041 0.0078 0.0042 0.0005 + -0.0042 -0.0195 0.0011 0.0018 0.0010 0.0070 + 0.0048 0.0037 -0.0021 0.0010 0.0011 -0.0075 + -0.0075 -0.0020 0.0084 0.0060 0.0003 0.0033 + 0.0035 0.0015 0.0006 0.0012 0.0021 0.0026 + 0.0011 -0.0021 -0.0028 0.0038 0.0120 0.0106 + -0.0036 -0.0005 0.0163 0.0177 0.0214 0.0293 + 0.0191 0.0124 0.0082 -0.0113 0.0026 -0.0058 + -0.0104 0.0114 0.0132 0.0088 -0.0026 -0.0046 + -0.0041 -0.0111 0.0026 0.0099 0.0114 0.0159 + 0.0132 0.0024 0.0040 -0.0018 -0.0022 -0.0049 + -0.0058 -0.0011 0.0039 0.0051 0.0058 0.0013 + -0.0029 -0.0006 0.0002 -0.0010 -0.0056 -0.0039 + -0.0021 0.0018 0.0007 -0.0013 0.0028 0.0131 + 0.0168 0.0080 0.0006 0.0072 0.0082 0.0050 + 0.0126 0.0205 0.0206 0.0068 -0.0036 -0.0038 + -0.0053 -0.0113 0.0114 0.0107 0.0122 -0.0031 + -0.0040 -0.0050 -0.0053 0.0118 0.0138 0.0123 + 0.0127 0.0077 0.0062 0.0024 -0.0035 -0.0006 + -0.0017 -0.0005 0.0037 0.0045 0.0065 0.0044 + 0.0022 -0.0011 -0.0001 0.0038 0.0031 -0.0023 + -0.0041 -0.0067 -0.0044 0.0009 0.0073 0.0103 + 0.0058 0.0054 0.0080 0.0014 0.0007 -0.0072 + -0.0080 0.0005 0.0170 0.0197 0.0067 -0.0003 + -0.0069 -0.0096 -0.0069 0.0057 0.0098 0.0186 + -0.0046 -0.0071 -0.0041 -0.0063 -0.0005 0.0072 + 0.0082 0.0096 0.0047 0.0085 0.0016 0.0028 + 0.0009 0.0025 0.0067 0.0074 0.0080 0.0097 + 0.0087 0.0037 -0.0009 -0.0055 -0.0031 0.0022 + 0.0072 -0.0017 -0.0077 -0.0098 -0.0022 0.0003 + -0.0035 -0.0118 -0.0160 -0.0141 -0.0119 -0.0088 + -0.0072 -0.0069 -0.0031 0.0116 0.0185 0.0028 + -0.0092 -0.0124 -0.0068 -0.0034 0.0071 0.0126 + 0.0170 -0.0014 -0.0023 -0.0003 -0.0031 0.0038 + 0.0100 0.0007 -0.0014 -0.0000 0.0009 -0.0008 + -0.0019 0.0075 -0.0021 0.0028 0.0074 0.0092 + 0.0111 0.0083 0.0016 -0.0050 -0.0084 -0.0052 + -0.0016 0.0156 0.0159 0.0034 -0.0088 -0.0039 + 0.0010 0.0009 -0.0004 -0.0025 -0.0036 -0.0057 + -0.0048 -0.0037 -0.0028 0.0006 0.0066 0.0165 + -0.0064 -0.0089 -0.0176 -0.0053 0.0001 0.0029 + 0.0100 0.0197 -0.0027 0.0010 0.0006 0.0032 + 0.0065 0.0089 0.0014 -0.0008 0.0010 -0.0013 + 0.0014 0.0040 0.0043 0.0096 0.0052 0.0039 + 0.0022 -0.0007 0.0010 0.0028 0.0071 0.0073 + 0.0034 0.0025 0.0142 0.0222 0.0160 0.0093 + 0.0012 0.0036 0.0059 0.0043 0.0013 -0.0046 + -0.0034 -0.0116 -0.0139 -0.0090 -0.0009 0.0094 + 0.0095 -0.0030 -0.0123 -0.0132 -0.0048 0.0053 + 0.0103 0.0085 0.0126 -0.0088 -0.0046 -0.0030 + 0.0085 0.0110 0.0080 0.0007 -0.0049 0.0074 + 0.0114 0.0012 -0.0004 0.0021 0.0026 0.0024 + 0.0009 0.0112 0.0157 0.0124 0.0087 0.0088 + 0.0112 0.0075 0.0054 0.0116 0.0149 0.0137 + 0.0140 0.0092 0.0014 -0.0002 -0.0042 -0.0118 + -0.0202 -0.0257 -0.0268 -0.0258 -0.0199 -0.0100 + 0.0087 0.0042 -0.0058 -0.0125 -0.0071 -0.0036 + 0.0062 0.0112 0.0115 0.0196 -0.0063 -0.0065 + -0.0015 0.0101 0.0118 0.0035 0.0048 -0.0063 + 0.0008 0.0035 -0.0051 -0.0038 -0.0016 -0.0054 + 0.0026 0.0086 0.0120 0.0215 0.0176 0.0129 + 0.0086 0.0080 0.0129 0.0126 0.0113 0.0159 + 0.0148 0.0049 -0.0067 -0.0168 -0.0177 -0.0218 + -0.0329 -0.0538 -0.0566 -0.0497 -0.0360 -0.0254 + -0.0103 0.0062 0.0091 0.0058 -0.0088 0.0012 + 0.0004 0.0046 0.0113 0.0161 0.0077 0.0030 + 0.0026 0.0009 0.0073 0.0106 0.0056 0.0074 + -0.0018 -0.0048 0.0014 0.0051 0.0011 0.0036 + -0.0031 0.0026 0.0078 0.0080 0.0106 0.0181 + 0.0199 0.0152 0.0135 0.0182 0.0150 0.0098 + 0.0060 0.0051 0.0038 -0.0049 -0.0117 -0.0211 + -0.0267 -0.0388 -0.0553 -0.0684 -0.0476 -0.0372 + -0.0258 -0.0131 0.0065 0.0117 0.0083 -0.0081 + -0.0099 0.0169 0.0102 0.0018 -0.0083 -0.0078 + 0.0048 0.0039 0.0084 0.0133 0.0119 0.0043 + -0.0034 -0.0064 -0.0039 -0.0024 0.0055 -0.0020 + 0.0026 0.0018 0.0068 0.0057 0.0056 0.0087 + 0.0108 0.0135 0.0134 0.0117 0.0143 0.0169 + 0.0081 0.0039 -0.0012 -0.0046 -0.0018 -0.0061 + -0.0187 -0.0267 -0.0306 -0.0442 -0.0524 -0.0412 + -0.0312 -0.0218 -0.0099 0.0135 0.0035 0.0029 + -0.0160 -0.0133 -0.0057 -0.0112 -0.0065 -0.0007 + 0.0018 0.0106 0.0103 0.0089 0.0091 0.0106 + 0.0059 0.0008 -0.0054 -0.0056 0.0063 -0.0069 + 0.0027 0.0026 0.0022 0.0013 0.0031 0.0084 + 0.0179 0.0161 0.0110 0.0095 0.0065 0.0108 + 0.0132 0.0084 0.0045 -0.0010 -0.0076 -0.0056 + -0.0058 -0.0146 -0.0223 -0.0192 -0.0215 -0.0322 + -0.0221 -0.0194 -0.0237 -0.0116 0.0045 -0.0005 + 0.0014 -0.0006 0.0028 -0.0027 -0.0075 -0.0015 + 0.0114 0.0077 0.0226 0.0159 0.0133 0.0073 + 0.0085 0.0089 0.0144 -0.0021 -0.0012 0.0065 + 0.0015 0.0064 0.0036 0.0001 -0.0043 -0.0019 + 0.0085 0.0165 0.0250 0.0195 0.0101 0.0032 + 0.0054 0.0083 0.0062 0.0031 -0.0008 -0.0010 + -0.0040 -0.0059 -0.0143 -0.0086 -0.0108 -0.0106 + -0.0044 -0.0090 -0.0068 -0.0060 -0.0093 0.0021 + 0.0041 0.0035 0.0062 0.0033 0.0013 0.0013 + 0.0003 0.0040 0.0042 0.0268 0.0303 0.0133 + 0.0076 0.0038 0.0061 0.0057 -0.0026 -0.0029 + 0.0058 0.0037 0.0080 0.0054 0.0021 -0.0018 + -0.0029 0.0013 0.0059 0.0092 0.0113 0.0054 + 0.0027 0.0027 0.0015 0.0015 0.0042 0.0039 + 0.0026 0.0013 -0.0002 -0.0006 -0.0106 -0.0132 + -0.0110 -0.0151 -0.0188 -0.0085 0.0013 -0.0058 + 0.0000 0.0013 0.0116 0.0061 0.0051 0.0047 + 0.0038 0.0079 0.0049 0.0036 0.0160 0.0204 + 0.0049 -0.0020 0.0017 0.0066 0.0081 0.0012 + -0.0048 0.0016 -0.0028 0.0015 0.0021 -0.0013 + -0.0004 0.0010 -0.0023 -0.0071 -0.0031 0.0027 + 0.0038 0.0058 0.0085 0.0040 -0.0012 0.0047 + 0.0044 -0.0008 -0.0023 -0.0020 -0.0090 -0.0149 + -0.0182 -0.0158 -0.0129 -0.0184 -0.0194 -0.0021 + -0.0057 0.0021 -0.0084 0.0077 0.0080 0.0163 + 0.0080 0.0017 0.0049 0.0044 -0.0029 0.0166 + 0.0137 0.0071 -0.0014 -0.0004 0.0111 0.0133 + -0.0000 0.0017 -0.0026 0.0006 0.0022 0.0010 + -0.0057 -0.0076 -0.0059 -0.0033 -0.0044 -0.0027 + 0.0019 0.0054 0.0072 0.0102 0.0078 0.0041 + -0.0037 -0.0150 -0.0117 -0.0146 -0.0189 -0.0184 + -0.0180 -0.0265 -0.0268 -0.0190 -0.0139 -0.0135 + -0.0061 -0.0071 -0.0026 -0.0004 0.0050 0.0130 + 0.0154 0.0073 0.0047 0.0025 0.0096 0.0085 + 0.0087 0.0104 0.0096 0.0025 0.0033 0.0142 + 0.0130 -0.0024 0.0010 -0.0060 0.0044 0.0049 + 0.0011 -0.0018 -0.0058 -0.0063 0.0019 0.0059 + 0.0004 -0.0013 -0.0003 0.0059 -0.0015 -0.0102 + -0.0147 -0.0267 -0.0099 -0.0124 -0.0210 -0.0227 + -0.0224 -0.0156 -0.0212 -0.0286 -0.0224 -0.0114 + -0.0064 -0.0066 -0.0111 -0.0054 0.0029 0.0073 + 0.0124 0.0126 0.0089 0.0106 0.0096 0.0094 + 0.0087 0.0102 0.0027 0.0052 0.0036 0.0081 + 0.0115 0.0111 -0.0041 0.0002 -0.0039 0.0006 + 0.0023 0.0011 0.0009 0.0028 -0.0022 0.0000 + 0.0083 0.0039 -0.0029 -0.0098 -0.0180 -0.0228 + -0.0239 -0.0241 -0.0222 -0.0166 -0.0139 -0.0152 + -0.0199 -0.0213 -0.0186 -0.0196 -0.0313 -0.0258 + -0.0154 -0.0050 -0.0042 -0.0147 -0.0059 0.0027 + 0.0050 0.0050 0.0019 0.0011 0.0089 0.0137 + 0.0126 0.0127 0.0130 0.0026 0.0009 0.0075 + 0.0125 0.0115 0.0116 0.0061 -0.0058 0.0018 + -0.0005 0.0009 0.0002 -0.0004 -0.0020 -0.0012 + -0.0037 -0.0038 -0.0019 -0.0064 -0.0064 -0.0135 + -0.0259 -0.0224 -0.0215 -0.0223 -0.0198 -0.0141 + -0.0087 -0.0112 -0.0131 -0.0195 -0.0212 -0.0212 + -0.0222 -0.0130 -0.0123 -0.0119 -0.0059 -0.0019 + 0.0042 -0.0017 0.0003 -0.0008 -0.0081 0.0041 + 0.0129 0.0149 0.0183 0.0247 0.0157 0.0025 + 0.0050 0.0084 0.0101 0.0108 0.0032 -0.0091 + -0.0012 0.0033 0.0034 -0.0025 -0.0047 -0.0082 + -0.0081 -0.0070 -0.0076 -0.0114 -0.0101 -0.0123 + -0.0127 -0.0211 -0.0192 -0.0127 -0.0123 -0.0145 + -0.0156 -0.0064 -0.0001 0.0058 -0.0044 -0.0122 + -0.0167 -0.0139 -0.0092 -0.0106 -0.0131 -0.0077 + 0.0053 0.0076 0.0022 0.0008 -0.0015 -0.0032 + 0.0062 0.0115 0.0083 0.0112 0.0157 0.0131 + 0.0075 0.0029 0.0055 0.0077 0.0067 0.0097 + -0.0080 0.0056 0.0020 -0.0040 -0.0123 -0.0152 + -0.0150 -0.0123 -0.0081 -0.0073 -0.0089 -0.0040 + -0.0126 -0.0120 -0.0103 -0.0052 -0.0086 -0.0112 + -0.0135 -0.0143 -0.0090 0.0016 0.0008 -0.0018 + -0.0029 -0.0066 -0.0077 -0.0070 -0.0065 -0.0063 + -0.0125 0.0030 0.0057 -0.0036 -0.0023 -0.0046 + -0.0016 0.0124 0.0186 0.0089 0.0120 0.0100 + 0.0096 0.0060 0.0035 0.0017 0.0023 0.0146 + 0.0095 -0.0033 0.0007 0.0017 0.0004 -0.0103 + -0.0160 -0.0154 -0.0151 -0.0136 -0.0100 -0.0013 + 0.0007 -0.0083 -0.0119 0.0065 0.0074 0.0028 + -0.0099 -0.0096 -0.0064 -0.0001 0.0057 0.0067 + 0.0038 -0.0011 -0.0064 -0.0083 -0.0055 -0.0096 + -0.0099 -0.0030 0.0033 0.0033 -0.0040 -0.0016 + -0.0061 -0.0005 0.0133 0.0238 0.0128 0.0113 + 0.0048 0.0013 -0.0069 -0.0027 0.0027 0.0008 + 0.0078 0.0074 -0.0024 -0.0082 0.0001 0.0022 + -0.0041 -0.0067 -0.0058 -0.0133 -0.0180 -0.0179 + -0.0069 -0.0042 -0.0129 -0.0133 0.0049 0.0023 + -0.0076 -0.0131 -0.0100 -0.0024 0.0031 0.0001 + -0.0010 0.0036 0.0050 -0.0080 -0.0104 -0.0031 + -0.0127 -0.0151 -0.0062 0.0088 0.0006 -0.0076 + -0.0053 -0.0042 -0.0008 0.0045 0.0087 0.0089 + 0.0030 0.0045 0.0038 0.0021 0.0009 0.0035 + -0.0006 0.0105 0.0053 -0.0018 0.0026 -0.0059 + -0.0038 -0.0078 -0.0110 -0.0159 -0.0210 -0.0281 + -0.0207 -0.0135 -0.0116 -0.0122 -0.0137 0.0016 + -0.0013 -0.0098 -0.0125 -0.0077 0.0002 0.0086 + 0.0007 -0.0010 0.0064 0.0058 -0.0030 -0.0011 + -0.0010 -0.0072 -0.0133 -0.0075 0.0067 0.0063 + -0.0085 -0.0068 -0.0013 -0.0076 0.0006 0.0010 + -0.0028 -0.0020 0.0135 0.0122 0.0101 0.0071 + 0.0028 -0.0028 0.0088 0.0083 0.0046 -0.0041 + 0.0001 -0.0012 -0.0095 -0.0199 -0.0233 -0.0266 + -0.0251 -0.0176 -0.0061 -0.0052 -0.0053 -0.0063 + 0.0035 0.0012 -0.0014 -0.0024 -0.0016 -0.0031 + -0.0018 0.0020 0.0039 0.0056 0.0020 0.0025 + -0.0006 0.0033 0.0101 -0.0036 -0.0014 0.0092 + 0.0008 -0.0097 -0.0127 -0.0059 -0.0087 -0.0037 + 0.0078 0.0051 -0.0011 0.0153 0.0156 0.0132 + 0.0109 0.0076 0.0001 0.0087 0.0089 0.0028 + -0.0032 0.0023 -0.0021 -0.0087 -0.0167 -0.0201 + -0.0150 -0.0172 -0.0125 -0.0051 -0.0037 0.0027 + 0.0094 0.0119 0.0085 0.0065 0.0049 -0.0064 + -0.0120 -0.0103 0.0001 0.0038 0.0037 -0.0002 + 0.0012 -0.0026 0.0120 0.0068 0.0070 -0.0072 + -0.0084 -0.0159 -0.0057 -0.0051 -0.0082 -0.0150 + -0.0059 0.0023 0.0036 0.0002 0.0161 0.0141 + 0.0109 0.0102 0.0093 0.0047 0.0041 0.0038 + 0.0021 0.0041 0.0055 -0.0027 -0.0121 -0.0176 + -0.0166 -0.0154 -0.0187 -0.0128 -0.0047 -0.0036 + 0.0070 0.0128 0.0073 0.0045 0.0043 0.0016 + -0.0058 -0.0097 -0.0053 0.0027 0.0026 -0.0009 + -0.0047 -0.0023 0.0032 0.0072 0.0085 -0.0045 + -0.0073 0.0022 0.0054 0.0009 -0.0002 -0.0057 + -0.0078 0.0077 -0.0039 -0.0040 -0.0086 0.0100 + 0.0138 0.0062 0.0085 0.0110 0.0081 0.0069 + 0.0165 0.0069 0.0009 0.0140 -0.0074 -0.0148 + -0.0133 -0.0112 -0.0128 -0.0128 -0.0058 -0.0111 + -0.0041 0.0096 0.0069 -0.0038 -0.0039 -0.0019 + -0.0045 -0.0041 -0.0073 -0.0039 0.0030 0.0003 + -0.0077 -0.0124 -0.0007 0.0037 0.0016 0.0069 + -0.0039 -0.0135 -0.0025 0.0071 0.0070 -0.0015 + 0.0082 0.0061 -0.0022 -0.0043 -0.0085 -0.0055 + 0.0007 -0.0037 0.0036 0.0106 0.0079 0.0095 + 0.0074 0.0112 0.0106 -0.0036 0.0081 -0.0105 + -0.0127 -0.0037 -0.0006 -0.0074 -0.0088 -0.0039 + -0.0101 -0.0097 -0.0031 0.0037 -0.0052 -0.0106 + -0.0072 -0.0037 -0.0063 -0.0077 -0.0054 -0.0029 + -0.0104 -0.0125 -0.0066 -0.0018 0.0006 0.0037 + 0.0055 0.0004 -0.0094 -0.0038 0.0035 0.0060 + -0.0040 0.0069 0.0082 0.0046 -0.0174 -0.0185 + -0.0115 -0.0050 -0.0028 0.0065 0.0104 0.0098 + 0.0109 0.0086 0.0043 0.0029 -0.0043 0.0033 + -0.0027 -0.0085 -0.0016 0.0046 -0.0009 -0.0026 + -0.0104 -0.0110 -0.0108 -0.0037 0.0035 -0.0001 + -0.0097 -0.0070 -0.0001 0.0057 0.0014 -0.0025 + -0.0092 -0.0158 -0.0054 0.0029 -0.0055 -0.0005 + 0.0008 -0.0016 0.0002 -0.0058 0.0043 0.0054 + 0.0025 -0.0038 -0.0011 0.0096 0.0157 -0.0160 + -0.0170 -0.0162 -0.0017 0.0027 0.0166 0.0125 + 0.0122 0.0134 0.0108 0.0029 -0.0036 -0.0071 + -0.0152 0.0059 -0.0088 -0.0075 0.0001 -0.0022 + -0.0027 -0.0041 -0.0045 0.0015 -0.0006 0.0031 + 0.0107 -0.0008 -0.0038 0.0000 -0.0077 -0.0088 + -0.0122 -0.0117 -0.0097 -0.0004 -0.0022 0.0012 + -0.0013 0.0021 -0.0013 -0.0054 -0.0060 0.0105 + 0.0123 0.0022 -0.0031 -0.0038 0.0058 -0.0023 + -0.0038 -0.0019 -0.0044 0.0042 0.0081 0.0161 + 0.0152 0.0109 0.0145 0.0099 -0.0002 -0.0104 + -0.0029 -0.0047 0.0060 0.0002 0.0076 0.0011 + -0.0006 -0.0114 -0.0091 -0.0010 -0.0022 -0.0006 + 0.0063 0.0092 -0.0035 -0.0053 -0.0043 -0.0073 + -0.0112 -0.0098 -0.0098 0.0007 0.0009 -0.0051 + 0.0056 0.0006 0.0076 -0.0044 -0.0132 -0.0070 + 0.0091 0.0109 0.0109 -0.0100 -0.0043 -0.0024 + -0.0049 0.0030 0.0003 0.0120 0.0097 0.0070 + 0.0076 0.0107 0.0136 0.0168 0.0147 -0.0006 + -0.0124 -0.0052 -0.0038 0.0017 0.0103 0.0097 + 0.0028 -0.0081 -0.0014 0.0056 0.0120 0.0142 + 0.0096 0.0065 0.0042 0.0028 -0.0040 0.0020 + 0.0053 0.0001 -0.0058 -0.0135 -0.0090 -0.0049 + -0.0041 -0.0065 -0.0093 0.0262 -0.0026 -0.0126 + -0.0117 -0.0004 0.0091 0.0058 -0.0031 -0.0104 + -0.0070 0.0047 0.0148 0.0066 0.0063 0.0081 + 0.0033 0.0007 0.0062 0.0087 0.0154 0.0158 + 0.0027 -0.0144 -0.0045 -0.0001 -0.0009 0.0004 + 0.0021 -0.0044 -0.0081 0.0007 0.0094 0.0148 + 0.0083 0.0060 0.0008 -0.0013 0.0016 0.0181 + 0.0059 0.0067 -0.0025 -0.0143 -0.0147 -0.0121 + -0.0044 -0.0008 -0.0119 -0.0099 0.0153 0.0006 + -0.0096 -0.0134 -0.0061 0.0071 -0.0008 0.0009 + -0.0064 -0.0024 0.0046 0.0165 0.0141 0.0022 + 0.0056 0.0030 -0.0060 -0.0049 0.0075 0.0051 + 0.0100 -0.0003 -0.0112 -0.0060 -0.0020 -0.0005 + -0.0058 -0.0148 -0.0056 0.0010 0.0004 0.0017 + 0.0056 0.0044 -0.0021 -0.0023 0.0060 0.0028 + 0.0102 0.0011 0.0037 -0.0010 -0.0062 0.0009 + 0.0029 0.0062 0.0016 -0.0060 0.0130 0.0141 + 0.0031 -0.0076 -0.0079 -0.0012 0.0034 0.0035 + 0.0082 0.0029 0.0125 0.0165 0.0164 0.0104 + -0.0007 0.0019 0.0076 -0.0017 -0.0060 -0.0037 + -0.0061 0.0006 0.0009 -0.0042 -0.0054 -0.0012 + -0.0043 -0.0033 -0.0084 -0.0026 0.0001 0.0010 + -0.0004 -0.0066 -0.0061 -0.0013 0.0107 0.0126 + 0.0174 0.0072 -0.0060 -0.0014 -0.0009 -0.0018 + 0.0028 0.0126 0.0169 0.0046 -0.0079 0.0127 + 0.0008 0.0002 -0.0133 -0.0036 0.0026 0.0104 + 0.0129 0.0116 0.0058 0.0147 0.0164 0.0101 + -0.0039 -0.0133 0.0060 0.0020 0.0001 -0.0063 + -0.0051 0.0052 0.0028 0.0015 0.0030 -0.0015 + -0.0049 -0.0092 -0.0060 0.0024 0.0063 -0.0018 + -0.0052 -0.0095 0.0006 0.0056 -0.0057 0.0031 + 0.0156 0.0076 -0.0020 -0.0042 -0.0047 -0.0029 + -0.0014 -0.0011 0.0127 0.0236 0.0142 0.0089 + -0.0045 -0.0088 -0.0037 -0.0138 0.0016 0.0068 + 0.0116 0.0097 0.0131 0.0109 0.0097 0.0054 + -0.0047 -0.0111 -0.0109 0.0033 -0.0057 -0.0053 + -0.0035 -0.0012 -0.0028 0.0010 -0.0014 -0.0016 + -0.0020 -0.0135 -0.0118 -0.0062 0.0160 0.0072 + -0.0063 -0.0072 -0.0002 0.0101 -0.0013 -0.0047 + 0.0031 0.0057 -0.0069 -0.0094 -0.0017 0.0023 + 0.0020 0.0022 0.0017 0.0112 0.0151 0.0029 + -0.0057 -0.0139 -0.0103 -0.0079 -0.0134 0.0033 + 0.0064 0.0113 0.0120 0.0134 0.0072 -0.0018 + -0.0043 -0.0075 -0.0122 -0.0079 0.0005 -0.0021 + -0.0004 -0.0043 -0.0032 -0.0013 -0.0028 -0.0035 + -0.0030 -0.0002 -0.0105 -0.0196 -0.0018 0.0104 + 0.0009 -0.0011 -0.0034 -0.0040 -0.0007 -0.0077 + -0.0114 -0.0095 -0.0046 -0.0062 -0.0076 -0.0010 + 0.0046 0.0031 0.0001 0.0016 0.0025 -0.0023 + -0.0087 -0.0124 -0.0148 -0.0120 -0.0162 -0.0156 + -0.0006 0.0074 0.0082 0.0084 0.0216 0.0108 + -0.0001 -0.0078 -0.0156 -0.0099 -0.0011 -0.0002 + -0.0052 -0.0018 -0.0013 -0.0016 -0.0037 -0.0044 + -0.0049 -0.0053 -0.0046 -0.0237 -0.0183 -0.0054 + 0.0035 0.0001 -0.0052 -0.0067 -0.0144 -0.0111 + -0.0180 -0.0146 -0.0024 -0.0027 -0.0048 -0.0013 + -0.0012 0.0024 -0.0099 -0.0075 -0.0051 -0.0047 + -0.0071 -0.0245 -0.0210 -0.0169 -0.0228 -0.0235 + -0.0095 0.0102 -0.0033 -0.0021 0.0041 0.0077 + 0.0026 -0.0011 -0.0131 -0.0114 -0.0071 0.0014 + 0.0039 0.0040 0.0015 0.0007 0.0048 0.0006 + -0.0059 0.0022 0.0069 0.0061 0.0041 0.0051 + 0.0044 0.0004 -0.0071 -0.0180 -0.0078 -0.0052 + 0.0033 -0.0057 -0.0061 0.0023 0.0040 -0.0033 + -0.0031 -0.0010 -0.0025 -0.0111 -0.0092 -0.0053 + -0.0038 -0.0073 -0.0156 -0.0161 -0.0185 -0.0226 + -0.0216 -0.0053 0.0068 -0.0004 -0.0010 -0.0002 + -0.0023 -0.0072 -0.0053 -0.0089 -0.0087 -0.0030 + 0.0038 0.0086 0.0058 0.0050 0.0099 0.0176 + 0.0099 0.0008 0.0039 0.0087 0.0040 0.0028 + 0.0019 -0.0024 -0.0086 -0.0146 -0.0104 -0.0034 + 0.0017 0.0063 0.0122 0.0051 -0.0028 -0.0029 + -0.0039 0.0026 0.0035 0.0037 0.0043 0.0092 + 0.0075 0.0061 -0.0037 -0.0036 -0.0056 -0.0153 + -0.0218 -0.0158 -0.0063 0.0003 0.0035 0.0040 + 0.0002 -0.0126 -0.0125 -0.0078 -0.0084 -0.0056 + 0.0032 0.0075 0.0053 0.0054 0.0030 -0.0049 + 0.0008 -0.0025 -0.0009 0.0021 0.0123 0.0135 + -0.0030 -0.0071 -0.0116 -0.0165 -0.0132 -0.0056 + 0.0022 0.0016 -0.0006 0.0063 0.0025 -0.0018 + -0.0068 -0.0042 -0.0047 0.0034 0.0067 0.0062 + 0.0097 0.0121 0.0117 -0.0009 -0.0152 -0.0114 + -0.0148 -0.0138 -0.0079 -0.0052 -0.0029 -0.0003 + 0.0065 -0.0057 -0.0186 -0.0150 -0.0040 0.0017 + 0.0060 0.0136 0.0143 0.0132 0.0140 0.0120 + 0.0006 -0.0079 -0.0070 -0.0023 0.0002 0.0048 + 0.0167 0.0007 -0.0088 -0.0055 -0.0011 -0.0079 + -0.0102 -0.0051 -0.0060 -0.0102 -0.0067 -0.0007 + 0.0001 -0.0048 -0.0024 0.0072 0.0024 -0.0034 + -0.0047 0.0039 0.0093 0.0055 -0.0131 -0.0172 + -0.0148 -0.0138 -0.0102 -0.0015 -0.0015 -0.0036 + 0.0053 0.0028 -0.0159 -0.0208 -0.0046 0.0057 + 0.0082 0.0147 0.0157 0.0116 0.0165 0.0141 + 0.0175 0.0102 -0.0032 -0.0065 -0.0056 -0.0029 + 0.0059 0.0181 -0.0071 -0.0057 0.0019 0.0090 + 0.0043 -0.0040 -0.0144 -0.0145 -0.0241 -0.0204 + 0.0025 0.0072 0.0048 -0.0004 -0.0021 -0.0060 + -0.0140 -0.0114 -0.0019 -0.0000 -0.0025 -0.0183 + -0.0161 -0.0119 -0.0087 -0.0043 0.0009 -0.0000 + -0.0021 0.0020 -0.0007 -0.0068 -0.0041 0.0007 + 0.0092 0.0134 0.0223 0.0167 0.0059 0.0174 + 0.0155 0.0162 0.0202 0.0063 -0.0038 -0.0013 + 0.0001 0.0026 0.0018 0.0062 0.0039 0.0052 + 0.0071 0.0074 0.0024 -0.0080 -0.0109 -0.0148 + -0.0064 0.0075 0.0135 0.0101 0.0030 -0.0104 + -0.0070 -0.0063 -0.0084 -0.0004 0.0033 -0.0059 + -0.0197 -0.0189 -0.0107 -0.0037 0.0010 -0.0027 + -0.0065 -0.0040 -0.0035 -0.0037 -0.0011 -0.0030 + 0.0011 0.0079 0.0160 0.0125 0.0053 0.0033 + 0.0140 0.0147 0.0123 0.0171 0.0117 0.0042 + 0.0021 0.0001 -0.0063 -0.0061 0.0009 0.0083 + 0.0091 0.0014 0.0111 0.0052 -0.0060 -0.0147 + -0.0154 -0.0045 0.0081 0.0131 0.0077 -0.0053 + -0.0162 -0.0026 0.0011 0.0022 0.0018 0.0019 + -0.0157 -0.0195 -0.0170 -0.0038 0.0054 0.0009 + -0.0025 -0.0038 -0.0064 -0.0092 -0.0060 0.0004 + 0.0049 0.0132 0.0196 0.0126 0.0116 0.0108 + 0.0093 0.0099 0.0090 0.0073 0.0070 0.0022 + -0.0001 -0.0001 -0.0053 -0.0071 -0.0122 0.0126 + 0.0168 0.0016 0.0059 0.0045 0.0013 -0.0068 + -0.0097 -0.0166 0.0068 0.0054 0.0046 -0.0032 + -0.0142 -0.0116 -0.0011 0.0048 0.0076 0.0012 + -0.0053 -0.0253 -0.0212 -0.0148 -0.0033 0.0063 + 0.0001 -0.0043 -0.0078 -0.0011 -0.0051 -0.0064 + 0.0049 0.0170 0.0199 0.0111 0.0041 0.0104 + 0.0146 0.0190 0.0032 0.0071 0.0131 0.0041 + -0.0006 0.0009 0.0086 0.0052 -0.0008 -0.0051 + 0.0044 0.0074 -0.0002 0.0014 0.0007 0.0025 + 0.0045 0.0012 0.0027 0.0002 -0.0012 -0.0081 + -0.0112 -0.0021 0.0014 0.0071 0.0129 0.0110 + 0.0079 -0.0053 -0.0142 -0.0124 -0.0064 0.0041 + 0.0051 -0.0015 -0.0006 -0.0041 0.0046 -0.0042 + 0.0032 0.0116 0.0083 0.0031 0.0060 -0.0014 + 0.0029 0.0071 0.0169 0.0017 0.0073 0.0078 + -0.0052 -0.0006 0.0044 0.0195 0.0116 0.0000 + 0.0019 0.0054 0.0006 0.0085 0.0024 0.0014 + -0.0031 0.0014 -0.0016 -0.0093 -0.0127 -0.0124 + -0.0143 0.0010 -0.0024 -0.0010 0.0074 0.0171 + 0.0072 0.0048 -0.0003 -0.0144 -0.0130 0.0059 + 0.0111 0.0060 -0.0012 -0.0053 -0.0080 -0.0017 + 0.0040 0.0093 0.0159 0.0037 -0.0054 -0.0048 + 0.0045 0.0127 0.0097 0.0127 -0.0090 -0.0022 + 0.0078 -0.0043 -0.0035 0.0017 0.0068 0.0092 + -0.0042 -0.0034 -0.0002 0.0102 0.0208 0.0205 + 0.0102 0.0014 -0.0091 -0.0125 -0.0127 -0.0120 + -0.0128 -0.0069 0.0023 -0.0038 -0.0068 0.0082 + 0.0231 0.0191 0.0109 0.0080 -0.0001 -0.0048 + 0.0057 0.0083 0.0038 -0.0021 -0.0045 -0.0085 + -0.0029 0.0038 0.0121 0.0148 0.0037 -0.0066 + -0.0059 -0.0004 0.0038 0.0052 0.0034 -0.0104 + -0.0041 -0.0041 -0.0046 0.0022 0.0096 0.0027 + -0.0054 -0.0002 0.0023 -0.0066 0.0193 0.0259 + 0.0211 0.0188 0.0121 0.0024 -0.0003 0.0041 + -0.0037 -0.0084 -0.0014 -0.0008 -0.0040 0.0012 + 0.0151 0.0240 0.0196 0.0101 0.0072 0.0052 + 0.0088 0.0017 0.0003 0.0031 0.0040 0.0012 + -0.0029 0.0025 0.0047 0.0126 0.0080 -0.0030 + -0.0086 -0.0045 -0.0028 -0.0010 0.0034 -0.0029 + -0.0120 -0.0112 -0.0153 -0.0128 0.0006 0.0114 + 0.0042 0.0002 0.0036 0.0047 0.0073 0.0198 + 0.0185 0.0092 0.0096 0.0122 0.0045 0.0034 + 0.0112 0.0083 0.0056 0.0028 -0.0070 -0.0029 + 0.0028 0.0171 0.0127 0.0146 0.0027 0.0004 + 0.0035 0.0068 0.0048 0.0022 0.0054 0.0073 + 0.0036 -0.0000 0.0032 -0.0006 0.0008 -0.0004 + -0.0026 0.0013 0.0016 0.0023 0.0048 0.0008 + -0.0112 -0.0052 -0.0057 -0.0123 -0.0162 -0.0055 + 0.0026 -0.0010 -0.0041 -0.0077 -0.0043 0.0095 + 0.0180 0.0117 0.0035 -0.0008 0.0072 0.0116 + 0.0081 0.0066 0.0078 0.0096 0.0077 -0.0018 + -0.0163 -0.0007 0.0219 0.0143 0.0065 -0.0014 + -0.0026 -0.0028 0.0004 0.0039 0.0041 0.0063 + 0.0102 0.0048 0.0050 0.0050 0.0019 -0.0001 + -0.0009 0.0003 0.0062 0.0104 0.0073 0.0014 + -0.0148 -0.0176 0.0042 -0.0051 -0.0095 -0.0131 + -0.0160 -0.0100 -0.0069 0.0005 -0.0025 -0.0047 + 0.0049 0.0061 0.0036 -0.0000 0.0023 0.0092 + 0.0124 0.0051 0.0009 0.0020 -0.0032 0.0192 + 0.0075 -0.0101 0.0035 0.0138 0.0094 0.0049 + -0.0065 -0.0087 -0.0111 -0.0074 -0.0044 -0.0017 + 0.0011 0.0034 0.0052 0.0101 0.0070 0.0002 + -0.0006 -0.0021 -0.0008 0.0085 0.0128 0.0052 + -0.0072 -0.0174 -0.0211 -0.0107 -0.0097 -0.0047 + -0.0018 -0.0041 -0.0052 -0.0037 0.0043 0.0021 + 0.0037 0.0068 0.0012 -0.0048 -0.0044 0.0044 + 0.0065 0.0049 -0.0041 -0.0052 -0.0067 -0.0090 + 0.0097 0.0102 -0.0100 -0.0024 0.0005 0.0064 + 0.0016 -0.0076 -0.0083 -0.0116 -0.0105 -0.0120 + -0.0077 0.0012 0.0087 0.0177 0.0060 0.0019 + -0.0029 -0.0061 -0.0030 -0.0015 0.0012 0.0060 + 0.0025 -0.0056 -0.0129 -0.0105 -0.0078 -0.0051 + -0.0011 0.0044 0.0051 -0.0042 0.0062 0.0034 + 0.0048 0.0068 0.0022 -0.0011 -0.0046 -0.0040 + 0.0005 -0.0014 -0.0029 0.0004 -0.0022 -0.0057 + -0.0019 0.0042 -0.0012 -0.0154 -0.0109 -0.0046 + 0.0023 0.0009 -0.0078 -0.0127 -0.0137 -0.0086 + -0.0092 -0.0025 0.0064 0.0140 0.0157 0.0059 + -0.0027 -0.0023 -0.0032 -0.0013 -0.0007 0.0023 + -0.0011 -0.0045 -0.0102 -0.0088 -0.0034 -0.0117 + -0.0109 -0.0064 -0.0002 0.0032 -0.0073 -0.0097 + 0.0019 0.0092 0.0086 -0.0027 -0.0074 -0.0071 + -0.0120 -0.0046 -0.0043 -0.0018 0.0017 0.0077 + 0.0027 -0.0023 0.0036 -0.0100 -0.0244 -0.0128 + 0.0095 0.0054 -0.0024 -0.0024 -0.0076 -0.0132 + -0.0086 -0.0092 0.0039 0.0059 0.0072 0.0078 + 0.0036 -0.0007 0.0027 0.0047 0.0031 0.0029 + -0.0015 -0.0090 -0.0039 -0.0052 -0.0016 -0.0029 + -0.0149 -0.0185 -0.0116 -0.0062 -0.0015 -0.0090 + -0.0156 -0.0063 0.0001 0.0006 -0.0061 -0.0056 + -0.0088 -0.0125 -0.0075 -0.0028 0.0019 0.0044 + 0.0055 0.0016 -0.0086 -0.0041 -0.0112 -0.0190 + 0.0013 0.0121 0.0054 0.0003 0.0006 -0.0054 + -0.0067 -0.0036 -0.0065 -0.0022 0.0010 0.0022 + 0.0027 0.0033 0.0062 0.0096 0.0112 0.0086 + 0.0012 -0.0017 -0.0091 -0.0099 -0.0073 -0.0018 + -0.0014 diff --git a/libres/test-data/local/geometry/surface/valid_binary.irap b/libres/test-data/local/geometry/surface/valid_binary.irap new file mode 100755 index 00000000000..813977df05d Binary files /dev/null and b/libres/test-data/local/geometry/surface/valid_binary.irap differ diff --git a/libres/test-data/local/geometry/surface/valid_small_ascii.irap b/libres/test-data/local/geometry/surface/valid_small_ascii.irap new file mode 100644 index 00000000000..4817d3295c0 --- /dev/null +++ b/libres/test-data/local/geometry/surface/valid_small_ascii.irap @@ -0,0 +1,104 @@ + -996 20 50.000000 50.000000 + 463325.562500 465725.562500 7336963.500000 7340863.500000 + 30 -65.000000 463325.562500 7336963.500000 + 0 0 0 0 0 0 0 + 0.0051 0.0017 -0.0101 -0.0126 -0.0078 0.0019 + 0.0002 -0.0039 -0.0100 -0.0134 -0.0076 -0.0017 + 0.0014 0.0045 0.0058 -0.0002 -0.0029 -0.0023 + 0.0048 0.0063 0.0107 0.0043 -0.0003 0.0044 + 0.0009 0.0036 0.0033 0.0055 0.0174 0.0168 + 0.0142 0.0073 0.0063 0.0113 0.0083 0.0050 + 0.0092 0.0054 0.0037 0.0034 0.0057 0.0034 + -0.0031 0.0034 0.0035 -0.0119 0.0004 0.0101 + 0.0218 -0.0013 -0.0026 -0.0042 -0.0107 -0.0079 + 0.0000 0.0034 -0.0032 -0.0111 -0.0106 -0.0028 + 0.0015 0.0050 0.0028 -0.0037 -0.0079 -0.0036 + 0.0047 -0.0039 0.0088 0.0091 0.0013 -0.0072 + -0.0075 -0.0027 0.0003 -0.0008 -0.0049 0.0036 + 0.0079 0.0021 0.0085 0.0151 0.0135 0.0112 + 0.0139 0.0176 0.0081 0.0041 0.0083 0.0077 + 0.0023 -0.0049 0.0042 -0.0074 -0.0134 -0.0015 + 0.0173 0.0204 -0.0083 -0.0086 -0.0008 -0.0014 + 0.0000 -0.0002 -0.0005 -0.0024 -0.0070 -0.0145 + -0.0050 0.0015 0.0019 -0.0029 -0.0121 -0.0136 + -0.0050 0.0054 0.0013 0.0056 0.0063 -0.0000 + -0.0084 -0.0124 -0.0023 -0.0012 -0.0062 -0.0078 + 0.0006 0.0045 0.0048 0.0084 0.0124 0.0145 + 0.0173 0.0205 0.0130 0.0071 0.0102 0.0179 + 0.0126 0.0054 -0.0094 0.0025 -0.0025 -0.0166 + -0.0021 0.0228 0.0183 -0.0099 -0.0111 -0.0123 + -0.0120 -0.0098 -0.0070 -0.0053 -0.0030 -0.0048 + -0.0051 -0.0031 0.0009 0.0004 -0.0016 -0.0107 + -0.0129 -0.0066 0.0036 0.0048 0.0015 0.0012 + 0.0030 -0.0055 -0.0066 -0.0023 -0.0030 -0.0007 + 0.0027 0.0078 0.0086 0.0070 0.0101 0.0135 + 0.0124 0.0216 0.0161 0.0074 0.0087 0.0149 + 0.0161 0.0131 0.0078 -0.0079 0.0048 -0.0069 + -0.0132 -0.0016 0.0319 0.0240 -0.0114 -0.0103 + -0.0131 -0.0154 -0.0105 -0.0060 -0.0094 -0.0076 + -0.0022 -0.0014 -0.0002 -0.0002 -0.0023 -0.0033 + -0.0097 -0.0033 -0.0019 0.0055 0.0001 -0.0007 + -0.0086 -0.0082 -0.0063 0.0029 0.0072 0.0071 + 0.0061 0.0140 0.0178 0.0160 0.0136 0.0152 + 0.0189 0.0182 0.0220 0.0201 0.0153 0.0110 + 0.0087 0.0073 0.0117 0.0069 -0.0149 0.0080 + -0.0062 -0.0185 -0.0008 0.0237 0.0083 -0.0104 + -0.0123 -0.0118 -0.0106 -0.0049 0.0025 0.0011 + -0.0009 0.0021 0.0013 0.0016 0.0014 -0.0005 + 0.0006 -0.0033 0.0024 0.0074 0.0009 -0.0040 + -0.0075 -0.0120 -0.0106 0.0043 0.0142 0.0162 + 0.0131 0.0128 0.0202 0.0246 0.0149 0.0068 + 0.0065 0.0058 0.0140 0.0269 0.0311 0.0138 + 0.0024 -0.0052 -0.0062 0.0026 0.0004 -0.0118 + 0.0003 0.0001 -0.0139 0.0065 0.0256 0.0177 + -0.0092 -0.0075 -0.0133 -0.0097 -0.0030 0.0077 + 0.0043 0.0046 -0.0011 -0.0014 0.0012 -0.0021 + -0.0009 -0.0016 0.0036 0.0069 -0.0023 0.0039 + -0.0109 -0.0086 -0.0060 0.0022 0.0148 0.0182 + 0.0134 0.0144 0.0187 0.0197 0.0181 0.0135 + 0.0053 0.0043 0.0135 0.0246 0.0226 0.0140 + 0.0025 -0.0046 -0.0036 -0.0043 -0.0081 0.0108 + -0.0009 -0.0077 -0.0014 -0.0134 0.0091 0.0187 + 0.0182 -0.0004 0.0013 -0.0078 -0.0107 -0.0033 + 0.0008 0.0026 0.0038 0.0007 -0.0001 -0.0023 + -0.0062 -0.0085 -0.0004 0.0099 0.0097 0.0007 + 0.0059 -0.0091 -0.0084 0.0068 0.0153 0.0180 + 0.0133 0.0103 0.0102 0.0142 0.0165 0.0134 + 0.0056 0.0044 0.0037 0.0110 0.0147 0.0119 + 0.0049 0.0008 0.0035 0.0070 -0.0025 -0.0037 + 0.0227 -0.0061 -0.0042 0.0001 -0.0122 0.0085 + 0.0154 0.0151 0.0007 -0.0031 -0.0067 -0.0080 + 0.0023 0.0018 -0.0027 -0.0006 -0.0036 -0.0071 + -0.0089 -0.0072 -0.0072 0.0063 0.0126 0.0161 + 0.0070 -0.0027 -0.0115 0.0002 0.0077 0.0122 + 0.0080 0.0112 0.0143 0.0152 0.0141 0.0140 + 0.0136 0.0111 0.0063 0.0054 0.0067 0.0074 + 0.0109 0.0138 0.0088 0.0088 0.0118 0.0027 + 0.0033 0.0045 -0.0119 0.0004 -0.0078 -0.0018 + 0.0051 0.0055 0.0033 0.0039 -0.0014 -0.0043 + 0.0001 0.0069 -0.0034 -0.0046 -0.0055 -0.0064 + -0.0059 -0.0017 0.0065 0.0068 0.0088 0.0131 + 0.0041 0.0010 -0.0020 0.0034 0.0068 0.0054 + 0.0080 0.0107 0.0155 0.0135 0.0169 0.0161 + 0.0094 0.0124 0.0138 0.0103 0.0079 0.0055 + 0.0060 0.0122 0.0193 0.0169 0.0227 0.0157 + 0.0098 0.0089 -0.0063 -0.0101 -0.0043 -0.0097 + 0.0206 0.0057 0.0016 0.0022 0.0037 0.0029 + 0.0045 0.0092 0.0073 -0.0041 -0.0055 -0.0103 + -0.0107 -0.0068 -0.0028 -0.0004 0.0007 0.0017 + -0.0022 -0.0001 0.0007 -0.0030 0.0064 0.0025 + 0.0045 0.0054 0.0060 0.0132 0.0164 0.0233 + 0.0204 0.0169 0.0172 0.0172 0.0148 0.0136 + 0.0143 0.0147 0.0150 0.0165 0.0156 0.0219 + 0.0157 0.0071 0.0103 -0.0051 -0.0107 -0.0103 + 0.0068 0.0203 0.0130 0.0104 0.0098 0.0038 + 0.0046 0.0099 0.0131 0.0117 -0.0061 -0.0010 + -0.0050 0.0006 0.0028 0.0034 0.0042 0.0030 + 0.0032 -0.0036 -0.0011 0.0054 -0.0062 0.0077 + 0.0073 0.0083 0.0079 0.0095 0.0091 0.0136 + 0.0198 0.0228 0.0248 0.0200 0.0164 0.0180 + 0.0182 0.0207 0.0211 0.0157 0.0134 0.0137 + 0.0103 0.0108 0.0148 0.0091 -0.0106 -0.0164 + -0.0062 0.0124 0.0180 0.0156 0.0117 0.0092 + 0.0060 0.0059 0.0045 0.0056 0.0058 -0.0049 + 0.0024 0.0080 0.0086 0.0108 -0.0004 -0.0023 diff --git a/libres/test-data/local/jobs_snake_oil/snake_oil_diff.py b/libres/test-data/local/jobs_snake_oil/snake_oil_diff.py new file mode 100755 index 00000000000..cdcf98cd7d8 --- /dev/null +++ b/libres/test-data/local/jobs_snake_oil/snake_oil_diff.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +from ecl.summary import EclSum + + +def writeDiff(filename, ecl_sum, key1, key2): + with open(filename, "w") as f: + for v1, v2 in zip(ecl_sum.numpy_vector(key1), ecl_sum.numpy_vector(key2)): + diff = v1 - v2 + f.write("%f\n" % diff) + + +if __name__ == "__main__": + ecl_sum = EclSum("SNAKE_OIL_FIELD") + + report_step = 199 + writeDiff( + "snake_oil_opr_diff_%d.txt" % report_step, ecl_sum, "WOPR:OP1", "WOPR:OP2" + ) + writeDiff( + "snake_oil_wpr_diff_%d.txt" % report_step, ecl_sum, "WWPR:OP1", "WWPR:OP2" + ) + writeDiff( + "snake_oil_gpr_diff_%d.txt" % report_step, ecl_sum, "WGPR:OP1", "WGPR:OP2" + ) diff --git a/libres/test-data/local/jobs_snake_oil/snake_oil_npv.py b/libres/test-data/local/jobs_snake_oil/snake_oil_npv.py new file mode 100755 index 00000000000..e1f4af7fa8b --- /dev/null +++ b/libres/test-data/local/jobs_snake_oil/snake_oil_npv.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python +from ecl.summary import EclSum + +OIL_PRICES = { + "2010-01-01": 78.33, + "2010-02-01": 76.39, + "2010-03-01": 81.20, + "2010-04-01": 84.29, + "2010-05-01": 73.74, + "2010-06-01": 75.34, + "2010-07-01": 76.32, + "2010-08-01": 76.60, + "2010-09-01": 75.24, + "2010-10-01": 81.89, + "2010-11-01": 84.25, + "2010-12-01": 89.15, + "2011-01-01": 89.17, + "2011-02-01": 88.58, + "2011-03-01": 102.86, + "2011-04-01": 109.53, + "2011-05-01": 100.90, + "2011-06-01": 96.26, + "2011-07-01": 97.30, + "2011-08-01": 86.33, + "2011-09-01": 85.52, + "2011-10-01": 86.32, + "2011-11-01": 97.16, + "2011-12-01": 98.56, + "2012-01-01": 100.27, + "2012-02-01": 102.20, + "2012-03-01": 106.16, + "2012-04-01": 103.32, + "2012-05-01": 94.65, + "2012-06-01": 82.30, + "2012-07-01": 87.90, + "2012-08-01": 94.13, + "2012-09-01": 94.51, + "2012-10-01": 89.49, + "2012-11-01": 86.53, + "2012-12-01": 87.86, + "2013-01-01": 94.76, + "2013-02-01": 95.31, + "2013-03-01": 92.94, + "2013-04-01": 92.02, + "2013-05-01": 94.51, + "2013-06-01": 95.77, + "2013-07-01": 104.67, + "2013-08-01": 106.57, + "2013-09-01": 106.29, + "2013-10-01": 100.54, + "2013-11-01": 93.86, + "2013-12-01": 97.63, + "2014-01-01": 94.62, + "2014-02-01": 100.82, + "2014-03-01": 100.80, + "2014-04-01": 102.07, + "2014-05-01": 102.18, + "2014-06-01": 105.79, + "2014-07-01": 103.59, + "2014-08-01": 96.54, + "2014-09-01": 93.21, + "2014-10-01": 84.40, + "2014-11-01": 75.79, + "2014-12-01": 59.29, + "2015-01-01": 47.22, + "2015-02-01": 50.58, + "2015-03-01": 47.82, + "2015-04-01": 54.45, + "2015-05-01": 59.27, + "2015-06-01": 59.82, + "2015-07-01": 50.90, + "2015-08-01": 42.87, + "2015-09-01": 45.48, +} + +if __name__ == "__main__": + ecl_sum = EclSum("SNAKE_OIL_FIELD") + start_time = ecl_sum.getStartTime() + date_ranges = ecl_sum.timeRange(start_time, interval="1M") + production_sums = ecl_sum.blockedProduction("FOPT", date_ranges) + + npv = 0.0 + for index in range(0, len(date_ranges) - 1): + date = date_ranges[index + 1] # end of period + production_sum = production_sums[index] + + oil_price = OIL_PRICES[date.date().strftime("%Y-%m-%d")] + + production_value = oil_price * production_sum + npv += production_value + + with open("snake_oil_npv.txt", "w") as output_file: + output_file.write("NPV %s\n" % npv) + + if npv < 80000: + rating = "POOR" + elif 80000 <= npv < 100000: + rating = "AVERAGE" + elif 100000 <= npv < 120000: + rating = "GOOD" + else: + rating = "EXCELLENT" + + output_file.write("RATING %s\n" % rating) diff --git a/libres/test-data/local/jobs_snake_oil/snake_oil_simulator.py b/libres/test-data/local/jobs_snake_oil/snake_oil_simulator.py new file mode 100755 index 00000000000..e5c315ae594 --- /dev/null +++ b/libres/test-data/local/jobs_snake_oil/snake_oil_simulator.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python +from datetime import datetime +import os +import sys + +from ecl.summary import EclSum, EclSumTStep +from ecl.util.test import ExtendedTestCase +import res + +from res.test.synthesizer import OilSimulator + + +def globalIndex(i, j, k, nx=10, ny=10, nz=10): + return i + nx * (j - 1) + nx * ny * (k - 1) + + +def readParameters(filename): + params = {} + with open(filename, "r") as f: + for line in f: + key, value = line.split(":", 1) + params[key] = value.strip() + + return params + + +def runSimulator(simulator, history_simulator, time_step_count): + """@rtype: EclSum""" + ecl_sum = EclSum.writer("SNAKE_OIL_FIELD", datetime(2010, 1, 1), 10, 10, 10) + + ecl_sum.addVariable("FOPT") + ecl_sum.addVariable("FOPR") + ecl_sum.addVariable("FGPT") + ecl_sum.addVariable("FGPR") + ecl_sum.addVariable("FWPT") + ecl_sum.addVariable("FWPR") + ecl_sum.addVariable("FGOR") + ecl_sum.addVariable("FWCT") + + ecl_sum.addVariable("FOPTH") + ecl_sum.addVariable("FOPRH") + ecl_sum.addVariable("FGPTH") + ecl_sum.addVariable("FGPRH") + ecl_sum.addVariable("FWPTH") + ecl_sum.addVariable("FWPRH") + ecl_sum.addVariable("FGORH") + ecl_sum.addVariable("FWCTH") + + ecl_sum.addVariable("WOPR", wgname="OP1") + ecl_sum.addVariable("WOPR", wgname="OP2") + ecl_sum.addVariable("WWPR", wgname="OP1") + ecl_sum.addVariable("WWPR", wgname="OP2") + ecl_sum.addVariable("WGPR", wgname="OP1") + ecl_sum.addVariable("WGPR", wgname="OP2") + ecl_sum.addVariable("WGOR", wgname="OP1") + ecl_sum.addVariable("WGOR", wgname="OP2") + ecl_sum.addVariable("WWCT", wgname="OP1") + ecl_sum.addVariable("WWCT", wgname="OP2") + + ecl_sum.addVariable("WOPRH", wgname="OP1") + ecl_sum.addVariable("WOPRH", wgname="OP2") + ecl_sum.addVariable("WWPRH", wgname="OP1") + ecl_sum.addVariable("WWPRH", wgname="OP2") + ecl_sum.addVariable("WGPRH", wgname="OP1") + ecl_sum.addVariable("WGPRH", wgname="OP2") + ecl_sum.addVariable("WGORH", wgname="OP1") + ecl_sum.addVariable("WGORH", wgname="OP2") + ecl_sum.addVariable("WWCTH", wgname="OP1") + ecl_sum.addVariable("WWCTH", wgname="OP2") + + ecl_sum.addVariable("BPR", num=globalIndex(5, 5, 5)) + ecl_sum.addVariable("BPR", num=globalIndex(1, 3, 8)) + + time_map = [] + mini_step_count = 10 + total_step_count = time_step_count * mini_step_count + + for report_step in range(time_step_count): + for mini_step in range(mini_step_count): + t_step = ecl_sum.addTStep( + report_step + 1, sim_days=report_step * mini_step_count + mini_step + ) + + time_map.append(t_step.getSimTime().datetime().strftime("%d/%m/%Y")) + + simulator.step(scale=1.0 / total_step_count) + history_simulator.step(scale=1.0 / total_step_count) + + t_step["FOPR"] = simulator.fopr() + t_step["FOPT"] = simulator.fopt() + t_step["FGPR"] = simulator.fgpr() + t_step["FGPT"] = simulator.fgpt() + t_step["FWPR"] = simulator.fwpr() + t_step["FWPT"] = simulator.fwpt() + t_step["FGOR"] = simulator.fgor() + t_step["FWCT"] = simulator.fwct() + + t_step["WOPR:OP1"] = simulator.opr("OP1") + t_step["WOPR:OP2"] = simulator.opr("OP2") + + t_step["WGPR:OP1"] = simulator.gpr("OP1") + t_step["WGPR:OP2"] = simulator.gpr("OP2") + + t_step["WWPR:OP1"] = simulator.wpr("OP1") + t_step["WWPR:OP2"] = simulator.wpr("OP2") + + t_step["WGOR:OP1"] = simulator.gor("OP1") + t_step["WGOR:OP2"] = simulator.gor("OP2") + + t_step["WWCT:OP1"] = simulator.wct("OP1") + t_step["WWCT:OP2"] = simulator.wct("OP2") + + t_step["BPR:5,5,5"] = simulator.bpr("5,5,5") + t_step["BPR:1,3,8"] = simulator.bpr("1,3,8") + + t_step["FOPRH"] = history_simulator.fopr() + t_step["FOPTH"] = history_simulator.fopt() + t_step["FGPRH"] = history_simulator.fgpr() + t_step["FGPTH"] = history_simulator.fgpt() + t_step["FWPRH"] = history_simulator.fwpr() + t_step["FWPTH"] = history_simulator.fwpt() + t_step["FGORH"] = history_simulator.fgor() + t_step["FWCTH"] = history_simulator.fwct() + + t_step["WOPRH:OP1"] = history_simulator.opr("OP1") + t_step["WOPRH:OP2"] = history_simulator.opr("OP2") + + t_step["WGPRH:OP1"] = history_simulator.gpr("OP1") + t_step["WGPRH:OP2"] = history_simulator.gpr("OP2") + + t_step["WWPRH:OP1"] = history_simulator.wpr("OP1") + t_step["WWPRH:OP2"] = history_simulator.wpr("OP2") + + t_step["WGORH:OP1"] = history_simulator.gor("OP1") + t_step["WGORH:OP2"] = history_simulator.gor("OP2") + + t_step["WWCTH:OP1"] = history_simulator.wct("OP1") + t_step["WWCTH:OP2"] = history_simulator.wct("OP2") + + return ecl_sum, time_map + + +def roundedInt(value): + return int(round(float(value))) + + +if __name__ == "__main__": + seed = int(readParameters("seed.txt")["SEED"]) + parameters = readParameters("snake_oil_params.txt") + + op1_divergence_scale = float(parameters["OP1_DIVERGENCE_SCALE"]) + op2_divergence_scale = float(parameters["OP2_DIVERGENCE_SCALE"]) + op1_persistence = float(parameters["OP1_PERSISTENCE"]) + op2_persistence = float(parameters["OP2_PERSISTENCE"]) + op1_offset = float(parameters["OP1_OFFSET"]) + op2_offset = float(parameters["OP2_OFFSET"]) + bpr_138_persistence = float(parameters["BPR_138_PERSISTENCE"]) + bpr_555_persistence = float(parameters["BPR_555_PERSISTENCE"]) + + op1_octaves = roundedInt(parameters["OP1_OCTAVES"]) + op2_octaves = roundedInt(parameters["OP2_OCTAVES"]) + + simulator = OilSimulator() + simulator.addWell( + "OP1", + seed * 997, + persistence=op1_persistence, + octaves=op1_octaves, + divergence_scale=op1_divergence_scale, + offset=op1_offset, + ) + simulator.addWell( + "OP2", + seed * 13, + persistence=op2_persistence, + octaves=op2_octaves, + divergence_scale=op2_divergence_scale, + offset=op2_offset, + ) + simulator.addBlock("5,5,5", seed * 37, persistence=bpr_555_persistence) + simulator.addBlock("1,3,8", seed * 31, persistence=bpr_138_persistence) + + history_simulator = OilSimulator() + history_simulator.addWell("OP1", 222118781) + history_simulator.addWell("OP2", 118116362) + + report_step_count = 200 + ecl_sum, time_map = runSimulator(simulator, history_simulator, report_step_count) + + ecl_sum.fwrite() + + with open("time_map.txt", "w") as f: + for t in time_map: + f.write("%s\n" % t) diff --git a/libres/test-data/local/mini_ert/Observations/observation_1 b/libres/test-data/local/mini_ert/Observations/observation_1 new file mode 100644 index 00000000000..9468fb30922 --- /dev/null +++ b/libres/test-data/local/mini_ert/Observations/observation_1 @@ -0,0 +1,5 @@ +GENERAL_OBSERVATION GEN_PERLIN_1 { + DATA = PERLIN; + RESTART = 1; + OBS_FILE = Observations/perlin_obs_1.txt; +}; diff --git a/libres/test-data/local/mini_ert/Observations/observation_2 b/libres/test-data/local/mini_ert/Observations/observation_2 new file mode 100644 index 00000000000..a52d2ddb9c6 --- /dev/null +++ b/libres/test-data/local/mini_ert/Observations/observation_2 @@ -0,0 +1,5 @@ +GENERAL_OBSERVATION GEN_PERLIN_2 { + DATA = PERLIN; + RESTART = 2; + OBS_FILE = Observations/perlin_obs_2.txt; +}; diff --git a/libres/test-data/local/mini_ert/Observations/observation_3 b/libres/test-data/local/mini_ert/Observations/observation_3 new file mode 100644 index 00000000000..7465c4c124e --- /dev/null +++ b/libres/test-data/local/mini_ert/Observations/observation_3 @@ -0,0 +1,5 @@ +GENERAL_OBSERVATION GEN_PERLIN_3 { + DATA = PERLIN; + RESTART = 3; + OBS_FILE = Observations/perlin_obs_3.txt; +}; \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/Observations/observations b/libres/test-data/local/mini_ert/Observations/observations new file mode 100644 index 00000000000..49d07ce9062 --- /dev/null +++ b/libres/test-data/local/mini_ert/Observations/observations @@ -0,0 +1,17 @@ +GENERAL_OBSERVATION GEN_PERLIN_1 { + DATA = PERLIN; + RESTART = 1; + OBS_FILE = perlin_obs_1.txt; +}; + +GENERAL_OBSERVATION GEN_PERLIN_2 { + DATA = PERLIN; + RESTART = 2; + OBS_FILE = perlin_obs_2.txt; +}; + +GENERAL_OBSERVATION GEN_PERLIN_3 { + DATA = PERLIN; + RESTART = 3; + OBS_FILE = perlin_obs_3.txt; +}; \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/Observations/perlin_obs_1.txt b/libres/test-data/local/mini_ert/Observations/perlin_obs_1.txt new file mode 100644 index 00000000000..fbfbf08d9ea --- /dev/null +++ b/libres/test-data/local/mini_ert/Observations/perlin_obs_1.txt @@ -0,0 +1,100 @@ +-0.616789 0.200000 +-0.626286 0.200000 +-0.642772 0.200000 +-0.623396 0.200000 +-0.601028 0.200000 +-0.587671 0.200000 +-0.545803 0.200000 +-0.548698 0.200000 +-0.576106 0.200000 +-0.547830 0.200000 +-0.499566 0.200000 +-0.390302 0.200000 +-0.274769 0.200000 +-0.218078 0.200000 +-0.206531 0.200000 +-0.256190 0.200000 +-0.286962 0.200000 +-0.290826 0.200000 +-0.279457 0.200000 +-0.220628 0.200000 +-0.175666 0.200000 +-0.134717 0.200000 +-0.041140 0.200000 +0.004634 0.200000 +-0.003586 0.200000 +-0.044667 0.200000 +-0.084128 0.200000 +-0.096405 0.200000 +-0.146272 0.200000 +-0.192659 0.200000 +-0.240387 0.200000 +-0.315781 0.200000 +-0.353862 0.200000 +-0.373289 0.200000 +-0.395017 0.200000 +-0.362747 0.200000 +-0.314473 0.200000 +-0.207889 0.200000 +-0.078605 0.200000 +-0.003939 0.200000 +0.034326 0.200000 +0.005587 0.200000 +0.013544 0.200000 +0.095579 0.200000 +0.191848 0.200000 +0.291420 0.200000 +0.337769 0.200000 +0.309285 0.200000 +0.283094 0.200000 +0.246823 0.200000 +0.190450 0.200000 +0.194585 0.200000 +0.244807 0.200000 +0.290661 0.200000 +0.329075 0.200000 +0.306089 0.200000 +0.264557 0.200000 +0.288901 0.200000 +0.331039 0.200000 +0.413380 0.200000 +0.466520 0.200000 +0.460285 0.200000 +0.444206 0.200000 +0.386893 0.200000 +0.350833 0.200000 +0.314020 0.200000 +0.255650 0.200000 +0.237356 0.200000 +0.229103 0.200000 +0.216303 0.200000 +0.232054 0.200000 +0.206562 0.200000 +0.164036 0.200000 +0.215742 0.200000 +0.336400 0.200000 +0.517183 0.200000 +0.654671 0.200000 +0.732607 0.200000 +0.814735 0.200000 +0.835433 0.200000 +0.826283 0.200000 +0.770065 0.200000 +0.673958 0.200000 +0.624376 0.200000 +0.615341 0.200000 +0.601582 0.200000 +0.585811 0.200000 +0.522072 0.200000 +0.459689 0.200000 +0.462806 0.200000 +0.463663 0.200000 +0.490711 0.200000 +0.483229 0.200000 +0.416017 0.200000 +0.344176 0.200000 +0.307064 0.200000 +0.311372 0.200000 +0.314551 0.200000 +0.275854 0.200000 +0.138191 0.200000 diff --git a/libres/test-data/local/mini_ert/Observations/perlin_obs_2.txt b/libres/test-data/local/mini_ert/Observations/perlin_obs_2.txt new file mode 100644 index 00000000000..1c0ee7d1312 --- /dev/null +++ b/libres/test-data/local/mini_ert/Observations/perlin_obs_2.txt @@ -0,0 +1,100 @@ +0.553636 0.200000 +0.562210 0.200000 +0.577093 0.200000 +0.516574 0.200000 +0.453354 0.200000 +0.438536 0.200000 +0.362335 0.200000 +0.299754 0.200000 +0.294947 0.200000 +0.258916 0.200000 +0.199226 0.200000 +0.154968 0.200000 +0.076430 0.200000 +0.038162 0.200000 +0.047076 0.200000 +0.040351 0.200000 +0.044383 0.200000 +-0.022339 0.200000 +-0.098926 0.200000 +-0.182296 0.200000 +-0.239174 0.200000 +-0.175066 0.200000 +-0.098687 0.200000 +0.046630 0.200000 +0.163052 0.200000 +0.239754 0.200000 +0.379917 0.200000 +0.457436 0.200000 +0.500511 0.200000 +0.547066 0.200000 +0.589134 0.200000 +0.630865 0.200000 +0.648067 0.200000 +0.546752 0.200000 +0.382873 0.200000 +0.254339 0.200000 +0.167602 0.200000 +0.165063 0.200000 +0.107654 0.200000 +0.014283 0.200000 +-0.003453 0.200000 +-0.014968 0.200000 +0.001727 0.200000 +0.011346 0.200000 +0.009215 0.200000 +0.083611 0.200000 +0.234321 0.200000 +0.391352 0.200000 +0.455607 0.200000 +0.429702 0.200000 +0.462148 0.200000 +0.563361 0.200000 +0.647466 0.200000 +0.778980 0.200000 +0.862696 0.200000 +0.876752 0.200000 +0.897362 0.200000 +0.858848 0.200000 +0.813029 0.200000 +0.815672 0.200000 +0.829471 0.200000 +0.883008 0.200000 +0.938118 0.200000 +0.943737 0.200000 +0.943930 0.200000 +0.887728 0.200000 +0.749709 0.200000 +0.570611 0.200000 +0.427346 0.200000 +0.279535 0.200000 +0.134481 0.200000 +0.091839 0.200000 +0.092425 0.200000 +0.142324 0.200000 +0.217527 0.200000 +0.182289 0.200000 +0.063206 0.200000 +-0.052929 0.200000 +-0.124322 0.200000 +-0.149079 0.200000 +-0.160036 0.200000 +-0.180000 0.200000 +-0.213176 0.200000 +-0.191568 0.200000 +-0.140832 0.200000 +-0.131431 0.200000 +-0.164401 0.200000 +-0.253597 0.200000 +-0.316338 0.200000 +-0.275519 0.200000 +-0.191733 0.200000 +-0.019671 0.200000 +0.187348 0.200000 +0.371249 0.200000 +0.532809 0.200000 +0.604516 0.200000 +0.620637 0.200000 +0.517488 0.200000 +0.382527 0.200000 +0.308772 0.200000 diff --git a/libres/test-data/local/mini_ert/Observations/perlin_obs_3.txt b/libres/test-data/local/mini_ert/Observations/perlin_obs_3.txt new file mode 100644 index 00000000000..f2cad960e0f --- /dev/null +++ b/libres/test-data/local/mini_ert/Observations/perlin_obs_3.txt @@ -0,0 +1,100 @@ +-0.258515 0.200000 +-0.356107 0.200000 +-0.525515 0.200000 +-0.663313 0.200000 +-0.770373 0.200000 +-0.752177 0.200000 +-0.720117 0.200000 +-0.712752 0.200000 +-0.678174 0.200000 +-0.634873 0.200000 +-0.510858 0.200000 +-0.401601 0.200000 +-0.371298 0.200000 +-0.399836 0.200000 +-0.388296 0.200000 +-0.362204 0.200000 +-0.377951 0.200000 +-0.339453 0.200000 +-0.258223 0.200000 +-0.088966 0.200000 +0.113885 0.200000 +0.276111 0.200000 +0.391728 0.200000 +0.418974 0.200000 +0.416504 0.200000 +0.381174 0.200000 +0.323184 0.200000 +0.270447 0.200000 +0.279357 0.200000 +0.328360 0.200000 +0.328559 0.200000 +0.323026 0.200000 +0.327307 0.200000 +0.348794 0.200000 +0.376927 0.200000 +0.383302 0.200000 +0.372235 0.200000 +0.356978 0.200000 +0.397867 0.200000 +0.522396 0.200000 +0.601574 0.200000 +0.657218 0.200000 +0.723345 0.200000 +0.698378 0.200000 +0.639636 0.200000 +0.584196 0.200000 +0.523544 0.200000 +0.490729 0.200000 +0.486421 0.200000 +0.409534 0.200000 +0.286329 0.200000 +0.120963 0.200000 +-0.046326 0.200000 +-0.129596 0.200000 +-0.224071 0.200000 +-0.277277 0.200000 +-0.271037 0.200000 +-0.304891 0.200000 +-0.363780 0.200000 +-0.395722 0.200000 +-0.438535 0.200000 +-0.504110 0.200000 +-0.546085 0.200000 +-0.616035 0.200000 +-0.673682 0.200000 +-0.650552 0.200000 +-0.604866 0.200000 +-0.567949 0.200000 +-0.539873 0.200000 +-0.552954 0.200000 +-0.570381 0.200000 +-0.517706 0.200000 +-0.474400 0.200000 +-0.373474 0.200000 +-0.263037 0.200000 +-0.203820 0.200000 +-0.103226 0.200000 +-0.051460 0.200000 +0.023379 0.200000 +0.085632 0.200000 +0.073926 0.200000 +0.114264 0.200000 +0.210600 0.200000 +0.367117 0.200000 +0.493523 0.200000 +0.541983 0.200000 +0.555534 0.200000 +0.507462 0.200000 +0.468411 0.200000 +0.494860 0.200000 +0.511384 0.200000 +0.505254 0.200000 +0.497490 0.200000 +0.404108 0.200000 +0.324978 0.200000 +0.329421 0.200000 +0.331172 0.200000 +0.357684 0.200000 +0.334654 0.200000 +0.226313 0.200000 diff --git a/libres/test-data/local/mini_ert/Parameters/PERLIN_PARAMS.txt b/libres/test-data/local/mini_ert/Parameters/PERLIN_PARAMS.txt new file mode 100644 index 00000000000..24926666bb8 --- /dev/null +++ b/libres/test-data/local/mini_ert/Parameters/PERLIN_PARAMS.txt @@ -0,0 +1,7 @@ +SCALE UNIFORM 0.1 1 +OFFSET UNIFORM -1 1 +PERSISTENCE UNIFORM 0.01 1 +OCTAVES UNIFORM 1 64 +PRIME_1 UNIFORM 0 127 +PRIME_2 UNIFORM 0 127 +PRIME_3 UNIFORM 0 127 \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/Refcase/CASE.EGRID b/libres/test-data/local/mini_ert/Refcase/CASE.EGRID new file mode 100644 index 00000000000..33da9f7a70e Binary files /dev/null and b/libres/test-data/local/mini_ert/Refcase/CASE.EGRID differ diff --git a/libres/test-data/local/mini_ert/Template/PERLIN_TEMPLATE b/libres/test-data/local/mini_ert/Template/PERLIN_TEMPLATE new file mode 100644 index 00000000000..527a92907cd --- /dev/null +++ b/libres/test-data/local/mini_ert/Template/PERLIN_TEMPLATE @@ -0,0 +1,7 @@ +SCALE: +OFFSET: +PERSISTENCE: +OCTAVES: +PRIME_1: +PRIME_2: +PRIME_3: \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/OK b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/OK new file mode 100644 index 00000000000..9590cb949c2 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/OK @@ -0,0 +1 @@ +All jobs complete \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/aggregated.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/aggregated.txt new file mode 100644 index 00000000000..de8aedc5622 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/aggregated.txt @@ -0,0 +1,4 @@ +PERLIN_3 0.190479 +PERLIN_2 1.002630 +PERLIN_1 -0.167794 +STATE Positive \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/aggregator.stderr b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/aggregator.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/aggregator.stdout b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/aggregator.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/parameters.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/parameters.txt new file mode 100644 index 00000000000..142dbecaab7 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/parameters.txt @@ -0,0 +1,7 @@ +PERLIN_PARAM:SCALE 0.249225 +PERLIN_PARAM:OFFSET 0.185709 +PERLIN_PARAM:PERSISTENCE 0.936132 +PERLIN_PARAM:OCTAVES 22.4008 +PERLIN_PARAM:PRIME_1 70.2373 +PERLIN_PARAM:PRIME_2 73.2881 +PERLIN_PARAM:PRIME_3 73.2557 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/perlin.stderr b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/perlin.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/perlin.stdout b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/perlin.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/perlin_1.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/perlin_1.txt new file mode 100644 index 00000000000..fec0fd107ef --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/perlin_1.txt @@ -0,0 +1,100 @@ +-0.167794 +-0.533157 +-0.582564 +-0.581187 +-0.544394 +-0.617389 +-0.471483 +-0.570636 +-0.513164 +-0.094431 +-0.489078 +-0.363713 +-0.116233 +0.104267 +-0.185805 +-0.200709 +-0.150918 +-0.384297 +0.293484 +0.122875 +-0.157933 +0.066183 +0.043334 +0.083941 +0.237257 +0.510188 +0.247252 +0.238908 +-0.087547 +-0.330292 +-0.126747 +-0.035823 +-0.234230 +0.031125 +-0.470002 +0.277528 +0.333005 +0.208194 +0.329248 +0.183316 +0.030305 +0.292607 +0.207885 +-0.055542 +0.267287 +0.489294 +0.351695 +0.473816 +0.486845 +0.161185 +0.750709 +0.334430 +0.487895 +-0.083531 +0.631333 +0.189402 +0.293179 +0.416287 +0.071861 +0.343657 +0.530727 +0.295893 +0.707495 +0.705177 +0.364062 +-0.028729 +0.627459 +0.665644 +0.110239 +0.074890 +0.898952 +0.507702 +0.793900 +0.376955 +0.740932 +0.768612 +1.072116 +0.641052 +0.951385 +1.114482 +0.779685 +1.030575 +0.960609 +0.819774 +0.728183 +0.880717 +0.390042 +0.616292 +0.534190 +0.332035 +0.597337 +0.310784 +0.480825 +0.510514 +0.527712 +0.106739 +0.459289 +0.091994 +0.167754 +0.291767 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/perlin_2.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/perlin_2.txt new file mode 100644 index 00000000000..a4c8ae22814 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/perlin_2.txt @@ -0,0 +1,100 @@ +1.002630 +0.655338 +0.637300 +0.558783 +0.509989 +0.408818 +0.436655 +0.277816 +0.357889 +0.712315 +0.209713 +0.181557 +0.234966 +0.360507 +0.067803 +0.095832 +0.180428 +-0.115810 +0.474014 +0.161206 +-0.221441 +0.025834 +-0.014213 +0.125937 +0.403895 +0.794609 +0.711298 +0.792749 +0.559236 +0.409434 +0.702774 +0.910823 +0.767699 +0.951166 +0.307889 +0.894615 +0.815080 +0.581146 +0.515508 +0.201538 +-0.007474 +0.272051 +0.196068 +-0.139775 +0.084654 +0.281485 +0.248246 +0.555883 +0.659358 +0.344063 +1.022407 +0.703206 +0.890554 +0.404788 +1.164953 +0.760065 +0.925984 +0.986234 +0.553851 +0.745948 +0.893678 +0.718616 +1.201408 +1.262021 +0.957160 +0.544980 +1.121517 +0.998898 +0.308482 +0.138122 +0.801380 +0.392979 +0.722289 +0.303537 +0.622059 +0.433718 +0.480651 +-0.144483 +0.012328 +0.129970 +-0.206634 +0.080509 +0.073475 +0.003830 +-0.027990 +0.147703 +-0.360170 +-0.159376 +-0.241837 +-0.406290 +-0.058059 +-0.199599 +0.184944 +0.465746 +0.716346 +0.404191 +0.768554 +0.294930 +0.274427 +0.462347 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/perlin_3.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/perlin_3.txt new file mode 100644 index 00000000000..13a10a9e5df --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/perlin_3.txt @@ -0,0 +1,100 @@ +0.190479 +-0.262978 +-0.465308 +-0.621105 +-0.713738 +-0.781896 +-0.645797 +-0.734690 +-0.615233 +-0.181474 +-0.500371 +-0.375013 +-0.212762 +-0.077491 +-0.367569 +-0.306723 +-0.241907 +-0.432924 +0.314717 +0.254536 +0.131618 +0.477011 +0.476202 +0.498281 +0.657347 +0.936028 +0.654565 +0.605760 +0.338082 +0.190727 +0.442199 +0.602984 +0.446939 +0.753207 +0.301943 +1.023577 +1.019714 +0.773061 +0.805721 +0.709651 +0.597553 +0.944238 +0.917685 +0.547257 +0.715075 +0.782070 +0.537469 +0.655260 +0.690172 +0.323895 +0.846589 +0.260808 +0.196762 +-0.503788 +0.078186 +-0.393964 +-0.242415 +-0.177505 +-0.622958 +-0.465445 +-0.374328 +-0.668502 +-0.282796 +-0.297752 +-0.660453 +-0.993300 +-0.233057 +-0.139662 +-0.658737 +-0.694367 +0.096517 +-0.216566 +0.155464 +-0.212261 +0.141495 +0.047609 +0.314219 +-0.143014 +0.160029 +0.364682 +0.027328 +0.374773 +0.497251 +0.562515 +0.606365 +0.821117 +0.359765 +0.601682 +0.542912 +0.364089 +0.645058 +0.325327 +0.495087 +0.498606 +0.508514 +0.129097 +0.479090 +0.135126 +0.226554 +0.379889 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/perlin_fail.status b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/perlin_fail.status new file mode 100644 index 00000000000..6ac0122a284 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/perlin_fail.status @@ -0,0 +1 @@ +Did nothing! \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/perlin_params.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/perlin_params.txt new file mode 100644 index 00000000000..99b2ec3ff64 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/perlin_params.txt @@ -0,0 +1,7 @@ +SCALE:0.249225 +OFFSET:0.185709 +PERSISTENCE:0.936132 +OCTAVES:22.4008 +PRIME_1:70.2373 +PRIME_2:73.2881 +PRIME_3:73.2557 \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/realization.ok b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/realization.ok new file mode 100644 index 00000000000..a0aba9318ad --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-0/iter-0/realization.ok @@ -0,0 +1 @@ +OK \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/OK b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/OK new file mode 100644 index 00000000000..9590cb949c2 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/OK @@ -0,0 +1 @@ +All jobs complete \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/aggregated.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/aggregated.txt new file mode 100644 index 00000000000..c42ebc25736 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/aggregated.txt @@ -0,0 +1,4 @@ +PERLIN_3 -0.584204 +PERLIN_2 0.227947 +PERLIN_1 -0.942477 +STATE Negative \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/aggregator.stderr b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/aggregator.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/aggregator.stdout b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/aggregator.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/parameters.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/parameters.txt new file mode 100644 index 00000000000..d931876083b --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/parameters.txt @@ -0,0 +1,7 @@ +PERLIN_PARAM:SCALE 0.24275 +PERLIN_PARAM:OFFSET -0.29096 +PERLIN_PARAM:PERSISTENCE 0.860518 +PERLIN_PARAM:OCTAVES 14.7858 +PERLIN_PARAM:PRIME_1 11.8217 +PERLIN_PARAM:PRIME_2 48.9187 +PERLIN_PARAM:PRIME_3 89.5122 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/perlin.stderr b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/perlin.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/perlin.stdout b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/perlin.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/perlin_2.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/perlin_2.txt new file mode 100644 index 00000000000..296be5b8cd9 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/perlin_2.txt @@ -0,0 +1,100 @@ +0.227947 +0.461424 +0.496254 +0.385250 +0.382965 +0.294599 +0.215538 +0.354808 +0.194641 +0.061050 +0.006348 +-0.033658 +-0.137511 +-0.052176 +0.054974 +0.004020 +-0.119814 +-0.186461 +-0.385196 +-0.286967 +-0.540945 +-0.434223 +-0.424480 +-0.139600 +-0.216224 +0.069846 +0.120217 +0.327327 +0.326008 +0.431300 +0.341933 +0.550826 +0.259922 +0.392107 +0.003858 +0.014051 +-0.354946 +-0.083760 +-0.201720 +-0.372664 +-0.515754 +-0.416670 +-0.452153 +-0.378276 +-0.502741 +-0.226850 +-0.117039 +-0.170353 +-0.103309 +0.018785 +0.134857 +0.299396 +0.256928 +0.466775 +0.638846 +0.679726 +0.643554 +0.744558 +0.717986 +0.632070 +0.597780 +0.726447 +0.907347 +0.748545 +0.576996 +0.711178 +0.663988 +0.233265 +0.069050 +-0.047240 +-0.055375 +-0.163060 +-0.421085 +-0.171631 +0.043688 +-0.021042 +-0.190991 +-0.247524 +-0.463284 +-0.413005 +-0.644291 +-0.650833 +-0.570592 +-0.466943 +-0.568175 +-0.409936 +-0.512981 +-0.655652 +-0.830461 +-0.878072 +-0.495081 +-0.517236 +-0.159439 +0.026080 +-0.084928 +0.074934 +0.020797 +0.156703 +-0.069071 +0.019410 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/perlin_3.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/perlin_3.txt new file mode 100644 index 00000000000..d43e140291b --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/perlin_3.txt @@ -0,0 +1,100 @@ +-0.584204 +-0.456892 +-0.606354 +-0.794637 +-0.840762 +-0.896115 +-0.866915 +-0.657698 +-0.778480 +-0.832740 +-0.703736 +-0.590227 +-0.585239 +-0.490175 +-0.380398 +-0.398535 +-0.542149 +-0.503575 +-0.544492 +-0.193638 +-0.187886 +0.016954 +0.065935 +0.232744 +0.037229 +0.211265 +0.063484 +0.140338 +0.104854 +0.212594 +0.081358 +0.242987 +-0.060839 +0.194148 +-0.002088 +0.143014 +-0.150313 +0.108155 +0.088492 +0.135449 +0.089272 +0.255517 +0.269465 +0.308757 +0.127679 +0.273734 +0.172184 +-0.070976 +-0.072495 +-0.001383 +-0.040962 +-0.143002 +-0.436864 +-0.441800 +-0.447921 +-0.474303 +-0.524845 +-0.419181 +-0.458823 +-0.579324 +-0.670226 +-0.660671 +-0.576856 +-0.811227 +-1.040616 +-0.827102 +-0.690587 +-0.905295 +-0.898169 +-0.879729 +-0.760237 +-0.772606 +-0.987910 +-0.687429 +-0.436876 +-0.407151 +-0.357423 +-0.246055 +-0.315583 +-0.178294 +-0.410329 +-0.356569 +-0.146816 +0.091742 +0.066180 +0.263478 +0.206954 +0.105406 +-0.045712 +-0.107693 +0.208035 +0.007690 +0.150704 +0.058939 +-0.292759 +-0.200161 +-0.268667 +-0.003101 +-0.116944 +-0.063049 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/perlin_fail.status b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/perlin_fail.status new file mode 100644 index 00000000000..46a71f4e166 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/perlin_fail.status @@ -0,0 +1 @@ +Deleted report step: 1 \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/perlin_params.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/perlin_params.txt new file mode 100644 index 00000000000..fb78e8c3e9b --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/perlin_params.txt @@ -0,0 +1,7 @@ +SCALE:0.24275 +OFFSET:-0.29096 +PERSISTENCE:0.860518 +OCTAVES:14.7858 +PRIME_1:11.8217 +PRIME_2:48.9187 +PRIME_3:89.5122 \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/realization.ok b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/realization.ok new file mode 100644 index 00000000000..a0aba9318ad --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-1/iter-0/realization.ok @@ -0,0 +1 @@ +OK \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/ERROR b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/ERROR new file mode 100644 index 00000000000..f5c9d83c767 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/ERROR @@ -0,0 +1,8 @@ + + + REALIZATION_FAIL + Could not find target_file:realization.ok + + + + diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/EXIT b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/EXIT new file mode 100644 index 00000000000..f5c9d83c767 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/EXIT @@ -0,0 +1,8 @@ + + + REALIZATION_FAIL + Could not find target_file:realization.ok + + + + diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/aggregated.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/aggregated.txt new file mode 100644 index 00000000000..39c71d12aab --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/aggregated.txt @@ -0,0 +1,4 @@ +PERLIN_3 -0.294383 +PERLIN_2 0.517768 +PERLIN_1 -0.652656 +STATE Negative \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/aggregator.stderr b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/aggregator.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/aggregator.stdout b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/aggregator.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/parameters.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/parameters.txt new file mode 100644 index 00000000000..0850de9c61b --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/parameters.txt @@ -0,0 +1,7 @@ +PERLIN_PARAM:SCALE 0.187883 +PERLIN_PARAM:OFFSET -0.512383 +PERLIN_PARAM:PERSISTENCE 0.850056 +PERLIN_PARAM:OCTAVES 39.9199 +PERLIN_PARAM:PRIME_1 76.7337 +PERLIN_PARAM:PRIME_2 54.1086 +PERLIN_PARAM:PRIME_3 16.0042 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/perlin.stderr b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/perlin.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/perlin.stdout b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/perlin.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/perlin_1.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/perlin_1.txt new file mode 100644 index 00000000000..122d4089eaa --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/perlin_1.txt @@ -0,0 +1,100 @@ +-0.652656 +-0.856575 +-0.907574 +-0.921559 +-0.907052 +-0.798535 +-0.892001 +-0.898300 +-0.932647 +-0.887530 +-0.745258 +-0.774438 +-0.680597 +-0.707552 +-0.616984 +-0.784930 +-0.705796 +-0.672207 +-0.677662 +-0.517634 +-0.460960 +-0.512998 +-0.485822 +-0.377630 +-0.469687 +-0.463782 +-0.644529 +-0.631042 +-0.610238 +-0.645129 +-0.841183 +-0.813056 +-0.824549 +-0.985474 +-0.818724 +-1.021755 +-0.749654 +-0.643111 +-0.382287 +-0.319701 +-0.240893 +-0.293783 +-0.356379 +-0.315861 +-0.243741 +-0.061641 +-0.016073 +-0.078540 +-0.166452 +-0.204278 +-0.206156 +-0.339674 +-0.324650 +-0.218586 +-0.220018 +-0.365619 +-0.213129 +-0.161173 +-0.144854 +-0.088350 +-0.193806 +-0.111286 +-0.100984 +-0.235155 +-0.165413 +-0.278588 +-0.429381 +-0.254946 +-0.240835 +-0.227267 +-0.524615 +-0.423858 +-0.341032 +-0.369948 +-0.180422 +0.176827 +0.282667 +0.455645 +0.421888 +0.461117 +0.478802 +0.408361 +0.301001 +0.266791 +0.167743 +0.066362 +0.101881 +0.183729 +-0.037895 +0.191528 +0.077918 +0.193353 +0.109073 +0.001759 +-0.061599 +-0.001108 +-0.164077 +-0.253534 +-0.200586 +-0.275259 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/perlin_3.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/perlin_3.txt new file mode 100644 index 00000000000..1e03faf793f --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/perlin_3.txt @@ -0,0 +1,100 @@ +-0.294383 +-0.586396 +-0.790317 +-0.961476 +-1.076396 +-0.963042 +-1.066316 +-1.062354 +-1.034715 +-0.974573 +-0.756551 +-0.785737 +-0.777126 +-0.889310 +-0.798749 +-0.890944 +-0.796785 +-0.720834 +-0.656429 +-0.385973 +-0.171410 +-0.102170 +-0.052955 +0.036709 +-0.049597 +-0.037941 +-0.237216 +-0.264190 +-0.184608 +-0.124110 +-0.272236 +-0.174249 +-0.143380 +-0.263391 +-0.046779 +-0.275706 +-0.062945 +-0.078243 +0.094185 +0.206634 +0.326354 +0.357848 +0.353421 +0.286938 +0.204047 +0.231135 +0.169701 +0.102903 +0.036875 +-0.041568 +-0.110276 +-0.413296 +-0.615784 +-0.638843 +-0.773164 +-0.948985 +-0.748723 +-0.754966 +-0.839672 +-0.897452 +-1.098861 +-1.075682 +-1.091275 +-1.238084 +-1.189928 +-1.243159 +-1.289897 +-1.060251 +-1.009811 +-0.996525 +-1.327050 +-1.148126 +-0.979468 +-0.959163 +-0.779859 +-0.544176 +-0.475229 +-0.328422 +-0.369468 +-0.288684 +-0.273556 +-0.247441 +-0.162358 +0.009532 +0.045926 +0.006763 +0.071604 +0.169119 +-0.029173 +0.223582 +0.125639 +0.207896 +0.123334 +-0.010150 +-0.080797 +0.021249 +-0.144276 +-0.210401 +-0.141786 +-0.187137 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/perlin_fail.status b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/perlin_fail.status new file mode 100644 index 00000000000..2494f445e5f --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/perlin_fail.status @@ -0,0 +1 @@ +Deleted report step: 2 \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/perlin_params.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/perlin_params.txt new file mode 100644 index 00000000000..a069d36ab89 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-2/iter-0/perlin_params.txt @@ -0,0 +1,7 @@ +SCALE:0.187883 +OFFSET:-0.512383 +PERSISTENCE:0.850056 +OCTAVES:39.9199 +PRIME_1:76.7337 +PRIME_2:54.1086 +PRIME_3:16.0042 \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/ERROR b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/ERROR new file mode 100644 index 00000000000..f5c9d83c767 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/ERROR @@ -0,0 +1,8 @@ + + + REALIZATION_FAIL + Could not find target_file:realization.ok + + + + diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/EXIT b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/EXIT new file mode 100644 index 00000000000..f5c9d83c767 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/EXIT @@ -0,0 +1,8 @@ + + + REALIZATION_FAIL + Could not find target_file:realization.ok + + + + diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/aggregated.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/aggregated.txt new file mode 100644 index 00000000000..b986f1b4814 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/aggregated.txt @@ -0,0 +1,4 @@ +PERLIN_3 -0.871387 +PERLIN_2 -0.059236 +PERLIN_1 -1.229660 +STATE Negative \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/aggregator.stderr b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/aggregator.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/aggregator.stdout b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/aggregator.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/parameters.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/parameters.txt new file mode 100644 index 00000000000..a7472afccff --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/parameters.txt @@ -0,0 +1,7 @@ +PERLIN_PARAM:SCALE 0.542054 +PERLIN_PARAM:OFFSET -0.936072 +PERLIN_PARAM:PERSISTENCE 0.66513 +PERLIN_PARAM:OCTAVES 59.6891 +PERLIN_PARAM:PRIME_1 58.3958 +PERLIN_PARAM:PRIME_2 51.6271 +PERLIN_PARAM:PRIME_3 37.3286 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/perlin.stderr b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/perlin.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/perlin.stdout b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/perlin.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/perlin_1.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/perlin_1.txt new file mode 100644 index 00000000000..1f0aabd1931 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/perlin_1.txt @@ -0,0 +1,100 @@ +-1.229660 +-1.403213 +-1.494882 +-1.481222 +-1.545058 +-1.605346 +-1.466829 +-1.460689 +-1.589462 +-1.462504 +-1.586371 +-1.419953 +-1.184666 +-1.064367 +-1.081730 +-1.130531 +-1.307146 +-1.330892 +-1.147255 +-1.230522 +-1.290812 +-1.243312 +-1.053225 +-0.837831 +-0.815416 +-0.794856 +-0.780118 +-0.867346 +-0.868595 +-0.844661 +-0.949974 +-1.135521 +-1.278698 +-1.237066 +-1.353473 +-1.274754 +-1.024540 +-1.062374 +-1.018191 +-0.989482 +-1.082235 +-1.083477 +-1.111900 +-0.884140 +-0.804378 +-0.649785 +-0.414051 +-0.453314 +-0.426393 +-0.376335 +-0.433952 +-0.369796 +-0.319725 +-0.318209 +-0.380495 +-0.274543 +-0.410077 +-0.265464 +-0.276032 +-0.293185 +-0.259478 +-0.307654 +-0.469020 +-0.606702 +-0.727993 +-0.657698 +-0.738949 +-0.779068 +-0.929409 +-0.925120 +-0.888902 +-0.818882 +-0.691387 +-0.806321 +-0.774223 +-0.725947 +-0.616177 +-0.575377 +-0.546778 +-0.619225 +-0.739783 +-0.805058 +-0.854443 +-1.003113 +-0.978222 +-0.724971 +-0.804323 +-0.920953 +-0.973738 +-0.936418 +-0.905518 +-0.717744 +-0.616881 +-0.785126 +-0.782606 +-0.680625 +-0.739233 +-0.708171 +-0.640854 +-0.813288 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/perlin_2.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/perlin_2.txt new file mode 100644 index 00000000000..58ac16bef25 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/perlin_2.txt @@ -0,0 +1,100 @@ +-0.059236 +-0.214718 +-0.275017 +-0.341253 +-0.490676 +-0.579139 +-0.558691 +-0.612236 +-0.718408 +-0.655757 +-0.887580 +-0.874683 +-0.833466 +-0.808127 +-0.828123 +-0.833990 +-0.975800 +-1.062405 +-0.966725 +-1.192190 +-1.354320 +-1.283661 +-1.110772 +-0.795835 +-0.648779 +-0.510435 +-0.316072 +-0.313505 +-0.221812 +-0.104936 +-0.120453 +-0.188875 +-0.276769 +-0.317026 +-0.575582 +-0.657667 +-0.542465 +-0.689421 +-0.831931 +-0.971260 +-1.120014 +-1.104033 +-1.123718 +-0.968373 +-0.987011 +-0.857594 +-0.517499 +-0.371248 +-0.253880 +-0.193457 +-0.162254 +-0.001019 +0.082934 +0.170110 +0.153126 +0.296120 +0.222728 +0.304483 +0.205958 +0.109107 +0.103474 +0.115069 +0.024892 +-0.049858 +-0.134896 +-0.083989 +-0.244890 +-0.445814 +-0.731166 +-0.861888 +-0.986474 +-0.933604 +-0.762998 +-0.879739 +-0.893096 +-1.060842 +-1.207642 +-1.360912 +-1.485835 +-1.603737 +-1.726102 +-1.755124 +-1.741578 +-1.819057 +-1.734395 +-1.457985 +-1.554534 +-1.696621 +-1.749765 +-1.674743 +-1.560913 +-1.228126 +-0.912762 +-0.829894 +-0.593973 +-0.383174 +-0.429968 +-0.505235 +-0.534181 +-0.642708 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/perlin_fail.status b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/perlin_fail.status new file mode 100644 index 00000000000..6842226365d --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/perlin_fail.status @@ -0,0 +1 @@ +Deleted report step: 3 \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/perlin_params.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/perlin_params.txt new file mode 100644 index 00000000000..f51328cc149 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-3/iter-0/perlin_params.txt @@ -0,0 +1,7 @@ +SCALE:0.542054 +OFFSET:-0.936072 +PERSISTENCE:0.66513 +OCTAVES:59.6891 +PRIME_1:58.3958 +PRIME_2:51.6271 +PRIME_3:37.3286 \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/OK b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/OK new file mode 100644 index 00000000000..9590cb949c2 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/OK @@ -0,0 +1 @@ +All jobs complete \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/aggregated.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/aggregated.txt new file mode 100644 index 00000000000..35536135e93 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/aggregated.txt @@ -0,0 +1,4 @@ +PERLIN_3 0.585905 +PERLIN_2 1.398056 +PERLIN_1 0.227632 +STATE Positive \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/aggregator.stderr b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/aggregator.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/aggregator.stdout b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/aggregator.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/parameters.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/parameters.txt new file mode 100644 index 00000000000..43486a893aa --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/parameters.txt @@ -0,0 +1,7 @@ +PERLIN_PARAM:SCALE 0.434448 +PERLIN_PARAM:OFFSET 0.831105 +PERLIN_PARAM:PERSISTENCE 0.630867 +PERLIN_PARAM:OCTAVES 8.00407 +PERLIN_PARAM:PRIME_1 85.4235 +PERLIN_PARAM:PRIME_2 21.3039 +PERLIN_PARAM:PRIME_3 104.858 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/perlin.stderr b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/perlin.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/perlin.stdout b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/perlin.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/perlin_2.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/perlin_2.txt new file mode 100644 index 00000000000..b747326d4e0 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/perlin_2.txt @@ -0,0 +1,100 @@ +1.398056 +1.370914 +1.363998 +1.279446 +1.232190 +1.280908 +1.103097 +1.044700 +1.071948 +1.061684 +1.094715 +0.964632 +0.831411 +0.781949 +0.814620 +0.849516 +0.851001 +0.765851 +0.758157 +0.773463 +0.753641 +0.789955 +0.752934 +0.828227 +0.915443 +0.973537 +1.081467 +1.241200 +1.241392 +1.257672 +1.377862 +1.470086 +1.421784 +1.325527 +1.150346 +1.051501 +1.044802 +1.106502 +1.157155 +1.178239 +1.131983 +1.091121 +1.099748 +1.022758 +0.922794 +1.005841 +1.071597 +1.179689 +1.248927 +1.203617 +1.238719 +1.303884 +1.370907 +1.578622 +1.711466 +1.711839 +1.668280 +1.577131 +1.546602 +1.622520 +1.688682 +1.788272 +1.882089 +1.796176 +1.789849 +1.824672 +1.596314 +1.354986 +1.209724 +1.109246 +0.923528 +0.922481 +0.911951 +0.973918 +1.090397 +1.192352 +1.060059 +0.993187 +1.014380 +0.913349 +0.932815 +0.882706 +0.835195 +0.874574 +0.888107 +0.876224 +0.749712 +0.614713 +0.449095 +0.520278 +0.614327 +0.737773 +0.860582 +0.969577 +1.155922 +1.219523 +1.274034 +1.070757 +0.993013 +0.832009 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/perlin_3.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/perlin_3.txt new file mode 100644 index 00000000000..c5dd1d518a9 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/perlin_3.txt @@ -0,0 +1,100 @@ +0.585905 +0.452598 +0.261390 +0.099559 +0.008463 +0.090195 +0.020645 +0.032193 +0.098826 +0.167894 +0.384631 +0.408063 +0.383683 +0.343951 +0.379248 +0.446961 +0.428666 +0.448737 +0.598860 +0.866793 +1.106700 +1.241132 +1.243349 +1.200570 +1.168896 +1.114956 +1.024733 +1.054210 +1.020238 +1.038965 +1.117287 +1.162247 +1.101024 +1.127568 +1.144400 +1.180464 +1.249435 +1.298417 +1.447368 +1.686352 +1.737009 +1.763308 +1.821366 +1.709790 +1.553214 +1.506426 +1.360820 +1.279066 +1.279741 +1.183449 +1.062901 +0.861486 +0.677115 +0.670046 +0.624699 +0.557809 +0.499881 +0.413392 +0.369794 +0.411126 +0.420676 +0.401154 +0.397886 +0.236404 +0.172236 +0.286392 +0.241739 +0.216426 +0.242506 +0.276757 +0.218665 +0.312935 +0.345126 +0.458120 +0.609833 +0.806243 +0.893627 +0.994656 +1.162081 +1.148060 +1.166777 +1.176970 +1.258971 +1.433259 +1.522462 +1.549638 +1.469646 +1.375772 +1.233844 +1.290657 +1.317444 +1.262699 +1.170724 +1.002436 +0.948090 +0.944428 +0.984570 +0.910954 +0.945140 +0.749551 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/perlin_fail.status b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/perlin_fail.status new file mode 100644 index 00000000000..46a71f4e166 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/perlin_fail.status @@ -0,0 +1 @@ +Deleted report step: 1 \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/perlin_params.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/perlin_params.txt new file mode 100644 index 00000000000..324cbda2ee5 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/perlin_params.txt @@ -0,0 +1,7 @@ +SCALE:0.434448 +OFFSET:0.831105 +PERSISTENCE:0.630867 +OCTAVES:8.00407 +PRIME_1:85.4235 +PRIME_2:21.3039 +PRIME_3:104.858 \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/realization.ok b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/realization.ok new file mode 100644 index 00000000000..a0aba9318ad --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-4/iter-0/realization.ok @@ -0,0 +1 @@ +OK \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/ERROR b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/ERROR new file mode 100644 index 00000000000..48bc5b6354f --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/ERROR @@ -0,0 +1,8 @@ + + + REALIZATION_FAIL + Could not find target_file:realization.ok + + + + diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/EXIT b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/EXIT new file mode 100644 index 00000000000..48bc5b6354f --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/EXIT @@ -0,0 +1,8 @@ + + + REALIZATION_FAIL + Could not find target_file:realization.ok + + + + diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/aggregated.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/aggregated.txt new file mode 100644 index 00000000000..72e58cbf645 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/aggregated.txt @@ -0,0 +1,4 @@ +PERLIN_3 0.638889 +PERLIN_2 1.451039 +PERLIN_1 0.280615 +STATE Positive \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/aggregator.stderr b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/aggregator.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/aggregator.stdout b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/aggregator.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/parameters.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/parameters.txt new file mode 100644 index 00000000000..463f5c41d85 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/parameters.txt @@ -0,0 +1,7 @@ +PERLIN_PARAM:SCALE 0.72828 +PERLIN_PARAM:OFFSET 0.48251 +PERLIN_PARAM:PERSISTENCE 0.878696 +PERLIN_PARAM:OCTAVES 62.5026 +PERLIN_PARAM:PRIME_1 10.3564 +PERLIN_PARAM:PRIME_2 34.9666 +PERLIN_PARAM:PRIME_3 46.2883 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/perlin.stderr b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/perlin.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/perlin.stdout b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/perlin.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/perlin_1.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/perlin_1.txt new file mode 100644 index 00000000000..11da497a094 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/perlin_1.txt @@ -0,0 +1,100 @@ +0.280615 +0.140016 +0.101814 +-0.129046 +0.108540 +0.255758 +-0.136433 +0.402995 +0.060006 +0.243033 +0.270699 +1.032076 +-0.013904 +-0.123877 +0.661223 +0.260982 +0.218027 +1.382060 +0.399264 +0.483361 +0.472825 +-0.400746 +1.339330 +0.692467 +0.043042 +0.542935 +-0.239154 +0.322095 +0.568886 +-0.253825 +0.069002 +0.151587 +-0.060719 +0.071557 +1.232762 +0.806173 +0.197592 +0.775339 +0.485706 +-0.340968 +0.563185 +-0.301442 +-0.470657 +0.391865 +1.604934 +1.366571 +0.978296 +0.533236 +0.199409 +0.640021 +0.724001 +0.620437 +-0.062286 +0.448127 +0.681424 +0.211811 +0.962468 +0.569284 +0.153403 +0.463251 +0.717336 +1.047732 +0.879313 +0.557671 +0.589243 +0.475895 +0.668305 +1.261389 +1.992548 +0.514705 +1.480108 +0.964709 +0.672593 +0.541782 +1.389203 +0.533862 +1.237538 +0.892042 +0.376384 +0.676731 +1.374955 +1.291646 +0.276911 +0.706050 +0.032507 +1.074094 +0.914922 +1.747032 +2.087744 +1.287205 +1.735100 +1.305039 +1.283753 +0.989592 +0.686460 +0.373096 +0.309114 +0.349820 +0.814016 +0.915557 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/perlin_3.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/perlin_3.txt new file mode 100644 index 00000000000..4b5a132c2c5 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/perlin_3.txt @@ -0,0 +1,100 @@ +0.638889 +0.410195 +0.219070 +-0.168964 +-0.060804 +0.091251 +-0.310748 +0.238941 +-0.042062 +0.155990 +0.259406 +1.020776 +-0.110433 +-0.305635 +0.479458 +0.154968 +0.127038 +1.333433 +0.420498 +0.615022 +0.762375 +0.010082 +1.772197 +1.106807 +0.463132 +0.968775 +0.168158 +0.688947 +0.994515 +0.267193 +0.637949 +0.790394 +0.620450 +0.793639 +2.004706 +1.552222 +0.884301 +1.340207 +0.962178 +0.185367 +1.130433 +0.350189 +0.239143 +0.994664 +2.052722 +1.659346 +1.164070 +0.714679 +0.402736 +0.802732 +0.819881 +0.546816 +-0.353419 +0.027870 +0.128278 +-0.371555 +0.426874 +-0.024508 +-0.541415 +-0.345851 +-0.187719 +0.083337 +-0.110978 +-0.445258 +-0.435272 +-0.488676 +-0.192211 +0.456083 +1.223572 +-0.254552 +0.677673 +0.240441 +0.034157 +-0.047433 +0.789765 +-0.187141 +0.479641 +0.107975 +-0.414972 +-0.073069 +0.622598 +0.635844 +-0.186447 +0.448791 +-0.089311 +1.014494 +0.884645 +1.732422 +2.096466 +1.319259 +1.782821 +1.319583 +1.298015 +0.977683 +0.667262 +0.395453 +0.328915 +0.392952 +0.872816 +1.003679 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/perlin_fail.status b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/perlin_fail.status new file mode 100644 index 00000000000..2494f445e5f --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/perlin_fail.status @@ -0,0 +1 @@ +Deleted report step: 2 \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/perlin_params.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/perlin_params.txt new file mode 100644 index 00000000000..6ffe51857b0 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-5/iter-0/perlin_params.txt @@ -0,0 +1,7 @@ +SCALE:0.72828 +OFFSET:0.48251 +PERSISTENCE:0.878696 +OCTAVES:62.5026 +PRIME_1:10.3564 +PRIME_2:34.9666 +PRIME_3:46.2883 \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/OK b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/OK new file mode 100644 index 00000000000..9590cb949c2 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/OK @@ -0,0 +1 @@ +All jobs complete \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/aggregated.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/aggregated.txt new file mode 100644 index 00000000000..07f12f2a08d --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/aggregated.txt @@ -0,0 +1,4 @@ +PERLIN_3 0.870550 +PERLIN_2 1.682701 +PERLIN_1 0.512277 +STATE Positive \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/aggregator.stderr b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/aggregator.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/aggregator.stdout b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/aggregator.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/parameters.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/parameters.txt new file mode 100644 index 00000000000..04dc1adb802 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/parameters.txt @@ -0,0 +1,7 @@ +PERLIN_PARAM:SCALE 0.105108 +PERLIN_PARAM:OFFSET 0.913379 +PERLIN_PARAM:PERSISTENCE 0.962764 +PERLIN_PARAM:OCTAVES 54.2875 +PERLIN_PARAM:PRIME_1 12.1353 +PERLIN_PARAM:PRIME_2 108.225 +PERLIN_PARAM:PRIME_3 58.2668 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/perlin.stderr b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/perlin.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/perlin.stdout b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/perlin.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/perlin_1.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/perlin_1.txt new file mode 100644 index 00000000000..309c31a80bd --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/perlin_1.txt @@ -0,0 +1,100 @@ +0.512277 +0.536404 +0.520619 +0.520234 +0.561383 +0.536841 +0.593527 +0.431159 +0.579716 +0.595432 +0.613877 +0.832759 +0.851509 +0.830369 +0.752398 +0.701988 +0.854139 +0.813144 +0.848016 +0.675839 +0.919173 +1.078956 +1.165071 +1.045397 +1.099168 +1.049976 +0.934854 +0.813458 +0.777245 +0.747191 +0.680688 +0.492784 +0.756629 +0.639112 +0.677362 +0.560299 +0.783620 +0.757035 +0.781494 +1.092201 +1.102767 +1.052654 +1.207713 +1.115009 +1.380213 +1.620005 +1.355587 +1.358326 +1.365758 +1.049275 +1.264629 +1.182026 +1.240226 +1.329804 +1.210895 +1.340758 +1.160254 +1.099240 +1.243394 +1.267952 +1.359115 +1.457122 +1.219766 +1.225781 +1.439935 +1.367284 +1.242810 +1.155161 +1.278395 +1.180081 +1.125988 +1.131542 +1.239492 +1.086901 +1.273243 +1.532824 +1.482385 +1.810195 +1.887451 +1.774089 +1.870171 +1.843973 +1.696110 +1.740468 +1.791604 +1.614504 +1.582554 +1.758357 +1.634232 +1.684106 +1.786180 +1.619646 +1.484936 +1.430948 +1.379634 +1.352505 +1.382218 +1.487943 +1.057155 +0.971585 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/perlin_2.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/perlin_2.txt new file mode 100644 index 00000000000..463edcab273 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/perlin_2.txt @@ -0,0 +1,100 @@ +1.682701 +1.724900 +1.740484 +1.660204 +1.615765 +1.563048 +1.501665 +1.279611 +1.450770 +1.402179 +1.312668 +1.378029 +1.202709 +1.086610 +1.006006 +0.998528 +1.185484 +1.081631 +1.028546 +0.714171 +0.855664 +1.038607 +1.107524 +1.087393 +1.265806 +1.334397 +1.398900 +1.367299 +1.424028 +1.486916 +1.510209 +1.439430 +1.758558 +1.559152 +1.455253 +1.177386 +1.265695 +1.129987 +0.967754 +1.110422 +1.064988 +1.032099 +1.195895 +1.030776 +1.197580 +1.412196 +1.252139 +1.440393 +1.538272 +1.232153 +1.536327 +1.550802 +1.642885 +1.818122 +1.744516 +1.911422 +1.793059 +1.669187 +1.725384 +1.670243 +1.722066 +1.879845 +1.713679 +1.782625 +2.033032 +1.940992 +1.736869 +1.488416 +1.476638 +1.243313 +1.028416 +1.016820 +1.167881 +1.013483 +1.154370 +1.197929 +0.890921 +1.024660 +0.948394 +0.789577 +0.883852 +0.893907 +0.808975 +0.924524 +1.035431 +0.881490 +0.832342 +0.982689 +0.858205 +0.945780 +1.130784 +1.109264 +1.189055 +1.386180 +1.568267 +1.649957 +1.691483 +1.690880 +1.163828 +1.142166 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/perlin_fail.status b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/perlin_fail.status new file mode 100644 index 00000000000..6842226365d --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/perlin_fail.status @@ -0,0 +1 @@ +Deleted report step: 3 \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/perlin_params.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/perlin_params.txt new file mode 100644 index 00000000000..29d18ec8995 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/perlin_params.txt @@ -0,0 +1,7 @@ +SCALE:0.105108 +OFFSET:0.913379 +PERSISTENCE:0.962764 +OCTAVES:54.2875 +PRIME_1:12.1353 +PRIME_2:108.225 +PRIME_3:58.2668 \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/realization.ok b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/realization.ok new file mode 100644 index 00000000000..a0aba9318ad --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-6/iter-0/realization.ok @@ -0,0 +1 @@ +OK \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/ERROR b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/ERROR new file mode 100644 index 00000000000..1eb5bf01146 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/ERROR @@ -0,0 +1,8 @@ + + + REALIZATION_FAIL + Could not find target_file:realization.ok + + + + diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/EXIT b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/EXIT new file mode 100644 index 00000000000..1eb5bf01146 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/EXIT @@ -0,0 +1,8 @@ + + + REALIZATION_FAIL + Could not find target_file:realization.ok + + + + diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/aggregated.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/aggregated.txt new file mode 100644 index 00000000000..6b3b4204d7b --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/aggregated.txt @@ -0,0 +1,4 @@ +PERLIN_3 -0.223092 +PERLIN_2 0.589059 +PERLIN_1 -0.581365 +STATE Negative \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/aggregator.stderr b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/aggregator.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/aggregator.stdout b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/aggregator.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/parameters.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/parameters.txt new file mode 100644 index 00000000000..0fec4832fd0 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/parameters.txt @@ -0,0 +1,7 @@ +PERLIN_PARAM:SCALE 0.100905 +PERLIN_PARAM:OFFSET 0.749785 +PERLIN_PARAM:PERSISTENCE 0.960394 +PERLIN_PARAM:OCTAVES 27.0099 +PERLIN_PARAM:PRIME_1 40.6138 +PERLIN_PARAM:PRIME_2 61.0175 +PERLIN_PARAM:PRIME_3 87.8034 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/perlin.stderr b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/perlin.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/perlin.stdout b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/perlin.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/perlin_1.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/perlin_1.txt new file mode 100644 index 00000000000..f4ab4143099 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/perlin_1.txt @@ -0,0 +1,100 @@ +-0.581365 +0.196529 +0.251092 +0.129792 +0.331063 +0.243506 +0.276892 +0.080451 +0.396119 +0.359775 +0.367571 +0.375403 +0.583877 +0.600213 +0.433206 +0.667000 +0.730207 +0.597004 +0.652034 +0.663611 +0.746119 +0.692967 +0.730701 +0.876335 +0.892405 +0.727578 +0.723739 +0.564331 +0.468088 +0.488808 +0.664805 +0.660028 +0.630792 +0.524399 +0.474312 +0.443139 +0.580488 +0.481039 +0.800572 +1.001564 +0.911524 +0.901357 +0.788429 +0.858484 +0.905262 +1.091118 +1.153703 +1.092191 +1.125297 +0.931559 +0.887913 +0.949225 +1.000548 +1.157858 +0.916321 +0.916039 +0.825100 +0.963569 +0.946300 +1.325529 +1.317807 +1.051895 +1.371837 +1.120878 +1.290153 +1.132642 +1.106899 +0.951717 +1.053646 +1.097485 +0.993622 +1.071263 +1.003207 +1.060272 +0.945896 +1.081059 +1.483716 +1.639907 +1.778137 +1.743444 +1.658571 +1.670091 +1.528567 +1.360096 +1.348333 +1.247859 +1.311556 +1.223970 +1.139654 +1.236625 +1.220851 +1.388255 +1.244178 +1.146154 +1.093360 +0.984612 +1.125281 +1.104519 +0.927790 +0.740832 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/perlin_2.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/perlin_2.txt new file mode 100644 index 00000000000..3b2079e9c1b --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/perlin_2.txt @@ -0,0 +1,100 @@ +0.589059 +1.385024 +1.470957 +1.269762 +1.385446 +1.269712 +1.185030 +0.928904 +1.267172 +1.166521 +1.066362 +0.920673 +0.935076 +0.856454 +0.686814 +0.963540 +1.061552 +0.865491 +0.832564 +0.701943 +0.682610 +0.652618 +0.673153 +0.918331 +1.059042 +1.011999 +1.187784 +1.118172 +1.114871 +1.228534 +1.494326 +1.606674 +1.632721 +1.444440 +1.252203 +1.060226 +1.062564 +0.853991 +0.986832 +1.019786 +0.873746 +0.880802 +0.776611 +0.774251 +0.722629 +0.883309 +1.050254 +1.174258 +1.297810 +1.114437 +1.159611 +1.318001 +1.403207 +1.646177 +1.449942 +1.486702 +1.457905 +1.533516 +1.428289 +1.727821 +1.680758 +1.474617 +1.865750 +1.677722 +1.883250 +1.706351 +1.600958 +1.284972 +1.251889 +1.160717 +0.896049 +0.956541 +0.931596 +0.986854 +0.827023 +0.746164 +0.892252 +0.854372 +0.839080 +0.758932 +0.672252 +0.720025 +0.641433 +0.544152 +0.592160 +0.514846 +0.561344 +0.448302 +0.363627 +0.498299 +0.565455 +0.877873 +0.948297 +1.101386 +1.281993 +1.282064 +1.434546 +1.307456 +1.034463 +0.911412 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/perlin_3.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/perlin_3.txt new file mode 100644 index 00000000000..520a51e79f7 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/perlin_3.txt @@ -0,0 +1,100 @@ +-0.223092 +0.466707 +0.368349 +0.089874 +0.161719 +0.078999 +0.102578 +-0.083603 +0.294051 +0.272732 +0.356278 +0.364104 +0.487348 +0.418455 +0.251441 +0.560986 +0.639218 +0.548377 +0.673267 +0.795272 +1.035669 +1.103795 +1.163568 +1.290674 +1.312494 +1.153418 +1.131051 +0.931183 +0.893717 +1.009827 +1.233752 +1.298835 +1.311961 +1.246482 +1.246256 +1.189188 +1.267197 +1.045907 +1.277045 +1.527899 +1.478772 +1.552988 +1.498229 +1.461283 +1.353050 +1.383894 +1.339477 +1.273635 +1.328624 +1.094269 +0.983792 +0.875603 +0.709414 +0.737601 +0.363175 +0.332673 +0.289505 +0.369777 +0.251481 +0.516427 +0.412752 +0.087499 +0.381547 +0.117950 +0.265638 +0.168071 +0.246383 +0.146412 +0.284670 +0.328228 +0.191186 +0.346995 +0.364771 +0.471056 +0.346459 +0.360055 +0.725820 +0.855841 +0.986781 +0.993643 +0.906214 +1.014289 +1.065209 +1.102837 +1.226516 +1.188260 +1.281279 +1.209360 +1.148376 +1.268678 +1.268572 +1.402798 +1.258439 +1.134246 +1.074161 +1.006970 +1.145082 +1.147652 +0.986590 +0.828954 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/perlin_fail.status b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/perlin_fail.status new file mode 100644 index 00000000000..6ac0122a284 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/perlin_fail.status @@ -0,0 +1 @@ +Did nothing! \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/perlin_params.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/perlin_params.txt new file mode 100644 index 00000000000..44551227721 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-7/iter-0/perlin_params.txt @@ -0,0 +1,7 @@ +SCALE:0.100905 +OFFSET:0.749785 +PERSISTENCE:0.960394 +OCTAVES:27.0099 +PRIME_1:40.6138 +PRIME_2:61.0175 +PRIME_3:87.8034 \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/OK b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/OK new file mode 100644 index 00000000000..9590cb949c2 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/OK @@ -0,0 +1 @@ +All jobs complete \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/aggregated.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/aggregated.txt new file mode 100644 index 00000000000..244709c6519 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/aggregated.txt @@ -0,0 +1,4 @@ +PERLIN_3 -0.917785 +PERLIN_2 -0.105634 +PERLIN_1 -1.276058 +STATE Negative \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/aggregator.stderr b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/aggregator.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/aggregator.stdout b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/aggregator.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/parameters.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/parameters.txt new file mode 100644 index 00000000000..59ba741905a --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/parameters.txt @@ -0,0 +1,7 @@ +PERLIN_PARAM:SCALE 0.494279 +PERLIN_PARAM:OFFSET -0.526799 +PERLIN_PARAM:PERSISTENCE 0.0911083 +PERLIN_PARAM:OCTAVES 43.0492 +PERLIN_PARAM:PRIME_1 123.299 +PERLIN_PARAM:PRIME_2 42.4258 +PERLIN_PARAM:PRIME_3 14.0702 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/perlin.stderr b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/perlin.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/perlin.stdout b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/perlin.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/perlin_1.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/perlin_1.txt new file mode 100644 index 00000000000..6aa944dccfb --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/perlin_1.txt @@ -0,0 +1,100 @@ +-1.276058 +-1.289017 +-1.314341 +-1.306601 +-1.296437 +-1.293440 +-1.259407 +-1.266510 +-1.294486 +-1.261902 +-1.202479 +-1.080475 +-0.954164 +-0.885534 +-0.858941 +-0.894787 +-0.919912 +-0.916939 +-0.886507 +-0.799550 +-0.721154 +-0.650200 +-0.537956 +-0.483812 +-0.489635 +-0.525394 +-0.546662 +-0.527208 +-0.534832 +-0.533549 +-0.538252 +-0.584050 +-0.611137 +-0.630059 +-0.650202 +-0.616093 +-0.568181 +-0.463276 +-0.334508 +-0.259597 +-0.221372 +-0.259230 +-0.279580 +-0.244302 +-0.207325 +-0.170186 +-0.177288 +-0.240439 +-0.278604 +-0.325685 +-0.411518 +-0.447688 +-0.438482 +-0.430901 +-0.426508 +-0.473419 +-0.523455 +-0.492174 +-0.429854 +-0.320462 +-0.242691 +-0.226471 +-0.221185 +-0.262628 +-0.292571 +-0.326766 +-0.377534 +-0.383441 +-0.373359 +-0.364832 +-0.330290 +-0.341756 +-0.378785 +-0.325450 +-0.201339 +-0.014930 +0.131198 +0.218084 +0.307262 +0.331535 +0.323047 +0.275638 +0.204499 +0.189781 +0.216423 +0.234694 +0.244237 +0.196780 +0.140343 +0.139795 +0.132064 +0.149348 +0.135500 +0.067575 +-0.002818 +-0.038351 +-0.033175 +-0.039802 +-0.104931 +-0.282153 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/perlin_2.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/perlin_2.txt new file mode 100644 index 00000000000..9527f5ea7a7 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/perlin_2.txt @@ -0,0 +1,100 @@ +-0.105634 +-0.100522 +-0.094477 +-0.166631 +-0.242055 +-0.267233 +-0.351269 +-0.418058 +-0.423433 +-0.455156 +-0.503687 +-0.535205 +-0.602964 +-0.629293 +-0.605334 +-0.598246 +-0.588567 +-0.648452 +-0.705977 +-0.761218 +-0.784663 +-0.690549 +-0.595504 +-0.441816 +-0.322998 +-0.240974 +-0.082616 +0.026633 +0.111952 +0.206176 +0.291269 +0.362596 +0.390792 +0.289982 +0.127689 +0.000994 +-0.086106 +-0.090324 +-0.148248 +-0.241375 +-0.259151 +-0.279786 +-0.291397 +-0.328535 +-0.389958 +-0.377995 +-0.280737 +-0.158372 +-0.106091 +-0.142807 +-0.139820 +-0.078912 +-0.035823 +0.057418 +0.107112 +0.097244 +0.109350 +0.077772 +0.052136 +0.081830 +0.120261 +0.196251 +0.272728 +0.294216 +0.300526 +0.246943 +0.116525 +-0.050187 +-0.175115 +-0.301600 +-0.427862 +-0.456478 +-0.450396 +-0.398868 +-0.320212 +-0.349825 +-0.460267 +-0.567451 +-0.631795 +-0.652977 +-0.663271 +-0.674428 +-0.682635 +-0.626163 +-0.539750 +-0.498320 +-0.505975 +-0.578888 +-0.635684 +-0.598530 +-0.523332 +-0.361034 +-0.160381 +0.022807 +0.185816 +0.259100 +0.276090 +0.163135 +0.001742 +-0.111572 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/perlin_3.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/perlin_3.txt new file mode 100644 index 00000000000..cd80bec7c26 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/perlin_3.txt @@ -0,0 +1,100 @@ +-0.917785 +-1.018838 +-1.197085 +-1.346518 +-1.465782 +-1.457947 +-1.433721 +-1.430564 +-1.396554 +-1.348945 +-1.213771 +-1.091774 +-1.050692 +-1.067291 +-1.040706 +-1.000801 +-1.010901 +-0.965566 +-0.865274 +-0.667889 +-0.431604 +-0.239372 +-0.105089 +-0.069473 +-0.069545 +-0.099554 +-0.139350 +-0.160356 +-0.109202 +-0.012531 +0.030695 +0.054757 +0.070031 +0.092023 +0.121743 +0.129957 +0.118527 +0.101591 +0.141964 +0.266738 +0.345876 +0.392401 +0.430221 +0.358497 +0.240463 +0.122590 +0.008486 +-0.058995 +-0.075277 +-0.162975 +-0.315639 +-0.521310 +-0.729615 +-0.851158 +-0.979655 +-1.056785 +-1.059049 +-1.085967 +-1.124672 +-1.129564 +-1.147745 +-1.190867 +-1.211475 +-1.265556 +-1.317086 +-1.291337 +-1.238050 +-1.188747 +-1.142334 +-1.134089 +-1.132725 +-1.066024 +-1.017221 +-0.914666 +-0.800777 +-0.735933 +-0.626698 +-0.565983 +-0.484094 +-0.418266 +-0.429310 +-0.380164 +-0.258859 +-0.067478 +0.094606 +0.175094 +0.213959 +0.182170 +0.149065 +0.171849 +0.179785 +0.163891 +0.149761 +0.055666 +-0.022016 +-0.015994 +-0.013374 +0.003331 +-0.046131 +-0.194030 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/perlin_fail.status b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/perlin_fail.status new file mode 100644 index 00000000000..6ac0122a284 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/perlin_fail.status @@ -0,0 +1 @@ +Did nothing! \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/perlin_params.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/perlin_params.txt new file mode 100644 index 00000000000..219b62155c1 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/perlin_params.txt @@ -0,0 +1,7 @@ +SCALE:0.494279 +OFFSET:-0.526799 +PERSISTENCE:0.0911083 +OCTAVES:43.0492 +PRIME_1:123.299 +PRIME_2:42.4258 +PRIME_3:14.0702 \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/realization.ok b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/realization.ok new file mode 100644 index 00000000000..a0aba9318ad --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-8/iter-0/realization.ok @@ -0,0 +1 @@ +OK \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/OK b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/OK new file mode 100644 index 00000000000..9590cb949c2 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/OK @@ -0,0 +1 @@ +All jobs complete \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/aggregated.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/aggregated.txt new file mode 100644 index 00000000000..48b8531624f --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/aggregated.txt @@ -0,0 +1,4 @@ +PERLIN_3 0.220371 +PERLIN_2 1.032522 +PERLIN_1 -0.137903 +STATE Positive \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/aggregator.stderr b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/aggregator.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/aggregator.stdout b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/aggregator.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/parameters.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/parameters.txt new file mode 100644 index 00000000000..2e4f0f7b049 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/parameters.txt @@ -0,0 +1,7 @@ +PERLIN_PARAM:SCALE 0.971949 +PERLIN_PARAM:OFFSET 0.568165 +PERLIN_PARAM:PERSISTENCE 0.332226 +PERLIN_PARAM:OCTAVES 8.48231 +PERLIN_PARAM:PRIME_1 106.04 +PERLIN_PARAM:PRIME_2 17.4819 +PERLIN_PARAM:PRIME_3 56.9629 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/perlin.stderr b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/perlin.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/perlin.stdout b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/perlin.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/perlin_1.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/perlin_1.txt new file mode 100644 index 00000000000..36798a3f535 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/perlin_1.txt @@ -0,0 +1,100 @@ +-0.137903 +-0.157851 +-0.192276 +-0.201670 +-0.192839 +-0.193214 +-0.209423 +-0.243628 +-0.261796 +-0.213269 +-0.207591 +-0.203993 +-0.142490 +-0.122206 +-0.157129 +-0.193732 +-0.206432 +-0.177612 +-0.114381 +-0.050740 +-0.070342 +-0.154469 +-0.151716 +-0.137877 +-0.155888 +-0.199095 +-0.226091 +-0.254674 +-0.326284 +-0.337833 +-0.312344 +-0.317023 +-0.346853 +-0.332528 +-0.238504 +-0.042655 +0.146262 +0.376535 +0.617211 +0.752626 +0.797129 +0.714055 +0.660213 +0.707589 +0.786058 +0.902157 +0.982988 +0.972517 +0.950945 +0.917937 +0.833802 +0.834170 +0.874761 +0.874663 +0.835114 +0.714452 +0.615202 +0.641686 +0.698232 +0.833272 +0.978281 +1.070483 +1.118218 +1.061277 +1.031902 +0.998036 +0.965574 +1.010296 +1.079127 +1.148032 +1.260956 +1.285149 +1.247059 +1.290888 +1.424147 +1.629905 +1.764537 +1.819917 +1.898802 +1.893787 +1.855403 +1.728917 +1.536779 +1.375375 +1.281240 +1.209446 +1.131790 +1.009449 +0.933443 +0.938227 +0.970148 +1.049769 +1.076923 +1.035402 +0.981504 +0.949433 +0.958861 +0.977078 +0.958006 +0.802606 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/perlin_2.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/perlin_2.txt new file mode 100644 index 00000000000..ba557199f40 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/perlin_2.txt @@ -0,0 +1,100 @@ +1.032522 +1.030645 +1.027589 +0.938300 +0.861544 +0.832993 +0.698715 +0.604825 +0.609257 +0.593477 +0.491201 +0.341277 +0.208709 +0.134035 +0.096478 +0.102808 +0.124913 +0.090875 +0.066149 +-0.012408 +-0.133850 +-0.194818 +-0.209263 +-0.095881 +0.010749 +0.085326 +0.237955 +0.299167 +0.320499 +0.401893 +0.517178 +0.629623 +0.655076 +0.587512 +0.539387 +0.574432 +0.628337 +0.749487 +0.803470 +0.770847 +0.759350 +0.693500 +0.648396 +0.623356 +0.603426 +0.694348 +0.879540 +1.054584 +1.123458 +1.100816 +1.105500 +1.202947 +1.277420 +1.362982 +1.368734 +1.285116 +1.248007 +1.211633 +1.180222 +1.235564 +1.341232 +1.493206 +1.612131 +1.618121 +1.625000 +1.571745 +1.459633 +1.343551 +1.277370 +1.211264 +1.163383 +1.170426 +1.175448 +1.217470 +1.305274 +1.295011 +1.173072 +1.034382 +0.959745 +0.909275 +0.869084 +0.778851 +0.649645 +0.559430 +0.525067 +0.476432 +0.381578 +0.233781 +0.157416 +0.199901 +0.314752 +0.539386 +0.781042 +0.990634 +1.170137 +1.246885 +1.268126 +1.180015 +1.064679 +0.973186 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/perlin_3.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/perlin_3.txt new file mode 100644 index 00000000000..2bb0cef781f --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/perlin_3.txt @@ -0,0 +1,100 @@ +0.220371 +0.112328 +-0.075019 +-0.241588 +-0.362184 +-0.357721 +-0.383737 +-0.407682 +-0.363864 +-0.300312 +-0.218883 +-0.215292 +-0.239019 +-0.303964 +-0.338894 +-0.299746 +-0.297421 +-0.226239 +-0.093147 +0.080922 +0.219209 +0.256359 +0.281152 +0.276463 +0.264202 +0.226745 +0.181221 +0.112178 +0.099345 +0.183186 +0.256603 +0.321784 +0.334316 +0.389554 +0.533441 +0.703395 +0.832970 +0.941402 +1.093683 +1.278960 +1.364377 +1.365686 +1.370013 +1.310389 +1.233846 +1.194933 +1.168762 +1.153961 +1.154271 +1.080648 +0.929681 +0.760549 +0.583628 +0.454406 +0.281967 +0.131086 +0.079608 +0.047894 +0.003413 +0.024170 +0.073226 +0.106088 +0.127928 +0.058349 +0.007387 +0.033465 +0.105058 +0.204991 +0.310151 +0.378775 +0.458521 +0.560881 +0.608623 +0.701672 +0.824709 +0.908902 +1.006640 +1.035850 +1.107446 +1.143986 +1.103046 +1.073115 +1.073421 +1.118115 +1.159422 +1.149846 +1.101513 +0.994839 +0.942165 +0.970280 +1.017869 +1.064312 +1.091185 +1.023493 +0.962306 +0.971791 +0.978662 +1.020211 +1.016806 +0.890728 diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/perlin_fail.status b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/perlin_fail.status new file mode 100644 index 00000000000..6ac0122a284 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/perlin_fail.status @@ -0,0 +1 @@ +Did nothing! \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/perlin_params.txt b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/perlin_params.txt new file mode 100644 index 00000000000..c3e63c5f538 --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/perlin_params.txt @@ -0,0 +1,7 @@ +SCALE:0.971949 +OFFSET:0.568165 +PERSISTENCE:0.332226 +OCTAVES:8.48231 +PRIME_1:106.04 +PRIME_2:17.4819 +PRIME_3:56.9629 \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/realization.ok b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/realization.ok new file mode 100644 index 00000000000..a0aba9318ad --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/Run/realisation-9/iter-0/realization.ok @@ -0,0 +1 @@ +OK \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/current_case b/libres/test-data/local/mini_ert/fail_storage/ertensemble/current_case new file mode 100644 index 00000000000..331d858ce9b --- /dev/null +++ b/libres/test-data/local/mini_ert/fail_storage/ertensemble/current_case @@ -0,0 +1 @@ +default \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/0001/files/PERLIN_active b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/0001/files/PERLIN_active new file mode 100644 index 00000000000..c72400474a6 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/0001/files/PERLIN_active differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/0002/files/PERLIN_active b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/0002/files/PERLIN_active new file mode 100644 index 00000000000..c72400474a6 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/0002/files/PERLIN_active differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/0003/files/PERLIN_active b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/0003/files/PERLIN_active new file mode 100644 index 00000000000..c72400474a6 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/0003/files/PERLIN_active differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/FORECAST.data_0 new file mode 100644 index 00000000000..efbe7f58b3c Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/FORECAST.data_0 differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/FORECAST.index new file mode 100644 index 00000000000..b0e9ebe6889 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/PARAMETER.data_0 new file mode 100644 index 00000000000..a9c2968ca34 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/PARAMETER.data_0 differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/PARAMETER.index new file mode 100644 index 00000000000..d16822022a2 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_0/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/FORECAST.data_0 new file mode 100644 index 00000000000..de1d02fa332 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/FORECAST.data_0 differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/FORECAST.index new file mode 100644 index 00000000000..fb519c3d539 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/PARAMETER.data_0 new file mode 100644 index 00000000000..5e8b2f1b175 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/PARAMETER.data_0 differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/PARAMETER.index new file mode 100644 index 00000000000..34f7beab32f Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_1/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/FORECAST.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/PARAMETER.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_10/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/FORECAST.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/PARAMETER.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_11/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/FORECAST.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/PARAMETER.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_12/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/FORECAST.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/PARAMETER.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_13/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/FORECAST.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/PARAMETER.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_14/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/FORECAST.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/PARAMETER.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_15/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/FORECAST.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/PARAMETER.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_16/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/FORECAST.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/PARAMETER.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_17/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/FORECAST.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/PARAMETER.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_18/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/FORECAST.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/PARAMETER.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_19/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/FORECAST.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/PARAMETER.data_0 new file mode 100644 index 00000000000..b7b2db9ef23 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/PARAMETER.data_0 differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/PARAMETER.index new file mode 100644 index 00000000000..6453ec00ca7 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_2/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/FORECAST.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/PARAMETER.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_20/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/FORECAST.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/PARAMETER.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_21/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/FORECAST.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/PARAMETER.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_22/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/FORECAST.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/PARAMETER.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_23/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/FORECAST.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/PARAMETER.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_24/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/FORECAST.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/PARAMETER.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_25/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/FORECAST.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/PARAMETER.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_26/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/FORECAST.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/PARAMETER.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_27/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/FORECAST.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/PARAMETER.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_28/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/FORECAST.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/PARAMETER.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_29/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/FORECAST.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/PARAMETER.data_0 new file mode 100644 index 00000000000..199393d8efb Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/PARAMETER.data_0 differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/PARAMETER.index new file mode 100644 index 00000000000..cd8301dcf3e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_3/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/FORECAST.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/PARAMETER.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_30/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/FORECAST.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/PARAMETER.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_31/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/FORECAST.data_0 new file mode 100644 index 00000000000..8b3a153a8a9 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/FORECAST.data_0 differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/FORECAST.index new file mode 100644 index 00000000000..b5a589b2713 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/PARAMETER.data_0 new file mode 100644 index 00000000000..f02909a32a7 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/PARAMETER.data_0 differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/PARAMETER.index new file mode 100644 index 00000000000..8ebbf01df8b Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_4/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/FORECAST.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/PARAMETER.data_0 new file mode 100644 index 00000000000..b5cb1eae3a1 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/PARAMETER.data_0 differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/PARAMETER.index new file mode 100644 index 00000000000..46988dc345b Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_5/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/FORECAST.data_0 new file mode 100644 index 00000000000..80f2a9aafd1 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/FORECAST.data_0 differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/FORECAST.index new file mode 100644 index 00000000000..10ceee2c80e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/PARAMETER.data_0 new file mode 100644 index 00000000000..d6b1ac3e58a Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/PARAMETER.data_0 differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/PARAMETER.index new file mode 100644 index 00000000000..a3ec21810e9 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_6/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/FORECAST.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/PARAMETER.data_0 new file mode 100644 index 00000000000..57a1b2511a0 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/PARAMETER.data_0 differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/PARAMETER.index new file mode 100644 index 00000000000..51cada30a8b Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_7/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/FORECAST.data_0 new file mode 100644 index 00000000000..24edf52b056 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/FORECAST.data_0 differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/FORECAST.index new file mode 100644 index 00000000000..bf076ad1aaa Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/PARAMETER.data_0 new file mode 100644 index 00000000000..703bfe7edb8 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/PARAMETER.data_0 differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/PARAMETER.index new file mode 100644 index 00000000000..21f092b0c6d Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_8/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/ANALYZED.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/ANALYZED.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/ANALYZED.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/ANALYZED.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/FORECAST.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/FORECAST.data_0 new file mode 100644 index 00000000000..83c5711b3d7 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/FORECAST.data_0 differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/FORECAST.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/FORECAST.index new file mode 100644 index 00000000000..fd35646b637 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/FORECAST.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/PARAMETER.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/PARAMETER.data_0 new file mode 100644 index 00000000000..c78a7697775 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/PARAMETER.data_0 differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/PARAMETER.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/PARAMETER.index new file mode 100644 index 00000000000..a8013e439c4 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/PARAMETER.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/STATIC.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/STATIC.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/STATIC.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/STATIC.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Ensemble/mod_9/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Index/INDEX.data_0 b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Index/INDEX.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Index/INDEX.index b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Index/INDEX.index new file mode 100644 index 00000000000..05ba4b90c10 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Index/INDEX.index differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Index/INDEX.mnt b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Index/INDEX.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/Index/INDEX.mnt differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/ert_fstab b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/ert_fstab new file mode 100644 index 00000000000..a5d67ad3a3c Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/ert_fstab differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/files/case_config b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/files/case_config new file mode 100644 index 00000000000..593f4708db8 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/files/case_config differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/files/custom_kw_config_set b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/files/custom_kw_config_set new file mode 100644 index 00000000000..b51c81a5bb4 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/files/custom_kw_config_set differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/files/state-map b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/files/state-map new file mode 100644 index 00000000000..c37c4be56ff Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/files/state-map differ diff --git a/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/files/summary-key-set b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/files/summary-key-set new file mode 100644 index 00000000000..593f4708db8 Binary files /dev/null and b/libres/test-data/local/mini_ert/fail_storage/ertensemble/default/files/summary-key-set differ diff --git a/libres/test-data/local/mini_ert/jobs/AGGREGATOR b/libres/test-data/local/mini_ert/jobs/AGGREGATOR new file mode 100644 index 00000000000..f9571d60f94 --- /dev/null +++ b/libres/test-data/local/mini_ert/jobs/AGGREGATOR @@ -0,0 +1,4 @@ +STDOUT aggregator.stdout +STDERR aggregator.stderr + +EXECUTABLE aggregator.py \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/jobs/PERLIN b/libres/test-data/local/mini_ert/jobs/PERLIN new file mode 100644 index 00000000000..fbf5c2ac879 --- /dev/null +++ b/libres/test-data/local/mini_ert/jobs/PERLIN @@ -0,0 +1,4 @@ +STDOUT perlin.stdout +STDERR perlin.stderr + +EXECUTABLE perlin.py \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/jobs/PERLIN_FAIL b/libres/test-data/local/mini_ert/jobs/PERLIN_FAIL new file mode 100644 index 00000000000..f6da4542e8a --- /dev/null +++ b/libres/test-data/local/mini_ert/jobs/PERLIN_FAIL @@ -0,0 +1,3 @@ +EXECUTABLE perlin_fail.py +TARGET_FILE perlin_fail.status +ARGLIST \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/jobs/REALIZATION_FAIL b/libres/test-data/local/mini_ert/jobs/REALIZATION_FAIL new file mode 100644 index 00000000000..533677cf99d --- /dev/null +++ b/libres/test-data/local/mini_ert/jobs/REALIZATION_FAIL @@ -0,0 +1,3 @@ +EXECUTABLE realization_fail.py +TARGET_FILE realization.ok +ARGLIST \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/jobs/aggregator.py b/libres/test-data/local/mini_ert/jobs/aggregator.py new file mode 100755 index 00000000000..6016e16ecf8 --- /dev/null +++ b/libres/test-data/local/mini_ert/jobs/aggregator.py @@ -0,0 +1,26 @@ +#! /usr/bin/env python + +if __name__ == "__main__": + files = { + "PERLIN_1": "perlin_1.txt", + "PERLIN_2": "perlin_2.txt", + "PERLIN_3": "perlin_3.txt", + } + + with open("aggregated.txt", "w") as output_file: + sum_of_sum = 0.0 + for key in files: + sum = 0.0 + with open(files[key], "r") as input_file: + sum += float(input_file.readline()) + sum_of_sum += sum + output_file.write("%s %f\n" % (key, sum)) + + if sum_of_sum < 0: + state = "Negative" + elif abs(sum_of_sum) < 0.00000001: + state = "Zero" + else: + state = "Positive" + + output_file.write("STATE %s" % state) diff --git a/libres/test-data/local/mini_ert/jobs/perlin.py b/libres/test-data/local/mini_ert/jobs/perlin.py new file mode 100755 index 00000000000..b23e75630a7 --- /dev/null +++ b/libres/test-data/local/mini_ert/jobs/perlin.py @@ -0,0 +1,538 @@ +#! /usr/bin/env python +import math +import random + +PRIME_INDEX_1 = [ + 49193, + 11887, + 23819, + 93983, + 28283, + 87179, + 74933, + 82561, + 29741, + 98453, + 72719, + 48193, + 66883, + 95071, + 12841, + 89603, + 49261, + 52529, + 57697, + 70321, + 54617, + 49363, + 41233, + 39883, + 35393, + 33149, + 37493, + 42989, + 58073, + 62507, + 99829, + 41999, + 44087, + 31907, + 10627, + 29231, + 57559, + 36809, + 17123, + 50593, + 38449, + 71317, + 38149, + 60637, + 10607, + 48677, + 23189, + 83701, + 78853, + 35617, + 28477, + 86117, + 46901, + 80819, + 89491, + 36097, + 54881, + 94781, + 20707, + 20011, + 69457, + 14593, + 49253, + 35257, + 14753, + 44851, + 10289, + 36097, + 62017, + 82723, + 10037, + 77551, + 89513, + 70429, + 30269, + 30703, + 77711, + 69313, + 20021, + 31657, + 33851, + 27749, + 85667, + 42793, + 47599, + 92789, + 97771, + 84551, + 61637, + 68659, + 93263, + 62201, + 42131, + 78823, + 17747, + 31183, + 89611, + 91009, + 76673, + 47051, + 94099, + 96757, + 90977, + 65141, + 46051, + 11093, + 19073, + 44633, + 27967, + 25171, + 41221, + 96223, + 88997, + 74941, + 49559, + 11909, + 11593, + 97369, + 55733, + 35869, + 55849, + 87931, + 81131, + 99023, + 76561, + 78977, + 48857, + 74717, +] + +PRIME_INDEX_2 = [ + 7360349, + 1287757, + 3764759, + 5276833, + 2735671, + 7351777, + 7705903, + 2034551, + 2427493, + 3883639, + 4260859, + 6814097, + 3226933, + 2648249, + 4458793, + 8015303, + 2323733, + 7991233, + 5560879, + 9826913, + 3634811, + 3746299, + 1051543, + 2954789, + 7874983, + 9380681, + 4577789, + 4306829, + 6714599, + 8395733, + 2718493, + 1429867, + 5675147, + 6104573, + 3118727, + 2657243, + 9750043, + 1853377, + 9441353, + 7247969, + 7669553, + 5334157, + 9376649, + 9518137, + 9368297, + 3912679, + 3230237, + 7291939, + 1361677, + 1034167, + 4998089, + 1178239, + 5160677, + 6130199, + 8056553, + 8527361, + 4261093, + 8640553, + 5553391, + 6024797, + 7275019, + 7245019, + 7661483, + 5120033, + 4388117, + 5941147, + 7682189, + 9303467, + 7165777, + 1509163, + 5223929, + 9696487, + 8012383, + 6254273, + 1400731, + 9958177, + 7733573, + 1498993, + 1553467, + 4791257, + 4524521, + 7048633, + 3630821, + 7931179, + 2341457, + 6432269, + 9597703, + 4338011, + 6665059, + 7911653, + 8384317, + 2230531, + 7904621, + 1633559, + 9096533, + 6873301, + 2717821, + 5897977, + 3608543, + 2248243, + 3174599, + 8634233, + 4028963, + 6435001, + 6611399, + 3250469, + 4046353, + 1429943, + 8552111, + 1970261, + 1045043, + 9552523, + 6993799, + 6141467, + 5723479, + 9578867, + 9233299, + 7224641, + 3165023, + 4583899, + 3905861, + 1633993, + 5013137, + 5092613, + 2197163, + 7732213, + 6559019, + 2538499, +] + +PRIME_INDEX_3 = [ + 1368916889, + 3054015583, + 6066123341, + 8673964289, + 9002483141, + 7187080993, + 5319345529, + 6961795349, + 1653814157, + 3416288497, + 6454122317, + 2480898239, + 3878100221, + 5956454227, + 9767569853, + 5981528503, + 4962084931, + 4489312199, + 3013312061, + 9818685161, + 4061204663, + 1816202221, + 7567463471, + 9839749459, + 3993070667, + 5872839331, + 9256050443, + 4854483611, + 4876755749, + 3823459247, + 6244209637, + 4199084081, + 6053970359, + 1093521049, + 7385602219, + 7289318273, + 9333908789, + 9701161343, + 8139801689, + 5013046681, + 4094649187, + 2372669671, + 9010267157, + 4298511787, + 7575340187, + 9252205969, + 5923706413, + 7112626819, + 6531270523, + 8379490583, + 4521945149, + 6804302789, + 6984132251, + 9173492033, + 1657527653, + 1532523367, + 3132088123, + 5910371431, + 7551540169, + 1643193353, + 6127000571, + 2637510193, + 7904761379, + 2954227033, + 7344843263, + 8077648457, + 9397237879, + 6775740173, + 1950824101, + 1152859999, + 2990299673, + 8197021109, + 2184824123, + 4309539167, + 1742841137, + 9113517421, + 4752058561, + 5594292329, + 9565022153, + 8519292151, + 6553311239, + 5204301593, + 8405487593, + 1987918357, + 3175759277, + 5659428917, + 6611421781, + 8765753053, + 3781235599, + 5651365571, + 8399394649, + 3867050417, + 3258145379, + 9836441977, + 2499690049, + 2742615479, + 7720787857, + 6135275183, + 9580731373, + 1860360379, + 2879750459, + 4302251633, + 8019104719, + 3889658671, + 7242891343, + 2516043583, + 8081336113, + 7718332591, + 4940550151, + 2216825899, + 7387662781, + 5562762407, + 2486416781, + 9111045257, + 1197705721, + 6649659239, + 6110149477, + 4548595937, + 3169540631, + 8993669479, + 6444114251, + 3098519969, + 1609592407, + 5803463207, + 8385117647, + 3056488453, + 1046337653, + 8165632597, +] + + +class PerlinNoise(object): + def __init__( + self, + persistence=0.5, + number_of_octaves=4, + prime_1=15731, + prime_2=789221, + prime_3=1376312589, + ): + self.persistence = persistence + self.number_of_octaves = number_of_octaves + self.prime_1 = prime_1 + self.prime_2 = prime_2 + self.prime_3 = prime_3 + + def cosineInterpolation(self, a, b, x): + ft = x * 3.1415927 + f = (1.0 - math.cos(ft)) * 0.5 + return a * (1 - f) + b * f + + def noise(self, x): + x = (x << 13) ^ x + return ( + 1.0 + - ((x * (x * x + self.prime_1 + self.prime_2) + self.prime_3) & 0x7FFFFFFF) + / 1073741824.0 + ) + + def smoothedNoise(self, x): + return self.noise(x) / 2.0 + self.noise(x - 1) / 4.0 + self.noise(x + 1) / 4.0 + + def interpolatedNoise(self, x): + int_x = int(x) + frac_x = x - int_x + + v1 = self.smoothedNoise(int_x) + v2 = self.smoothedNoise(int_x + 1) + + return self.cosineInterpolation(v1, v2, frac_x) + + def perlinNoise1D(self, x): + total = 0.0 + + for octave in range(self.number_of_octaves - 1): + frequency = math.pow(2, octave) + amplitude = math.pow(self.persistence, octave) + + total += self.interpolatedNoise(x * frequency) * amplitude + + return total + + @staticmethod + def isPrime(num): + for j in range(2, int(math.sqrt(num) + 1)): + if (num % j) == 0: + return False + return True + + @staticmethod + def createPrime(digits=10): + done = False + low = int("1" + "0" * (digits - 1)) + high = int("9" * digits) + + if low == 1: + low = 2 + + while not done: + num = random.randint(low, high) + if PerlinNoise.isPrime(num): + return num + + +def createObservationFile(report_step, observation, count, std=0.2): + with open("perlin_obs_%d.txt" % report_step, "w") as f: + + for index in range(count): + x = index / 8.0 + f.write("%f %f\n" % (observation.perlinNoise1D(x), std)) + + +def readParameters(filename): + params = {} + with open(filename, "r") as f: + for line in f: + key, value = line.split(":", 1) + params[key] = float(value) + + return params + + +if __name__ == "__main__": + count = 100 + + # primes = [] + # for p in range(128): + # primes.append(str(PerlinNoise.createPrime(7))) + # + # print(",".join(primes)) + + observations = { + 1: PerlinNoise(prime_1=15731, prime_2=789221, prime_3=1376312589), + 2: PerlinNoise(prime_1=8831, prime_2=1300237, prime_3=32416187567), + 3: PerlinNoise(prime_1=10657, prime_2=105767, prime_3=2902956923), + } + + for report_step in observations: + observation = observations[report_step] + # createObservationFile(report_step, observation, count) + + params = readParameters("perlin_params.txt") + + scale = params["SCALE"] + offset = params["OFFSET"] + octaves = int(round(params["OCTAVES"])) + persistence = params["PERSISTENCE"] + p1_index = int(round(params["PRIME_1"])) + p2_index = int(round(params["PRIME_2"])) + p3_index = int(round(params["PRIME_3"])) + + with open("perlin_%d.txt" % report_step, "w") as f: + P1 = PRIME_INDEX_1[p1_index] + P2 = PRIME_INDEX_2[p2_index] + P3 = PRIME_INDEX_3[p3_index] + # P1 = PerlinNoise.createPrime() + # P2 = PerlinNoise.createPrime() + # P3 = PerlinNoise.createPrime() + report_step_noise = PerlinNoise( + persistence=persistence, + number_of_octaves=octaves, + prime_1=P1, + prime_2=P2, + prime_3=P3, + ) + + for i in range(count): + x = i / 8.0 + obs = observation.perlinNoise1D(x) + noise = report_step_noise.perlinNoise1D(x) + f.write("%f\n" % (obs + offset + noise * scale)) diff --git a/libres/test-data/local/mini_ert/jobs/perlin_fail.py b/libres/test-data/local/mini_ert/jobs/perlin_fail.py new file mode 100755 index 00000000000..7fa0df5148d --- /dev/null +++ b/libres/test-data/local/mini_ert/jobs/perlin_fail.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +import os +import sys + +iens = None +if len(sys.argv) > 1: + iens = int(sys.argv[1]) + +numbers = [1, 2, 3, 4, 5, 6] +if iens in numbers: + random_report_step = (numbers.index(iens) % 3) + 1 + os.remove("perlin_%d.txt" % random_report_step) + + with open("perlin_fail.status", "w") as f: + f.write("Deleted report step: %d" % random_report_step) + +else: + with open("perlin_fail.status", "w") as f: + f.write("Did nothing!") diff --git a/libres/test-data/local/mini_ert/jobs/realization_fail.py b/libres/test-data/local/mini_ert/jobs/realization_fail.py new file mode 100755 index 00000000000..e1bd24c1d97 --- /dev/null +++ b/libres/test-data/local/mini_ert/jobs/realization_fail.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python +import sys + +iens = None +if len(sys.argv) > 1: + iens = int(sys.argv[1]) + +if iens is None or not iens in [2, 3, 5, 7]: + with open("realization.ok", "w") as f: + f.write("OK") diff --git a/libres/test-data/local/mini_ert/mini_config b/libres/test-data/local/mini_ert/mini_config new file mode 100644 index 00000000000..bce60389ef3 --- /dev/null +++ b/libres/test-data/local/mini_ert/mini_config @@ -0,0 +1,33 @@ +QUEUE_SYSTEM LOCAL + +JOBNAME MINI_%d + +DEFINE storage + +NUM_REALIZATIONS 10 + +RUNPATH /Run/realisation-%d/iter-%d +ENSPATH /ertensemble + +INSTALL_JOB AGGREGATOR jobs/AGGREGATOR +INSTALL_JOB PERLIN jobs/PERLIN + +FORWARD_MODEL PERLIN +FORWARD_MODEL AGGREGATOR + +TIME_MAP time_map +GEN_KW PERLIN_PARAM Template/PERLIN_TEMPLATE perlin_params.txt Parameters/PERLIN_PARAMS.txt +GEN_DATA PERLIN INPUT_FORMAT:ASCII RESULT_FILE:perlin_%d.txt REPORT_STEPS:1,2,3 + + +ENKF_MERGE_OBSERVATIONS TRUE + +LOG_LEVEL INFO +LOG_FILE log/log.txt +UPDATE_LOG_PATH log/UP + +DATA_KW CSV_OUTPUT_PATH custom_output.csv + +OBS_CONFIG Observations/observations + +GRID Refcase/CASE.EGRID \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/mini_config_define b/libres/test-data/local/mini_ert/mini_config_define new file mode 100644 index 00000000000..819215c73a9 --- /dev/null +++ b/libres/test-data/local/mini_ert/mini_config_define @@ -0,0 +1,34 @@ +QUEUE_SYSTEM LOCAL + +JOBNAME MINI_%d + +DEFINE storage + +NUM_REALIZATIONS 10 + +RUNPATH /Run/realisation-%d/iter-%d +ENSPATH /ertensemble + +INSTALL_JOB AGGREGATOR jobs/AGGREGATOR +INSTALL_JOB PERLIN jobs/PERLIN + +FORWARD_MODEL PERLIN +FORWARD_MODEL AGGREGATOR + +TIME_MAP time_map +GEN_KW PERLIN_PARAM Template/PERLIN_TEMPLATE perlin_params.txt Parameters/PERLIN_PARAMS.txt +GEN_DATA PERLIN INPUT_FORMAT:ASCII RESULT_FILE:perlin_%d.txt REPORT_STEPS:1,2,3 + +ENKF_MERGE_OBSERVATIONS TRUE + +LOG_LEVEL INFO +LOG_FILE log/log.txt +UPDATE_LOG_PATH log/UP + +DATA_KW CSV_OUTPUT_PATH custom_output.csv + +OBS_CONFIG Observations/observations + +GRID Refcase/CASE.EGRID + +DEFINE MY_PATH diff --git a/libres/test-data/local/mini_ert/mini_fail_config b/libres/test-data/local/mini_ert/mini_fail_config new file mode 100644 index 00000000000..319fc76b9e7 --- /dev/null +++ b/libres/test-data/local/mini_ert/mini_fail_config @@ -0,0 +1,35 @@ +QUEUE_SYSTEM LOCAL + +JOBNAME MINI_FAIL_%d + +DEFINE fail_storage + +NUM_REALIZATIONS 10 +MAX_SUBMIT 1 + +RUNPATH /Run/realisation-%d/iter-%d +ENSPATH /ertensemble + +INSTALL_JOB AGGREGATOR jobs/AGGREGATOR +INSTALL_JOB PERLIN jobs/PERLIN +INSTALL_JOB PERLIN_FAIL jobs/PERLIN_FAIL +INSTALL_JOB REALIZATION_FAIL jobs/REALIZATION_FAIL + +FORWARD_MODEL PERLIN +FORWARD_MODEL AGGREGATOR +FORWARD_MODEL PERLIN_FAIL +FORWARD_MODEL REALIZATION_FAIL + +TIME_MAP time_map +GEN_KW PERLIN_PARAM Template/PERLIN_TEMPLATE perlin_params.txt Parameters/PERLIN_PARAMS.txt +GEN_DATA PERLIN INPUT_FORMAT:ASCII RESULT_FILE:perlin_%d.txt REPORT_STEPS:1,2,3 + +ENKF_MERGE_OBSERVATIONS TRUE + +LOG_LEVEL INFO +LOG_FILE log/fail_log.txt +UPDATE_LOG_PATH log/UP + +DATA_KW CSV_OUTPUT_PATH custom_output.csv + +OBS_CONFIG Observations/observations diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-0/iter-0/aggregated.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-0/iter-0/aggregated.txt new file mode 100644 index 00000000000..de8aedc5622 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-0/iter-0/aggregated.txt @@ -0,0 +1,4 @@ +PERLIN_3 0.190479 +PERLIN_2 1.002630 +PERLIN_1 -0.167794 +STATE Positive \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-0/iter-0/aggregator.stderr b/libres/test-data/local/mini_ert/storage/Run/realisation-0/iter-0/aggregator.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-0/iter-0/aggregator.stdout b/libres/test-data/local/mini_ert/storage/Run/realisation-0/iter-0/aggregator.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-0/iter-0/parameters.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-0/iter-0/parameters.txt new file mode 100644 index 00000000000..142dbecaab7 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-0/iter-0/parameters.txt @@ -0,0 +1,7 @@ +PERLIN_PARAM:SCALE 0.249225 +PERLIN_PARAM:OFFSET 0.185709 +PERLIN_PARAM:PERSISTENCE 0.936132 +PERLIN_PARAM:OCTAVES 22.4008 +PERLIN_PARAM:PRIME_1 70.2373 +PERLIN_PARAM:PRIME_2 73.2881 +PERLIN_PARAM:PRIME_3 73.2557 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-0/iter-0/perlin.stderr b/libres/test-data/local/mini_ert/storage/Run/realisation-0/iter-0/perlin.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-0/iter-0/perlin.stdout b/libres/test-data/local/mini_ert/storage/Run/realisation-0/iter-0/perlin.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-0/iter-0/perlin_1.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-0/iter-0/perlin_1.txt new file mode 100644 index 00000000000..fec0fd107ef --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-0/iter-0/perlin_1.txt @@ -0,0 +1,100 @@ +-0.167794 +-0.533157 +-0.582564 +-0.581187 +-0.544394 +-0.617389 +-0.471483 +-0.570636 +-0.513164 +-0.094431 +-0.489078 +-0.363713 +-0.116233 +0.104267 +-0.185805 +-0.200709 +-0.150918 +-0.384297 +0.293484 +0.122875 +-0.157933 +0.066183 +0.043334 +0.083941 +0.237257 +0.510188 +0.247252 +0.238908 +-0.087547 +-0.330292 +-0.126747 +-0.035823 +-0.234230 +0.031125 +-0.470002 +0.277528 +0.333005 +0.208194 +0.329248 +0.183316 +0.030305 +0.292607 +0.207885 +-0.055542 +0.267287 +0.489294 +0.351695 +0.473816 +0.486845 +0.161185 +0.750709 +0.334430 +0.487895 +-0.083531 +0.631333 +0.189402 +0.293179 +0.416287 +0.071861 +0.343657 +0.530727 +0.295893 +0.707495 +0.705177 +0.364062 +-0.028729 +0.627459 +0.665644 +0.110239 +0.074890 +0.898952 +0.507702 +0.793900 +0.376955 +0.740932 +0.768612 +1.072116 +0.641052 +0.951385 +1.114482 +0.779685 +1.030575 +0.960609 +0.819774 +0.728183 +0.880717 +0.390042 +0.616292 +0.534190 +0.332035 +0.597337 +0.310784 +0.480825 +0.510514 +0.527712 +0.106739 +0.459289 +0.091994 +0.167754 +0.291767 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-0/iter-0/perlin_2.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-0/iter-0/perlin_2.txt new file mode 100644 index 00000000000..a4c8ae22814 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-0/iter-0/perlin_2.txt @@ -0,0 +1,100 @@ +1.002630 +0.655338 +0.637300 +0.558783 +0.509989 +0.408818 +0.436655 +0.277816 +0.357889 +0.712315 +0.209713 +0.181557 +0.234966 +0.360507 +0.067803 +0.095832 +0.180428 +-0.115810 +0.474014 +0.161206 +-0.221441 +0.025834 +-0.014213 +0.125937 +0.403895 +0.794609 +0.711298 +0.792749 +0.559236 +0.409434 +0.702774 +0.910823 +0.767699 +0.951166 +0.307889 +0.894615 +0.815080 +0.581146 +0.515508 +0.201538 +-0.007474 +0.272051 +0.196068 +-0.139775 +0.084654 +0.281485 +0.248246 +0.555883 +0.659358 +0.344063 +1.022407 +0.703206 +0.890554 +0.404788 +1.164953 +0.760065 +0.925984 +0.986234 +0.553851 +0.745948 +0.893678 +0.718616 +1.201408 +1.262021 +0.957160 +0.544980 +1.121517 +0.998898 +0.308482 +0.138122 +0.801380 +0.392979 +0.722289 +0.303537 +0.622059 +0.433718 +0.480651 +-0.144483 +0.012328 +0.129970 +-0.206634 +0.080509 +0.073475 +0.003830 +-0.027990 +0.147703 +-0.360170 +-0.159376 +-0.241837 +-0.406290 +-0.058059 +-0.199599 +0.184944 +0.465746 +0.716346 +0.404191 +0.768554 +0.294930 +0.274427 +0.462347 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-0/iter-0/perlin_3.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-0/iter-0/perlin_3.txt new file mode 100644 index 00000000000..13a10a9e5df --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-0/iter-0/perlin_3.txt @@ -0,0 +1,100 @@ +0.190479 +-0.262978 +-0.465308 +-0.621105 +-0.713738 +-0.781896 +-0.645797 +-0.734690 +-0.615233 +-0.181474 +-0.500371 +-0.375013 +-0.212762 +-0.077491 +-0.367569 +-0.306723 +-0.241907 +-0.432924 +0.314717 +0.254536 +0.131618 +0.477011 +0.476202 +0.498281 +0.657347 +0.936028 +0.654565 +0.605760 +0.338082 +0.190727 +0.442199 +0.602984 +0.446939 +0.753207 +0.301943 +1.023577 +1.019714 +0.773061 +0.805721 +0.709651 +0.597553 +0.944238 +0.917685 +0.547257 +0.715075 +0.782070 +0.537469 +0.655260 +0.690172 +0.323895 +0.846589 +0.260808 +0.196762 +-0.503788 +0.078186 +-0.393964 +-0.242415 +-0.177505 +-0.622958 +-0.465445 +-0.374328 +-0.668502 +-0.282796 +-0.297752 +-0.660453 +-0.993300 +-0.233057 +-0.139662 +-0.658737 +-0.694367 +0.096517 +-0.216566 +0.155464 +-0.212261 +0.141495 +0.047609 +0.314219 +-0.143014 +0.160029 +0.364682 +0.027328 +0.374773 +0.497251 +0.562515 +0.606365 +0.821117 +0.359765 +0.601682 +0.542912 +0.364089 +0.645058 +0.325327 +0.495087 +0.498606 +0.508514 +0.129097 +0.479090 +0.135126 +0.226554 +0.379889 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-0/iter-0/perlin_params.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-0/iter-0/perlin_params.txt new file mode 100644 index 00000000000..99b2ec3ff64 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-0/iter-0/perlin_params.txt @@ -0,0 +1,7 @@ +SCALE:0.249225 +OFFSET:0.185709 +PERSISTENCE:0.936132 +OCTAVES:22.4008 +PRIME_1:70.2373 +PRIME_2:73.2881 +PRIME_3:73.2557 \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-1/iter-0/aggregated.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-1/iter-0/aggregated.txt new file mode 100644 index 00000000000..c42ebc25736 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-1/iter-0/aggregated.txt @@ -0,0 +1,4 @@ +PERLIN_3 -0.584204 +PERLIN_2 0.227947 +PERLIN_1 -0.942477 +STATE Negative \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-1/iter-0/aggregator.stderr b/libres/test-data/local/mini_ert/storage/Run/realisation-1/iter-0/aggregator.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-1/iter-0/aggregator.stdout b/libres/test-data/local/mini_ert/storage/Run/realisation-1/iter-0/aggregator.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-1/iter-0/parameters.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-1/iter-0/parameters.txt new file mode 100644 index 00000000000..d931876083b --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-1/iter-0/parameters.txt @@ -0,0 +1,7 @@ +PERLIN_PARAM:SCALE 0.24275 +PERLIN_PARAM:OFFSET -0.29096 +PERLIN_PARAM:PERSISTENCE 0.860518 +PERLIN_PARAM:OCTAVES 14.7858 +PERLIN_PARAM:PRIME_1 11.8217 +PERLIN_PARAM:PRIME_2 48.9187 +PERLIN_PARAM:PRIME_3 89.5122 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-1/iter-0/perlin.stderr b/libres/test-data/local/mini_ert/storage/Run/realisation-1/iter-0/perlin.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-1/iter-0/perlin.stdout b/libres/test-data/local/mini_ert/storage/Run/realisation-1/iter-0/perlin.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-1/iter-0/perlin_1.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-1/iter-0/perlin_1.txt new file mode 100644 index 00000000000..20e4b557933 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-1/iter-0/perlin_1.txt @@ -0,0 +1,100 @@ +-0.942477 +-0.727071 +-0.723611 +-0.754719 +-0.671417 +-0.731608 +-0.692600 +-0.493644 +-0.676412 +-0.745697 +-0.692443 +-0.578927 +-0.488710 +-0.308417 +-0.198633 +-0.292521 +-0.451160 +-0.454948 +-0.565726 +-0.325299 +-0.477437 +-0.393874 +-0.366932 +-0.181596 +-0.382861 +-0.214575 +-0.343829 +-0.226514 +-0.320776 +-0.308425 +-0.487588 +-0.395820 +-0.742008 +-0.527934 +-0.774032 +-0.603036 +-0.837021 +-0.456712 +-0.387980 +-0.390885 +-0.477976 +-0.396114 +-0.440335 +-0.294043 +-0.320108 +-0.019041 +-0.013591 +-0.252420 +-0.275822 +-0.164093 +-0.136841 +-0.069380 +-0.145731 +-0.021543 +0.105226 +0.109063 +0.010749 +0.174611 +0.235996 +0.229778 +0.234829 +0.303725 +0.413435 +0.191701 +-0.016101 +0.137470 +0.169930 +-0.099989 +-0.129193 +-0.110471 +0.042198 +-0.048338 +-0.349474 +-0.098213 +0.162561 +0.313852 +0.400474 +0.538012 +0.475773 +0.571507 +0.342028 +0.299233 +0.316542 +0.349001 +0.187998 +0.323078 +0.237231 +0.120016 +-0.054434 +-0.139747 +0.160315 +-0.006853 +0.136442 +0.070848 +-0.273561 +-0.222518 +-0.288468 +-0.046234 +-0.175744 +-0.151171 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-1/iter-0/perlin_2.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-1/iter-0/perlin_2.txt new file mode 100644 index 00000000000..296be5b8cd9 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-1/iter-0/perlin_2.txt @@ -0,0 +1,100 @@ +0.227947 +0.461424 +0.496254 +0.385250 +0.382965 +0.294599 +0.215538 +0.354808 +0.194641 +0.061050 +0.006348 +-0.033658 +-0.137511 +-0.052176 +0.054974 +0.004020 +-0.119814 +-0.186461 +-0.385196 +-0.286967 +-0.540945 +-0.434223 +-0.424480 +-0.139600 +-0.216224 +0.069846 +0.120217 +0.327327 +0.326008 +0.431300 +0.341933 +0.550826 +0.259922 +0.392107 +0.003858 +0.014051 +-0.354946 +-0.083760 +-0.201720 +-0.372664 +-0.515754 +-0.416670 +-0.452153 +-0.378276 +-0.502741 +-0.226850 +-0.117039 +-0.170353 +-0.103309 +0.018785 +0.134857 +0.299396 +0.256928 +0.466775 +0.638846 +0.679726 +0.643554 +0.744558 +0.717986 +0.632070 +0.597780 +0.726447 +0.907347 +0.748545 +0.576996 +0.711178 +0.663988 +0.233265 +0.069050 +-0.047240 +-0.055375 +-0.163060 +-0.421085 +-0.171631 +0.043688 +-0.021042 +-0.190991 +-0.247524 +-0.463284 +-0.413005 +-0.644291 +-0.650833 +-0.570592 +-0.466943 +-0.568175 +-0.409936 +-0.512981 +-0.655652 +-0.830461 +-0.878072 +-0.495081 +-0.517236 +-0.159439 +0.026080 +-0.084928 +0.074934 +0.020797 +0.156703 +-0.069071 +0.019410 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-1/iter-0/perlin_3.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-1/iter-0/perlin_3.txt new file mode 100644 index 00000000000..d43e140291b --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-1/iter-0/perlin_3.txt @@ -0,0 +1,100 @@ +-0.584204 +-0.456892 +-0.606354 +-0.794637 +-0.840762 +-0.896115 +-0.866915 +-0.657698 +-0.778480 +-0.832740 +-0.703736 +-0.590227 +-0.585239 +-0.490175 +-0.380398 +-0.398535 +-0.542149 +-0.503575 +-0.544492 +-0.193638 +-0.187886 +0.016954 +0.065935 +0.232744 +0.037229 +0.211265 +0.063484 +0.140338 +0.104854 +0.212594 +0.081358 +0.242987 +-0.060839 +0.194148 +-0.002088 +0.143014 +-0.150313 +0.108155 +0.088492 +0.135449 +0.089272 +0.255517 +0.269465 +0.308757 +0.127679 +0.273734 +0.172184 +-0.070976 +-0.072495 +-0.001383 +-0.040962 +-0.143002 +-0.436864 +-0.441800 +-0.447921 +-0.474303 +-0.524845 +-0.419181 +-0.458823 +-0.579324 +-0.670226 +-0.660671 +-0.576856 +-0.811227 +-1.040616 +-0.827102 +-0.690587 +-0.905295 +-0.898169 +-0.879729 +-0.760237 +-0.772606 +-0.987910 +-0.687429 +-0.436876 +-0.407151 +-0.357423 +-0.246055 +-0.315583 +-0.178294 +-0.410329 +-0.356569 +-0.146816 +0.091742 +0.066180 +0.263478 +0.206954 +0.105406 +-0.045712 +-0.107693 +0.208035 +0.007690 +0.150704 +0.058939 +-0.292759 +-0.200161 +-0.268667 +-0.003101 +-0.116944 +-0.063049 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-1/iter-0/perlin_params.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-1/iter-0/perlin_params.txt new file mode 100644 index 00000000000..fb78e8c3e9b --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-1/iter-0/perlin_params.txt @@ -0,0 +1,7 @@ +SCALE:0.24275 +OFFSET:-0.29096 +PERSISTENCE:0.860518 +OCTAVES:14.7858 +PRIME_1:11.8217 +PRIME_2:48.9187 +PRIME_3:89.5122 \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-2/iter-0/aggregated.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-2/iter-0/aggregated.txt new file mode 100644 index 00000000000..39c71d12aab --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-2/iter-0/aggregated.txt @@ -0,0 +1,4 @@ +PERLIN_3 -0.294383 +PERLIN_2 0.517768 +PERLIN_1 -0.652656 +STATE Negative \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-2/iter-0/aggregator.stderr b/libres/test-data/local/mini_ert/storage/Run/realisation-2/iter-0/aggregator.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-2/iter-0/aggregator.stdout b/libres/test-data/local/mini_ert/storage/Run/realisation-2/iter-0/aggregator.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-2/iter-0/parameters.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-2/iter-0/parameters.txt new file mode 100644 index 00000000000..0850de9c61b --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-2/iter-0/parameters.txt @@ -0,0 +1,7 @@ +PERLIN_PARAM:SCALE 0.187883 +PERLIN_PARAM:OFFSET -0.512383 +PERLIN_PARAM:PERSISTENCE 0.850056 +PERLIN_PARAM:OCTAVES 39.9199 +PERLIN_PARAM:PRIME_1 76.7337 +PERLIN_PARAM:PRIME_2 54.1086 +PERLIN_PARAM:PRIME_3 16.0042 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-2/iter-0/perlin.stderr b/libres/test-data/local/mini_ert/storage/Run/realisation-2/iter-0/perlin.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-2/iter-0/perlin.stdout b/libres/test-data/local/mini_ert/storage/Run/realisation-2/iter-0/perlin.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-2/iter-0/perlin_1.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-2/iter-0/perlin_1.txt new file mode 100644 index 00000000000..122d4089eaa --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-2/iter-0/perlin_1.txt @@ -0,0 +1,100 @@ +-0.652656 +-0.856575 +-0.907574 +-0.921559 +-0.907052 +-0.798535 +-0.892001 +-0.898300 +-0.932647 +-0.887530 +-0.745258 +-0.774438 +-0.680597 +-0.707552 +-0.616984 +-0.784930 +-0.705796 +-0.672207 +-0.677662 +-0.517634 +-0.460960 +-0.512998 +-0.485822 +-0.377630 +-0.469687 +-0.463782 +-0.644529 +-0.631042 +-0.610238 +-0.645129 +-0.841183 +-0.813056 +-0.824549 +-0.985474 +-0.818724 +-1.021755 +-0.749654 +-0.643111 +-0.382287 +-0.319701 +-0.240893 +-0.293783 +-0.356379 +-0.315861 +-0.243741 +-0.061641 +-0.016073 +-0.078540 +-0.166452 +-0.204278 +-0.206156 +-0.339674 +-0.324650 +-0.218586 +-0.220018 +-0.365619 +-0.213129 +-0.161173 +-0.144854 +-0.088350 +-0.193806 +-0.111286 +-0.100984 +-0.235155 +-0.165413 +-0.278588 +-0.429381 +-0.254946 +-0.240835 +-0.227267 +-0.524615 +-0.423858 +-0.341032 +-0.369948 +-0.180422 +0.176827 +0.282667 +0.455645 +0.421888 +0.461117 +0.478802 +0.408361 +0.301001 +0.266791 +0.167743 +0.066362 +0.101881 +0.183729 +-0.037895 +0.191528 +0.077918 +0.193353 +0.109073 +0.001759 +-0.061599 +-0.001108 +-0.164077 +-0.253534 +-0.200586 +-0.275259 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-2/iter-0/perlin_2.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-2/iter-0/perlin_2.txt new file mode 100644 index 00000000000..6e35f0db005 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-2/iter-0/perlin_2.txt @@ -0,0 +1,100 @@ +0.517768 +0.331920 +0.312291 +0.218411 +0.147331 +0.227672 +0.016137 +-0.049848 +-0.061594 +-0.080783 +-0.046467 +-0.229168 +-0.329398 +-0.451311 +-0.363377 +-0.488389 +-0.374451 +-0.403720 +-0.497132 +-0.479302 +-0.524469 +-0.553347 +-0.543370 +-0.335634 +-0.303049 +-0.179361 +-0.180483 +-0.077200 +0.036546 +0.094596 +-0.011662 +0.133590 +0.177380 +-0.065433 +-0.040833 +-0.404669 +-0.267578 +-0.270158 +-0.196027 +-0.301479 +-0.278672 +-0.314339 +-0.368196 +-0.400094 +-0.426374 +-0.269450 +-0.119522 +0.003527 +0.006061 +-0.021400 +0.065542 +0.029102 +0.078009 +0.269733 +0.313603 +0.205045 +0.419677 +0.408773 +0.337136 +0.313942 +0.169145 +0.311436 +0.392928 +0.321689 +0.427685 +0.295121 +0.064678 +0.078309 +-0.042592 +-0.164036 +-0.622187 +-0.538581 +-0.412642 +-0.443366 +-0.299295 +-0.158067 +-0.308798 +-0.329891 +-0.517169 +-0.523395 +-0.507517 +-0.541705 +-0.586134 +-0.549153 +-0.588430 +-0.666651 +-0.648330 +-0.591939 +-0.813922 +-0.546797 +-0.577477 +-0.317029 +-0.186808 +-0.043009 +0.127034 +0.296344 +0.145188 +-0.050597 +-0.093913 +-0.104679 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-2/iter-0/perlin_3.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-2/iter-0/perlin_3.txt new file mode 100644 index 00000000000..1e03faf793f --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-2/iter-0/perlin_3.txt @@ -0,0 +1,100 @@ +-0.294383 +-0.586396 +-0.790317 +-0.961476 +-1.076396 +-0.963042 +-1.066316 +-1.062354 +-1.034715 +-0.974573 +-0.756551 +-0.785737 +-0.777126 +-0.889310 +-0.798749 +-0.890944 +-0.796785 +-0.720834 +-0.656429 +-0.385973 +-0.171410 +-0.102170 +-0.052955 +0.036709 +-0.049597 +-0.037941 +-0.237216 +-0.264190 +-0.184608 +-0.124110 +-0.272236 +-0.174249 +-0.143380 +-0.263391 +-0.046779 +-0.275706 +-0.062945 +-0.078243 +0.094185 +0.206634 +0.326354 +0.357848 +0.353421 +0.286938 +0.204047 +0.231135 +0.169701 +0.102903 +0.036875 +-0.041568 +-0.110276 +-0.413296 +-0.615784 +-0.638843 +-0.773164 +-0.948985 +-0.748723 +-0.754966 +-0.839672 +-0.897452 +-1.098861 +-1.075682 +-1.091275 +-1.238084 +-1.189928 +-1.243159 +-1.289897 +-1.060251 +-1.009811 +-0.996525 +-1.327050 +-1.148126 +-0.979468 +-0.959163 +-0.779859 +-0.544176 +-0.475229 +-0.328422 +-0.369468 +-0.288684 +-0.273556 +-0.247441 +-0.162358 +0.009532 +0.045926 +0.006763 +0.071604 +0.169119 +-0.029173 +0.223582 +0.125639 +0.207896 +0.123334 +-0.010150 +-0.080797 +0.021249 +-0.144276 +-0.210401 +-0.141786 +-0.187137 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-2/iter-0/perlin_params.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-2/iter-0/perlin_params.txt new file mode 100644 index 00000000000..a069d36ab89 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-2/iter-0/perlin_params.txt @@ -0,0 +1,7 @@ +SCALE:0.187883 +OFFSET:-0.512383 +PERSISTENCE:0.850056 +OCTAVES:39.9199 +PRIME_1:76.7337 +PRIME_2:54.1086 +PRIME_3:16.0042 \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-3/iter-0/aggregated.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-3/iter-0/aggregated.txt new file mode 100644 index 00000000000..b986f1b4814 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-3/iter-0/aggregated.txt @@ -0,0 +1,4 @@ +PERLIN_3 -0.871387 +PERLIN_2 -0.059236 +PERLIN_1 -1.229660 +STATE Negative \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-3/iter-0/aggregator.stderr b/libres/test-data/local/mini_ert/storage/Run/realisation-3/iter-0/aggregator.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-3/iter-0/aggregator.stdout b/libres/test-data/local/mini_ert/storage/Run/realisation-3/iter-0/aggregator.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-3/iter-0/parameters.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-3/iter-0/parameters.txt new file mode 100644 index 00000000000..a7472afccff --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-3/iter-0/parameters.txt @@ -0,0 +1,7 @@ +PERLIN_PARAM:SCALE 0.542054 +PERLIN_PARAM:OFFSET -0.936072 +PERLIN_PARAM:PERSISTENCE 0.66513 +PERLIN_PARAM:OCTAVES 59.6891 +PERLIN_PARAM:PRIME_1 58.3958 +PERLIN_PARAM:PRIME_2 51.6271 +PERLIN_PARAM:PRIME_3 37.3286 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-3/iter-0/perlin.stderr b/libres/test-data/local/mini_ert/storage/Run/realisation-3/iter-0/perlin.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-3/iter-0/perlin.stdout b/libres/test-data/local/mini_ert/storage/Run/realisation-3/iter-0/perlin.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-3/iter-0/perlin_1.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-3/iter-0/perlin_1.txt new file mode 100644 index 00000000000..1f0aabd1931 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-3/iter-0/perlin_1.txt @@ -0,0 +1,100 @@ +-1.229660 +-1.403213 +-1.494882 +-1.481222 +-1.545058 +-1.605346 +-1.466829 +-1.460689 +-1.589462 +-1.462504 +-1.586371 +-1.419953 +-1.184666 +-1.064367 +-1.081730 +-1.130531 +-1.307146 +-1.330892 +-1.147255 +-1.230522 +-1.290812 +-1.243312 +-1.053225 +-0.837831 +-0.815416 +-0.794856 +-0.780118 +-0.867346 +-0.868595 +-0.844661 +-0.949974 +-1.135521 +-1.278698 +-1.237066 +-1.353473 +-1.274754 +-1.024540 +-1.062374 +-1.018191 +-0.989482 +-1.082235 +-1.083477 +-1.111900 +-0.884140 +-0.804378 +-0.649785 +-0.414051 +-0.453314 +-0.426393 +-0.376335 +-0.433952 +-0.369796 +-0.319725 +-0.318209 +-0.380495 +-0.274543 +-0.410077 +-0.265464 +-0.276032 +-0.293185 +-0.259478 +-0.307654 +-0.469020 +-0.606702 +-0.727993 +-0.657698 +-0.738949 +-0.779068 +-0.929409 +-0.925120 +-0.888902 +-0.818882 +-0.691387 +-0.806321 +-0.774223 +-0.725947 +-0.616177 +-0.575377 +-0.546778 +-0.619225 +-0.739783 +-0.805058 +-0.854443 +-1.003113 +-0.978222 +-0.724971 +-0.804323 +-0.920953 +-0.973738 +-0.936418 +-0.905518 +-0.717744 +-0.616881 +-0.785126 +-0.782606 +-0.680625 +-0.739233 +-0.708171 +-0.640854 +-0.813288 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-3/iter-0/perlin_2.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-3/iter-0/perlin_2.txt new file mode 100644 index 00000000000..58ac16bef25 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-3/iter-0/perlin_2.txt @@ -0,0 +1,100 @@ +-0.059236 +-0.214718 +-0.275017 +-0.341253 +-0.490676 +-0.579139 +-0.558691 +-0.612236 +-0.718408 +-0.655757 +-0.887580 +-0.874683 +-0.833466 +-0.808127 +-0.828123 +-0.833990 +-0.975800 +-1.062405 +-0.966725 +-1.192190 +-1.354320 +-1.283661 +-1.110772 +-0.795835 +-0.648779 +-0.510435 +-0.316072 +-0.313505 +-0.221812 +-0.104936 +-0.120453 +-0.188875 +-0.276769 +-0.317026 +-0.575582 +-0.657667 +-0.542465 +-0.689421 +-0.831931 +-0.971260 +-1.120014 +-1.104033 +-1.123718 +-0.968373 +-0.987011 +-0.857594 +-0.517499 +-0.371248 +-0.253880 +-0.193457 +-0.162254 +-0.001019 +0.082934 +0.170110 +0.153126 +0.296120 +0.222728 +0.304483 +0.205958 +0.109107 +0.103474 +0.115069 +0.024892 +-0.049858 +-0.134896 +-0.083989 +-0.244890 +-0.445814 +-0.731166 +-0.861888 +-0.986474 +-0.933604 +-0.762998 +-0.879739 +-0.893096 +-1.060842 +-1.207642 +-1.360912 +-1.485835 +-1.603737 +-1.726102 +-1.755124 +-1.741578 +-1.819057 +-1.734395 +-1.457985 +-1.554534 +-1.696621 +-1.749765 +-1.674743 +-1.560913 +-1.228126 +-0.912762 +-0.829894 +-0.593973 +-0.383174 +-0.429968 +-0.505235 +-0.534181 +-0.642708 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-3/iter-0/perlin_3.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-3/iter-0/perlin_3.txt new file mode 100644 index 00000000000..3e287032343 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-3/iter-0/perlin_3.txt @@ -0,0 +1,100 @@ +-0.871387 +-1.133034 +-1.377625 +-1.521140 +-1.714403 +-1.769853 +-1.641143 +-1.624743 +-1.691530 +-1.549547 +-1.597664 +-1.431252 +-1.281194 +-1.246125 +-1.263495 +-1.236545 +-1.398135 +-1.379519 +-1.126021 +-1.098861 +-1.001261 +-0.832484 +-0.620357 +-0.423492 +-0.395326 +-0.369015 +-0.372806 +-0.500494 +-0.442966 +-0.323643 +-0.381027 +-0.496714 +-0.597530 +-0.514984 +-0.581528 +-0.528705 +-0.337832 +-0.497506 +-0.541718 +-0.463147 +-0.514987 +-0.431846 +-0.402100 +-0.281341 +-0.356591 +-0.357009 +-0.228277 +-0.271871 +-0.223066 +-0.213625 +-0.338073 +-0.443417 +-0.610858 +-0.738466 +-0.933641 +-0.857909 +-0.945672 +-0.859256 +-0.970850 +-1.102287 +-1.164532 +-1.272049 +-1.459311 +-1.609631 +-1.752508 +-1.622269 +-1.599465 +-1.584374 +-1.698385 +-1.694377 +-1.691337 +-1.543150 +-1.329823 +-1.395537 +-1.373660 +-1.446951 +-1.374073 +-1.359444 +-1.338134 +-1.369026 +-1.492140 +-1.460860 +-1.317801 +-1.260372 +-1.100040 +-0.784571 +-0.834600 +-0.935563 +-0.965016 +-0.904364 +-0.857797 +-0.703201 +-0.602620 +-0.797035 +-0.801804 +-0.658268 +-0.719432 +-0.665039 +-0.582054 +-0.725166 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-3/iter-0/perlin_params.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-3/iter-0/perlin_params.txt new file mode 100644 index 00000000000..f51328cc149 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-3/iter-0/perlin_params.txt @@ -0,0 +1,7 @@ +SCALE:0.542054 +OFFSET:-0.936072 +PERSISTENCE:0.66513 +OCTAVES:59.6891 +PRIME_1:58.3958 +PRIME_2:51.6271 +PRIME_3:37.3286 \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-4/iter-0/aggregated.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-4/iter-0/aggregated.txt new file mode 100644 index 00000000000..35536135e93 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-4/iter-0/aggregated.txt @@ -0,0 +1,4 @@ +PERLIN_3 0.585905 +PERLIN_2 1.398056 +PERLIN_1 0.227632 +STATE Positive \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-4/iter-0/aggregator.stderr b/libres/test-data/local/mini_ert/storage/Run/realisation-4/iter-0/aggregator.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-4/iter-0/aggregator.stdout b/libres/test-data/local/mini_ert/storage/Run/realisation-4/iter-0/aggregator.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-4/iter-0/parameters.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-4/iter-0/parameters.txt new file mode 100644 index 00000000000..43486a893aa --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-4/iter-0/parameters.txt @@ -0,0 +1,7 @@ +PERLIN_PARAM:SCALE 0.434448 +PERLIN_PARAM:OFFSET 0.831105 +PERLIN_PARAM:PERSISTENCE 0.630867 +PERLIN_PARAM:OCTAVES 8.00407 +PERLIN_PARAM:PRIME_1 85.4235 +PERLIN_PARAM:PRIME_2 21.3039 +PERLIN_PARAM:PRIME_3 104.858 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-4/iter-0/perlin.stderr b/libres/test-data/local/mini_ert/storage/Run/realisation-4/iter-0/perlin.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-4/iter-0/perlin.stdout b/libres/test-data/local/mini_ert/storage/Run/realisation-4/iter-0/perlin.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-4/iter-0/perlin_1.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-4/iter-0/perlin_1.txt new file mode 100644 index 00000000000..7ea431597a2 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-4/iter-0/perlin_1.txt @@ -0,0 +1,100 @@ +0.227632 +0.182419 +0.144134 +0.139476 +0.177808 +0.254702 +0.194959 +0.196247 +0.200894 +0.254938 +0.395923 +0.419362 +0.480212 +0.525709 +0.561013 +0.552975 +0.519655 +0.497365 +0.577627 +0.735132 +0.817150 +0.830304 +0.810482 +0.786231 +0.748806 +0.689116 +0.617421 +0.687358 +0.594608 +0.517947 +0.548341 +0.523440 +0.419855 +0.405486 +0.372455 +0.434415 +0.562727 +0.733549 +0.970895 +1.160018 +1.169762 +1.111676 +1.111566 +1.106991 +1.105426 +1.213650 +1.175046 +1.097622 +1.076414 +1.020738 +0.967021 +0.935108 +0.968248 +1.090303 +1.177846 +1.141175 +1.035475 +1.007184 +1.064612 +1.220228 +1.325731 +1.365550 +1.388177 +1.239332 +1.196752 +1.250964 +1.102255 +1.021732 +1.011481 +1.046014 +1.021100 +1.037203 +0.983562 +1.047336 +1.209270 +1.527246 +1.651524 +1.778722 +1.953437 +1.897861 +1.919134 +1.832772 +1.722330 +1.690519 +1.644280 +1.609237 +1.499923 +1.390382 +1.225122 +1.258604 +1.269723 +1.248156 +1.156463 +1.014345 +0.967288 +0.922071 +0.964769 +0.867821 +0.886340 +0.661428 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-4/iter-0/perlin_2.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-4/iter-0/perlin_2.txt new file mode 100644 index 00000000000..b747326d4e0 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-4/iter-0/perlin_2.txt @@ -0,0 +1,100 @@ +1.398056 +1.370914 +1.363998 +1.279446 +1.232190 +1.280908 +1.103097 +1.044700 +1.071948 +1.061684 +1.094715 +0.964632 +0.831411 +0.781949 +0.814620 +0.849516 +0.851001 +0.765851 +0.758157 +0.773463 +0.753641 +0.789955 +0.752934 +0.828227 +0.915443 +0.973537 +1.081467 +1.241200 +1.241392 +1.257672 +1.377862 +1.470086 +1.421784 +1.325527 +1.150346 +1.051501 +1.044802 +1.106502 +1.157155 +1.178239 +1.131983 +1.091121 +1.099748 +1.022758 +0.922794 +1.005841 +1.071597 +1.179689 +1.248927 +1.203617 +1.238719 +1.303884 +1.370907 +1.578622 +1.711466 +1.711839 +1.668280 +1.577131 +1.546602 +1.622520 +1.688682 +1.788272 +1.882089 +1.796176 +1.789849 +1.824672 +1.596314 +1.354986 +1.209724 +1.109246 +0.923528 +0.922481 +0.911951 +0.973918 +1.090397 +1.192352 +1.060059 +0.993187 +1.014380 +0.913349 +0.932815 +0.882706 +0.835195 +0.874574 +0.888107 +0.876224 +0.749712 +0.614713 +0.449095 +0.520278 +0.614327 +0.737773 +0.860582 +0.969577 +1.155922 +1.219523 +1.274034 +1.070757 +0.993013 +0.832009 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-4/iter-0/perlin_3.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-4/iter-0/perlin_3.txt new file mode 100644 index 00000000000..c5dd1d518a9 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-4/iter-0/perlin_3.txt @@ -0,0 +1,100 @@ +0.585905 +0.452598 +0.261390 +0.099559 +0.008463 +0.090195 +0.020645 +0.032193 +0.098826 +0.167894 +0.384631 +0.408063 +0.383683 +0.343951 +0.379248 +0.446961 +0.428666 +0.448737 +0.598860 +0.866793 +1.106700 +1.241132 +1.243349 +1.200570 +1.168896 +1.114956 +1.024733 +1.054210 +1.020238 +1.038965 +1.117287 +1.162247 +1.101024 +1.127568 +1.144400 +1.180464 +1.249435 +1.298417 +1.447368 +1.686352 +1.737009 +1.763308 +1.821366 +1.709790 +1.553214 +1.506426 +1.360820 +1.279066 +1.279741 +1.183449 +1.062901 +0.861486 +0.677115 +0.670046 +0.624699 +0.557809 +0.499881 +0.413392 +0.369794 +0.411126 +0.420676 +0.401154 +0.397886 +0.236404 +0.172236 +0.286392 +0.241739 +0.216426 +0.242506 +0.276757 +0.218665 +0.312935 +0.345126 +0.458120 +0.609833 +0.806243 +0.893627 +0.994656 +1.162081 +1.148060 +1.166777 +1.176970 +1.258971 +1.433259 +1.522462 +1.549638 +1.469646 +1.375772 +1.233844 +1.290657 +1.317444 +1.262699 +1.170724 +1.002436 +0.948090 +0.944428 +0.984570 +0.910954 +0.945140 +0.749551 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-4/iter-0/perlin_params.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-4/iter-0/perlin_params.txt new file mode 100644 index 00000000000..324cbda2ee5 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-4/iter-0/perlin_params.txt @@ -0,0 +1,7 @@ +SCALE:0.434448 +OFFSET:0.831105 +PERSISTENCE:0.630867 +OCTAVES:8.00407 +PRIME_1:85.4235 +PRIME_2:21.3039 +PRIME_3:104.858 \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-5/iter-0/aggregated.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-5/iter-0/aggregated.txt new file mode 100644 index 00000000000..72e58cbf645 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-5/iter-0/aggregated.txt @@ -0,0 +1,4 @@ +PERLIN_3 0.638889 +PERLIN_2 1.451039 +PERLIN_1 0.280615 +STATE Positive \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-5/iter-0/aggregator.stderr b/libres/test-data/local/mini_ert/storage/Run/realisation-5/iter-0/aggregator.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-5/iter-0/aggregator.stdout b/libres/test-data/local/mini_ert/storage/Run/realisation-5/iter-0/aggregator.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-5/iter-0/parameters.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-5/iter-0/parameters.txt new file mode 100644 index 00000000000..463f5c41d85 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-5/iter-0/parameters.txt @@ -0,0 +1,7 @@ +PERLIN_PARAM:SCALE 0.72828 +PERLIN_PARAM:OFFSET 0.48251 +PERLIN_PARAM:PERSISTENCE 0.878696 +PERLIN_PARAM:OCTAVES 62.5026 +PERLIN_PARAM:PRIME_1 10.3564 +PERLIN_PARAM:PRIME_2 34.9666 +PERLIN_PARAM:PRIME_3 46.2883 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-5/iter-0/perlin.stderr b/libres/test-data/local/mini_ert/storage/Run/realisation-5/iter-0/perlin.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-5/iter-0/perlin.stdout b/libres/test-data/local/mini_ert/storage/Run/realisation-5/iter-0/perlin.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-5/iter-0/perlin_1.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-5/iter-0/perlin_1.txt new file mode 100644 index 00000000000..11da497a094 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-5/iter-0/perlin_1.txt @@ -0,0 +1,100 @@ +0.280615 +0.140016 +0.101814 +-0.129046 +0.108540 +0.255758 +-0.136433 +0.402995 +0.060006 +0.243033 +0.270699 +1.032076 +-0.013904 +-0.123877 +0.661223 +0.260982 +0.218027 +1.382060 +0.399264 +0.483361 +0.472825 +-0.400746 +1.339330 +0.692467 +0.043042 +0.542935 +-0.239154 +0.322095 +0.568886 +-0.253825 +0.069002 +0.151587 +-0.060719 +0.071557 +1.232762 +0.806173 +0.197592 +0.775339 +0.485706 +-0.340968 +0.563185 +-0.301442 +-0.470657 +0.391865 +1.604934 +1.366571 +0.978296 +0.533236 +0.199409 +0.640021 +0.724001 +0.620437 +-0.062286 +0.448127 +0.681424 +0.211811 +0.962468 +0.569284 +0.153403 +0.463251 +0.717336 +1.047732 +0.879313 +0.557671 +0.589243 +0.475895 +0.668305 +1.261389 +1.992548 +0.514705 +1.480108 +0.964709 +0.672593 +0.541782 +1.389203 +0.533862 +1.237538 +0.892042 +0.376384 +0.676731 +1.374955 +1.291646 +0.276911 +0.706050 +0.032507 +1.074094 +0.914922 +1.747032 +2.087744 +1.287205 +1.735100 +1.305039 +1.283753 +0.989592 +0.686460 +0.373096 +0.309114 +0.349820 +0.814016 +0.915557 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-5/iter-0/perlin_2.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-5/iter-0/perlin_2.txt new file mode 100644 index 00000000000..1a12f1d5e3c --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-5/iter-0/perlin_2.txt @@ -0,0 +1,100 @@ +1.451039 +1.328511 +1.321678 +1.010924 +1.162923 +1.281965 +0.771705 +1.251448 +0.931060 +1.049780 +0.969490 +1.577345 +0.337295 +0.132363 +0.914830 +0.557523 +0.549373 +1.650547 +0.579795 +0.521693 +0.409316 +-0.441095 +1.281782 +0.734463 +0.209679 +0.827356 +0.224891 +0.875937 +1.215669 +0.485900 +0.898524 +1.098233 +0.941210 +0.991598 +2.010653 +1.423259 +0.679667 +1.148292 +0.671966 +-0.322746 +0.525407 +-0.321998 +-0.482474 +0.307632 +1.422302 +1.158762 +0.874847 +0.615302 +0.371922 +0.822899 +0.995699 +0.989214 +0.340374 +0.936446 +1.215045 +0.782475 +1.595273 +1.139231 +0.635393 +0.865543 +1.080287 +1.470455 +1.373225 +1.114515 +1.182340 +1.049604 +1.162364 +1.594643 +2.190791 +0.577937 +1.382536 +0.849987 +0.600982 +0.468364 +1.270330 +0.198968 +0.646073 +0.106506 +-0.562673 +-0.307781 +0.388636 +0.341580 +-0.610223 +-0.109894 +-0.723666 +0.341080 +0.164710 +0.971363 +1.311717 +0.548880 +1.079704 +0.794657 +0.987872 +0.944824 +0.875093 +0.670548 +0.618379 +0.552756 +0.920689 +1.086138 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-5/iter-0/perlin_3.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-5/iter-0/perlin_3.txt new file mode 100644 index 00000000000..4b5a132c2c5 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-5/iter-0/perlin_3.txt @@ -0,0 +1,100 @@ +0.638889 +0.410195 +0.219070 +-0.168964 +-0.060804 +0.091251 +-0.310748 +0.238941 +-0.042062 +0.155990 +0.259406 +1.020776 +-0.110433 +-0.305635 +0.479458 +0.154968 +0.127038 +1.333433 +0.420498 +0.615022 +0.762375 +0.010082 +1.772197 +1.106807 +0.463132 +0.968775 +0.168158 +0.688947 +0.994515 +0.267193 +0.637949 +0.790394 +0.620450 +0.793639 +2.004706 +1.552222 +0.884301 +1.340207 +0.962178 +0.185367 +1.130433 +0.350189 +0.239143 +0.994664 +2.052722 +1.659346 +1.164070 +0.714679 +0.402736 +0.802732 +0.819881 +0.546816 +-0.353419 +0.027870 +0.128278 +-0.371555 +0.426874 +-0.024508 +-0.541415 +-0.345851 +-0.187719 +0.083337 +-0.110978 +-0.445258 +-0.435272 +-0.488676 +-0.192211 +0.456083 +1.223572 +-0.254552 +0.677673 +0.240441 +0.034157 +-0.047433 +0.789765 +-0.187141 +0.479641 +0.107975 +-0.414972 +-0.073069 +0.622598 +0.635844 +-0.186447 +0.448791 +-0.089311 +1.014494 +0.884645 +1.732422 +2.096466 +1.319259 +1.782821 +1.319583 +1.298015 +0.977683 +0.667262 +0.395453 +0.328915 +0.392952 +0.872816 +1.003679 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-5/iter-0/perlin_params.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-5/iter-0/perlin_params.txt new file mode 100644 index 00000000000..6ffe51857b0 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-5/iter-0/perlin_params.txt @@ -0,0 +1,7 @@ +SCALE:0.72828 +OFFSET:0.48251 +PERSISTENCE:0.878696 +OCTAVES:62.5026 +PRIME_1:10.3564 +PRIME_2:34.9666 +PRIME_3:46.2883 \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-6/iter-0/aggregated.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-6/iter-0/aggregated.txt new file mode 100644 index 00000000000..07f12f2a08d --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-6/iter-0/aggregated.txt @@ -0,0 +1,4 @@ +PERLIN_3 0.870550 +PERLIN_2 1.682701 +PERLIN_1 0.512277 +STATE Positive \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-6/iter-0/aggregator.stderr b/libres/test-data/local/mini_ert/storage/Run/realisation-6/iter-0/aggregator.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-6/iter-0/aggregator.stdout b/libres/test-data/local/mini_ert/storage/Run/realisation-6/iter-0/aggregator.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-6/iter-0/parameters.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-6/iter-0/parameters.txt new file mode 100644 index 00000000000..04dc1adb802 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-6/iter-0/parameters.txt @@ -0,0 +1,7 @@ +PERLIN_PARAM:SCALE 0.105108 +PERLIN_PARAM:OFFSET 0.913379 +PERLIN_PARAM:PERSISTENCE 0.962764 +PERLIN_PARAM:OCTAVES 54.2875 +PERLIN_PARAM:PRIME_1 12.1353 +PERLIN_PARAM:PRIME_2 108.225 +PERLIN_PARAM:PRIME_3 58.2668 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-6/iter-0/perlin.stderr b/libres/test-data/local/mini_ert/storage/Run/realisation-6/iter-0/perlin.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-6/iter-0/perlin.stdout b/libres/test-data/local/mini_ert/storage/Run/realisation-6/iter-0/perlin.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-6/iter-0/perlin_1.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-6/iter-0/perlin_1.txt new file mode 100644 index 00000000000..309c31a80bd --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-6/iter-0/perlin_1.txt @@ -0,0 +1,100 @@ +0.512277 +0.536404 +0.520619 +0.520234 +0.561383 +0.536841 +0.593527 +0.431159 +0.579716 +0.595432 +0.613877 +0.832759 +0.851509 +0.830369 +0.752398 +0.701988 +0.854139 +0.813144 +0.848016 +0.675839 +0.919173 +1.078956 +1.165071 +1.045397 +1.099168 +1.049976 +0.934854 +0.813458 +0.777245 +0.747191 +0.680688 +0.492784 +0.756629 +0.639112 +0.677362 +0.560299 +0.783620 +0.757035 +0.781494 +1.092201 +1.102767 +1.052654 +1.207713 +1.115009 +1.380213 +1.620005 +1.355587 +1.358326 +1.365758 +1.049275 +1.264629 +1.182026 +1.240226 +1.329804 +1.210895 +1.340758 +1.160254 +1.099240 +1.243394 +1.267952 +1.359115 +1.457122 +1.219766 +1.225781 +1.439935 +1.367284 +1.242810 +1.155161 +1.278395 +1.180081 +1.125988 +1.131542 +1.239492 +1.086901 +1.273243 +1.532824 +1.482385 +1.810195 +1.887451 +1.774089 +1.870171 +1.843973 +1.696110 +1.740468 +1.791604 +1.614504 +1.582554 +1.758357 +1.634232 +1.684106 +1.786180 +1.619646 +1.484936 +1.430948 +1.379634 +1.352505 +1.382218 +1.487943 +1.057155 +0.971585 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-6/iter-0/perlin_2.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-6/iter-0/perlin_2.txt new file mode 100644 index 00000000000..463edcab273 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-6/iter-0/perlin_2.txt @@ -0,0 +1,100 @@ +1.682701 +1.724900 +1.740484 +1.660204 +1.615765 +1.563048 +1.501665 +1.279611 +1.450770 +1.402179 +1.312668 +1.378029 +1.202709 +1.086610 +1.006006 +0.998528 +1.185484 +1.081631 +1.028546 +0.714171 +0.855664 +1.038607 +1.107524 +1.087393 +1.265806 +1.334397 +1.398900 +1.367299 +1.424028 +1.486916 +1.510209 +1.439430 +1.758558 +1.559152 +1.455253 +1.177386 +1.265695 +1.129987 +0.967754 +1.110422 +1.064988 +1.032099 +1.195895 +1.030776 +1.197580 +1.412196 +1.252139 +1.440393 +1.538272 +1.232153 +1.536327 +1.550802 +1.642885 +1.818122 +1.744516 +1.911422 +1.793059 +1.669187 +1.725384 +1.670243 +1.722066 +1.879845 +1.713679 +1.782625 +2.033032 +1.940992 +1.736869 +1.488416 +1.476638 +1.243313 +1.028416 +1.016820 +1.167881 +1.013483 +1.154370 +1.197929 +0.890921 +1.024660 +0.948394 +0.789577 +0.883852 +0.893907 +0.808975 +0.924524 +1.035431 +0.881490 +0.832342 +0.982689 +0.858205 +0.945780 +1.130784 +1.109264 +1.189055 +1.386180 +1.568267 +1.649957 +1.691483 +1.690880 +1.163828 +1.142166 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-6/iter-0/perlin_3.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-6/iter-0/perlin_3.txt new file mode 100644 index 00000000000..bb7c0090329 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-6/iter-0/perlin_3.txt @@ -0,0 +1,100 @@ +0.870550 +0.806583 +0.637876 +0.480317 +0.392038 +0.372334 +0.419213 +0.267105 +0.477648 +0.508389 +0.602584 +0.821460 +0.754981 +0.648612 +0.570634 +0.595973 +0.763150 +0.764517 +0.869249 +0.807500 +1.208723 +1.489784 +1.597939 +1.459736 +1.519258 +1.475816 +1.342167 +1.180310 +1.202874 +1.268209 +1.249634 +1.131591 +1.437798 +1.361194 +1.449307 +1.306349 +1.470329 +1.321902 +1.257966 +1.618535 +1.670015 +1.704286 +1.917513 +1.717808 +1.828000 +1.912781 +1.541362 +1.539769 +1.569085 +1.211985 +1.360508 +1.108404 +0.949092 +0.909547 +0.657749 +0.757392 +0.624659 +0.505448 +0.548575 +0.458850 +0.454060 +0.492727 +0.229476 +0.222852 +0.415420 +0.402712 +0.382294 +0.349856 +0.509419 +0.410824 +0.323553 +0.407274 +0.601056 +0.497685 +0.673805 +0.811821 +0.724489 +1.026129 +1.096095 +1.024289 +1.117814 +1.188171 +1.232752 +1.483209 +1.669786 +1.554904 +1.552277 +1.743747 +1.642953 +1.716159 +1.833900 +1.634190 +1.499198 +1.419039 +1.360436 +1.374862 +1.402018 +1.531076 +1.115955 +1.059708 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-6/iter-0/perlin_params.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-6/iter-0/perlin_params.txt new file mode 100644 index 00000000000..29d18ec8995 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-6/iter-0/perlin_params.txt @@ -0,0 +1,7 @@ +SCALE:0.105108 +OFFSET:0.913379 +PERSISTENCE:0.962764 +OCTAVES:54.2875 +PRIME_1:12.1353 +PRIME_2:108.225 +PRIME_3:58.2668 \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-7/iter-0/aggregated.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-7/iter-0/aggregated.txt new file mode 100644 index 00000000000..6b3b4204d7b --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-7/iter-0/aggregated.txt @@ -0,0 +1,4 @@ +PERLIN_3 -0.223092 +PERLIN_2 0.589059 +PERLIN_1 -0.581365 +STATE Negative \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-7/iter-0/aggregator.stderr b/libres/test-data/local/mini_ert/storage/Run/realisation-7/iter-0/aggregator.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-7/iter-0/aggregator.stdout b/libres/test-data/local/mini_ert/storage/Run/realisation-7/iter-0/aggregator.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-7/iter-0/parameters.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-7/iter-0/parameters.txt new file mode 100644 index 00000000000..0fec4832fd0 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-7/iter-0/parameters.txt @@ -0,0 +1,7 @@ +PERLIN_PARAM:SCALE 0.100905 +PERLIN_PARAM:OFFSET 0.749785 +PERLIN_PARAM:PERSISTENCE 0.960394 +PERLIN_PARAM:OCTAVES 27.0099 +PERLIN_PARAM:PRIME_1 40.6138 +PERLIN_PARAM:PRIME_2 61.0175 +PERLIN_PARAM:PRIME_3 87.8034 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-7/iter-0/perlin.stderr b/libres/test-data/local/mini_ert/storage/Run/realisation-7/iter-0/perlin.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-7/iter-0/perlin.stdout b/libres/test-data/local/mini_ert/storage/Run/realisation-7/iter-0/perlin.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-7/iter-0/perlin_1.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-7/iter-0/perlin_1.txt new file mode 100644 index 00000000000..f4ab4143099 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-7/iter-0/perlin_1.txt @@ -0,0 +1,100 @@ +-0.581365 +0.196529 +0.251092 +0.129792 +0.331063 +0.243506 +0.276892 +0.080451 +0.396119 +0.359775 +0.367571 +0.375403 +0.583877 +0.600213 +0.433206 +0.667000 +0.730207 +0.597004 +0.652034 +0.663611 +0.746119 +0.692967 +0.730701 +0.876335 +0.892405 +0.727578 +0.723739 +0.564331 +0.468088 +0.488808 +0.664805 +0.660028 +0.630792 +0.524399 +0.474312 +0.443139 +0.580488 +0.481039 +0.800572 +1.001564 +0.911524 +0.901357 +0.788429 +0.858484 +0.905262 +1.091118 +1.153703 +1.092191 +1.125297 +0.931559 +0.887913 +0.949225 +1.000548 +1.157858 +0.916321 +0.916039 +0.825100 +0.963569 +0.946300 +1.325529 +1.317807 +1.051895 +1.371837 +1.120878 +1.290153 +1.132642 +1.106899 +0.951717 +1.053646 +1.097485 +0.993622 +1.071263 +1.003207 +1.060272 +0.945896 +1.081059 +1.483716 +1.639907 +1.778137 +1.743444 +1.658571 +1.670091 +1.528567 +1.360096 +1.348333 +1.247859 +1.311556 +1.223970 +1.139654 +1.236625 +1.220851 +1.388255 +1.244178 +1.146154 +1.093360 +0.984612 +1.125281 +1.104519 +0.927790 +0.740832 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-7/iter-0/perlin_2.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-7/iter-0/perlin_2.txt new file mode 100644 index 00000000000..3b2079e9c1b --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-7/iter-0/perlin_2.txt @@ -0,0 +1,100 @@ +0.589059 +1.385024 +1.470957 +1.269762 +1.385446 +1.269712 +1.185030 +0.928904 +1.267172 +1.166521 +1.066362 +0.920673 +0.935076 +0.856454 +0.686814 +0.963540 +1.061552 +0.865491 +0.832564 +0.701943 +0.682610 +0.652618 +0.673153 +0.918331 +1.059042 +1.011999 +1.187784 +1.118172 +1.114871 +1.228534 +1.494326 +1.606674 +1.632721 +1.444440 +1.252203 +1.060226 +1.062564 +0.853991 +0.986832 +1.019786 +0.873746 +0.880802 +0.776611 +0.774251 +0.722629 +0.883309 +1.050254 +1.174258 +1.297810 +1.114437 +1.159611 +1.318001 +1.403207 +1.646177 +1.449942 +1.486702 +1.457905 +1.533516 +1.428289 +1.727821 +1.680758 +1.474617 +1.865750 +1.677722 +1.883250 +1.706351 +1.600958 +1.284972 +1.251889 +1.160717 +0.896049 +0.956541 +0.931596 +0.986854 +0.827023 +0.746164 +0.892252 +0.854372 +0.839080 +0.758932 +0.672252 +0.720025 +0.641433 +0.544152 +0.592160 +0.514846 +0.561344 +0.448302 +0.363627 +0.498299 +0.565455 +0.877873 +0.948297 +1.101386 +1.281993 +1.282064 +1.434546 +1.307456 +1.034463 +0.911412 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-7/iter-0/perlin_3.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-7/iter-0/perlin_3.txt new file mode 100644 index 00000000000..520a51e79f7 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-7/iter-0/perlin_3.txt @@ -0,0 +1,100 @@ +-0.223092 +0.466707 +0.368349 +0.089874 +0.161719 +0.078999 +0.102578 +-0.083603 +0.294051 +0.272732 +0.356278 +0.364104 +0.487348 +0.418455 +0.251441 +0.560986 +0.639218 +0.548377 +0.673267 +0.795272 +1.035669 +1.103795 +1.163568 +1.290674 +1.312494 +1.153418 +1.131051 +0.931183 +0.893717 +1.009827 +1.233752 +1.298835 +1.311961 +1.246482 +1.246256 +1.189188 +1.267197 +1.045907 +1.277045 +1.527899 +1.478772 +1.552988 +1.498229 +1.461283 +1.353050 +1.383894 +1.339477 +1.273635 +1.328624 +1.094269 +0.983792 +0.875603 +0.709414 +0.737601 +0.363175 +0.332673 +0.289505 +0.369777 +0.251481 +0.516427 +0.412752 +0.087499 +0.381547 +0.117950 +0.265638 +0.168071 +0.246383 +0.146412 +0.284670 +0.328228 +0.191186 +0.346995 +0.364771 +0.471056 +0.346459 +0.360055 +0.725820 +0.855841 +0.986781 +0.993643 +0.906214 +1.014289 +1.065209 +1.102837 +1.226516 +1.188260 +1.281279 +1.209360 +1.148376 +1.268678 +1.268572 +1.402798 +1.258439 +1.134246 +1.074161 +1.006970 +1.145082 +1.147652 +0.986590 +0.828954 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-7/iter-0/perlin_params.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-7/iter-0/perlin_params.txt new file mode 100644 index 00000000000..44551227721 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-7/iter-0/perlin_params.txt @@ -0,0 +1,7 @@ +SCALE:0.100905 +OFFSET:0.749785 +PERSISTENCE:0.960394 +OCTAVES:27.0099 +PRIME_1:40.6138 +PRIME_2:61.0175 +PRIME_3:87.8034 \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-8/iter-0/aggregated.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-8/iter-0/aggregated.txt new file mode 100644 index 00000000000..244709c6519 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-8/iter-0/aggregated.txt @@ -0,0 +1,4 @@ +PERLIN_3 -0.917785 +PERLIN_2 -0.105634 +PERLIN_1 -1.276058 +STATE Negative \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-8/iter-0/aggregator.stderr b/libres/test-data/local/mini_ert/storage/Run/realisation-8/iter-0/aggregator.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-8/iter-0/aggregator.stdout b/libres/test-data/local/mini_ert/storage/Run/realisation-8/iter-0/aggregator.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-8/iter-0/parameters.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-8/iter-0/parameters.txt new file mode 100644 index 00000000000..59ba741905a --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-8/iter-0/parameters.txt @@ -0,0 +1,7 @@ +PERLIN_PARAM:SCALE 0.494279 +PERLIN_PARAM:OFFSET -0.526799 +PERLIN_PARAM:PERSISTENCE 0.0911083 +PERLIN_PARAM:OCTAVES 43.0492 +PERLIN_PARAM:PRIME_1 123.299 +PERLIN_PARAM:PRIME_2 42.4258 +PERLIN_PARAM:PRIME_3 14.0702 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-8/iter-0/perlin.stderr b/libres/test-data/local/mini_ert/storage/Run/realisation-8/iter-0/perlin.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-8/iter-0/perlin.stdout b/libres/test-data/local/mini_ert/storage/Run/realisation-8/iter-0/perlin.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-8/iter-0/perlin_1.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-8/iter-0/perlin_1.txt new file mode 100644 index 00000000000..6aa944dccfb --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-8/iter-0/perlin_1.txt @@ -0,0 +1,100 @@ +-1.276058 +-1.289017 +-1.314341 +-1.306601 +-1.296437 +-1.293440 +-1.259407 +-1.266510 +-1.294486 +-1.261902 +-1.202479 +-1.080475 +-0.954164 +-0.885534 +-0.858941 +-0.894787 +-0.919912 +-0.916939 +-0.886507 +-0.799550 +-0.721154 +-0.650200 +-0.537956 +-0.483812 +-0.489635 +-0.525394 +-0.546662 +-0.527208 +-0.534832 +-0.533549 +-0.538252 +-0.584050 +-0.611137 +-0.630059 +-0.650202 +-0.616093 +-0.568181 +-0.463276 +-0.334508 +-0.259597 +-0.221372 +-0.259230 +-0.279580 +-0.244302 +-0.207325 +-0.170186 +-0.177288 +-0.240439 +-0.278604 +-0.325685 +-0.411518 +-0.447688 +-0.438482 +-0.430901 +-0.426508 +-0.473419 +-0.523455 +-0.492174 +-0.429854 +-0.320462 +-0.242691 +-0.226471 +-0.221185 +-0.262628 +-0.292571 +-0.326766 +-0.377534 +-0.383441 +-0.373359 +-0.364832 +-0.330290 +-0.341756 +-0.378785 +-0.325450 +-0.201339 +-0.014930 +0.131198 +0.218084 +0.307262 +0.331535 +0.323047 +0.275638 +0.204499 +0.189781 +0.216423 +0.234694 +0.244237 +0.196780 +0.140343 +0.139795 +0.132064 +0.149348 +0.135500 +0.067575 +-0.002818 +-0.038351 +-0.033175 +-0.039802 +-0.104931 +-0.282153 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-8/iter-0/perlin_2.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-8/iter-0/perlin_2.txt new file mode 100644 index 00000000000..9527f5ea7a7 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-8/iter-0/perlin_2.txt @@ -0,0 +1,100 @@ +-0.105634 +-0.100522 +-0.094477 +-0.166631 +-0.242055 +-0.267233 +-0.351269 +-0.418058 +-0.423433 +-0.455156 +-0.503687 +-0.535205 +-0.602964 +-0.629293 +-0.605334 +-0.598246 +-0.588567 +-0.648452 +-0.705977 +-0.761218 +-0.784663 +-0.690549 +-0.595504 +-0.441816 +-0.322998 +-0.240974 +-0.082616 +0.026633 +0.111952 +0.206176 +0.291269 +0.362596 +0.390792 +0.289982 +0.127689 +0.000994 +-0.086106 +-0.090324 +-0.148248 +-0.241375 +-0.259151 +-0.279786 +-0.291397 +-0.328535 +-0.389958 +-0.377995 +-0.280737 +-0.158372 +-0.106091 +-0.142807 +-0.139820 +-0.078912 +-0.035823 +0.057418 +0.107112 +0.097244 +0.109350 +0.077772 +0.052136 +0.081830 +0.120261 +0.196251 +0.272728 +0.294216 +0.300526 +0.246943 +0.116525 +-0.050187 +-0.175115 +-0.301600 +-0.427862 +-0.456478 +-0.450396 +-0.398868 +-0.320212 +-0.349825 +-0.460267 +-0.567451 +-0.631795 +-0.652977 +-0.663271 +-0.674428 +-0.682635 +-0.626163 +-0.539750 +-0.498320 +-0.505975 +-0.578888 +-0.635684 +-0.598530 +-0.523332 +-0.361034 +-0.160381 +0.022807 +0.185816 +0.259100 +0.276090 +0.163135 +0.001742 +-0.111572 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-8/iter-0/perlin_3.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-8/iter-0/perlin_3.txt new file mode 100644 index 00000000000..cd80bec7c26 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-8/iter-0/perlin_3.txt @@ -0,0 +1,100 @@ +-0.917785 +-1.018838 +-1.197085 +-1.346518 +-1.465782 +-1.457947 +-1.433721 +-1.430564 +-1.396554 +-1.348945 +-1.213771 +-1.091774 +-1.050692 +-1.067291 +-1.040706 +-1.000801 +-1.010901 +-0.965566 +-0.865274 +-0.667889 +-0.431604 +-0.239372 +-0.105089 +-0.069473 +-0.069545 +-0.099554 +-0.139350 +-0.160356 +-0.109202 +-0.012531 +0.030695 +0.054757 +0.070031 +0.092023 +0.121743 +0.129957 +0.118527 +0.101591 +0.141964 +0.266738 +0.345876 +0.392401 +0.430221 +0.358497 +0.240463 +0.122590 +0.008486 +-0.058995 +-0.075277 +-0.162975 +-0.315639 +-0.521310 +-0.729615 +-0.851158 +-0.979655 +-1.056785 +-1.059049 +-1.085967 +-1.124672 +-1.129564 +-1.147745 +-1.190867 +-1.211475 +-1.265556 +-1.317086 +-1.291337 +-1.238050 +-1.188747 +-1.142334 +-1.134089 +-1.132725 +-1.066024 +-1.017221 +-0.914666 +-0.800777 +-0.735933 +-0.626698 +-0.565983 +-0.484094 +-0.418266 +-0.429310 +-0.380164 +-0.258859 +-0.067478 +0.094606 +0.175094 +0.213959 +0.182170 +0.149065 +0.171849 +0.179785 +0.163891 +0.149761 +0.055666 +-0.022016 +-0.015994 +-0.013374 +0.003331 +-0.046131 +-0.194030 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-8/iter-0/perlin_params.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-8/iter-0/perlin_params.txt new file mode 100644 index 00000000000..219b62155c1 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-8/iter-0/perlin_params.txt @@ -0,0 +1,7 @@ +SCALE:0.494279 +OFFSET:-0.526799 +PERSISTENCE:0.0911083 +OCTAVES:43.0492 +PRIME_1:123.299 +PRIME_2:42.4258 +PRIME_3:14.0702 \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-9/iter-0/aggregated.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-9/iter-0/aggregated.txt new file mode 100644 index 00000000000..48b8531624f --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-9/iter-0/aggregated.txt @@ -0,0 +1,4 @@ +PERLIN_3 0.220371 +PERLIN_2 1.032522 +PERLIN_1 -0.137903 +STATE Positive \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-9/iter-0/aggregator.stderr b/libres/test-data/local/mini_ert/storage/Run/realisation-9/iter-0/aggregator.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-9/iter-0/aggregator.stdout b/libres/test-data/local/mini_ert/storage/Run/realisation-9/iter-0/aggregator.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-9/iter-0/parameters.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-9/iter-0/parameters.txt new file mode 100644 index 00000000000..2e4f0f7b049 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-9/iter-0/parameters.txt @@ -0,0 +1,7 @@ +PERLIN_PARAM:SCALE 0.971949 +PERLIN_PARAM:OFFSET 0.568165 +PERLIN_PARAM:PERSISTENCE 0.332226 +PERLIN_PARAM:OCTAVES 8.48231 +PERLIN_PARAM:PRIME_1 106.04 +PERLIN_PARAM:PRIME_2 17.4819 +PERLIN_PARAM:PRIME_3 56.9629 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-9/iter-0/perlin.stderr b/libres/test-data/local/mini_ert/storage/Run/realisation-9/iter-0/perlin.stderr new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-9/iter-0/perlin.stdout b/libres/test-data/local/mini_ert/storage/Run/realisation-9/iter-0/perlin.stdout new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-9/iter-0/perlin_1.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-9/iter-0/perlin_1.txt new file mode 100644 index 00000000000..36798a3f535 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-9/iter-0/perlin_1.txt @@ -0,0 +1,100 @@ +-0.137903 +-0.157851 +-0.192276 +-0.201670 +-0.192839 +-0.193214 +-0.209423 +-0.243628 +-0.261796 +-0.213269 +-0.207591 +-0.203993 +-0.142490 +-0.122206 +-0.157129 +-0.193732 +-0.206432 +-0.177612 +-0.114381 +-0.050740 +-0.070342 +-0.154469 +-0.151716 +-0.137877 +-0.155888 +-0.199095 +-0.226091 +-0.254674 +-0.326284 +-0.337833 +-0.312344 +-0.317023 +-0.346853 +-0.332528 +-0.238504 +-0.042655 +0.146262 +0.376535 +0.617211 +0.752626 +0.797129 +0.714055 +0.660213 +0.707589 +0.786058 +0.902157 +0.982988 +0.972517 +0.950945 +0.917937 +0.833802 +0.834170 +0.874761 +0.874663 +0.835114 +0.714452 +0.615202 +0.641686 +0.698232 +0.833272 +0.978281 +1.070483 +1.118218 +1.061277 +1.031902 +0.998036 +0.965574 +1.010296 +1.079127 +1.148032 +1.260956 +1.285149 +1.247059 +1.290888 +1.424147 +1.629905 +1.764537 +1.819917 +1.898802 +1.893787 +1.855403 +1.728917 +1.536779 +1.375375 +1.281240 +1.209446 +1.131790 +1.009449 +0.933443 +0.938227 +0.970148 +1.049769 +1.076923 +1.035402 +0.981504 +0.949433 +0.958861 +0.977078 +0.958006 +0.802606 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-9/iter-0/perlin_2.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-9/iter-0/perlin_2.txt new file mode 100644 index 00000000000..ba557199f40 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-9/iter-0/perlin_2.txt @@ -0,0 +1,100 @@ +1.032522 +1.030645 +1.027589 +0.938300 +0.861544 +0.832993 +0.698715 +0.604825 +0.609257 +0.593477 +0.491201 +0.341277 +0.208709 +0.134035 +0.096478 +0.102808 +0.124913 +0.090875 +0.066149 +-0.012408 +-0.133850 +-0.194818 +-0.209263 +-0.095881 +0.010749 +0.085326 +0.237955 +0.299167 +0.320499 +0.401893 +0.517178 +0.629623 +0.655076 +0.587512 +0.539387 +0.574432 +0.628337 +0.749487 +0.803470 +0.770847 +0.759350 +0.693500 +0.648396 +0.623356 +0.603426 +0.694348 +0.879540 +1.054584 +1.123458 +1.100816 +1.105500 +1.202947 +1.277420 +1.362982 +1.368734 +1.285116 +1.248007 +1.211633 +1.180222 +1.235564 +1.341232 +1.493206 +1.612131 +1.618121 +1.625000 +1.571745 +1.459633 +1.343551 +1.277370 +1.211264 +1.163383 +1.170426 +1.175448 +1.217470 +1.305274 +1.295011 +1.173072 +1.034382 +0.959745 +0.909275 +0.869084 +0.778851 +0.649645 +0.559430 +0.525067 +0.476432 +0.381578 +0.233781 +0.157416 +0.199901 +0.314752 +0.539386 +0.781042 +0.990634 +1.170137 +1.246885 +1.268126 +1.180015 +1.064679 +0.973186 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-9/iter-0/perlin_3.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-9/iter-0/perlin_3.txt new file mode 100644 index 00000000000..2bb0cef781f --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-9/iter-0/perlin_3.txt @@ -0,0 +1,100 @@ +0.220371 +0.112328 +-0.075019 +-0.241588 +-0.362184 +-0.357721 +-0.383737 +-0.407682 +-0.363864 +-0.300312 +-0.218883 +-0.215292 +-0.239019 +-0.303964 +-0.338894 +-0.299746 +-0.297421 +-0.226239 +-0.093147 +0.080922 +0.219209 +0.256359 +0.281152 +0.276463 +0.264202 +0.226745 +0.181221 +0.112178 +0.099345 +0.183186 +0.256603 +0.321784 +0.334316 +0.389554 +0.533441 +0.703395 +0.832970 +0.941402 +1.093683 +1.278960 +1.364377 +1.365686 +1.370013 +1.310389 +1.233846 +1.194933 +1.168762 +1.153961 +1.154271 +1.080648 +0.929681 +0.760549 +0.583628 +0.454406 +0.281967 +0.131086 +0.079608 +0.047894 +0.003413 +0.024170 +0.073226 +0.106088 +0.127928 +0.058349 +0.007387 +0.033465 +0.105058 +0.204991 +0.310151 +0.378775 +0.458521 +0.560881 +0.608623 +0.701672 +0.824709 +0.908902 +1.006640 +1.035850 +1.107446 +1.143986 +1.103046 +1.073115 +1.073421 +1.118115 +1.159422 +1.149846 +1.101513 +0.994839 +0.942165 +0.970280 +1.017869 +1.064312 +1.091185 +1.023493 +0.962306 +0.971791 +0.978662 +1.020211 +1.016806 +0.890728 diff --git a/libres/test-data/local/mini_ert/storage/Run/realisation-9/iter-0/perlin_params.txt b/libres/test-data/local/mini_ert/storage/Run/realisation-9/iter-0/perlin_params.txt new file mode 100644 index 00000000000..c3e63c5f538 --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/Run/realisation-9/iter-0/perlin_params.txt @@ -0,0 +1,7 @@ +SCALE:0.971949 +OFFSET:0.568165 +PERSISTENCE:0.332226 +OCTAVES:8.48231 +PRIME_1:106.04 +PRIME_2:17.4819 +PRIME_3:56.9629 \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/current_case b/libres/test-data/local/mini_ert/storage/ertensemble/current_case new file mode 100644 index 00000000000..331d858ce9b --- /dev/null +++ b/libres/test-data/local/mini_ert/storage/ertensemble/current_case @@ -0,0 +1 @@ +default \ No newline at end of file diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/0001/files/PERLIN_active b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/0001/files/PERLIN_active new file mode 100644 index 00000000000..c72400474a6 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/0001/files/PERLIN_active differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/0002/files/PERLIN_active b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/0002/files/PERLIN_active new file mode 100644 index 00000000000..c72400474a6 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/0002/files/PERLIN_active differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/0003/files/PERLIN_active b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/0003/files/PERLIN_active new file mode 100644 index 00000000000..c72400474a6 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/0003/files/PERLIN_active differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/FORECAST.data_0 new file mode 100644 index 00000000000..8dcc722d5a1 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/FORECAST.data_0 differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/FORECAST.index new file mode 100644 index 00000000000..0a01a88233f Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/PARAMETER.data_0 new file mode 100644 index 00000000000..1d8b6c18da5 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/PARAMETER.data_0 differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/PARAMETER.index new file mode 100644 index 00000000000..1f0db16ccc8 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_0/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/FORECAST.data_0 new file mode 100644 index 00000000000..8cfcfa22502 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/FORECAST.data_0 differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/FORECAST.index new file mode 100644 index 00000000000..9e25666ab4f Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/PARAMETER.data_0 new file mode 100644 index 00000000000..ff8922efbf2 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/PARAMETER.data_0 differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/PARAMETER.index new file mode 100644 index 00000000000..2f8c799aedf Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_1/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/FORECAST.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/PARAMETER.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_10/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/FORECAST.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/PARAMETER.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_11/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/FORECAST.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/PARAMETER.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_12/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/FORECAST.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/PARAMETER.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_13/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/FORECAST.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/PARAMETER.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_14/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/FORECAST.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/PARAMETER.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_15/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/FORECAST.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/PARAMETER.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_16/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/FORECAST.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/PARAMETER.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_17/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/FORECAST.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/PARAMETER.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_18/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/FORECAST.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/PARAMETER.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_19/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/FORECAST.data_0 new file mode 100644 index 00000000000..6f7244c4fae Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/FORECAST.data_0 differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/FORECAST.index new file mode 100644 index 00000000000..d90f7a9f75f Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/PARAMETER.data_0 new file mode 100644 index 00000000000..aad9490e0c2 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/PARAMETER.data_0 differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/PARAMETER.index new file mode 100644 index 00000000000..4c804b5b87f Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_2/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/FORECAST.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/PARAMETER.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_20/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/FORECAST.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/PARAMETER.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_21/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/FORECAST.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/PARAMETER.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_22/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/FORECAST.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/PARAMETER.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_23/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/FORECAST.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/PARAMETER.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_24/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/FORECAST.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/PARAMETER.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_25/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/FORECAST.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/PARAMETER.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_26/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/FORECAST.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/PARAMETER.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_27/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/FORECAST.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/PARAMETER.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_28/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/FORECAST.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/PARAMETER.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_29/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/FORECAST.data_0 new file mode 100644 index 00000000000..fd7246d7ab8 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/FORECAST.data_0 differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/FORECAST.index new file mode 100644 index 00000000000..718956c2429 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/PARAMETER.data_0 new file mode 100644 index 00000000000..2fc669aaf73 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/PARAMETER.data_0 differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/PARAMETER.index new file mode 100644 index 00000000000..cbeb9c12244 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_3/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/FORECAST.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/PARAMETER.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_30/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/FORECAST.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/PARAMETER.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_31/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/FORECAST.data_0 new file mode 100644 index 00000000000..28665371dc0 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/FORECAST.data_0 differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/FORECAST.index new file mode 100644 index 00000000000..a879653abd8 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/PARAMETER.data_0 new file mode 100644 index 00000000000..90b4c5e871d Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/PARAMETER.data_0 differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/PARAMETER.index new file mode 100644 index 00000000000..b7afb6bf58e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_4/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/FORECAST.data_0 new file mode 100644 index 00000000000..178960502d1 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/FORECAST.data_0 differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/FORECAST.index new file mode 100644 index 00000000000..7e09d8c4826 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/PARAMETER.data_0 new file mode 100644 index 00000000000..b47d88c6ceb Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/PARAMETER.data_0 differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/PARAMETER.index new file mode 100644 index 00000000000..68c8652b8a6 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_5/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/FORECAST.data_0 new file mode 100644 index 00000000000..cfb98aa00e7 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/FORECAST.data_0 differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/FORECAST.index new file mode 100644 index 00000000000..2f03d8d11b2 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/PARAMETER.data_0 new file mode 100644 index 00000000000..bb97f570e88 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/PARAMETER.data_0 differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/PARAMETER.index new file mode 100644 index 00000000000..e86f73216f0 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_6/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/FORECAST.data_0 new file mode 100644 index 00000000000..c8b34c31795 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/FORECAST.data_0 differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/FORECAST.index new file mode 100644 index 00000000000..7ef99ebfde3 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/PARAMETER.data_0 new file mode 100644 index 00000000000..9bc6a115435 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/PARAMETER.data_0 differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/PARAMETER.index new file mode 100644 index 00000000000..40817c501ff Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_7/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/FORECAST.data_0 new file mode 100644 index 00000000000..6ce8ff11424 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/FORECAST.data_0 differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/FORECAST.index new file mode 100644 index 00000000000..c5604133605 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/PARAMETER.data_0 new file mode 100644 index 00000000000..674b8594d63 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/PARAMETER.data_0 differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/PARAMETER.index new file mode 100644 index 00000000000..86d42f2def0 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_8/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/ANALYZED.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/ANALYZED.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/ANALYZED.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/ANALYZED.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/ANALYZED.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/ANALYZED.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/FORECAST.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/FORECAST.data_0 new file mode 100644 index 00000000000..27d18051fda Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/FORECAST.data_0 differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/FORECAST.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/FORECAST.index new file mode 100644 index 00000000000..9bbcfc9ea70 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/FORECAST.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/FORECAST.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/FORECAST.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/PARAMETER.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/PARAMETER.data_0 new file mode 100644 index 00000000000..a6ce92b4719 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/PARAMETER.data_0 differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/PARAMETER.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/PARAMETER.index new file mode 100644 index 00000000000..727e9128504 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/PARAMETER.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/PARAMETER.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/PARAMETER.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/STATIC.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/STATIC.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/STATIC.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/STATIC.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/STATIC.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Ensemble/mod_9/STATIC.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Index/INDEX.data_0 b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Index/INDEX.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Index/INDEX.index b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Index/INDEX.index new file mode 100644 index 00000000000..e1de74ede38 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Index/INDEX.index differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Index/INDEX.mnt b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Index/INDEX.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/Index/INDEX.mnt differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/ert_fstab b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/ert_fstab new file mode 100644 index 00000000000..a5d67ad3a3c Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/ert_fstab differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/files/case_config b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/files/case_config new file mode 100644 index 00000000000..593f4708db8 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/files/case_config differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/files/custom_kw_config_set b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/files/custom_kw_config_set new file mode 100644 index 00000000000..b51c81a5bb4 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/files/custom_kw_config_set differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/files/state-map b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/files/state-map new file mode 100644 index 00000000000..f2112badc8f Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/files/state-map differ diff --git a/libres/test-data/local/mini_ert/storage/ertensemble/test_run/files/summary-key-set b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/files/summary-key-set new file mode 100644 index 00000000000..593f4708db8 Binary files /dev/null and b/libres/test-data/local/mini_ert/storage/ertensemble/test_run/files/summary-key-set differ diff --git a/libres/test-data/local/mini_ert/time_map b/libres/test-data/local/mini_ert/time_map new file mode 100644 index 00000000000..95ec9b0216d --- /dev/null +++ b/libres/test-data/local/mini_ert/time_map @@ -0,0 +1,4 @@ +01/01/2015 +01/02/2015 +01/03/2015 +01/04/2015 \ No newline at end of file diff --git a/libres/test-data/local/poly_normal/POLY_EVAL b/libres/test-data/local/poly_normal/POLY_EVAL new file mode 100644 index 00000000000..8c0137b18c5 --- /dev/null +++ b/libres/test-data/local/poly_normal/POLY_EVAL @@ -0,0 +1 @@ +EXECUTABLE poly_eval.py diff --git a/libres/test-data/local/poly_normal/README.md b/libres/test-data/local/poly_normal/README.md new file mode 100644 index 00000000000..e6035249a94 --- /dev/null +++ b/libres/test-data/local/poly_normal/README.md @@ -0,0 +1,55 @@ +## Polynomial curve fitting - A minimal model updating case +A display of a truly minimal model updating case. It is done with a second +degree polynomial as the _true reality_. The model is `ax^2 + bx + c` where +`a`, `b` and `c` are the parameters. + +### Observed data +The observed data was generated with the following _Python_ code: +``` +def p(x): + return 0.5*x**2 + x + 3 + +[(p(x)+random.gauss(0, 0.25*x**2+0.1), 0.25*x**2+0.1) for x in [0, 2, 4, 6, 8]] +``` + +This gives us observations (both a value and an uncertainty) for even `x` less +than 10. These values appear in `poly_obs_data.txt`. Finally, these values are +represented as an observation in `observations`. Here we give the data a name. +And we specify that the values that should be used from the `forward_model` is only +the even ones (the `forward model` spits out the image of the polynomial on the +range `[0, 9]`). It also specifies that the time step to consider is `0` +(`RESTART`). We do not really have a time concept in this setup and hence we +only use `0` as a dummy value. We could of course have considered the values +feed to the polynomial as time; but that is left as an exercise for the reader. + +### Parameters +As mentioned above `a`, `b` and `c` forms the parameters of the model `ax^2 + bx + c`. +They are all specified to be uniformly distributed over ranges in +`coeff_priors` and are sampled by `GEN_KW` and dumped to the forward model +following the `json`-template in `coeff.tmpl`. + +### Forward model +After the parameters are dumped to the runpath, _forward model_'s are launched +for each of the realizations. The forward model consists of a single script +described in `poly_eval.py`, that loads the dumped parameters and outputs the +values of the polynomial (given the parameters) for integer `x in [0, 10]` to the +file `poly_0.out`. + +The very minimal job description file `POLY_EVAL` just points to the script. + +### Loading data +The configuration specifies a `GEN_DATA` that expects there to be a result file +`poly_%d.out` for report step `0`. In other words it expects to load data from +`poly_0.out`, the exact file that the forward model produces. + +### Model update +Then the loaded data is compared to the observed data and the parameters are +updated accordingly. + +### Time +Although we don't really have a clear concept of time in this case _ERT_ +expects us to have so. Since we have specified that all of our data is for time +step `0`, we had to create an artificial time map `time_map` that specifies +that the very first and only report step corresponds to `1/10/2006`. This could +be any date, so we put it to the date of the first commit of the +`ensembles/ert` repository on _Github_. diff --git a/libres/test-data/local/poly_normal/coeff.tmpl b/libres/test-data/local/poly_normal/coeff.tmpl new file mode 100644 index 00000000000..e55ade502df --- /dev/null +++ b/libres/test-data/local/poly_normal/coeff.tmpl @@ -0,0 +1,5 @@ +{ + "a": , + "b": , + "c": +} diff --git a/libres/test-data/local/poly_normal/coeff_priors b/libres/test-data/local/poly_normal/coeff_priors new file mode 100644 index 00000000000..54d0a601043 --- /dev/null +++ b/libres/test-data/local/poly_normal/coeff_priors @@ -0,0 +1,3 @@ +COEFF_A NORMAL 0.5 0.5 +COEFF_B NORMAL 1.0 1.0 +COEFF_C NORMAL 2.5 2.5 diff --git a/libres/test-data/local/poly_normal/observations b/libres/test-data/local/poly_normal/observations new file mode 100644 index 00000000000..dff1ae0b526 --- /dev/null +++ b/libres/test-data/local/poly_normal/observations @@ -0,0 +1,6 @@ +GENERAL_OBSERVATION POLY_OBS { + DATA = POLY_RES; + INDEX_LIST = 0,2,4,6,8; + RESTART = 0; + OBS_FILE = poly_obs_data.txt; +}; diff --git a/libres/test-data/local/poly_normal/poly-ies.ert b/libres/test-data/local/poly_normal/poly-ies.ert new file mode 100644 index 00000000000..fe335864755 --- /dev/null +++ b/libres/test-data/local/poly_normal/poly-ies.ert @@ -0,0 +1,21 @@ +JOBNAME poly_%d + +QUEUE_SYSTEM LOCAL +QUEUE_OPTION LOCAL MAX_RUNNING 50 + +RUNPATH poly_out/real_%d/iter_%d + +OBS_CONFIG observations +TIME_MAP time_map + +NUM_REALIZATIONS 100 +MIN_REALIZATIONS 1 + +GEN_KW COEFFS coeff.tmpl coeffs.json coeff_priors +GEN_DATA POLY_RES RESULT_FILE:poly_%d.out REPORT_STEPS:0 INPUT_FORMAT:ASCII + +INSTALL_JOB poly_eval POLY_EVAL +SIMULATION_JOB poly_eval +-- Load the RML module, this does not imply that it is actually used. That will +-- be determined by the user interactively later. +ANALYSIS_LOAD RML rml_enkf.so diff --git a/libres/test-data/local/poly_normal/poly.ert b/libres/test-data/local/poly_normal/poly.ert new file mode 100644 index 00000000000..27f0bbe8884 --- /dev/null +++ b/libres/test-data/local/poly_normal/poly.ert @@ -0,0 +1,18 @@ +JOBNAME poly_%d + +QUEUE_SYSTEM LOCAL +QUEUE_OPTION LOCAL MAX_RUNNING 50 + +RUNPATH poly_out/real_%d/iter_%d + +OBS_CONFIG observations +TIME_MAP time_map + +NUM_REALIZATIONS 20 +MIN_REALIZATIONS 1 + +GEN_KW COEFFS coeff.tmpl coeffs.json coeff_priors +GEN_DATA POLY_RES RESULT_FILE:poly_%d.out REPORT_STEPS:0 INPUT_FORMAT:ASCII + +INSTALL_JOB poly_eval POLY_EVAL +SIMULATION_JOB poly_eval diff --git a/libres/test-data/local/poly_normal/poly_eval.py b/libres/test-data/local/poly_normal/poly_eval.py new file mode 100755 index 00000000000..5ed27038d12 --- /dev/null +++ b/libres/test-data/local/poly_normal/poly_eval.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python + +import json + + +def _load_coeffs(filename): + with open(filename) as f: + return json.load(f) + + +def _evaluate(coeffs, x): + return coeffs["a"] * x ** 2 + coeffs["b"] * x + coeffs["c"] + + +if __name__ == "__main__": + coeffs = _load_coeffs("coeffs.json") + output = [_evaluate(coeffs, x) for x in range(10)] + with open("poly_0.out", "w") as f: + f.write("\n".join(map(str, output))) diff --git a/libres/test-data/local/poly_normal/poly_obs_data.txt b/libres/test-data/local/poly_normal/poly_obs_data.txt new file mode 100644 index 00000000000..d792b163daa --- /dev/null +++ b/libres/test-data/local/poly_normal/poly_obs_data.txt @@ -0,0 +1,5 @@ +2.8532509308 0.1 +7.20311703432 1.1 +21.3864899107 4.1 +31.5145559347 9.1 +53.5676660405 16.1 diff --git a/libres/test-data/local/poly_normal/time_map b/libres/test-data/local/poly_normal/time_map new file mode 100644 index 00000000000..a8353d49b24 --- /dev/null +++ b/libres/test-data/local/poly_normal/time_map @@ -0,0 +1 @@ +1/10/2006 diff --git a/libres/test-data/local/poly_uniform/POLY_EVAL b/libres/test-data/local/poly_uniform/POLY_EVAL new file mode 100644 index 00000000000..8c0137b18c5 --- /dev/null +++ b/libres/test-data/local/poly_uniform/POLY_EVAL @@ -0,0 +1 @@ +EXECUTABLE poly_eval.py diff --git a/libres/test-data/local/poly_uniform/README.md b/libres/test-data/local/poly_uniform/README.md new file mode 100644 index 00000000000..e6035249a94 --- /dev/null +++ b/libres/test-data/local/poly_uniform/README.md @@ -0,0 +1,55 @@ +## Polynomial curve fitting - A minimal model updating case +A display of a truly minimal model updating case. It is done with a second +degree polynomial as the _true reality_. The model is `ax^2 + bx + c` where +`a`, `b` and `c` are the parameters. + +### Observed data +The observed data was generated with the following _Python_ code: +``` +def p(x): + return 0.5*x**2 + x + 3 + +[(p(x)+random.gauss(0, 0.25*x**2+0.1), 0.25*x**2+0.1) for x in [0, 2, 4, 6, 8]] +``` + +This gives us observations (both a value and an uncertainty) for even `x` less +than 10. These values appear in `poly_obs_data.txt`. Finally, these values are +represented as an observation in `observations`. Here we give the data a name. +And we specify that the values that should be used from the `forward_model` is only +the even ones (the `forward model` spits out the image of the polynomial on the +range `[0, 9]`). It also specifies that the time step to consider is `0` +(`RESTART`). We do not really have a time concept in this setup and hence we +only use `0` as a dummy value. We could of course have considered the values +feed to the polynomial as time; but that is left as an exercise for the reader. + +### Parameters +As mentioned above `a`, `b` and `c` forms the parameters of the model `ax^2 + bx + c`. +They are all specified to be uniformly distributed over ranges in +`coeff_priors` and are sampled by `GEN_KW` and dumped to the forward model +following the `json`-template in `coeff.tmpl`. + +### Forward model +After the parameters are dumped to the runpath, _forward model_'s are launched +for each of the realizations. The forward model consists of a single script +described in `poly_eval.py`, that loads the dumped parameters and outputs the +values of the polynomial (given the parameters) for integer `x in [0, 10]` to the +file `poly_0.out`. + +The very minimal job description file `POLY_EVAL` just points to the script. + +### Loading data +The configuration specifies a `GEN_DATA` that expects there to be a result file +`poly_%d.out` for report step `0`. In other words it expects to load data from +`poly_0.out`, the exact file that the forward model produces. + +### Model update +Then the loaded data is compared to the observed data and the parameters are +updated accordingly. + +### Time +Although we don't really have a clear concept of time in this case _ERT_ +expects us to have so. Since we have specified that all of our data is for time +step `0`, we had to create an artificial time map `time_map` that specifies +that the very first and only report step corresponds to `1/10/2006`. This could +be any date, so we put it to the date of the first commit of the +`ensembles/ert` repository on _Github_. diff --git a/libres/test-data/local/poly_uniform/coeff.tmpl b/libres/test-data/local/poly_uniform/coeff.tmpl new file mode 100644 index 00000000000..e55ade502df --- /dev/null +++ b/libres/test-data/local/poly_uniform/coeff.tmpl @@ -0,0 +1,5 @@ +{ + "a": , + "b": , + "c": +} diff --git a/libres/test-data/local/poly_uniform/coeff_priors b/libres/test-data/local/poly_uniform/coeff_priors new file mode 100644 index 00000000000..ed3dc731012 --- /dev/null +++ b/libres/test-data/local/poly_uniform/coeff_priors @@ -0,0 +1,3 @@ +COEFF_A UNIFORM 0 1 +COEFF_B UNIFORM 0 2 +COEFF_C UNIFORM 0 5 diff --git a/libres/test-data/local/poly_uniform/observations b/libres/test-data/local/poly_uniform/observations new file mode 100644 index 00000000000..dff1ae0b526 --- /dev/null +++ b/libres/test-data/local/poly_uniform/observations @@ -0,0 +1,6 @@ +GENERAL_OBSERVATION POLY_OBS { + DATA = POLY_RES; + INDEX_LIST = 0,2,4,6,8; + RESTART = 0; + OBS_FILE = poly_obs_data.txt; +}; diff --git a/libres/test-data/local/poly_uniform/poly-ies.ert b/libres/test-data/local/poly_uniform/poly-ies.ert new file mode 100644 index 00000000000..fe335864755 --- /dev/null +++ b/libres/test-data/local/poly_uniform/poly-ies.ert @@ -0,0 +1,21 @@ +JOBNAME poly_%d + +QUEUE_SYSTEM LOCAL +QUEUE_OPTION LOCAL MAX_RUNNING 50 + +RUNPATH poly_out/real_%d/iter_%d + +OBS_CONFIG observations +TIME_MAP time_map + +NUM_REALIZATIONS 100 +MIN_REALIZATIONS 1 + +GEN_KW COEFFS coeff.tmpl coeffs.json coeff_priors +GEN_DATA POLY_RES RESULT_FILE:poly_%d.out REPORT_STEPS:0 INPUT_FORMAT:ASCII + +INSTALL_JOB poly_eval POLY_EVAL +SIMULATION_JOB poly_eval +-- Load the RML module, this does not imply that it is actually used. That will +-- be determined by the user interactively later. +ANALYSIS_LOAD RML rml_enkf.so diff --git a/libres/test-data/local/poly_uniform/poly.ert b/libres/test-data/local/poly_uniform/poly.ert new file mode 100644 index 00000000000..6cd86bdbd1b --- /dev/null +++ b/libres/test-data/local/poly_uniform/poly.ert @@ -0,0 +1,18 @@ +JOBNAME poly_%d + +QUEUE_SYSTEM LOCAL +QUEUE_OPTION LOCAL MAX_RUNNING 50 + +RUNPATH poly_out/real_%d/iter_%d + +OBS_CONFIG observations +TIME_MAP time_map + +NUM_REALIZATIONS 100 +MIN_REALIZATIONS 1 + +GEN_KW COEFFS coeff.tmpl coeffs.json coeff_priors +GEN_DATA POLY_RES RESULT_FILE:poly_%d.out REPORT_STEPS:0 INPUT_FORMAT:ASCII + +INSTALL_JOB poly_eval POLY_EVAL +SIMULATION_JOB poly_eval diff --git a/libres/test-data/local/poly_uniform/poly_eval.py b/libres/test-data/local/poly_uniform/poly_eval.py new file mode 100755 index 00000000000..5ed27038d12 --- /dev/null +++ b/libres/test-data/local/poly_uniform/poly_eval.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python + +import json + + +def _load_coeffs(filename): + with open(filename) as f: + return json.load(f) + + +def _evaluate(coeffs, x): + return coeffs["a"] * x ** 2 + coeffs["b"] * x + coeffs["c"] + + +if __name__ == "__main__": + coeffs = _load_coeffs("coeffs.json") + output = [_evaluate(coeffs, x) for x in range(10)] + with open("poly_0.out", "w") as f: + f.write("\n".join(map(str, output))) diff --git a/libres/test-data/local/poly_uniform/poly_obs_data.txt b/libres/test-data/local/poly_uniform/poly_obs_data.txt new file mode 100644 index 00000000000..d792b163daa --- /dev/null +++ b/libres/test-data/local/poly_uniform/poly_obs_data.txt @@ -0,0 +1,5 @@ +2.8532509308 0.1 +7.20311703432 1.1 +21.3864899107 4.1 +31.5145559347 9.1 +53.5676660405 16.1 diff --git a/libres/test-data/local/poly_uniform/time_map b/libres/test-data/local/poly_uniform/time_map new file mode 100644 index 00000000000..a8353d49b24 --- /dev/null +++ b/libres/test-data/local/poly_uniform/time_map @@ -0,0 +1 @@ +1/10/2006 diff --git a/libres/test-data/local/resopt/config/simple/config b/libres/test-data/local/resopt/config/simple/config new file mode 100644 index 00000000000..8111c471134 --- /dev/null +++ b/libres/test-data/local/resopt/config/simple/config @@ -0,0 +1,5 @@ +JOBNAME Job%d +RUNPATH /tmp/simulations/run%d +NUM_REALIZATIONS 1 + + diff --git a/libres/test-data/local/row_scaling/CASE.EGRID b/libres/test-data/local/row_scaling/CASE.EGRID new file mode 100644 index 00000000000..1df43c1261a Binary files /dev/null and b/libres/test-data/local/row_scaling/CASE.EGRID differ diff --git a/libres/test-data/local/row_scaling/config b/libres/test-data/local/row_scaling/config new file mode 100644 index 00000000000..def34295d0f --- /dev/null +++ b/libres/test-data/local/row_scaling/config @@ -0,0 +1,12 @@ +NUM_REALIZATIONS 10 +GRID CASE.EGRID +FIELD PORO PARAMETER poro.grdecl INIT_FILES:fields/poro%d.grdecl +SUMMARY WBHP +SUMMARY WWCT +OBS_CONFIG observations.txt + + +LOAD_WORKFLOW_JOB workflows/ROW_SCALING_JOB1 +LOAD_WORKFLOW workflows/ROW_SCALING_WORKFLOW1 + +TIME_MAP timemap.txt diff --git a/libres/test-data/local/row_scaling/observations.txt b/libres/test-data/local/row_scaling/observations.txt new file mode 100644 index 00000000000..52e50eac3d7 --- /dev/null +++ b/libres/test-data/local/row_scaling/observations.txt @@ -0,0 +1,13 @@ +SUMMARY_OBSERVATION WBHP0 { + VALUE = 125; + ERROR = 25; + RESTART = 1; + KEY = WBHP; +}; + +SUMMARY_OBSERVATION WWCT0 { + VALUE = 0.75; + ERROR = 0.25; + RESTART = 1; + KEY = WWCT; +}; diff --git a/libres/test-data/local/row_scaling/timemap.txt b/libres/test-data/local/row_scaling/timemap.txt new file mode 100644 index 00000000000..a4ac5df6477 --- /dev/null +++ b/libres/test-data/local/row_scaling/timemap.txt @@ -0,0 +1,2 @@ +01/01/2020 +01/02/2020 diff --git a/libres/test-data/local/row_scaling/workflows/ROW_SCALING1 b/libres/test-data/local/row_scaling/workflows/ROW_SCALING1 new file mode 100644 index 00000000000..b934485f73c --- /dev/null +++ b/libres/test-data/local/row_scaling/workflows/ROW_SCALING1 @@ -0,0 +1 @@ +SCRIPT script/row_scaling1.py diff --git a/libres/test-data/local/row_scaling/workflows/ROW_SCALING_JOB1 b/libres/test-data/local/row_scaling/workflows/ROW_SCALING_JOB1 new file mode 100644 index 00000000000..267af4429ea --- /dev/null +++ b/libres/test-data/local/row_scaling/workflows/ROW_SCALING_JOB1 @@ -0,0 +1,2 @@ +INTERNAL True +SCRIPT scripts/row_scaling_job1.py diff --git a/libres/test-data/local/row_scaling/workflows/ROW_SCALING_WORKFLOW1 b/libres/test-data/local/row_scaling/workflows/ROW_SCALING_WORKFLOW1 new file mode 100644 index 00000000000..637b0b7c26b --- /dev/null +++ b/libres/test-data/local/row_scaling/workflows/ROW_SCALING_WORKFLOW1 @@ -0,0 +1 @@ +ROW_SCALING_JOB1 diff --git a/libres/test-data/local/row_scaling/workflows/scripts/row_scaling_job1.py b/libres/test-data/local/row_scaling/workflows/scripts/row_scaling_job1.py new file mode 100644 index 00000000000..34750804538 --- /dev/null +++ b/libres/test-data/local/row_scaling/workflows/scripts/row_scaling_job1.py @@ -0,0 +1,44 @@ +from res.enkf import ErtScript +from functools import partial +import math + + +def gaussian_decay(obs_pos, length_scale, grid, data_index): + x, y, z = grid.get_xyz(active_index=data_index) + dx = (obs_pos[0] - x) / length_scale[0] + dy = (obs_pos[1] - y) / length_scale[1] + dz = (obs_pos[2] - z) / length_scale[2] + + exp_arg = -0.5 * (dx * dx + dy * dy + dz * dz) + return math.exp(exp_arg) + + +class RowScalingJob1(ErtScript): + def run(self): + main = self.ert() + local_config = main.getLocalConfig() + local_config.clear() + + local_data = local_config.createDataset("LOCAL") + local_data.addNode("PORO") + row_scaling = local_data.row_scaling("PORO") + + ens_config = main.ensembleConfig() + poro_config = ens_config["PORO"] + field_config = poro_config.getFieldModelConfig() + + # ------------------------------------------------------------------------------------- + grid = main.eclConfig().getGrid() + obs_pos = grid.get_xyz(ijk=(5, 5, 1)) + length_scale = (2, 1, 0.50) + gaussian = partial(gaussian_decay, obs_pos, length_scale, grid) + row_scaling.assign(field_config.get_data_size(), gaussian) + # ------------------------------------------------------------------------------------- + + obs = local_config.createObsdata("OBSSET_LOCAL") + obs.addNode("WBHP0") + ministep = local_config.createMinistep("MINISTEP_LOCAL") + ministep.attachDataset(local_data) + ministep.attachObsset(obs) + updatestep = local_config.getUpdatestep() + updatestep.attachMinistep(ministep) diff --git a/libres/test-data/local/simple_config/analysis_config b/libres/test-data/local/simple_config/analysis_config new file mode 100644 index 00000000000..55ab9eee48f --- /dev/null +++ b/libres/test-data/local/simple_config/analysis_config @@ -0,0 +1,18 @@ +JOBNAME Job%d +RUNPATH /tmp/simulations/run%d +NUM_REALIZATIONS 10 +MIN_REALIZATIONS 10 +JOB_SCRIPT script.sh + +ANALYSIS_LOAD RML_ENKF rml_enkf.so +ANALYSIS_LOAD MODULE_ENKF rml_enkf.so +ANALYSIS_COPY STD_ENKF ENKF_HIGH_TRUNCATION +ANALYSIS_SET_VAR STD_ENKF ENKF_NCOMP 2 +ANALYSIS_SET_VAR ENKF_HIGH_TRUNCATION ENKF_TRUNCATION 0.99 +ANALYSIS_SELECT ENKF_HIGH_TRUNCATION + +QUEUE_SYSTEM LOCAL +QUEUE_OPTION LOCAL MAX_RUNNING 50 + +-- This is not strictly necessary, a sensible default will be used if not given. +ENSPATH Ensemble diff --git a/libres/test-data/local/simple_config/config_umask b/libres/test-data/local/simple_config/config_umask new file mode 100644 index 00000000000..772b9c30540 --- /dev/null +++ b/libres/test-data/local/simple_config/config_umask @@ -0,0 +1,5 @@ +JOBNAME Job%d +RUNPATH simulations/run%d +NUM_REALIZATIONS 1 +JOB_SCRIPT script.sh +UMASK 0022 diff --git a/libres/test-data/local/simple_config/minimum_config b/libres/test-data/local/simple_config/minimum_config new file mode 100644 index 00000000000..704bbfcf249 --- /dev/null +++ b/libres/test-data/local/simple_config/minimum_config @@ -0,0 +1,10 @@ +JOBNAME Job%d +RUNPATH /tmp/simulations/run%d +NUM_REALIZATIONS 10 +MIN_REALIZATIONS 10 +JOB_SCRIPT script.sh +QUEUE_SYSTEM LOCAL +QUEUE_OPTION LOCAL MAX_RUNNING 50 + +-- This is not strictly necessary, a sensible default will be used if not given. +ENSPATH Ensemble diff --git a/libres/test-data/local/simple_config/script.sh b/libres/test-data/local/simple_config/script.sh new file mode 100755 index 00000000000..441bae5d2b6 --- /dev/null +++ b/libres/test-data/local/simple_config/script.sh @@ -0,0 +1,2 @@ +#!/bin/sh +ls \ No newline at end of file diff --git a/libres/test-data/local/simple_config/slurm_config b/libres/test-data/local/simple_config/slurm_config new file mode 100644 index 00000000000..d58309e66f0 --- /dev/null +++ b/libres/test-data/local/simple_config/slurm_config @@ -0,0 +1,14 @@ +JOBNAME Job%d +RUNPATH /tmp/simulations/run%d +NUM_REALIZATIONS 10 +MIN_REALIZATIONS 10 +JOB_SCRIPT script.sh +QUEUE_SYSTEM SLURM +QUEUE_OPTION SLURM MAX_RUNNING 50 +QUEUE_OPTION SLURM SBATCH /path/to/sbatch +QUEUE_OPTION SLURM SQUEUE /path/to/squeue + + + +-- This is not strictly necessary, a sensible default will be used if not given. +ENSPATH Ensemble diff --git a/libres/test-data/local/simulation_model/jobs/NEW_TYPE_A b/libres/test-data/local/simulation_model/jobs/NEW_TYPE_A new file mode 100644 index 00000000000..e0a9b33c550 --- /dev/null +++ b/libres/test-data/local/simulation_model/jobs/NEW_TYPE_A @@ -0,0 +1,12 @@ +STDERR null + +EXECUTABLE echo + +MIN_ARG 1 +MAX_ARG 6 +ARG_TYPE 0 STRING +ARG_TYPE 1 BOOL +ARG_TYPE 2 FLOAT +ARG_TYPE 3 INT +ARG_TYPE 4 STRING +ARG_TYPE 5 STRING diff --git a/libres/test-data/local/simulation_model/jobs/NEW_TYPE_B b/libres/test-data/local/simulation_model/jobs/NEW_TYPE_B new file mode 100644 index 00000000000..7ce196d59dd --- /dev/null +++ b/libres/test-data/local/simulation_model/jobs/NEW_TYPE_B @@ -0,0 +1,9 @@ +STDERR null + +EXECUTABLE echo + +MIN_ARG 1 +MAX_ARG 2 +ARG_TYPE 0 STRING +ARG_TYPE 0 STRING + diff --git a/libres/test-data/local/simulation_model/jobs/NEW_TYPE_C b/libres/test-data/local/simulation_model/jobs/NEW_TYPE_C new file mode 100644 index 00000000000..751f72687af --- /dev/null +++ b/libres/test-data/local/simulation_model/jobs/NEW_TYPE_C @@ -0,0 +1 @@ +EXECUTABLE echo diff --git a/libres/test-data/local/simulation_model/jobs/OLD_TYPE_A b/libres/test-data/local/simulation_model/jobs/OLD_TYPE_A new file mode 100644 index 00000000000..f7977131def --- /dev/null +++ b/libres/test-data/local/simulation_model/jobs/OLD_TYPE_A @@ -0,0 +1,6 @@ +STDERR null + +EXECUTABLE echo + +ARGLIST WORD_A + diff --git a/libres/test-data/local/simulation_model/jobs/OLD_TYPE_B b/libres/test-data/local/simulation_model/jobs/OLD_TYPE_B new file mode 100644 index 00000000000..fd2f0a108df --- /dev/null +++ b/libres/test-data/local/simulation_model/jobs/OLD_TYPE_B @@ -0,0 +1,5 @@ +STDERR null + +EXECUTABLE echo + +ARGLIST diff --git a/libres/test-data/local/simulation_model/jobs/OLD_TYPE_C b/libres/test-data/local/simulation_model/jobs/OLD_TYPE_C new file mode 100644 index 00000000000..70ef2266963 --- /dev/null +++ b/libres/test-data/local/simulation_model/jobs/OLD_TYPE_C @@ -0,0 +1,8 @@ +STDERR null + +EXECUTABLE echo + +DEFAULT DEFAULT_ARGA_VALUE + +ARGLIST + diff --git a/libres/test-data/local/simulation_model/sim_kw.ert b/libres/test-data/local/simulation_model/sim_kw.ert new file mode 100644 index 00000000000..c4e26956895 --- /dev/null +++ b/libres/test-data/local/simulation_model/sim_kw.ert @@ -0,0 +1,38 @@ +QUEUE_SYSTEM LOCAL +ECLBASE SIM_KW + +NUM_REALIZATIONS 1 +NUM_CPU 5 + +DEFINE storage/ + +-- An argument name in OLD_TYPE_C is ARGUMENTB +-- We showcase that when using DEFINE, the resulting +-- argument value to the job is configured_argumentB +DEFINE +-- Note that if we include the following: +-- DEFINE DEFINED_ARGUMENTB_VALUE +-- The resolved argument will be DEFINED_ARGUMENTB_VALUE +-- even in the case where the user has specified the value +-- in the configuration as such: +-- OLD_TYPE_C(=configured_argumentB) + +DEFINE DEFINED_ARGC_VALUE + +RUNPATH_FILE directory/test_runpath_list.txt +RUNPATH /runpath/realisation-%d/iter-%d +ENSPATH /ensemble + +INSTALL_JOB NEW_TYPE_A jobs/NEW_TYPE_A +INSTALL_JOB NEW_TYPE_B jobs/NEW_TYPE_B +INSTALL_JOB OLD_TYPE_A jobs/OLD_TYPE_A +INSTALL_JOB OLD_TYPE_B jobs/OLD_TYPE_B +INSTALL_JOB OLD_TYPE_C jobs/OLD_TYPE_C + + +FORWARD_MODEL OLD_TYPE_B(=yy) +SIMULATION_JOB NEW_TYPE_A Hello True 3.14 4 +SIMULATION_JOB NEW_TYPE_B word +FORWARD_MODEL OLD_TYPE_A +FORWARD_MODEL OLD_TYPE_C(=configured_argumentA, =configured_argumentB) +FORWARD_MODEL OLD_TYPE_C() diff --git a/libres/test-data/local/snake_oil/jobs/SNAKE_OIL_DIFF b/libres/test-data/local/snake_oil/jobs/SNAKE_OIL_DIFF new file mode 100644 index 00000000000..98a867d9594 --- /dev/null +++ b/libres/test-data/local/snake_oil/jobs/SNAKE_OIL_DIFF @@ -0,0 +1,4 @@ +STDOUT snake_oil_diff.stdout +STDERR snake_oil_diff.stderr + +EXECUTABLE snake_oil_diff.py \ No newline at end of file diff --git a/libres/test-data/local/snake_oil/jobs/SNAKE_OIL_NPV b/libres/test-data/local/snake_oil/jobs/SNAKE_OIL_NPV new file mode 100644 index 00000000000..887830c756f --- /dev/null +++ b/libres/test-data/local/snake_oil/jobs/SNAKE_OIL_NPV @@ -0,0 +1,4 @@ +STDOUT snake_oil_npv.stdout +STDERR snake_oil_npv.stderr + +EXECUTABLE snake_oil_npv.py \ No newline at end of file diff --git a/libres/test-data/local/snake_oil/jobs/SNAKE_OIL_SIMULATOR b/libres/test-data/local/snake_oil/jobs/SNAKE_OIL_SIMULATOR new file mode 100644 index 00000000000..b4b7f9928fd --- /dev/null +++ b/libres/test-data/local/snake_oil/jobs/SNAKE_OIL_SIMULATOR @@ -0,0 +1,4 @@ +STDOUT snake_oil.stdout +STDERR snake_oil.stderr + +EXECUTABLE snake_oil_simulator.py \ No newline at end of file diff --git a/libres/test-data/local/snake_oil/jobs/snake_oil_diff.py b/libres/test-data/local/snake_oil/jobs/snake_oil_diff.py new file mode 120000 index 00000000000..126a5b22456 --- /dev/null +++ b/libres/test-data/local/snake_oil/jobs/snake_oil_diff.py @@ -0,0 +1 @@ +../../jobs_snake_oil/snake_oil_diff.py \ No newline at end of file diff --git a/libres/test-data/local/snake_oil/jobs/snake_oil_npv.py b/libres/test-data/local/snake_oil/jobs/snake_oil_npv.py new file mode 120000 index 00000000000..a69f08b34fb --- /dev/null +++ b/libres/test-data/local/snake_oil/jobs/snake_oil_npv.py @@ -0,0 +1 @@ +../../jobs_snake_oil/snake_oil_npv.py \ No newline at end of file diff --git a/libres/test-data/local/snake_oil/jobs/snake_oil_simulator.py b/libres/test-data/local/snake_oil/jobs/snake_oil_simulator.py new file mode 120000 index 00000000000..4e6d00ddcf3 --- /dev/null +++ b/libres/test-data/local/snake_oil/jobs/snake_oil_simulator.py @@ -0,0 +1 @@ +../../jobs_snake_oil/snake_oil_simulator.py \ No newline at end of file diff --git a/libres/test-data/local/snake_oil/observations/observations.txt b/libres/test-data/local/snake_oil/observations/observations.txt new file mode 100644 index 00000000000..483c1d59ab3 --- /dev/null +++ b/libres/test-data/local/snake_oil/observations/observations.txt @@ -0,0 +1,56 @@ +HISTORY_OBSERVATION FOPR; + +SUMMARY_OBSERVATION WOPR_OP1_9 +{ + VALUE = 0.1; + ERROR = 0.05; + RESTART = 9; + KEY = WOPR:OP1; +}; + +SUMMARY_OBSERVATION WOPR_OP1_36 +{ + VALUE = 0.7; + ERROR = 0.07; + RESTART = 36; + KEY = WOPR:OP1; +}; + +SUMMARY_OBSERVATION WOPR_OP1_72 +{ + VALUE = 0.5; + ERROR = 0.05; + RESTART = 72; + KEY = WOPR:OP1; +}; + +SUMMARY_OBSERVATION WOPR_OP1_108 +{ + VALUE = 0.3; + ERROR = 0.075; + RESTART = 108; + KEY = WOPR:OP1; +}; + +SUMMARY_OBSERVATION WOPR_OP1_144 +{ + VALUE = 0.2; + ERROR = 0.035; + RESTART = 144; + KEY = WOPR:OP1; +}; + +SUMMARY_OBSERVATION WOPR_OP1_190 +{ + VALUE = 0.015; + ERROR = 0.01; + RESTART = 190; + KEY = WOPR:OP1; +}; + +GENERAL_OBSERVATION WPR_DIFF_1 { + DATA = SNAKE_OIL_WPR_DIFF; + INDEX_LIST = 400,800,1200,1800; + RESTART = 199; + OBS_FILE = wpr_diff_obs.txt; +}; diff --git a/libres/test-data/local/snake_oil/observations/wpr_diff_obs.txt b/libres/test-data/local/snake_oil/observations/wpr_diff_obs.txt new file mode 100644 index 00000000000..4aee6d7ef7c --- /dev/null +++ b/libres/test-data/local/snake_oil/observations/wpr_diff_obs.txt @@ -0,0 +1,4 @@ +0.0 0.1 +0.1 0.2 +0.2 0.15 +0.0 0.05 diff --git a/libres/test-data/local/snake_oil/parameters/snake_oil_parameters.txt b/libres/test-data/local/snake_oil/parameters/snake_oil_parameters.txt new file mode 100644 index 00000000000..64573d0058b --- /dev/null +++ b/libres/test-data/local/snake_oil/parameters/snake_oil_parameters.txt @@ -0,0 +1,11 @@ +OP1_PERSISTENCE UNIFORM 0.01 0.4 +OP1_OCTAVES UNIFORM 3 5 +OP1_DIVERGENCE_SCALE UNIFORM 0.25 1.25 +OP1_OFFSET UNIFORM -0.1 0.1 +OP2_PERSISTENCE UNIFORM 0.1 0.6 +OP2_OCTAVES UNIFORM 5 12 +OP2_DIVERGENCE_SCALE UNIFORM 0.5 1.5 +OP2_OFFSET UNIFORM -0.2 0.2 +BPR_555_PERSISTENCE UNIFORM 0.1 0.5 +BPR_138_PERSISTENCE UNIFORM 0.2 0.7 + diff --git a/libres/test-data/local/snake_oil/refcase/SNAKE_OIL_FIELD.SMSPEC b/libres/test-data/local/snake_oil/refcase/SNAKE_OIL_FIELD.SMSPEC new file mode 100644 index 00000000000..c5aa629876e Binary files /dev/null and b/libres/test-data/local/snake_oil/refcase/SNAKE_OIL_FIELD.SMSPEC differ diff --git a/libres/test-data/local/snake_oil/refcase/SNAKE_OIL_FIELD.UNSMRY b/libres/test-data/local/snake_oil/refcase/SNAKE_OIL_FIELD.UNSMRY new file mode 100644 index 00000000000..854f523fa7e Binary files /dev/null and b/libres/test-data/local/snake_oil/refcase/SNAKE_OIL_FIELD.UNSMRY differ diff --git a/libres/test-data/local/snake_oil/refcase/refcase_readme.txt b/libres/test-data/local/snake_oil/refcase/refcase_readme.txt new file mode 100644 index 00000000000..a3d0fe60589 --- /dev/null +++ b/libres/test-data/local/snake_oil/refcase/refcase_readme.txt @@ -0,0 +1 @@ +To create a refcase run the snake_oil_simulator.py job with the this as working directory. \ No newline at end of file diff --git a/libres/test-data/local/snake_oil/refcase/seed.txt b/libres/test-data/local/snake_oil/refcase/seed.txt new file mode 100644 index 00000000000..0009f6e89a5 --- /dev/null +++ b/libres/test-data/local/snake_oil/refcase/seed.txt @@ -0,0 +1 @@ +SEED:268776 \ No newline at end of file diff --git a/libres/test-data/local/snake_oil/refcase/snake_oil_params.txt b/libres/test-data/local/snake_oil/refcase/snake_oil_params.txt new file mode 100644 index 00000000000..3868522924c --- /dev/null +++ b/libres/test-data/local/snake_oil/refcase/snake_oil_params.txt @@ -0,0 +1,10 @@ +OP1_PERSISTENCE:0.15 +OP1_OCTAVES:4 +OP1_DIVERGENCE_SCALE:0.5 +OP1_OFFSET:0.0 +OP2_PERSISTENCE:0.25 +OP2_OCTAVES:7.0 +OP2_DIVERGENCE_SCALE:1.0 +OP2_OFFSET:0.0 +BPR_555_PERSISTENCE:0.25 +BPR_138_PERSISTENCE:0.35 diff --git a/libres/test-data/local/snake_oil/refcase/time_map.txt b/libres/test-data/local/snake_oil/refcase/time_map.txt new file mode 100644 index 00000000000..d54b4293aec --- /dev/null +++ b/libres/test-data/local/snake_oil/refcase/time_map.txt @@ -0,0 +1,2000 @@ +01/01/2010 +02/01/2010 +03/01/2010 +04/01/2010 +05/01/2010 +06/01/2010 +07/01/2010 +08/01/2010 +09/01/2010 +10/01/2010 +11/01/2010 +12/01/2010 +13/01/2010 +14/01/2010 +15/01/2010 +16/01/2010 +17/01/2010 +18/01/2010 +19/01/2010 +20/01/2010 +21/01/2010 +22/01/2010 +23/01/2010 +24/01/2010 +25/01/2010 +26/01/2010 +27/01/2010 +28/01/2010 +29/01/2010 +30/01/2010 +31/01/2010 +01/02/2010 +02/02/2010 +03/02/2010 +04/02/2010 +05/02/2010 +06/02/2010 +07/02/2010 +08/02/2010 +09/02/2010 +10/02/2010 +11/02/2010 +12/02/2010 +13/02/2010 +14/02/2010 +15/02/2010 +16/02/2010 +17/02/2010 +18/02/2010 +19/02/2010 +20/02/2010 +21/02/2010 +22/02/2010 +23/02/2010 +24/02/2010 +25/02/2010 +26/02/2010 +27/02/2010 +28/02/2010 +01/03/2010 +02/03/2010 +03/03/2010 +04/03/2010 +05/03/2010 +06/03/2010 +07/03/2010 +08/03/2010 +09/03/2010 +10/03/2010 +11/03/2010 +12/03/2010 +13/03/2010 +14/03/2010 +15/03/2010 +16/03/2010 +17/03/2010 +18/03/2010 +19/03/2010 +20/03/2010 +21/03/2010 +22/03/2010 +23/03/2010 +24/03/2010 +25/03/2010 +26/03/2010 +27/03/2010 +28/03/2010 +29/03/2010 +30/03/2010 +31/03/2010 +01/04/2010 +02/04/2010 +03/04/2010 +04/04/2010 +05/04/2010 +06/04/2010 +07/04/2010 +08/04/2010 +09/04/2010 +10/04/2010 +11/04/2010 +12/04/2010 +13/04/2010 +14/04/2010 +15/04/2010 +16/04/2010 +17/04/2010 +18/04/2010 +19/04/2010 +20/04/2010 +21/04/2010 +22/04/2010 +23/04/2010 +24/04/2010 +25/04/2010 +26/04/2010 +27/04/2010 +28/04/2010 +29/04/2010 +30/04/2010 +01/05/2010 +02/05/2010 +03/05/2010 +04/05/2010 +05/05/2010 +06/05/2010 +07/05/2010 +08/05/2010 +09/05/2010 +10/05/2010 +11/05/2010 +12/05/2010 +13/05/2010 +14/05/2010 +15/05/2010 +16/05/2010 +17/05/2010 +18/05/2010 +19/05/2010 +20/05/2010 +21/05/2010 +22/05/2010 +23/05/2010 +24/05/2010 +25/05/2010 +26/05/2010 +27/05/2010 +28/05/2010 +29/05/2010 +30/05/2010 +31/05/2010 +01/06/2010 +02/06/2010 +03/06/2010 +04/06/2010 +05/06/2010 +06/06/2010 +07/06/2010 +08/06/2010 +09/06/2010 +10/06/2010 +11/06/2010 +12/06/2010 +13/06/2010 +14/06/2010 +15/06/2010 +16/06/2010 +17/06/2010 +18/06/2010 +19/06/2010 +20/06/2010 +21/06/2010 +22/06/2010 +23/06/2010 +24/06/2010 +25/06/2010 +26/06/2010 +27/06/2010 +28/06/2010 +29/06/2010 +30/06/2010 +01/07/2010 +02/07/2010 +03/07/2010 +04/07/2010 +05/07/2010 +06/07/2010 +07/07/2010 +08/07/2010 +09/07/2010 +10/07/2010 +11/07/2010 +12/07/2010 +13/07/2010 +14/07/2010 +15/07/2010 +16/07/2010 +17/07/2010 +18/07/2010 +19/07/2010 +20/07/2010 +21/07/2010 +22/07/2010 +23/07/2010 +24/07/2010 +25/07/2010 +26/07/2010 +27/07/2010 +28/07/2010 +29/07/2010 +30/07/2010 +31/07/2010 +01/08/2010 +02/08/2010 +03/08/2010 +04/08/2010 +05/08/2010 +06/08/2010 +07/08/2010 +08/08/2010 +09/08/2010 +10/08/2010 +11/08/2010 +12/08/2010 +13/08/2010 +14/08/2010 +15/08/2010 +16/08/2010 +17/08/2010 +18/08/2010 +19/08/2010 +20/08/2010 +21/08/2010 +22/08/2010 +23/08/2010 +24/08/2010 +25/08/2010 +26/08/2010 +27/08/2010 +28/08/2010 +29/08/2010 +30/08/2010 +31/08/2010 +01/09/2010 +02/09/2010 +03/09/2010 +04/09/2010 +05/09/2010 +06/09/2010 +07/09/2010 +08/09/2010 +09/09/2010 +10/09/2010 +11/09/2010 +12/09/2010 +13/09/2010 +14/09/2010 +15/09/2010 +16/09/2010 +17/09/2010 +18/09/2010 +19/09/2010 +20/09/2010 +21/09/2010 +22/09/2010 +23/09/2010 +24/09/2010 +25/09/2010 +26/09/2010 +27/09/2010 +28/09/2010 +29/09/2010 +30/09/2010 +01/10/2010 +02/10/2010 +03/10/2010 +04/10/2010 +05/10/2010 +06/10/2010 +07/10/2010 +08/10/2010 +09/10/2010 +10/10/2010 +11/10/2010 +12/10/2010 +13/10/2010 +14/10/2010 +15/10/2010 +16/10/2010 +17/10/2010 +18/10/2010 +19/10/2010 +20/10/2010 +21/10/2010 +22/10/2010 +23/10/2010 +24/10/2010 +25/10/2010 +26/10/2010 +27/10/2010 +28/10/2010 +29/10/2010 +30/10/2010 +31/10/2010 +01/11/2010 +02/11/2010 +03/11/2010 +04/11/2010 +05/11/2010 +06/11/2010 +07/11/2010 +08/11/2010 +09/11/2010 +10/11/2010 +11/11/2010 +12/11/2010 +13/11/2010 +14/11/2010 +15/11/2010 +16/11/2010 +17/11/2010 +18/11/2010 +19/11/2010 +20/11/2010 +21/11/2010 +22/11/2010 +23/11/2010 +24/11/2010 +25/11/2010 +26/11/2010 +27/11/2010 +28/11/2010 +29/11/2010 +30/11/2010 +01/12/2010 +02/12/2010 +03/12/2010 +04/12/2010 +05/12/2010 +06/12/2010 +07/12/2010 +08/12/2010 +09/12/2010 +10/12/2010 +11/12/2010 +12/12/2010 +13/12/2010 +14/12/2010 +15/12/2010 +16/12/2010 +17/12/2010 +18/12/2010 +19/12/2010 +20/12/2010 +21/12/2010 +22/12/2010 +23/12/2010 +24/12/2010 +25/12/2010 +26/12/2010 +27/12/2010 +28/12/2010 +29/12/2010 +30/12/2010 +31/12/2010 +01/01/2011 +02/01/2011 +03/01/2011 +04/01/2011 +05/01/2011 +06/01/2011 +07/01/2011 +08/01/2011 +09/01/2011 +10/01/2011 +11/01/2011 +12/01/2011 +13/01/2011 +14/01/2011 +15/01/2011 +16/01/2011 +17/01/2011 +18/01/2011 +19/01/2011 +20/01/2011 +21/01/2011 +22/01/2011 +23/01/2011 +24/01/2011 +25/01/2011 +26/01/2011 +27/01/2011 +28/01/2011 +29/01/2011 +30/01/2011 +31/01/2011 +01/02/2011 +02/02/2011 +03/02/2011 +04/02/2011 +05/02/2011 +06/02/2011 +07/02/2011 +08/02/2011 +09/02/2011 +10/02/2011 +11/02/2011 +12/02/2011 +13/02/2011 +14/02/2011 +15/02/2011 +16/02/2011 +17/02/2011 +18/02/2011 +19/02/2011 +20/02/2011 +21/02/2011 +22/02/2011 +23/02/2011 +24/02/2011 +25/02/2011 +26/02/2011 +27/02/2011 +28/02/2011 +01/03/2011 +02/03/2011 +03/03/2011 +04/03/2011 +05/03/2011 +06/03/2011 +07/03/2011 +08/03/2011 +09/03/2011 +10/03/2011 +11/03/2011 +12/03/2011 +13/03/2011 +14/03/2011 +15/03/2011 +16/03/2011 +17/03/2011 +18/03/2011 +19/03/2011 +20/03/2011 +21/03/2011 +22/03/2011 +23/03/2011 +24/03/2011 +25/03/2011 +26/03/2011 +27/03/2011 +28/03/2011 +29/03/2011 +30/03/2011 +31/03/2011 +01/04/2011 +02/04/2011 +03/04/2011 +04/04/2011 +05/04/2011 +06/04/2011 +07/04/2011 +08/04/2011 +09/04/2011 +10/04/2011 +11/04/2011 +12/04/2011 +13/04/2011 +14/04/2011 +15/04/2011 +16/04/2011 +17/04/2011 +18/04/2011 +19/04/2011 +20/04/2011 +21/04/2011 +22/04/2011 +23/04/2011 +24/04/2011 +25/04/2011 +26/04/2011 +27/04/2011 +28/04/2011 +29/04/2011 +30/04/2011 +01/05/2011 +02/05/2011 +03/05/2011 +04/05/2011 +05/05/2011 +06/05/2011 +07/05/2011 +08/05/2011 +09/05/2011 +10/05/2011 +11/05/2011 +12/05/2011 +13/05/2011 +14/05/2011 +15/05/2011 +16/05/2011 +17/05/2011 +18/05/2011 +19/05/2011 +20/05/2011 +21/05/2011 +22/05/2011 +23/05/2011 +24/05/2011 +25/05/2011 +26/05/2011 +27/05/2011 +28/05/2011 +29/05/2011 +30/05/2011 +31/05/2011 +01/06/2011 +02/06/2011 +03/06/2011 +04/06/2011 +05/06/2011 +06/06/2011 +07/06/2011 +08/06/2011 +09/06/2011 +10/06/2011 +11/06/2011 +12/06/2011 +13/06/2011 +14/06/2011 +15/06/2011 +16/06/2011 +17/06/2011 +18/06/2011 +19/06/2011 +20/06/2011 +21/06/2011 +22/06/2011 +23/06/2011 +24/06/2011 +25/06/2011 +26/06/2011 +27/06/2011 +28/06/2011 +29/06/2011 +30/06/2011 +01/07/2011 +02/07/2011 +03/07/2011 +04/07/2011 +05/07/2011 +06/07/2011 +07/07/2011 +08/07/2011 +09/07/2011 +10/07/2011 +11/07/2011 +12/07/2011 +13/07/2011 +14/07/2011 +15/07/2011 +16/07/2011 +17/07/2011 +18/07/2011 +19/07/2011 +20/07/2011 +21/07/2011 +22/07/2011 +23/07/2011 +24/07/2011 +25/07/2011 +26/07/2011 +27/07/2011 +28/07/2011 +29/07/2011 +30/07/2011 +31/07/2011 +01/08/2011 +02/08/2011 +03/08/2011 +04/08/2011 +05/08/2011 +06/08/2011 +07/08/2011 +08/08/2011 +09/08/2011 +10/08/2011 +11/08/2011 +12/08/2011 +13/08/2011 +14/08/2011 +15/08/2011 +16/08/2011 +17/08/2011 +18/08/2011 +19/08/2011 +20/08/2011 +21/08/2011 +22/08/2011 +23/08/2011 +24/08/2011 +25/08/2011 +26/08/2011 +27/08/2011 +28/08/2011 +29/08/2011 +30/08/2011 +31/08/2011 +01/09/2011 +02/09/2011 +03/09/2011 +04/09/2011 +05/09/2011 +06/09/2011 +07/09/2011 +08/09/2011 +09/09/2011 +10/09/2011 +11/09/2011 +12/09/2011 +13/09/2011 +14/09/2011 +15/09/2011 +16/09/2011 +17/09/2011 +18/09/2011 +19/09/2011 +20/09/2011 +21/09/2011 +22/09/2011 +23/09/2011 +24/09/2011 +25/09/2011 +26/09/2011 +27/09/2011 +28/09/2011 +29/09/2011 +30/09/2011 +01/10/2011 +02/10/2011 +03/10/2011 +04/10/2011 +05/10/2011 +06/10/2011 +07/10/2011 +08/10/2011 +09/10/2011 +10/10/2011 +11/10/2011 +12/10/2011 +13/10/2011 +14/10/2011 +15/10/2011 +16/10/2011 +17/10/2011 +18/10/2011 +19/10/2011 +20/10/2011 +21/10/2011 +22/10/2011 +23/10/2011 +24/10/2011 +25/10/2011 +26/10/2011 +27/10/2011 +28/10/2011 +29/10/2011 +30/10/2011 +31/10/2011 +01/11/2011 +02/11/2011 +03/11/2011 +04/11/2011 +05/11/2011 +06/11/2011 +07/11/2011 +08/11/2011 +09/11/2011 +10/11/2011 +11/11/2011 +12/11/2011 +13/11/2011 +14/11/2011 +15/11/2011 +16/11/2011 +17/11/2011 +18/11/2011 +19/11/2011 +20/11/2011 +21/11/2011 +22/11/2011 +23/11/2011 +24/11/2011 +25/11/2011 +26/11/2011 +27/11/2011 +28/11/2011 +29/11/2011 +30/11/2011 +01/12/2011 +02/12/2011 +03/12/2011 +04/12/2011 +05/12/2011 +06/12/2011 +07/12/2011 +08/12/2011 +09/12/2011 +10/12/2011 +11/12/2011 +12/12/2011 +13/12/2011 +14/12/2011 +15/12/2011 +16/12/2011 +17/12/2011 +18/12/2011 +19/12/2011 +20/12/2011 +21/12/2011 +22/12/2011 +23/12/2011 +24/12/2011 +25/12/2011 +26/12/2011 +27/12/2011 +28/12/2011 +29/12/2011 +30/12/2011 +31/12/2011 +01/01/2012 +02/01/2012 +03/01/2012 +04/01/2012 +05/01/2012 +06/01/2012 +07/01/2012 +08/01/2012 +09/01/2012 +10/01/2012 +11/01/2012 +12/01/2012 +13/01/2012 +14/01/2012 +15/01/2012 +16/01/2012 +17/01/2012 +18/01/2012 +19/01/2012 +20/01/2012 +21/01/2012 +22/01/2012 +23/01/2012 +24/01/2012 +25/01/2012 +26/01/2012 +27/01/2012 +28/01/2012 +29/01/2012 +30/01/2012 +31/01/2012 +01/02/2012 +02/02/2012 +03/02/2012 +04/02/2012 +05/02/2012 +06/02/2012 +07/02/2012 +08/02/2012 +09/02/2012 +10/02/2012 +11/02/2012 +12/02/2012 +13/02/2012 +14/02/2012 +15/02/2012 +16/02/2012 +17/02/2012 +18/02/2012 +19/02/2012 +20/02/2012 +21/02/2012 +22/02/2012 +23/02/2012 +24/02/2012 +25/02/2012 +26/02/2012 +27/02/2012 +28/02/2012 +29/02/2012 +01/03/2012 +02/03/2012 +03/03/2012 +04/03/2012 +05/03/2012 +06/03/2012 +07/03/2012 +08/03/2012 +09/03/2012 +10/03/2012 +11/03/2012 +12/03/2012 +13/03/2012 +14/03/2012 +15/03/2012 +16/03/2012 +17/03/2012 +18/03/2012 +19/03/2012 +20/03/2012 +21/03/2012 +22/03/2012 +23/03/2012 +24/03/2012 +25/03/2012 +26/03/2012 +27/03/2012 +28/03/2012 +29/03/2012 +30/03/2012 +31/03/2012 +01/04/2012 +02/04/2012 +03/04/2012 +04/04/2012 +05/04/2012 +06/04/2012 +07/04/2012 +08/04/2012 +09/04/2012 +10/04/2012 +11/04/2012 +12/04/2012 +13/04/2012 +14/04/2012 +15/04/2012 +16/04/2012 +17/04/2012 +18/04/2012 +19/04/2012 +20/04/2012 +21/04/2012 +22/04/2012 +23/04/2012 +24/04/2012 +25/04/2012 +26/04/2012 +27/04/2012 +28/04/2012 +29/04/2012 +30/04/2012 +01/05/2012 +02/05/2012 +03/05/2012 +04/05/2012 +05/05/2012 +06/05/2012 +07/05/2012 +08/05/2012 +09/05/2012 +10/05/2012 +11/05/2012 +12/05/2012 +13/05/2012 +14/05/2012 +15/05/2012 +16/05/2012 +17/05/2012 +18/05/2012 +19/05/2012 +20/05/2012 +21/05/2012 +22/05/2012 +23/05/2012 +24/05/2012 +25/05/2012 +26/05/2012 +27/05/2012 +28/05/2012 +29/05/2012 +30/05/2012 +31/05/2012 +01/06/2012 +02/06/2012 +03/06/2012 +04/06/2012 +05/06/2012 +06/06/2012 +07/06/2012 +08/06/2012 +09/06/2012 +10/06/2012 +11/06/2012 +12/06/2012 +13/06/2012 +14/06/2012 +15/06/2012 +16/06/2012 +17/06/2012 +18/06/2012 +19/06/2012 +20/06/2012 +21/06/2012 +22/06/2012 +23/06/2012 +24/06/2012 +25/06/2012 +26/06/2012 +27/06/2012 +28/06/2012 +29/06/2012 +30/06/2012 +01/07/2012 +02/07/2012 +03/07/2012 +04/07/2012 +05/07/2012 +06/07/2012 +07/07/2012 +08/07/2012 +09/07/2012 +10/07/2012 +11/07/2012 +12/07/2012 +13/07/2012 +14/07/2012 +15/07/2012 +16/07/2012 +17/07/2012 +18/07/2012 +19/07/2012 +20/07/2012 +21/07/2012 +22/07/2012 +23/07/2012 +24/07/2012 +25/07/2012 +26/07/2012 +27/07/2012 +28/07/2012 +29/07/2012 +30/07/2012 +31/07/2012 +01/08/2012 +02/08/2012 +03/08/2012 +04/08/2012 +05/08/2012 +06/08/2012 +07/08/2012 +08/08/2012 +09/08/2012 +10/08/2012 +11/08/2012 +12/08/2012 +13/08/2012 +14/08/2012 +15/08/2012 +16/08/2012 +17/08/2012 +18/08/2012 +19/08/2012 +20/08/2012 +21/08/2012 +22/08/2012 +23/08/2012 +24/08/2012 +25/08/2012 +26/08/2012 +27/08/2012 +28/08/2012 +29/08/2012 +30/08/2012 +31/08/2012 +01/09/2012 +02/09/2012 +03/09/2012 +04/09/2012 +05/09/2012 +06/09/2012 +07/09/2012 +08/09/2012 +09/09/2012 +10/09/2012 +11/09/2012 +12/09/2012 +13/09/2012 +14/09/2012 +15/09/2012 +16/09/2012 +17/09/2012 +18/09/2012 +19/09/2012 +20/09/2012 +21/09/2012 +22/09/2012 +23/09/2012 +24/09/2012 +25/09/2012 +26/09/2012 +27/09/2012 +28/09/2012 +29/09/2012 +30/09/2012 +01/10/2012 +02/10/2012 +03/10/2012 +04/10/2012 +05/10/2012 +06/10/2012 +07/10/2012 +08/10/2012 +09/10/2012 +10/10/2012 +11/10/2012 +12/10/2012 +13/10/2012 +14/10/2012 +15/10/2012 +16/10/2012 +17/10/2012 +18/10/2012 +19/10/2012 +20/10/2012 +21/10/2012 +22/10/2012 +23/10/2012 +24/10/2012 +25/10/2012 +26/10/2012 +27/10/2012 +28/10/2012 +29/10/2012 +30/10/2012 +31/10/2012 +01/11/2012 +02/11/2012 +03/11/2012 +04/11/2012 +05/11/2012 +06/11/2012 +07/11/2012 +08/11/2012 +09/11/2012 +10/11/2012 +11/11/2012 +12/11/2012 +13/11/2012 +14/11/2012 +15/11/2012 +16/11/2012 +17/11/2012 +18/11/2012 +19/11/2012 +20/11/2012 +21/11/2012 +22/11/2012 +23/11/2012 +24/11/2012 +25/11/2012 +26/11/2012 +27/11/2012 +28/11/2012 +29/11/2012 +30/11/2012 +01/12/2012 +02/12/2012 +03/12/2012 +04/12/2012 +05/12/2012 +06/12/2012 +07/12/2012 +08/12/2012 +09/12/2012 +10/12/2012 +11/12/2012 +12/12/2012 +13/12/2012 +14/12/2012 +15/12/2012 +16/12/2012 +17/12/2012 +18/12/2012 +19/12/2012 +20/12/2012 +21/12/2012 +22/12/2012 +23/12/2012 +24/12/2012 +25/12/2012 +26/12/2012 +27/12/2012 +28/12/2012 +29/12/2012 +30/12/2012 +31/12/2012 +01/01/2013 +02/01/2013 +03/01/2013 +04/01/2013 +05/01/2013 +06/01/2013 +07/01/2013 +08/01/2013 +09/01/2013 +10/01/2013 +11/01/2013 +12/01/2013 +13/01/2013 +14/01/2013 +15/01/2013 +16/01/2013 +17/01/2013 +18/01/2013 +19/01/2013 +20/01/2013 +21/01/2013 +22/01/2013 +23/01/2013 +24/01/2013 +25/01/2013 +26/01/2013 +27/01/2013 +28/01/2013 +29/01/2013 +30/01/2013 +31/01/2013 +01/02/2013 +02/02/2013 +03/02/2013 +04/02/2013 +05/02/2013 +06/02/2013 +07/02/2013 +08/02/2013 +09/02/2013 +10/02/2013 +11/02/2013 +12/02/2013 +13/02/2013 +14/02/2013 +15/02/2013 +16/02/2013 +17/02/2013 +18/02/2013 +19/02/2013 +20/02/2013 +21/02/2013 +22/02/2013 +23/02/2013 +24/02/2013 +25/02/2013 +26/02/2013 +27/02/2013 +28/02/2013 +01/03/2013 +02/03/2013 +03/03/2013 +04/03/2013 +05/03/2013 +06/03/2013 +07/03/2013 +08/03/2013 +09/03/2013 +10/03/2013 +11/03/2013 +12/03/2013 +13/03/2013 +14/03/2013 +15/03/2013 +16/03/2013 +17/03/2013 +18/03/2013 +19/03/2013 +20/03/2013 +21/03/2013 +22/03/2013 +23/03/2013 +24/03/2013 +25/03/2013 +26/03/2013 +27/03/2013 +28/03/2013 +29/03/2013 +30/03/2013 +31/03/2013 +01/04/2013 +02/04/2013 +03/04/2013 +04/04/2013 +05/04/2013 +06/04/2013 +07/04/2013 +08/04/2013 +09/04/2013 +10/04/2013 +11/04/2013 +12/04/2013 +13/04/2013 +14/04/2013 +15/04/2013 +16/04/2013 +17/04/2013 +18/04/2013 +19/04/2013 +20/04/2013 +21/04/2013 +22/04/2013 +23/04/2013 +24/04/2013 +25/04/2013 +26/04/2013 +27/04/2013 +28/04/2013 +29/04/2013 +30/04/2013 +01/05/2013 +02/05/2013 +03/05/2013 +04/05/2013 +05/05/2013 +06/05/2013 +07/05/2013 +08/05/2013 +09/05/2013 +10/05/2013 +11/05/2013 +12/05/2013 +13/05/2013 +14/05/2013 +15/05/2013 +16/05/2013 +17/05/2013 +18/05/2013 +19/05/2013 +20/05/2013 +21/05/2013 +22/05/2013 +23/05/2013 +24/05/2013 +25/05/2013 +26/05/2013 +27/05/2013 +28/05/2013 +29/05/2013 +30/05/2013 +31/05/2013 +01/06/2013 +02/06/2013 +03/06/2013 +04/06/2013 +05/06/2013 +06/06/2013 +07/06/2013 +08/06/2013 +09/06/2013 +10/06/2013 +11/06/2013 +12/06/2013 +13/06/2013 +14/06/2013 +15/06/2013 +16/06/2013 +17/06/2013 +18/06/2013 +19/06/2013 +20/06/2013 +21/06/2013 +22/06/2013 +23/06/2013 +24/06/2013 +25/06/2013 +26/06/2013 +27/06/2013 +28/06/2013 +29/06/2013 +30/06/2013 +01/07/2013 +02/07/2013 +03/07/2013 +04/07/2013 +05/07/2013 +06/07/2013 +07/07/2013 +08/07/2013 +09/07/2013 +10/07/2013 +11/07/2013 +12/07/2013 +13/07/2013 +14/07/2013 +15/07/2013 +16/07/2013 +17/07/2013 +18/07/2013 +19/07/2013 +20/07/2013 +21/07/2013 +22/07/2013 +23/07/2013 +24/07/2013 +25/07/2013 +26/07/2013 +27/07/2013 +28/07/2013 +29/07/2013 +30/07/2013 +31/07/2013 +01/08/2013 +02/08/2013 +03/08/2013 +04/08/2013 +05/08/2013 +06/08/2013 +07/08/2013 +08/08/2013 +09/08/2013 +10/08/2013 +11/08/2013 +12/08/2013 +13/08/2013 +14/08/2013 +15/08/2013 +16/08/2013 +17/08/2013 +18/08/2013 +19/08/2013 +20/08/2013 +21/08/2013 +22/08/2013 +23/08/2013 +24/08/2013 +25/08/2013 +26/08/2013 +27/08/2013 +28/08/2013 +29/08/2013 +30/08/2013 +31/08/2013 +01/09/2013 +02/09/2013 +03/09/2013 +04/09/2013 +05/09/2013 +06/09/2013 +07/09/2013 +08/09/2013 +09/09/2013 +10/09/2013 +11/09/2013 +12/09/2013 +13/09/2013 +14/09/2013 +15/09/2013 +16/09/2013 +17/09/2013 +18/09/2013 +19/09/2013 +20/09/2013 +21/09/2013 +22/09/2013 +23/09/2013 +24/09/2013 +25/09/2013 +26/09/2013 +27/09/2013 +28/09/2013 +29/09/2013 +30/09/2013 +01/10/2013 +02/10/2013 +03/10/2013 +04/10/2013 +05/10/2013 +06/10/2013 +07/10/2013 +08/10/2013 +09/10/2013 +10/10/2013 +11/10/2013 +12/10/2013 +13/10/2013 +14/10/2013 +15/10/2013 +16/10/2013 +17/10/2013 +18/10/2013 +19/10/2013 +20/10/2013 +21/10/2013 +22/10/2013 +23/10/2013 +24/10/2013 +25/10/2013 +26/10/2013 +27/10/2013 +28/10/2013 +29/10/2013 +30/10/2013 +31/10/2013 +01/11/2013 +02/11/2013 +03/11/2013 +04/11/2013 +05/11/2013 +06/11/2013 +07/11/2013 +08/11/2013 +09/11/2013 +10/11/2013 +11/11/2013 +12/11/2013 +13/11/2013 +14/11/2013 +15/11/2013 +16/11/2013 +17/11/2013 +18/11/2013 +19/11/2013 +20/11/2013 +21/11/2013 +22/11/2013 +23/11/2013 +24/11/2013 +25/11/2013 +26/11/2013 +27/11/2013 +28/11/2013 +29/11/2013 +30/11/2013 +01/12/2013 +02/12/2013 +03/12/2013 +04/12/2013 +05/12/2013 +06/12/2013 +07/12/2013 +08/12/2013 +09/12/2013 +10/12/2013 +11/12/2013 +12/12/2013 +13/12/2013 +14/12/2013 +15/12/2013 +16/12/2013 +17/12/2013 +18/12/2013 +19/12/2013 +20/12/2013 +21/12/2013 +22/12/2013 +23/12/2013 +24/12/2013 +25/12/2013 +26/12/2013 +27/12/2013 +28/12/2013 +29/12/2013 +30/12/2013 +31/12/2013 +01/01/2014 +02/01/2014 +03/01/2014 +04/01/2014 +05/01/2014 +06/01/2014 +07/01/2014 +08/01/2014 +09/01/2014 +10/01/2014 +11/01/2014 +12/01/2014 +13/01/2014 +14/01/2014 +15/01/2014 +16/01/2014 +17/01/2014 +18/01/2014 +19/01/2014 +20/01/2014 +21/01/2014 +22/01/2014 +23/01/2014 +24/01/2014 +25/01/2014 +26/01/2014 +27/01/2014 +28/01/2014 +29/01/2014 +30/01/2014 +31/01/2014 +01/02/2014 +02/02/2014 +03/02/2014 +04/02/2014 +05/02/2014 +06/02/2014 +07/02/2014 +08/02/2014 +09/02/2014 +10/02/2014 +11/02/2014 +12/02/2014 +13/02/2014 +14/02/2014 +15/02/2014 +16/02/2014 +17/02/2014 +18/02/2014 +19/02/2014 +20/02/2014 +21/02/2014 +22/02/2014 +23/02/2014 +24/02/2014 +25/02/2014 +26/02/2014 +27/02/2014 +28/02/2014 +01/03/2014 +02/03/2014 +03/03/2014 +04/03/2014 +05/03/2014 +06/03/2014 +07/03/2014 +08/03/2014 +09/03/2014 +10/03/2014 +11/03/2014 +12/03/2014 +13/03/2014 +14/03/2014 +15/03/2014 +16/03/2014 +17/03/2014 +18/03/2014 +19/03/2014 +20/03/2014 +21/03/2014 +22/03/2014 +23/03/2014 +24/03/2014 +25/03/2014 +26/03/2014 +27/03/2014 +28/03/2014 +29/03/2014 +30/03/2014 +31/03/2014 +01/04/2014 +02/04/2014 +03/04/2014 +04/04/2014 +05/04/2014 +06/04/2014 +07/04/2014 +08/04/2014 +09/04/2014 +10/04/2014 +11/04/2014 +12/04/2014 +13/04/2014 +14/04/2014 +15/04/2014 +16/04/2014 +17/04/2014 +18/04/2014 +19/04/2014 +20/04/2014 +21/04/2014 +22/04/2014 +23/04/2014 +24/04/2014 +25/04/2014 +26/04/2014 +27/04/2014 +28/04/2014 +29/04/2014 +30/04/2014 +01/05/2014 +02/05/2014 +03/05/2014 +04/05/2014 +05/05/2014 +06/05/2014 +07/05/2014 +08/05/2014 +09/05/2014 +10/05/2014 +11/05/2014 +12/05/2014 +13/05/2014 +14/05/2014 +15/05/2014 +16/05/2014 +17/05/2014 +18/05/2014 +19/05/2014 +20/05/2014 +21/05/2014 +22/05/2014 +23/05/2014 +24/05/2014 +25/05/2014 +26/05/2014 +27/05/2014 +28/05/2014 +29/05/2014 +30/05/2014 +31/05/2014 +01/06/2014 +02/06/2014 +03/06/2014 +04/06/2014 +05/06/2014 +06/06/2014 +07/06/2014 +08/06/2014 +09/06/2014 +10/06/2014 +11/06/2014 +12/06/2014 +13/06/2014 +14/06/2014 +15/06/2014 +16/06/2014 +17/06/2014 +18/06/2014 +19/06/2014 +20/06/2014 +21/06/2014 +22/06/2014 +23/06/2014 +24/06/2014 +25/06/2014 +26/06/2014 +27/06/2014 +28/06/2014 +29/06/2014 +30/06/2014 +01/07/2014 +02/07/2014 +03/07/2014 +04/07/2014 +05/07/2014 +06/07/2014 +07/07/2014 +08/07/2014 +09/07/2014 +10/07/2014 +11/07/2014 +12/07/2014 +13/07/2014 +14/07/2014 +15/07/2014 +16/07/2014 +17/07/2014 +18/07/2014 +19/07/2014 +20/07/2014 +21/07/2014 +22/07/2014 +23/07/2014 +24/07/2014 +25/07/2014 +26/07/2014 +27/07/2014 +28/07/2014 +29/07/2014 +30/07/2014 +31/07/2014 +01/08/2014 +02/08/2014 +03/08/2014 +04/08/2014 +05/08/2014 +06/08/2014 +07/08/2014 +08/08/2014 +09/08/2014 +10/08/2014 +11/08/2014 +12/08/2014 +13/08/2014 +14/08/2014 +15/08/2014 +16/08/2014 +17/08/2014 +18/08/2014 +19/08/2014 +20/08/2014 +21/08/2014 +22/08/2014 +23/08/2014 +24/08/2014 +25/08/2014 +26/08/2014 +27/08/2014 +28/08/2014 +29/08/2014 +30/08/2014 +31/08/2014 +01/09/2014 +02/09/2014 +03/09/2014 +04/09/2014 +05/09/2014 +06/09/2014 +07/09/2014 +08/09/2014 +09/09/2014 +10/09/2014 +11/09/2014 +12/09/2014 +13/09/2014 +14/09/2014 +15/09/2014 +16/09/2014 +17/09/2014 +18/09/2014 +19/09/2014 +20/09/2014 +21/09/2014 +22/09/2014 +23/09/2014 +24/09/2014 +25/09/2014 +26/09/2014 +27/09/2014 +28/09/2014 +29/09/2014 +30/09/2014 +01/10/2014 +02/10/2014 +03/10/2014 +04/10/2014 +05/10/2014 +06/10/2014 +07/10/2014 +08/10/2014 +09/10/2014 +10/10/2014 +11/10/2014 +12/10/2014 +13/10/2014 +14/10/2014 +15/10/2014 +16/10/2014 +17/10/2014 +18/10/2014 +19/10/2014 +20/10/2014 +21/10/2014 +22/10/2014 +23/10/2014 +24/10/2014 +25/10/2014 +26/10/2014 +27/10/2014 +28/10/2014 +29/10/2014 +30/10/2014 +31/10/2014 +01/11/2014 +02/11/2014 +03/11/2014 +04/11/2014 +05/11/2014 +06/11/2014 +07/11/2014 +08/11/2014 +09/11/2014 +10/11/2014 +11/11/2014 +12/11/2014 +13/11/2014 +14/11/2014 +15/11/2014 +16/11/2014 +17/11/2014 +18/11/2014 +19/11/2014 +20/11/2014 +21/11/2014 +22/11/2014 +23/11/2014 +24/11/2014 +25/11/2014 +26/11/2014 +27/11/2014 +28/11/2014 +29/11/2014 +30/11/2014 +01/12/2014 +02/12/2014 +03/12/2014 +04/12/2014 +05/12/2014 +06/12/2014 +07/12/2014 +08/12/2014 +09/12/2014 +10/12/2014 +11/12/2014 +12/12/2014 +13/12/2014 +14/12/2014 +15/12/2014 +16/12/2014 +17/12/2014 +18/12/2014 +19/12/2014 +20/12/2014 +21/12/2014 +22/12/2014 +23/12/2014 +24/12/2014 +25/12/2014 +26/12/2014 +27/12/2014 +28/12/2014 +29/12/2014 +30/12/2014 +31/12/2014 +01/01/2015 +02/01/2015 +03/01/2015 +04/01/2015 +05/01/2015 +06/01/2015 +07/01/2015 +08/01/2015 +09/01/2015 +10/01/2015 +11/01/2015 +12/01/2015 +13/01/2015 +14/01/2015 +15/01/2015 +16/01/2015 +17/01/2015 +18/01/2015 +19/01/2015 +20/01/2015 +21/01/2015 +22/01/2015 +23/01/2015 +24/01/2015 +25/01/2015 +26/01/2015 +27/01/2015 +28/01/2015 +29/01/2015 +30/01/2015 +31/01/2015 +01/02/2015 +02/02/2015 +03/02/2015 +04/02/2015 +05/02/2015 +06/02/2015 +07/02/2015 +08/02/2015 +09/02/2015 +10/02/2015 +11/02/2015 +12/02/2015 +13/02/2015 +14/02/2015 +15/02/2015 +16/02/2015 +17/02/2015 +18/02/2015 +19/02/2015 +20/02/2015 +21/02/2015 +22/02/2015 +23/02/2015 +24/02/2015 +25/02/2015 +26/02/2015 +27/02/2015 +28/02/2015 +01/03/2015 +02/03/2015 +03/03/2015 +04/03/2015 +05/03/2015 +06/03/2015 +07/03/2015 +08/03/2015 +09/03/2015 +10/03/2015 +11/03/2015 +12/03/2015 +13/03/2015 +14/03/2015 +15/03/2015 +16/03/2015 +17/03/2015 +18/03/2015 +19/03/2015 +20/03/2015 +21/03/2015 +22/03/2015 +23/03/2015 +24/03/2015 +25/03/2015 +26/03/2015 +27/03/2015 +28/03/2015 +29/03/2015 +30/03/2015 +31/03/2015 +01/04/2015 +02/04/2015 +03/04/2015 +04/04/2015 +05/04/2015 +06/04/2015 +07/04/2015 +08/04/2015 +09/04/2015 +10/04/2015 +11/04/2015 +12/04/2015 +13/04/2015 +14/04/2015 +15/04/2015 +16/04/2015 +17/04/2015 +18/04/2015 +19/04/2015 +20/04/2015 +21/04/2015 +22/04/2015 +23/04/2015 +24/04/2015 +25/04/2015 +26/04/2015 +27/04/2015 +28/04/2015 +29/04/2015 +30/04/2015 +01/05/2015 +02/05/2015 +03/05/2015 +04/05/2015 +05/05/2015 +06/05/2015 +07/05/2015 +08/05/2015 +09/05/2015 +10/05/2015 +11/05/2015 +12/05/2015 +13/05/2015 +14/05/2015 +15/05/2015 +16/05/2015 +17/05/2015 +18/05/2015 +19/05/2015 +20/05/2015 +21/05/2015 +22/05/2015 +23/05/2015 +24/05/2015 +25/05/2015 +26/05/2015 +27/05/2015 +28/05/2015 +29/05/2015 +30/05/2015 +31/05/2015 +01/06/2015 +02/06/2015 +03/06/2015 +04/06/2015 +05/06/2015 +06/06/2015 +07/06/2015 +08/06/2015 +09/06/2015 +10/06/2015 +11/06/2015 +12/06/2015 +13/06/2015 +14/06/2015 +15/06/2015 +16/06/2015 +17/06/2015 +18/06/2015 +19/06/2015 +20/06/2015 +21/06/2015 +22/06/2015 +23/06/2015 diff --git a/libres/test-data/local/snake_oil/snake_oil.ert b/libres/test-data/local/snake_oil/snake_oil.ert new file mode 100644 index 00000000000..d993ad54bed --- /dev/null +++ b/libres/test-data/local/snake_oil/snake_oil.ert @@ -0,0 +1,42 @@ +QUEUE_SYSTEM LOCAL + +NUM_REALIZATIONS 25 + +DEFINE storage/ +RANDOM_SEED 3593114179000630026631423308983283277868 + +RUNPATH /runpath/realisation-%d/iter-%d +ENSPATH /ensemble +ECLBASE SNAKE_OIL_FIELD +SUMMARY * + +HISTORY_SOURCE REFCASE_HISTORY +REFCASE refcase/SNAKE_OIL_FIELD + +TIME_MAP refcase/time_map.txt +OBS_CONFIG observations/observations.txt + +INSTALL_JOB SNAKE_OIL_SIMULATOR jobs/SNAKE_OIL_SIMULATOR +INSTALL_JOB SNAKE_OIL_NPV jobs/SNAKE_OIL_NPV +INSTALL_JOB SNAKE_OIL_DIFF jobs/SNAKE_OIL_DIFF + +FORWARD_MODEL SNAKE_OIL_SIMULATOR +FORWARD_MODEL SNAKE_OIL_NPV +FORWARD_MODEL SNAKE_OIL_DIFF + +RUN_TEMPLATE templates/seed_template.txt seed.txt +GEN_KW SNAKE_OIL_PARAM templates/snake_oil_template.txt snake_oil_params.txt parameters/snake_oil_parameters.txt + +GEN_DATA SNAKE_OIL_OPR_DIFF INPUT_FORMAT:ASCII RESULT_FILE:snake_oil_opr_diff_%d.txt REPORT_STEPS:199 +GEN_DATA SNAKE_OIL_WPR_DIFF INPUT_FORMAT:ASCII RESULT_FILE:snake_oil_wpr_diff_%d.txt REPORT_STEPS:199 +GEN_DATA SNAKE_OIL_GPR_DIFF INPUT_FORMAT:ASCII RESULT_FILE:snake_oil_gpr_diff_%d.txt REPORT_STEPS:199 + +LOG_LEVEL INFO +UPDATE_LOG_PATH log/update + +UMASK 0022 + +SETENV SILLY_VAR silly-valye +SETENV OPTIONAL_VAR optional-value + +LICENSE_PATH some/random/path diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/current_case b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/current_case new file mode 100644 index 00000000000..abe2ad0fa39 --- /dev/null +++ b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/current_case @@ -0,0 +1 @@ +default_0 \ No newline at end of file diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/0199/files/SNAKE_OIL_GPR_DIFF_active b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/0199/files/SNAKE_OIL_GPR_DIFF_active new file mode 100644 index 00000000000..200047ee9ed Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/0199/files/SNAKE_OIL_GPR_DIFF_active differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/0199/files/SNAKE_OIL_OPR_DIFF_active b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/0199/files/SNAKE_OIL_OPR_DIFF_active new file mode 100644 index 00000000000..200047ee9ed Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/0199/files/SNAKE_OIL_OPR_DIFF_active differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/0199/files/SNAKE_OIL_WPR_DIFF_active b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/0199/files/SNAKE_OIL_WPR_DIFF_active new file mode 100644 index 00000000000..200047ee9ed Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/0199/files/SNAKE_OIL_WPR_DIFF_active differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/FORECAST.data_0 new file mode 100644 index 00000000000..f903ae21cf7 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/FORECAST.index new file mode 100644 index 00000000000..ea3e7b2759c Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/PARAMETER.data_0 new file mode 100644 index 00000000000..914e758e0ab Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/PARAMETER.index new file mode 100644 index 00000000000..394a6acddb2 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_0/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/FORECAST.data_0 new file mode 100644 index 00000000000..a0c416f1a23 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/FORECAST.index new file mode 100644 index 00000000000..b13f099382d Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/PARAMETER.data_0 new file mode 100644 index 00000000000..c729e0adca4 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/PARAMETER.index new file mode 100644 index 00000000000..7faf5e3e70c Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_1/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/FORECAST.data_0 new file mode 100644 index 00000000000..b3afe7d7da3 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/FORECAST.index new file mode 100644 index 00000000000..4c4f0387431 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/PARAMETER.data_0 new file mode 100644 index 00000000000..89089d51ff0 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/PARAMETER.index new file mode 100644 index 00000000000..9ae730ac13b Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_10/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/FORECAST.data_0 new file mode 100644 index 00000000000..e449618e192 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/FORECAST.index new file mode 100644 index 00000000000..fcba30db17c Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/PARAMETER.data_0 new file mode 100644 index 00000000000..8901a336c24 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/PARAMETER.index new file mode 100644 index 00000000000..a1761eace25 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_11/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/FORECAST.data_0 new file mode 100644 index 00000000000..bda1686421e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/FORECAST.index new file mode 100644 index 00000000000..74511a9abc7 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/PARAMETER.data_0 new file mode 100644 index 00000000000..38f6f229bea Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/PARAMETER.index new file mode 100644 index 00000000000..7ed2b239bae Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_12/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/FORECAST.data_0 new file mode 100644 index 00000000000..e3e9a7b88f8 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/FORECAST.index new file mode 100644 index 00000000000..3f775f88538 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/PARAMETER.data_0 new file mode 100644 index 00000000000..e81f2dac3ad Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/PARAMETER.index new file mode 100644 index 00000000000..eb7291b5874 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_13/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/FORECAST.data_0 new file mode 100644 index 00000000000..e13ec6e9e90 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/FORECAST.index new file mode 100644 index 00000000000..56143a90b44 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/PARAMETER.data_0 new file mode 100644 index 00000000000..7abd7ab1164 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/PARAMETER.index new file mode 100644 index 00000000000..5e147e8afa3 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_14/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/FORECAST.data_0 new file mode 100644 index 00000000000..f7a62199f51 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/FORECAST.index new file mode 100644 index 00000000000..fa19a420df3 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/PARAMETER.data_0 new file mode 100644 index 00000000000..d5a3cc93ac1 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/PARAMETER.index new file mode 100644 index 00000000000..3e91b068669 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_15/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/FORECAST.data_0 new file mode 100644 index 00000000000..1255e298ae8 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/FORECAST.index new file mode 100644 index 00000000000..14605408296 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/PARAMETER.data_0 new file mode 100644 index 00000000000..0f7fb022cec Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/PARAMETER.index new file mode 100644 index 00000000000..863806367a5 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_16/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/FORECAST.data_0 new file mode 100644 index 00000000000..6cccb3e4dc0 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/FORECAST.index new file mode 100644 index 00000000000..f1908387fbe Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/PARAMETER.data_0 new file mode 100644 index 00000000000..e6042a0cb4e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/PARAMETER.index new file mode 100644 index 00000000000..796c562bd65 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_17/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/FORECAST.data_0 new file mode 100644 index 00000000000..5c1128da5f4 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/FORECAST.index new file mode 100644 index 00000000000..3c5f22a423b Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/PARAMETER.data_0 new file mode 100644 index 00000000000..5ac5e6633ba Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/PARAMETER.index new file mode 100644 index 00000000000..31f31b7c812 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_18/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/FORECAST.data_0 new file mode 100644 index 00000000000..eb0f3462533 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/FORECAST.index new file mode 100644 index 00000000000..7181e4036c2 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/PARAMETER.data_0 new file mode 100644 index 00000000000..b9393bd3d16 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/PARAMETER.index new file mode 100644 index 00000000000..bc3f4758ad6 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_19/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/FORECAST.data_0 new file mode 100644 index 00000000000..d684b0e58af Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/FORECAST.index new file mode 100644 index 00000000000..560a8225276 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/PARAMETER.data_0 new file mode 100644 index 00000000000..1b78159dc12 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/PARAMETER.index new file mode 100644 index 00000000000..98e78da5688 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_2/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/FORECAST.data_0 new file mode 100644 index 00000000000..0193c9858fe Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/FORECAST.index new file mode 100644 index 00000000000..62cfffa12c9 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/PARAMETER.data_0 new file mode 100644 index 00000000000..f804b9c01ba Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/PARAMETER.index new file mode 100644 index 00000000000..bacebc6b31e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_20/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/FORECAST.data_0 new file mode 100644 index 00000000000..d0f0b09d962 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/FORECAST.index new file mode 100644 index 00000000000..827472ecd2a Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/PARAMETER.data_0 new file mode 100644 index 00000000000..ba2c8fcb430 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/PARAMETER.index new file mode 100644 index 00000000000..bbc36c70459 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_21/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/FORECAST.data_0 new file mode 100644 index 00000000000..38299e9badb Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/FORECAST.index new file mode 100644 index 00000000000..93b2a8a40f8 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/PARAMETER.data_0 new file mode 100644 index 00000000000..0be9c7bd1b4 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/PARAMETER.index new file mode 100644 index 00000000000..8775a960fb1 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_22/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/FORECAST.data_0 new file mode 100644 index 00000000000..cc4636a3ae2 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/FORECAST.index new file mode 100644 index 00000000000..457dbed5366 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/PARAMETER.data_0 new file mode 100644 index 00000000000..ef0f06cab06 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/PARAMETER.index new file mode 100644 index 00000000000..5cead4c75cf Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_23/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/FORECAST.data_0 new file mode 100644 index 00000000000..6288c34a795 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/FORECAST.index new file mode 100644 index 00000000000..fe66f53d31a Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/PARAMETER.data_0 new file mode 100644 index 00000000000..41ecb4c3a61 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/PARAMETER.index new file mode 100644 index 00000000000..db5e6dd43d0 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_24/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/FORECAST.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/PARAMETER.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_25/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/FORECAST.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/PARAMETER.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_26/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/FORECAST.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/PARAMETER.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_27/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/FORECAST.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/PARAMETER.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_28/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/FORECAST.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/PARAMETER.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_29/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/FORECAST.data_0 new file mode 100644 index 00000000000..d712b2fd9f7 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/FORECAST.index new file mode 100644 index 00000000000..ecdfc3d830e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/PARAMETER.data_0 new file mode 100644 index 00000000000..a137a5e6b10 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/PARAMETER.index new file mode 100644 index 00000000000..76c019a3a7a Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_3/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/FORECAST.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/PARAMETER.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_30/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/FORECAST.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/PARAMETER.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_31/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/FORECAST.data_0 new file mode 100644 index 00000000000..732dacdc28c Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/FORECAST.index new file mode 100644 index 00000000000..ca341105468 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/PARAMETER.data_0 new file mode 100644 index 00000000000..7f52a5fe220 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/PARAMETER.index new file mode 100644 index 00000000000..78ef983cb5f Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_4/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/FORECAST.data_0 new file mode 100644 index 00000000000..fbe0a4b97a8 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/FORECAST.index new file mode 100644 index 00000000000..55f38217c36 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/PARAMETER.data_0 new file mode 100644 index 00000000000..2f431ce03dc Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/PARAMETER.index new file mode 100644 index 00000000000..2cda545a608 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_5/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/FORECAST.data_0 new file mode 100644 index 00000000000..b45e5214a88 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/FORECAST.index new file mode 100644 index 00000000000..4fb2f8d61da Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/PARAMETER.data_0 new file mode 100644 index 00000000000..3023b7b1cac Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/PARAMETER.index new file mode 100644 index 00000000000..28ddb9559c2 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_6/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/FORECAST.data_0 new file mode 100644 index 00000000000..86542a20068 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/FORECAST.index new file mode 100644 index 00000000000..78b322106ea Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/PARAMETER.data_0 new file mode 100644 index 00000000000..05dee2ddd2e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/PARAMETER.index new file mode 100644 index 00000000000..0cb9ab22af2 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_7/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/FORECAST.data_0 new file mode 100644 index 00000000000..a7e80be3b94 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/FORECAST.index new file mode 100644 index 00000000000..865dde56f70 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/PARAMETER.data_0 new file mode 100644 index 00000000000..7664ed432b0 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/PARAMETER.index new file mode 100644 index 00000000000..113befd57e0 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_8/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/ANALYZED.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/FORECAST.data_0 new file mode 100644 index 00000000000..f1417e17ced Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/FORECAST.index new file mode 100644 index 00000000000..c661372176b Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/PARAMETER.data_0 new file mode 100644 index 00000000000..3ec5272b40d Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/PARAMETER.index new file mode 100644 index 00000000000..9a2da486793 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/STATIC.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Ensemble/mod_9/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Index/INDEX.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Index/INDEX.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Index/INDEX.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Index/INDEX.index new file mode 100644 index 00000000000..4377afdcd79 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Index/INDEX.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Index/INDEX.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Index/INDEX.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/Index/INDEX.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/ert_fstab b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/ert_fstab new file mode 100644 index 00000000000..a5d67ad3a3c Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/ert_fstab differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/files/case_config b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/files/case_config new file mode 100644 index 00000000000..593f4708db8 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/files/case_config differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/files/custom_kw_config_set b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/files/custom_kw_config_set new file mode 100644 index 00000000000..ec71bfdcb0a Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/files/custom_kw_config_set differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/files/state-map b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/files/state-map new file mode 100644 index 00000000000..847546ba7af Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/files/state-map differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/files/summary-key-set b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/files/summary-key-set new file mode 100644 index 00000000000..374e2509371 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/files/summary-key-set differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/files/time-map b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/files/time-map new file mode 100644 index 00000000000..75e73e5c3c4 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_0/files/time-map differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/FORECAST.data_0 new file mode 100644 index 00000000000..7712e6890c2 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/FORECAST.index new file mode 100644 index 00000000000..2ec82d5de21 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/PARAMETER.data_0 new file mode 100644 index 00000000000..7a759415c8f Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/PARAMETER.index new file mode 100644 index 00000000000..124fd152927 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_0/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/FORECAST.data_0 new file mode 100644 index 00000000000..f6cc9a9d7e9 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/FORECAST.index new file mode 100644 index 00000000000..afa1d3eb133 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/PARAMETER.data_0 new file mode 100644 index 00000000000..69a76c69ef3 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/PARAMETER.index new file mode 100644 index 00000000000..b3d275e53fb Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_1/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/FORECAST.data_0 new file mode 100644 index 00000000000..9088aaea34e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/FORECAST.index new file mode 100644 index 00000000000..6491ad708a8 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/PARAMETER.data_0 new file mode 100644 index 00000000000..d13d1306c10 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/PARAMETER.index new file mode 100644 index 00000000000..f7a5c2b1ced Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_10/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/FORECAST.data_0 new file mode 100644 index 00000000000..bd132b8b6a9 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/FORECAST.index new file mode 100644 index 00000000000..aa96a8a85c2 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/PARAMETER.data_0 new file mode 100644 index 00000000000..8dbe842ea45 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/PARAMETER.index new file mode 100644 index 00000000000..93ec9e230fc Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_11/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/FORECAST.data_0 new file mode 100644 index 00000000000..e13367c600f Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/FORECAST.index new file mode 100644 index 00000000000..c32bea26540 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/PARAMETER.data_0 new file mode 100644 index 00000000000..06192b663d4 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/PARAMETER.index new file mode 100644 index 00000000000..1b3e2dd2fba Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_12/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/FORECAST.data_0 new file mode 100644 index 00000000000..88f26164127 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/FORECAST.index new file mode 100644 index 00000000000..c9cabd67fe1 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/PARAMETER.data_0 new file mode 100644 index 00000000000..0f77feb6135 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/PARAMETER.index new file mode 100644 index 00000000000..28e06a2b124 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_13/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/FORECAST.data_0 new file mode 100644 index 00000000000..15ba295ff24 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/FORECAST.index new file mode 100644 index 00000000000..b0f52d50677 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/PARAMETER.data_0 new file mode 100644 index 00000000000..e8778073dce Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/PARAMETER.index new file mode 100644 index 00000000000..4a3adecf64c Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_14/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/FORECAST.data_0 new file mode 100644 index 00000000000..ca6fcfd5b5c Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/FORECAST.index new file mode 100644 index 00000000000..2852e1d32ff Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/PARAMETER.data_0 new file mode 100644 index 00000000000..30b22bb3264 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/PARAMETER.index new file mode 100644 index 00000000000..d72eca18318 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_15/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/FORECAST.data_0 new file mode 100644 index 00000000000..f1a07702af9 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/FORECAST.index new file mode 100644 index 00000000000..aca2e32260e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/PARAMETER.data_0 new file mode 100644 index 00000000000..f29b9e20c3a Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/PARAMETER.index new file mode 100644 index 00000000000..25fdec46857 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_16/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/FORECAST.data_0 new file mode 100644 index 00000000000..a33bfb12cb1 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/FORECAST.index new file mode 100644 index 00000000000..2f9c397b4d9 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/PARAMETER.data_0 new file mode 100644 index 00000000000..6e82c5c2dbb Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/PARAMETER.index new file mode 100644 index 00000000000..4dc7149bcf5 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_17/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/FORECAST.data_0 new file mode 100644 index 00000000000..7c069ba0690 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/FORECAST.index new file mode 100644 index 00000000000..d3ad129f4dc Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/PARAMETER.data_0 new file mode 100644 index 00000000000..91e10004174 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/PARAMETER.index new file mode 100644 index 00000000000..8137e1e6264 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_18/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/FORECAST.data_0 new file mode 100644 index 00000000000..5ad52e627a5 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/FORECAST.index new file mode 100644 index 00000000000..8006b4ea6a0 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/PARAMETER.data_0 new file mode 100644 index 00000000000..487f09c68fb Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/PARAMETER.index new file mode 100644 index 00000000000..97668fe2ba9 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_19/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/FORECAST.data_0 new file mode 100644 index 00000000000..d2f61efb51b Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/FORECAST.index new file mode 100644 index 00000000000..1e38fe0e2b1 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/PARAMETER.data_0 new file mode 100644 index 00000000000..96b186a4661 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/PARAMETER.index new file mode 100644 index 00000000000..cfc51a05e81 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_2/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/FORECAST.data_0 new file mode 100644 index 00000000000..982a2723e3b Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/FORECAST.index new file mode 100644 index 00000000000..9b280c1568c Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/PARAMETER.data_0 new file mode 100644 index 00000000000..86829b22705 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/PARAMETER.index new file mode 100644 index 00000000000..c33aed0d8a1 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_20/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/FORECAST.data_0 new file mode 100644 index 00000000000..3bc4bc3caac Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/FORECAST.index new file mode 100644 index 00000000000..fe0f0d7c2f0 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/PARAMETER.data_0 new file mode 100644 index 00000000000..7873a46f81f Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/PARAMETER.index new file mode 100644 index 00000000000..1cf58d702b1 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_21/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/FORECAST.data_0 new file mode 100644 index 00000000000..367a2add567 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/FORECAST.index new file mode 100644 index 00000000000..432db22d865 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/PARAMETER.data_0 new file mode 100644 index 00000000000..ae315352bac Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/PARAMETER.index new file mode 100644 index 00000000000..e4ae83c833b Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_22/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/FORECAST.data_0 new file mode 100644 index 00000000000..8b9b240ee5f Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/FORECAST.index new file mode 100644 index 00000000000..f0861607098 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/PARAMETER.data_0 new file mode 100644 index 00000000000..8e6f6150a09 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/PARAMETER.index new file mode 100644 index 00000000000..81a864974b5 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_23/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/FORECAST.data_0 new file mode 100644 index 00000000000..f948378ae44 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/FORECAST.index new file mode 100644 index 00000000000..635db16f6fe Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/PARAMETER.data_0 new file mode 100644 index 00000000000..dc0a0e16318 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/PARAMETER.index new file mode 100644 index 00000000000..105c02c7b8f Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_24/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/FORECAST.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/PARAMETER.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_25/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/FORECAST.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/PARAMETER.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_26/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/FORECAST.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/PARAMETER.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_27/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/FORECAST.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/PARAMETER.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_28/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/FORECAST.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/PARAMETER.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_29/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/FORECAST.data_0 new file mode 100644 index 00000000000..b36f758a291 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/FORECAST.index new file mode 100644 index 00000000000..e3a507872de Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/PARAMETER.data_0 new file mode 100644 index 00000000000..b1f41588e52 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/PARAMETER.index new file mode 100644 index 00000000000..14e4241100c Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_3/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/FORECAST.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/PARAMETER.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_30/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/FORECAST.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/FORECAST.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/PARAMETER.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/PARAMETER.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_31/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/FORECAST.data_0 new file mode 100644 index 00000000000..afb802c53b2 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/FORECAST.index new file mode 100644 index 00000000000..e7bdf52f5a6 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/PARAMETER.data_0 new file mode 100644 index 00000000000..01708a37a93 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/PARAMETER.index new file mode 100644 index 00000000000..fec5f5d82ee Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_4/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/FORECAST.data_0 new file mode 100644 index 00000000000..808f1841fbf Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/FORECAST.index new file mode 100644 index 00000000000..3fb65418a09 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/PARAMETER.data_0 new file mode 100644 index 00000000000..7ae7c0f6cdb Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/PARAMETER.index new file mode 100644 index 00000000000..0018eecf4c9 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_5/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/FORECAST.data_0 new file mode 100644 index 00000000000..6142d1e6a80 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/FORECAST.index new file mode 100644 index 00000000000..2ac26beb097 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/PARAMETER.data_0 new file mode 100644 index 00000000000..b0d6438d5eb Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/PARAMETER.index new file mode 100644 index 00000000000..031003b32ed Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_6/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/FORECAST.data_0 new file mode 100644 index 00000000000..ab1521c9151 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/FORECAST.index new file mode 100644 index 00000000000..aa3e002d224 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/PARAMETER.data_0 new file mode 100644 index 00000000000..5f970b15fd7 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/PARAMETER.index new file mode 100644 index 00000000000..d2054df4c93 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_7/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/FORECAST.data_0 new file mode 100644 index 00000000000..41d0a4aaa74 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/FORECAST.index new file mode 100644 index 00000000000..6803d231776 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/PARAMETER.data_0 new file mode 100644 index 00000000000..b2a3976b54c Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/PARAMETER.index new file mode 100644 index 00000000000..506140f1b5b Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_8/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/ANALYZED.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/ANALYZED.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/ANALYZED.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/ANALYZED.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/ANALYZED.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/ANALYZED.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/ANALYZED.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/ANALYZED.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/FORECAST.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/FORECAST.data_0 new file mode 100644 index 00000000000..ed5fbcde99c Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/FORECAST.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/FORECAST.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/FORECAST.index new file mode 100644 index 00000000000..1f1df9fac42 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/FORECAST.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/FORECAST.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/FORECAST.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/FORECAST.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/PARAMETER.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/PARAMETER.data_0 new file mode 100644 index 00000000000..9c68873c7a1 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/PARAMETER.data_0 differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/PARAMETER.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/PARAMETER.index new file mode 100644 index 00000000000..e62045d14ab Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/PARAMETER.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/PARAMETER.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/PARAMETER.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/PARAMETER.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/STATIC.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/STATIC.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/STATIC.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/STATIC.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/STATIC.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/STATIC.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/STATIC.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Ensemble/mod_9/STATIC.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Index/INDEX.data_0 b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Index/INDEX.data_0 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Index/INDEX.index b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Index/INDEX.index new file mode 100644 index 00000000000..6ae8e935356 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Index/INDEX.index differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Index/INDEX.mnt b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Index/INDEX.mnt new file mode 100644 index 00000000000..5280360395e Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/Index/INDEX.mnt differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/ert_fstab b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/ert_fstab new file mode 100644 index 00000000000..a5d67ad3a3c Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/ert_fstab differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/files/case_config b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/files/case_config new file mode 100644 index 00000000000..593f4708db8 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/files/case_config differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/files/custom_kw_config_set b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/files/custom_kw_config_set new file mode 100644 index 00000000000..ec71bfdcb0a Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/files/custom_kw_config_set differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/files/state-map b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/files/state-map new file mode 100644 index 00000000000..847546ba7af Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/files/state-map differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/files/summary-key-set b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/files/summary-key-set new file mode 100644 index 00000000000..374e2509371 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/files/summary-key-set differ diff --git a/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/files/time-map b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/files/time-map new file mode 100644 index 00000000000..75e73e5c3c4 Binary files /dev/null and b/libres/test-data/local/snake_oil/storage/snake_oil/ensemble/default_1/files/time-map differ diff --git a/libres/test-data/local/snake_oil/templates/seed_template.txt b/libres/test-data/local/snake_oil/templates/seed_template.txt new file mode 100644 index 00000000000..a0bca49fbdb --- /dev/null +++ b/libres/test-data/local/snake_oil/templates/seed_template.txt @@ -0,0 +1 @@ +SEED: \ No newline at end of file diff --git a/libres/test-data/local/snake_oil/templates/snake_oil_template.txt b/libres/test-data/local/snake_oil/templates/snake_oil_template.txt new file mode 100644 index 00000000000..ad2c648a0b5 --- /dev/null +++ b/libres/test-data/local/snake_oil/templates/snake_oil_template.txt @@ -0,0 +1,10 @@ +OP1_PERSISTENCE: +OP1_OCTAVES: +OP1_DIVERGENCE_SCALE: +OP1_OFFSET: +OP2_PERSISTENCE: +OP2_OCTAVES: +OP2_DIVERGENCE_SCALE: +OP2_OFFSET: +BPR_555_PERSISTENCE: +BPR_138_PERSISTENCE: diff --git a/libres/test-data/local/snake_oil_field/fields/permx0.grdecl b/libres/test-data/local/snake_oil_field/fields/permx0.grdecl new file mode 100644 index 00000000000..481f9d8b838 --- /dev/null +++ b/libres/test-data/local/snake_oil_field/fields/permx0.grdecl @@ -0,0 +1,127 @@ +PERMX + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 + 0.92063643E-01 0.92063643E-01 0.92063643E-01 0.92063643E-01 +/ diff --git a/libres/test-data/local/snake_oil_field/fields/permx1.grdecl b/libres/test-data/local/snake_oil_field/fields/permx1.grdecl new file mode 100644 index 00000000000..03959ed75a8 --- /dev/null +++ b/libres/test-data/local/snake_oil_field/fields/permx1.grdecl @@ -0,0 +1,127 @@ +PERMX + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 + 0.53819609E+00 0.53819609E+00 0.53819609E+00 0.53819609E+00 +/ diff --git a/libres/test-data/local/snake_oil_field/fields/permx2.grdecl b/libres/test-data/local/snake_oil_field/fields/permx2.grdecl new file mode 100644 index 00000000000..7d8e9bb96ac --- /dev/null +++ b/libres/test-data/local/snake_oil_field/fields/permx2.grdecl @@ -0,0 +1,127 @@ +PERMX + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 + 0.84981030E+00 0.84981030E+00 0.84981030E+00 0.84981030E+00 +/ diff --git a/libres/test-data/local/snake_oil_field/fields/permx3.grdecl b/libres/test-data/local/snake_oil_field/fields/permx3.grdecl new file mode 100644 index 00000000000..5b733e38b2a --- /dev/null +++ b/libres/test-data/local/snake_oil_field/fields/permx3.grdecl @@ -0,0 +1,127 @@ +PERMX + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 + 0.24182262E+00 0.24182262E+00 0.24182262E+00 0.24182262E+00 +/ diff --git a/libres/test-data/local/snake_oil_field/fields/permx4.grdecl b/libres/test-data/local/snake_oil_field/fields/permx4.grdecl new file mode 100644 index 00000000000..39a483cea14 --- /dev/null +++ b/libres/test-data/local/snake_oil_field/fields/permx4.grdecl @@ -0,0 +1,127 @@ +PERMX + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 + 0.59172921E-01 0.59172921E-01 0.59172921E-01 0.59172921E-01 +/ diff --git a/libres/test-data/local/snake_oil_field/fields/permx5.grdecl b/libres/test-data/local/snake_oil_field/fields/permx5.grdecl new file mode 100644 index 00000000000..9286ac8ec97 --- /dev/null +++ b/libres/test-data/local/snake_oil_field/fields/permx5.grdecl @@ -0,0 +1,127 @@ +PERMX + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 + 0.76347315E+00 0.76347315E+00 0.76347315E+00 0.76347315E+00 +/ diff --git a/libres/test-data/local/snake_oil_field/fields/permx6.grdecl b/libres/test-data/local/snake_oil_field/fields/permx6.grdecl new file mode 100644 index 00000000000..8e33d568fe8 --- /dev/null +++ b/libres/test-data/local/snake_oil_field/fields/permx6.grdecl @@ -0,0 +1,127 @@ +PERMX + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 + 0.37705892E+00 0.37705892E+00 0.37705892E+00 0.37705892E+00 +/ diff --git a/libres/test-data/local/snake_oil_field/fields/permx7.grdecl b/libres/test-data/local/snake_oil_field/fields/permx7.grdecl new file mode 100644 index 00000000000..399a9fc9d03 --- /dev/null +++ b/libres/test-data/local/snake_oil_field/fields/permx7.grdecl @@ -0,0 +1,127 @@ +PERMX + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 + 0.84188782E-01 0.84188782E-01 0.84188782E-01 0.84188782E-01 +/ diff --git a/libres/test-data/local/snake_oil_field/fields/permx8.grdecl b/libres/test-data/local/snake_oil_field/fields/permx8.grdecl new file mode 100644 index 00000000000..ec1f83277bf --- /dev/null +++ b/libres/test-data/local/snake_oil_field/fields/permx8.grdecl @@ -0,0 +1,127 @@ +PERMX + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 + 0.52828066E-01 0.52828066E-01 0.52828066E-01 0.52828066E-01 +/ diff --git a/libres/test-data/local/snake_oil_field/fields/permx9.grdecl b/libres/test-data/local/snake_oil_field/fields/permx9.grdecl new file mode 100644 index 00000000000..02dd5a1a0e0 --- /dev/null +++ b/libres/test-data/local/snake_oil_field/fields/permx9.grdecl @@ -0,0 +1,127 @@ +PERMX + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 + 0.67972553E+00 0.67972553E+00 0.67972553E+00 0.67972553E+00 +/ diff --git a/libres/test-data/local/snake_oil_field/fields/poro0.grdecl b/libres/test-data/local/snake_oil_field/fields/poro0.grdecl new file mode 100644 index 00000000000..b6075fa61d6 --- /dev/null +++ b/libres/test-data/local/snake_oil_field/fields/poro0.grdecl @@ -0,0 +1,127 @@ +PORO + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 + 0.94346166E+00 0.94346166E+00 0.94346166E+00 0.94346166E+00 +/ diff --git a/libres/test-data/local/snake_oil_field/fields/poro1.grdecl b/libres/test-data/local/snake_oil_field/fields/poro1.grdecl new file mode 100644 index 00000000000..e814818eb80 --- /dev/null +++ b/libres/test-data/local/snake_oil_field/fields/poro1.grdecl @@ -0,0 +1,127 @@ +PORO + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 + 0.68036640E+00 0.68036640E+00 0.68036640E+00 0.68036640E+00 +/ diff --git a/libres/test-data/local/snake_oil_field/fields/poro2.grdecl b/libres/test-data/local/snake_oil_field/fields/poro2.grdecl new file mode 100644 index 00000000000..ba3eb9d6e8b --- /dev/null +++ b/libres/test-data/local/snake_oil_field/fields/poro2.grdecl @@ -0,0 +1,127 @@ +PORO + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 + 0.21387884E+00 0.21387884E+00 0.21387884E+00 0.21387884E+00 +/ diff --git a/libres/test-data/local/snake_oil_field/fields/poro3.grdecl b/libres/test-data/local/snake_oil_field/fields/poro3.grdecl new file mode 100644 index 00000000000..f276fb55a37 --- /dev/null +++ b/libres/test-data/local/snake_oil_field/fields/poro3.grdecl @@ -0,0 +1,127 @@ +PORO + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 + 0.72398102E+00 0.72398102E+00 0.72398102E+00 0.72398102E+00 +/ diff --git a/libres/test-data/local/snake_oil_field/fields/poro4.grdecl b/libres/test-data/local/snake_oil_field/fields/poro4.grdecl new file mode 100644 index 00000000000..934fecb3ed5 --- /dev/null +++ b/libres/test-data/local/snake_oil_field/fields/poro4.grdecl @@ -0,0 +1,127 @@ +PORO + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 + 0.68414760E+00 0.68414760E+00 0.68414760E+00 0.68414760E+00 +/ diff --git a/libres/test-data/local/snake_oil_field/fields/poro5.grdecl b/libres/test-data/local/snake_oil_field/fields/poro5.grdecl new file mode 100644 index 00000000000..b0448eb9742 --- /dev/null +++ b/libres/test-data/local/snake_oil_field/fields/poro5.grdecl @@ -0,0 +1,127 @@ +PORO + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 + 0.81912923E+00 0.81912923E+00 0.81912923E+00 0.81912923E+00 +/ diff --git a/libres/test-data/local/snake_oil_field/fields/poro6.grdecl b/libres/test-data/local/snake_oil_field/fields/poro6.grdecl new file mode 100644 index 00000000000..919c438a61b --- /dev/null +++ b/libres/test-data/local/snake_oil_field/fields/poro6.grdecl @@ -0,0 +1,127 @@ +PORO + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 + 0.43541095E+00 0.43541095E+00 0.43541095E+00 0.43541095E+00 +/ diff --git a/libres/test-data/local/snake_oil_field/fields/poro7.grdecl b/libres/test-data/local/snake_oil_field/fields/poro7.grdecl new file mode 100644 index 00000000000..f8a6beed8d0 --- /dev/null +++ b/libres/test-data/local/snake_oil_field/fields/poro7.grdecl @@ -0,0 +1,127 @@ +PORO + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 + 0.48421845E+00 0.48421845E+00 0.48421845E+00 0.48421845E+00 +/ diff --git a/libres/test-data/local/snake_oil_field/fields/poro8.grdecl b/libres/test-data/local/snake_oil_field/fields/poro8.grdecl new file mode 100644 index 00000000000..285e34c970b --- /dev/null +++ b/libres/test-data/local/snake_oil_field/fields/poro8.grdecl @@ -0,0 +1,127 @@ +PORO + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 + 0.98167807E+00 0.98167807E+00 0.98167807E+00 0.98167807E+00 +/ diff --git a/libres/test-data/local/snake_oil_field/fields/poro9.grdecl b/libres/test-data/local/snake_oil_field/fields/poro9.grdecl new file mode 100644 index 00000000000..1b0faeefd00 --- /dev/null +++ b/libres/test-data/local/snake_oil_field/fields/poro9.grdecl @@ -0,0 +1,127 @@ +PORO + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 + 0.82837886E+00 0.82837886E+00 0.82837886E+00 0.82837886E+00 +/ diff --git a/libres/test-data/local/snake_oil_field/grid/CASE.EGRID b/libres/test-data/local/snake_oil_field/grid/CASE.EGRID new file mode 100644 index 00000000000..33da9f7a70e Binary files /dev/null and b/libres/test-data/local/snake_oil_field/grid/CASE.EGRID differ diff --git a/libres/test-data/local/snake_oil_field/jobs/SNAKE_OIL_DIFF b/libres/test-data/local/snake_oil_field/jobs/SNAKE_OIL_DIFF new file mode 100644 index 00000000000..98a867d9594 --- /dev/null +++ b/libres/test-data/local/snake_oil_field/jobs/SNAKE_OIL_DIFF @@ -0,0 +1,4 @@ +STDOUT snake_oil_diff.stdout +STDERR snake_oil_diff.stderr + +EXECUTABLE snake_oil_diff.py \ No newline at end of file diff --git a/libres/test-data/local/snake_oil_field/jobs/SNAKE_OIL_NPV b/libres/test-data/local/snake_oil_field/jobs/SNAKE_OIL_NPV new file mode 100644 index 00000000000..887830c756f --- /dev/null +++ b/libres/test-data/local/snake_oil_field/jobs/SNAKE_OIL_NPV @@ -0,0 +1,4 @@ +STDOUT snake_oil_npv.stdout +STDERR snake_oil_npv.stderr + +EXECUTABLE snake_oil_npv.py \ No newline at end of file diff --git a/libres/test-data/local/snake_oil_field/jobs/SNAKE_OIL_SIMULATOR b/libres/test-data/local/snake_oil_field/jobs/SNAKE_OIL_SIMULATOR new file mode 100644 index 00000000000..b4b7f9928fd --- /dev/null +++ b/libres/test-data/local/snake_oil_field/jobs/SNAKE_OIL_SIMULATOR @@ -0,0 +1,4 @@ +STDOUT snake_oil.stdout +STDERR snake_oil.stderr + +EXECUTABLE snake_oil_simulator.py \ No newline at end of file diff --git a/libres/test-data/local/snake_oil_field/jobs/snake_oil_diff.py b/libres/test-data/local/snake_oil_field/jobs/snake_oil_diff.py new file mode 120000 index 00000000000..126a5b22456 --- /dev/null +++ b/libres/test-data/local/snake_oil_field/jobs/snake_oil_diff.py @@ -0,0 +1 @@ +../../jobs_snake_oil/snake_oil_diff.py \ No newline at end of file diff --git a/libres/test-data/local/snake_oil_field/jobs/snake_oil_npv.py b/libres/test-data/local/snake_oil_field/jobs/snake_oil_npv.py new file mode 120000 index 00000000000..a69f08b34fb --- /dev/null +++ b/libres/test-data/local/snake_oil_field/jobs/snake_oil_npv.py @@ -0,0 +1 @@ +../../jobs_snake_oil/snake_oil_npv.py \ No newline at end of file diff --git a/libres/test-data/local/snake_oil_field/jobs/snake_oil_simulator.py b/libres/test-data/local/snake_oil_field/jobs/snake_oil_simulator.py new file mode 120000 index 00000000000..4e6d00ddcf3 --- /dev/null +++ b/libres/test-data/local/snake_oil_field/jobs/snake_oil_simulator.py @@ -0,0 +1 @@ +../../jobs_snake_oil/snake_oil_simulator.py \ No newline at end of file diff --git a/libres/test-data/local/snake_oil_field/make_data.py b/libres/test-data/local/snake_oil_field/make_data.py new file mode 100755 index 00000000000..894872729ac --- /dev/null +++ b/libres/test-data/local/snake_oil_field/make_data.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python +import os +import os.path + +from ecl import EclTypeEnum +from ecl.eclfile import EclKW +from ecl.grid import EclGrid +from ecl.util import RandomNumberGenerator + +# This little script is used as a one-shot operation to generate the +# grid and the corresponding PERMX and PORO fields used for this test +# case. The script itself is not used bye the test. + +nx = 10 +ny = 10 +nz = 5 +ens_size = 10 + + +def make_grid(): + grid = EclGrid.createRectangular((nx, ny, nz), (1, 1, 1)) + if not os.path.isdir("grid"): + os.makedirs("grid") + grid.save_EGRID("grid/CASE.EGRID") + + return grid + + +def make_field(rng, grid, iens): + permx = EclKW.create("PERMX", grid.getGlobalSize(), EclTypeEnum.ECL_FLOAT_TYPE) + permx.assign(rng.getDouble()) + + poro = EclKW.create("PORO", grid.getGlobalSize(), EclTypeEnum.ECL_FLOAT_TYPE) + poro.assign(rng.getDouble()) + + if not os.path.isdir("fields"): + os.makedirs("fields") + + with open("fields/permx%d.grdecl" % iens, "w") as f: + permx.write_grdecl(f) + + with open("fields/poro%d.grdecl" % iens, "w") as f: + poro.write_grdecl(f) + + +rng = RandomNumberGenerator() +rng.setState("ABCD6375ejascEFGHIJ") + + +grid = make_grid() +for iens in range(ens_size): + make_field(rng, grid, iens) diff --git a/libres/test-data/local/snake_oil_field/observations/observations.txt b/libres/test-data/local/snake_oil_field/observations/observations.txt new file mode 100644 index 00000000000..483c1d59ab3 --- /dev/null +++ b/libres/test-data/local/snake_oil_field/observations/observations.txt @@ -0,0 +1,56 @@ +HISTORY_OBSERVATION FOPR; + +SUMMARY_OBSERVATION WOPR_OP1_9 +{ + VALUE = 0.1; + ERROR = 0.05; + RESTART = 9; + KEY = WOPR:OP1; +}; + +SUMMARY_OBSERVATION WOPR_OP1_36 +{ + VALUE = 0.7; + ERROR = 0.07; + RESTART = 36; + KEY = WOPR:OP1; +}; + +SUMMARY_OBSERVATION WOPR_OP1_72 +{ + VALUE = 0.5; + ERROR = 0.05; + RESTART = 72; + KEY = WOPR:OP1; +}; + +SUMMARY_OBSERVATION WOPR_OP1_108 +{ + VALUE = 0.3; + ERROR = 0.075; + RESTART = 108; + KEY = WOPR:OP1; +}; + +SUMMARY_OBSERVATION WOPR_OP1_144 +{ + VALUE = 0.2; + ERROR = 0.035; + RESTART = 144; + KEY = WOPR:OP1; +}; + +SUMMARY_OBSERVATION WOPR_OP1_190 +{ + VALUE = 0.015; + ERROR = 0.01; + RESTART = 190; + KEY = WOPR:OP1; +}; + +GENERAL_OBSERVATION WPR_DIFF_1 { + DATA = SNAKE_OIL_WPR_DIFF; + INDEX_LIST = 400,800,1200,1800; + RESTART = 199; + OBS_FILE = wpr_diff_obs.txt; +}; diff --git a/libres/test-data/local/snake_oil_field/observations/wpr_diff_obs.txt b/libres/test-data/local/snake_oil_field/observations/wpr_diff_obs.txt new file mode 100644 index 00000000000..4aee6d7ef7c --- /dev/null +++ b/libres/test-data/local/snake_oil_field/observations/wpr_diff_obs.txt @@ -0,0 +1,4 @@ +0.0 0.1 +0.1 0.2 +0.2 0.15 +0.0 0.05 diff --git a/libres/test-data/local/snake_oil_field/parameters/snake_oil_parameters.txt b/libres/test-data/local/snake_oil_field/parameters/snake_oil_parameters.txt new file mode 100644 index 00000000000..64573d0058b --- /dev/null +++ b/libres/test-data/local/snake_oil_field/parameters/snake_oil_parameters.txt @@ -0,0 +1,11 @@ +OP1_PERSISTENCE UNIFORM 0.01 0.4 +OP1_OCTAVES UNIFORM 3 5 +OP1_DIVERGENCE_SCALE UNIFORM 0.25 1.25 +OP1_OFFSET UNIFORM -0.1 0.1 +OP2_PERSISTENCE UNIFORM 0.1 0.6 +OP2_OCTAVES UNIFORM 5 12 +OP2_DIVERGENCE_SCALE UNIFORM 0.5 1.5 +OP2_OFFSET UNIFORM -0.2 0.2 +BPR_555_PERSISTENCE UNIFORM 0.1 0.5 +BPR_138_PERSISTENCE UNIFORM 0.2 0.7 + diff --git a/libres/test-data/local/snake_oil_field/refcase/SNAKE_OIL_FIELD.SMSPEC b/libres/test-data/local/snake_oil_field/refcase/SNAKE_OIL_FIELD.SMSPEC new file mode 100644 index 00000000000..5a29e043b2d Binary files /dev/null and b/libres/test-data/local/snake_oil_field/refcase/SNAKE_OIL_FIELD.SMSPEC differ diff --git a/libres/test-data/local/snake_oil_field/refcase/SNAKE_OIL_FIELD.UNSMRY b/libres/test-data/local/snake_oil_field/refcase/SNAKE_OIL_FIELD.UNSMRY new file mode 100644 index 00000000000..dfa04f80027 Binary files /dev/null and b/libres/test-data/local/snake_oil_field/refcase/SNAKE_OIL_FIELD.UNSMRY differ diff --git a/libres/test-data/local/snake_oil_field/refcase/refcase_readme.txt b/libres/test-data/local/snake_oil_field/refcase/refcase_readme.txt new file mode 100644 index 00000000000..a3d0fe60589 --- /dev/null +++ b/libres/test-data/local/snake_oil_field/refcase/refcase_readme.txt @@ -0,0 +1 @@ +To create a refcase run the snake_oil_simulator.py job with the this as working directory. \ No newline at end of file diff --git a/libres/test-data/local/snake_oil_field/refcase/seed.txt b/libres/test-data/local/snake_oil_field/refcase/seed.txt new file mode 100644 index 00000000000..0009f6e89a5 --- /dev/null +++ b/libres/test-data/local/snake_oil_field/refcase/seed.txt @@ -0,0 +1 @@ +SEED:268776 \ No newline at end of file diff --git a/libres/test-data/local/snake_oil_field/refcase/snake_oil_params.txt b/libres/test-data/local/snake_oil_field/refcase/snake_oil_params.txt new file mode 100644 index 00000000000..3868522924c --- /dev/null +++ b/libres/test-data/local/snake_oil_field/refcase/snake_oil_params.txt @@ -0,0 +1,10 @@ +OP1_PERSISTENCE:0.15 +OP1_OCTAVES:4 +OP1_DIVERGENCE_SCALE:0.5 +OP1_OFFSET:0.0 +OP2_PERSISTENCE:0.25 +OP2_OCTAVES:7.0 +OP2_DIVERGENCE_SCALE:1.0 +OP2_OFFSET:0.0 +BPR_555_PERSISTENCE:0.25 +BPR_138_PERSISTENCE:0.35 diff --git a/libres/test-data/local/snake_oil_field/refcase/time_map.txt b/libres/test-data/local/snake_oil_field/refcase/time_map.txt new file mode 100644 index 00000000000..d54b4293aec --- /dev/null +++ b/libres/test-data/local/snake_oil_field/refcase/time_map.txt @@ -0,0 +1,2000 @@ +01/01/2010 +02/01/2010 +03/01/2010 +04/01/2010 +05/01/2010 +06/01/2010 +07/01/2010 +08/01/2010 +09/01/2010 +10/01/2010 +11/01/2010 +12/01/2010 +13/01/2010 +14/01/2010 +15/01/2010 +16/01/2010 +17/01/2010 +18/01/2010 +19/01/2010 +20/01/2010 +21/01/2010 +22/01/2010 +23/01/2010 +24/01/2010 +25/01/2010 +26/01/2010 +27/01/2010 +28/01/2010 +29/01/2010 +30/01/2010 +31/01/2010 +01/02/2010 +02/02/2010 +03/02/2010 +04/02/2010 +05/02/2010 +06/02/2010 +07/02/2010 +08/02/2010 +09/02/2010 +10/02/2010 +11/02/2010 +12/02/2010 +13/02/2010 +14/02/2010 +15/02/2010 +16/02/2010 +17/02/2010 +18/02/2010 +19/02/2010 +20/02/2010 +21/02/2010 +22/02/2010 +23/02/2010 +24/02/2010 +25/02/2010 +26/02/2010 +27/02/2010 +28/02/2010 +01/03/2010 +02/03/2010 +03/03/2010 +04/03/2010 +05/03/2010 +06/03/2010 +07/03/2010 +08/03/2010 +09/03/2010 +10/03/2010 +11/03/2010 +12/03/2010 +13/03/2010 +14/03/2010 +15/03/2010 +16/03/2010 +17/03/2010 +18/03/2010 +19/03/2010 +20/03/2010 +21/03/2010 +22/03/2010 +23/03/2010 +24/03/2010 +25/03/2010 +26/03/2010 +27/03/2010 +28/03/2010 +29/03/2010 +30/03/2010 +31/03/2010 +01/04/2010 +02/04/2010 +03/04/2010 +04/04/2010 +05/04/2010 +06/04/2010 +07/04/2010 +08/04/2010 +09/04/2010 +10/04/2010 +11/04/2010 +12/04/2010 +13/04/2010 +14/04/2010 +15/04/2010 +16/04/2010 +17/04/2010 +18/04/2010 +19/04/2010 +20/04/2010 +21/04/2010 +22/04/2010 +23/04/2010 +24/04/2010 +25/04/2010 +26/04/2010 +27/04/2010 +28/04/2010 +29/04/2010 +30/04/2010 +01/05/2010 +02/05/2010 +03/05/2010 +04/05/2010 +05/05/2010 +06/05/2010 +07/05/2010 +08/05/2010 +09/05/2010 +10/05/2010 +11/05/2010 +12/05/2010 +13/05/2010 +14/05/2010 +15/05/2010 +16/05/2010 +17/05/2010 +18/05/2010 +19/05/2010 +20/05/2010 +21/05/2010 +22/05/2010 +23/05/2010 +24/05/2010 +25/05/2010 +26/05/2010 +27/05/2010 +28/05/2010 +29/05/2010 +30/05/2010 +31/05/2010 +01/06/2010 +02/06/2010 +03/06/2010 +04/06/2010 +05/06/2010 +06/06/2010 +07/06/2010 +08/06/2010 +09/06/2010 +10/06/2010 +11/06/2010 +12/06/2010 +13/06/2010 +14/06/2010 +15/06/2010 +16/06/2010 +17/06/2010 +18/06/2010 +19/06/2010 +20/06/2010 +21/06/2010 +22/06/2010 +23/06/2010 +24/06/2010 +25/06/2010 +26/06/2010 +27/06/2010 +28/06/2010 +29/06/2010 +30/06/2010 +01/07/2010 +02/07/2010 +03/07/2010 +04/07/2010 +05/07/2010 +06/07/2010 +07/07/2010 +08/07/2010 +09/07/2010 +10/07/2010 +11/07/2010 +12/07/2010 +13/07/2010 +14/07/2010 +15/07/2010 +16/07/2010 +17/07/2010 +18/07/2010 +19/07/2010 +20/07/2010 +21/07/2010 +22/07/2010 +23/07/2010 +24/07/2010 +25/07/2010 +26/07/2010 +27/07/2010 +28/07/2010 +29/07/2010 +30/07/2010 +31/07/2010 +01/08/2010 +02/08/2010 +03/08/2010 +04/08/2010 +05/08/2010 +06/08/2010 +07/08/2010 +08/08/2010 +09/08/2010 +10/08/2010 +11/08/2010 +12/08/2010 +13/08/2010 +14/08/2010 +15/08/2010 +16/08/2010 +17/08/2010 +18/08/2010 +19/08/2010 +20/08/2010 +21/08/2010 +22/08/2010 +23/08/2010 +24/08/2010 +25/08/2010 +26/08/2010 +27/08/2010 +28/08/2010 +29/08/2010 +30/08/2010 +31/08/2010 +01/09/2010 +02/09/2010 +03/09/2010 +04/09/2010 +05/09/2010 +06/09/2010 +07/09/2010 +08/09/2010 +09/09/2010 +10/09/2010 +11/09/2010 +12/09/2010 +13/09/2010 +14/09/2010 +15/09/2010 +16/09/2010 +17/09/2010 +18/09/2010 +19/09/2010 +20/09/2010 +21/09/2010 +22/09/2010 +23/09/2010 +24/09/2010 +25/09/2010 +26/09/2010 +27/09/2010 +28/09/2010 +29/09/2010 +30/09/2010 +01/10/2010 +02/10/2010 +03/10/2010 +04/10/2010 +05/10/2010 +06/10/2010 +07/10/2010 +08/10/2010 +09/10/2010 +10/10/2010 +11/10/2010 +12/10/2010 +13/10/2010 +14/10/2010 +15/10/2010 +16/10/2010 +17/10/2010 +18/10/2010 +19/10/2010 +20/10/2010 +21/10/2010 +22/10/2010 +23/10/2010 +24/10/2010 +25/10/2010 +26/10/2010 +27/10/2010 +28/10/2010 +29/10/2010 +30/10/2010 +31/10/2010 +01/11/2010 +02/11/2010 +03/11/2010 +04/11/2010 +05/11/2010 +06/11/2010 +07/11/2010 +08/11/2010 +09/11/2010 +10/11/2010 +11/11/2010 +12/11/2010 +13/11/2010 +14/11/2010 +15/11/2010 +16/11/2010 +17/11/2010 +18/11/2010 +19/11/2010 +20/11/2010 +21/11/2010 +22/11/2010 +23/11/2010 +24/11/2010 +25/11/2010 +26/11/2010 +27/11/2010 +28/11/2010 +29/11/2010 +30/11/2010 +01/12/2010 +02/12/2010 +03/12/2010 +04/12/2010 +05/12/2010 +06/12/2010 +07/12/2010 +08/12/2010 +09/12/2010 +10/12/2010 +11/12/2010 +12/12/2010 +13/12/2010 +14/12/2010 +15/12/2010 +16/12/2010 +17/12/2010 +18/12/2010 +19/12/2010 +20/12/2010 +21/12/2010 +22/12/2010 +23/12/2010 +24/12/2010 +25/12/2010 +26/12/2010 +27/12/2010 +28/12/2010 +29/12/2010 +30/12/2010 +31/12/2010 +01/01/2011 +02/01/2011 +03/01/2011 +04/01/2011 +05/01/2011 +06/01/2011 +07/01/2011 +08/01/2011 +09/01/2011 +10/01/2011 +11/01/2011 +12/01/2011 +13/01/2011 +14/01/2011 +15/01/2011 +16/01/2011 +17/01/2011 +18/01/2011 +19/01/2011 +20/01/2011 +21/01/2011 +22/01/2011 +23/01/2011 +24/01/2011 +25/01/2011 +26/01/2011 +27/01/2011 +28/01/2011 +29/01/2011 +30/01/2011 +31/01/2011 +01/02/2011 +02/02/2011 +03/02/2011 +04/02/2011 +05/02/2011 +06/02/2011 +07/02/2011 +08/02/2011 +09/02/2011 +10/02/2011 +11/02/2011 +12/02/2011 +13/02/2011 +14/02/2011 +15/02/2011 +16/02/2011 +17/02/2011 +18/02/2011 +19/02/2011 +20/02/2011 +21/02/2011 +22/02/2011 +23/02/2011 +24/02/2011 +25/02/2011 +26/02/2011 +27/02/2011 +28/02/2011 +01/03/2011 +02/03/2011 +03/03/2011 +04/03/2011 +05/03/2011 +06/03/2011 +07/03/2011 +08/03/2011 +09/03/2011 +10/03/2011 +11/03/2011 +12/03/2011 +13/03/2011 +14/03/2011 +15/03/2011 +16/03/2011 +17/03/2011 +18/03/2011 +19/03/2011 +20/03/2011 +21/03/2011 +22/03/2011 +23/03/2011 +24/03/2011 +25/03/2011 +26/03/2011 +27/03/2011 +28/03/2011 +29/03/2011 +30/03/2011 +31/03/2011 +01/04/2011 +02/04/2011 +03/04/2011 +04/04/2011 +05/04/2011 +06/04/2011 +07/04/2011 +08/04/2011 +09/04/2011 +10/04/2011 +11/04/2011 +12/04/2011 +13/04/2011 +14/04/2011 +15/04/2011 +16/04/2011 +17/04/2011 +18/04/2011 +19/04/2011 +20/04/2011 +21/04/2011 +22/04/2011 +23/04/2011 +24/04/2011 +25/04/2011 +26/04/2011 +27/04/2011 +28/04/2011 +29/04/2011 +30/04/2011 +01/05/2011 +02/05/2011 +03/05/2011 +04/05/2011 +05/05/2011 +06/05/2011 +07/05/2011 +08/05/2011 +09/05/2011 +10/05/2011 +11/05/2011 +12/05/2011 +13/05/2011 +14/05/2011 +15/05/2011 +16/05/2011 +17/05/2011 +18/05/2011 +19/05/2011 +20/05/2011 +21/05/2011 +22/05/2011 +23/05/2011 +24/05/2011 +25/05/2011 +26/05/2011 +27/05/2011 +28/05/2011 +29/05/2011 +30/05/2011 +31/05/2011 +01/06/2011 +02/06/2011 +03/06/2011 +04/06/2011 +05/06/2011 +06/06/2011 +07/06/2011 +08/06/2011 +09/06/2011 +10/06/2011 +11/06/2011 +12/06/2011 +13/06/2011 +14/06/2011 +15/06/2011 +16/06/2011 +17/06/2011 +18/06/2011 +19/06/2011 +20/06/2011 +21/06/2011 +22/06/2011 +23/06/2011 +24/06/2011 +25/06/2011 +26/06/2011 +27/06/2011 +28/06/2011 +29/06/2011 +30/06/2011 +01/07/2011 +02/07/2011 +03/07/2011 +04/07/2011 +05/07/2011 +06/07/2011 +07/07/2011 +08/07/2011 +09/07/2011 +10/07/2011 +11/07/2011 +12/07/2011 +13/07/2011 +14/07/2011 +15/07/2011 +16/07/2011 +17/07/2011 +18/07/2011 +19/07/2011 +20/07/2011 +21/07/2011 +22/07/2011 +23/07/2011 +24/07/2011 +25/07/2011 +26/07/2011 +27/07/2011 +28/07/2011 +29/07/2011 +30/07/2011 +31/07/2011 +01/08/2011 +02/08/2011 +03/08/2011 +04/08/2011 +05/08/2011 +06/08/2011 +07/08/2011 +08/08/2011 +09/08/2011 +10/08/2011 +11/08/2011 +12/08/2011 +13/08/2011 +14/08/2011 +15/08/2011 +16/08/2011 +17/08/2011 +18/08/2011 +19/08/2011 +20/08/2011 +21/08/2011 +22/08/2011 +23/08/2011 +24/08/2011 +25/08/2011 +26/08/2011 +27/08/2011 +28/08/2011 +29/08/2011 +30/08/2011 +31/08/2011 +01/09/2011 +02/09/2011 +03/09/2011 +04/09/2011 +05/09/2011 +06/09/2011 +07/09/2011 +08/09/2011 +09/09/2011 +10/09/2011 +11/09/2011 +12/09/2011 +13/09/2011 +14/09/2011 +15/09/2011 +16/09/2011 +17/09/2011 +18/09/2011 +19/09/2011 +20/09/2011 +21/09/2011 +22/09/2011 +23/09/2011 +24/09/2011 +25/09/2011 +26/09/2011 +27/09/2011 +28/09/2011 +29/09/2011 +30/09/2011 +01/10/2011 +02/10/2011 +03/10/2011 +04/10/2011 +05/10/2011 +06/10/2011 +07/10/2011 +08/10/2011 +09/10/2011 +10/10/2011 +11/10/2011 +12/10/2011 +13/10/2011 +14/10/2011 +15/10/2011 +16/10/2011 +17/10/2011 +18/10/2011 +19/10/2011 +20/10/2011 +21/10/2011 +22/10/2011 +23/10/2011 +24/10/2011 +25/10/2011 +26/10/2011 +27/10/2011 +28/10/2011 +29/10/2011 +30/10/2011 +31/10/2011 +01/11/2011 +02/11/2011 +03/11/2011 +04/11/2011 +05/11/2011 +06/11/2011 +07/11/2011 +08/11/2011 +09/11/2011 +10/11/2011 +11/11/2011 +12/11/2011 +13/11/2011 +14/11/2011 +15/11/2011 +16/11/2011 +17/11/2011 +18/11/2011 +19/11/2011 +20/11/2011 +21/11/2011 +22/11/2011 +23/11/2011 +24/11/2011 +25/11/2011 +26/11/2011 +27/11/2011 +28/11/2011 +29/11/2011 +30/11/2011 +01/12/2011 +02/12/2011 +03/12/2011 +04/12/2011 +05/12/2011 +06/12/2011 +07/12/2011 +08/12/2011 +09/12/2011 +10/12/2011 +11/12/2011 +12/12/2011 +13/12/2011 +14/12/2011 +15/12/2011 +16/12/2011 +17/12/2011 +18/12/2011 +19/12/2011 +20/12/2011 +21/12/2011 +22/12/2011 +23/12/2011 +24/12/2011 +25/12/2011 +26/12/2011 +27/12/2011 +28/12/2011 +29/12/2011 +30/12/2011 +31/12/2011 +01/01/2012 +02/01/2012 +03/01/2012 +04/01/2012 +05/01/2012 +06/01/2012 +07/01/2012 +08/01/2012 +09/01/2012 +10/01/2012 +11/01/2012 +12/01/2012 +13/01/2012 +14/01/2012 +15/01/2012 +16/01/2012 +17/01/2012 +18/01/2012 +19/01/2012 +20/01/2012 +21/01/2012 +22/01/2012 +23/01/2012 +24/01/2012 +25/01/2012 +26/01/2012 +27/01/2012 +28/01/2012 +29/01/2012 +30/01/2012 +31/01/2012 +01/02/2012 +02/02/2012 +03/02/2012 +04/02/2012 +05/02/2012 +06/02/2012 +07/02/2012 +08/02/2012 +09/02/2012 +10/02/2012 +11/02/2012 +12/02/2012 +13/02/2012 +14/02/2012 +15/02/2012 +16/02/2012 +17/02/2012 +18/02/2012 +19/02/2012 +20/02/2012 +21/02/2012 +22/02/2012 +23/02/2012 +24/02/2012 +25/02/2012 +26/02/2012 +27/02/2012 +28/02/2012 +29/02/2012 +01/03/2012 +02/03/2012 +03/03/2012 +04/03/2012 +05/03/2012 +06/03/2012 +07/03/2012 +08/03/2012 +09/03/2012 +10/03/2012 +11/03/2012 +12/03/2012 +13/03/2012 +14/03/2012 +15/03/2012 +16/03/2012 +17/03/2012 +18/03/2012 +19/03/2012 +20/03/2012 +21/03/2012 +22/03/2012 +23/03/2012 +24/03/2012 +25/03/2012 +26/03/2012 +27/03/2012 +28/03/2012 +29/03/2012 +30/03/2012 +31/03/2012 +01/04/2012 +02/04/2012 +03/04/2012 +04/04/2012 +05/04/2012 +06/04/2012 +07/04/2012 +08/04/2012 +09/04/2012 +10/04/2012 +11/04/2012 +12/04/2012 +13/04/2012 +14/04/2012 +15/04/2012 +16/04/2012 +17/04/2012 +18/04/2012 +19/04/2012 +20/04/2012 +21/04/2012 +22/04/2012 +23/04/2012 +24/04/2012 +25/04/2012 +26/04/2012 +27/04/2012 +28/04/2012 +29/04/2012 +30/04/2012 +01/05/2012 +02/05/2012 +03/05/2012 +04/05/2012 +05/05/2012 +06/05/2012 +07/05/2012 +08/05/2012 +09/05/2012 +10/05/2012 +11/05/2012 +12/05/2012 +13/05/2012 +14/05/2012 +15/05/2012 +16/05/2012 +17/05/2012 +18/05/2012 +19/05/2012 +20/05/2012 +21/05/2012 +22/05/2012 +23/05/2012 +24/05/2012 +25/05/2012 +26/05/2012 +27/05/2012 +28/05/2012 +29/05/2012 +30/05/2012 +31/05/2012 +01/06/2012 +02/06/2012 +03/06/2012 +04/06/2012 +05/06/2012 +06/06/2012 +07/06/2012 +08/06/2012 +09/06/2012 +10/06/2012 +11/06/2012 +12/06/2012 +13/06/2012 +14/06/2012 +15/06/2012 +16/06/2012 +17/06/2012 +18/06/2012 +19/06/2012 +20/06/2012 +21/06/2012 +22/06/2012 +23/06/2012 +24/06/2012 +25/06/2012 +26/06/2012 +27/06/2012 +28/06/2012 +29/06/2012 +30/06/2012 +01/07/2012 +02/07/2012 +03/07/2012 +04/07/2012 +05/07/2012 +06/07/2012 +07/07/2012 +08/07/2012 +09/07/2012 +10/07/2012 +11/07/2012 +12/07/2012 +13/07/2012 +14/07/2012 +15/07/2012 +16/07/2012 +17/07/2012 +18/07/2012 +19/07/2012 +20/07/2012 +21/07/2012 +22/07/2012 +23/07/2012 +24/07/2012 +25/07/2012 +26/07/2012 +27/07/2012 +28/07/2012 +29/07/2012 +30/07/2012 +31/07/2012 +01/08/2012 +02/08/2012 +03/08/2012 +04/08/2012 +05/08/2012 +06/08/2012 +07/08/2012 +08/08/2012 +09/08/2012 +10/08/2012 +11/08/2012 +12/08/2012 +13/08/2012 +14/08/2012 +15/08/2012 +16/08/2012 +17/08/2012 +18/08/2012 +19/08/2012 +20/08/2012 +21/08/2012 +22/08/2012 +23/08/2012 +24/08/2012 +25/08/2012 +26/08/2012 +27/08/2012 +28/08/2012 +29/08/2012 +30/08/2012 +31/08/2012 +01/09/2012 +02/09/2012 +03/09/2012 +04/09/2012 +05/09/2012 +06/09/2012 +07/09/2012 +08/09/2012 +09/09/2012 +10/09/2012 +11/09/2012 +12/09/2012 +13/09/2012 +14/09/2012 +15/09/2012 +16/09/2012 +17/09/2012 +18/09/2012 +19/09/2012 +20/09/2012 +21/09/2012 +22/09/2012 +23/09/2012 +24/09/2012 +25/09/2012 +26/09/2012 +27/09/2012 +28/09/2012 +29/09/2012 +30/09/2012 +01/10/2012 +02/10/2012 +03/10/2012 +04/10/2012 +05/10/2012 +06/10/2012 +07/10/2012 +08/10/2012 +09/10/2012 +10/10/2012 +11/10/2012 +12/10/2012 +13/10/2012 +14/10/2012 +15/10/2012 +16/10/2012 +17/10/2012 +18/10/2012 +19/10/2012 +20/10/2012 +21/10/2012 +22/10/2012 +23/10/2012 +24/10/2012 +25/10/2012 +26/10/2012 +27/10/2012 +28/10/2012 +29/10/2012 +30/10/2012 +31/10/2012 +01/11/2012 +02/11/2012 +03/11/2012 +04/11/2012 +05/11/2012 +06/11/2012 +07/11/2012 +08/11/2012 +09/11/2012 +10/11/2012 +11/11/2012 +12/11/2012 +13/11/2012 +14/11/2012 +15/11/2012 +16/11/2012 +17/11/2012 +18/11/2012 +19/11/2012 +20/11/2012 +21/11/2012 +22/11/2012 +23/11/2012 +24/11/2012 +25/11/2012 +26/11/2012 +27/11/2012 +28/11/2012 +29/11/2012 +30/11/2012 +01/12/2012 +02/12/2012 +03/12/2012 +04/12/2012 +05/12/2012 +06/12/2012 +07/12/2012 +08/12/2012 +09/12/2012 +10/12/2012 +11/12/2012 +12/12/2012 +13/12/2012 +14/12/2012 +15/12/2012 +16/12/2012 +17/12/2012 +18/12/2012 +19/12/2012 +20/12/2012 +21/12/2012 +22/12/2012 +23/12/2012 +24/12/2012 +25/12/2012 +26/12/2012 +27/12/2012 +28/12/2012 +29/12/2012 +30/12/2012 +31/12/2012 +01/01/2013 +02/01/2013 +03/01/2013 +04/01/2013 +05/01/2013 +06/01/2013 +07/01/2013 +08/01/2013 +09/01/2013 +10/01/2013 +11/01/2013 +12/01/2013 +13/01/2013 +14/01/2013 +15/01/2013 +16/01/2013 +17/01/2013 +18/01/2013 +19/01/2013 +20/01/2013 +21/01/2013 +22/01/2013 +23/01/2013 +24/01/2013 +25/01/2013 +26/01/2013 +27/01/2013 +28/01/2013 +29/01/2013 +30/01/2013 +31/01/2013 +01/02/2013 +02/02/2013 +03/02/2013 +04/02/2013 +05/02/2013 +06/02/2013 +07/02/2013 +08/02/2013 +09/02/2013 +10/02/2013 +11/02/2013 +12/02/2013 +13/02/2013 +14/02/2013 +15/02/2013 +16/02/2013 +17/02/2013 +18/02/2013 +19/02/2013 +20/02/2013 +21/02/2013 +22/02/2013 +23/02/2013 +24/02/2013 +25/02/2013 +26/02/2013 +27/02/2013 +28/02/2013 +01/03/2013 +02/03/2013 +03/03/2013 +04/03/2013 +05/03/2013 +06/03/2013 +07/03/2013 +08/03/2013 +09/03/2013 +10/03/2013 +11/03/2013 +12/03/2013 +13/03/2013 +14/03/2013 +15/03/2013 +16/03/2013 +17/03/2013 +18/03/2013 +19/03/2013 +20/03/2013 +21/03/2013 +22/03/2013 +23/03/2013 +24/03/2013 +25/03/2013 +26/03/2013 +27/03/2013 +28/03/2013 +29/03/2013 +30/03/2013 +31/03/2013 +01/04/2013 +02/04/2013 +03/04/2013 +04/04/2013 +05/04/2013 +06/04/2013 +07/04/2013 +08/04/2013 +09/04/2013 +10/04/2013 +11/04/2013 +12/04/2013 +13/04/2013 +14/04/2013 +15/04/2013 +16/04/2013 +17/04/2013 +18/04/2013 +19/04/2013 +20/04/2013 +21/04/2013 +22/04/2013 +23/04/2013 +24/04/2013 +25/04/2013 +26/04/2013 +27/04/2013 +28/04/2013 +29/04/2013 +30/04/2013 +01/05/2013 +02/05/2013 +03/05/2013 +04/05/2013 +05/05/2013 +06/05/2013 +07/05/2013 +08/05/2013 +09/05/2013 +10/05/2013 +11/05/2013 +12/05/2013 +13/05/2013 +14/05/2013 +15/05/2013 +16/05/2013 +17/05/2013 +18/05/2013 +19/05/2013 +20/05/2013 +21/05/2013 +22/05/2013 +23/05/2013 +24/05/2013 +25/05/2013 +26/05/2013 +27/05/2013 +28/05/2013 +29/05/2013 +30/05/2013 +31/05/2013 +01/06/2013 +02/06/2013 +03/06/2013 +04/06/2013 +05/06/2013 +06/06/2013 +07/06/2013 +08/06/2013 +09/06/2013 +10/06/2013 +11/06/2013 +12/06/2013 +13/06/2013 +14/06/2013 +15/06/2013 +16/06/2013 +17/06/2013 +18/06/2013 +19/06/2013 +20/06/2013 +21/06/2013 +22/06/2013 +23/06/2013 +24/06/2013 +25/06/2013 +26/06/2013 +27/06/2013 +28/06/2013 +29/06/2013 +30/06/2013 +01/07/2013 +02/07/2013 +03/07/2013 +04/07/2013 +05/07/2013 +06/07/2013 +07/07/2013 +08/07/2013 +09/07/2013 +10/07/2013 +11/07/2013 +12/07/2013 +13/07/2013 +14/07/2013 +15/07/2013 +16/07/2013 +17/07/2013 +18/07/2013 +19/07/2013 +20/07/2013 +21/07/2013 +22/07/2013 +23/07/2013 +24/07/2013 +25/07/2013 +26/07/2013 +27/07/2013 +28/07/2013 +29/07/2013 +30/07/2013 +31/07/2013 +01/08/2013 +02/08/2013 +03/08/2013 +04/08/2013 +05/08/2013 +06/08/2013 +07/08/2013 +08/08/2013 +09/08/2013 +10/08/2013 +11/08/2013 +12/08/2013 +13/08/2013 +14/08/2013 +15/08/2013 +16/08/2013 +17/08/2013 +18/08/2013 +19/08/2013 +20/08/2013 +21/08/2013 +22/08/2013 +23/08/2013 +24/08/2013 +25/08/2013 +26/08/2013 +27/08/2013 +28/08/2013 +29/08/2013 +30/08/2013 +31/08/2013 +01/09/2013 +02/09/2013 +03/09/2013 +04/09/2013 +05/09/2013 +06/09/2013 +07/09/2013 +08/09/2013 +09/09/2013 +10/09/2013 +11/09/2013 +12/09/2013 +13/09/2013 +14/09/2013 +15/09/2013 +16/09/2013 +17/09/2013 +18/09/2013 +19/09/2013 +20/09/2013 +21/09/2013 +22/09/2013 +23/09/2013 +24/09/2013 +25/09/2013 +26/09/2013 +27/09/2013 +28/09/2013 +29/09/2013 +30/09/2013 +01/10/2013 +02/10/2013 +03/10/2013 +04/10/2013 +05/10/2013 +06/10/2013 +07/10/2013 +08/10/2013 +09/10/2013 +10/10/2013 +11/10/2013 +12/10/2013 +13/10/2013 +14/10/2013 +15/10/2013 +16/10/2013 +17/10/2013 +18/10/2013 +19/10/2013 +20/10/2013 +21/10/2013 +22/10/2013 +23/10/2013 +24/10/2013 +25/10/2013 +26/10/2013 +27/10/2013 +28/10/2013 +29/10/2013 +30/10/2013 +31/10/2013 +01/11/2013 +02/11/2013 +03/11/2013 +04/11/2013 +05/11/2013 +06/11/2013 +07/11/2013 +08/11/2013 +09/11/2013 +10/11/2013 +11/11/2013 +12/11/2013 +13/11/2013 +14/11/2013 +15/11/2013 +16/11/2013 +17/11/2013 +18/11/2013 +19/11/2013 +20/11/2013 +21/11/2013 +22/11/2013 +23/11/2013 +24/11/2013 +25/11/2013 +26/11/2013 +27/11/2013 +28/11/2013 +29/11/2013 +30/11/2013 +01/12/2013 +02/12/2013 +03/12/2013 +04/12/2013 +05/12/2013 +06/12/2013 +07/12/2013 +08/12/2013 +09/12/2013 +10/12/2013 +11/12/2013 +12/12/2013 +13/12/2013 +14/12/2013 +15/12/2013 +16/12/2013 +17/12/2013 +18/12/2013 +19/12/2013 +20/12/2013 +21/12/2013 +22/12/2013 +23/12/2013 +24/12/2013 +25/12/2013 +26/12/2013 +27/12/2013 +28/12/2013 +29/12/2013 +30/12/2013 +31/12/2013 +01/01/2014 +02/01/2014 +03/01/2014 +04/01/2014 +05/01/2014 +06/01/2014 +07/01/2014 +08/01/2014 +09/01/2014 +10/01/2014 +11/01/2014 +12/01/2014 +13/01/2014 +14/01/2014 +15/01/2014 +16/01/2014 +17/01/2014 +18/01/2014 +19/01/2014 +20/01/2014 +21/01/2014 +22/01/2014 +23/01/2014 +24/01/2014 +25/01/2014 +26/01/2014 +27/01/2014 +28/01/2014 +29/01/2014 +30/01/2014 +31/01/2014 +01/02/2014 +02/02/2014 +03/02/2014 +04/02/2014 +05/02/2014 +06/02/2014 +07/02/2014 +08/02/2014 +09/02/2014 +10/02/2014 +11/02/2014 +12/02/2014 +13/02/2014 +14/02/2014 +15/02/2014 +16/02/2014 +17/02/2014 +18/02/2014 +19/02/2014 +20/02/2014 +21/02/2014 +22/02/2014 +23/02/2014 +24/02/2014 +25/02/2014 +26/02/2014 +27/02/2014 +28/02/2014 +01/03/2014 +02/03/2014 +03/03/2014 +04/03/2014 +05/03/2014 +06/03/2014 +07/03/2014 +08/03/2014 +09/03/2014 +10/03/2014 +11/03/2014 +12/03/2014 +13/03/2014 +14/03/2014 +15/03/2014 +16/03/2014 +17/03/2014 +18/03/2014 +19/03/2014 +20/03/2014 +21/03/2014 +22/03/2014 +23/03/2014 +24/03/2014 +25/03/2014 +26/03/2014 +27/03/2014 +28/03/2014 +29/03/2014 +30/03/2014 +31/03/2014 +01/04/2014 +02/04/2014 +03/04/2014 +04/04/2014 +05/04/2014 +06/04/2014 +07/04/2014 +08/04/2014 +09/04/2014 +10/04/2014 +11/04/2014 +12/04/2014 +13/04/2014 +14/04/2014 +15/04/2014 +16/04/2014 +17/04/2014 +18/04/2014 +19/04/2014 +20/04/2014 +21/04/2014 +22/04/2014 +23/04/2014 +24/04/2014 +25/04/2014 +26/04/2014 +27/04/2014 +28/04/2014 +29/04/2014 +30/04/2014 +01/05/2014 +02/05/2014 +03/05/2014 +04/05/2014 +05/05/2014 +06/05/2014 +07/05/2014 +08/05/2014 +09/05/2014 +10/05/2014 +11/05/2014 +12/05/2014 +13/05/2014 +14/05/2014 +15/05/2014 +16/05/2014 +17/05/2014 +18/05/2014 +19/05/2014 +20/05/2014 +21/05/2014 +22/05/2014 +23/05/2014 +24/05/2014 +25/05/2014 +26/05/2014 +27/05/2014 +28/05/2014 +29/05/2014 +30/05/2014 +31/05/2014 +01/06/2014 +02/06/2014 +03/06/2014 +04/06/2014 +05/06/2014 +06/06/2014 +07/06/2014 +08/06/2014 +09/06/2014 +10/06/2014 +11/06/2014 +12/06/2014 +13/06/2014 +14/06/2014 +15/06/2014 +16/06/2014 +17/06/2014 +18/06/2014 +19/06/2014 +20/06/2014 +21/06/2014 +22/06/2014 +23/06/2014 +24/06/2014 +25/06/2014 +26/06/2014 +27/06/2014 +28/06/2014 +29/06/2014 +30/06/2014 +01/07/2014 +02/07/2014 +03/07/2014 +04/07/2014 +05/07/2014 +06/07/2014 +07/07/2014 +08/07/2014 +09/07/2014 +10/07/2014 +11/07/2014 +12/07/2014 +13/07/2014 +14/07/2014 +15/07/2014 +16/07/2014 +17/07/2014 +18/07/2014 +19/07/2014 +20/07/2014 +21/07/2014 +22/07/2014 +23/07/2014 +24/07/2014 +25/07/2014 +26/07/2014 +27/07/2014 +28/07/2014 +29/07/2014 +30/07/2014 +31/07/2014 +01/08/2014 +02/08/2014 +03/08/2014 +04/08/2014 +05/08/2014 +06/08/2014 +07/08/2014 +08/08/2014 +09/08/2014 +10/08/2014 +11/08/2014 +12/08/2014 +13/08/2014 +14/08/2014 +15/08/2014 +16/08/2014 +17/08/2014 +18/08/2014 +19/08/2014 +20/08/2014 +21/08/2014 +22/08/2014 +23/08/2014 +24/08/2014 +25/08/2014 +26/08/2014 +27/08/2014 +28/08/2014 +29/08/2014 +30/08/2014 +31/08/2014 +01/09/2014 +02/09/2014 +03/09/2014 +04/09/2014 +05/09/2014 +06/09/2014 +07/09/2014 +08/09/2014 +09/09/2014 +10/09/2014 +11/09/2014 +12/09/2014 +13/09/2014 +14/09/2014 +15/09/2014 +16/09/2014 +17/09/2014 +18/09/2014 +19/09/2014 +20/09/2014 +21/09/2014 +22/09/2014 +23/09/2014 +24/09/2014 +25/09/2014 +26/09/2014 +27/09/2014 +28/09/2014 +29/09/2014 +30/09/2014 +01/10/2014 +02/10/2014 +03/10/2014 +04/10/2014 +05/10/2014 +06/10/2014 +07/10/2014 +08/10/2014 +09/10/2014 +10/10/2014 +11/10/2014 +12/10/2014 +13/10/2014 +14/10/2014 +15/10/2014 +16/10/2014 +17/10/2014 +18/10/2014 +19/10/2014 +20/10/2014 +21/10/2014 +22/10/2014 +23/10/2014 +24/10/2014 +25/10/2014 +26/10/2014 +27/10/2014 +28/10/2014 +29/10/2014 +30/10/2014 +31/10/2014 +01/11/2014 +02/11/2014 +03/11/2014 +04/11/2014 +05/11/2014 +06/11/2014 +07/11/2014 +08/11/2014 +09/11/2014 +10/11/2014 +11/11/2014 +12/11/2014 +13/11/2014 +14/11/2014 +15/11/2014 +16/11/2014 +17/11/2014 +18/11/2014 +19/11/2014 +20/11/2014 +21/11/2014 +22/11/2014 +23/11/2014 +24/11/2014 +25/11/2014 +26/11/2014 +27/11/2014 +28/11/2014 +29/11/2014 +30/11/2014 +01/12/2014 +02/12/2014 +03/12/2014 +04/12/2014 +05/12/2014 +06/12/2014 +07/12/2014 +08/12/2014 +09/12/2014 +10/12/2014 +11/12/2014 +12/12/2014 +13/12/2014 +14/12/2014 +15/12/2014 +16/12/2014 +17/12/2014 +18/12/2014 +19/12/2014 +20/12/2014 +21/12/2014 +22/12/2014 +23/12/2014 +24/12/2014 +25/12/2014 +26/12/2014 +27/12/2014 +28/12/2014 +29/12/2014 +30/12/2014 +31/12/2014 +01/01/2015 +02/01/2015 +03/01/2015 +04/01/2015 +05/01/2015 +06/01/2015 +07/01/2015 +08/01/2015 +09/01/2015 +10/01/2015 +11/01/2015 +12/01/2015 +13/01/2015 +14/01/2015 +15/01/2015 +16/01/2015 +17/01/2015 +18/01/2015 +19/01/2015 +20/01/2015 +21/01/2015 +22/01/2015 +23/01/2015 +24/01/2015 +25/01/2015 +26/01/2015 +27/01/2015 +28/01/2015 +29/01/2015 +30/01/2015 +31/01/2015 +01/02/2015 +02/02/2015 +03/02/2015 +04/02/2015 +05/02/2015 +06/02/2015 +07/02/2015 +08/02/2015 +09/02/2015 +10/02/2015 +11/02/2015 +12/02/2015 +13/02/2015 +14/02/2015 +15/02/2015 +16/02/2015 +17/02/2015 +18/02/2015 +19/02/2015 +20/02/2015 +21/02/2015 +22/02/2015 +23/02/2015 +24/02/2015 +25/02/2015 +26/02/2015 +27/02/2015 +28/02/2015 +01/03/2015 +02/03/2015 +03/03/2015 +04/03/2015 +05/03/2015 +06/03/2015 +07/03/2015 +08/03/2015 +09/03/2015 +10/03/2015 +11/03/2015 +12/03/2015 +13/03/2015 +14/03/2015 +15/03/2015 +16/03/2015 +17/03/2015 +18/03/2015 +19/03/2015 +20/03/2015 +21/03/2015 +22/03/2015 +23/03/2015 +24/03/2015 +25/03/2015 +26/03/2015 +27/03/2015 +28/03/2015 +29/03/2015 +30/03/2015 +31/03/2015 +01/04/2015 +02/04/2015 +03/04/2015 +04/04/2015 +05/04/2015 +06/04/2015 +07/04/2015 +08/04/2015 +09/04/2015 +10/04/2015 +11/04/2015 +12/04/2015 +13/04/2015 +14/04/2015 +15/04/2015 +16/04/2015 +17/04/2015 +18/04/2015 +19/04/2015 +20/04/2015 +21/04/2015 +22/04/2015 +23/04/2015 +24/04/2015 +25/04/2015 +26/04/2015 +27/04/2015 +28/04/2015 +29/04/2015 +30/04/2015 +01/05/2015 +02/05/2015 +03/05/2015 +04/05/2015 +05/05/2015 +06/05/2015 +07/05/2015 +08/05/2015 +09/05/2015 +10/05/2015 +11/05/2015 +12/05/2015 +13/05/2015 +14/05/2015 +15/05/2015 +16/05/2015 +17/05/2015 +18/05/2015 +19/05/2015 +20/05/2015 +21/05/2015 +22/05/2015 +23/05/2015 +24/05/2015 +25/05/2015 +26/05/2015 +27/05/2015 +28/05/2015 +29/05/2015 +30/05/2015 +31/05/2015 +01/06/2015 +02/06/2015 +03/06/2015 +04/06/2015 +05/06/2015 +06/06/2015 +07/06/2015 +08/06/2015 +09/06/2015 +10/06/2015 +11/06/2015 +12/06/2015 +13/06/2015 +14/06/2015 +15/06/2015 +16/06/2015 +17/06/2015 +18/06/2015 +19/06/2015 +20/06/2015 +21/06/2015 +22/06/2015 +23/06/2015 diff --git a/libres/test-data/local/snake_oil_field/snake_oil.ert b/libres/test-data/local/snake_oil_field/snake_oil.ert new file mode 100644 index 00000000000..27a0ff9e86a --- /dev/null +++ b/libres/test-data/local/snake_oil_field/snake_oil.ert @@ -0,0 +1,47 @@ +QUEUE_SYSTEM LOCAL + +NUM_REALIZATIONS 10 +GRID grid/CASE.EGRID + +DEFINE storage/ +RANDOM_SEED 3593114179000630026631423308983283277868 + +RUNPATH /runpath/realisation-%d/iter-%d +ENSPATH /ensemble +ECLBASE SNAKE_OIL_FIELD +SUMMARY * + +HISTORY_SOURCE REFCASE_HISTORY +REFCASE refcase/SNAKE_OIL_FIELD + +TIME_MAP refcase/time_map.txt +OBS_CONFIG observations/observations.txt + +INSTALL_JOB SNAKE_OIL_SIMULATOR jobs/SNAKE_OIL_SIMULATOR +INSTALL_JOB SNAKE_OIL_NPV jobs/SNAKE_OIL_NPV +INSTALL_JOB SNAKE_OIL_DIFF jobs/SNAKE_OIL_DIFF + +FORWARD_MODEL SNAKE_OIL_SIMULATOR +FORWARD_MODEL SNAKE_OIL_NPV +FORWARD_MODEL SNAKE_OIL_DIFF + +RUN_TEMPLATE templates/seed_template.txt seed.txt + +GEN_KW SNAKE_OIL_PARAM templates/snake_oil_template.txt snake_oil_params.txt parameters/snake_oil_parameters.txt + +GEN_DATA SNAKE_OIL_OPR_DIFF INPUT_FORMAT:ASCII RESULT_FILE:snake_oil_opr_diff_%d.txt REPORT_STEPS:199 +GEN_DATA SNAKE_OIL_WPR_DIFF INPUT_FORMAT:ASCII RESULT_FILE:snake_oil_wpr_diff_%d.txt REPORT_STEPS:199 +GEN_DATA SNAKE_OIL_GPR_DIFF INPUT_FORMAT:ASCII RESULT_FILE:snake_oil_gpr_diff_%d.txt REPORT_STEPS:199 + + +-- Observe that the snake_oil simulator does not really take these +-- field parameters into account, they are mainly here to create a +-- test case with field parameters. + +FIELD PERMX PARAMETER permx.grdcel INIT_FILES:fields/permx%d.grdecl +FIELD PORO PARAMETER poro.grdcel INIT_FILES:fields/poro%d.grdecl + + +LOG_LEVEL INFO +LOG_FILE log/log_snake_oil.ert.txt +UPDATE_LOG_PATH log/update diff --git a/libres/test-data/local/snake_oil_field/snake_oil_surface.ert b/libres/test-data/local/snake_oil_field/snake_oil_surface.ert new file mode 100644 index 00000000000..0d75d960dac --- /dev/null +++ b/libres/test-data/local/snake_oil_field/snake_oil_surface.ert @@ -0,0 +1,47 @@ +QUEUE_SYSTEM LOCAL + +NUM_REALIZATIONS 10 +GRID grid/CASE.EGRID + +DEFINE storage/ + +RANDOM_SEED 3593114179000630026631423308983283277868 + +RUNPATH /runpath/realisation-%d/iter-%d +ENSPATH /ensemble +ECLBASE SNAKE_OIL_FIELD +SUMMARY * + +HISTORY_SOURCE REFCASE_HISTORY +REFCASE refcase/SNAKE_OIL_FIELD + +TIME_MAP refcase/time_map.txt +OBS_CONFIG observations/observations.txt + +INSTALL_JOB SNAKE_OIL_SIMULATOR jobs/SNAKE_OIL_SIMULATOR +INSTALL_JOB SNAKE_OIL_NPV jobs/SNAKE_OIL_NPV +INSTALL_JOB SNAKE_OIL_DIFF jobs/SNAKE_OIL_DIFF + +FORWARD_MODEL SNAKE_OIL_SIMULATOR +FORWARD_MODEL SNAKE_OIL_NPV +FORWARD_MODEL SNAKE_OIL_DIFF + +RUN_TEMPLATE templates/seed_template.txt seed.txt + +GEN_KW SNAKE_OIL_PARAM templates/snake_oil_template.txt snake_oil_params.txt parameters/snake_oil_parameters.txt + +GEN_DATA SNAKE_OIL_OPR_DIFF INPUT_FORMAT:ASCII RESULT_FILE:snake_oil_opr_diff_%d.txt REPORT_STEPS:199 +GEN_DATA SNAKE_OIL_WPR_DIFF INPUT_FORMAT:ASCII RESULT_FILE:snake_oil_wpr_diff_%d.txt REPORT_STEPS:199 +GEN_DATA SNAKE_OIL_GPR_DIFF INPUT_FORMAT:ASCII RESULT_FILE:snake_oil_gpr_diff_%d.txt REPORT_STEPS:199 + + +-- Observe that the snake_oil simulator does not really take these +-- surface parameters into account, they are mainly here to create a +-- test case with surface parameters. + +SURFACE TOP INIT_FILES:surface/small.irap OUTPUT_FILE:surface/small_out.irap BASE_SURFACE:surface/small.irap + + +LOG_LEVEL INFO +LOG_FILE log/log_snake_oil_surface.ert.txt +UPDATE_LOG_PATH log/update diff --git a/libres/test-data/local/snake_oil_field/surface/small.irap b/libres/test-data/local/snake_oil_field/surface/small.irap new file mode 100644 index 00000000000..4817d3295c0 --- /dev/null +++ b/libres/test-data/local/snake_oil_field/surface/small.irap @@ -0,0 +1,104 @@ + -996 20 50.000000 50.000000 + 463325.562500 465725.562500 7336963.500000 7340863.500000 + 30 -65.000000 463325.562500 7336963.500000 + 0 0 0 0 0 0 0 + 0.0051 0.0017 -0.0101 -0.0126 -0.0078 0.0019 + 0.0002 -0.0039 -0.0100 -0.0134 -0.0076 -0.0017 + 0.0014 0.0045 0.0058 -0.0002 -0.0029 -0.0023 + 0.0048 0.0063 0.0107 0.0043 -0.0003 0.0044 + 0.0009 0.0036 0.0033 0.0055 0.0174 0.0168 + 0.0142 0.0073 0.0063 0.0113 0.0083 0.0050 + 0.0092 0.0054 0.0037 0.0034 0.0057 0.0034 + -0.0031 0.0034 0.0035 -0.0119 0.0004 0.0101 + 0.0218 -0.0013 -0.0026 -0.0042 -0.0107 -0.0079 + 0.0000 0.0034 -0.0032 -0.0111 -0.0106 -0.0028 + 0.0015 0.0050 0.0028 -0.0037 -0.0079 -0.0036 + 0.0047 -0.0039 0.0088 0.0091 0.0013 -0.0072 + -0.0075 -0.0027 0.0003 -0.0008 -0.0049 0.0036 + 0.0079 0.0021 0.0085 0.0151 0.0135 0.0112 + 0.0139 0.0176 0.0081 0.0041 0.0083 0.0077 + 0.0023 -0.0049 0.0042 -0.0074 -0.0134 -0.0015 + 0.0173 0.0204 -0.0083 -0.0086 -0.0008 -0.0014 + 0.0000 -0.0002 -0.0005 -0.0024 -0.0070 -0.0145 + -0.0050 0.0015 0.0019 -0.0029 -0.0121 -0.0136 + -0.0050 0.0054 0.0013 0.0056 0.0063 -0.0000 + -0.0084 -0.0124 -0.0023 -0.0012 -0.0062 -0.0078 + 0.0006 0.0045 0.0048 0.0084 0.0124 0.0145 + 0.0173 0.0205 0.0130 0.0071 0.0102 0.0179 + 0.0126 0.0054 -0.0094 0.0025 -0.0025 -0.0166 + -0.0021 0.0228 0.0183 -0.0099 -0.0111 -0.0123 + -0.0120 -0.0098 -0.0070 -0.0053 -0.0030 -0.0048 + -0.0051 -0.0031 0.0009 0.0004 -0.0016 -0.0107 + -0.0129 -0.0066 0.0036 0.0048 0.0015 0.0012 + 0.0030 -0.0055 -0.0066 -0.0023 -0.0030 -0.0007 + 0.0027 0.0078 0.0086 0.0070 0.0101 0.0135 + 0.0124 0.0216 0.0161 0.0074 0.0087 0.0149 + 0.0161 0.0131 0.0078 -0.0079 0.0048 -0.0069 + -0.0132 -0.0016 0.0319 0.0240 -0.0114 -0.0103 + -0.0131 -0.0154 -0.0105 -0.0060 -0.0094 -0.0076 + -0.0022 -0.0014 -0.0002 -0.0002 -0.0023 -0.0033 + -0.0097 -0.0033 -0.0019 0.0055 0.0001 -0.0007 + -0.0086 -0.0082 -0.0063 0.0029 0.0072 0.0071 + 0.0061 0.0140 0.0178 0.0160 0.0136 0.0152 + 0.0189 0.0182 0.0220 0.0201 0.0153 0.0110 + 0.0087 0.0073 0.0117 0.0069 -0.0149 0.0080 + -0.0062 -0.0185 -0.0008 0.0237 0.0083 -0.0104 + -0.0123 -0.0118 -0.0106 -0.0049 0.0025 0.0011 + -0.0009 0.0021 0.0013 0.0016 0.0014 -0.0005 + 0.0006 -0.0033 0.0024 0.0074 0.0009 -0.0040 + -0.0075 -0.0120 -0.0106 0.0043 0.0142 0.0162 + 0.0131 0.0128 0.0202 0.0246 0.0149 0.0068 + 0.0065 0.0058 0.0140 0.0269 0.0311 0.0138 + 0.0024 -0.0052 -0.0062 0.0026 0.0004 -0.0118 + 0.0003 0.0001 -0.0139 0.0065 0.0256 0.0177 + -0.0092 -0.0075 -0.0133 -0.0097 -0.0030 0.0077 + 0.0043 0.0046 -0.0011 -0.0014 0.0012 -0.0021 + -0.0009 -0.0016 0.0036 0.0069 -0.0023 0.0039 + -0.0109 -0.0086 -0.0060 0.0022 0.0148 0.0182 + 0.0134 0.0144 0.0187 0.0197 0.0181 0.0135 + 0.0053 0.0043 0.0135 0.0246 0.0226 0.0140 + 0.0025 -0.0046 -0.0036 -0.0043 -0.0081 0.0108 + -0.0009 -0.0077 -0.0014 -0.0134 0.0091 0.0187 + 0.0182 -0.0004 0.0013 -0.0078 -0.0107 -0.0033 + 0.0008 0.0026 0.0038 0.0007 -0.0001 -0.0023 + -0.0062 -0.0085 -0.0004 0.0099 0.0097 0.0007 + 0.0059 -0.0091 -0.0084 0.0068 0.0153 0.0180 + 0.0133 0.0103 0.0102 0.0142 0.0165 0.0134 + 0.0056 0.0044 0.0037 0.0110 0.0147 0.0119 + 0.0049 0.0008 0.0035 0.0070 -0.0025 -0.0037 + 0.0227 -0.0061 -0.0042 0.0001 -0.0122 0.0085 + 0.0154 0.0151 0.0007 -0.0031 -0.0067 -0.0080 + 0.0023 0.0018 -0.0027 -0.0006 -0.0036 -0.0071 + -0.0089 -0.0072 -0.0072 0.0063 0.0126 0.0161 + 0.0070 -0.0027 -0.0115 0.0002 0.0077 0.0122 + 0.0080 0.0112 0.0143 0.0152 0.0141 0.0140 + 0.0136 0.0111 0.0063 0.0054 0.0067 0.0074 + 0.0109 0.0138 0.0088 0.0088 0.0118 0.0027 + 0.0033 0.0045 -0.0119 0.0004 -0.0078 -0.0018 + 0.0051 0.0055 0.0033 0.0039 -0.0014 -0.0043 + 0.0001 0.0069 -0.0034 -0.0046 -0.0055 -0.0064 + -0.0059 -0.0017 0.0065 0.0068 0.0088 0.0131 + 0.0041 0.0010 -0.0020 0.0034 0.0068 0.0054 + 0.0080 0.0107 0.0155 0.0135 0.0169 0.0161 + 0.0094 0.0124 0.0138 0.0103 0.0079 0.0055 + 0.0060 0.0122 0.0193 0.0169 0.0227 0.0157 + 0.0098 0.0089 -0.0063 -0.0101 -0.0043 -0.0097 + 0.0206 0.0057 0.0016 0.0022 0.0037 0.0029 + 0.0045 0.0092 0.0073 -0.0041 -0.0055 -0.0103 + -0.0107 -0.0068 -0.0028 -0.0004 0.0007 0.0017 + -0.0022 -0.0001 0.0007 -0.0030 0.0064 0.0025 + 0.0045 0.0054 0.0060 0.0132 0.0164 0.0233 + 0.0204 0.0169 0.0172 0.0172 0.0148 0.0136 + 0.0143 0.0147 0.0150 0.0165 0.0156 0.0219 + 0.0157 0.0071 0.0103 -0.0051 -0.0107 -0.0103 + 0.0068 0.0203 0.0130 0.0104 0.0098 0.0038 + 0.0046 0.0099 0.0131 0.0117 -0.0061 -0.0010 + -0.0050 0.0006 0.0028 0.0034 0.0042 0.0030 + 0.0032 -0.0036 -0.0011 0.0054 -0.0062 0.0077 + 0.0073 0.0083 0.0079 0.0095 0.0091 0.0136 + 0.0198 0.0228 0.0248 0.0200 0.0164 0.0180 + 0.0182 0.0207 0.0211 0.0157 0.0134 0.0137 + 0.0103 0.0108 0.0148 0.0091 -0.0106 -0.0164 + -0.0062 0.0124 0.0180 0.0156 0.0117 0.0092 + 0.0060 0.0059 0.0045 0.0056 0.0058 -0.0049 + 0.0024 0.0080 0.0086 0.0108 -0.0004 -0.0023 diff --git a/libres/test-data/local/snake_oil_field/templates/seed_template.txt b/libres/test-data/local/snake_oil_field/templates/seed_template.txt new file mode 100644 index 00000000000..a0bca49fbdb --- /dev/null +++ b/libres/test-data/local/snake_oil_field/templates/seed_template.txt @@ -0,0 +1 @@ +SEED: \ No newline at end of file diff --git a/libres/test-data/local/snake_oil_field/templates/snake_oil_template.txt b/libres/test-data/local/snake_oil_field/templates/snake_oil_template.txt new file mode 100644 index 00000000000..ad2c648a0b5 --- /dev/null +++ b/libres/test-data/local/snake_oil_field/templates/snake_oil_template.txt @@ -0,0 +1,10 @@ +OP1_PERSISTENCE: +OP1_OCTAVES: +OP1_DIVERGENCE_SCALE: +OP1_OFFSET: +OP2_PERSISTENCE: +OP2_OCTAVES: +OP2_DIVERGENCE_SCALE: +OP2_OFFSET: +BPR_555_PERSISTENCE: +BPR_138_PERSISTENCE: diff --git a/libres/test-data/local/snake_oil_no_data/jobs/SNAKE_OIL_DIFF b/libres/test-data/local/snake_oil_no_data/jobs/SNAKE_OIL_DIFF new file mode 100644 index 00000000000..98a867d9594 --- /dev/null +++ b/libres/test-data/local/snake_oil_no_data/jobs/SNAKE_OIL_DIFF @@ -0,0 +1,4 @@ +STDOUT snake_oil_diff.stdout +STDERR snake_oil_diff.stderr + +EXECUTABLE snake_oil_diff.py \ No newline at end of file diff --git a/libres/test-data/local/snake_oil_no_data/jobs/SNAKE_OIL_NPV b/libres/test-data/local/snake_oil_no_data/jobs/SNAKE_OIL_NPV new file mode 100644 index 00000000000..887830c756f --- /dev/null +++ b/libres/test-data/local/snake_oil_no_data/jobs/SNAKE_OIL_NPV @@ -0,0 +1,4 @@ +STDOUT snake_oil_npv.stdout +STDERR snake_oil_npv.stderr + +EXECUTABLE snake_oil_npv.py \ No newline at end of file diff --git a/libres/test-data/local/snake_oil_no_data/jobs/SNAKE_OIL_SIMULATOR b/libres/test-data/local/snake_oil_no_data/jobs/SNAKE_OIL_SIMULATOR new file mode 100644 index 00000000000..b4b7f9928fd --- /dev/null +++ b/libres/test-data/local/snake_oil_no_data/jobs/SNAKE_OIL_SIMULATOR @@ -0,0 +1,4 @@ +STDOUT snake_oil.stdout +STDERR snake_oil.stderr + +EXECUTABLE snake_oil_simulator.py \ No newline at end of file diff --git a/libres/test-data/local/snake_oil_no_data/jobs/snake_oil_diff.py b/libres/test-data/local/snake_oil_no_data/jobs/snake_oil_diff.py new file mode 120000 index 00000000000..126a5b22456 --- /dev/null +++ b/libres/test-data/local/snake_oil_no_data/jobs/snake_oil_diff.py @@ -0,0 +1 @@ +../../jobs_snake_oil/snake_oil_diff.py \ No newline at end of file diff --git a/libres/test-data/local/snake_oil_no_data/jobs/snake_oil_npv.py b/libres/test-data/local/snake_oil_no_data/jobs/snake_oil_npv.py new file mode 120000 index 00000000000..a69f08b34fb --- /dev/null +++ b/libres/test-data/local/snake_oil_no_data/jobs/snake_oil_npv.py @@ -0,0 +1 @@ +../../jobs_snake_oil/snake_oil_npv.py \ No newline at end of file diff --git a/libres/test-data/local/snake_oil_no_data/jobs/snake_oil_simulator.py b/libres/test-data/local/snake_oil_no_data/jobs/snake_oil_simulator.py new file mode 120000 index 00000000000..4e6d00ddcf3 --- /dev/null +++ b/libres/test-data/local/snake_oil_no_data/jobs/snake_oil_simulator.py @@ -0,0 +1 @@ +../../jobs_snake_oil/snake_oil_simulator.py \ No newline at end of file diff --git a/libres/test-data/local/snake_oil_no_data/observations/observations.txt b/libres/test-data/local/snake_oil_no_data/observations/observations.txt new file mode 100644 index 00000000000..483c1d59ab3 --- /dev/null +++ b/libres/test-data/local/snake_oil_no_data/observations/observations.txt @@ -0,0 +1,56 @@ +HISTORY_OBSERVATION FOPR; + +SUMMARY_OBSERVATION WOPR_OP1_9 +{ + VALUE = 0.1; + ERROR = 0.05; + RESTART = 9; + KEY = WOPR:OP1; +}; + +SUMMARY_OBSERVATION WOPR_OP1_36 +{ + VALUE = 0.7; + ERROR = 0.07; + RESTART = 36; + KEY = WOPR:OP1; +}; + +SUMMARY_OBSERVATION WOPR_OP1_72 +{ + VALUE = 0.5; + ERROR = 0.05; + RESTART = 72; + KEY = WOPR:OP1; +}; + +SUMMARY_OBSERVATION WOPR_OP1_108 +{ + VALUE = 0.3; + ERROR = 0.075; + RESTART = 108; + KEY = WOPR:OP1; +}; + +SUMMARY_OBSERVATION WOPR_OP1_144 +{ + VALUE = 0.2; + ERROR = 0.035; + RESTART = 144; + KEY = WOPR:OP1; +}; + +SUMMARY_OBSERVATION WOPR_OP1_190 +{ + VALUE = 0.015; + ERROR = 0.01; + RESTART = 190; + KEY = WOPR:OP1; +}; + +GENERAL_OBSERVATION WPR_DIFF_1 { + DATA = SNAKE_OIL_WPR_DIFF; + INDEX_LIST = 400,800,1200,1800; + RESTART = 199; + OBS_FILE = wpr_diff_obs.txt; +}; diff --git a/libres/test-data/local/snake_oil_no_data/observations/wpr_diff_obs.txt b/libres/test-data/local/snake_oil_no_data/observations/wpr_diff_obs.txt new file mode 100644 index 00000000000..4aee6d7ef7c --- /dev/null +++ b/libres/test-data/local/snake_oil_no_data/observations/wpr_diff_obs.txt @@ -0,0 +1,4 @@ +0.0 0.1 +0.1 0.2 +0.2 0.15 +0.0 0.05 diff --git a/libres/test-data/local/snake_oil_no_data/parameters/snake_oil_parameters.txt b/libres/test-data/local/snake_oil_no_data/parameters/snake_oil_parameters.txt new file mode 100644 index 00000000000..64573d0058b --- /dev/null +++ b/libres/test-data/local/snake_oil_no_data/parameters/snake_oil_parameters.txt @@ -0,0 +1,11 @@ +OP1_PERSISTENCE UNIFORM 0.01 0.4 +OP1_OCTAVES UNIFORM 3 5 +OP1_DIVERGENCE_SCALE UNIFORM 0.25 1.25 +OP1_OFFSET UNIFORM -0.1 0.1 +OP2_PERSISTENCE UNIFORM 0.1 0.6 +OP2_OCTAVES UNIFORM 5 12 +OP2_DIVERGENCE_SCALE UNIFORM 0.5 1.5 +OP2_OFFSET UNIFORM -0.2 0.2 +BPR_555_PERSISTENCE UNIFORM 0.1 0.5 +BPR_138_PERSISTENCE UNIFORM 0.2 0.7 + diff --git a/libres/test-data/local/snake_oil_no_data/refcase/SNAKE_OIL_FIELD.SMSPEC b/libres/test-data/local/snake_oil_no_data/refcase/SNAKE_OIL_FIELD.SMSPEC new file mode 100644 index 00000000000..5a29e043b2d Binary files /dev/null and b/libres/test-data/local/snake_oil_no_data/refcase/SNAKE_OIL_FIELD.SMSPEC differ diff --git a/libres/test-data/local/snake_oil_no_data/refcase/SNAKE_OIL_FIELD.UNSMRY b/libres/test-data/local/snake_oil_no_data/refcase/SNAKE_OIL_FIELD.UNSMRY new file mode 100644 index 00000000000..dfa04f80027 Binary files /dev/null and b/libres/test-data/local/snake_oil_no_data/refcase/SNAKE_OIL_FIELD.UNSMRY differ diff --git a/libres/test-data/local/snake_oil_no_data/refcase/refcase_readme.txt b/libres/test-data/local/snake_oil_no_data/refcase/refcase_readme.txt new file mode 100644 index 00000000000..a3d0fe60589 --- /dev/null +++ b/libres/test-data/local/snake_oil_no_data/refcase/refcase_readme.txt @@ -0,0 +1 @@ +To create a refcase run the snake_oil_simulator.py job with the this as working directory. \ No newline at end of file diff --git a/libres/test-data/local/snake_oil_no_data/refcase/seed.txt b/libres/test-data/local/snake_oil_no_data/refcase/seed.txt new file mode 100644 index 00000000000..0009f6e89a5 --- /dev/null +++ b/libres/test-data/local/snake_oil_no_data/refcase/seed.txt @@ -0,0 +1 @@ +SEED:268776 \ No newline at end of file diff --git a/libres/test-data/local/snake_oil_no_data/refcase/snake_oil_params.txt b/libres/test-data/local/snake_oil_no_data/refcase/snake_oil_params.txt new file mode 100644 index 00000000000..3868522924c --- /dev/null +++ b/libres/test-data/local/snake_oil_no_data/refcase/snake_oil_params.txt @@ -0,0 +1,10 @@ +OP1_PERSISTENCE:0.15 +OP1_OCTAVES:4 +OP1_DIVERGENCE_SCALE:0.5 +OP1_OFFSET:0.0 +OP2_PERSISTENCE:0.25 +OP2_OCTAVES:7.0 +OP2_DIVERGENCE_SCALE:1.0 +OP2_OFFSET:0.0 +BPR_555_PERSISTENCE:0.25 +BPR_138_PERSISTENCE:0.35 diff --git a/libres/test-data/local/snake_oil_no_data/refcase/time_map.txt b/libres/test-data/local/snake_oil_no_data/refcase/time_map.txt new file mode 100644 index 00000000000..d54b4293aec --- /dev/null +++ b/libres/test-data/local/snake_oil_no_data/refcase/time_map.txt @@ -0,0 +1,2000 @@ +01/01/2010 +02/01/2010 +03/01/2010 +04/01/2010 +05/01/2010 +06/01/2010 +07/01/2010 +08/01/2010 +09/01/2010 +10/01/2010 +11/01/2010 +12/01/2010 +13/01/2010 +14/01/2010 +15/01/2010 +16/01/2010 +17/01/2010 +18/01/2010 +19/01/2010 +20/01/2010 +21/01/2010 +22/01/2010 +23/01/2010 +24/01/2010 +25/01/2010 +26/01/2010 +27/01/2010 +28/01/2010 +29/01/2010 +30/01/2010 +31/01/2010 +01/02/2010 +02/02/2010 +03/02/2010 +04/02/2010 +05/02/2010 +06/02/2010 +07/02/2010 +08/02/2010 +09/02/2010 +10/02/2010 +11/02/2010 +12/02/2010 +13/02/2010 +14/02/2010 +15/02/2010 +16/02/2010 +17/02/2010 +18/02/2010 +19/02/2010 +20/02/2010 +21/02/2010 +22/02/2010 +23/02/2010 +24/02/2010 +25/02/2010 +26/02/2010 +27/02/2010 +28/02/2010 +01/03/2010 +02/03/2010 +03/03/2010 +04/03/2010 +05/03/2010 +06/03/2010 +07/03/2010 +08/03/2010 +09/03/2010 +10/03/2010 +11/03/2010 +12/03/2010 +13/03/2010 +14/03/2010 +15/03/2010 +16/03/2010 +17/03/2010 +18/03/2010 +19/03/2010 +20/03/2010 +21/03/2010 +22/03/2010 +23/03/2010 +24/03/2010 +25/03/2010 +26/03/2010 +27/03/2010 +28/03/2010 +29/03/2010 +30/03/2010 +31/03/2010 +01/04/2010 +02/04/2010 +03/04/2010 +04/04/2010 +05/04/2010 +06/04/2010 +07/04/2010 +08/04/2010 +09/04/2010 +10/04/2010 +11/04/2010 +12/04/2010 +13/04/2010 +14/04/2010 +15/04/2010 +16/04/2010 +17/04/2010 +18/04/2010 +19/04/2010 +20/04/2010 +21/04/2010 +22/04/2010 +23/04/2010 +24/04/2010 +25/04/2010 +26/04/2010 +27/04/2010 +28/04/2010 +29/04/2010 +30/04/2010 +01/05/2010 +02/05/2010 +03/05/2010 +04/05/2010 +05/05/2010 +06/05/2010 +07/05/2010 +08/05/2010 +09/05/2010 +10/05/2010 +11/05/2010 +12/05/2010 +13/05/2010 +14/05/2010 +15/05/2010 +16/05/2010 +17/05/2010 +18/05/2010 +19/05/2010 +20/05/2010 +21/05/2010 +22/05/2010 +23/05/2010 +24/05/2010 +25/05/2010 +26/05/2010 +27/05/2010 +28/05/2010 +29/05/2010 +30/05/2010 +31/05/2010 +01/06/2010 +02/06/2010 +03/06/2010 +04/06/2010 +05/06/2010 +06/06/2010 +07/06/2010 +08/06/2010 +09/06/2010 +10/06/2010 +11/06/2010 +12/06/2010 +13/06/2010 +14/06/2010 +15/06/2010 +16/06/2010 +17/06/2010 +18/06/2010 +19/06/2010 +20/06/2010 +21/06/2010 +22/06/2010 +23/06/2010 +24/06/2010 +25/06/2010 +26/06/2010 +27/06/2010 +28/06/2010 +29/06/2010 +30/06/2010 +01/07/2010 +02/07/2010 +03/07/2010 +04/07/2010 +05/07/2010 +06/07/2010 +07/07/2010 +08/07/2010 +09/07/2010 +10/07/2010 +11/07/2010 +12/07/2010 +13/07/2010 +14/07/2010 +15/07/2010 +16/07/2010 +17/07/2010 +18/07/2010 +19/07/2010 +20/07/2010 +21/07/2010 +22/07/2010 +23/07/2010 +24/07/2010 +25/07/2010 +26/07/2010 +27/07/2010 +28/07/2010 +29/07/2010 +30/07/2010 +31/07/2010 +01/08/2010 +02/08/2010 +03/08/2010 +04/08/2010 +05/08/2010 +06/08/2010 +07/08/2010 +08/08/2010 +09/08/2010 +10/08/2010 +11/08/2010 +12/08/2010 +13/08/2010 +14/08/2010 +15/08/2010 +16/08/2010 +17/08/2010 +18/08/2010 +19/08/2010 +20/08/2010 +21/08/2010 +22/08/2010 +23/08/2010 +24/08/2010 +25/08/2010 +26/08/2010 +27/08/2010 +28/08/2010 +29/08/2010 +30/08/2010 +31/08/2010 +01/09/2010 +02/09/2010 +03/09/2010 +04/09/2010 +05/09/2010 +06/09/2010 +07/09/2010 +08/09/2010 +09/09/2010 +10/09/2010 +11/09/2010 +12/09/2010 +13/09/2010 +14/09/2010 +15/09/2010 +16/09/2010 +17/09/2010 +18/09/2010 +19/09/2010 +20/09/2010 +21/09/2010 +22/09/2010 +23/09/2010 +24/09/2010 +25/09/2010 +26/09/2010 +27/09/2010 +28/09/2010 +29/09/2010 +30/09/2010 +01/10/2010 +02/10/2010 +03/10/2010 +04/10/2010 +05/10/2010 +06/10/2010 +07/10/2010 +08/10/2010 +09/10/2010 +10/10/2010 +11/10/2010 +12/10/2010 +13/10/2010 +14/10/2010 +15/10/2010 +16/10/2010 +17/10/2010 +18/10/2010 +19/10/2010 +20/10/2010 +21/10/2010 +22/10/2010 +23/10/2010 +24/10/2010 +25/10/2010 +26/10/2010 +27/10/2010 +28/10/2010 +29/10/2010 +30/10/2010 +31/10/2010 +01/11/2010 +02/11/2010 +03/11/2010 +04/11/2010 +05/11/2010 +06/11/2010 +07/11/2010 +08/11/2010 +09/11/2010 +10/11/2010 +11/11/2010 +12/11/2010 +13/11/2010 +14/11/2010 +15/11/2010 +16/11/2010 +17/11/2010 +18/11/2010 +19/11/2010 +20/11/2010 +21/11/2010 +22/11/2010 +23/11/2010 +24/11/2010 +25/11/2010 +26/11/2010 +27/11/2010 +28/11/2010 +29/11/2010 +30/11/2010 +01/12/2010 +02/12/2010 +03/12/2010 +04/12/2010 +05/12/2010 +06/12/2010 +07/12/2010 +08/12/2010 +09/12/2010 +10/12/2010 +11/12/2010 +12/12/2010 +13/12/2010 +14/12/2010 +15/12/2010 +16/12/2010 +17/12/2010 +18/12/2010 +19/12/2010 +20/12/2010 +21/12/2010 +22/12/2010 +23/12/2010 +24/12/2010 +25/12/2010 +26/12/2010 +27/12/2010 +28/12/2010 +29/12/2010 +30/12/2010 +31/12/2010 +01/01/2011 +02/01/2011 +03/01/2011 +04/01/2011 +05/01/2011 +06/01/2011 +07/01/2011 +08/01/2011 +09/01/2011 +10/01/2011 +11/01/2011 +12/01/2011 +13/01/2011 +14/01/2011 +15/01/2011 +16/01/2011 +17/01/2011 +18/01/2011 +19/01/2011 +20/01/2011 +21/01/2011 +22/01/2011 +23/01/2011 +24/01/2011 +25/01/2011 +26/01/2011 +27/01/2011 +28/01/2011 +29/01/2011 +30/01/2011 +31/01/2011 +01/02/2011 +02/02/2011 +03/02/2011 +04/02/2011 +05/02/2011 +06/02/2011 +07/02/2011 +08/02/2011 +09/02/2011 +10/02/2011 +11/02/2011 +12/02/2011 +13/02/2011 +14/02/2011 +15/02/2011 +16/02/2011 +17/02/2011 +18/02/2011 +19/02/2011 +20/02/2011 +21/02/2011 +22/02/2011 +23/02/2011 +24/02/2011 +25/02/2011 +26/02/2011 +27/02/2011 +28/02/2011 +01/03/2011 +02/03/2011 +03/03/2011 +04/03/2011 +05/03/2011 +06/03/2011 +07/03/2011 +08/03/2011 +09/03/2011 +10/03/2011 +11/03/2011 +12/03/2011 +13/03/2011 +14/03/2011 +15/03/2011 +16/03/2011 +17/03/2011 +18/03/2011 +19/03/2011 +20/03/2011 +21/03/2011 +22/03/2011 +23/03/2011 +24/03/2011 +25/03/2011 +26/03/2011 +27/03/2011 +28/03/2011 +29/03/2011 +30/03/2011 +31/03/2011 +01/04/2011 +02/04/2011 +03/04/2011 +04/04/2011 +05/04/2011 +06/04/2011 +07/04/2011 +08/04/2011 +09/04/2011 +10/04/2011 +11/04/2011 +12/04/2011 +13/04/2011 +14/04/2011 +15/04/2011 +16/04/2011 +17/04/2011 +18/04/2011 +19/04/2011 +20/04/2011 +21/04/2011 +22/04/2011 +23/04/2011 +24/04/2011 +25/04/2011 +26/04/2011 +27/04/2011 +28/04/2011 +29/04/2011 +30/04/2011 +01/05/2011 +02/05/2011 +03/05/2011 +04/05/2011 +05/05/2011 +06/05/2011 +07/05/2011 +08/05/2011 +09/05/2011 +10/05/2011 +11/05/2011 +12/05/2011 +13/05/2011 +14/05/2011 +15/05/2011 +16/05/2011 +17/05/2011 +18/05/2011 +19/05/2011 +20/05/2011 +21/05/2011 +22/05/2011 +23/05/2011 +24/05/2011 +25/05/2011 +26/05/2011 +27/05/2011 +28/05/2011 +29/05/2011 +30/05/2011 +31/05/2011 +01/06/2011 +02/06/2011 +03/06/2011 +04/06/2011 +05/06/2011 +06/06/2011 +07/06/2011 +08/06/2011 +09/06/2011 +10/06/2011 +11/06/2011 +12/06/2011 +13/06/2011 +14/06/2011 +15/06/2011 +16/06/2011 +17/06/2011 +18/06/2011 +19/06/2011 +20/06/2011 +21/06/2011 +22/06/2011 +23/06/2011 +24/06/2011 +25/06/2011 +26/06/2011 +27/06/2011 +28/06/2011 +29/06/2011 +30/06/2011 +01/07/2011 +02/07/2011 +03/07/2011 +04/07/2011 +05/07/2011 +06/07/2011 +07/07/2011 +08/07/2011 +09/07/2011 +10/07/2011 +11/07/2011 +12/07/2011 +13/07/2011 +14/07/2011 +15/07/2011 +16/07/2011 +17/07/2011 +18/07/2011 +19/07/2011 +20/07/2011 +21/07/2011 +22/07/2011 +23/07/2011 +24/07/2011 +25/07/2011 +26/07/2011 +27/07/2011 +28/07/2011 +29/07/2011 +30/07/2011 +31/07/2011 +01/08/2011 +02/08/2011 +03/08/2011 +04/08/2011 +05/08/2011 +06/08/2011 +07/08/2011 +08/08/2011 +09/08/2011 +10/08/2011 +11/08/2011 +12/08/2011 +13/08/2011 +14/08/2011 +15/08/2011 +16/08/2011 +17/08/2011 +18/08/2011 +19/08/2011 +20/08/2011 +21/08/2011 +22/08/2011 +23/08/2011 +24/08/2011 +25/08/2011 +26/08/2011 +27/08/2011 +28/08/2011 +29/08/2011 +30/08/2011 +31/08/2011 +01/09/2011 +02/09/2011 +03/09/2011 +04/09/2011 +05/09/2011 +06/09/2011 +07/09/2011 +08/09/2011 +09/09/2011 +10/09/2011 +11/09/2011 +12/09/2011 +13/09/2011 +14/09/2011 +15/09/2011 +16/09/2011 +17/09/2011 +18/09/2011 +19/09/2011 +20/09/2011 +21/09/2011 +22/09/2011 +23/09/2011 +24/09/2011 +25/09/2011 +26/09/2011 +27/09/2011 +28/09/2011 +29/09/2011 +30/09/2011 +01/10/2011 +02/10/2011 +03/10/2011 +04/10/2011 +05/10/2011 +06/10/2011 +07/10/2011 +08/10/2011 +09/10/2011 +10/10/2011 +11/10/2011 +12/10/2011 +13/10/2011 +14/10/2011 +15/10/2011 +16/10/2011 +17/10/2011 +18/10/2011 +19/10/2011 +20/10/2011 +21/10/2011 +22/10/2011 +23/10/2011 +24/10/2011 +25/10/2011 +26/10/2011 +27/10/2011 +28/10/2011 +29/10/2011 +30/10/2011 +31/10/2011 +01/11/2011 +02/11/2011 +03/11/2011 +04/11/2011 +05/11/2011 +06/11/2011 +07/11/2011 +08/11/2011 +09/11/2011 +10/11/2011 +11/11/2011 +12/11/2011 +13/11/2011 +14/11/2011 +15/11/2011 +16/11/2011 +17/11/2011 +18/11/2011 +19/11/2011 +20/11/2011 +21/11/2011 +22/11/2011 +23/11/2011 +24/11/2011 +25/11/2011 +26/11/2011 +27/11/2011 +28/11/2011 +29/11/2011 +30/11/2011 +01/12/2011 +02/12/2011 +03/12/2011 +04/12/2011 +05/12/2011 +06/12/2011 +07/12/2011 +08/12/2011 +09/12/2011 +10/12/2011 +11/12/2011 +12/12/2011 +13/12/2011 +14/12/2011 +15/12/2011 +16/12/2011 +17/12/2011 +18/12/2011 +19/12/2011 +20/12/2011 +21/12/2011 +22/12/2011 +23/12/2011 +24/12/2011 +25/12/2011 +26/12/2011 +27/12/2011 +28/12/2011 +29/12/2011 +30/12/2011 +31/12/2011 +01/01/2012 +02/01/2012 +03/01/2012 +04/01/2012 +05/01/2012 +06/01/2012 +07/01/2012 +08/01/2012 +09/01/2012 +10/01/2012 +11/01/2012 +12/01/2012 +13/01/2012 +14/01/2012 +15/01/2012 +16/01/2012 +17/01/2012 +18/01/2012 +19/01/2012 +20/01/2012 +21/01/2012 +22/01/2012 +23/01/2012 +24/01/2012 +25/01/2012 +26/01/2012 +27/01/2012 +28/01/2012 +29/01/2012 +30/01/2012 +31/01/2012 +01/02/2012 +02/02/2012 +03/02/2012 +04/02/2012 +05/02/2012 +06/02/2012 +07/02/2012 +08/02/2012 +09/02/2012 +10/02/2012 +11/02/2012 +12/02/2012 +13/02/2012 +14/02/2012 +15/02/2012 +16/02/2012 +17/02/2012 +18/02/2012 +19/02/2012 +20/02/2012 +21/02/2012 +22/02/2012 +23/02/2012 +24/02/2012 +25/02/2012 +26/02/2012 +27/02/2012 +28/02/2012 +29/02/2012 +01/03/2012 +02/03/2012 +03/03/2012 +04/03/2012 +05/03/2012 +06/03/2012 +07/03/2012 +08/03/2012 +09/03/2012 +10/03/2012 +11/03/2012 +12/03/2012 +13/03/2012 +14/03/2012 +15/03/2012 +16/03/2012 +17/03/2012 +18/03/2012 +19/03/2012 +20/03/2012 +21/03/2012 +22/03/2012 +23/03/2012 +24/03/2012 +25/03/2012 +26/03/2012 +27/03/2012 +28/03/2012 +29/03/2012 +30/03/2012 +31/03/2012 +01/04/2012 +02/04/2012 +03/04/2012 +04/04/2012 +05/04/2012 +06/04/2012 +07/04/2012 +08/04/2012 +09/04/2012 +10/04/2012 +11/04/2012 +12/04/2012 +13/04/2012 +14/04/2012 +15/04/2012 +16/04/2012 +17/04/2012 +18/04/2012 +19/04/2012 +20/04/2012 +21/04/2012 +22/04/2012 +23/04/2012 +24/04/2012 +25/04/2012 +26/04/2012 +27/04/2012 +28/04/2012 +29/04/2012 +30/04/2012 +01/05/2012 +02/05/2012 +03/05/2012 +04/05/2012 +05/05/2012 +06/05/2012 +07/05/2012 +08/05/2012 +09/05/2012 +10/05/2012 +11/05/2012 +12/05/2012 +13/05/2012 +14/05/2012 +15/05/2012 +16/05/2012 +17/05/2012 +18/05/2012 +19/05/2012 +20/05/2012 +21/05/2012 +22/05/2012 +23/05/2012 +24/05/2012 +25/05/2012 +26/05/2012 +27/05/2012 +28/05/2012 +29/05/2012 +30/05/2012 +31/05/2012 +01/06/2012 +02/06/2012 +03/06/2012 +04/06/2012 +05/06/2012 +06/06/2012 +07/06/2012 +08/06/2012 +09/06/2012 +10/06/2012 +11/06/2012 +12/06/2012 +13/06/2012 +14/06/2012 +15/06/2012 +16/06/2012 +17/06/2012 +18/06/2012 +19/06/2012 +20/06/2012 +21/06/2012 +22/06/2012 +23/06/2012 +24/06/2012 +25/06/2012 +26/06/2012 +27/06/2012 +28/06/2012 +29/06/2012 +30/06/2012 +01/07/2012 +02/07/2012 +03/07/2012 +04/07/2012 +05/07/2012 +06/07/2012 +07/07/2012 +08/07/2012 +09/07/2012 +10/07/2012 +11/07/2012 +12/07/2012 +13/07/2012 +14/07/2012 +15/07/2012 +16/07/2012 +17/07/2012 +18/07/2012 +19/07/2012 +20/07/2012 +21/07/2012 +22/07/2012 +23/07/2012 +24/07/2012 +25/07/2012 +26/07/2012 +27/07/2012 +28/07/2012 +29/07/2012 +30/07/2012 +31/07/2012 +01/08/2012 +02/08/2012 +03/08/2012 +04/08/2012 +05/08/2012 +06/08/2012 +07/08/2012 +08/08/2012 +09/08/2012 +10/08/2012 +11/08/2012 +12/08/2012 +13/08/2012 +14/08/2012 +15/08/2012 +16/08/2012 +17/08/2012 +18/08/2012 +19/08/2012 +20/08/2012 +21/08/2012 +22/08/2012 +23/08/2012 +24/08/2012 +25/08/2012 +26/08/2012 +27/08/2012 +28/08/2012 +29/08/2012 +30/08/2012 +31/08/2012 +01/09/2012 +02/09/2012 +03/09/2012 +04/09/2012 +05/09/2012 +06/09/2012 +07/09/2012 +08/09/2012 +09/09/2012 +10/09/2012 +11/09/2012 +12/09/2012 +13/09/2012 +14/09/2012 +15/09/2012 +16/09/2012 +17/09/2012 +18/09/2012 +19/09/2012 +20/09/2012 +21/09/2012 +22/09/2012 +23/09/2012 +24/09/2012 +25/09/2012 +26/09/2012 +27/09/2012 +28/09/2012 +29/09/2012 +30/09/2012 +01/10/2012 +02/10/2012 +03/10/2012 +04/10/2012 +05/10/2012 +06/10/2012 +07/10/2012 +08/10/2012 +09/10/2012 +10/10/2012 +11/10/2012 +12/10/2012 +13/10/2012 +14/10/2012 +15/10/2012 +16/10/2012 +17/10/2012 +18/10/2012 +19/10/2012 +20/10/2012 +21/10/2012 +22/10/2012 +23/10/2012 +24/10/2012 +25/10/2012 +26/10/2012 +27/10/2012 +28/10/2012 +29/10/2012 +30/10/2012 +31/10/2012 +01/11/2012 +02/11/2012 +03/11/2012 +04/11/2012 +05/11/2012 +06/11/2012 +07/11/2012 +08/11/2012 +09/11/2012 +10/11/2012 +11/11/2012 +12/11/2012 +13/11/2012 +14/11/2012 +15/11/2012 +16/11/2012 +17/11/2012 +18/11/2012 +19/11/2012 +20/11/2012 +21/11/2012 +22/11/2012 +23/11/2012 +24/11/2012 +25/11/2012 +26/11/2012 +27/11/2012 +28/11/2012 +29/11/2012 +30/11/2012 +01/12/2012 +02/12/2012 +03/12/2012 +04/12/2012 +05/12/2012 +06/12/2012 +07/12/2012 +08/12/2012 +09/12/2012 +10/12/2012 +11/12/2012 +12/12/2012 +13/12/2012 +14/12/2012 +15/12/2012 +16/12/2012 +17/12/2012 +18/12/2012 +19/12/2012 +20/12/2012 +21/12/2012 +22/12/2012 +23/12/2012 +24/12/2012 +25/12/2012 +26/12/2012 +27/12/2012 +28/12/2012 +29/12/2012 +30/12/2012 +31/12/2012 +01/01/2013 +02/01/2013 +03/01/2013 +04/01/2013 +05/01/2013 +06/01/2013 +07/01/2013 +08/01/2013 +09/01/2013 +10/01/2013 +11/01/2013 +12/01/2013 +13/01/2013 +14/01/2013 +15/01/2013 +16/01/2013 +17/01/2013 +18/01/2013 +19/01/2013 +20/01/2013 +21/01/2013 +22/01/2013 +23/01/2013 +24/01/2013 +25/01/2013 +26/01/2013 +27/01/2013 +28/01/2013 +29/01/2013 +30/01/2013 +31/01/2013 +01/02/2013 +02/02/2013 +03/02/2013 +04/02/2013 +05/02/2013 +06/02/2013 +07/02/2013 +08/02/2013 +09/02/2013 +10/02/2013 +11/02/2013 +12/02/2013 +13/02/2013 +14/02/2013 +15/02/2013 +16/02/2013 +17/02/2013 +18/02/2013 +19/02/2013 +20/02/2013 +21/02/2013 +22/02/2013 +23/02/2013 +24/02/2013 +25/02/2013 +26/02/2013 +27/02/2013 +28/02/2013 +01/03/2013 +02/03/2013 +03/03/2013 +04/03/2013 +05/03/2013 +06/03/2013 +07/03/2013 +08/03/2013 +09/03/2013 +10/03/2013 +11/03/2013 +12/03/2013 +13/03/2013 +14/03/2013 +15/03/2013 +16/03/2013 +17/03/2013 +18/03/2013 +19/03/2013 +20/03/2013 +21/03/2013 +22/03/2013 +23/03/2013 +24/03/2013 +25/03/2013 +26/03/2013 +27/03/2013 +28/03/2013 +29/03/2013 +30/03/2013 +31/03/2013 +01/04/2013 +02/04/2013 +03/04/2013 +04/04/2013 +05/04/2013 +06/04/2013 +07/04/2013 +08/04/2013 +09/04/2013 +10/04/2013 +11/04/2013 +12/04/2013 +13/04/2013 +14/04/2013 +15/04/2013 +16/04/2013 +17/04/2013 +18/04/2013 +19/04/2013 +20/04/2013 +21/04/2013 +22/04/2013 +23/04/2013 +24/04/2013 +25/04/2013 +26/04/2013 +27/04/2013 +28/04/2013 +29/04/2013 +30/04/2013 +01/05/2013 +02/05/2013 +03/05/2013 +04/05/2013 +05/05/2013 +06/05/2013 +07/05/2013 +08/05/2013 +09/05/2013 +10/05/2013 +11/05/2013 +12/05/2013 +13/05/2013 +14/05/2013 +15/05/2013 +16/05/2013 +17/05/2013 +18/05/2013 +19/05/2013 +20/05/2013 +21/05/2013 +22/05/2013 +23/05/2013 +24/05/2013 +25/05/2013 +26/05/2013 +27/05/2013 +28/05/2013 +29/05/2013 +30/05/2013 +31/05/2013 +01/06/2013 +02/06/2013 +03/06/2013 +04/06/2013 +05/06/2013 +06/06/2013 +07/06/2013 +08/06/2013 +09/06/2013 +10/06/2013 +11/06/2013 +12/06/2013 +13/06/2013 +14/06/2013 +15/06/2013 +16/06/2013 +17/06/2013 +18/06/2013 +19/06/2013 +20/06/2013 +21/06/2013 +22/06/2013 +23/06/2013 +24/06/2013 +25/06/2013 +26/06/2013 +27/06/2013 +28/06/2013 +29/06/2013 +30/06/2013 +01/07/2013 +02/07/2013 +03/07/2013 +04/07/2013 +05/07/2013 +06/07/2013 +07/07/2013 +08/07/2013 +09/07/2013 +10/07/2013 +11/07/2013 +12/07/2013 +13/07/2013 +14/07/2013 +15/07/2013 +16/07/2013 +17/07/2013 +18/07/2013 +19/07/2013 +20/07/2013 +21/07/2013 +22/07/2013 +23/07/2013 +24/07/2013 +25/07/2013 +26/07/2013 +27/07/2013 +28/07/2013 +29/07/2013 +30/07/2013 +31/07/2013 +01/08/2013 +02/08/2013 +03/08/2013 +04/08/2013 +05/08/2013 +06/08/2013 +07/08/2013 +08/08/2013 +09/08/2013 +10/08/2013 +11/08/2013 +12/08/2013 +13/08/2013 +14/08/2013 +15/08/2013 +16/08/2013 +17/08/2013 +18/08/2013 +19/08/2013 +20/08/2013 +21/08/2013 +22/08/2013 +23/08/2013 +24/08/2013 +25/08/2013 +26/08/2013 +27/08/2013 +28/08/2013 +29/08/2013 +30/08/2013 +31/08/2013 +01/09/2013 +02/09/2013 +03/09/2013 +04/09/2013 +05/09/2013 +06/09/2013 +07/09/2013 +08/09/2013 +09/09/2013 +10/09/2013 +11/09/2013 +12/09/2013 +13/09/2013 +14/09/2013 +15/09/2013 +16/09/2013 +17/09/2013 +18/09/2013 +19/09/2013 +20/09/2013 +21/09/2013 +22/09/2013 +23/09/2013 +24/09/2013 +25/09/2013 +26/09/2013 +27/09/2013 +28/09/2013 +29/09/2013 +30/09/2013 +01/10/2013 +02/10/2013 +03/10/2013 +04/10/2013 +05/10/2013 +06/10/2013 +07/10/2013 +08/10/2013 +09/10/2013 +10/10/2013 +11/10/2013 +12/10/2013 +13/10/2013 +14/10/2013 +15/10/2013 +16/10/2013 +17/10/2013 +18/10/2013 +19/10/2013 +20/10/2013 +21/10/2013 +22/10/2013 +23/10/2013 +24/10/2013 +25/10/2013 +26/10/2013 +27/10/2013 +28/10/2013 +29/10/2013 +30/10/2013 +31/10/2013 +01/11/2013 +02/11/2013 +03/11/2013 +04/11/2013 +05/11/2013 +06/11/2013 +07/11/2013 +08/11/2013 +09/11/2013 +10/11/2013 +11/11/2013 +12/11/2013 +13/11/2013 +14/11/2013 +15/11/2013 +16/11/2013 +17/11/2013 +18/11/2013 +19/11/2013 +20/11/2013 +21/11/2013 +22/11/2013 +23/11/2013 +24/11/2013 +25/11/2013 +26/11/2013 +27/11/2013 +28/11/2013 +29/11/2013 +30/11/2013 +01/12/2013 +02/12/2013 +03/12/2013 +04/12/2013 +05/12/2013 +06/12/2013 +07/12/2013 +08/12/2013 +09/12/2013 +10/12/2013 +11/12/2013 +12/12/2013 +13/12/2013 +14/12/2013 +15/12/2013 +16/12/2013 +17/12/2013 +18/12/2013 +19/12/2013 +20/12/2013 +21/12/2013 +22/12/2013 +23/12/2013 +24/12/2013 +25/12/2013 +26/12/2013 +27/12/2013 +28/12/2013 +29/12/2013 +30/12/2013 +31/12/2013 +01/01/2014 +02/01/2014 +03/01/2014 +04/01/2014 +05/01/2014 +06/01/2014 +07/01/2014 +08/01/2014 +09/01/2014 +10/01/2014 +11/01/2014 +12/01/2014 +13/01/2014 +14/01/2014 +15/01/2014 +16/01/2014 +17/01/2014 +18/01/2014 +19/01/2014 +20/01/2014 +21/01/2014 +22/01/2014 +23/01/2014 +24/01/2014 +25/01/2014 +26/01/2014 +27/01/2014 +28/01/2014 +29/01/2014 +30/01/2014 +31/01/2014 +01/02/2014 +02/02/2014 +03/02/2014 +04/02/2014 +05/02/2014 +06/02/2014 +07/02/2014 +08/02/2014 +09/02/2014 +10/02/2014 +11/02/2014 +12/02/2014 +13/02/2014 +14/02/2014 +15/02/2014 +16/02/2014 +17/02/2014 +18/02/2014 +19/02/2014 +20/02/2014 +21/02/2014 +22/02/2014 +23/02/2014 +24/02/2014 +25/02/2014 +26/02/2014 +27/02/2014 +28/02/2014 +01/03/2014 +02/03/2014 +03/03/2014 +04/03/2014 +05/03/2014 +06/03/2014 +07/03/2014 +08/03/2014 +09/03/2014 +10/03/2014 +11/03/2014 +12/03/2014 +13/03/2014 +14/03/2014 +15/03/2014 +16/03/2014 +17/03/2014 +18/03/2014 +19/03/2014 +20/03/2014 +21/03/2014 +22/03/2014 +23/03/2014 +24/03/2014 +25/03/2014 +26/03/2014 +27/03/2014 +28/03/2014 +29/03/2014 +30/03/2014 +31/03/2014 +01/04/2014 +02/04/2014 +03/04/2014 +04/04/2014 +05/04/2014 +06/04/2014 +07/04/2014 +08/04/2014 +09/04/2014 +10/04/2014 +11/04/2014 +12/04/2014 +13/04/2014 +14/04/2014 +15/04/2014 +16/04/2014 +17/04/2014 +18/04/2014 +19/04/2014 +20/04/2014 +21/04/2014 +22/04/2014 +23/04/2014 +24/04/2014 +25/04/2014 +26/04/2014 +27/04/2014 +28/04/2014 +29/04/2014 +30/04/2014 +01/05/2014 +02/05/2014 +03/05/2014 +04/05/2014 +05/05/2014 +06/05/2014 +07/05/2014 +08/05/2014 +09/05/2014 +10/05/2014 +11/05/2014 +12/05/2014 +13/05/2014 +14/05/2014 +15/05/2014 +16/05/2014 +17/05/2014 +18/05/2014 +19/05/2014 +20/05/2014 +21/05/2014 +22/05/2014 +23/05/2014 +24/05/2014 +25/05/2014 +26/05/2014 +27/05/2014 +28/05/2014 +29/05/2014 +30/05/2014 +31/05/2014 +01/06/2014 +02/06/2014 +03/06/2014 +04/06/2014 +05/06/2014 +06/06/2014 +07/06/2014 +08/06/2014 +09/06/2014 +10/06/2014 +11/06/2014 +12/06/2014 +13/06/2014 +14/06/2014 +15/06/2014 +16/06/2014 +17/06/2014 +18/06/2014 +19/06/2014 +20/06/2014 +21/06/2014 +22/06/2014 +23/06/2014 +24/06/2014 +25/06/2014 +26/06/2014 +27/06/2014 +28/06/2014 +29/06/2014 +30/06/2014 +01/07/2014 +02/07/2014 +03/07/2014 +04/07/2014 +05/07/2014 +06/07/2014 +07/07/2014 +08/07/2014 +09/07/2014 +10/07/2014 +11/07/2014 +12/07/2014 +13/07/2014 +14/07/2014 +15/07/2014 +16/07/2014 +17/07/2014 +18/07/2014 +19/07/2014 +20/07/2014 +21/07/2014 +22/07/2014 +23/07/2014 +24/07/2014 +25/07/2014 +26/07/2014 +27/07/2014 +28/07/2014 +29/07/2014 +30/07/2014 +31/07/2014 +01/08/2014 +02/08/2014 +03/08/2014 +04/08/2014 +05/08/2014 +06/08/2014 +07/08/2014 +08/08/2014 +09/08/2014 +10/08/2014 +11/08/2014 +12/08/2014 +13/08/2014 +14/08/2014 +15/08/2014 +16/08/2014 +17/08/2014 +18/08/2014 +19/08/2014 +20/08/2014 +21/08/2014 +22/08/2014 +23/08/2014 +24/08/2014 +25/08/2014 +26/08/2014 +27/08/2014 +28/08/2014 +29/08/2014 +30/08/2014 +31/08/2014 +01/09/2014 +02/09/2014 +03/09/2014 +04/09/2014 +05/09/2014 +06/09/2014 +07/09/2014 +08/09/2014 +09/09/2014 +10/09/2014 +11/09/2014 +12/09/2014 +13/09/2014 +14/09/2014 +15/09/2014 +16/09/2014 +17/09/2014 +18/09/2014 +19/09/2014 +20/09/2014 +21/09/2014 +22/09/2014 +23/09/2014 +24/09/2014 +25/09/2014 +26/09/2014 +27/09/2014 +28/09/2014 +29/09/2014 +30/09/2014 +01/10/2014 +02/10/2014 +03/10/2014 +04/10/2014 +05/10/2014 +06/10/2014 +07/10/2014 +08/10/2014 +09/10/2014 +10/10/2014 +11/10/2014 +12/10/2014 +13/10/2014 +14/10/2014 +15/10/2014 +16/10/2014 +17/10/2014 +18/10/2014 +19/10/2014 +20/10/2014 +21/10/2014 +22/10/2014 +23/10/2014 +24/10/2014 +25/10/2014 +26/10/2014 +27/10/2014 +28/10/2014 +29/10/2014 +30/10/2014 +31/10/2014 +01/11/2014 +02/11/2014 +03/11/2014 +04/11/2014 +05/11/2014 +06/11/2014 +07/11/2014 +08/11/2014 +09/11/2014 +10/11/2014 +11/11/2014 +12/11/2014 +13/11/2014 +14/11/2014 +15/11/2014 +16/11/2014 +17/11/2014 +18/11/2014 +19/11/2014 +20/11/2014 +21/11/2014 +22/11/2014 +23/11/2014 +24/11/2014 +25/11/2014 +26/11/2014 +27/11/2014 +28/11/2014 +29/11/2014 +30/11/2014 +01/12/2014 +02/12/2014 +03/12/2014 +04/12/2014 +05/12/2014 +06/12/2014 +07/12/2014 +08/12/2014 +09/12/2014 +10/12/2014 +11/12/2014 +12/12/2014 +13/12/2014 +14/12/2014 +15/12/2014 +16/12/2014 +17/12/2014 +18/12/2014 +19/12/2014 +20/12/2014 +21/12/2014 +22/12/2014 +23/12/2014 +24/12/2014 +25/12/2014 +26/12/2014 +27/12/2014 +28/12/2014 +29/12/2014 +30/12/2014 +31/12/2014 +01/01/2015 +02/01/2015 +03/01/2015 +04/01/2015 +05/01/2015 +06/01/2015 +07/01/2015 +08/01/2015 +09/01/2015 +10/01/2015 +11/01/2015 +12/01/2015 +13/01/2015 +14/01/2015 +15/01/2015 +16/01/2015 +17/01/2015 +18/01/2015 +19/01/2015 +20/01/2015 +21/01/2015 +22/01/2015 +23/01/2015 +24/01/2015 +25/01/2015 +26/01/2015 +27/01/2015 +28/01/2015 +29/01/2015 +30/01/2015 +31/01/2015 +01/02/2015 +02/02/2015 +03/02/2015 +04/02/2015 +05/02/2015 +06/02/2015 +07/02/2015 +08/02/2015 +09/02/2015 +10/02/2015 +11/02/2015 +12/02/2015 +13/02/2015 +14/02/2015 +15/02/2015 +16/02/2015 +17/02/2015 +18/02/2015 +19/02/2015 +20/02/2015 +21/02/2015 +22/02/2015 +23/02/2015 +24/02/2015 +25/02/2015 +26/02/2015 +27/02/2015 +28/02/2015 +01/03/2015 +02/03/2015 +03/03/2015 +04/03/2015 +05/03/2015 +06/03/2015 +07/03/2015 +08/03/2015 +09/03/2015 +10/03/2015 +11/03/2015 +12/03/2015 +13/03/2015 +14/03/2015 +15/03/2015 +16/03/2015 +17/03/2015 +18/03/2015 +19/03/2015 +20/03/2015 +21/03/2015 +22/03/2015 +23/03/2015 +24/03/2015 +25/03/2015 +26/03/2015 +27/03/2015 +28/03/2015 +29/03/2015 +30/03/2015 +31/03/2015 +01/04/2015 +02/04/2015 +03/04/2015 +04/04/2015 +05/04/2015 +06/04/2015 +07/04/2015 +08/04/2015 +09/04/2015 +10/04/2015 +11/04/2015 +12/04/2015 +13/04/2015 +14/04/2015 +15/04/2015 +16/04/2015 +17/04/2015 +18/04/2015 +19/04/2015 +20/04/2015 +21/04/2015 +22/04/2015 +23/04/2015 +24/04/2015 +25/04/2015 +26/04/2015 +27/04/2015 +28/04/2015 +29/04/2015 +30/04/2015 +01/05/2015 +02/05/2015 +03/05/2015 +04/05/2015 +05/05/2015 +06/05/2015 +07/05/2015 +08/05/2015 +09/05/2015 +10/05/2015 +11/05/2015 +12/05/2015 +13/05/2015 +14/05/2015 +15/05/2015 +16/05/2015 +17/05/2015 +18/05/2015 +19/05/2015 +20/05/2015 +21/05/2015 +22/05/2015 +23/05/2015 +24/05/2015 +25/05/2015 +26/05/2015 +27/05/2015 +28/05/2015 +29/05/2015 +30/05/2015 +31/05/2015 +01/06/2015 +02/06/2015 +03/06/2015 +04/06/2015 +05/06/2015 +06/06/2015 +07/06/2015 +08/06/2015 +09/06/2015 +10/06/2015 +11/06/2015 +12/06/2015 +13/06/2015 +14/06/2015 +15/06/2015 +16/06/2015 +17/06/2015 +18/06/2015 +19/06/2015 +20/06/2015 +21/06/2015 +22/06/2015 +23/06/2015 diff --git a/libres/test-data/local/snake_oil_no_data/snake_oil.ert b/libres/test-data/local/snake_oil_no_data/snake_oil.ert new file mode 100644 index 00000000000..aa31e43aada --- /dev/null +++ b/libres/test-data/local/snake_oil_no_data/snake_oil.ert @@ -0,0 +1,43 @@ +QUEUE_SYSTEM LOCAL +ECLBASE SNAKE_OIL_FIELD + +NUM_REALIZATIONS 25 + +DEFINE storage/ +RANDOM_SEED 3593114179000630026631423308983283277868 + +RUNPATH_FILE directory/test_runpath_list.txt +RUNPATH /runpath/realisation-%d/iter-%d +ENSPATH /ensemble +SUMMARY * + +SETENV FIRST TheFirstValue +SETENV SECOND TheSecondValue + +UPDATE_PATH THIRD TheThirdValue +UPDATE_PATH FOURTH TheFourthValue + +HISTORY_SOURCE REFCASE_HISTORY +REFCASE refcase/SNAKE_OIL_FIELD + +TIME_MAP refcase/time_map.txt +OBS_CONFIG observations/observations.txt + +INSTALL_JOB SNAKE_OIL_SIMULATOR jobs/SNAKE_OIL_SIMULATOR +INSTALL_JOB SNAKE_OIL_NPV jobs/SNAKE_OIL_NPV +INSTALL_JOB SNAKE_OIL_DIFF jobs/SNAKE_OIL_DIFF + +FORWARD_MODEL SNAKE_OIL_SIMULATOR +FORWARD_MODEL SNAKE_OIL_NPV +FORWARD_MODEL SNAKE_OIL_DIFF + +RUN_TEMPLATE templates/seed_template.txt seed.txt +GEN_KW SNAKE_OIL_PARAM templates/snake_oil_template.txt snake_oil_params.txt parameters/snake_oil_parameters.txt + +GEN_DATA SNAKE_OIL_OPR_DIFF INPUT_FORMAT:ASCII RESULT_FILE:snake_oil_opr_diff_%d.txt REPORT_STEPS:199 +GEN_DATA SNAKE_OIL_WPR_DIFF INPUT_FORMAT:ASCII RESULT_FILE:snake_oil_wpr_diff_%d.txt REPORT_STEPS:199 +GEN_DATA SNAKE_OIL_GPR_DIFF INPUT_FORMAT:ASCII RESULT_FILE:snake_oil_gpr_diff_%d.txt REPORT_STEPS:199 + +LOG_LEVEL INFO +LOG_FILE log/log.txt +UPDATE_LOG_PATH log/update diff --git a/libres/test-data/local/snake_oil_no_data/snake_oil_GEN_DATA.ert b/libres/test-data/local/snake_oil_no_data/snake_oil_GEN_DATA.ert new file mode 100644 index 00000000000..01dd0304b3a --- /dev/null +++ b/libres/test-data/local/snake_oil_no_data/snake_oil_GEN_DATA.ert @@ -0,0 +1,35 @@ +QUEUE_SYSTEM LOCAL + +NUM_REALIZATIONS 25 + +DEFINE storage/snake_oil +RANDOM_SEED 3593114179000630026631423308983283277868 + +RUNPATH_FILE directory/test_runpath_list.txt +RUNPATH /runpath/realisation-%d/iter-%d +ENSPATH /ensemble_GEN_DATA +ECLBASE SNAKE_OIL_FIELD + +HISTORY_SOURCE REFCASE_HISTORY +REFCASE refcase/SNAKE_OIL_FIELD + +INSTALL_JOB SNAKE_OIL_SIMULATOR jobs/SNAKE_OIL_SIMULATOR +INSTALL_JOB SNAKE_OIL_NPV jobs/SNAKE_OIL_NPV +INSTALL_JOB SNAKE_OIL_DIFF jobs/SNAKE_OIL_DIFF + +FORWARD_MODEL SNAKE_OIL_SIMULATOR +FORWARD_MODEL SNAKE_OIL_NPV +FORWARD_MODEL SNAKE_OIL_DIFF + +RUN_TEMPLATE templates/seed_template.txt seed.txt +GEN_KW SNAKE_OIL_PARAM templates/snake_oil_template.txt snake_oil_params.txt parameters/snake_oil_parameters.txt + +GEN_DATA SNAKE_OIL_OPR_DIFF INPUT_FORMAT:ASCII RESULT_FILE:snake_oil_opr_diff_%d.txt REPORT_STEPS:199 +GEN_DATA SNAKE_OIL_WPR_DIFF INPUT_FORMAT:ASCII RESULT_FILE:snake_oil_wpr_diff_%d.txt REPORT_STEPS:199 +GEN_DATA SNAKE_OIL_GPR_DIFF INPUT_FORMAT:ASCII RESULT_FILE:snake_oil_gpr_diff_%d.txt REPORT_STEPS:199 + +LOG_LEVEL INFO +LOG_FILE log/log.txt +UPDATE_LOG_PATH log/update + +SUMMARY FOPT -- SUMMARY keyword needed to force time map load from summary file \ No newline at end of file diff --git a/libres/test-data/local/snake_oil_no_data/snake_oil_GEO_ID.ert b/libres/test-data/local/snake_oil_no_data/snake_oil_GEO_ID.ert new file mode 100644 index 00000000000..dbd292194de --- /dev/null +++ b/libres/test-data/local/snake_oil_no_data/snake_oil_GEO_ID.ert @@ -0,0 +1,37 @@ +QUEUE_SYSTEM LOCAL + +NUM_REALIZATIONS 25 + +DEFINE storage/ + +RANDOM_SEED 3593114179000630026631423308983283277868 + +RUNPATH simulations//realisation-%d/iter-%d/magic-real-/magic-iter- +ENSPATH /ensemble +ECLBASE SNAKE_OIL_FIELD +SUMMARY * + +HISTORY_SOURCE REFCASE_HISTORY +REFCASE refcase/SNAKE_OIL_FIELD + +TIME_MAP refcase/time_map.txt +OBS_CONFIG observations/observations.txt + +INSTALL_JOB SNAKE_OIL_SIMULATOR jobs/SNAKE_OIL_SIMULATOR +INSTALL_JOB SNAKE_OIL_NPV jobs/SNAKE_OIL_NPV +INSTALL_JOB SNAKE_OIL_DIFF jobs/SNAKE_OIL_DIFF + +FORWARD_MODEL SNAKE_OIL_SIMULATOR +FORWARD_MODEL SNAKE_OIL_NPV +FORWARD_MODEL SNAKE_OIL_DIFF + +RUN_TEMPLATE templates/seed_template.txt seed.txt +GEN_KW SNAKE_OIL_PARAM templates/snake_oil_template.txt snake_oil_params.txt parameters/snake_oil_parameters.txt + +GEN_DATA SNAKE_OIL_OPR_DIFF INPUT_FORMAT:ASCII RESULT_FILE:snake_oil_opr_diff_%d.txt REPORT_STEPS:199 +GEN_DATA SNAKE_OIL_WPR_DIFF INPUT_FORMAT:ASCII RESULT_FILE:snake_oil_wpr_diff_%d.txt REPORT_STEPS:199 +GEN_DATA SNAKE_OIL_GPR_DIFF INPUT_FORMAT:ASCII RESULT_FILE:snake_oil_gpr_diff_%d.txt REPORT_STEPS:199 + +LOG_LEVEL INFO +LOG_FILE geo_id/log.txt +UPDATE_LOG_PATH log/update diff --git a/libres/test-data/local/snake_oil_no_data/snake_oil_no_gen_kw.ert b/libres/test-data/local/snake_oil_no_data/snake_oil_no_gen_kw.ert new file mode 100644 index 00000000000..f97e9d4dc15 --- /dev/null +++ b/libres/test-data/local/snake_oil_no_data/snake_oil_no_gen_kw.ert @@ -0,0 +1,37 @@ +QUEUE_SYSTEM LOCAL + +NUM_REALIZATIONS 25 + +DEFINE storage/ + +RANDOM_SEED 3593114179000630026631423308983283277868 + +RUNPATH_FILE directory/test_runpath_list.txt +RUNPATH /runpath/realisation-%d/iter-%d +ENSPATH /ensemble +ECLBASE SNAKE_OIL_FIELD +SUMMARY * + +HISTORY_SOURCE REFCASE_HISTORY +REFCASE refcase/SNAKE_OIL_FIELD + +TIME_MAP refcase/time_map.txt +OBS_CONFIG observations/observations.txt + +INSTALL_JOB SNAKE_OIL_SIMULATOR jobs/SNAKE_OIL_SIMULATOR +INSTALL_JOB SNAKE_OIL_NPV jobs/SNAKE_OIL_NPV +INSTALL_JOB SNAKE_OIL_DIFF jobs/SNAKE_OIL_DIFF + +FORWARD_MODEL SNAKE_OIL_SIMULATOR +FORWARD_MODEL SNAKE_OIL_NPV +FORWARD_MODEL SNAKE_OIL_DIFF + +RUN_TEMPLATE templates/seed_template.txt seed.txt + +GEN_DATA SNAKE_OIL_OPR_DIFF INPUT_FORMAT:ASCII RESULT_FILE:snake_oil_opr_diff_%d.txt REPORT_STEPS:199 +GEN_DATA SNAKE_OIL_WPR_DIFF INPUT_FORMAT:ASCII RESULT_FILE:snake_oil_wpr_diff_%d.txt REPORT_STEPS:199 +GEN_DATA SNAKE_OIL_GPR_DIFF INPUT_FORMAT:ASCII RESULT_FILE:snake_oil_gpr_diff_%d.txt REPORT_STEPS:199 + +LOG_LEVEL INFO +LOG_FILE log/log.txt +UPDATE_LOG_PATH log/update diff --git a/libres/test-data/local/snake_oil_no_data/storage/snake_oil/runpath/realisation-0/iter-0/SNAKE_OIL_FIELD.SMSPEC b/libres/test-data/local/snake_oil_no_data/storage/snake_oil/runpath/realisation-0/iter-0/SNAKE_OIL_FIELD.SMSPEC new file mode 100644 index 00000000000..37e0136085f Binary files /dev/null and b/libres/test-data/local/snake_oil_no_data/storage/snake_oil/runpath/realisation-0/iter-0/SNAKE_OIL_FIELD.SMSPEC differ diff --git a/libres/test-data/local/snake_oil_no_data/storage/snake_oil/runpath/realisation-0/iter-0/SNAKE_OIL_FIELD.UNSMRY b/libres/test-data/local/snake_oil_no_data/storage/snake_oil/runpath/realisation-0/iter-0/SNAKE_OIL_FIELD.UNSMRY new file mode 100644 index 00000000000..57282b13f61 Binary files /dev/null and b/libres/test-data/local/snake_oil_no_data/storage/snake_oil/runpath/realisation-0/iter-0/SNAKE_OIL_FIELD.UNSMRY differ diff --git a/libres/test-data/local/snake_oil_no_data/storage/snake_oil/runpath/realisation-0/iter-0/snake_oil_gpr_diff_199.txt b/libres/test-data/local/snake_oil_no_data/storage/snake_oil/runpath/realisation-0/iter-0/snake_oil_gpr_diff_199.txt new file mode 100644 index 00000000000..f98ec983cc1 --- /dev/null +++ b/libres/test-data/local/snake_oil_no_data/storage/snake_oil/runpath/realisation-0/iter-0/snake_oil_gpr_diff_199.txt @@ -0,0 +1,2000 @@ +-0.008206 +-0.008206 +-0.008205 +-0.008205 +-0.008205 +-0.008204 +-0.008203 +-0.008202 +-0.008201 +-0.008200 +-0.008199 +-0.008197 +-0.008196 +-0.008194 +-0.008192 +-0.008190 +-0.008188 +-0.008186 +-0.008184 +-0.008182 +-0.008179 +-0.008176 +-0.008174 +-0.008171 +-0.008168 +-0.008165 +-0.008162 +-0.008159 +-0.008156 +-0.008153 +-0.008149 +-0.008146 +-0.008142 +-0.008139 +-0.008135 +-0.008132 +-0.008128 +-0.008125 +-0.008121 +-0.008118 +-0.008114 +-0.008110 +-0.008107 +-0.008103 +-0.008099 +-0.008096 +-0.008092 +-0.008089 +-0.008086 +-0.008082 +-0.008079 +-0.008076 +-0.008072 +-0.008069 +-0.008066 +-0.008064 +-0.008061 +-0.008058 +-0.008056 +-0.008053 +-0.008051 +-0.008049 +-0.008047 +-0.008045 +-0.008043 +-0.008042 +-0.008041 +-0.008040 +-0.008039 +-0.008038 +-0.008038 +-0.008037 +-0.008037 +-0.008038 +-0.008038 +-0.008039 +-0.008040 +-0.008041 +-0.008042 +-0.008044 +-0.008046 +-0.008048 +-0.008051 +-0.008054 +-0.008057 +-0.008061 +-0.008065 +-0.008069 +-0.008073 +-0.008078 +-0.008083 +-0.008089 +-0.008095 +-0.008101 +-0.008108 +-0.008115 +-0.008122 +-0.008130 +-0.008138 +-0.008147 +-0.008156 +-0.008165 +-0.008174 +-0.008185 +-0.008195 +-0.008206 +-0.008217 +-0.008229 +-0.008241 +-0.008253 +-0.008266 +-0.008280 +-0.008294 +-0.008308 +-0.008322 +-0.008337 +-0.008353 +-0.008368 +-0.008385 +-0.008401 +-0.008418 +-0.008436 +-0.008454 +-0.008472 +-0.008491 +-0.008510 +-0.008529 +-0.008549 +-0.008569 +-0.008590 +-0.008611 +-0.008633 +-0.008654 +-0.008677 +-0.008699 +-0.008722 +-0.008745 +-0.008769 +-0.008793 +-0.008817 +-0.008842 +-0.008867 +-0.008892 +-0.008918 +-0.008944 +-0.008970 +-0.008996 +-0.009023 +-0.009050 +-0.009078 +-0.009105 +-0.009133 +-0.009161 +-0.009189 +-0.009217 +-0.009246 +-0.009275 +-0.009304 +-0.009333 +-0.009362 +-0.009392 +-0.009421 +-0.009451 +-0.009481 +-0.009511 +-0.009540 +-0.009570 +-0.009601 +-0.009631 +-0.009661 +-0.009691 +-0.009721 +-0.009751 +-0.009781 +-0.009811 +-0.009841 +-0.009870 +-0.009900 +-0.009930 +-0.009959 +-0.009988 +-0.010018 +-0.010046 +-0.010075 +-0.010104 +-0.010132 +-0.010160 +-0.010188 +-0.010215 +-0.010242 +-0.010269 +-0.010296 +-0.010322 +-0.010347 +-0.010373 +-0.010398 +-0.010422 +-0.010446 +-0.010470 +-0.010493 +-0.010515 +-0.010538 +-0.010561 +-0.010584 +-0.010607 +-0.010631 +-0.010656 +-0.010680 +-0.010705 +-0.010731 +-0.010756 +-0.010783 +-0.010809 +-0.010836 +-0.010864 +-0.010891 +-0.010920 +-0.010948 +-0.010977 +-0.011007 +-0.011037 +-0.011067 +-0.011098 +-0.011130 +-0.011162 +-0.011194 +-0.011227 +-0.011260 +-0.011294 +-0.011328 +-0.011363 +-0.011399 +-0.011434 +-0.011471 +-0.011508 +-0.011545 +-0.011583 +-0.011622 +-0.011661 +-0.011701 +-0.011741 +-0.011782 +-0.011823 +-0.011865 +-0.011907 +-0.011950 +-0.011994 +-0.012038 +-0.012083 +-0.012129 +-0.012174 +-0.012221 +-0.012268 +-0.012316 +-0.012364 +-0.012413 +-0.012463 +-0.012513 +-0.012564 +-0.012615 +-0.012667 +-0.012720 +-0.012773 +-0.012827 +-0.012882 +-0.012937 +-0.012992 +-0.013049 +-0.013106 +-0.013163 +-0.013221 +-0.013280 +-0.013339 +-0.013399 +-0.013460 +-0.013521 +-0.013583 +-0.013645 +-0.013708 +-0.013772 +-0.013836 +-0.013901 +-0.013967 +-0.014033 +-0.014099 +-0.014166 +-0.014234 +-0.014302 +-0.014371 +-0.014441 +-0.014511 +-0.014581 +-0.014652 +-0.014724 +-0.014796 +-0.014869 +-0.014942 +-0.015016 +-0.015090 +-0.015165 +-0.015241 +-0.015316 +-0.015393 +-0.015469 +-0.015547 +-0.015624 +-0.015703 +-0.015781 +-0.015860 +-0.015940 +-0.016020 +-0.016100 +-0.016181 +-0.016262 +-0.016344 +-0.016426 +-0.016508 +-0.016590 +-0.016673 +-0.016757 +-0.016840 +-0.016924 +-0.017009 +-0.017093 +-0.017178 +-0.017263 +-0.017349 +-0.017434 +-0.017520 +-0.017606 +-0.017693 +-0.017779 +-0.017866 +-0.017953 +-0.018040 +-0.018127 +-0.018214 +-0.018302 +-0.018389 +-0.018477 +-0.018565 +-0.018652 +-0.018740 +-0.018828 +-0.018916 +-0.019004 +-0.019092 +-0.019180 +-0.019268 +-0.019356 +-0.019444 +-0.019531 +-0.019619 +-0.019706 +-0.019794 +-0.019881 +-0.019968 +-0.020055 +-0.020142 +-0.020229 +-0.020315 +-0.020401 +-0.020487 +-0.020573 +-0.020658 +-0.020744 +-0.020829 +-0.020913 +-0.020997 +-0.021081 +-0.021165 +-0.021248 +-0.021331 +-0.021413 +-0.021495 +-0.021577 +-0.021658 +-0.021738 +-0.021818 +-0.021898 +-0.021977 +-0.022056 +-0.022134 +-0.022212 +-0.022289 +-0.022365 +-0.022441 +-0.022516 +-0.022591 +-0.022665 +-0.022738 +-0.022811 +-0.022883 +-0.022954 +-0.023025 +-0.023095 +-0.023164 +-0.023233 +-0.023300 +-0.023367 +-0.023434 +-0.023499 +-0.023564 +-0.023628 +-0.023691 +-0.023753 +-0.023815 +-0.023876 +-0.023936 +-0.023995 +-0.024053 +-0.024111 +-0.024168 +-0.024223 +-0.024278 +-0.024332 +-0.024386 +-0.024438 +-0.024489 +-0.024540 +-0.024589 +-0.024638 +-0.024686 +-0.024732 +-0.024778 +-0.024823 +-0.024867 +-0.024910 +-0.024952 +-0.024993 +-0.025033 +-0.025072 +-0.025111 +-0.025148 +-0.025184 +-0.025219 +-0.025253 +-0.025287 +-0.025319 +-0.025350 +-0.025380 +-0.025410 +-0.025438 +-0.025465 +-0.025491 +-0.025517 +-0.025541 +-0.025564 +-0.025586 +-0.025607 +-0.025628 +-0.025647 +-0.025665 +-0.025683 +-0.025699 +-0.025714 +-0.025729 +-0.025742 +-0.025755 +-0.025766 +-0.025776 +-0.025786 +-0.025795 +-0.025802 +-0.025809 +-0.025815 +-0.025820 +-0.025824 +-0.025827 +-0.025829 +-0.025831 +-0.025831 +-0.025831 +-0.025829 +-0.025827 +-0.025824 +-0.025820 +-0.025816 +-0.025810 +-0.025804 +-0.025797 +-0.025789 +-0.025781 +-0.025772 +-0.025762 +-0.025751 +-0.025740 +-0.025728 +-0.025715 +-0.025701 +-0.025687 +-0.025673 +-0.025657 +-0.025641 +-0.025625 +-0.025608 +-0.025590 +-0.025572 +-0.025553 +-0.025534 +-0.025514 +-0.025494 +-0.025474 +-0.025453 +-0.025431 +-0.025409 +-0.025387 +-0.025365 +-0.025342 +-0.025318 +-0.025295 +-0.025271 +-0.025247 +-0.025223 +-0.025198 +-0.025174 +-0.025149 +-0.025124 +-0.025099 +-0.025073 +-0.025048 +-0.025023 +-0.024997 +-0.024971 +-0.024946 +-0.024920 +-0.024895 +-0.024870 +-0.024844 +-0.024819 +-0.024794 +-0.024769 +-0.024744 +-0.024720 +-0.024696 +-0.024671 +-0.024648 +-0.024624 +-0.024601 +-0.024578 +-0.024555 +-0.024533 +-0.024511 +-0.024490 +-0.024469 +-0.024449 +-0.024429 +-0.024410 +-0.024391 +-0.024372 +-0.024355 +-0.024338 +-0.024321 +-0.024305 +-0.024290 +-0.024276 +-0.024262 +-0.024249 +-0.024237 +-0.024225 +-0.024215 +-0.024205 +-0.024196 +-0.024188 +-0.024180 +-0.024174 +-0.024169 +-0.024164 +-0.024161 +-0.024158 +-0.024157 +-0.024156 +-0.024157 +-0.024159 +-0.024162 +-0.024166 +-0.024171 +-0.024177 +-0.024184 +-0.024193 +-0.024202 +-0.024213 +-0.024225 +-0.024239 +-0.024253 +-0.024269 +-0.024287 +-0.024305 +-0.024325 +-0.024346 +-0.024369 +-0.024393 +-0.024418 +-0.024445 +-0.024473 +-0.024503 +-0.024534 +-0.024566 +-0.024600 +-0.024635 +-0.024672 +-0.024709 +-0.024743 +-0.024776 +-0.024807 +-0.024836 +-0.024862 +-0.024887 +-0.024910 +-0.024931 +-0.024949 +-0.024966 +-0.024980 +-0.024993 +-0.025003 +-0.025012 +-0.025018 +-0.025022 +-0.025025 +-0.025025 +-0.025023 +-0.025019 +-0.025012 +-0.025004 +-0.024994 +-0.024981 +-0.024967 +-0.024950 +-0.024931 +-0.024910 +-0.024887 +-0.024862 +-0.024835 +-0.024806 +-0.024774 +-0.024741 +-0.024705 +-0.024668 +-0.024628 +-0.024587 +-0.024543 +-0.024497 +-0.024450 +-0.024400 +-0.024348 +-0.024294 +-0.024239 +-0.024181 +-0.024122 +-0.024060 +-0.023997 +-0.023932 +-0.023864 +-0.023795 +-0.023724 +-0.023652 +-0.023577 +-0.023501 +-0.023422 +-0.023342 +-0.023261 +-0.023177 +-0.023092 +-0.023006 +-0.022917 +-0.022827 +-0.022735 +-0.022642 +-0.022547 +-0.022451 +-0.022353 +-0.022254 +-0.022153 +-0.022051 +-0.021947 +-0.021842 +-0.021736 +-0.021628 +-0.021520 +-0.021409 +-0.021298 +-0.021185 +-0.021071 +-0.020957 +-0.020841 +-0.020724 +-0.020605 +-0.020486 +-0.020366 +-0.020245 +-0.020123 +-0.020000 +-0.019877 +-0.019752 +-0.019627 +-0.019501 +-0.019374 +-0.019247 +-0.019119 +-0.018991 +-0.018862 +-0.018732 +-0.018602 +-0.018472 +-0.018341 +-0.018210 +-0.018078 +-0.017946 +-0.017814 +-0.017682 +-0.017550 +-0.017417 +-0.017285 +-0.017152 +-0.017019 +-0.016887 +-0.016754 +-0.016622 +-0.016490 +-0.016358 +-0.016226 +-0.016094 +-0.015963 +-0.015832 +-0.015702 +-0.015572 +-0.015442 +-0.015313 +-0.015185 +-0.015057 +-0.014929 +-0.014803 +-0.014677 +-0.014552 +-0.014428 +-0.014304 +-0.014182 +-0.014060 +-0.013939 +-0.013820 +-0.013701 +-0.013583 +-0.013467 +-0.013352 +-0.013237 +-0.013124 +-0.013013 +-0.012902 +-0.012793 +-0.012686 +-0.012579 +-0.012474 +-0.012371 +-0.012269 +-0.012169 +-0.012070 +-0.011973 +-0.011878 +-0.011784 +-0.011692 +-0.011601 +-0.011513 +-0.011426 +-0.011341 +-0.011258 +-0.011177 +-0.011098 +-0.011021 +-0.010945 +-0.010872 +-0.010801 +-0.010732 +-0.010665 +-0.010600 +-0.010537 +-0.010477 +-0.010418 +-0.010362 +-0.010308 +-0.010257 +-0.010207 +-0.010160 +-0.010116 +-0.010073 +-0.010033 +-0.009996 +-0.009961 +-0.009928 +-0.009898 +-0.009870 +-0.009845 +-0.009822 +-0.009802 +-0.009784 +-0.009769 +-0.009756 +-0.009746 +-0.009738 +-0.009733 +-0.009731 +-0.009731 +-0.009733 +-0.009734 +-0.009735 +-0.009736 +-0.009737 +-0.009737 +-0.009738 +-0.009738 +-0.009738 +-0.009738 +-0.009737 +-0.009737 +-0.009736 +-0.009735 +-0.009734 +-0.009732 +-0.009731 +-0.009729 +-0.009727 +-0.009725 +-0.009723 +-0.009721 +-0.009718 +-0.009715 +-0.009712 +-0.009709 +-0.009706 +-0.009702 +-0.009699 +-0.009695 +-0.009691 +-0.009686 +-0.009682 +-0.009677 +-0.009673 +-0.009668 +-0.009663 +-0.009657 +-0.009652 +-0.009646 +-0.009640 +-0.009634 +-0.009628 +-0.009622 +-0.009616 +-0.009609 +-0.009602 +-0.009595 +-0.009588 +-0.009581 +-0.009574 +-0.009566 +-0.009559 +-0.009551 +-0.009543 +-0.009535 +-0.009527 +-0.009518 +-0.009510 +-0.009501 +-0.009492 +-0.009484 +-0.009475 +-0.009466 +-0.009456 +-0.009447 +-0.009438 +-0.009428 +-0.009418 +-0.009408 +-0.009399 +-0.009389 +-0.009378 +-0.009368 +-0.009358 +-0.009348 +-0.009337 +-0.009327 +-0.009316 +-0.009305 +-0.009294 +-0.009284 +-0.009273 +-0.009262 +-0.009251 +-0.009239 +-0.009228 +-0.009217 +-0.009206 +-0.009194 +-0.009183 +-0.009171 +-0.009160 +-0.009148 +-0.009137 +-0.009125 +-0.009114 +-0.009102 +-0.009090 +-0.009079 +-0.009067 +-0.009055 +-0.009044 +-0.009032 +-0.009020 +-0.009008 +-0.008997 +-0.008985 +-0.008973 +-0.008961 +-0.008950 +-0.008938 +-0.008926 +-0.008915 +-0.008903 +-0.008892 +-0.008880 +-0.008869 +-0.008857 +-0.008846 +-0.008834 +-0.008823 +-0.008812 +-0.008801 +-0.008789 +-0.008778 +-0.008767 +-0.008756 +-0.008745 +-0.008735 +-0.008724 +-0.008713 +-0.008703 +-0.008692 +-0.008682 +-0.008672 +-0.008661 +-0.008651 +-0.008641 +-0.008631 +-0.008622 +-0.008612 +-0.008602 +-0.008593 +-0.008583 +-0.008574 +-0.008565 +-0.008556 +-0.008547 +-0.008539 +-0.008530 +-0.008522 +-0.008513 +-0.008505 +-0.008497 +-0.008489 +-0.008481 +-0.008474 +-0.008466 +-0.008459 +-0.008452 +-0.008445 +-0.008438 +-0.008431 +-0.008425 +-0.008418 +-0.008412 +-0.008406 +-0.008400 +-0.008395 +-0.008389 +-0.008384 +-0.008379 +-0.008374 +-0.008369 +-0.008364 +-0.008360 +-0.008355 +-0.008351 +-0.008347 +-0.008344 +-0.008340 +-0.008337 +-0.008334 +-0.008331 +-0.008328 +-0.008325 +-0.008323 +-0.008321 +-0.008318 +-0.008317 +-0.008315 +-0.008314 +-0.008312 +-0.008311 +-0.008310 +-0.008310 +-0.008309 +-0.008309 +-0.008309 +-0.008310 +-0.008314 +-0.008321 +-0.008330 +-0.008343 +-0.008358 +-0.008375 +-0.008395 +-0.008419 +-0.008444 +-0.008473 +-0.008504 +-0.008538 +-0.008575 +-0.008614 +-0.008656 +-0.008701 +-0.008749 +-0.008799 +-0.008852 +-0.008908 +-0.008967 +-0.009029 +-0.009093 +-0.009160 +-0.009230 +-0.009303 +-0.009378 +-0.009457 +-0.009538 +-0.009622 +-0.009709 +-0.009799 +-0.009891 +-0.009987 +-0.010085 +-0.010186 +-0.010290 +-0.010397 +-0.010507 +-0.010620 +-0.010736 +-0.010854 +-0.010976 +-0.011100 +-0.011228 +-0.011358 +-0.011492 +-0.011628 +-0.011767 +-0.011910 +-0.012055 +-0.012203 +-0.012354 +-0.012509 +-0.012666 +-0.012826 +-0.012989 +-0.013156 +-0.013325 +-0.013497 +-0.013673 +-0.013851 +-0.014032 +-0.014216 +-0.014404 +-0.014594 +-0.014787 +-0.014983 +-0.015183 +-0.015385 +-0.015590 +-0.015798 +-0.016010 +-0.016224 +-0.016441 +-0.016661 +-0.016884 +-0.017110 +-0.017338 +-0.017570 +-0.017805 +-0.018042 +-0.018282 +-0.018525 +-0.018771 +-0.019020 +-0.019271 +-0.019525 +-0.019782 +-0.020041 +-0.020304 +-0.020568 +-0.020836 +-0.021106 +-0.021379 +-0.021654 +-0.021931 +-0.022211 +-0.022494 +-0.022779 +-0.023066 +-0.023356 +-0.023648 +-0.023942 +-0.024238 +-0.024536 +-0.024837 +-0.025140 +-0.025444 +-0.025751 +-0.026059 +-0.026370 +-0.026682 +-0.026996 +-0.027312 +-0.027629 +-0.027948 +-0.028269 +-0.028590 +-0.028914 +-0.029239 +-0.029565 +-0.029892 +-0.030220 +-0.030550 +-0.030881 +-0.031212 +-0.031545 +-0.031878 +-0.032212 +-0.032547 +-0.032882 +-0.033218 +-0.033554 +-0.033891 +-0.034228 +-0.034565 +-0.034903 +-0.035240 +-0.035578 +-0.035915 +-0.036252 +-0.036589 +-0.036926 +-0.037262 +-0.037597 +-0.037932 +-0.038266 +-0.038600 +-0.038933 +-0.039264 +-0.039595 +-0.039924 +-0.040252 +-0.040579 +-0.040904 +-0.041228 +-0.041551 +-0.041871 +-0.042190 +-0.042507 +-0.042821 +-0.043134 +-0.043445 +-0.043753 +-0.044059 +-0.044362 +-0.044663 +-0.044961 +-0.045257 +-0.045549 +-0.045839 +-0.046126 +-0.046409 +-0.046689 +-0.046966 +-0.047239 +-0.047509 +-0.047776 +-0.048038 +-0.048297 +-0.048552 +-0.048803 +-0.049050 +-0.049293 +-0.049531 +-0.049765 +-0.049995 +-0.050220 +-0.050441 +-0.050656 +-0.050868 +-0.051074 +-0.051275 +-0.051472 +-0.051663 +-0.051849 +-0.052030 +-0.052205 +-0.052379 +-0.052554 +-0.052731 +-0.052909 +-0.053089 +-0.053271 +-0.053454 +-0.053639 +-0.053826 +-0.054014 +-0.054204 +-0.054396 +-0.054589 +-0.054784 +-0.054981 +-0.055179 +-0.055379 +-0.055581 +-0.055784 +-0.055990 +-0.056196 +-0.056405 +-0.056615 +-0.056826 +-0.057040 +-0.057255 +-0.057472 +-0.057690 +-0.057910 +-0.058132 +-0.058355 +-0.058580 +-0.058806 +-0.059035 +-0.059264 +-0.059495 +-0.059728 +-0.059963 +-0.060199 +-0.060436 +-0.060675 +-0.060916 +-0.061158 +-0.061401 +-0.061646 +-0.061892 +-0.062140 +-0.062389 +-0.062640 +-0.062891 +-0.063145 +-0.063399 +-0.063655 +-0.063912 +-0.064170 +-0.064430 +-0.064690 +-0.064952 +-0.065215 +-0.065479 +-0.065745 +-0.066011 +-0.066278 +-0.066546 +-0.066816 +-0.067086 +-0.067357 +-0.067629 +-0.067901 +-0.068175 +-0.068449 +-0.068724 +-0.069000 +-0.069276 +-0.069553 +-0.069831 +-0.070109 +-0.070387 +-0.070666 +-0.070946 +-0.071226 +-0.071506 +-0.071786 +-0.072067 +-0.072348 +-0.072629 +-0.072910 +-0.073192 +-0.073473 +-0.073754 +-0.074036 +-0.074317 +-0.074598 +-0.074879 +-0.075159 +-0.075440 +-0.075720 +-0.075999 +-0.076278 +-0.076557 +-0.076835 +-0.077112 +-0.077389 +-0.077666 +-0.077941 +-0.078216 +-0.078490 +-0.078763 +-0.079035 +-0.079306 +-0.079576 +-0.079845 +-0.080112 +-0.080379 +-0.080644 +-0.080908 +-0.081171 +-0.081432 +-0.081692 +-0.081950 +-0.082207 +-0.082462 +-0.082716 +-0.082967 +-0.083217 +-0.083466 +-0.083712 +-0.083956 +-0.084199 +-0.084439 +-0.084678 +-0.084914 +-0.085148 +-0.085380 +-0.085609 +-0.085836 +-0.086061 +-0.086284 +-0.086504 +-0.086721 +-0.086936 +-0.087148 +-0.087358 +-0.087565 +-0.087769 +-0.087971 +-0.088169 +-0.088365 +-0.088558 +-0.088748 +-0.088934 +-0.089118 +-0.089299 +-0.089476 +-0.089651 +-0.089822 +-0.089990 +-0.090154 +-0.090315 +-0.090473 +-0.090628 +-0.090779 +-0.090926 +-0.091070 +-0.091211 +-0.091348 +-0.091481 +-0.091611 +-0.091737 +-0.091859 +-0.091978 +-0.092093 +-0.092204 +-0.092311 +-0.092414 +-0.092514 +-0.092610 +-0.092702 +-0.092789 +-0.092873 +-0.092953 +-0.093029 +-0.093101 +-0.093169 +-0.093233 +-0.093293 +-0.093349 +-0.093400 +-0.093448 +-0.093492 +-0.093531 +-0.093566 +-0.093597 +-0.093624 +-0.093647 +-0.093666 +-0.093681 +-0.093691 +-0.093697 +-0.093699 +-0.093691 +-0.093668 +-0.093629 +-0.093574 +-0.093503 +-0.093417 +-0.093316 +-0.093199 +-0.093066 +-0.092918 +-0.092754 +-0.092575 +-0.092381 +-0.092171 +-0.091946 +-0.091706 +-0.091451 +-0.091181 +-0.090895 +-0.090596 +-0.090281 +-0.089952 +-0.089608 +-0.089249 +-0.088876 +-0.088489 +-0.088088 +-0.087673 +-0.087244 +-0.086801 +-0.086345 +-0.085875 +-0.085391 +-0.084895 +-0.084385 +-0.083862 +-0.083327 +-0.082779 +-0.082218 +-0.081645 +-0.081060 +-0.080463 +-0.079854 +-0.079234 +-0.078602 +-0.077959 +-0.077305 +-0.076640 +-0.075964 +-0.075278 +-0.074581 +-0.073874 +-0.073158 +-0.072431 +-0.071696 +-0.070951 +-0.070196 +-0.069433 +-0.068662 +-0.067882 +-0.067093 +-0.066297 +-0.065493 +-0.064682 +-0.063863 +-0.063038 +-0.062205 +-0.061366 +-0.060520 +-0.059669 +-0.058811 +-0.057948 +-0.057080 +-0.056206 +-0.055328 +-0.054444 +-0.053557 +-0.052665 +-0.051769 +-0.050870 +-0.049967 +-0.049062 +-0.048153 +-0.047241 +-0.046327 +-0.045411 +-0.044493 +-0.043574 +-0.042653 +-0.041731 +-0.040808 +-0.039884 +-0.038960 +-0.038035 +-0.037111 +-0.036187 +-0.035264 +-0.034341 +-0.033420 +-0.032500 +-0.031581 +-0.030664 +-0.029749 +-0.028837 +-0.027927 +-0.027020 +-0.026116 +-0.025215 +-0.024317 +-0.023423 +-0.022533 +-0.021648 +-0.020766 +-0.019890 +-0.019018 +-0.018151 +-0.017289 +-0.016433 +-0.015583 +-0.014738 +-0.013900 +-0.013067 +-0.012242 +-0.011423 +-0.010611 +-0.009806 +-0.009009 +-0.008219 +-0.007436 +-0.006662 +-0.005895 +-0.005137 +-0.004388 +-0.003646 +-0.002914 +-0.002191 +-0.001476 +-0.000771 +-0.000076 +0.000611 +0.001287 +0.001953 +0.002610 +0.003256 +0.003892 +0.004518 +0.005133 +0.005737 +0.006330 +0.006913 +0.007484 +0.008045 +0.008594 +0.009131 +0.009657 +0.010172 +0.010675 +0.011166 +0.011645 +0.012112 +0.012567 +0.013010 +0.013441 +0.013860 +0.014266 +0.014660 +0.015041 +0.015410 +0.015767 +0.016110 +0.016442 +0.016760 +0.017066 +0.017359 +0.017639 +0.017906 +0.018160 +0.018402 +0.018631 +0.018847 +0.019050 +0.019240 +0.019417 +0.019581 +0.019732 +0.019871 +0.019997 +0.020109 +0.020209 +0.020296 +0.020371 +0.020432 +0.020481 +0.020518 +0.020541 +0.020552 +0.020551 +0.020537 +0.020510 +0.020471 +0.020430 +0.020398 +0.020374 +0.020358 +0.020351 +0.020351 +0.020360 +0.020377 +0.020402 +0.020435 +0.020476 +0.020525 +0.020582 +0.020647 +0.020719 +0.020799 +0.020886 +0.020981 +0.021084 +0.021194 +0.021311 +0.021436 +0.021567 +0.021706 +0.021852 +0.022004 +0.022164 +0.022330 +0.022503 +0.022682 +0.022868 +0.023061 +0.023260 +0.023465 +0.023676 +0.023893 +0.024116 +0.024345 +0.024580 +0.024821 +0.025067 +0.025319 +0.025576 +0.025838 +0.026105 +0.026378 +0.026656 +0.026938 +0.027225 +0.027517 +0.027814 +0.028115 +0.028421 +0.028730 +0.029044 +0.029362 +0.029684 +0.030010 +0.030339 +0.030672 +0.031009 +0.031349 +0.031693 +0.032039 +0.032389 +0.032742 +0.033097 +0.033455 +0.033817 +0.034180 +0.034546 +0.034915 +0.035285 +0.035658 +0.036033 +0.036409 +0.036788 +0.037168 +0.037550 +0.037933 +0.038318 +0.038704 +0.039091 +0.039479 +0.039868 +0.040258 +0.040649 +0.041040 +0.041432 +0.041824 +0.042217 +0.042610 +0.043003 +0.043396 +0.043789 +0.044182 +0.044575 +0.044967 +0.045359 +0.045750 +0.046141 +0.046531 +0.046920 +0.047308 +0.047695 +0.048081 +0.048466 +0.048849 +0.049231 +0.049612 +0.049991 +0.050369 +0.050744 +0.051118 +0.051490 +0.051860 +0.052228 +0.052594 +0.052958 +0.053319 +0.053678 +0.054034 +0.054388 +0.054739 +0.055088 +0.055433 +0.055776 +0.056116 +0.056453 +0.056787 +0.057118 +0.057445 +0.057770 +0.058091 +0.058408 +0.058722 +0.059033 +0.059340 +0.059643 +0.059942 +0.060238 +0.060530 +0.060818 +0.061102 +0.061382 +0.061658 +0.061930 +0.062198 +0.062461 +0.062721 +0.062976 +0.063226 +0.063472 +0.063714 +0.063951 +0.064184 +0.064412 +0.064635 +0.064853 +0.065067 +0.065277 +0.065481 +0.065680 +0.065875 +0.066065 +0.066249 +0.066429 +0.066604 +0.066774 +0.066938 +0.067098 +0.067252 +0.067401 +0.067545 +0.067684 +0.067818 +0.067946 +0.068069 +0.068186 +0.068299 +0.068406 +0.068507 +0.068604 +0.068694 +0.068780 +0.068860 +0.068934 +0.069003 +0.069067 +0.069125 +0.069177 +0.069224 +0.069266 +0.069302 +0.069332 +0.069357 +0.069377 +0.069391 +0.069399 +0.069402 +0.069397 +0.069383 +0.069359 +0.069325 +0.069282 +0.069229 +0.069167 +0.069095 +0.069014 +0.068923 +0.068823 +0.068713 +0.068595 +0.068466 +0.068329 +0.068183 +0.068027 +0.067863 +0.067689 +0.067507 +0.067316 +0.067116 +0.066907 +0.066690 +0.066465 +0.066231 +0.065989 +0.065738 +0.065480 +0.065214 +0.064940 +0.064658 +0.064368 +0.064072 +0.063767 +0.063456 +0.063137 +0.062812 +0.062479 +0.062140 +0.061794 +0.061442 +0.061084 +0.060719 +0.060349 +0.059972 +0.059590 +0.059202 +0.058809 +0.058411 +0.058008 +0.057599 +0.057186 +0.056768 +0.056346 +0.055920 +0.055489 +0.055054 +0.054616 +0.054174 +0.053728 +0.053279 +0.052827 +0.052372 +0.051915 +0.051454 +0.050991 +0.050526 +0.050058 +0.049588 +0.049117 +0.048644 +0.048169 +0.047693 +0.047216 +0.046738 +0.046259 +0.045779 +0.045298 +0.044817 +0.044336 +0.043855 +0.043374 +0.042893 +0.042412 +0.041932 +0.041452 +0.040973 +0.040495 +0.040018 +0.039542 +0.039068 +0.038595 +0.038123 +0.037653 +0.037185 +0.036719 +0.036255 +0.035793 +0.035334 +0.034877 +0.034422 +0.033970 +0.033521 +0.033074 +0.032631 +0.032191 +0.031753 +0.031319 +0.030889 +0.030461 +0.030038 +0.029618 +0.029201 +0.028788 +0.028379 +0.027974 +0.027573 +0.027176 +0.026784 +0.026395 +0.026010 +0.025630 +0.025255 +0.024883 +0.024516 +0.024154 +0.023796 +0.023443 +0.023094 +0.022750 +0.022411 +0.022077 +0.021747 +0.021422 +0.021102 +0.020787 +0.020477 +0.020172 +0.019872 +0.019576 +0.019286 +0.019001 +0.018720 +0.018445 +0.018175 +0.017910 +0.017650 +0.017395 +0.017145 +0.016900 +0.016661 +0.016426 +0.016197 +0.015972 +0.015753 +0.015539 +0.015330 +0.015126 +0.014927 +0.014733 +0.014544 +0.014360 +0.014182 +0.014008 +0.013840 +0.013676 +0.013518 +0.013364 +0.013216 +0.013072 +0.012934 +0.012800 +0.012672 +0.012548 +0.012430 +0.012316 +0.012207 +0.012103 +0.012004 +0.011910 +0.011821 +0.011737 +0.011657 +0.011583 +0.011513 +0.011448 +0.011388 +0.011333 +0.011283 +0.011237 +0.011196 +0.011160 +0.011129 +0.011103 +0.011081 +0.011065 +0.011053 +0.011045 diff --git a/libres/test-data/local/snake_oil_no_data/storage/snake_oil/runpath/realisation-0/iter-0/snake_oil_npv.txt b/libres/test-data/local/snake_oil_no_data/storage/snake_oil/runpath/realisation-0/iter-0/snake_oil_npv.txt new file mode 100644 index 00000000000..3af0f8f5cf1 --- /dev/null +++ b/libres/test-data/local/snake_oil_no_data/storage/snake_oil/runpath/realisation-0/iter-0/snake_oil_npv.txt @@ -0,0 +1,2 @@ +NPV 124867.850223 +RATING EXCELLENT diff --git a/libres/test-data/local/snake_oil_no_data/storage/snake_oil/runpath/realisation-0/iter-0/snake_oil_opr_diff_199.txt b/libres/test-data/local/snake_oil_no_data/storage/snake_oil/runpath/realisation-0/iter-0/snake_oil_opr_diff_199.txt new file mode 100644 index 00000000000..3e688fda1e1 --- /dev/null +++ b/libres/test-data/local/snake_oil_no_data/storage/snake_oil/runpath/realisation-0/iter-0/snake_oil_opr_diff_199.txt @@ -0,0 +1,2000 @@ +-0.008206 +-0.008205 +-0.008204 +-0.008202 +-0.008199 +-0.008194 +-0.008190 +-0.008184 +-0.008177 +-0.008170 +-0.008162 +-0.008152 +-0.008143 +-0.008132 +-0.008121 +-0.008109 +-0.008096 +-0.008083 +-0.008069 +-0.008054 +-0.008039 +-0.008024 +-0.008008 +-0.007991 +-0.007975 +-0.007957 +-0.007940 +-0.007922 +-0.007904 +-0.007885 +-0.007866 +-0.007847 +-0.007828 +-0.007809 +-0.007790 +-0.007770 +-0.007751 +-0.007732 +-0.007712 +-0.007693 +-0.007675 +-0.007657 +-0.007639 +-0.007621 +-0.007605 +-0.007589 +-0.007574 +-0.007559 +-0.007546 +-0.007534 +-0.007523 +-0.007512 +-0.007502 +-0.007492 +-0.007482 +-0.007472 +-0.007463 +-0.007453 +-0.007444 +-0.007435 +-0.007426 +-0.007418 +-0.007410 +-0.007403 +-0.007397 +-0.007391 +-0.007387 +-0.007384 +-0.007382 +-0.007382 +-0.007383 +-0.007387 +-0.007393 +-0.007401 +-0.007411 +-0.007424 +-0.007440 +-0.007456 +-0.007473 +-0.007492 +-0.007512 +-0.007532 +-0.007554 +-0.007577 +-0.007602 +-0.007627 +-0.007654 +-0.007682 +-0.007712 +-0.007742 +-0.007775 +-0.007808 +-0.007844 +-0.007881 +-0.007919 +-0.007959 +-0.008001 +-0.008045 +-0.008090 +-0.008137 +-0.008187 +-0.008239 +-0.008295 +-0.008357 +-0.008423 +-0.008493 +-0.008569 +-0.008649 +-0.008734 +-0.008823 +-0.008917 +-0.009015 +-0.009118 +-0.009225 +-0.009335 +-0.009450 +-0.009567 +-0.009688 +-0.009812 +-0.009938 +-0.010067 +-0.010198 +-0.010330 +-0.010464 +-0.010600 +-0.010737 +-0.010875 +-0.011016 +-0.011160 +-0.011306 +-0.011454 +-0.011605 +-0.011758 +-0.011913 +-0.012070 +-0.012229 +-0.012390 +-0.012553 +-0.012718 +-0.012884 +-0.013053 +-0.013223 +-0.013396 +-0.013570 +-0.013745 +-0.013922 +-0.014101 +-0.014282 +-0.014464 +-0.014647 +-0.014832 +-0.015020 +-0.015211 +-0.015407 +-0.015608 +-0.015812 +-0.016021 +-0.016234 +-0.016451 +-0.016673 +-0.016899 +-0.017130 +-0.017365 +-0.017605 +-0.017849 +-0.018098 +-0.018351 +-0.018608 +-0.018870 +-0.019137 +-0.019407 +-0.019681 +-0.019959 +-0.020241 +-0.020526 +-0.020814 +-0.021106 +-0.021401 +-0.021700 +-0.022003 +-0.022308 +-0.022615 +-0.022924 +-0.023233 +-0.023543 +-0.023852 +-0.024159 +-0.024464 +-0.024766 +-0.025064 +-0.025356 +-0.025643 +-0.025923 +-0.026195 +-0.026458 +-0.026712 +-0.026956 +-0.027188 +-0.027409 +-0.027618 +-0.027814 +-0.028007 +-0.028206 +-0.028412 +-0.028624 +-0.028843 +-0.029068 +-0.029300 +-0.029538 +-0.029781 +-0.030030 +-0.030284 +-0.030543 +-0.030807 +-0.031074 +-0.031345 +-0.031619 +-0.031895 +-0.032173 +-0.032453 +-0.032734 +-0.033015 +-0.033296 +-0.033576 +-0.033856 +-0.034134 +-0.034413 +-0.034693 +-0.034975 +-0.035258 +-0.035542 +-0.035826 +-0.036111 +-0.036395 +-0.036680 +-0.036963 +-0.037246 +-0.037527 +-0.037806 +-0.038083 +-0.038358 +-0.038630 +-0.038900 +-0.039165 +-0.039428 +-0.039686 +-0.039940 +-0.040191 +-0.040436 +-0.040677 +-0.040914 +-0.041149 +-0.041384 +-0.041621 +-0.041859 +-0.042098 +-0.042338 +-0.042581 +-0.042827 +-0.043074 +-0.043326 +-0.043580 +-0.043839 +-0.044102 +-0.044370 +-0.044644 +-0.044923 +-0.045209 +-0.045502 +-0.045802 +-0.046110 +-0.046425 +-0.046749 +-0.047082 +-0.047423 +-0.047773 +-0.048125 +-0.048474 +-0.048818 +-0.049158 +-0.049495 +-0.049828 +-0.050159 +-0.050487 +-0.050815 +-0.051142 +-0.051469 +-0.051799 +-0.052131 +-0.052468 +-0.052810 +-0.053158 +-0.053515 +-0.053880 +-0.054255 +-0.054641 +-0.055039 +-0.055450 +-0.055874 +-0.056313 +-0.056766 +-0.057223 +-0.057672 +-0.058115 +-0.058550 +-0.058977 +-0.059397 +-0.059811 +-0.060217 +-0.060617 +-0.061010 +-0.061398 +-0.061780 +-0.062157 +-0.062530 +-0.062899 +-0.063264 +-0.063626 +-0.063986 +-0.064345 +-0.064702 +-0.065058 +-0.065414 +-0.065770 +-0.066126 +-0.066484 +-0.066842 +-0.067202 +-0.067563 +-0.067925 +-0.068289 +-0.068655 +-0.069022 +-0.069391 +-0.069761 +-0.070133 +-0.070506 +-0.070880 +-0.071255 +-0.071630 +-0.072006 +-0.072381 +-0.072757 +-0.073132 +-0.073507 +-0.073880 +-0.074253 +-0.074624 +-0.074995 +-0.075363 +-0.075731 +-0.076096 +-0.076457 +-0.076813 +-0.077165 +-0.077513 +-0.077854 +-0.078190 +-0.078519 +-0.078840 +-0.079153 +-0.079456 +-0.079750 +-0.080031 +-0.080301 +-0.080558 +-0.080801 +-0.081029 +-0.081242 +-0.081439 +-0.081620 +-0.081785 +-0.081933 +-0.082065 +-0.082180 +-0.082280 +-0.082381 +-0.082500 +-0.082638 +-0.082796 +-0.082973 +-0.083169 +-0.083386 +-0.083621 +-0.083874 +-0.084144 +-0.084430 +-0.084730 +-0.085043 +-0.085368 +-0.085703 +-0.086045 +-0.086394 +-0.086747 +-0.087104 +-0.087463 +-0.087821 +-0.088180 +-0.088536 +-0.088891 +-0.089243 +-0.089584 +-0.089908 +-0.090214 +-0.090502 +-0.090771 +-0.091022 +-0.091254 +-0.091468 +-0.091663 +-0.091840 +-0.091998 +-0.092138 +-0.092259 +-0.092362 +-0.092447 +-0.092514 +-0.092564 +-0.092596 +-0.092612 +-0.092612 +-0.092596 +-0.092565 +-0.092519 +-0.092459 +-0.092387 +-0.092292 +-0.092165 +-0.092007 +-0.091820 +-0.091606 +-0.091367 +-0.091105 +-0.090823 +-0.090524 +-0.090212 +-0.089889 +-0.089559 +-0.089226 +-0.088894 +-0.088566 +-0.088245 +-0.087937 +-0.087642 +-0.087366 +-0.087111 +-0.086880 +-0.086674 +-0.086497 +-0.086349 +-0.086232 +-0.086112 +-0.085957 +-0.085765 +-0.085539 +-0.085279 +-0.084987 +-0.084665 +-0.084314 +-0.083938 +-0.083539 +-0.083121 +-0.082685 +-0.082236 +-0.081778 +-0.081312 +-0.080844 +-0.080376 +-0.079912 +-0.079454 +-0.079006 +-0.078571 +-0.078150 +-0.077747 +-0.077363 +-0.077000 +-0.076652 +-0.076312 +-0.075982 +-0.075661 +-0.075349 +-0.075049 +-0.074758 +-0.074478 +-0.074207 +-0.073947 +-0.073695 +-0.073453 +-0.073219 +-0.072993 +-0.072774 +-0.072561 +-0.072354 +-0.072153 +-0.071956 +-0.071763 +-0.071575 +-0.071389 +-0.071207 +-0.071028 +-0.070852 +-0.070674 +-0.070491 +-0.070303 +-0.070108 +-0.069907 +-0.069700 +-0.069486 +-0.069264 +-0.069034 +-0.068795 +-0.068547 +-0.068289 +-0.068020 +-0.067739 +-0.067446 +-0.067140 +-0.066820 +-0.066486 +-0.066138 +-0.065775 +-0.065396 +-0.065002 +-0.064592 +-0.064168 +-0.063728 +-0.063260 +-0.062751 +-0.062201 +-0.061613 +-0.060989 +-0.060331 +-0.059643 +-0.058928 +-0.058191 +-0.057436 +-0.056668 +-0.055891 +-0.055112 +-0.054335 +-0.053566 +-0.052809 +-0.052070 +-0.051353 +-0.050664 +-0.050006 +-0.049383 +-0.048798 +-0.048254 +-0.047754 +-0.047299 +-0.046863 +-0.046422 +-0.045976 +-0.045525 +-0.045070 +-0.044612 +-0.044151 +-0.043688 +-0.043225 +-0.042762 +-0.042300 +-0.041840 +-0.041384 +-0.040933 +-0.040487 +-0.040048 +-0.039617 +-0.039196 +-0.038784 +-0.038384 +-0.037996 +-0.037621 +-0.037261 +-0.036914 +-0.036583 +-0.036279 +-0.036011 +-0.035781 +-0.035587 +-0.035430 +-0.035307 +-0.035217 +-0.035159 +-0.035130 +-0.035127 +-0.035149 +-0.035191 +-0.035252 +-0.035327 +-0.035415 +-0.035511 +-0.035614 +-0.035719 +-0.035825 +-0.035929 +-0.036029 +-0.036123 +-0.036209 +-0.036288 +-0.036357 +-0.036429 +-0.036517 +-0.036621 +-0.036740 +-0.036873 +-0.037021 +-0.037183 +-0.037356 +-0.037540 +-0.037734 +-0.037937 +-0.038146 +-0.038360 +-0.038579 +-0.038800 +-0.039021 +-0.039242 +-0.039461 +-0.039676 +-0.039887 +-0.040091 +-0.040289 +-0.040479 +-0.040660 +-0.040832 +-0.041022 +-0.041255 +-0.041531 +-0.041847 +-0.042203 +-0.042595 +-0.043020 +-0.043473 +-0.043951 +-0.044448 +-0.044960 +-0.045482 +-0.046007 +-0.046530 +-0.047046 +-0.047549 +-0.048033 +-0.048494 +-0.048926 +-0.049325 +-0.049687 +-0.050008 +-0.050286 +-0.050519 +-0.050703 +-0.050887 +-0.051117 +-0.051391 +-0.051710 +-0.052070 +-0.052471 +-0.052910 +-0.053384 +-0.053890 +-0.054425 +-0.054985 +-0.055567 +-0.056165 +-0.056777 +-0.057397 +-0.058022 +-0.058647 +-0.059268 +-0.059880 +-0.060480 +-0.061063 +-0.061625 +-0.062163 +-0.062672 +-0.063151 +-0.063571 +-0.063904 +-0.064149 +-0.064305 +-0.064374 +-0.064356 +-0.064255 +-0.064073 +-0.063816 +-0.063489 +-0.063096 +-0.062645 +-0.062141 +-0.061592 +-0.061004 +-0.060384 +-0.059739 +-0.059075 +-0.058398 +-0.057714 +-0.057028 +-0.056343 +-0.055663 +-0.054991 +-0.054328 +-0.053668 +-0.053005 +-0.052341 +-0.051676 +-0.051014 +-0.050357 +-0.049709 +-0.049073 +-0.048454 +-0.047857 +-0.047286 +-0.046746 +-0.046243 +-0.045781 +-0.045366 +-0.045002 +-0.044693 +-0.044443 +-0.044256 +-0.044135 +-0.044082 +-0.044098 +-0.044184 +-0.044341 +-0.044566 +-0.044846 +-0.045164 +-0.045518 +-0.045904 +-0.046319 +-0.046758 +-0.047217 +-0.047693 +-0.048178 +-0.048670 +-0.049163 +-0.049650 +-0.050128 +-0.050591 +-0.051033 +-0.051451 +-0.051838 +-0.052191 +-0.052506 +-0.052778 +-0.053004 +-0.053182 +-0.053308 +-0.053382 +-0.053401 +-0.053408 +-0.053445 +-0.053511 +-0.053604 +-0.053723 +-0.053865 +-0.054026 +-0.054204 +-0.054393 +-0.054590 +-0.054791 +-0.054990 +-0.055184 +-0.055368 +-0.055536 +-0.055684 +-0.055809 +-0.055906 +-0.055971 +-0.056002 +-0.055996 +-0.055950 +-0.055864 +-0.055736 +-0.055566 +-0.055400 +-0.055286 +-0.055224 +-0.055212 +-0.055250 +-0.055333 +-0.055460 +-0.055627 +-0.055829 +-0.056061 +-0.056319 +-0.056596 +-0.056888 +-0.057188 +-0.057491 +-0.057791 +-0.058082 +-0.058358 +-0.058616 +-0.058851 +-0.059058 +-0.059234 +-0.059377 +-0.059485 +-0.059556 +-0.059607 +-0.059656 +-0.059702 +-0.059742 +-0.059775 +-0.059796 +-0.059802 +-0.059788 +-0.059750 +-0.059683 +-0.059580 +-0.059436 +-0.059245 +-0.059002 +-0.058701 +-0.058336 +-0.057902 +-0.057396 +-0.056813 +-0.056149 +-0.055403 +-0.054572 +-0.053656 +-0.052654 +-0.051568 +-0.050426 +-0.049257 +-0.048063 +-0.046848 +-0.045614 +-0.044365 +-0.043104 +-0.041833 +-0.040557 +-0.039278 +-0.038001 +-0.036727 +-0.035461 +-0.034206 +-0.032964 +-0.031740 +-0.030534 +-0.029352 +-0.028194 +-0.027064 +-0.025964 +-0.024896 +-0.023861 +-0.022861 +-0.021898 +-0.020914 +-0.019852 +-0.018714 +-0.017504 +-0.016228 +-0.014891 +-0.013500 +-0.012064 +-0.010592 +-0.009093 +-0.007577 +-0.006055 +-0.004538 +-0.003036 +-0.001560 +-0.000121 +0.001270 +0.002605 +0.003874 +0.005069 +0.006182 +0.007209 +0.008142 +0.008979 +0.009717 +0.010405 +0.011093 +0.011781 +0.012466 +0.013148 +0.013824 +0.014493 +0.015152 +0.015799 +0.016430 +0.017044 +0.017638 +0.018208 +0.018752 +0.019266 +0.019749 +0.020197 +0.020607 +0.020978 +0.021308 +0.021594 +0.021836 +0.022030 +0.022178 +0.022278 +0.022320 +0.022293 +0.022200 +0.022040 +0.021818 +0.021535 +0.021195 +0.020803 +0.020362 +0.019878 +0.019356 +0.018802 +0.018221 +0.017620 +0.017005 +0.016381 +0.015755 +0.015133 +0.014519 +0.013920 +0.013340 +0.012784 +0.012257 +0.011760 +0.011298 +0.010882 +0.010523 +0.010222 +0.009980 +0.009794 +0.009665 +0.009589 +0.009563 +0.009584 +0.009648 +0.009750 +0.009884 +0.010047 +0.010232 +0.010434 +0.010647 +0.010867 +0.011089 +0.011306 +0.011517 +0.011716 +0.011900 +0.012068 +0.012216 +0.012345 +0.012462 +0.012575 +0.012686 +0.012792 +0.012895 +0.012994 +0.013088 +0.013176 +0.013258 +0.013334 +0.013403 +0.013463 +0.013516 +0.013559 +0.013592 +0.013616 +0.013628 +0.013630 +0.013620 +0.013599 +0.013566 +0.013522 +0.013466 +0.013399 +0.013321 +0.013183 +0.012937 +0.012585 +0.012134 +0.011588 +0.010955 +0.010246 +0.009469 +0.008636 +0.007761 +0.006855 +0.005932 +0.005006 +0.004091 +0.003200 +0.002347 +0.001546 +0.000807 +0.000143 +-0.000438 +-0.000925 +-0.001312 +-0.001592 +-0.001762 +-0.001819 +-0.001828 +-0.001854 +-0.001896 +-0.001957 +-0.002034 +-0.002130 +-0.002243 +-0.002375 +-0.002525 +-0.002695 +-0.002884 +-0.003093 +-0.003323 +-0.003575 +-0.003848 +-0.004143 +-0.004462 +-0.004804 +-0.005171 +-0.005562 +-0.005980 +-0.006423 +-0.006894 +-0.007392 +-0.007918 +-0.008416 +-0.008832 +-0.009169 +-0.009431 +-0.009625 +-0.009760 +-0.009845 +-0.009891 +-0.009909 +-0.009912 +-0.009913 +-0.009927 +-0.009967 +-0.010047 +-0.010182 +-0.010385 +-0.010668 +-0.011043 +-0.011522 +-0.012114 +-0.012828 +-0.013670 +-0.014645 +-0.015756 +-0.017006 +-0.018323 +-0.019636 +-0.020944 +-0.022247 +-0.023544 +-0.024836 +-0.026123 +-0.027404 +-0.028681 +-0.029953 +-0.031222 +-0.032488 +-0.033751 +-0.035013 +-0.036273 +-0.037533 +-0.038794 +-0.040055 +-0.041317 +-0.042580 +-0.043845 +-0.045111 +-0.046378 +-0.047646 +-0.048914 +-0.050214 +-0.051577 +-0.053000 +-0.054479 +-0.056009 +-0.057584 +-0.059197 +-0.060840 +-0.062503 +-0.064178 +-0.065856 +-0.067525 +-0.069175 +-0.070797 +-0.072380 +-0.073915 +-0.075391 +-0.076801 +-0.078136 +-0.079390 +-0.080555 +-0.081626 +-0.082600 +-0.083473 +-0.084245 +-0.084968 +-0.085700 +-0.086439 +-0.087183 +-0.087932 +-0.088682 +-0.089432 +-0.090179 +-0.090920 +-0.091653 +-0.092372 +-0.093076 +-0.093761 +-0.094423 +-0.095058 +-0.095664 +-0.096237 +-0.096773 +-0.097271 +-0.097726 +-0.098137 +-0.098502 +-0.098818 +-0.099086 +-0.099302 +-0.099463 +-0.099564 +-0.099605 +-0.099587 +-0.099511 +-0.099380 +-0.099196 +-0.098962 +-0.098680 +-0.098354 +-0.097988 +-0.097585 +-0.097150 +-0.096687 +-0.096200 +-0.095692 +-0.095167 +-0.094631 +-0.094084 +-0.093532 +-0.092977 +-0.092422 +-0.091869 +-0.091319 +-0.090774 +-0.090207 +-0.089590 +-0.088925 +-0.088214 +-0.087460 +-0.086666 +-0.085834 +-0.084970 +-0.084076 +-0.083158 +-0.082219 +-0.081264 +-0.080298 +-0.079326 +-0.078352 +-0.077381 +-0.076417 +-0.075465 +-0.074529 +-0.073612 +-0.072719 +-0.071853 +-0.071016 +-0.070210 +-0.069439 +-0.068711 +-0.068033 +-0.067406 +-0.066830 +-0.066305 +-0.065829 +-0.065400 +-0.065016 +-0.064675 +-0.064373 +-0.064107 +-0.063873 +-0.063669 +-0.063490 +-0.063333 +-0.063193 +-0.063068 +-0.062954 +-0.062847 +-0.062746 +-0.062647 +-0.062549 +-0.062449 +-0.062348 +-0.062243 +-0.062145 +-0.062063 +-0.061996 +-0.061944 +-0.061903 +-0.061872 +-0.061850 +-0.061832 +-0.061816 +-0.061799 +-0.061777 +-0.061748 +-0.061706 +-0.061650 +-0.061575 +-0.061478 +-0.061357 +-0.061209 +-0.061030 +-0.060820 +-0.060576 +-0.060297 +-0.059982 +-0.059632 +-0.059246 +-0.058826 +-0.058374 +-0.057891 +-0.057380 +-0.056842 +-0.056282 +-0.055701 +-0.055105 +-0.054496 +-0.053879 +-0.053258 +-0.052638 +-0.052022 +-0.051414 +-0.050820 +-0.050244 +-0.049688 +-0.049157 +-0.048655 +-0.048183 +-0.047745 +-0.047343 +-0.046979 +-0.046653 +-0.046367 +-0.046081 +-0.045757 +-0.045395 +-0.044999 +-0.044569 +-0.044110 +-0.043625 +-0.043118 +-0.042594 +-0.042057 +-0.041512 +-0.040964 +-0.040418 +-0.039880 +-0.039354 +-0.038846 +-0.038360 +-0.037900 +-0.037471 +-0.037076 +-0.036719 +-0.036402 +-0.036127 +-0.035897 +-0.035711 +-0.035553 +-0.035403 +-0.035262 +-0.035130 +-0.035009 +-0.034897 +-0.034795 +-0.034704 +-0.034624 +-0.034555 +-0.034498 +-0.034452 +-0.034418 +-0.034396 +-0.034386 +-0.034388 +-0.034403 +-0.034431 +-0.034471 +-0.034524 +-0.034589 +-0.034668 +-0.034759 +-0.034863 +-0.034979 +-0.035113 +-0.035268 +-0.035443 +-0.035637 +-0.035849 +-0.036078 +-0.036321 +-0.036576 +-0.036840 +-0.037112 +-0.037389 +-0.037667 +-0.037945 +-0.038220 +-0.038488 +-0.038749 +-0.039000 +-0.039238 +-0.039464 +-0.039675 +-0.039871 +-0.040051 +-0.040216 +-0.040366 +-0.040503 +-0.040648 +-0.040825 +-0.041035 +-0.041278 +-0.041556 +-0.041868 +-0.042217 +-0.042600 +-0.043020 +-0.043475 +-0.043964 +-0.044489 +-0.045047 +-0.045638 +-0.046261 +-0.046916 +-0.047602 +-0.048316 +-0.049059 +-0.049829 +-0.050625 +-0.051447 +-0.052292 +-0.053161 +-0.054052 +-0.054946 +-0.055824 +-0.056685 +-0.057529 +-0.058356 +-0.059166 +-0.059959 +-0.060735 +-0.061494 +-0.062237 +-0.062965 +-0.063678 +-0.064376 +-0.065060 +-0.065731 +-0.066390 +-0.067037 +-0.067673 +-0.068297 +-0.068912 +-0.069516 +-0.070110 +-0.070695 +-0.071270 +-0.071835 +-0.072368 +-0.072848 +-0.073275 +-0.073650 +-0.073976 +-0.074253 +-0.074484 +-0.074673 +-0.074822 +-0.074935 +-0.075015 +-0.075067 +-0.075094 +-0.075101 +-0.075091 +-0.075068 +-0.075036 +-0.074998 +-0.074959 +-0.074920 +-0.074885 +-0.074855 +-0.074833 +-0.074818 +-0.074814 +-0.074816 +-0.074822 +-0.074832 +-0.074848 +-0.074868 +-0.074894 +-0.074927 +-0.074967 +-0.075015 +-0.075073 +-0.075140 +-0.075219 +-0.075309 +-0.075412 +-0.075529 +-0.075659 +-0.075804 +-0.075964 +-0.076138 +-0.076327 +-0.076531 +-0.076748 +-0.076977 +-0.077218 +-0.077469 +-0.077741 +-0.078042 +-0.078371 +-0.078723 +-0.079095 +-0.079483 +-0.079880 +-0.080283 +-0.080684 +-0.081079 +-0.081461 +-0.081823 +-0.082160 +-0.082465 +-0.082732 +-0.082955 +-0.083128 +-0.083246 +-0.083305 +-0.083300 +-0.083226 +-0.083082 +-0.082865 +-0.082573 +-0.082205 +-0.081807 +-0.081425 +-0.081060 +-0.080709 +-0.080373 +-0.080050 +-0.079739 +-0.079439 +-0.079149 +-0.078866 +-0.078589 +-0.078317 +-0.078048 +-0.077779 +-0.077509 +-0.077235 +-0.076957 +-0.076671 +-0.076377 +-0.076072 +-0.075755 +-0.075424 +-0.075077 +-0.074713 +-0.074331 +-0.073919 +-0.073467 +-0.072974 +-0.072441 +-0.071867 +-0.071255 +-0.070604 +-0.069916 +-0.069194 +-0.068440 +-0.067656 +-0.066844 +-0.066007 +-0.065148 +-0.064270 +-0.063376 +-0.062467 +-0.061548 +-0.060619 +-0.059683 +-0.058743 +-0.057800 +-0.056854 +-0.055908 +-0.054962 +-0.054029 +-0.053122 +-0.052240 +-0.051382 +-0.050547 +-0.049735 +-0.048944 +-0.048172 +-0.047417 +-0.046678 +-0.045952 +-0.045236 +-0.044529 +-0.043828 +-0.043131 +-0.042436 +-0.041740 +-0.041041 +-0.040338 +-0.039628 +-0.038910 +-0.038183 +-0.037445 +-0.036696 +-0.035935 +-0.035178 +-0.034442 +-0.033726 +-0.033029 +-0.032349 +-0.031684 +-0.031034 +-0.030396 +-0.029768 +-0.029148 +-0.028533 +-0.027922 +-0.027312 +-0.026702 +-0.026088 +-0.025468 +-0.024842 +-0.024207 +-0.023561 +-0.022904 +-0.022234 +-0.021551 +-0.020853 +-0.020141 +-0.019415 +-0.018667 +-0.017887 +-0.017079 +-0.016242 +-0.015380 +-0.014495 +-0.013588 +-0.012664 +-0.011725 +-0.010775 +-0.009816 +-0.008854 +-0.007891 +-0.006930 +-0.005977 +-0.005035 +-0.004106 +-0.003195 +-0.002305 +-0.001439 +-0.000600 +0.000210 +0.000988 +0.001732 +0.002441 +0.003133 +0.003826 +0.004519 +0.005208 +0.005892 +0.006567 +0.007230 +0.007878 +0.008507 +0.009114 +0.009697 +0.010250 +0.010771 +0.011256 +0.011703 +0.012108 +0.012468 +0.012781 +0.013045 +0.013258 +0.013418 +0.013525 +0.013577 +0.013574 +0.013516 +0.013447 +0.013410 +0.013405 +0.013432 +0.013489 +0.013577 +0.013693 +0.013839 +0.014013 +0.014213 +0.014440 +0.014692 +0.014968 +0.015267 +0.015588 +0.015929 +0.016290 +0.016668 +0.017063 +0.017473 +0.017897 +0.018332 +0.018777 +0.019231 +0.019692 +0.020144 +0.020574 +0.020980 +0.021363 +0.021721 +0.022055 +0.022366 +0.022654 +0.022921 +0.023167 +0.023394 +0.023604 +0.023798 +0.023978 +0.024147 +0.024304 +0.024453 +0.024595 +0.024731 +0.024863 +0.024991 +0.025118 +0.025242 +0.025366 +0.025489 +0.025603 +0.025703 +0.025789 +0.025861 +0.025919 +0.025966 +0.026002 +0.026028 +0.026046 +0.026058 +0.026064 +0.026067 +0.026068 +0.026070 +0.026072 +0.026078 +0.026088 +0.026103 +0.026126 +0.026157 +0.026198 +0.026248 +0.026309 +0.026381 +0.026464 +0.026551 +0.026636 +0.026717 +0.026796 +0.026873 +0.026948 +0.027021 +0.027094 +0.027166 +0.027238 +0.027311 +0.027384 +0.027459 +0.027536 +0.027615 +0.027697 +0.027781 +0.027869 +0.027961 +0.028057 +0.028157 +0.028261 +0.028369 +0.028482 +0.028599 +0.028716 +0.028826 +0.028931 +0.029029 +0.029121 +0.029206 +0.029285 +0.029357 +0.029422 +0.029480 +0.029530 +0.029572 +0.029606 +0.029631 +0.029648 +0.029657 +0.029657 +0.029648 +0.029630 +0.029604 +0.029570 +0.029527 +0.029477 +0.029419 +0.029355 +0.029294 +0.029248 +0.029215 +0.029195 +0.029189 +0.029196 +0.029215 +0.029245 +0.029285 +0.029334 +0.029391 +0.029454 +0.029522 +0.029594 +0.029668 +0.029743 +0.029817 +0.029890 +0.029960 +0.030027 +0.030089 +0.030146 +0.030197 +0.030242 +0.030281 +0.030318 +0.030361 +0.030408 +0.030458 +0.030513 +0.030571 +0.030631 +0.030694 +0.030758 +0.030822 +0.030887 +0.030951 +0.031013 +0.031073 +0.031130 +0.031183 +0.031231 +0.031275 +0.031312 +0.031343 +0.031368 +0.031385 +0.031396 +0.031398 +0.031394 +0.031386 +0.031380 +0.031376 +0.031373 +0.031372 +0.031373 +0.031375 +0.031378 +0.031382 +0.031388 +0.031394 +0.031401 +0.031409 +0.031416 +0.031424 +0.031432 +0.031440 +0.031447 +0.031454 +0.031460 +0.031465 +0.031469 +0.031472 +0.031473 +0.031474 +0.031467 +0.031445 +0.031409 +0.031360 +0.031296 +0.031218 +0.031127 +0.031023 +0.030907 +0.030777 +0.030636 +0.030484 +0.030321 +0.030147 +0.029963 +0.029770 +0.029568 +0.029357 +0.029139 +0.028914 +0.028682 +0.028444 +0.028200 +0.027951 +0.027697 +0.027436 +0.027165 +0.026886 +0.026598 +0.026303 +0.026001 +0.025692 +0.025379 +0.025061 +0.024739 +0.024415 +0.024088 +0.023761 +0.023433 +0.023105 +0.022779 +0.022455 +0.022133 +0.021814 +0.021499 +0.021188 +0.020882 +0.020580 +0.020283 +0.019992 +0.019702 +0.019411 +0.019118 +0.018824 +0.018528 +0.018230 +0.017930 +0.017627 +0.017321 +0.017012 +0.016699 +0.016381 +0.016059 +0.015732 +0.015400 +0.015062 +0.014718 +0.014369 +0.014013 +0.013651 +0.013284 +0.012911 +0.012532 +0.012149 +0.011761 +0.011376 +0.010999 +0.010632 +0.010273 +0.009923 +0.009581 +0.009249 +0.008924 +0.008607 +0.008298 +0.007996 +0.007700 +0.007411 +0.007127 +0.006848 +0.006574 +0.006304 +0.006039 +0.005776 +0.005517 +0.005261 +0.005007 +0.004756 +0.004508 +0.004262 +0.004017 +0.003771 +0.003524 +0.003278 +0.003031 +0.002785 +0.002539 +0.002295 +0.002052 +0.001812 +0.001573 +0.001338 +0.001105 +0.000876 +0.000651 +0.000430 +0.000213 +0.000001 +-0.000207 +-0.000409 +-0.000607 +-0.000799 +-0.000986 +-0.001169 +-0.001346 +-0.001520 +-0.001693 +-0.001865 +-0.002035 +-0.002203 +-0.002370 +-0.002534 +-0.002697 +-0.002857 +-0.003015 +-0.003170 +-0.003322 +-0.003471 +-0.003616 +-0.003759 +-0.003898 +-0.004033 +-0.004165 +-0.004293 +-0.004417 +-0.004537 +-0.004654 +-0.004766 +-0.004875 +-0.004981 +-0.005082 +-0.005178 +-0.005269 +-0.005356 +-0.005438 +-0.005516 +-0.005590 +-0.005660 +-0.005726 +-0.005789 +-0.005848 +-0.005904 +-0.005957 +-0.006007 +-0.006054 +-0.006098 +-0.006139 +-0.006179 +-0.006216 +-0.006250 +-0.006283 +-0.006314 +-0.006342 +-0.006370 +-0.006395 +-0.006419 +-0.006441 +-0.006462 +-0.006482 +-0.006500 +-0.006518 +-0.006534 +-0.006549 +-0.006563 +-0.006576 +-0.006588 +-0.006599 +-0.006609 +-0.006619 +-0.006627 +-0.006635 +-0.006642 +-0.006648 +-0.006653 +-0.006657 +-0.006661 +-0.006664 +-0.006666 +-0.006667 diff --git a/libres/test-data/local/snake_oil_no_data/storage/snake_oil/runpath/realisation-0/iter-0/snake_oil_wpr_diff_199.txt b/libres/test-data/local/snake_oil_no_data/storage/snake_oil/runpath/realisation-0/iter-0/snake_oil_wpr_diff_199.txt new file mode 100644 index 00000000000..44a829637e2 --- /dev/null +++ b/libres/test-data/local/snake_oil_no_data/storage/snake_oil/runpath/realisation-0/iter-0/snake_oil_wpr_diff_199.txt @@ -0,0 +1,2000 @@ +-0.008206 +-0.008206 +-0.008205 +-0.008205 +-0.008204 +-0.008203 +-0.008202 +-0.008201 +-0.008200 +-0.008199 +-0.008197 +-0.008195 +-0.008193 +-0.008191 +-0.008189 +-0.008186 +-0.008184 +-0.008181 +-0.008178 +-0.008175 +-0.008172 +-0.008169 +-0.008166 +-0.008163 +-0.008159 +-0.008156 +-0.008152 +-0.008149 +-0.008145 +-0.008142 +-0.008138 +-0.008134 +-0.008130 +-0.008126 +-0.008122 +-0.008119 +-0.008115 +-0.008111 +-0.008107 +-0.008103 +-0.008100 +-0.008096 +-0.008092 +-0.008089 +-0.008086 +-0.008082 +-0.008079 +-0.008076 +-0.008074 +-0.008071 +-0.008069 +-0.008067 +-0.008065 +-0.008063 +-0.008061 +-0.008059 +-0.008057 +-0.008055 +-0.008053 +-0.008052 +-0.008050 +-0.008048 +-0.008047 +-0.008045 +-0.008044 +-0.008043 +-0.008042 +-0.008041 +-0.008041 +-0.008041 +-0.008041 +-0.008042 +-0.008043 +-0.008045 +-0.008047 +-0.008049 +-0.008052 +-0.008056 +-0.008059 +-0.008063 +-0.008067 +-0.008071 +-0.008075 +-0.008080 +-0.008085 +-0.008090 +-0.008095 +-0.008101 +-0.008107 +-0.008113 +-0.008119 +-0.008126 +-0.008133 +-0.008141 +-0.008148 +-0.008156 +-0.008165 +-0.008174 +-0.008183 +-0.008192 +-0.008202 +-0.008212 +-0.008224 +-0.008236 +-0.008249 +-0.008263 +-0.008278 +-0.008294 +-0.008311 +-0.008329 +-0.008348 +-0.008368 +-0.008388 +-0.008410 +-0.008432 +-0.008454 +-0.008478 +-0.008502 +-0.008527 +-0.008552 +-0.008578 +-0.008604 +-0.008631 +-0.008657 +-0.008685 +-0.008712 +-0.008740 +-0.008768 +-0.008797 +-0.008826 +-0.008855 +-0.008886 +-0.008916 +-0.008947 +-0.008979 +-0.009010 +-0.009043 +-0.009075 +-0.009108 +-0.009141 +-0.009175 +-0.009209 +-0.009244 +-0.009278 +-0.009314 +-0.009349 +-0.009385 +-0.009421 +-0.009457 +-0.009494 +-0.009531 +-0.009569 +-0.009607 +-0.009646 +-0.009686 +-0.009727 +-0.009769 +-0.009811 +-0.009855 +-0.009899 +-0.009944 +-0.009991 +-0.010038 +-0.010086 +-0.010134 +-0.010184 +-0.010235 +-0.010286 +-0.010339 +-0.010392 +-0.010446 +-0.010501 +-0.010556 +-0.010613 +-0.010670 +-0.010727 +-0.010786 +-0.010845 +-0.010905 +-0.010965 +-0.011026 +-0.011088 +-0.011149 +-0.011211 +-0.011273 +-0.011335 +-0.011396 +-0.011457 +-0.011518 +-0.011577 +-0.011636 +-0.011693 +-0.011749 +-0.011803 +-0.011856 +-0.011907 +-0.011956 +-0.012002 +-0.012046 +-0.012088 +-0.012127 +-0.012166 +-0.012206 +-0.012247 +-0.012289 +-0.012333 +-0.012378 +-0.012425 +-0.012472 +-0.012521 +-0.012571 +-0.012621 +-0.012673 +-0.012726 +-0.012779 +-0.012834 +-0.012888 +-0.012944 +-0.012999 +-0.013055 +-0.013111 +-0.013168 +-0.013224 +-0.013280 +-0.013336 +-0.013391 +-0.013447 +-0.013503 +-0.013560 +-0.013616 +-0.013673 +-0.013730 +-0.013787 +-0.013844 +-0.013900 +-0.013957 +-0.014014 +-0.014070 +-0.014126 +-0.014181 +-0.014236 +-0.014291 +-0.014344 +-0.014398 +-0.014450 +-0.014502 +-0.014553 +-0.014603 +-0.014652 +-0.014700 +-0.014747 +-0.014794 +-0.014841 +-0.014889 +-0.014936 +-0.014984 +-0.015032 +-0.015081 +-0.015130 +-0.015179 +-0.015230 +-0.015281 +-0.015332 +-0.015385 +-0.015439 +-0.015493 +-0.015549 +-0.015606 +-0.015665 +-0.015725 +-0.015787 +-0.015850 +-0.015914 +-0.015981 +-0.016049 +-0.016119 +-0.016190 +-0.016259 +-0.016328 +-0.016396 +-0.016463 +-0.016530 +-0.016596 +-0.016662 +-0.016727 +-0.016793 +-0.016858 +-0.016924 +-0.016991 +-0.017058 +-0.017127 +-0.017196 +-0.017267 +-0.017341 +-0.017416 +-0.017493 +-0.017572 +-0.017655 +-0.017739 +-0.017827 +-0.017918 +-0.018009 +-0.018099 +-0.018187 +-0.018274 +-0.018360 +-0.018444 +-0.018527 +-0.018608 +-0.018688 +-0.018767 +-0.018844 +-0.018921 +-0.018996 +-0.019071 +-0.019144 +-0.019217 +-0.019290 +-0.019362 +-0.019434 +-0.019505 +-0.019576 +-0.019647 +-0.019719 +-0.019790 +-0.019861 +-0.019933 +-0.020005 +-0.020077 +-0.020150 +-0.020222 +-0.020295 +-0.020369 +-0.020443 +-0.020517 +-0.020591 +-0.020666 +-0.020741 +-0.020815 +-0.020891 +-0.020966 +-0.021041 +-0.021116 +-0.021191 +-0.021266 +-0.021341 +-0.021415 +-0.021489 +-0.021563 +-0.021637 +-0.021711 +-0.021784 +-0.021856 +-0.021927 +-0.021998 +-0.022067 +-0.022135 +-0.022203 +-0.022268 +-0.022333 +-0.022395 +-0.022456 +-0.022514 +-0.022571 +-0.022625 +-0.022676 +-0.022725 +-0.022770 +-0.022813 +-0.022852 +-0.022889 +-0.022922 +-0.022951 +-0.022977 +-0.023001 +-0.023021 +-0.023041 +-0.023065 +-0.023092 +-0.023124 +-0.023159 +-0.023198 +-0.023242 +-0.023289 +-0.023339 +-0.023393 +-0.023451 +-0.023511 +-0.023573 +-0.023638 +-0.023705 +-0.023774 +-0.023843 +-0.023914 +-0.023985 +-0.024057 +-0.024129 +-0.024200 +-0.024272 +-0.024343 +-0.024413 +-0.024481 +-0.024546 +-0.024607 +-0.024665 +-0.024719 +-0.024769 +-0.024815 +-0.024858 +-0.024897 +-0.024933 +-0.024964 +-0.024992 +-0.025016 +-0.025037 +-0.025054 +-0.025067 +-0.025077 +-0.025084 +-0.025087 +-0.025087 +-0.025084 +-0.025077 +-0.025068 +-0.025056 +-0.025042 +-0.025023 +-0.024997 +-0.024966 +-0.024929 +-0.024886 +-0.024838 +-0.024786 +-0.024729 +-0.024669 +-0.024607 +-0.024542 +-0.024476 +-0.024410 +-0.024343 +-0.024278 +-0.024214 +-0.024152 +-0.024093 +-0.024038 +-0.023987 +-0.023941 +-0.023899 +-0.023864 +-0.023834 +-0.023811 +-0.023787 +-0.023756 +-0.023718 +-0.023672 +-0.023620 +-0.023562 +-0.023498 +-0.023427 +-0.023352 +-0.023272 +-0.023189 +-0.023102 +-0.023012 +-0.022920 +-0.022827 +-0.022733 +-0.022640 +-0.022547 +-0.022455 +-0.022366 +-0.022279 +-0.022195 +-0.022114 +-0.022037 +-0.021965 +-0.021895 +-0.021827 +-0.021761 +-0.021697 +-0.021634 +-0.021574 +-0.021516 +-0.021460 +-0.021406 +-0.021354 +-0.021304 +-0.021255 +-0.021208 +-0.021163 +-0.021119 +-0.021077 +-0.021035 +-0.020995 +-0.020956 +-0.020917 +-0.020879 +-0.020842 +-0.020806 +-0.020770 +-0.020735 +-0.020699 +-0.020663 +-0.020625 +-0.020586 +-0.020546 +-0.020505 +-0.020462 +-0.020417 +-0.020371 +-0.020324 +-0.020274 +-0.020222 +-0.020169 +-0.020112 +-0.020054 +-0.019993 +-0.019929 +-0.019862 +-0.019792 +-0.019719 +-0.019644 +-0.019565 +-0.019483 +-0.019398 +-0.019310 +-0.019217 +-0.019115 +-0.019005 +-0.018887 +-0.018762 +-0.018631 +-0.018493 +-0.018350 +-0.018203 +-0.018052 +-0.017898 +-0.017743 +-0.017587 +-0.017432 +-0.017278 +-0.017126 +-0.016979 +-0.016835 +-0.016697 +-0.016566 +-0.016441 +-0.016324 +-0.016215 +-0.016115 +-0.016024 +-0.015937 +-0.015849 +-0.015760 +-0.015670 +-0.015579 +-0.015487 +-0.015395 +-0.015302 +-0.015210 +-0.015117 +-0.015025 +-0.014933 +-0.014841 +-0.014751 +-0.014662 +-0.014574 +-0.014488 +-0.014404 +-0.014321 +-0.014241 +-0.014164 +-0.014089 +-0.014017 +-0.013947 +-0.013881 +-0.013820 +-0.013767 +-0.013721 +-0.013682 +-0.013650 +-0.013626 +-0.013608 +-0.013596 +-0.013590 +-0.013590 +-0.013594 +-0.013603 +-0.013615 +-0.013630 +-0.013648 +-0.013667 +-0.013687 +-0.013708 +-0.013730 +-0.013750 +-0.013770 +-0.013789 +-0.013806 +-0.013822 +-0.013836 +-0.013850 +-0.013868 +-0.013889 +-0.013912 +-0.013939 +-0.013969 +-0.014001 +-0.014036 +-0.014073 +-0.014111 +-0.014152 +-0.014194 +-0.014237 +-0.014280 +-0.014325 +-0.014369 +-0.014413 +-0.014457 +-0.014500 +-0.014542 +-0.014583 +-0.014622 +-0.014660 +-0.014697 +-0.014731 +-0.014769 +-0.014816 +-0.014871 +-0.014934 +-0.015005 +-0.015084 +-0.015168 +-0.015259 +-0.015355 +-0.015454 +-0.015557 +-0.015661 +-0.015766 +-0.015871 +-0.015974 +-0.016074 +-0.016171 +-0.016263 +-0.016350 +-0.016429 +-0.016502 +-0.016566 +-0.016622 +-0.016668 +-0.016705 +-0.016742 +-0.016788 +-0.016843 +-0.016906 +-0.016979 +-0.017059 +-0.017147 +-0.017241 +-0.017343 +-0.017450 +-0.017562 +-0.017678 +-0.017798 +-0.017920 +-0.018044 +-0.018169 +-0.018294 +-0.018418 +-0.018541 +-0.018661 +-0.018777 +-0.018890 +-0.018997 +-0.019099 +-0.019195 +-0.019279 +-0.019345 +-0.019394 +-0.019426 +-0.019439 +-0.019436 +-0.019415 +-0.019379 +-0.019328 +-0.019262 +-0.019184 +-0.019094 +-0.018993 +-0.018883 +-0.018765 +-0.018641 +-0.018512 +-0.018380 +-0.018244 +-0.018107 +-0.017970 +-0.017833 +-0.017697 +-0.017563 +-0.017430 +-0.017298 +-0.017166 +-0.017033 +-0.016900 +-0.016767 +-0.016636 +-0.016506 +-0.016379 +-0.016255 +-0.016136 +-0.016022 +-0.015914 +-0.015813 +-0.015721 +-0.015638 +-0.015565 +-0.015503 +-0.015453 +-0.015416 +-0.015392 +-0.015381 +-0.015384 +-0.015401 +-0.015433 +-0.015478 +-0.015534 +-0.015597 +-0.015668 +-0.015745 +-0.015828 +-0.015916 +-0.016008 +-0.016103 +-0.016200 +-0.016299 +-0.016397 +-0.016495 +-0.016590 +-0.016683 +-0.016771 +-0.016855 +-0.016932 +-0.017003 +-0.017066 +-0.017120 +-0.017165 +-0.017201 +-0.017226 +-0.017241 +-0.017245 +-0.017246 +-0.017253 +-0.017267 +-0.017285 +-0.017309 +-0.017338 +-0.017370 +-0.017405 +-0.017443 +-0.017483 +-0.017523 +-0.017563 +-0.017601 +-0.017638 +-0.017672 +-0.017701 +-0.017726 +-0.017746 +-0.017759 +-0.017765 +-0.017764 +-0.017755 +-0.017737 +-0.017712 +-0.017678 +-0.017645 +-0.017622 +-0.017609 +-0.017607 +-0.017615 +-0.017631 +-0.017657 +-0.017690 +-0.017730 +-0.017777 +-0.017828 +-0.017884 +-0.017942 +-0.018002 +-0.018063 +-0.018123 +-0.018181 +-0.018236 +-0.018288 +-0.018335 +-0.018376 +-0.018411 +-0.018440 +-0.018462 +-0.018476 +-0.018486 +-0.018496 +-0.018505 +-0.018513 +-0.018519 +-0.018524 +-0.018525 +-0.018522 +-0.018515 +-0.018501 +-0.018481 +-0.018452 +-0.018414 +-0.018365 +-0.018305 +-0.018232 +-0.018145 +-0.018044 +-0.017927 +-0.017794 +-0.017645 +-0.017479 +-0.017296 +-0.017095 +-0.016878 +-0.016650 +-0.016416 +-0.016177 +-0.015934 +-0.015687 +-0.015438 +-0.015185 +-0.014931 +-0.014676 +-0.014420 +-0.014165 +-0.013910 +-0.013657 +-0.013406 +-0.013157 +-0.012912 +-0.012671 +-0.012435 +-0.012203 +-0.011977 +-0.011757 +-0.011544 +-0.011337 +-0.011137 +-0.010944 +-0.010747 +-0.010535 +-0.010307 +-0.010065 +-0.009810 +-0.009543 +-0.009265 +-0.008977 +-0.008683 +-0.008383 +-0.008080 +-0.007776 +-0.007472 +-0.007172 +-0.006877 +-0.006589 +-0.006311 +-0.006044 +-0.005790 +-0.005551 +-0.005328 +-0.005123 +-0.004936 +-0.004769 +-0.004621 +-0.004484 +-0.004346 +-0.004208 +-0.004071 +-0.003935 +-0.003800 +-0.003666 +-0.003534 +-0.003405 +-0.003278 +-0.003156 +-0.003037 +-0.002923 +-0.002814 +-0.002711 +-0.002615 +-0.002525 +-0.002443 +-0.002369 +-0.002303 +-0.002246 +-0.002197 +-0.002158 +-0.002129 +-0.002109 +-0.002101 +-0.002106 +-0.002125 +-0.002156 +-0.002201 +-0.002258 +-0.002325 +-0.002404 +-0.002492 +-0.002589 +-0.002693 +-0.002804 +-0.002920 +-0.003041 +-0.003164 +-0.003288 +-0.003414 +-0.003538 +-0.003661 +-0.003781 +-0.003896 +-0.004008 +-0.004113 +-0.004213 +-0.004305 +-0.004388 +-0.004460 +-0.004520 +-0.004569 +-0.004606 +-0.004632 +-0.004647 +-0.004652 +-0.004648 +-0.004635 +-0.004615 +-0.004588 +-0.004555 +-0.004518 +-0.004478 +-0.004435 +-0.004391 +-0.004347 +-0.004303 +-0.004261 +-0.004221 +-0.004185 +-0.004151 +-0.004121 +-0.004096 +-0.004072 +-0.004050 +-0.004027 +-0.004006 +-0.003985 +-0.003966 +-0.003947 +-0.003929 +-0.003913 +-0.003898 +-0.003884 +-0.003872 +-0.003861 +-0.003853 +-0.003846 +-0.003841 +-0.003839 +-0.003839 +-0.003840 +-0.003845 +-0.003851 +-0.003860 +-0.003871 +-0.003885 +-0.003900 +-0.003928 +-0.003977 +-0.004047 +-0.004138 +-0.004247 +-0.004373 +-0.004515 +-0.004671 +-0.004837 +-0.005012 +-0.005194 +-0.005378 +-0.005563 +-0.005746 +-0.005925 +-0.006095 +-0.006255 +-0.006403 +-0.006536 +-0.006652 +-0.006750 +-0.006827 +-0.006883 +-0.006917 +-0.006928 +-0.006930 +-0.006935 +-0.006943 +-0.006955 +-0.006970 +-0.006989 +-0.007011 +-0.007037 +-0.007066 +-0.007099 +-0.007137 +-0.007178 +-0.007223 +-0.007273 +-0.007327 +-0.007386 +-0.007449 +-0.007517 +-0.007591 +-0.007669 +-0.007753 +-0.007843 +-0.007938 +-0.008039 +-0.008147 +-0.008249 +-0.008334 +-0.008404 +-0.008458 +-0.008499 +-0.008528 +-0.008546 +-0.008556 +-0.008561 +-0.008562 +-0.008564 +-0.008567 +-0.008577 +-0.008595 +-0.008624 +-0.008669 +-0.008730 +-0.008812 +-0.008917 +-0.009046 +-0.009203 +-0.009388 +-0.009604 +-0.009850 +-0.010129 +-0.010424 +-0.010721 +-0.011019 +-0.011318 +-0.011618 +-0.011919 +-0.012221 +-0.012525 +-0.012830 +-0.013137 +-0.013445 +-0.013756 +-0.014068 +-0.014383 +-0.014701 +-0.015021 +-0.015344 +-0.015670 +-0.016000 +-0.016333 +-0.016670 +-0.017010 +-0.017353 +-0.017701 +-0.018051 +-0.018413 +-0.018794 +-0.019193 +-0.019610 +-0.020045 +-0.020494 +-0.020958 +-0.021434 +-0.021920 +-0.022414 +-0.022913 +-0.023416 +-0.023919 +-0.024420 +-0.024917 +-0.025405 +-0.025884 +-0.026350 +-0.026802 +-0.027236 +-0.027651 +-0.028046 +-0.028418 +-0.028767 +-0.029091 +-0.029406 +-0.029726 +-0.030052 +-0.030383 +-0.030719 +-0.031058 +-0.031402 +-0.031748 +-0.032096 +-0.032445 +-0.032794 +-0.033143 +-0.033489 +-0.033832 +-0.034171 +-0.034504 +-0.034831 +-0.035150 +-0.035461 +-0.035762 +-0.036052 +-0.036331 +-0.036597 +-0.036850 +-0.037090 +-0.037314 +-0.037520 +-0.037710 +-0.037881 +-0.038035 +-0.038172 +-0.038293 +-0.038397 +-0.038486 +-0.038560 +-0.038620 +-0.038669 +-0.038705 +-0.038732 +-0.038749 +-0.038758 +-0.038761 +-0.038759 +-0.038752 +-0.038742 +-0.038730 +-0.038717 +-0.038703 +-0.038689 +-0.038676 +-0.038653 +-0.038610 +-0.038547 +-0.038466 +-0.038365 +-0.038247 +-0.038112 +-0.037962 +-0.037797 +-0.037619 +-0.037430 +-0.037231 +-0.037024 +-0.036810 +-0.036592 +-0.036372 +-0.036150 +-0.035930 +-0.035712 +-0.035499 +-0.035292 +-0.035092 +-0.034901 +-0.034720 +-0.034551 +-0.034397 +-0.034262 +-0.034146 +-0.034051 +-0.033975 +-0.033920 +-0.033884 +-0.033866 +-0.033867 +-0.033884 +-0.033917 +-0.033965 +-0.034025 +-0.034097 +-0.034178 +-0.034268 +-0.034365 +-0.034467 +-0.034572 +-0.034681 +-0.034790 +-0.034900 +-0.035009 +-0.035118 +-0.035224 +-0.035335 +-0.035453 +-0.035579 +-0.035713 +-0.035853 +-0.035998 +-0.036148 +-0.036302 +-0.036456 +-0.036611 +-0.036763 +-0.036912 +-0.037054 +-0.037189 +-0.037314 +-0.037427 +-0.037526 +-0.037610 +-0.037677 +-0.037725 +-0.037753 +-0.037761 +-0.037746 +-0.037710 +-0.037652 +-0.037571 +-0.037469 +-0.037347 +-0.037206 +-0.037046 +-0.036869 +-0.036677 +-0.036471 +-0.036255 +-0.036030 +-0.035799 +-0.035564 +-0.035328 +-0.035094 +-0.034865 +-0.034642 +-0.034430 +-0.034229 +-0.034044 +-0.033875 +-0.033724 +-0.033595 +-0.033487 +-0.033403 +-0.033342 +-0.033280 +-0.033190 +-0.033074 +-0.032931 +-0.032764 +-0.032574 +-0.032363 +-0.032134 +-0.031889 +-0.031632 +-0.031366 +-0.031094 +-0.030821 +-0.030548 +-0.030282 +-0.030023 +-0.029778 +-0.029548 +-0.029337 +-0.029148 +-0.028983 +-0.028846 +-0.028737 +-0.028659 +-0.028613 +-0.028586 +-0.028564 +-0.028547 +-0.028537 +-0.028533 +-0.028535 +-0.028545 +-0.028561 +-0.028586 +-0.028618 +-0.028658 +-0.028707 +-0.028765 +-0.028832 +-0.028907 +-0.028993 +-0.029088 +-0.029193 +-0.029308 +-0.029433 +-0.029569 +-0.029715 +-0.029871 +-0.030039 +-0.030217 +-0.030409 +-0.030619 +-0.030847 +-0.031091 +-0.031351 +-0.031626 +-0.031914 +-0.032213 +-0.032521 +-0.032837 +-0.033157 +-0.033480 +-0.033804 +-0.034126 +-0.034444 +-0.034755 +-0.035059 +-0.035354 +-0.035637 +-0.035908 +-0.036166 +-0.036411 +-0.036642 +-0.036861 +-0.037067 +-0.037281 +-0.037524 +-0.037796 +-0.038098 +-0.038433 +-0.038799 +-0.039199 +-0.039632 +-0.040099 +-0.040599 +-0.041132 +-0.041699 +-0.042298 +-0.042930 +-0.043593 +-0.044287 +-0.045011 +-0.045765 +-0.046547 +-0.047356 +-0.048192 +-0.049053 +-0.049940 +-0.050850 +-0.051783 +-0.052721 +-0.053646 +-0.054556 +-0.055451 +-0.056332 +-0.057197 +-0.058046 +-0.058880 +-0.059699 +-0.060503 +-0.061292 +-0.062066 +-0.062827 +-0.063575 +-0.064309 +-0.065031 +-0.065741 +-0.066440 +-0.067127 +-0.067804 +-0.068469 +-0.069124 +-0.069769 +-0.070402 +-0.071025 +-0.071615 +-0.072150 +-0.072632 +-0.073061 +-0.073438 +-0.073765 +-0.074045 +-0.074280 +-0.074473 +-0.074627 +-0.074747 +-0.074835 +-0.074897 +-0.074935 +-0.074953 +-0.074956 +-0.074948 +-0.074931 +-0.074910 +-0.074886 +-0.074863 +-0.074843 +-0.074827 +-0.074817 +-0.074814 +-0.074816 +-0.074823 +-0.074835 +-0.074853 +-0.074876 +-0.074906 +-0.074944 +-0.074989 +-0.075043 +-0.075107 +-0.075182 +-0.075268 +-0.075368 +-0.075480 +-0.075607 +-0.075748 +-0.075905 +-0.076077 +-0.076265 +-0.076468 +-0.076686 +-0.076918 +-0.077165 +-0.077423 +-0.077693 +-0.077983 +-0.078305 +-0.078655 +-0.079029 +-0.079425 +-0.079836 +-0.080259 +-0.080688 +-0.081118 +-0.081541 +-0.081952 +-0.082345 +-0.082713 +-0.083050 +-0.083350 +-0.083607 +-0.083814 +-0.083966 +-0.084060 +-0.084089 +-0.084051 +-0.083942 +-0.083760 +-0.083502 +-0.083168 +-0.082804 +-0.082457 +-0.082127 +-0.081812 +-0.081513 +-0.081227 +-0.080954 +-0.080692 +-0.080441 +-0.080198 +-0.079962 +-0.079730 +-0.079502 +-0.079275 +-0.079047 +-0.078816 +-0.078580 +-0.078338 +-0.078087 +-0.077825 +-0.077551 +-0.077263 +-0.076959 +-0.076638 +-0.076299 +-0.075929 +-0.075518 +-0.075064 +-0.074569 +-0.074032 +-0.073454 +-0.072836 +-0.072180 +-0.071488 +-0.070761 +-0.070003 +-0.069215 +-0.068401 +-0.067563 +-0.066704 +-0.065826 +-0.064933 +-0.064026 +-0.063110 +-0.062184 +-0.061253 +-0.060316 +-0.059377 +-0.058435 +-0.057492 +-0.056561 +-0.055655 +-0.054775 +-0.053919 +-0.053086 +-0.052275 +-0.051486 +-0.050715 +-0.049962 +-0.049223 +-0.048497 +-0.047781 +-0.047074 +-0.046371 +-0.045671 +-0.044972 +-0.044270 +-0.043564 +-0.042852 +-0.042131 +-0.041400 +-0.040657 +-0.039901 +-0.039131 +-0.038346 +-0.037564 +-0.036803 +-0.036062 +-0.035339 +-0.034634 +-0.033944 +-0.033268 +-0.032603 +-0.031948 +-0.031299 +-0.030656 +-0.030014 +-0.029372 +-0.028727 +-0.028077 +-0.027419 +-0.026751 +-0.026072 +-0.025379 +-0.024671 +-0.023946 +-0.023204 +-0.022444 +-0.021665 +-0.020868 +-0.020042 +-0.019179 +-0.018280 +-0.017346 +-0.016380 +-0.015384 +-0.014360 +-0.013313 +-0.012244 +-0.011159 +-0.010061 +-0.008953 +-0.007841 +-0.006729 +-0.005620 +-0.004519 +-0.003431 +-0.002358 +-0.001306 +-0.000278 +0.000723 +0.001693 +0.002630 +0.003531 +0.004393 +0.005239 +0.006091 +0.006945 +0.007799 +0.008649 +0.009493 +0.010327 +0.011146 +0.011946 +0.012723 +0.013474 +0.014193 +0.014875 +0.015518 +0.016116 +0.016666 +0.017164 +0.017607 +0.017991 +0.018314 +0.018574 +0.018768 +0.018895 +0.018955 +0.018946 +0.018924 +0.018942 +0.019001 +0.019100 +0.019239 +0.019416 +0.019632 +0.019886 +0.020177 +0.020504 +0.020867 +0.021264 +0.021694 +0.022157 +0.022650 +0.023173 +0.023723 +0.024301 +0.024902 +0.025527 +0.026173 +0.026838 +0.027520 +0.028218 +0.028928 +0.029631 +0.030308 +0.030957 +0.031577 +0.032169 +0.032731 +0.033265 +0.033771 +0.034250 +0.034705 +0.035136 +0.035546 +0.035936 +0.036310 +0.036669 +0.037015 +0.037352 +0.037680 +0.038002 +0.038320 +0.038636 +0.038950 +0.039263 +0.039577 +0.039892 +0.040197 +0.040482 +0.040748 +0.040996 +0.041226 +0.041441 +0.041640 +0.041827 +0.042003 +0.042170 +0.042330 +0.042486 +0.042640 +0.042795 +0.042953 +0.043116 +0.043286 +0.043466 +0.043657 +0.043862 +0.044082 +0.044318 +0.044571 +0.044843 +0.045133 +0.045431 +0.045725 +0.046017 +0.046306 +0.046593 +0.046878 +0.047162 +0.047447 +0.047731 +0.048017 +0.048305 +0.048595 +0.048889 +0.049186 +0.049489 +0.049797 +0.050111 +0.050432 +0.050760 +0.051095 +0.051439 +0.051791 +0.052151 +0.052520 +0.052898 +0.053275 +0.053644 +0.054003 +0.054354 +0.054695 +0.055026 +0.055346 +0.055656 +0.055954 +0.056239 +0.056513 +0.056772 +0.057018 +0.057250 +0.057467 +0.057669 +0.057855 +0.058025 +0.058180 +0.058319 +0.058443 +0.058551 +0.058645 +0.058724 +0.058790 +0.058861 +0.058956 +0.059074 +0.059216 +0.059380 +0.059567 +0.059775 +0.060002 +0.060246 +0.060507 +0.060780 +0.061064 +0.061357 +0.061656 +0.061958 +0.062261 +0.062562 +0.062860 +0.063151 +0.063435 +0.063708 +0.063971 +0.064222 +0.064459 +0.064683 +0.064904 +0.065131 +0.065365 +0.065606 +0.065851 +0.066100 +0.066353 +0.066608 +0.066864 +0.067119 +0.067372 +0.067621 +0.067865 +0.068102 +0.068331 +0.068549 +0.068757 +0.068951 +0.069132 +0.069297 +0.069447 +0.069580 +0.069695 +0.069793 +0.069874 +0.069945 +0.070016 +0.070087 +0.070158 +0.070228 +0.070299 +0.070369 +0.070438 +0.070506 +0.070572 +0.070637 +0.070700 +0.070760 +0.070818 +0.070872 +0.070922 +0.070969 +0.071010 +0.071047 +0.071079 +0.071106 +0.071127 +0.071141 +0.071151 +0.071154 +0.071139 +0.071095 +0.071022 +0.070921 +0.070791 +0.070634 +0.070449 +0.070237 +0.069999 +0.069736 +0.069449 +0.069139 +0.068806 +0.068452 +0.068078 +0.067685 +0.067273 +0.066845 +0.066400 +0.065941 +0.065468 +0.064982 +0.064484 +0.063976 +0.063458 +0.062925 +0.062373 +0.061803 +0.061216 +0.060614 +0.059997 +0.059369 +0.058729 +0.058080 +0.057424 +0.056762 +0.056096 +0.055427 +0.054758 +0.054089 +0.053423 +0.052760 +0.052102 +0.051450 +0.050805 +0.050168 +0.049540 +0.048922 +0.048313 +0.047714 +0.047119 +0.046520 +0.045919 +0.045314 +0.044706 +0.044093 +0.043476 +0.042854 +0.042226 +0.041591 +0.040948 +0.040298 +0.039638 +0.038968 +0.038287 +0.037596 +0.036894 +0.036180 +0.035454 +0.034717 +0.033969 +0.033210 +0.032440 +0.031662 +0.030875 +0.030093 +0.029329 +0.028582 +0.027853 +0.027142 +0.026448 +0.025771 +0.025111 +0.024466 +0.023836 +0.023220 +0.022618 +0.022027 +0.021448 +0.020879 +0.020319 +0.019768 +0.019225 +0.018689 +0.018159 +0.017636 +0.017118 +0.016605 +0.016098 +0.015596 +0.015096 +0.014595 +0.014093 +0.013591 +0.013090 +0.012591 +0.012093 +0.011599 +0.011108 +0.010621 +0.010140 +0.009664 +0.009195 +0.008734 +0.008280 +0.007835 +0.007398 +0.006971 +0.006553 +0.006146 +0.005748 +0.005360 +0.004983 +0.004616 +0.004259 +0.003908 +0.003560 +0.003215 +0.002874 +0.002537 +0.002204 +0.001876 +0.001552 +0.001234 +0.000921 +0.000615 +0.000314 +0.000020 +-0.000268 +-0.000548 +-0.000822 +-0.001087 +-0.001346 +-0.001597 +-0.001840 +-0.002076 +-0.002304 +-0.002524 +-0.002737 +-0.002943 +-0.003141 +-0.003329 +-0.003508 +-0.003679 +-0.003842 +-0.003998 +-0.004145 +-0.004286 +-0.004420 +-0.004548 +-0.004669 +-0.004785 +-0.004895 +-0.005000 +-0.005100 +-0.005195 +-0.005287 +-0.005373 +-0.005456 +-0.005535 +-0.005611 +-0.005683 +-0.005752 +-0.005818 +-0.005882 +-0.005942 +-0.006000 +-0.006055 +-0.006108 +-0.006158 +-0.006207 +-0.006252 +-0.006296 +-0.006337 +-0.006376 +-0.006412 +-0.006446 +-0.006478 +-0.006508 +-0.006535 +-0.006560 +-0.006582 +-0.006602 +-0.006619 +-0.006634 +-0.006646 +-0.006655 +-0.006662 +-0.006666 diff --git a/libres/test-data/local/snake_oil_no_data/templates/seed_template.txt b/libres/test-data/local/snake_oil_no_data/templates/seed_template.txt new file mode 100644 index 00000000000..a0bca49fbdb --- /dev/null +++ b/libres/test-data/local/snake_oil_no_data/templates/seed_template.txt @@ -0,0 +1 @@ +SEED: \ No newline at end of file diff --git a/libres/test-data/local/snake_oil_no_data/templates/snake_oil_template.txt b/libres/test-data/local/snake_oil_no_data/templates/snake_oil_template.txt new file mode 100644 index 00000000000..ad2c648a0b5 --- /dev/null +++ b/libres/test-data/local/snake_oil_no_data/templates/snake_oil_template.txt @@ -0,0 +1,10 @@ +OP1_PERSISTENCE: +OP1_OCTAVES: +OP1_DIVERGENCE_SCALE: +OP1_OFFSET: +OP2_PERSISTENCE: +OP2_OCTAVES: +OP2_DIVERGENCE_SCALE: +OP2_OFFSET: +BPR_555_PERSISTENCE: +BPR_138_PERSISTENCE: diff --git a/libres/test-data/local/snake_oil_structure/eclipse/include/grid/CASE.EGRID b/libres/test-data/local/snake_oil_structure/eclipse/include/grid/CASE.EGRID new file mode 100644 index 00000000000..33da9f7a70e Binary files /dev/null and b/libres/test-data/local/snake_oil_structure/eclipse/include/grid/CASE.EGRID differ diff --git a/libres/test-data/local/snake_oil_structure/eclipse/model/SNAKE_OIL.DATA b/libres/test-data/local/snake_oil_structure/eclipse/model/SNAKE_OIL.DATA new file mode 100644 index 00000000000..e0a2329dda4 --- /dev/null +++ b/libres/test-data/local/snake_oil_structure/eclipse/model/SNAKE_OIL.DATA @@ -0,0 +1,4 @@ +START + +-- DAY MONTH YEAR + 1 'JAN' 2017 / diff --git a/libres/test-data/local/snake_oil_structure/ert/bin/workflows/MAGIC_PRINT b/libres/test-data/local/snake_oil_structure/ert/bin/workflows/MAGIC_PRINT new file mode 100644 index 00000000000..2d64d86805a --- /dev/null +++ b/libres/test-data/local/snake_oil_structure/ert/bin/workflows/MAGIC_PRINT @@ -0,0 +1 @@ +MAGIC_PRINT magic-list.txt __MAGIC__ diff --git a/libres/test-data/local/snake_oil_structure/ert/bin/workflows/workflowjobs/UBER_PRINT b/libres/test-data/local/snake_oil_structure/ert/bin/workflows/workflowjobs/UBER_PRINT new file mode 100644 index 00000000000..66f9f577efb --- /dev/null +++ b/libres/test-data/local/snake_oil_structure/ert/bin/workflows/workflowjobs/UBER_PRINT @@ -0,0 +1,3 @@ +INTERNAL False +EXECUTABLE bin/uber_print.py +MIN_ARG 1 diff --git a/libres/test-data/local/snake_oil_structure/ert/bin/workflows/workflowjobs/bin/uber_print.py b/libres/test-data/local/snake_oil_structure/ert/bin/workflows/workflowjobs/bin/uber_print.py new file mode 100755 index 00000000000..8d13045a8a6 --- /dev/null +++ b/libres/test-data/local/snake_oil_structure/ert/bin/workflows/workflowjobs/bin/uber_print.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python +import sys + +fileH = open(sys.argv[1], "w") +for arg in sys.argv[2:]: + fileH.write("%s\n" % arg) +fileH.close() diff --git a/libres/test-data/local/snake_oil_structure/ert/input/distributions/sigma.dist b/libres/test-data/local/snake_oil_structure/ert/input/distributions/sigma.dist new file mode 100755 index 00000000000..a929e3c30f7 --- /dev/null +++ b/libres/test-data/local/snake_oil_structure/ert/input/distributions/sigma.dist @@ -0,0 +1 @@ +SIGMA LOGUNIF 0.01 100 \ No newline at end of file diff --git a/libres/test-data/local/snake_oil_structure/ert/input/observations/obsfiles/observations.txt b/libres/test-data/local/snake_oil_structure/ert/input/observations/obsfiles/observations.txt new file mode 100644 index 00000000000..de28837efb4 --- /dev/null +++ b/libres/test-data/local/snake_oil_structure/ert/input/observations/obsfiles/observations.txt @@ -0,0 +1,56 @@ +HISTORY_OBSERVATION FOPR; + +SUMMARY_OBSERVATION WOPR_OP1_9 +{ + VALUE = 0.1; + ERROR = 0.05; + RESTART = 9; + KEY = WOPR:OP1; +}; + +SUMMARY_OBSERVATION WOPR_OP1_36 +{ + VALUE = 0.7; + ERROR = 0.07; + RESTART = 36; + KEY = WOPR:OP1; +}; + +SUMMARY_OBSERVATION WOPR_OP1_72 +{ + VALUE = 0.5; + ERROR = 0.05; + RESTART = 72; + KEY = WOPR:OP1; +}; + +SUMMARY_OBSERVATION WOPR_OP1_108 +{ + VALUE = 0.3; + ERROR = 0.075; + RESTART = 108; + KEY = WOPR:OP1; +}; + +SUMMARY_OBSERVATION WOPR_OP1_144 +{ + VALUE = 0.2; + ERROR = 0.035; + RESTART = 144; + KEY = WOPR:OP1; +}; + +SUMMARY_OBSERVATION WOPR_OP1_190 +{ + VALUE = 0.015; + ERROR = 0.01; + RESTART = 190; + KEY = WOPR:OP1; +}; + +GENERAL_OBSERVATION WPR_DIFF_1 { + DATA = SNAKE_OIL_WPR_DIFF; + INDEX_LIST = 400,800,1200,1800; + RESTART = 199; + OBS_FILE = observations/wpr_diff_obs.txt; +}; \ No newline at end of file diff --git a/libres/test-data/local/snake_oil_structure/ert/input/refcase/SNAKE_OIL_FIELD.SMSPEC b/libres/test-data/local/snake_oil_structure/ert/input/refcase/SNAKE_OIL_FIELD.SMSPEC new file mode 100644 index 00000000000..5a29e043b2d Binary files /dev/null and b/libres/test-data/local/snake_oil_structure/ert/input/refcase/SNAKE_OIL_FIELD.SMSPEC differ diff --git a/libres/test-data/local/snake_oil_structure/ert/input/refcase/SNAKE_OIL_FIELD.UNSMRY b/libres/test-data/local/snake_oil_structure/ert/input/refcase/SNAKE_OIL_FIELD.UNSMRY new file mode 100644 index 00000000000..dfa04f80027 Binary files /dev/null and b/libres/test-data/local/snake_oil_structure/ert/input/refcase/SNAKE_OIL_FIELD.UNSMRY differ diff --git a/libres/test-data/local/snake_oil_structure/ert/input/refcase/time_map.txt b/libres/test-data/local/snake_oil_structure/ert/input/refcase/time_map.txt new file mode 100644 index 00000000000..d54b4293aec --- /dev/null +++ b/libres/test-data/local/snake_oil_structure/ert/input/refcase/time_map.txt @@ -0,0 +1,2000 @@ +01/01/2010 +02/01/2010 +03/01/2010 +04/01/2010 +05/01/2010 +06/01/2010 +07/01/2010 +08/01/2010 +09/01/2010 +10/01/2010 +11/01/2010 +12/01/2010 +13/01/2010 +14/01/2010 +15/01/2010 +16/01/2010 +17/01/2010 +18/01/2010 +19/01/2010 +20/01/2010 +21/01/2010 +22/01/2010 +23/01/2010 +24/01/2010 +25/01/2010 +26/01/2010 +27/01/2010 +28/01/2010 +29/01/2010 +30/01/2010 +31/01/2010 +01/02/2010 +02/02/2010 +03/02/2010 +04/02/2010 +05/02/2010 +06/02/2010 +07/02/2010 +08/02/2010 +09/02/2010 +10/02/2010 +11/02/2010 +12/02/2010 +13/02/2010 +14/02/2010 +15/02/2010 +16/02/2010 +17/02/2010 +18/02/2010 +19/02/2010 +20/02/2010 +21/02/2010 +22/02/2010 +23/02/2010 +24/02/2010 +25/02/2010 +26/02/2010 +27/02/2010 +28/02/2010 +01/03/2010 +02/03/2010 +03/03/2010 +04/03/2010 +05/03/2010 +06/03/2010 +07/03/2010 +08/03/2010 +09/03/2010 +10/03/2010 +11/03/2010 +12/03/2010 +13/03/2010 +14/03/2010 +15/03/2010 +16/03/2010 +17/03/2010 +18/03/2010 +19/03/2010 +20/03/2010 +21/03/2010 +22/03/2010 +23/03/2010 +24/03/2010 +25/03/2010 +26/03/2010 +27/03/2010 +28/03/2010 +29/03/2010 +30/03/2010 +31/03/2010 +01/04/2010 +02/04/2010 +03/04/2010 +04/04/2010 +05/04/2010 +06/04/2010 +07/04/2010 +08/04/2010 +09/04/2010 +10/04/2010 +11/04/2010 +12/04/2010 +13/04/2010 +14/04/2010 +15/04/2010 +16/04/2010 +17/04/2010 +18/04/2010 +19/04/2010 +20/04/2010 +21/04/2010 +22/04/2010 +23/04/2010 +24/04/2010 +25/04/2010 +26/04/2010 +27/04/2010 +28/04/2010 +29/04/2010 +30/04/2010 +01/05/2010 +02/05/2010 +03/05/2010 +04/05/2010 +05/05/2010 +06/05/2010 +07/05/2010 +08/05/2010 +09/05/2010 +10/05/2010 +11/05/2010 +12/05/2010 +13/05/2010 +14/05/2010 +15/05/2010 +16/05/2010 +17/05/2010 +18/05/2010 +19/05/2010 +20/05/2010 +21/05/2010 +22/05/2010 +23/05/2010 +24/05/2010 +25/05/2010 +26/05/2010 +27/05/2010 +28/05/2010 +29/05/2010 +30/05/2010 +31/05/2010 +01/06/2010 +02/06/2010 +03/06/2010 +04/06/2010 +05/06/2010 +06/06/2010 +07/06/2010 +08/06/2010 +09/06/2010 +10/06/2010 +11/06/2010 +12/06/2010 +13/06/2010 +14/06/2010 +15/06/2010 +16/06/2010 +17/06/2010 +18/06/2010 +19/06/2010 +20/06/2010 +21/06/2010 +22/06/2010 +23/06/2010 +24/06/2010 +25/06/2010 +26/06/2010 +27/06/2010 +28/06/2010 +29/06/2010 +30/06/2010 +01/07/2010 +02/07/2010 +03/07/2010 +04/07/2010 +05/07/2010 +06/07/2010 +07/07/2010 +08/07/2010 +09/07/2010 +10/07/2010 +11/07/2010 +12/07/2010 +13/07/2010 +14/07/2010 +15/07/2010 +16/07/2010 +17/07/2010 +18/07/2010 +19/07/2010 +20/07/2010 +21/07/2010 +22/07/2010 +23/07/2010 +24/07/2010 +25/07/2010 +26/07/2010 +27/07/2010 +28/07/2010 +29/07/2010 +30/07/2010 +31/07/2010 +01/08/2010 +02/08/2010 +03/08/2010 +04/08/2010 +05/08/2010 +06/08/2010 +07/08/2010 +08/08/2010 +09/08/2010 +10/08/2010 +11/08/2010 +12/08/2010 +13/08/2010 +14/08/2010 +15/08/2010 +16/08/2010 +17/08/2010 +18/08/2010 +19/08/2010 +20/08/2010 +21/08/2010 +22/08/2010 +23/08/2010 +24/08/2010 +25/08/2010 +26/08/2010 +27/08/2010 +28/08/2010 +29/08/2010 +30/08/2010 +31/08/2010 +01/09/2010 +02/09/2010 +03/09/2010 +04/09/2010 +05/09/2010 +06/09/2010 +07/09/2010 +08/09/2010 +09/09/2010 +10/09/2010 +11/09/2010 +12/09/2010 +13/09/2010 +14/09/2010 +15/09/2010 +16/09/2010 +17/09/2010 +18/09/2010 +19/09/2010 +20/09/2010 +21/09/2010 +22/09/2010 +23/09/2010 +24/09/2010 +25/09/2010 +26/09/2010 +27/09/2010 +28/09/2010 +29/09/2010 +30/09/2010 +01/10/2010 +02/10/2010 +03/10/2010 +04/10/2010 +05/10/2010 +06/10/2010 +07/10/2010 +08/10/2010 +09/10/2010 +10/10/2010 +11/10/2010 +12/10/2010 +13/10/2010 +14/10/2010 +15/10/2010 +16/10/2010 +17/10/2010 +18/10/2010 +19/10/2010 +20/10/2010 +21/10/2010 +22/10/2010 +23/10/2010 +24/10/2010 +25/10/2010 +26/10/2010 +27/10/2010 +28/10/2010 +29/10/2010 +30/10/2010 +31/10/2010 +01/11/2010 +02/11/2010 +03/11/2010 +04/11/2010 +05/11/2010 +06/11/2010 +07/11/2010 +08/11/2010 +09/11/2010 +10/11/2010 +11/11/2010 +12/11/2010 +13/11/2010 +14/11/2010 +15/11/2010 +16/11/2010 +17/11/2010 +18/11/2010 +19/11/2010 +20/11/2010 +21/11/2010 +22/11/2010 +23/11/2010 +24/11/2010 +25/11/2010 +26/11/2010 +27/11/2010 +28/11/2010 +29/11/2010 +30/11/2010 +01/12/2010 +02/12/2010 +03/12/2010 +04/12/2010 +05/12/2010 +06/12/2010 +07/12/2010 +08/12/2010 +09/12/2010 +10/12/2010 +11/12/2010 +12/12/2010 +13/12/2010 +14/12/2010 +15/12/2010 +16/12/2010 +17/12/2010 +18/12/2010 +19/12/2010 +20/12/2010 +21/12/2010 +22/12/2010 +23/12/2010 +24/12/2010 +25/12/2010 +26/12/2010 +27/12/2010 +28/12/2010 +29/12/2010 +30/12/2010 +31/12/2010 +01/01/2011 +02/01/2011 +03/01/2011 +04/01/2011 +05/01/2011 +06/01/2011 +07/01/2011 +08/01/2011 +09/01/2011 +10/01/2011 +11/01/2011 +12/01/2011 +13/01/2011 +14/01/2011 +15/01/2011 +16/01/2011 +17/01/2011 +18/01/2011 +19/01/2011 +20/01/2011 +21/01/2011 +22/01/2011 +23/01/2011 +24/01/2011 +25/01/2011 +26/01/2011 +27/01/2011 +28/01/2011 +29/01/2011 +30/01/2011 +31/01/2011 +01/02/2011 +02/02/2011 +03/02/2011 +04/02/2011 +05/02/2011 +06/02/2011 +07/02/2011 +08/02/2011 +09/02/2011 +10/02/2011 +11/02/2011 +12/02/2011 +13/02/2011 +14/02/2011 +15/02/2011 +16/02/2011 +17/02/2011 +18/02/2011 +19/02/2011 +20/02/2011 +21/02/2011 +22/02/2011 +23/02/2011 +24/02/2011 +25/02/2011 +26/02/2011 +27/02/2011 +28/02/2011 +01/03/2011 +02/03/2011 +03/03/2011 +04/03/2011 +05/03/2011 +06/03/2011 +07/03/2011 +08/03/2011 +09/03/2011 +10/03/2011 +11/03/2011 +12/03/2011 +13/03/2011 +14/03/2011 +15/03/2011 +16/03/2011 +17/03/2011 +18/03/2011 +19/03/2011 +20/03/2011 +21/03/2011 +22/03/2011 +23/03/2011 +24/03/2011 +25/03/2011 +26/03/2011 +27/03/2011 +28/03/2011 +29/03/2011 +30/03/2011 +31/03/2011 +01/04/2011 +02/04/2011 +03/04/2011 +04/04/2011 +05/04/2011 +06/04/2011 +07/04/2011 +08/04/2011 +09/04/2011 +10/04/2011 +11/04/2011 +12/04/2011 +13/04/2011 +14/04/2011 +15/04/2011 +16/04/2011 +17/04/2011 +18/04/2011 +19/04/2011 +20/04/2011 +21/04/2011 +22/04/2011 +23/04/2011 +24/04/2011 +25/04/2011 +26/04/2011 +27/04/2011 +28/04/2011 +29/04/2011 +30/04/2011 +01/05/2011 +02/05/2011 +03/05/2011 +04/05/2011 +05/05/2011 +06/05/2011 +07/05/2011 +08/05/2011 +09/05/2011 +10/05/2011 +11/05/2011 +12/05/2011 +13/05/2011 +14/05/2011 +15/05/2011 +16/05/2011 +17/05/2011 +18/05/2011 +19/05/2011 +20/05/2011 +21/05/2011 +22/05/2011 +23/05/2011 +24/05/2011 +25/05/2011 +26/05/2011 +27/05/2011 +28/05/2011 +29/05/2011 +30/05/2011 +31/05/2011 +01/06/2011 +02/06/2011 +03/06/2011 +04/06/2011 +05/06/2011 +06/06/2011 +07/06/2011 +08/06/2011 +09/06/2011 +10/06/2011 +11/06/2011 +12/06/2011 +13/06/2011 +14/06/2011 +15/06/2011 +16/06/2011 +17/06/2011 +18/06/2011 +19/06/2011 +20/06/2011 +21/06/2011 +22/06/2011 +23/06/2011 +24/06/2011 +25/06/2011 +26/06/2011 +27/06/2011 +28/06/2011 +29/06/2011 +30/06/2011 +01/07/2011 +02/07/2011 +03/07/2011 +04/07/2011 +05/07/2011 +06/07/2011 +07/07/2011 +08/07/2011 +09/07/2011 +10/07/2011 +11/07/2011 +12/07/2011 +13/07/2011 +14/07/2011 +15/07/2011 +16/07/2011 +17/07/2011 +18/07/2011 +19/07/2011 +20/07/2011 +21/07/2011 +22/07/2011 +23/07/2011 +24/07/2011 +25/07/2011 +26/07/2011 +27/07/2011 +28/07/2011 +29/07/2011 +30/07/2011 +31/07/2011 +01/08/2011 +02/08/2011 +03/08/2011 +04/08/2011 +05/08/2011 +06/08/2011 +07/08/2011 +08/08/2011 +09/08/2011 +10/08/2011 +11/08/2011 +12/08/2011 +13/08/2011 +14/08/2011 +15/08/2011 +16/08/2011 +17/08/2011 +18/08/2011 +19/08/2011 +20/08/2011 +21/08/2011 +22/08/2011 +23/08/2011 +24/08/2011 +25/08/2011 +26/08/2011 +27/08/2011 +28/08/2011 +29/08/2011 +30/08/2011 +31/08/2011 +01/09/2011 +02/09/2011 +03/09/2011 +04/09/2011 +05/09/2011 +06/09/2011 +07/09/2011 +08/09/2011 +09/09/2011 +10/09/2011 +11/09/2011 +12/09/2011 +13/09/2011 +14/09/2011 +15/09/2011 +16/09/2011 +17/09/2011 +18/09/2011 +19/09/2011 +20/09/2011 +21/09/2011 +22/09/2011 +23/09/2011 +24/09/2011 +25/09/2011 +26/09/2011 +27/09/2011 +28/09/2011 +29/09/2011 +30/09/2011 +01/10/2011 +02/10/2011 +03/10/2011 +04/10/2011 +05/10/2011 +06/10/2011 +07/10/2011 +08/10/2011 +09/10/2011 +10/10/2011 +11/10/2011 +12/10/2011 +13/10/2011 +14/10/2011 +15/10/2011 +16/10/2011 +17/10/2011 +18/10/2011 +19/10/2011 +20/10/2011 +21/10/2011 +22/10/2011 +23/10/2011 +24/10/2011 +25/10/2011 +26/10/2011 +27/10/2011 +28/10/2011 +29/10/2011 +30/10/2011 +31/10/2011 +01/11/2011 +02/11/2011 +03/11/2011 +04/11/2011 +05/11/2011 +06/11/2011 +07/11/2011 +08/11/2011 +09/11/2011 +10/11/2011 +11/11/2011 +12/11/2011 +13/11/2011 +14/11/2011 +15/11/2011 +16/11/2011 +17/11/2011 +18/11/2011 +19/11/2011 +20/11/2011 +21/11/2011 +22/11/2011 +23/11/2011 +24/11/2011 +25/11/2011 +26/11/2011 +27/11/2011 +28/11/2011 +29/11/2011 +30/11/2011 +01/12/2011 +02/12/2011 +03/12/2011 +04/12/2011 +05/12/2011 +06/12/2011 +07/12/2011 +08/12/2011 +09/12/2011 +10/12/2011 +11/12/2011 +12/12/2011 +13/12/2011 +14/12/2011 +15/12/2011 +16/12/2011 +17/12/2011 +18/12/2011 +19/12/2011 +20/12/2011 +21/12/2011 +22/12/2011 +23/12/2011 +24/12/2011 +25/12/2011 +26/12/2011 +27/12/2011 +28/12/2011 +29/12/2011 +30/12/2011 +31/12/2011 +01/01/2012 +02/01/2012 +03/01/2012 +04/01/2012 +05/01/2012 +06/01/2012 +07/01/2012 +08/01/2012 +09/01/2012 +10/01/2012 +11/01/2012 +12/01/2012 +13/01/2012 +14/01/2012 +15/01/2012 +16/01/2012 +17/01/2012 +18/01/2012 +19/01/2012 +20/01/2012 +21/01/2012 +22/01/2012 +23/01/2012 +24/01/2012 +25/01/2012 +26/01/2012 +27/01/2012 +28/01/2012 +29/01/2012 +30/01/2012 +31/01/2012 +01/02/2012 +02/02/2012 +03/02/2012 +04/02/2012 +05/02/2012 +06/02/2012 +07/02/2012 +08/02/2012 +09/02/2012 +10/02/2012 +11/02/2012 +12/02/2012 +13/02/2012 +14/02/2012 +15/02/2012 +16/02/2012 +17/02/2012 +18/02/2012 +19/02/2012 +20/02/2012 +21/02/2012 +22/02/2012 +23/02/2012 +24/02/2012 +25/02/2012 +26/02/2012 +27/02/2012 +28/02/2012 +29/02/2012 +01/03/2012 +02/03/2012 +03/03/2012 +04/03/2012 +05/03/2012 +06/03/2012 +07/03/2012 +08/03/2012 +09/03/2012 +10/03/2012 +11/03/2012 +12/03/2012 +13/03/2012 +14/03/2012 +15/03/2012 +16/03/2012 +17/03/2012 +18/03/2012 +19/03/2012 +20/03/2012 +21/03/2012 +22/03/2012 +23/03/2012 +24/03/2012 +25/03/2012 +26/03/2012 +27/03/2012 +28/03/2012 +29/03/2012 +30/03/2012 +31/03/2012 +01/04/2012 +02/04/2012 +03/04/2012 +04/04/2012 +05/04/2012 +06/04/2012 +07/04/2012 +08/04/2012 +09/04/2012 +10/04/2012 +11/04/2012 +12/04/2012 +13/04/2012 +14/04/2012 +15/04/2012 +16/04/2012 +17/04/2012 +18/04/2012 +19/04/2012 +20/04/2012 +21/04/2012 +22/04/2012 +23/04/2012 +24/04/2012 +25/04/2012 +26/04/2012 +27/04/2012 +28/04/2012 +29/04/2012 +30/04/2012 +01/05/2012 +02/05/2012 +03/05/2012 +04/05/2012 +05/05/2012 +06/05/2012 +07/05/2012 +08/05/2012 +09/05/2012 +10/05/2012 +11/05/2012 +12/05/2012 +13/05/2012 +14/05/2012 +15/05/2012 +16/05/2012 +17/05/2012 +18/05/2012 +19/05/2012 +20/05/2012 +21/05/2012 +22/05/2012 +23/05/2012 +24/05/2012 +25/05/2012 +26/05/2012 +27/05/2012 +28/05/2012 +29/05/2012 +30/05/2012 +31/05/2012 +01/06/2012 +02/06/2012 +03/06/2012 +04/06/2012 +05/06/2012 +06/06/2012 +07/06/2012 +08/06/2012 +09/06/2012 +10/06/2012 +11/06/2012 +12/06/2012 +13/06/2012 +14/06/2012 +15/06/2012 +16/06/2012 +17/06/2012 +18/06/2012 +19/06/2012 +20/06/2012 +21/06/2012 +22/06/2012 +23/06/2012 +24/06/2012 +25/06/2012 +26/06/2012 +27/06/2012 +28/06/2012 +29/06/2012 +30/06/2012 +01/07/2012 +02/07/2012 +03/07/2012 +04/07/2012 +05/07/2012 +06/07/2012 +07/07/2012 +08/07/2012 +09/07/2012 +10/07/2012 +11/07/2012 +12/07/2012 +13/07/2012 +14/07/2012 +15/07/2012 +16/07/2012 +17/07/2012 +18/07/2012 +19/07/2012 +20/07/2012 +21/07/2012 +22/07/2012 +23/07/2012 +24/07/2012 +25/07/2012 +26/07/2012 +27/07/2012 +28/07/2012 +29/07/2012 +30/07/2012 +31/07/2012 +01/08/2012 +02/08/2012 +03/08/2012 +04/08/2012 +05/08/2012 +06/08/2012 +07/08/2012 +08/08/2012 +09/08/2012 +10/08/2012 +11/08/2012 +12/08/2012 +13/08/2012 +14/08/2012 +15/08/2012 +16/08/2012 +17/08/2012 +18/08/2012 +19/08/2012 +20/08/2012 +21/08/2012 +22/08/2012 +23/08/2012 +24/08/2012 +25/08/2012 +26/08/2012 +27/08/2012 +28/08/2012 +29/08/2012 +30/08/2012 +31/08/2012 +01/09/2012 +02/09/2012 +03/09/2012 +04/09/2012 +05/09/2012 +06/09/2012 +07/09/2012 +08/09/2012 +09/09/2012 +10/09/2012 +11/09/2012 +12/09/2012 +13/09/2012 +14/09/2012 +15/09/2012 +16/09/2012 +17/09/2012 +18/09/2012 +19/09/2012 +20/09/2012 +21/09/2012 +22/09/2012 +23/09/2012 +24/09/2012 +25/09/2012 +26/09/2012 +27/09/2012 +28/09/2012 +29/09/2012 +30/09/2012 +01/10/2012 +02/10/2012 +03/10/2012 +04/10/2012 +05/10/2012 +06/10/2012 +07/10/2012 +08/10/2012 +09/10/2012 +10/10/2012 +11/10/2012 +12/10/2012 +13/10/2012 +14/10/2012 +15/10/2012 +16/10/2012 +17/10/2012 +18/10/2012 +19/10/2012 +20/10/2012 +21/10/2012 +22/10/2012 +23/10/2012 +24/10/2012 +25/10/2012 +26/10/2012 +27/10/2012 +28/10/2012 +29/10/2012 +30/10/2012 +31/10/2012 +01/11/2012 +02/11/2012 +03/11/2012 +04/11/2012 +05/11/2012 +06/11/2012 +07/11/2012 +08/11/2012 +09/11/2012 +10/11/2012 +11/11/2012 +12/11/2012 +13/11/2012 +14/11/2012 +15/11/2012 +16/11/2012 +17/11/2012 +18/11/2012 +19/11/2012 +20/11/2012 +21/11/2012 +22/11/2012 +23/11/2012 +24/11/2012 +25/11/2012 +26/11/2012 +27/11/2012 +28/11/2012 +29/11/2012 +30/11/2012 +01/12/2012 +02/12/2012 +03/12/2012 +04/12/2012 +05/12/2012 +06/12/2012 +07/12/2012 +08/12/2012 +09/12/2012 +10/12/2012 +11/12/2012 +12/12/2012 +13/12/2012 +14/12/2012 +15/12/2012 +16/12/2012 +17/12/2012 +18/12/2012 +19/12/2012 +20/12/2012 +21/12/2012 +22/12/2012 +23/12/2012 +24/12/2012 +25/12/2012 +26/12/2012 +27/12/2012 +28/12/2012 +29/12/2012 +30/12/2012 +31/12/2012 +01/01/2013 +02/01/2013 +03/01/2013 +04/01/2013 +05/01/2013 +06/01/2013 +07/01/2013 +08/01/2013 +09/01/2013 +10/01/2013 +11/01/2013 +12/01/2013 +13/01/2013 +14/01/2013 +15/01/2013 +16/01/2013 +17/01/2013 +18/01/2013 +19/01/2013 +20/01/2013 +21/01/2013 +22/01/2013 +23/01/2013 +24/01/2013 +25/01/2013 +26/01/2013 +27/01/2013 +28/01/2013 +29/01/2013 +30/01/2013 +31/01/2013 +01/02/2013 +02/02/2013 +03/02/2013 +04/02/2013 +05/02/2013 +06/02/2013 +07/02/2013 +08/02/2013 +09/02/2013 +10/02/2013 +11/02/2013 +12/02/2013 +13/02/2013 +14/02/2013 +15/02/2013 +16/02/2013 +17/02/2013 +18/02/2013 +19/02/2013 +20/02/2013 +21/02/2013 +22/02/2013 +23/02/2013 +24/02/2013 +25/02/2013 +26/02/2013 +27/02/2013 +28/02/2013 +01/03/2013 +02/03/2013 +03/03/2013 +04/03/2013 +05/03/2013 +06/03/2013 +07/03/2013 +08/03/2013 +09/03/2013 +10/03/2013 +11/03/2013 +12/03/2013 +13/03/2013 +14/03/2013 +15/03/2013 +16/03/2013 +17/03/2013 +18/03/2013 +19/03/2013 +20/03/2013 +21/03/2013 +22/03/2013 +23/03/2013 +24/03/2013 +25/03/2013 +26/03/2013 +27/03/2013 +28/03/2013 +29/03/2013 +30/03/2013 +31/03/2013 +01/04/2013 +02/04/2013 +03/04/2013 +04/04/2013 +05/04/2013 +06/04/2013 +07/04/2013 +08/04/2013 +09/04/2013 +10/04/2013 +11/04/2013 +12/04/2013 +13/04/2013 +14/04/2013 +15/04/2013 +16/04/2013 +17/04/2013 +18/04/2013 +19/04/2013 +20/04/2013 +21/04/2013 +22/04/2013 +23/04/2013 +24/04/2013 +25/04/2013 +26/04/2013 +27/04/2013 +28/04/2013 +29/04/2013 +30/04/2013 +01/05/2013 +02/05/2013 +03/05/2013 +04/05/2013 +05/05/2013 +06/05/2013 +07/05/2013 +08/05/2013 +09/05/2013 +10/05/2013 +11/05/2013 +12/05/2013 +13/05/2013 +14/05/2013 +15/05/2013 +16/05/2013 +17/05/2013 +18/05/2013 +19/05/2013 +20/05/2013 +21/05/2013 +22/05/2013 +23/05/2013 +24/05/2013 +25/05/2013 +26/05/2013 +27/05/2013 +28/05/2013 +29/05/2013 +30/05/2013 +31/05/2013 +01/06/2013 +02/06/2013 +03/06/2013 +04/06/2013 +05/06/2013 +06/06/2013 +07/06/2013 +08/06/2013 +09/06/2013 +10/06/2013 +11/06/2013 +12/06/2013 +13/06/2013 +14/06/2013 +15/06/2013 +16/06/2013 +17/06/2013 +18/06/2013 +19/06/2013 +20/06/2013 +21/06/2013 +22/06/2013 +23/06/2013 +24/06/2013 +25/06/2013 +26/06/2013 +27/06/2013 +28/06/2013 +29/06/2013 +30/06/2013 +01/07/2013 +02/07/2013 +03/07/2013 +04/07/2013 +05/07/2013 +06/07/2013 +07/07/2013 +08/07/2013 +09/07/2013 +10/07/2013 +11/07/2013 +12/07/2013 +13/07/2013 +14/07/2013 +15/07/2013 +16/07/2013 +17/07/2013 +18/07/2013 +19/07/2013 +20/07/2013 +21/07/2013 +22/07/2013 +23/07/2013 +24/07/2013 +25/07/2013 +26/07/2013 +27/07/2013 +28/07/2013 +29/07/2013 +30/07/2013 +31/07/2013 +01/08/2013 +02/08/2013 +03/08/2013 +04/08/2013 +05/08/2013 +06/08/2013 +07/08/2013 +08/08/2013 +09/08/2013 +10/08/2013 +11/08/2013 +12/08/2013 +13/08/2013 +14/08/2013 +15/08/2013 +16/08/2013 +17/08/2013 +18/08/2013 +19/08/2013 +20/08/2013 +21/08/2013 +22/08/2013 +23/08/2013 +24/08/2013 +25/08/2013 +26/08/2013 +27/08/2013 +28/08/2013 +29/08/2013 +30/08/2013 +31/08/2013 +01/09/2013 +02/09/2013 +03/09/2013 +04/09/2013 +05/09/2013 +06/09/2013 +07/09/2013 +08/09/2013 +09/09/2013 +10/09/2013 +11/09/2013 +12/09/2013 +13/09/2013 +14/09/2013 +15/09/2013 +16/09/2013 +17/09/2013 +18/09/2013 +19/09/2013 +20/09/2013 +21/09/2013 +22/09/2013 +23/09/2013 +24/09/2013 +25/09/2013 +26/09/2013 +27/09/2013 +28/09/2013 +29/09/2013 +30/09/2013 +01/10/2013 +02/10/2013 +03/10/2013 +04/10/2013 +05/10/2013 +06/10/2013 +07/10/2013 +08/10/2013 +09/10/2013 +10/10/2013 +11/10/2013 +12/10/2013 +13/10/2013 +14/10/2013 +15/10/2013 +16/10/2013 +17/10/2013 +18/10/2013 +19/10/2013 +20/10/2013 +21/10/2013 +22/10/2013 +23/10/2013 +24/10/2013 +25/10/2013 +26/10/2013 +27/10/2013 +28/10/2013 +29/10/2013 +30/10/2013 +31/10/2013 +01/11/2013 +02/11/2013 +03/11/2013 +04/11/2013 +05/11/2013 +06/11/2013 +07/11/2013 +08/11/2013 +09/11/2013 +10/11/2013 +11/11/2013 +12/11/2013 +13/11/2013 +14/11/2013 +15/11/2013 +16/11/2013 +17/11/2013 +18/11/2013 +19/11/2013 +20/11/2013 +21/11/2013 +22/11/2013 +23/11/2013 +24/11/2013 +25/11/2013 +26/11/2013 +27/11/2013 +28/11/2013 +29/11/2013 +30/11/2013 +01/12/2013 +02/12/2013 +03/12/2013 +04/12/2013 +05/12/2013 +06/12/2013 +07/12/2013 +08/12/2013 +09/12/2013 +10/12/2013 +11/12/2013 +12/12/2013 +13/12/2013 +14/12/2013 +15/12/2013 +16/12/2013 +17/12/2013 +18/12/2013 +19/12/2013 +20/12/2013 +21/12/2013 +22/12/2013 +23/12/2013 +24/12/2013 +25/12/2013 +26/12/2013 +27/12/2013 +28/12/2013 +29/12/2013 +30/12/2013 +31/12/2013 +01/01/2014 +02/01/2014 +03/01/2014 +04/01/2014 +05/01/2014 +06/01/2014 +07/01/2014 +08/01/2014 +09/01/2014 +10/01/2014 +11/01/2014 +12/01/2014 +13/01/2014 +14/01/2014 +15/01/2014 +16/01/2014 +17/01/2014 +18/01/2014 +19/01/2014 +20/01/2014 +21/01/2014 +22/01/2014 +23/01/2014 +24/01/2014 +25/01/2014 +26/01/2014 +27/01/2014 +28/01/2014 +29/01/2014 +30/01/2014 +31/01/2014 +01/02/2014 +02/02/2014 +03/02/2014 +04/02/2014 +05/02/2014 +06/02/2014 +07/02/2014 +08/02/2014 +09/02/2014 +10/02/2014 +11/02/2014 +12/02/2014 +13/02/2014 +14/02/2014 +15/02/2014 +16/02/2014 +17/02/2014 +18/02/2014 +19/02/2014 +20/02/2014 +21/02/2014 +22/02/2014 +23/02/2014 +24/02/2014 +25/02/2014 +26/02/2014 +27/02/2014 +28/02/2014 +01/03/2014 +02/03/2014 +03/03/2014 +04/03/2014 +05/03/2014 +06/03/2014 +07/03/2014 +08/03/2014 +09/03/2014 +10/03/2014 +11/03/2014 +12/03/2014 +13/03/2014 +14/03/2014 +15/03/2014 +16/03/2014 +17/03/2014 +18/03/2014 +19/03/2014 +20/03/2014 +21/03/2014 +22/03/2014 +23/03/2014 +24/03/2014 +25/03/2014 +26/03/2014 +27/03/2014 +28/03/2014 +29/03/2014 +30/03/2014 +31/03/2014 +01/04/2014 +02/04/2014 +03/04/2014 +04/04/2014 +05/04/2014 +06/04/2014 +07/04/2014 +08/04/2014 +09/04/2014 +10/04/2014 +11/04/2014 +12/04/2014 +13/04/2014 +14/04/2014 +15/04/2014 +16/04/2014 +17/04/2014 +18/04/2014 +19/04/2014 +20/04/2014 +21/04/2014 +22/04/2014 +23/04/2014 +24/04/2014 +25/04/2014 +26/04/2014 +27/04/2014 +28/04/2014 +29/04/2014 +30/04/2014 +01/05/2014 +02/05/2014 +03/05/2014 +04/05/2014 +05/05/2014 +06/05/2014 +07/05/2014 +08/05/2014 +09/05/2014 +10/05/2014 +11/05/2014 +12/05/2014 +13/05/2014 +14/05/2014 +15/05/2014 +16/05/2014 +17/05/2014 +18/05/2014 +19/05/2014 +20/05/2014 +21/05/2014 +22/05/2014 +23/05/2014 +24/05/2014 +25/05/2014 +26/05/2014 +27/05/2014 +28/05/2014 +29/05/2014 +30/05/2014 +31/05/2014 +01/06/2014 +02/06/2014 +03/06/2014 +04/06/2014 +05/06/2014 +06/06/2014 +07/06/2014 +08/06/2014 +09/06/2014 +10/06/2014 +11/06/2014 +12/06/2014 +13/06/2014 +14/06/2014 +15/06/2014 +16/06/2014 +17/06/2014 +18/06/2014 +19/06/2014 +20/06/2014 +21/06/2014 +22/06/2014 +23/06/2014 +24/06/2014 +25/06/2014 +26/06/2014 +27/06/2014 +28/06/2014 +29/06/2014 +30/06/2014 +01/07/2014 +02/07/2014 +03/07/2014 +04/07/2014 +05/07/2014 +06/07/2014 +07/07/2014 +08/07/2014 +09/07/2014 +10/07/2014 +11/07/2014 +12/07/2014 +13/07/2014 +14/07/2014 +15/07/2014 +16/07/2014 +17/07/2014 +18/07/2014 +19/07/2014 +20/07/2014 +21/07/2014 +22/07/2014 +23/07/2014 +24/07/2014 +25/07/2014 +26/07/2014 +27/07/2014 +28/07/2014 +29/07/2014 +30/07/2014 +31/07/2014 +01/08/2014 +02/08/2014 +03/08/2014 +04/08/2014 +05/08/2014 +06/08/2014 +07/08/2014 +08/08/2014 +09/08/2014 +10/08/2014 +11/08/2014 +12/08/2014 +13/08/2014 +14/08/2014 +15/08/2014 +16/08/2014 +17/08/2014 +18/08/2014 +19/08/2014 +20/08/2014 +21/08/2014 +22/08/2014 +23/08/2014 +24/08/2014 +25/08/2014 +26/08/2014 +27/08/2014 +28/08/2014 +29/08/2014 +30/08/2014 +31/08/2014 +01/09/2014 +02/09/2014 +03/09/2014 +04/09/2014 +05/09/2014 +06/09/2014 +07/09/2014 +08/09/2014 +09/09/2014 +10/09/2014 +11/09/2014 +12/09/2014 +13/09/2014 +14/09/2014 +15/09/2014 +16/09/2014 +17/09/2014 +18/09/2014 +19/09/2014 +20/09/2014 +21/09/2014 +22/09/2014 +23/09/2014 +24/09/2014 +25/09/2014 +26/09/2014 +27/09/2014 +28/09/2014 +29/09/2014 +30/09/2014 +01/10/2014 +02/10/2014 +03/10/2014 +04/10/2014 +05/10/2014 +06/10/2014 +07/10/2014 +08/10/2014 +09/10/2014 +10/10/2014 +11/10/2014 +12/10/2014 +13/10/2014 +14/10/2014 +15/10/2014 +16/10/2014 +17/10/2014 +18/10/2014 +19/10/2014 +20/10/2014 +21/10/2014 +22/10/2014 +23/10/2014 +24/10/2014 +25/10/2014 +26/10/2014 +27/10/2014 +28/10/2014 +29/10/2014 +30/10/2014 +31/10/2014 +01/11/2014 +02/11/2014 +03/11/2014 +04/11/2014 +05/11/2014 +06/11/2014 +07/11/2014 +08/11/2014 +09/11/2014 +10/11/2014 +11/11/2014 +12/11/2014 +13/11/2014 +14/11/2014 +15/11/2014 +16/11/2014 +17/11/2014 +18/11/2014 +19/11/2014 +20/11/2014 +21/11/2014 +22/11/2014 +23/11/2014 +24/11/2014 +25/11/2014 +26/11/2014 +27/11/2014 +28/11/2014 +29/11/2014 +30/11/2014 +01/12/2014 +02/12/2014 +03/12/2014 +04/12/2014 +05/12/2014 +06/12/2014 +07/12/2014 +08/12/2014 +09/12/2014 +10/12/2014 +11/12/2014 +12/12/2014 +13/12/2014 +14/12/2014 +15/12/2014 +16/12/2014 +17/12/2014 +18/12/2014 +19/12/2014 +20/12/2014 +21/12/2014 +22/12/2014 +23/12/2014 +24/12/2014 +25/12/2014 +26/12/2014 +27/12/2014 +28/12/2014 +29/12/2014 +30/12/2014 +31/12/2014 +01/01/2015 +02/01/2015 +03/01/2015 +04/01/2015 +05/01/2015 +06/01/2015 +07/01/2015 +08/01/2015 +09/01/2015 +10/01/2015 +11/01/2015 +12/01/2015 +13/01/2015 +14/01/2015 +15/01/2015 +16/01/2015 +17/01/2015 +18/01/2015 +19/01/2015 +20/01/2015 +21/01/2015 +22/01/2015 +23/01/2015 +24/01/2015 +25/01/2015 +26/01/2015 +27/01/2015 +28/01/2015 +29/01/2015 +30/01/2015 +31/01/2015 +01/02/2015 +02/02/2015 +03/02/2015 +04/02/2015 +05/02/2015 +06/02/2015 +07/02/2015 +08/02/2015 +09/02/2015 +10/02/2015 +11/02/2015 +12/02/2015 +13/02/2015 +14/02/2015 +15/02/2015 +16/02/2015 +17/02/2015 +18/02/2015 +19/02/2015 +20/02/2015 +21/02/2015 +22/02/2015 +23/02/2015 +24/02/2015 +25/02/2015 +26/02/2015 +27/02/2015 +28/02/2015 +01/03/2015 +02/03/2015 +03/03/2015 +04/03/2015 +05/03/2015 +06/03/2015 +07/03/2015 +08/03/2015 +09/03/2015 +10/03/2015 +11/03/2015 +12/03/2015 +13/03/2015 +14/03/2015 +15/03/2015 +16/03/2015 +17/03/2015 +18/03/2015 +19/03/2015 +20/03/2015 +21/03/2015 +22/03/2015 +23/03/2015 +24/03/2015 +25/03/2015 +26/03/2015 +27/03/2015 +28/03/2015 +29/03/2015 +30/03/2015 +31/03/2015 +01/04/2015 +02/04/2015 +03/04/2015 +04/04/2015 +05/04/2015 +06/04/2015 +07/04/2015 +08/04/2015 +09/04/2015 +10/04/2015 +11/04/2015 +12/04/2015 +13/04/2015 +14/04/2015 +15/04/2015 +16/04/2015 +17/04/2015 +18/04/2015 +19/04/2015 +20/04/2015 +21/04/2015 +22/04/2015 +23/04/2015 +24/04/2015 +25/04/2015 +26/04/2015 +27/04/2015 +28/04/2015 +29/04/2015 +30/04/2015 +01/05/2015 +02/05/2015 +03/05/2015 +04/05/2015 +05/05/2015 +06/05/2015 +07/05/2015 +08/05/2015 +09/05/2015 +10/05/2015 +11/05/2015 +12/05/2015 +13/05/2015 +14/05/2015 +15/05/2015 +16/05/2015 +17/05/2015 +18/05/2015 +19/05/2015 +20/05/2015 +21/05/2015 +22/05/2015 +23/05/2015 +24/05/2015 +25/05/2015 +26/05/2015 +27/05/2015 +28/05/2015 +29/05/2015 +30/05/2015 +31/05/2015 +01/06/2015 +02/06/2015 +03/06/2015 +04/06/2015 +05/06/2015 +06/06/2015 +07/06/2015 +08/06/2015 +09/06/2015 +10/06/2015 +11/06/2015 +12/06/2015 +13/06/2015 +14/06/2015 +15/06/2015 +16/06/2015 +17/06/2015 +18/06/2015 +19/06/2015 +20/06/2015 +21/06/2015 +22/06/2015 +23/06/2015 diff --git a/libres/test-data/local/snake_oil_structure/ert/input/rng/SEED b/libres/test-data/local/snake_oil_structure/ert/input/rng/SEED new file mode 100644 index 00000000000..314cda88f68 --- /dev/null +++ b/libres/test-data/local/snake_oil_structure/ert/input/rng/SEED @@ -0,0 +1 @@ +67 -110 42 -42 106 34 96 0 18 42 76 -69 44 -40 -78 -61 \ No newline at end of file diff --git a/libres/test-data/local/snake_oil_structure/ert/input/templates/seed_template.txt b/libres/test-data/local/snake_oil_structure/ert/input/templates/seed_template.txt new file mode 100644 index 00000000000..a0bca49fbdb --- /dev/null +++ b/libres/test-data/local/snake_oil_structure/ert/input/templates/seed_template.txt @@ -0,0 +1 @@ +SEED: \ No newline at end of file diff --git a/libres/test-data/local/snake_oil_structure/ert/input/templates/sigma.tmpl b/libres/test-data/local/snake_oil_structure/ert/input/templates/sigma.tmpl new file mode 100755 index 00000000000..02141ce1efb --- /dev/null +++ b/libres/test-data/local/snake_oil_structure/ert/input/templates/sigma.tmpl @@ -0,0 +1,2 @@ +SIGMA + / \ No newline at end of file diff --git a/libres/test-data/local/snake_oil_structure/ert/model/user_config.ert b/libres/test-data/local/snake_oil_structure/ert/model/user_config.ert new file mode 100755 index 00000000000..1a3d0a50e37 --- /dev/null +++ b/libres/test-data/local/snake_oil_structure/ert/model/user_config.ert @@ -0,0 +1,114 @@ +--=============================================================== +-- +-- ERT CONFIGURATION FILE +-- +--=============================================================== + +JOBNAME SNAKE_OIL_STRUCTURE_%d + +--------------------------------- +-- SECTION: Internal variables -- +--------------------------------- +DEFINE TEST_USER +DEFINE scratch/ert +DEFINE the_extensive_case +DEFINE XYZ + +--------------------------------------------------- +-- SECTION: Eclipse, runpath and storage settings +--------------------------------------------------- +DATA_FILE ../../eclipse/model/SNAKE_OIL.DATA -- Location of the Eclipse .DATA-file +GRID ../../eclipse/include/grid/CASE.EGRID -- Name of the Eclipse .GRID-file +RUNPATH ///realization-%d/iter-%d -- Runpath for simulations (first %d will be replaced with realization number, second %d with iteration number +ECLBASE eclipse/model/-%d -- Name used for the ECLIPSE simulations (%d will be replaced with the realization number) +ENSPATH ../output/storage/ -- Storage of internal ert data +RUNPATH_FILE ../output/run_path_file/.ert-runpath-list_ -- Ert runpath file for workflows + +REFCASE ../input/refcase/SNAKE_OIL_FIELD -- Used for plotting and neccessary for AHM for reading historical production, files needed by ert: .SMSPEC and .UNSMRY + +LOG_LEVEL INFO +UPDATE_LOG_PATH ../output/update_log/ -- Storage of update log (list of active and inactive data points) +LOG_FILE ../output/log/ert_.log -- Ert log file + +RANDOM_SEED 3593114179000630026631423308983283277868 +-- -------------------------------------------------- +-- -- SECTION: LSF settings, AHM configuration etc +-- -------------------------------------------------- +NUM_REALIZATIONS 10 -- Set number of realizations to run +MAX_RUNTIME 23400 -- Set the maximum allowed runtime in + -- seconds. (Killed when exceeding) +MIN_REALIZATIONS 50% -- Set minimum number of realization that + -- must be completed before moving to next + -- iteration + +QUEUE_SYSTEM LSF +QUEUE_OPTION LSF MAX_RUNNING 100 +QUEUE_OPTION LSF LSF_RESOURCE select[x86_64Linux] same[type:model] -- arbitrary IBM LSF string +QUEUE_OPTION LSF LSF_SERVER simulacrum -- name of server +QUEUE_OPTION LSF LSF_QUEUE mr -- name of queue, mr=multiple realizations + +MAX_SUBMIT 13 -- How many times should the queue system + -- retry a simulation. Default == 2. Use + -- 1 when debugging) + +UMASK 007 -- umask == chmod o-xrw + +-- ------------------------------------------------------------------------ +-- -- SECTION: Uncertainty parameters +-- ------------------------------------------------------------------------ + +GEN_DATA super_data INPUT_FORMAT:ASCII RESULT_FILE:super_data_%d REPORT_STEPS:1 + +-- ------ --------------------------------------------------------------------------------------------------------------------- +-- ------ ert identifier template file result file parameter distribution +-- ------ --------------------------------------------------------------------------------------------------------------------- +GEN_KW SIGMA ../input/templates/sigma.tmpl coarse.sigma ../input/distributions/sigma.dist + +RUN_TEMPLATE ../input/templates/seed_template.txt seed.txt + +-- ------------------------------------------------------- +-- -- SECTION: Forward models +-- ------------------------------------------------------- + +-- Run Snakeoil + +INSTALL_JOB SNAKE_OIL_SIMULATOR ../../snake_oil/jobs/SNAKE_OIL_SIMULATOR +INSTALL_JOB SNAKE_OIL_NPV ../../snake_oil/jobs/SNAKE_OIL_NPV +INSTALL_JOB SNAKE_OIL_DIFF ../../snake_oil/jobs/SNAKE_OIL_DIFF + +FORWARD_MODEL SNAKE_OIL_SIMULATOR +FORWARD_MODEL SNAKE_OIL_NPV +FORWARD_MODEL SNAKE_OIL_DIFF + +-------------------------------------------------------------------------------------------------- +-- -------------------------------------- +-- -- SECTION: Observations +-- -------------------------------------- + +HISTORY_SOURCE REFCASE_HISTORY + +OBS_CONFIG ../input/observations/obsfiles/observations.txt + +TIME_MAP ../input/refcase/time_map.txt + +-- -------------------------------------- +-- -- SECTION: Plotting +-- -------------------------------------- + +SUMMARY WOPR:PROD +SUMMARY WOPT:PROD +SUMMARY WWPR:PROD +SUMMARY WWCT:PROD +SUMMARY WWPT:PROD +SUMMARY WBHP:PROD +SUMMARY WWIR:INJ +SUMMARY WWIT:INJ +SUMMARY WBHP:INJ +SUMMARY ROE:1 + +-- -------------------------------------- +-- -- SECTION: Workflows +-- -------------------------------------- + +LOAD_WORKFLOW_JOB ../bin/workflows/workflowjobs/UBER_PRINT +LOAD_WORKFLOW ../bin/workflows/MAGIC_PRINT diff --git a/libres/test-data/local/snake_oil_structure/snake_oil/jobs/SNAKE_OIL_DIFF b/libres/test-data/local/snake_oil_structure/snake_oil/jobs/SNAKE_OIL_DIFF new file mode 100644 index 00000000000..98a867d9594 --- /dev/null +++ b/libres/test-data/local/snake_oil_structure/snake_oil/jobs/SNAKE_OIL_DIFF @@ -0,0 +1,4 @@ +STDOUT snake_oil_diff.stdout +STDERR snake_oil_diff.stderr + +EXECUTABLE snake_oil_diff.py \ No newline at end of file diff --git a/libres/test-data/local/snake_oil_structure/snake_oil/jobs/SNAKE_OIL_NPV b/libres/test-data/local/snake_oil_structure/snake_oil/jobs/SNAKE_OIL_NPV new file mode 100644 index 00000000000..887830c756f --- /dev/null +++ b/libres/test-data/local/snake_oil_structure/snake_oil/jobs/SNAKE_OIL_NPV @@ -0,0 +1,4 @@ +STDOUT snake_oil_npv.stdout +STDERR snake_oil_npv.stderr + +EXECUTABLE snake_oil_npv.py \ No newline at end of file diff --git a/libres/test-data/local/snake_oil_structure/snake_oil/jobs/SNAKE_OIL_SIMULATOR b/libres/test-data/local/snake_oil_structure/snake_oil/jobs/SNAKE_OIL_SIMULATOR new file mode 100644 index 00000000000..b4b7f9928fd --- /dev/null +++ b/libres/test-data/local/snake_oil_structure/snake_oil/jobs/SNAKE_OIL_SIMULATOR @@ -0,0 +1,4 @@ +STDOUT snake_oil.stdout +STDERR snake_oil.stderr + +EXECUTABLE snake_oil_simulator.py \ No newline at end of file diff --git a/libres/test-data/local/snake_oil_structure/snake_oil/jobs/snake_oil_diff.py b/libres/test-data/local/snake_oil_structure/snake_oil/jobs/snake_oil_diff.py new file mode 100755 index 00000000000..cdcf98cd7d8 --- /dev/null +++ b/libres/test-data/local/snake_oil_structure/snake_oil/jobs/snake_oil_diff.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +from ecl.summary import EclSum + + +def writeDiff(filename, ecl_sum, key1, key2): + with open(filename, "w") as f: + for v1, v2 in zip(ecl_sum.numpy_vector(key1), ecl_sum.numpy_vector(key2)): + diff = v1 - v2 + f.write("%f\n" % diff) + + +if __name__ == "__main__": + ecl_sum = EclSum("SNAKE_OIL_FIELD") + + report_step = 199 + writeDiff( + "snake_oil_opr_diff_%d.txt" % report_step, ecl_sum, "WOPR:OP1", "WOPR:OP2" + ) + writeDiff( + "snake_oil_wpr_diff_%d.txt" % report_step, ecl_sum, "WWPR:OP1", "WWPR:OP2" + ) + writeDiff( + "snake_oil_gpr_diff_%d.txt" % report_step, ecl_sum, "WGPR:OP1", "WGPR:OP2" + ) diff --git a/libres/test-data/local/snake_oil_structure/snake_oil/jobs/snake_oil_npv.py b/libres/test-data/local/snake_oil_structure/snake_oil/jobs/snake_oil_npv.py new file mode 100755 index 00000000000..e1f4af7fa8b --- /dev/null +++ b/libres/test-data/local/snake_oil_structure/snake_oil/jobs/snake_oil_npv.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python +from ecl.summary import EclSum + +OIL_PRICES = { + "2010-01-01": 78.33, + "2010-02-01": 76.39, + "2010-03-01": 81.20, + "2010-04-01": 84.29, + "2010-05-01": 73.74, + "2010-06-01": 75.34, + "2010-07-01": 76.32, + "2010-08-01": 76.60, + "2010-09-01": 75.24, + "2010-10-01": 81.89, + "2010-11-01": 84.25, + "2010-12-01": 89.15, + "2011-01-01": 89.17, + "2011-02-01": 88.58, + "2011-03-01": 102.86, + "2011-04-01": 109.53, + "2011-05-01": 100.90, + "2011-06-01": 96.26, + "2011-07-01": 97.30, + "2011-08-01": 86.33, + "2011-09-01": 85.52, + "2011-10-01": 86.32, + "2011-11-01": 97.16, + "2011-12-01": 98.56, + "2012-01-01": 100.27, + "2012-02-01": 102.20, + "2012-03-01": 106.16, + "2012-04-01": 103.32, + "2012-05-01": 94.65, + "2012-06-01": 82.30, + "2012-07-01": 87.90, + "2012-08-01": 94.13, + "2012-09-01": 94.51, + "2012-10-01": 89.49, + "2012-11-01": 86.53, + "2012-12-01": 87.86, + "2013-01-01": 94.76, + "2013-02-01": 95.31, + "2013-03-01": 92.94, + "2013-04-01": 92.02, + "2013-05-01": 94.51, + "2013-06-01": 95.77, + "2013-07-01": 104.67, + "2013-08-01": 106.57, + "2013-09-01": 106.29, + "2013-10-01": 100.54, + "2013-11-01": 93.86, + "2013-12-01": 97.63, + "2014-01-01": 94.62, + "2014-02-01": 100.82, + "2014-03-01": 100.80, + "2014-04-01": 102.07, + "2014-05-01": 102.18, + "2014-06-01": 105.79, + "2014-07-01": 103.59, + "2014-08-01": 96.54, + "2014-09-01": 93.21, + "2014-10-01": 84.40, + "2014-11-01": 75.79, + "2014-12-01": 59.29, + "2015-01-01": 47.22, + "2015-02-01": 50.58, + "2015-03-01": 47.82, + "2015-04-01": 54.45, + "2015-05-01": 59.27, + "2015-06-01": 59.82, + "2015-07-01": 50.90, + "2015-08-01": 42.87, + "2015-09-01": 45.48, +} + +if __name__ == "__main__": + ecl_sum = EclSum("SNAKE_OIL_FIELD") + start_time = ecl_sum.getStartTime() + date_ranges = ecl_sum.timeRange(start_time, interval="1M") + production_sums = ecl_sum.blockedProduction("FOPT", date_ranges) + + npv = 0.0 + for index in range(0, len(date_ranges) - 1): + date = date_ranges[index + 1] # end of period + production_sum = production_sums[index] + + oil_price = OIL_PRICES[date.date().strftime("%Y-%m-%d")] + + production_value = oil_price * production_sum + npv += production_value + + with open("snake_oil_npv.txt", "w") as output_file: + output_file.write("NPV %s\n" % npv) + + if npv < 80000: + rating = "POOR" + elif 80000 <= npv < 100000: + rating = "AVERAGE" + elif 100000 <= npv < 120000: + rating = "GOOD" + else: + rating = "EXCELLENT" + + output_file.write("RATING %s\n" % rating) diff --git a/libres/test-data/local/snake_oil_structure/snake_oil/jobs/snake_oil_simulator.py b/libres/test-data/local/snake_oil_structure/snake_oil/jobs/snake_oil_simulator.py new file mode 100755 index 00000000000..105c0bef0db --- /dev/null +++ b/libres/test-data/local/snake_oil_structure/snake_oil/jobs/snake_oil_simulator.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python +from datetime import datetime +import os +import sys + +from ecl.summary import EclSum, EclSumTStep +from ecl.test import ExtendedTestCase + +from res.test.synthesizer import OilSimulator + + +def globalIndex(i, j, k, nx=10, ny=10, nz=10): + return i + nx * (j - 1) + nx * ny * (k - 1) + + +def readParameters(filename): + params = {} + with open(filename, "r") as f: + for line in f: + key, value = line.split(":", 1) + params[key] = value.strip() + + return params + + +def runSimulator(simulator, history_simulator, time_step_count): + """@rtype: EclSum""" + ecl_sum = EclSum.writer("SNAKE_OIL_FIELD", datetime(2010, 1, 1), 10, 10, 10) + + ecl_sum.addVariable("FOPT") + ecl_sum.addVariable("FOPR") + ecl_sum.addVariable("FGPT") + ecl_sum.addVariable("FGPR") + ecl_sum.addVariable("FWPT") + ecl_sum.addVariable("FWPR") + ecl_sum.addVariable("FGOR") + ecl_sum.addVariable("FWCT") + + ecl_sum.addVariable("FOPTH") + ecl_sum.addVariable("FOPRH") + ecl_sum.addVariable("FGPTH") + ecl_sum.addVariable("FGPRH") + ecl_sum.addVariable("FWPTH") + ecl_sum.addVariable("FWPRH") + ecl_sum.addVariable("FGORH") + ecl_sum.addVariable("FWCTH") + + ecl_sum.addVariable("WOPR", wgname="OP1") + ecl_sum.addVariable("WOPR", wgname="OP2") + ecl_sum.addVariable("WWPR", wgname="OP1") + ecl_sum.addVariable("WWPR", wgname="OP2") + ecl_sum.addVariable("WGPR", wgname="OP1") + ecl_sum.addVariable("WGPR", wgname="OP2") + ecl_sum.addVariable("WGOR", wgname="OP1") + ecl_sum.addVariable("WGOR", wgname="OP2") + ecl_sum.addVariable("WWCT", wgname="OP1") + ecl_sum.addVariable("WWCT", wgname="OP2") + + ecl_sum.addVariable("WOPRH", wgname="OP1") + ecl_sum.addVariable("WOPRH", wgname="OP2") + ecl_sum.addVariable("WWPRH", wgname="OP1") + ecl_sum.addVariable("WWPRH", wgname="OP2") + ecl_sum.addVariable("WGPRH", wgname="OP1") + ecl_sum.addVariable("WGPRH", wgname="OP2") + ecl_sum.addVariable("WGORH", wgname="OP1") + ecl_sum.addVariable("WGORH", wgname="OP2") + ecl_sum.addVariable("WWCTH", wgname="OP1") + ecl_sum.addVariable("WWCTH", wgname="OP2") + + ecl_sum.addVariable("BPR", num=globalIndex(5, 5, 5)) + ecl_sum.addVariable("BPR", num=globalIndex(1, 3, 8)) + + time_map = [] + mini_step_count = 10 + total_step_count = time_step_count * mini_step_count + + for report_step in range(time_step_count): + for mini_step in range(mini_step_count): + t_step = ecl_sum.addTStep( + report_step + 1, sim_days=report_step * mini_step_count + mini_step + ) + + time_map.append(t_step.getSimTime().datetime().strftime("%d/%m/%Y")) + + simulator.step(scale=1.0 / total_step_count) + history_simulator.step(scale=1.0 / total_step_count) + + t_step["FOPR"] = simulator.fopr() + t_step["FOPT"] = simulator.fopt() + t_step["FGPR"] = simulator.fgpr() + t_step["FGPT"] = simulator.fgpt() + t_step["FWPR"] = simulator.fwpr() + t_step["FWPT"] = simulator.fwpt() + t_step["FGOR"] = simulator.fgor() + t_step["FWCT"] = simulator.fwct() + + t_step["WOPR:OP1"] = simulator.opr("OP1") + t_step["WOPR:OP2"] = simulator.opr("OP2") + + t_step["WGPR:OP1"] = simulator.gpr("OP1") + t_step["WGPR:OP2"] = simulator.gpr("OP2") + + t_step["WWPR:OP1"] = simulator.wpr("OP1") + t_step["WWPR:OP2"] = simulator.wpr("OP2") + + t_step["WGOR:OP1"] = simulator.gor("OP1") + t_step["WGOR:OP2"] = simulator.gor("OP2") + + t_step["WWCT:OP1"] = simulator.wct("OP1") + t_step["WWCT:OP2"] = simulator.wct("OP2") + + t_step["BPR:5,5,5"] = simulator.bpr("5,5,5") + t_step["BPR:1,3,8"] = simulator.bpr("1,3,8") + + t_step["FOPRH"] = history_simulator.fopr() + t_step["FOPTH"] = history_simulator.fopt() + t_step["FGPRH"] = history_simulator.fgpr() + t_step["FGPTH"] = history_simulator.fgpt() + t_step["FWPRH"] = history_simulator.fwpr() + t_step["FWPTH"] = history_simulator.fwpt() + t_step["FGORH"] = history_simulator.fgor() + t_step["FWCTH"] = history_simulator.fwct() + + t_step["WOPRH:OP1"] = history_simulator.opr("OP1") + t_step["WOPRH:OP2"] = history_simulator.opr("OP2") + + t_step["WGPRH:OP1"] = history_simulator.gpr("OP1") + t_step["WGPRH:OP2"] = history_simulator.gpr("OP2") + + t_step["WWPRH:OP1"] = history_simulator.wpr("OP1") + t_step["WWPRH:OP2"] = history_simulator.wpr("OP2") + + t_step["WGORH:OP1"] = history_simulator.gor("OP1") + t_step["WGORH:OP2"] = history_simulator.gor("OP2") + + t_step["WWCTH:OP1"] = history_simulator.wct("OP1") + t_step["WWCTH:OP2"] = history_simulator.wct("OP2") + + return ecl_sum, time_map + + +def roundedInt(value): + return int(round(float(value))) + + +if __name__ == "__main__": + seed = int(readParameters("seed.txt")["SEED"]) + parameters = readParameters("snake_oil_params.txt") + + op1_divergence_scale = float(parameters["OP1_DIVERGENCE_SCALE"]) + op2_divergence_scale = float(parameters["OP2_DIVERGENCE_SCALE"]) + op1_persistence = float(parameters["OP1_PERSISTENCE"]) + op2_persistence = float(parameters["OP2_PERSISTENCE"]) + op1_offset = float(parameters["OP1_OFFSET"]) + op2_offset = float(parameters["OP2_OFFSET"]) + bpr_138_persistence = float(parameters["BPR_138_PERSISTENCE"]) + bpr_555_persistence = float(parameters["BPR_555_PERSISTENCE"]) + + op1_octaves = roundedInt(parameters["OP1_OCTAVES"]) + op2_octaves = roundedInt(parameters["OP2_OCTAVES"]) + + simulator = OilSimulator() + simulator.addWell( + "OP1", + seed * 997, + persistence=op1_persistence, + octaves=op1_octaves, + divergence_scale=op1_divergence_scale, + offset=op1_offset, + ) + simulator.addWell( + "OP2", + seed * 13, + persistence=op2_persistence, + octaves=op2_octaves, + divergence_scale=op2_divergence_scale, + offset=op2_offset, + ) + simulator.addBlock("5,5,5", seed * 37, persistence=bpr_555_persistence) + simulator.addBlock("1,3,8", seed * 31, persistence=bpr_138_persistence) + + history_simulator = OilSimulator() + history_simulator.addWell("OP1", 222118781) + history_simulator.addWell("OP2", 118116362) + + report_step_count = 200 + ecl_sum, time_map = runSimulator(simulator, history_simulator, report_step_count) + + ecl_sum.fwrite() + + with open("time_map.txt", "w") as f: + for t in time_map: + f.write("%s\n" % t) diff --git a/libres/testjenkins.sh b/libres/testjenkins.sh new file mode 100644 index 00000000000..f9b17037baf --- /dev/null +++ b/libres/testjenkins.sh @@ -0,0 +1,178 @@ +#!/bin/bash +set -x +# This script is designed to be able to run in stages in the Jenkins pipline +# +# You can also run it locally. Note that only the committed code is tested, +# not changes in your working directory. +# +# If you want to run the whole build in one go: +# sh testjenkins.sh build_and_test +# +# If you want to run the build in separate stages: +# sh testjenkins.sh setup +# sh testjenkins.sh build_elc +# etc... +# +# By default it will try to build everything inside a folder 'jenkinsbuild' +# You can override this with the env variable WORKING_DIR e.g.: +# WORKING_DIR=$(mktemp -d) sh testjenkins.sh build_and_test +# +# After https://github.com/equinor/ert/issues/1634 the ERT_SOURCE_ROOT needs to +# point at the ert source root (where .git is). + +build_and_test () { + rm -rf jenkinsbuild + mkdir jenkinsbuild + run setup + run build_libecl + run build_libres + run build_res + run run_ctest + run run_pytest_normal + run run_pytest_equinor +} + +setup () { + run setup_variables + run create_directories + run create_virtualenv + run source_build_tools + run clone_repos +} + +build_libecl () { + run enable_environment + + pushd $LIBECL_BUILD + cmake .. -DCMAKE_INSTALL_PREFIX=$INSTALL + make -j 6 install + popd +} + +build_libres () { + run enable_environment + + pushd $LIBRES_BUILD + cmake ${ERT_SOURCE_ROOT}/libres \ + -DCMAKE_PREFIX_PATH=$INSTALL \ + -DCMAKE_INSTALL_PREFIX=$INSTALL \ + -DBUILD_TESTS=ON \ + -DEQUINOR_TESTDATA_ROOT=/project/res-testdata/ErtTestData + make -j 6 install + popd +} + +build_res () { + run enable_environment + pip install ${ERT_SOURCE_ROOT} + pip install -r dev-requirements.txt +} + +source_build_tools() { + export PATH=/opt/rh/devtoolset-8/root/bin:$PATH + python --version + gcc --version + cmake --version + + set -e +} + +setup_variables () { + ENV=$WORKING_DIR/venv + INSTALL=$WORKING_DIR/install + + LIBECL_ROOT=$WORKING_DIR/libecl + LIBECL_BUILD=$LIBECL_ROOT/build + + LIBRES_ROOT=$WORKING_DIR/_libres + LIBRES_BUILD=$LIBRES_ROOT/build +} + +enable_environment () { + run setup_variables + source $ENV/bin/activate + run source_build_tools + + export ERT_SHOW_BACKTRACE=Y + export RMS_SITE_CONFIG=/prog/res/komodo/bleeding-py36-rhel7/root/lib/python3.6/site-packages/ert_configurations/resources/rms_config.yml +} + +create_directories () { + mkdir $INSTALL +} + +clone_repos () { + echo "Cloning into $LIBECL_ROOT" + git clone https://github.com/equinor/libecl $LIBECL_ROOT + mkdir -p $LIBECL_BUILD + + echo "Cloning into $LIBRES_ROOT" + git clone . $LIBRES_ROOT + mkdir -p $LIBRES_BUILD + ln -s /project/res-testdata/ErtTestData $LIBRES_ROOT/test-data/Equinor +} + +create_virtualenv () { + mkdir $ENV + python3 -m venv $ENV + source $ENV/bin/activate + pip install -U pip wheel setuptools cmake +} + +run_ctest () { + run enable_environment + pushd $LIBRES_BUILD + export ERT_SITE_CONFIG=${ERT_SOURCE_ROOT}/share/ert/site-config + ctest -j $CTEST_JARG -E Lint --output-on-failure + popd +} + +run_pytest_normal () { + run enable_environment + + # Avoid implicitly loaded cwd modules + pushd ${LIBRES_BUILD} + python -m pytest -m "not equinor_test" --durations=10 ${ERT_SOURCE_ROOT}/libres/tests + popd +} + + +run_pytest_equinor () { + ln -s /project/res-testdata/ErtTestData ${ERT_SOURCE_ROOT}/libres/test-data/Equinor + run enable_environment + + # Avoid implicitly loaded cwd modules + pushd ${LIBRES_BUILD} + python -m pytest -m "equinor_test" --durations=10 ${ERT_SOURCE_ROOT}/libres/tests + popd +} + + +run () { + echo "" + echo "----- Running step: $1 -----" + echo "" + $1 + + echo "" + echo "----- $1 finished -----" + echo "" +} + +if [ -z "$CTEST_JARG" ] + then + CTEST_JARG="6" +fi + +if [ -z "$WORKING_DIR" ] + then + WORKING_DIR=$(pwd)/jenkinsbuild + ERT_SOURCE_ROOT=$(pwd) + else + ERT_SOURCE_ROOT=${WORKING_DIR} +fi + +if [ $# -ne 0 ] + then + run $1 +fi diff --git a/libres/tests/__init__.py b/libres/tests/__init__.py new file mode 100644 index 00000000000..84bb443621d --- /dev/null +++ b/libres/tests/__init__.py @@ -0,0 +1,43 @@ +from pathlib import Path +import types +import os.path +import functools +from ecl.util.test import ExtendedTestCase + + +def libres_source_root() -> Path: + src = Path("@CMAKE_CURRENT_SOURCE_DIR@/../..") + if src.is_dir(): + return src.relative_to(Path.cwd()) + + # If the file was not correctly configured by cmake, look for the source + # folder, assuming the build folder is inside the source folder. + test_path = Path(__file__) + while test_path != Path("/"): + if (test_path / ".git").is_dir(): + return test_path / "libres" + test_path = test_path.parent + raise RuntimeError("Cannot find the source folder") + + +class ResTest(ExtendedTestCase): + SOURCE_ROOT = libres_source_root() + TESTDATA_ROOT = SOURCE_ROOT / "test-data" + SHARE_ROOT = SOURCE_ROOT.parent / "share" + EQUINOR_DATA = (TESTDATA_ROOT / "Equinor").is_symlink() + + def assertItemsEqual(self, data1, data2): + if len(data1) != len(data2): + raise AssertionError("Element count not equal.") + + for value in data1: + if not value in data2: + raise AssertionError(value, "not in", data2) + + @classmethod + def createSharePath(cls, path): + if cls.SHARE_ROOT is None: + raise Exception( + "Trying to create directory rooted in 'SHARE_ROOT' - variable 'SHARE_ROOT' is not set." + ) + return os.path.realpath(os.path.join(cls.SHARE_ROOT, path)) diff --git a/libres/tests/conftest.py b/libres/tests/conftest.py new file mode 100644 index 00000000000..f649d75b631 --- /dev/null +++ b/libres/tests/conftest.py @@ -0,0 +1,39 @@ +from tests import libres_source_root +import pytest +import unittest +import resource +import functools +import os + + +@pytest.fixture +def source_root(): + return libres_source_root() + + +def has_equinor_test_data(): + return os.path.isdir(os.path.join(libres_source_root(), "test-data", "Equinor")) + + +def pytest_runtest_setup(item): + if item.get_closest_marker("equinor_test") and not has_equinor_test_data(): + pytest.skip("Test requires Equinor data") + + +def pytest_configure(config): + config.addinivalue_line("markers", "equinor_test") + + +@pytest.fixture(autouse=True) +def env_save(): + environment_pre = [ + (key, val) for key, val in os.environ.items() if key != "PYTEST_CURRENT_TEST" + ] + yield + environment_post = [ + (key, val) for key, val in os.environ.items() if key != "PYTEST_CURRENT_TEST" + ] + if set(environment_pre) != set(environment_post): + raise EnvironmentError( + "Your environment has changed after that test, please reset" + ) diff --git a/libres/tests/ctest_import.py b/libres/tests/ctest_import.py new file mode 100755 index 00000000000..c7892a0ec38 --- /dev/null +++ b/libres/tests/ctest_import.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python +import os +import sys + +PYTHONPATH = sys.argv[1] +sys.path.insert(0, PYTHONPATH) + +from import_tester import ImportTester + +package_name = sys.argv[2] +package_path = os.path.join(PYTHONPATH, package_name) + +if ImportTester.importRecursively(package_path, package_name): + sys.exit(0) +else: + sys.exit(1) diff --git a/libres/tests/ctest_run.py b/libres/tests/ctest_run.py new file mode 100755 index 00000000000..bd5bc553dad --- /dev/null +++ b/libres/tests/ctest_run.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python +import os +import sys + +try: + from unittest2 import TextTestRunner +except ImportError: + from unittest import TextTestRunner + + +def runTestCase(tests, verbosity=0): + test_result = TextTestRunner(verbosity=verbosity).run(tests) + + if len(test_result.errors) or len(test_result.failures): + test_result.printErrors() + return False + else: + return True + + +if __name__ == "__main__": + TEST_PYTHONPATH = sys.argv[1] + os.environ["PYTHONPATH"] = ( + TEST_PYTHONPATH + os.pathsep + os.getenv("PYTHONPATH", "") + ) + for path_element in reversed(TEST_PYTHONPATH.split(os.pathsep)): + sys.path.insert(0, path_element) + + test_class_path = sys.argv[2] + argv = [] + + try: + argv = sys.argv[3:] + except IndexError: + pass + + from ecl.test import ErtTestRunner + + tests = ErtTestRunner.getTestsFromTestClass(test_class_path, argv) + + # Set verbosity to 2 to see which test method in a class that fails. + if runTestCase(tests, verbosity=0): + sys.exit(0) + else: + sys.exit(1) diff --git a/libres/tests/import_tester.py b/libres/tests/import_tester.py new file mode 100644 index 00000000000..47816057545 --- /dev/null +++ b/libres/tests/import_tester.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +import os +import sys +import traceback + + +class ImportTester(object): + @staticmethod + def testImport(module): + try: + if "__pycache__" in str(module): + # python 3 hack + return True + else: + print("Importing module %s." % str(module)) + __import__(module) + return True + except ImportError: + tb = traceback.format_exc() + sys.stderr.write("Error importing module %s!\n\n" % module) + sys.stderr.write(str(tb)) + sys.stderr.write("\n") + except Exception: + tb = traceback.format_exc() + sys.stderr.write("Import of module %s caused errors!\n\n" % module) + sys.stderr.write(str(tb)) + sys.stderr.write("\n") + + return False + + @staticmethod + def importRecursively(path, package_name): + entries = os.listdir(path) + + result = True + + for entry in sorted(entries): + import_success = True + + entry_path = os.path.join(path, entry) + if os.path.isdir(entry_path): + package = "%s.%s" % (package_name, entry) + import_success = ImportTester.testImport(package) + new_path = os.path.join(path, entry) + import_success = import_success and ImportTester.importRecursively( + new_path, package + ) + elif os.path.isfile(entry_path): + if not entry.startswith("__init__") and entry.endswith(".py"): + module = entry[0 : len(entry) - 3] + import_success = ImportTester.testImport( + "%s.%s" % (package_name, module) + ) + else: + # skip other files + pass + # print("Skipped entry: %s" % entry) + + if not import_success: + result = False + + return result + + +if __name__ == "__main__": + PYTHONPATH = sys.argv[1] + package_name = sys.argv[2] + + sys.path.insert(0, PYTHONPATH) + + package_path = os.path.join(PYTHONPATH, package_name) + + if ImportTester.importRecursively(package_path, package_name): + sys.exit(0) + else: + sys.exit(1) diff --git a/libres/tests/job_runner/test_event_reporter.py b/libres/tests/job_runner/test_event_reporter.py new file mode 100644 index 00000000000..05e0b4b563f --- /dev/null +++ b/libres/tests/job_runner/test_event_reporter.py @@ -0,0 +1,142 @@ +import contextlib +import os +import threading +from functools import partial + +from job_runner.job import Job +from job_runner.reporting import Event +from job_runner.reporting.event import ( + _FM_JOB_FAILURE, + _FM_JOB_RUNNING, + _FM_JOB_START, + _FM_JOB_SUCCESS, +) +from job_runner.reporting.message import Exited, Finish, Init, Running, Start +import json + +from tests.utils import _mock_ws_thread + + +def test_report_with_successful_start_message_argument(unused_tcp_port): + host = "localhost" + url = f"ws://{host}:{unused_tcp_port}" + reporter = Event(evaluator_url=url) + job1 = Job({"name": "job1", "stdout": "stdout", "stderr": "stderr"}, 0) + lines = [] + with _mock_ws_thread(host, unused_tcp_port, lines): + reporter.report(Init([job1], 1, 19, ee_id="ee_id", real_id=0, step_id=0)) + reporter.report(Start(job1)) + reporter.report(Finish()) + + assert len(lines) == 1 + event = json.loads(lines[0]) + assert event["type"] == _FM_JOB_START + assert event["source"] == "/ert/ee/ee_id/real/0/step/0/job/0" + assert os.path.basename(event["data"]["stdout"]) == "stdout" + assert os.path.basename(event["data"]["stderr"]) == "stderr" + + +def test_report_with_failed_start_message_argument(unused_tcp_port): + host = "localhost" + url = f"ws://{host}:{unused_tcp_port}" + reporter = Event(evaluator_url=url) + + job1 = Job({"name": "job1", "stdout": "stdout", "stderr": "stderr"}, 0) + + lines = [] + with _mock_ws_thread(host, unused_tcp_port, lines): + reporter.report(Init([job1], 1, 19, ee_id="ee_id", real_id=0, step_id=0)) + + msg = Start(job1).with_error("massive_failure") + + reporter.report(msg) + reporter.report(Finish()) + + assert len(lines) == 2 + event = json.loads(lines[1]) + assert event["type"] == _FM_JOB_FAILURE + assert event["data"]["error_msg"] == "massive_failure" + + +def test_report_with_successful_exit_message_argument(unused_tcp_port): + host = "localhost" + url = f"ws://{host}:{unused_tcp_port}" + reporter = Event(evaluator_url=url) + job1 = Job({"name": "job1", "stdout": "stdout", "stderr": "stderr"}, 0) + + lines = [] + with _mock_ws_thread(host, unused_tcp_port, lines): + reporter.report(Init([job1], 1, 19, ee_id="ee_id", real_id=0, step_id=0)) + reporter.report(Exited(job1, 0)) + reporter.report(Finish().with_error("failed")) + + assert len(lines) == 1 + event = json.loads(lines[0]) + assert event["type"] == _FM_JOB_SUCCESS + + +def test_report_with_failed_exit_message_argument(unused_tcp_port): + host = "localhost" + url = f"ws://{host}:{unused_tcp_port}" + reporter = Event(evaluator_url=url) + job1 = Job({"name": "job1", "stdout": "stdout", "stderr": "stderr"}, 0) + + lines = [] + with _mock_ws_thread(host, unused_tcp_port, lines): + reporter.report(Init([job1], 1, 19, ee_id="ee_id", real_id=0, step_id=0)) + reporter.report(Exited(job1, 1).with_error("massive_failure")) + reporter.report(Finish()) + + assert len(lines) == 1 + event = json.loads(lines[0]) + assert event["type"] == _FM_JOB_FAILURE + assert event["data"]["error_msg"] == "massive_failure" + + +def test_report_with_running_message_argument(unused_tcp_port): + host = "localhost" + url = f"ws://{host}:{unused_tcp_port}" + reporter = Event(evaluator_url=url) + job1 = Job({"name": "job1", "stdout": "stdout", "stderr": "stderr"}, 0) + + lines = [] + with _mock_ws_thread(host, unused_tcp_port, lines): + reporter.report(Init([job1], 1, 19, ee_id="ee_id", real_id=0, step_id=0)) + reporter.report(Running(job1, 100, 10)) + reporter.report(Finish()) + + assert len(lines) == 1 + event = json.loads(lines[0]) + assert event["type"] == _FM_JOB_RUNNING + assert event["data"]["max_memory_usage"] == 100 + assert event["data"]["current_memory_usage"] == 10 + + +def test_report_only_job_running_for_successful_run(unused_tcp_port): + host = "localhost" + url = f"ws://{host}:{unused_tcp_port}" + reporter = Event(evaluator_url=url) + job1 = Job({"name": "job1", "stdout": "stdout", "stderr": "stderr"}, 0) + + lines = [] + with _mock_ws_thread(host, unused_tcp_port, lines): + reporter.report(Init([job1], 1, 19, ee_id="ee_id", real_id=0, step_id=0)) + reporter.report(Running(job1, 100, 10)) + reporter.report(Finish()) + + assert len(lines) == 1 + + +def test_report_with_failed_finish_message_argument(unused_tcp_port): + host = "localhost" + url = f"ws://{host}:{unused_tcp_port}" + reporter = Event(evaluator_url=url) + job1 = Job({"name": "job1", "stdout": "stdout", "stderr": "stderr"}, 0) + + lines = [] + with _mock_ws_thread(host, unused_tcp_port, lines): + reporter.report(Init([job1], 1, 19, ee_id="ee_id", real_id=0, step_id=0)) + reporter.report(Running(job1, 100, 10)) + reporter.report(Finish().with_error("massive_failure")) + + assert len(lines) == 1 diff --git a/libres/tests/job_runner/test_file_reporter.py b/libres/tests/job_runner/test_file_reporter.py new file mode 100644 index 00000000000..de51303c57c --- /dev/null +++ b/libres/tests/job_runner/test_file_reporter.py @@ -0,0 +1,261 @@ +import os +import os.path +from unittest import TestCase + +from job_runner.job import Job +from job_runner.reporting import File +from job_runner.reporting.message import Exited, Finish, Init, Running, Start +from tests.utils import tmpdir + + +class FileReporterTests(TestCase): + def setUp(self): + self.reporter = File(sync_disc_timeout=0) + + @tmpdir(None) + def test_report_with_init_message_argument(self): + r = self.reporter + job1 = Job({"name": "job1", "stdout": "/stdout", "stderr": "/stderr"}, 0) + + r.report(Init([job1], 1, 19)) + + with open(self.reporter.STATUS_file, "r") as f: + self.assertIn( + "Current host", f.readline(), "STATUS file missing expected value" + ) + with open(self.reporter.STATUS_json, "r") as f: + contents = "".join(f.readlines()) + self.assertIn('"name": "job1"', contents, "status.json missing job1") + self.assertIn( + '"status": "Waiting"', contents, "status.json missing Waiting status" + ) + + @tmpdir(None) + def test_report_with_successful_start_message_argument(self): + msg = Start( + Job( + { + "name": "job1", + "stdout": "/stdout.0", + "stderr": "/stderr.0", + "argList": ["--foo", "1", "--bar", "2"], + "executable": "/bin/bash", + }, + 0, + ) + ) + self.reporter.status_dict = self.reporter._init_job_status_dict( + msg.timestamp, 0, [msg.job] + ) + + self.reporter.report(msg) + + with open(self.reporter.STATUS_file, "r") as f: + self.assertIn("job1", f.readline(), "STATUS file missing job1") + with open(self.reporter.LOG_file, "r") as f: + self.assertIn( + "Calling: /bin/bash --foo 1 --bar 2", + f.readline(), + """JOB_LOG file missing executable and arguments""", + ) + + with open(self.reporter.STATUS_json, "r") as f: + contents = "".join(f.readlines()) + self.assertIn( + '"status": "Running"', contents, "status.json missing Running status" + ) + self.assertNotIn('"start_time": null', contents, "start_time not set") + + @tmpdir(None) + def test_report_with_failed_start_message_argument(self): + msg = Start(Job({"name": "job1"}, 0)).with_error("massive_failure") + self.reporter.status_dict = self.reporter._init_job_status_dict( + msg.timestamp, 0, [msg.job] + ) + + self.reporter.report(msg) + + with open(self.reporter.STATUS_file, "r") as f: + self.assertIn( + "EXIT: {}/{}".format(-10, "massive_failure"), + f.readline(), + "STATUS file missing EXIT message", + ) + with open(self.reporter.STATUS_json, "r") as f: + contents = "".join(f.readlines()) + self.assertIn( + '"status": "Failure"', contents, "status.json missing Failure status" + ) + self.assertIn( + '"error": "massive_failure"', + contents, + "status.json missing error message", + ) + self.assertIsNotNone( + self.reporter.status_dict["jobs"][0]["end_time"], + "end_time not set for job1", + ) + + @tmpdir(None) + def test_report_with_successful_exit_message_argument(self): + msg = Exited(Job({"name": "job1"}, 0), 0) + self.reporter.status_dict = self.reporter._init_job_status_dict( + msg.timestamp, 0, [msg.job] + ) + + self.reporter.report(msg) + + with open(self.reporter.STATUS_json, "r") as f: + contents = "".join(f.readlines()) + self.assertIn( + '"status": "Success"', contents, "status.json missing Success status" + ) + + @tmpdir(None) + def test_report_with_failed_exit_message_argument(self): + msg = Exited(Job({"name": "job1"}, 0), 1).with_error("massive_failure") + self.reporter.status_dict = self.reporter._init_job_status_dict( + msg.timestamp, 0, [msg.job] + ) + + self.reporter.report(msg) + + with open(self.reporter.STATUS_file, "r") as f: + self.assertIn("EXIT: {}/{}".format(1, "massive_failure"), f.readline()) + with open(self.reporter.ERROR_file, "r") as f: + contents = "".join(f.readlines()) + self.assertIn("job1", contents, "ERROR file missing job") + self.assertIn( + "massive_failure", + contents, + "ERROR file missing reason", + ) + self.assertIn( + "", + contents, + "ERROR had invalid stderr information", + ) + with open(self.reporter.STATUS_json, "r") as f: + contents = "".join(f.readlines()) + self.assertIn( + '"status": "Failure"', contents, "status.json missing Failure status" + ) + self.assertIn( + '"error": "massive_failure"', + contents, + "status.json missing error message", + ) + self.assertIsNotNone(self.reporter.status_dict["jobs"][0]["end_time"]) + + @tmpdir(None) + def test_report_with_running_message_argument(self): + msg = Running(Job({"name": "job1"}, 0), 100, 10) + self.reporter.status_dict = self.reporter._init_job_status_dict( + msg.timestamp, 0, [msg.job] + ) + + self.reporter.report(msg) + + with open(self.reporter.STATUS_json, "r") as f: + contents = "".join(f.readlines()) + self.assertIn('"status": "Running"', contents, "status.json missing status") + self.assertIn( + '"max_memory_usage": 100', + contents, + "status.json missing max_memory_usage", + ) + self.assertIn( + '"current_memory_usage": 10', + contents, + "status.json missing current_memory_usage", + ) + + @tmpdir(None) + def test_report_with_successful_finish_message_argument(self): + msg = Finish() + self.reporter.status_dict = self.reporter._init_job_status_dict( + msg.timestamp, 0, [] + ) + + self.reporter.report(msg) + + with open(self.reporter.OK_file, "r") as f: + self.assertIn( + "All jobs complete", f.readline(), "OK file missing expected value" + ) + + @tmpdir(None) + def test_dump_error_file_with_stderr(self): + """ + Assert that, in the case of an stderr file, it is included in the XML + that constitutes the ERROR file. + The report system is left out, since this was tested in the fail case. + """ + with open("stderr.out.0", "a") as stderr: + stderr.write("Error:\n") + stderr.write("E_MASSIVE_FAILURE\n") + + self.reporter._dump_error_file( + Job({"name": "job1", "stderr": "stderr.out.0"}, 0), "massive_failure" + ) + + with open(self.reporter.ERROR_file, "r") as f: + contents = "".join(f.readlines()) + self.assertIn( + "E_MASSIVE_FAILURE", contents, "ERROR file missing stderr content" + ) + self.assertIn("", contents, "ERROR missing stderr_file part") + + @tmpdir(None) + def test_old_file_deletion(self): + r = self.reporter + # touch all files that are to be removed + for f in [r.ERROR_file, r.STATUS_file, r.OK_file]: + open(f, "a").close() + + r._delete_old_status_files() + + for f in [r.ERROR_file, r.STATUS_file, r.OK_file]: + self.assertFalse(os.path.isfile(f), "{} was not deleted".format(r)) + + @tmpdir(None) + def test_status_file_is_correct(self): + """The STATUS file is a file to which we append data about jobs as they + are run. So this involves multiple reports, and should be tested as + such. + See https://github.com/equinor/libres/issues/764 + """ + j_1 = Job({"name": "j_1", "executable": "", "argList": []}, 0) + j_2 = Job({"name": "j_2", "executable": "", "argList": []}, 0) + init = Init([j_1, j_2], 1, 1) + start_j_1 = Start(j_1) + exited_j_1 = Exited(j_1, 0) + start_j_2 = Start(j_2) + exited_j_2 = Exited(j_2, 9).with_error("failed horribly") + + for msg in [init, start_j_1, exited_j_1, start_j_2, exited_j_2]: + self.reporter.report(msg) + + expected_j1_line = ( + "{:32}: {start_ts:%H:%M:%S} .... {end_ts:%H:%M:%S} \n".format( # noqa + j_1.name(), start_ts=start_j_1.timestamp, end_ts=exited_j_1.timestamp + ) + ) + expected_j2_line = "{:32}: {start_ts:%H:%M:%S} .... {end_ts:%H:%M:%S} EXIT: {code}/{msg}\n".format( # noqa + j_2.name(), + start_ts=start_j_2.timestamp, + end_ts=exited_j_2.timestamp, + code=exited_j_2.exit_code, + msg=exited_j_2.error_message, + ) + + with open(self.reporter.STATUS_file, "r") as f: + for expected in [ + "Current host", + expected_j1_line, + expected_j2_line, + ]: # noqa + self.assertIn(expected, f.readline()) + + # EOF + self.assertEqual("", f.readline()) diff --git a/libres/tests/job_runner/test_job.py b/libres/tests/job_runner/test_job.py new file mode 100644 index 00000000000..8f4259ed9d0 --- /dev/null +++ b/libres/tests/job_runner/test_job.py @@ -0,0 +1,95 @@ +import os +import sys +from unittest import TestCase + +from tests.utils import tmpdir +from job_runner.reporting.message import Exited, Running, Start +from job_runner.job import Job + +from unittest.mock import patch, PropertyMock + + +class JobTests(TestCase): + @patch("job_runner.job.assert_file_executable") + @patch("job_runner.job.Popen") + @patch("job_runner.job.Process") + @tmpdir(None) + def test_run_with_process_failing( + self, mock_process, mock_popen, mock_assert_file_executable + ): + job = Job({}, 0) + type(mock_process.return_value.memory_info.return_value).rss = PropertyMock( + return_value=10 + ) + mock_process.return_value.wait.return_value = 9 + + run = job.run() + + self.assertIsInstance(next(run), Start, "run did not yield Start message") + self.assertIsInstance(next(run), Running, "run did not yield Running message") + exited = next(run) + self.assertIsInstance(exited, Exited, "run did not yield Exited message") + self.assertEqual(9, exited.exit_code, "Exited message had unexpected exit code") + + with self.assertRaises(StopIteration): + next(run) + + @tmpdir(None) + def test_run_fails_using_exit_bash_builtin(self): + job = Job( + { + "name": "exit 1", + "executable": "/bin/bash", + "stdout": "exit_out", + "stderr": "exit_err", + "argList": ["-c", 'echo "failed with {}" 1>&2 ; exit {}'.format(1, 1)], + }, + 0, + ) + + statuses = list(job.run()) + + self.assertEqual(3, len(statuses), "Wrong statuses count") + self.assertEqual(1, statuses[2].exit_code, "Exited status wrong exit_code") + self.assertEqual( + "Process exited with status code 1", + statuses[2].error_message, + "Exited status wrong error_message", + ) + + @tmpdir(None) + def test_run_with_defined_executable_but_missing(self): + executable = os.path.join(os.getcwd(), "this/is/not/a/file") + job = Job( + { + "name": "TEST_EXECUTABLE_NOT_FOUND", + "executable": executable, + "stdout": "mkdir_out", + "stderr": "mkdir_err", + }, + 0, + ) + + with self.assertRaises(IOError): + for _ in job.run(): + pass + + @tmpdir(None) + def test_run_with_defined_executable_no_exec_bit(self): + non_executable = os.path.join(os.getcwd(), "foo") + with open(non_executable, "a"): + pass + + job = Job( + { + "name": "TEST_EXECUTABLE_NOT_EXECUTABLE", + "executable": non_executable, + "stdout": "mkdir_out", + "stderr": "mkdir_err", + }, + 0, + ) + + with self.assertRaises(IOError): + for _ in job.run(): + pass diff --git a/libres/tests/job_runner/test_job_dispatch.py b/libres/tests/job_runner/test_job_dispatch.py new file mode 100644 index 00000000000..cf627d4611a --- /dev/null +++ b/libres/tests/job_runner/test_job_dispatch.py @@ -0,0 +1,286 @@ +import json +import os +import signal +import stat +import sys +import time +import unittest +import importlib +from subprocess import Popen +from textwrap import dedent + +import psutil +import pytest + +from subprocess import Popen +from job_runner.cli import main, _setup_reporters +from job_runner.reporting.message import Init, Finish +from job_runner.reporting import Event, Interactive +from pytest_asyncio.plugin import _unused_tcp_port + +from tests.utils import tmpdir, wait_until, _mock_ws_thread +from unittest.mock import patch, mock_open + + +class JobDispatchTest(unittest.TestCase): + @tmpdir(None) + def test_terminate_jobs(self): + + # Executes it self recursively and sleeps for 100 seconds + with open("dummy_executable", "w") as f: + f.write( + """#!/usr/bin/env python +import sys, os, time +counter = eval(sys.argv[1]) +if counter > 0: + os.fork() + os.execv(sys.argv[0],[sys.argv[0], str(counter - 1) ]) +else: + time.sleep(100)""" + ) + + executable = os.path.realpath("dummy_executable") + os.chmod("dummy_executable", stat.S_IRWXU | stat.S_IRWXO | stat.S_IRWXG) + + self.job_list = { + "umask": "0002", + "DATA_ROOT": "", + "global_environment": {}, + "global_update_path": {}, + "jobList": [ + { + "name": "dummy_executable", + "executable": executable, + "target_file": None, + "error_file": None, + "start_file": None, + "stdout": "dummy.stdout", + "stderr": "dummy.stderr", + "stdin": None, + "argList": ["3"], + "environment": None, + "exec_env": None, + "license_path": None, + "max_running_minutes": None, + "max_running": None, + "min_arg": 1, + "arg_types": [], + "max_arg": None, + } + ], + "run_id": "", + "ert_pid": "", + } + + with open("jobs.json", "w") as f: + f.write(json.dumps(self.job_list)) + + # macOS doesn't provide /usr/bin/setsid, so we roll our own + with open("setsid", "w") as f: + f.write( + dedent( + """\ + #!/usr/bin/env python + import os + import sys + os.setsid() + os.execvp(sys.argv[1], sys.argv[1:]) + """ + ) + ) + os.chmod("setsid", 0o755) + + job_dispatch_script = importlib.util.find_spec("job_runner.job_dispatch").origin + job_dispatch_process = Popen( + [ + os.getcwd() + "/setsid", + sys.executable, + job_dispatch_script, + os.getcwd(), + ] + ) + + p = psutil.Process(job_dispatch_process.pid) + + # Three levels of processes should spawn 8 children in total + wait_until(lambda: self.assertEqual(len(p.children(recursive=True)), 8)) + + p.terminate() + + wait_until(lambda: self.assertEqual(len(p.children(recursive=True)), 0)) + + os.wait() # allow os to clean up zombie processes + + @tmpdir(None) + def test_job_dispatch_run_subset_specified_as_parmeter(self): + with open("dummy_executable", "w") as f: + f.write( + "#!/usr/bin/env python\n" + "import sys, os\n" + 'filename = "job_{}.out".format(sys.argv[1])\n' + 'f = open(filename, "w")\n' + "f.close()\n" + ) + + executable = os.path.realpath("dummy_executable") + os.chmod("dummy_executable", stat.S_IRWXU | stat.S_IRWXO | stat.S_IRWXG) + + self.job_list = { + "umask": "0002", + "DATA_ROOT": "", + "global_environment": {}, + "global_update_path": {}, + "jobList": [ + { + "name": "job_A", + "executable": executable, + "target_file": None, + "error_file": None, + "start_file": None, + "stdout": "dummy.stdout", + "stderr": "dummy.stderr", + "stdin": None, + "argList": ["A"], + "environment": None, + "exec_env": None, + "license_path": None, + "max_running_minutes": None, + "max_running": None, + "min_arg": 1, + "arg_types": [], + "max_arg": None, + }, + { + "name": "job_B", + "executable": executable, + "target_file": None, + "error_file": None, + "start_file": None, + "stdout": "dummy.stdout", + "stderr": "dummy.stderr", + "stdin": None, + "argList": ["B"], + "environment": None, + "exec_env": None, + "license_path": None, + "max_running_minutes": None, + "max_running": None, + "min_arg": 1, + "arg_types": [], + "max_arg": None, + }, + { + "name": "job_C", + "executable": executable, + "target_file": None, + "error_file": None, + "start_file": None, + "stdout": "dummy.stdout", + "stderr": "dummy.stderr", + "stdin": None, + "argList": ["C"], + "environment": None, + "exec_env": None, + "license_path": None, + "max_running_minutes": None, + "max_running": None, + "min_arg": 1, + "arg_types": [], + "max_arg": None, + }, + ], + "run_id": "", + "ert_pid": "", + } + + with open("jobs.json", "w") as f: + f.write(json.dumps(self.job_list)) + + # macOS doesn't provide /usr/bin/setsid, so we roll our own + with open("setsid", "w") as f: + f.write( + dedent( + """\ + #!/usr/bin/env python + import os + import sys + os.setsid() + os.execvp(sys.argv[1], sys.argv[1:]) + """ + ) + ) + os.chmod("setsid", 0o755) + + job_dispatch_script = importlib.util.find_spec("job_runner.job_dispatch").origin + job_dispatch_process = Popen( + [ + os.getcwd() + "/setsid", + sys.executable, + job_dispatch_script, + os.getcwd(), + "job_B", + "job_C", + ] + ) + + job_dispatch_process.wait() + + assert not os.path.isfile("job_A.out") + assert os.path.isfile("job_B.out") + assert os.path.isfile("job_C.out") + + def test_no_jobs_json_file(self): + with self.assertRaises(IOError): + main(["script.py", os.path.realpath(os.curdir)]) + + @tmpdir(None) + def test_no_json_jobs_json_file(self): + path = os.path.realpath(os.curdir) + jobs_file = os.path.join(path, "jobs.json") + + with open(jobs_file, "w") as f: + f.write("not json") + + with self.assertRaises(OSError): + main(["script.py", path]) + + +@pytest.mark.parametrize( + "is_interactive_run, ee_id", + [(False, None), (False, "1234"), (True, None), (True, "1234")], +) +def test_setup_reporters(is_interactive_run, ee_id): + reporters = _setup_reporters(is_interactive_run, ee_id, "") + + if not is_interactive_run and not ee_id: + assert len(reporters) == 2 + assert not any([isinstance(r, Event) for r in reporters]) + + if not is_interactive_run and ee_id: + assert len(reporters) == 3 + assert any([isinstance(r, Event) for r in reporters]) + + if is_interactive_run and ee_id: + assert len(reporters) == 1 + assert any([isinstance(r, Interactive) for r in reporters]) + + +@tmpdir(None) +def test_job_dispatch_kills_itself_after_unsuccessful_job(unused_tcp_port): + host = "localhost" + port = unused_tcp_port + jobs_json = json.dumps({"ee_id": "_id_", "dispatch_url": f"ws://localhost:{port}"}) + + with patch("job_runner.cli.os") as mock_os, patch( + "job_runner.cli.open", new=mock_open(read_data=jobs_json) + ) as mock_file, patch("job_runner.cli.JobRunner") as mock_runner: + mock_runner.return_value.run.return_value = [ + Init([], 0, 0), + Finish().with_error("overall bad run"), + ] + mock_os.getpgid.return_value = 17 + + with _mock_ws_thread(host, port, []): + main(["script.py", "/foo/bar/baz"]) + + mock_os.killpg.assert_called_with(17, signal.SIGKILL) diff --git a/libres/tests/job_runner/test_job_manager_runtime_kw.py b/libres/tests/job_runner/test_job_manager_runtime_kw.py new file mode 100644 index 00000000000..b75c8fcf7f7 --- /dev/null +++ b/libres/tests/job_runner/test_job_manager_runtime_kw.py @@ -0,0 +1,92 @@ +import json +import os +import os.path +from unittest import TestCase + +from job_runner.reporting.message import Exited, Finish, Start +from job_runner.runner import JobRunner + +from tests.utils import tmpdir + + +class JobManagerTestRuntimeKW(TestCase): + def tearDown(self): + if "DATA_ROOT" in os.environ: + del os.environ["DATA_ROOT"] + + @tmpdir(None) + def test_run_one_job_with_an_integer_arg_is_actually_a_fractional(self): + executable = "echo" + + job_0 = { + "name": "JOB_1", + "executable": executable, + "stdout": "outfile.stdout.1", + "stderr": None, + "argList": ["a_file", "5.12"], + "min_arg": 1, + "max_arg": 2, + "arg_types": ["STRING", "RUNTIME_INT"], + } + + data = {"umask": "0000", "DATA_ROOT": "/path/to/data", "jobList": [job_0]} + + runner = JobRunner(data) + statuses = list(runner.run([])) + starts = [e for e in statuses if isinstance(e, Start)] + + self.assertEqual(1, len(starts), "There should be 1 start message") + self.assertFalse(starts[0].success(), "job should not start with success") + + @tmpdir(None) + def test_run_given_one_job_with_missing_file_and_one_file_present(self): + with open("a_file", "w") as f: + f.write("Hello") + + executable = "echo" + + job_1 = { + "name": "JOB_0", + "executable": executable, + "stdout": "outfile.stdout.0", + "stderr": None, + "argList": ["some_file"], + "min_arg": 1, + "max_arg": 1, + "arg_types": ["RUNTIME_FILE"], + } + + job_0 = { + "name": "JOB_1", + "executable": executable, + "stdout": "outfile.stdout.1", + "stderr": None, + "argList": ["5", "a_file"], + "min_arg": 1, + "max_arg": 2, + "arg_types": ["RUNTIME_INT", "RUNTIME_FILE"], + } + + data = { + "umask": "0000", + "DATA_ROOT": "/path/to/data", + "jobList": [job_0, job_1], + } + + runner = JobRunner(data) + + statuses = list(runner.run([])) + + starts = [e for e in statuses if isinstance(e, Start)] + self.assertEqual(2, len(starts), "There should be 2 start messages") + self.assertTrue(starts[0].success(), "first job should start with success") + self.assertFalse( + starts[1].success(), "second job should not start with success" + ) + + exits = [e for e in statuses if isinstance(e, Exited)] + self.assertEqual(1, len(exits), "There should be 1 exit message") + self.assertTrue(exits[0].success(), "first job should exit with success") + + self.assertEqual(Finish, type(statuses[-1]), "last message should be Finish") + self.assertFalse(statuses[-1].success(), "Finish status should not be success") diff --git a/libres/tests/job_runner/test_jobmanager.py b/libres/tests/job_runner/test_jobmanager.py new file mode 100644 index 00000000000..2e1ee397b4f --- /dev/null +++ b/libres/tests/job_runner/test_jobmanager.py @@ -0,0 +1,269 @@ +import json +import os +import os.path +import stat + +from unittest import TestCase +from job_runner.reporting.message import Exited, Start +from job_runner.runner import JobRunner +from res.job_queue import EnvironmentVarlist, ExtJob, ExtJoblist, ForwardModel +from res.util import SubstitutionList +from tests.utils import tmpdir + +# Test data generated by ForwardModel +JSON_STRING = """ +{ + "DATA_ROOT" : "/path/to/data", + "run_id" : "ERT_RUN_ID", + "umask" : "0000", + "jobList" : [ {"name" : "PERLIN", + "executable" : "perlin.py", + "target_file" : "my_target_file", + "error_file" : "error_file", + "start_file" : "some_start_file", + "stdout" : "perlin.stdoit", + "stderr" : "perlin.stderr", + "stdin" : "intput4thewin", + "argList" : ["-speed","hyper"], + "environment" : {"TARGET" : "flatland"}, + "license_path" : "this/is/my/license/PERLIN", + "max_running_minutes" : 12, + "max_running" : 30 +}, +{"name" : "PERGEN", + "executable" : "pergen.py", + "target_file" : "my_target_file", + "error_file" : "error_file", + "start_file" : "some_start_file", + "stdout" : "perlin.stdoit", + "stderr" : "perlin.stderr", + "stdin" : "intput4thewin", + "argList" : ["-speed","hyper"], + "environment" : {"TARGET" : "flatland"}, + "license_path" : "this/is/my/license/PERGEN", + "max_running_minutes" : 12, + "max_running" : 30 +}] +} +""" + +JSON_STRING_NO_DATA_ROOT = """ +{ + "umask" : "0000", + "jobList" : [] +} +""" + + +def create_jobs_json(job_list, umask="0000"): + return {"umask": umask, "DATA_ROOT": "/path/to/data", "jobList": job_list} + + +class JobRunnerTest(TestCase): + def setUp(self): + self.dispatch_imp = None + if "DATA_ROOT" in os.environ: + del os.environ["DATA_ROOT"] + + if "ERT_RUN_ID" in os.environ: + del os.environ["ERT_RUN_ID"] + + def tearDown(self): + + keys = ( + "KEY_ONE", + "KEY_TWO", + "KEY_THREE", + "KEY_FOUR", + "PATH104", + "DATA_ROOT", + "ERT_RUN_ID", + ) + + for key in keys: + if key in os.environ: + del os.environ[key] + + @tmpdir(None) + def test_missing_joblist_json(self): + with self.assertRaises(KeyError): + JobRunner({"umask": "0000"}) + + @tmpdir(None) + def test_missing_umask_json(self): + with self.assertRaises(KeyError): + JobRunner({"jobList": "[]"}) + + @tmpdir(None) + def test_run_output_rename(self): + job = { + "name": "TEST_JOB", + "executable": "/bin/mkdir", + "stdout": "out", + "stderr": "err", + } + joblist = [job, job, job, job, job] + + jobm = JobRunner(create_jobs_json(joblist)) + + for status in enumerate(jobm.run([])): + if isinstance(status, Start): + self.assertEqual(status.job.std_err, "err.{}".format(status.job.index)) + self.assertEqual(status.job.std_out, "out.{}".format(status.job.index)) + + @tmpdir(None) + def test_run_multiple_ok(self): + joblist = [] + dir_list = ["1", "2", "3", "4", "5"] + for job_index in dir_list: + job = { + "name": "MKDIR", + "executable": "/bin/mkdir", + "stdout": "mkdir_out.{}".format(job_index), + "stderr": "mkdir_err.{}".format(job_index), + "argList": ["-p", "-v", job_index], + } + joblist.append(job) + + jobm = JobRunner(create_jobs_json(joblist)) + + statuses = [s for s in list(jobm.run([])) if isinstance(s, Exited)] + + self.assertEqual(len(statuses), 5) + for status in statuses: + self.assertEqual(status.exit_code, 0) + + for dir_number in dir_list: + self.assertTrue(os.path.isdir(dir_number)) + self.assertTrue(os.path.isfile("mkdir_out.{}".format(dir_number))) + self.assertTrue(os.path.isfile("mkdir_err.{}".format(dir_number))) + self.assertEqual(0, os.path.getsize("mkdir_err.{}".format(dir_number))) + + @tmpdir(None) + def test_run_multiple_fail_only_runs_one(self): + joblist = [] + for index in range(1, 6): + job = { + "name": "exit", + "executable": "/bin/bash", + "stdout": "exit_out", + "stderr": "exit_err", + # produces something on stderr, and exits with + "argList": [ + "-c", + 'echo "failed with {}" 1>&2 ; exit {}'.format(index, index), + ], + } + joblist.append(job) + + jobm = JobRunner(create_jobs_json(joblist)) + + statuses = [s for s in list(jobm.run([])) if isinstance(s, Exited)] + + self.assertEqual(len(statuses), 1) + for i, status in enumerate(statuses): + self.assertEqual(status.exit_code, i + 1) + + @tmpdir(None) + def test_given_global_env_and_update_path_executable_env_is_updated(self): + executable = "./x.py" + outfile = "outfile.stdout.0" + + with open(executable, "w") as f: + f.write("#!/usr/bin/env python\n") + f.write("import os\n") + f.write("print(os.environ['KEY_ONE'])\n") + f.write("print(os.environ['KEY_TWO'])\n") + f.write("print(os.environ['PATH104'])\n") + f.write("print(os.environ['KEY_THREE'])\n") + f.write("print(os.environ['KEY_FOUR'])\n") + os.chmod(executable, stat.S_IEXEC + stat.S_IREAD) + + job = { + "name": "TEST_GET_ENV1", + "executable": executable, + "stdout": outfile, + "stderr": "outfile.stderr.0", + "argList": [], + } + + data = { + "umask": "0000", + "global_environment": { + "KEY_ONE": "FirstValue", + "KEY_TWO": "SecondValue", + "KEY_THREE": "ThirdValue", + "KEY_FOUR": "ThirdValue:FourthValue", + }, + "global_update_path": { + "PATH104": "NewPath", + "KEY_THREE": "FourthValue", + "KEY_FOUR": "FifthValue:SixthValue", + }, + "DATA_ROOT": "/path/to/data", + "jobList": [job], + } + + statuses = list(JobRunner(data).run([])) + + exited_messages = [m for m in statuses if isinstance(m, Exited) and m.success()] + number_of_finished_scripts = len(exited_messages) + self.assertEqual( + number_of_finished_scripts, + 1, + "guard check, script must finish successfully", + ) + + with open(outfile, "r") as out0: + content = list(out0.read().splitlines()) + self.assertEqual(content[0], "FirstValue") + self.assertEqual(content[1], "SecondValue") + self.assertEqual(content[2], "NewPath") + self.assertEqual(content[3], "FourthValue:ThirdValue") + self.assertEqual( + content[4], + "FifthValue:SixthValue:ThirdValue:FourthValue", + """should be a concatenation of the variable from + environment and update_path""", + ) + + @tmpdir(None) + def test_exec_env(self): + with open("exec_env.py", "w") as f: + f.write( + """#!/usr/bin/env python\n +import os +import json +with open("exec_env_exec_env.json") as f: + exec_env = json.load(f) +assert exec_env["TEST_ENV"] == "123" +assert exec_env["NOT_SET"] is None + """ + ) + os.chmod("exec_env.py", stat.S_IEXEC + stat.S_IREAD) + + with open("EXEC_ENV", "w") as f: + f.write("EXECUTABLE exec_env.py\n") + f.write("EXEC_ENV TEST_ENV 123\n") + f.write("EXEC_ENV NOT_SET") + + ext_job = ExtJob("EXEC_ENV", False) + job_list = ExtJoblist() + job_list.add_job("EXEC_ENV", ext_job) + forward_model = ForwardModel(job_list) + forward_model.add_job("EXEC_ENV") + global_args = SubstitutionList() + env_varlist = EnvironmentVarlist() + forward_model.formatted_fprintf( + "run_id", None, "data_root", global_args, 0, env_varlist + ) + + with open("jobs.json", "r") as f: + jobs_json = json.load(f) + + for msg in list(JobRunner(jobs_json).run([])): + if isinstance(msg, Start): + with open("exec_env_exec_env.json") as f: + exec_env = json.load(f) + assert exec_env["TEST_ENV"] == "123" + assert exec_env["NOT_SET"] is None diff --git a/libres/tests/job_runner/test_network_reporter.py b/libres/tests/job_runner/test_network_reporter.py new file mode 100644 index 00000000000..c9aa5115b17 --- /dev/null +++ b/libres/tests/job_runner/test_network_reporter.py @@ -0,0 +1,58 @@ +import sys +from datetime import datetime as dt +from unittest import TestCase + +from job_runner.job import Job +from job_runner.reporting import Network +from job_runner.reporting.message import Exited, Init, Finish + +from unittest.mock import patch + +import pytest + +# Network reporting is disabled temporarily in python/ert_logger/__init__.py +# according to issue #1095. +@pytest.mark.skip(reason="Logging to the network is currently disabled") +class NetworkReporterTests(TestCase): + def setUp(self): + self.reporter = Network() + + @patch("job_runner.reporting.network.requests.post") + def test_init_msg(self, post_mock): + self.reporter.report(Init([], 0, 1)) + + self.assertTrue(post_mock.called) + + @patch("job_runner.reporting.network.requests.post") + def test_failed_job_is_reported(self, post_mock): + self.reporter.start_time = dt.now() + job = Job({"name": "failing job", "executable": "/dev/null", "argList": []}, 0) + + self.reporter.report(Exited(job, 9).with_error("failed")) + _, data = post_mock.call_args + + self.assertTrue(post_mock.called, "post not called for failed Exit") + self.assertIn('"status": "exit"', data["data"], "no exit in data") + self.assertIn('"error": true', data["data"], "no set err flag in data") + + @patch("job_runner.reporting.network.requests.post") + def test_successful_job_not_reported(self, post_mock): + self.reporter.report(Exited(None, 9)) + + self.assertFalse(post_mock.called, "post called on successful Exit") + + @patch("job_runner.reporting.network.requests.post") + def test_successful_forward_model_reported(self, post_mock): + self.reporter.start_time = dt.now() + + self.reporter.report(Finish()) + _, data = post_mock.call_args + + self.assertTrue(post_mock.called, "post not called on OK Finish") + self.assertIn('"status": "OK"', data["data"], "no OK in data") + + @patch("job_runner.reporting.network.requests.post") + def test_failed_forward_model_not_reported(self, post_mock): + self.reporter.report(Finish().with_error("failed")) + + self.assertFalse(post_mock.called, "post called on failed Finish") diff --git a/libres/tests/res/__init__.py b/libres/tests/res/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/tests/res/analysis/__init__.py b/libres/tests/res/analysis/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/tests/res/analysis/test_analysis_module.py b/libres/tests/res/analysis/test_analysis_module.py new file mode 100644 index 00000000000..6a9b17a96c9 --- /dev/null +++ b/libres/tests/res/analysis/test_analysis_module.py @@ -0,0 +1,161 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'test_analysis_module.py' 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. + +import sys + +from tests import ResTest +from res.analysis import ( + AnalysisModule, + AnalysisModuleLoadStatusEnum, + AnalysisModuleOptionsEnum, +) +from ecl.util.enums import RngAlgTypeEnum, RngInitModeEnum +from ecl.util.util.rng import RandomNumberGenerator +from res.util import Matrix +from os.path import basename + + +class AnalysisModuleTest(ResTest): + def setUp(self): + if sys.platform.lower() == "darwin": + self.libname = "rml_enkf.dylib" + else: + self.libname = "rml_enkf.so" + + self.rng = RandomNumberGenerator( + RngAlgTypeEnum.MZRAN, RngInitModeEnum.INIT_DEFAULT + ) + + def createAnalysisModule(self): + return AnalysisModule(lib_name=self.libname) + + def test_load_status_enum(self): + source_file_path = "lib/include/ert/analysis/analysis_module.hpp" + self.assertEnumIsFullyDefined( + AnalysisModuleLoadStatusEnum, + "analysis_module_load_status_enum", + source_file_path, + ) + + def test_analysis_module(self): + am = self.createAnalysisModule() + + self.assertEqual(basename(am.getLibName()), self.libname) + + self.assertFalse(am.getInternal()) + + self.assertTrue(am.setVar("ITER", "1")) + + self.assertEqual(am.getTableName(), "analysis_table") + + self.assertTrue(am.checkOption(AnalysisModuleOptionsEnum.ANALYSIS_ITERABLE)) + + self.assertTrue(am.hasVar("ITER")) + + self.assertIsInstance(am.getDouble("ENKF_TRUNCATION"), float) + + self.assertIsInstance(am.getInt("ITER"), int) + + def test_set_get_var(self): + mod = AnalysisModule(name="STD_ENKF") + with self.assertRaises(KeyError): + mod.setVar("NO-NOT_THIS_KEY", 100) + + with self.assertRaises(KeyError): + mod.getInt("NO-NOT_THIS_KEY") + + def test_create_internal(self): + with self.assertRaises(KeyError): + mod = AnalysisModule(name="STD_ENKFXXX") + + mod = AnalysisModule(name="STD_ENKF") + + def test_initX_enkf_linalg_lowrankCinv(self): + """Test AnalysisModule.initX with EE=False and GE=False""" + mod = AnalysisModule(name="STD_ENKF") + A, S, R, dObs, E, D = self._n_identity_mcs() + self.assertFalse(mod.getBool("USE_EE")) + self.assertFalse(mod.getBool("USE_GE")) + + elt_a, elt_b = 1.222, -0.111 + vals = (elt_a, elt_b, elt_b, elt_b, elt_a, elt_b, elt_b, elt_b, elt_a) + expected = self.construct_matrix(3, vals) + + X = mod.initX(A, S, R, dObs, E, D, self.rng) + self._matrix_close(X, expected) + + def test_initX_enkf_linalg_lowrank_EE(self): + """Test AnalysisModule.initX with EE=True and GE=False""" + mod = AnalysisModule(name="STD_ENKF") + A, S, R, dObs, E, D = self._n_identity_mcs() + mod.setVar("USE_EE", True) + self.assertTrue(mod.getBool("USE_EE")) + self.assertFalse(mod.getBool("USE_GE")) + + elt_a, elt_b = 1.33, -0.167 + vals = (elt_a, elt_b, elt_b, elt_b, elt_a, elt_b, elt_b, elt_b, elt_a) + expected = self.construct_matrix(3, vals) + X = mod.initX(A, S, R, dObs, E, D, self.rng) + self._matrix_close(X, expected) + + def test_initX_subspace_inversion_algorithm(self): + """Test AnalysisModule.initX with EE=True and GE=True, the subspace inversion algorithm""" + mod = AnalysisModule(name="STD_ENKF") + A, S, R, dObs, E, D = self._n_identity_mcs() + + mod.setVar("USE_EE", True) + mod.setVar("USE_GE", True) + self.assertTrue(mod.getBool("USE_EE")) + self.assertTrue(mod.getBool("USE_GE")) + + elt_a, elt_b = 1.33, -0.167 + vals = (elt_a, elt_b, elt_b, elt_b, elt_a, elt_b, elt_b, elt_b, elt_a) + expected = self.construct_matrix(3, vals) + X = mod.initX(A, S, R, dObs, E, D, self.rng) + self._matrix_close(X, expected) + + def construct_matrix(self, n, vals): + """Constructs n*n matrix with vals as entries""" + self.assertEqual(n * n, len(vals)) + m = Matrix(n, n) + idx = 0 + for i in range(n): + for j in range(n): + m[(i, j)] = vals[idx] + idx += 1 + return m + + def _n_identity_mcs(self, n=6, s=3): + """return n copies of the identity matrix on s*s elts""" + return tuple([Matrix.identity(s) for i in range(n)]) + + def _matrix_close(self, m1, m2, epsilon=0.01): + """Check that matrices m1 and m2 are of same dimension and that they are + pointwise within epsilon difference.""" + + c = m1.columns() + r = m1.rows() + self.assertEqual(c, m2.columns(), "Number of columns for m1 differ from m2") + self.assertEqual(r, m2.rows(), "Number of rows for m1 differ from m2") + for i in range(0, c): + for j in range(0, r): + pos = (i, j) + diff = abs(m1[pos] - m2[pos]) + self.assertTrue( + diff <= epsilon, + "Matrices differ at (i,j) = (%d,%d). %f != %f" + % (i, j, m1[pos], m2[pos]), + ) diff --git a/libres/tests/res/analysis/test_linalg.py b/libres/tests/res/analysis/test_linalg.py new file mode 100644 index 00000000000..eaae350b525 --- /dev/null +++ b/libres/tests/res/analysis/test_linalg.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python +# Copyright (C) 2015 Equinor ASA, Norway. +# +# The file 'test_linalg.py' 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. + + +from res.enkf import ObsVector +from res.util import Matrix +from res.analysis import Linalg +from tests import ResTest + + +class LinalgTest(ResTest): + def test_num_PC(self): + S = Matrix(3, 3) + S[0, 0] = 1 + S[1, 1] = 1 + S[2, 2] = 1 + + with self.assertRaises(ValueError): + num_pc = Linalg.numPC(S, 0) + + with self.assertRaises(ValueError): + num_pc = Linalg.numPC(S, 1.5) + + num_pc = Linalg.numPC(S, 0.20) + self.assertEqual(num_pc, 1) + + num_pc = Linalg.numPC(S, 0.50) + self.assertEqual(num_pc, 2) + + num_pc = Linalg.numPC(S, 0.80) + self.assertEqual(num_pc, 3) diff --git a/libres/tests/res/analysis/test_options_enum.py b/libres/tests/res/analysis/test_options_enum.py new file mode 100644 index 00000000000..6b93ca537bd --- /dev/null +++ b/libres/tests/res/analysis/test_options_enum.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'test_options_enum.py' 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. + +from tests import ResTest +from res.analysis import AnalysisModuleOptionsEnum + + +class AnalysisOptionsEnumTest(ResTest): + def test_items(self): + source_file_path = "lib/include/ert/analysis/analysis_module.hpp" + self.assertEnumIsFullyDefined( + AnalysisModuleOptionsEnum, "analysis_module_flag_enum", source_file_path + ) diff --git a/libres/tests/res/analysis/test_rml.py b/libres/tests/res/analysis/test_rml.py new file mode 100644 index 00000000000..f96c9804bdf --- /dev/null +++ b/libres/tests/res/analysis/test_rml.py @@ -0,0 +1,138 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'test_analysis_module.py' 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. + +import random +import sys + +from tests import ResTest +from ecl.util.enums import RngAlgTypeEnum, RngInitModeEnum +from ecl.util.util import BoolVector, RandomNumberGenerator +from res.util import Matrix +from res.analysis import ( + AnalysisModule, + AnalysisModuleLoadStatusEnum, + AnalysisModuleOptionsEnum, +) +from res.enkf import MeasData, ObsData + +from tests.utils import tmpdir + + +def forward_model(params, model_error=False): + # Y = A*p[0] + B*p[1] + A = 2 + B = -1 + C = 1 + D = 0.1 + state = [A * params[0] + B * params[1], C * params[0] - D * params[1] * params[0]] + return state + + +def measure(state): + return 0.25 * state[0] - 0.1 * state[1] * state[1] + + +def init_matrices(ens, mask, obs, rng): + state_size = 2 + report_step = 5 + meas_data = MeasData(mask) + meas_block = meas_data.addBlock("OBS", report_step, len(obs)) + + A = Matrix(state_size, mask.countEqual(True)) + active_iens = 0 + for iens, params in enumerate(ens): + if mask[iens]: + state = forward_model(params) + meas_block[0, iens] = measure(state) + + A[0, active_iens] = params[0] + A[1, active_iens] = params[1] + + active_iens += 1 + + S = meas_data.createS() + + obs_data = ObsData() + obs_block = obs_data.addBlock("OBS", 1) + for iobs, obs_value in enumerate(obs): + obs_block[iobs] = obs_value + + R = obs_data.createR() + dObs = obs_data.createDObs() + E = obs_data.createE(rng, meas_data.getActiveEnsSize()) + D = obs_data.createD(E, S) + + obs_data.scale(S, E=E, D=D, R=R, D_obs=dObs) + return (A, S, E, D, R, dObs) + + +class RMLTest(ResTest): + def setUp(self): + if sys.platform.lower() == "darwin": + self.libname = "rml_enkf.dylib" + else: + self.libname = "rml_enkf.so" + self.user = "TEST" + + def createAnalysisModule(self): + rng = RandomNumberGenerator(RngAlgTypeEnum.MZRAN, RngInitModeEnum.INIT_DEFAULT) + return AnalysisModule(lib_name=self.libname) + + def test_load_status_enum(self): + source_file_path = "lib/include/ert/analysis/analysis_module.hpp" + self.assertEnumIsFullyDefined( + AnalysisModuleLoadStatusEnum, + "analysis_module_load_status_enum", + source_file_path, + ) + + @tmpdir() + def test_analysis_module(self): + + rng = RandomNumberGenerator() + module = self.createAnalysisModule() + ens_size = 12 + obs_size = 1 + state_size = 2 + + true_params = [1.25, 0.75] + true_state = forward_model(true_params) + obs = [(measure(true_state), 0.75)] + A = Matrix(state_size, ens_size) + + ens = [] + for iens in range(ens_size): + param = [random.gauss(1.00, 1.00), random.gauss(1.00, 1.00)] + ens.append(param) + + ens_mask = BoolVector(default_value=True, initial_size=ens_size) + ens_mask[2] = False + obs_mask = BoolVector() + obs_mask[0] = 1 + (A, S, E, D, R, dObs) = init_matrices(ens, ens_mask, obs, rng) + + module.initUpdate(ens_mask, obs_mask, S, R, dObs, E, D, rng) + module.updateA(A, S, R, dObs, E, D, rng) + + ens_mask[10] = False + ens_mask[5] = False + (A, S, E, D, R, dObs) = init_matrices(ens, ens_mask, obs, rng) + self.assertEqual(S.dims(), (obs_size, ens_mask.countEqual(True))) + self.assertEqual(E.dims(), (obs_size, ens_mask.countEqual(True))) + self.assertEqual(D.dims(), (obs_size, ens_mask.countEqual(True))) + + module.initUpdate(ens_mask, obs_mask, S, R, dObs, E, D, rng) + module.updateA(A, S, R, dObs, E, D, rng) diff --git a/libres/tests/res/analysis/test_std_enkf.py b/libres/tests/res/analysis/test_std_enkf.py new file mode 100644 index 00000000000..98a566da9dd --- /dev/null +++ b/libres/tests/res/analysis/test_std_enkf.py @@ -0,0 +1,53 @@ +# Copyright (C) 2016 Equinor ASA, Norway. +# +# The file 'test_analysis_module.py' 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. + +from tests import ResTest +from res.analysis import ( + AnalysisModule, + AnalysisModuleLoadStatusEnum, + AnalysisModuleOptionsEnum, +) + +from ecl.util.enums import RngAlgTypeEnum, RngInitModeEnum +from ecl.util.util.rng import RandomNumberGenerator + + +class StdEnKFTest(ResTest): + def setUp(self): + self.rng = RandomNumberGenerator( + RngAlgTypeEnum.MZRAN, RngInitModeEnum.INIT_DEFAULT + ) + self.module = AnalysisModule(name="STD_ENKF") + + def toggleKey(self, key): + self.assertTrue(self.module.hasVar(key)) + + # check it is true + self.assertTrue(self.module.setVar(key, True)) + self.assertTrue(self.module.getBool(key)) + + # set it to false + self.assertTrue(self.module.setVar(key, False)) + self.assertFalse(self.module.getBool(key)) + + def test_EE_option(self): + self.toggleKey("USE_EE") + + def test_GE_option(self): + self.toggleKey("USE_GE") + + def test_scaledata_option(self): + self.toggleKey("ANALYSIS_SCALE_DATA") diff --git a/libres/tests/res/analysis/test_std_enkf_debug.py b/libres/tests/res/analysis/test_std_enkf_debug.py new file mode 100644 index 00000000000..a4ab8353334 --- /dev/null +++ b/libres/tests/res/analysis/test_std_enkf_debug.py @@ -0,0 +1,74 @@ +# Copyright (C) 2016 Equinor ASA, Norway. +# +# The file 'test_analysis_module.py' 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. +import sys +import os + +import pytest + +from tests import ResTest +from res.analysis import ( + AnalysisModule, + AnalysisModuleLoadStatusEnum, + AnalysisModuleOptionsEnum, +) +from ecl.util.enums import RngAlgTypeEnum, RngInitModeEnum +from ecl.util.util.rng import RandomNumberGenerator + + +def find_file(path, filter): + import fnmatch + + for root, dirnames, filenames in os.walk(path): + for filename in fnmatch.filter(filenames, filter): + return os.path.join(root, filename) + return None + + +class StdEnKFDebugTest(ResTest): + def setUp(self): + self.rng = RandomNumberGenerator( + RngAlgTypeEnum.MZRAN, RngInitModeEnum.INIT_DEFAULT + ) + if sys.platform.lower() == "darwin": + lib_name = "std_enkf_debug.dylib" + else: + lib_name = "std_enkf_debug.so" + self.libname = find_file(self.SOURCE_ROOT, lib_name) + if self.libname: + self.module = AnalysisModule(lib_name=self.libname) + else: + pytest.skip("Debug library not found") + + def toggleKey(self, key): + self.assertTrue(self.module.hasVar(key)) + + # check it is true + self.assertTrue(self.module.setVar(key, True)) + self.assertTrue(self.module.getBool(key)) + + # set it to false + self.assertTrue(self.module.setVar(key, False)) + self.assertFalse(self.module.getBool(key)) + + def test_EE_option(self): + self.toggleKey("USE_EE") + + def test_scaledata_option(self): + self.toggleKey("ANALYSIS_SCALE_DATA") + + def test_prefix(self): + self.assertTrue(self.module.hasVar("PREFIX")) + self.assertTrue(self.module.setVar("PREFIX", "Path")) diff --git a/libres/tests/res/config/__init__.py b/libres/tests/res/config/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/tests/res/config/test_config.py b/libres/tests/res/config/test_config.py new file mode 100755 index 00000000000..e6000cc3005 --- /dev/null +++ b/libres/tests/res/config/test_config.py @@ -0,0 +1,445 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'test_config.py' 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. +import os + +from cwrap import Prototype, clib +from ecl.util.test import TestAreaContext +from tests import ResTest + +from res import ResPrototype +from res.config import UnrecognizedEnum, SchemaItem +from res.config import ContentTypeEnum, ContentItem, ContentNode +from res.config import ConfigContent, ConfigParser, ConfigSettings + + +class TestConfigPrototype(ResPrototype): + def __init__(self, prototype, bind=False): + super(TestConfigPrototype, self).__init__(prototype, bind=bind) + + +# Adding extra functions to the ConfigContent object for the ability +# to test low level C functions which are not exposed in Python. +_safe_iget = TestConfigPrototype( + "char* config_content_safe_iget(config_content, char*, int, int)" +) +_iget = TestConfigPrototype( + "char* config_content_iget(config_content, char*, int, int)" +) +_iget_as_int = TestConfigPrototype( + "int config_content_iget_as_int(config_content, char*, int, int)" +) +_iget_as_bool = TestConfigPrototype( + "bool config_content_iget_as_bool(config_content, char*, int, int)" +) +_iget_as_double = TestConfigPrototype( + "double config_content_iget_as_double(config_content, char*, int, int)" +) +_get_occurences = TestConfigPrototype( + "int config_content_get_occurences(config_content, char*)" +) + + +class ConfigTest(ResTest): + def setUp(self): + self.file_list = [] + + def test_enums(self): + source_file_path = "lib/include/ert/config/config_schema_item.hpp" + self.assertEnumIsFullyDefined( + ContentTypeEnum, "config_item_types", source_file_path + ) + self.assertEnumIsFullyDefined( + UnrecognizedEnum, "config_schema_unrecognized_enum", source_file_path + ) + + def test_item_types(self): + with TestAreaContext("config/types") as test_area: + with open("config", "w") as f: + f.write("TYPE_ITEM 10 3.14 TruE String file\n") + + conf = ConfigParser() + self.assertEqual(0, len(conf)) + schema_item = conf.add("TYPE_ITEM", False) + schema_item.iset_type(0, ContentTypeEnum.CONFIG_INT) + schema_item.iset_type(1, ContentTypeEnum.CONFIG_FLOAT) + schema_item.iset_type(2, ContentTypeEnum.CONFIG_BOOL) + schema_item.iset_type(3, ContentTypeEnum.CONFIG_STRING) + schema_item.iset_type(4, ContentTypeEnum.CONFIG_PATH) + self.assertEqual(1, len(conf)) + self.assertNotIn("TYPE_XX", conf) + self.assertIn("TYPE_ITEM", conf) + + content = conf.parse("config") + type_item = content["TYPE_ITEM"][0] + int_value = type_item[0] + self.assertEqual(int_value, 10) + self.assertEqual(type_item.igetString(0), "10") + + float_value = type_item[1] + self.assertEqual(float_value, 3.14) + self.assertEqual(type_item.igetString(1), "3.14") + + bool_value = type_item[2] + self.assertEqual(bool_value, True) + self.assertEqual(type_item.igetString(2), "TruE") + + string_value = type_item[3] + self.assertEqual(string_value, "String") + self.assertEqual(type_item.igetString(3), "String") + + path_value = type_item[4] + self.assertEqual(path_value, "file") + self.assertEqual(type_item.igetString(4), "file") + + # test __getitem__ + self.assertTrue(conf["TYPE_ITEM"]) + with self.assertRaises(KeyError): + _ = conf["TYPE_XX"] + + self.assertIn("ConfigParser", repr(conf)) + self.assertIn("size=1", repr(conf)) + + def test_parse(self): + conf = ConfigParser() + conf.add("FIELD", False) + schema_item = conf.add("RSH_HOST", False) + self.assertIsInstance(schema_item, SchemaItem) + test_path = self.createTestPath("local/config/simple_config") + content = conf.parse( + test_path, unrecognized=UnrecognizedEnum.CONFIG_UNRECOGNIZED_IGNORE + ) + self.assertTrue(content.isValid()) + + content_item = content["RSH_HOST"] + self.assertIsInstance(content_item, ContentItem) + self.assertEqual(len(content_item), 1) + with self.assertRaises(TypeError): + content_item["BJARNE"] + + with self.assertRaises(IndexError): + content_item[10] + + content_node = content_item[0] + self.assertIsInstance(content_node, ContentNode) + self.assertEqual(len(content_node), 2) + self.assertEqual(content_node[1], "be-lx633214:2") + self.assertEqual(content_node.content(sep=","), "be-lx655082:2,be-lx633214:2") + self.assertEqual(content_node.content(), "be-lx655082:2 be-lx633214:2") + + content_item = content["FIELD"] + self.assertEqual(len(content_item), 5) + with self.assertRaises(IOError): + conf.parse("DoesNotExits") + + def test_parse_invalid(self): + conf = ConfigParser() + conf.add("INT", value_type=ContentTypeEnum.CONFIG_INT) + with TestAreaContext("config/parse2"): + with open("config", "w") as fileH: + fileH.write("INT xx\n") + + with self.assertRaises(ValueError): + conf.parse("config") + + content = conf.parse("config", validate=False) + self.assertFalse(content.isValid()) + self.assertEqual(len(content.getErrors()), 1) + + def test_parse_deprecated(self): + conf = ConfigParser() + item = conf.add("INT", value_type=ContentTypeEnum.CONFIG_INT) + msg = "ITEM INT IS DEPRECATED" + item.setDeprecated(msg) + with TestAreaContext("config/parse2"): + with open("config", "w") as fileH: + fileH.write("INT 100\n") + + content = conf.parse("config") + self.assertTrue(content.isValid()) + + warnings = content.getWarnings() + self.assertEqual(len(warnings), 1) + self.assertEqual(warnings[0], msg) + + def test_parse_dotdot_relative(self): + conf = ConfigParser() + schema_item = conf.add("EXECUTABLE", False) + schema_item.iset_type(0, ContentTypeEnum.CONFIG_PATH) + + with TestAreaContext("config/parse_dotdot"): + os.makedirs("cwd/jobs") + os.makedirs("eclipse/bin") + script_path = os.path.join(os.getcwd(), "eclipse/bin/script.sh") + with open(script_path, "w") as f: + f.write("This is a test script") + + with open("cwd/jobs/JOB", "w") as fileH: + fileH.write("EXECUTABLE ../../eclipse/bin/script.sh\n") + + os.makedirs("cwd/ert") + os.chdir("cwd/ert") + content = conf.parse("../jobs/JOB") + item = content["EXECUTABLE"] + node = item[0] + self.assertEqual(script_path, node.getPath()) + + def test_parser_content(self): + conf = ConfigParser() + conf.add("KEY2", False) + schema_item = conf.add("KEY", False) + schema_item.iset_type(2, ContentTypeEnum.CONFIG_INT) + schema_item.iset_type(3, ContentTypeEnum.CONFIG_BOOL) + schema_item.iset_type(4, ContentTypeEnum.CONFIG_FLOAT) + schema_item.iset_type(5, ContentTypeEnum.CONFIG_PATH) + schema_item = conf.add("NOT_IN_CONTENT", False) + + with TestAreaContext("config/parse2"): + with open("config", "w") as fileH: + fileH.write("KEY VALUE1 VALUE2 100 True 3.14 path/file.txt\n") + + cwd0 = os.getcwd() + os.makedirs("tmp") + os.chdir("tmp") + content = conf.parse("../config") + d = content.as_dict() + self.assertTrue(content.isValid()) + self.assertTrue("KEY" in content) + self.assertFalse("NOKEY" in content) + self.assertEqual(cwd0, content.get_config_path()) + + keys = content.keys() + self.assertEqual(len(keys), 1) + self.assertIn("KEY", keys) + d = content.as_dict() + self.assertIn("KEY", d) + item_list = d["KEY"] + self.assertEqual(len(item_list), 1) + l = item_list[0] + self.assertEqual(l[0], "VALUE1") + self.assertEqual(l[1], "VALUE2") + self.assertEqual(l[2], 100) + self.assertEqual(l[3], True) + self.assertEqual(l[4], 3.14) + self.assertEqual(l[5], "../path/file.txt") + + self.assertFalse("NOT_IN_CONTENT" in content) + item = content["NOT_IN_CONTENT"] + self.assertEqual(len(item), 0) + + with self.assertRaises(KeyError): + content["Nokey"] + + item = content["KEY"] + self.assertEqual(len(item), 1) + + line = item[0] + with self.assertRaises(TypeError): + line.getPath(4) + + with self.assertRaises(TypeError): + line.getPath() + + rel_path = line.getPath(index=5, absolute=False) + self.assertEqual(rel_path, "../path/file.txt") + get = line[5] + self.assertEqual(get, "../path/file.txt") + abs_path = line.getPath(index=5) + self.assertEqual(abs_path, os.path.join(cwd0, "path/file.txt")) + + rel_path = line.getPath(index=5, absolute=False, relative_start="../") + self.assertEqual(rel_path, "path/file.txt") + + with self.assertRaises(IndexError): + item[10] + + node = item[0] + self.assertEqual(len(node), 6) + with self.assertRaises(IndexError): + node[6] + + self.assertEqual(node[0], "VALUE1") + self.assertEqual(node[1], "VALUE2") + self.assertEqual(node[2], 100) + self.assertEqual(node[3], True) + self.assertEqual(node[4], 3.14) + + self.assertEqual(content.getValue("KEY", 0, 1), "VALUE2") + self.assertEqual(_iget(content, "KEY", 0, 1), "VALUE2") + + self.assertEqual(content.getValue("KEY", 0, 2), 100) + self.assertEqual(_iget_as_int(content, "KEY", 0, 2), 100) + + self.assertEqual(content.getValue("KEY", 0, 3), True) + self.assertEqual(_iget_as_bool(content, "KEY", 0, 3), True) + + self.assertEqual(content.getValue("KEY", 0, 4), 3.14) + self.assertEqual(_iget_as_double(content, "KEY", 0, 4), 3.14) + + self.assertIsNone(_safe_iget(content, "KEY2", 0, 0)) + + self.assertEqual(_get_occurences(content, "KEY2"), 0) + self.assertEqual(_get_occurences(content, "KEY"), 1) + self.assertEqual(_get_occurences(content, "MISSING-KEY"), 0) + + def test_schema(self): + schema_item = SchemaItem("TestItem") + self.assertIsInstance(schema_item, SchemaItem) + self.assertEqual(schema_item.iget_type(6), ContentTypeEnum.CONFIG_STRING) + schema_item.iset_type(0, ContentTypeEnum.CONFIG_INT) + self.assertEqual(schema_item.iget_type(0), ContentTypeEnum.CONFIG_INT) + schema_item.set_argc_minmax(3, 6) + + del schema_item + + def test_settings(self): + cs = ConfigSettings("SETTINGS") + + with self.assertRaises(TypeError): + cs.addSetting("SETTING1", ContentTypeEnum.CONFIG_INT, 100.67) + + self.assertFalse("SETTING1" in cs) + cs.addSetting("SETTING2", ContentTypeEnum.CONFIG_INT, 100) + self.assertTrue("SETTING2" in cs) + + with self.assertRaises(KeyError): + value = cs["NO_SUCH_KEY"] + + self.assertEqual(cs["SETTING2"], 100) + + with self.assertRaises(KeyError): + cs["NO_SUCH_KEY"] = 100 + + parser = ConfigParser() + cs = ConfigSettings("SETTINGS") + cs.addSetting("A", ContentTypeEnum.CONFIG_INT, 1) + cs.addSetting("B", ContentTypeEnum.CONFIG_INT, 1) + cs.addSetting("C", ContentTypeEnum.CONFIG_INT, 1) + cs.initParser(parser) + + with TestAreaContext("config/parse3"): + with open("config", "w") as fileH: + fileH.write("SETTINGS A 100\n") + fileH.write("SETTINGS B 200\n") + fileH.write("SETTINGS C 300\n") + + content = parser.parse("config") + cs.apply(content) + self.assertEqual(cs["A"], 100) + self.assertEqual(cs["B"], 200) + self.assertEqual(cs["C"], 300) + + keys = cs.keys() + self.assertTrue("A" in keys) + self.assertTrue("B" in keys) + self.assertTrue("C" in keys) + self.assertEqual(len(keys), 3) + + cs = ConfigSettings("SETTINGS") + cs.addDoubleSetting("A", 1.0) + self.assertEqual(ContentTypeEnum.CONFIG_FLOAT, cs.getType("A")) + + cs = ConfigSettings("SETTINGS") + cs.addDoubleSetting("A", 1.0) + cs.addIntSetting("B", 1) + cs.addStringSetting("C", "1") + cs.addBoolSetting("D", True) + cs.initParser(parser) + + with TestAreaContext("config/parse4"): + with open("config", "w") as fileH: + fileH.write("SETTINGS A 100.1\n") + fileH.write("SETTINGS B 200\n") + fileH.write("SETTINGS C 300\n") + fileH.write("SETTINGS D False\n") + + content = parser.parse("config") + + cs.apply(content) + self.assertEqual(cs["A"], 100.1) + self.assertEqual(cs["B"], 200) + self.assertEqual(cs["C"], "300") + self.assertEqual(cs["D"], False) + + with self.assertRaises(Exception): + cs["A"] = "Hei" + + def test_add_unknown_keyowrds(self): + parser = ConfigParser() + with TestAreaContext("config/parse4"): + with open("config", "w") as fileH: + fileH.write("SETTINGS A 100.1\n") + fileH.write("SETTINGS B 200 STRING1 STRING2\n") + fileH.write("SETTINGS C 300\n") + fileH.write("SETTINGS D False\n") + + content = parser.parse( + "config", unrecognized=UnrecognizedEnum.CONFIG_UNRECOGNIZED_ADD + ) + + self.assertIn("SETTINGS", content) + item = content["SETTINGS"] + self.assertEqual(len(item), 4) + + nodeA = item[0] + self.assertEqual(nodeA[0], "A") + self.assertEqual(nodeA[1], "100.1") + self.assertEqual(len(nodeA), 2) + + nodeB = item[1] + self.assertEqual(nodeB[0], "B") + self.assertEqual(nodeB[1], "200") + self.assertEqual(nodeB[3], "STRING2") + self.assertEqual(len(nodeB), 4) + + self.assertEqual(len(content), 4) + + def test_valid_string_runtime_file(self): + with TestAreaContext("assert_runtime_file"): + with open("some_file", "w") as f: + f.write("This i.") + self.assertTrue(ContentTypeEnum.CONFIG_RUNTIME_FILE.valid_string("no_file")) + self.assertTrue( + ContentTypeEnum.CONFIG_RUNTIME_FILE.valid_string("some_file", True) + ) + self.assertFalse( + ContentTypeEnum.CONFIG_RUNTIME_FILE.valid_string("no_file", True) + ) + + def test_valid_string(self): + self.assertTrue(ContentTypeEnum.CONFIG_FLOAT.valid_string("1.25")) + self.assertTrue(ContentTypeEnum.CONFIG_RUNTIME_INT.valid_string("1.7")) + self.assertFalse( + ContentTypeEnum.CONFIG_RUNTIME_INT.valid_string("1.7", runtime=True) + ) + self.assertTrue( + ContentTypeEnum.CONFIG_FLOAT.valid_string("1.125", runtime=True) + ) + self.assertEqual(ContentTypeEnum.CONFIG_FLOAT.convert_string("1.25"), 1.25) + self.assertEqual(ContentTypeEnum.CONFIG_INT.convert_string("100"), 100) + + with self.assertRaises(ValueError): + ContentTypeEnum.CONFIG_INT.convert_string("100x") + + with self.assertRaises(ValueError): + ContentTypeEnum.CONFIG_FLOAT.convert_string("100X") + + with self.assertRaises(ValueError): + ContentTypeEnum.CONFIG_BOOL.convert_string("a_random_string") + + self.assertTrue(ContentTypeEnum.CONFIG_BOOL.convert_string("TRUE")) + self.assertTrue(ContentTypeEnum.CONFIG_BOOL.convert_string("True")) + self.assertFalse(ContentTypeEnum.CONFIG_BOOL.convert_string("False")) + self.assertFalse(ContentTypeEnum.CONFIG_BOOL.convert_string("F")) diff --git a/libres/tests/res/enkf/__init__.py b/libres/tests/res/enkf/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/tests/res/enkf/data/__init__.py b/libres/tests/res/enkf/data/__init__.py new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/libres/tests/res/enkf/data/__init__.py @@ -0,0 +1 @@ + diff --git a/libres/tests/res/enkf/data/test_enkf_config_node.py b/libres/tests/res/enkf/data/test_enkf_config_node.py new file mode 100644 index 00000000000..5f088a5cdc7 --- /dev/null +++ b/libres/tests/res/enkf/data/test_enkf_config_node.py @@ -0,0 +1,29 @@ +import os.path +import json + +from res.enkf.config import EnkfConfigNode +from ecl.util.test import TestAreaContext +from tests import ResTest + + +class EnkfConfigNodeTest(ResTest): + def test_gen_data(self): + + # Must have %d in filename argument + with self.assertRaises(ValueError): + config_node = EnkfConfigNode.create_gen_data("KEY", "FILE") + + config_node = EnkfConfigNode.create_gen_data("KEY", "FILE%d") + self.assertIsInstance(config_node, EnkfConfigNode) + gen_data = config_node.getModelConfig() + self.assertEqual(1, gen_data.getNumReportStep()) + self.assertEqual(0, gen_data.getReportStep(0)) + + config_node = EnkfConfigNode.create_gen_data( + "KEY", "FILE%d", report_steps=[10, 20, 30] + ) + self.assertIsInstance(config_node, EnkfConfigNode) + gen_data = config_node.getModelConfig() + self.assertEqual(3, gen_data.getNumReportStep()) + for r1, r2 in zip([10, 20, 30], gen_data.getReportSteps()): + self.assertEqual(r1, r2) diff --git a/libres/tests/res/enkf/data/test_enkf_node.py b/libres/tests/res/enkf/data/test_enkf_node.py new file mode 100644 index 00000000000..05c25c2779a --- /dev/null +++ b/libres/tests/res/enkf/data/test_enkf_node.py @@ -0,0 +1,24 @@ +import json +import os.path + +from res.enkf.data import ExtParam +from res.enkf.data import EnkfNode +from res.enkf.config import ExtParamConfig, EnkfConfigNode +from ecl.util.test import TestAreaContext +from tests import ResTest + + +class EnkfNodeTest(ResTest): + def test_config(self): + keys = ["Key1", "Key2", "Key3"] + with TestAreaContext("enkf_node"): + config = EnkfConfigNode.create_ext_param("key", keys) + node = EnkfNode(config) + ext_node = node.as_ext_param() + ext_config = config.getModelConfig() + + ext_node.set_vector([1, 2, 3]) + node.ecl_write("path") + d = json.load(open("path/key.json")) + self.assertEqual(d["Key1"], 1) + self.assertEqual(d["Key3"], 3) diff --git a/libres/tests/res/enkf/data/test_ext_param.py b/libres/tests/res/enkf/data/test_ext_param.py new file mode 100644 index 00000000000..1cbba40beeb --- /dev/null +++ b/libres/tests/res/enkf/data/test_ext_param.py @@ -0,0 +1,140 @@ +import os.path +import json + +from res.enkf.data import ExtParam +from res.enkf.config import ExtParamConfig +from ecl.util.test import TestAreaContext +from tests import ResTest + + +class ExtParamTest(ResTest): + def test_config(self): + input_keys = ["key1", "key2", "key3"] + config = ExtParamConfig("Key", input_keys) + self.assertTrue(len(config), 3) + + for i in range(len(config)): + configkey, _ = config[i] + self.assertEqual(configkey, input_keys[i]) + + with self.assertRaises(IndexError): + c = config[100] + + keys = [] + for key in config.keys(): + keys.append(key) + self.assertEqual(keys, input_keys) + + self.assertIn("key1", config) + + def test_config_with_suffixes(self): + input_suffixes = [ + ["a", "b", "c"], + ["2"], + ["asd", "qwe", "zxc"], + ] + input_dict = { + "key1": input_suffixes[0], + "key2": input_suffixes[1], + "key3": input_suffixes[2], + } + config = ExtParamConfig("Key", input_dict) + + self.assertTrue(len(config), 3) + self.assertIn("key3", config) + self.assertNotIn("not_me", config) + self.assertIn(("key3", "asd"), config) + self.assertNotIn(("key3", "not_me_either"), config) + self.assertNotIn(("who", "b"), config) + + for i in range(len(config)): + configkey, configsuffixes = config[i] + self.assertIn(configkey, input_dict) + self.assertIn(configsuffixes, input_suffixes) + + for k in input_dict: + configsuffixes = config[k] + self.assertIn(configsuffixes, input_suffixes) + + with self.assertRaises(IndexError): + c = config[100] + + with self.assertRaises(IndexError): + c = config["no_such_key"] + + self.assertEqual(set(config.keys()), set(input_dict.keys())) + + d = {k: s for k, s in config.items()} + self.assertEqual(d, input_dict) + + def test_data(self): + input_keys = ["key1", "key2", "key3"] + config = ExtParamConfig("Key", input_keys) + data = ExtParam(config) + + with self.assertRaises(IndexError): + d = data[100] + with self.assertRaises(IndexError): + d = data[-4] + + with self.assertRaises(KeyError): + d = data["NoSuchKey"] + with self.assertRaises(KeyError): + d = data["key1", "a_suffix"] + + self.assertIn("key1", data) + data[0] = 177 + self.assertEqual(data[0], 177) + + data["key2"] = 321 + self.assertEqual(data[-2], 321) + + with self.assertRaises(ValueError): + data.set_vector([1, 2]) + + data.set_vector([1, 2, 3]) + for i in range(len(data)): + self.assertEqual(i + 1, data[i]) + + with TestAreaContext("json"): + data.export("file.json") + d = json.load(open("file.json")) + for key in data.config.keys(): + self.assertEqual(data[key], d[key]) + + def test_data_with_suffixes(self): + input_suffixes = [ + ["a", "b", "c"], + ["2"], + ["asd", "qwe", "zxc"], + ] + input_dict = { + "key1": input_suffixes[0], + "key2": input_suffixes[1], + "key3": input_suffixes[2], + } + config = ExtParamConfig("Key", input_dict) + data = ExtParam(config) + + with self.assertRaises(IndexError): + d = data[0] # Cannot use indices when we have suffixes + with self.assertRaises(TypeError): + d = data["key1", 1] + with self.assertRaises(KeyError): + d = data["NoSuchKey"] + with self.assertRaises(KeyError): + d = data["key1"] # requires a suffix + with self.assertRaises(KeyError): + d = data["key1", "no_such_suffix"] + + data["key1", "a"] = 1 + data["key1", "b"] = 500.5 + data["key2", "2"] = 2.1 + data["key3", "asd"] = -85 + self.assertEqual(data["key1", "a"], 1) + self.assertEqual(data["key1", "b"], 500.5) + self.assertEqual(data["key2", "2"], 2.1) + self.assertEqual(data["key3", "asd"], -85) + + # We don't know what the value is, but it should be possible to read it + _ = data["key3", "zxc"] diff --git a/libres/tests/res/enkf/data/test_field_config.py b/libres/tests/res/enkf/data/test_field_config.py new file mode 100644 index 00000000000..015d07df600 --- /dev/null +++ b/libres/tests/res/enkf/data/test_field_config.py @@ -0,0 +1,28 @@ +# Copyright (C) 2015 Equinor ASA, Norway. +# +# The file 'test_field_config.py' 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. + +import os + +from res.enkf import FieldConfig +from res.enkf import ActiveList +from tests import ResTest +from ecl.grid import EclGridGenerator + + +class FieldConfigTest(ResTest): + def test_create(self): + grid = EclGridGenerator.create_rectangular((10, 10, 5), (1, 1, 1)) + field_config = FieldConfig("SWAT", grid) diff --git a/libres/tests/res/enkf/data/test_gen_data.py b/libres/tests/res/enkf/data/test_gen_data.py new file mode 100644 index 00000000000..1aa8833f9cd --- /dev/null +++ b/libres/tests/res/enkf/data/test_gen_data.py @@ -0,0 +1,27 @@ +import pytest + +from tests import ResTest +from res.test import ErtTestContext + +from res.enkf.data.enkf_node import EnkfNode +from res.enkf.node_id import NodeId + + +@pytest.mark.equinor_test +class GenDataTest(ResTest): + def setUp(self): + self.config_file = self.createTestPath("Equinor/config/with_GEN_DATA/config") + + def test_create(self): + with ErtTestContext("gen_data_test", self.config_file) as test_context: + ert = test_context.getErt() + fs1 = ert.getEnkfFsManager().getCurrentFileSystem() + config_node = ert.ensembleConfig().getNode("TIMESHIFT") + + data_node = EnkfNode(config_node) + data_node.tryLoad(fs1, NodeId(60, 0)) + + gen_data = data_node.asGenData() + data = gen_data.getData() + + self.assertEqual(len(data), 2560) diff --git a/libres/tests/res/enkf/data/test_gen_data_config.py b/libres/tests/res/enkf/data/test_gen_data_config.py new file mode 100644 index 00000000000..34ea10e8e84 --- /dev/null +++ b/libres/tests/res/enkf/data/test_gen_data_config.py @@ -0,0 +1,87 @@ +import pytest + +from tests import ResTest +from res.test import ErtTestContext + +from res import ResPrototype +from res.enkf.data import EnkfNode +from res.enkf.config import GenDataConfig +from res.enkf import NodeId, RunArg +from res.enkf import ForwardLoadContext + + +@pytest.mark.equinor_test +class GenDataConfigTest(ResTest): + _get_active_mask = ResPrototype( + "bool_vector_ref gen_data_config_get_active_mask( gen_data_config )", bind=False + ) + _update_active_mask = ResPrototype( + "void gen_data_config_update_active( gen_data_config, forward_load_context , bool_vector)", + bind=False, + ) + + def setUp(self): + self.config_file = self.createTestPath("Equinor/config/with_GEN_DATA/config") + + def load_active_masks(self, case1, case2): + with ErtTestContext("gen_data_config_test", self.config_file) as test_context: + ert = test_context.getErt() + subst_list = ert.resConfig().subst_config.subst_list + + fs1 = ert.getEnkfFsManager().getFileSystem(case1) + config_node = ert.ensembleConfig().getNode("TIMESHIFT") + data_node = EnkfNode(config_node) + data_node.tryLoad(fs1, NodeId(60, 0)) + + active_mask = self._get_active_mask(config_node.getDataModelConfig()) + first_active_mask_length = len(active_mask) + self.assertEqual(first_active_mask_length, 2560) + + fs2 = ert.getEnkfFsManager().getFileSystem(case2) + data_node = EnkfNode(config_node) + data_node.tryLoad(fs2, NodeId(60, 0)) + + active_mask = self._get_active_mask(config_node.getDataModelConfig()) + second_active_mask_len = len(active_mask) + self.assertEqual(second_active_mask_len, 2560) + self.assertEqual(first_active_mask_length, second_active_mask_len) + + # Setting one element to False, load different case, check, reload, and check. + self.assertTrue(active_mask[10]) + active_mask_modified = active_mask.copy() + active_mask_modified[10] = False + + self.updateMask( + config_node.getDataModelConfig(), + 60, + fs2, + active_mask_modified, + subst_list, + ) + active_mask = self._get_active_mask(config_node.getDataModelConfig()) + self.assertFalse(active_mask[10]) + + # Load first - check element is true + data_node = EnkfNode(config_node) + data_node.tryLoad(fs1, NodeId(60, 0)) + active_mask = self._get_active_mask(config_node.getDataModelConfig()) + self.assertTrue(active_mask[10]) + + # Reload second again, should now be false at 10, due to the update further up + data_node = EnkfNode(config_node) + data_node.tryLoad(fs2, NodeId(60, 0)) + active_mask = self._get_active_mask(config_node.getDataModelConfig()) + self.assertFalse(active_mask[10]) + + def test_loading_two_cases_with_and_without_active_file(self): + self.load_active_masks("missing-active", "default") + + def test_create(self): + conf = GenDataConfig("KEY") + + def updateMask(self, gen_data_config, report_step, fs, active_mask, subst_list): + run_arg = RunArg.createEnsembleExperimentRunArg( + "run_id", fs, 0, "Path", "jobname", subst_list + ) + load_context = ForwardLoadContext(run_arg=run_arg, report_step=report_step) + self._update_active_mask(gen_data_config, load_context, active_mask) diff --git a/libres/tests/res/enkf/data/test_gen_kw.py b/libres/tests/res/enkf/data/test_gen_kw.py new file mode 100644 index 00000000000..5a9269ef2f1 --- /dev/null +++ b/libres/tests/res/enkf/data/test_gen_kw.py @@ -0,0 +1,105 @@ +import os.path +from res.enkf.data import GenKw +from res.enkf.config import GenKwConfig +from ecl.util.test import TestAreaContext +from tests import ResTest + + +def create_gen_kw(): + parameter_file = "MULTFLT.txt" + template_file = "MULTFLT.tmpl" + with open(parameter_file, "w") as f: + f.write("MULTFLT1 NORMAL 0 1\n") + f.write("MULTFLT2 RAW \n") + f.write("MULTFLT3 NORMAL 0 1\n") + + with open(template_file, "w") as f: + f.write(" \n") + f.write("/\n") + + gen_kw_config = GenKwConfig("MULTFLT", template_file, parameter_file) + gen_kw = GenKw(gen_kw_config) + + return (gen_kw_config, gen_kw) + + +class GenKwTest(ResTest): + def test_gen_kw_get_set(self): + with TestAreaContext("enkf/data/gen_kwt"): + + (gen_kw_config, gen_kw) = create_gen_kw() + self.assertIsInstance(gen_kw, GenKw) + + gen_kw[0] = 3.0 + self.assertEqual(gen_kw[0], 3.0) + + gen_kw["MULTFLT1"] = 4.0 + self.assertEqual(gen_kw["MULTFLT1"], 4.0) + self.assertEqual(gen_kw[0], 4.0) + + gen_kw["MULTFLT2"] = 8.0 + self.assertEqual(gen_kw["MULTFLT2"], 8.0) + self.assertEqual(gen_kw[1], 8.0) + + gen_kw["MULTFLT3"] = 12.0 + self.assertEqual(gen_kw["MULTFLT3"], 12.0) + self.assertEqual(gen_kw[2], 12.0) + + self.assertEqual(len(gen_kw), 3) + + with self.assertRaises(IndexError): + gen_kw[4] + + with self.assertRaises(TypeError): + gen_kw[1.5] + + with self.assertRaises(KeyError): + gen_kw["MULTFLT_2"] + + self.assertTrue("MULTFLT1" in gen_kw) + + items = gen_kw.items() + self.assertEqual(len(items), 3) + self.assertEqual(items[0][0], "MULTFLT1") + self.assertEqual(items[1][0], "MULTFLT2") + self.assertEqual(items[2][0], "MULTFLT3") + + self.assertEqual(items[0][1], 4) + self.assertEqual(items[1][1], 8) + self.assertEqual(items[2][1], 12) + + def test_gen_kw_get_set_vector(self): + with TestAreaContext("enkf/data/gen_kwt"): + + (gen_kw_config, gen_kw) = create_gen_kw() + with self.assertRaises(ValueError): + gen_kw.setValues([0]) + + with self.assertRaises(TypeError): + gen_kw.setValues(["A", "B", "C"]) + + gen_kw.setValues([0, 1, 2]) + self.assertEqual(gen_kw[0], 0) + self.assertEqual(gen_kw[1], 1) + self.assertEqual(gen_kw[2], 2) + + self.assertEqual(gen_kw["MULTFLT1"], 0) + self.assertEqual(gen_kw["MULTFLT2"], 1) + self.assertEqual(gen_kw["MULTFLT3"], 2) + + def test_gen_kw_ecl_write(self): + with TestAreaContext("enkf/data/gen_kwt"): + (gen_kw_config, gen_kw) = create_gen_kw() + + with self.assertRaises(IOError): + gen_kw.eclWrite("tmp", "file.txt") + + gen_kw.eclWrite(None, "file.txt") + self.assertTrue(os.path.isfile("file.txt")) + + os.mkdir("tmp") + gen_kw.eclWrite("tmp", "file.txt") + self.assertTrue(os.path.isfile("tmp/file.txt")) + + gen_kw.exportParameters("tmp/export.txt") + self.assertTrue(os.path.isfile("tmp/export.txt")) diff --git a/libres/tests/res/enkf/data/test_gen_kw_config.py b/libres/tests/res/enkf/data/test_gen_kw_config.py new file mode 100644 index 00000000000..ddc07203c0d --- /dev/null +++ b/libres/tests/res/enkf/data/test_gen_kw_config.py @@ -0,0 +1,119 @@ +from ecl.util.test import TestAreaContext +from tests import ResTest +from res.test import ErtTestContext +from tests.utils import tmpdir +from res.enkf import ErtImplType, GenKwConfig + + +class GenKwConfigTest(ResTest): + def test_gen_kw_config(self): + with TestAreaContext("gen_kw_config"): + with open("template.txt", "w") as f: + f.write("Hello") + + with open("parameters.txt", "w") as f: + f.write("KEY UNIFORM 0 1 \n") + + with open("parameters_with_comments.txt", "w") as f: + f.write("KEY1 UNIFORM 0 1 -- COMMENT\n") + f.write("\n\n") # Two blank lines + f.write("KEY2 UNIFORM 0 1\n") + f.write("--KEY3 \n") + f.write("KEY3 UNIFORM 0 1\n") + + template_file = "template.txt" + parameter_file = "parameters.txt" + parameter_file_comments = "parameters_with_comments.txt" + with self.assertRaises(IOError): + conf = GenKwConfig("KEY", template_file, "does_not_exist") + + with self.assertRaises(IOError): + conf = GenKwConfig("Key", "does_not_exist", parameter_file) + + conf = GenKwConfig("KEY", template_file, parameter_file) + conf = GenKwConfig("KEY", template_file, parameter_file_comments) + self.assertEqual(len(conf), 3) + + @tmpdir(None) + def test_gen_kw_config_get_priors(self): + parameter_file = "parameters.txt" + template_file = "template.txt" + + with open(template_file, "w") as f: + f.write("Hello") + + with open(parameter_file, "w") as f: + f.write("KEY1 NORMAL 0 1\n") + f.write("KEY2 LOGNORMAL 2 3\n") + f.write("KEY3 TRUNCATED_NORMAL 4 5 6 7\n") + f.write("KEY4 TRIANGULAR 0 1 2\n") + f.write("KEY5 UNIFORM 2 3\n") + f.write("KEY6 DUNIF 3 0 1\n") + f.write("KEY7 ERRF 0 1 2 3\n") + f.write("KEY8 DERRF 0 1 2 3 4\n") + f.write("KEY9 LOGUNIF 0 1\n") + f.write("KEY10 CONST 10\n") + + conf = GenKwConfig("KEY", template_file, parameter_file) + priors = conf.get_priors() + self.assertEqual(len(conf), 10) + + assert { + "key": "KEY1", + "function": "NORMAL", + "parameters": {"MEAN": 0, "STD": 1}, + } in priors + + assert { + "key": "KEY2", + "function": "LOGNORMAL", + "parameters": {"MEAN": 2, "STD": 3}, + } in priors + + assert { + "key": "KEY3", + "function": "TRUNCATED_NORMAL", + "parameters": {"MEAN": 4, "STD": 5, "MIN": 6, "MAX": 7}, + } in priors + + assert { + "key": "KEY4", + "function": "TRIANGULAR", + "parameters": {"XMIN": 0, "XMODE": 1, "XMAX": 2}, + } in priors + + assert { + "key": "KEY5", + "function": "UNIFORM", + "parameters": {"MIN": 2, "MAX": 3}, + } in priors + + assert { + "key": "KEY6", + "function": "DUNIF", + "parameters": {"STEPS": 3, "MIN": 0, "MAX": 1}, + } in priors + + assert { + "key": "KEY7", + "function": "ERRF", + "parameters": {"MIN": 0, "MAX": 1, "SKEWNESS": 2, "WIDTH": 3}, + } in priors + + assert { + "key": "KEY8", + "function": "DERRF", + "parameters": {"STEPS": 0, "MIN": 1, "MAX": 2, "SKEWNESS": 3, "WIDTH": 4}, + } in priors + + assert { + "key": "KEY9", + "function": "LOGUNIF", + "parameters": {"MIN": 0, "MAX": 1}, + } in priors + + assert { + "key": "KEY10", + "function": "CONST", + "parameters": {"VALUE": 10}, + } in priors diff --git a/libres/tests/res/enkf/data/test_gen_kw_config_equinor.py b/libres/tests/res/enkf/data/test_gen_kw_config_equinor.py new file mode 100644 index 00000000000..cf3f0261d6a --- /dev/null +++ b/libres/tests/res/enkf/data/test_gen_kw_config_equinor.py @@ -0,0 +1,52 @@ +import pytest + +from tests import ResTest +from res.test import ErtTestContext + +from res.enkf import ErtImplType, GenKwConfig + + +@pytest.mark.equinor_test +class GenKwConfigTest(ResTest): + def setUp(self): + self.config = self.createTestPath("Equinor/config/with_data/config") + + def test_gen_kw_config(self): + + with ErtTestContext("python/enkf/data/gen_kw_config", self.config) as context: + + ert = context.getErt() + + result_gen_kw_keys = ert.ensembleConfig().getKeylistFromImplType( + ErtImplType.GEN_KW + ) + + expected_keys = ["GRID_PARAMS", "FLUID_PARAMS", "MULTFLT"] + + self.assertItemsEqual(result_gen_kw_keys, expected_keys) + self.assertEqual(len(expected_keys), len(result_gen_kw_keys)) + + for key in expected_keys: + node = ert.ensembleConfig().getNode(key) + gen_kw_config = node.getModelConfig() + self.assertIsInstance(gen_kw_config, GenKwConfig) + + self.assertEqual(gen_kw_config.getKey(), key) + + if key == "GRID_PARAMS": + expected_values = ["MULTPV2", "MULTPV3"] + + self.assertFalse(gen_kw_config.shouldUseLogScale(0)) + self.assertFalse(gen_kw_config.shouldUseLogScale(1)) + + elif key == "MULTFLT": + expected_values = ["F3"] + + self.assertTrue(gen_kw_config.shouldUseLogScale(0)) + + elif key == "FLUID_PARAMS": + expected_values = ["SWCR", "SGCR"] + self.assertFalse(gen_kw_config.shouldUseLogScale(0)) + self.assertFalse(gen_kw_config.shouldUseLogScale(1)) + + self.assertEqual([value for value in gen_kw_config], expected_values) diff --git a/libres/tests/res/enkf/data/test_summary.py b/libres/tests/res/enkf/data/test_summary.py new file mode 100644 index 00000000000..7e62132dd25 --- /dev/null +++ b/libres/tests/res/enkf/data/test_summary.py @@ -0,0 +1,27 @@ +import os.path +import json + +from ecl.util.test.ecl_mock import createEclSum +from res.enkf.data.summary import Summary +from res.enkf.config import SummaryConfig +from ecl.util.test import TestAreaContext +from tests import ResTest + + +class SummaryTest(ResTest): + def test_create(self): + config = SummaryConfig("WWCT:OP_5") + summary = Summary(config) + self.assertEqual(len(summary), 0) + + with self.assertRaises(IndexError): + v = summary[100] + + summary[0] = 75 + self.assertEqual(summary[0], 75) + + summary[10] = 100 + self.assertEqual(summary[10], 100) + + with self.assertRaises(ValueError): + v5 = summary[5] diff --git a/libres/tests/res/enkf/export/__init__.py b/libres/tests/res/enkf/export/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/tests/res/enkf/export/test_arg_loader.py b/libres/tests/res/enkf/export/test_arg_loader.py new file mode 100644 index 00000000000..e20353dd1a3 --- /dev/null +++ b/libres/tests/res/enkf/export/test_arg_loader.py @@ -0,0 +1,26 @@ +import pytest + +from tests import ResTest +from res.test import ErtTestContext + +from res.enkf.export import ArgLoader + + +@pytest.mark.equinor_test +class ArgLoaderTest(ResTest): + def test_arg_loader(self): + + with self.assertRaises(IOError): + arg = ArgLoader.load("arg1X") + + arg_file = self.createTestPath( + "Equinor/config/with_GEN_DATA_RFT/wellpath/WI_1.txt" + ) + + with self.assertRaises(ValueError): + arg = ArgLoader.load( + arg_file, column_names=["Col1", "Col2", "Col3", "COl5", "Col6"] + ) + + arg = ArgLoader.load(arg_file, column_names=["utm_x", "utm_y", "md", "tvd"]) + self.assertFloatEqual(arg["utm_x"][0], 461317.620646) diff --git a/libres/tests/res/enkf/export/test_design_matrix.py b/libres/tests/res/enkf/export/test_design_matrix.py new file mode 100644 index 00000000000..b8e8b864e21 --- /dev/null +++ b/libres/tests/res/enkf/export/test_design_matrix.py @@ -0,0 +1,95 @@ +from res.enkf.export import DesignMatrixReader +from tests import ResTest +from ecl.util.test.test_area import TestAreaContext + + +def dumpDesignMatrix1(path): + with open(path, "w") as dm: + dm.write( + "Case CORR_SEIS_HEIMDAL VOL_FRAC_HEIMDAL AZIM_IND_HEIMDAL VARIO_PARAL_HEIMDAL VARIO_NORM_HEIMDAL VARIO_VERT_HEIMDAL SEIS_COND_HEIMDAL\n" + ) + dm.write("0 0.8 0.08 125 1000 500 25 ON\n") + dm.write("1 0.8 0.15 125 2000 1000 25 ON\n") + dm.write("2 0.8 0.20 125 4000 2000 25 ON\n") + + +def dumpDesignMatrix2(path): + with open(path, "w") as dm: + dm.write( + "Case CORR_SEIS_HEIMDAL VOL_FRAC_HEIMDAL AZIM_IND_HEIMDAL VARIO_PARAL_HEIMDAL VARIO_NORM_HEIMDAL VARIO_VERT_HEIMDAL SEIS_COND_HEIMDAL\n" + ) + dm.write("0 0.8 0.08 125 1000 500 25 ON\n") + dm.write("1 0.8 0.15 125 2000 1000 25 ON\n") + dm.write("2 0.8 0.20 125 4000 2000 25 ON\n") + dm.write("4 0.8 0.30 125 16000 8000 25 ON\n") + + +class DesignMatrixTest(ResTest): + def test_read_design_matrix(self): + with TestAreaContext("python/enkf/export/design_matrix"): + dumpDesignMatrix1("DesignMatrix.txt") + dm = DesignMatrixReader.loadDesignMatrix("DesignMatrix.txt") + + self.assertEqual(dm["CORR_SEIS_HEIMDAL"][0], 0.8) + self.assertEqual(dm["VOL_FRAC_HEIMDAL"][0], 0.08) + self.assertEqual(dm["AZIM_IND_HEIMDAL"][0], 125) + self.assertEqual(dm["VARIO_PARAL_HEIMDAL"][0], 1000) + self.assertEqual(dm["VARIO_NORM_HEIMDAL"][0], 500) + self.assertEqual(dm["VARIO_VERT_HEIMDAL"][0], 25) + self.assertEqual(dm["SEIS_COND_HEIMDAL"][0], "ON") + + self.assertEqual(dm["CORR_SEIS_HEIMDAL"][1], 0.8) + self.assertEqual(dm["VOL_FRAC_HEIMDAL"][1], 0.15) + self.assertEqual(dm["AZIM_IND_HEIMDAL"][1], 125) + self.assertEqual(dm["VARIO_PARAL_HEIMDAL"][1], 2000) + self.assertEqual(dm["VARIO_NORM_HEIMDAL"][1], 1000) + self.assertEqual(dm["VARIO_VERT_HEIMDAL"][1], 25) + self.assertEqual(dm["SEIS_COND_HEIMDAL"][1], "ON") + + self.assertEqual(dm["CORR_SEIS_HEIMDAL"][2], 0.8) + self.assertEqual(dm["VOL_FRAC_HEIMDAL"][2], 0.20) + self.assertEqual(dm["AZIM_IND_HEIMDAL"][2], 125) + self.assertEqual(dm["VARIO_PARAL_HEIMDAL"][2], 4000) + self.assertEqual(dm["VARIO_NORM_HEIMDAL"][2], 2000) + self.assertEqual(dm["VARIO_VERT_HEIMDAL"][2], 25) + self.assertEqual(dm["SEIS_COND_HEIMDAL"][2], "ON") + + def test_read_design_matrix_2(self): + with TestAreaContext("python/enkf/export/design_matrix_2"): + dumpDesignMatrix2("DesignMatrix2.txt") + dm = DesignMatrixReader.loadDesignMatrix("DesignMatrix2.txt") + + self.assertEqual(dm["CORR_SEIS_HEIMDAL"][0], 0.8) + self.assertEqual(dm["VOL_FRAC_HEIMDAL"][0], 0.08) + self.assertEqual(dm["AZIM_IND_HEIMDAL"][0], 125) + self.assertEqual(dm["VARIO_PARAL_HEIMDAL"][0], 1000) + self.assertEqual(dm["VARIO_NORM_HEIMDAL"][0], 500) + self.assertEqual(dm["VARIO_VERT_HEIMDAL"][0], 25) + self.assertEqual(dm["SEIS_COND_HEIMDAL"][0], "ON") + + self.assertEqual(dm["CORR_SEIS_HEIMDAL"][1], 0.8) + self.assertEqual(dm["VOL_FRAC_HEIMDAL"][1], 0.15) + self.assertEqual(dm["AZIM_IND_HEIMDAL"][1], 125) + self.assertEqual(dm["VARIO_PARAL_HEIMDAL"][1], 2000) + self.assertEqual(dm["VARIO_NORM_HEIMDAL"][1], 1000) + self.assertEqual(dm["VARIO_VERT_HEIMDAL"][1], 25) + self.assertEqual(dm["SEIS_COND_HEIMDAL"][1], "ON") + + self.assertEqual(dm["CORR_SEIS_HEIMDAL"][2], 0.8) + self.assertEqual(dm["VOL_FRAC_HEIMDAL"][2], 0.20) + self.assertEqual(dm["AZIM_IND_HEIMDAL"][2], 125) + self.assertEqual(dm["VARIO_PARAL_HEIMDAL"][2], 4000) + self.assertEqual(dm["VARIO_NORM_HEIMDAL"][2], 2000) + self.assertEqual(dm["VARIO_VERT_HEIMDAL"][2], 25) + self.assertEqual(dm["SEIS_COND_HEIMDAL"][2], "ON") + + with self.assertRaises(KeyError): + self.assertEqual(dm["CORR_SEIS_HEIMDAL"][3], 0.8) + + self.assertEqual(dm["CORR_SEIS_HEIMDAL"][4], 0.8) + self.assertEqual(dm["VOL_FRAC_HEIMDAL"][4], 0.30) + self.assertEqual(dm["AZIM_IND_HEIMDAL"][4], 125) + self.assertEqual(dm["VARIO_PARAL_HEIMDAL"][4], 16000) + self.assertEqual(dm["VARIO_NORM_HEIMDAL"][4], 8000) + self.assertEqual(dm["VARIO_VERT_HEIMDAL"][4], 25) + self.assertEqual(dm["SEIS_COND_HEIMDAL"][4], "ON") diff --git a/libres/tests/res/enkf/export/test_export_join.py b/libres/tests/res/enkf/export/test_export_join.py new file mode 100644 index 00000000000..430775679e9 --- /dev/null +++ b/libres/tests/res/enkf/export/test_export_join.py @@ -0,0 +1,113 @@ +import pandas +import numpy +import os + +from tests import ResTest +from res.test import ErtTestContext +from pytest import MonkeyPatch + +from res.enkf.export import ( + DesignMatrixReader, + SummaryCollector, + GenKwCollector, + MisfitCollector, +) + + +def dumpDesignMatrix(path): + with open(path, "w") as dm: + dm.write( + "REALIZATION EXTRA_FLOAT_COLUMN EXTRA_INT_COLUMN EXTRA_STRING_COLUMN\n" + ) + dm.write("0 0.08 125 ON\n") + dm.write("1 0.07 225 OFF\n") + dm.write("2 0.08 325 ON\n") + dm.write("3 0.06 425 ON\n") + dm.write("4 0.08 525 OFF\n") + dm.write("5 0.08 625 ON\n") + dm.write("6 0.09 725 ON\n") + dm.write("7 0.08 825 OFF\n") + dm.write("8 0.02 925 ON\n") + dm.write("9 0.08 125 ON\n") + dm.write("10 0.08 225 ON\n") + dm.write("11 0.05 325 OFF\n") + dm.write("12 0.08 425 ON\n") + dm.write("13 0.07 525 ON\n") + dm.write("14 0.08 625 UNKNOWN\n") + dm.write("15 0.08 725 ON\n") + dm.write("16 0.08 825 ON\n") + dm.write("17 0.08 925 OFF\n") + dm.write("18 0.09 125 ON\n") + dm.write("19 0.08 225 ON\n") + dm.write("20 0.06 325 OFF\n") + dm.write("21 0.08 425 ON\n") + dm.write("22 0.07 525 ON\n") + dm.write("23 0.08 625 OFF\n") + dm.write("24 0.08 725 ON\n") + + +class ExportJoinTest(ResTest): + def setUp(self): + self.monkeypatch = MonkeyPatch() + self.monkeypatch.setenv( + "TZ", "CET" + ) # The ert_statoil case was generated in CET + self.config = self.createTestPath("local/snake_oil/snake_oil.ert") + + def tearDown(self): + self.monkeypatch.undo() + + def test_join(self): + + with ErtTestContext("python/enkf/export/export_join", self.config) as context: + dumpDesignMatrix("DesignMatrix.txt") + ert = context.getErt() + + summary_data = SummaryCollector.loadAllSummaryData(ert, "default_1") + gen_kw_data = GenKwCollector.loadAllGenKwData(ert, "default_1") + misfit = MisfitCollector.loadAllMisfitData(ert, "default_1") + dm = DesignMatrixReader.loadDesignMatrix("DesignMatrix.txt") + + result = summary_data.join(gen_kw_data, how="inner") + result = result.join(misfit, how="inner") + result = result.join(dm, how="inner") + + first_date = "2010-01-10" + last_date = "2015-06-23" + + self.assertFloatEqual( + result["SNAKE_OIL_PARAM:OP1_OCTAVES"][0][first_date], 3.947766 + ) + self.assertFloatEqual( + result["SNAKE_OIL_PARAM:OP1_OCTAVES"][24][first_date], 4.206698 + ) + self.assertFloatEqual( + result["SNAKE_OIL_PARAM:OP1_OCTAVES"][24][last_date], 4.206698 + ) + + self.assertFloatEqual(result["EXTRA_FLOAT_COLUMN"][0][first_date], 0.08) + self.assertEqual(result["EXTRA_INT_COLUMN"][0][first_date], 125) + self.assertEqual(result["EXTRA_STRING_COLUMN"][0][first_date], "ON") + + self.assertFloatEqual(result["EXTRA_FLOAT_COLUMN"][0][last_date], 0.08) + self.assertEqual(result["EXTRA_INT_COLUMN"][0][last_date], 125) + self.assertEqual(result["EXTRA_STRING_COLUMN"][0][last_date], "ON") + + self.assertFloatEqual(result["EXTRA_FLOAT_COLUMN"][1][last_date], 0.07) + self.assertEqual(result["EXTRA_INT_COLUMN"][1][last_date], 225) + self.assertEqual(result["EXTRA_STRING_COLUMN"][1][last_date], "OFF") + + self.assertFloatEqual(result["MISFIT:FOPR"][0][last_date], 457.491003) + self.assertFloatEqual(result["MISFIT:FOPR"][24][last_date], 1630.774198) + + self.assertFloatEqual(result["MISFIT:TOTAL"][0][first_date], 468.469969) + self.assertFloatEqual(result["MISFIT:TOTAL"][0][last_date], 468.469969) + self.assertFloatEqual(result["MISFIT:TOTAL"][24][last_date], 1714.662370) + + with self.assertRaises(KeyError): + realization_13 = result.loc[60] + + column_count = len(result.columns) + self.assertEqual(result.dtypes[0], numpy.float64) + self.assertEqual(result.dtypes[column_count - 1], numpy.object) + self.assertEqual(result.dtypes[column_count - 2], numpy.int64) diff --git a/libres/tests/res/enkf/export/test_gen_data_collector.py b/libres/tests/res/enkf/export/test_gen_data_collector.py new file mode 100644 index 00000000000..fbb308d05e4 --- /dev/null +++ b/libres/tests/res/enkf/export/test_gen_data_collector.py @@ -0,0 +1,27 @@ +from tests import ResTest +from res.test import ErtTestContext + +from res.enkf.export import GenDataCollector + + +class GenDataCollectorTest(ResTest): + def test_gen_data_collector(self): + config = self.createTestPath("local/snake_oil/snake_oil.ert") + with ErtTestContext("python/enkf/export/gen_data_collector", config) as context: + ert = context.getErt() + + with self.assertRaises(KeyError): + data = GenDataCollector.loadGenData(ert, "default_0", "RFT_XX", 199) + + with self.assertRaises(ValueError): + data = GenDataCollector.loadGenData( + ert, "default_0", "SNAKE_OIL_OPR_DIFF", 198 + ) + + data1 = GenDataCollector.loadGenData( + ert, "default_0", "SNAKE_OIL_OPR_DIFF", 199 + ) + + self.assertFloatEqual(data1[0][0], -0.008206) + self.assertFloatEqual(data1[24][1], -0.119255) + self.assertFloatEqual(data1[24][1000], -0.258516) diff --git a/libres/tests/res/enkf/export/test_gen_data_observation_collector.py b/libres/tests/res/enkf/export/test_gen_data_observation_collector.py new file mode 100644 index 00000000000..b57c0a50d27 --- /dev/null +++ b/libres/tests/res/enkf/export/test_gen_data_observation_collector.py @@ -0,0 +1,50 @@ +from tests import ResTest +from res.test import ErtTestContext + +from res.enkf.export import GenDataObservationCollector + + +class GenDataObservationCollectorTest(ResTest): + def test_gen_data_collector(self): + config = self.createTestPath("local/mini_ert/mini_config") + with ErtTestContext( + "python/enkf/export/gen_data_observation_collector", config + ) as context: + ert = context.getErt() + + obs_key = GenDataObservationCollector.getObservationKeyForDataKey( + ert, "PERLIN", 1 + ) + self.assertEqual(obs_key, "GEN_PERLIN_1") + + obs_key = GenDataObservationCollector.getObservationKeyForDataKey( + ert, "PERLIN", 2 + ) + self.assertEqual(obs_key, "GEN_PERLIN_2") + + obs_key = GenDataObservationCollector.getObservationKeyForDataKey( + ert, "PERLIN", 3 + ) + self.assertEqual(obs_key, "GEN_PERLIN_3") + + obs_key = GenDataObservationCollector.getObservationKeyForDataKey( + ert, "PERLIN", 4 + ) + self.assertIsNone(obs_key) + + obs_key = GenDataObservationCollector.getObservationKeyForDataKey( + ert, "PERLINk", 1 + ) + self.assertIsNone(obs_key) + + data = GenDataObservationCollector.loadGenDataObservations( + ert, "default", "GEN_PERLIN_1" + ) + + self.assertFloatEqual(data["GEN_PERLIN_1"][0], -0.616789) + self.assertFloatEqual(data["STD_GEN_PERLIN_1"][0], 0.2) + + with self.assertRaises(KeyError): + GenDataObservationCollector.loadGenDataObservations( + ert, "default", "GEN_PERLIN_4" + ) diff --git a/libres/tests/res/enkf/export/test_gen_kw_collector.py b/libres/tests/res/enkf/export/test_gen_kw_collector.py new file mode 100644 index 00000000000..eab7d5708a9 --- /dev/null +++ b/libres/tests/res/enkf/export/test_gen_kw_collector.py @@ -0,0 +1,40 @@ +from tests import ResTest +from res.test import ErtTestContext + +from res.enkf.export import GenKwCollector + + +class GenKwCollectorTest(ResTest): + def setUp(self): + self.config = self.createTestPath("local/snake_oil/snake_oil.ert") + + def test_gen_kw_collector(self): + with ErtTestContext( + "python/enkf/export/gen_kw_collector", self.config + ) as context: + ert = context.getErt() + + data = GenKwCollector.loadAllGenKwData(ert, "default_0") + + self.assertFloatEqual(data["SNAKE_OIL_PARAM:OP1_PERSISTENCE"][0], 0.047517) + self.assertFloatEqual(data["SNAKE_OIL_PARAM:OP1_PERSISTENCE"][24], 0.160907) + + self.assertFloatEqual(data["SNAKE_OIL_PARAM:OP1_OFFSET"][0], 0.054539) + self.assertFloatEqual(data["SNAKE_OIL_PARAM:OP1_OFFSET"][12], 0.057807) + + realization_20 = data.loc[20] + + with self.assertRaises(KeyError): + realization_60 = data.loc[60] + + data = GenKwCollector.loadAllGenKwData( + ert, + "default_0", + ["SNAKE_OIL_PARAM:OP1_PERSISTENCE", "SNAKE_OIL_PARAM:OP1_OFFSET"], + ) + + self.assertFloatEqual(data["SNAKE_OIL_PARAM:OP1_PERSISTENCE"][0], 0.047517) + self.assertFloatEqual(data["SNAKE_OIL_PARAM:OP1_OFFSET"][0], 0.054539) + + with self.assertRaises(KeyError): + data["SNAKE_OIL_PARAM:OP1_DIVERGENCE_SCALE"] diff --git a/libres/tests/res/enkf/export/test_misfit_collector.py b/libres/tests/res/enkf/export/test_misfit_collector.py new file mode 100644 index 00000000000..90d174fd4b6 --- /dev/null +++ b/libres/tests/res/enkf/export/test_misfit_collector.py @@ -0,0 +1,27 @@ +from tests import ResTest +from res.test import ErtTestContext + +from res.enkf.export import MisfitCollector + + +class MisfitCollectorTest(ResTest): + def setUp(self): + self.config = self.createTestPath("local/snake_oil/snake_oil.ert") + + def test_misfit_collector(self): + with ErtTestContext( + "python/enkf/export/misfit_collector", self.config + ) as context: + ert = context.getErt() + data = MisfitCollector.loadAllMisfitData(ert, "default_0") + + self.assertFloatEqual(data["MISFIT:FOPR"][0], 737.436374) + self.assertFloatEqual(data["MISFIT:FOPR"][24], 1258.644538) + + self.assertFloatEqual(data["MISFIT:TOTAL"][0], 765.709246) + self.assertFloatEqual(data["MISFIT:TOTAL"][24], 1357.730551) + + realization_20 = data.loc[20] + + with self.assertRaises(KeyError): + realization_60 = data.loc[60] diff --git a/libres/tests/res/enkf/export/test_summary_collector.py b/libres/tests/res/enkf/export/test_summary_collector.py new file mode 100644 index 00000000000..2f929b190fb --- /dev/null +++ b/libres/tests/res/enkf/export/test_summary_collector.py @@ -0,0 +1,47 @@ +import os +from pytest import MonkeyPatch + +from tests import ResTest +from res.test import ErtTestContext +from res.enkf.export import SummaryCollector + + +class SummaryCollectorTest(ResTest): + def setUp(self): + self.monkeypatch = MonkeyPatch() + self.monkeypatch.setenv( + "TZ", "CET" + ) # The ert_statoil case was generated in CET + self.config = self.createTestPath("local/snake_oil/snake_oil.ert") + + def tearDown(self): + self.monkeypatch.undo() + + def test_summary_collector(self): + with ErtTestContext( + "python/enkf/export/summary_collector", self.config + ) as context: + ert = context.getErt() + + data = SummaryCollector.loadAllSummaryData(ert, "default_0") + + self.assertFloatEqual(data["WWCT:OP2"][0]["2010-01-10"], 0.385549) + self.assertFloatEqual(data["WWCT:OP2"][24]["2010-01-10"], 0.498331) + + self.assertFloatEqual(data["FOPR"][0]["2010-01-10"], 0.118963) + self.assertFloatEqual(data["FOPR"][0]["2015-06-23"], 0.133601) + + realization_20 = data.loc[20] + + with self.assertRaises(KeyError): + realization_60 = data.loc[60] + + data = SummaryCollector.loadAllSummaryData( + ert, "default_0", ["WWCT:OP1", "WWCT:OP2"] + ) + + self.assertFloatEqual(data["WWCT:OP1"][0]["2010-01-10"], 0.352953) + self.assertFloatEqual(data["WWCT:OP2"][0]["2010-01-10"], 0.385549) + + with self.assertRaises(KeyError): + data["FOPR"] diff --git a/libres/tests/res/enkf/export/test_summary_observation_collector.py b/libres/tests/res/enkf/export/test_summary_observation_collector.py new file mode 100644 index 00000000000..051b8f6046e --- /dev/null +++ b/libres/tests/res/enkf/export/test_summary_observation_collector.py @@ -0,0 +1,59 @@ +import os +from tests import ResTest +from res.test import ErtTestContext +from pytest import MonkeyPatch + +from res.enkf.export import SummaryObservationCollector + + +class SummaryObservationCollectorTest(ResTest): + def setUp(self): + self.monkeypatch = MonkeyPatch() + self.monkeypatch.setenv( + "TZ", "CET" + ) # The ert_statoil case was generated in CET + self.config = self.createTestPath("local/snake_oil/snake_oil.ert") + + def tearDown(self): + self.monkeypatch.undo() + + def test_summary_observation_collector(self): + + with ErtTestContext( + "python/enkf/export/summary_observation_collector", self.config + ) as context: + + ert = context.getErt() + + self.assertTrue( + SummaryObservationCollector.summaryKeyHasObservations(ert, "FOPR") + ) + self.assertFalse( + SummaryObservationCollector.summaryKeyHasObservations(ert, "FOPT") + ) + + keys = SummaryObservationCollector.getAllObservationKeys(ert) + self.assertTrue("FOPR" in keys) + self.assertTrue("WOPR:OP1" in keys) + self.assertFalse("WOPR:OP2" in keys) + + data = SummaryObservationCollector.loadObservationData(ert, "default_0") + + self.assertFloatEqual(data["FOPR"]["2010-01-10"], 0.001696887) + self.assertFloatEqual(data["STD_FOPR"]["2010-01-10"], 0.1) + + self.assertFloatEqual(data["WOPR:OP1"]["2010-03-31"], 0.1) + self.assertFloatEqual(data["STD_WOPR:OP1"]["2010-03-31"], 0.05) + + with self.assertRaises(KeyError): + fgir = data["FGIR"] + + data = SummaryObservationCollector.loadObservationData( + ert, "default_0", ["WOPR:OP1"] + ) + + self.assertFloatEqual(data["WOPR:OP1"]["2010-03-31"], 0.1) + self.assertFloatEqual(data["STD_WOPR:OP1"]["2010-03-31"], 0.05) + + with self.assertRaises(KeyError): + data["FOPR"] diff --git a/libres/tests/res/enkf/plot/__init__.py b/libres/tests/res/enkf/plot/__init__.py new file mode 100644 index 00000000000..b5e3ebb4f95 --- /dev/null +++ b/libres/tests/res/enkf/plot/__init__.py @@ -0,0 +1 @@ +__author__ = "jpb" diff --git a/libres/tests/res/enkf/plot/test_plot_data.py b/libres/tests/res/enkf/plot/test_plot_data.py new file mode 100644 index 00000000000..0e86136cc47 --- /dev/null +++ b/libres/tests/res/enkf/plot/test_plot_data.py @@ -0,0 +1,96 @@ +import pytest + +from tests import ResTest +from res.test import ErtTestContext + +from res.enkf.plot_data import PlotBlockData, PlotBlockDataLoader, PlotBlockVector +from ecl.util.util import DoubleVector + + +@pytest.mark.equinor_test +class PlotDataTest(ResTest): + def setUp(self): + self.config_file = self.createTestPath("Equinor/config/with_RFT/config") + + def test_plot_block_vector(self): + vector = DoubleVector() + vector.append(1.5) + vector.append(2.5) + vector.append(3.5) + plot_block_vector = PlotBlockVector(1, vector) + + self.assertEqual(plot_block_vector.getRealizationNumber(), 1) + self.assertEqual(plot_block_vector[0], 1.5) + self.assertEqual(plot_block_vector[2], 3.5) + + self.assertEqual(len(plot_block_vector), len(vector)) + + def test_plot_block_data(self): + depth = DoubleVector() + depth.append(2.5) + depth.append(3.5) + + data = PlotBlockData(depth) + + self.assertEqual(data.getDepth(), depth) + + vector = PlotBlockVector(1, DoubleVector()) + data.addPlotBlockVector(vector) + data.addPlotBlockVector(PlotBlockVector(2, DoubleVector())) + + self.assertEqual(len(data), 2) + + self.assertEqual(vector, data[1]) + + def compareLists(self, source, target): + self.assertEqual(len(source), len(target)) + for index, value in enumerate(source): + self.assertEqual(value, target[index]) + + def checkBlockData(self, ert, obs_key, report_step): + """ + @type ert: EnKFMain + @type obs_key: str + @type report_step: int + """ + enkf_obs = ert.getObservations() + obs_vector = enkf_obs[obs_key] + loader = PlotBlockDataLoader(obs_vector) + + fs = ert.getEnkfFsManager().getCurrentFileSystem() + plot_block_data = loader.load(fs, report_step) + + self.assertEqual(ert.getEnsembleSize(), len(plot_block_data)) + + depth = plot_block_data.getDepth() + + depth_test_values = [1752.24998474, 1757.88926697, 1760.70924377] + if report_step == 56: + depth_test_values.append(1763.52885437) + + self.assertAlmostEqualList(depth_test_values, depth) + + block_obs = len(obs_vector.getNode(report_step)) + self.assertEqual(block_obs, len(plot_block_data[0])) + self.assertEqual(block_obs, len(plot_block_data[9])) + + if report_step == 50: + rft_values = [244.681655884, 245.217041016, 245.48500061] + else: + rft_values = [239.7550354, 240.290313721, 240.558197021, 240.825881958] + + self.assertAlmostEqualList(rft_values, plot_block_data[0]) + + if report_step == 50: + rft_values = [238.702560425, 239.237838745, 239.505737305] + else: + rft_values = [234.41583252, 234.95098877, 235.218841553, 235.486480713] + + self.assertAlmostEqualList(rft_values, plot_block_data[9]) + + def test_plot_block_data_fs(self): + with ErtTestContext("plot_block_data_test", self.config_file) as test_context: + ert = test_context.getErt() + + self.checkBlockData(ert, "RFT2", 50) + self.checkBlockData(ert, "RFT5", 56) diff --git a/libres/tests/res/enkf/test_active_list.py b/libres/tests/res/enkf/test_active_list.py new file mode 100644 index 00000000000..0d24b81ea22 --- /dev/null +++ b/libres/tests/res/enkf/test_active_list.py @@ -0,0 +1,64 @@ +# Copyright (C) 2015 Equinor ASA, Norway. +# +# The file 'test_active_list.py' 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. + +from res.enkf import ActiveList +from res.enkf import ActiveMode +from tests import ResTest + + +class ActiveListTest(ResTest): + def test_active_mode_enum(self): + self.assertEqual(ActiveMode.ALL_ACTIVE, 1) + self.assertEqual(ActiveMode.INACTIVE, 2) + self.assertEqual(ActiveMode.PARTLY_ACTIVE, 3) + self.assertEqual(ActiveMode(1).name, "ALL_ACTIVE") + self.assertEqual(ActiveMode(2).name, "INACTIVE") + self.assertEqual(ActiveMode(3).name, "PARTLY_ACTIVE") + + def test_active_size(self): + al = ActiveList() + self.assertEqual(None, al.getActiveSize(None)) + self.assertEqual(7, al.getActiveSize(7)) + self.assertEqual(-1, al.getActiveSize(-1)) + + al.addActiveIndex(10) + self.assertEqual(1, al.getActiveSize(7)) + al.addActiveIndex(10) + self.assertEqual(1, al.getActiveSize(7)) + al.addActiveIndex(100) + self.assertEqual(2, al.getActiveSize(7)) + + def test_create(self): + active_list = ActiveList() + self.assertEqual(active_list.getMode(), ActiveMode.ALL_ACTIVE) + active_list.addActiveIndex(10) + self.assertEqual(active_list.getMode(), ActiveMode.PARTLY_ACTIVE) + + def test_repr(self): + al = ActiveList() + rep = repr(al) + self.assertFalse("PARTLY_ACTIVE" in rep) + self.assertFalse("INACTIVE" in rep) + self.assertTrue("ALL_ACTIVE" in rep) + pfx = "ActiveList(" + self.assertEqual(pfx, rep[: len(pfx)]) + for i in range(150): + al.addActiveIndex(3 * i) + rep = repr(al) + self.assertTrue("150" in rep) + self.assertTrue("PARTLY_ACTIVE" in rep) + self.assertFalse("INACTIVE" in rep) + self.assertFalse("ALL_ACTIVE" in rep) diff --git a/libres/tests/res/enkf/test_analysis_config.py b/libres/tests/res/enkf/test_analysis_config.py new file mode 100644 index 00000000000..3c8e949703a --- /dev/null +++ b/libres/tests/res/enkf/test_analysis_config.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'test_analysis_config.py' 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. + +import os + +from ecl.util.test import TestAreaContext +from tests import ResTest + +from res.enkf import AnalysisConfig +from res.enkf import ConfigKeys + + +class AnalysisConfigTest(ResTest): + def setUp(self): + self.case_directory = self.createTestPath("local/simple_config/") + self.case_file = "simple_config/minimum_config" + + def test_invalid_user_config(self): + with TestAreaContext("void land"): + with self.assertRaises(IOError): + AnalysisConfig("this/is/not/a/file") + + def test_keywords_for_monitoring_simulation_runtime(self): + with TestAreaContext("analysis_config_init_test") as work_area: + work_area.copy_directory(self.case_directory) + ac = AnalysisConfig(self.case_file) + + # Unless the MIN_REALIZATIONS is set in config, one is required to have "all" realizations. + self.assertFalse(ac.haveEnoughRealisations(5, 10)) + self.assertTrue(ac.haveEnoughRealisations(10, 10)) + + ac.set_max_runtime(50) + self.assertEqual(50, ac.get_max_runtime()) + + ac.set_stop_long_running(True) + self.assertTrue(ac.get_stop_long_running()) + + def test_analysis_modules(self): + with TestAreaContext("analysis_config_init_test") as work_area: + work_area.copy_directory(self.case_directory) + ac = AnalysisConfig(self.case_file) + self.assertIsNotNone(ac.activeModuleName()) + self.assertIsNotNone(ac.getModuleList()) + + def test_analysis_config_global_std_scaling(self): + with TestAreaContext("analysis_config_init_test") as work_area: + work_area.copy_directory(self.case_directory) + ac = AnalysisConfig(self.case_file) + self.assertFloatEqual(ac.getGlobalStdScaling(), 1.0) + ac.setGlobalStdScaling(0.77) + self.assertFloatEqual(ac.getGlobalStdScaling(), 0.77) + + def test_init(self): + with TestAreaContext("analysis_config_init_test") as work_area: + work_area.copy_directory(self.case_directory) + analysis_config = AnalysisConfig(self.case_file) + self.assertIsNotNone(analysis_config) + + def test_analysis_config_constructor(self): + with TestAreaContext("analysis_config_constructor_test") as work_area: + work_area.copy_directory(self.case_directory) + config_dict = { + ConfigKeys.ALPHA_KEY: 3, + ConfigKeys.RERUN_KEY: False, + ConfigKeys.RERUN_START_KEY: 0, + ConfigKeys.MERGE_OBSERVATIONS: False, + ConfigKeys.UPDATE_LOG_PATH: "update_log", + ConfigKeys.STD_CUTOFF_KEY: 1e-6, + ConfigKeys.STOP_LONG_RUNNING: False, + ConfigKeys.SINGLE_NODE_UPDATE: False, + ConfigKeys.STD_CORRELATED_OBS: False, + ConfigKeys.GLOBAL_STD_SCALING: 1, + ConfigKeys.MAX_RUNTIME: 0, + ConfigKeys.MIN_REALIZATIONS: 0, + ConfigKeys.ANALYSIS_LOAD: [ + { + ConfigKeys.USER_NAME: "RML_ENKF", + ConfigKeys.LIB_NAME: "rml_enkf.so", + }, + { + ConfigKeys.USER_NAME: "MODULE_ENKF", + ConfigKeys.LIB_NAME: "rml_enkf.so", + }, + ], + ConfigKeys.ANALYSIS_COPY: [ + { + ConfigKeys.SRC_NAME: "STD_ENKF", + ConfigKeys.DST_NAME: "ENKF_HIGH_TRUNCATION", + } + ], + ConfigKeys.ANALYSIS_SET_VAR: [ + { + ConfigKeys.MODULE_NAME: "STD_ENKF", + ConfigKeys.VAR_NAME: "ENKF_NCOMP", + ConfigKeys.VALUE: 2, + }, + { + ConfigKeys.MODULE_NAME: "ENKF_HIGH_TRUNCATION", + ConfigKeys.VAR_NAME: "ENKF_TRUNCATION", + ConfigKeys.VALUE: 0.99, + }, + ], + ConfigKeys.ANALYSIS_SELECT: "ENKF_HIGH_TRUNCATION", + } + _config_file = "simple_config/analysis_config" + analysis_config_file = AnalysisConfig(user_config_file=_config_file) + analysis_config_dict = AnalysisConfig(config_dict=config_dict) + self.assertEqual(analysis_config_dict, analysis_config_file) diff --git a/libres/tests/res/enkf/test_analysis_iter_config.py b/libres/tests/res/enkf/test_analysis_iter_config.py new file mode 100644 index 00000000000..0afa4eba602 --- /dev/null +++ b/libres/tests/res/enkf/test_analysis_iter_config.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +# Copyright (C) 2014 Equinor ASA, Norway. +# +# The file 'test_analysis_iter_config.py' 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. + +import os +from res.enkf import AnalysisIterConfig +from tests import ResTest + + +class AnalysisIterConfigTest(ResTest): + def test_set_analysis_iter_config(self): + c = AnalysisIterConfig() + + self.assertFalse(c.caseFormatSet()) + c.setCaseFormat("case%d") + self.assertTrue(c.caseFormatSet()) + + self.assertFalse(c.numIterationsSet()) + c.setNumIterations(1) + self.assertTrue(c.numIterationsSet()) + + def test_analysis_iter_config_constructor(self): + config_dict = { + "ITER_CASE": "ITERATED_ENSEMBLE_SMOOTHER%d", + "ITER_COUNT": 4, + "ITER_RETRY_COUNT": 4, + } + c_default = AnalysisIterConfig() + c_dict = AnalysisIterConfig(config_dict=config_dict) + self.assertEqual(c_default, c_dict) diff --git a/libres/tests/res/enkf/test_block_obs.py b/libres/tests/res/enkf/test_block_obs.py new file mode 100644 index 00000000000..9763255f75e --- /dev/null +++ b/libres/tests/res/enkf/test_block_obs.py @@ -0,0 +1,53 @@ +# Copyright (C) 2015 Equinor ASA, Norway. +# +# The file 'test_block_obs.py' 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. + +import os + +from res.enkf import BlockObservation +from res.enkf import ActiveList, FieldConfig +from tests import ResTest +from ecl.grid import EclGridGenerator + + +class BlockObsTest(ResTest): + def test_create(self): + grid = EclGridGenerator.create_rectangular((10, 20, 5), (1, 1, 1)) + field_config = FieldConfig("PRESSURE", grid) + block_obs = BlockObservation("P-CONFIG", field_config, grid) + + self.assertEqual(len(block_obs), 0) + + block_obs.addPoint(1, 2, 3, 100, 25) + self.assertEqual(len(block_obs), 1) + self.assertEqual(block_obs.getValue(0), 100) + self.assertEqual(block_obs.getStd(0), 25) + self.assertEqual(block_obs.getStdScaling(0), 1) + + block_obs.addPoint(1, 2, 4, 200, 50) + self.assertEqual(len(block_obs), 2) + self.assertEqual(block_obs.getValue(1), 200) + self.assertEqual(block_obs.getStd(1), 50) + self.assertEqual(block_obs.getStdScaling(1), 1) + + active_list = ActiveList() + block_obs.updateStdScaling(0.50, active_list) + self.assertEqual(block_obs.getStdScaling(0), 0.50) + self.assertEqual(block_obs.getStdScaling(1), 0.50) + + active_list.addActiveIndex(1) + block_obs.updateStdScaling(2.00, active_list) + self.assertEqual(block_obs.getStdScaling(0), 0.50) + self.assertEqual(block_obs.getStdScaling(1), 2.00) diff --git a/libres/tests/res/enkf/test_data_kw_define.py b/libres/tests/res/enkf/test_data_kw_define.py new file mode 100644 index 00000000000..71d792e0e1d --- /dev/null +++ b/libres/tests/res/enkf/test_data_kw_define.py @@ -0,0 +1,29 @@ +# Copyright (C) 2017 Equinor ASA, Norway. +# +# The file 'test_data_kw_define.py' 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. +import os + +from tests import ResTest +from res.test import ErtTestContext + + +class DataKWTest(ResTest): + def test_it(self): + config = self.createTestPath("local/mini_ert/mini_config_define") + with ErtTestContext("mini_config_define", config) as context: + ert = context.getErt() + data_kw = ert.getDataKW() + my_path = data_kw["MY_PATH"] + self.assertEqual(my_path, os.getcwd()) diff --git a/libres/tests/res/enkf/test_deprecation.py b/libres/tests/res/enkf/test_deprecation.py new file mode 100644 index 00000000000..57332e3bfa2 --- /dev/null +++ b/libres/tests/res/enkf/test_deprecation.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +# Copyright (C) 2011 Equinor ASA, Norway. +# +# The file 'test_deprecation.py' 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. +import warnings + +from tests import ResTest +from res.test import ErtTestContext + + +class DeprecationTest(ResTest): + pass diff --git a/libres/tests/res/enkf/test_ecl_config.py b/libres/tests/res/enkf/test_ecl_config.py new file mode 100644 index 00000000000..b1343944c96 --- /dev/null +++ b/libres/tests/res/enkf/test_ecl_config.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python +# Copyright (C) 2013 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. + +import pytest + +import os.path + +from res.enkf import EclConfig, ResConfig, ConfigKeys +from ecl.util.test import TestAreaContext +from tests import ResTest +from res.util import UIReturn +from ecl.summary import EclSum + +EGRID_file = "Equinor/ECLIPSE/Gurbat/ECLIPSE.EGRID" +SMSPEC_file = "Equinor/ECLIPSE/Gurbat/ECLIPSE.SMSPEC" +DATA_file = "Equinor/ECLIPSE/Gurbat/ECLIPSE.DATA" +INIT_file = "Equinor/ECLIPSE/Gurbat/EQUIL.INC" +DATA_INIT_file = "Equinor/ECLIPSE/Gurbat/ECLIPSE_INIT.DATA" + + +class EclConfigTest(ResTest): + @pytest.mark.equinor_test + def test_grid(self): + grid_file = self.createTestPath(EGRID_file) + smspec_file = self.createTestPath(SMSPEC_file) + ec = EclConfig() + ui = ec.validateGridFile(grid_file) + self.assertTrue(ui) + self.assertTrue(isinstance(ui, UIReturn)) + + ui = ec.validateGridFile("Does/Not/Exist") + self.assertFalse(ui) + + self.assertTrue(os.path.exists(smspec_file)) + ui = ec.validateGridFile(smspec_file) + self.assertFalse(ui) + + @pytest.mark.equinor_test + def test_datafile(self): + ec = EclConfig() + ui = ec.validateDataFile("DoesNotExist") + self.assertFalse(ui) + + dfile = self.createTestPath(DATA_file) + ui = ec.validateDataFile(dfile) + self.assertTrue(ui) + ec.setDataFile(dfile) + self.assertEqual(dfile, ec.getDataFile()) + + @pytest.mark.equinor_test + def test_refcase(self): + ec = EclConfig() + dfile = self.createTestPath(DATA_file) + + ui = ec.validateRefcase("Does/not/exist") + self.assertFalse(ui) + + ui = ec.validateRefcase(dfile) + self.assertTrue(ui) + ec.loadRefcase(dfile) + refcase = ec.getRefcase() + self.assertTrue(isinstance(refcase, EclSum)) + refcaseName = ec.getRefcaseName() + ".DATA" + self.assertEqual(dfile, refcaseName) + + def test_ecl_config_constructor(self): + config_dict = { + ConfigKeys.DATA_FILE: "configuration_tests/input/SPE1.DATA", + ConfigKeys.ECLBASE: "configuration_tests/input/-%d", + ConfigKeys.GRID: "configuration_tests/input/CASE.EGRID", + ConfigKeys.REFCASE: "configuration_tests/input/SNAKE_OIL_FIELD", + ConfigKeys.END_DATE: "10/10/2010", + ConfigKeys.SCHEDULE_PREDICTION_FILE: "configuration_tests/input/schedule.sch", + } + + self.case_directory = self.createTestPath("local/configuration_tests/") + with TestAreaContext("ecl_config_test") as work_area: + work_area.copy_directory(self.case_directory) + res_config = ResConfig("configuration_tests/ecl_config.ert") + ecl_config_file = res_config.ecl_config + ecl_config_dict = EclConfig(config_dict=config_dict) + + self.assertEqual(ecl_config_dict, ecl_config_file) diff --git a/libres/tests/res/enkf/test_enkf.py b/libres/tests/res/enkf/test_enkf.py new file mode 100755 index 00000000000..b01c3d65a33 --- /dev/null +++ b/libres/tests/res/enkf/test_enkf.py @@ -0,0 +1,316 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'test_enkf.py' 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. + +import sys, os +import os.path +import pytest +from tests import ResTest + +from ecl.util.util import BoolVector +from res.enkf import ( + EnsembleConfig, + AnalysisConfig, + ModelConfig, + SiteConfig, + EclConfig, + EnkfObs, + ErtTemplates, + EnkfFs, + EnKFState, + EnkfVarType, + ObsVector, + RunArg, + ResConfig, +) +from res.enkf.config import EnkfConfigNode +from res.enkf.enkf_main import EnKFMain +from res.enkf.enums import ( + EnkfObservationImplementationType, + LoadFailTypeEnum, + EnkfInitModeEnum, + ErtImplType, + RealizationStateEnum, + EnkfRunType, + EnkfFieldFileFormatEnum, + EnkfTruncationType, + ActiveMode, +) + +from ecl.util.test import TestAreaContext +from res.enkf.observations.summary_observation import SummaryObservation + +from tests.utils import tmpdir + + +class EnKFTest(ResTest): + def setUp(self): + self.case_directory = self.createTestPath("local/simple_config/") + self.case_directory_snake_oil = self.createTestPath("local/snake_oil/") + + @tmpdir() + def test_repr(self): + with TestAreaContext("enkf_test", store_area=True) as work_area: + work_area.copy_directory(self.case_directory) + res_config = ResConfig("simple_config/minimum_config") + main = EnKFMain(res_config) + pfx = "EnKFMain(ensemble_size" + self.assertEqual(pfx, repr(main)[: len(pfx)]) + + @tmpdir() + def test_bootstrap(self): + with TestAreaContext("enkf_test", store_area=True) as work_area: + work_area.copy_directory(self.case_directory) + res_config = ResConfig("simple_config/minimum_config") + main = EnKFMain(res_config) + self.assertTrue(main, "Load failed") + + @tmpdir() + def test_site_condif(self): + with TestAreaContext("enkf_test", store_area=True) as work_area: + work_area.copy_directory(self.case_directory) + res_config = ResConfig("simple_config/minimum_config") + main = EnKFMain(res_config) + + self.assertTrue(main, "Load failed") + + self.assertEqual( + res_config.site_config_file, main.resConfig().site_config_file + ) + + self.assertEqual( + res_config.user_config_file, main.resConfig().user_config_file + ) + + @tmpdir() + def test_site_bootstrap(self): + with TestAreaContext("enkf_test", store_area=True) as work_area: + with self.assertRaises(ValueError): + EnKFMain(None) + + @tmpdir() + def test_default_res_config(self): + with TestAreaContext("enkf_test", store_area=True) as work_area: + work_area.copy_directory(self.case_directory) + res_config = ResConfig("simple_config/minimum_config") + main = EnKFMain(res_config) + + self.assertIsNotNone(main.resConfig) + self.assertIsNotNone(main.siteConfig) + self.assertIsNotNone(main.analysisConfig) + + @tmpdir() + def test_invalid_res_config(self): + with TestAreaContext("enkf_test") as work_area: + with self.assertRaises(TypeError): + work_area.copy_directory(self.case_directory) + main = EnKFMain(res_config="This is not a ResConfig instance") + + @tmpdir() + def test_invalid_parameter_count_2_res_config(self): + with TestAreaContext("enkf_test") as work_area: + with self.assertRaises(ValueError): + work_area.copy_directory(self.case_directory) + res_config = ResConfig(user_config_file="a", config="b") + + @tmpdir() + def test_invalid_parameter_count_3_res_config(self): + with TestAreaContext("enkf_test") as work_area: + with self.assertRaises(ValueError): + work_area.copy_directory(self.case_directory) + res_config = ResConfig( + user_config_file="a", config="b", config_dict="c" + ) + + @tmpdir() + def test_enum(self): + self.assertEnumIsFullyDefined( + EnkfVarType, "enkf_var_type", "lib/include/ert/enkf/enkf_types.hpp" + ) + self.assertEnumIsFullyDefined( + ErtImplType, "ert_impl_type", "lib/include/ert/enkf/enkf_types.hpp" + ) + self.assertEnumIsFullyDefined( + EnkfInitModeEnum, "init_mode_type", "lib/include/ert/enkf/enkf_types.hpp" + ) + self.assertEnumIsFullyDefined( + RealizationStateEnum, + "realisation_state_enum", + "lib/include/ert/enkf/enkf_types.hpp", + ) + self.assertEnumIsFullyDefined( + EnkfTruncationType, "truncation_type", "lib/include/ert/enkf/enkf_types.hpp" + ) + self.assertEnumIsFullyDefined( + EnkfRunType, "run_mode_type", "lib/include/ert/enkf/enkf_types.hpp" + ) + + self.assertEnumIsFullyDefined( + EnkfObservationImplementationType, + "obs_impl_type", + "lib/include/ert/enkf/obs_vector.hpp", + ) + self.assertEnumIsFullyDefined( + LoadFailTypeEnum, + "load_fail_type", + "lib/include/ert/enkf/summary_config.hpp", + ) + self.assertEnumIsFullyDefined( + EnkfFieldFileFormatEnum, + "field_file_format_type", + "lib/include/ert/enkf/field_config.hpp", + ) + self.assertEnumIsFullyDefined( + ActiveMode, "active_mode_type", "lib/include/ert/enkf/enkf_types.hpp" + ) + + @tmpdir() + def test_observations(self): + with TestAreaContext("enkf_test") as work_area: + work_area.copy_directory(self.case_directory) + + res_config = ResConfig("simple_config/minimum_config") + main = EnKFMain(res_config) + + count = 10 + summary_key = "test_key" + observation_key = "test_obs_key" + summary_observation_node = EnkfConfigNode.createSummaryConfigNode( + summary_key, LoadFailTypeEnum.LOAD_FAIL_EXIT + ) + observation_vector = ObsVector( + EnkfObservationImplementationType.SUMMARY_OBS, + observation_key, + summary_observation_node, + count, + ) + + main.getObservations().addObservationVector(observation_vector) + + values = [] + for index in range(0, count): + value = index * 10.5 + std = index / 10.0 + summary_observation_node = SummaryObservation( + summary_key, observation_key, value, std + ) + observation_vector.installNode(index, summary_observation_node) + self.assertEqual( + observation_vector.getNode(index), summary_observation_node + ) + self.assertEqual(value, summary_observation_node.getValue()) + values.append((index, value, std)) + + observations = main.getObservations() + test_vector = observations[observation_key] + index = 0 + for node in test_vector: + self.assertTrue(isinstance(node, SummaryObservation)) + self.assertEqual(node.getValue(), index * 10.5) + index += 1 + + self.assertEqual(observation_vector, test_vector) + for index, value, std in values: + self.assertTrue(test_vector.isActive(index)) + + summary_observation_node = test_vector.getNode(index) + """@type: SummaryObservation""" + + self.assertEqual(value, summary_observation_node.getValue()) + self.assertEqual(std, summary_observation_node.getStandardDeviation()) + self.assertEqual(summary_key, summary_observation_node.getSummaryKey()) + + @tmpdir() + def test_config(self): + with TestAreaContext("enkf_test") as work_area: + work_area.copy_directory(self.case_directory) + + res_config = ResConfig("simple_config/minimum_config") + main = EnKFMain(res_config) + + self.assertIsInstance(main.ensembleConfig(), EnsembleConfig) + self.assertIsInstance(main.analysisConfig(), AnalysisConfig) + self.assertIsInstance(main.getModelConfig(), ModelConfig) + self.assertIsInstance(main.siteConfig(), SiteConfig) + self.assertIsInstance(main.eclConfig(), EclConfig) + + self.assertIsInstance(main.getObservations(), EnkfObs) + self.assertIsInstance(main.get_templates(), ErtTemplates) + self.assertIsInstance( + main.getEnkfFsManager().getCurrentFileSystem(), EnkfFs + ) + self.assertIsInstance(main.getMemberRunningState(0), EnKFState) + + self.assertEqual("simple_config/Ensemble", main.getMountPoint()) + + @tmpdir() + def test_run_context(self): + with TestAreaContext("enkf_test") as work_area: + work_area.copy_directory(self.case_directory) + res_config = ResConfig("simple_config/minimum_config") + main = EnKFMain(res_config) + fs_manager = main.getEnkfFsManager() + fs = fs_manager.getCurrentFileSystem() + iactive = BoolVector(initial_size=10, default_value=True) + iactive[0] = False + iactive[1] = False + run_context = main.getRunContextENSEMPLE_EXPERIMENT(fs, iactive) + + self.assertEqual(len(run_context), 10) + + with self.assertRaises(IndexError): + run_context[10] + + with self.assertRaises(TypeError): + run_context["String"] + + self.assertIsNone(run_context[0]) + run_arg = run_context[2] + self.assertTrue(isinstance(run_arg, RunArg)) + + rng1 = main.rng() + rng1.setState("ABCDEFGHIJ012345") + d1 = rng1.getDouble() + rng1.setState("ABCDEFGHIJ012345") + rng2 = main.rng() + d2 = rng2.getDouble() + self.assertEqual(d1, d2) + + @tmpdir() + def test_run_context_from_external_folder(self): + with TestAreaContext("enkf_test") as work_area: + work_area.copy_directory(self.case_directory_snake_oil) + res_config = ResConfig("snake_oil/snake_oil.ert") + main = EnKFMain(res_config) + fs_manager = main.getEnkfFsManager() + fs = fs_manager.getCurrentFileSystem() + + mask = BoolVector(default_value=False, initial_size=10) + mask[0] = True + run_context = main.getRunContextENSEMPLE_EXPERIMENT(fs, mask) + + self.assertEqual(len(run_context), 10) + + job_queue = main.get_queue_config().create_job_queue() + main.getEnkfSimulationRunner().createRunPath(run_context) + num = main.getEnkfSimulationRunner().runEnsembleExperiment( + job_queue, run_context + ) + self.assertTrue( + os.path.isdir( + "snake_oil/storage/snake_oil/runpath/realisation-0/iter-0" + ) + ) + self.assertEqual(num, 1) diff --git a/libres/tests/res/enkf/test_enkf_fs.py b/libres/tests/res/enkf/test_enkf_fs.py new file mode 100644 index 00000000000..3dc3b84d257 --- /dev/null +++ b/libres/tests/res/enkf/test_enkf_fs.py @@ -0,0 +1,55 @@ +import os +import pytest + +from ecl.util.test import TestAreaContext +from tests import ResTest +from tests.utils import tmpdir +from res.test import ErtTestContext + +from res.enkf import EnkfFs +from res.enkf import EnKFMain +from res.enkf.enums import EnKFFSType + + +@pytest.mark.equinor_test +class EnKFFSTest(ResTest): + def setUp(self): + self.mount_point = "storage/default" + self.config_file = self.createTestPath("Equinor/config/with_data/config") + + def test_id_enum(self): + self.assertEnumIsFullyDefined( + EnKFFSType, "fs_driver_impl", "lib/include/ert/enkf/fs_types.hpp" + ) + + def test_create(self): + with TestAreaContext("create_fs") as work_area: + work_area.copy_parent_content(self.config_file) + + self.assertTrue(EnkfFs.exists(self.mount_point)) + fs = EnkfFs(self.mount_point) + self.assertEqual(1, fs.refCount()) + fs.umount() + + self.assertFalse(EnkfFs.exists("newFS")) + fs = EnkfFs.createFileSystem("newFS") + self.assertTrue(EnkfFs.exists("newFS")) + self.assertTrue(fs is None) + + with self.assertRaises(IOError): + version = EnkfFs.diskVersion("does/not/exist") + + version = EnkfFs.diskVersion("newFS") + self.assertTrue(version >= 106) + + @tmpdir() + def test_create2(self): + with TestAreaContext("create_fs2") as work_area: + work_area.copy_parent_content(self.config_file) + + new_fs = EnkfFs.createFileSystem("newFS", mount=True) + self.assertTrue(isinstance(new_fs, EnkfFs)) + + def test_throws(self): + with self.assertRaises(Exception): + fs = EnkfFs("/does/not/exist") diff --git a/libres/tests/res/enkf/test_enkf_fs_manager1.py b/libres/tests/res/enkf/test_enkf_fs_manager1.py new file mode 100644 index 00000000000..f7c63727dd1 --- /dev/null +++ b/libres/tests/res/enkf/test_enkf_fs_manager1.py @@ -0,0 +1,46 @@ +import os +from res.test import ErtTestContext +from tests import ResTest +from tests.utils import tmpdir + +from res.enkf import EnkfFs +from res.enkf import EnKFMain +from res.enkf import EnkfFsManager + + +class EnKFFSManagerTest1(ResTest): + def setUp(self): + self.config_file = self.createTestPath("local/snake_oil/snake_oil.ert") + + @tmpdir() + def test_create(self): + # We are indirectly testing the create through the create + # already in the enkf_main object. In principle we could + # create a separate manager instance from the ground up, but + # then the reference count will be weird. + with ErtTestContext( + "enkf_fs_manager_create_test", self.config_file + ) as testContext: + ert = testContext.getErt() + fsm = ert.getEnkfFsManager() + + fs = fsm.getCurrentFileSystem() + self.assertTrue(fsm.isCaseMounted("default_0")) + self.assertTrue(fsm.caseExists("default_0")) + self.assertTrue(fsm.caseHasData("default_0")) + self.assertFalse(fsm.isCaseRunning("default_0")) + + self.assertEqual(1, fsm.getFileSystemCount()) + + self.assertFalse(fsm.isCaseMounted("newFS")) + self.assertFalse(fsm.caseExists("newFS")) + self.assertFalse(fsm.caseHasData("newFS")) + self.assertFalse(fsm.isCaseRunning("newFS")) + + fs2 = fsm.getFileSystem("newFS") + self.assertEqual(2, fsm.getFileSystemCount()) + + self.assertTrue(fsm.isCaseMounted("newFS")) + self.assertTrue(fsm.caseExists("newFS")) + self.assertFalse(fsm.caseHasData("newFS")) + self.assertFalse(fsm.isCaseRunning("newFS")) diff --git a/libres/tests/res/enkf/test_enkf_fs_manager2.py b/libres/tests/res/enkf/test_enkf_fs_manager2.py new file mode 100644 index 00000000000..f9ff8f15d72 --- /dev/null +++ b/libres/tests/res/enkf/test_enkf_fs_manager2.py @@ -0,0 +1,52 @@ +import sys +import os +from tests import ResTest +from res.test import ErtTestContext +from tests.utils import tmpdir + + +from res.enkf import EnkfFs +from res.enkf import EnKFMain +from res.enkf import EnkfFsManager + + +class EnKFFSManagerTest2(ResTest): + def setUp(self): + self.config_file = self.createTestPath("local/mini_ert/mini_config") + + @tmpdir() + def test_rotate(self): + + # We are indirectly testing the create through the create + # already in the enkf_main object. In principle we could + # create a separate manager instance from the ground up, but + # then the reference count will be weird. + with ErtTestContext( + "enkf_fs_manager_rotate_test", self.config_file + ) as testContext: + ert = testContext.getErt() + fsm = ert.getEnkfFsManager() + self.assertEqual(0, fsm.getFileSystemCount()) + + fs_list = [] + for index in range(EnkfFsManager.DEFAULT_CAPACITY): + fs_list.append(fsm.getFileSystem("fs_fill_%d" % index)) + + for fs in fs_list: + self.assertEqual(2, fs.refCount()) + fs_copy = fs.copy() + self.assertEqual(3, fs.refCount()) + self.assertEqual(3, fs_copy.refCount()) + + del fs_copy + self.assertEqual(2, fs.refCount()) + + self.assertEqual(EnkfFsManager.DEFAULT_CAPACITY, fsm.getFileSystemCount()) + + for i in range(3 * EnkfFsManager.DEFAULT_CAPACITY): + fs_name = "fs_test_%d" % i + sys.stderr.write("Mounting: %s\n" % fs_name) + fs = fsm.getFileSystem(fs_name) + self.assertEqual( + EnkfFsManager.DEFAULT_CAPACITY, fsm.getFileSystemCount() + ) diff --git a/libres/tests/res/enkf/test_enkf_library.py b/libres/tests/res/enkf/test_enkf_library.py new file mode 100644 index 00000000000..75ffde94de1 --- /dev/null +++ b/libres/tests/res/enkf/test_enkf_library.py @@ -0,0 +1,61 @@ +import os +import pytest + + +from tests import ResTest +from tests.utils import tmpdir + +from ecl.summary import EclSum +from ecl.util.test import TestAreaContext +from res.enkf import ( + AnalysisConfig, + EclConfig, + GenKwConfig, + EnkfConfigNode, + SiteConfig, + ObsVector, +) +from res.enkf import EnKFMain, ResConfig +from res.enkf import ErtTemplate, ErtTemplates, LocalConfig, ModelConfig +from res.enkf import ( + GenDataConfig, + FieldConfig, + EnkfFs, + EnkfObs, + EnKFState, + EnsembleConfig, +) +from res.enkf.util import TimeMap + + +@pytest.mark.unstable +@pytest.mark.equinor_test +class EnKFLibraryTest(ResTest): + def setUp(self): + self.case_directory = self.createTestPath("local/simple_config/") + + def test_failed_class_creation(self): + classes = [EnkfConfigNode, EnKFState, ErtTemplate, LocalConfig] + + for cls in classes: + with self.assertRaises(NotImplementedError): + temp = cls() + + @tmpdir() + def test_ecl_config_creation(self): + with TestAreaContext("enkf_library_test") as work_area: + work_area.copy_directory(self.case_directory) + + res_config = ResConfig("simple_config/minimum_config") + main = EnKFMain(res_config) + + self.assertIsInstance(main.analysisConfig(), AnalysisConfig) + self.assertIsInstance(main.eclConfig(), EclConfig) + + with self.assertRaises(AssertionError): # Null pointer! + self.assertIsInstance(main.eclConfig().getRefcase(), EclSum) + + file_system = main.getEnkfFsManager().getCurrentFileSystem() + self.assertEqual(file_system.getCaseName(), "default") + time_map = file_system.getTimeMap() + self.assertIsInstance(time_map, TimeMap) diff --git a/libres/tests/res/enkf/test_enkf_load_results_manually.py b/libres/tests/res/enkf/test_enkf_load_results_manually.py new file mode 100755 index 00000000000..0298d11086b --- /dev/null +++ b/libres/tests/res/enkf/test_enkf_load_results_manually.py @@ -0,0 +1,76 @@ +import pytest + +from tests import ResTest +from tests.utils import tmpdir +from res.test import ErtTestContext + +from res.enkf.enums.realization_state_enum import RealizationStateEnum +from ecl.util.util import BoolVector + +from tests.utils import tmpdir + + +@pytest.mark.unstable +@pytest.mark.equinor_test +class LoadResultsManuallyTest(ResTest): + def setUp(self): + self.config_file = self.createTestPath("Equinor/config/with_data/config") + + @tmpdir() + def test_load_results_manually(self): + with ErtTestContext("manual_load_test", self.config_file) as test_context: + ert = test_context.getErt() + load_into_case = "A1" + load_from_case = "default" + + load_into = ert.getEnkfFsManager().getFileSystem(load_into_case) + load_from = ert.getEnkfFsManager().getFileSystem(load_from_case) + + ert.getEnkfFsManager().switchFileSystem(load_from) + realisations = BoolVector(default_value=True, initial_size=25) + realisations[7] = False + iteration = 0 + + loaded = ert.loadFromForwardModel(realisations, iteration, load_into) + + load_into_case_state_map = load_into.getStateMap() + + load_into_states = [state for state in load_into_case_state_map] + + expected = [RealizationStateEnum.STATE_HAS_DATA] * 25 + expected[7] = RealizationStateEnum.STATE_UNDEFINED + + self.assertListEqual(load_into_states, expected) + self.assertEqual(24, loaded) + self.assertEqual(25, len(expected)) + self.assertEqual(25, len(realisations)) + + @tmpdir() + def test_load_results_from_run_context(self): + with ErtTestContext("manual_load_test", self.config_file) as test_context: + ert = test_context.getErt() + load_into_case = "A1" + load_from_case = "default" + + load_into = ert.getEnkfFsManager().getFileSystem(load_into_case) + load_from = ert.getEnkfFsManager().getFileSystem(load_from_case) + + ert.getEnkfFsManager().switchFileSystem(load_from) + realisations = BoolVector(default_value=True, initial_size=25) + realisations[7] = False + iteration = 0 + + run_context = ert.getRunContextENSEMPLE_EXPERIMENT(load_into, realisations) + + loaded = ert.loadFromRunContext(run_context, load_into) + + load_into_case_state_map = load_into.getStateMap() + load_into_states = [state for state in load_into_case_state_map] + + expected = [RealizationStateEnum.STATE_HAS_DATA] * 25 + expected[7] = RealizationStateEnum.STATE_UNDEFINED + + self.assertListEqual(load_into_states, expected) + self.assertEqual(24, loaded) + self.assertEqual(25, len(expected)) + self.assertEqual(25, len(realisations)) diff --git a/libres/tests/res/enkf/test_enkf_obs.py b/libres/tests/res/enkf/test_enkf_obs.py new file mode 100644 index 00000000000..b5ff60326ef --- /dev/null +++ b/libres/tests/res/enkf/test_enkf_obs.py @@ -0,0 +1,271 @@ +import pytest + +from tests import ResTest +from tests.utils import tmpdir +from res.test import ErtTestContext + +from ecl.grid import EclGrid +from ecl.summary import EclSum +from res.sched import History + +from ecl.util.util import BoolVector, IntVector +from res.enkf import ActiveMode, EnsembleConfig +from res.enkf import ( + ObsVector, + LocalObsdata, + EnkfObs, + TimeMap, + LocalObsdataNode, + ObsData, + MeasData, + ActiveList, +) + + +@pytest.mark.equinor_test +class EnKFObsTest(ResTest): + def setUp(self): + self.config_file = self.createTestPath("Equinor/config/obs_testing/config") + self.obs_config = self.createTestPath("Equinor/config/obs_testing/observations") + self.obs_config2 = self.createTestPath( + "Equinor/config/obs_testing/observations2" + ) + self.refcase = self.createTestPath("Equinor/config/obs_testing/EXAMPLE_01_BASE") + self.grid = self.createTestPath( + "Equinor/config/obs_testing/EXAMPLE_01_BASE.EGRID" + ) + + @tmpdir() + def test_scale_obs(self): + with ErtTestContext("obs_test", self.config_file) as test_context: + ert = test_context.getErt() + obs = ert.getObservations() + + obs1 = obs["WWCT:OP_1"].getNode(50) + obs2 = obs["WWCT:OP_1_50"].getNode(50) + + self.assertEqual(obs1.getStandardDeviation(), obs2.getStandardDeviation()) + std0 = obs1.getStandardDeviation() + + local_obsdata = LocalObsdata("obs", obs) + node1 = local_obsdata.addNode("WWCT:OP_1") + node2 = local_obsdata.addNode("WWCT:OP_1_50") + node1.addTimeStep(50) + node2.addTimeStep(50) + + mask = BoolVector(default_value=True) + mask[2] = True + meas_data = MeasData(mask) + obs_data = ObsData() + fs = ert.getEnkfFsManager().getCurrentFileSystem() + active_list = IntVector() + active_list.initRange(0, 2, 1) + obs.getObservationAndMeasureData( + fs, local_obsdata, active_list, meas_data, obs_data + ) + self.assertEqual(2, len(obs_data)) + + v1 = obs_data[0] + v2 = obs_data[1] + + self.assertEqual(v1[1], std0) + self.assertEqual(v2[1], std0) + + meas_data = MeasData(mask) + obs_data = ObsData(10) + obs.getObservationAndMeasureData( + fs, local_obsdata, active_list, meas_data, obs_data + ) + self.assertEqual(2, len(obs_data)) + + v1 = obs_data[0] + v2 = obs_data[1] + + self.assertEqual(v1[1], std0 * 10) + self.assertEqual(v2[1], std0 * 10) + + actl = ActiveList() + obs1.updateStdScaling(10, actl) + obs2.updateStdScaling(20, actl) + meas_data = MeasData(mask) + obs_data = ObsData() + obs.getObservationAndMeasureData( + fs, local_obsdata, active_list, meas_data, obs_data + ) + self.assertEqual(2, len(obs_data)) + + v1 = obs_data[0] + v2 = obs_data[1] + + self.assertEqual(v1[1], std0 * 10) + self.assertEqual(v2[1], std0 * 20) + + @tmpdir() + def testObs(self): + with ErtTestContext("obs_test", self.config_file) as test_context: + ert = test_context.getErt() + obs = ert.getObservations() + + self.assertEqual(32, len(obs)) + for v in obs: + self.assertTrue(isinstance(v, ObsVector)) + + self.assertEqual(obs[-1].getKey(), "RFT_TEST") + self.assertEqual(obs[-1].getDataKey(), "4289383") + self.assertEqual(obs[-1].getObsKey(), "RFT_TEST") + + with self.assertRaises(IndexError): + v = obs[-40] + with self.assertRaises(IndexError): + v = obs[40] + + with self.assertRaises(KeyError): + v = obs["No-this-does-not-exist"] + + v1 = obs["WWCT:OP_3"] + v2 = obs["GOPT:OP"] + mask = BoolVector(True, ert.getEnsembleSize()) + current_fs = ert.getEnkfFsManager().getCurrentFileSystem() + + self.assertTrue(v1.hasData(mask, current_fs)) + self.assertFalse(v2.hasData(mask, current_fs)) + + local_node = v1.createLocalObs() + for t in v1.getStepList(): + self.assertTrue(local_node.tstepActive(t)) + + @tmpdir() + def test_obs_block_scale_std(self): + with ErtTestContext("obs_test_scale", self.config_file) as test_context: + ert = test_context.getErt() + fs = ert.getEnkfFsManager().getCurrentFileSystem() + active_list = IntVector() + active_list.initRange(0, ert.getEnsembleSize(), 1) + + obs = ert.getObservations() + obs_data = LocalObsdata("OBSxx", obs) + obs_vector = obs["WWCT:OP_1"] + obs_data.addObsVector(obs_vector) + scale_factor = obs.scaleCorrelatedStd(fs, obs_data, active_list) + + for obs_node in obs_vector: + for index in range(len(obs_node)): + self.assertEqual(scale_factor, obs_node.getStdScaling(index)) + + def test_obs_block_all_active_local(self): + with ErtTestContext("obs_test_all_active", self.config_file) as test_context: + ert = test_context.getErt() + obs = ert.getObservations() + obs_data = obs.getAllActiveLocalObsdata() + + self.assertEqual(len(obs_data), len(obs)) + for obs_vector in obs: + self.assertIn(obs_vector.getObservationKey(), obs_data) + + tstep_list1 = obs_vector.getStepList() + local_node = obs_data[obs_vector.getObservationKey()] + for t in tstep_list1: + self.assertTrue(local_node.tstepActive(t)) + + active_list = local_node.getActiveList() + self.assertEqual(active_list.getMode(), ActiveMode.ALL_ACTIVE) + + def test_create(self): + ensemble_config = EnsembleConfig() + obs = EnkfObs(ensemble_config) + self.assertEqual(len(obs), 0) + + self.assertFalse(obs.valid) + with self.assertRaises(ValueError): + obs.load(self.obs_config) + self.assertEqual(len(obs), 0) + + time_map = TimeMap() + obs = EnkfObs(ensemble_config, external_time_map=time_map) + self.assertEqual(len(obs), 0) + + grid = EclGrid(self.grid) + refcase = EclSum(self.refcase) + + history = History(refcase, False) + obs = EnkfObs(ensemble_config, grid=grid, history=history) + self.assertTrue(obs.valid) + with self.assertRaises(IOError): + obs.load("/does/not/exist") + + obs.load(self.obs_config) + self.assertTrue(obs.valid) + self.assertEqual(len(obs), 33) + obs.clear() + self.assertEqual(len(obs), 0) + + obs.load(self.obs_config) + self.assertEqual(len(obs), 33) + self.assertNotIn("RFT2", obs) + obs.load(self.obs_config2) + self.assertEqual(len(obs), 35) + self.assertIn("RFT2", obs) + + def test_hookmanager_runpathlist(self): + with ErtTestContext("obs_test", self.config_file) as test_context: + ert = test_context.getErt() + hm = ert.getHookManager() + self.assertGreater(len(repr(hm)), 0) + + rpl = hm.getRunpathList() + self.assertGreater(len(repr(rpl)), 0) + + ef = rpl.getExportFile() + self.assertTrue(".ert_runpath_list" in ef) + nf = "myExportCamel" + rpl.setExportFile("myExportCamel") + ef = rpl.getExportFile() + self.assertTrue(nf in ef) + + @pytest.mark.skip(reason="Aborts") + def test_ert_obs_reload(self): + with ErtTestContext("obs_test_reload", self.config_file) as test_context: + ert = test_context.getErt() + local_config = ert.getLocalConfig() + update_step = local_config.getUpdatestep() + mini_step = update_step[0] + local_obs = mini_step.getLocalObsData() + self.assertTrue("WGOR:OP_5" in local_obs) + self.assertTrue("RPR2_1" in local_obs) + + ens_config = ert.ensembleConfig() + wwct_op1 = ens_config["WWCT:OP_1"] + wopr_op5 = ens_config["WOPR:OP_5"] + + obs = ert.getObservations() + self.assertEqual(len(obs), 32) + + keys = wwct_op1.getObservationKeys() + self.assertEqual(len(keys), 2) + self.assertTrue("WWCT:OP_1" in keys) + self.assertTrue("WWCT:OP_1_50" in keys) + + self.assertEqual(wopr_op5.getObservationKeys(), []) + + ert.loadObservations("observations2") + self.assertEqual(len(obs), 2) + self.assertEqual(wwct_op1.getObservationKeys(), []) + self.assertEqual(wopr_op5.getObservationKeys(), ["WOPR:OP_5"]) + + local_config = ert.getLocalConfig() + update_step = local_config.getUpdatestep() + mini_step = update_step[0] + local_obs = mini_step.getLocalObsData() + self.assertTrue("WOPR:OP_5" in local_obs) + self.assertTrue("RFT2" in local_obs) + self.assertFalse("WGOR:OP_5" in local_obs) + self.assertFalse("RPR2_1" in local_obs) + + ert.loadObservations("observations", clear=False) + self.assertEqual(len(obs), 34) + keys = wwct_op1.getObservationKeys() + self.assertEqual(len(keys), 2) + self.assertTrue("WWCT:OP_1" in keys) + self.assertTrue("WWCT:OP_1_50" in keys) + + self.assertEqual(wopr_op5.getObservationKeys(), ["WOPR:OP_5"]) diff --git a/libres/tests/res/enkf/test_enkf_runpath.py b/libres/tests/res/enkf/test_enkf_runpath.py new file mode 100755 index 00000000000..f1b9a67cef9 --- /dev/null +++ b/libres/tests/res/enkf/test_enkf_runpath.py @@ -0,0 +1,130 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'test_enkf_runpath.py' 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. + +import pytest + +from ecl.util.test import TestAreaContext +from tests import ResTest +from tests.utils import tmpdir +from ecl.util.util import BoolVector + +from res.enkf import ( + EnsembleConfig, + AnalysisConfig, + ModelConfig, + SiteConfig, + EclConfig, + EnkfObs, + ErtTemplates, + EnkfFs, + EnKFState, + EnkfVarType, + ObsVector, + RunArg, + ResConfig, +) +from res.enkf.config import EnkfConfigNode +from res.enkf.enkf_main import EnKFMain +from res.enkf.enums import ( + EnkfObservationImplementationType, + LoadFailTypeEnum, + EnkfInitModeEnum, + ErtImplType, + RealizationStateEnum, + EnkfRunType, + EnkfFieldFileFormatEnum, + EnkfTruncationType, + ActiveMode, +) + +from res.enkf.observations.summary_observation import SummaryObservation + +import os + + +@pytest.mark.unstable +class EnKFRunpathTest(ResTest): + def setUp(self): + pass + + @tmpdir() + def test_with_gen_kw(self): + case_directory = self.createTestPath("local/snake_oil_no_data/") + with TestAreaContext("test_enkf_runpath", store_area=True) as work_area: + work_area.copy_directory(case_directory) + res_config = ResConfig("snake_oil_no_data/snake_oil.ert") + main = EnKFMain(res_config) + iactive = BoolVector( + initial_size=main.getEnsembleSize(), default_value=False + ) + iactive[0] = True + fs = main.getEnkfFsManager().getCurrentFileSystem() + run_context = main.getRunContextENSEMPLE_EXPERIMENT(fs, iactive) + main.createRunpath(run_context) + self.assertFileExists( + "snake_oil_no_data/storage/snake_oil/runpath/realisation-0/iter-0/parameters.txt" + ) + self.assertEqual( + len(os.listdir("snake_oil_no_data/storage/snake_oil/runpath")), 1 + ) + self.assertEqual( + len( + os.listdir( + "snake_oil_no_data/storage/snake_oil/runpath/realisation-0" + ) + ), + 1, + ) + + rp = main.create_runpath_list() + self.assertEqual(len(rp), 0) + rp.load() + self.assertEqual(len(rp), 1) + + @tmpdir() + def test_without_gen_kw(self): + case_directory = self.createTestPath("local/snake_oil_no_data/") + with TestAreaContext("test_enkf_runpath", store_area=False) as work_area: + work_area.copy_directory(case_directory) + res_config = ResConfig("snake_oil_no_data/snake_oil_no_gen_kw.ert") + main = EnKFMain(res_config) + iactive = BoolVector( + initial_size=main.getEnsembleSize(), default_value=False + ) + iactive[0] = True + fs = main.getEnkfFsManager().getCurrentFileSystem() + run_context = main.getRunContextENSEMPLE_EXPERIMENT(fs, iactive) + main.createRunpath(run_context) + self.assertDirectoryExists( + "snake_oil_no_data/storage/snake_oil_no_gen_kw/runpath/realisation-0/iter-0" + ) + self.assertFileDoesNotExist( + "snake_oil_no_data/storage/snake_oil_no_gen_kw/runpath/realisation-0/iter-0/parameters.txt" + ) + self.assertEqual( + len( + os.listdir("snake_oil_no_data/storage/snake_oil_no_gen_kw/runpath") + ), + 1, + ) + self.assertEqual( + len( + os.listdir( + "snake_oil_no_data/storage/snake_oil_no_gen_kw/runpath/realisation-0" + ) + ), + 1, + ) diff --git a/libres/tests/res/enkf/test_enkf_sim_model.py b/libres/tests/res/enkf/test_enkf_sim_model.py new file mode 100644 index 00000000000..a8c80b3aa7a --- /dev/null +++ b/libres/tests/res/enkf/test_enkf_sim_model.py @@ -0,0 +1,155 @@ +# Copyright (C) 2017 Equinor ASA, Norway. +# +# The file 'test_enkf_sim_model.py' 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. + +import os +import sys +import json +import subprocess +import pytest + +from ecl.util.test import TestAreaContext +from tests import ResTest +from tests.utils import tmpdir +from ecl.util.util import BoolVector + +from res.job_queue import ExtJob + +from res.enkf import ( + EnsembleConfig, + AnalysisConfig, + ModelConfig, + SiteConfig, + EclConfig, + EnkfObs, + ErtTemplates, + EnkfFs, + EnKFState, + EnkfVarType, + ObsVector, + RunArg, + ResConfig, +) +from res.enkf.config import EnkfConfigNode +from res.enkf.enkf_main import EnKFMain +from res.enkf.ert_run_context import ErtRunContext +from res.enkf.enums import ( + EnkfObservationImplementationType, + LoadFailTypeEnum, + EnkfInitModeEnum, + ErtImplType, + RealizationStateEnum, + EnkfRunType, + EnkfFieldFileFormatEnum, + EnkfTruncationType, + ActiveMode, +) + +from res.enkf.observations.summary_observation import SummaryObservation +from res.test import ErtTestContext + + +@pytest.mark.unstable +class EnKFTestSimModel(ResTest): + def setUp(self): + pass + + @tmpdir() + def test_simulation_model(self): + + with TestAreaContext("enkf_test_sim_model_kw") as work_area: + base_path = os.getcwd() + source_path = self.createTestPath("local/simulation_model") + + work_area.copy_directory(source_path) + dir_ert = os.path.join(base_path, "simulation_model") + assert os.path.isdir(dir_ert) + + file_ert = os.path.join(dir_ert, "sim_kw.ert") + assert os.path.isfile(file_ert) + + with ErtTestContext( + "sim_kw", model_config=file_ert, store_area=True + ) as ctx: + ert = ctx.getErt() + fs_manager = ert.getEnkfFsManager() + result_fs = fs_manager.getCurrentFileSystem() + + model_config = ert.getModelConfig() + forward_model = model_config.getForwardModel() + self.assertEqual(forward_model.get_size(), 6) + + self.assertEqual(forward_model.iget_job(3).get_arglist(), ["WORD_A"]) + self.assertEqual( + forward_model.iget_job(0).get_arglist(), [""] + ) + self.assertEqual( + forward_model.iget_job(1).get_arglist(), + ["Hello", "True", "3.14", "4"], + ) + self.assertEqual( + forward_model.iget_job(2).get_arglist(), ["word", ""] + ) + + self.assertEqual(forward_model.iget_job(0).get_argvalues(), ["yy"]) + self.assertEqual( + forward_model.iget_job(1).get_argvalues(), + ["Hello", "True", "3.14", "4"], + ) + self.assertEqual( + forward_model.iget_job(2).get_argvalues(), ["word", ""] + ) + self.assertEqual(forward_model.iget_job(3).get_argvalues(), ["WORD_A"]) + self.assertEqual( + list(forward_model.iget_job(4).get_argvalues()), + [ + "configured_argumentA", + "configured_argumentB", + "DEFINED_ARGC_VALUE", + ], + ) + self.assertEqual( + list(forward_model.iget_job(5).get_argvalues()), + ["DEFAULT_ARGA_VALUE", "", "DEFINED_ARGC_VALUE"], + ) + + runpath_fmt = model_config.getRunpathFormat() + jobname_fmt = model_config.getJobnameFormat() + + subst_list = ert.getDataKW() + itr = 0 + mask = BoolVector(default_value=True, initial_size=1) + + run_context = ErtRunContext.ensemble_experiment( + result_fs, mask, runpath_fmt, jobname_fmt, subst_list, itr + ) + ert.getEnkfSimulationRunner().createRunPath(run_context) + queue_config = ert.get_queue_config() + self.assertEqual(queue_config.num_cpu, 5) + os.chdir("storage/sim_kw/runpath/realisation-0/iter-0") + assert os.path.isfile("jobs.json") + with open("jobs.json", "r") as f: + data = json.load(f) + jobList = data["jobList"] + old_job_A = jobList[3] + self.assertEqual(old_job_A["argList"], ["WORD_A"]) + old_job_B = jobList[0] + self.assertEqual(old_job_B["argList"], ["yy"]) + new_job_A = jobList[1] + self.assertEqual( + new_job_A["argList"], ["Hello", "True", "3.14", "4"] + ) + new_job_B = jobList[2] + self.assertEqual(new_job_B["argList"], ["word", "SIM_KW"]) diff --git a/libres/tests/res/enkf/test_enkf_simulation_runner.py b/libres/tests/res/enkf/test_enkf_simulation_runner.py new file mode 100644 index 00000000000..36fb876ffdc --- /dev/null +++ b/libres/tests/res/enkf/test_enkf_simulation_runner.py @@ -0,0 +1,71 @@ +from res.job_queue import JobStatusType, JobQueue +from unittest import TestCase, mock + + +class MockedJob: + def __init__(self, status): + self.status = status + self._start_time = 0 + self._current_time = 0 + + @property + def runtime(self): + return self._end_time - self._start_time + + def stop(self): + self.status = JobStatusType.JOB_QUEUE_FAILED + + def convertToCReference(self, _): + pass + + +class EnkfSimulationRunnerTest(TestCase): + def test_stop_long_running(self): + """ + This test should verify that only the jobs that are has a runtime + 25% longer than the average completed are stopped when + stop_long_running_jobs is called. + """ + job_list = [MockedJob(JobStatusType.JOB_QUEUE_WAITING) for i in range(10)] + + for i in range(5): + job_list[i].status = JobStatusType.JOB_QUEUE_DONE + job_list[i]._start_time = 0 + job_list[i]._end_time = 10 + + for i in range(5, 8): + job_list[i].status = JobStatusType.JOB_QUEUE_RUNNING + job_list[i]._start_time = 0 + job_list[i]._end_time = 20 + + for i in range(8, 10): + job_list[i].status = JobStatusType.JOB_QUEUE_RUNNING + job_list[i]._start_time = 0 + job_list[i]._end_time = 5 + + # The driver is of no consequence, so resolving it in the c layer is + # uninteresting and mocked out. + with mock.patch("res.job_queue.JobQueue._set_driver"): + queue = JobQueue(mock.MagicMock()) + + # We don't need the c layer call here, we only need it added to + # the queue's job_list. + with mock.patch("res.job_queue.JobQueue._add_job") as _add_job: + for idx, job in enumerate(job_list): + _add_job.return_value = idx + queue.add_job(job, idx) + + queue.stop_long_running_jobs(5) + queue._transition() + + for i in range(5): + assert job_list[i].status == JobStatusType.JOB_QUEUE_DONE + assert queue.snapshot()[i] == str(JobStatusType.JOB_QUEUE_DONE) + + for i in range(5, 8): + assert job_list[i].status == JobStatusType.JOB_QUEUE_FAILED + assert queue.snapshot()[i] == str(JobStatusType.JOB_QUEUE_FAILED) + + for i in range(8, 10): + assert job_list[i].status == JobStatusType.JOB_QUEUE_RUNNING + assert queue.snapshot()[i] == str(JobStatusType.JOB_QUEUE_RUNNING) diff --git a/libres/tests/res/enkf/test_enkf_transfer_env.py b/libres/tests/res/enkf/test_enkf_transfer_env.py new file mode 100644 index 00000000000..16a30efd3d0 --- /dev/null +++ b/libres/tests/res/enkf/test_enkf_transfer_env.py @@ -0,0 +1,108 @@ +# Copyright (C) 2017 Equinor ASA, Norway. +# +# The file 'test_enkf_transfer_env.py' 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. + +import os +import sys +import json +import subprocess +import pytest + +from ecl.util.test import TestAreaContext +from tests import ResTest +from tests.utils import tmpdir +from ecl.util.util import BoolVector + +from res.enkf import ( + EnsembleConfig, + AnalysisConfig, + ModelConfig, + SiteConfig, + EclConfig, + EnkfObs, + ErtTemplates, + EnkfFs, + EnKFState, + EnkfVarType, + ObsVector, + RunArg, + ResConfig, +) +from res.enkf.config import EnkfConfigNode +from res.enkf.enkf_main import EnKFMain +from res.enkf.ert_run_context import ErtRunContext +from res.enkf.enums import ( + EnkfObservationImplementationType, + LoadFailTypeEnum, + EnkfInitModeEnum, + ErtImplType, + RealizationStateEnum, + EnkfRunType, + EnkfFieldFileFormatEnum, + EnkfTruncationType, + ActiveMode, +) + +from res.enkf.observations.summary_observation import SummaryObservation +from res.test import ErtTestContext + + +@pytest.mark.unstable +class EnKFTestTransferEnv(ResTest): + def setUp(self): + pass + + @tmpdir() + def test_transfer_var(self): + + with TestAreaContext("enkf_test_transfer_env") as work_area: + base_path = os.getcwd() + source_path = self.createTestPath("local/snake_oil_no_data") + + work_area.copy_directory(source_path) + dir_ert = os.path.join(base_path, "snake_oil_no_data") + assert os.path.isdir(dir_ert) + + file_ert = os.path.join(dir_ert, "snake_oil.ert") + assert os.path.isfile(file_ert) + + with ErtTestContext( + "transfer_env_var", model_config=file_ert, store_area=True + ) as ctx: + ert = ctx.getErt() + fs_manager = ert.getEnkfFsManager() + result_fs = fs_manager.getCurrentFileSystem() + + model_config = ert.getModelConfig() + runpath_fmt = model_config.getRunpathFormat() + jobname_fmt = model_config.getJobnameFormat() + subst_list = ert.getDataKW() + itr = 0 + mask = BoolVector(default_value=True, initial_size=1) + run_context = ErtRunContext.ensemble_experiment( + result_fs, mask, runpath_fmt, jobname_fmt, subst_list, itr + ) + ert.getEnkfSimulationRunner().createRunPath(run_context) + os.chdir("storage/snake_oil/runpath/realisation-0/iter-0") + assert os.path.isfile("jobs.json") + with open("jobs.json", "r") as f: + data = json.load(f) + env_data = data["global_environment"] + self.assertEqual("TheFirstValue", env_data["FIRST"]) + self.assertEqual("TheSecondValue", env_data["SECOND"]) + + path_data = data["global_update_path"] + self.assertEqual("TheThirdValue", path_data["THIRD"]) + self.assertEqual("TheFourthValue", path_data["FOURTH"]) diff --git a/libres/tests/res/enkf/test_ensemble_config.py b/libres/tests/res/enkf/test_ensemble_config.py new file mode 100644 index 00000000000..3831404f836 --- /dev/null +++ b/libres/tests/res/enkf/test_ensemble_config.py @@ -0,0 +1,119 @@ +from tests import ResTest + +from res.enkf import EnsembleConfig, ResConfig +from res.enkf import ConfigKeys +from ecl.util.test import TestAreaContext +from res.enkf.enums import GenDataFileType + + +class EnsembleConfigTest(ResTest): + def setUp(self): + self.case_directory = self.createTestPath("local/simple_config/") + self.case_file = "simple_config/ensemble_config" + + def test_create(self): + conf = EnsembleConfig() + self.assertEqual(len(conf), 0) + self.assertFalse("XYZ" in conf) + + with self.assertRaises(KeyError): + node = conf["KEY"] + + def test_ensemble_config_constructor(self): + config_dict = { + ConfigKeys.GEN_KW_TAG_FORMAT: "<%s>", + ConfigKeys.GEN_PARAM: [ + { + ConfigKeys.NAME: "GP", + ConfigKeys.FORWARD_INIT: False, + ConfigKeys.INPUT_FORMAT: GenDataFileType.ASCII, + ConfigKeys.OUTPUT_FORMAT: GenDataFileType.ASCII, + ConfigKeys.INIT_FILES: "GP/GP.txt", + ConfigKeys.ECL_FILE: "GP.txt", + ConfigKeys.MIN_STD: None, + ConfigKeys.TEMPLATE: None, + ConfigKeys.KEY_KEY: None, + }, + ], + ConfigKeys.GEN_DATA: [ + { + ConfigKeys.NAME: "SNAKE_OIL_OPR_DIFF", + ConfigKeys.INPUT_FORMAT: GenDataFileType.ASCII, + ConfigKeys.RESULT_FILE: "snake_oil_opr_diff_%d.txt", + ConfigKeys.REPORT_STEPS: [199], + ConfigKeys.INIT_FILES: None, + ConfigKeys.ECL_FILE: None, + ConfigKeys.TEMPLATE: None, + ConfigKeys.KEY_KEY: None, + }, + { + ConfigKeys.NAME: "SNAKE_OIL_GPR_DIFF", + ConfigKeys.INPUT_FORMAT: GenDataFileType.ASCII, + ConfigKeys.RESULT_FILE: "snake_oil_gpr_diff_%d.txt", + ConfigKeys.REPORT_STEPS: [199], + ConfigKeys.INIT_FILES: None, + ConfigKeys.ECL_FILE: None, + ConfigKeys.TEMPLATE: None, + ConfigKeys.KEY_KEY: None, + }, + ], + ConfigKeys.GEN_KW: [ + { + ConfigKeys.NAME: "MULTFLT", + ConfigKeys.TEMPLATE: "configuration_tests/FAULT_TEMPLATE", + ConfigKeys.OUT_FILE: "MULTFLT.INC", + ConfigKeys.PARAMETER_FILE: "configuration_tests/MULTFLT.TXT", + ConfigKeys.INIT_FILES: None, + ConfigKeys.MIN_STD: None, + ConfigKeys.FORWARD_INIT: False, + } + ], + ConfigKeys.SURFACE_KEY: [ + { + ConfigKeys.NAME: "TOP", + ConfigKeys.INIT_FILES: "configuration_tests/surface/small.irap", + ConfigKeys.OUT_FILE: "configuration_tests/surface/small_out.irap", + ConfigKeys.BASE_SURFACE_KEY: "configuration_tests/surface/small.irap", + ConfigKeys.MIN_STD: None, + ConfigKeys.FORWARD_INIT: False, + } + ], + ConfigKeys.SUMMARY: ["WOPR:OP_1"], + ConfigKeys.FIELD_KEY: [ + { + ConfigKeys.NAME: "PERMX", + ConfigKeys.VAR_TYPE: "PARAMETER", + ConfigKeys.INIT_FILES: "fields/permx%d.grdecl", + ConfigKeys.OUT_FILE: "permx.grdcel", + ConfigKeys.ENKF_INFILE: None, + ConfigKeys.INIT_TRANSFORM: None, + ConfigKeys.OUTPUT_TRANSFORM: None, + ConfigKeys.INPUT_TRANSFORM: None, + ConfigKeys.MIN_KEY: None, + ConfigKeys.MAX_KEY: None, + ConfigKeys.MIN_STD: None, + ConfigKeys.FORWARD_INIT: False, + } + ], + ConfigKeys.SCHEDULE_PREDICTION_FILE: [ + { + ConfigKeys.TEMPLATE: "configuration_tests/input/schedule.sch", + ConfigKeys.INIT_FILES: None, + ConfigKeys.MIN_STD: None, + ConfigKeys.PARAMETER_KEY: None, + } + ], + ConfigKeys.CONTAINER_KEY: [ + {ConfigKeys.NAME: "CXX", ConfigKeys.ARGLIST: ["PERMX", "MULTFLT"]} + ], + } + + self.case_directory = self.createTestPath("local/configuration_tests/") + with TestAreaContext("ensemble_config_test") as work_area: + work_area.copy_directory(self.case_directory) + res_config = ResConfig("configuration_tests/ensemble_config.ert") + ensemble_config_file = res_config.ensemble_config + ensemble_config_dict = EnsembleConfig( + config_dict=config_dict, grid=res_config.ecl_config.getGrid() + ) + self.assertEqual(ensemble_config_dict, ensemble_config_file) diff --git a/libres/tests/res/enkf/test_ert_context.py b/libres/tests/res/enkf/test_ert_context.py new file mode 100644 index 00000000000..6f7b1aadafb --- /dev/null +++ b/libres/tests/res/enkf/test_ert_context.py @@ -0,0 +1,127 @@ +from tests import ResTest +import sys + +from res.test import ErtTestContext +import pytest + +import os + + +@pytest.mark.equinor_test +class ErtTestContextTest(ResTest): + def setUp(self): + self.config = self.createTestPath("Equinor/config/with_data/config") + + def test_raises(self): + with self.assertRaises(IOError): + testContext = ErtTestContext("ExistTest", "Does/not/exist") + + def initFromCaseTest(self, context, root_path): + ert = context.getErt() + + init_case_job = self.createSharePath("%s/INIT_CASE_FROM_EXISTING" % root_path) + context.installWorkflowJob("INIT_CASE_JOB", init_case_job) + self.assertTrue( + context.runWorkflowJob("INIT_CASE_JOB", "default", "new_not_current_case") + ) + + default_fs = ert.getEnkfFsManager().getFileSystem("default") + new_fs = ert.getEnkfFsManager().getFileSystem("new_not_current_case") + + self.assertIsNotNone(default_fs) + self.assertIsNotNone(new_fs) + + self.assertTrue(len(default_fs.getStateMap()) > 0) + self.assertEqual(len(default_fs.getStateMap()), len(new_fs.getStateMap())) + + def createCaseTest(self, context, root_path): + create_case_job = self.createSharePath("%s/CREATE_CASE" % root_path) + context.installWorkflowJob("CREATE_CASE_JOB", create_case_job) + self.assertTrue(context.runWorkflowJob("CREATE_CASE_JOB", "newly_created_case")) + self.assertDirectoryExists("storage/newly_created_case") + + def selectCaseTest(self, context, root_path): + ert = context.getErt() + select_case_job = self.createSharePath("%s/SELECT_CASE" % root_path) + + default_fs = ert.getEnkfFsManager().getCurrentFileSystem() + + custom_fs = ert.getEnkfFsManager().getFileSystem("CustomCase") + + self.assertEqual(ert.getEnkfFsManager().getCurrentFileSystem(), default_fs) + + context.installWorkflowJob("SELECT_CASE_JOB", select_case_job) + self.assertTrue(context.runWorkflowJob("SELECT_CASE_JOB", "CustomCase")) + + self.assertEqual(ert.getEnkfFsManager().getCurrentFileSystem(), custom_fs) + + def loadResultsTest(self, context): + load_results_job = self.createSharePath( + "ert/workflows/jobs/internal/config/LOAD_RESULTS" + ) + context.installWorkflowJob("LOAD_RESULTS_JOB", load_results_job) + self.assertTrue(context.runWorkflowJob("LOAD_RESULTS_JOB", 0, 1)) + + def rankRealizationsOnObservationsTest(self, context): + rank_job = self.createSharePath( + "ert/workflows/jobs/internal/config/OBSERVATION_RANKING" + ) + + context.installWorkflowJob("OBS_RANK_JOB", rank_job) + + self.assertTrue( + context.runWorkflowJob("OBS_RANK_JOB", "NameOfObsRanking1", "|", "WOPR:*") + ) + self.assertTrue( + context.runWorkflowJob( + "OBS_RANK_JOB", + "NameOfObsRanking2", + "1-5", + "55", + "|", + "WWCT:*", + "WOPR:*", + ) + ) + self.assertTrue( + context.runWorkflowJob("OBS_RANK_JOB", "NameOfObsRanking3", "5", "55", "|") + ) + self.assertTrue( + context.runWorkflowJob( + "OBS_RANK_JOB", "NameOfObsRanking4", "1,3,5-10", "55" + ) + ) + self.assertTrue(context.runWorkflowJob("OBS_RANK_JOB", "NameOfObsRanking5")) + self.assertTrue( + context.runWorkflowJob( + "OBS_RANK_JOB", "NameOfObsRanking6", "|", "UnrecognizableObservation" + ) + ) + + def test_workflow_function_jobs(self): + + with ErtTestContext( + "python/enkf/ert_test_context_workflow_function_job", self.config + ) as context: + internal_config = "ert/workflows/jobs/internal-tui/config" + self.createCaseTest(context, root_path=internal_config) + self.selectCaseTest(context, root_path=internal_config) + + # Due to EnKFFs caching and unmonitored C functions this will fail + # self.initFromCaseTest(context, root_path=internal_config) + + self.loadResultsTest(context) + self.rankRealizationsOnObservationsTest(context) + + def test_workflow_ert_script_jobs(self): + + with ErtTestContext( + "python/enkf/ert_test_context_workflow_ert_script_job", self.config + ) as context: + with self.assertRaises(IOError): + context.installWorkflowJob("JOB_NAME", "DOES/NOT/EXIST") + + ert_scripts_config = "ert/workflows/jobs/internal-gui/config" + self.createCaseTest(context, root_path=ert_scripts_config) + self.selectCaseTest(context, root_path=ert_scripts_config) + self.initFromCaseTest(context, root_path=ert_scripts_config) diff --git a/libres/tests/res/enkf/test_ert_run_context.py b/libres/tests/res/enkf/test_ert_run_context.py new file mode 100644 index 00000000000..5ebcd34b707 --- /dev/null +++ b/libres/tests/res/enkf/test_ert_run_context.py @@ -0,0 +1,27 @@ +# Copyright (C) 2018 Equinor ASA, Norway. +# +# The file 'test_ert_run_context.py' 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. + + +from tests import ResTest + +from ecl.util.util import BoolVector +from res.enkf import ErtRunContext + + +class ErtRunContextTest(ResTest): + def test_case_init(self): + mask = BoolVector(default_value=True, initial_size=100) + context = ErtRunContext.case_init(None, mask) diff --git a/libres/tests/res/enkf/test_ert_templates.py b/libres/tests/res/enkf/test_ert_templates.py new file mode 100644 index 00000000000..6fe57f6ae08 --- /dev/null +++ b/libres/tests/res/enkf/test_ert_templates.py @@ -0,0 +1,129 @@ +# Copyright (C) 2017 Equinor ASA, Norway. +# +# The file 'test_res_config.py' 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. +from ecl.util.test import TestArea +from res.enkf import ErtTemplates +from res.enkf import ConfigKeys +from res.enkf import ResConfig +import unittest +from tests import ResTest +import os +import copy + + +class ErtTemplatesTest(ResTest): + def setUp(self): + self.work_area = TestArea("ert_templates_test_tmp") + self.config_data = { + ConfigKeys.CONFIG_DIRECTORY: self.work_area.get_cwd(), + ConfigKeys.CONFIG_FILE_KEY: "config", + ConfigKeys.RUN_TEMPLATE: [ + ( + "namea", + "filea", + [("keyaa", "valaa"), ("keyab", "valab"), ("keyac", "valac")], + ), + ( + "nameb", + "fileb", + [("keyba", "valba"), ("keybb", "valbb"), ("keybc", "valbc")], + ), + ( + "namec", + "filec", + [("keyca", "valca"), ("keycb", "valcb"), ("keycc", "valcc")], + ), + ], + } + self.filename = self.config_data[ConfigKeys.CONFIG_FILE_KEY] + + # write a config file in order to load ResConfig + self.make_config_file(self.filename) + self.make_empty_file("namea") + self.make_empty_file("nameb") + self.make_empty_file("namec") + self.res_config = ResConfig(user_config_file=self.filename) + + def tearDown(self): + del self.work_area + + def test_all_templates_exist_with_correct_properties(self): + templates = ErtTemplates( + self.res_config.subst_config.subst_list, config_dict=self.config_data + ) + template_names = templates.getTemplateNames() + configured_target_files = [ + t[0] for t in self.config_data[ConfigKeys.RUN_TEMPLATE] + ] + assert set(template_names) == set(configured_target_files) + for configured_template in self.config_data[ConfigKeys.RUN_TEMPLATE]: + template = templates.get_template(configured_template[0]) + self.assertEqual( + template.get_template_file(), + os.path.join(self.work_area.get_cwd(), configured_template[0]), + ) + self.assertEqual(template.get_target_file(), configured_template[1]) + expected_arg_string = ", ".join( + ["{}={}".format(key, val) for key, val in configured_template[2]] + ) + self.assertEqual(expected_arg_string, template.get_args_as_string()) + + def test_old_and_new_logic_produces_equal_objects(self): + templates = ErtTemplates( + self.res_config.subst_config.subst_list, config_dict=self.config_data + ) + self.assertEqual(templates, self.res_config.ert_templates) + + def test_unequal_objects_are_unequal(self): + templates = ErtTemplates( + self.res_config.subst_config.subst_list, config_dict=self.config_data + ) + templates2 = ErtTemplates( + self.res_config.subst_config.subst_list, + config_dict=self.change_template_arg(1, 1, "XXX", "YYY"), + ) + self.assertNotEqual(templates, templates2) + + def remove_key(self, key): + return {i: self.config_data[i] for i in self.config_data if i != key} + + def change_template_arg(self, template_index, arg_index, new_key, new_val): + conf_copy = copy.deepcopy(self.config_data) + conf_copy[ConfigKeys.RUN_TEMPLATE][template_index][2][arg_index] = ( + new_key, + new_val, + ) + return conf_copy + + def make_config_file(self, filename): + with open(filename, "w+") as config: + # necessary in the file, but irrelevant to this test + config.write("JOBNAME Job%d\n") + config.write("NUM_REALIZATIONS 1\n") + + for template, target, args in self.config_data[ConfigKeys.RUN_TEMPLATE]: + argstring = " ".join("{}:{}".format(key, val) for key, val in args) + config.write( + "{} {} {} {}\n".format( + ConfigKeys.RUN_TEMPLATE, template, target, argstring + ) + ) + + def make_empty_file(self, filename): + open(filename, "a").close() + + +if __name__ == "__main__": + unittest.main() diff --git a/libres/tests/res/enkf/test_es_update.py b/libres/tests/res/enkf/test_es_update.py new file mode 100644 index 00000000000..57cc1271662 --- /dev/null +++ b/libres/tests/res/enkf/test_es_update.py @@ -0,0 +1,114 @@ +from tests import ResTest +from tests.utils import tmpdir +from res.test import ErtTestContext +from ecl.util.util import BoolVector + + +from res.enkf import NodeId +from res.enkf import ESUpdate +from res.enkf import ErtRunContext +from res.enkf import EnkfNode + + +class ESUpdateTest(ResTest): + @tmpdir() + def test_create(self): + config = self.createTestPath("local/mini_ert/mini_config") + with ErtTestContext("python/enkf/data/mini_ert_simulated", config) as context: + ert = context.getErt() + es_update = ESUpdate(ert) + + self.assertFalse(es_update.hasModule("NO_NOT_THIS_MODULE")) + with self.assertRaises(KeyError): + m = es_update.getModule("STD_ENKF_XXX") + + module = es_update.getModule("STD_ENKF") + + @tmpdir() + def test_update(self): + config = self.createTestPath("local/snake_oil/snake_oil.ert") + with ErtTestContext("update_test", config) as context: + ert = context.getErt() + es_update = ESUpdate(ert) + fsm = ert.getEnkfFsManager() + + sim_fs = fsm.getFileSystem("default_0") + target_fs = fsm.getFileSystem("target") + mask = BoolVector(initial_size=ert.getEnsembleSize(), default_value=True) + run_context = ErtRunContext.ensemble_smoother_update(sim_fs, target_fs) + es_update.smootherUpdate(run_context) + + conf = ert.ensembleConfig()["SNAKE_OIL_PARAM"] + sim_node = EnkfNode(conf) + target_node = EnkfNode(conf) + + node_id = NodeId(0, 0) + sim_node.load(sim_fs, node_id) + target_node.load(target_fs, node_id) + + sim_gen_kw = sim_node.asGenKw() + target_gen_kw = target_node.asGenKw() + + # Test that an update has actually taken place + for index in range(len(sim_gen_kw)): + self.assertNotEqual(sim_gen_kw[index], target_gen_kw[index]) + + @tmpdir() + def test_localization(self): + config = self.createTestPath("local/snake_oil/snake_oil.ert") + with ErtTestContext("localization_test", config) as context: + ert = context.getErt() + es_update = ESUpdate(ert) + fsm = ert.getEnkfFsManager() + sim_fs = fsm.getFileSystem("default_0") + target_fs = fsm.getFileSystem("target") + + # perform localization + localized_idxs = (1, 2) + local_config = ert.getLocalConfig() + local_config.clear() + dataset = local_config.createDataset("DATASET_SCALAR_LOCA") + dataset.addNode("SNAKE_OIL_PARAM") + active_list = dataset.getActiveList("SNAKE_OIL_PARAM") + for i in localized_idxs: + active_list.addActiveIndex(i) + obs = local_config.createObsdata("OBSSET_LOCA") + obs.addNode("WOPR_OP1_72") + ministep = local_config.createMinistep("MINISTEP_LOCA") + ministep.attachDataset(dataset) + ministep.attachObsset(obs) + updatestep = local_config.getUpdatestep() + updatestep.attachMinistep(ministep) + + # Run enseble smoother + mask = BoolVector(initial_size=ert.getEnsembleSize(), default_value=True) + model_config = ert.getModelConfig() + path_fmt = model_config.getRunpathFormat() + jobname_fmt = model_config.getJobnameFormat() + subst_list = None + run_context = ErtRunContext.ensemble_smoother( + sim_fs, target_fs, mask, path_fmt, jobname_fmt, subst_list, 0 + ) + es_update.smootherUpdate(run_context) + + conf = ert.ensembleConfig()["SNAKE_OIL_PARAM"] + sim_node = EnkfNode(conf) + target_node = EnkfNode(conf) + + node_id = NodeId(0, 0) + sim_node.load(sim_fs, node_id) + target_node.load(target_fs, node_id) + + sim_gen_kw = sim_node.asGenKw() + target_gen_kw = target_node.asGenKw() + + # Test that the localized values has been updated + for i in localized_idxs: + self.assertNotEqual(sim_gen_kw[i], target_gen_kw[i]) + + # test that all the other values are left unchanged + non_localized_idxs = ( + x for x in range(len(sim_gen_kw)) if x not in localized_idxs + ) + for i in non_localized_idxs: + self.assertEqual(sim_gen_kw[i], target_gen_kw[i]) diff --git a/libres/tests/res/enkf/test_field_config.py b/libres/tests/res/enkf/test_field_config.py new file mode 100644 index 00000000000..eead8610d87 --- /dev/null +++ b/libres/tests/res/enkf/test_field_config.py @@ -0,0 +1,91 @@ +# Copyright (C) 2017 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. +from os.path import abspath +from ecl.util.test import TestAreaContext +from tests import ResTest + +from ecl.grid import EclGridGenerator +from res.enkf.config import FieldTypeEnum, FieldConfig +from res.enkf.enums import EnkfFieldFileFormatEnum + + +class FieldConfigTest(ResTest): + def test_field_guess_filetype(self): + with TestAreaContext("field_config") as test_context: + fname = abspath("test.kw.grdecl") + with open(fname, "w") as f: + f.write("-- my comment\n") + f.write("-- more comments\n") + f.write("SOWCR\n") + # The function guessing file types determines whether the file + # is binary or 7 bit ASCII based on bit 8 heuristics. For this + # to be "reliable" the file is required to be more than 256 + # bytes. + for i in range(256 // 8): + f.write("0 0 0 0\n") + + ft = FieldConfig.guessFiletype(fname) + grdecl_type = EnkfFieldFileFormatEnum(5) + self.assertEqual("ECL_GRDECL_FILE", grdecl_type.name) + self.assertEqual(grdecl_type, ft) + + def test_field_type_enum(self): + self.assertEqual(FieldTypeEnum(2), FieldTypeEnum.ECLIPSE_PARAMETER) + gen = FieldTypeEnum.GENERAL + self.assertEqual("GENERAL", str(gen)) + gen = FieldTypeEnum(3) + self.assertEqual("GENERAL", str(gen)) + + def test_export_format(self): + self.assertEqual( + FieldConfig.exportFormat("file.grdecl"), + EnkfFieldFileFormatEnum.ECL_GRDECL_FILE, + ) + self.assertEqual( + FieldConfig.exportFormat("file.xyz.grdecl"), + EnkfFieldFileFormatEnum.ECL_GRDECL_FILE, + ) + self.assertEqual( + FieldConfig.exportFormat("file.roFF"), EnkfFieldFileFormatEnum.RMS_ROFF_FILE + ) + self.assertEqual( + FieldConfig.exportFormat("file.xyz.roFF"), + EnkfFieldFileFormatEnum.RMS_ROFF_FILE, + ) + + with self.assertRaises(ValueError): + FieldConfig.exportFormat("file.xyz") + + with self.assertRaises(ValueError): + FieldConfig.exportFormat("file.xyz") + + def test_basics(self): + nx = 17 + ny = 13 + nz = 11 + actnum = [1] * nx * ny * nz + actnum[0] = 0 + grid = EclGridGenerator.create_rectangular((nx, ny, nz), (1, 1, 1), actnum) + fc = FieldConfig("PORO", grid) + pfx = "FieldConfig(type" + rep = repr(fc) + self.assertEqual(pfx, rep[: len(pfx)]) + fc_xyz = fc.get_nx(), fc.get_ny(), fc.get_nz() + ex_xyz = nx, ny, nz + self.assertEqual(ex_xyz, fc_xyz) + self.assertEqual(0, fc.get_truncation_mode()) + self.assertEqual(ex_xyz, (grid.getNX(), grid.getNY(), grid.getNZ())) + self.assertEqual(fc.get_data_size(), grid.get_num_active()) diff --git a/libres/tests/res/enkf/test_field_export.py b/libres/tests/res/enkf/test_field_export.py new file mode 100644 index 00000000000..c56d91ffb61 --- /dev/null +++ b/libres/tests/res/enkf/test_field_export.py @@ -0,0 +1,92 @@ +import os +import pytest + +from tests import ResTest +from res.test import ErtTestContext + +from ecl.util.util import IntVector +from ecl.grid import EclGrid + +from res.enkf.config import FieldTypeEnum, FieldConfig +from res.enkf.data import EnkfNode +from res.enkf.enums import EnkfFieldFileFormatEnum +from res.enkf import NodeId + + +@pytest.mark.equinor_test +class FieldExportTest(ResTest): + def setUp(self): + self.config_file = self.createTestPath("Equinor/config/obs_testing/config") + + def test_field_type_enum(self): + with ErtTestContext("export_test", self.config_file) as test_context: + ert = test_context.getErt() + ens_config = ert.ensembleConfig() + fc = ens_config["PERMX"].getFieldModelConfig() + self.assertEqual(FieldTypeEnum.ECLIPSE_PARAMETER, fc.get_type()) + + @pytest.mark.skip(reason="Aborts") + def test_field_basics(self): + with ErtTestContext("export_test", self.config_file) as test_context: + ert = test_context.getErt() + ens_config = ert.ensembleConfig() + fc = ens_config["PERMX"].getFieldModelConfig() + pfx = "FieldConfig(type" + rep = repr(fc) + self.assertEqual(pfx, rep[: len(pfx)]) + fc_xyz = fc.get_nx(), fc.get_ny(), fc.get_nz() + ex_xyz = 40, 64, 14 + self.assertEqual(ex_xyz, fc_xyz) + self.assertEqual(1, fc.get_truncation_mode()) + self.assertEqual(0.001, fc.get_truncation_min()) + self.assertEqual(-1.0, fc.get_truncation_max()) + self.assertEqual("LOG", fc.get_init_transform_name()) + self.assertEqual(None, fc.get_output_transform_name()) + grid = fc.get_grid() + self.assertEqual(ex_xyz, (grid.getNX(), grid.getNY(), grid.getNZ())) + + field_node = EnkfNode(fc) + self.assertEqual(grid.get_num_active(), len(field_node)) + with self.assertRaises(IndexError): + field_node[grid.get_num_active()] + value0 = field_node[0] + + def test_field_export(self): + with ErtTestContext("export_test", self.config_file) as test_context: + ert = test_context.getErt() + fs_manager = ert.getEnkfFsManager() + ens_config = ert.ensembleConfig() + config_node = ens_config["PERMX"] + data_node = EnkfNode(config_node) + node_id = NodeId(0, 0) + fs = fs_manager.getCurrentFileSystem() + data_node.tryLoad(fs, node_id) + + data_node.export("export/with/path/PERMX.grdecl") + self.assertTrue(os.path.isfile("export/with/path/PERMX.grdecl")) + + def test_field_export_many(self): + with ErtTestContext("export_test", self.config_file) as test_context: + ert = test_context.getErt() + fs_manager = ert.getEnkfFsManager() + ens_config = ert.ensembleConfig() + config_node = ens_config["PERMX"] + iens_list = IntVector() + iens_list.append(0) + iens_list.append(2) + iens_list.append(4) + + fs = fs_manager.getCurrentFileSystem() + + # Filename without embedded %d - TypeError + with self.assertRaises(TypeError): + EnkfNode.exportMany( + config_node, "export/with/path/PERMX.grdecl", fs, iens_list + ) + + EnkfNode.exportMany( + config_node, "export/with/path/PERMX_%d.grdecl", fs, iens_list + ) + self.assertTrue(os.path.isfile("export/with/path/PERMX_0.grdecl")) + self.assertTrue(os.path.isfile("export/with/path/PERMX_2.grdecl")) + self.assertTrue(os.path.isfile("export/with/path/PERMX_4.grdecl")) diff --git a/libres/tests/res/enkf/test_forward_load_context.py b/libres/tests/res/enkf/test_forward_load_context.py new file mode 100644 index 00000000000..260111dec53 --- /dev/null +++ b/libres/tests/res/enkf/test_forward_load_context.py @@ -0,0 +1,8 @@ +from tests import ResTest +from res.enkf import ForwardLoadContext + + +class ForwardLoadContextTest(ResTest): + def test_create(self): + ctx = ForwardLoadContext(report_step=1) + self.assertEqual(1, ctx.getLoadStep()) diff --git a/libres/tests/res/enkf/test_gen_obs.py b/libres/tests/res/enkf/test_gen_obs.py new file mode 100644 index 00000000000..8d2407e666b --- /dev/null +++ b/libres/tests/res/enkf/test_gen_obs.py @@ -0,0 +1,52 @@ +import os.path + +from ecl.util.test import TestAreaContext +from tests import ResTest +from res.enkf import GenObservation, GenDataConfig, ActiveList + + +class GenObsTest(ResTest): + def setUp(self): + pass + + def test_create(self): + data_config = GenDataConfig("KEY") + with self.assertRaises(ValueError): + gen_obs = GenObservation("KEY", data_config) + + with TestAreaContext("gen_obs/create"): + with open("obs1.txt", "w") as f: + f.write("10 5 12 6\n") + + with self.assertRaises(ValueError): + gen_obs = GenObservation( + "KEY", data_config, scalar_value=(1, 2), obs_file="obs1.txt" + ) + + with self.assertRaises(TypeError): + gen_obs = GenObservation("KEY", data_config, scalar_value=1) + + with self.assertRaises(IOError): + gen_obs = GenObservation("KEY", data_config, obs_file="does/not/exist") + + gen_obs = GenObservation( + "KEY", data_config, obs_file="obs1.txt", data_index="10,20" + ) + self.assertEqual(len(gen_obs), 2) + self.assertEqual(gen_obs[0], (10, 5)) + self.assertEqual(gen_obs[1], (12, 6)) + + self.assertEqual(gen_obs.getValue(0), 10) + self.assertEqual(gen_obs.getDataIndex(1), 20) + self.assertEqual(gen_obs.getStdScaling(0), 1) + self.assertEqual(gen_obs.getStdScaling(1), 1) + + active_list = ActiveList() + gen_obs.updateStdScaling(0.25, active_list) + self.assertEqual(gen_obs.getStdScaling(0), 0.25) + self.assertEqual(gen_obs.getStdScaling(1), 0.25) + + active_list.addActiveIndex(1) + gen_obs.updateStdScaling(2.00, active_list) + self.assertEqual(gen_obs.getStdScaling(0), 0.25) + self.assertEqual(gen_obs.getStdScaling(1), 2.00) diff --git a/libres/tests/res/enkf/test_hook_manager.py b/libres/tests/res/enkf/test_hook_manager.py new file mode 100644 index 00000000000..97a357e61d9 --- /dev/null +++ b/libres/tests/res/enkf/test_hook_manager.py @@ -0,0 +1,144 @@ +# Copyright (C) 2017 Equinor ASA, Norway. +# +# The file 'test_res_config.py' 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. +import os +import os.path +from ecl.util.test import TestArea +from res.enkf import HookManager +from res.enkf import ConfigKeys +from res.enkf import ResConfig +from res.enkf import HookRuntime +import unittest +from tests import ResTest + + +class HookManagerTest(ResTest): + def setUp(self): + self.work_area = TestArea("hook_manager_test_tmp") + # in order to test HOOK_WORKFLOWS there need to be some workflows loaded + self.work_area.copy_directory( + self.createTestPath("local/config/workflows/workflowjobs") + ) + self.work_area.copy_directory( + self.createTestPath("local/config/workflows/workflows") + ) + self.config_data = { + ConfigKeys.RUNPATH_FILE: "runpath", + ConfigKeys.CONFIG_DIRECTORY: self.work_area.get_cwd(), + ConfigKeys.CONFIG_FILE_KEY: "config", + ConfigKeys.HOOK_WORKFLOW_KEY: [("MAGIC_PRINT", "PRE_SIMULATION")], + # these two entries makes the workflow_list load this workflow, but are not needed by hook_manager directly + ConfigKeys.LOAD_WORKFLOW_JOB: "workflowjobs/MAGIC_PRINT", + ConfigKeys.LOAD_WORKFLOW: "workflows/MAGIC_PRINT", + } + self.filename = self.config_data[ConfigKeys.CONFIG_FILE_KEY] + # these files must exist + self.make_empty_file(self.config_data[ConfigKeys.RUNPATH_FILE]) + + # write a config file in order to load ResConfig + self.make_config_file(self.filename) + self.res_config = ResConfig(user_config_file=self.filename) + + def tearDown(self): + del self.work_area + + def test_different_runpath_gives_not_equal_hook_managers(self): + res_config2 = ResConfig(user_config_file=self.filename) + hook_manager1 = HookManager( + workflow_list=self.res_config.ert_workflow_list, + config_dict=self.config_data, + ) + hook_manager2 = HookManager( + workflow_list=res_config2.ert_workflow_list, + config_dict=self.set_key(ConfigKeys.RUNPATH_FILE, "runpath2"), + ) + + self.assertNotEqual(hook_manager1, hook_manager2) + + def test_different_hook_workflow_gives_not_equal_hook_managers(self): + res_config2 = ResConfig(user_config_file=self.filename) + hook_manager1 = HookManager( + workflow_list=self.res_config.ert_workflow_list, + config_dict=self.config_data, + ) + hook_manager2 = HookManager( + workflow_list=res_config2.ert_workflow_list, + config_dict=self.remove_key(ConfigKeys.HOOK_WORKFLOW_KEY), + ) + + self.assertNotEqual(hook_manager1, hook_manager2) + + def test_old_and_new_constructor_creates_equal_config(self): + res_config2 = ResConfig(user_config_file=self.filename) + old = res_config2.hook_manager + new = HookManager( + workflow_list=self.res_config.ert_workflow_list, + config_dict=self.config_data, + ) + + self.assertEqual(old, new) + + def test_all_config_entries_are_set(self): + hook_manager = HookManager( + workflow_list=self.res_config.ert_workflow_list, + config_dict=self.config_data, + ) + list_file = hook_manager.getRunpathListFile() + conf_dir = self.config_data[ConfigKeys.CONFIG_DIRECTORY] + self.assertEqual( + list_file, os.path.join(conf_dir, self.config_data[ConfigKeys.RUNPATH_FILE]) + ) + + self.assertEqual(len(hook_manager), 1) + + magic_workflow = hook_manager[0] + self.assertEqual( + magic_workflow.getWorkflow().src_file, + os.path.join(conf_dir, self.config_data[ConfigKeys.LOAD_WORKFLOW]), + ) + self.assertEqual(magic_workflow.getRunMode(), HookRuntime.PRE_SIMULATION) + + def remove_key(self, key): + return {i: self.config_data[i] for i in self.config_data if i != key} + + def set_key(self, key, val): + copy = self.config_data.copy() + copy[key] = val + return copy + + def make_config_file(self, filename): + with open(filename, "w+") as config: + # necessary in the file, but irrelevant to this test + config.write("JOBNAME Job%d\n") + config.write("NUM_REALIZATIONS 1\n") + for key, val in self.config_data.items(): + if ( + key == ConfigKeys.CONFIG_FILE_KEY + or key == ConfigKeys.CONFIG_DIRECTORY + ): + continue + if isinstance(val, str): + config.write("{} {}\n".format(key, val)) + else: + # assume this is the list of tuple for hook workflows + for val1, val2 in val: + config.write("{} {} {}\n".format(key, val1, val2)) + + def make_empty_file(self, filename): + open(filename, "a").close() + + +if __name__ == "__main__": + unittest.main() diff --git a/libres/tests/res/enkf/test_hook_workflow.py b/libres/tests/res/enkf/test_hook_workflow.py new file mode 100644 index 00000000000..09321ee7450 --- /dev/null +++ b/libres/tests/res/enkf/test_hook_workflow.py @@ -0,0 +1,12 @@ +from res.enkf.enums import HookRuntime +from tests import ResTest + + +class HookWorkFlowTest(ResTest): + def test_enum(self): + self.assertEnumIsFullyDefined( + HookRuntime, + "hook_run_mode_enum", + "lib/include/ert/enkf/hook_workflow.hpp", + verbose=True, + ) diff --git a/libres/tests/res/enkf/test_key_manager.py b/libres/tests/res/enkf/test_key_manager.py new file mode 100644 index 00000000000..00bca2950d7 --- /dev/null +++ b/libres/tests/res/enkf/test_key_manager.py @@ -0,0 +1,60 @@ +from res.enkf.key_manager import KeyManager +from res.test import ErtTestContext + +from tests import ResTest + + +class KeyManagerTest(ResTest): + def setUp(self): + self.config_file = self.createTestPath("local/snake_oil/snake_oil.ert") + + def test_summary_keys(self): + with ErtTestContext("enkf_key_manager_test", self.config_file) as testContext: + ert = testContext.getErt() + key_man = KeyManager(ert) + + self.assertEqual(len(key_man.summaryKeys()), 47) + self.assertTrue("FOPT" in key_man.summaryKeys()) + + self.assertEqual(len(key_man.summaryKeysWithObservations()), 2) + self.assertTrue("FOPR" in key_man.summaryKeysWithObservations()) + self.assertTrue(key_man.isKeyWithObservations("FOPR")) + + def test_gen_data_keys(self): + with ErtTestContext("enkf_key_manager_test", self.config_file) as testContext: + ert = testContext.getErt() + key_man = KeyManager(ert) + + self.assertEqual(len(key_man.genDataKeys()), 3) + self.assertTrue("SNAKE_OIL_WPR_DIFF@199" in key_man.genDataKeys()) + + self.assertEqual(len(key_man.genDataKeysWithObservations()), 1) + self.assertTrue( + "SNAKE_OIL_WPR_DIFF@199" in key_man.genDataKeysWithObservations() + ) + self.assertTrue(key_man.isKeyWithObservations("SNAKE_OIL_WPR_DIFF@199")) + + def test_gen_kw_keys(self): + with ErtTestContext("enkf_key_manager_test", self.config_file) as testContext: + ert = testContext.getErt() + key_man = KeyManager(ert) + + self.assertEqual(len(key_man.genKwKeys()), 10) + self.assertTrue( + "SNAKE_OIL_PARAM:BPR_555_PERSISTENCE" in key_man.genKwKeys() + ) + + def test_gen_kw_priors(self): + with ErtTestContext("enkf_key_manager_test", self.config_file) as testContext: + ert = testContext.getErt() + key_man = KeyManager(ert) + priors = key_man.gen_kw_priors() + self.assertEqual(len(priors["SNAKE_OIL_PARAM"]), 10) + self.assertTrue( + { + "key": "OP1_PERSISTENCE", + "function": "UNIFORM", + "parameters": {"MIN": 0.01, "MAX": 0.4}, + } + in priors["SNAKE_OIL_PARAM"] + ) diff --git a/libres/tests/res/enkf/test_labscale.py b/libres/tests/res/enkf/test_labscale.py new file mode 100644 index 00000000000..19ca25f533d --- /dev/null +++ b/libres/tests/res/enkf/test_labscale.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python +# Copyright (C) 2015 Equinor ASA, Norway. +# +# The file 'test_labscale.py' 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. + +import pytest + +from tests import ResTest +from res.test import ErtTestContext + +from res.enkf import ObsVector + + +@pytest.mark.skip("Currently failing because a config object is missing a key") +@pytest.mark.equinor_test +class LabScaleTest(ResTest): + def testObs(self): + config_file = self.createTestPath("Equinor/config/labscale/config") + with ErtTestContext("labscale", config_file) as test_context: + ert = test_context.getErt() + obs = ert.getObservations() + + self.assertEqual(4, len(obs)) + for v in obs: + self.assertTrue(isinstance(v, ObsVector)) + + v1 = obs["WWCT_1"] + self.assertEqual(v1.activeStep(), 5) + node = v1.getNode(5) + self.assertFloatEqual(node.getValue(), 0.00) + + v2 = obs["WWCT_2"] + self.assertEqual(v2.activeStep(), 31) + node = v2.getNode(31) + self.assertFloatEqual(node.getValue(), 0.575828) + + v3 = obs["WWCT_3"] + self.assertEqual(v3.activeStep(), 73) + node = v3.getNode(73) + self.assertFloatEqual(node.getValue(), 1.00) + + bpr = obs["BPR"] + self.assertEqual(bpr.activeStep(), 31) + node = bpr.getNode(31) + self.assertFloatEqual(node.getValue(0), 10.284) + + def testObs_beijing(self): + config_file = self.createTestPath("Equinor/config/lab-beijing/labunits/config") + with ErtTestContext("labscale-beijing", config_file) as test_context: + ert = test_context.getErt() + obs = ert.getObservations() + + v0 = obs["WCT0"] + self.assertEqual(v0.activeStep(), 18) + node = v0.getNode(18) + self.assertEqual(node.getValue(), 0.12345) + + v1 = obs["WCT1"] + self.assertEqual(v1.activeStep(), 18) + node = v1.getNode(18) + self.assertEqual(node.getValue(), 0.12345) diff --git a/libres/tests/res/enkf/test_local_config.py b/libres/tests/res/enkf/test_local_config.py new file mode 100644 index 00000000000..048a0df303f --- /dev/null +++ b/libres/tests/res/enkf/test_local_config.py @@ -0,0 +1,216 @@ +# Copyright (C) 2017 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. + +import os.path + +from tests import ResTest +from tests.utils import tmpdir + +from res.test import ErtTestContext +from res.enkf import ESUpdate +from res.enkf import ErtRunContext +from res.enkf.local_ministep import LocalMinistep +from res.enkf.local_obsdata import LocalObsdata +from res.enkf.local_updatestep import LocalUpdateStep +from res.enkf.local_obsdata_node import LocalObsdataNode + + +class LocalConfigTest(ResTest): + def setUp(self): + self.config = self.createTestPath("local/mini_ert/mini_config") + self.local_conf_path = "python/enkf/data/local_config" + + def test_write_summary(self): + with ErtTestContext(self.local_conf_path, self.config) as test_context: + main = test_context.getErt() + + local_config = main.getLocalConfig() + + fname = "local_config_summary.txt" + local_config.writeSummaryFile(fname) + self.assertTrue(os.path.isfile(fname)) + + def test_get_grid(self): + with ErtTestContext(self.local_conf_path, self.config) as test_context: + main = test_context.getErt() + local_config = main.getLocalConfig() + grid = local_config.getGrid() + dimens = grid.getNX(), grid.getNY(), grid.getNZ() + self.assertEqual((10, 10, 5), dimens) + + def test_local_obs_data(self): + with ErtTestContext(self.local_conf_path, self.config) as test_context: + main = test_context.getErt() + self.assertTrue(main, msg="Load failed") + + local_config = main.getLocalConfig() + + local_config.clear() + updatestep = local_config.getUpdatestep() + self.assertEqual(0, len(updatestep)) + + # Creating obsdata + local_obs_data_1 = local_config.createObsdata("OBSSET_1") + self.assertTrue(isinstance(local_obs_data_1, LocalObsdata)) + + # Try to add existing obsdata + with self.assertRaises(ValueError): + local_config.createObsdata("OBSSET_1") + + # Add node with range + with self.assertRaises(KeyError): + local_obs_data_1.addNodeAndRange("MISSING_KEY", 0, 1) + + local_obs_data_1.addNodeAndRange("GEN_PERLIN_1", 0, 1) + local_obs_data_1.addNodeAndRange("GEN_PERLIN_2", 0, 1) + self.assertEqual(len(local_obs_data_1), 2) + + # Delete node + del local_obs_data_1["GEN_PERLIN_1"] + self.assertEqual(len(local_obs_data_1), 1) + + # Get node + node = local_obs_data_1["GEN_PERLIN_2"] + self.assertTrue(isinstance(node, LocalObsdataNode)) + + # Add node again with no range and check return type + node_again = local_obs_data_1.addNode("GEN_PERLIN_1") + self.assertTrue(isinstance(node_again, LocalObsdataNode)) + + # Error when adding existing obs node + with self.assertRaises(KeyError): + local_obs_data_1.addNode("GEN_PERLIN_1") + + def test_attach_obs_data(self): + with ErtTestContext(self.local_conf_path, self.config) as test_context: + main = test_context.getErt() + + local_config = main.getLocalConfig() + local_obs_data_2 = local_config.createObsdata("OBSSET_2") + self.assertTrue(isinstance(local_obs_data_2, LocalObsdata)) + + # Obsdata + local_obs_data_2.addNodeAndRange("GEN_PERLIN_1", 0, 1) + local_obs_data_2.addNodeAndRange("GEN_PERLIN_2", 0, 1) + + # Ministep + ministep = local_config.createMinistep("MINISTEP") + self.assertTrue(isinstance(ministep, LocalMinistep)) + + # Attach obsset + ministep.attachObsset(local_obs_data_2) + + # Retrieve attached obsset + local_obs_data_new = ministep.getLocalObsData() + self.assertEqual(len(local_obs_data_new), 2) + + def test_all_active(self): + with ErtTestContext(self.local_conf_path, self.config) as test_context: + main = test_context.getErt() + + local_config = main.getLocalConfig() + + updatestep = local_config.getUpdatestep() + ministep = updatestep[0] + self.assertEqual(1, len(ministep)) + dataset = ministep["ALL_DATA"] + self.assertTrue("PERLIN_PARAM" in dataset) + + obsdata = ministep.getLocalObsData() + self.assertEqual(len(obsdata), 3) + + def test_ministep(self): + with ErtTestContext( + "python/enkf/data/local_config", self.config + ) as test_context: + main = test_context.getErt() + + local_config = main.getLocalConfig() + analysis_module = main.analysisConfig().getModule("STD_ENKF") + + # Ministep + ministep = local_config.createMinistep("MINISTEP", analysis_module) + self.assertTrue(isinstance(ministep, LocalMinistep)) + + with self.assertRaises(KeyError): + _ = local_config.createMinistep("MINISTEP", None) + + self.assertFalse("DATA" in ministep) + with self.assertRaises(KeyError): + _ = ministep["DATA"] + + self.assertIsNone(ministep.get_obs_data()) + + def test_attach_ministep(self): + with ErtTestContext(self.local_conf_path, self.config) as test_context: + main = test_context.getErt() + + local_config = main.getLocalConfig() + + # Update step + updatestep = local_config.getUpdatestep() + self.assertTrue(isinstance(updatestep, LocalUpdateStep)) + upd_size = len(updatestep) + + # Ministep + ministep = local_config.createMinistep("MINISTEP") + self.assertTrue(isinstance(ministep, LocalMinistep)) + + # Attach + updatestep.attachMinistep(ministep) + self.assertTrue(isinstance(updatestep[0], LocalMinistep)) + self.assertEqual(len(updatestep), upd_size + 1) + + @tmpdir() + def test_attach_obs_data_to_ministep(self): + config = self.createTestPath("local/snake_oil/snake_oil.ert") + + expected_keys = { + "WPR_DIFF_1", + "WOPR_OP1_108", + "FOPR", + "WOPR_OP1_144", + "WOPR_OP1_190", + "WOPR_OP1_9", + "WOPR_OP1_36", + "WOPR_OP1_72", + } + + with ErtTestContext("obs_data_ministep_test", config) as context: + ert = context.getErt() + es_update = ESUpdate(ert) + fsm = ert.getEnkfFsManager() + + sim_fs = fsm.getFileSystem("default_0") + target_fs = fsm.getFileSystem("target") + run_context = ErtRunContext.ensemble_smoother_update(sim_fs, target_fs) + es_update.smootherUpdate(run_context) + + update_step = ert.getLocalConfig().getUpdatestep() + ministep = update_step[len(update_step) - 1] + obs_data = ministep.get_obs_data() + self.assertEqual(len(expected_keys), obs_data.get_num_blocks()) + + observed_obs_keys = set() + for block_num in range(obs_data.get_num_blocks()): + block = obs_data.get_block(block_num) + + obs_key = block.get_obs_key() + observed_obs_keys.add(obs_key) + for i in range(len(block)): + self.assertTrue(block.is_active(i)) + + self.assertSetEqual(expected_keys, observed_obs_keys) diff --git a/libres/tests/res/enkf/test_local_dataset.py b/libres/tests/res/enkf/test_local_dataset.py new file mode 100644 index 00000000000..6176f54a79e --- /dev/null +++ b/libres/tests/res/enkf/test_local_dataset.py @@ -0,0 +1,141 @@ +# Copyright (C) 2017 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. + +import os.path + +from tests import ResTest +from tests.utils import tmpdir +from res.test import ErtTestContext + +from res.enkf.active_list import ActiveList +from res.enkf.local_dataset import LocalDataset + +from ecl.grid import EclRegion +from ecl.util.geometry import Surface, GeoRegion + + +class LocalDatasetTest(ResTest): + def setUp(self): + self.config = self.createTestPath("local/mini_ert/mini_config") + self.field_config = self.createTestPath("local/snake_oil_field/snake_oil.ert") + self.surf_config = self.createTestPath( + "local/snake_oil_field/snake_oil_surface.ert" + ) + + def _small_surf(self): + # values copied from irap surface_small + ny, nx = 20, 30 + xinc, yinc = 50.0, 50.0 + xstart, ystart = 463325.5625, 7336963.5 + angle = -65.0 + s_args = (None, nx, ny, xinc, yinc, xstart, ystart, angle) + return Surface(*s_args) + + @tmpdir() + def test_local_field(self): + with ErtTestContext( + "python/enkf/data/local_config", self.field_config + ) as test_context: + main = test_context.getErt() + local_config = main.getLocalConfig() + + # Creating dataset + data_scale = local_config.createDataset("DATA_SCALE") + grid = local_config.getGrid() + ecl_reg = EclRegion(grid, False) + ecl_reg.select_islice(10, 20) + data_scale.addField("PERMX", ecl_reg) + self.assertEqual(1, len(data_scale)) + + # A totally invalid key -> KeyError exception + with self.assertRaises(KeyError): + data_scale.row_scaling("NO_SUCH_KEY") + + row_scaling = data_scale.row_scaling("PERMX") + + @tmpdir() + def test_local_surface(self): + with ErtTestContext( + "python/enkf/data/local_config", self.surf_config + ) as test_context: + main = test_context.getErt() + local_config = main.getLocalConfig() + + # Creating dataset + data_scale = local_config.createDataset("DATA_SCALE") + surf = self._small_surf() + ps = surf.getPointset() + geo_reg = GeoRegion(ps) + data_scale.addSurface("TOP", geo_reg) + self.assertEqual(1, len(data_scale)) + + @tmpdir() + def test_local_dataset(self): + with ErtTestContext( + "python/enkf/data/local_config", self.config + ) as test_context: + main = test_context.getErt() + + local_config = main.getLocalConfig() + + # Creating dataset + data_scale = local_config.createDataset("DATA_SCALE") + self.assertTrue(isinstance(data_scale, LocalDataset)) + + # Try to add existing dataset + with self.assertRaises(ValueError): + local_config.createDataset("DATA_SCALE") + + with self.assertRaises(KeyError): + data_scale.addNode("KEY_NOTIN_ENSEMBLE") + + self.assertEqual(0, len(data_scale)) + data_scale.addNode("PERLIN_PARAM") + self.assertEqual(1, len(data_scale)) + active_list = data_scale.getActiveList("PERLIN_PARAM") + self.assertTrue(isinstance(active_list, ActiveList)) + active_list.addActiveIndex(0) + self.assertTrue("PERLIN_PARAM" in data_scale) + self.assertFalse("KEY_NOTIN_ENSEMBLE" in data_scale) + + ministep = local_config.createMinistep("MINISTEP") + ministep.attachDataset(data_scale) + self.assertTrue("DATA_SCALE" in ministep) + data_scale_get = ministep["DATA_SCALE"] + self.assertTrue("PERLIN_PARAM" in data_scale_get) + + # Error when adding existing data node + with self.assertRaises(KeyError): + data_scale.addNode("PERLIN_PARAM") + + @tmpdir() + def test_keys(self): + with ErtTestContext( + "python/enkf/data/local_config", self.config + ) as test_context: + main = test_context.getErt() + + local_config = main.getLocalConfig() + + # Creating dataset + local_data = local_config.createDataset("DATA_SCALE") + keys = local_data.keys() + self.assertEqual(len(keys), 0) + + local_data.addNode("PERLIN_PARAM") + keys = local_data.keys() + self.assertEqual(len(keys), 1) + self.assertTrue("PERLIN_PARAM" in keys) diff --git a/libres/tests/res/enkf/test_local_obsdata_node.py b/libres/tests/res/enkf/test_local_obsdata_node.py new file mode 100644 index 00000000000..5d2ce359e94 --- /dev/null +++ b/libres/tests/res/enkf/test_local_obsdata_node.py @@ -0,0 +1,19 @@ +from res.enkf import LocalObsdataNode +from tests import ResTest + + +class LocalObsdataNodeTest(ResTest): + def setUp(self): + pass + + def test_tstep(self): + node = LocalObsdataNode("KEY") + self.assertTrue(node.allTimeStepActive()) + self.assertTrue(node.tstepActive(10)) + self.assertTrue(node.tstepActive(0)) + + node.addTimeStep(10) + self.assertFalse(node.allTimeStepActive()) + + self.assertTrue(node.tstepActive(10)) + self.assertFalse(node.tstepActive(0)) diff --git a/libres/tests/res/enkf/test_log_config.py b/libres/tests/res/enkf/test_log_config.py new file mode 100644 index 00000000000..d82b829fdc9 --- /dev/null +++ b/libres/tests/res/enkf/test_log_config.py @@ -0,0 +1,129 @@ +# Copyright (C) 2017 Equinor ASA, Norway. +# +# The file 'test_log_config.py' 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. + +import itertools +import os +import sys + +from ecl.util.test import TestAreaContext + +from res.enkf import LogConfig, ResConfig, ConfigKeys +from res.util.enums import MessageLevelEnum +from tests import ResTest + + +class LogConfigTest(ResTest): + def setUp(self): + self.case_directory = self.createTestPath("local/simple_config/") + self.config_file = "simple_config/minimum_config" + + self.log_files = [ + (None, "simple_config/log.txt"), + ("file_loglog", "simple_config/file_loglog"), + ("this/is/../my/log/file.loglog", "simple_config/this/my/log/file.loglog"), + ] + + self.log_levels = [(None, MessageLevelEnum.LOG_WARNING)] + for message_level in [lev for lev in MessageLevelEnum.enums() if lev.value]: + self.log_levels.append((message_level.name.split("_")[1], message_level)) + + def assert_log_config_load( + self, log_file, exp_log_file, log_level, exp_log_level, write_abs_path=False + ): + + with TestAreaContext("log_config_test") as work_area: + work_area.copy_directory(self.case_directory) + + config_dict = {} + # Append config file + with open(self.config_file, "a") as cf: + if log_file: + config_dict[ConfigKeys.LOG_FILE] = os.path.realpath( + os.path.join( + os.path.abspath(os.path.split(self.config_file)[0]), + log_file, + ) + ) + + if write_abs_path: + log_file = config_dict[ConfigKeys.LOG_FILE] + + cf.write("\nLOG_FILE %s\n" % log_file) + + else: + config_dict[ConfigKeys.LOG_FILE] = os.path.realpath( + os.path.join( + os.path.abspath(os.path.split(self.config_file)[0]), + "log.txt", + ) + ) + + if log_level: + level = log_level + if not log_level.isalpha(): + level = int(float(level)) + cf.write("\nLOG_LEVEL %s\n" % level) + if type(level) is str and level.isdigit(): + config_dict[ConfigKeys.LOG_LEVEL] = MessageLevelEnum.to_enum( + eval(level) + ) + elif type(level) is str: + config_dict[ + ConfigKeys.LOG_LEVEL + ] = MessageLevelEnum.from_string("LOG_" + level) + else: + config_dict[ConfigKeys.LOG_LEVEL] = MessageLevelEnum.to_enum( + level + ) + else: + config_dict[ConfigKeys.LOG_LEVEL] = MessageLevelEnum.LOG_WARNING + + log_config = LogConfig(user_config_file=self.config_file) + res_config = ResConfig(self.config_file) + log_config_dict = LogConfig(config_dict=config_dict) + self.assertEqual(log_config, log_config_dict) + self.assertEqual(log_config, res_config.log_config) + + self.assertTrue(os.path.isabs(log_config.log_file)) + + self.assertEqual( + os.path.normpath(log_config.log_file), + os.path.normpath(os.path.abspath(exp_log_file)), + ) + + if isinstance(log_config.log_level, int): + level = MessageLevelEnum.to_enum(log_config.log_level) + else: + level = log_config.log_level + + self.assertEqual(level, exp_log_level) + + def test_log_config(self): + test_cases = itertools.product(self.log_files, self.log_levels) + + for log_file_data, log_level_data in test_cases: + self.assert_log_config_load( + log_file_data[0], log_file_data[1], log_level_data[0], log_level_data[1] + ) + + if log_file_data[0]: + self.assert_log_config_load( + log_file_data[0], + log_file_data[1], + log_level_data[0], + log_level_data[1], + write_abs_path=True, + ) diff --git a/libres/tests/res/enkf/test_meas_block.py b/libres/tests/res/enkf/test_meas_block.py new file mode 100644 index 00000000000..6a24ade5082 --- /dev/null +++ b/libres/tests/res/enkf/test_meas_block.py @@ -0,0 +1,89 @@ +import datetime + +from ecl.util.test import TestAreaContext +from tests import ResTest +from ecl.util.util import BoolVector +from res.enkf import MeasBlock + + +class MeasBlockTest(ResTest): + def test_create(self): + key = "OBS" + ens_size = 100 + obs_size = 77 + ens_mask = BoolVector(default_value=True, initial_size=ens_size) + + ens_mask[67] = False + block = MeasBlock(key, obs_size, ens_mask) + self.assertEqual(block.getObsSize(), obs_size) + self.assertEqual(block.getActiveEnsSize(), ens_size - 1) + self.assertEqual(block.getTotalEnsSize(), ens_size) + + self.assertTrue(block.iensActive(66)) + self.assertFalse(block.iensActive(67)) + + def test_update(self): + key = "OBS" + obs_size = 4 + ens_size = 10 + ens_mask = BoolVector(default_value=True, initial_size=ens_size) + block = MeasBlock(key, obs_size, ens_mask) + + with self.assertRaises(TypeError): + block["String"] = 10 + + with self.assertRaises(TypeError): + block[10] = 10 + + with self.assertRaises(IndexError): + block[obs_size, 0] = 10 + + with self.assertRaises(IndexError): + block[0, ens_size] = 10 + + # ----------------------------------------------------------------- + + with self.assertRaises(TypeError): + a = block["String"] + + with self.assertRaises(TypeError): + a = block[10] + + with self.assertRaises(IndexError): + val = block[obs_size, 0] + + with self.assertRaises(IndexError): + val = block[0, ens_size] + + block[1, 2] = 3 + self.assertEqual(3, block[1, 2]) + + def test_inactive(self): + key = "OBS" + obs_size = 2 + ens_size = 10 + ens_mask = BoolVector(default_value=True, initial_size=ens_size) + ens_mask[5] = False + block = MeasBlock(key, obs_size, ens_mask) + + self.assertFalse(block.iensActive(5)) + + with self.assertRaises(ValueError): + block[0, 5] = 10 + + def test_stat(self): + key = "OBS" + obs_size = 2 + ens_size = 10 + ens_mask = BoolVector(default_value=True, initial_size=ens_size) + block = MeasBlock(key, obs_size, ens_mask) + + for iens in range(ens_size): + block[0, iens] = iens + block[1, iens] = iens + 1 + + self.assertEqual(4.5, block.igetMean(0)) + self.assertEqual(5.5, block.igetMean(1)) + + self.assertFloatEqual(2.872281, block.igetStd(0)) + self.assertFloatEqual(2.872281, block.igetStd(1)) diff --git a/libres/tests/res/enkf/test_meas_data.py b/libres/tests/res/enkf/test_meas_data.py new file mode 100644 index 00000000000..3613e15d557 --- /dev/null +++ b/libres/tests/res/enkf/test_meas_data.py @@ -0,0 +1,70 @@ +import datetime + +from ecl.util.util import BoolVector +from ecl.util.test import TestAreaContext +from tests import ResTest +from res.enkf import MeasBlock, MeasData + + +class MeasDataTest(ResTest): + def test_create(self): + ens_size = 10 + ens_mask = BoolVector(default_value=True, initial_size=ens_size) + data = MeasData(ens_mask) + self.assertEqual(len(data), 0) + self.assertTrue(isinstance(data, MeasData)) + + block1 = data.addBlock("OBS1", 10, 5) + block2 = data.addBlock("OBS2", 27, 10) + + with self.assertRaises(TypeError): + data[1.782] + + with self.assertRaises(KeyError): + data["NO-this-does-not-exist"] + + with self.assertRaises(IndexError): + data[2] + + last0 = data[-1] + last1 = data[1] + self.assertEqual(last0, last1) + + self.assertTrue("OBS1-10" in data) + self.assertTrue("OBS2-27" in data) + self.assertEqual(len(data), 2) + + self.assertTrue(isinstance(block1, MeasBlock)) + self.assertTrue(isinstance(block2, MeasBlock)) + + self.assertEqual(block1.getObsSize(), 5) + self.assertEqual(block2.getObsSize(), 10) + + l = [] + for b in data: + l.append(b) + + self.assertEqual(len(l), 2) + self.assertEqual(l[0], block1) + self.assertEqual(l[1], block2) + + with self.assertRaises(ValueError): + S = data.createS() + + for iens in range(ens_size): + block1[0, iens] = 5 + block2[0, iens] = 10 + block2[1, iens] = 15 + + self.assertEqual(3, data.activeObsSize()) + S = data.createS() + + self.assertEqual(S.dims(), (3, ens_size)) + + for iens in range(ens_size): + self.assertEqual(S[0, iens], 5) + self.assertEqual(S[1, iens], 10) + self.assertEqual(S[2, iens], 15) + + pfx = "MeasData(len = " + self.assertEqual(pfx, repr(data)[: len(pfx)]) diff --git a/libres/tests/res/enkf/test_model_config.py b/libres/tests/res/enkf/test_model_config.py new file mode 100644 index 00000000000..d10449f3c6a --- /dev/null +++ b/libres/tests/res/enkf/test_model_config.py @@ -0,0 +1,166 @@ +# Copyright (C) 2017 Equinor ASA, Norway. +# +# The file 'test_model_config.py' 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. +import os +from ecl.util.test import TestAreaContext +from tests import ResTest +from res.enkf import ResConfig, ConfigKeys, ModelConfig +from res.sched import HistorySourceEnum +from res.test import ErtTestContext + + +class ModelConfigTest(ResTest): + def setUp(self): + self.config_both = { + "INTERNALS": { + "CONFIG_DIRECTORY": "simple_config", + }, + "SIMULATION": { + "QUEUE_SYSTEM": { + "JOBNAME": "JOBNAME%d", + }, + "RUNPATH": "/tmp/simulations/run%d", + "NUM_REALIZATIONS": 1, + "JOB_SCRIPT": "script.sh", + "ENSPATH": "Ensemble", + "ECLBASE": "ECLBASE%d", + }, + } + + self.config_eclbase = { + "INTERNALS": { + "CONFIG_DIRECTORY": "simple_config", + }, + "SIMULATION": { + "RUNPATH": "/tmp/simulations/run%d", + "NUM_REALIZATIONS": 1, + "JOB_SCRIPT": "script.sh", + "ENSPATH": "Ensemble", + "ECLBASE": "ECLBASE%d", + }, + } + + self.config_jobname = { + "INTERNALS": { + "CONFIG_DIRECTORY": "simple_config", + }, + "SIMULATION": { + "QUEUE_SYSTEM": { + "JOBNAME": "JOBNAME%d", + }, + "RUNPATH": "/tmp/simulations/run%d", + "NUM_REALIZATIONS": 1, + "JOB_SCRIPT": "script.sh", + "ENSPATH": "Ensemble", + }, + } + + def test_eclbase_and_jobname(self): + case_directory = self.createTestPath("local/simple_config") + with TestAreaContext("test_eclbase_and_jobname") as work_area: + work_area.copy_directory(case_directory) + + res_config = ResConfig(config=self.config_both) + model_config = res_config.model_config + ecl_config = res_config.ecl_config + + self.assertTrue(ecl_config.active()) + self.assertEqual("JOBNAME%d", model_config.getJobnameFormat()) + + def test_eclbase(self): + case_directory = self.createTestPath("local/simple_config") + with TestAreaContext("test_eclbase") as work_area: + work_area.copy_directory(case_directory) + + res_config = ResConfig(config=self.config_eclbase) + model_config = res_config.model_config + ecl_config = res_config.ecl_config + + self.assertTrue(ecl_config.active()) + self.assertEqual("ECLBASE%d", model_config.getJobnameFormat()) + + def test_jobname(self): + case_directory = self.createTestPath("local/simple_config") + with TestAreaContext("test_jobname") as work_area: + work_area.copy_directory(case_directory) + + res_config = ResConfig(config=self.config_jobname) + model_config = res_config.model_config + ecl_config = res_config.ecl_config + + self.assertFalse(ecl_config.active()) + self.assertEqual("JOBNAME%d", model_config.getJobnameFormat()) + + def test_model_config_dict_constructor(self): + case_directory = self.createTestPath("local/configuration_tests") + with TestAreaContext("test_constructor") as work_area: + work_area.copy_directory(case_directory) + res_config = ResConfig( + user_config_file="configuration_tests/model_config.ert" + ) + config_dict = { + ConfigKeys.MAX_RESAMPLE: 1, + ConfigKeys.JOBNAME: "model_config_test", + ConfigKeys.RUNPATH: "/tmp/simulations/run%d", + ConfigKeys.NUM_REALIZATIONS: 10, + ConfigKeys.ENSPATH: "configuration_tests/Ensemble", + ConfigKeys.TIME_MAP: "configuration_tests/input/refcase/time_map.txt", + ConfigKeys.OBS_CONFIG: "configuration_tests/input/observations/observations.txt", + ConfigKeys.DATAROOT: "configuration_tests/", + ConfigKeys.HISTORY_SOURCE: HistorySourceEnum(1), + ConfigKeys.GEN_KW_EXPORT_NAME: "parameter_test.json", + ConfigKeys.FORWARD_MODEL: [ + { + ConfigKeys.NAME: "COPY_FILE", + ConfigKeys.ARGLIST: "=input/schedule.sch, =output/schedule_copy.sch", + }, + { + ConfigKeys.NAME: "SNAKE_OIL_SIMULATOR", + ConfigKeys.ARGLIST: "", + }, + { + ConfigKeys.NAME: "SNAKE_OIL_NPV", + ConfigKeys.ARGLIST: "", + }, + { + ConfigKeys.NAME: "SNAKE_OIL_DIFF", + ConfigKeys.ARGLIST: "", + }, + ], + } + model_config = ModelConfig( + data_root="", + joblist=res_config.site_config.get_installed_jobs(), + last_history_restart=res_config.ecl_config.getLastHistoryRestart(), + refcase=res_config.ecl_config.getRefcase(), + config_dict=config_dict, + ) + self.assertEqual(model_config, res_config.model_config) + + def test_schedule_file_as_history_is_disallowed(self): + case_directory = self.createTestPath("local/configuration_tests") + with TestAreaContext("test_constructor") as work_area: + work_area.copy_directory(case_directory) + with self.assertRaises(ValueError) as cm: + ResConfig( + user_config_file="configuration_tests/sched_file_as_history_source.ert" + ) + + # Any assert should per the unittest documentation be outside the + # scope of the assertRaises with-block. + expected = "{} as {} is not supported".format( + str(HistorySourceEnum.SCHEDULE), ConfigKeys.HISTORY_SOURCE + ) + self.assertIn(expected, str(cm.exception)) diff --git a/libres/tests/res/enkf/test_obs_block.py b/libres/tests/res/enkf/test_obs_block.py new file mode 100644 index 00000000000..4e389bace03 --- /dev/null +++ b/libres/tests/res/enkf/test_obs_block.py @@ -0,0 +1,71 @@ +import datetime + +from ecl.util.util import BoolVector +from ecl.util.test import TestAreaContext +from tests import ResTest +from res.enkf import ObsBlock + + +class ObsBlockTest(ResTest): + def test_create(self): + block = ObsBlock("OBS", 1000) + self.assertTrue(isinstance(block, ObsBlock)) + self.assertEqual(1000, block.totalSize()) + self.assertEqual(0, block.activeSize()) + + def test_access(self): + obs_size = 10 + block = ObsBlock("OBS", obs_size) + + with self.assertRaises(IndexError): + block[100] = (1, 1) + + with self.assertRaises(IndexError): + block[-100] = (1, 1) + + with self.assertRaises(TypeError): + block[4] = 10 + + with self.assertRaises(TypeError): + block[4] = (1, 1, 9) + + # ------ + + with self.assertRaises(IndexError): + v = block[100] + + with self.assertRaises(IndexError): + v = block[-100] + + block[0] = (10, 1) + v = block[0] + self.assertEqual(v, (10, 1)) + self.assertEqual(1, block.activeSize()) + + block[-1] = (17, 19) + self.assertEqual(block[-1], (17, 19)) + + def test_is_active(self): + obs_size = 10 + block = ObsBlock("OBS", obs_size) + self.assertEqual(obs_size, block.totalSize()) + self.assertEqual(0, block.activeSize()) + for i in range(block.totalSize()): + self.assertFalse(block.is_active(i)) + + active_indexes = [2, 5, 8, 9] + for index in active_indexes: + block[index] = (10 * index, index) + self.assertEqual(obs_size, block.totalSize()) + self.assertEqual(len(active_indexes), block.activeSize()) + + for i in active_indexes: + self.assertTrue(block.is_active(i)) + + for i in set(range(block.totalSize())) - set(active_indexes): + self.assertFalse(block.is_active(i)) + + def test_obs_key(self): + obs_size = 10 + block = ObsBlock("OBS", obs_size) + self.assertEqual("OBS", block.get_obs_key()) diff --git a/libres/tests/res/enkf/test_obs_data.py b/libres/tests/res/enkf/test_obs_data.py new file mode 100644 index 00000000000..7cee3b006ce --- /dev/null +++ b/libres/tests/res/enkf/test_obs_data.py @@ -0,0 +1,58 @@ +import datetime + +from ecl.util.util import BoolVector +from ecl.util.test import TestAreaContext +from tests import ResTest +from res.enkf import ObsData, ObsBlock +from res.util import Matrix + + +class ObsDataTest(ResTest): + def test_create(self): + obs_data = ObsData() + obs_size = 10 + block = obs_data.addBlock("OBS", obs_size) + self.assertTrue(isinstance(block, ObsBlock)) + + block[0] = (100, 10) + block[1] = (120, 12) + D = obs_data.createDObs() + self.assertTrue(isinstance(D, Matrix)) + self.assertEqual(D.dims(), (2, 2)) + + self.assertEqual(D[0, 0], 100) + self.assertEqual(D[1, 0], 120) + self.assertEqual(D[0, 1], 10) + self.assertEqual(D[1, 1], 12) + + obs_data.scaleMatrix(D) + self.assertEqual(D[0, 0], 10) + self.assertEqual(D[1, 0], 10) + self.assertEqual(D[0, 1], 1) + self.assertEqual(D[1, 1], 1) + + R = obs_data.createR() + self.assertEqual((2, 2), R.dims()) + + with self.assertRaises(IndexError): + obs_data[10] + + v, s = obs_data[0] + self.assertEqual(v, 100) + self.assertEqual(s, 10) + + v, s = obs_data[1] + self.assertEqual(v, 120) + self.assertEqual(s, 12) + + def test_get_block(self): + obs_data = ObsData() + + obs_blocks = [("OBS1", 10), ("OBS2", 7), ("OBS3", 15)] + for name, size in obs_blocks: + obs_data.addBlock(name, size) + + self.assertEqual(len(obs_blocks), obs_data.get_num_blocks()) + + for i in range(obs_data.get_num_blocks()): + self.assertEqual(obs_blocks[i][0], obs_data.get_block(i).get_obs_key()) diff --git a/libres/tests/res/enkf/test_programmatic_res_config.py b/libres/tests/res/enkf/test_programmatic_res_config.py new file mode 100644 index 00000000000..7e98b6c1995 --- /dev/null +++ b/libres/tests/res/enkf/test_programmatic_res_config.py @@ -0,0 +1,465 @@ +# Copyright (C) 2017 Equinor ASA, Norway. +# +# The file 'test_programmatic_res_config.py' 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. +import os +from ecl.util.test import TestAreaContext +from tests import ResTest + +from res.test import ErtTestContext +from res.enkf import ResConfig, ConfigKeys + + +class ProgrammaticResConfigTest(ResTest): + def setUp(self): + self.minimum_config = { + "INTERNALS": { + "CONFIG_DIRECTORY": "simple_config", + }, + "SIMULATION": { + "QUEUE_SYSTEM": { + "JOBNAME": "Job%d", + }, + "RUNPATH": "/tmp/simulations/run%d", + "NUM_REALIZATIONS": 10, + "MIN_REALIZATIONS": 10, + "JOB_SCRIPT": "script.sh", + "ENSPATH": "Ensemble", + }, + } + + self.minimum_config_extra_key = { + "INTERNALS": { + "CONFIG_DIRECTORY": "simple_config", + }, + "UNKNOWN_KEY": "Have/not/got/a/clue", + "SIMULATION": { + "QUEUE_SYSTEM": { + "JOBNAME": "Job%d", + }, + "RUNPATH": "/tmp/simulations/run%d", + "NUM_REALIZATIONS": 10, + "JOB_SCRIPT": "script.sh", + "ENSPATH": "Ensemble", + }, + } + + self.minimum_config_error = { + "INTERNALS": { + "CONFIG_DIRECTORY": "simple_config", + }, + "SIMULATION": { + "QUEUE_SYSTEM": { + "JOBNAME": "Job%d", + }, + "RUNPATH": "/tmp/simulations/run%d", + "NUM_REALIZATIONS": "/should/be/an/integer", + "JOB_SCRIPT": "script.sh", + "ENSPATH": "Ensemble", + }, + } + + self.minimum_config_cwd = { + "INTERNALS": {}, + "SIMULATION": { + "QUEUE_SYSTEM": { + "JOBNAME": "Job%d", + }, + "RUNPATH": "/tmp/simulations/run%d", + "NUM_REALIZATIONS": 10, + "JOB_SCRIPT": "script.sh", + "ENSPATH": "Ensemble", + }, + } + + self.new_config = { + "INTERNALS": {}, + "SIMULATION": { + "QUEUE_SYSTEM": { + "QUEUE_SYSTEM": "LOCAL", + "JOBNAME": "SIM_KW", + }, + "ECLBASE": "SIM_KW", + "NUM_REALIZATIONS": 10, + "INSTALL_JOB": [ + {"NAME": "NEW_JOB_A", "PATH": "simulation_model/jobs/NEW_TYPE_A"}, + {"NAME": "NEW_JOB_B", "PATH": "simulation_model/jobs/NEW_TYPE_B"}, + {"NAME": "NEW_JOB_C", "PATH": "simulation_model/jobs/NEW_TYPE_C"}, + ], + "SIMULATION_JOB": [ + {"NAME": "NEW_JOB_A", "ARGLIST": ["Hello", True, 3.14, 4]}, + {"NAME": "NEW_JOB_B", "ARGLIST": ["word"]}, + ], + }, + } + + self.large_config = { + "DEFINES": { + "": "TEST_USER", + "": "scratch/ert", + "": "the_extensive_case", + "": "XYZ", + }, + "INTERNALS": { + "CONFIG_DIRECTORY": "snake_oil_structure/ert/model", + }, + "SIMULATION": { + "QUEUE_SYSTEM": { + "JOBNAME": "SNAKE_OIL_STRUCTURE_%d", + "QUEUE_SYSTEM": "LSF", + "MAX_RUNTIME": 23400, + "MIN_REALIZATIONS": "50%", + "MAX_SUBMIT": 13, + "UMASK": "007", + "QUEUE_OPTION": [ + {"DRIVER_NAME": "LSF", "OPTION": "MAX_RUNNING", "VALUE": 100}, + { + "DRIVER_NAME": "LSF", + "OPTION": "LSF_RESOURCE", + "VALUE": "select[x86_64Linux] same[type:model]", + }, + { + "DRIVER_NAME": "LSF", + "OPTION": "LSF_SERVER", + "VALUE": "simulacrum", + }, + { + "DRIVER_NAME": "LSF", + "OPTION": "LSF_QUEUE", + "VALUE": "mr", + }, + ], + }, + "DATA_FILE": "../../eclipse/model/SNAKE_OIL.DATA", + "RUNPATH": "///realization-%d/iter-%d", + "RUNPATH_FILE": "../output/run_path_file/.ert-runpath-list_", + "ECLBASE": "eclipse/model/-%d", + "NUM_REALIZATIONS": "10", + "ENSPATH": "../output/storage/", + "GRID": "../../eclipse/include/grid/CASE.EGRID", + "REFCASE": "../input/refcase/SNAKE_OIL_FIELD", + "HISTORY_SOURCE": "REFCASE_HISTORY", + "OBS_CONFIG": "../input/observations/obsfiles/observations.txt", + "TIME_MAP": "../input/refcase/time_map.txt", + "SUMMARY": [ + "WOPR:PROD", + "WOPT:PROD", + "WWPR:PROD", + "WWCT:PROD", + "WWPT:PROD", + "WBHP:PROD", + "WWIR:INJ", + "WWIT:INJ", + "WBHP:INJ", + "ROE:1", + ], + "INSTALL_JOB": [ + { + "NAME": "SNAKE_OIL_SIMULATOR", + "PATH": "../../snake_oil/jobs/SNAKE_OIL_SIMULATOR", + }, + { + "NAME": "SNAKE_OIL_NPV", + "PATH": "../../snake_oil/jobs/SNAKE_OIL_NPV", + }, + { + "NAME": "SNAKE_OIL_DIFF", + "PATH": "../../snake_oil/jobs/SNAKE_OIL_DIFF", + }, + ], + "LOAD_WORKFLOW_JOB": ["../bin/workflows/workflowjobs/UBER_PRINT"], + "LOAD_WORKFLOW": ["../bin/workflows/MAGIC_PRINT"], + "FORWARD_MODEL": [ + "SNAKE_OIL_SIMULATOR", + "SNAKE_OIL_NPV", + "SNAKE_OIL_DIFF", + ], + "RUN_TEMPLATE": [ + { + "TEMPLATE": "../input/templates/seed_template.txt", + "EXPORT": "seed.txt", + } + ], + "GEN_KW": [ + { + "NAME": "SIGMA", + "TEMPLATE": "../input/templates/sigma.tmpl", + "OUT_FILE": "coarse.sigma", + "PARAMETER_FILE": "../input/distributions/sigma.dist", + } + ], + "GEN_DATA": [ + { + "NAME": "super_data", + "RESULT_FILE": "super_data_%d", + "REPORT_STEPS": 1, + } + ], + "LOGGING": { + "LOG_LEVEL": "INFO", + "UPDATE_LOG_PATH": "../output/update_log/", + "LOG_FILE": "../output/log/ert_.log", + }, + }, + } + + def test_minimum_config(self): + case_directory = self.createTestPath("local/simple_config") + config_file = "simple_config/minimum_config" + + with TestAreaContext("res_config_prog_test") as work_area: + work_area.copy_directory(case_directory) + + loaded_res_config = ResConfig(user_config_file=config_file) + prog_res_config = ResConfig(config=self.minimum_config) + + self.assertEqual( + loaded_res_config.model_config.num_realizations, + prog_res_config.model_config.num_realizations, + ) + + self.assertEqual( + loaded_res_config.model_config.getJobnameFormat(), + prog_res_config.model_config.getJobnameFormat(), + ) + + self.assertEqual( + loaded_res_config.model_config.getRunpathAsString(), + prog_res_config.model_config.getRunpathAsString(), + ) + + self.assertEqual( + loaded_res_config.model_config.getEnspath(), + prog_res_config.model_config.getEnspath(), + ) + + self.assertEqual(0, len(prog_res_config.errors)) + self.assertEqual(0, len(prog_res_config.failed_keys)) + + def test_no_config_directory(self): + case_directory = self.createTestPath("local/simple_config") + config_file = "simple_config/minimum_config" + + with TestAreaContext("res_config_prog_test") as work_area: + work_area.copy_directory(case_directory) + + with self.assertRaises(ValueError): + ResConfig(config=self.minimum_config_cwd) + + def test_errors(self): + case_directory = self.createTestPath("local/simple_config") + config_file = "simple_config/minimum_config" + + with TestAreaContext("res_config_prog_test") as work_area: + work_area.copy_directory(case_directory) + + with self.assertRaises(ValueError): + res_config = ResConfig(config=self.minimum_config_error) + + res_config = ResConfig( + config=self.minimum_config_error, throw_on_error=False + ) + + self.assertTrue(len(res_config.errors) > 0) + self.assertEqual(0, len(res_config.failed_keys)) + + def test_failed_keys(self): + case_directory = self.createTestPath("local/simple_config") + config_file = "simple_config/minimum_config" + + with TestAreaContext("res_config_prog_test") as work_area: + work_area.copy_directory(case_directory) + + res_config = ResConfig(config=self.minimum_config_extra_key) + + self.assertTrue(len(res_config.failed_keys) == 1) + self.assertEqual(["UNKNOWN_KEY"], list(res_config.failed_keys.keys())) + self.assertEqual( + self.minimum_config_extra_key["UNKNOWN_KEY"], + res_config.failed_keys["UNKNOWN_KEY"], + ) + + def assert_equal_model_config(self, loaded_model_config, prog_model_config): + self.assertEqual( + loaded_model_config.num_realizations, prog_model_config.num_realizations + ) + + self.assertEqual( + loaded_model_config.getJobnameFormat(), prog_model_config.getJobnameFormat() + ) + + self.assertEqual( + loaded_model_config.getRunpathAsString(), + prog_model_config.getRunpathAsString(), + ) + + self.assertEqual( + loaded_model_config.getEnspath(), prog_model_config.getEnspath() + ) + + self.assertEqual( + loaded_model_config.get_history_source(), + prog_model_config.get_history_source(), + ) + + self.assertEqual( + loaded_model_config.obs_config_file, prog_model_config.obs_config_file + ) + + self.assertEqual( + loaded_model_config.getForwardModel().joblist(), + prog_model_config.getForwardModel().joblist(), + ) + + def assert_equal_site_config(self, loaded_site_config, prog_site_config): + self.assertEqual(loaded_site_config, prog_site_config) + + def assert_equal_ecl_config(self, loaded_ecl_config, prog_ecl_config): + self.assertEqual(loaded_ecl_config, prog_ecl_config) + + def assert_equal_analysis_config(self, loaded_config, prog_config): + self.assertEqual(loaded_config.get_log_path(), prog_config.get_log_path()) + + self.assertEqual(loaded_config.get_max_runtime(), prog_config.get_max_runtime()) + + def assert_equal_hook_manager(self, loaded_hook_manager, prog_hook_manager): + self.assertEqual(loaded_hook_manager, prog_hook_manager) + + def assert_equal_log_config(self, loaded_log_config, prog_log_config): + self.assertEqual(loaded_log_config.log_file, prog_log_config.log_file) + + self.assertEqual(loaded_log_config.log_level, prog_log_config.log_level) + + def assert_equal_ensemble_config(self, loaded_config, prog_config): + self.assertEqual( + set(loaded_config.alloc_keylist()), set(prog_config.alloc_keylist()) + ) + + def assert_equal_ert_templates(self, loaded_templates, prog_templates): + self.assertEqual( + loaded_templates.getTemplateNames(), prog_templates.getTemplateNames() + ) + + for template_name in loaded_templates.getTemplateNames(): + let = loaded_templates.get_template(template_name) + pet = prog_templates.get_template(template_name) + + self.assertEqual(let.get_template_file(), pet.get_template_file()) + self.assertEqual(let.get_target_file(), pet.get_target_file()) + + def assert_equal_ert_workflow(self, loaded_workflow_list, prog_workflow_list): + self.assertEqual(loaded_workflow_list, prog_workflow_list) + + def assert_equal_simulation_config( + self, loaded_simulation_config, prog_simulation_config + ): + self.assertEqual( + loaded_simulation_config.getPath(), prog_simulation_config.getPath() + ) + + def test_new_config(self): + case_directory = self.createTestPath("local/simulation_model") + config_file = "simulation_model/sim_kw.ert" + + with TestAreaContext("res_config_sim_job") as work_area: + work_area.copy_directory(case_directory) + + prog_res_config = ResConfig(config=self.new_config) + forward_model = prog_res_config.model_config.getForwardModel() + job_A = forward_model.iget_job(0) + job_B = forward_model.iget_job(1) + self.assertEqual(job_A.get_arglist(), ["Hello", "True", "3.14", "4"]) + self.assertEqual(job_B.get_arglist(), ["word"]) + + def test_large_config(self): + case_directory = self.createTestPath("local/snake_oil_structure") + config_file = "snake_oil_structure/ert/model/user_config.ert" + + with TestAreaContext("res_config_prog_test") as work_area: + work_area.copy_directory(case_directory) + + loaded_res_config = ResConfig(user_config_file=config_file) + prog_res_config = ResConfig(config=self.large_config) + + self.assert_equal_model_config( + loaded_res_config.model_config, prog_res_config.model_config + ) + + self.assert_equal_site_config( + loaded_res_config.site_config, prog_res_config.site_config + ) + + self.assert_equal_ecl_config( + loaded_res_config.ecl_config, prog_res_config.ecl_config + ) + + self.assert_equal_analysis_config( + loaded_res_config.analysis_config, prog_res_config.analysis_config + ) + + self.assert_equal_hook_manager( + loaded_res_config.hook_manager, prog_res_config.hook_manager + ) + + self.assert_equal_log_config( + loaded_res_config.log_config, prog_res_config.log_config + ) + + self.assert_equal_ensemble_config( + loaded_res_config.ensemble_config, prog_res_config.ensemble_config + ) + + self.assert_equal_ert_templates( + loaded_res_config.ert_templates, prog_res_config.ert_templates + ) + + self.assert_equal_ert_workflow( + loaded_res_config.ert_workflow_list, prog_res_config.ert_workflow_list + ) + + self.assertEqual( + loaded_res_config.queue_config, prog_res_config.queue_config + ) + + self.assertEqual(0, len(prog_res_config.failed_keys)) + + def test_test_context(self): + case_directory = self.createTestPath("local/simple_config") + + # We first make sure that the files referred to in the + # minimum_config dictionary are found are located correctly by + # creating a working area, and then we create testcontext from + # there. + with TestAreaContext("res_config_prog_test", store_area=True) as work_area: + work_area.copy_directory(case_directory) + self.assertTrue(os.path.isfile("simple_config/script.sh")) + + with ErtTestContext( + "dict_test", config_dict=self.minimum_config, store_area=True + ): + pass + + os.chdir("simple_config") + # The directory referenced in INTERNALS.CONFIG_DIRECTORY does not exist => IOError + with self.assertRaises(IOError): + with ErtTestContext( + "dict_test", config_dict=self.minimum_config, store_area=True + ): + pass + + # Create minimum config in cwd: + with ErtTestContext( + "dict_test", config_dict=self.minimum_config_cwd, store_area=True + ): + pass diff --git a/libres/tests/res/enkf/test_queue_config.py b/libres/tests/res/enkf/test_queue_config.py new file mode 100644 index 00000000000..24703f5fa45 --- /dev/null +++ b/libres/tests/res/enkf/test_queue_config.py @@ -0,0 +1,80 @@ +# Copyright (C) 2017 Equinor ASA, Norway. +# +# The file 'test_summary_obs.py' 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. + +import os +from ecl.util.test import TestAreaContext + +from res.job_queue import QueueDriverEnum +from tests import ResTest +from res.enkf import QueueConfig, ConfigKeys +from res.config import ConfigContent + + +class QueueConfigTest(ResTest): + def setUp(self): + self.case_directory = self.createTestPath("local/simple_config/") + + def test_get_queue_config(self): + with TestAreaContext("queue_config_init_test") as work_area: + work_area.copy_directory(self.case_directory) + + config_file = "simple_config/minimum_config" + queue_config = QueueConfig(config_file) + job_queue = queue_config.create_job_queue() + queue_config_copy = queue_config.create_local_copy() + + self.assertEqual( + queue_config.has_job_script(), queue_config_copy.has_job_script() + ) + + config_content = ConfigContent(config_file) + + with self.assertRaises(ValueError): + queue_config = QueueConfig( + user_config_file=config_file, config_content=config_content + ) + + def test_queue_config_constructor(self): + with TestAreaContext("queue_config_constructor_test") as work_area: + work_area.copy_directory(self.case_directory) + config_file = "simple_config/minimum_config" + config_dict = { + ConfigKeys.JOB_SCRIPT: os.getcwd() + "/simple_config/script.sh", + ConfigKeys.QUEUE_SYSTEM: QueueDriverEnum(2), + ConfigKeys.USER_MODE: True, + ConfigKeys.MAX_SUBMIT: 2, + ConfigKeys.NUM_CPU: 0, + ConfigKeys.QUEUE_OPTION: [ + {ConfigKeys.NAME: "MAX_RUNNING", ConfigKeys.VALUE: "50"} + ], + } + + queue_config_file = QueueConfig(user_config_file=config_file) + queue_config_dict = QueueConfig(config_dict=config_dict) + self.assertEqual(queue_config_dict, queue_config_file) + + def test_get_slurm_queue_config(self): + with TestAreaContext("queue_config_slurm_test") as work_area: + work_area.copy_directory(self.case_directory) + + config_file = "simple_config/slurm_config" + queue_config = QueueConfig(config_file) + self.assertEqual(queue_config.queue_system, "SLURM") + + driver = queue_config.driver + self.assertEqual(driver.get_option("SBATCH"), "/path/to/sbatch") + self.assertEqual(driver.get_option("SCONTROL"), "scontrol") + self.assertEqual(driver.name, "SLURM") diff --git a/libres/tests/res/enkf/test_res_config.py b/libres/tests/res/enkf/test_res_config.py new file mode 100644 index 00000000000..6b4870fe845 --- /dev/null +++ b/libres/tests/res/enkf/test_res_config.py @@ -0,0 +1,680 @@ +# Copyright (C) 2017 Equinor ASA, Norway. +# +# The file 'test_res_config.py' 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. +import os, os.path +import stat +from copy import deepcopy +from datetime import date + +from ecl.util.test import TestAreaContext +from tests import ResTest +from tests.utils import tmpdir +from ecl.util.util import CTime +from ecl.util.enums import RngAlgTypeEnum +from res.util.enums import MessageLevelEnum + +from res.sched import HistorySourceEnum + +from res.job_queue import QueueDriverEnum +from res.enkf import ResConfig, SiteConfig, AnalysisConfig, ConfigKeys, GenDataFileType +from res.test import ErtTestContext + +from res.enkf import QueueConfig + +# The res_config object should set the environment variable +# 'DATA_ROOT' to the root directory with the config +# file. Unfortunately the python methods to get environment variable, +# os.getenv() and os.environ[] do not reflect the: +# +# setenv( "DATA_ROOT" , ...) +# +# call in the res_config C code. We therefor create a wrapper to the +# underlying libc getenv() function to be used for testing. + +from cwrap import Prototype +from cwrap import load + +clib = load(None) +clib_getenv = Prototype(clib, "char* getenv( char* )", bind=False) + + +config_defines = { + "": "TEST_USER", + "": "scratch/ert", + "": "the_extensive_case", + "": "XYZ", +} + +config_data = { + "RUNPATH": "///realization-%d/iter-%d", + "NUM_REALIZATIONS": 10, + "MAX_RUNTIME": 23400, + "MIN_REALIZATIONS": "50%", + "MAX_SUBMIT": 13, + "QUEUE_SYSTEM": "LSF", + "LSF_QUEUE": "mr", + "LSF_SERVER": "simulacrum", + "LSF_RESOURCE": "select[x86_64Linux] same[type:model]", + "UMASK": int("007", 8), + "MAX_RUNNING": "100", + "DATA_FILE": "../../eclipse/model/SNAKE_OIL.DATA", + "START": date(2017, 1, 1), + "SUMMARY": [ + "WOPR:PROD", + "WOPT:PROD", + "WWPR:PROD", + "WWCT:PROD", + "WWPT:PROD", + "WBHP:PROD", + "WWIR:INJ", + "WWIT:INJ", + "WBHP:INJ", + "ROE:1", + ], + "GEN_KW": ["SIGMA"], + "GEN_DATA": ["super_data"], + "ECLBASE": "eclipse/model/-%d", + "ENSPATH": "../output/storage/", + "PLOT_PATH": "../output/results/plot/", + "UPDATE_LOG_PATH": "../output/update_log/", + "LOG_FILE": "../output/log/ert_.log", + "RUNPATH_FILE": "../output/run_path_file/.ert-runpath-list_", + "REFCASE": "../input/refcase/SNAKE_OIL_FIELD", + "SIGMA": { + "TEMPLATE": "../input/templates/sigma.tmpl", + "RESULT": "coarse.sigma", + "PARAMETER": "../input/distributions/sigma.dist", + }, + "JOBNAME": "SNAKE_OIL_STRUCTURE_%d", + "INSTALL_JOB": { + "SNAKE_OIL_SIMULATOR": { + "CONFIG": "../../snake_oil/jobs/SNAKE_OIL_SIMULATOR", + "STDOUT": "snake_oil.stdout", + "STDERR": "snake_oil.stderr", + "EXECUTABLE": "snake_oil_simulator.py", + }, + "SNAKE_OIL_NPV": { + "CONFIG": "../../snake_oil/jobs/SNAKE_OIL_NPV", + "STDOUT": "snake_oil_npv.stdout", + "STDERR": "snake_oil_npv.stderr", + "EXECUTABLE": "snake_oil_npv.py", + }, + "SNAKE_OIL_DIFF": { + "CONFIG": "../../snake_oil/jobs/SNAKE_OIL_DIFF", + "STDOUT": "snake_oil_diff.stdout", + "STDERR": "snake_oil_diff.stderr", + "EXECUTABLE": "snake_oil_diff.py", + }, + }, + "FORWARD_MODEL": ["SNAKE_OIL_SIMULATOR", "SNAKE_OIL_NPV", "SNAKE_OIL_DIFF"], + "HISTORY_SOURCE": HistorySourceEnum.REFCASE_HISTORY, + "OBS_CONFIG": "../input/observations/obsfiles/observations.txt", + "LOAD_WORKFLOW": {"MAGIC_PRINT": "../bin/workflows/MAGIC_PRINT"}, + "LOAD_WORKFLOW_JOB": { + "UBER_PRINT": "../bin/workflows/workflowjobs/bin/uber_print.py" + }, + "LOG_LEVEL": MessageLevelEnum.LOG_INFO, + "RNG_ALG_TYPE": RngAlgTypeEnum.MZRAN, + "GRID": "../../eclipse/include/grid/CASE.EGRID", + "RUN_TEMPLATE": { + "seed_template": { + "TEMPLATE_FILE": "../input/templates/seed_template.txt", + "TARGET_FILE": "seed.txt", + } + }, +} + + +config_data_new = { + ConfigKeys.ALPHA_KEY: 3, + ConfigKeys.RERUN_KEY: False, + ConfigKeys.RERUN_START_KEY: 0, + ConfigKeys.MERGE_OBSERVATIONS: False, + ConfigKeys.UPDATE_LOG_PATH: "update_log", + ConfigKeys.STD_CUTOFF_KEY: 1e-6, + ConfigKeys.STOP_LONG_RUNNING: False, + ConfigKeys.SINGLE_NODE_UPDATE: False, + ConfigKeys.STD_CORRELATED_OBS: False, + ConfigKeys.GLOBAL_STD_SCALING: 1, + ConfigKeys.MIN_REALIZATIONS: 5, + # "MIN_REALIZATIONS" : "50%", percentages need to be fixed or removed + ConfigKeys.RUNPATH: "///realization-%d/iter-%d", # model + ConfigKeys.NUM_REALIZATIONS: 10, # model + ConfigKeys.MAX_RUNTIME: 23400, + ConfigKeys.END_DATE: "10/10/2010", + ConfigKeys.JOB_SCRIPT: "../../../script.sh", + ConfigKeys.QUEUE_SYSTEM: QueueDriverEnum.LSF_DRIVER, + ConfigKeys.USER_MODE: True, + ConfigKeys.MAX_SUBMIT: 13, + ConfigKeys.NUM_CPU: 0, + ConfigKeys.QUEUE_OPTION: [ + {ConfigKeys.NAME: "MAX_RUNNING", ConfigKeys.VALUE: "100"}, + {ConfigKeys.NAME: QueueConfig.LSF_QUEUE_NAME_KEY, ConfigKeys.VALUE: "mr"}, + {ConfigKeys.NAME: QueueConfig.LSF_SERVER_KEY, ConfigKeys.VALUE: "simulacrum"}, + { + ConfigKeys.NAME: QueueConfig.LSF_RESOURCE_KEY, + ConfigKeys.VALUE: "select[x86_64Linux] same[type:model]", + }, + ], + ConfigKeys.UMASK: int("007", 8), + ConfigKeys.MAX_RUNNING: "100", + ConfigKeys.DATA_FILE: "../../eclipse/model/SNAKE_OIL.DATA", + # "START" : date(2017, 1, 1), no clue where this comes from + ConfigKeys.GEN_KW_TAG_FORMAT: "<%s>", + ConfigKeys.SUMMARY: [ + "WOPR:PROD", + "WOPT:PROD", + "WWPR:PROD", + "WWCT:PROD", + "WWPT:PROD", + "WBHP:PROD", + "WWIR:INJ", + "WWIT:INJ", + "WBHP:INJ", + "ROE:1", + ], # ensemble + ConfigKeys.GEN_KW: [ + { + ConfigKeys.NAME: "SIGMA", + ConfigKeys.TEMPLATE: "../input/templates/sigma.tmpl", + ConfigKeys.OUT_FILE: "coarse.sigma", + ConfigKeys.PARAMETER_FILE: "../input/distributions/sigma.dist", + ConfigKeys.INIT_FILES: None, + ConfigKeys.MIN_STD: None, + ConfigKeys.FORWARD_INIT: False, + } # ensemble + ], + ConfigKeys.GEN_DATA: [ + { + ConfigKeys.NAME: "super_data", + ConfigKeys.INPUT_FORMAT: GenDataFileType.ASCII, + ConfigKeys.RESULT_FILE: "super_data_%d", + ConfigKeys.REPORT_STEPS: [1], + ConfigKeys.INIT_FILES: None, + ConfigKeys.ECL_FILE: None, + ConfigKeys.TEMPLATE: None, + ConfigKeys.KEY_KEY: None, + } # ensemble + ], + ConfigKeys.ECLBASE: "eclipse/model/-%d", # model, ecl + ConfigKeys.ENSPATH: "../output/storage/", # model + "PLOT_PATH": "../output/results/plot/", # removed, previously plot config + ConfigKeys.UPDATE_LOG_PATH: "../output/update_log/", # analysis + ConfigKeys.LOG_FILE: "../output/log/ert_.log", # log + ConfigKeys.RUNPATH_FILE: "../output/run_path_file/.ert-runpath-list_", # subst + ConfigKeys.DEFINE_KEY: { + "": "TEST_USER", + "": "scratch/ert", + "": "the_extensive_case", + "": "XYZ", + }, # subst + ConfigKeys.REFCASE: "../input/refcase/SNAKE_OIL_FIELD", # ecl + ConfigKeys.JOBNAME: "SNAKE_OIL_STRUCTURE_%d", # model + ConfigKeys.MAX_RESAMPLE: 1, # model + ConfigKeys.TIME_MAP: "../input/refcase/time_map.txt", # model + ConfigKeys.INSTALL_JOB: [ + { + ConfigKeys.NAME: "SNAKE_OIL_SIMULATOR", + ConfigKeys.PATH: "../../snake_oil/jobs/SNAKE_OIL_SIMULATOR", + }, + { + ConfigKeys.NAME: "SNAKE_OIL_NPV", + ConfigKeys.PATH: "../../snake_oil/jobs/SNAKE_OIL_NPV", + }, + { + ConfigKeys.NAME: "SNAKE_OIL_DIFF", + ConfigKeys.PATH: "../../snake_oil/jobs/SNAKE_OIL_DIFF", + }, # site + ], + ConfigKeys.FORWARD_MODEL: [ + { + ConfigKeys.NAME: "SNAKE_OIL_SIMULATOR", + ConfigKeys.ARGLIST: "", + }, + { + ConfigKeys.NAME: "SNAKE_OIL_NPV", + ConfigKeys.ARGLIST: "", + }, + {ConfigKeys.NAME: "SNAKE_OIL_DIFF", ConfigKeys.ARGLIST: ""}, # model + ], + ConfigKeys.HISTORY_SOURCE: HistorySourceEnum.REFCASE_HISTORY, + ConfigKeys.OBS_CONFIG: "../input/observations/obsfiles/observations.txt", + ConfigKeys.GEN_KW_EXPORT_NAME: "parameters", + ConfigKeys.LOAD_WORKFLOW_JOB: [ + { + ConfigKeys.NAME: "UBER_PRINT", + ConfigKeys.PATH: "../bin/workflows/workflowjobs/UBER_PRINT", + } # workflow_list + ], + ConfigKeys.LOAD_WORKFLOW: [ + { + ConfigKeys.NAME: "MAGIC_PRINT", + ConfigKeys.PATH: "../bin/workflows/MAGIC_PRINT", + } # workflow_list + ], + ConfigKeys.LOG_LEVEL: MessageLevelEnum.LOG_INFO, # model, log + "RNG_ALG_TYPE": RngAlgTypeEnum.MZRAN, + ConfigKeys.RANDOM_SEED: "3593114179000630026631423308983283277868", # rng + ConfigKeys.GRID: "../../eclipse/include/grid/CASE.EGRID", # ecl + ConfigKeys.RUN_TEMPLATE: [ + ( + "../input/templates/seed_template.txt", + "seed.txt", + [], + ) # ert_templates not sure about this we might do a proper dict instead? + ], +} + + +def expand_config_data(): + """Expands all strings in config_data according to config_defines. + + This is to enable one to copy the expected data directly from the + configuration file without having to do manual expantion (that is what we + have computers for anyway). + + """ + + for define_key in config_defines: + for data_key in config_data: + if isinstance(config_data[data_key], str): + config_data[data_key] = config_data[data_key].replace( + define_key, config_defines[define_key] + ) + + +class ResConfigTest(ResTest): + def set_up_simple(self): + self.case_directory = self.createTestPath("local/simple_config/") + + def set_up_snake_oil_structure(self): + self.case_directory = self.createTestPath("local/snake_oil_structure") + self.config_file = "snake_oil_structure/ert/model/user_config.ert" + expand_config_data() + + @tmpdir() + def test_invalid_user_config(self): + self.set_up_simple() + + with TestAreaContext("void land"): + with self.assertRaises(IOError): + ResConfig("this/is/not/a/file") + + @tmpdir() + def test_init(self): + self.set_up_simple() + + with TestAreaContext("res_config_init_test") as work_area: + cwd = os.getcwd() + work_area.copy_directory(self.case_directory) + + config_file = "simple_config/minimum_config" + res_config = ResConfig(user_config_file=config_file) + + self.assertEqual( + res_config.model_config.data_root(), os.path.join(cwd, "simple_config") + ) + self.assertEqual( + clib_getenv("DATA_ROOT"), os.path.join(cwd, "simple_config") + ) + + # This fails with an not-understandable Python error: + # ----------------------------------------------------------------- + # res_config.model_config.set_data_root( "NEW" ) + # self.assertEqual( res_config.model_config.data_root( ) , "NEW") + # self.assertEqual( clib_getenv("DATA_ROOT") , "NEW") + + self.assertIsNotNone(res_config) + self.assert_same_config_file( + config_file, res_config.user_config_file, os.getcwd() + ) + + self.assertIsNotNone(res_config.site_config) + self.assertTrue(isinstance(res_config.site_config, SiteConfig)) + + self.assertIsNotNone(res_config.analysis_config) + self.assertTrue(isinstance(res_config.analysis_config, AnalysisConfig)) + + self.assertEqual(res_config.config_path, os.path.join(cwd, "simple_config")) + + config_file = os.path.join(cwd, "simple_config/minimum_config") + res_config = ResConfig(user_config_file=config_file) + self.assertEqual(res_config.config_path, os.path.join(cwd, "simple_config")) + + os.chdir("simple_config") + config_file = "minimum_config" + res_config = ResConfig(user_config_file=config_file) + self.assertEqual(res_config.config_path, os.path.join(cwd, "simple_config")) + + subst_config = res_config.subst_config + for t in subst_config: + print(t) + self.assertEqual( + subst_config[""], os.path.join(cwd, "simple_config") + ) + + def assert_same_config_file(self, expected_filename, filename, prefix): + prefix_path = lambda fn: fn if os.path.isabs(fn) else os.path.join(prefix, fn) + canonical_path = lambda fn: os.path.realpath(os.path.abspath(prefix_path(fn))) + + self.assertEqual(canonical_path(expected_filename), canonical_path(filename)) + + def assert_path(self, rel_path, config_path, model_path): + self.assertEqual( + os.path.normpath(os.path.join(rel_path, config_path)), model_path + ) + + def assert_model_config(self, model_config, config_data, working_dir): + self.assert_path( + working_dir, config_data["RUNPATH"], model_config.getRunpathAsString() + ) + self.assert_path(working_dir, config_data["ENSPATH"], model_config.getEnspath()) + + self.assertEqual(config_data["JOBNAME"], model_config.getJobnameFormat()) + + self.assertEqual( + config_data["FORWARD_MODEL"], model_config.getForwardModel().joblist() + ) + + self.assertEqual( + config_data["HISTORY_SOURCE"], model_config.get_history_source() + ) + + self.assertEqual(config_data["NUM_REALIZATIONS"], model_config.num_realizations) + + self.assert_same_config_file( + config_data["OBS_CONFIG"], model_config.obs_config_file, working_dir + ) + + def assert_analysis_config(self, analysis_config, config_data): + self.assertEqual(config_data["MAX_RUNTIME"], analysis_config.get_max_runtime()) + + self.assertEqual(config_data["UPDATE_LOG_PATH"], analysis_config.get_log_path()) + + def assert_queue_config(self, queue_config, config_data): + corresponding = [ + ("MAX_SUBMIT", queue_config.max_submit), + ("LSF_QUEUE", queue_config.queue_name), + ("LSF_SERVER", queue_config.lsf_server), + ("LSF_RESOURCE", queue_config.lsf_resource), + ("QUEUE_SYSTEM", queue_config.queue_system), + ("QUEUE_SYSTEM", queue_config.driver.name), + ("MAX_RUNNING", queue_config.driver.get_option("MAX_RUNNING")), + ] + for key, act in corresponding: + exp = config_data[key] + errmsg = 'Error for key {key}, expected: "{exp}", was: "{act}".' + errmsg = errmsg.format(key=key, act=act, exp=exp) + self.assertEqual(exp, act, msg=errmsg) + + def assert_site_config(self, site_config, config_data, working_dir): + self.assertEqual(site_config.umask, config_data["UMASK"]) + + job_list = site_config.get_installed_jobs() + for job_name in config_data["INSTALL_JOB"]: + self.assertTrue(job_name in job_list) + + exp_job_data = config_data["INSTALL_JOB"][job_name] + + self.assert_same_config_file( + exp_job_data["CONFIG"], + job_list[job_name].get_config_file(), + working_dir, + ) + + self.assertEqual( + exp_job_data["STDERR"], job_list[job_name].get_stderr_file() + ) + + self.assertEqual( + exp_job_data["STDOUT"], job_list[job_name].get_stdout_file() + ) + + def assert_ecl_config(self, ecl_config, config_data, working_dir): + self.assert_same_config_file( + config_data["DATA_FILE"], ecl_config.getDataFile(), working_dir + ) + + self.assertEqual(CTime(config_data["START"]), ecl_config.getStartDate()) + + for extension in ["SMSPEC", "UNSMRY"]: + self.assert_same_config_file( + config_data["REFCASE"] + "." + extension, + ecl_config.getRefcaseName() + "." + extension, + working_dir, + ) + + self.assert_same_config_file( + config_data["GRID"], ecl_config.get_gridfile(), working_dir + ) + + def assert_ensemble_config(self, ensemble_config, config_data, working_dir): + self.assertEqual( + set( + config_data["SUMMARY"] + config_data["GEN_KW"] + config_data["GEN_DATA"] + ), + set(ensemble_config.alloc_keylist()), + ) + + loaded_template_file = ( + ensemble_config["SIGMA"].getKeywordModelConfig().getTemplateFile() + ) + self.assert_same_config_file( + config_data["SIGMA"]["TEMPLATE"], loaded_template_file, working_dir + ) + + loaded_parameter_file = ( + ensemble_config["SIGMA"].getKeywordModelConfig().getParameterFile() + ) + self.assert_same_config_file( + config_data["SIGMA"]["PARAMETER"], loaded_parameter_file, working_dir + ) + + self.assert_same_config_file( + config_data["SIGMA"]["RESULT"], + ensemble_config["SIGMA"]._get_enkf_outfile(), + working_dir, + ) + + def assert_hook_manager(self, hook_manager, config_data, working_dir): + self.assert_same_config_file( + config_data["RUNPATH_FILE"], + hook_manager.getRunpathList().getExportFile(), + working_dir, + ) + + def assert_ert_workflow_list(self, ert_workflow_list, config_data, working_dir): + self.assertEqual( + len(config_data["LOAD_WORKFLOW"]), len(ert_workflow_list.getWorkflowNames()) + ) + + for w_name in config_data["LOAD_WORKFLOW"]: + self.assertTrue(w_name in ert_workflow_list) + + self.assert_same_config_file( + config_data["LOAD_WORKFLOW"][w_name], + ert_workflow_list[w_name].src_file, + working_dir, + ) + + for wj_name in config_data["LOAD_WORKFLOW_JOB"]: + self.assertTrue(ert_workflow_list.hasJob(wj_name)) + job = ert_workflow_list.getJob(wj_name) + + self.assertEqual(wj_name, job.name()) + self.assert_same_config_file( + config_data["LOAD_WORKFLOW_JOB"][wj_name], job.executable(), working_dir + ) + + def assert_rng_config(self, rng_config, config_data, working_dir): + self.assertEqual(config_data["RNG_ALG_TYPE"], rng_config.alg_type) + + def assert_ert_templates(self, ert_templates, config_data, working_dir): + self.assertEqual( + config_data["RUN_TEMPLATE"].keys(), ert_templates.getTemplateNames() + ) + + for template_name in ert_templates.getTemplateNames(): + ert_template = ert_templates.get_template(template_name) + config_template = config_data["RUN_TEMPLATE"][template_name] + + self.assert_same_config_file( + config_template["TEMPLATE_FILE"], + ert_template.get_template_file(), + working_dir, + ) + + self.assertEqual( + config_template["TARGET_FILE"], + ert_template.get_target_file(), + working_dir, + ) + + def assert_log_config(self, log_config, config_data, working_dir): + self.assert_same_config_file( + config_data["LOG_FILE"], log_config.log_file, working_dir + ) + + self.assertEqual(config_data["LOG_LEVEL"], log_config.log_level) + + @tmpdir() + def test_extensive_config(self): + self.set_up_snake_oil_structure() + + with TestAreaContext("enkf_test_other_area") as work_area: + work_area.copy_directory(self.case_directory) + + # Move to another directory + run_dir = "i/ll/camp/here" + os.makedirs(run_dir) + os.chdir(run_dir) + inv_run_dir = "/".join([".."] * len(run_dir.split("/"))) + rel_config_file = os.path.join(inv_run_dir, self.config_file) + work_dir = os.path.split(rel_config_file)[0] + res_config = ResConfig(rel_config_file) + + self.assert_model_config(res_config.model_config, config_data, work_dir) + self.assert_analysis_config(res_config.analysis_config, config_data) + self.assert_queue_config(res_config.queue_config, config_data) + self.assert_site_config(res_config.site_config, config_data, work_dir) + self.assert_ecl_config(res_config.ecl_config, config_data, work_dir) + self.assert_ensemble_config( + res_config.ensemble_config, config_data, work_dir + ) + self.assert_hook_manager(res_config.hook_manager, config_data, work_dir) + self.assert_ert_workflow_list( + res_config.ert_workflow_list, config_data, work_dir + ) + self.assert_rng_config(res_config.rng_config, config_data, work_dir) + self.assert_ert_templates(res_config.ert_templates, config_data, work_dir) + self.assert_log_config(res_config.log_config, config_data, work_dir) + + # TODO: Not tested + # - MIN_REALIZATIONS + + @tmpdir() + def test_missing_directory(self): + config = { + "INTERNALS": { + "CONFIG_DIRECTORY": "does_not_exist", + }, + "SIMULATION": { + "QUEUE_SYSTEM": { + "JOBNAME": "Job%d", + }, + "RUNPATH": "/tmp/simulations/run%d", + "NUM_REALIZATIONS": 1, + "JOB_SCRIPT": "script.sh", + "ENSPATH": "Ensemble", + }, + } + + with self.assertRaises(IOError): + ResConfig(config=config) + + @tmpdir() + def test_res_config_dict_constructor(self): + self.set_up_snake_oil_structure() + + with TestAreaContext("enkf_test_other_area") as work_area: + work_area.copy_directory(self.case_directory) + + # create script file + script_file = "script.sh" + with open(script_file, "w") as f: + f.write("""#!/bin/sh\nls""") + + st = os.stat(script_file) + os.chmod(script_file, stat.S_IEXEC | st.st_mode) + + # add a missing entries to config file + with open(self.config_file, "a+") as ert_file: + ert_file.write("JOB_SCRIPT ../../../script.sh\n") + ert_file.write("END_DATE 10/10/2010\n") + + # split config_file to path and filename + cfg_path, cfg_file = os.path.split(os.path.realpath(self.config_file)) + + # replace define keys only in root strings, this should be updated and validated in configsuite instead + for define_key in config_data_new[ConfigKeys.DEFINE_KEY]: + for data_key in config_data_new: + if isinstance(config_data_new[data_key], str): + config_data_new[data_key] = config_data_new[data_key].replace( + define_key, + config_data_new[ConfigKeys.DEFINE_KEY].get(define_key), + ) + + # change dir to actual location of cfg_file + os.chdir(cfg_path) + + # load res_file + res_config_file = ResConfig(user_config_file=cfg_file) + + # get site_config location + ERT_SHARE_PATH = os.path.dirname(res_config_file.site_config.getLocation()) + + # update dictionary + # commit missing entries, this should be updated and validated in configsuite instead + config_data_new[ConfigKeys.CONFIG_FILE_KEY] = cfg_file + config_data_new[ConfigKeys.INSTALL_JOB_DIRECTORY] = [ + ERT_SHARE_PATH + "/forward-models/res", + ERT_SHARE_PATH + "/forward-models/shell", + ERT_SHARE_PATH + "/forward-models/templating", + ERT_SHARE_PATH + "/forward-models/old_style", + ] + config_data_new[ConfigKeys.WORKFLOW_JOB_DIRECTORY] = [ + ERT_SHARE_PATH + "/workflows/jobs/shell", + ERT_SHARE_PATH + "/workflows/jobs/internal/config", + ERT_SHARE_PATH + "/workflows/jobs/internal-gui/config", + ] + for ip in config_data_new[ConfigKeys.INSTALL_JOB]: + ip[ConfigKeys.PATH] = os.path.realpath(ip[ConfigKeys.PATH]) + + for ip in config_data_new[ConfigKeys.LOAD_WORKFLOW]: + ip[ConfigKeys.PATH] = os.path.realpath(ip[ConfigKeys.PATH]) + for ip in config_data_new[ConfigKeys.LOAD_WORKFLOW_JOB]: + ip[ConfigKeys.PATH] = os.path.realpath(ip[ConfigKeys.PATH]) + + config_data_new[ConfigKeys.JOB_SCRIPT] = os.path.normpath( + os.path.realpath(config_data_new[ConfigKeys.JOB_SCRIPT]) + ) + config_data_new[ConfigKeys.ANALYSIS_LOAD] = [ + {ConfigKeys.USER_NAME: "RML_ENKF", ConfigKeys.LIB_NAME: "rml_enkf.so"} + ] + + # open config via dictionary + res_config_dict = ResConfig(config_dict=config_data_new) + + self.assertEqual(res_config_file, res_config_dict) diff --git a/libres/tests/res/enkf/test_rng_config.py b/libres/tests/res/enkf/test_rng_config.py new file mode 100644 index 00000000000..8a29aacab6c --- /dev/null +++ b/libres/tests/res/enkf/test_rng_config.py @@ -0,0 +1,66 @@ +# Copyright (C) 2017 Equinor ASA, Norway. +# +# The file 'test_rng_config.py' 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. + +from ecl.util.test import TestAreaContext +from tests import ResTest + +from res.enkf import ResConfig, RNGConfig, ConfigKeys + + +class RNGConfigTest(ResTest): + def create_base_config(self): + return { + "INTERNALS": { + "CONFIG_DIRECTORY": "simple_config", + }, + "SIMULATION": { + "QUEUE_SYSTEM": { + "JOBNAME": "Job%d", + }, + "RUNPATH": "/tmp/simulations/run%d", + "NUM_REALIZATIONS": 1, + "JOB_SCRIPT": "script.sh", + "ENSPATH": "Ensemble", + "LOGGING": {"LOG_LEVEL": "DEBUG"}, + }, + } + + def test_dict_constructor(self): + config = self.create_base_config() + + case_directory = self.createTestPath("local/simple_config") + with TestAreaContext("rng_config") as work_area: + work_area.copy_directory(case_directory) + + random_seed = "abcdefghijklmnop" + config["SIMULATION"]["SEED"] = {ConfigKeys.RANDOM_SEED: random_seed} + + rng_config = RNGConfig(config_dict=config["SIMULATION"]["SEED"]) + res_config = ResConfig(config=config) + self.assertEqual(rng_config, res_config.rng_config) + + def test_random_seed(self): + config = self.create_base_config() + + random_seed = "abcdefghijklmnop" + config["SIMULATION"]["SEED"] = {ConfigKeys.RANDOM_SEED: random_seed} + + case_directory = self.createTestPath("local/simple_config") + with TestAreaContext("rng_config") as work_area: + work_area.copy_directory(case_directory) + res_config = ResConfig(config=config) + + self.assertEqual(random_seed, res_config.rng_config.random_seed) diff --git a/libres/tests/res/enkf/test_row_scaling.py b/libres/tests/res/enkf/test_row_scaling.py new file mode 100644 index 00000000000..14a8d365f76 --- /dev/null +++ b/libres/tests/res/enkf/test_row_scaling.py @@ -0,0 +1,116 @@ +# Copyright (C) 2020 Equinor ASA, Norway. +# +# The file 'test_row_scaling.py' 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. + +import pytest +import random +from functools import partial +from res.enkf import RowScaling + +import math +from ecl.grid import EclGridGenerator +from res.enkf import FieldConfig + + +def row_scaling_one(data_index): + return 1.0 + + +def row_scaling_inverse_size(size, data_index): + return data_index / size + + +def row_scaling_coloumb(nx, ny, data_index): + j = data_index / nx + i = data_index - j * nx + + dx = 0.5 + i + dy = 0.5 + j + + r2 = dx * dx + dy * dy + return 0.50 / r2 + + +def gaussian_decay(obs_pos, length_scale, grid, data_index): + x, y, z = grid.get_xyz(active_index=data_index) + dx = (obs_pos[0] - x) / length_scale[0] + dy = (obs_pos[1] - y) / length_scale[1] + dz = (obs_pos[2] - z) / length_scale[2] + + exp_arg = -0.5 * (dx * dx + dy * dy + dz * dz) + return math.exp(exp_arg) + + +def test_basic(): + row_scaling = RowScaling() + assert len(row_scaling) == 0 + + row_scaling[9] = 0.25 + assert len(row_scaling) == 10 + assert row_scaling[0] == 0 + assert row_scaling[9] == 0.25 + + with pytest.raises(IndexError): + var = row_scaling[10] + + for i in range(len(row_scaling)): + r = random.random() + row_scaling[i] = r + assert row_scaling[i] == row_scaling.clamp(r) + + nx = 10 + ny = 10 + row_scaling.assign(nx * ny, row_scaling_one) + assert len(row_scaling) == nx * ny + assert row_scaling[0] == 1 + assert row_scaling[nx * ny - 1] == 1 + + inverse_size = partial(row_scaling_inverse_size, nx * ny) + row_scaling.assign(nx * ny, inverse_size) + for g in range(nx * ny): + assert row_scaling[g] == row_scaling.clamp(g / (nx * ny)) + + coloumb = partial(row_scaling_coloumb, nx, ny) + row_scaling.assign(nx * ny, coloumb) + for j in range(ny): + for i in range(nx): + g = j * nx + i + assert row_scaling[g] == row_scaling.clamp(row_scaling_coloumb(nx, ny, g)) + + with pytest.raises(TypeError): + row_scaling.assign_vector(123.0) + + +def test_field_config(): + nx = 10 + ny = 10 + nz = 5 + actnum = [1] * nx * ny * nz + actnum[0] = 0 + actnum[3] = 0 + actnum[10] = 0 + + grid = EclGridGenerator.create_rectangular((nx, ny, nz), (1, 1, 1), actnum) + fc = FieldConfig("PORO", grid) + row_scaling = RowScaling() + obs_pos = grid.get_xyz(ijk=(5, 5, 1)) + length_scale = (2, 1, 0.50) + + gaussian = partial(gaussian_decay, obs_pos, length_scale, grid) + row_scaling.assign(grid.get_num_active(), gaussian) + for g in range(grid.get_num_active()): + assert row_scaling[g] == row_scaling.clamp( + gaussian_decay(obs_pos, length_scale, grid, g) + ) diff --git a/libres/tests/res/enkf/test_row_scaling_case.py b/libres/tests/res/enkf/test_row_scaling_case.py new file mode 100644 index 00000000000..3ca3d10d7fb --- /dev/null +++ b/libres/tests/res/enkf/test_row_scaling_case.py @@ -0,0 +1,620 @@ +# Copyright (C) 2020 Equinor ASA, Norway. +# +# The file 'test_row_scaling.py' 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. + +import random +import os +import math +import numpy as np +import tempfile +import shutil + +from tests import ResTest +from tests.utils import tmpdir + +from ecl.util.util import BoolVector +from ecl.grid import EclGridGenerator + +from res.test import ErtTestContext +from res.enkf import RowScaling, ResConfig +from res.enkf import EnkfNode, NodeId +from res.enkf import ESUpdate +from res.enkf import FieldConfig, ErtRunContext +from res.enkf.enums import RealizationStateEnum +from res.enkf import EnKFMain + +# This function will initialize the data in the case before the actual row +# scaling test can be performed. The function will initialize the PORO field +# which is the parameter to be updated, and it will assign one value for the +# summary value WBHP which will served as simulated response in the update +# process. +# +# The parameter field PORO is initialized by first creating input files +# poro/poro%d.grdecl and then the normal main.initRun() function is used to +# initialize the case and load the PORO data from disk. The summary data is +# normally loaded from a summary case found on disk, here we instead explicitly +# assign a value to the WBHP field at report step 1. Since we circumvent the +# normal load from forward model functionality we must manually update the +# state_map of the file system with the RealizationStateEnum.STATE_HAS_DATA +# flag. The return value from this function is the enkf_fs instance which has +# been initialized. + + +def init_data(main): + fsm = main.getEnkfFsManager() + init_fs = fsm.getFileSystem("init") + grid = main.eclConfig().getGrid() + + # Model: bhp = poro * 1000 + poro_mean = 0.15 + poro_std = 0.10 + bhp_std = 125 + + # Model: wct = poro * 4 + wct_std = 0.30 + + bhp = [] + wct = [] + num_realisations = main.getEnsembleSize() + + # The path fields/poro{}.grdecl must be consistent with the INIT_FILES: argument in the + # PORO configuration in the configuration file used for the testcase. + os.mkdir("fields") + random.seed(12345) + for i in range(num_realisations): + with open("fields/poro{}.grdecl".format(i), "w") as f: + poro = random.gauss(poro_mean, poro_std) + f.write("PORO") + for i in range(grid.get_num_active()): + if i % 10 == 0: + f.write("\n") + + f.write("{:<7.5} ".format(poro)) + f.write("\n/\n") + bhp.append(poro * 1000 + random.gauss(0, bhp_std)) + wct.append(poro * 4 + random.gauss(0, wct_std)) + + mask = BoolVector(initial_size=main.getEnsembleSize(), default_value=True) + init_context = ErtRunContext.case_init(init_fs, mask) + main.initRun(init_context) + + ens_config = main.ensembleConfig() + bhp_config = ens_config["WBHP"] + wct_config = ens_config["WWCT"] + state_map = init_fs.getStateMap() + for iens in range(main.getEnsembleSize()): + bhp_node = EnkfNode(bhp_config) + bhp_summary = bhp_node.as_summary() + bhp_summary[1] = bhp[iens] + + wct_node = EnkfNode(wct_config) + wct_summary = wct_node.as_summary() + wct_summary[1] = wct[iens] + + node_id = NodeId(1, iens) + bhp_node.save(init_fs, node_id) + wct_node.save(init_fs, node_id) + state_map[iens] = RealizationStateEnum.STATE_HAS_DATA + + return init_fs + + +# This is an example callable which decays as a gaussian away from a position +# obs_pos; this is (probaly) a simplified version of tapering function which +# will be interesting to use. +class GaussianDecay(object): + def __init__(self, obs_pos, length_scale, grid): + self.obs_pos = obs_pos + self.length_scale = length_scale + self.grid = grid + + def __call__(self, data_index): + x, y, z = self.grid.get_xyz(active_index=data_index) + dx = (self.obs_pos[0] - x) / self.length_scale[0] + dy = (self.obs_pos[1] - y) / self.length_scale[1] + dz = (self.obs_pos[2] - z) / self.length_scale[2] + + exp_arg = -0.5 * (dx * dx + dy * dy + dz * dz) + return math.exp(exp_arg) + + +class SelectLayer(object): + def __init__(self, layer, grid): + self.layer = layer + self.grid = grid + + def __call__(self, data_index): + ijk = self.grid.get_ijk(active_index=data_index) + if ijk[2] == self.layer: + return 1 + else: + return 0 + + +# This is a quite contrived row scaling callable which is mainly designed to +# test that the correct parts of the field are updated. The behavior of the +# ScalingTest function is as follows: +# +# k = 0: In the upper layer the scaling fcuntion behaves as a step function, +# for i <= 5 the scaling function returns one, and the updated field +# should be identical to the field updated without row scaling applied. +# +# For i > 5 the scaling function will return zero - corresponding to +# zero update. In this case the field returned from the updated process +# should be identical to the input field. +# +# k = 1: In the lower layer he scaling function is constantly equal to 0.50, +# that implies the updated values should be between the initial values +# and the normal full update value. +# +# The function assert_field_update() verifies that the updated field satisfies +# these constraints. + + +class ScalingTest(object): + def __init__(self, grid): + self.grid = grid + + def __call__(self, data_index): + i, j, k = self.grid.get_ijk(active_index=data_index) + if k == 0: + if i <= 5: + return 1 + else: + return 0 + else: + return 0.5 + + +def assert_field_update(grid, init_field, update_field1, update_field2): + k = 0 + for j in range(grid.ny): + for i in range(6): + assert update_field1.ijk_get_double( + i, j, k + ) == update_field2.ijk_get_double(i, j, k) + + for i in range(6, grid.nx): + assert init_field.ijk_get_double(i, j, k) == update_field2.ijk_get_double( + i, j, k + ) + + k = 1 + for j in range(grid.ny): + for i in range(grid.nx): + init = init_field.ijk_get_double(i, j, k) + update1 = update_field1.ijk_get_double(i, j, k) + update2 = update_field1.ijk_get_double(i, j, k) + assert (update2 - init) * (update1 - init) > 0 + + +class RowScalingTest(ResTest): + def setUp(self): + self.config_file = self.createTestPath("local/row_scaling/config") + + # The test_update_workflow() applies the row scaling through a workflow, + # that is probably the way it will be done by users. The test does not + # really verify the results in any way, but serves to demonstrate how + # things are connected. The job in workflows/row_scaling_job1.py uses + # functools.partial() as an alternative to Class::__call__() as callable + # when the row scaling is applied. + def test_update_workflow(self): + with ErtTestContext("row_scaling", self.config_file) as tc: + main = tc.getErt() + workflow_list = main.getWorkflowList() + workflow = workflow_list["ROW_SCALING_WORKFLOW1"] + self.assertTrue(workflow.run(main)) + + init_fs = init_data(main) + target_fs = main.getEnkfFsManager().getFileSystem("target") + + es_update = ESUpdate(main) + run_context = ErtRunContext.ensemble_smoother_update(init_fs, target_fs) + es_update.smootherUpdate(run_context) + + # The test_update_code() applies the row scaling through code inlined in + # the test, and also uses the GaussianDecay callable class instead of + # functools.partial() to create a callable for the scaling operation. + def test_update_code1(self): + with ErtTestContext("row_scaling", self.config_file) as tc: + main = tc.getErt() + + local_config = main.getLocalConfig() + local_config.clear() + local_data = local_config.createDataset("LOCAL") + local_data.addNode("PORO") + obs = local_config.createObsdata("OBSSET_LOCAL") + obs.addNode("WBHP0") + obs.addNode("WWCT0") + ministep = local_config.createMinistep("MINISTEP_LOCAL") + ministep.attachDataset(local_data) + ministep.attachObsset(obs) + updatestep = local_config.getUpdatestep() + updatestep.attachMinistep(ministep) + + row_scaling = local_data.row_scaling("PORO") + ens_config = main.ensembleConfig() + poro_config = ens_config["PORO"] + field_config = poro_config.getFieldModelConfig() + + # ------------------------------------------------------------------------------------- + grid = main.eclConfig().getGrid() + obs_pos = grid.get_xyz(ijk=(5, 5, 1)) + length_scale = (2, 1, 0.50) + row_scaling.assign( + field_config.get_data_size(), GaussianDecay(obs_pos, length_scale, grid) + ) + # ------------------------------------------------------------------------------------- + + init_fs = init_data(main) + target_fs = main.getEnkfFsManager().getFileSystem("target") + es_update = ESUpdate(main) + run_context = ErtRunContext.ensemble_smoother_update(init_fs, target_fs) + es_update.smootherUpdate(run_context) + + # This test does two smoother updates, first without row scaling in update1 + # and then afterwards with row scaling in update2. The row scaling function + # is designed so that it is possible to test the updates results. + def test_update_code2(self): + random_seed = "ABCDEFGHIJK0123456" + with ErtTestContext("row_scaling", self.config_file) as tc: + main = tc.getErt() + init_fs = init_data(main) + update_fs1 = main.getEnkfFsManager().getFileSystem("target1") + + # The first smoother update without row scaling + es_update = ESUpdate(main) + run_context = ErtRunContext.ensemble_smoother_update(init_fs, update_fs1) + rng = main.rng() + rng.setState(random_seed) + es_update.smootherUpdate(run_context) + + # Configure the local updates + local_config = main.getLocalConfig() + local_config.clear() + local_data = local_config.createDataset("LOCAL") + local_data.addNode("PORO") + obs = local_config.createObsdata("OBSSET_LOCAL") + obs.addNode("WWCT0") + obs.addNode("WBHP0") + ministep = local_config.createMinistep("MINISTEP_LOCAL") + ministep.attachDataset(local_data) + ministep.attachObsset(obs) + updatestep = local_config.getUpdatestep() + updatestep.attachMinistep(ministep) + + # Apply the row scaling + row_scaling = local_data.row_scaling("PORO") + ens_config = main.ensembleConfig() + poro_config = ens_config["PORO"] + field_config = poro_config.getFieldModelConfig() + grid = main.eclConfig().getGrid() + row_scaling.assign(field_config.get_data_size(), ScalingTest(grid)) + + # Second update with row scaling + update_fs2 = main.getEnkfFsManager().getFileSystem("target2") + es_update = ESUpdate(main) + run_context = ErtRunContext.ensemble_smoother_update(init_fs, update_fs2) + rng.setState(random_seed) + es_update.smootherUpdate(run_context) + + # Fetch the three values initial, update without row scaling and + # update with row scaling and verify that the row scaling has been + # correctly applied. + init_node = EnkfNode(poro_config) + update_node1 = EnkfNode(poro_config) + update_node2 = EnkfNode(poro_config) + for iens in range(main.getEnsembleSize()): + node_id = NodeId(0, iens) + + init_node.load(init_fs, node_id) + update_node1.load(update_fs1, node_id) + update_node2.load(update_fs2, node_id) + + assert_field_update( + grid, + init_node.asField(), + update_node1.asField(), + update_node2.asField(), + ) + + # This test is identical to test_update_code2(), but the row scaling is + # applied with the function row_scaling.assign_vector() instead of + # using a callable. + def test_row_scaling_using_assign_vector(self): + random_seed = "ABCDEFGHIJK0123456" + with ErtTestContext("row_scaling", self.config_file) as tc: + main = tc.getErt() + init_fs = init_data(main) + update_fs1 = main.getEnkfFsManager().getFileSystem("target1") + + # The first smoother update without row scaling + es_update = ESUpdate(main) + run_context = ErtRunContext.ensemble_smoother_update(init_fs, update_fs1) + rng = main.rng() + rng.setState(random_seed) + es_update.smootherUpdate(run_context) + + # Configure the local updates + local_config = main.getLocalConfig() + local_config.clear() + local_data = local_config.createDataset("LOCAL") + local_data.addNode("PORO") + obs = local_config.createObsdata("OBSSET_LOCAL") + obs.addNode("WWCT0") + obs.addNode("WBHP0") + ministep = local_config.createMinistep("MINISTEP_LOCAL") + ministep.attachDataset(local_data) + ministep.attachObsset(obs) + updatestep = local_config.getUpdatestep() + updatestep.attachMinistep(ministep) + + # Apply the row scaling + row_scaling = local_data.row_scaling("PORO") + ens_config = main.ensembleConfig() + poro_config = ens_config["PORO"] + field_config = poro_config.getFieldModelConfig() + grid = main.eclConfig().getGrid() + + scaling = ScalingTest(grid) + scaling_vector = np.ndarray( + [field_config.get_data_size()], dtype=np.float32 + ) + for i in range(field_config.get_data_size()): + scaling_vector[i] = scaling(i) + row_scaling.assign_vector(scaling_vector) + + # Second update with row scaling + update_fs2 = main.getEnkfFsManager().getFileSystem("target2") + es_update = ESUpdate(main) + run_context = ErtRunContext.ensemble_smoother_update(init_fs, update_fs2) + rng.setState(random_seed) + es_update.smootherUpdate(run_context) + + # Fetch the three values initial, update without row scaling and + # update with row scaling and verify that the row scaling has been + # correctly applied. + init_node = EnkfNode(poro_config) + update_node1 = EnkfNode(poro_config) + update_node2 = EnkfNode(poro_config) + for iens in range(main.getEnsembleSize()): + node_id = NodeId(0, iens) + + init_node.load(init_fs, node_id) + update_node1.load(update_fs1, node_id) + update_node2.load(update_fs2, node_id) + + assert_field_update( + grid, + init_node.asField(), + update_node1.asField(), + update_node2.asField(), + ) + + # This test has a configuration where the update consists of two ministeps, + # where the same field is updated in both steps. Because the + # obs_data_allocE() function uses random state it is difficult to get + # identical results from one ministep updating everything and two ministeps + # updating different parts of the field. + def test_2ministep(self): + with ErtTestContext("row_scaling", self.config_file) as tc: + main = tc.getErt() + init_fs = init_data(main) + update_fs1 = main.getEnkfFsManager().getFileSystem("target1") + + # The first smoother update without row scaling + es_update = ESUpdate(main) + run_context = ErtRunContext.ensemble_smoother_update(init_fs, update_fs1) + rng = main.rng() + es_update.smootherUpdate(run_context) + + # Configure the local updates + local_config = main.getLocalConfig() + local_config.clear() + obs = local_config.createObsdata("OBSSET_LOCAL") + obs.addNode("WBHP0") + + ministep1 = local_config.createMinistep("MINISTEP1") + local_data1 = local_config.createDataset("LOCAL1") + local_data1.addNode("PORO") + row_scaling1 = local_data1.row_scaling("PORO") + ministep1.attachDataset(local_data1) + ministep1.attachObsset(obs) + + ministep2 = local_config.createMinistep("MINISTEP2") + local_data2 = local_config.createDataset("LOCAL2") + local_data2.addNode("PORO") + row_scaling2 = local_data2.row_scaling("PORO") + ministep2.attachDataset(local_data2) + ministep2.attachObsset(obs) + + updatestep = local_config.getUpdatestep() + updatestep.attachMinistep(ministep1) + updatestep.attachMinistep(ministep2) + + # Apply the row scaling + ens_config = main.ensembleConfig() + poro_config = ens_config["PORO"] + field_config = poro_config.getFieldModelConfig() + grid = main.eclConfig().getGrid() + + row_scaling1.assign(field_config.get_data_size(), SelectLayer(0, grid)) + row_scaling2.assign(field_config.get_data_size(), SelectLayer(1, grid)) + + update_fs2 = main.getEnkfFsManager().getFileSystem("target2") + es_update = ESUpdate(main) + run_context = ErtRunContext.ensemble_smoother_update(init_fs, update_fs2) + es_update.smootherUpdate(run_context) + + init_node = EnkfNode(poro_config) + node1 = EnkfNode(poro_config) + node2 = EnkfNode(poro_config) + for iens in range(main.getEnsembleSize()): + node_id = NodeId(0, iens) + + init_node.load(init_fs, node_id) + node1.load(update_fs1, node_id) + node2.load(update_fs2, node_id) + + init_field = init_node.asField() + field1 = node1.asField() + field2 = node2.asField() + for iv, v1, v2 in zip(init_field, field1, field2): + assert iv != v1 + + # In the enkf_main_update() routine the A matrix is allocated with a + # default size; and should grow when exposed to a larger node. At a stage + # there was a bug in code for combination of row_scaling and + # matrix_resize(). The purpose of this test is to ensure that we create a + # sufficiently large node to invoke the rescaling. + @tmpdir() + def test_large_case(self): + with open("config", "w") as fp: + fp.write( + """NUM_REALIZATIONS 10 +GRID CASE.EGRID +FIELD PORO PARAMETER poro.grdecl INIT_FILES:fields/poro%d.grdecl +SUMMARY WBHP +OBS_CONFIG observations.txt +TIME_MAP timemap.txt +""" + ) + + for f in ["timemap.txt", "observations.txt"]: + src_file = self.createTestPath(os.path.join("local/row_scaling", f)) + shutil.copy(src_file, "./") + # The grid size must be greater than 250000 (the default matrix size in + # enkf_main_update()) + grid = EclGridGenerator.create_rectangular((70, 70, 70), (1, 1, 1)) + grid.save_EGRID("CASE.EGRID") + res_config = ResConfig(user_config_file="config") + main = EnKFMain(res_config) + init_fs = init_data(main) + + # Configure the local updates + local_config = main.getLocalConfig() + local_config.clear() + local_data = local_config.createDataset("LOCAL") + local_data.addNode("PORO") + obs = local_config.createObsdata("OBSSET_LOCAL") + obs.addNode("WBHP0") + ministep = local_config.createMinistep("MINISTEP_LOCAL") + ministep.attachDataset(local_data) + ministep.attachObsset(obs) + updatestep = local_config.getUpdatestep() + updatestep.attachMinistep(ministep) + + # Apply the row scaling + row_scaling = local_data.row_scaling("PORO") + ens_config = main.ensembleConfig() + poro_config = ens_config["PORO"] + field_config = poro_config.getFieldModelConfig() + grid = main.eclConfig().getGrid() + row_scaling.assign(field_config.get_data_size(), ScalingTest(grid)) + es_update = ESUpdate(main) + update_fs = main.getEnkfFsManager().getFileSystem("target2") + run_context = ErtRunContext.ensemble_smoother_update(init_fs, update_fs) + es_update.smootherUpdate(run_context) + + # This test updates three times, all updates should produce the same result: + # + # 1. A normal update with the default configuration and no explicit local + # config. + # + # 2. We have a local configuration, but it is based on a trivial reuse of + # the autogenerated ALL_OBS observation set. + # + # 3. The update consists of two ministeps. The first is created by deleting + # an entry from the ALL_OBS set, and then that same entry is added to + # the next. + def test_reuse_ALL_ACTIVE(self): + random_seed = "ABCDEFGHIJK0123456" + with ErtTestContext("row_scaling", self.config_file) as tc: + main = tc.getErt() + grid = main.eclConfig().getGrid() + init_fs = init_data(main) + es_update = ESUpdate(main) + update_fs1 = main.getEnkfFsManager().getFileSystem("target1") + run_context = ErtRunContext.ensemble_smoother_update(init_fs, update_fs1) + rng = main.rng() + rng.setState(random_seed) + # Normal update without any local configuration + es_update.smootherUpdate(run_context) + + local_config = main.getLocalConfig() + local_config.clear_active() + with self.assertRaises(KeyError): + obs_data = local_config.copyObsdata("NO_SUCH_OBS", "my_obs") + + local_data = local_config.createDataset("LOCAL") + local_data.addNode("PORO") + obs_data = local_config.copyObsdata("ALL_OBS", "my_obs") + ministep = local_config.createMinistep("MINISTEP_LOCAL") + ministep.attachDataset(local_data) + ministep.attachObsset(obs_data) + updatestep = local_config.getUpdatestep() + updatestep.attachMinistep(ministep) + + update_fs2 = main.getEnkfFsManager().getFileSystem("target2") + run_context = ErtRunContext.ensemble_smoother_update(init_fs, update_fs2) + rng.setState(random_seed) + # Local update with reused ALL_OBS observation configuration + es_update.smootherUpdate(run_context) + + del obs_data["WBHP0"] + ministep2 = local_config.createMinistep("MINISTEP_LOCAL2") + obs_data2 = local_config.createObsdata("OBSDATA2") + obs_data2.addNode("WBHP0") + ministep2.attachDataset(local_data) + ministep2.attachObsset(obs_data2) + updatestep.attachMinistep(ministep2) + update_fs3 = main.getEnkfFsManager().getFileSystem("target3") + run_context = ErtRunContext.ensemble_smoother_update(init_fs, update_fs3) + # Local update with two ministeps - where one observation has been removed from the first + es_update.smootherUpdate(run_context) + + ens_config = main.ensembleConfig() + poro_config = ens_config["PORO"] + update_node1 = EnkfNode(poro_config) + update_node2 = EnkfNode(poro_config) + update_node3 = EnkfNode(poro_config) + for iens in range(main.getEnsembleSize()): + node_id = NodeId(0, iens) + + update_node1.load(update_fs1, node_id) + update_node2.load(update_fs2, node_id) + update_node3.load(update_fs3, node_id) + + field1 = update_node1.asField() + field2 = update_node2.asField() + field3 = update_node3.asField() + + for k in range(grid.nz): + for j in range(grid.ny): + for i in range(grid.nx): + assert field1.ijk_get_double( + i, j, k + ) == field2.ijk_get_double(i, j, k) + + f1 = field1.ijk_get_double(i, j, k) + f3 = field3.ijk_get_double(i, j, k) + + # Due to the randomness in the sampling process, + # which becomes different when the update steps is + # split in two ministeps we can not enforce + # equality here. + + diff = abs(f1 - f3) + assert diff < 0.01 diff --git a/libres/tests/res/enkf/test_run_context.py b/libres/tests/res/enkf/test_run_context.py new file mode 100644 index 00000000000..b8b1b3d658e --- /dev/null +++ b/libres/tests/res/enkf/test_run_context.py @@ -0,0 +1,59 @@ +from ecl.util.util import BoolVector +from res.util.substitution_list import SubstitutionList +from ecl.util.test import TestAreaContext +from tests import ResTest +from tests.utils import tmpdir +from res.enkf import ErtRunContext +from res.enkf.enums import EnkfRunType +from res.enkf import EnkfFs +from res.util import PathFormat + + +class ErtRunContextTest(ResTest): + @tmpdir() + def test_create(self): + with TestAreaContext("run_context"): + sim_fs = EnkfFs.createFileSystem("sim_fs") + target_fs = None + + mask = BoolVector(initial_size=100, default_value=True) + mask[50] = False + runpath_fmt = PathFormat("path/to/sim%d") + subst_list = SubstitutionList() + itr = 0 + jobname_fmt = "job%d" + run_context1 = ErtRunContext( + EnkfRunType.ENSEMBLE_EXPERIMENT, + sim_fs, + target_fs, + mask, + runpath_fmt, + jobname_fmt, + subst_list, + itr, + ) + run_id1 = run_context1.get_id() + + run_arg0 = run_context1[0] + with self.assertRaises(ValueError): + run_arg0.getQueueIndex() + + self.assertEqual(run_arg0.iter_id, itr) + self.assertEqual(run_id1, run_arg0.get_run_id()) + + run_context2 = ErtRunContext( + EnkfRunType.ENSEMBLE_EXPERIMENT, + sim_fs, + target_fs, + mask, + runpath_fmt, + jobname_fmt, + subst_list, + itr, + ) + run_id2 = run_context2.get_id() + + self.assertFalse(run_id1 == run_id2) + + self.assertTrue(run_context1.is_active(49)) + self.assertFalse(run_context1.is_active(50)) diff --git a/libres/tests/res/enkf/test_runpath_list.py b/libres/tests/res/enkf/test_runpath_list.py new file mode 100644 index 00000000000..5728423e7ee --- /dev/null +++ b/libres/tests/res/enkf/test_runpath_list.py @@ -0,0 +1,161 @@ +import unittest +import os.path + +from ecl.util.test import TestAreaContext +from tests import ResTest +from res.test import ErtTestContext + +from res.enkf import RunpathList, RunpathNode, ErtRunContext +from res.enkf.enums import EnkfInitModeEnum, EnkfRunType +from ecl.util.util import BoolVector +from res.util.substitution_list import SubstitutionList + + +def path(idx): + return "path_%d" % idx + + +def base(idx): + return "base_%d" % idx + + +class RunpathListTest(ResTest): + def test_load(self): + with TestAreaContext("runpath_list"): + with self.assertRaises(IOError): + rp = RunpathList("") + + node0 = RunpathNode(0, 0, "path0", "base0") + node1 = RunpathNode(1, 1, "path1", "base1") + + runpath_list = RunpathList("filex.list") + with self.assertRaises(IOError): + runpath_list.load() + + with open("invalid_file", "w") as f: + f.write("X X X X\n") + + rp = RunpathList("invalid_file") + with self.assertRaises(IOError): + rp.load() + + rp = RunpathList("list.txt") + rp.add(node0.realization, node0.iteration, node0.runpath, node0.basename) + rp.add(node1.realization, node1.iteration, node1.runpath, node1.basename) + rp.export() + self.assertTrue(os.path.isfile("list.txt")) + + rp2 = RunpathList("list.txt") + rp2.load() + self.assertEqual(len(rp2), 2) + self.assertEqual(rp2[0], node0) + self.assertEqual(rp2[1], node1) + + def test_runpath_list(self): + runpath_list = RunpathList("file") + + self.assertEqual(len(runpath_list), 0) + + test_runpath_nodes = [ + RunpathNode(0, 0, "runpath0", "basename0"), + RunpathNode(1, 0, "runpath1", "basename0"), + ] + + runpath_node = test_runpath_nodes[0] + runpath_list.add( + runpath_node.realization, + runpath_node.iteration, + runpath_node.runpath, + runpath_node.basename, + ) + + self.assertEqual(len(runpath_list), 1) + self.assertEqual(runpath_list[0], test_runpath_nodes[0]) + + runpath_node = test_runpath_nodes[1] + runpath_list.add( + runpath_node.realization, + runpath_node.iteration, + runpath_node.runpath, + runpath_node.basename, + ) + + self.assertEqual(len(runpath_list), 2) + self.assertEqual(runpath_list[1], test_runpath_nodes[1]) + + for index, runpath_node in enumerate(runpath_list): + self.assertEqual(runpath_node, test_runpath_nodes[index]) + + runpath_list.clear() + + self.assertEqual(len(runpath_list), 0) + + def test_collection(self): + """Testing len, adding, getting (idx and slice), printing, clearing.""" + with TestAreaContext("runpath_list_collection"): + runpath_list = RunpathList("EXPORT.txt") + runpath_list.add(3, 1, path(3), base(3)) + runpath_list.add(1, 1, path(1), base(1)) + runpath_list.add(2, 1, path(2), base(2)) + runpath_list.add(0, 0, path(0), base(0)) + runpath_list.add(3, 0, path(3), base(3)) + runpath_list.add(1, 0, path(1), base(1)) + runpath_list.add(2, 0, path(2), base(2)) + runpath_list.add(0, 1, path(0), base(0)) + + self.assertEqual(8, len(runpath_list)) + pfx = "RunpathList(size" # the __repr__ function + self.assertEqual(pfx, repr(runpath_list)[: len(pfx)]) + node2 = RunpathNode(2, 1, path(2), base(2)) + self.assertEqual(node2, runpath_list[2]) + + node3 = RunpathNode(0, 0, path(0), base(0)) + node4 = RunpathNode(3, 0, path(3), base(3)) + node5 = RunpathNode(1, 0, path(1), base(1)) + node6 = RunpathNode(2, 0, path(2), base(2)) + nodeslice = [node3, node4, node5, node6] + self.assertEqual(nodeslice, runpath_list[3:7]) + self.assertEqual(node6, runpath_list[-2]) + with self.assertRaises(TypeError): + runpath_list["key"] + with self.assertRaises(IndexError): + runpath_list[12] + + runpath_list.clear() + self.assertEqual(0, len(runpath_list)) + with self.assertRaises(IndexError): + runpath_list[0] + self.assertEqual("EXPORT.txt", runpath_list.getExportFile()) + + def test_sorted_export(self): + with TestAreaContext("runpath_list_sorted"): + runpath_list = RunpathList("EXPORT.txt") + runpath_list.add(3, 1, "path", "base") + runpath_list.add(1, 1, "path", "base") + runpath_list.add(2, 1, "path", "base") + runpath_list.add(0, 0, "path", "base") + + runpath_list.add(3, 0, "path", "base") + runpath_list.add(1, 0, "path", "base") + runpath_list.add(2, 0, "path", "base") + runpath_list.add(0, 1, "path", "base") + + runpath_list.export() + + path_list = [] + with open("EXPORT.txt") as f: + for line in f.readlines(): + tmp = line.split() + iens = int(tmp[0]) + iteration = int(tmp[3]) + + path_list.append((iens, iteration)) + + for iens in range(4): + t0 = path_list[iens] + t4 = path_list[iens + 4] + self.assertEqual(t0[0], iens) + self.assertEqual(t4[0], iens) + + self.assertEqual(t0[1], 0) + self.assertEqual(t4[1], 1) diff --git a/libres/tests/res/enkf/test_runpath_list_dump.py b/libres/tests/res/enkf/test_runpath_list_dump.py new file mode 100644 index 00000000000..218f2736d39 --- /dev/null +++ b/libres/tests/res/enkf/test_runpath_list_dump.py @@ -0,0 +1,89 @@ +import unittest, os +import itertools + +from ecl.util.test import TestAreaContext +from tests import ResTest +from tests.utils import tmpdir +from res.test import ErtTestContext + +from ecl.util.util import BoolVector + +from res.enkf import ResConfig, EnKFMain, EnkfFs, ErtRunContext +from res.enkf.enums import EnKFFSType, EnkfRunType +from res.util import PathFormat + + +def render_dynamic_values(s, itr, iens, geo_id): + dynamic_magic_strings = { + "": geo_id, + "": itr, + "": iens, + } + for key, val in dynamic_magic_strings.items(): + s = s.replace(key, str(val)) + + return s + + +class RunpathListDumpTest(ResTest): + def setUp(self): + self.config_rel_path = "local/snake_oil_no_data/snake_oil_GEO_ID.ert" + self.config_path = self.createTestPath(self.config_rel_path) + + def _verify_runpath_rendering(self, itr): + with ErtTestContext( + "add_all_runpath_dump", model_config=self.config_path + ) as ctx: + res = ctx.getErt() + fs_manager = res.getEnkfFsManager() + sim_fs = fs_manager.getFileSystem("sim_fs") + + num_realizations = 25 + mask = BoolVector(initial_size=num_realizations, default_value=True) + mask[13] = False + + runpath_fmt = "simulations//realisation-%d/iter-%d/magic-real-/magic-iter-" + jobname_fmt = "SNAKE_OIL_%d" + + subst_list = res.resConfig().subst_config.subst_list + run_context = ErtRunContext.ensemble_experiment( + sim_fs, mask, PathFormat(runpath_fmt), jobname_fmt, subst_list, itr + ) + + res.initRun(run_context) + + for i, run_arg in enumerate(run_context): + if mask[i]: + run_arg.geo_id = 10 * i + + res.createRunpath(run_context) + + for i, run_arg in enumerate(run_context): + if not mask[i]: + continue + + self.assertTrue(os.path.isdir("simulations/%d" % run_arg.geo_id)) + + runpath_list_path = ".ert_runpath_list" + self.assertTrue(os.path.isfile(runpath_list_path)) + + exp_runpaths = [ + render_dynamic_values(runpath_fmt, itr, iens, run_arg.geo_id) + % (iens, itr) + for iens, run_arg in enumerate(run_context) + if mask[iens] + ] + exp_runpaths = map(os.path.realpath, exp_runpaths) + + with open(runpath_list_path, "r") as f: + dumped_runpaths = list(zip(*[line.split() for line in f.readlines()]))[ + 1 + ] + + self.assertEqual(list(exp_runpaths), list(dumped_runpaths)) + + @tmpdir() + def test_add_all(self): + test_base = [0, 1, 2, 17] + for itr in test_base: + self._verify_runpath_rendering(itr) diff --git a/libres/tests/res/enkf/test_runpath_list_ert.py b/libres/tests/res/enkf/test_runpath_list_ert.py new file mode 100644 index 00000000000..0fdf5207e2d --- /dev/null +++ b/libres/tests/res/enkf/test_runpath_list_ert.py @@ -0,0 +1,103 @@ +import unittest +import os +from res.test import ErtTestContext +from tests import ResTest +from tests.utils import tmpdir + +from res.enkf import RunpathList, RunpathNode, ErtRunContext +from res.enkf.enums import EnkfInitModeEnum, EnkfRunType +from ecl.util.util import BoolVector +from res.util.substitution_list import SubstitutionList + + +class RunpathListTestErt(ResTest): + @tmpdir() + def test_an_enkf_runpath(self): + # TODO this test is flaky and we need to figure out why. See #1370 + # enkf_util_assert_buffer_type: wrong target type in file (expected:104 got:0) + test_path = self.createTestPath("local/snake_oil_field/snake_oil.ert") + with ErtTestContext("runpathlist_basic", test_path) as tc: + pass + + @tmpdir() + def test_assert_export(self): + with ErtTestContext( + "create_runpath_export", + self.createTestPath("local/snake_oil_no_data/snake_oil.ert"), + ) as tc: + ert = tc.getErt() + runpath_list = ert.getRunpathList() + self.assertFalse(os.path.isfile(runpath_list.getExportFile())) + + ens_size = ert.getEnsembleSize() + runner = ert.getEnkfSimulationRunner() + fs_manager = ert.getEnkfFsManager() + + init_fs = fs_manager.getFileSystem("init_fs") + mask = BoolVector(initial_size=25, default_value=True) + runpath_fmt = ert.getModelConfig().getRunpathFormat() + subst_list = SubstitutionList() + itr = 0 + jobname_fmt = ert.getModelConfig().getJobnameFormat() + run_context1 = ErtRunContext( + EnkfRunType.INIT_ONLY, + init_fs, + None, + mask, + runpath_fmt, + jobname_fmt, + subst_list, + itr, + ) + + runner.createRunPath(run_context1) + + self.assertTrue(os.path.isfile(runpath_list.getExportFile())) + self.assertEqual( + "test_runpath_list.txt", os.path.basename(runpath_list.getExportFile()) + ) + + @tmpdir() + def test_assert_symlink_deleted(self): + with ErtTestContext( + "create_runpath_symlink_deleted", + self.createTestPath("local/snake_oil_field/snake_oil.ert"), + ) as tc: + ert = tc.getErt() + runpath_list = ert.getRunpathList() + + ens_size = ert.getEnsembleSize() + runner = ert.getEnkfSimulationRunner() + mask = BoolVector(initial_size=ens_size, default_value=True) + fs_manager = ert.getEnkfFsManager() + init_fs = fs_manager.getFileSystem("init_fs") + + # create directory structure + runpath_fmt = ert.getModelConfig().getRunpathFormat() + subst_list = SubstitutionList() + itr = 0 + jobname_fmt = ert.getModelConfig().getJobnameFormat() + run_context = ErtRunContext( + EnkfRunType.INIT_ONLY, + init_fs, + None, + mask, + runpath_fmt, + jobname_fmt, + subst_list, + itr, + ) + runner.createRunPath(run_context) + + # replace field file with symlink + linkpath = "%s/permx.grdcel" % str(runpath_list[0].runpath) + targetpath = "%s/permx.grdcel.target" % str(runpath_list[0].runpath) + open(targetpath, "a").close() + os.remove(linkpath) + os.symlink(targetpath, linkpath) + + # recreate directory structure + runner.createRunPath(run_context) + + # ensure field symlink is replaced by file + self.assertFalse(os.path.islink(linkpath)) diff --git a/libres/tests/res/enkf/test_simulation_batch.py b/libres/tests/res/enkf/test_simulation_batch.py new file mode 100644 index 00000000000..19f8f811be0 --- /dev/null +++ b/libres/tests/res/enkf/test_simulation_batch.py @@ -0,0 +1,100 @@ +import os +import sys +from ecl.util.test import TestAreaContext +from tests import ResTest +from tests.utils import tmpdir +from ecl.util.util import BoolVector + +from res.test import ErtTestContext +from res.enkf import EnkfFs, EnkfConfigNode, NodeId, EnkfNode +from res.enkf import EnKFMain, ErtRunContext +from res.enkf.enums import EnKFFSType +from res.enkf.enums import RealizationStateEnum + + +class SimulationBatchTest(ResTest): + def setUp(self): + pass + + @tmpdir() + def test_run(self): + ens_size = 2 + config_file = self.createTestPath("local/config/simulation_batch/config.ert") + with ErtTestContext("simulation_batch", model_config=config_file) as ctx: + ert = ctx.getErt() + ens_config = ert.ensembleConfig() + + # Observe that a significant amount of hardcoding + # regarding the GEN_DATA and EXT_PARAM nodes is assumed + # between this test, the config file and the forward model. + + # Add control nodes + order_control = EnkfConfigNode.create_ext_param( + "WELL_ORDER", ["W1", "W2", "W3"] + ) + injection_control = EnkfConfigNode.create_ext_param( + "WELL_INJECTION", ["W1", "W4"] + ) + ens_config.addNode(order_control) + ens_config.addNode(injection_control) + + # Add result nodes + order_result = EnkfConfigNode.create_gen_data("ORDER", "order_%d") + injection_result = EnkfConfigNode.create_gen_data( + "INJECTION", "injection_%d" + ) + ens_config.addNode(order_result) + ens_config.addNode(injection_result) + + order_node = EnkfNode(order_control) + order_node_ext = order_node.as_ext_param() + injection_node = EnkfNode(injection_control) + injection_node_ext = injection_node.as_ext_param() + + fs_manager = ert.getEnkfFsManager() + sim_fs = fs_manager.getFileSystem("sim_fs") + state_map = sim_fs.getStateMap() + batch_size = ens_size + 2 + for iens in range(batch_size): + node_id = NodeId(0, iens) + + order_node_ext["W1"] = iens + order_node_ext["W2"] = iens * 10 + order_node_ext["W3"] = iens * 100 + order_node.save(sim_fs, node_id) + + injection_node_ext["W1"] = iens + 1 + injection_node_ext["W4"] = 3 * (iens + 1) + injection_node.save(sim_fs, node_id) + state_map[iens] = RealizationStateEnum.STATE_INITIALIZED + + mask = BoolVector(default_value=True, initial_size=batch_size) + model_config = ert.getModelConfig() + runpath_fmt = model_config.getRunpathFormat() + jobname_fmt = model_config.getJobnameFormat() + subst_list = ert.getDataKW() + itr = 0 + run_context = ErtRunContext.ensemble_experiment( + sim_fs, mask, runpath_fmt, jobname_fmt, subst_list, itr + ) + ert.getEnkfSimulationRunner().createRunPath(run_context) + job_queue = ert.get_queue_config().create_job_queue() + + ert.createRunpath(run_context) + num = ert.getEnkfSimulationRunner().runEnsembleExperiment( + job_queue, run_context + ) + self.assertEqual(num, batch_size) + + order_result = EnkfNode(ens_config["ORDER"]) + injection_result = EnkfNode(ens_config["INJECTION"]) + + for iens in range(batch_size): + node_id = NodeId(0, iens) + order_result.load(sim_fs, node_id) + data = order_result.asGenData() + + order_node.load(sim_fs, node_id) + self.assertEqual(order_node_ext["W1"], data[0]) + self.assertEqual(order_node_ext["W2"], data[1]) + self.assertEqual(order_node_ext["W3"], data[2]) diff --git a/libres/tests/res/enkf/test_site_config.py b/libres/tests/res/enkf/test_site_config.py new file mode 100755 index 00000000000..bd12fdf0494 --- /dev/null +++ b/libres/tests/res/enkf/test_site_config.py @@ -0,0 +1,131 @@ +# Copyright (C) 2017 Equinor ASA, Norway. +# +# The file 'test_site_config.py' 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. + +from res.enkf import SiteConfig, ConfigKeys, ResConfig +import os + +from ecl.util.test import TestAreaContext +from tests import ResTest +from tests.utils import tmpdir +from pytest import MonkeyPatch + + +class SiteConfigTest(ResTest): + def setUp(self): + self.case_directory = self.createTestPath("local/simple_config/") + self.snake_case_directory = self.createTestPath("local/snake_oil/") + + self.monkeypatch = MonkeyPatch() + + def tearDown(self): + self.monkeypatch.undo() + + def test_invalid_user_config(self): + with TestAreaContext("void land"): + with self.assertRaises(IOError): + SiteConfig("this/is/not/a/file") + + def test_init(self): + with TestAreaContext("site_config_init_test") as work_area: + work_area.copy_directory(self.case_directory) + config_file = "simple_config/minimum_config" + site_config = SiteConfig(user_config_file=config_file) + self.assertIsNotNone(site_config) + + def test_constructors(self): + with TestAreaContext("site_config_constructor_test") as work_area: + work_area.copy_directory(self.snake_case_directory) + config_file = "snake_oil/snake_oil.ert" + + ERT_SITE_CONFIG = SiteConfig.getLocation() + ERT_SHARE_PATH = os.path.dirname(ERT_SITE_CONFIG) + snake_config_dict = { + ConfigKeys.INSTALL_JOB: [ + { + ConfigKeys.NAME: "SNAKE_OIL_SIMULATOR", + ConfigKeys.PATH: os.getcwd() + + "/snake_oil/jobs/SNAKE_OIL_SIMULATOR", + }, + { + ConfigKeys.NAME: "SNAKE_OIL_NPV", + ConfigKeys.PATH: os.getcwd() + "/snake_oil/jobs/SNAKE_OIL_NPV", + }, + { + ConfigKeys.NAME: "SNAKE_OIL_DIFF", + ConfigKeys.PATH: os.getcwd() + "/snake_oil/jobs/SNAKE_OIL_DIFF", + }, + ], + ConfigKeys.INSTALL_JOB_DIRECTORY: [ + ERT_SHARE_PATH + "/forward-models/res", + ERT_SHARE_PATH + "/forward-models/shell", + ERT_SHARE_PATH + "/forward-models/templating", + ERT_SHARE_PATH + "/forward-models/old_style", + ], + ConfigKeys.SETENV: [ + {ConfigKeys.NAME: "SILLY_VAR", ConfigKeys.VALUE: "silly-value"}, + { + ConfigKeys.NAME: "OPTIONAL_VAR", + ConfigKeys.VALUE: "optional-value", + }, + ], + ConfigKeys.LICENSE_PATH: "some/random/path", + ConfigKeys.UMASK: 18, + } + + site_config_user_file = SiteConfig(user_config_file=config_file) + site_config_dict = SiteConfig(config_dict=snake_config_dict) + self.assertEqual(site_config_dict, site_config_user_file) + + with self.assertRaises(ValueError): + site_config = SiteConfig( + user_config_file=config_file, config_dict=snake_config_dict + ) + + @tmpdir() + def test_site_config_hook_workflow(self): + site_config_filename = "test_site_config" + test_config_filename = "test_config" + site_config_content = """ +LOAD_WORKFLOW_JOB ECHO_WORKFLOW_JOB +LOAD_WORKFLOW ECHO_WORKFLOW +HOOK_WORKFLOW ECHO_WORKFLOW PRE_SIMULATION +""" + + with open(site_config_filename, "w") as fh: + fh.write(site_config_content) + + with open(test_config_filename, "w") as fh: + fh.write("NUM_REALIZATIONS 1\n") + + with open("ECHO_WORKFLOW_JOB", "w") as fh: + fh.write( + """INTERNAL False +EXECUTABLE echo +MIN_ARG 1 +""" + ) + + with open("ECHO_WORKFLOW", "w") as fh: + fh.write("ECHO_WORKFLOW_JOB hello") + + self.monkeypatch.setenv("ERT_SITE_CONFIG", site_config_filename) + + res_config = ResConfig(user_config_file=test_config_filename) + self.assertTrue(len(res_config.hook_manager) == 1) + self.assertEqual( + res_config.hook_manager[0].getWorkflow().src_file, + os.path.join(os.getcwd(), "ECHO_WORKFLOW"), + ) diff --git a/libres/tests/res/enkf/test_state_map.py b/libres/tests/res/enkf/test_state_map.py new file mode 100644 index 00000000000..4ac36c5766f --- /dev/null +++ b/libres/tests/res/enkf/test_state_map.py @@ -0,0 +1,151 @@ +from res.enkf.enums.realization_state_enum import RealizationStateEnum +from res.enkf.state_map import StateMap +from ecl.util.test import TestAreaContext +from tests import ResTest + + +class StateMapTest(ResTest): + def test_state_map(self): + state_map = StateMap() + + self.assertEqual(len(state_map), 0) + + with self.assertRaises(TypeError): + r = state_map["r"] + + with self.assertRaises(IOError): + s2 = StateMap("DoesNotExist") + + with self.assertRaises(IOError): + state_map.load("/file/does/not/exist") + + with self.assertRaises(IndexError): + v = state_map[0] + + with self.assertRaises(TypeError): + state_map["r"] = RealizationStateEnum.STATE_INITIALIZED + + with self.assertRaises(TypeError): + state_map[0] = "INITIALIZED" + + with self.assertRaises(IndexError): + state_map[-1] = RealizationStateEnum.STATE_INITIALIZED + + state_map[0] = RealizationStateEnum.STATE_INITIALIZED + + self.assertEqual(len(state_map), 1) + + state_map[1] = RealizationStateEnum.STATE_INITIALIZED + state_map[1] = RealizationStateEnum.STATE_HAS_DATA + + self.assertEqual(len(state_map), 2) + + index = 0 + for state in state_map: + self.assertEqual(state, state_map[index]) + index += 1 + + states = [state for state in state_map] + + self.assertEqual( + states, + [ + RealizationStateEnum.STATE_INITIALIZED, + RealizationStateEnum.STATE_HAS_DATA, + ], + ) + + state_map[5] = RealizationStateEnum.STATE_INITIALIZED + self.assertEqual(len(state_map), 6) + + self.assertEqual(state_map[2], RealizationStateEnum.STATE_UNDEFINED) + self.assertEqual(state_map[3], RealizationStateEnum.STATE_UNDEFINED) + self.assertEqual(state_map[4], RealizationStateEnum.STATE_UNDEFINED) + self.assertEqual(state_map[5], RealizationStateEnum.STATE_INITIALIZED) + + self.assertFalse(state_map.isReadOnly()) + + with TestAreaContext("python/state-map/fwrite") as work_area: + state_map.save("MAP") + s2 = StateMap("MAP") + self.assertTrue(state_map == s2) + + def test_state_map_transitions(self): + self.assertTrue( + StateMap.isLegalTransition( + RealizationStateEnum.STATE_UNDEFINED, + RealizationStateEnum.STATE_INITIALIZED, + ) + ) + self.assertTrue( + StateMap.isLegalTransition( + RealizationStateEnum.STATE_INITIALIZED, + RealizationStateEnum.STATE_HAS_DATA, + ) + ) + self.assertTrue( + StateMap.isLegalTransition( + RealizationStateEnum.STATE_INITIALIZED, + RealizationStateEnum.STATE_LOAD_FAILURE, + ) + ) + self.assertTrue( + StateMap.isLegalTransition( + RealizationStateEnum.STATE_INITIALIZED, + RealizationStateEnum.STATE_PARENT_FAILURE, + ) + ) + self.assertTrue( + StateMap.isLegalTransition( + RealizationStateEnum.STATE_HAS_DATA, + RealizationStateEnum.STATE_PARENT_FAILURE, + ) + ) + + self.assertFalse( + StateMap.isLegalTransition( + RealizationStateEnum.STATE_UNDEFINED, + RealizationStateEnum.STATE_LOAD_FAILURE, + ) + ) + self.assertFalse( + StateMap.isLegalTransition( + RealizationStateEnum.STATE_UNDEFINED, + RealizationStateEnum.STATE_HAS_DATA, + ) + ) + + with self.assertRaises(TypeError): + StateMap.isLegalTransition("error", RealizationStateEnum.STATE_UNDEFINED) + + with self.assertRaises(TypeError): + StateMap.isLegalTransition(RealizationStateEnum.STATE_UNDEFINED, "error") + + with self.assertRaises(TypeError): + StateMap.isLegalTransition("error", "exception") + + def test_active_list(self): + state_map = StateMap() + state_map[0] = RealizationStateEnum.STATE_INITIALIZED + state_map[2] = RealizationStateEnum.STATE_INITIALIZED + state_map[2] = RealizationStateEnum.STATE_HAS_DATA + + initialized = state_map.realizationList(RealizationStateEnum.STATE_INITIALIZED) + self.assertEqual(len(initialized), 1) + self.assertEqual(initialized[0], 0) + + mask = state_map.createMask(RealizationStateEnum.STATE_INITIALIZED) + self.assertEqual([True, False, False], list(mask)) + + has_data = state_map.realizationList(RealizationStateEnum.STATE_HAS_DATA) + self.assertEqual(len(has_data), 1) + self.assertEqual(has_data[0], 2) + + mask = state_map.createMask(RealizationStateEnum.STATE_HAS_DATA) + self.assertEqual([False, False, True], list(mask)) + + state = ( + RealizationStateEnum.STATE_HAS_DATA | RealizationStateEnum.STATE_INITIALIZED + ) + mask = state_map.createMask(state) + self.assertEqual([True, False, True], list(mask)) diff --git a/libres/tests/res/enkf/test_subst_config.py b/libres/tests/res/enkf/test_subst_config.py new file mode 100644 index 00000000000..b471ba5c75f --- /dev/null +++ b/libres/tests/res/enkf/test_subst_config.py @@ -0,0 +1,141 @@ +# Copyright (C) 2017 Equinor ASA, Norway. +# +# The file 'test_res_config.py' 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. +import os +import os.path +from ecl.util.test import TestAreaContext +from res.enkf import SubstConfig +from res.enkf import ConfigKeys +from res.enkf import ResConfig +import unittest +from tests import ResTest + + +class SubstConfigTest(ResTest): + def setUp(self): + self.path = self.createTestPath("local/snake_oil_structure/") + self.config_data = { + ConfigKeys.RUNPATH_FILE: "runpath", + ConfigKeys.CONFIG_DIRECTORY: self.path, + ConfigKeys.CONFIG_FILE_KEY: "config", + ConfigKeys.DEFINE_KEY: {"keyA": "valA", "keyB": "valB"}, + ConfigKeys.DATA_KW_KEY: {"keyC": "valC", "keyD": "valD"}, + ConfigKeys.DATA_FILE: "eclipse/model/SNAKE_OIL.DATA", + } + + def test_two_instances_of_same_config_are_equal(self): + subst_config1 = SubstConfig(config_dict=self.config_data) + subst_config2 = SubstConfig(config_dict=self.config_data) + self.assertEqual(subst_config1, subst_config2) + + def test_two_instances_of_different_config_are_not_equal(self): + subst_config1 = SubstConfig(config_dict=self.config_data) + subst_config2 = SubstConfig( + config_dict=self.set_key(ConfigKeys.RUNPATH_FILE, "aaaaa") + ) + self.assertNotEqual(subst_config1, subst_config2) + + def test_old_and_new_constructor_creates_equal_config(self): + with TestAreaContext("subst_config_test_tmp") as work_area: + work_area.copy_directory(os.path.join(self.path, "eclipse")) + cwd = os.getcwd() + filename = self.config_data[ConfigKeys.CONFIG_FILE_KEY] + self.make_config_file(filename) + res_config = ResConfig(user_config_file=filename) + subst_config1 = res_config.subst_config + subst_config2 = SubstConfig( + config_dict=self.set_key(ConfigKeys.CONFIG_DIRECTORY, cwd) + ) + + self.assertEqual( + subst_config1, + subst_config2, + str(subst_config1) + "\n\nis not equal to:\n\n" + str(subst_config2), + ) + + def test_complete_config_reads_correct_values(self): + subst_config = SubstConfig(config_dict=self.config_data) + self.assertKeyValue(subst_config, "", self.path) + self.assertKeyValue(subst_config, "", self.path) + self.assertKeyValue(subst_config, "keyA", "valA") + self.assertKeyValue(subst_config, "keyB", "valB") + self.assertKeyValue(subst_config, "keyC", "valC") + self.assertKeyValue(subst_config, "keyD", "valD") + self.assertKeyValue(subst_config, "", self.path + "/runpath") + self.assertKeyValue(subst_config, "", "1") + + def test_missing_runpath_gives_default_value(self): + subst_config = SubstConfig(config_dict=self.remove_key(ConfigKeys.RUNPATH_FILE)) + self.assertKeyValue( + subst_config, "", self.path + "/.ert_runpath_list" + ) + + def test_empty_config_raises_error(self): + with self.assertRaises(ValueError): + SubstConfig(config_dict={}) + + def test_missing_config_directory_raises_error(self): + with self.assertRaises(ValueError): + SubstConfig(config_dict=self.remove_key(ConfigKeys.CONFIG_DIRECTORY)) + + def test_data_file_not_found_raises_error(self): + with self.assertRaises(IOError): + SubstConfig(config_dict=self.set_key(ConfigKeys.DATA_FILE, "not_a_file")) + + def remove_key(self, key): + return {i: self.config_data[i] for i in self.config_data if i != key} + + def set_key(self, key, val): + copy = self.config_data.copy() + copy[key] = val + return copy + + def assertKeyValue(self, subst_config, key, val): + actual_val = subst_config.__getitem__(key) + assert ( + actual_val == val + ), "subst_config does not contain key/value pair ({}, {}). Actual value was: {}".format( + key, val, actual_val + ) + + def make_config_file(self, filename): + with open(filename, "w+") as config: + # necessary in the file, but irrelevant to this test + config.write("JOBNAME Job%d\n") + config.write("NUM_REALIZATIONS 1\n") + + # write the rest of the relevant config items to the file + config.write( + "{} {}\n".format( + ConfigKeys.RUNPATH_FILE, self.config_data[ConfigKeys.RUNPATH_FILE] + ) + ) + defines = self.config_data[ConfigKeys.DEFINE_KEY] + for key in defines: + val = defines[key] + config.write("{} {} {}\n".format(ConfigKeys.DEFINE_KEY, key, val)) + data_kws = self.config_data[ConfigKeys.DATA_KW_KEY] + for key in data_kws: + val = data_kws[key] + config.write("{} {} {}\n".format(ConfigKeys.DATA_KW_KEY, key, val)) + config.write( + "{} {}\n".format( + ConfigKeys.DATA_FILE, self.config_data[ConfigKeys.DATA_FILE] + ) + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/libres/tests/res/enkf/test_summary_key_matcher.py b/libres/tests/res/enkf/test_summary_key_matcher.py new file mode 100644 index 00000000000..78aae72fac2 --- /dev/null +++ b/libres/tests/res/enkf/test_summary_key_matcher.py @@ -0,0 +1,28 @@ +from res.enkf import SummaryKeyMatcher +from tests import ResTest + + +class SummaryKeyMatcherTest(ResTest): + def test_creation(self): + matcher = SummaryKeyMatcher() + + self.assertTrue(len(matcher) == 0) + + matcher.addSummaryKey("F*") + self.assertTrue(len(matcher) == 1) + + matcher.addSummaryKey("F*") + self.assertTrue(len(matcher) == 1) + + matcher.addSummaryKey("FOPT") + self.assertTrue(len(matcher) == 2) + + self.assertItemsEqual(["F*", "FOPT"], matcher.keys()) + + self.assertTrue("FGIR" in matcher) + self.assertTrue("FOPT" in matcher) + self.assertFalse("TCPU" in matcher) + + self.assertTrue(matcher.isRequired("FOPT")) + self.assertFalse(matcher.isRequired("FGIR")) + self.assertFalse(matcher.isRequired("TCPU")) diff --git a/libres/tests/res/enkf/test_summary_key_set.py b/libres/tests/res/enkf/test_summary_key_set.py new file mode 100644 index 00000000000..88169d80d21 --- /dev/null +++ b/libres/tests/res/enkf/test_summary_key_set.py @@ -0,0 +1,99 @@ +import os +import pytest + +from ecl.util.test.test_area import TestAreaContext +from tests import ResTest +from res.test.ert_test_context import ErtTestContext + +from res.enkf import SummaryKeySet +from res.enkf.enkf_fs import EnkfFs +from res.enkf import EnKFMain, ResConfig + + +@pytest.mark.equinor_test +class SummaryKeySetTest(ResTest): + def test_creation(self): + + keys = SummaryKeySet() + + self.assertEqual(len(keys), 0) + + self.assertTrue(keys.addSummaryKey("FOPT")) + + self.assertEqual(len(keys), 1) + + self.assertTrue("FOPT" in keys) + + self.assertItemsEqual(["FOPT"], keys.keys()) + + self.assertTrue(keys.addSummaryKey("WWCT")) + + self.assertEqual(len(keys), 2) + + self.assertTrue("WWCT" in keys) + + self.assertItemsEqual(["WWCT", "FOPT"], keys.keys()) + + def test_read_only_creation(self): + with TestAreaContext("enkf/summary_key_set/read_only_write_test"): + keys = SummaryKeySet() + + keys.addSummaryKey("FOPT") + keys.addSummaryKey("WWCT") + + filename = "test.txt" + keys.writeToFile(filename) + + keys_from_file = SummaryKeySet(filename, read_only=True) + self.assertItemsEqual(keys.keys(), keys_from_file.keys()) + + self.assertTrue(keys_from_file.isReadOnly()) + self.assertFalse(keys_from_file.addSummaryKey("WOPR")) + + def test_write_to_and_read_from_file(self): + with TestAreaContext("enkf/summary_key_set/write_test"): + keys = SummaryKeySet() + + keys.addSummaryKey("FOPT") + keys.addSummaryKey("WWCT") + + filename = "test.txt" + + self.assertFalse(os.path.exists(filename)) + + keys.writeToFile(filename) + + self.assertTrue(os.path.exists(filename)) + + keys_from_file = SummaryKeySet(filename) + self.assertItemsEqual(keys.keys(), keys_from_file.keys()) + + def test_with_enkf_fs(self): + config_file = self.createTestPath("Equinor/config/with_data/config") + + with TestAreaContext( + "enkf/summary_key_set/enkf_fs", store_area=True + ) as context: + context.copy_parent_content(config_file) + + fs = EnkfFs("storage/default") + summary_key_set = fs.getSummaryKeySet() + summary_key_set.addSummaryKey("FOPT") + summary_key_set.addSummaryKey("WWCT") + summary_key_set.addSummaryKey("WOPR") + fs.umount() + + res_config = ResConfig("config") + ert = EnKFMain(res_config) + fs = ert.getEnkfFsManager().getCurrentFileSystem() + summary_key_set = fs.getSummaryKeySet() + self.assertTrue("FOPT" in summary_key_set) + self.assertTrue("WWCT" in summary_key_set) + self.assertTrue("WOPR" in summary_key_set) + + ensemble_config = ert.ensembleConfig() + + self.assertTrue("FOPT" in ensemble_config) + self.assertTrue("WWCT" in ensemble_config) + self.assertTrue("WOPR" in ensemble_config) + self.assertFalse("TCPU" in ensemble_config) diff --git a/libres/tests/res/enkf/test_summary_obs.py b/libres/tests/res/enkf/test_summary_obs.py new file mode 100644 index 00000000000..b585931e3bf --- /dev/null +++ b/libres/tests/res/enkf/test_summary_obs.py @@ -0,0 +1,38 @@ +# Copyright (C) 2015 Equinor ASA, Norway. +# +# The file 'test_summary_obs.py' 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. + +import os + +from res.enkf import SummaryObservation +from res.enkf import ActiveList +from tests import ResTest + + +class SummaryObsTest(ResTest): + def test_create(self): + sum_obs = SummaryObservation("WWCT:OP_X", "WWCT:OP_X", 0.25, 0.12) + + self.assertEqual(sum_obs.getValue(), 0.25) + self.assertEqual(sum_obs.getStandardDeviation(), 0.12) + self.assertEqual(sum_obs.getStdScaling(), 1.0) + + def test_std_scaling(self): + sum_obs = SummaryObservation("WWCT:OP_X", "WWCT:OP_X", 0.25, 0.12) + + active_list = ActiveList() + sum_obs.updateStdScaling(0.50, active_list) + sum_obs.updateStdScaling(0.125, active_list) + self.assertEqual(sum_obs.getStdScaling(), 0.125) diff --git a/libres/tests/res/enkf/test_time_map.py b/libres/tests/res/enkf/test_time_map.py new file mode 100644 index 00000000000..faf2976ec48 --- /dev/null +++ b/libres/tests/res/enkf/test_time_map.py @@ -0,0 +1,214 @@ +import datetime + +from res.enkf.enums.realization_state_enum import RealizationStateEnum +from res.enkf import TimeMap +from ecl.util.test import TestAreaContext +from tests import ResTest + + +class TimeMapTest(ResTest): + def test_time_map(self): + with self.assertRaises(IOError): + TimeMap("Does/not/exist") + + tm = TimeMap() + with self.assertRaises(IndexError): + t = tm[10] + + pfx = "TimeMap(" + rep = repr(tm) + print('repr(time_map) = "%s"' % repr(tm)) + self.assertEqual(pfx, rep[: len(pfx)]) + + self.assertTrue(tm.update(0, datetime.date(2000, 1, 1))) + self.assertEqual(tm[0], datetime.date(2000, 1, 1)) + + self.assertTrue(tm.isStrict()) + with self.assertRaises(Exception): + tm.update(0, datetime.date(2000, 1, 2)) + + tm.setStrict(False) + self.assertFalse(tm.update(0, datetime.date(2000, 1, 2))) + + tm.setStrict(True) + self.assertTrue(tm.update(1, datetime.date(2000, 1, 2))) + d = tm.dump() + self.assertEqual( + d, [(0, datetime.date(2000, 1, 1), 0), (1, datetime.date(2000, 1, 2), 1)] + ) + + def test_fscanf(self): + tm = TimeMap() + + with self.assertRaises(IOError): + tm.fload("Does/not/exist") + + with TestAreaContext("timemap/fload1") as work_area: + with open("map.txt", "w") as fileH: + fileH.write("10/10/2000\n") + fileH.write("12/10/2000\n") + fileH.write("14/10/2000\n") + fileH.write("16/10/2000\n") + + tm.fload("map.txt") + self.assertEqual(4, len(tm)) + self.assertEqual(datetime.date(2000, 10, 10), tm[0]) + self.assertEqual(datetime.date(2000, 10, 16), tm[3]) + + with TestAreaContext("timemap/fload2") as work_area: + with open("map.txt", "w") as fileH: + fileH.write("10/10/200X\n") + + with self.assertRaises(Exception): + tm.fload("map.txt") + + self.assertEqual(4, len(tm)) + self.assertEqual(datetime.date(2000, 10, 10), tm[0]) + self.assertEqual(datetime.date(2000, 10, 16), tm[3]) + + with TestAreaContext("timemap/fload2") as work_area: + with open("map.txt", "w") as fileH: + fileH.write("12/10/2000\n") + fileH.write("10/10/2000\n") + + with self.assertRaises(Exception): + tm.fload("map.txt") + + self.assertEqual(4, len(tm)) + self.assertEqual(datetime.date(2000, 10, 10), tm[0]) + self.assertEqual(datetime.date(2000, 10, 16), tm[3]) + + def test_setitem(self): + tm = TimeMap() + tm[0] = datetime.date(2000, 1, 1) + tm[1] = datetime.date(2000, 1, 2) + self.assertEqual(2, len(tm)) + + self.assertEqual(tm[0], datetime.date(2000, 1, 1)) + self.assertEqual(tm[1], datetime.date(2000, 1, 2)) + + def test_in(self): + tm = TimeMap() + tm[0] = datetime.date(2000, 1, 1) + tm[1] = datetime.date(2000, 1, 2) + tm[2] = datetime.date(2000, 1, 3) + + self.assertTrue(datetime.date(2000, 1, 1) in tm) + self.assertTrue(datetime.date(2000, 1, 2) in tm) + self.assertTrue(datetime.date(2000, 1, 3) in tm) + + self.assertFalse(datetime.date(2001, 1, 3) in tm) + self.assertFalse(datetime.date(1999, 1, 3) in tm) + + def test_lookupDate(self): + tm = TimeMap() + tm[0] = datetime.date(2000, 1, 1) + tm[1] = datetime.date(2000, 1, 2) + tm[2] = datetime.date(2000, 1, 3) + + self.assertEqual(0, tm.lookupTime(datetime.date(2000, 1, 1))) + self.assertEqual(0, tm.lookupTime(datetime.datetime(2000, 1, 1, 0, 0, 0))) + + self.assertEqual(2, tm.lookupTime(datetime.date(2000, 1, 3))) + self.assertEqual(2, tm.lookupTime(datetime.datetime(2000, 1, 3, 0, 0, 0))) + + with self.assertRaises(ValueError): + tm.lookupTime(datetime.date(1999, 10, 10)) + + def test_lookupDays(self): + tm = TimeMap() + + with self.assertRaises(ValueError): + tm.lookupDays(0) + + tm[0] = datetime.date(2000, 1, 1) + tm[1] = datetime.date(2000, 1, 2) + tm[2] = datetime.date(2000, 1, 3) + + self.assertEqual(0, tm.lookupDays(0)) + self.assertEqual(1, tm.lookupDays(1)) + self.assertEqual(2, tm.lookupDays(2)) + + with self.assertRaises(ValueError): + tm.lookupDays(-1) + + with self.assertRaises(ValueError): + tm.lookupDays(0.50) + + with self.assertRaises(ValueError): + tm.lookupDays(3) + + def test_nearest_date_lookup(self): + tm = TimeMap() + with self.assertRaises(ValueError): + tm.lookupTime(datetime.date(1999, 1, 1)) + + with self.assertRaises(ValueError): + tm.lookupTime( + datetime.date(1999, 1, 1), + tolerance_seconds_before=10, + tolerance_seconds_after=10, + ) + + tm[0] = datetime.date(2000, 1, 1) + tm[1] = datetime.date(2000, 2, 1) + tm[2] = datetime.date(2000, 3, 1) + + # Outside of total range will raise an exception, irrespective of + # the tolerances used. + with self.assertRaises(ValueError): + tm.lookupTime( + datetime.date(1999, 1, 1), + tolerance_seconds_before=-1, + tolerance_seconds_after=-1, + ) + + with self.assertRaises(ValueError): + tm.lookupTime( + datetime.date(2001, 1, 1), + tolerance_seconds_before=-1, + tolerance_seconds_after=-1, + ) + + self.assertEqual( + 0, + tm.lookupTime( + datetime.datetime(2000, 1, 1, 0, 0, 10), tolerance_seconds_after=15 + ), + ) + self.assertEqual( + 1, + tm.lookupTime( + datetime.datetime(2000, 1, 1, 0, 0, 10), + tolerance_seconds_before=3600 * 24 * 40, + ), + ) + + self.assertEqual( + 0, + tm.lookupTime( + datetime.date(2000, 1, 10), + tolerance_seconds_before=-1, + tolerance_seconds_after=-1, + ), + ) + self.assertEqual( + 1, + tm.lookupTime( + datetime.date(2000, 1, 20), + tolerance_seconds_before=-1, + tolerance_seconds_after=-1, + ), + ) + + with self.assertRaises(ValueError): + tm.lookupTime( + datetime.date(2001, 10, 1), + tolerance_seconds_before=10, + tolerance_seconds_after=10, + ) + + def test_empty(self): + tm = TimeMap() + last_step = tm.getLastStep() + self.assertEqual(last_step, -1) diff --git a/libres/tests/res/enkf/test_update.py b/libres/tests/res/enkf/test_update.py new file mode 100644 index 00000000000..421c14a25bb --- /dev/null +++ b/libres/tests/res/enkf/test_update.py @@ -0,0 +1,94 @@ +# Copyright (C) 2015 Equinor ASA, Norway. +# +# The file 'test_update.py' 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. + +import random +import sys +import os + +import pytest +import res + +from tests import ResTest +from res.test import ErtTestContext + +from ecl.util.enums import RngAlgTypeEnum, RngInitModeEnum +from ecl.util.util import BoolVector, RandomNumberGenerator +from res.util import Matrix +from res.analysis import ( + AnalysisModule, + AnalysisModuleLoadStatusEnum, + AnalysisModuleOptionsEnum, +) +from res.enkf import MeasData, ObsData, LocalObsdata + + +def update(rng, mask, module, ert, meas_data, obs_data, state_size): + S = meas_data.createS() + R = obs_data.createR() + dObs = obs_data.createDObs() + E = obs_data.createE(rng, meas_data.getActiveEnsSize()) + D = obs_data.createD(E, S) + obs_data.scale(S, E=E, D=D, R=R, D_obs=dObs) + + A = Matrix(state_size, meas_data.getActiveEnsSize()) + A.randomInit(rng) + + module.initUpdate(mask, S, R, dObs, E, D) + module.updateA(A, S, R, dObs, E, D) + + +@pytest.mark.skip() +@pytest.mark.equinor_test +class UpdateTest(ResTest): + def setUp(self): + if sys.platform.lower() == "darwin": + self.libname = os.path.join(res.res_lib_path, "rml_enkf.dylib") + else: + self.libname = os.path.join(res.res_lib_path, "rml_enkf.so") + self.config_file = self.createTestPath("Equinor/config/obs_testing2/config") + self.rng = RandomNumberGenerator( + RngAlgTypeEnum.MZRAN, RngInitModeEnum.INIT_DEFAULT + ) + + def createAnalysisModule(self): + return AnalysisModule(self.rng, lib_name=self.libname) + + def test_it(self): + state_size = 10 + with ErtTestContext("update", self.config_file) as tc: + analysis = self.createAnalysisModule() + ert = tc.getErt() + obs = ert.getObservations() + local_obsdata = obs.getAllActiveLocalObsdata() + + fs = ert.getEnkfFsManager().getCurrentFileSystem() + + mask = BoolVector(initial_size=ert.getEnsembleSize(), default_value=True) + meas_data = MeasData(mask) + obs_data = ObsData() + obs.getObservationAndMeasureData( + fs, local_obsdata, mask.createActiveList(), meas_data, obs_data + ) + update(self.rng, mask, analysis, ert, meas_data, obs_data, state_size) + + mask[0] = False + mask[4] = False + meas_data = MeasData(mask) + obs_data = ObsData() + obs.getObservationAndMeasureData( + fs, local_obsdata, mask.createActiveList(), meas_data, obs_data + ) + update(self.rng, mask, analysis, ert, meas_data, obs_data, state_size) diff --git a/libres/tests/res/enkf/test_workflow_list.py b/libres/tests/res/enkf/test_workflow_list.py new file mode 100644 index 00000000000..cc8f2606a7c --- /dev/null +++ b/libres/tests/res/enkf/test_workflow_list.py @@ -0,0 +1,82 @@ +# Copyright (C) 2017 Equinor ASA, Norway. +# +# The file 'test_summary_obs.py' 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. + +import os +from ecl.util.test import TestAreaContext +from tests import ResTest +from res.enkf import ErtWorkflowList, ResConfig, SiteConfig, ConfigKeys + + +class ErtWorkflowListTest(ResTest): + def setUp(self): + self.case_directory = self.createTestPath("local/simple_config/") + + def test_workflow_list_constructor(self): + with TestAreaContext("ert_workflow_list_test") as work_area: + + ERT_SITE_CONFIG = SiteConfig.getLocation() + ERT_SHARE_PATH = os.path.dirname(ERT_SITE_CONFIG) + + self.config_dict = { + ConfigKeys.LOAD_WORKFLOW_JOB: [ + { + ConfigKeys.NAME: "print_uber", + ConfigKeys.PATH: os.getcwd() + + "/simple_config/workflows/UBER_PRINT", + } + ], + ConfigKeys.LOAD_WORKFLOW: [ + { + ConfigKeys.NAME: "magic_print", + ConfigKeys.PATH: os.getcwd() + + "/simple_config/workflows/MAGIC_PRINT", + } + ], + ConfigKeys.WORKFLOW_JOB_DIRECTORY: [ + ERT_SHARE_PATH + "/workflows/jobs/shell", + ERT_SHARE_PATH + "/workflows/jobs/internal/config", + ERT_SHARE_PATH + "/workflows/jobs/internal-gui/config", + ], + } + + work_area.copy_directory(self.case_directory) + + config_file = "simple_config/minimum_config" + with open(config_file, "a+") as ert_file: + ert_file.write("LOAD_WORKFLOW_JOB workflows/UBER_PRINT print_uber\n") + ert_file.write("LOAD_WORKFLOW workflows/MAGIC_PRINT magic_print\n") + + os.mkdir("simple_config/workflows") + + with open("simple_config/workflows/MAGIC_PRINT", "w") as f: + f.write("print_uber\n") + with open("simple_config/workflows/UBER_PRINT", "w") as f: + f.write("EXECUTABLE ls\n") + + res_config = ResConfig(config_file) + list_from_content = res_config.ert_workflow_list + list_from_dict = ErtWorkflowList( + subst_list=res_config.subst_config.subst_list, + config_dict=self.config_dict, + ) + self.assertEqual(list_from_content, list_from_dict) + + def test_illegal_configs(self): + with self.assertRaises(ValueError): + ErtWorkflowList(subst_list=[], config_dict=[], config_content=[]) + + with self.assertRaises(ValueError): + ErtWorkflowList() diff --git a/libres/tests/res/fm/__init__.py b/libres/tests/res/fm/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/tests/res/fm/ecl_run_fail b/libres/tests/res/fm/ecl_run_fail new file mode 100755 index 00000000000..92729107d4b --- /dev/null +++ b/libres/tests/res/fm/ecl_run_fail @@ -0,0 +1,4 @@ +#!/usr/bin/env python +import sys + +sys.exit("Designed to fail") diff --git a/libres/tests/res/fm/rms b/libres/tests/res/fm/rms new file mode 100755 index 00000000000..64198d2e2b5 --- /dev/null +++ b/libres/tests/res/fm/rms @@ -0,0 +1,52 @@ +#!/usr/bin/env python +# This is mock RMS script; not very functional ... +import json +import sys +import os +import time + +config = {"exit_status": 0} +if os.path.isfile("action.json"): + config = json.load(open("action.json")) + + +def write_target_file(target_file): + with open(target_file, "w") as f: + f.write("this is the target file {}\n".format(time.time())) + + +if "target_file" in config: + target_file = config["target_file"] + if os.path.isfile(target_file): + mtime = os.path.getmtime(target_file) + else: + mtime = None + + write_target_file(target_file) + + # Try to ensure that mtime is updated for target file + base_sleep_time = 1 + for sleep_pow in range(5): + if mtime is not None and mtime == os.path.getmtime(target_file): + time.sleep(base_sleep_time * 2 ** sleep_pow) + write_target_file(target_file) + time.sleep(base_sleep_time * 2 ** sleep_pow) + else: + break + + +with open("env.json", "w") as f: + env = {} + for key in ( + "PATH", + "RMS_TEST_VAR", + "PYTHONPATH", + "_PRE_RMS_PYTHONPATH", + "_PRE_RMS_BACKUP", + ): + if key in os.environ: + env[key] = os.environ[key] + + json.dump(env, f) + +sys.exit(config["exit_status"]) diff --git a/libres/tests/res/fm/test_ecl_config.py b/libres/tests/res/fm/test_ecl_config.py new file mode 100644 index 00000000000..f83ad369090 --- /dev/null +++ b/libres/tests/res/fm/test_ecl_config.py @@ -0,0 +1,205 @@ +# Copyright (C) 2018 Equinor ASA, Norway. +# +# The file 'test_ecl.py' 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. +import os +import stat +import inspect +import unittest +import yaml +from ecl.util.test import TestAreaContext +from tests import ResTest +from pytest import MonkeyPatch + +from res.fm.ecl import Ecl100Config +from res.fm.ecl.ecl_config import Keys + + +class EclConfigTest(ResTest): + def setUp(self): + self.ecl_config_path = os.path.dirname(inspect.getsourcefile(Ecl100Config)) + self.monkeypatch = MonkeyPatch() + + def tearDown(self): + self.monkeypatch.undo() + + def test_load(self): + self.monkeypatch.setenv("ECL100_SITE_CONFIG", "file/does/not/exist") + with self.assertRaises(IOError): + conf = Ecl100Config() + + self.monkeypatch.setenv( + "ECL100_SITE_CONFIG", + os.path.join(self.ecl_config_path, "ecl100_config.yml"), + ) + conf = Ecl100Config() + + with TestAreaContext("yaml_invalid"): + with open("file.yml", "w") as f: + f.write("this:\n -should\n-be\ninvalid:yaml?") + + self.monkeypatch.setenv("ECL100_SITE_CONFIG", "file.yml") + with self.assertRaises(ValueError): + conf = Ecl100Config() + + scalar_path = "scalar" + scalar_exe = "bin/scalar_exe" + mpi_exe = "bin/mpi_exe" + mpi_run = "bin/mpi_run" + + os.mkdir("bin") + for f in ["scalar_exe", "mpi_exe", "mpi_run"]: + fname = os.path.join("bin", f) + with open(fname, "w") as fh: + fh.write("This is an exectable ...") + + os.chmod(fname, stat.S_IEXEC) + + intel_path = "intel" + self.monkeypatch.setenv("ENV1", "A") + self.monkeypatch.setenv("ENV2", "C") + d = { + Keys.env: {"LICENSE_SERVER": "license@company.com"}, + Keys.versions: { + "2015": { + Keys.scalar: {Keys.executable: scalar_exe}, + Keys.mpi: { + Keys.executable: mpi_exe, + Keys.mpirun: mpi_run, + Keys.env: { + "I_MPI_ROOT": "$ENV1:B:$ENV2", + "TEST_VAR": "$ENV1.B.$ENV2 $UNKNOWN_VAR", + "P4_RSHCOMMAND": "", + "LD_LIBRARY_PATH": "{}:$LD_LIBRARY_PATH".format( + intel_path + ), + "PATH": "{}/bin64:$PATH".format(intel_path), + }, + }, + }, + "2016": { + Keys.scalar: {Keys.executable: "/does/not/exist"}, + Keys.mpi: { + Keys.executable: "/does/not/exist", + Keys.mpirun: mpi_run, + }, + }, + "2017": { + Keys.mpi: { + Keys.executable: mpi_exe, + Keys.mpirun: "/does/not/exist", + } + }, + }, + } + + with open("file.yml", "w") as f: + f.write(yaml.dump(d)) + + conf = Ecl100Config() + # Fails because there is no version 2020 + with self.assertRaises(KeyError): + sim = conf.sim("2020") + + # Fails because the 2016 version points to a not existing executable + with self.assertRaises(OSError): + sim = conf.sim("2016") + + # Fails because the 2016 mpi version points to a non existing mpi executable + with self.assertRaises(OSError): + sim = conf.mpi_sim("2016") + + # Fails because the 2017 mpi version mpirun points to a non existing mpi executable + with self.assertRaises(OSError): + sim = conf.mpi_sim("2017") + + # Fails because the 2017 scalar version is not registered + with self.assertRaises(KeyError): + sim = conf.sim("2017") + + sim = conf.sim("2015") + mpi_sim = conf.mpi_sim("2015") + + # Check that global environment has been propagated down. + self.assertIn("LICENSE_SERVER", mpi_sim.env) + + # Check replacement of $ENV_VAR in values. + self.assertEqual(mpi_sim.env["I_MPI_ROOT"], "A:B:C") + self.assertEqual(mpi_sim.env["TEST_VAR"], "A.B.C $UNKNOWN_VAR") + self.assertEqual(len(mpi_sim.env), 1 + 5) + + sim = conf.sim("2015") + self.assertEqual(sim.executable, scalar_exe) + self.assertIsNone(sim.mpirun) + + with self.assertRaises(Exception): + simulators = conf.simulators() + + simulators = conf.simulators(strict=False) + self.assertEqual(len(simulators), 2) + + def test_default(self): + with TestAreaContext("default"): + os.mkdir("bin") + scalar_exe = "bin/scalar_exe" + with open(scalar_exe, "w") as fh: + fh.write("This is an exectable ...") + os.chmod(scalar_exe, stat.S_IEXEC) + + d0 = { + Keys.versions: { + "2015": {Keys.scalar: {Keys.executable: scalar_exe}}, + "2016": {Keys.scalar: {Keys.executable: scalar_exe}}, + } + } + + d1 = { + Keys.default_version: "2015", + Keys.versions: { + "2015": {Keys.scalar: {Keys.executable: scalar_exe}}, + "2016": {Keys.scalar: {Keys.executable: scalar_exe}}, + }, + } + + self.monkeypatch.setenv("ECL100_SITE_CONFIG", os.path.join("file.yml")) + with open("file.yml", "w") as f: + f.write(yaml.dump(d1)) + + conf = Ecl100Config() + sim = conf.sim() + self.assertEqual(sim.version, "2015") + self.assertIn("2015", conf) + self.assertNotIn("xxxx", conf) + self.assertIn(Keys.default, conf) + self.assertIn(None, conf) + + sim = conf.sim("default") + self.assertEqual(sim.version, "2015") + + with open("file.yml", "w") as f: + f.write(yaml.dump(d0)) + + conf = Ecl100Config() + self.assertNotIn(Keys.default, conf) + self.assertIsNone(conf.default_version) + + with self.assertRaises(Exception): + sim = conf.sim() + + with self.assertRaises(Exception): + sim = conf.sim(Keys.default) + + +if __name__ == "__main__": + unittest.main() diff --git a/libres/tests/res/fm/test_ecl_run.py b/libres/tests/res/fm/test_ecl_run.py new file mode 100644 index 00000000000..15e4d215225 --- /dev/null +++ b/libres/tests/res/fm/test_ecl_run.py @@ -0,0 +1,616 @@ +# Copyright (C) 2018 Equinor ASA, Norway. +# +# The file 'test_ecl.py' 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. +import os +import stat +import unittest +import shutil +import functools +import inspect +import yaml +import pytest +import re + +from ecl.summary import EclSum +from ecl.util.test import TestAreaContext +from tests import ResTest +from tests.utils import tmpdir +from res.fm.ecl import * +from res.fm.ecl.ecl_run import make_SLURM_machine_list +from subprocess import Popen, PIPE +from subprocess import Popen, PIPE +from distutils.spawn import find_executable +from pytest import MonkeyPatch + + +def flow_install(): + try: + Popen(["flow"]) + return True + except OSError: + pass + return False + + +flow_installed = pytest.mark.skipif(not flow_install(), reason="Requires flow") + + +def find_version(output): + return re.search(r"flow\s*([\d.]+)", output).group(1) + + +class EclRunTest(ResTest): + def setUp(self): + self.ecl_config_path = os.path.dirname(inspect.getsourcefile(Ecl100Config)) + self.monkeypatch = MonkeyPatch() + + def tearDown(self): + self.monkeypatch.undo() + + def init_ecl100_config(self): + conf = { + "env": { + "F_UFMTENDIAN": "big", + "LM_LICENSE_FILE": "7321@eclipse-lic-no.statoil.no", + "ARCH": "x86_64", + }, + "versions": { + "2014.2": { + "scalar": { + "executable": "/prog/ecl/grid/2014.2/bin/linux_x86_64/eclipse.exe" + }, + "mpi": { + "executable": "/prog/ecl/grid/2014.2/bin/linux_x86_64/eclipse_ilmpi.exe", + "mpirun": "/prog/ecl/grid/tools/linux_x86_64/intel/mpi/5.0.2.044/bin64/mpirun", + "env": { + "I_MPI_ROOT": "/prog/ecl/grid/tools/linux_x86_64/intel/mpi/5.0.2.044/", + "P4_RSHCOMMAND": "ssh", + "LD_LIBRARY_PATH": "/prog/ecl/grid/tools/linux_x86_64/intel/mpi/5.0.2.044/lib64:$LD_LIBRARY_PATH", + "PATH": "/prog/ecl/grid/tools/linux_x86_64/intel/mpi/5.0.2.044/bin64:$PATH", + }, + }, + } + }, + } + with open("ecl100_config.yml", "w") as f: + f.write(yaml.dump(conf)) + self.monkeypatch.setenv("ECL100_SITE_CONFIG", "ecl100_config.yml") + + def init_flow_config(self): + version = "2018.10" + + p = Popen(["flow", "--version"], stdin=PIPE, stdout=PIPE, stderr=PIPE) + output, err = p.communicate() + rc = p.returncode + if rc == 0: + version = find_version(output) + path_to_exe = find_executable("flow") + + conf = { + "default_version": version, + "versions": { + version: { + "scalar": {"executable": path_to_exe}, + } + }, + } + + with open("flow_config.yml", "w") as f: + f.write(yaml.dump(conf)) + self.monkeypatch.setenv("FLOW_SITE_CONFIG", "flow_config.yml") + + def test_make_LSB_MCPU_machine_list(self): + self.assertListEqual( + ["host1", "host1", "host1", "host1", "host2", "host2", "host2", "host2"], + ecl_run.make_LSB_MCPU_machine_list("host1 4 host2 4"), + ) + + @tmpdir() + def test_create(self): + # This test can make do with a mock simulator; - just something executable + + conf = { + "versions": { + "2014.2": { + "scalar": {"executable": "bin/scalar_exe"}, + "mpi": {"executable": "bin/mpi_exe", "mpirun": "bin/mpirun"}, + } + } + } + with open("ecl100_config.yml", "w") as f: + f.write(yaml.dump(conf)) + + os.mkdir("bin") + self.monkeypatch.setenv("ECL100_SITE_CONFIG", "ecl100_config.yml") + for f in ["scalar_exe", "mpi_exe", "mpirun"]: + fname = os.path.join("bin", f) + with open(fname, "w") as fh: + fh.write("This is an exectable ...") + + os.chmod(fname, stat.S_IEXEC) + + with open("ECLIPSE.DATA", "w") as f: + f.write("Mock eclipse data file") + + ecl_config = Ecl100Config() + sim = ecl_config.sim("2014.2") + mpi_sim = ecl_config.mpi_sim("2014.2") + ecl_run = EclRun("ECLIPSE.DATA", sim) + self.assertEqual(ecl_run.runPath(), os.getcwd()) + + os.mkdir("path") + with open("path/ECLIPSE.DATA", "w") as f: + f.write("Mock eclipse data file") + + ecl_run = EclRun("path/ECLIPSE.DATA", sim) + self.assertEqual(ecl_run.runPath(), os.path.join(os.getcwd(), "path")) + self.assertEqual(ecl_run.baseName(), "ECLIPSE") + self.assertEqual(1, ecl_run.numCpu()) + + # invalid number of CPU + with self.assertRaises(ValueError): + ecl_run = EclRun("path/ECLIPSE.DATA", sim, num_cpu="xxx") + + ecl_run = EclRun("path/ECLIPSE.DATA", mpi_sim, num_cpu="10") + self.assertEqual(10, ecl_run.numCpu()) + + # Missing datafile + with self.assertRaises(IOError): + ecl_run = EclRun("DOES/NOT/EXIST", mpi_sim, num_cpu="10") + + @pytest.mark.xfail( + reason="Finding a version on Komodo of flow that is not OPM-flow" + ) + @flow_installed + @tmpdir() + def test_flow(self): + self.init_flow_config() + shutil.copy( + os.path.join(self.SOURCE_ROOT, "test-data/local/eclipse/SPE1.DATA"), + "SPE1.DATA", + ) + shutil.copy( + os.path.join(self.SOURCE_ROOT, "test-data/local/eclipse/SPE1_ERROR.DATA"), + "SPE1_ERROR.DATA", + ) + flow_config = FlowConfig() + sim = flow_config.sim() + flow_run = EclRun("SPE1.DATA", sim) + flow_run.runEclipse() + + run(flow_config, ["SPE1.DATA"]) + + flow_run = EclRun("SPE1_ERROR.DATA", sim) + with self.assertRaises(Exception): + flow_run.runEclipse() + + run(flow_config, ["SPE1_ERROR.DATA", "--ignore-errors"]) + + # Invalid version + with self.assertRaises(Exception): + run(flow_config, ["SPE1.DATA", "--version=no/such/version"]) + + @tmpdir() + def test_running_flow_given_env_config_can_still_read_parent_env(self): + version = "1111.11" + + # create a script that prints env vars ENV1 and ENV2 to a file + with open("flow", "w") as f: + f.write("#!/bin/bash\n") + f.write("echo $ENV1 > out.txt\n") + f.write("echo $ENV2 >> out.txt\n") + executable = os.path.join(os.getcwd(), "flow") + os.chmod(executable, 0o777) + + # create a flow_config.yml with environment extension ENV2 + conf = { + "default_version": version, + "versions": { + version: { + "scalar": {"executable": executable, "env": {"ENV2": "VAL2"}}, + } + }, + } + + with open("flow_config.yml", "w") as f: + f.write(yaml.dump(conf)) + + # set the environment variable ENV1 + self.monkeypatch.setenv("ENV1", "VAL1") + self.monkeypatch.setenv("FLOW_SITE_CONFIG", "flow_config.yml") + + with open("DUMMY.DATA", "w") as f: + f.write("dummy") + + with open("DUMMY.PRT", "w") as f: + f.write("Errors 0\n") + f.write("Bugs 0\n") + + # run the script + flow_config = FlowConfig() + sim = flow_config.sim() + flow_run = EclRun("DUMMY.DATA", sim) + flow_run.runEclipse() + + # assert that the script was able to read both the variables correctly + with open("out.txt") as f: + lines = f.readlines() + + self.assertEqual(len(lines), 2) + self.assertEqual(lines[0].strip(), "VAL1") + self.assertEqual(lines[1].strip(), "VAL2") + + @tmpdir() + def test_running_flow_given_no_env_config_can_still_read_parent_env(self): + version = "1111.11" + + # create a script that prints env vars ENV1 and ENV2 to a file + with open("flow", "w") as f: + f.write("#!/bin/bash\n") + f.write("echo $ENV1 > out.txt\n") + f.write("echo $ENV2 >> out.txt\n") + executable = os.path.join(os.getcwd(), "flow") + os.chmod(executable, 0o777) + + # create a flow_config.yml with environment extension ENV2 + conf = { + "default_version": version, + "versions": { + version: { + "scalar": {"executable": executable}, + } + }, + } + + with open("flow_config.yml", "w") as f: + f.write(yaml.dump(conf)) + + # set the environment variable ENV1 + self.monkeypatch.setenv("ENV1", "VAL1") + self.monkeypatch.setenv("ENV2", "VAL2") + self.monkeypatch.setenv("FLOW_SITE_CONFIG", "flow_config.yml") + + with open("DUMMY.DATA", "w") as f: + f.write("dummy") + + with open("DUMMY.PRT", "w") as f: + f.write("Errors 0\n") + f.write("Bugs 0\n") + + # run the script + flow_config = FlowConfig() + sim = flow_config.sim() + flow_run = EclRun("DUMMY.DATA", sim) + flow_run.runEclipse() + + # assert that the script was able to read both the variables correctly + with open("out.txt") as f: + lines = f.readlines() + + self.assertEqual(len(lines), 2) + self.assertEqual(lines[0].strip(), "VAL1") + self.assertEqual(lines[1].strip(), "VAL2") + + @tmpdir() + def test_running_flow_given_env_variables_with_same_name_as_parent_env_variables_will_overwrite( + self, + ): + version = "1111.11" + + # create a script that prints env vars ENV1 and ENV2 to a file + with open("flow", "w") as f: + f.write("#!/bin/bash\n") + f.write("echo $ENV1 > out.txt\n") + f.write("echo $ENV2 >> out.txt\n") + executable = os.path.join(os.getcwd(), "flow") + os.chmod(executable, 0o777) + + # create a flow_config.yml with environment extension ENV2 + conf = { + "default_version": version, + "versions": { + version: { + "scalar": { + "executable": executable, + "env": {"ENV1": "OVERWRITTEN1", "ENV2": "OVERWRITTEN2"}, + }, + } + }, + } + + with open("flow_config.yml", "w") as f: + f.write(yaml.dump(conf)) + + # set the environment variable ENV1 + self.monkeypatch.setenv("ENV1", "VAL1") + self.monkeypatch.setenv("ENV2", "VAL2") + self.monkeypatch.setenv("FLOW_SITE_CONFIG", "flow_config.yml") + + with open("DUMMY.DATA", "w") as f: + f.write("dummy") + + with open("DUMMY.PRT", "w") as f: + f.write("Errors 0\n") + f.write("Bugs 0\n") + + # run the script + flow_config = FlowConfig() + sim = flow_config.sim() + flow_run = EclRun("DUMMY.DATA", sim) + flow_run.runEclipse() + + # assert that the script was able to read both the variables correctly + with open("out.txt") as f: + lines = f.readlines() + + self.assertEqual(len(lines), 2) + self.assertEqual(lines[0].strip(), "OVERWRITTEN1") + self.assertEqual(lines[1].strip(), "OVERWRITTEN2") + + @tmpdir() + @pytest.mark.equinor_test + def test_run(self): + self.init_ecl100_config() + shutil.copy( + os.path.join(self.SOURCE_ROOT, "test-data/local/eclipse/SPE1.DATA"), + "SPE1.DATA", + ) + ecl_config = Ecl100Config() + sim = ecl_config.sim("2014.2") + ecl_run = EclRun("SPE1.DATA", sim) + ecl_run.runEclipse() + + ok_path = os.path.join(ecl_run.runPath(), "{}.OK".format(ecl_run.baseName())) + log_path = os.path.join(ecl_run.runPath(), "{}.LOG".format(ecl_run.baseName())) + + self.assertTrue(os.path.isfile(ok_path)) + self.assertTrue(os.path.isfile(log_path)) + self.assertTrue(os.path.getsize(log_path) > 0) + + errors = ecl_run.parseErrors() + self.assertEqual(0, len(errors)) + + # Monkey patching the ecl_run to use an executable which + # will fail with exit(1); don't think Eclipse actually + # fails with exit(1) - but let us at least be prepared + # when/if it does. + ecl_run.sim.executable = os.path.join( + self.SOURCE_ROOT, "tests/res/fm/ecl_run_fail" + ) + with self.assertRaises(Exception): + ecl_run.runEclipse() + + @pytest.mark.equinor_test + @tmpdir() + def test_run_api(self): + self.init_ecl100_config() + shutil.copy( + os.path.join(self.SOURCE_ROOT, "test-data/local/eclipse/SPE1.DATA"), + "SPE1.DATA", + ) + ecl_config = Ecl100Config() + run(ecl_config, ["SPE1.DATA", "--version=2014.2"]) + + self.assertTrue(os.path.isfile("SPE1.DATA")) + + @pytest.mark.equinor_test + @tmpdir() + def test_failed_run(self): + self.init_ecl100_config() + shutil.copy( + os.path.join(self.SOURCE_ROOT, "test-data/local/eclipse/SPE1_ERROR.DATA"), + "SPE1_ERROR.DATA", + ) + ecl_config = Ecl100Config() + sim = ecl_config.sim("2014.2") + ecl_run = EclRun("SPE1_ERROR", sim) + with self.assertRaises(Exception): + ecl_run.runEclipse() + try: + ecl_run.runEclipse() + except Exception as e: + self.assertTrue("ERROR" in str(e)) + + @pytest.mark.equinor_test + @tmpdir() + def test_failed_run_OK(self): + self.init_ecl100_config() + shutil.copy( + os.path.join(self.SOURCE_ROOT, "test-data/local/eclipse/SPE1_ERROR.DATA"), + "SPE1_ERROR.DATA", + ) + ecl_config = Ecl100Config() + run(ecl_config, ["SPE1_ERROR", "--version=2014.2", "--ignore-errors"]) + + # Monkey patching the ecl_run to use an executable which will fail with exit(1), + # in the nocheck mode that should also be OK. + sim = ecl_config.sim("2014.2") + ecl_run = EclRun("SPE1_ERROR", sim, check_status=False) + ecl_run.sim.executable = os.path.join( + self.SOURCE_ROOT, "tests/res/fm/ecl_run_fail" + ) + ecl_run.runEclipse() + + @pytest.mark.equinor_test + @tmpdir() + def test_mpi_run(self): + self.init_ecl100_config() + shutil.copy( + os.path.join( + self.SOURCE_ROOT, "test-data/local/eclipse/SPE1_PARALLELL.DATA" + ), + "SPE1_PARALLELL.DATA", + ) + ecl_config = Ecl100Config() + run(ecl_config, ["SPE1_PARALLELL.DATA", "--version=2014.2", "--num-cpu=2"]) + self.assertTrue(os.path.isfile("SPE1_PARALLELL.LOG")) + self.assertTrue(os.path.getsize("SPE1_PARALLELL.LOG") > 0) + + @pytest.mark.equinor_test + @tmpdir() + def test_summary_block(self): + self.init_ecl100_config() + shutil.copy( + os.path.join(self.SOURCE_ROOT, "test-data/local/eclipse/SPE1.DATA"), + "SPE1.DATA", + ) + ecl_config = Ecl100Config() + sim = ecl_config.sim("2014.2") + ecl_run = EclRun("SPE1.DATA", sim) + ret_value = ecl_run.summary_block() + self.assertTrue(ret_value is None) + + ecl_run.runEclipse() + ecl_sum = ecl_run.summary_block() + self.assertTrue(isinstance(ecl_sum, EclSum)) + + @pytest.mark.equinor_test + @tmpdir() + def test_check(self): + full_case = os.path.join( + self.SOURCE_ROOT, "test-data/Equinor/ECLIPSE/Gurbat/ECLIPSE" + ) + short_case = os.path.join( + self.SOURCE_ROOT, "test-data/Equinor/ECLIPSE/ShortSummary/ECLIPSE" + ) + failed_case = os.path.join( + self.SOURCE_ROOT, + "test-data/Equinor/ECLIPSE/SummaryFail/NOR-2013A_R002_1208-0", + ) + + with self.assertRaises(IOError): + self.assertTrue(EclRun.checkCase(full_case, failed_case)) + + with self.assertRaises(IOError): + self.assertTrue(EclRun.checkCase(full_case, "DOES-NOT-EXIST")) + + with self.assertRaises(IOError): + self.assertTrue(EclRun.checkCase("DOES-NOT-EXIST", full_case)) + + with self.assertRaises(ValueError): + EclRun.checkCase(full_case, short_case) + + self.assertTrue(not os.path.isfile("CHECK_ECLIPSE_RUN.OK")) + self.assertTrue(EclRun.checkCase(full_case, full_case)) + self.assertTrue(os.path.isfile("CHECK_ECLIPSE_RUN.OK")) + + os.remove("CHECK_ECLIPSE_RUN.OK") + self.assertTrue( + EclRun.checkCase(short_case, full_case) + ) # Simulation is longer than refcase - OK + self.assertTrue(os.path.isfile("CHECK_ECLIPSE_RUN.OK")) + + @pytest.mark.equinor_test + @tmpdir() + def test_error_parse(self): + self.init_ecl100_config() + shutil.copy( + os.path.join(self.SOURCE_ROOT, "test-data/local/eclipse/SPE1.DATA"), + "SPE1.DATA", + ) + prt_file = os.path.join( + self.SOURCE_ROOT, "test-data/local/eclipse/parse/ERROR.PRT" + ) + shutil.copy(prt_file, "SPE1.PRT") + + ecl_config = Ecl100Config() + sim = ecl_config.sim("2014.2") + ecl_run = EclRun("SPE1.DATA", sim) + + error_list = ecl_run.parseErrors() + self.assertEqual(len(error_list), 2) + + # NB: The ugly white space in the error0 literal is actually part of + # the string we are matching; i.e. it must be retained. + error0 = """ @-- ERROR AT TIME 0.0 DAYS ( 1-JAN-0): + @ UNABLE TO OPEN INCLUDED FILE + @ /private/joaho/ERT/git/Gurbat/XXexample_grid_sim.GRDECL + @ SYSTEM ERROR CODE IS 29 """ + + error1 = """ @-- ERROR AT TIME 0.0 DAYS ( 1-JAN-0): + @ INCLUDE FILES MISSING. """ + + self.assertEqual(error_list[0], error0) + self.assertEqual(error_list[1], error1) + + def test_slurm_env_parsing(self): + host_list = make_SLURM_machine_list("ws", "2") + self.assertEqual(host_list, ["ws", "ws"]) + + host_list = make_SLURM_machine_list("ws1,ws2", "2,3") + self.assertEqual(host_list, ["ws1", "ws1", "ws2", "ws2", "ws2"]) + + host_list = make_SLURM_machine_list("ws[1-3]", "1,2,3") + self.assertEqual(host_list, ["ws1", "ws2", "ws2", "ws3", "ws3", "ws3"]) + + host_list = make_SLURM_machine_list("ws[1,3]", "1,3") + self.assertEqual(host_list, ["ws1", "ws3", "ws3", "ws3"]) + + host_list = make_SLURM_machine_list("ws[1-3,6-8]", "1,2,3,1,2,3") + self.assertEqual( + host_list, + [ + "ws1", + "ws2", + "ws2", + "ws3", + "ws3", + "ws3", + "ws6", + "ws7", + "ws7", + "ws8", + "ws8", + "ws8", + ], + ) + + host_list = make_SLURM_machine_list("ws[1-3,6-8]", "2(x2),3,1,2(x2)") + self.assertEqual( + host_list, + [ + "ws1", + "ws1", + "ws2", + "ws2", + "ws3", + "ws3", + "ws3", + "ws6", + "ws7", + "ws7", + "ws8", + "ws8", + ], + ) + + host_list = make_SLURM_machine_list("ws[1-3,6],ws[7-8]", "2(x2),3,1,2(x2)") + self.assertEqual( + host_list, + [ + "ws1", + "ws1", + "ws2", + "ws2", + "ws3", + "ws3", + "ws3", + "ws6", + "ws7", + "ws7", + "ws8", + "ws8", + ], + ) diff --git a/libres/tests/res/fm/test_ecl_run_new.py b/libres/tests/res/fm/test_ecl_run_new.py new file mode 100644 index 00000000000..cfa63836779 --- /dev/null +++ b/libres/tests/res/fm/test_ecl_run_new.py @@ -0,0 +1,258 @@ +# Copyright (C) 2018 Equinor ASA, Norway. +# +# The file 'test_ecl.py' 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. +import os +import stat +import unittest +import shutil +import functools +import inspect +import yaml +import pytest +import re +import json +from unittest import mock + +from ecl.summary import EclSum +from ecl.util.test import TestAreaContext +from tests import ResTest +from tests.utils import tmpdir +from res.fm.ecl import run, Ecl100Config, EclrunConfig, EclRun +from subprocess import Popen, PIPE +from subprocess import Popen, PIPE +from distutils.spawn import find_executable +from pytest import MonkeyPatch + + +def find_version(output): + return re.search(r"flow\s*([\d.]+)", output).group(1) + + +class EclRunTest(ResTest): + def setUp(self): + self.ecl_config_path = os.path.dirname(inspect.getsourcefile(Ecl100Config)) + self.monkeypatch = MonkeyPatch() + + def tearDown(self): + self.monkeypatch.undo() + + @staticmethod + def _eclrun_conf(): + return { + "eclrun_env": { + "SLBSLS_LICENSE_FILE": "7321@eclipse-lic-no.statoil.no", + "ECLPATH": "/prog/res/ecl/grid", + "PATH": "/prog/res/ecl/grid/macros", + "F_UFMTENDIAN": "big", + "LSB_JOB_ID": None, + } + } + + def init_eclrun_config(self): + conf = EclRunTest._eclrun_conf() + with open("ecl100_config.yml", "w") as f: + f.write(yaml.dump(conf)) + self.monkeypatch.setenv("ECL100_SITE_CONFIG", "ecl100_config.yml") + + @tmpdir() + @mock.patch.dict(os.environ, {"LSB_JOBID": "some-id"}) + def test_env(self): + self.init_eclrun_config() + with open("eclrun", "w") as f, open("DUMMY.DATA", "w"): + f.write( + """#!/usr/bin/env python +import os +import json +with open("env.json", "w") as f: + json.dump(dict(os.environ), f) +""" + ) + os.chmod("eclrun", os.stat("eclrun").st_mode | stat.S_IEXEC) + ecl_config = Ecl100Config() + eclrun_config = EclrunConfig(ecl_config, "2019.3") + ecl_run = EclRun("DUMMY", None, check_status=False) + with mock.patch.object( + ecl_run, "_get_run_command", mock.MagicMock(return_value="./eclrun") + ): + ecl_run.runEclipse(eclrun_config=eclrun_config) + with open("env.json") as f: + run_env = json.load(f) + + eclrun_env = self._eclrun_conf()["eclrun_env"] + for k, v in eclrun_env.items(): + if v is None: + assert k not in run_env + continue + + if k == "PATH": + assert run_env[k].startswith(v) + else: + assert v == run_env[k] + + @tmpdir() + @pytest.mark.equinor_test + def test_run(self): + self.init_eclrun_config() + shutil.copy( + os.path.join(self.SOURCE_ROOT, "test-data/local/eclipse/SPE1.DATA"), + "SPE1.DATA", + ) + ecl_config = Ecl100Config() + + ecl_run = EclRun("SPE1.DATA", None) + ecl_run.runEclipse(eclrun_config=EclrunConfig(ecl_config, "2019.3")) + + ok_path = os.path.join(ecl_run.runPath(), "{}.OK".format(ecl_run.baseName())) + log_path = os.path.join(ecl_run.runPath(), "{}.LOG".format(ecl_run.baseName())) + + self.assertTrue(os.path.isfile(ok_path)) + self.assertTrue(os.path.isfile(log_path)) + self.assertTrue(os.path.getsize(log_path) > 0) + + errors = ecl_run.parseErrors() + self.assertEqual(0, len(errors)) + + @pytest.mark.equinor_test + @tmpdir() + def test_run_api(self): + self.init_eclrun_config() + shutil.copy( + os.path.join(self.SOURCE_ROOT, "test-data/local/eclipse/SPE1.DATA"), + "SPE1.DATA", + ) + ecl_config = Ecl100Config() + run(ecl_config, ["SPE1.DATA", "--version=2019.3"]) + + self.assertTrue(os.path.isfile("SPE1.DATA")) + + @pytest.mark.equinor_test + @tmpdir() + def test_failed_run(self): + self.init_eclrun_config() + shutil.copy( + os.path.join(self.SOURCE_ROOT, "test-data/local/eclipse/SPE1_ERROR.DATA"), + "SPE1_ERROR.DATA", + ) + ecl_config = Ecl100Config() + eclrun_config = EclrunConfig(ecl_config, "2019.3") + ecl_run = EclRun("SPE1_ERROR", None) + with self.assertRaises(Exception) as error_context: + ecl_run.runEclipse(eclrun_config=eclrun_config) + self.assertIn("ERROR", str(error_context.exception)) + + @pytest.mark.equinor_test + @tmpdir() + def test_failed_run_OK(self): + self.init_eclrun_config() + shutil.copy( + os.path.join(self.SOURCE_ROOT, "test-data/local/eclipse/SPE1_ERROR.DATA"), + "SPE1_ERROR.DATA", + ) + ecl_config = Ecl100Config() + run(ecl_config, ["SPE1_ERROR", "--version=2019.3", "--ignore-errors"]) + + @pytest.mark.equinor_test + @tmpdir() + def test_no_hdf5_output_by_default_with_ecl100(self): + self.init_eclrun_config() + shutil.copy( + os.path.join(self.SOURCE_ROOT, "test-data/local/eclipse/SPE1.DATA"), + "SPE1.DATA", + ) + ecl_config = Ecl100Config() + # check that by default .h5 file IS NOT produced + run(ecl_config, ["SPE1.DATA", "--version=2019.3"]) + self.assertFalse(os.path.exists("SPE1.h5")) + + @pytest.mark.equinor_test + @tmpdir() + def test_flag_to_produce_hdf5_output_with_ecl100(self): + self.init_eclrun_config() + shutil.copy( + os.path.join(self.SOURCE_ROOT, "test-data/local/eclipse/SPE1.DATA"), + "SPE1.DATA", + ) + ecl_config = Ecl100Config() + # check that with flag .h5 file IS produced + run(ecl_config, ["SPE1.DATA", "--version=2019.3", "--summary-conversion"]) + self.assertTrue(os.path.exists("SPE1.h5")) + + @pytest.mark.equinor_test + @tmpdir() + def test_mpi_run(self): + self.init_eclrun_config() + shutil.copy( + os.path.join( + self.SOURCE_ROOT, "test-data/local/eclipse/SPE1_PARALLELL.DATA" + ), + "SPE1_PARALLELL.DATA", + ) + ecl_config = Ecl100Config() + run(ecl_config, ["SPE1_PARALLELL.DATA", "--version=2019.3", "--num-cpu=2"]) + self.assertTrue(os.path.isfile("SPE1_PARALLELL.LOG")) + self.assertTrue(os.path.getsize("SPE1_PARALLELL.LOG") > 0) + + @pytest.mark.equinor_test + @tmpdir() + def test_summary_block(self): + self.init_eclrun_config() + shutil.copy( + os.path.join(self.SOURCE_ROOT, "test-data/local/eclipse/SPE1.DATA"), + "SPE1.DATA", + ) + ecl_config = Ecl100Config() + ecl_run = EclRun("SPE1.DATA", None) + ret_value = ecl_run.summary_block() + self.assertTrue(ret_value is None) + + ecl_run.runEclipse(eclrun_config=EclrunConfig(ecl_config, "2019.3")) + ecl_sum = ecl_run.summary_block() + self.assertTrue(isinstance(ecl_sum, EclSum)) + + @pytest.mark.equinor_test + @tmpdir() + def test_check(self): + full_case = os.path.join( + self.SOURCE_ROOT, "test-data/Equinor/ECLIPSE/Gurbat/ECLIPSE" + ) + short_case = os.path.join( + self.SOURCE_ROOT, "test-data/Equinor/ECLIPSE/ShortSummary/ECLIPSE" + ) + failed_case = os.path.join( + self.SOURCE_ROOT, + "test-data/Equinor/ECLIPSE/SummaryFail/NOR-2013A_R002_1208-0", + ) + + with self.assertRaises(IOError): + self.assertTrue(EclRun.checkCase(full_case, failed_case)) + + with self.assertRaises(IOError): + self.assertTrue(EclRun.checkCase(full_case, "DOES-NOT-EXIST")) + + with self.assertRaises(IOError): + self.assertTrue(EclRun.checkCase("DOES-NOT-EXIST", full_case)) + + with self.assertRaises(ValueError): + EclRun.checkCase(full_case, short_case) + + self.assertTrue(not os.path.isfile("CHECK_ECLIPSE_RUN.OK")) + self.assertTrue(EclRun.checkCase(full_case, full_case)) + self.assertTrue(os.path.isfile("CHECK_ECLIPSE_RUN.OK")) + + os.remove("CHECK_ECLIPSE_RUN.OK") + self.assertTrue( + EclRun.checkCase(short_case, full_case) + ) # Simulation is longer than refcase - OK + self.assertTrue(os.path.isfile("CHECK_ECLIPSE_RUN.OK")) diff --git a/libres/tests/res/fm/test_fm_config.py b/libres/tests/res/fm/test_fm_config.py new file mode 100644 index 00000000000..f352e72844d --- /dev/null +++ b/libres/tests/res/fm/test_fm_config.py @@ -0,0 +1,43 @@ +import os +import unittest +from tests import ResTest + + +class TestFMValidity(ResTest): + def setUp(self): + pass + + def _extract_executable(self, filename): + with open(filename, "r") as f: + for line in f.readlines(): + l = line.strip().split() + if len(l) > 1 and l[0] == "EXECUTABLE": + return l[1] + return None + + def _file_exist_and_is_executable(self, file_path): + return os.path.isfile(file_path) and os.access(file_path, os.X_OK) + + def test_validate_scripts(self): + fm_path = self.SHARE_ROOT / "ert" / "forward-models" + for fm_dir in os.listdir(fm_path): + fm_dir = os.path.join(fm_path, fm_dir) + # get all sub-folder in forward-models + if os.path.isdir(fm_dir): + files = os.listdir(fm_dir) + for fn in files: + fn = os.path.join(fm_dir, fn) + # get all files in sub-folders + if os.path.isfile(fn): + # extract executable (if any) + executable_script = self._extract_executable(fn) + if executable_script is not None: + self.assertTrue( + self._file_exist_and_is_executable( + os.path.join(fm_dir, executable_script) + ) + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/libres/tests/res/fm/test_rms_config.py b/libres/tests/res/fm/test_rms_config.py new file mode 100644 index 00000000000..4d2ae3a5b38 --- /dev/null +++ b/libres/tests/res/fm/test_rms_config.py @@ -0,0 +1,110 @@ +# Copyright (C) 2018 Equinor ASA, Norway. +# +# The file 'test_ecl.py' 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. +import os +import stat +import unittest +import yaml +from ecl.util.test import TestAreaContext +from tests import ResTest +from pytest import MonkeyPatch + +from res.fm.rms import RMSConfig + + +class RMSConfigTest(ResTest): + def setUp(self): + self.monkeypatch = MonkeyPatch() + pass + + def tearDown(self): + self.monkeypatch.undo() + + def test_load(self): + self.monkeypatch.setenv("RMS_SITE_CONFIG", "file/does/not/exist") + with self.assertRaises(IOError): + conf = RMSConfig() + + self.monkeypatch.setenv("RMS_SITE_CONFIG", RMSConfig.DEFAULT_CONFIG_FILE) + conf = RMSConfig() + + with self.assertRaises(OSError): + exe = conf.executable + + with TestAreaContext("yaml"): + with open("file.yml", "w") as f: + f.write("this:\n -should\n-be\ninvalid:yaml?") + + self.monkeypatch.setenv("RMS_SITE_CONFIG", "file.yml") + with self.assertRaises(ValueError): + conf = RMSConfig() + + os.mkdir("bin") + with open("bin/rms", "w") as f: + f.write("This is an RMS executable ...") + os.chmod("bin/rms", stat.S_IEXEC) + + with open("file.yml", "w") as f: + f.write("executable: bin/rms") + + conf = RMSConfig() + self.assertEqual(conf.executable, "bin/rms") + self.assertIsNone(conf.threads) + + with open("file.yml", "w") as f: + f.write("executable: bin/rms\n") + f.write("threads: 17") + + conf = RMSConfig() + self.assertEqual(conf.threads, 17) + + with open("file.yml", "w") as f: + f.write("executable: bin/rms\n") + f.write("wrapper: not-exisiting-exec") + + conf = RMSConfig() + + with self.assertRaises(OSError): + conf.wrapper + + with open("file.yml", "w") as f: + f.write("executable: bin/rms\n") + f.write("wrapper: bash") + + conf = RMSConfig() + self.assertEqual(conf.wrapper, "bash") + + def test_load_env(self): + with TestAreaContext("yaml"): + self.monkeypatch.setenv("RMS_SITE_CONFIG", "file.yml") + with open("file.yml", "w") as f: + f.write( + """\ +executable: bin/rms\n +wrapper: bash +env: + 10.1.3: + PATH_PREFIX: /some/path + PYTHONPATH: /some/pythonpath +""" + ) + conf = RMSConfig() + self.assertEqual(conf.env("10.1.3")["PATH_PREFIX"], "/some/path") + self.assertEqual(conf.env("10.1.3")["PYTHONPATH"], "/some/pythonpath") + self.assertEqual(conf.env("non_existing"), {}) + + +if __name__ == "__main__": + unittest.main() diff --git a/libres/tests/res/fm/test_rms_run.py b/libres/tests/res/fm/test_rms_run.py new file mode 100644 index 00000000000..752daaa6d0b --- /dev/null +++ b/libres/tests/res/fm/test_rms_run.py @@ -0,0 +1,693 @@ +# Copyright (C) 2018 Equinor ASA, Norway. +# +# The file 'test_rms.py' 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. +import os +import sys +import stat +import subprocess +import unittest +from unittest.mock import patch +import yaml +import shutil +import json +from ecl.util.test import TestAreaContext +from tests import ResTest +from pytest import MonkeyPatch + +from res.fm.rms import RMSRun, RMSRunException +import res.fm.rms + +from tests.utils import tmpdir +import pytest + + +TEST_ENV_WRAPPER = """\ +#!/usr/bin/env bash +PATH_PREFIX_EXPECTED={expected_path_prefix} +if [[ $PATH_PREFIX != $PATH_PREFIX_EXPECTED ]] +then + echo "PATH_PREFIX set incorrectly" + echo $PATH_PREFIX should be $PATH_PREFIX_EXPECTED + exit 1 +fi +PYPATH_EXPECTED={expected_pythonpath} +if [[ $PYTHONPATH != $PYPATH_EXPECTED ]] # first user defined, then config defined, then rest +then + echo "PYTHONPATH set incorrectly" + echo $PYTHONPATH should be $PYPATH_EXPECTED + exit 1 +fi +$@ +""" + + +def _mocked_run(**kwargs): + print(kwargs) + + +@tmpdir() +@pytest.mark.parametrize( + "test_input,expected_result", + [ + (0, 422851785), + (1, 723121249), + (2, 132312123), + ], +) +def test_run_class_multi_seed( + tmpdir, monkeypatch, test_input, expected_result, source_root +): + with open("rms_config.yml", "w") as f: + f.write("executable: {}/bin/rms".format(os.getcwd())) + + os.mkdir("test_run_multi_seed") + os.mkdir("run_path") + os.mkdir("bin") + os.mkdir("project") + shutil.copy(os.path.join(source_root, "tests/res/fm/rms"), "bin") + monkeypatch.setenv("RMS_SITE_CONFIG", "rms_config.yml") + + action = {"exit_status": 0} + with open("run_path/action.json", "w") as f: + f.write(json.dumps(action)) + + seed_file = ["3", "422851785", "723121249", "132312123"] + with open("run_path/random.seeds", "w") as f: + f.write("\n".join(seed_file)) + + r = RMSRun(test_input, "project", "workflow", run_path="run_path") + assert r.seed == expected_result + + +class RMSRunTest(ResTest): + def setUp(self): + self.monkeypatch = MonkeyPatch() + pass + + def tearDown(self): + self.monkeypatch.undo() + + def test_create(self): + with self.assertRaises(OSError): + r = RMSRun(0, "/project/does/not/exist", "workflow") + + with TestAreaContext("test_create"): + os.mkdir("rms") + r = RMSRun(0, "rms", "workflow") + + def test_run_class(self): + with TestAreaContext("test_run"): + with open("rms_config.yml", "w") as f: + f.write("executable: {}/bin/rms".format(os.getcwd())) + + os.mkdir("run_path") + os.mkdir("bin") + os.mkdir("project") + shutil.copy(os.path.join(self.SOURCE_ROOT, "tests/res/fm/rms"), "bin") + self.monkeypatch.setenv("RMS_SITE_CONFIG", "rms_config.yml") + + action = {"exit_status": 0} + with open("run_path/action.json", "w") as f: + f.write(json.dumps(action)) + + r = RMSRun(0, "project", "workflow", run_path="run_path", allow_no_env=True) + r.run() + + # ----------------------------------------------------------------- + + action = {"exit_status": 1} + with open("run_path/action.json", "w") as f: + f.write(json.dumps(action)) + + r = RMSRun(0, "project", "workflow", run_path="run_path", allow_no_env=True) + with self.assertRaises(RMSRunException): + r.run() + + # ----------------------------------------------------------------- + + action = {"exit_status": 0} + with open("run_path/action.json", "w") as f: + f.write(json.dumps(action)) + + r = RMSRun( + 0, + "project", + "workflow", + run_path="run_path", + target_file="some_file", + allow_no_env=True, + ) + with self.assertRaises(RMSRunException): + r.run() + + # ----------------------------------------------------------------- + + action = { + "exit_status": 0, + "target_file": os.path.join(os.getcwd(), "some_file"), + } + with open("run_path/action.json", "w") as f: + f.write(json.dumps(action)) + + r = RMSRun( + 0, + "project", + "workflow", + run_path="run_path", + target_file="some_file", + allow_no_env=True, + ) + r.run() + + def test_run(self): + with TestAreaContext("test_run"): + with open("rms_config.yml", "w") as f: + f.write("executable: {}/bin/rms".format(os.getcwd())) + + os.mkdir("run_path") + os.mkdir("bin") + os.mkdir("project") + shutil.copy(os.path.join(self.SOURCE_ROOT, "tests/res/fm/rms"), "bin") + self.monkeypatch.setenv("RMS_SITE_CONFIG", "rms_config.yml") + + action = {"exit_status": 0} + with open("run_path/action.json", "w") as f: + f.write(json.dumps(action)) + + res.fm.rms.run( + 0, "project", "workflow", run_path="run_path", allow_no_env=True + ) + + # ----------------------------------------------------------------- + + action = {"exit_status": 1} + with open("run_path/action.json", "w") as f: + f.write(json.dumps(action)) + + with self.assertRaises(RMSRunException): + res.fm.rms.run( + 0, "project", "workflow", run_path="run_path", allow_no_env=True + ) + + # ----------------------------------------------------------------- + + action = {"exit_status": 0} + with open("run_path/action.json", "w") as f: + f.write(json.dumps(action)) + + with self.assertRaises(RMSRunException): + res.fm.rms.run( + 0, + "project", + "workflow", + run_path="run_path", + target_file="some_file", + allow_no_env=True, + ) + + # ----------------------------------------------------------------- + + action = { + "exit_status": 0, + "target_file": os.path.join(os.getcwd(), "some_file"), + } + + with open("run_path/action.json", "w") as f: + f.write(json.dumps(action)) + res.fm.rms.run( + 0, + "project", + "workflow", + run_path="run_path", + target_file="some_file", + allow_no_env=True, + ) + + def test_rms_load_env(self): + test_bed = [ + (" ", False), + ("", False), + (None, False), + ("SOME_VAL", True), + ] + for val, carry_over in test_bed: + with TestAreaContext("test_drop_path"): + # Setup RMS project + with open("rms_config.yml", "w") as f: + json.dump( + { + "executable": os.path.realpath("bin/rms"), + }, + f, + ) + + with open("rms_exec_env.json", "w") as f: + json.dump( + { + "RMS_TEST_VAR": val, + }, + f, + ) + + os.mkdir("run_path") + os.mkdir("bin") + os.mkdir("project") + shutil.copy(os.path.join(self.SOURCE_ROOT, "tests/res/fm/rms"), "bin") + self.monkeypatch.setenv("RMS_SITE_CONFIG", "rms_config.yml") + + action = {"exit_status": 0} + with open("run_path/action.json", "w") as f: + f.write(json.dumps(action)) + + rms_exec = os.path.join( + self.SHARE_ROOT, "ert/forward-models/res/script/rms" + ) + subprocess.check_call( + [ + rms_exec, + "--run-path", + "run_path", + "0", + "--version", + "10.4", + "project", + "--import-path", + "./", + "--export-path", + "./", + "workflow", + "-a", + ] + ) + + with open("run_path/env.json") as f: + env = json.load(f) + + if carry_over: + self.assertIn("RMS_TEST_VAR", env) + else: + self.assertNotIn("RMS_TEST_VAR", env) + + def test_rms_drop_env(self): + test_bed = [ + (" ", False), + ("", False), + (None, False), + ("SOME_VAL", True), + ] + for val, carry_over in test_bed: + with TestAreaContext("test_drop_path"): + # Setup RMS project + with open("rms_config.yml", "w") as f: + json.dump( + { + "executable": os.path.realpath("bin/rms"), + }, + f, + ) + + with open("rms_exec_env.json", "w") as f: + json.dump( + { + "RMS_TEST_VAR": val, + }, + f, + ) + self.monkeypatch.setenv("RMS_TEST_VAR", "fdsgfdgfdsgfds") + + os.mkdir("run_path") + os.mkdir("bin") + os.mkdir("project") + shutil.copy(os.path.join(self.SOURCE_ROOT, "tests/res/fm/rms"), "bin") + self.monkeypatch.setenv("RMS_SITE_CONFIG", "rms_config.yml") + + action = {"exit_status": 0} + with open("run_path/action.json", "w") as f: + f.write(json.dumps(action)) + + rms_exec = os.path.join( + self.SHARE_ROOT, "ert/forward-models/res/script/rms" + ) + subprocess.check_call( + [ + rms_exec, + "--run-path", + "run_path", + "0", + "--version", + "10.4", + "project", + "--import-path", + "./", + "--export-path", + "./", + "workflow", + "-a", + ] + ) + + with open("run_path/env.json") as f: + env = json.load(f) + + if carry_over: + self.assertIn("RMS_TEST_VAR", env) + else: + self.assertNotIn("RMS_TEST_VAR", env) + + def test_run_class_with_existing_target_file(self): + with TestAreaContext("test_run_existing_target"): + with open("rms_config.yml", "w") as f: + f.write("executable: {}/bin/rms".format(os.getcwd())) + + os.mkdir("run_path") + os.mkdir("bin") + os.mkdir("project") + shutil.copy(os.path.join(self.SOURCE_ROOT, "tests/res/fm/rms"), "bin") + self.monkeypatch.setenv("RMS_SITE_CONFIG", "rms_config.yml") + + target_file = os.path.join(os.getcwd(), "rms_target_file") + action = { + "exit_status": 0, + "target_file": target_file, + } + with open("run_path/action.json", "w") as f: + f.write(json.dumps(action)) + + with open(target_file, "w") as f: + f.write("This is a dummy target file") + + r = RMSRun( + 0, + "project", + "workflow", + run_path="run_path", + target_file=target_file, + allow_no_env=True, + ) + r.run() + + def test_run_wrapper(self): + with TestAreaContext("test_run"): + wrapper_file_name = f"{os.getcwd()}/bin/rms_wrapper" + with open("rms_config.yml", "w") as f: + f.write("executable: {}/bin/rms\n".format(os.getcwd())) + f.write(f"wrapper: {wrapper_file_name}") + + os.mkdir("run_path") + os.mkdir("bin") + os.mkdir("project") + shutil.copy(os.path.join(self.SOURCE_ROOT, "tests/res/fm/rms"), "bin") + + with open(wrapper_file_name, "w") as f: + f.write("#!/bin/bash\n") + f.write("exec ${@:1}") + st = os.stat(wrapper_file_name) + os.chmod(wrapper_file_name, st.st_mode | stat.S_IEXEC) + self.monkeypatch.setenv("RMS_SITE_CONFIG", "rms_config.yml") + self.monkeypatch.setenv("PATH", f"{os.getcwd()}/bin:{os.environ['PATH']}") + + action = {"exit_status": 0} + with open("run_path/action.json", "w") as f: + f.write(json.dumps(action)) + + res.fm.rms.run( + 0, "project", "workflow", run_path="run_path", allow_no_env=True + ) + + # ----------------------------------------------------------------- + + action = {"exit_status": 1} + with open("run_path/action.json", "w") as f: + f.write(json.dumps(action)) + + with self.assertRaises(RMSRunException): + res.fm.rms.run( + 0, "project", "workflow", run_path="run_path", allow_no_env=True + ) + + # ----------------------------------------------------------------- + + action = {"exit_status": 0} + with open("run_path/action.json", "w") as f: + f.write(json.dumps(action)) + + with self.assertRaises(RMSRunException): + res.fm.rms.run( + 0, + "project", + "workflow", + run_path="run_path", + target_file="some_file", + allow_no_env=True, + ) + + # ----------------------------------------------------------------- + + action = { + "exit_status": 0, + "target_file": os.path.join(os.getcwd(), "some_file"), + } + + with open("run_path/action.json", "w") as f: + f.write(json.dumps(action)) + res.fm.rms.run( + 0, + "project", + "workflow", + run_path="run_path", + target_file="some_file", + allow_no_env=True, + ) + + def test_run_version_env(self): + with TestAreaContext("test_run"): + wrapper_file_name = f"{os.getcwd()}/bin/rms_wrapper" + with open("rms_config.yml", "w") as f: + f.write( + f"""\ +executable: {os.getcwd()}/bin/rms +wrapper: {wrapper_file_name} +env: + 10.1.3: + PATH_PREFIX: /some/path + PYTHONPATH: /some/pythonpath +""" + ) + + os.mkdir("run_path") + os.mkdir("bin") + os.mkdir("project") + shutil.copy(os.path.join(self.SOURCE_ROOT, "tests/res/fm/rms"), "bin") + + with open(wrapper_file_name, "w") as f: + f.write( + TEST_ENV_WRAPPER.format( + expected_path_prefix="/some/path", + expected_pythonpath="/some/pythonpath", + ) + ) + + st = os.stat(wrapper_file_name) + os.chmod(wrapper_file_name, st.st_mode | stat.S_IEXEC) + self.monkeypatch.setenv("RMS_SITE_CONFIG", "rms_config.yml") + self.monkeypatch.setenv("PATH", f"{os.getcwd()}/bin:{os.environ['PATH']}") + + action = { + "exit_status": 0, + "target_file": os.path.join(os.getcwd(), "some_file"), + } + + with open("run_path/action.json", "w") as f: + f.write(json.dumps(action)) + res.fm.rms.run( + 0, + "project", + "workflow", + run_path="run_path", + target_file="some_file", + version="10.1.3", + ) + + def test_run_version_env_with_user_env(self): + with TestAreaContext("test_run"): + wrapper_file_name = f"{os.getcwd()}/bin/rms_wrapper" + with open("rms_config.yml", "w") as f: + f.write( + f"""\ +executable: {os.getcwd()}/bin/rms +wrapper: {wrapper_file_name} +env: + 10.1.3: + PATH_PREFIX: /some/path + PYTHONPATH: /some/pythonpath +""" + ) + + os.mkdir("run_path") + os.mkdir("bin") + os.mkdir("project") + shutil.copy(os.path.join(self.SOURCE_ROOT, "tests/res/fm/rms"), "bin") + + with open(wrapper_file_name, "w") as f: + f.write( + TEST_ENV_WRAPPER.format( + expected_path_prefix="/some/other/path:/some/path", + expected_pythonpath="/some/other/pythonpath:/some/pythonpath", + ) + ) + with open("rms_exec_env.json", "w") as f: + f.write( + """\ +{ + "PATH_PREFIX" : "/some/other/path", + "PYTHONPATH" : "/some/other/pythonpath" +} +""" + ) + + st = os.stat(wrapper_file_name) + os.chmod(wrapper_file_name, st.st_mode | stat.S_IEXEC) + self.monkeypatch.setenv("RMS_SITE_CONFIG", "rms_config.yml") + self.monkeypatch.setenv("PATH", f"{os.getcwd()}/bin:{os.environ['PATH']}") + + with patch.object(sys, "argv", ["rms"]): + action = { + "exit_status": 0, + "target_file": os.path.join(os.getcwd(), "some_file"), + } + + with open("run_path/action.json", "w") as f: + f.write(json.dumps(action)) + res.fm.rms.run( + 0, + "project", + "workflow", + run_path="run_path", + target_file="some_file", + version="10.1.3", + ) + + def test_run_allow_no_env(self): + with TestAreaContext("test_run"): + wrapper_file_name = f"{os.getcwd()}/bin/rms_wrapper" + with open("rms_config.yml", "w") as f: + f.write( + f"""\ +executable: {os.getcwd()}/bin/rms +env: + 10.1.3: + PATH_PREFIX: /some/path + PYTHONPATH: /some/pythonpath +""" + ) + + os.mkdir("run_path") + os.mkdir("bin") + os.mkdir("project") + shutil.copy(os.path.join(self.SOURCE_ROOT, "tests/res/fm/rms"), "bin") + + self.monkeypatch.setenv("RMS_SITE_CONFIG", "rms_config.yml") + self.monkeypatch.setenv("PATH", f"{os.getcwd()}/bin:{os.environ['PATH']}") + action = { + "exit_status": 0, + "target_file": os.path.join(os.getcwd(), "some_file"), + } + + with open("run_path/action.json", "w") as f: + f.write(json.dumps(action)) + + with self.assertRaises(RMSRunException) as e: + res.fm.rms.run( + 0, + "project", + "workflow", + run_path="run_path", + target_file="some_file", + version="non-existing", + ) + assert "non-existing" in str(e) + + res.fm.rms.run( + 0, + "project", + "workflow", + run_path="run_path", + target_file="some_file", + version="non-existing", + allow_no_env=True, + ) + + def test_rms_job_script_parser(self): + with TestAreaContext("test_run"): + # Setup RMS project + with open("rms_config.yml", "w") as f: + json.dump( + { + "executable": os.path.realpath("bin/rms"), + "env": {"10.1.3": {"PATH": ""}}, + }, + f, + ) + + self.monkeypatch.setenv("RMS_TEST_VAR", "fdsgfdgfdsgfds") + + os.mkdir("run_path") + os.mkdir("bin") + os.mkdir("project") + shutil.copy(os.path.join(self.SOURCE_ROOT, "tests/res/fm/rms"), "bin") + self.monkeypatch.setenv("RMS_SITE_CONFIG", "rms_config.yml") + + action = {"exit_status": 0} + with open("run_path/action.json", "w") as f: + f.write(json.dumps(action)) + + rms_exec = os.path.join( + self.SHARE_ROOT, "ert/forward-models/res/script/rms" + ) + subprocess.check_call( + [ + rms_exec, + "--run-path", + "run_path", + "0", + "--version", + "10.1.3", + "project", + "--import-path", + "./", + "--export-path", + "./", + "workflow", + "", + ] + ) + + subprocess.check_call( + [ + rms_exec, + "--run-path", + "run_path", + "0", + "--version", + "10.1.3", + "project", + "workflow", + "-a", + ] + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/libres/tests/res/fm/test_shell.py b/libres/tests/res/fm/test_shell.py new file mode 100644 index 00000000000..98f70f42f01 --- /dev/null +++ b/libres/tests/res/fm/test_shell.py @@ -0,0 +1,302 @@ +import unittest +import os +import os.path +import contextlib + +from ecl.util.test import TestAreaContext +from tests import ResTest +from res.fm.shell import * +from pytest import MonkeyPatch + + +@contextlib.contextmanager +def pushd(path): + cwd0 = os.getcwd() + os.chdir(path) + + yield + + os.chdir(cwd0) + + +class ShellTest(ResTest): + def setUp(self): + self.monkeypatch = MonkeyPatch() + + def tearDown(self): + self.monkeypatch.undo() + + def test_symlink(self): + with self.assertRaises(IOError): + symlink("target/does/not/exist", "link") + + with TestAreaContext("symlink/test"): + with open("target", "w") as fileH: + fileH.write("target ...") + + symlink("target", "link") + self.assertTrue(os.path.islink("link")) + self.assertEqual(os.readlink("link"), "target") + + with open("target2", "w") as fileH: + fileH.write("target ...") + + with self.assertRaises(OSError): + symlink("target2", "target") + + symlink("target2", "link") + self.assertTrue(os.path.islink("link")) + self.assertEqual(os.readlink("link"), "target2") + + os.makedirs("root1/sub1/sub2") + os.makedirs("root2/sub1/sub2") + os.makedirs("run") + + symlink("../target", "linkpath/link") + self.assertTrue(os.path.isdir("linkpath")) + self.assertTrue(os.path.islink("linkpath/link")) + + symlink("../target", "linkpath/link") + self.assertTrue(os.path.isdir("linkpath")) + self.assertTrue(os.path.islink("linkpath/link")) + + with TestAreaContext("symlink/test2"): + os.makedirs("path") + with open("path/target", "w") as f: + f.write("1234") + + symlink("path/target", "link") + self.assertTrue(os.path.islink("link")) + self.assertTrue(os.path.isfile("path/target")) + + symlink("path/target", "link") + self.assertTrue(os.path.islink("link")) + self.assertTrue(os.path.isfile("path/target")) + with open("link") as f: + s = f.read() + self.assertEqual(s, "1234") + + def test_mkdir(self): + with TestAreaContext("shell/mkdir"): + with open("file", "w") as f: + f.write("Hei") + + with self.assertRaises(OSError): + mkdir("file") + + mkdir("path") + self.assertTrue(os.path.isdir("path")) + mkdir("path") + + mkdir("path/subpath") + self.assertTrue(os.path.isdir("path/subpath")) + + def test_move_file(self): + with TestAreaContext("shell/move_file"): + with open("file", "w") as f: + f.write("Hei") + + move_file("file", "file2") + self.assertTrue(os.path.isfile("file2")) + self.assertFalse(os.path.isfile("file")) + + with self.assertRaises(IOError): + move_file("file2", "path/file2") + + mkdir("path") + move_file("file2", "path/file2") + self.assertTrue(os.path.isfile("path/file2")) + self.assertFalse(os.path.isfile("file2")) + + with self.assertRaises(IOError): + move_file("path", "path2") + + with self.assertRaises(IOError): + move_file("not_existing", "target") + + with open("file2", "w") as f: + f.write("123") + + move_file("file2", "path/file2") + self.assertTrue(os.path.isfile("path/file2")) + self.assertFalse(os.path.isfile("file2")) + + mkdir("rms/ipl") + with open("global_variables.ipl", "w") as f: + f.write("123") + + move_file("global_variables.ipl", "rms/ipl/global_variables.ipl") + + def test_move_file_into_folder_file_exists(self): + with TestAreaContext("shell/test_move_file_into_folder_file_exists"): + mkdir("dst_folder") + with open("dst_folder/file", "w") as f: + f.write("old") + + with open("file", "w") as f: + f.write("new") + + with open("dst_folder/file", "r") as f: + content = f.read() + self.assertEqual(content, "old") + + move_file("file", "dst_folder") + with open("dst_folder/file", "r") as f: + content = f.read() + self.assertEqual(content, "new") + + self.assertFalse(os.path.exists("file")) + + def test_move_pathfile_into_folder(self): + with TestAreaContext("shell/test_move_pathfile"): + mkdir("dst_folder") + mkdir("source1/source2/") + with open("source1/source2/file", "w") as f: + f.write("stuff") + + move_file("source1/source2/file", "dst_folder") + with open("dst_folder/file", "r") as f: + content = f.read() + self.assertEqual(content, "stuff") + + self.assertFalse(os.path.exists("source1/source2/file")) + + def test_move_pathfile_into_folder_file_exists(self): + with TestAreaContext("shell/test_move_pathfile"): + mkdir("dst_folder") + mkdir("source1/source2/") + with open("source1/source2/file", "w") as f: + f.write("stuff") + + with open("dst_folder/file", "w") as f: + f.write("garbage") + + move_file("source1/source2/file", "dst_folder") + with open("dst_folder/file", "r") as f: + content = f.read() + self.assertEqual(content, "stuff") + + self.assertFalse(os.path.exists("source1/source2/file")) + + def test_delete_file(self): + with TestAreaContext("delete/file"): + mkdir("pathx") + with self.assertRaises(IOError): + delete_file("pathx") + + # deleteFile which does not exist - is silently ignored + delete_file("does/not/exist") + + with open("file", "w") as f: + f.write("hei") + symlink("file", "link") + self.assertTrue(os.path.islink("link")) + + delete_file("file") + self.assertFalse(os.path.isfile("file")) + self.assertTrue(os.path.islink("link")) + delete_file("link") + self.assertFalse(os.path.islink("link")) + + def test_delete_directory(self): + with TestAreaContext("delete/directory"): + # deleteDriecteory which does not exist - is silently ignored + delete_directory("does/not/exist") + + with open("file", "w") as f: + f.write("hei") + + with self.assertRaises(IOError): + delete_directory("file") + + mkdir("link_target/subpath") + with open("link_target/link_file", "w") as f: + f.write("hei") + + mkdir("path/subpath") + with open("path/file", "w") as f: + f.write("hei") + + with open("path/subpath/file", "w") as f: + f.write("hei") + + symlink("../link_target", "path/link") + delete_directory("path") + self.assertFalse(os.path.exists("path")) + self.assertTrue(os.path.exists("link_target/link_file")) + + def test_copy_directory_error(self): + with self.assertRaises(IOError): + copy_directory("does/not/exist", "target") + + with TestAreaContext("copy/directory"): + with open("file", "w") as f: + f.write("hei") + + with self.assertRaises(IOError): + copy_directory("hei", "target") + + def test_copy_file(self): + with TestAreaContext("copy/file"): + with self.assertRaises(IOError): + copy_file("does/not/exist", "target") + + mkdir("path") + with self.assertRaises(IOError): + copy_file("path", "target") + + with open("file1", "w") as f: + f.write("hei") + + copy_file("file1", "file2") + self.assertTrue(os.path.isfile("file2")) + + copy_file("file1", "path") + self.assertTrue(os.path.isfile("path/file1")) + + copy_file("file1", "path2/file1") + self.assertTrue(os.path.isfile("path2/file1")) + + with TestAreaContext("copy/file2"): + mkdir("root/sub/path") + + with open("file", "w") as f: + f.write("Hei ...") + + copy_file("file", "root/sub/path/file") + self.assertTrue(os.path.isfile("root/sub/path/file")) + + with open("file2", "w") as f: + f.write("Hei ...") + + with pushd("root/sub/path"): + copy_file("../../../file2") + self.assertTrue(os.path.isfile("file2")) + + def test_copy_file2(self): + with TestAreaContext("copy/file2"): + mkdir("rms/output") + + with open("file.txt", "w") as f: + f.write("Hei") + + copy_file("file.txt", "rms/output/") + self.assertTrue(os.path.isfile("rms/output/file.txt")) + + def test_careful_copy_file(self): + with TestAreaContext("careful/copy/file"): + with open("file1", "w") as f: + f.write("hei") + with open("file2", "w") as f: + f.write("hallo") + + careful_copy_file("file1", "file2") + with open("file2", "r") as f: + self.assertEqual("hallo", f.readline()) + + careful_copy_file("file1", "file3") + self.assertTrue(os.path.isfile("file3")) + + +if __name__ == "__main__": + unittest.main() diff --git a/libres/tests/res/fm/test_templating.py b/libres/tests/res/fm/test_templating.py new file mode 100644 index 00000000000..db8c1800957 --- /dev/null +++ b/libres/tests/res/fm/test_templating.py @@ -0,0 +1,225 @@ +from ecl.util.test import TestAreaContext +from tests import ResTest +from tests.utils import tmpdir +from res.fm.templating import * + +import jinja2 + +import subprocess +import os +import json +import yaml + + +class TemplatingTest(ResTest): + well_drill_tmpl = ( + 'PROD1 takes value {{ well_drill.PROD1 }}, implying {{ "on" if well_drill.PROD1 >= 0.5 else "off" }}\n' + 'PROD2 takes value {{ well_drill.PROD2 }}, implying {{ "on" if well_drill.PROD2 >= 0.5 else "off" }}\n' + "---------------------------------- \n" + "{%- for well in well_drill.INJ %}\n" + '{{ well.name }} takes value {{ well.value|round(1) }}, implying {{ "on" if well.value >= 0.5 else "off"}}\n' + "{%- endfor %}" + ) + + optimal_template = "{{well_drill.values() | sum()}}" + dual_input = "{{ well_drill_north.PROD1 }} vs {{ well_drill_south.PROD1 }}" + + mulitple_input_template = ( + "FILENAME\n" + + "F1 {{parameters.key1.subkey1}}\n" + + "OTH {{second.key1.subkey2}}\n" + + "OTH_TEST {{third.key1.subkey1}}" + ) + + mulitple_input_template_no_param = ( + "FILENAME\n" + + "F1 {{not_the_standard_parameters.key1.subkey1}}\n" + + "OTH {{second.key1.subkey2}}\n" + + "OTH_TEST {{third.key1.subkey1}}" + ) + + default_parameters = { + "key1": {"subkey1": 1999.22, "subkey2": 200}, + "key2": {"subkey1": 300}, + } + + @tmpdir() + def test_render_invalid(self): + with TestAreaContext("templating") as tac: + + prod_wells = {"PROD%d" % idx: 0.3 * idx for idx in range(4)} + prod_in = "well_drill.json" + with open(prod_in, "w") as fout: + json.dump(prod_wells, fout) + with open("parameters.json", "w") as fout: + json.dump(self.default_parameters, fout) + with open("template_file", "w") as fout: + fout.write(self.well_drill_tmpl) + + wells_out = "wells.out" + + # undefined template elements + with self.assertRaises(jinja2.exceptions.UndefinedError): + render_template(None, "template_file", wells_out) + + # file not found + with self.assertRaises(ValueError): + render_template(2 * prod_in, "template_file", wells_out) + + # no template file + with self.assertRaises(TypeError): + render_template(prod_in, None, wells_out) + + # templatefile not found + with self.assertRaises(ValueError): + render_template(prod_in, "template_file" + "nogo", wells_out) + + # no output file + with self.assertRaises(TypeError): + render_template(prod_in, "template_file", None) + + @tmpdir() + def test_render(self): + + wells = {"PROD%d" % idx: 0.2 * idx for idx in range(1, 5)} + wells.update( + { + "INJ": [ + {"name": "INJ{0}".format(idx), "value": 1 - 0.2 * idx} + for idx in range(1, 5) + ] + } + ) + wells_in = "well_drill.json" + wells_tmpl = "well_drill_tmpl" + wells_out = "wells.out" + + with open(wells_in, "w") as fout: + json.dump(wells, fout) + with open("parameters.json", "w") as fout: + json.dump(self.default_parameters, fout) + with open(wells_tmpl, "w") as fout: + fout.write(self.well_drill_tmpl) + + render_template(wells_in, wells_tmpl, wells_out) + self.maxDiff = None + expected_template_out = [ + "PROD1 takes value 0.2, implying off\n", + "PROD2 takes value 0.4, implying off\n", + "----------------------------------\n", + "INJ1 takes value 0.8, implying on\n", + "INJ2 takes value 0.6, implying on\n", + "INJ3 takes value 0.4, implying off\n", + "INJ4 takes value 0.2, implying off", + ] + + with open(wells_out) as fin: + output = fin.readlines() + + self.assertEqual(expected_template_out, output) + + @tmpdir() + def test_template_multiple_input(self): + with open("template", "w") as template_file: + template_file.write(self.mulitple_input_template) + + with open("parameters.json", "w") as json_file: + json_file.write(json.dumps(self.default_parameters)) + + with open("second.json", "w") as json_file: + parameters = {"key1": {"subkey2": 1400}} + json.dump(parameters, json_file) + with open("third.json", "w") as json_file: + parameters = { + "key1": { + "subkey1": 3000.22, + } + } + json.dump(parameters, json_file) + + render_template(["second.json", "third.json"], "template", "out_file") + + with open("out_file", "r") as parameter_file: + expected_output = ( + "FILENAME\n" + "F1 1999.22\n" + "OTH 1400\n" + "OTH_TEST 3000.22" + ) + + self.assertEqual(parameter_file.read(), expected_output) + + @tmpdir() + def test_no_parameters_json(self): + with open("template", "w") as template_file: + template_file.write(self.mulitple_input_template_no_param) + + with open("not_the_standard_parameters.json", "w") as json_file: + json_file.write(json.dumps(self.default_parameters)) + + with open("second.json", "w") as json_file: + parameters = {"key1": {"subkey2": 1400}} + json.dump(parameters, json_file) + with open("third.json", "w") as json_file: + parameters = { + "key1": { + "subkey1": 3000.22, + } + } + json.dump(parameters, json_file) + + render_template( + ["second.json", "third.json", "not_the_standard_parameters.json"], + "template", + "out_file", + ) + + with open("out_file", "r") as parameter_file: + expected_output = ( + "FILENAME\n" + "F1 1999.22\n" + "OTH 1400\n" + "OTH_TEST 3000.22" + ) + + self.assertEqual(parameter_file.read(), expected_output) + + @tmpdir() + def test_template_executable(self): + with TestAreaContext("templating") as tac: + with open("template", "w") as template_file: + template_file.write( + "FILENAME\n" + + "F1 {{parameters.key1.subkey1}}\n" + + "F2 {{other.key1.subkey1}}" + ) + + with open("parameters.json", "w") as json_file: + json_file.write(json.dumps(self.default_parameters)) + + with open("other.json", "w") as json_file: + parameters = { + "key1": { + "subkey1": 200, + } + } + json_file.write(json.dumps(parameters)) + + params = " --output_file out_file --template_file template --input_files other.json" + template_render_exec = os.path.join( + self.SHARE_ROOT, + "ert/forward-models/templating/script/template_render", + ) + + subprocess.call( + template_render_exec + params, shell=True, stdout=subprocess.PIPE + ) + + with open("out_file", "r") as parameter_file: + expected_output = "FILENAME\n" + "F1 1999.22\n" + "F2 200" + self.assertEqual(parameter_file.read(), expected_output) + + @tmpdir() + def test_load_parameters(self): + + with TestAreaContext("templating") as tac: + with open("parameters.json", "w") as json_file: + json_file.write(json.dumps(self.default_parameters)) + + input_parameters = load_parameters() + + self.assertEqual(input_parameters, self.default_parameters) diff --git a/libres/tests/res/job_queue/__init__.py b/libres/tests/res/job_queue/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/tests/res/job_queue/test_equinor_jobmanager.py b/libres/tests/res/job_queue/test_equinor_jobmanager.py new file mode 100644 index 00000000000..171272244c5 --- /dev/null +++ b/libres/tests/res/job_queue/test_equinor_jobmanager.py @@ -0,0 +1,79 @@ +import json +import socket +import os +import os.path +import pytest + +from tests import ResTest + +# Test data generated by ForwardModel +JSON_STRING = """ +{ + "umask" : "0000", + "jobList" : [ {"name" : "PERLIN", + "executable" : "perlin.py", + "target_file" : "my_target_file", + "error_file" : "error_file", + "start_file" : "some_start_file", + "stdout" : "perlin.stdoit", + "stderr" : "perlin.stderr", + "stdin" : "intput4thewin", + "argList" : ["-speed","hyper"], + "environment" : {"TARGET" : "flatland"}, + "license_path" : "this/is/my/license/PERLIN", + "max_running_minutes" : 12, + "max_running" : 30 +}, +{"name" : "PERGEN", + "executable" : "pergen.py", + "target_file" : "my_target_file", + "error_file" : "error_file", + "start_file" : "some_start_file", + "stdout" : "perlin.stdoit", + "stderr" : "perlin.stderr", + "stdin" : "intput4thewin", + "argList" : ["-speed","hyper"], + "environment" : {"TARGET" : "flatland"}, + "license_path" : "this/is/my/license/PERGEN", + "max_running_minutes" : 12, + "max_running" : 30 +}] +} +""" + + +def gen_area_name(base, f): + return base + "_" + f.func_name.split("_")[-1] + + +def create_jobs_py(jobList): + jobs_file = os.path.join(os.getcwd(), "jobs.py") + compiled_jobs_file = jobs_file + "c" + + for fname in [jobs_file, compiled_jobs_file]: + if os.path.isfile(fname): + os.unlink(fname) + + with open(jobs_file, "w") as f: + f.write("jobList = ") + f.write(json.dumps(jobList, indent=1)) + f.write("\n") + + return jobs_file + + +def create_jobs_json(jobList, umask="0000"): + data = {"umask": umask, "jobList": jobList} + + jobs_file = os.path.join(os.getcwd(), "jobs.json") + with open(jobs_file, "w") as f: + f.write(json.dumps(data), indent=1) + + +@pytest.mark.equinor_test +class JobManagerEquinorTest(ResTest): + def assert_ip_address(self, ip): + try: + socket.inet_aton(ip) + except Exception as err: + self.assertTrue(False, msg="On input %s: %s." % (ip, err)) # noqa diff --git a/libres/tests/res/job_queue/test_ert_plugin.py b/libres/tests/res/job_queue/test_ert_plugin.py new file mode 100644 index 00000000000..605f69f850c --- /dev/null +++ b/libres/tests/res/job_queue/test_ert_plugin.py @@ -0,0 +1,83 @@ +from res.enkf import ErtPlugin, CancelPluginException +from tests import ResTest + + +class SimplePlugin(ErtPlugin): + def run(self, parameter1, parameter2): + assert parameter1 == "one" + assert parameter2 == 2 + + def getArguments(self, parent=None): + return ["one", 2] + + +class FullPlugin(ErtPlugin): + def getName(self): + return "FullPlugin" + + def getDescription(self): + return "Fully described!" + + def run(self, arg1, arg2, arg3=None): + assert arg1 == 5 + assert arg2 == "No" + assert arg3 is None + + def getArguments(self, parent=None): + return [5, "No"] + + +class CanceledPlugin(ErtPlugin): + def run(self, arg1): + pass + + def getArguments(self, parent=None): + raise CancelPluginException("Cancel test!") + + +# class GUIPlugin(ErtPlugin): +# def getArguments(self, parent=None): +# value1 = QInputDialog.getInt(parent, "Enter a number!", "Enter a nice number (nothing else than 5):", value=0, min=0, max=10) +# value2 = QInputDialog.getInt(parent, "Enter a number!", "Enter a nice number (nothing else than 6):", value=0, min=0, max=10) +# print(value1, value2) +# return [value1[0], value2[0]] +# +# def run(self, arg1, arg2, arg3=None): +# assert arg1 == 5 +# assert arg2 == 6 + + +class ErtPluginTest(ResTest): + def test_simple_ert_plugin(self): + + simple_plugin = SimplePlugin("ert") + + arguments = simple_plugin.getArguments() + + self.assertTrue("SimplePlugin" in simple_plugin.getName()) + self.assertEqual("No description provided!", simple_plugin.getDescription()) + + simple_plugin.initializeAndRun([str, int], arguments) + + def test_full_ert_plugin(self): + plugin = FullPlugin("ert") + + self.assertEqual(plugin.getName(), "FullPlugin") + self.assertEqual(plugin.getDescription(), "Fully described!") + + arguments = plugin.getArguments() + + plugin.initializeAndRun([int, str, float], arguments) + + def test_cancel_plugin(self): + plugin = CanceledPlugin("ert") + + with self.assertRaises(CancelPluginException): + plugin.getArguments() + + # def test_gui_ert_plugin(self): + # app = QApplication([]) + # plugin = GUIPlugin("ert") + # + # arguments = plugin.getArguments() + # plugin.initializeAndRun([int, int], arguments) diff --git a/libres/tests/res/job_queue/test_ert_script.py b/libres/tests/res/job_queue/test_ert_script.py new file mode 100644 index 00000000000..f1fd732de40 --- /dev/null +++ b/libres/tests/res/job_queue/test_ert_script.py @@ -0,0 +1,79 @@ +from res.job_queue import ErtScript +from ecl.util.test import TestAreaContext +from tests import ResTest +from .workflow_common import WorkflowCommon + + +class ReturnErtScript(ErtScript): + def run(self): + return self.ert() + + +class AddScript(ErtScript): + def run(self, arg1, arg2): + return arg1 + arg2 + + +class FailScript(ErtScript): + def rum(self): + pass + + +class NoneScript(ErtScript): + def run(self, arg): + assert arg is None + + +class ErtScriptTest(ResTest): + @staticmethod + def createScripts(): + WorkflowCommon.createErtScriptsJob() + + with open("syntax_error_script.py", "w") as f: + f.write("from res.enkf not_legal_syntax ErtScript\n") + + with open("import_error_script.py", "w") as f: + f.write("from res.enkf import DoesNotExist\n") + + with open("empty_script.py", "w") as f: + f.write("from res.enkf import ErtScript\n") + + def test_ert_script_return_ert(self): + script = ReturnErtScript("ert") + result = script.initializeAndRun([], []) + self.assertEqual(result, "ert") + + def test_ert_script_add(self): + script = AddScript("ert") + + result = script.initializeAndRun([int, int], ["5", "4"]) + + self.assertEqual(result, 9) + + with self.assertRaises(ValueError): + result = script.initializeAndRun([int, int], ["5", "4.6"]) + + def test_ert_script_failed_implementation(self): + with self.assertRaises(UserWarning): + script = FailScript("ert") + + def test_ert_script_from_file(self): + with TestAreaContext("python/job_queue/ert_script") as work_area: + ErtScriptTest.createScripts() + + script_object = ErtScript.loadScriptFromFile("subtract_script.py") + + script = script_object("ert") + result = script.initializeAndRun([int, int], ["1", "2"]) + self.assertEqual(result, -1) + + # with self.assertRaises(ErtScriptError): + self.assertIsNone(ErtScript.loadScriptFromFile("syntax_error_script.py")) + self.assertIsNone(ErtScript.loadScriptFromFile("import_error_script.py")) + self.assertIsNone(ErtScript.loadScriptFromFile("empty_script.py")) + + def test_none_ert_script(self): + # Check if None is not converted to string "None" + script = NoneScript("ert") + + script.initializeAndRun([str], [None]) diff --git a/libres/tests/res/job_queue/test_ext_job.py b/libres/tests/res/job_queue/test_ext_job.py new file mode 100644 index 00000000000..dbd79da5227 --- /dev/null +++ b/libres/tests/res/job_queue/test_ext_job.py @@ -0,0 +1,137 @@ +import os.path + +from ecl.util.test import TestAreaContext +from tests import ResTest +from res.job_queue.ext_job import ExtJob +from res.config import ContentTypeEnum + + +def create_valid_config(config_file): + with open(config_file, "w") as f: + f.write("STDOUT null\n") + f.write("STDERR null\n") + f.write("EXECUTABLE script.sh\n") + + with open("script.sh", "w") as f: + f.write("This is a script") + + +def create_upgraded_valid_config(config_file): + with open(config_file, "w") as f: + f.write("EXECUTABLE script.sh\n") + f.write("MIN_ARG 2\n") + f.write("MAX_ARG 7\n") + f.write("ARG_TYPE 0 INT\n") + f.write("ARG_TYPE 1 FLOAT\n") + f.write("ARG_TYPE 2 STRING\n") + f.write("ARG_TYPE 3 BOOL\n") + f.write("ARG_TYPE 4 RUNTIME_FILE\n") + f.write("ARG_TYPE 5 RUNTIME_INT\n") + + with open("script.sh", "w") as f: + f.write("This is a script") + + +def create_config_missing_executable(config_file): + with open(config_file, "w") as f: + f.write("EXECUTABLE missing_script.sh\n") + + +def create_config_missing_EXECUTABLE(config_file): + with open(config_file, "w") as f: + f.write("EXECU missing_script.sh\n") + + +def create_config_executable_directory(config_file): + with open(config_file, "w") as f: + f.write("EXECUTABLE /tmp\n") + + +def create_config_foreign_file(config_file): + with open(config_file, "w") as f: + f.write("EXECUTABLE /etc/passwd\n") + + +class ExtJobTest(ResTest): + def test_load_forward_model(self): + with self.assertRaises(IOError): + job = ExtJob("CONFIG_FILE", True) + + with TestAreaContext("python/job_queue/forward_model1"): + create_valid_config("CONFIG") + job = ExtJob("CONFIG", True) + self.assertEqual(job.name(), "CONFIG") + self.assertEqual(job.get_stdout_file(), None) + self.assertEqual(job.get_stderr_file(), None) + + self.assertEqual( + job.get_executable(), os.path.join(os.getcwd(), "script.sh") + ) + self.assertTrue(os.access(job.get_executable(), os.X_OK)) + + self.assertEqual(job.min_arg, -1) + + job = ExtJob("CONFIG", True, name="Job") + self.assertEqual(job.name(), "Job") + pfx = "ExtJob(" + self.assertEqual(pfx, repr(job)[: len(pfx)]) + + with TestAreaContext("python/job_queue/forward_model1a"): + create_upgraded_valid_config("CONFIG") + job = ExtJob("CONFIG", True) + self.assertEqual(job.min_arg, 2) + self.assertEqual(job.max_arg, 7) + argTypes = job.arg_types + self.assertEqual( + argTypes, + [ + ContentTypeEnum.CONFIG_INT, + ContentTypeEnum.CONFIG_FLOAT, + ContentTypeEnum.CONFIG_STRING, + ContentTypeEnum.CONFIG_BOOL, + ContentTypeEnum.CONFIG_RUNTIME_FILE, + ContentTypeEnum.CONFIG_RUNTIME_INT, + ContentTypeEnum.CONFIG_STRING, + ], + ) + + with TestAreaContext("python/job_queue/forward_model2"): + create_config_missing_executable("CONFIG") + with self.assertRaises(ValueError): + job = ExtJob("CONFIG", True) + + with TestAreaContext("python/job_queue/forward_model3"): + create_config_missing_EXECUTABLE("CONFIG") + with self.assertRaises(ValueError): + job = ExtJob("CONFIG", True) + + with TestAreaContext("python/job_queue/forward_model4"): + create_config_executable_directory("CONFIG") + with self.assertRaises(ValueError): + job = ExtJob("CONFIG", True) + + with TestAreaContext("python/job_queue/forward_model5"): + create_config_foreign_file("CONFIG") + with self.assertRaises(ValueError): + job = ExtJob("CONFIG", True) + + def test_valid_args(self): + arg_types = [ + ContentTypeEnum.CONFIG_FLOAT, + ContentTypeEnum.CONFIG_INT, + ContentTypeEnum.CONFIG_BOOL, + ContentTypeEnum.CONFIG_STRING, + ] + run_arg_types = [ + ContentTypeEnum.CONFIG_RUNTIME_INT, + ContentTypeEnum.CONFIG_RUNTIME_INT, + ] + + arg_list = ["5.6", "65", "True", "car"] + self.assertTrue(ExtJob.valid_args(arg_types, arg_list)) + arg_list2 = ["True", "True", "8", "car"] + self.assertFalse(ExtJob.valid_args(arg_types, arg_list2)) + + run_arg_list = ["Trjue", "76"] + self.assertTrue(ExtJob.valid_args(run_arg_types, run_arg_list)) + self.assertFalse(ExtJob.valid_args(run_arg_types, run_arg_list, True)) diff --git a/libres/tests/res/job_queue/test_forward_model_formatted_print.py b/libres/tests/res/job_queue/test_forward_model_formatted_print.py new file mode 100644 index 00000000000..7211238fab8 --- /dev/null +++ b/libres/tests/res/job_queue/test_forward_model_formatted_print.py @@ -0,0 +1,511 @@ +import os.path +import datetime +import json + +from ecl.util.test import TestAreaContext +from ecl.util.util import EclVersion, Version +from tests import ResTest +from res.util.substitution_list import SubstitutionList +from res.job_queue.environment_varlist import EnvironmentVarlist +from res.job_queue.forward_model import ForwardModel +from res.job_queue.forward_model_status import ForwardModelStatus +from res.job_queue.ext_job import ExtJob +from res.job_queue.ext_joblist import ExtJoblist + +# +# Data for testing +# +joblist = [ + { + "name": "PERLIN", + "executable": "perlin.py", + "target_file": "my_target_file", + "error_file": "error_file", + "start_file": "some_start_file", + "stdout": "perlin.stdout", + "stderr": "perlin.stderr", + "stdin": "input4thewin", + "argList": ["-speed", "hyper"], + "environment": {"TARGET": "flatland"}, + "license_path": "this/is/my/license/PERLIN", + "max_running_minutes": 12, + "max_running": 30, + }, + { + "name": "AGGREGATOR", + "executable": "aggregator.py", + "target_file": "target", + "error_file": "None", + "start_file": "eple", + "stdout": "aggregator.stdout", + "stderr": "aggregator.stderr", + "stdin": "illgiveyousome", + "argList": ["-o"], + "environment": {"STATE": "awesome"}, + "license_path": "I/will/pay/ya/tomorrow/AGGREGATOR", + "max_running_minutes": 1, + "max_running": 14, + }, + { + "name": "PI", + "executable": "pi.py", + "target_file": "my_target_file", + "error_file": "error_file", + "start_file": "some_start_file", + "stdout": "pi.stdout", + "stderr": "pi.stderr", + "stdin": "input4thewin", + "argList": ["-p", "8"], + "environment": {"LOCATION": "earth"}, + "license_path": "license/PI", + "max_running_minutes": 12, + "max_running": 30, + }, + { + "name": "OPTIMUS", + "executable": "optimus.py", + "target_file": "target", + "error_file": "None", + "start_file": "eple", + "stdout": "optimus.stdout", + "stderr": "optimus.stderr", + "stdin": "illgiveyousome", + "argList": ["-help"], + "environment": {"PATH": "/ubertools/4.1"}, + "license_path": "license/OPTIMUS", + "max_running_minutes": 1, + "max_running": 14, + }, +] + +# +# Keywords for the ext_job initialization file +# +ext_job_keywords = [ + "MAX_RUNNING", + "STDIN", + "STDOUT", + "STDERR", + "EXECUTABLE", + "TARGET_FILE", + "ERROR_FILE", + "START_FILE", + "ARGLIST", + "ENV", + "MAX_RUNNING_MINUTES", +] + +# +# JSON keywords +# +json_keywords = [ + "name", + "executable", + "target_file", + "error_file", + "start_file", + "stdout", + "stderr", + "stdin", + "license_path", + "max_running_minutes", + "max_running", + "argList", + "environment", +] + + +def str_none_sensitive(x): + return str(x) if x is not None else None + + +DEFAULT_NAME = "default_job_name" + + +def _generate_job( + name, + executable, + target_file, + error_file, + start_file, + stdout, + stderr, + stdin, + environment, + arglist, + max_running_minutes, + max_running, + license_root_path, + private, +): + config_file = DEFAULT_NAME if name is None else name + environment = ( + None + if environment is None + else list(environment.keys()) + list(environment.values()) + ) + + values = [ + str_none_sensitive(max_running), + stdin, + stdout, + stderr, + executable, + target_file, + error_file, + start_file, + None if arglist is None else " ".join(arglist), + None if environment is None else " ".join(environment), + str_none_sensitive(max_running_minutes), + ] + + conf = open(config_file, "w") + for key, val in zip(ext_job_keywords, values): + if val is not None: + conf.write("%s %s\n" % (key, val)) + conf.close() + + exec_file = open(executable, "w") + exec_file.close() + + ext_job = ExtJob(config_file, private, name, license_root_path) + os.unlink(config_file) + os.unlink(executable) + + return ext_job + + +def empty_list_if_none(l): + return [] if l is None else l + + +def default_name_if_none(name): + return DEFAULT_NAME if name is None else name + + +def get_license_root_path(license_path): + return os.path.split(license_path)[0] + + +def dump_config_to_terminal(): + print("############ JSON ####################") + with open("jobs.json", "r") as f: + print(f.read()) + + print("############ PY ######################") + with open("jobs.py", "r") as f: + print(f.read()) + + print("######################################") + + +def load_configs(config_file): + with open(config_file, "r") as cf: + jobs = json.load(cf) + + return jobs + + +def create_std_file(config, std="stdout", job_index=None): + if job_index is None: + if config[std]: + return "{}".format(config[std]) + else: + return "{}.{}".format(config["name"], std) + else: + if config[std]: + return "{}.{}".format(config[std], job_index) + else: + return "{}.{}.{}".format(config["name"], std, job_index) + + +class ForwardModelFormattedPrintTest(ResTest): + + JOBS_JSON_FILE = "jobs.json" + + @classmethod + def setUpClass(cls): + # Make all executable paths absolute + for job in joblist: + if not os.path.isabs(job["executable"]): + job["executable"] = os.path.join(os.getcwd(), job["executable"]) + + def validate_ext_job(self, ext_job, ext_job_config): + zero_if_none = lambda x: 0 if x is None else x + + self.assertEqual(ext_job.name(), default_name_if_none(ext_job_config["name"])) + self.assertEqual(ext_job.get_executable(), ext_job_config["executable"]) + self.assertEqual(ext_job.get_target_file(), ext_job_config["target_file"]) + self.assertEqual(ext_job.get_error_file(), ext_job_config["error_file"]) + self.assertEqual(ext_job.get_start_file(), ext_job_config["start_file"]) + self.assertEqual( + ext_job.get_stdout_file(), create_std_file(ext_job_config, std="stdout") + ) + self.assertEqual( + ext_job.get_stderr_file(), create_std_file(ext_job_config, std="stderr") + ) + self.assertEqual(ext_job.get_stdin_file(), ext_job_config["stdin"]) + self.assertEqual( + ext_job.get_max_running_minutes(), + zero_if_none(ext_job_config["max_running_minutes"]), + ) + self.assertEqual( + ext_job.get_max_running(), zero_if_none(ext_job_config["max_running"]) + ) + self.assertEqual(ext_job.get_license_path(), ext_job_config["license_path"]) + self.assertEqual( + ext_job.get_arglist(), empty_list_if_none(ext_job_config["argList"]) + ) + if ext_job_config["environment"] is None: + self.assertTrue(len(ext_job.get_environment().keys()) == 0) + else: + self.assertEqual( + ext_job.get_environment().keys(), ext_job_config["environment"].keys() + ) + for key in ext_job_config["environment"].keys(): + self.assertEqual( + ext_job.get_environment()[key], ext_job_config["environment"][key] + ) + + def generate_job_from_dict(self, ext_job_config, private=True): + ext_job = _generate_job( + ext_job_config["name"], + ext_job_config["executable"], + ext_job_config["target_file"], + ext_job_config["error_file"], + ext_job_config["start_file"], + ext_job_config["stdout"], + ext_job_config["stderr"], + ext_job_config["stdin"], + ext_job_config["environment"], + ext_job_config["argList"], + ext_job_config["max_running_minutes"], + ext_job_config["max_running"], + get_license_root_path(ext_job_config["license_path"]), + private, + ) + + self.validate_ext_job(ext_job, ext_job_config) + return ext_job + + def set_up_forward_model(self, selected_jobs=None): + if selected_jobs is None: + selected_jobs = range(len(joblist)) + jobs = [self.generate_job_from_dict(job) for job in joblist] + + ext_joblist = ExtJoblist() + for job in jobs: + ext_joblist.add_job(job.name(), job) + + forward_model = ForwardModel(ext_joblist) + for index in selected_jobs: + forward_model.add_job(jobs[index].name()) + + return forward_model + + def verify_json_dump(self, selected_jobs, global_args, umask, run_id): + self.assertTrue(os.path.isfile(self.JOBS_JSON_FILE)) + config = load_configs(self.JOBS_JSON_FILE) + + self.assertEqual(run_id, config["run_id"]) + self.assertEqual(umask, int(config["umask"], 8)) + self.assertEqual(len(selected_jobs), len(config["jobList"])) + + for job_index in range(len(selected_jobs)): + job = joblist[selected_jobs[job_index]] + loaded_job = config["jobList"][job_index] + + # Since no argList is loaded as an empty list by ext_job + arg_list_back_up = job["argList"] + job["argList"] = empty_list_if_none(job["argList"]) + + # Since name is set to default if none provided by ext_job + name_back_up = job["name"] + job["name"] = default_name_if_none(job["name"]) + + for key in json_keywords: + if key in ["stdout", "stderr"]: + self.assertEqual( + create_std_file(job, std=key, job_index=job_index), + loaded_job[key], + ) + else: + self.assertEqual(job[key], loaded_job[key]) + + job["argList"] = arg_list_back_up + job["name"] = name_back_up + + def test_no_jobs(self): + with TestAreaContext("python/job_queue/forward_model_no_jobs"): + forward_model = self.set_up_forward_model([]) + run_id = "test_no_jobs_id" + umask = 4 + global_args = SubstitutionList() + varlist = EnvironmentVarlist() + forward_model.formatted_fprintf( + run_id, os.getcwd(), "data_root", global_args, umask, varlist + ) + + self.verify_json_dump([], global_args, umask, run_id) + + def test_transfer_arg_types(self): + with TestAreaContext("python/job_queue/forward_model_transfer_arg_types"): + with open("FWD_MODEL", "w") as f: + f.write("EXECUTABLE ls\n") + f.write("MIN_ARG 2\n") + f.write("MAX_ARG 6\n") + f.write("ARG_TYPE 0 INT\n") + f.write("ARG_TYPE 1 FLOAT\n") + f.write("ARG_TYPE 2 STRING\n") + f.write("ARG_TYPE 3 BOOL\n") + f.write("ARG_TYPE 4 RUNTIME_FILE\n") + f.write("ARG_TYPE 5 RUNTIME_INT\n") + f.write("ENV KEY1 VALUE2\n") + f.write("ENV KEY2 VALUE2\n") + + job = ExtJob("FWD_MODEL", True) + + ext_joblist = ExtJoblist() + ext_joblist.add_job(job.name(), job) + forward_model = ForwardModel(ext_joblist) + forward_model.add_job("FWD_MODEL") + + run_id = "test_no_jobs_id" + umask = 4 + global_args = SubstitutionList() + + forward_model.formatted_fprintf( + run_id, + os.getcwd(), + "data_root", + global_args, + umask, + EnvironmentVarlist(), + ) + config = load_configs(self.JOBS_JSON_FILE) + printed_job = config["jobList"][0] + self.assertEqual(printed_job["min_arg"], 2) + self.assertEqual(printed_job["max_arg"], 6) + self.assertEqual( + printed_job["arg_types"], + ["INT", "FLOAT", "STRING", "BOOL", "RUNTIME_FILE", "RUNTIME_INT"], + ) + + def test_env_varlist(self): + varlist_string = "global_environment" + update_string = "global_update_path" + first = "FIRST" + second = "SECOND" + third = "THIRD" + first_value = "TheFirstValue" + second_value = "TheSecondValue" + third_value = "$FIRST:$SECOND" + third_value_correct = "%s:%s" % (first_value, second_value) + varlist = EnvironmentVarlist() + varlist[first] = first_value + varlist[second] = second_value + varlist[third] = third_value + self.assertEqual(len(varlist), 3) + with TestAreaContext("python/job_queue/env_varlist"): + forward_model = self.set_up_forward_model([]) + run_id = "test_no_jobs_id" + umask = 4 + global_args = SubstitutionList() + forward_model.formatted_fprintf( + run_id, os.getcwd(), "data_root", global_args, umask, varlist + ) + config = load_configs(self.JOBS_JSON_FILE) + env_config = config[varlist_string] + self.assertEqual(first_value, env_config[first]) + self.assertEqual(second_value, env_config[second]) + self.assertEqual(third_value_correct, env_config[third]) + update_config = config[update_string] + + def test_repr(self): + with TestAreaContext("python/job_queue/forward_model_one_job"): + forward_model = self.set_up_forward_model() + self.assertTrue(repr(forward_model).startswith("ForwardModel")) + + def test_one_job(self): + with TestAreaContext("python/job_queue/forward_model_one_job"): + for i in range(len(joblist)): + forward_model = self.set_up_forward_model([i]) + run_id = "test_one_job" + umask = 11 + global_args = SubstitutionList() + varlist = EnvironmentVarlist() + forward_model.formatted_fprintf( + run_id, os.getcwd(), "data_root", global_args, umask, varlist + ) + + self.verify_json_dump([i], global_args, umask, run_id) + + def run_all(self): + forward_model = self.set_up_forward_model(range(len(joblist))) + umask = 0 + run_id = "run_all" + global_args = SubstitutionList() + varlist = EnvironmentVarlist() + forward_model.formatted_fprintf( + run_id, os.getcwd(), "data_root", global_args, umask, varlist + ) + + self.verify_json_dump(range(len(joblist)), global_args, umask, run_id) + + def test_all_jobs(self): + with TestAreaContext("python/job_queue/forward_model_all"): + self.run_all() + + def test_name_none(self): + with TestAreaContext("python/job_queue/forward_model_no_name"): + name_back_up = joblist[0]["name"] + license_path_back_up = joblist[0]["license_path"] + + joblist[0]["name"] = None + joblist[0]["license_path"] = os.path.join( + get_license_root_path(joblist[0]["license_path"]), DEFAULT_NAME + ) + + self.run_all() + + joblist[0]["name"] = name_back_up + joblist[0]["license_path"] = license_path_back_up + + def test_various_null_fields(self): + for key in [ + "target_file", + "error_file", + "start_file", + "stdout", + "stderr", + "max_running_minutes", + "argList", + "environment", + "stdin", + ]: + with TestAreaContext("python/job_queue/forward_model_none_" + key): + back_up = joblist[0][key] + joblist[0][key] = None + self.run_all() + joblist[0][key] = back_up + + def test_status_file(self): + with TestAreaContext("status_json"): + forward_model = self.set_up_forward_model() + run_id = "test_no_jobs_id" + umask = 4 + global_args = SubstitutionList() + varlist = EnvironmentVarlist() + forward_model.formatted_fprintf( + run_id, os.getcwd(), "data_root", global_args, umask, varlist + ) + + s = '{"start_time": null, "jobs": [{"status": "Success", "start_time": 1519653419.0, "end_time": 1519653419.0, "name": "SQUARE_PARAMS", "error": null, "current_memory_usage": 2000, "max_memory_usage": 3000}], "end_time": null, "run_id": ""}' + + with open("status.json", "w") as f: + f.write(s) + + status = ForwardModelStatus.try_load("") + for job in status.jobs: + self.assertTrue(isinstance(job.start_time, datetime.datetime)) + self.assertTrue(isinstance(job.end_time, datetime.datetime)) diff --git a/libres/tests/res/job_queue/test_function_ert_script.py b/libres/tests/res/job_queue/test_function_ert_script.py new file mode 100644 index 00000000000..3cae3f41e4a --- /dev/null +++ b/libres/tests/res/job_queue/test_function_ert_script.py @@ -0,0 +1,30 @@ +from cwrap import clib +from ecl.util.test import TestAreaContext +from res.job_queue import WorkflowJob +from .workflow_common import WorkflowCommon +from tests import ResTest + + +class FunctionErtScriptTest(ResTest): + def test_compare(self): + with TestAreaContext("python/job_queue/workflow_job") as work_area: + WorkflowCommon.createInternalFunctionJob() + + parser = WorkflowJob.configParser() + with self.assertRaises(IOError): + workflow_job = WorkflowJob.fromFile("no/such/file") + + workflow_job = WorkflowJob.fromFile( + "compare_job", name="COMPARE", parser=parser + ) + self.assertEqual(workflow_job.name(), "COMPARE") + + result = workflow_job.run(None, ["String", "string"]) + self.assertNotEqual(result, 0) + + result = workflow_job.run(None, ["String", "String"]) + # result is returned as c_void_p -> automatic conversion to None if value is 0 + self.assertIsNone(result) + + workflow_job = WorkflowJob.fromFile("compare_job") + self.assertEqual(workflow_job.name(), "compare_job") diff --git a/libres/tests/res/job_queue/test_job_queue.py b/libres/tests/res/job_queue/test_job_queue.py new file mode 100644 index 00000000000..c2c8da9beb0 --- /dev/null +++ b/libres/tests/res/job_queue/test_job_queue.py @@ -0,0 +1,262 @@ +from res.job_queue import JobStatusType, Driver, QueueDriverEnum, JobQueue, JobQueueNode +from tests import ResTest +from tests.utils import wait_until +from ecl.util.test import TestAreaContext +import os, stat, time, pathlib, json +from threading import BoundedSemaphore + + +def dummy_ok_callback(args): + print(args) + + +def dummy_exit_callback(args): + print(args) + + +dummy_config = { + "job_script": "job_script.py", + "num_cpu": 1, + "job_name": "dummy_job_{}", + "run_path": "dummy_path_{}", + "ok_callback": dummy_ok_callback, + "exit_callback": dummy_exit_callback, +} + +simple_script = """#!/usr/bin/env python +print('hello') +""" + +never_ending_script = """#!/usr/bin/env python +import time +while True: + time.sleep(0.5) +""" + +failing_script = """#!/usr/bin/env python +import sys +sys.exit(1) +""" + + +def create_queue(script, max_submit=1, max_runtime=None, callback_timeout=None): + driver = Driver(driver_type=QueueDriverEnum.LOCAL_DRIVER, max_running=5) + job_queue = JobQueue(driver, max_submit=max_submit) + with open(dummy_config["job_script"], "w") as f: + f.write(script) + os.chmod(dummy_config["job_script"], stat.S_IRWXU | stat.S_IRWXO | stat.S_IRWXG) + for i in range(10): + os.mkdir(dummy_config["run_path"].format(i)) + job = JobQueueNode( + job_script=dummy_config["job_script"], + job_name=dummy_config["job_name"].format(i), + run_path=dummy_config["run_path"].format(i), + num_cpu=dummy_config["num_cpu"], + status_file=job_queue.status_file, + ok_file=job_queue.ok_file, + exit_file=job_queue.exit_file, + done_callback_function=dummy_config["ok_callback"], + exit_callback_function=dummy_config["exit_callback"], + callback_arguments=[{"job_number": i}], + max_runtime=max_runtime, + callback_timeout=callback_timeout, + ) + + job_queue.add_job(job, i) + + return job_queue + + +def start_all(job_queue, sema_pool): + job = job_queue.fetch_next_waiting() + while job is not None: + job.run(job_queue.driver, sema_pool, job_queue.max_submit) + job = job_queue.fetch_next_waiting() + + +class JobQueueTest(ResTest): + def testStatusEnum(self): + source_path = "lib/include/ert/job_queue/job_status.hpp" + self.assertEnumIsFullyDefined(JobStatusType, "job_status_type", source_path) + + def test_kill_jobs(self): + with TestAreaContext("job_queue_test_kill") as work_area: + job_queue = create_queue(never_ending_script) + + assert job_queue.queue_size == 10 + assert job_queue.is_active() + + pool_sema = BoundedSemaphore(value=10) + start_all(job_queue, pool_sema) + + # make sure never ending jobs are running + wait_until(lambda: self.assertTrue(job_queue.is_active())) + + for job in job_queue.job_list: + job.stop() + + wait_until(lambda: self.assertFalse(job_queue.is_active())) + + job_queue._transition() + + for q_index, job in enumerate(job_queue.job_list): + assert job.status == JobStatusType.JOB_QUEUE_IS_KILLED + iens = job_queue._qindex_to_iens[q_index] + assert job_queue.snapshot()[iens] == str( + JobStatusType.JOB_QUEUE_IS_KILLED + ) + + for job in job_queue.job_list: + job.wait_for() + + def test_add_jobs(self): + with TestAreaContext("job_queue_test_add") as work_area: + job_queue = create_queue(simple_script) + + assert job_queue.queue_size == 10 + assert job_queue.is_active() + assert job_queue.fetch_next_waiting() is not None + + pool_sema = BoundedSemaphore(value=10) + start_all(job_queue, pool_sema) + + for job in job_queue.job_list: + job.stop() + + wait_until(lambda: self.assertFalse(job_queue.is_active())) + + for job in job_queue.job_list: + job.wait_for() + + def test_failing_jobs(self): + with TestAreaContext("job_queue_test_add") as work_area: + job_queue = create_queue(failing_script, max_submit=1) + + assert job_queue.queue_size == 10 + assert job_queue.is_active() + + pool_sema = BoundedSemaphore(value=10) + start_all(job_queue, pool_sema) + + wait_until( + func=(lambda: self.assertFalse(job_queue.is_active())), + ) + + for job in job_queue.job_list: + job.wait_for() + + job_queue._transition() + + assert job_queue.fetch_next_waiting() is None + + for q_index, job in enumerate(job_queue.job_list): + assert job.status == JobStatusType.JOB_QUEUE_FAILED + iens = job_queue._qindex_to_iens[q_index] + assert job_queue.snapshot()[iens] == str(JobStatusType.JOB_QUEUE_FAILED) + + def test_timeout_jobs(self): + with TestAreaContext("job_queue_test_kill") as work_area: + job_numbers = set() + + def callback(arg): + nonlocal job_numbers + job_numbers.add(arg[0]["job_number"]) + + job_queue = create_queue( + never_ending_script, + max_submit=1, + max_runtime=5, + callback_timeout=callback, + ) + + assert job_queue.queue_size == 10 + assert job_queue.is_active() + + pool_sema = BoundedSemaphore(value=10) + start_all(job_queue, pool_sema) + + # make sure never ending jobs are running + wait_until(lambda: self.assertTrue(job_queue.is_active())) + + wait_until(lambda: self.assertFalse(job_queue.is_active())) + + job_queue._transition() + + for q_index, job in enumerate(job_queue.job_list): + assert job.status == JobStatusType.JOB_QUEUE_IS_KILLED + iens = job_queue._qindex_to_iens[q_index] + assert job_queue.snapshot()[iens] == str( + JobStatusType.JOB_QUEUE_IS_KILLED + ) + + assert job_numbers == set(range(10)) + + for job in job_queue.job_list: + job.wait_for() + + def test_add_ensemble_evaluator_info(self): + with TestAreaContext("job_queue_add_ensemble_evaluator_info") as work_area: + job_queue = create_queue(simple_script) + ee_id = "some_id" + dispatch_url = "wss://some_url.com" + cert = "My very nice cert" + token = "my_super_secret_token" + cert_file = ".ee.pem" + runpaths = [ + pathlib.Path(dummy_config["run_path"].format(i)) for i in range(10) + ] + for runpath in runpaths: + with open(runpath / "jobs.json", "w") as f: + json.dump({}, f) + job_queue.add_ensemble_evaluator_information_to_jobs_file( + ee_id=ee_id, + dispatch_url=dispatch_url, + cert=cert, + token=token, + ) + + for runpath in runpaths: + job_file_path = runpath / "jobs.json" + with open(job_file_path) as f: + content = json.load(f) + assert content["step_id"] == 0 + assert content["dispatch_url"] == dispatch_url + assert content["ee_token"] == token + + assert content["ee_cert_path"] == str(runpath / cert_file) + with open(runpath / cert_file) as f: + assert f.read() == cert + + def test_add_ensemble_evaluator_info_cert_none(self): + with TestAreaContext( + "job_queue_add_ensemble_evaluator_info_cert_none" + ) as work_area: + job_queue = create_queue(simple_script) + ee_id = "some_id" + dispatch_url = "wss://some_url.com" + cert = None + token = None + cert_file = ".ee.pem" + runpaths = [ + pathlib.Path(dummy_config["run_path"].format(i)) for i in range(10) + ] + for runpath in runpaths: + with open(runpath / "jobs.json", "w") as f: + json.dump({}, f) + job_queue.add_ensemble_evaluator_information_to_jobs_file( + ee_id=ee_id, + dispatch_url=dispatch_url, + cert=cert, + token=token, + ) + + for runpath in runpaths: + job_file_path = runpath / "jobs.json" + with open(job_file_path) as f: + content = json.load(f) + assert content["step_id"] == 0 + assert content["dispatch_url"] == dispatch_url + assert content["ee_token"] == token + + assert content["ee_cert_path"] == None + assert not (runpath / cert_file).exists() diff --git a/libres/tests/res/job_queue/test_job_queue_manager.py b/libres/tests/res/job_queue/test_job_queue_manager.py new file mode 100644 index 00000000000..d5a6449f5e2 --- /dev/null +++ b/libres/tests/res/job_queue/test_job_queue_manager.py @@ -0,0 +1,187 @@ +from res.job_queue import ( + JobStatusType, + Driver, + QueueDriverEnum, + JobQueue, + JobQueueNode, + JobQueueManager, +) +from res.enkf import ResConfig +from tests import ResTest +from tests.utils import wait_until +from ecl.util.test import TestAreaContext +import os, stat + +from threading import BoundedSemaphore + + +def dummy_ok_callback(args): + print("success {}".format(args[1])) + with open(os.path.join(args[1], "OK"), "w") as f: + f.write("success") + + +def dummy_exit_callback(args): + print("failure {}".format(args)) + with open("ERROR", "w") as f: + f.write("failure") + + +dummy_config = { + "job_script": "job_script.py", + "num_cpu": 1, + "job_name": "dummy_job_{}", + "run_path": "dummy_path_{}", + "ok_callback": dummy_ok_callback, + "exit_callback": dummy_exit_callback, +} + +simple_script = """#!/usr/bin/env python +with open('STATUS', 'w') as f: + f.write('finished successfully') +""" + +failing_script = """#!/usr/bin/env python +import sys +sys.exit(1) +""" + +never_ending_script = """#!/usr/bin/env python +import time +while True: + time.sleep(0.5) +""" + +mock_bsub = """#!/usr/bin/env python +import sys +with open("test.out", "w") as f: + f.write(" ".join(sys.argv)) +""" + + +def create_queue(script, max_submit=2): + driver = Driver(driver_type=QueueDriverEnum.LOCAL_DRIVER, max_running=5) + job_queue = JobQueue(driver, max_submit=max_submit) + + with open(dummy_config["job_script"], "w") as f: + f.write(script) + + os.chmod(dummy_config["job_script"], stat.S_IRWXU | stat.S_IRWXO | stat.S_IRWXG) + for i in range(10): + os.mkdir(dummy_config["run_path"].format(i)) + job = JobQueueNode( + job_script=dummy_config["job_script"], + job_name=dummy_config["job_name"].format(i), + run_path=os.path.realpath(dummy_config["run_path"].format(i)), + num_cpu=dummy_config["num_cpu"], + status_file=job_queue.status_file, + ok_file=job_queue.ok_file, + exit_file=job_queue.exit_file, + done_callback_function=dummy_config["ok_callback"], + exit_callback_function=dummy_config["exit_callback"], + callback_arguments=[ + {"job_number": i}, + os.path.realpath(dummy_config["run_path"].format(i)), + ], + ) + job_queue.add_job(job, i) + job_queue.submit_complete() + return job_queue + + +class JobQueueManagerTest(ResTest): + def test_num_cpu_submitted_correctly(self): + with TestAreaContext("job_node_test"): + os.putenv("PATH", os.getcwd() + ":" + os.getenv("PATH")) + driver = Driver(driver_type=QueueDriverEnum.LSF_DRIVER, max_running=1) + + with open(dummy_config["job_script"], "w") as f: + f.write(simple_script) + os.chmod( + dummy_config["job_script"], stat.S_IRWXU | stat.S_IRWXO | stat.S_IRWXG + ) + + with open("bsub", "w") as f: + f.write(mock_bsub) + os.chmod("bsub", stat.S_IRWXU | stat.S_IRWXO | stat.S_IRWXG) + + job_id = 0 + num_cpus = 4 + os.mkdir(dummy_config["run_path"].format(job_id)) + job = JobQueueNode( + job_script=dummy_config["job_script"], + job_name=dummy_config["job_name"].format(job_id), + run_path=os.path.realpath(dummy_config["run_path"].format(job_id)), + num_cpu=num_cpus, + status_file="STATUS", + ok_file="OK", + exit_file="ERROR", + done_callback_function=dummy_config["ok_callback"], + exit_callback_function=dummy_config["exit_callback"], + callback_arguments=[ + {"job_number": job_id}, + os.path.realpath(dummy_config["run_path"].format(job_id)), + ], + ) + + pool_sema = BoundedSemaphore(value=2) + job.run(driver, pool_sema) + job.stop() + job.wait_for() + + with open("test.out") as f: + bsub_argv = f.read().split() + + found_cpu_arg = False + for arg_i, arg in enumerate(bsub_argv): + if arg == "-n": + self.assertEqual( + bsub_argv[arg_i + 1], + str(num_cpus), + "num_cpu argument does not match specified number of cpus", + ) + found_cpu_arg = True + + self.assertTrue(found_cpu_arg, "num_cpu argument not found") + + def test_execute_queue(self): + + with TestAreaContext("job_queue_manager_test") as work_area: + job_queue = create_queue(simple_script) + manager = JobQueueManager(job_queue) + manager.execute_queue() + + self.assertFalse(job_queue.isRunning()) + + for job in job_queue.job_list: + ok_file = os.path.realpath(os.path.join(job.run_path, "OK")) + assert os.path.isfile(ok_file) + with open(ok_file, "r") as f: + assert f.read() == "success" + + def test_max_submit_reached(self): + with TestAreaContext("job_queue_manager_test") as work_area: + max_submit_num = 5 + job_queue = create_queue(failing_script, max_submit=max_submit_num) + manager = JobQueueManager(job_queue) + manager.execute_queue() + + self.assertFalse(manager.isRunning()) + + # check if it is really max_submit_num + assert job_queue.max_submit == max_submit_num + + for job in job_queue.job_list: + assert job.status == JobStatusType.JOB_QUEUE_FAILED + assert job.submit_attempt == job_queue.max_submit + + def test_kill_queue(self): + with TestAreaContext("job_queue_manager_test") as work_area: + max_submit_num = 5 + job_queue = create_queue(simple_script, max_submit=max_submit_num) + manager = JobQueueManager(job_queue) + job_queue.kill_all_jobs() + manager.execute_queue() + + for job in job_queue.job_list: + assert job.status == JobStatusType.JOB_QUEUE_FAILED diff --git a/libres/tests/res/job_queue/test_workflow.py b/libres/tests/res/job_queue/test_workflow.py new file mode 100644 index 00000000000..e56526b756c --- /dev/null +++ b/libres/tests/res/job_queue/test_workflow.py @@ -0,0 +1,67 @@ +from res.job_queue import Workflow, WorkflowJoblist +from ecl.util.test import TestAreaContext +from tests import ResTest +from res.util.substitution_list import SubstitutionList +from .workflow_common import WorkflowCommon + + +class WorkflowTest(ResTest): + def test_workflow(self): + with TestAreaContext("python/job_queue/workflow") as work_area: + WorkflowCommon.createExternalDumpJob() + + joblist = WorkflowJoblist() + self.assertTrue(joblist.addJobFromFile("DUMP", "dump_job")) + + with self.assertRaises(UserWarning): + joblist.addJobFromFile("KNOCK", "knock_job") + + self.assertTrue("DUMP" in joblist) + + workflow = Workflow("dump_workflow", joblist) + + self.assertEqual(len(workflow), 2) + + job, args = workflow[0] + self.assertEqual(job, joblist["DUMP"]) + self.assertEqual(args[0], "dump1") + self.assertEqual(args[1], "dump_text_1") + + job, args = workflow[1] + self.assertEqual(job, joblist["DUMP"]) + + def test_workflow_run(self): + with TestAreaContext("python/job_queue/workflow") as work_area: + WorkflowCommon.createExternalDumpJob() + + joblist = WorkflowJoblist() + self.assertTrue(joblist.addJobFromFile("DUMP", "dump_job")) + self.assertTrue("DUMP" in joblist) + + workflow = Workflow("dump_workflow", joblist) + + self.assertTrue(len(workflow), 2) + + context = SubstitutionList() + context.addItem("", "text") + + self.assertTrue(workflow.run(None, verbose=True, context=context)) + + with open("dump1", "r") as f: + self.assertEqual(f.read(), "dump_text_1") + + with open("dump2", "r") as f: + self.assertEqual(f.read(), "dump_text_2") + + def test_failing_workflow_run(self): + with TestAreaContext("python/job_queue/workflow") as work_area: + WorkflowCommon.createExternalDumpJob() + + joblist = WorkflowJoblist() + self.assertTrue(joblist.addJobFromFile("DUMP", "dump_job")) + self.assertTrue("DUMP" in joblist) + + workflow = Workflow("undefined", joblist) + context = SubstitutionList() + + self.assertFalse(workflow.run(None, verbose=True, context=context)) diff --git a/libres/tests/res/job_queue/test_workflow_job.py b/libres/tests/res/job_queue/test_workflow_job.py new file mode 100644 index 00000000000..15095c65e90 --- /dev/null +++ b/libres/tests/res/job_queue/test_workflow_job.py @@ -0,0 +1,118 @@ +import ecl +import res.enkf # noqa + +from res import ResPrototype +from res.job_queue import WorkflowJob +from tests import ResTest +from ecl.util.test import TestAreaContext +from .workflow_common import WorkflowCommon + + +class _TestWorkflowJobPrototype(ResPrototype): + def __init__(self, prototype, bind=True): + super(_TestWorkflowJobPrototype, self).__init__(prototype, bind=bind) + + +class WorkflowJobTest(ResTest): + _alloc_config = _TestWorkflowJobPrototype( + "void* workflow_job_alloc_config()", bind=False + ) + _alloc_from_file = _TestWorkflowJobPrototype( + "workflow_job_obj workflow_job_config_alloc(char*, void*, char*)", bind=False + ) + + def test_workflow_job_creation(self): + workflow_job = WorkflowJob("Test") + + self.assertTrue(workflow_job.isInternal()) + self.assertEqual(workflow_job.name(), "Test") + + def test_read_internal_function(self): + with TestAreaContext("python/job_queue/workflow_job") as work_area: + WorkflowCommon.createInternalFunctionJob() + WorkflowCommon.createErtScriptsJob() + + config = self._alloc_config() + workflow_job = self._alloc_from_file( + "SELECT_CASE", config, "select_case_job" + ) + + self.assertEqual(workflow_job.name(), "SELECT_CASE") + self.assertTrue(workflow_job.isInternal()) + self.assertEqual(workflow_job.functionName(), "enkf_main_select_case_JOB") + + self.assertFalse(workflow_job.isInternalScript()) + self.assertIsNone(workflow_job.getInternalScriptPath()) + + workflow_job = self._alloc_from_file( + "SUBTRACT", config, "subtract_script_job" + ) + self.assertEqual(workflow_job.name(), "SUBTRACT") + self.assertTrue(workflow_job.isInternal()) + self.assertIsNone(workflow_job.functionName()) + + self.assertTrue(workflow_job.isInternalScript()) + self.assertTrue( + workflow_job.getInternalScriptPath().endswith("subtract_script.py") + ) + + def test_arguments(self): + with TestAreaContext("python/job_queue/workflow_job") as work_area: + WorkflowCommon.createInternalFunctionJob() + + config = self._alloc_config() + job = self._alloc_from_file("PRINTF", config, "printf_job") + + self.assertEqual(job.minimumArgumentCount(), 4) + self.assertEqual(job.maximumArgumentCount(), 5) + self.assertEqual(job.argumentTypes(), [str, int, float, bool, str]) + + self.assertTrue(job.run(None, ["x %d %f %d", 1, 2.5, True])) + self.assertTrue(job.run(None, ["x %d %f %d %s", 1, 2.5, True, "y"])) + + with self.assertRaises(UserWarning): # Too few arguments + job.run(None, ["x %d %f", 1, 2.5]) + + with self.assertRaises(UserWarning): # Too many arguments + job.run(None, ["x %d %f %d %s", 1, 2.5, True, "y", "nada"]) + + def test_run_external_job(self): + + with TestAreaContext("python/job_queue/workflow_job") as work_area: + WorkflowCommon.createExternalDumpJob() + + config = self._alloc_config() + job = self._alloc_from_file("DUMP", config, "dump_job") + + self.assertFalse(job.isInternal()) + argTypes = job.argumentTypes() + self.assertEqual(argTypes, [str, str]) + self.assertIsNone(job.run(None, ["test", "text"])) + self.assertEqual(job.stdoutdata(), "Hello World\n") + + with open("test", "r") as f: + self.assertEqual(f.read(), "text") + + def test_error_handling_external_job(self): + + with TestAreaContext("python/job_queue/workflow_job") as work_area: + WorkflowCommon.createExternalDumpJob() + + config = self._alloc_config() + job = self._alloc_from_file("DUMP", config, "dump_failing_job") + + self.assertFalse(job.isInternal()) + argTypes = job.argumentTypes() + self.assertIsNone(job.run(None, [])) + self.assertTrue(job.stderrdata().startswith("Traceback")) + + def test_run_internal_script(self): + with TestAreaContext("python/job_queue/workflow_job") as work_area: + WorkflowCommon.createErtScriptsJob() + + config = self._alloc_config() + job = self._alloc_from_file("SUBTRACT", config, "subtract_script_job") + + result = job.run(None, ["1", "2"]) + + self.assertEqual(result, -1) diff --git a/libres/tests/res/job_queue/test_workflow_joblist.py b/libres/tests/res/job_queue/test_workflow_joblist.py new file mode 100644 index 00000000000..906625bb338 --- /dev/null +++ b/libres/tests/res/job_queue/test_workflow_joblist.py @@ -0,0 +1,41 @@ +import res.enkf # noqa +from res.job_queue import WorkflowJoblist, WorkflowJob +from ecl.util.test import TestAreaContext +from tests import ResTest +from .workflow_common import WorkflowCommon + + +class WorkflowJoblistTest(ResTest): + def test_workflow_joblist_creation(self): + joblist = WorkflowJoblist() + + job = WorkflowJob("JOB1") + + joblist.addJob(job) + + self.assertTrue(job in joblist) + self.assertTrue("JOB1" in joblist) + + job_ref = joblist["JOB1"] + + self.assertEqual(job.name(), job_ref.name()) + + def test_workflow_joblist_with_files(self): + with TestAreaContext("python/job_queue/workflow_joblist") as work_area: + WorkflowCommon.createErtScriptsJob() + WorkflowCommon.createExternalDumpJob() + WorkflowCommon.createInternalFunctionJob() + + joblist = WorkflowJoblist() + + joblist.addJobFromFile("DUMP_JOB", "dump_job") + joblist.addJobFromFile("SELECT_CASE_JOB", "select_case_job") + joblist.addJobFromFile("SUBTRACT_SCRIPT_JOB", "subtract_script_job") + + self.assertTrue("DUMP_JOB" in joblist) + self.assertTrue("SELECT_CASE_JOB" in joblist) + self.assertTrue("SUBTRACT_SCRIPT_JOB" in joblist) + + self.assertFalse((joblist["DUMP_JOB"]).isInternal()) + self.assertTrue((joblist["SELECT_CASE_JOB"]).isInternal()) + self.assertTrue((joblist["SUBTRACT_SCRIPT_JOB"]).isInternal()) diff --git a/libres/tests/res/job_queue/test_workflow_runner.py b/libres/tests/res/job_queue/test_workflow_runner.py new file mode 100644 index 00000000000..8e9ba675355 --- /dev/null +++ b/libres/tests/res/job_queue/test_workflow_runner.py @@ -0,0 +1,133 @@ +import os +import time +from res.job_queue import WorkflowJoblist, Workflow, WorkflowRunner +from tests import ResTest +from tests.utils import wait_until +from ecl.util.test import TestAreaContext +from res.util.substitution_list import SubstitutionList +from .workflow_common import WorkflowCommon +import sys +from unittest.mock import patch + + +class WorkflowRunnerTest(ResTest): + def test_workflow_thread_cancel_ert_script(self): + with TestAreaContext( + "python/job_queue/workflow_runner_ert_script" + ) as work_area: + WorkflowCommon.createWaitJob() + + joblist = WorkflowJoblist() + self.assertTrue(joblist.addJobFromFile("WAIT", "wait_job")) + self.assertTrue("WAIT" in joblist) + + workflow = Workflow("wait_workflow", joblist) + + self.assertEqual(len(workflow), 3) + + workflow_runner = WorkflowRunner(workflow) + + self.assertFalse(workflow_runner.isRunning()) + + with workflow_runner: + self.assertIsNone(workflow_runner.workflowResult()) + + wait_until(lambda: self.assertTrue(workflow_runner.isRunning())) + wait_until(lambda: self.assertFileExists("wait_started_0")) + + wait_until(lambda: self.assertFileExists("wait_finished_0")) + + wait_until(lambda: self.assertFileExists("wait_started_1")) + + workflow_runner.cancel() + + wait_until(lambda: self.assertFileExists("wait_cancelled_1")) + + self.assertTrue(workflow_runner.isCancelled()) + + self.assertFileDoesNotExist("wait_finished_1") + self.assertFileDoesNotExist("wait_started_2") + self.assertFileDoesNotExist("wait_cancelled_2") + self.assertFileDoesNotExist("wait_finished_2") + + def test_workflow_thread_cancel_external(self): + with TestAreaContext("python/job_queue/workflow_runner_external") as work_area: + WorkflowCommon.createWaitJob() + + joblist = WorkflowJoblist() + self.assertTrue(joblist.addJobFromFile("WAIT", "external_wait_job")) + self.assertTrue("WAIT" in joblist) + + workflow = Workflow("wait_workflow", joblist) + + self.assertEqual(len(workflow), 3) + + workflow_runner = WorkflowRunner( + workflow, ert=None, context=SubstitutionList() + ) + + self.assertFalse(workflow_runner.isRunning()) + + with workflow_runner: + wait_until(lambda: self.assertTrue(workflow_runner.isRunning())) + wait_until(lambda: self.assertFileExists("wait_started_0")) + wait_until(lambda: self.assertFileExists("wait_finished_0")) + wait_until(lambda: self.assertFileExists("wait_started_1")) + workflow_runner.cancel() + self.assertTrue(workflow_runner.isCancelled()) + + self.assertFileDoesNotExist("wait_finished_1") + self.assertFileDoesNotExist("wait_started_2") + self.assertFileDoesNotExist("wait_cancelled_2") + self.assertFileDoesNotExist("wait_finished_2") + + def test_workflow_failed_job(self): + with TestAreaContext("python/job_queue/workflow_runner_fails") as work_area: + WorkflowCommon.createExternalDumpJob() + + joblist = WorkflowJoblist() + self.assertTrue(joblist.addJobFromFile("DUMP", "dump_failing_job")) + workflow = Workflow("dump_workflow", joblist) + self.assertEqual(len(workflow), 2) + + workflow_runner = WorkflowRunner( + workflow, ert=None, context=SubstitutionList() + ) + + self.assertFalse(workflow_runner.isRunning()) + with patch.object( + Workflow, "run", side_effect=Exception("mocked workflow error") + ), workflow_runner: + workflow_runner.wait() + self.assertNotEqual(workflow_runner.exception(), None) + + def test_workflow_success(self): + with TestAreaContext("python/job_queue/workflow_runner_fast") as work_area: + WorkflowCommon.createWaitJob() + + joblist = WorkflowJoblist() + self.assertTrue(joblist.addJobFromFile("WAIT", "wait_job")) + self.assertTrue( + joblist.addJobFromFile("EXTERNAL_WAIT", "external_wait_job") + ) + + workflow = Workflow("fast_wait_workflow", joblist) + + self.assertEqual(len(workflow), 2) + + workflow_runner = WorkflowRunner( + workflow, ert=None, context=SubstitutionList() + ) + + self.assertFalse(workflow_runner.isRunning()) + with workflow_runner: + workflow_runner.wait() + self.assertFileExists("wait_started_0") + self.assertFileDoesNotExist("wait_cancelled_0") + self.assertFileExists("wait_finished_0") + + self.assertFileExists("wait_started_1") + self.assertFileDoesNotExist("wait_cancelled_1") + self.assertFileExists("wait_finished_1") + + self.assertTrue(workflow_runner.workflowResult()) diff --git a/libres/tests/res/job_queue/workflow_common.py b/libres/tests/res/job_queue/workflow_common.py new file mode 100644 index 00000000000..cec31a7f6f6 --- /dev/null +++ b/libres/tests/res/job_queue/workflow_common.py @@ -0,0 +1,148 @@ +import os +import stat + + +class WorkflowCommon(object): + @staticmethod + def createExternalDumpJob(): + with open("dump_job", "w") as f: + f.write("INTERNAL FALSE\n") + f.write("EXECUTABLE dump.py\n") + f.write("MIN_ARG 2\n") + f.write("MAX_ARG 2\n") + f.write("ARG_TYPE 0 STRING\n") + + with open("dump_failing_job", "w") as f: + f.write("INTERNAL FALSE\n") + f.write("EXECUTABLE dump_failing.py\n") + + with open("dump.py", "w") as f: + f.write("#!/usr/bin/env python\n") + f.write("import sys\n") + f.write("f = open('%s' % sys.argv[1], 'w')\n") + f.write("f.write('%s' % sys.argv[2])\n") + f.write("f.close()\n") + f.write('print("Hello World")') + + with open("dump_failing.py", "w") as f: + f.write("#!/usr/bin/env python\n") + f.write('print("Hello Failing")\n') + f.write("raise Exception") + + st = os.stat("dump.py") + os.chmod( + "dump.py", st.st_mode | stat.S_IEXEC + ) # | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH + st = os.stat("dump_failing.py") + os.chmod("dump_failing.py", st.st_mode | stat.S_IEXEC) + + with open("dump_workflow", "w") as f: + f.write("DUMP dump1 dump_text_1\n") + f.write("DUMP dump2 dump__2\n") + + @staticmethod + def createInternalFunctionJob(): + with open("select_case_job", "w") as f: + f.write("INTERNAL True\n") + f.write("FUNCTION enkf_main_select_case_JOB\n") + f.write("MIN_ARG 1\n") + f.write("MAX_ARG 1\n") + f.write("ARG_TYPE 0 STRING\n") + + with open("printf_job", "w") as f: + f.write("INTERNAL True\n") + f.write("FUNCTION printf\n") + f.write("MIN_ARG 4\n") + f.write("MAX_ARG 5\n") + f.write("ARG_TYPE 0 STRING\n") + f.write("ARG_TYPE 1 INT\n") + f.write("ARG_TYPE 2 FLOAT\n") + f.write("ARG_TYPE 3 BOOL\n") + f.write("ARG_TYPE 4 STRING\n") + + with open("compare_job", "w") as f: + f.write("INTERNAL True\n") + f.write("FUNCTION strcmp\n") + f.write("MIN_ARG 2\n") + f.write("MAX_ARG 2\n") + f.write("ARG_TYPE 0 STRING\n") + f.write("ARG_TYPE 1 STRING\n") + + @staticmethod + def createErtScriptsJob(): + with open("subtract_script.py", "w") as f: + f.write("from res.job_queue import ErtScript\n") + f.write("\n") + f.write("class SubtractScript(ErtScript):\n") + f.write(" def run(self, arg1, arg2):\n") + f.write(" return arg1 - arg2\n") + + with open("subtract_script_job", "w") as f: + f.write("INTERNAL True\n") + f.write("SCRIPT subtract_script.py\n") + f.write("MIN_ARG 2\n") + f.write("MAX_ARG 2\n") + f.write("ARG_TYPE 0 FLOAT\n") + f.write("ARG_TYPE 1 FLOAT\n") + + @staticmethod + def createWaitJob(): + with open("wait_job.py", "w") as f: + f.write("from res.job_queue import ErtScript\n") + f.write("import time\n") + f.write("\n") + f.write("class WaitScript(ErtScript):\n") + f.write(" def dump(self, filename, content):\n") + f.write(" with open(filename, 'w') as f:\n") + f.write(" f.write(content)\n") + f.write("\n") + f.write(" def run(self, number, wait_time):\n") + f.write(" self.dump('wait_started_%d' % number, 'text')\n") + f.write(" start = time.time()\n") + f.write(" diff = 0\n") + f.write(" while not self.isCancelled() and diff < wait_time: \n") + f.write(" time.sleep(0.2)\n") + f.write(" diff = time.time() - start\n") + f.write("\n") + f.write(" if self.isCancelled():\n") + f.write(" self.dump('wait_cancelled_%d' % number, 'text')\n") + f.write(" else:\n") + f.write(" self.dump('wait_finished_%d' % number, 'text')\n") + f.write("\n") + f.write(" return None\n") + + with open("external_wait_job.sh", "w") as f: + f.write("#!/usr/bin/env bash\n") + f.write('echo "text" > wait_started_$1\n') + f.write("sleep $2\n") + f.write('echo "text" > wait_finished_$1\n') + + st = os.stat("external_wait_job.sh") + os.chmod( + "external_wait_job.sh", st.st_mode | stat.S_IEXEC + ) # | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH + + with open("wait_job", "w") as f: + f.write("INTERNAL True\n") + f.write("SCRIPT wait_job.py\n") + f.write("MIN_ARG 2\n") + f.write("MAX_ARG 2\n") + f.write("ARG_TYPE 0 INT\n") + f.write("ARG_TYPE 1 INT\n") + + with open("external_wait_job", "w") as f: + f.write("INTERNAL False\n") + f.write("EXECUTABLE external_wait_job.sh\n") + f.write("MIN_ARG 2\n") + f.write("MAX_ARG 2\n") + f.write("ARG_TYPE 0 INT\n") + f.write("ARG_TYPE 1 INT\n") + + with open("wait_workflow", "w") as f: + f.write("WAIT 0 1\n") + f.write("WAIT 1 10\n") + f.write("WAIT 2 1\n") + + with open("fast_wait_workflow", "w") as f: + f.write("WAIT 0 1\n") + f.write("EXTERNAL_WAIT 1 1\n") diff --git a/libres/tests/res/run/__init__.py b/libres/tests/res/run/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/tests/res/run/test_run.py b/libres/tests/res/run/test_run.py new file mode 100644 index 00000000000..2039843b798 --- /dev/null +++ b/libres/tests/res/run/test_run.py @@ -0,0 +1,64 @@ +import os.path +import random +from tests import ResTest +from ecl.util.test import TestRun, path_exists + + +class RunTest(ResTest): + def setUp(self): + # Slightly weird - tests need existing file, + # but it can be empty .... + self.testConfig = "/tmp/config-%06d" % random.randint(100000, 999999) + with open(self.testConfig, "w") as f: + pass + + def test_init(self): + with self.assertRaises(IOError): + TestRun("Does/notExist") + + tr = TestRun(self.testConfig) + self.assertEqual(tr.config_file, os.path.split(self.testConfig)[1]) + self.assertEqual(tr.ert_version, "stable") + + def test_args(self): + tr = TestRun(self.testConfig, args=["-v", "latest"]) + self.assertEqual(tr.ert_version, "latest") + + def test_cmd(self): + tr = TestRun(self.testConfig) + self.assertEqual(tr.ert_cmd, TestRun.default_ert_cmd) + + tr.ert_cmd = "/tmp/test" + self.assertEqual("/tmp/test", tr.ert_cmd) + + def test_args2(self): + tr = TestRun(self.testConfig, args=["arg1", "arg2", "-v", "latest"]) + self.assertEqual(tr.get_args(), ["arg1", "arg2"]) + self.assertEqual(tr.ert_version, "latest") + + def test_workflows(self): + tr = TestRun(self.testConfig) + self.assertEqual(tr.get_workflows(), []) + + tr.add_workflow("wf1") + tr.add_workflow("wf2") + self.assertEqual(tr.get_workflows(), ["wf1", "wf2"]) + + def test_run_no_workflow(self): + tr = TestRun(self.testConfig) + with self.assertRaises(Exception): + tr.run() + + def test_runpath(self): + tr = TestRun(self.testConfig, "Name") + self.assertEqual(TestRun.default_path_prefix, tr.path_prefix) + + def test_check(self): + tr = TestRun(self.testConfig, "Name") + tr.add_check(path_exists, "some/file") + + with self.assertRaises(Exception): + tr.add_check(25, "arg") + + with self.assertRaises(Exception): + tr.add_check(func_does_not_exist, "arg") diff --git a/libres/tests/res/simulator/__init__.py b/libres/tests/res/simulator/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/tests/res/simulator/test_batch_sim.py b/libres/tests/res/simulator/test_batch_sim.py new file mode 100644 index 00000000000..82b4e1f4685 --- /dev/null +++ b/libres/tests/res/simulator/test_batch_sim.py @@ -0,0 +1,603 @@ +import os +import time +import shutil +import sys +import unittest + +from ecl.util.test import TestAreaContext + +from res.simulator import BatchSimulator, BatchContext +from res.job_queue import JobStatusType +from res.enkf import ResConfig +from tests import ResTest +from tests.utils import tmpdir + + +class MockMonitor(object): + def __init__(self): + self.sim_context = None + + def start_callback(self, *args, **kwargs): + self.sim_context = args[0] + + +def _wait_for_completion(ctx): + while ctx.running(): + status = ctx.status + time.sleep(1) + sys.stderr.write("status: %s\n" % str(status)) + for job_index in range(len(ctx)): + status = ctx.job_status(job_index) + progress = ctx.job_progress(job_index) + if progress: + for job in progress.jobs: + sys.stderr.write(" %s: \n" % str(job)) + + +class BatchSimulatorTest(ResTest): + @tmpdir() + def test_invalid_simulator_creation(self): + config_file = self.createTestPath("local/batch_sim/batch_sim.ert") + + with TestAreaContext("batch_sim") as test_area: + test_area.copy_parent_content(config_file) + + # Not valid ResConfig instance as first argument + with self.assertRaises(ValueError): + rsim = BatchSimulator( + "ARG", + { + "WELL_ORDER": ["W1", "W2", "W3"], + "WELL_ON_OFF": ["W1", "W2", "W3"], + }, + ["ORDER", "ON_OFF"], + ) + + res_config = ResConfig(user_config_file=os.path.basename(config_file)) + + # Control argument not a dict - Exception + with self.assertRaises(Exception): + rsim = BatchSimulator( + res_config, ["WELL_ORDER", ["W1", "W2", "W3"]], ["ORDER"] + ) + + # Duplicate keys + with self.assertRaises(ValueError): + rsim = BatchSimulator( + res_config, {"WELL_ORDER": ["W3", "W2", "W3"]}, ["ORDER"] + ) + + rsim = BatchSimulator( + res_config, + {"WELL_ORDER": ["W1", "W2", "W3"], "WELL_ON_OFF": ["W1", "W2", "W3"]}, + ["ORDER", "ON_OFF"], + ) + + # The key for one of the controls is invalid => KeyError + with self.assertRaises(KeyError): + rsim.start( + "case", + [ + ( + 2, + { + "WELL_ORDERX": {"W1": 0, "W2": 0, "W3": 1}, + "WELL_ON_OFF": {"W1": 0, "W2": 0, "W3": 1}, + }, + ), + ( + 2, + { + "WELL_ORDER": {"W1": 0, "W2": 0, "W3": 0}, + "WELL_ON_OFF": {"W1": 0, "W2": 0, "W3": 1}, + }, + ), + ], + ) + + # The key for one of the variables is invalid => KeyError + with self.assertRaises(KeyError): + rsim.start( + "case", + [ + ( + 2, + { + "WELL_ORDER": {"W1": 0, "W4": 0, "W3": 1}, + "WELL_ON_OFF": {"W1": 0, "W2": 0, "W3": 1}, + }, + ), + ( + 1, + { + "WELL_ORDER": {"W1": 0, "W2": 0, "W3": 0}, + "WELL_ON_OFF": {"W1": 0, "W2": 0, "W3": 1}, + }, + ), + ], + ) + + # The key for one of the variables is invalid => KeyError + with self.assertRaises(KeyError): + rsim.start( + "case", + [ + ( + 2, + { + "WELL_ORDER": {"W1": 0, "W2": 0, "W3": 1, "W0": 0}, + "WELL_ON_OFF": {"W1": 0, "W2": 0, "W3": 1}, + }, + ), + ( + 1, + { + "WELL_ORDER": {"W1": 0, "W2": 0, "W3": 0}, + "WELL_ON_OFF": {"W1": 0, "W2": 0, "W3": 1}, + }, + ), + ], + ) + + # Missing the key WELL_ON_OFF => KeyError + with self.assertRaises(KeyError): + rsim.start("case", [(2, {"WELL_ORDER": {"W1": 0, "W2": 0, "W3": 1}})]) + + # One of the numeric vectors has wrong length => ValueError: + with self.assertRaises(KeyError): + rsim.start( + "case", + [ + ( + 2, + { + "WELL_ORDER": {"W1": 0, "W2": 0, "W3": 1}, + "WELL_ON_OFF": {"W2": 0}, + }, + ), + ], + ) + + # Not numeric values => Exception + with self.assertRaises(Exception): + rsim.start( + "case", + [ + ( + 2, + { + "WELL_ORDER": {"W1": 0, "W2": 0, "W3": 1}, + "WELL_ON_OFF": {"W1": 0, "W2": 1, "W3": "X"}, + }, + ), + ], + ) + + # Not numeric values => Exception + with self.assertRaises(Exception): + rsim.start( + "case", + [ + ( + "2", + { + "WELL_ORDER": {"W1": 0, "W2": 0, "W3": 1}, + "WELL_ON_OFF": {"W1": 0, "W2": 1, "W3": 4}, + }, + ), + ], + ) + + @tmpdir() + def test_batch_simulation(self): + config_file = self.createTestPath("local/batch_sim/batch_sim.ert") + + with TestAreaContext("batch_sim") as test_area: + test_area.copy_parent_content(config_file) + + res_config = ResConfig(user_config_file=os.path.basename(config_file)) + monitor = MockMonitor() + rsim = BatchSimulator( + res_config, + {"WELL_ORDER": ["W1", "W2", "W3"], "WELL_ON_OFF": ["W1", "W2", "W3"]}, + ["ORDER", "ON_OFF"], + callback=monitor.start_callback, + ) + + # Starting a simulation which should actually run through. + case_data = [ + ( + 2, + { + "WELL_ORDER": {"W1": 1, "W2": 2, "W3": 3}, + "WELL_ON_OFF": {"W1": 4, "W2": 5, "W3": 6}, + }, + ), + ( + 1, + { + "WELL_ORDER": {"W1": 7, "W2": 8, "W3": 9}, + "WELL_ON_OFF": {"W1": 10, "W2": 11, "W3": 12}, + }, + ), + ] + + ctx = rsim.start("case", case_data) + self.assertEqual(len(case_data), len(ctx)) + + # Asking for results before it is complete. + with self.assertRaises(RuntimeError): + ctx.results() + + # Ask for status of simulation we do not have. + with self.assertRaises(KeyError): + ctx.job_status(1973) + + with self.assertRaises(KeyError): + ctx.job_progress(1987) + + # Carry out simulations.. + _wait_for_completion(ctx) + + # Fetch and validate results + results = ctx.results() + self.assertEqual(len(results), 2) + + for result, (_, controls) in zip(results, case_data): + self.assertEqual(sorted(["ORDER", "ON_OFF"]), sorted(result.keys())) + + for res_key, ctrl_key in ( + ("ORDER", "WELL_ORDER"), + ("ON_OFF", "WELL_ON_OFF"), + ): + + # The forward model job SQUARE_PARAMS will load the control + # values and square them before writing results to disk in + # the order W1, W2, W3. + self.assertEqual( + [ + controls[ctrl_key][var_name] ** 2 + for var_name in ["W1", "W2", "W3"] + ], + list(result[res_key]), + ) + + self.assertTrue(isinstance(monitor.sim_context, BatchContext)) + + @tmpdir() + def test_batch_simulation_invalid_suffixes(self): + config_file = self.createTestPath("local/batch_sim/batch_sim.ert") + with TestAreaContext("batch_sim") as test_area: + test_area.copy_parent_content(config_file) + res_config = ResConfig(user_config_file=os.path.basename(config_file)) + + # If suffixes are given, must be all non-empty string collections + type_err_suffixes = ( + 27, + "astring", + b"somebytes", + True, + False, + [True, False], + None, + range(3), + ) + for sfx in type_err_suffixes: + with self.assertRaises(TypeError): + BatchSimulator( + res_config, + { + "WELL_ORDER": {"W1": ["a"], "W3": sfx}, + }, + ["ORDER"], + ) + val_err_suffixes = ( + [], + {}, + [""], + ["a", "a"], + ) + for sfx in val_err_suffixes: + with self.assertRaises(ValueError): + BatchSimulator( + res_config, + { + "WELL_ORDER": {"W1": ["a"], "W3": sfx}, + }, + ["ORDER"], + ) + + rsim = BatchSimulator( + res_config, + { + "WELL_ORDER": { + "W1": ["a", "b"], + "W3": ["c"], + }, + }, + ["ORDER"], + ) + + # suffixes not taken into account + with self.assertRaises(KeyError): + rsim.start("case", [(1, {"WELL_ORDER": {"W1": 3, "W3": 2}})]) + with self.assertRaises(KeyError): + rsim.start("case", [(1, {"WELL_ORDER": {"W1": {}, "W3": {}}})]) + + # wrong suffixes + with self.assertRaises(KeyError): + rsim.start( + "case", + [ + ( + 1, + { + "WELL_ORDER": { + "W1": {"a": 3, "x": 3}, + "W3": {"c": 2}, + } + }, + ) + ], + ) + + # missing one suffix + with self.assertRaises(KeyError): + rsim.start( + "case", + [ + ( + 1, + { + "WELL_ORDER": { + "W1": {"a": 3}, + "W3": {"c": 2}, + } + }, + ) + ], + ) + + # wrong type for values + # Exception cause atm this would raise a ctypes.ArgumentError + # but that's an implementation detail that will hopefully change + # not so far in the future + with self.assertRaises(Exception): + rsim.start( + "case", + [ + ( + 1, + { + "WELL_ORDER": { + "W1": {"a": "3", "b": 3}, + "W3": {"c": 2}, + } + }, + ) + ], + ) + + @tmpdir() + def test_batch_simulation_suffixes(self): + config_file = self.createTestPath("local/batch_sim/batch_sim.ert") + with TestAreaContext("batch_sim") as test_area: + test_area.copy_parent_content(config_file) + + res_config = ResConfig(user_config_file=os.path.basename(config_file)) + monitor = MockMonitor() + rsim = BatchSimulator( + res_config, + { + "WELL_ORDER": { + "W1": ["a", "b"], + "W2": ["c"], + "W3": ["a", "b"], + }, + "WELL_ON_OFF": ["W1", "W2", "W3"], + }, + ["ORDER", "ON_OFF"], + callback=monitor.start_callback, + ) + # Starting a simulation which should actually run through. + case_data = [ + ( + 2, + { + "WELL_ORDER": { + "W1": {"a": 0.5, "b": 0.2}, + "W2": {"c": 2}, + "W3": {"a": -0.5, "b": -0.2}, + }, + "WELL_ON_OFF": {"W1": 4, "W2": 5, "W3": 6}, + }, + ), + ( + 1, + { + "WELL_ORDER": { + "W1": {"a": 0.8, "b": 0.9}, + "W2": {"c": 1.6}, + "W3": {"a": -0.8, "b": -0.9}, + }, + "WELL_ON_OFF": {"W1": 10, "W2": 11, "W3": 12}, + }, + ), + ] + + ctx = rsim.start("case", case_data) + self.assertEqual(len(case_data), len(ctx)) + _wait_for_completion(ctx) + + # Fetch and validate results + results = ctx.results() + self.assertEqual(len(results), 2) + + for result in results: + self.assertEqual(sorted(["ORDER", "ON_OFF"]), sorted(result.keys())) + + keys = ("W1", "W2", "W3") + for result, (_, controls) in zip(results, case_data): + expected = [controls["WELL_ON_OFF"][key] ** 2 for key in keys] + self.assertEqual(expected, list(result["ON_OFF"])) + + expected = [ + v ** 2 + for key in keys + for _, v in controls["WELL_ORDER"][key].items() + ] + for exp, act in zip(expected, list(result["ORDER"])): + self.assertAlmostEqual(exp, act) + + @tmpdir() + def test_stop_sim(self): + config_file = self.createTestPath("local/batch_sim/batch_sim.ert") + with TestAreaContext("batch_sim_stop") as test_area: + test_area.copy_parent_content(config_file) + res_config = ResConfig(user_config_file=os.path.basename(config_file)) + + rsim = BatchSimulator( + res_config, + {"WELL_ORDER": ["W1", "W2", "W3"], "WELL_ON_OFF": ["W1", "W2", "W3"]}, + ["ORDER", "ON_OFF"], + ) + + case_name = "MyCaseName_123" + + # Starting a simulation which should actually run through. + ctx = rsim.start( + case_name, + [ + ( + 2, + { + "WELL_ORDER": {"W1": 1, "W2": 2, "W3": 3}, + "WELL_ON_OFF": {"W1": 4, "W2": 5, "W3": 6}, + }, + ), + ( + 1, + { + "WELL_ORDER": {"W1": 7, "W2": 8, "W3": 9}, + "WELL_ON_OFF": {"W1": 10, "W2": 11, "W3": 12}, + }, + ), + ], + ) + + ctx.stop() + status = ctx.status + + self.assertEqual(status.complete, 0) + self.assertEqual(status.running, 0) + + runpath = "storage/batch_sim/runpath/%s/realisation-0" % case_name + self.assertTrue(os.path.exists(runpath)) + + @tmpdir() + def test_workflow_pre_simulation(self): + config_file = self.createTestPath("local/batch_sim/batch_sim.ert") + with TestAreaContext("batch_sim_stop") as test_area: + test_area.copy_parent_content(config_file) + res_config = ResConfig(user_config_file=os.path.basename(config_file)) + + rsim = BatchSimulator( + res_config, + {"WELL_ORDER": ["W1", "W2", "W3"], "WELL_ON_OFF": ["W1", "W2", "W3"]}, + ["ORDER", "ON_OFF"], + ) + + case_name = "TestCase42" + case_data = [ + ( + 2, + { + "WELL_ORDER": {"W1": 1, "W2": 2, "W3": 3}, + "WELL_ON_OFF": {"W1": 4, "W2": 5, "W3": 6}, + }, + ), + ( + 1, + { + "WELL_ORDER": {"W1": 7, "W2": 8, "W3": 9}, + "WELL_ON_OFF": {"W1": 10, "W2": 11, "W3": 12}, + }, + ), + ] + + # Starting a simulation which should actually run through. + ctx = rsim.start(case_name, case_data) + ctx.stop() + + status = ctx.status + + self.assertEqual(status.complete, 0) + self.assertEqual(status.running, 0) + for idx, _ in enumerate(case_data): + path = "storage/batch_sim/runpath/{}/realisation-{}/iter-0/realization.number".format( + case_name, idx + ) + self.assertTrue(os.path.isfile(path)) + with open(path, "r") as f: + self.assertEqual(f.readline(1), str(idx)) + + def assertContextStatusOddFailures(self, batch_ctx, final_state_only=False): + running_status = set( + ( + JobStatusType.JOB_QUEUE_WAITING, + JobStatusType.JOB_QUEUE_SUBMITTED, + JobStatusType.JOB_QUEUE_PENDING, + JobStatusType.JOB_QUEUE_RUNNING, + JobStatusType.JOB_QUEUE_UNKNOWN, + JobStatusType.JOB_QUEUE_EXIT, + JobStatusType.JOB_QUEUE_DONE, + ) + ) + + for idx in range(len(batch_ctx)): + status = batch_ctx.job_status(idx) + if not final_state_only and status in running_status: + continue + elif idx % 2 == 0: + self.assertEqual(JobStatusType.JOB_QUEUE_SUCCESS, status) + else: + self.assertEqual(JobStatusType.JOB_QUEUE_FAILED, status) + + @tmpdir() + def test_batch_ctx_status_failing_jobs(self): + + config_dir = os.path.join(self.SOURCE_ROOT, "test-data/local/batch_sim") + shutil.copytree(config_dir, "batch_sim") + os.chdir("batch_sim") + + self.assertTrue(os.path.isfile("batch_sim_sleep_and_fail.ert")) + res_config = ResConfig(user_config_file="batch_sim_sleep_and_fail.ert") + + external_parameters = { + "WELL_ORDER": ("W1", "W2", "W3"), + "WELL_ON_OFF": ("W1", "W2", "W3"), + } + results = ("ORDER", "ON_OFF") + rsim = BatchSimulator(res_config, external_parameters, results) + + cases = [ + ( + 0, + { + "WELL_ORDER": {"W1": idx + 1, "W2": idx + 2, "W3": idx + 3}, + "WELL_ON_OFF": {"W1": idx * 4, "W2": idx * 5, "W3": idx * 6}, + }, + ) + for idx in range(10) + ] + + batch_ctx = rsim.start("case_name", cases) + while batch_ctx.running(): + self.assertContextStatusOddFailures(batch_ctx) + time.sleep(1) + + self.assertContextStatusOddFailures(batch_ctx, final_state_only=True) + + +if __name__ == "__main__": + unittest.main() diff --git a/libres/tests/res/simulator/test_simulation_context.py b/libres/tests/res/simulator/test_simulation_context.py new file mode 100644 index 00000000000..0762dd9b683 --- /dev/null +++ b/libres/tests/res/simulator/test_simulation_context.py @@ -0,0 +1,80 @@ +import time +from tests import ResTest +from tests.utils import tmpdir +from ecl.util.util import BoolVector + +from res.test import ErtTestContext +from tests.utils import wait_until +from res.enkf.enums import RealizationStateEnum +from res.simulator import SimulationContext + + +class SimulationContextTest(ResTest): + @tmpdir() + def test_simulation_context(self): + config_file = self.createTestPath("local/batch_sim/sleepy_time.ert") + with ErtTestContext("res/sim/simulation_context", config_file) as test_context: + ert = test_context.getErt() + + size = 4 + even_mask = BoolVector(initial_size=size) + odd_mask = BoolVector(initial_size=size) + + for iens_2 in range(size // 2): + even_mask[2 * iens_2] = True + even_mask[2 * iens_2 + 1] = False + + odd_mask[2 * iens_2] = False + odd_mask[2 * iens_2 + 1] = True + + fs_manager = ert.getEnkfFsManager() + even_half = fs_manager.getFileSystem("even_half") + odd_half = fs_manager.getFileSystem("odd_half") + + # i represents geo_id + case_data = [(i, {}) for i in range(size)] + even_ctx = SimulationContext(ert, even_half, even_mask, 0, case_data) + odd_ctx = SimulationContext(ert, odd_half, odd_mask, 0, case_data) + + for iens in range(size): + # do we have the proper geo_id in run_args? + if iens % 2 == 0: + self.assertFalse(even_ctx.isRealizationFinished(iens)) + self.assertEqual(even_ctx.get_run_args(iens).geo_id, iens) + else: + self.assertFalse(odd_ctx.isRealizationFinished(iens)) + self.assertEqual(odd_ctx.get_run_args(iens).geo_id, iens) + + def any_is_running(): + return even_ctx.isRunning() or odd_ctx.isRunning() + + wait_until(func=(lambda: self.assertFalse(any_is_running())), timeout=90) + + self.assertEqual(even_ctx.getNumFailed(), 0) + self.assertEqual(even_ctx.getNumRunning(), 0) + self.assertEqual(even_ctx.getNumSuccess(), size / 2) + + self.assertEqual(odd_ctx.getNumFailed(), 0) + self.assertEqual(odd_ctx.getNumRunning(), 0) + self.assertEqual(odd_ctx.getNumSuccess(), size / 2) + + even_state_map = even_half.getStateMap() + odd_state_map = odd_half.getStateMap() + + for iens in range(size): + if iens % 2 == 0: + self.assertTrue(even_ctx.didRealizationSucceed(iens)) + self.assertFalse(even_ctx.didRealizationFail(iens)) + self.assertTrue(even_ctx.isRealizationFinished(iens)) + + self.assertEqual( + even_state_map[iens], RealizationStateEnum.STATE_HAS_DATA + ) + else: + self.assertTrue(odd_ctx.didRealizationSucceed(iens)) + self.assertFalse(odd_ctx.didRealizationFail(iens)) + self.assertTrue(odd_ctx.isRealizationFinished(iens)) + + self.assertEqual( + odd_state_map[iens], RealizationStateEnum.STATE_HAS_DATA + ) diff --git a/libres/tests/res/testcase/__init__.py b/libres/tests/res/testcase/__init__.py new file mode 100644 index 00000000000..5e08e797f52 --- /dev/null +++ b/libres/tests/res/testcase/__init__.py @@ -0,0 +1 @@ + diff --git a/libres/tests/res/testcase/test_mini_config.py b/libres/tests/res/testcase/test_mini_config.py new file mode 100644 index 00000000000..c83cf4c4c6e --- /dev/null +++ b/libres/tests/res/testcase/test_mini_config.py @@ -0,0 +1,44 @@ +from res.enkf.enums.realization_state_enum import RealizationStateEnum +from res.test import ErtTestContext +from tests import ResTest +from tests.utils import tmpdir + + +class MiniConfigTest(ResTest): + @tmpdir() + def test_failed_realizations(self): + + # mini_fail_config has the following realization success/failures: + # + # 0 OK + # 1 GenData report step 1 missing + # 2 GenData report step 2 missing, Forward Model Component Target File not found. + # 3 GenData report step 3 missing, Forward Model Component Target File not found. + # 4 GenData report step 1 missing + # 5 GenData report step 2 missing, Forward Model Component Target File not found. + # 6 GenData report step 3 missing + # 7 Forward Model Target File not found. + # 8 OK + # 9 OK + + config = self.createTestPath("local/mini_ert/mini_fail_config") + with ErtTestContext("python/enkf/data/mini_ert_simulated", config) as context: + ert = context.getErt() + + fs = ert.getEnkfFsManager().getCurrentFileSystem() + + realizations_list = fs.realizationList(RealizationStateEnum.STATE_HAS_DATA) + self.assertTrue(0 in realizations_list) + self.assertTrue(8 in realizations_list) + self.assertTrue(9 in realizations_list) + + realizations_list = fs.realizationList( + RealizationStateEnum.STATE_LOAD_FAILURE + ) + self.assertTrue(1 in realizations_list) + self.assertTrue(2 in realizations_list) + self.assertTrue(3 in realizations_list) + self.assertTrue(4 in realizations_list) + self.assertTrue(5 in realizations_list) + self.assertTrue(6 in realizations_list) + self.assertTrue(7 in realizations_list) diff --git a/libres/tests/res/testcase/test_testarea.py b/libres/tests/res/testcase/test_testarea.py new file mode 100644 index 00000000000..2814506ef12 --- /dev/null +++ b/libres/tests/res/testcase/test_testarea.py @@ -0,0 +1,37 @@ +# Copyright (C) 2015 Equinor ASA, Norway. +# +# The file 'test_testarea.py' 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. + +import os + +from ecl.util.test import TestAreaContext +from tests import ResTest + + +class TestTestArea(ResTest): + def test_test_area_ok(self): + orig_cwd = os.getcwd() + f_name = "this_is_not_a_file?" + self.assertFalse(os.path.isfile(f_name)) + + with TestAreaContext("give_me_a_tmp_dir"): + self.assertNotEqual(orig_cwd, os.getcwd()) + + f_name = os.path.join(os.getcwd(), f_name) + with open(f_name, "w") as f: + f.write("Some text") + self.assertTrue(os.path.isfile(f_name)) + + self.assertFalse(os.path.isfile(f_name)) diff --git a/libres/tests/res/testcase/test_testcase.py b/libres/tests/res/testcase/test_testcase.py new file mode 100644 index 00000000000..5e08b9c1dcf --- /dev/null +++ b/libres/tests/res/testcase/test_testcase.py @@ -0,0 +1,25 @@ +# Copyright (C) 2015 Equinor ASA, Norway. +# +# The file 'test_testcase.py' 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. + +import functools +import math +from tests import ResTest + + +class TestTestCase(ResTest): + def test_not_raises(self): + call_sin = functools.partial(math.sin, 0.5 * math.pi) + self.assertNotRaises(call_sin) diff --git a/libres/tests/res/util/__init__.py b/libres/tests/res/util/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/tests/res/util/test_log.py b/libres/tests/res/util/test_log.py new file mode 100644 index 00000000000..a55eea79e87 --- /dev/null +++ b/libres/tests/res/util/test_log.py @@ -0,0 +1,10 @@ +from res.util.enums import MessageLevelEnum + +from tests import ResTest + + +class LogTest(ResTest): + def test_enums(self): + self.assertEnumIsFullyDefined( + MessageLevelEnum, "message_level_type", "lib/include/ert/res_util/log.hpp" + ) diff --git a/libres/tests/res/util/test_matrix.py b/libres/tests/res/util/test_matrix.py new file mode 100644 index 00000000000..11863d90cf3 --- /dev/null +++ b/libres/tests/res/util/test_matrix.py @@ -0,0 +1,241 @@ +from ecl.util.util import RandomNumberGenerator +from ecl.util.enums import RngAlgTypeEnum, RngInitModeEnum +from ecl.util.test import TestAreaContext +from res.util import Matrix +from tests import ResTest +from cwrap import CFILE, BaseCClass, load, open as copen + + +class MatrixTest(ResTest): + def test_matrix(self): + m = Matrix(2, 3) + + self.assertEqual(m.rows(), 2) + self.assertEqual(m.columns(), 3) + + self.assertEqual(m[(0, 0)], 0) + + m[(1, 1)] = 1.5 + self.assertEqual(m[(1, 1)], 1.5) + + m[1, 0] = 5 + self.assertEqual(m[1, 0], 5) + + with self.assertRaises(TypeError): + m[5] = 5 + + with self.assertRaises(IndexError): + m[2, 0] = 0 + + with self.assertRaises(IndexError): + m[0, 3] = 0 + + def test_matrix_set(self): + m1 = Matrix(2, 2) + m1.setAll(99) + self.assertEqual(99, m1[0, 0]) + self.assertEqual(99, m1[1, 1]) + m2 = Matrix(2, 2, value=99) + self.assertEqual(m1, m2) + + def test_matrix_random_init(self): + m = Matrix(10, 10) + rng = RandomNumberGenerator(RngAlgTypeEnum.MZRAN, RngInitModeEnum.INIT_DEFAULT) + m.randomInit(rng) + + def test_matrix_copy_column(self): + m = Matrix(10, 2) + rng = RandomNumberGenerator(RngAlgTypeEnum.MZRAN, RngInitModeEnum.INIT_DEFAULT) + m.randomInit(rng) + + with self.assertRaises(ValueError): + m.copyColumn(0, 2) + + with self.assertRaises(ValueError): + m.copyColumn(2, 0) + + with self.assertRaises(ValueError): + m.copyColumn(-2, 0) + + m.copyColumn(1, 0) + for i in range(m.rows()): + self.assertEqual(m[i, 0], m[i, 1]) + + def test_matrix_scale(self): + m = Matrix(2, 2, value=1) + m.scaleColumn(0, 2) + self.assertEqual(2, m[0, 0]) + self.assertEqual(2, m[1, 0]) + + m.setAll(1) + m.scaleRow(1, 2) + self.assertEqual(2, m[1, 0]) + self.assertEqual(2, m[1, 1]) + + with self.assertRaises(IndexError): + m.scaleColumn(10, 99) + + with self.assertRaises(IndexError): + m.scaleRow(10, 99) + + def test_matrix_equality(self): + m = Matrix(2, 2) + m[0, 0] = 2 + m[1, 1] = 4 + + s = Matrix(2, 3) + s[0, 0] = 2 + s[1, 1] = 4 + + self.assertNotEqual(m, s) + + r = Matrix(2, 2) + r[0, 0] = 2 + r[1, 1] = 3 + + self.assertNotEqual(m, r) + + r[1, 1] = 4 + + self.assertEqual(m, r) + + def test_str(self): + m = Matrix(2, 2) + s = "%s" % m + + m[0, 0] = 0 + m[0, 1] = 1 + m[1, 0] = 2 + m[1, 1] = 3 + + with TestAreaContext("matrix_fprint"): + with copen("matrix.txt", "w") as f: + m.fprint(f) + + with open("matrix.txt") as f: + l1 = [float(x) for x in f.readline().split()] + l2 = [float(x) for x in f.readline().split()] + + self.assertEqual(l1[0], m[0, 0]) + self.assertEqual(l1[1], m[0, 1]) + self.assertEqual(l2[0], m[1, 0]) + self.assertEqual(l2[1], m[1, 1]) + + def test_copy_equal(self): + m1 = Matrix(2, 2) + m1[0, 0] = 0 + m1[0, 1] = 1 + m1[1, 0] = 2 + m1[1, 1] = 3 + + m2 = m1.copy() + self.assertTrue(m1 == m2) + + def test_sub_copy(self): + m1 = Matrix(3, 3) + rng = RandomNumberGenerator(RngAlgTypeEnum.MZRAN, RngInitModeEnum.INIT_DEFAULT) + m1.randomInit(rng) + + with self.assertRaises(ValueError): + m2 = m1.subCopy(0, 0, 4, 2) + + with self.assertRaises(ValueError): + m2 = m1.subCopy(0, 0, 2, 4) + + with self.assertRaises(ValueError): + m2 = m1.subCopy(4, 0, 1, 1) + + with self.assertRaises(ValueError): + m2 = m1.subCopy(0, 2, 1, 2) + + m2 = m1.subCopy(0, 0, 2, 2) + for i in range(2): + for j in range(2): + self.assertEqual(m1[i, j], m2[i, j]) + + def test_transpose(self): + m = Matrix(3, 2) + m[0, 0] = 0 + m[1, 0] = 2 + m[2, 0] = 4 + + m[0, 1] = 1 + m[1, 1] = 3 + m[2, 1] = 5 + + mt = m.transpose() + + self.assertEqual(m[0, 0], 0) + self.assertEqual(m[1, 0], 2) + self.assertEqual(m[2, 0], 4) + + self.assertEqual(m[0, 1], 1) + self.assertEqual(m[1, 1], 3) + self.assertEqual(m[2, 1], 5) + + self.assertEqual(mt.rows(), m.columns()) + self.assertEqual(mt.columns(), m.rows()) + self.assertEqual(mt[0, 0], 0) + self.assertEqual(mt[1, 0], 1) + + self.assertEqual(mt[0, 1], 2) + self.assertEqual(mt[1, 1], 3) + + self.assertEqual(mt[0, 2], 4) + self.assertEqual(mt[1, 2], 5) + + m.transpose(inplace=True) + self.assertEqual(m, mt) + + def test_matmul(self): + m1 = Matrix(3, 3) + m2 = Matrix(2, 2) + + with self.assertRaises(ValueError): + Matrix.matmul(m1, m2) + + m = Matrix(3, 2) + m[0, 0] = 0 + m[1, 0] = 2 + m[2, 0] = 4 + + m[0, 1] = 1 + m[1, 1] = 3 + m[2, 1] = 5 + + mt = m.transpose() + + m2 = Matrix.matmul(m, mt) + + self.assertEqual(m2[0, 0], 1) + self.assertEqual(m2[1, 1], 13) + self.assertEqual(m2[2, 2], 41) + + def test_csv(self): + m = Matrix(2, 2) + m[0, 0] = 2 + m[1, 1] = 4 + with TestAreaContext("matrix_csv"): + m.dumpCSV("matrix.csv") + + def test_identity(self): + m1 = Matrix.identity(1) + self.assertEqual(m1.rows(), 1) + self.assertEqual(m1.columns(), 1) + self.assertEqual(m1[0, 0], 1) + + with self.assertRaises(ValueError): + Matrix.identity(0) + with self.assertRaises(ValueError): + Matrix.identity(-3) + + m = Matrix.identity(17) + self.assertEqual(m.rows(), 17) + self.assertEqual(m.columns(), 17) + for i in range(17): + for j in range(17): + elt = m[i, j] + if i == j: + self.assertEqual(elt, 1) + else: + self.assertEqual(elt, 0) diff --git a/libres/tests/res/util/test_path_fmt.py b/libres/tests/res/util/test_path_fmt.py new file mode 100644 index 00000000000..a59c2defdf7 --- /dev/null +++ b/libres/tests/res/util/test_path_fmt.py @@ -0,0 +1,10 @@ +import os +from tests import ResTest +from res.util import PathFormat + + +class PathFmtTest(ResTest): + def test_create(self): + path_fmt = PathFormat("random/path/%d-%d") + self.assertIn("random/path", repr(path_fmt)) + self.assertTrue(str(path_fmt).startswith("PathFormat(")) diff --git a/libres/tests/res/util/test_res_log.py b/libres/tests/res/util/test_res_log.py new file mode 100644 index 00000000000..c54a7d4ec86 --- /dev/null +++ b/libres/tests/res/util/test_res_log.py @@ -0,0 +1,30 @@ +import os +from res.util import ResLog, Log +from ecl.util.test import TestAreaContext +from tests import ResTest +from res.util.enums import MessageLevelEnum + + +class ResLogTest(ResTest): + def test_log(self): + with TestAreaContext("python/res_log/log") as work_area: + test_log_filename = "test_log" + ResLog.init(1, test_log_filename, True) + message = "This is fun" + ResLog.log(1, message) + + self.assertTrue(os.path.isfile(test_log_filename)) + + with open(test_log_filename, "r") as f: + text = f.readlines() + self.assertTrue(len(text) > 0) + self.assertTrue(message in text[-1]) + + def test_get_filename(self): + with TestAreaContext("python/res_log/log") as work_area: + test_log_filename = "log_test_file.txt" + ResLog.init(1, test_log_filename, True) + message = "This is fun" + ResLog.log(1, message) + + self.assertEqual(ResLog.getFilename(), test_log_filename) diff --git a/libres/tests/res/util/test_stat.py b/libres/tests/res/util/test_stat.py new file mode 100644 index 00000000000..07e89447ca8 --- /dev/null +++ b/libres/tests/res/util/test_stat.py @@ -0,0 +1,30 @@ +from tests import ResTest +from ecl.util.util import DoubleVector +from res.util import polyfit + + +class StatTest(ResTest): + def test_polyfit(self): + x_list = DoubleVector() + y_list = DoubleVector() + S = DoubleVector() + + A = 7.25 + B = -4 + C = 0.025 + + x = 0 + dx = 0.1 + for i in range(100): + y = A + B * x + C * x * x + x_list.append(x) + y_list.append(y) + + x += dx + S.append(1.0) + + beta = polyfit(3, x_list, y_list, None) + + self.assertAlmostEqual(A, beta[0]) + self.assertAlmostEqual(B, beta[1]) + self.assertAlmostEqual(C, beta[2]) diff --git a/libres/tests/res/util/test_subprocess.py b/libres/tests/res/util/test_subprocess.py new file mode 100644 index 00000000000..c3717e16e86 --- /dev/null +++ b/libres/tests/res/util/test_subprocess.py @@ -0,0 +1,60 @@ +# Copyright (C) 2019 Equinor ASA, Norway. +# +# The file 'test_subprocess.py' 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. + + +import unittest +import os +from tests.utils import tmpdir +from subprocess import Popen, PIPE + +from res.util.subprocess import await_process_tee + + +class TestSubprocess(unittest.TestCase): + @tmpdir() + def test_await_process_tee(self): + with open("a", "wb") as a_fh, open("b", "wb") as b_fh: + process = Popen(["/bin/cat", "/bin/cat"], stdout=PIPE) + await_process_tee(process, a_fh, b_fh) + + with open("a", "rb") as f: + a_content = f.read() + with open("b", "rb") as f: + b_content = f.read() + with open("/bin/cat", "rb") as f: + cat_content = f.read() + + self.assertTrue(process.stdout.closed) + self.assertEqual(cat_content, a_content) + self.assertEqual(cat_content, b_content) + + @tmpdir() + def test_await_process_finished_tee(self): + with open("a", "wb") as a_fh, open("b", "wb") as b_fh: + process = Popen(["/bin/cat", "/bin/cat"], stdout=PIPE) + process.wait() + await_process_tee(process, a_fh, b_fh) + + with open("a", "rb") as f: + a_content = f.read() + with open("b", "rb") as f: + b_content = f.read() + with open("/bin/cat", "rb") as f: + cat_content = f.read() + + self.assertTrue(process.stdout.closed) + self.assertEqual(cat_content, a_content) + self.assertEqual(cat_content, b_content) diff --git a/libres/tests/res/util/test_substitution_list.py b/libres/tests/res/util/test_substitution_list.py new file mode 100644 index 00000000000..b4a765e8f5e --- /dev/null +++ b/libres/tests/res/util/test_substitution_list.py @@ -0,0 +1,41 @@ +from tests import ResTest +from res.util.substitution_list import SubstitutionList + + +class SubstitutionListTest(ResTest): + def test_substitution_list(self): + subst_list = SubstitutionList() + + subst_list.addItem("Key", "Value", "Doc String") + + self.assertEqual(len(subst_list), 1) + + with self.assertRaises(KeyError): + item = subst_list[2] + + with self.assertRaises(KeyError): + item = subst_list["NoSuchKey"] + + with self.assertRaises(KeyError): + item = subst_list.doc("NoSuchKey") + + self.assertTrue("Key" in subst_list) + self.assertEqual(subst_list["Key"], "Value") + self.assertEqual(subst_list.doc("Key"), "Doc String") + + subst_list.addItem("Key2", "Value2", "Doc String2") + self.assertEqual(len(subst_list), 2) + + keys = subst_list.keys() + self.assertEqual(keys[0], "Key") + self.assertEqual(keys[1], "Key2") + + self.assertIn("Key", str(subst_list)) + self.assertIn("SubstitutionList", repr(subst_list)) + self.assertIn("2", repr(subst_list)) + + self.assertEqual(1729, subst_list.get("nosuchkey", 1729)) + self.assertIsNone(subst_list.get("nosuchkey")) + self.assertIsNone(subst_list.get(513)) + for key in ("Key", "Key2"): + self.assertEqual(subst_list[key], subst_list.get(key)) diff --git a/libres/tests/res/util/test_ui_return.py b/libres/tests/res/util/test_ui_return.py new file mode 100644 index 00000000000..7d299410b91 --- /dev/null +++ b/libres/tests/res/util/test_ui_return.py @@ -0,0 +1,75 @@ +import os + +from res.util import UIReturn +from res.util.enums import UIReturnStatusEnum +from tests import ResTest + + +class UIReturnTest(ResTest): + def test_create(self): + ui_return = UIReturn(UIReturnStatusEnum.UI_RETURN_OK) + self.assertTrue(ui_return) + + ui_return = UIReturn(UIReturnStatusEnum.UI_RETURN_FAIL) + self.assertFalse(ui_return) + + self.assertEqual(0, len(ui_return)) + + def test_help(self): + ui_return = UIReturn(UIReturnStatusEnum.UI_RETURN_OK) + self.assertEqual("", ui_return.help_text()) + + ui_return.add_help("Help1") + self.assertEqual("Help1", ui_return.help_text()) + + ui_return.add_help("Help2") + self.assertEqual("Help1 Help2", ui_return.help_text()) + + def test_error_raises_OK(self): + ui_return = UIReturn(UIReturnStatusEnum.UI_RETURN_OK) + with self.assertRaises(ValueError): + ui_return.add_error("Error1") + + with self.assertRaises(ValueError): + ui_return.last_error() + + with self.assertRaises(ValueError): + ui_return.first_error() + + def test_add_error(self): + ui_return = UIReturn(UIReturnStatusEnum.UI_RETURN_FAIL) + ui_return.add_error("Error1") + ui_return.add_error("Error2") + ui_return.add_error("Error3") + self.assertEqual(3, len(ui_return)) + + self.assertEqual("Error1", ui_return.first_error()) + self.assertEqual("Error3", ui_return.last_error()) + + def test_iget_error(self): + ui_return = UIReturn(UIReturnStatusEnum.UI_RETURN_FAIL) + ui_return.add_error("Error1") + ui_return.add_error("Error2") + ui_return.add_error("Error3") + + errorList = [] + for index in range(len(ui_return)): + errorList.append(ui_return.iget_error(index)) + self.assertEqual(errorList, ["Error1", "Error2", "Error3"]) + + with self.assertRaises(TypeError): + ui_return.iget_error("XX") + + ui_return = UIReturn(UIReturnStatusEnum.UI_RETURN_OK) + errorList = [] + for index in range(len(ui_return)): + errorList.append(ui_return.iget_error(index)) + self.assertEqual(errorList, []) + + def test_status_enum(self): + source_file_path = os.path.join( + "lib", "include", "ert", "res_util", "ui_return.hpp" + ) + self.assertEnumIsFullyDefined( + UIReturnStatusEnum, "ui_return_status_enum", source_file_path + ) diff --git a/libres/tests/res/util/test_version.py b/libres/tests/res/util/test_version.py new file mode 100644 index 00000000000..af21a2d707e --- /dev/null +++ b/libres/tests/res/util/test_version.py @@ -0,0 +1,8 @@ +from tests import ResTest +from res import ResVersion + + +# This is only an import test. +class VersionTest(ResTest): + def test(self): + pass diff --git a/libres/tests/share/__init__.py b/libres/tests/share/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libres/tests/share/test_shell_jobs.py b/libres/tests/share/test_shell_jobs.py new file mode 100644 index 00000000000..64a8c52d9d6 --- /dev/null +++ b/libres/tests/share/test_shell_jobs.py @@ -0,0 +1,53 @@ +from res.enkf import ResConfig + +from tests import ResTest + + +class TestSiteConfigShellJobs(ResTest): + def test_shell_script_jobs_availability(self): + config_file = self.createTestPath("local/simple_config/minimum_config") + + res_config = ResConfig(config_file) + site_config = res_config.site_config + + installed_jobs = site_config.get_installed_jobs() + fm_shell_jobs = {} + for job in installed_jobs: + exe = job.get_executable() + if "shell_scripts" in exe: + fm_shell_jobs[job.name().upper()] = exe + + list_from_content = res_config.ert_workflow_list + wf_shell_jobs = {} + for wf_name in list_from_content.getJobNames(): + exe = list_from_content.getJob(wf_name).executable() + if exe and "shell_scripts" in exe: + wf_shell_jobs[wf_name] = exe + + assert fm_shell_jobs == wf_shell_jobs + + def test_shell_script_jobs_names(self): + config_file = self.createTestPath("local/simple_config/minimum_config") + + shell_job_names = [ + "DELETE_FILE", + "DELETE_DIRECTORY", + "COPY_DIRECTORY", + "MAKE_SYMLINK", + "MOVE_FILE", + "MAKE_DIRECTORY", + "CAREFUL_COPY_FILE", + "SYMLINK", + "COPY_FILE", + ] + + res_config = ResConfig(config_file) + found_jobs = set() + list_from_content = res_config.ert_workflow_list + for wf_name in list_from_content.getJobNames(): + exe = list_from_content.getJob(wf_name).executable() + if exe and "shell_scripts" in exe: + assert wf_name in shell_job_names + found_jobs.add(wf_name) + + assert len(shell_job_names) == len(found_jobs) diff --git a/libres/tests/share/test_synthesizer.py b/libres/tests/share/test_synthesizer.py new file mode 100644 index 00000000000..a46e81f90f7 --- /dev/null +++ b/libres/tests/share/test_synthesizer.py @@ -0,0 +1,350 @@ +import sys +import os + +from tests import ResTest + +try: + from res.test.synthesizer import OilSimulator +except ImportError as e: + share_lib_path = os.path.join(ResTest.createSharePath("lib")) + + sys.path.insert(0, share_lib_path) + synthesizer_module = __import__("synthesizer") + OilSimulator = synthesizer_module.OilSimulator + sys.path.pop(0) + +if sys.version_info >= (3, 2): + # version >= 3.2 random got a refactoring in 3.2 + EXPECTED_VALUES = [ + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0], + [ + 0.36223552960708655, + 0.36223552960708655, + 0.25228270813778375, + 0.25228270813778375, + 0.0035589166489957804, + 0.0035589166489957804, + 0.009729280161088842, + 0.6964604173738352, + 0.36223552960708655, + 0.25228270813778375, + 0.0035589166489957804, + 0.6964604173738352, + 0.009729280161088842, + 0.8467167800173389, + ], + [ + 0.7399853327970171, + 1.1022208624041037, + 0.5415917925800195, + 0.7938745007178033, + 0.005031386203683649, + 0.008590302852679428, + 0.006753386971546496, + 0.7318953073473712, + 0.7399853327970171, + 0.5415917925800195, + 0.005031386203683649, + 0.7318953073473712, + 0.006753386971546496, + 0.6842612650326583, + ], + [ + 0.6931433336847672, + 1.7953641960888709, + 0.5489089668696403, + 1.3427834675874437, + 0.07168010746080981, + 0.08027041031348925, + 0.09372111732538566, + 0.7919126394127266, + 0.6931433336847672, + 0.5489089668696403, + 0.07168010746080981, + 0.7919126394127266, + 0.09372111732538566, + 0.6050461737304799, + ], + [ + 0.5399385912999576, + 2.3353027873888283, + 0.6765262712266249, + 2.0193097388140684, + 0.18058175414227, + 0.26085216445575926, + 0.2506268633281076, + 1.2529689155906016, + 0.5399385912999576, + 0.6765262712266249, + 0.18058175414227, + 1.2529689155906016, + 0.2506268633281076, + 0.4889715173355835, + ], + [ + 0.26859437948706744, + 2.6038971668758957, + 0.7114414779650978, + 2.7307512167791663, + 0.2429638618796885, + 0.5038160263354478, + 0.4749485830402218, + 2.648757875439285, + 0.26859437948706744, + 0.7114414779650978, + 0.2429638618796885, + 2.648757875439285, + 0.4749485830402218, + 0.45492528299297097, + ], + [ + 0.15577087834941575, + 2.7596680452253115, + 0.6075067291135949, + 3.338257945892761, + 0.38084454641982823, + 0.884660572755276, + 0.709715988100043, + 3.9000019487010458, + 0.15577087834941575, + 0.6075067291135949, + 0.38084454641982823, + 3.9000019487010458, + 0.709715988100043, + 0.37306808399768776, + ], + [ + 0.15474693115262986, + 2.9144149763779414, + 0.5480152851776219, + 3.886273231070383, + 0.6549972409469127, + 1.5396578137021888, + 0.808894047670149, + 3.5413644787378944, + 0.15474693115262986, + 0.5480152851776219, + 0.6549972409469127, + 3.5413644787378944, + 0.808894047670149, + 0.29196857718168495, + ], + [ + 0.16480127666696362, + 3.079216253044905, + 0.5131817827087846, + 4.399455013779168, + 0.686406202004116, + 2.2260640157063047, + 0.8063911786533474, + 3.11394300510087, + 0.16480127666696362, + 0.5131817827087846, + 0.686406202004116, + 3.11394300510087, + 0.8063911786533474, + 0.2041654171004479, + ], + [ + 0.041212696700238804, + 3.120428949745144, + 0.37426453070047244, + 4.77371954447964, + 0.8481645277841262, + 3.074228543490431, + 0.8945330719830857, + 3.7426453070047243, + 0.041212696700238804, + 0.37426453070047244, + 0.8481645277841262, + 3.7426453070047243, + 0.8945330719830857, + 0.09156245545255788, + ], + ] +else: + # version < 3.2 + EXPECTED_VALUES = [ + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0], + [ + 0.3359771423145687, + 0.3359771423145687, + 0.25672494192349865, + 0.25672494192349865, + 0.010039005455891323, + 0.010039005455891323, + 0.029013112597713192, + 0.7641143089523995, + 0.3359771423145687, + 0.25672494192349865, + 0.010039005455891323, + 0.7641143089523995, + 0.029013112597713192, + 0.8462347957619747, + ], + [ + 0.7252470407619624, + 1.0612241830765312, + 0.5175173529594699, + 0.7742422948829686, + 0.017973831236885583, + 0.028012836692776905, + 0.02418370085402209, + 0.7135738912023045, + 0.7252470407619624, + 0.5175173529594699, + 0.017973831236885583, + 0.7135738912023045, + 0.02418370085402209, + 0.6888364145396828, + ], + [ + 0.7723163496234255, + 1.8335405326999568, + 0.5742386073607806, + 1.3484809022437492, + 0.11041673583737899, + 0.1384295725301559, + 0.12508507685507134, + 0.7435277106858791, + 0.7723163496234255, + 0.5742386073607806, + 0.11041673583737899, + 0.7435277106858791, + 0.12508507685507134, + 0.6403046565762696, + ], + [ + 0.6038799675664164, + 2.437420500266373, + 0.6888868738548185, + 2.037367776098568, + 0.267892132439122, + 0.4063217049692779, + 0.3072960610203287, + 1.140767885762087, + 0.6038799675664164, + 0.6888868738548185, + 0.267892132439122, + 1.140767885762087, + 0.3072960610203287, + 0.5205364945011657, + ], + [ + 0.23016535126253962, + 2.6675858515289126, + 0.721655666522216, + 2.7590234426207836, + 0.35552466124555465, + 0.7618463662148325, + 0.6070184801736589, + 3.135379250454838, + 0.23016535126253962, + 0.721655666522216, + 0.35552466124555465, + 3.135379250454838, + 0.6070184801736589, + 0.4800677649914682, + ], + [ + 0.026293782934652718, + 2.693879634463565, + 0.7131990780527108, + 3.4722225206734945, + 0.6392372725163122, + 1.4010836387311447, + 0.8647254356377257, + 7.131990780527107, + 0.026293782934652718, + 0.7131990780527108, + 0.6392372725163122, + 7.131990780527107, + 0.8647254356377257, + 0.3872974839053025, + ], + [ + 0.0, + 2.693879634463565, + 0.8676997908824122, + 4.339922311555907, + 0.8580356376693129, + 2.2591192764004577, + 0.8956197493411856, + 8.676997908824122, + 0.0, + 0.8676997908824122, + 0.8580356376693129, + 8.676997908824122, + 0.8956197493411856, + 0.22557165737149715, + ], + [ + 0.10560669451549878, + 2.799486328979064, + 0.869082212788759, + 5.209004524344666, + 0.8903674796589355, + 3.1494867560593933, + 0.8939664328113363, + 8.229423492288294, + 0.10560669451549878, + 0.869082212788759, + 0.8903674796589355, + 8.229423492288294, + 0.8939664328113363, + 0.1340241573819292, + ], + [ + 0.08615885630000791, + 2.885645185279072, + 0.44074890315982446, + 5.64975342750449, + 0.9425699260811738, + 4.0920566821405675, + 0.9040831722665535, + 4.407489031598244, + 0.08615885630000791, + 0.44074890315982446, + 0.9425699260811738, + 4.407489031598244, + 0.9040831722665535, + 0.13404047971467026, + ], + ] + + +class SynthesizerTest(ResTest): + def test_oil_simulator(self): + sim = OilSimulator() + sim.addWell("OP1", seed=1) + sim.addBlock("6,6,6", seed=2) + + for report_step in range(10): + sim.step(scale=1.0 / 10.0) + + values = [ + sim.fopr(), + sim.fopt(), + sim.fgpr(), + sim.fgpt(), + sim.fwpr(), + sim.fwpt(), + sim.fwct(), + sim.fgor(), + sim.opr("OP1"), + sim.gpr("OP1"), + sim.wpr("OP1"), + sim.gor("OP1"), + sim.wct("OP1"), + sim.bpr("6,6,6"), + ] + + self.assertAlmostEqual(values[0], values[8]) # fopr = opr:op1 + self.assertAlmostEqual(values[2], values[9]) # fgpr = gpr:op1 + self.assertAlmostEqual(values[4], values[10]) # fwpr = wpr:op1 + + self.assertAlmostEqual(sim.foip(), sim.ooip - sim.fopt()) + self.assertAlmostEqual(sim.fgip(), sim.goip - sim.fgpt()) + self.assertAlmostEqual(sim.fwip(), sim.woip - sim.fwpt()) + + self.assertAlmostEqualList(values, EXPECTED_VALUES[report_step]) diff --git a/libres/tests/utils/__init__.py b/libres/tests/utils/__init__.py new file mode 100644 index 00000000000..b3e1318fd18 --- /dev/null +++ b/libres/tests/utils/__init__.py @@ -0,0 +1,133 @@ +import asyncio +import contextlib +import logging +import os +import tempfile +import shutil +import threading +from functools import partial + +import decorator +import time + +import websockets +from job_runner.util.client import Client + +""" +Swiped from +https://github.com/equinor/everest/blob/master/tests/utils/__init__.py +""" + + +def tmpdir(path=None, teardown=True): + """Decorator based on the `tmp` context""" + + def real_decorator(function): + def wrapper(function, *args, **kwargs): + with tmp(path, teardown=teardown): + return function(*args, **kwargs) + + return decorator.decorator(wrapper, function) + + return real_decorator + + +@contextlib.contextmanager +def tmp(path=None, teardown=True): + """Create and go into tmp directory, returns the path. + This function creates a temporary directory and enters that directory. The + returned object is the path to the created directory. + If @path is not specified, we create an empty directory, otherwise, it must + be a path to an existing directory. In that case, the directory will be + copied into the temporary directory. + If @teardown is True (defaults to True), the directory is (attempted) + deleted after context, otherwise it is kept as is. + """ + cwd = os.getcwd() + fname = tempfile.NamedTemporaryFile().name + + if path: + if not os.path.isdir(path): + logging.debug("tmp:raise no such path") + raise IOError("No such directory: %s" % path) + shutil.copytree(path, fname) + else: + # no path to copy, create empty dir + os.mkdir(fname) + + os.chdir(fname) + + yield fname # give control to caller scope + + os.chdir(cwd) + + if teardown: + try: + shutil.rmtree(fname) + except OSError as oserr: + logging.debug("tmp:rmtree failed %s (%s)" % (fname, oserr)) + shutil.rmtree(fname, ignore_errors=True) + + +def wait_until(func, interval=0.5, timeout=30): + """Expects 'func' to raise an AssertionError to indicate failure. + Repeatedly calls 'func' until it does not throw an AssertionError. + Waits 'interval' seconds before each invocation. If 'timeout' is + reached, will raise the AssertionError. + + Example of how to wait for a file to be created: + + wait_until(lambda: assertFileExists("/some/file"))""" + t = 0 + while True: + time.sleep(interval) + t += interval + try: + func() + return + except AssertionError: + if t >= timeout: + raise AssertionError( + "Timeout reached in wait_until (function {%s}, timeout {%d}) when waiting for assertion.".format( + func.__name__, timeout + ) + ) + + +def _mock_ws(host, port, messages, delay_startup=0): + loop = asyncio.new_event_loop() + done = loop.create_future() + + async def _handler(websocket, path): + while True: + msg = await websocket.recv() + messages.append(msg) + if msg == "stop": + done.set_result(None) + break + + async def _run_server(): + await asyncio.sleep(delay_startup) + async with websockets.serve(_handler, host, port): + await done + + loop.run_until_complete(_run_server()) + loop.close() + + +@contextlib.contextmanager +def _mock_ws_thread(host, port, messages): + mock_ws_thread = threading.Thread( + target=partial(_mock_ws, messages=messages), + args=( + host, + port, + ), + ) + mock_ws_thread.start() + yield + url = f"ws://{host}:{port}" + with Client(url) as client: + client.send("stop") + mock_ws_thread.join() + messages.pop() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000000..930320fbc4e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,2 @@ +[build-system] +requires = ["setuptools", "setuptools_scm", "wheel", "scikit-build", "cmake", "ninja", "ecl"] diff --git a/res/__init__.py b/res/__init__.py new file mode 100644 index 00000000000..996f8cc9abb --- /dev/null +++ b/res/__init__.py @@ -0,0 +1,106 @@ +# Copyright (C) 2011 Equinor ASA, Norway. +# +# The file '__init__.py' 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. +""" +Ert - Ensemble Reservoir Tool - a package for reservoir modeling. +""" +import os.path +import sys +import platform +import ecl + +import warnings + +warnings.filterwarnings(action="always", category=DeprecationWarning, module=r"res|ert") + +from cwrap import Prototype + +try: + from ._version import version as __version__ +except ImportError: + pass + + +def _load_lib(): + import ctypes + + # Find and dlopen libres + lib_path = os.path.join(os.path.dirname(__file__), ".libs") + if not os.path.isdir(lib_path): + lib_path = "" + + if platform.system() == "Linux": + lib_path = os.path.join(lib_path, "libres.so") + elif platform.system() == "Darwin": + lib_path = os.path.join(lib_path, "libres.dylib") + else: + raise NotImplementedError("Invalid platform") + + lib = ctypes.CDLL(lib_path, ctypes.RTLD_GLOBAL) + + # Configure site_config to be a ctypes.CFUNCTION with type: + # void set_site_config(char *); + site_config = lib.set_site_config + site_config.restype = None + site_config.argtypes = (ctypes.c_char_p,) + + # Find share/ert + from pathlib import Path + + path = Path(__file__).parent + for p in path.parents: + npath = p / "share" / "ert" / "site-config" + if npath.is_file(): + path = npath + break + else: + raise ImportError("Could not find `share/ert/site-config`") + + # Set site-config to point to [PREFIX]/share/ert/site-config + site_config(str(path).encode("utf-8")) + + # Configure set_analysis_modules_dir to be a ctypes.CFUNCTION with type: + # void set_analysis_modules_dir(char *); + set_analysis_modules_dir = lib.set_analysis_modules_dir + set_analysis_modules_dir.restype = None + set_analysis_modules_dir.argtypes = (ctypes.c_char_p,) + + # Set analysis modules dir to be [CURRENT DIR]/.libs + path = os.path.join(os.path.dirname(__file__), ".libs") + set_analysis_modules_dir(path.encode("utf-8")) + + return lib + + +class ResPrototype(Prototype): + lib = _load_lib() + + def __init__(self, prototype, bind=True): + super(ResPrototype, self).__init__(ResPrototype.lib, prototype, bind=bind) + + +RES_LIB = ResPrototype.lib + +from res.util import ResVersion +from ecl.util.util import updateAbortSignals + +updateAbortSignals() + + +def root(): + """ + Will print the filesystem root of the current ert package. + """ + return os.path.abspath(os.path.join(os.path.dirname(__file__), "../")) diff --git a/res/analysis/__init__.py b/res/analysis/__init__.py new file mode 100644 index 00000000000..d42628b07bb --- /dev/null +++ b/res/analysis/__init__.py @@ -0,0 +1,24 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file '__init__.py' 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. + + +import res +from cwrap import Prototype +import ecl.util + +from .enums import AnalysisModuleOptionsEnum, AnalysisModuleLoadStatusEnum +from .analysis_module import AnalysisModule +from .linalg import Linalg diff --git a/res/analysis/analysis_module.py b/res/analysis/analysis_module.py new file mode 100644 index 00000000000..dbdfdb214b7 --- /dev/null +++ b/res/analysis/analysis_module.py @@ -0,0 +1,275 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'analysis_module.py' 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. + +from cwrap import BaseCClass +from ecl.util.util.rng import RandomNumberGenerator +from res import ResPrototype +from os import path + +import res +from res.util import Matrix + + +class AnalysisModule(BaseCClass): + TYPE_NAME = "analysis_module" + + _alloc_external = ResPrototype( + "void* analysis_module_alloc_external(char*)", bind=False + ) + _alloc_internal = ResPrototype( + "void* analysis_module_alloc_internal(char*)", bind=False + ) + _free = ResPrototype("void analysis_module_free(analysis_module)") + _get_lib_name = ResPrototype("char* analysis_module_get_lib_name(analysis_module)") + _get_module_internal = ResPrototype( + "bool analysis_module_internal(analysis_module)" + ) + _set_var = ResPrototype( + "bool analysis_module_set_var(analysis_module, char*, char*)" + ) + _get_table_name = ResPrototype( + "char* analysis_module_get_table_name(analysis_module)" + ) + _get_name = ResPrototype("char* analysis_module_get_name(analysis_module)") + _check_option = ResPrototype( + "bool analysis_module_check_option(analysis_module, analysis_module_options_enum)" + ) + _has_var = ResPrototype("bool analysis_module_has_var(analysis_module, char*)") + _get_double = ResPrototype( + "double analysis_module_get_double(analysis_module, char*)" + ) + _get_int = ResPrototype("int analysis_module_get_int(analysis_module, char*)") + _get_bool = ResPrototype("bool analysis_module_get_bool(analysis_module, char*)") + _get_str = ResPrototype("char* analysis_module_get_ptr(analysis_module, char*)") + _init_update = ResPrototype( + "void analysis_module_init_update(analysis_module, bool_vector, bool_vector, matrix, matrix, matrix, matrix, matrix, rng)" + ) + _updateA = ResPrototype( + "void analysis_module_updateA(analysis_module, matrix, matrix, matrix, matrix, matrix, matrix, void*, rng)" + ) + _initX = ResPrototype( + "void analysis_module_initX(analysis_module, matrix, matrix, matrix, matrix, matrix, matrix, matrix, rng)" + ) + + # The VARIABLE_NAMES field is a completly broken special case + # which only applies to the rml module. + VARIABLE_NAMES = { + "IES_MAX_STEPLENGTH": { + "type": float, + "description": "Max step Length of Gauss Newton Iteration", + }, + "IES_MIN_STEPLENGTH": { + "type": float, + "description": "Min step Length of Gauss Newton Iteration", + }, + "IES_DEC_STEPLENGTH": { + "type": float, + "description": "Decline of step Length in Gauss Newton Iteration", + }, + "IES_INVERSION": {"type": int, "description": "Inversion algorithm"}, + "IES_DEBUG": { + "type": bool, + "description": "Print extensive log for IES analysis steps", + }, + "IES_LOGFILE": {"type": str, "description": "IES Log File"}, + "IES_AAPROJECTION": { + "type": str, + "description": "Include projection Y (A^+A) for n +# for more details. + + +from cwrap import BaseCEnum + + +class AnalysisModuleLoadStatusEnum(BaseCEnum): + TYPE_NAME = "analysis_module_load_status_enum" + LOAD_OK = None + DLOPEN_FAILURE = None + LOAD_SYMBOL_TABLE_NOT_FOUND = None + + +AnalysisModuleLoadStatusEnum.addEnum("LOAD_OK", 0) +AnalysisModuleLoadStatusEnum.addEnum("DLOPEN_FAILURE", 1) +AnalysisModuleLoadStatusEnum.addEnum("LOAD_SYMBOL_TABLE_NOT_FOUND", 2) diff --git a/res/analysis/enums/analysis_module_options_enum.py b/res/analysis/enums/analysis_module_options_enum.py new file mode 100644 index 00000000000..fa4811c6ad5 --- /dev/null +++ b/res/analysis/enums/analysis_module_options_enum.py @@ -0,0 +1,32 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'analysis_module_options_enum.py' 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. +from cwrap import BaseCEnum + + +class AnalysisModuleOptionsEnum(BaseCEnum): + TYPE_NAME = "analysis_module_options_enum" + ANALYSIS_NEED_ED = None + ANALYSIS_USE_A = None + ANALYSIS_UPDATE_A = None + ANALYSIS_SCALE_DATA = None + ANALYSIS_ITERABLE = None + + +AnalysisModuleOptionsEnum.addEnum("ANALYSIS_NEED_ED", 1) +AnalysisModuleOptionsEnum.addEnum("ANALYSIS_USE_A", 4) +AnalysisModuleOptionsEnum.addEnum("ANALYSIS_UPDATE_A", 8) +AnalysisModuleOptionsEnum.addEnum("ANALYSIS_SCALE_DATA", 16) +AnalysisModuleOptionsEnum.addEnum("ANALYSIS_ITERABLE", 32) diff --git a/res/analysis/linalg.py b/res/analysis/linalg.py new file mode 100644 index 00000000000..deb462c7304 --- /dev/null +++ b/res/analysis/linalg.py @@ -0,0 +1,38 @@ +# Copyright (C) 2015 Equinor ASA, Norway. +# +# The file 'linalg.py' 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. + +from cwrap import BaseCClass +from res import ResPrototype + + +__all__ = ["numPC"] + + +class Linalg(BaseCClass): + """ + The linalg class is a purely static class which mainly serves as a + namespace for a collection of ensemble based linear algebra + methods. + """ + + _get_num_PC = ResPrototype("int enkf_linalg_num_PC( matrix , double)", bind=False) + + @staticmethod + def numPC(S, truncation): + if 0 < truncation <= 1: + return Linalg._get_num_PC(S, truncation) + else: + raise ValueError("truncation must be in the interval (0,1]") diff --git a/res/config/__init__.py b/res/config/__init__.py new file mode 100644 index 00000000000..f0575d5e00d --- /dev/null +++ b/res/config/__init__.py @@ -0,0 +1,27 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file '__init__.py' 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. +from cwrap import Prototype +import res + + +from .config_path_elm import ConfigPathElm +from .unrecognized_enum import UnrecognizedEnum +from .content_type_enum import ContentTypeEnum +from .config_error import ConfigError +from .schema_item import SchemaItem +from .config_content import ConfigContent, ContentItem, ContentNode +from .config_parser import ConfigParser +from .config_settings import ConfigSettings diff --git a/res/config/config_content.py b/res/config/config_content.py new file mode 100644 index 00000000000..4dd95ce5aca --- /dev/null +++ b/res/config/config_content.py @@ -0,0 +1,268 @@ +# Copyright (C) 2015 Equinor ASA, Norway. +# +# The file 'config_content.py' 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. + +import os.path +from res import ResPrototype +from res.config import UnrecognizedEnum, ContentTypeEnum, ConfigError, SchemaItem +from cwrap import BaseCClass + + +class ContentNode(BaseCClass): + TYPE_NAME = "content_node" + + _iget = ResPrototype("char* config_content_node_iget( content_node , int)") + _size = ResPrototype("int config_content_node_get_size( content_node )") + _get_full_string = ResPrototype( + "char* config_content_node_get_full_string( content_node , char* )" + ) + _iget_type = ResPrototype( + "config_content_type_enum config_content_node_iget_type( content_node , int)" + ) + _iget_as_abspath = ResPrototype( + "char* config_content_node_iget_as_abspath( content_node , int)" + ) + _iget_as_relpath = ResPrototype( + "char* config_content_node_iget_as_relpath( content_node , int)" + ) + _iget_as_string = ResPrototype( + "char* config_content_node_iget( content_node , int)" + ) + _iget_as_int = ResPrototype( + "int config_content_node_iget_as_int( content_node , int)" + ) + _iget_as_double = ResPrototype( + "double config_content_node_iget_as_double( content_node , int)" + ) + _iget_as_path = ResPrototype( + "char* config_content_node_iget_as_path( content_node , int)" + ) + _iget_as_bool = ResPrototype( + "bool config_content_node_iget_as_bool( content_node , int)" + ) + _iget_as_isodate = ResPrototype( + "time_t config_content_node_iget_as_isodate( content_node , int)" + ) + + typed_get = { + ContentTypeEnum.CONFIG_STRING: _iget_as_string, + ContentTypeEnum.CONFIG_INT: _iget_as_int, + ContentTypeEnum.CONFIG_FLOAT: _iget_as_double, + ContentTypeEnum.CONFIG_PATH: _iget_as_path, + ContentTypeEnum.CONFIG_EXISTING_PATH: _iget_as_path, + ContentTypeEnum.CONFIG_BOOL: _iget_as_bool, + ContentTypeEnum.CONFIG_ISODATE: _iget_as_isodate, + } + + def __init__(self): + raise NotImplementedError("Class can not be instantiated directly!") + + def __len__(self): + return self._size() + + def __assertIndex(self, index): + if isinstance(index, int): + if index < 0: + index += len(self) + + if not 0 <= index < len(self): + raise IndexError + return index + else: + raise TypeError("Invalid argument type: %s" % index) + + def __getitem__(self, index): + index = self.__assertIndex(index) + + content_type = self._iget_type(index) + typed_get = self.typed_get[content_type] + return typed_get(self, index) + + def getPath(self, index=0, absolute=True, relative_start=None): + index = self.__assertIndex(index) + content_type = self._iget_type(index) + if content_type in [ + ContentTypeEnum.CONFIG_EXISTING_PATH, + ContentTypeEnum.CONFIG_PATH, + ]: + if absolute: + return self._iget_as_abspath(index) + else: + if relative_start is None: + return self._iget_as_relpath(index) + else: + abs_path = self._iget_as_abspath(index) + return os.path.relpath(abs_path, relative_start) + else: + raise TypeError("The getPath() method can only be called on PATH items") + + def content(self, sep=" "): + return self._get_full_string(sep) + + def igetString(self, index): + index = self.__assertIndex(index) + return self._iget(index) + + def asList(self): + return [x for x in self] + + +class ContentItem(BaseCClass): + TYPE_NAME = "content_item" + + _alloc = ResPrototype( + "void* config_content_item_alloc( schema_item , void* )", bind=False + ) + _size = ResPrototype("int config_content_item_get_size( content_item )") + _iget_content_node = ResPrototype( + "content_node_ref config_content_item_iget_node( content_item , int)" + ) + _free = ResPrototype("void config_content_item_free( content_item )") + + def __init__(self, schema_item): + path_elm = None + c_ptr = self._alloc(schema_item, path_elm) + super(ContentItem, self).__init__(c_ptr) + + def __len__(self): + return self._size() + + def __getitem__(self, index): + if isinstance(index, int): + if index < 0: + index += len(self) + + if (index >= 0) and (index < len(self)): + return self._iget_content_node(index).setParent(self) + else: + raise IndexError( + "Expected 0 <= index < %d, was 0 <= %d < %d" + % (len(self), index, len(self)) + ) + else: + raise TypeError("[] operator must have integer index") + + def last(self): + return self[-1] + + def getValue(self, item_index=-1, node_index=0): + node = self[item_index] + return node[node_index] + + def free(self): + self._free() + + +class ConfigContent(BaseCClass): + TYPE_NAME = "config_content" + + _alloc = ResPrototype("void* config_content_alloc(char*)", bind=False) + _free = ResPrototype("void config_content_free( config_content )") + _is_valid = ResPrototype("bool config_content_is_valid( config_content )") + _has_key = ResPrototype("bool config_content_has_item( config_content , char*)") + _get_item = ResPrototype( + "content_item_ref config_content_get_item( config_content , char*)" + ) + _get_errors = ResPrototype( + "config_error_ref config_content_get_errors( config_content )" + ) + _get_warnings = ResPrototype( + "stringlist_ref config_content_get_warnings( config_content )" + ) + _get_config_path = ResPrototype( + "char* config_content_get_config_path( config_content )" + ) + _create_path_elm = ResPrototype( + "config_path_elm_ref config_content_add_path_elm(config_content, char*)" + ) + _add_define = ResPrototype( + "void config_content_add_define(config_content, char*, char*)" + ) + _size = ResPrototype("int config_content_get_size(config_content)") + _keys = ResPrototype("stringlist_obj config_content_alloc_keys(config_content)") + + def __init__(self, filename): + c_ptr = self._alloc(filename) + + if c_ptr: + super(ConfigContent, self).__init__(c_ptr) + else: + raise ValueError( + "Failed to construct ConfigContent instance from config file %s." + % filename + ) + + def __len__(self): + return self._size() + + def __contains__(self, key): + return self._has_key(key) + + def setParser(self, parser): + self._parser = parser + + def __getitem__(self, key): + if key in self: + item = self._get_item(key) + item.setParent(self) + return item + else: + if key in self._parser: + schema_item = SchemaItem(key) + return ContentItem(schema_item) + else: + raise KeyError("No such key: %s" % key) + + def hasKey(self, key): + return key in self + + def getValue(self, key, item_index=-1, node_index=0): + item = self[key] + return item.getValue(item_index, node_index) + + def isValid(self): + return self._is_valid() + + def free(self): + self._free() + + def getErrors(self): + """@rtype: ConfigError""" + return self._get_errors() + + def getWarnings(self): + """@rtype: ConfigError""" + return self._get_warnings() + + def get_config_path(self): + return self._get_config_path() + + def create_path_elm(self, path): + return self._create_path_elm(path) + + def add_define(self, key, value): + self._add_define(key, value) + + def keys(self): + return self._keys() + + def as_dict(self): + d = {} + for key in self.keys(): + d[key] = [] + item = self[key] + for node in item: + d[key].append([x for x in node]) + return d diff --git a/res/config/config_error.py b/res/config/config_error.py new file mode 100644 index 00000000000..04de22dbd6d --- /dev/null +++ b/res/config/config_error.py @@ -0,0 +1,45 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'enkf_fs.py' 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. +from res import ResPrototype +from cwrap import BaseCClass + + +class ConfigError(BaseCClass): + TYPE_NAME = "config_error" + _free = ResPrototype("void config_error_free(config_error)") + _count = ResPrototype("int config_error_count(config_error)") + _iget = ResPrototype("char* config_error_iget(config_error, int)") + + def __init__(self): + raise NotImplementedError("Class can not be instantiated directly!") + + def __getitem__(self, index): + """@rtype: str""" + if not isinstance(index, int): + raise TypeError("Expected an integer") + + size = len(self) + if index >= size: + raise IndexError("Index out of range: %d < %d" % (index, size)) + + return self._iget(index) + + def __len__(self): + """@rtype: int""" + return self._count() + + def free(self): + self._free() diff --git a/res/config/config_parser.py b/res/config/config_parser.py new file mode 100644 index 00000000000..c2adca872e4 --- /dev/null +++ b/res/config/config_parser.py @@ -0,0 +1,126 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'config_parser.py' 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. + +import sys +import os.path + +from cwrap import BaseCClass +from res import ResPrototype +from res.config import ConfigContent, UnrecognizedEnum + + +class ConfigParser(BaseCClass): + TYPE_NAME = "config_parser" + + _alloc = ResPrototype("void* config_alloc()", bind=False) + _add = ResPrototype( + "schema_item_ref config_add_schema_item(config_parser, char*, bool)" + ) + _free = ResPrototype("void config_free(config_parser)") + _parse = ResPrototype( + "config_content_obj config_parse(config_parser, char*, char*, char*, char*, hash, config_unrecognized_enum, bool)" + ) + _size = ResPrototype("int config_get_schema_size(config_parser)") + _get_schema_item = ResPrototype( + "schema_item_ref config_get_schema_item(config_parser, char*)" + ) + _has_schema_item = ResPrototype("bool config_has_schema_item(config_parser, char*)") + _add_key_value = ResPrototype( + "bool config_parser_add_key_values(config_parser, config_content, char*, stringlist, config_path_elm, char*, config_unrecognized_enum)" + ) + _validate = ResPrototype("void config_validate(config_parser, config_content)") + + def __init__(self): + c_ptr = self._alloc() + super(ConfigParser, self).__init__(c_ptr) + + def __contains__(self, keyword): + return self._has_schema_item(keyword) + + def __len__(self): + return self._size() + + def __repr__(self): + return self._create_repr("size=%d" % len(self)) + + def add(self, keyword, required=False, value_type=None): + item = self._add(keyword, required).setParent(self) + if value_type: + item.iset_type(0, value_type) + return item + + def __getitem__(self, keyword): + if keyword in self: + item = self._get_schema_item(keyword) + item.setParent(self) + return item + else: + raise KeyError("Config parser does not have item:%s" % keyword) + + def parse( + self, + config_file, + comment_string="--", + include_kw="INCLUDE", + define_kw="DEFINE", + pre_defined_kw_map=None, + unrecognized=UnrecognizedEnum.CONFIG_UNRECOGNIZED_WARN, + validate=True, + ): + """@rtype: ConfigContent""" + + assert isinstance(unrecognized, UnrecognizedEnum) + + if not os.path.exists(config_file): + raise IOError("File: %s does not exists" % config_file) + config_content = self._parse( + config_file, + comment_string, + include_kw, + define_kw, + pre_defined_kw_map, + unrecognized, + validate, + ) + config_content.setParser(self) + + if validate and not config_content.isValid(): + sys.stderr.write("Errors parsing:%s \n" % config_file) + for count, error in enumerate(config_content.getErrors()): + sys.stderr.write(" %02d:%s\n" % (count, error)) + raise ValueError("Parsing:%s failed" % config_file) + + return config_content + + def free(self): + self._free() + + def validate(self, config_content): + self._validate(config_content) + + def add_key_value( + self, + config_content, + key, + value, + path_elm=None, + config_filename=None, + unrecognized_action=UnrecognizedEnum.CONFIG_UNRECOGNIZED_WARN, + ): + + return self._add_key_value( + config_content, key, value, path_elm, config_filename, unrecognized_action + ) diff --git a/res/config/config_path_elm.py b/res/config/config_path_elm.py new file mode 100644 index 00000000000..77612263914 --- /dev/null +++ b/res/config/config_path_elm.py @@ -0,0 +1,41 @@ +# Copyright (C) 2017 Equinor ASA, Norway. +# +# The file 'config_path_elm.py' 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. + + +from cwrap import BaseCClass +from res import ResPrototype + + +class ConfigPathElm(BaseCClass): + TYPE_NAME = "config_path_elm" + + _free = ResPrototype("void config_path_elm_free(config_path_elm)") + _rel_path = ResPrototype("char* config_path_elm_get_relpath(config_path_elm)") + _abs_path = ResPrototype("char* config_path_elm_get_abspath(config_path_elm)") + + def __init__(self): + raise NotImplementedError("Not possible to instantiate ConfigPathElm directly.") + + def free(self): + self._free() + + @property + def rel_path(self): + return self._rel_path() + + @property + def abs_path(self): + return self._abs_path() diff --git a/res/config/config_settings.py b/res/config/config_settings.py new file mode 100644 index 00000000000..f94ab970f8a --- /dev/null +++ b/res/config/config_settings.py @@ -0,0 +1,159 @@ +# Copyright (C) 2017 Equinor ASA, Norway. +# +# The file 'config_settings.py' 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. + +from cwrap import BaseCClass + +from res import ResPrototype +from res.config import ContentTypeEnum +from res.config import SchemaItem + + +class ConfigSettings(BaseCClass): + TYPE_NAME = "config_settings" + _alloc = ResPrototype("void* config_settings_alloc(char*)", bind=False) + _free = ResPrototype("void config_settings_free(config_settings)") + + _add_setting = ResPrototype( + "bool config_settings_add_setting(config_settings , char* , config_content_type_enum, char*)" + ) + _add_double_setting = ResPrototype( + "void config_settings_add_double_setting(config_settings , char* , double)" + ) + _add_int_setting = ResPrototype( + "void config_settings_add_int_setting(config_settings , char* , int)" + ) + _add_string_setting = ResPrototype( + "void config_settings_add_string_setting(config_settings , char* , char*)" + ) + _add_bool_setting = ResPrototype( + "void config_settings_add_bool_setting(config_settings , char* , bool)" + ) + _has_key = ResPrototype("bool config_settings_has_key(config_settings , char*)") + _get_type = ResPrototype( + "config_content_type_enum config_settings_get_value_type(config_settings, char*)" + ) + _init_parser = ResPrototype( + "void config_settings_init_parser(config_settings, config_parser, bool)" + ) + _apply = ResPrototype("void config_settings_apply(config_settings, config_content)") + _alloc_keys = ResPrototype( + "stringlist_obj config_settings_alloc_keys(config_settings)" + ) + + _get = ResPrototype("char* config_settings_get_value(config_settings, char*)") + _get_int = ResPrototype("int config_settings_get_int_value(config_settings, char*)") + _get_double = ResPrototype( + "double config_settings_get_double_value(config_settings, char*)" + ) + _get_bool = ResPrototype( + "bool config_settings_get_bool_value(config_settings, char*)" + ) + + _set = ResPrototype("bool config_settings_set_value(config_settings, char*, char*)") + _set_int = ResPrototype( + "bool config_settings_set_int_value(config_settings, char*, int)" + ) + _set_double = ResPrototype( + "bool config_settings_set_double_value(config_settings, char*, double)" + ) + _set_bool = ResPrototype( + "bool config_settings_set_bool_value(config_settings, char*, bool)" + ) + + def __init__(self, root_key, c_ptr=None): + if c_ptr is None: + c_ptr = ConfigSettings._alloc(root_key) + + super(ConfigSettings, self).__init__(c_ptr) + + def __contains__(self, key): + return self._has_key(key) + + def __getitem__(self, key): + if key in self: + value_type = self._get_type(key) + if value_type == ContentTypeEnum.CONFIG_INT: + return self._get_int(key) + + if value_type == ContentTypeEnum.CONFIG_FLOAT: + return self._get_double(key) + + if value_type == ContentTypeEnum.CONFIG_BOOL: + return self._get_bool(key) + + return self._get(key) + else: + raise KeyError("Settings object does not support key:%s" % key) + + def __setitem__(self, key, value): + if key in self: + value_type = self._get_type(key) + + if value_type == ContentTypeEnum.CONFIG_INT: + self._set_int(key, value) + return + + if value_type == ContentTypeEnum.CONFIG_FLOAT: + self._set_double(key, value) + return + + if value_type == ContentTypeEnum.CONFIG_BOOL: + if type(value) is bool: + self._set_bool(key, value) + return + else: + raise TypeError("Type of %s should be boolean" % key) + + if not self._set(key, value): + raise TypeError("Setting %s=%s failed \n" % (key, value)) + else: + raise KeyError("Settings object does not support key:%s" % key) + + def free(self): + self._free() + + def addSetting(self, key, value_type, initial_value): + if not self._add_setting(key, value_type, str(initial_value)): + raise TypeError( + "Failed to add setting %s with initial value %s" % (key, initial_value) + ) + + def addIntSetting(self, key, initial_value): + self._add_int_setting(key, initial_value) + + def addDoubleSetting(self, key, initial_value): + self._add_double_setting(key, initial_value) + + def addStringSetting(self, key, initial_value): + self._add_string_setting(key, initial_value) + + def addBoolSetting(self, key, initial_value): + self._add_bool_setting(key, initial_value) + + def initParser(self, parser, required=False): + self._init_parser(parser, required) + + def apply(self, config_content): + self._apply(config_content) + + def keys(self): + return self._alloc_keys() + + def getType(self, key): + if key in self: + return self._get_type(key) + else: + raise KeyError("No such key:%s" % key) diff --git a/res/config/content_type_enum.py b/res/config/content_type_enum.py new file mode 100644 index 00000000000..b4e64adb8f9 --- /dev/null +++ b/res/config/content_type_enum.py @@ -0,0 +1,77 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'content_type_enum.py' 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. + +import ctypes +from cwrap import BaseCEnum +from ecl import EclPrototype +from res import ResPrototype + + +class ContentTypeEnum(BaseCEnum): + TYPE_NAME = "config_content_type_enum" + CONFIG_STRING = None + CONFIG_INT = None + CONFIG_FLOAT = None + CONFIG_PATH = None + CONFIG_EXISTING_PATH = None + CONFIG_BOOL = None + CONFIG_CONFIG = None + CONFIG_BYTESIZE = None + CONFIG_EXECUTABLE = None + CONFIG_ISODATE = None + CONFIG_INVALID = None + CONFIG_RUNTIME_FILE = None + CONFIG_RUNTIME_INT = None + + _valid_string = ResPrototype( + "bool config_schema_item_valid_string(config_content_type_enum , char*, bool)" + ) + _sscanf_bool = EclPrototype("bool util_sscanf_bool( char* , bool*)", bind=False) + + def valid_string(self, string, runtime=False): + return self._valid_string(string, runtime) + + def convert_string(self, string): + if not self.valid_string(string, runtime=True): + raise ValueError("Can not convert %s to %s" % (string, self)) + + if self == ContentTypeEnum.CONFIG_INT: + return int(string) + + if self == ContentTypeEnum.CONFIG_FLOAT: + return float(string) + + if self == ContentTypeEnum.CONFIG_BOOL: + bool_value = ctypes.c_bool() + ContentTypeEnum._sscanf_bool(string, ctypes.byref(bool_value)) + return bool_value.value + + return string + + +ContentTypeEnum.addEnum("CONFIG_STRING", 1) +ContentTypeEnum.addEnum("CONFIG_INT", 2) +ContentTypeEnum.addEnum("CONFIG_FLOAT", 4) +ContentTypeEnum.addEnum("CONFIG_PATH", 8) +ContentTypeEnum.addEnum("CONFIG_EXISTING_PATH", 16) +ContentTypeEnum.addEnum("CONFIG_BOOL", 32) +ContentTypeEnum.addEnum("CONFIG_CONFIG", 64) +ContentTypeEnum.addEnum("CONFIG_BYTESIZE", 128) +ContentTypeEnum.addEnum("CONFIG_EXECUTABLE", 256) +ContentTypeEnum.addEnum("CONFIG_ISODATE", 512) +ContentTypeEnum.addEnum("CONFIG_INVALID", 1024) +ContentTypeEnum.addEnum("CONFIG_RUNTIME_INT", 2048) +ContentTypeEnum.addEnum("CONFIG_RUNTIME_FILE", 4096) diff --git a/res/config/schema_item.py b/res/config/schema_item.py new file mode 100644 index 00000000000..8a54a76c5ec --- /dev/null +++ b/res/config/schema_item.py @@ -0,0 +1,80 @@ +# 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. +import ctypes + +from ecl import EclPrototype +from res import ResPrototype +from res.config import ContentTypeEnum +from cwrap import BaseCClass + + +class SchemaItem(BaseCClass): + TYPE_NAME = "schema_item" + + _alloc = ResPrototype("void* config_schema_item_alloc( char* , bool )", bind=False) + _free = ResPrototype("void config_schema_item_free( schema_item )") + _iget_type = ResPrototype( + "config_content_type_enum config_schema_item_iget_type( schema_item, int)" + ) + _iset_type = ResPrototype( + "void config_schema_item_iset_type( schema_item , int , config_content_type_enum)" + ) + _set_argc_minmax = ResPrototype( + "void config_schema_item_set_argc_minmax( schema_item , int , int)" + ) + _add_alternative = ResPrototype( + "void config_schema_item_add_indexed_alternative(schema_item , int , char*)" + ) + _set_deprecated = ResPrototype( + "void config_schema_item_set_deprecated(schema_item , char*)" + ) + + def __init__(self, keyword, required=False): + c_ptr = self._alloc(keyword, required) + super(SchemaItem, self).__init__(c_ptr) + + def iget_type(self, index): + """@rtype: ContentTypeEnum""" + return self._iget_type(index) + + def iset_type(self, index, schema_type): + """ + @type schema_type: ContentTypeEnum + """ + assert isinstance(schema_type, ContentTypeEnum) + self._iset_type(index, schema_type) + + def set_argc_minmax(self, minimum, maximum): + self._set_argc_minmax(minimum, maximum) + + def initSelection(self, index, alternatives): + for alt in alternatives: + self.addAlternative(index, alt) + + def addAlternative(self, index, alt): + self._add_alternative(index, alt) + + def setDeprecated(self, msg): + """This method can be used to mark this item as deprecated. + + If the deprecated item is used in a configuration file the + @msg will be added to the warnings of the ConfigContent + object, + """ + self._set_deprecated(msg) + + def free(self): + self._free() diff --git a/res/config/unrecognized_enum.py b/res/config/unrecognized_enum.py new file mode 100644 index 00000000000..ac6f60e07af --- /dev/null +++ b/res/config/unrecognized_enum.py @@ -0,0 +1,31 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'unrecognized_enum.py' 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. + +from cwrap import BaseCEnum + + +class UnrecognizedEnum(BaseCEnum): + TYPE_NAME = "config_unrecognized_enum" + CONFIG_UNRECOGNIZED_IGNORE = None + CONFIG_UNRECOGNIZED_WARN = None + CONFIG_UNRECOGNIZED_ERROR = None + CONFIG_UNRECOGNZIED_ADD = None + + +UnrecognizedEnum.addEnum("CONFIG_UNRECOGNIZED_IGNORE", 0) +UnrecognizedEnum.addEnum("CONFIG_UNRECOGNIZED_WARN", 1) +UnrecognizedEnum.addEnum("CONFIG_UNRECOGNIZED_ERROR", 2) +UnrecognizedEnum.addEnum("CONFIG_UNRECOGNIZED_ADD", 3) diff --git a/res/enkf/__init__.py b/res/enkf/__init__.py new file mode 100644 index 00000000000..3f72e92297d --- /dev/null +++ b/res/enkf/__init__.py @@ -0,0 +1,102 @@ +# Copyright (C) 2011 Equinor ASA, Norway. +# +# The file '__init__.py' 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. + + +from cwrap import Prototype +import res +from .config_keys import ConfigKeys +import ecl.util +import ecl.util.geometry +import ecl +import ecl.eclfile +import ecl.grid +import ecl.grid.faults +import ecl.gravimetry +import ecl.summary +import ecl.rft + +import res.analysis +import res.sched +import res.config +import res.job_queue + +from .enums import * + +from .node_id import NodeId + +from .enkf_linalg import EnkfLinalg +from .util import TimeMap +from .state_map import StateMap +from .summary_key_set import SummaryKeySet +from .summary_key_matcher import SummaryKeyMatcher +from .enkf_fs import EnkfFs + + +from .row_scaling import RowScaling +from .active_list import ActiveList +from .config import * +from .data import * + +from .obs_block import ObsBlock +from .obs_data import ObsData +from .local_dataset import LocalDataset +from .local_obsdata_node import LocalObsdataNode +from .local_obsdata import LocalObsdata +from .local_ministep import LocalMinistep +from .local_updatestep import LocalUpdateStep + +from .observations import * + +from .meas_block import MeasBlock +from .meas_data import MeasData + +from .analysis_iter_config import AnalysisIterConfig +from .analysis_config import AnalysisConfig + +from .enkf_defaults import EnkfDefaults + +from .ecl_config import EclConfig +from .queue_config import QueueConfig +from .ert_workflow_list import ErtWorkflowList +from .site_config import SiteConfig +from .subst_config import SubstConfig +from .ensemble_config import EnsembleConfig +from .enkf_obs import EnkfObs +from .ert_template import ErtTemplate +from .ert_templates import ErtTemplates +from .local_config import LocalConfig +from .model_config import ModelConfig +from .runpath_list import RunpathList, RunpathNode +from .hook_workflow import HookWorkflow +from .hook_manager import HookManager +from .rng_config import RNGConfig +from .log_config import LogConfig +from .res_config import ResConfig + +from .es_update import ESUpdate +from .run_arg import RunArg +from .enkf_state import EnKFState +from .ert_run_context import ErtRunContext +from .enkf_simulation_runner import EnkfSimulationRunner +from .enkf_fs_manager import EnkfFsManager +from .enkf_main import EnKFMain +from .forward_load_context import ForwardLoadContext + +from res.job_queue import ErtScript as ErtScript +from res.job_queue import ( + ErtPlugin as ErtPlugin, + CancelPluginException as CancelPluginException, +) diff --git a/res/enkf/active_list.py b/res/enkf/active_list.py new file mode 100644 index 00000000000..571c02f3408 --- /dev/null +++ b/res/enkf/active_list.py @@ -0,0 +1,61 @@ +# Copyright (C) 2015 Equinor ASA, Norway. +# +# The file 'active_list.py' 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. + +from cwrap import BaseCClass +from res import ResPrototype +from res.enkf import ActiveMode + + +class ActiveList(BaseCClass): + TYPE_NAME = "active_list" + + _alloc = ResPrototype("void* active_list_alloc()", bind=False) + _free = ResPrototype("void active_list_free(active_list)") + _add_index = ResPrototype("void active_list_add_index(active_list , int)") + _asize = ResPrototype("int active_list_get_active_size(active_list, int)") + _get_mode = ResPrototype("active_mode_enum active_list_get_mode(active_list)") + + def __init__(self): + c_ptr = self._alloc() + super(ActiveList, self).__init__(c_ptr) + + def getMode(self): + return self._get_mode() + + def addActiveIndex(self, index): + self._add_index(index) + + def getActiveSize(self, default_value): + """In mode PARTLY_ACTIVE, we return the size of the active set; In mode + INACTIVE 0 is returned and if the mode is ALL_ACTIVE, the input + default_value is returned. + """ + mode = self.getMode() + if mode == ActiveMode.PARTLY_ACTIVE: + return self._asize(0) + if mode == ActiveMode.INACTIVE: + return 0 + return default_value + + def free(self): + self._free() + + def __repr__(self): + size = "" + if self.getMode() == ActiveMode.PARTLY_ACTIVE: + size = ", active_size = %d" % self._asize(0) + cnt = "mode = %s%s" % (self.getMode(), size) + return self._create_repr(cnt) diff --git a/res/enkf/analysis_config.py b/res/enkf/analysis_config.py new file mode 100644 index 00000000000..74c1974c654 --- /dev/null +++ b/res/enkf/analysis_config.py @@ -0,0 +1,349 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'analysis_config.py' 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. + +from os.path import isfile +from os.path import realpath + +from cwrap import BaseCClass + +from ecl.util.util import StringList + +from res import ResPrototype +from res.enkf import ConfigKeys +from res.enkf import AnalysisIterConfig +from res.analysis import AnalysisModule + + +class AnalysisConfig(BaseCClass): + TYPE_NAME = "analysis_config" + + _alloc = ResPrototype("void* analysis_config_alloc(config_content)", bind=False) + _alloc_load = ResPrototype("void* analysis_config_alloc_load(char*)", bind=False) + _alloc_full = ResPrototype( + "void* analysis_config_alloc_full(double, bool, " + "bool, int, char*, double, bool, bool, " + "bool, double, int, int)", + bind=False, + ) + + _add_module_copy = ResPrototype( + "void analysis_config_add_module_copy( analysis_config, " "char* , char* )" + ) + _load_external_module = ResPrototype( + "bool analysis_config_load_external_module( analysis_config, " "char* , char* )" + ) + + _free = ResPrototype("void analysis_config_free( analysis_config )") + _get_rerun = ResPrototype("int analysis_config_get_rerun( analysis_config )") + _set_rerun = ResPrototype("void analysis_config_set_rerun( analysis_config, bool)") + _get_rerun_start = ResPrototype( + "int analysis_config_get_rerun_start( analysis_config )" + ) + _set_rerun_start = ResPrototype( + "void analysis_config_set_rerun_start( analysis_config, int)" + ) + _get_log_path = ResPrototype("char* analysis_config_get_log_path( analysis_config)") + _set_log_path = ResPrototype( + "void analysis_config_set_log_path( analysis_config, char*)" + ) + _get_merge_observations = ResPrototype( + "bool analysis_config_get_merge_observations(analysis_config)" + ) + _set_merge_observations = ResPrototype( + "void analysis_config_set_merge_observations(analysis_config, bool)" + ) + _get_iter_config = ResPrototype( + "analysis_iter_config_ref analysis_config_get_iter_config(analysis_config)" + ) + _have_enough_realisations = ResPrototype( + "bool analysis_config_have_enough_realisations(analysis_config, int, int)" + ) + _get_max_runtime = ResPrototype( + "int analysis_config_get_max_runtime(analysis_config)" + ) + _set_max_runtime = ResPrototype( + "void analysis_config_set_max_runtime(analysis_config, int)" + ) + _get_stop_long_running = ResPrototype( + "bool analysis_config_get_stop_long_running(analysis_config)" + ) + _set_stop_long_running = ResPrototype( + "void analysis_config_set_stop_long_running(analysis_config, bool)" + ) + _get_active_module_name = ResPrototype( + "char* analysis_config_get_active_module_name(analysis_config)" + ) + _get_module_list = ResPrototype( + "stringlist_obj analysis_config_alloc_module_names(analysis_config)" + ) + _get_module = ResPrototype( + "analysis_module_ref analysis_config_get_module(analysis_config, char*)" + ) + _select_module = ResPrototype( + "bool analysis_config_select_module(analysis_config, char*)" + ) + _has_module = ResPrototype( + "bool analysis_config_has_module(analysis_config, char*)" + ) + _get_alpha = ResPrototype("double analysis_config_get_alpha(analysis_config)") + _set_alpha = ResPrototype("void analysis_config_set_alpha(analysis_config, double)") + _get_std_cutoff = ResPrototype( + "double analysis_config_get_std_cutoff(analysis_config)" + ) + _set_std_cutoff = ResPrototype( + "void analysis_config_set_std_cutoff(analysis_config, double)" + ) + _set_global_std_scaling = ResPrototype( + "void analysis_config_set_global_std_scaling(analysis_config, double)" + ) + _get_global_std_scaling = ResPrototype( + "double analysis_config_get_global_std_scaling(analysis_config)" + ) + _get_min_realizations = ResPrototype( + "int analysis_config_get_min_realisations(analysis_config)" + ) + + def __init__(self, user_config_file=None, config_content=None, config_dict=None): + configs = sum( + [ + 1 + for x in [user_config_file, config_content, config_dict] + if x is not None + ] + ) + + if configs > 1: + raise ValueError( + "Attempting to create AnalysisConfig object with multiple config objects" + ) + + if configs == 0: + raise ValueError( + "Error trying to create AnalysisConfig without any configuration" + ) + + c_ptr = None + + if user_config_file is not None: + if not isfile(user_config_file): + raise IOError('No such configuration file "%s".' % user_config_file) + + c_ptr = self._alloc_load(user_config_file) + if c_ptr: + super(AnalysisConfig, self).__init__(c_ptr) + else: + raise ValueError( + "Failed to construct AnalysisConfig instance from config file %s." + % user_config_file + ) + + if config_content is not None: + c_ptr = self._alloc(config_content) + if c_ptr: + super(AnalysisConfig, self).__init__(c_ptr) + else: + raise ValueError("Failed to construct AnalysisConfig instance.") + + if config_dict is not None: + c_ptr = self._alloc_full( + config_dict.get(ConfigKeys.ALPHA_KEY, 3.0), + config_dict.get(ConfigKeys.MERGE_OBSERVATIONS, False), + config_dict.get(ConfigKeys.RERUN_KEY, False), + config_dict.get(ConfigKeys.RERUN_START_KEY, 0), + realpath(config_dict.get(ConfigKeys.UPDATE_LOG_PATH, "update_log")), + config_dict.get(ConfigKeys.STD_CUTOFF_KEY, 1e-6), + config_dict.get(ConfigKeys.STOP_LONG_RUNNING, False), + config_dict.get(ConfigKeys.SINGLE_NODE_UPDATE, False), + config_dict.get(ConfigKeys.STD_CORRELATED_OBS, False), + config_dict.get(ConfigKeys.GLOBAL_STD_SCALING, 1.0), + config_dict.get(ConfigKeys.MAX_RUNTIME, 0), + config_dict.get(ConfigKeys.MIN_REALIZATIONS, 0), + ) + if c_ptr: + super(AnalysisConfig, self).__init__(c_ptr) + + # external modules + ext_modules_list = config_dict.get(ConfigKeys.ANALYSIS_LOAD, []) + for ext_module in ext_modules_list: + self._load_external_module( + ext_module[ConfigKeys.LIB_NAME], + ext_module[ConfigKeys.USER_NAME], + ) + + # copy modules + analysis_copy_list = config_dict.get(ConfigKeys.ANALYSIS_COPY, []) + for analysis_copy in analysis_copy_list: + self._add_module_copy( + analysis_copy[ConfigKeys.SRC_NAME], + analysis_copy[ConfigKeys.DST_NAME], + ) + + # set var list + set_var_list = config_dict.get(ConfigKeys.ANALYSIS_SET_VAR, []) + for set_var in set_var_list: + module = self._get_module(set_var[ConfigKeys.MODULE_NAME]) + module._set_var( + set_var[ConfigKeys.VAR_NAME], str(set_var[ConfigKeys.VALUE]) + ) + + if ConfigKeys.ANALYSIS_SELECT in config_dict: + self._select_module(config_dict[ConfigKeys.ANALYSIS_SELECT]) + + else: + raise ValueError("Failed to construct AnalysisConfig from dict.") + + def get_rerun(self): + return self._get_rerun() + + def set_rerun(self, rerun): + self._set_rerun(rerun) + + def get_rerun_start(self): + return self._get_rerun_start() + + def set_rerun_start(self, index): + self._set_rerun_start(index) + + def get_log_path(self): + return self._get_log_path() + + def set_log_path(self, path): + self._set_log_path(path) + + def getEnkfAlpha(self): + """:rtype: float""" + return self._get_alpha() + + def setEnkfAlpha(self, alpha): + self._set_alpha(alpha) + + def getStdCutoff(self): + """:rtype: float""" + return self._get_std_cutoff() + + def setStdCutoff(self, std_cutoff): + self._set_std_cutoff(std_cutoff) + + def get_merge_observations(self): + return self._get_merge_observations() + + def set_merge_observations(self, merge_observations): + return self._set_merge_observations(merge_observations) + + def getAnalysisIterConfig(self): + """@rtype: AnalysisIterConfig""" + return self._get_iter_config().setParent(self) + + def get_stop_long_running(self): + """@rtype: bool""" + return self._get_stop_long_running() + + def set_stop_long_running(self, stop_long_running): + self._set_stop_long_running(stop_long_running) + + def get_max_runtime(self): + """@rtype: int""" + return self._get_max_runtime() + + def set_max_runtime(self, max_runtime): + self._set_max_runtime(max_runtime) + + def free(self): + self._free() + + def activeModuleName(self): + """:rtype: str""" + return self._get_active_module_name() + + def getModuleList(self): + """:rtype: StringList""" + return self._get_module_list() + + def getModule(self, module_name): + """@rtype: AnalysisModule""" + return self._get_module(module_name) + + def hasModule(self, module_name): + """@rtype: bool""" + return self._has_module(module_name) + + def selectModule(self, module_name): + """@rtype: bool""" + return self._select_module(module_name) + + def getActiveModule(self): + """:rtype: AnalysisModule""" + return self.getModule(self.activeModuleName()) + + def setGlobalStdScaling(self, std_scaling): + self._set_global_std_scaling(std_scaling) + + def getGlobalStdScaling(self): + return self._get_global_std_scaling() + + @property + def minimum_required_realizations(self): + return self._get_min_realizations() + + def haveEnoughRealisations(self, realizations, ensemble_size): + return realizations >= min(self.minimum_required_realizations, ensemble_size) + + def __ne__(self, other): + return not self == other + + def __eq__(self, other): + if realpath(self.get_log_path()) != realpath(other.get_log_path()): + return False + + if self.get_max_runtime() != other.get_max_runtime(): + return False + + if self.getGlobalStdScaling() != other.getGlobalStdScaling(): + return False + + if self.get_stop_long_running() != other.get_stop_long_running(): + return False + + if self.getStdCutoff() != other.getStdCutoff(): + return False + + if self.getEnkfAlpha() != other.getEnkfAlpha(): + return False + + if self.get_merge_observations() != other.get_merge_observations(): + return False + + if self.get_rerun() != other.get_rerun(): + return False + + if self.get_rerun_start() != other.get_rerun_start(): + return False + + if set(self.getModuleList()) != set(other.getModuleList()): + return False + + if self.activeModuleName() != other.activeModuleName(): + return False + + if self.getAnalysisIterConfig() != other.getAnalysisIterConfig(): + return False + + # compare each module + for a in list(self.getModuleList()): + if self.getModule(a) != other.getModule(a): + return False + + return True diff --git a/res/enkf/analysis_iter_config.py b/res/enkf/analysis_iter_config.py new file mode 100644 index 00000000000..51d19005bc8 --- /dev/null +++ b/res/enkf/analysis_iter_config.py @@ -0,0 +1,132 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'analysis_iter_config.py' 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. +from cwrap import BaseCClass +from res import ResPrototype +from res.enkf import ConfigKeys + + +class AnalysisIterConfig(BaseCClass): + TYPE_NAME = "analysis_iter_config" + + _alloc = ResPrototype("void* analysis_iter_config_alloc( )", bind=False) + _alloc_full = ResPrototype( + "void* analysis_iter_config_alloc_full( char*, int, int )", bind=False + ) + _free = ResPrototype("void analysis_iter_config_free( analysis_iter_config )") + _set_num_iterations = ResPrototype( + "void analysis_iter_config_set_num_iterations(analysis_iter_config, int)" + ) + _get_num_iterations = ResPrototype( + "int analysis_iter_config_get_num_iterations(analysis_iter_config)" + ) + _get_num_retries = ResPrototype( + "int analysis_iter_config_get_num_retries_per_iteration(analysis_iter_config)" + ) + _num_iterations_set = ResPrototype( + "bool analysis_iter_config_num_iterations_set(analysis_iter_config)" + ) + _set_case_fmt = ResPrototype( + "void analysis_iter_config_set_case_fmt( analysis_iter_config , char* )" + ) + _get_case_fmt = ResPrototype( + "char* analysis_iter_config_get_case_fmt( analysis_iter_config)" + ) + _case_fmt_set = ResPrototype( + "bool analysis_iter_config_case_fmt_set(analysis_iter_config)" + ) + + def __init__(self, config_dict=None): + + if config_dict is None: + c_ptr = self._alloc() + if c_ptr: + super(AnalysisIterConfig, self).__init__(c_ptr) + else: + raise ValueError("Failed to construct AnalysisIterConfig instance.") + else: + c_ptr = self._alloc_full( + config_dict[ConfigKeys.ITER_CASE], + config_dict[ConfigKeys.ITER_COUNT], + config_dict[ConfigKeys.ITER_RETRY_COUNT], + ) + if c_ptr: + super(AnalysisIterConfig, self).__init__(c_ptr) + else: + raise ValueError( + "Failed to construct AnalysisIterConfig instance for dictionary." + ) + + def getNumIterations(self): + """@rtype: int""" + return self._get_num_iterations() + + def __len__(self): + """Returns number of iterations.""" + return self.getNumIterations() + + def setNumIterations(self, num_iterations): + self._set_num_iterations(num_iterations) + + def numIterationsSet(self): + return self._num_iterations_set() + + def getNumRetries(self): + """@rtype: int""" + return self._get_num_retries() + + def getCaseFormat(self): + """@rtype: str""" + return self._get_case_fmt() + + def setCaseFormat(self, case_fmt): + self._set_case_fmt(case_fmt) + + def caseFormatSet(self): + return self._case_fmt_set() + + def free(self): + self._free() + + def _short_case_fmt(self, maxlen=10): + fmt = self.getCaseFormat() + if len(fmt) <= maxlen: + return fmt + return fmt[: maxlen - 2] + ".." + + def __repr__(self): + cfs = "format = False" + if self.caseFormatSet(): + cfs = "format = True" + fmt = self._short_case_fmt() + ret = "AnalysisIterConfig(iterations = %d, retries = %d, fmt = %s, %s) at 0x%x" + its = len(self) + rets = self.getNumRetries() + return ret % (its, rets, fmt, cfs, self._address) + + def __ne__(self, other): + return not self == other + + def __eq__(self, other): + if self.getCaseFormat() != other.getCaseFormat(): + return False + + if self.getNumIterations() != other.getNumIterations(): + return False + + if self.getNumRetries() != other.getNumRetries(): + return False + + return True diff --git a/res/enkf/config/__init__.py b/res/enkf/config/__init__.py new file mode 100644 index 00000000000..c9761363d09 --- /dev/null +++ b/res/enkf/config/__init__.py @@ -0,0 +1,18 @@ +from .field_config import FieldConfig +from .field_type_enum import FieldTypeEnum +from .gen_data_config import GenDataConfig +from .gen_kw_config import GenKwConfig +from .summary_config import SummaryConfig +from .ext_param_config import ExtParamConfig +from .enkf_config_node import EnkfConfigNode + + +__all__ = [ + "FieldConfig", + "FieldTypeEnum", + "GenKwConfig", + "GenDataConfig", + "EnkfConfigNode", + "SummaryConfig", + "ExtParamConfig", +] diff --git a/res/enkf/config/enkf_config_node.py b/res/enkf/config/enkf_config_node.py new file mode 100644 index 00000000000..c86c59bafb9 --- /dev/null +++ b/res/enkf/config/enkf_config_node.py @@ -0,0 +1,585 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'enkf_config_node.py' 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. +from cwrap import BaseCClass + +from ecl.grid import EclGrid +from ecl.util.util import StringList, IntVector + +from res import ResPrototype +from res.enkf.config import ( + FieldConfig, + GenDataConfig, + GenKwConfig, + SummaryConfig, + ExtParamConfig, +) +from res.enkf.enums import ( + EnkfTruncationType, + ErtImplType, + LoadFailTypeEnum, + EnkfVarType, +) +from res.enkf import ConfigKeys +import os + + +class EnkfConfigNode(BaseCClass): + TYPE_NAME = "enkf_config_node" + + _alloc = ResPrototype( + "enkf_config_node_obj enkf_config_node_alloc(enkf_var_type_enum, ert_impl_type_enum, bool, char*, char* , char*, char*, void*)", + bind=False, + ) + _alloc_gen_data_everest = ResPrototype( + "enkf_config_node_obj enkf_config_node_alloc_GEN_DATA_everest(char*, char* , int_vector)", + bind=False, + ) + _alloc_summary_node = ResPrototype( + "enkf_config_node_obj enkf_config_node_alloc_summary(char*, load_fail_type)", + bind=False, + ) + _alloc_field_node = ResPrototype( + "enkf_config_node_obj enkf_config_node_alloc_field(char*, ecl_grid, void*, bool)", + bind=False, + ) + _get_ref = ResPrototype( + "void* enkf_config_node_get_ref(enkf_config_node)" + ) # todo: fix return type + _get_impl_type = ResPrototype( + "ert_impl_type_enum enkf_config_node_get_impl_type(enkf_config_node)" + ) + _get_enkf_outfile = ResPrototype( + "char* enkf_config_node_get_enkf_outfile(enkf_config_node)" + ) + _get_min_std_file = ResPrototype( + "char* enkf_config_node_get_min_std_file(enkf_config_node)" + ) + _get_enkf_infile = ResPrototype( + "char* enkf_config_node_get_enkf_infile(enkf_config_node)" + ) + _get_init_file = ResPrototype( + "char* enkf_config_node_get_FIELD_fill_file(enkf_config_node, path_fmt)" + ) + _get_init_file_fmt = ResPrototype( + "char* enkf_config_node_get_init_file_fmt(enkf_config_node)" + ) + _get_var_type = ResPrototype( + "enkf_var_type_enum enkf_config_node_get_var_type(enkf_config_node)" + ) # todo: fix return type as enum + _get_key = ResPrototype("char* enkf_config_node_get_key(enkf_config_node)") + _get_obs_keys = ResPrototype( + "stringlist_ref enkf_config_node_get_obs_keys(enkf_config_node)" + ) + _free = ResPrototype("void enkf_config_node_free(enkf_config_node)") + _use_forward_init = ResPrototype( + "bool enkf_config_node_use_forward_init(enkf_config_node)" + ) + + # ensemble config aux + _alloc_gen_param_full = ResPrototype( + "enkf_config_node_obj enkf_config_node_alloc_GEN_PARAM_full( char*,\ + bool, \ + gen_data_file_format_type, \ + gen_data_file_format_type, \ + char*, \ + char*, \ + char*, \ + char*, \ + char*)", + bind=False, + ) + + _alloc_gen_data_full = ResPrototype( + "enkf_config_node_obj enkf_config_node_alloc_GEN_DATA_full( char*,\ + char*, \ + gen_data_file_format_type, \ + int_vector, \ + char*, \ + char*, \ + char*, \ + char*)", + bind=False, + ) + + _alloc_gen_kw_full = ResPrototype( + "enkf_config_node_obj enkf_config_node_alloc_GEN_KW_full( char*,\ + bool, \ + char*, \ + char*, \ + char*, \ + char*, \ + char*, \ + char*)", + bind=False, + ) + + _alloc_surface_full = ResPrototype( + "enkf_config_node_obj enkf_config_node_alloc_SURFACE_full( char*,\ + bool, \ + char*, \ + char*, \ + char*, \ + char*)", + bind=False, + ) + + _alloc_container = ResPrototype( + "enkf_config_node_obj enkf_config_node_new_container(char*)", bind=False + ) + _update_container = ResPrototype( + "void enkf_config_node_update_container(enkf_config_node, enkf_config_node)" + ) + _get_container_size = ResPrototype( + "int enkf_config_node_container_size(enkf_config_node)" + ) + _iget_container_key = ResPrototype( + "char* enkf_config_node_iget_container_key(enkf_config_node, int)" + ) + _update_parameter_field = ResPrototype( + "void enkf_config_node_update_parameter_field(enkf_config_node, \ + char*, \ + char*, \ + char*, \ + enkf_truncation_type_enum, \ + double, \ + double, \ + char*, \ + char*)", + bind=True, + ) + _update_general_field = ResPrototype( + "void enkf_config_node_update_general_field(enkf_config_node, \ + char*, \ + char*, \ + char*, \ + char*, \ + enkf_truncation_type_enum, \ + double, \ + double, \ + char*, \ + char*, \ + char*)", + bind=True, + ) + + def __init__(self): + raise NotImplementedError("Class can not be instantiated directly!") + + def get_container_size(self): + return self._get_container_size() + + def get_container_key(self, index): + return self._iget_container_key(index) + + def getImplementationType(self): + """@rtype: ErtImplType""" + return self._get_impl_type() + + def getVariableType(self): + return self._get_var_type() + + def getPointerReference(self): + return self._get_ref() + + def getUseForwardInit(self): + return self._use_forward_init() + + def getInitFile(self, model_config): + return self._enkf_config_node_get_init_file(model_config.getRunpathFormat()) + + def get_min_std_file(self): + return self._get_min_std_file() + + def get_enkf_outfile(self): + return self._get_enkf_outfile() + + def getFieldModelConfig(self): + """@rtype: FieldConfig""" + return FieldConfig.createCReference(self._get_ref(), parent=self) + + def getDataModelConfig(self): + """@rtype: GenDataConfig""" + return GenDataConfig.createCReference(self._get_ref(), parent=self) + + def getKeywordModelConfig(self): + """@rtype: GenKWConfig""" + return GenKwConfig.createCReference(self._get_ref(), parent=self) + + def getSummaryModelConfig(self): + """@rtype: SummaryConfig""" + return SummaryConfig.createCReference(self._get_ref(), parent=self) + + def get_enkf_infile(self): + return self._get_enkf_infile() + + def get_init_file_fmt(self): + return self._get_init_file_fmt() + + def getObservationKeys(self): + """@rtype: StringList""" + return self._get_obs_keys().setParent(self) + + @classmethod + def createSummaryConfigNode(cls, key, load_fail_type): + """ + @type key: str + @type load_fail_type: LoadFailTypeEnum + @rtype: EnkfConfigNode + """ + + assert isinstance(load_fail_type, LoadFailTypeEnum) + return cls._alloc_summary_node(key, load_fail_type) + + @classmethod + def createFieldConfigNode(cls, key, grid, trans_table=None, forward_init=False): + """ + @type grid: EclGrid + @rtype: EnkfConfigNode + """ + return cls._alloc_field_node(key, grid, trans_table, forward_init) + + @classmethod + def create_ext_param(cls, key, input_keys, output_file=None): + config = ExtParamConfig(key, input_keys) + output_file = output_file or key + ".json" + node = cls._alloc( + EnkfVarType.EXT_PARAMETER, + ErtImplType.EXT_PARAM, + False, + key, + None, + output_file, + None, + ExtParamConfig.from_param(config), + ) + config.convertToCReference(node) # config gets freed when node dies + return node + + # This method only exposes the details relevant for Everest usage. + @classmethod + def create_gen_data(cls, key, file_fmt, report_steps=(0,)): + active_steps = IntVector() + for step in report_steps: + active_steps.append(step) + + config_node = cls._alloc_gen_data_everest(key, file_fmt, active_steps) + if config_node is None: + raise ValueError("Failed to create GEN_DATA node for:%s" % key) + + return config_node + + # GEN DATA FULL creation + @classmethod + def create_gen_data_full( + cls, + key, + result_file, + input_format, + report_steps, + ecl_file, + init_file_fmt, + template_file, + data_key, + ): + active_steps = IntVector() + for step in report_steps: + active_steps.append(step) + + config_node = cls._alloc_gen_data_full( + key, + result_file, + input_format, + active_steps, + ecl_file, + init_file_fmt, + template_file, + data_key, + ) + if config_node is None: + raise ValueError( + "Failed to create GEN_DATA with FULL specs node for:%s" % key + ) + + return config_node + + # GEN PARAM FULL creation + @classmethod + def create_gen_param( + cls, + key, + forward_init, + input_format, + output_format, + init_file_fmt, + ecl_file, + min_std_file, + template_file, + data_key, + ): + + config_node = cls._alloc_gen_param_full( + key, + forward_init, + input_format, + output_format, + init_file_fmt, + ecl_file, + min_std_file, + template_file, + data_key, + ) + if config_node is None: + raise ValueError("Failed to create GEN_PARAM node for:%s" % key) + + return config_node + + # GEN KW FULL creation + @classmethod + def create_gen_kw( + cls, + key, + template_file, + enkf_outfile, + parameter_file, + forward_init, + mid_std_file, + init_file_fmt, + gen_kw_format, + ): + + config_node = cls._alloc_gen_kw_full( + key, + forward_init, + gen_kw_format, + template_file, + enkf_outfile, + parameter_file, + mid_std_file, + init_file_fmt, + ) + if config_node is None: + raise ValueError("Failed to create GEN KW node for:%s" % key) + + return config_node + + # SURFACE FULL creation + @classmethod + def create_surface( + cls, + key, + init_file_fmt, + output_file, + base_surface_file, + min_std_file, + forward_init, + ): + + if base_surface_file is not None: + base_surface_file = os.path.realpath(base_surface_file) + config_node = cls._alloc_surface_full( + key, + forward_init, + output_file, + base_surface_file, + min_std_file, + init_file_fmt, + ) + if config_node is None: + raise ValueError("Failed to create SURFACE node for:%s" % key) + + return config_node + + # FIELD FULL creation + @classmethod + def create_field( + cls, + key, + var_type_string, + grid, + field_trans_table, + ecl_file, + enkf_infile, + forward_init, + init_transform, + output_transform, + input_transform, + min_std_file, + min_key, + max_key, + init_file_fmt, + ): + + truncation = EnkfTruncationType.TRUNCATE_NONE + value_min = -1 + value_max = -1 + if min_key is not None: + value_min = min_key + truncation = truncation | EnkfTruncationType.TRUNCATE_MIN + if max_key is not None: + value_max = max_key + truncation = truncation | EnkfTruncationType.TRUNCATE_MAX + + config_node = cls._alloc_field_node(key, grid, field_trans_table, forward_init) + if config_node is None: + raise ValueError("Failed to create FIELD node for:%s" % key) + + if var_type_string == ConfigKeys.PARAMETER_KEY: + config_node._update_parameter_field( + ecl_file, + init_file_fmt, + min_std_file, + truncation, + value_min, + value_max, + init_transform, + output_transform, + ) + + elif var_type_string == ConfigKeys.GENERAL_KEY: + config_node._update_general_field( + ecl_file, + enkf_infile, + init_file_fmt, + min_std_file, + truncation, + value_min, + value_max, + init_transform, + input_transform, + output_transform, + ) + + return config_node + + # CONTAINER creation + @classmethod + def create_container(cls, key): + config_node = cls._alloc_container(key) + + if config_node is None: + raise ValueError("Failed to create CONTAINER node for:%s" % key) + + return config_node + + def free(self): + self._free() + + def __repr__(self): + key = self.getKey() + vt = self.getVariableType() + imp = self.getImplementationType() + content = "key = %s, var_type = %s, implementation = %s" % (key, vt, imp) + return self._create_repr(content) + + def getModelConfig(self): + implementation_type = self.getImplementationType() + + if implementation_type == ErtImplType.FIELD: + return self.getFieldModelConfig() + elif implementation_type == ErtImplType.GEN_DATA: + return self.getDataModelConfig() + elif implementation_type == ErtImplType.GEN_KW: + return self.getKeywordModelConfig() + elif implementation_type == ErtImplType.SUMMARY: + return SummaryConfig.createCReference( + self.getPointerReference(), parent=self + ) + elif implementation_type == ErtImplType.EXT_PARAM: + return ExtParamConfig.createCReference( + self.getPointerReference(), parent=self + ) + else: + print( + "[EnkfConfigNode::getModelConfig()] Unhandled implementation model type: %i" + % implementation_type + ) + # raise NotImplementedError("Unknown model type: %i" % type) + + def getKey(self): + return self._get_key() + + def __ne__(self, other): + return not self == other + + def __eq__(self, other): + """@rtype: bool""" + if self.getImplementationType() != other.getImplementationType(): + return False + + if self.getKey() != other.getKey(): + return False + + if self.getImplementationType() == ErtImplType.EXT_PARAM: + if self.get_init_file_fmt() != other.get_init_file_fmt(): + return False + if self.get_min_std_file() != other.get_min_std_file(): + return False + if self.get_enkf_outfile() != other.get_enkf_outfile(): + return False + if self.getUseForwardInit() != other.getUseForwardInit(): + return False + elif self.getImplementationType() == ErtImplType.GEN_DATA: + if self.getDataModelConfig() != other.getDataModelConfig(): + return False + if self.get_init_file_fmt() != other.get_init_file_fmt(): + return False + if self.get_enkf_outfile() != other.get_enkf_outfile(): + return False + if self.get_enkf_infile() != other.get_enkf_infile(): + return False + if self.getUseForwardInit() != other.getUseForwardInit(): + return False + elif self.getImplementationType() == ErtImplType.GEN_KW: + if self.getKeywordModelConfig() != other.getKeywordModelConfig(): + return False + if self.get_init_file_fmt() != other.get_init_file_fmt(): + return False + if self.get_min_std_file() != other.get_min_std_file(): + return False + if self.get_enkf_outfile() != other.get_enkf_outfile(): + return False + if self.getUseForwardInit() != other.getUseForwardInit(): + return False + elif self.getImplementationType() == ErtImplType.CONTAINER: + a = [self.get_container_key(i) for i in range(self.get_container_size())] + b = [other.get_container_key(i) for i in range(other.get_container_size())] + if a != b: + return False + elif self.getImplementationType() == ErtImplType.SUMMARY: + if self.getSummaryModelConfig() != other.getSummaryModelConfig(): + return False + elif self.getImplementationType() == ErtImplType.SURFACE: + if self.get_init_file_fmt() != other.get_init_file_fmt(): + return False + if self.getUseForwardInit() != other.getUseForwardInit(): + return False + if self.get_enkf_outfile() != other.get_enkf_outfile(): + return False + if self.get_min_std_file() != other.get_min_std_file(): + return False + elif self.getImplementationType() == ErtImplType.FIELD: + if self.getFieldModelConfig() != other.getFieldModelConfig(): + return False + if self.getUseForwardInit() != other.getUseForwardInit(): + return False + if self.get_init_file_fmt() != other.get_init_file_fmt(): + return False + if self.get_min_std_file() != other.get_min_std_file(): + return False + if self.get_enkf_outfile() != other.get_enkf_outfile(): + return False + + return True diff --git a/res/enkf/config/ext_param_config.py b/res/enkf/config/ext_param_config.py new file mode 100644 index 00000000000..4268e075c47 --- /dev/null +++ b/res/enkf/config/ext_param_config.py @@ -0,0 +1,150 @@ +# Copyright (C) 2017 Equinor ASA, Norway. +# +# The file 'ext_param_config.py' 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. +from cwrap import BaseCClass +from six import string_types +from res import ResPrototype +from ecl.util.util import StringList + + +class ExtParamConfig(BaseCClass): + TYPE_NAME = "ext_param_config" + _alloc = ResPrototype( + "void* ext_param_config_alloc( char*, stringlist )", bind=False + ) + _size = ResPrototype("int ext_param_config_get_data_size( ext_param_config )") + _iget_key = ResPrototype( + "char* ext_param_config_iget_key( ext_param_config , int)" + ) + _free = ResPrototype("void ext_param_config_free( ext_param_config )") + _has_key = ResPrototype( + "bool ext_param_config_has_key( ext_param_config , char* )" + ) + _key_index = ResPrototype( + "int ext_param_config_get_key_index(ext_param_config, char*)" + ) + _ikey_get_suffix_count = ResPrototype( + "int ext_param_config_ikey_get_suffix_count(ext_param_config, int)" + ) + _ikey_iget_suffix = ResPrototype( + "char* ext_param_config_ikey_iget_suffix(ext_param_config, int, int)" + ) + _ikey_set_suffixes = ResPrototype( + "void ext_param_config_ikey_set_suffixes(ext_param_config, int, stringlist)" + ) + + def __init__(self, key, input_keys): + """Create an ExtParamConfig for @key with the given @input_keys + + @input_keys can be either a list of keys as strings or a dict with + keys as strings and a list of suffixes for each key. + If a list of strings is given, the order is preserved. + """ + try: + keys = input_keys.keys() # extract keys if suffixes are also given + suffixmap = input_keys.items() + except AttributeError: + keys = input_keys # assume list of keys + suffixmap = {} + + if len(keys) != len(set(keys)): + raise ValueError("Duplicate keys for key '{}' - keys: {}".format(key, keys)) + + keys = StringList(initial=input_keys) + c_ptr = self._alloc(key, keys) + super(ExtParamConfig, self).__init__(c_ptr) + + for k, suffixes in suffixmap: + suffixlist = StringList(initial=suffixes) + if len(suffixes) == 0: + raise ValueError( + "No suffixes for key '{}/{}' - suffixes: {}".format( + key, k, suffixes + ) + ) + if len(suffixes) != len(set(suffixes)): + raise ValueError( + "Duplicate suffixes for key '{}/{}' - suffixes: {}".format( + key, k, suffixes + ) + ) + if any(len(s) == 0 for s in suffixes): + raise ValueError( + "Empty suffix encountered for key '{}/{}' - suffixes: {}".format( + key, k, suffixes + ) + ) + self._ikey_set_suffixes(self._key_index(k), suffixlist) + + def __len__(self): + return self._size() + + def __contains__(self, key): + """Check if the @key is present in the configuration + + @key can be a single string or a tuple (key, suffix) + """ + if isinstance(key, tuple): + key, sfx = key + kidx = self._key_index(key) + if kidx < 0: + return False + return sfx in self._get_suffixes(kidx) + + # assume key is just a string + return self._has_key(key) + + def __getitem__(self, index): + """Retrieve an item from the configuration + + If @index is a string, assumes its a key and retrieves the suffixes + for that key + if @index is an integer value, return the key and the suffixes for + that index + An IndexError is raised if the item is not found + """ + if isinstance(index, string_types): + index = self._key_index(index) + if index < 0: + raise IndexError('Key "{}" not found'.format(index)) + return self._get_suffixes(index) + + # assume index is an integer + if index < 0: + return index + len(self) + if index >= len(self): + raise IndexError( + "Invalid key index {}. Valid range is [0, {})".format(index, len(self)) + ) + key = self._iget_key(index) + suffixes = self._get_suffixes(index) + return key, suffixes + + def _get_suffixes(self, kidx): + suffix_count = self._ikey_get_suffix_count(kidx) + return [self._ikey_iget_suffix(kidx, s) for s in range(suffix_count)] + + def items(self): + index = 0 + while index < len(self): + yield self[index] + index += 1 + + def keys(self): + for k, _ in self.items(): + yield k + + def free(self): + self._free() diff --git a/res/enkf/config/field_config.py b/res/enkf/config/field_config.py new file mode 100644 index 00000000000..8c7da9e87d2 --- /dev/null +++ b/res/enkf/config/field_config.py @@ -0,0 +1,149 @@ +# Copyright (C) 2012 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. +from cwrap import BaseCClass + +from res import ResPrototype +from res.enkf.enums import EnkfFieldFileFormatEnum +from ecl.grid import EclGrid +from .field_type_enum import FieldTypeEnum + + +class FieldConfig(BaseCClass): + TYPE_NAME = "field_config" + + _alloc = ResPrototype( + "void* field_config_alloc_empty(char* , ecl_grid , void* , bool)", bind=False + ) + _free = ResPrototype("void field_config_free( field_config )") + _get_type = ResPrototype("field_type_enum field_config_get_type(field_config)") + _get_truncation_mode = ResPrototype( + "int field_config_get_truncation_mode(field_config)" + ) + _get_truncation_min = ResPrototype( + "double field_config_get_truncation_min(field_config)" + ) + _get_truncation_max = ResPrototype( + "double field_config_get_truncation_max(field_config)" + ) + _get_init_transform_name = ResPrototype( + "char* field_config_get_init_transform_name(field_config)" + ) + _get_output_transform_name = ResPrototype( + "char* field_config_get_output_transform_name(field_config)" + ) + _ijk_active = ResPrototype( + "bool field_config_ijk_active(field_config, int, int, int)" + ) + _get_nx = ResPrototype("int field_config_get_nx(field_config)") + _get_ny = ResPrototype("int field_config_get_ny(field_config)") + _get_nz = ResPrototype("int field_config_get_nz(field_config)") + _get_grid = ResPrototype("ecl_grid_ref field_config_get_grid(field_config)") + _get_data_size = ResPrototype( + "int field_config_get_data_size_from_grid(field_config)" + ) + _export_format = ResPrototype( + "enkf_field_file_format_enum field_config_default_export_format(char*)", + bind=False, + ) + _guess_filetype = ResPrototype( + "enkf_field_file_format_enum field_config_guess_file_type(char*)", bind=False + ) + + def __init__(self, kw, grid): + c_ptr = self._alloc(kw, grid, None, False) + super(FieldConfig, self).__init__(c_ptr) + + @classmethod + def exportFormat(cls, filename): + export_format = cls._export_format(filename) + if export_format in [ + EnkfFieldFileFormatEnum.ECL_GRDECL_FILE, + EnkfFieldFileFormatEnum.RMS_ROFF_FILE, + ]: + return export_format + else: + raise ValueError( + "Could not determine grdecl / roff format from:%s" % filename + ) + + @classmethod + def guessFiletype(cls, filename): + return cls._guess_filetype(filename) + + def get_type(self): + return self._get_type() + + def get_truncation_mode(self): + return self._get_truncation_mode() + + def get_truncation_min(self): + return self._get_truncation_min() + + def get_init_transform_name(self): + return self._get_init_transform_name() + + def get_output_transform_name(self): + return self._get_output_transform_name() + + def get_truncation_max(self): + return self._get_truncation_max() + + def get_nx(self): + return self._get_nx() + + def get_ny(self): + return self._get_ny() + + def get_nz(self): + return self._get_nz() + + def get_data_size(self): + return self._get_data_size() + + def get_grid(self): + return self._get_grid() + + def ijk_active(self, i, j, k): + return self._ijk_active(i, j, k) + + def free(self): + self._free() + + def __repr__(self): + tp = self.get_type() + nx, ny, nz = self.get_nx(), self.get_ny(), self.get_nz() + cnt = "type = %s, nx = %d, ny = %d, nz = %d" % (tp, nx, ny, nz) + return self._create_repr(cnt) + + def __ne__(self, other): + return not self == other + + def __eq__(self, other): + """@rtype: bool""" + if self.get_init_transform_name() != other.get_init_transform_name(): + return False + if self.get_output_transform_name() != other.get_output_transform_name(): + return False + if self.get_truncation_max() != other.get_truncation_max(): + return False + if self.get_truncation_min() != other.get_truncation_min(): + return False + if self.get_truncation_mode() != other.get_truncation_mode(): + return False + if self.get_type() != other.get_type(): + return False + + return True diff --git a/res/enkf/config/field_type_enum.py b/res/enkf/config/field_type_enum.py new file mode 100644 index 00000000000..fd2d5cf7c99 --- /dev/null +++ b/res/enkf/config/field_type_enum.py @@ -0,0 +1,31 @@ +# 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. + +from cwrap import BaseCEnum + + +class FieldTypeEnum(BaseCEnum): + TYPE_NAME = "field_type_enum" + ECLIPSE_RESTART = None + ECLIPSE_PARAMETER = None + GENERAL = None + UNKNOWN_FIELD_TYPE = None + + +FieldTypeEnum.addEnum("ECLIPSE_RESTART", 1) +FieldTypeEnum.addEnum("ECLIPSE_PARAMETER", 2) +FieldTypeEnum.addEnum("GENERAL", 3) +FieldTypeEnum.addEnum("UNKNOWN_FIELD_TYPE", 4) diff --git a/res/enkf/config/gen_data_config.py b/res/enkf/config/gen_data_config.py new file mode 100644 index 00000000000..0d630b54162 --- /dev/null +++ b/res/enkf/config/gen_data_config.py @@ -0,0 +1,151 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'gen_data_config.py' 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. +from cwrap import BaseCClass +from res import ResPrototype +from res.enkf.enums import GenDataFileType + + +class GenDataConfig(BaseCClass): + TYPE_NAME = "gen_data_config" + + _alloc = ResPrototype( + "void* gen_data_config_alloc_GEN_DATA_result( char* , gen_data_file_format_type)", + bind=False, + ) + _free = ResPrototype("void gen_data_config_free( gen_data_config )") + _get_output_format = ResPrototype( + "gen_data_file_format_type gen_data_config_get_output_format(gen_data_config)" + ) + _get_input_format = ResPrototype( + "gen_data_file_format_type gen_data_config_get_input_format(gen_data_config)" + ) + _get_template_file = ResPrototype( + "char* gen_data_config_get_template_file(gen_data_config)" + ) + _get_template_key = ResPrototype( + "char* gen_data_config_get_template_key(gen_data_config)" + ) + _get_initial_size = ResPrototype( + "int gen_data_config_get_initial_size(gen_data_config)" + ) + _has_report_step = ResPrototype( + "bool gen_data_config_has_report_step(gen_data_config, int)" + ) + _get_data_size = ResPrototype( + "int gen_data_config_get_data_size__(gen_data_config , int)" + ) + _get_key = ResPrototype("char* gen_data_config_get_key(gen_data_config)") + _get_active_mask = ResPrototype( + "bool_vector_ref gen_data_config_get_active_mask(gen_data_config)" + ) + _get_num_report_step = ResPrototype( + "int gen_data_config_num_report_step(gen_data_config)" + ) + _iget_report_step = ResPrototype( + "int gen_data_config_iget_report_step(gen_data_config, int)" + ) + + def __init__(self, key, input_format=GenDataFileType.ASCII): + # Can currently only create GEN_DATA instances which should be used + # as result variables. + c_pointer = self._alloc(key, input_format) + super(GenDataConfig, self).__init__(c_pointer) + + def get_template_file(self): + return self._get_template_file() + + def get_template_key(self): + return self._get_template_key() + + def getDataSize(self, report_step): + data_size = self._get_data_size(report_step) + if data_size < 0: + raise ValueError( + "No data has been loaded for %s at report step:%d " + % (self.getName(), report_step) + ) + else: + return data_size + + def getActiveMask(self): + return self._get_active_mask() + + def getName(self): + return self.name() + + def name(self): + return self._get_key() + + def get_initial_size(self): + return self._get_initial_size() + + def getOutputFormat(self): + return self._get_output_format() + + def getInputFormat(self): + return self._get_input_format() + + def free(self): + self._free() + + def __repr__(self): + nm = self.name() + tk = self.get_template_key() + iz = self.get_initial_size() + return "GenDataConfig(name = %s, template_key = %s, initial_size = %d) %s" % ( + nm, + tk, + iz, + self._ad_str(), + ) + + def hasReportStep(self, report_step): + """@rtype: bool""" + return self._has_report_step(report_step) + + def getNumReportStep(self): + """@rtype: int""" + return self._get_num_report_step() + + def getReportStep(self, index): + """@rtype: int""" + return self._iget_report_step(index) + + def getReportSteps(self): + """@rtype: list of int""" + return [self.getReportStep(index) for index in range(self.getNumReportStep())] + + def __ne__(self, other): + return not self == other + + def __eq__(self, other): + """@rtype: bool""" + if self.getName() != other.getName(): + return False + + if self.get_template_key() != other.get_template_key(): + return False + + if self.getInputFormat() != other.getInputFormat(): + return False + + if self.getOutputFormat() != other.getOutputFormat(): + return False + + if self.getReportSteps() != other.getReportSteps(): + return False + + return True diff --git a/res/enkf/config/gen_kw_config.py b/res/enkf/config/gen_kw_config.py new file mode 100644 index 00000000000..a386569c52a --- /dev/null +++ b/res/enkf/config/gen_kw_config.py @@ -0,0 +1,173 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'gen_kw_config.py' 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. +from cwrap import BaseCClass +import os +from res import ResPrototype +from ecl.util.util import StringList + + +class GenKwConfig(BaseCClass): + TYPE_NAME = "gen_kw_config" + + _free = ResPrototype("void gen_kw_config_free( gen_kw_config )") + _alloc_empty = ResPrototype( + "void* gen_kw_config_alloc_empty( char*, char* )", bind=False + ) + _get_template_file = ResPrototype( + "char* gen_kw_config_get_template_file(gen_kw_config)" + ) + _set_template_file = ResPrototype( + "void gen_kw_config_set_template_file(gen_kw_config , char*)" + ) + _get_parameter_file = ResPrototype( + "char* gen_kw_config_get_parameter_file(gen_kw_config)" + ) + _set_parameter_file = ResPrototype( + "void gen_kw_config_set_parameter_file(gen_kw_config, char*)" + ) + _alloc_name_list = ResPrototype( + "stringlist_obj gen_kw_config_alloc_name_list(gen_kw_config)" + ) + _should_use_log_scale = ResPrototype( + "bool gen_kw_config_should_use_log_scale(gen_kw_config, int)" + ) + _get_key = ResPrototype("char* gen_kw_config_get_key(gen_kw_config)") + _get_tag_fmt = ResPrototype("char* gen_kw_config_get_tag_fmt(gen_kw_config)") + _size = ResPrototype("int gen_kw_config_get_data_size(gen_kw_config)") + _iget_name = ResPrototype("char* gen_kw_config_iget_name(gen_kw_config, int)") + _get_function_type = ResPrototype( + "char* gen_kw_config_iget_function_type(gen_kw_config, int)" + ) + _get_function_parameter_names = ResPrototype( + "stringlist_ref gen_kw_config_iget_function_parameter_names(gen_kw_config, int)" + ) + _get_function_parameter_values = ResPrototype( + "double_vector_ref gen_kw_config_iget_function_parameter_values(gen_kw_config, int)" + ) + + def __init__(self, key, template_file, parameter_file, tag_fmt="<%s>"): + """ + @type key: str + @type tag_fmt: str + """ + if not os.path.isfile(template_file): + raise IOError("No such file:%s" % template_file) + + if not os.path.isfile(parameter_file): + raise IOError("No such file:%s" % parameter_file) + + c_ptr = self._alloc_empty(key, tag_fmt) + if c_ptr: + super(GenKwConfig, self).__init__(c_ptr) + else: + raise ValueError( + 'Could not instantiate GenKwConfig with key="%s" and tag_fmt="%s"' + % (key, tag_fmt) + ) + self._set_parameter_file(parameter_file) + self._set_template_file(template_file) + self.__str__ = self.__repr__ + + def getTemplateFile(self): + return self._get_template_file() + + def getParameterFile(self): + return self._get_parameter_file() + + def getKeyWords(self): + """@rtype: StringList""" + return self._alloc_name_list() + + def shouldUseLogScale(self, index): + """@rtype: bool""" + return self._should_use_log_scale(index) + + def free(self): + self._free() + + def __repr__(self): + return 'GenKwConfig(key = "%s", tag_fmt = "%s") at 0x%x' % ( + self.getKey(), + self.tag_fmt, + self._address(), + ) + + def getKey(self): + """@rtype: str""" + return self._get_key() + + @property + def tag_fmt(self): + return self._get_tag_fmt() + + def __len__(self): + return self._size() + + def __getitem__(self, index): + """@rtype: str""" + return self._iget_name(index) + + def __iter__(self): + index = 0 + while index < len(self): + yield self[index] + index += 1 + + def __ne__(self, other): + return not self == other + + def __eq__(self, other): + """@rtype: bool""" + if self.getTemplateFile() != other.getTemplateFile(): + return False + + if self.getParameterFile() != other.getParameterFile(): + return False + + if self.getKey() != other.getKey(): + return False + + return True + + def get_priors(self): + """ + @rtype: list + [ + { + "key" : "", + "function" : "" + "parameters" : { + "" : "" + } + } + ] + """ + priors = [] + keys = self.getKeyWords() + for i, key in enumerate(keys): + function_type = self._get_function_type(i) + parameter_names = self._get_function_parameter_names(i) + parameter_values = self._get_function_parameter_values(i) + el = { + "key": key, + "function": function_type, + "parameters": { + name: value + for (name, value) in zip(parameter_names, parameter_values) + }, + } + priors.append(el) + return priors diff --git a/res/enkf/config/summary_config.py b/res/enkf/config/summary_config.py new file mode 100644 index 00000000000..b37ada607f8 --- /dev/null +++ b/res/enkf/config/summary_config.py @@ -0,0 +1,51 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'field_config.py' 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. +from cwrap import BaseCClass +from res import ResPrototype +from res.enkf import LoadFailTypeEnum + + +class SummaryConfig(BaseCClass): + TYPE_NAME = "summary_config" + _alloc = ResPrototype( + "void* summary_config_alloc(char*, load_fail_type)", bind=False + ) + _free = ResPrototype("void summary_config_free(summary_config)") + _get_var = ResPrototype("char* summary_config_get_var(summary_config)") + + def __init__(self, key, load_fail=LoadFailTypeEnum.LOAD_FAIL_WARN): + c_ptr = self._alloc(key, load_fail) + super(SummaryConfig, self).__init__(c_ptr) + + def __repr__(self): + return "SummaryConfig() %s" % self._ad_str() + + def free(self): + self._free() + + @property + def key(self): + return self._get_var() + + def __ne__(self, other): + return not self == other + + def __eq__(self, other): + """@rtype: bool""" + if self.key != other.key: + return False + + return True diff --git a/res/enkf/config_keys.py b/res/enkf/config_keys.py new file mode 100644 index 00000000000..fb55f06446c --- /dev/null +++ b/res/enkf/config_keys.py @@ -0,0 +1,382 @@ +# Copyright (C) 2017 Equinor ASA, Norway. +# +# The file 'config_keys.py' 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. + +from res import ResPrototype + + +class ConfigKeys: + + _config_directory_key = ResPrototype( + "char* config_keys_get_config_directory_key()", bind=False + ) + _config_file_key = ResPrototype( + "char* config_keys_get_config_file_key()", bind=False + ) + _queue_system_key = ResPrototype( + "char* config_keys_get_queue_system_key()", bind=False + ) + _run_template_key = ResPrototype( + "char* config_keys_get_run_template_key()", bind=False + ) + _gen_kw_key = ResPrototype("char* config_keys_get_gen_kw_key()", bind=False) + _history_source_key = ResPrototype( + "char* config_keys_get_history_source_key()", bind=False + ) + _queue_option_key = ResPrototype( + "char* config_keys_get_queue_option_key()", bind=False + ) + _install_job_key = ResPrototype( + "char* config_keys_get_install_job_key()", bind=False + ) + _path_key = ResPrototype("char* config_keys_get_path_key()", bind=False) + _show_refcase_key = ResPrototype( + "char* config_keys_get_show_refcase_key()", bind=False + ) + _show_history_key = ResPrototype( + "char* config_keys_get_show_history_key()", bind=False + ) + _install_job_directory_key = ResPrototype( + "char* config_keys_get_install_job_directory_key()", bind=False + ) + + _log_file_key = ResPrototype("char* config_keys_get_log_file_key()", bind=False) + _log_level_key = ResPrototype("char* config_keys_get_log_level_key()", bind=False) + _update_log_path = ResPrototype( + "char* config_keys_get_update_log_path_key()", bind=False + ) + _summary = ResPrototype("char* config_keys_get_summary_key()", bind=False) + + _max_runtime = ResPrototype("char* config_keys_get_max_runtime_key()", bind=False) + _min_realizations = ResPrototype( + "char* config_keys_get_min_realizations_key()", bind=False + ) + _max_submit = ResPrototype("char* config_keys_get_max_submit_key()", bind=False) + _umask = ResPrototype("char* config_keys_get_umask_key()", bind=False) + _data_kw_key = ResPrototype("char* config_keys_get_data_kw_key()", bind=False) + _runpath_file = ResPrototype("char* config_keys_get_runpath_file_key()", bind=False) + + # ************* ECL config ************* + _eclbase = ResPrototype("char* config_keys_get_eclbase_key()", bind=False) + _data_file = ResPrototype("char* config_keys_get_data_file_key()", bind=False) + _grid = ResPrototype("char* config_keys_get_grid_key()", bind=False) + _refcase = ResPrototype("char* config_keys_get_refcase_key()", bind=False) + _refcase_list = ResPrototype("char* config_keys_get_refcase_list_key()", bind=False) + _end_date = ResPrototype("char* config_keys_get_end_date_key()", bind=False) + _schedule_prediction_file = ResPrototype( + "char* config_keys_get_schedule_prediction_file_key()", bind=False + ) + # ************* ECL config ************* + + # ************* Model config ************* + _num_realizations = ResPrototype( + "char* config_keys_get_num_realizations_key()", bind=False + ) + _enspath = ResPrototype("char* config_keys_get_enspath_key()", bind=False) + _history_source = ResPrototype( + "char* config_keys_get_history_source_key()", bind=False + ) + _obs_config = ResPrototype("char* config_keys_get_obs_config_key()", bind=False) + _time_map = ResPrototype("char* config_keys_get_time_map_key()", bind=False) + _jobname = ResPrototype("char* config_keys_get_jobname_key()", bind=False) + _forward_model_key = ResPrototype( + "char* config_keys_get_forward_model_key()", bind=False + ) + _simulation_job_key = ResPrototype( + "char* config_keys_get_simulation_job_key()", bind=False + ) + _max_resample_key = ResPrototype( + "char* config_keys_get_max_resample_key()", bind=False + ) + _data_root_key = ResPrototype("char* config_keys_get_data_root_key()", bind=False) + _rftpath_key = ResPrototype("char* config_keys_get_rftpath_key()", bind=False) + _gen_kw_export_name_key = ResPrototype( + "char* config_keys_get_gen_kw_export_name_key()", bind=False + ) + _runpath = ResPrototype("char* config_keys_get_runpath_key()", bind=False) + # ************* Model config ************* + + _gen_data = ResPrototype("char* config_keys_get_gen_data_key()", bind=False) + _result_file = ResPrototype("char* config_keys_get_result_file()", bind=False) + _report_steps = ResPrototype("char* config_keys_get_report_steps()", bind=False) + _input_format = ResPrototype("char* config_keys_get_input_format()", bind=False) + _ecl_file = ResPrototype("char* config_keys_get_ecl_file()", bind=False) + _output_format = ResPrototype("char* config_keys_get_output_format()", bind=False) + _init_files = ResPrototype("char* config_keys_get_init_files()", bind=False) + _random_seed = ResPrototype("char* config_keys_get_random_seed()", bind=False) + _license_path_key = ResPrototype( + "char* config_keys_get_license_path_key()", bind=False + ) + _setenv_key = ResPrototype("char* config_keys_get_setenv_key()", bind=False) + _job_script_key = ResPrototype("char* config_keys_get_job_script_key()", bind=False) + _num_cpu_key = ResPrototype("char* config_keys_get_num_cpu_key()", bind=False) + _define_key = ResPrototype("char* config_keys_get_define_key()", bind=False) + _load_workflow_job_key = ResPrototype( + "char* config_keys_get_load_workflow_job_key()", bind=False + ) + _workflow_job_directory_key = ResPrototype( + "char* config_keys_get_workflow_job_directory_key()", bind=False + ) + _load_workflow_key = ResPrototype( + "char* config_keys_get_load_workflow_key()", bind=False + ) + + # hook_manager config keys + _hook_workflow_key = ResPrototype( + "char* config_keys_get_hook_workflow_key()", bind=False + ) + HOOK_WORKFLOW_KEY = _hook_workflow_key() + # hook_manager config keys + + # analysis_iter_config keys + _iter_case_key = ResPrototype("char* config_keys_get_iter_case_key()", bind=False) + _iter_count_key = ResPrototype("char* config_keys_get_iter_count_key()", bind=False) + _iter_retry_count_key = ResPrototype( + "char* config_keys_get_iter_retry_count_key()", bind=False + ) + + ITER_CASE = _iter_case_key() + ITER_COUNT = _iter_count_key() + ITER_RETRY_COUNT = _iter_retry_count_key() + # analysis_iter_config keys + + # analysis_config keys + _alpha_key = ResPrototype("char* config_keys_get_alpha()", bind=False) + _std_cutoff_key = ResPrototype("char* config_keys_get_std_cutoff()", bind=False) + _stop_long_running = ResPrototype( + "char* config_keys_get_stop_long_running()", bind=False + ) + _single_node_update = ResPrototype( + "char* config_keys_get_single_node_update()", bind=False + ) + _std_correlated_obs = ResPrototype( + "char* config_keys_get_std_scale_correlated_obs()", bind=False + ) + + _rerun_key = ResPrototype("char* config_keys_get_rerun()", bind=False) + _rerun_start_key = ResPrototype("char* config_keys_get_rerun_start()", bind=False) + _merge_observations = ResPrototype( + "char* config_keys_get_merge_observations()", bind=False + ) + _analysis_load = ResPrototype("char* config_keys_get_analysis_load()", bind=False) + _analysis_copy = ResPrototype("char* config_keys_get_analysis_copy()", bind=False) + _analysis_select = ResPrototype( + "char* config_keys_get_analysis_select()", bind=False + ) + _analysis_set_var = ResPrototype( + "char* config_keys_get_analysis_set_var()", bind=False + ) + + # slurm options + _sbatch_option = ResPrototype( + "char* config_keys_get_slurm_sbatch_option()", bind=False + ) + _scancel_option = ResPrototype( + "char* config_keys_get_slurm_scancel_option()", bind=False + ) + _scontrol_option = ResPrototype( + "char* config_keys_get_slurm_scontrol_option()", bind=False + ) + _squeue_option = ResPrototype( + "char* config_keys_get_slurm_squeue_option()", bind=False + ) + _partition_option = ResPrototype( + "char* config_keys_get_slurm_partition_option()", bind=False + ) + _squeue_timeout_option = ResPrototype( + "char* config_keys_get_slurm_squeue_timeout_option()", bind=False + ) + _max_runtime_option = ResPrototype( + "char* config_keys_get_slurm_max_runtime_option()", bind=False + ) + _memory_option = ResPrototype( + "char* config_keys_get_slurm_memory_option()", bind=False + ) + _memory_per_cpu_option = ResPrototype( + "char* config_keys_get_slurm_memory_per_cpu_option()", bind=False + ) + _exclude_host_option = ResPrototype( + "char* config_keys_get_slurm_exclude_host_option()", bind=False + ) + _include_host_option = ResPrototype( + "char* config_keys_get_slurm_include_host_option()", bind=False + ) + + ALPHA_KEY = _alpha_key() + STD_CUTOFF_KEY = _std_cutoff_key() + STOP_LONG_RUNNING = _stop_long_running() + SINGLE_NODE_UPDATE = _single_node_update() + STD_CORRELATED_OBS = _std_correlated_obs() + GLOBAL_STD_SCALING = "GLOBAL_STD_SCALING" + RERUN_KEY = _rerun_key() + RERUN_START_KEY = _rerun_start_key() + MERGE_OBSERVATIONS = _merge_observations() + ANALYSIS_LOAD = _analysis_load() + USER_NAME = "USER_NAME" + LIB_NAME = "LIB_NAME" + ANALYSIS_COPY = _analysis_copy() + SRC_NAME = "SRC_NAME" + DST_NAME = "DST_NAME" + ANALYSIS_SET_VAR = _analysis_set_var() + MODULE_NAME = "MODULE_NAME" + VAR_NAME = "VAR_NAME" + ANALYSIS_SELECT = _analysis_select() + # analysis_config keys + + ARGLIST = "ARGLIST" + CONFIG_DIRECTORY = _config_directory_key() + CONFIG_FILE_KEY = _config_file_key() + DEFINES = "DEFINES" + DEFINE_KEY = _define_key() + INTERNALS = "INTERNALS" + SIMULATION = "SIMULATION" + LOGGING = "LOGGING" + SEED = "SEED" + QUEUE_SYSTEM = _queue_system_key() + RUN_TEMPLATE = _run_template_key() + _template_key = ResPrototype("char* config_keys_get_template_key()", bind=False) + TEMPLATE = _template_key() + EXPORT = "EXPORT" + GEN_KW = _gen_kw_key() + NAME = "NAME" + OUT_FILE = "OUT_FILE" + PARAMETER_FILE = "PARAMETER_FILE" + PATH = "PATH" + QUEUE_OPTION = _queue_option_key() + DRIVER_NAME = "DRIVER_NAME" + OPTION = "OPTION" + VALUE = "VALUE" + INSTALL_JOB = _install_job_key() + PATH_KEY = _path_key + SHOW_REFCASE_KEY = _show_refcase_key + SHOW_HISTORY_KEY = _show_history_key + LOG_FILE = _log_file_key() + LOG_LEVEL = _log_level_key() + UPDATE_LOG_PATH = _update_log_path() + RANDOM_SEED = _random_seed() + SUMMARY = _summary() + MAX_RUNTIME = _max_runtime() + MIN_REALIZATIONS = _min_realizations() + MAX_SUBMIT = _max_submit() + UMASK = _umask() + MAX_RUNNING = "MAX_RUNNING" + DATA_KW_KEY = _data_kw_key() + RUNPATH_FILE = _runpath_file() + RUNPATH_LIST_FILE = ".ert_runpath_list" + GEN_DATA = _gen_data() + RESULT_FILE = _result_file() + REPORT_STEPS = _report_steps() + INPUT_FORMAT = _input_format() + ECL_FILE = _ecl_file() + OUTPUT_FORMAT = _output_format() + INIT_FILES = _init_files() + LICENSE_PATH = _license_path_key() + INSTALL_JOB_DIRECTORY = _install_job_directory_key() + SETENV = _setenv_key() + JOB_SCRIPT = _job_script_key() + NUM_CPU = _num_cpu_key() + USER_MODE = "USER_MODE" + LOAD_WORKFLOW_JOB = _load_workflow_job_key() + WORKFLOW_JOB_DIRECTORY = _workflow_job_directory_key() + LOAD_WORKFLOW = _load_workflow_key() + + # ************* ECL config ************* + ECLBASE = _eclbase() + DATA_FILE = _data_file() + GRID = _grid() + REFCASE = _refcase() + REFCASE_LIST = _refcase_list() + END_DATE = _end_date() + SCHEDULE_PREDICTION_FILE = _schedule_prediction_file() + # ************* ECL config ************* + + # ************* Model config ************* + JOBNAME = _jobname() + FORWARD_MODEL = _forward_model_key() + SIMULATION_JOB = _simulation_job_key() + RUNPATH = _runpath() + MAX_RESAMPLE = _max_resample_key() + DATAROOT = _data_root_key() + RFTPATH = _rftpath_key() + GEN_KW_EXPORT_NAME = _gen_kw_export_name_key() + NUM_REALIZATIONS = _num_realizations() + ENSPATH = _enspath() + HISTORY_SOURCE = _history_source() + OBS_CONFIG = _obs_config() + TIME_MAP = _time_map() + # ************* Model config ************* + + # ************* Ensemble config ************* + _gen_param_key = ResPrototype("char* config_keys_get_gen_param_key()", bind=False) + GEN_PARAM = _gen_param_key() + _forward_init_key = ResPrototype( + "char* config_keys_get_forward_init_key()", bind=False + ) + FORWARD_INIT = _forward_init_key() + _min_std_key = ResPrototype("char* config_keys_get_min_std_key()", bind=False) + MIN_STD = _min_std_key() + _key_key = ResPrototype("char* config_keys_get_key_key()", bind=False) + KEY_KEY = _key_key() + _kw_tag_format_key = ResPrototype( + "char* config_keys_get_kw_tag_format_key()", bind=False + ) + GEN_KW_TAG_FORMAT = _kw_tag_format_key() + _surface_key = ResPrototype("char* config_keys_get_surface_key()", bind=False) + SURFACE_KEY = _surface_key() + _base_surface_key = ResPrototype( + "char* config_keys_get_base_surface_key()", bind=False + ) + BASE_SURFACE_KEY = _base_surface_key() + _field_key = ResPrototype("char* config_keys_get_field_key()", bind=False) + FIELD_KEY = _field_key() + VAR_TYPE = "VAR_TYPE" + _init_transform = ResPrototype( + "char* config_keys_get_init_transform_key()", bind=False + ) + INIT_TRANSFORM = _init_transform() + _input_transform = ResPrototype( + "char* config_keys_get_input_transform_key()", bind=False + ) + INPUT_TRANSFORM = _input_transform() + _output_transform = ResPrototype( + "char* config_keys_get_output_transform_key()", bind=False + ) + OUTPUT_TRANSFORM = _output_transform() + _min_key = ResPrototype("char* config_keys_get_min_key()", bind=False) + MIN_KEY = _min_key() + _max_key = ResPrototype("char* config_keys_get_max_key()", bind=False) + MAX_KEY = _max_key() + ENKF_INFILE = "ENKF_INFILE" + _parameter_key = ResPrototype("char* config_keys_get_parameter_key()", bind=False) + PARAMETER_KEY = _parameter_key() + _general_key = ResPrototype("char* config_keys_get_general_key()", bind=False) + GENERAL_KEY = _general_key() + _pred_key = ResPrototype("char* config_keys_get_pred_key()", bind=False) + PRED_KEY = _pred_key() + _container_key = ResPrototype("char* config_keys_get_container_key()", bind=False) + CONTAINER_KEY = _container_key() + # ************* Ensemble config ************* + + SLURM_SBATCH_OPTION = _sbatch_option() + SLURM_SCANCEL_OPTION = _scancel_option() + SLURM_SCONTROL_OPTION = _scontrol_option() + SLURM_SQUEUE_OPTION = _squeue_option() + SLURM_PARTITION_OPTION = _partition_option() + SLURM_SQUEUE_TIMEOUT_OPTION = _squeue_timeout_option() + SLURM_MAX_RUNTIME_OPTION = _max_runtime_option() + SLURM_MEMORY_OPTION = _memory_option() + SLURM_MEMORY_PER_CPU_OPTION = _memory_per_cpu_option() + SLURM_EXCLUDE_HOST_OPTION = _exclude_host_option() + SLURM_INCLUDE_HOST_OPTION = _include_host_option() diff --git a/res/enkf/data/__init__.py b/res/enkf/data/__init__.py new file mode 100644 index 00000000000..fc1da228a0c --- /dev/null +++ b/res/enkf/data/__init__.py @@ -0,0 +1,7 @@ +from .field import Field +from .gen_data import GenData +from .gen_kw import GenKw +from .ext_param import ExtParam +from .enkf_node import EnkfNode + +__all__ = ["Field", "GenKw", "GenData", "ExtParam", "EnkfNode"] diff --git a/res/enkf/data/enkf_node.py b/res/enkf/data/enkf_node.py new file mode 100644 index 00000000000..c182d01c04e --- /dev/null +++ b/res/enkf/data/enkf_node.py @@ -0,0 +1,169 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'enkf_node.py' 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. +import sys +from res.enkf.enums import ErtImplType +from cwrap import BaseCClass +from res import ResPrototype +from res.enkf import EnkfFs, NodeId +from res.enkf.data import GenKw, GenData, Field, ExtParam +from res.enkf.data.summary import Summary + + +class EnkfNode(BaseCClass): + TYPE_NAME = "enkf_node" + _alloc = ResPrototype("void* enkf_node_alloc(enkf_config_node)", bind=False) + _alloc_private = ResPrototype( + "void* enkf_node_alloc_private_container(enkf_config_node)", bind=False + ) + _free = ResPrototype("void enkf_node_free(enkf_node)") + _get_name = ResPrototype("char* enkf_node_get_key(enkf_node)") + _value_ptr = ResPrototype("void* enkf_node_value_ptr(enkf_node)") + _try_load = ResPrototype("bool enkf_node_try_load(enkf_node, enkf_fs, node_id)") + _store = ResPrototype("bool enkf_node_store(enkf_node, enkf_fs, node_id)") + _get_impl_type = ResPrototype( + "ert_impl_type_enum enkf_node_get_impl_type(enkf_node)" + ) + _ecl_write = ResPrototype("void enkf_node_ecl_write(enkf_node, char*, void*, int)") + + def __init__(self, config_node, private=False): + self._private = private + if private: + c_pointer = self._alloc_private(config_node) + else: + c_pointer = self._alloc(config_node) + + if c_pointer: + super(EnkfNode, self).__init__(c_pointer, config_node, True) + else: + p_err = "private " if private else "" + raise ValueError( + "Unable to create %sEnkfNode from given config node." % p_err + ) + + @classmethod + def exportMany( + cls, + config_node, + file_format, + fs, + iens_list, + report_step=0, + file_type=None, + arg=None, + ): + node = EnkfNode(config_node) + for iens in iens_list: + filename = file_format % iens + node_id = NodeId(report_step, iens) + if node.tryLoad(fs, node_id): + if node.export(filename, file_type=file_type, arg=arg): + print("%s[%03d] -> %s" % (config_node.getKey(), iens, filename)) + else: + sys.stderr.write( + "** ERROR: Could not load realisation:%d - export failed" % iens + ) + + def export(self, filename, file_type=None, arg=None): + impl_type = self.getImplType() + if impl_type == ErtImplType.FIELD: + field_node = self.asField() + return field_node.export(filename, file_type=file_type, init_file=arg) + else: + raise NotImplementedError("The export method is only implemented for field") + + def valuePointer(self): + return self._value_ptr() + + def getImplType(self): + """@rtype: res.enkf.enums.ert_impl_type_enum.ErtImplType""" + return self._get_impl_type() + + def asGenData(self): + """@rtype: GenData""" + impl_type = self.getImplType() + assert impl_type == ErtImplType.GEN_DATA + + return GenData.createCReference(self.valuePointer(), self) + + def asGenKw(self): + """@rtype: GenKw""" + impl_type = self.getImplType() + assert impl_type == ErtImplType.GEN_KW + + return GenKw.createCReference(self.valuePointer(), self) + + def asField(self): + """@rtype: Field""" + impl_type = self.getImplType() + assert impl_type == ErtImplType.FIELD + + return Field.createCReference(self.valuePointer(), self) + + def as_summary(self): + """@rtype: Summary""" + impl_type = self.getImplType() + assert impl_type == ErtImplType.SUMMARY + + return Summary.createCReference(self.valuePointer(), self) + + def as_ext_param(self): + """@rtype: ExtParam""" + impl_type = self.getImplType() + assert impl_type == ErtImplType.EXT_PARAM + + return ExtParam.createCReference(self.valuePointer(), self) + + def tryLoad(self, fs, node_id): + """ + @type fs: EnkfFS + @type node_id: NodeId + @rtype: bool + """ + if not isinstance(fs, EnkfFs): + raise TypeError("fs must be an EnkfFs, not %s" % type(fs)) + if not isinstance(node_id, NodeId): + raise TypeError("node_id must be a NodeId, not %s" % type(node_id)) + + return self._try_load(fs, node_id) + + def name(self): + """@rtype: str""" + return self._get_name() + + def load(self, fs, node_id): + if not self.tryLoad(fs, node_id): + raise Exception( + "Could not load node: %s iens: %d report: %d" + % (self.name(), node_id.iens, node_id.report_step) + ) + + def save(self, fs, node_id): + assert isinstance(fs, EnkfFs) + assert isinstance(node_id, NodeId) + + return self._store(fs, node_id) + + def free(self): + self._free() + + def __repr__(self): + pp = ", private" if self._private else "" + return 'EnkfNode(name = "%s"%s) %s' % (self.name(), pp, self._ad_str()) + + def ecl_write(self, path): + filestream_ptr = None + report_step = 0 + self._ecl_write(path, filestream_ptr, report_step) diff --git a/res/enkf/data/ext_param.py b/res/enkf/data/ext_param.py new file mode 100644 index 00000000000..5dcb55b9273 --- /dev/null +++ b/res/enkf/data/ext_param.py @@ -0,0 +1,176 @@ +# Copyright (C) 2017 Equinor ASA, Norway. +# +# The file 'ext_param.py' 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. +import os.path + +from cwrap import BaseCClass, CFILE +from six import string_types + +from res import ResPrototype +from res.enkf.config import ExtParamConfig + + +class ExtParam(BaseCClass): + TYPE_NAME = "ext_param" + _alloc = ResPrototype("void* ext_param_alloc( ext_param_config )", bind=False) + _free = ResPrototype("void ext_param_free( ext_param )") + _iset = ResPrototype("void ext_param_iset( ext_param, int, double)") + _iiset = ResPrototype("void ext_param_iiset( ext_param, int, int, double)") + _key_set = ResPrototype("void ext_param_key_set( ext_param, char*, double)") + _key_suffix_set = ResPrototype( + "void ext_param_key_suffix_set( ext_param, char*, char*, double)" + ) + _iget = ResPrototype("double ext_param_iget( ext_param, int)") + _iiget = ResPrototype("double ext_param_iiget( ext_param, int, int)") + _key_get = ResPrototype("double ext_param_key_get( ext_param, char*)") + _key_suffix_get = ResPrototype( + "double ext_param_key_suffix_get( ext_param, char*, char*)" + ) + _export = ResPrototype("void ext_param_json_export( ext_param, char*)") + _get_config = ResPrototype("void* ext_param_get_config(ext_param)") + + def __init__(self, config): + c_ptr = self._alloc(config) + super(ExtParam, self).__init__(c_ptr) + + def __contains__(self, key): + return key in self.config + + def __len__(self): + return len(self.config) + + def __getitem__(self, index): + if isinstance(index, tuple): + # if the index is key suffix, assume they are both strings + key, suffix = index + if not isinstance(key, string_types) or not isinstance( + suffix, string_types + ): + raise TypeError("Expected a pair of strings, got {}".format(index)) + self._check_key_suffix(key, suffix) + return self._key_suffix_get(key, suffix) + + # index is just the key, it can be either a string or an int + if isinstance(index, string_types): + self._check_key_suffix(index) + return self._key_get(index) + + index = self._roll_key_index(index) + self._check_index(index) + return self._iget(index) + + def __setitem__(self, index, value): + if isinstance(index, tuple): + # if the index is key suffix, assume they are both strings + key, suffix = index + if not isinstance(key, string_types) or not isinstance( + suffix, string_types + ): + raise TypeError("Expected a pair of strings, got {}".format(index)) + self._check_key_suffix(key, suffix) + self._key_suffix_set(key, suffix, value) + return + + # index is just the key, it can be either a string or an int + if isinstance(index, string_types): + self._check_key_suffix(index) + self._key_set(index, value) + else: + index = self._roll_key_index(index) + self._check_index(index) + self._iset(index, value) + + def _roll_key_index(self, index): + """Support indexing from the end of the list of keys""" + return index if index >= 0 else index + len(self) + + def _check_index(self, kidx, sidx=None): + """Raise if any of the following is true: + - kidx is not a valid index for keys + - the key referred to by kidx has no suffixes, but sidx is given + - the key referred to by kidx has suffixes, but sidx is None + - the key referred to by kidx has suffixes, and sidx is not a valid + suffix index + """ + if kidx < 0 or kidx >= len(self): + raise IndexError( + "Invalid key index {}. Valid range is [0, {})".format(kidx, len(self)) + ) + key, suffixes = self.config[kidx] + if not suffixes: + if sidx is None: + return # we are good + raise IndexError( + "Key {} has no suffixes, but suffix {} requested".format(key, sidx) + ) + assert len(suffixes) > 0 + if sidx is None: + raise IndexError( + "Key {} has suffixes, a suffix index must be specified".format(key) + ) + if sidx < 0 or sidx >= len(suffixes): + raise IndexError( + ( + "Suffix index {} is out of range for key {}. Valid range is " + "[0, {})" + ).format(sidx, key, len(suffixes)) + ) + + def _check_key_suffix(self, key, suffix=None): + """Raise if any of the following is true: + - key is not present in config + - key has no suffixes but a suffix is given + - key has suffixes but suffix is None + - key has suffixes but suffix is not among them + """ + if not key in self: + raise KeyError("No such key: {}".format(key)) + suffixes = self.config[key] + if not suffixes: + if suffix is None: + return + raise KeyError( + "Key {} has no suffixes, but suffix {} requested".format(key, suffix) + ) + assert len(suffixes) > 0 + if suffix is None: + raise KeyError( + "Key {} has suffixes, a suffix must be specified".format(key) + ) + if suffix not in suffixes: + raise KeyError( + "Key {} has suffixes {}. Can't find the requested suffix {}".format( + key, suffixes, suffix + ) + ) + + @property + def config(self): + return ExtParamConfig.createCReference(self._get_config(), self) + + # This could in the future be specialized to take a numpy vector, + # which could be vector-assigned in C. + def set_vector(self, values): + if len(values) != len(self): + raise ValueError("Size mismatch") + + for index, value in enumerate(values): + self[index] = value + + def free(self): + self._free() + + def export(self, fname): + self._export(fname) diff --git a/res/enkf/data/field.py b/res/enkf/data/field.py new file mode 100644 index 00000000000..8260399d4c3 --- /dev/null +++ b/res/enkf/data/field.py @@ -0,0 +1,65 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'field.py' 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. +import sys + +from cwrap import BaseCClass +from res import ResPrototype +from res.enkf.config import FieldConfig + + +class Field(BaseCClass): + TYPE_NAME = "field" + + _free = ResPrototype("void field_free( field )") + _get_size = ResPrototype("int field_get_size(field)") + _ijk_get_double = ResPrototype("double field_ijk_get_double(field, int, int, int)") + _iget_double = ResPrototype("double field_iget_double(field, int)") + _export = ResPrototype( + "void field_export(field, char* , fortio , enkf_field_file_format_enum , bool , char*)" + ) + + def __init__(self): + raise NotImplementedError("Class can not be instantiated directly!") + + def __len__(self): + return self._get_size() + + def ijk_get_double(self, i, j, k): + return self._ijk_get_double(i, j, k) + + def __getitem__(self, index): + if 0 <= index < len(self): + return self._iget_double(index) + else: + raise IndexError(f"Index: {index} out of range: [0,{len(self)})") + + def export(self, filename, file_type=None, init_file=None): + output_transform = False + if file_type is None: + try: + file_type = FieldConfig.exportFormat(filename) + except ValueError: + sys.stderr.write( + "Sorry - could not infer output format from filename:%s\n" + % filename + ) + return False + + self._export(filename, None, file_type, output_transform, init_file) + return True + + def free(self): + self._free() diff --git a/res/enkf/data/gen_data.py b/res/enkf/data/gen_data.py new file mode 100644 index 00000000000..ea9a0e1fe13 --- /dev/null +++ b/res/enkf/data/gen_data.py @@ -0,0 +1,61 @@ +from cwrap import BaseCClass +from ecl.util.util import DoubleVector +from res import ResPrototype + + +class GenData(BaseCClass): + TYPE_NAME = "gen_data" + _alloc = ResPrototype("void* gen_data_alloc()", bind=False) + _free = ResPrototype("void gen_data_free(gen_data)") + _size = ResPrototype("int gen_data_get_size(gen_data)") + _iget = ResPrototype("double gen_data_iget_double(gen_data , int)") + _export = ResPrototype( + "void gen_data_export(gen_data , char*, gen_data_file_format_type, fortio)" + ) + _export_data = ResPrototype("void gen_data_export_data(gen_data , double_vector)") + + def __init__(self): + c_ptr = self._alloc() + if c_ptr: + super(GenData, self).__init__(c_ptr) + else: + raise ValueError("Unable to construct GenData object.") + + def __len__(self): + """@rtype: int""" + return self._size() + + def free(self): + self._free() + + def __repr__(self): + return "GenData(len = %d) %s" % (len(self), self._ad_str()) + + def export(self, file_name, file_format_type, fortio): + """ + @type: str + @type: GenDataFileType + @type: FortIO + """ + self._export(file_name, file_format_type, fortio) + + def getData(self): + data = DoubleVector() + self._export_data(data) + return data + + def __getitem__(self, idx): + """Returns an item, or a list if idx is a slice. + Note: When idx is a slice it does not return a new GenData! + """ + ls = len(self) + if isinstance(idx, int): + if idx < 0: + idx += ls + if 0 <= idx < ls: + return self._iget(idx) + raise IndexError("List index out of range.") + if isinstance(idx, slice): + vec = self.getData() + return [vec[i] for i in range(*idx.indices(ls))] + raise TypeError("List indices must be integers, not %s." % str(type(idx))) diff --git a/res/enkf/data/gen_kw.py b/res/enkf/data/gen_kw.py new file mode 100644 index 00000000000..f41c973d5ce --- /dev/null +++ b/res/enkf/data/gen_kw.py @@ -0,0 +1,144 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'gen_kw.py' 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. +import os.path + +from cwrap import BaseCClass, CFILE +import numbers + +from ecl.util.util import DoubleVector +from res import ResPrototype +from res.enkf.config import GenKwConfig + + +class GenKw(BaseCClass): + TYPE_NAME = "gen_kw" + _alloc = ResPrototype("void* gen_kw_alloc(gen_kw_config)", bind=False) + _free = ResPrototype("void gen_kw_free(gen_kw_config)") + _export_parameters = ResPrototype("void gen_kw_write_export_file(gen_kw , char*)") + _export_template = ResPrototype("void gen_kw_ecl_write_template(gen_kw , char* )") + _data_iget = ResPrototype("double gen_kw_data_iget(gen_kw, int, bool)") + _data_iset = ResPrototype("void gen_kw_data_iset(gen_kw, int, double)") + _set_values = ResPrototype("void gen_kw_data_set_vector(gen_kw, double_vector)") + _data_get = ResPrototype("double gen_kw_data_get(gen_kw, char*, bool)") + _data_set = ResPrototype("void gen_kw_data_set(gen_kw, char*, double)") + _size = ResPrototype("int gen_kw_data_size(gen_kw)") + _has_key = ResPrototype("bool gen_kw_data_has_key(gen_kw, char*)") + _ecl_write = ResPrototype( + "void gen_kw_ecl_write(gen_kw, char* , char* , void*)" + ) + _iget_key = ResPrototype("char* gen_kw_get_name(gen_kw, int)") + + def __init__(self, gen_kw_config): + """ + @type gen_kw_config: GenKwConfig + """ + c_ptr = self._alloc(gen_kw_config) + + if c_ptr: + super(GenKw, self).__init__(c_ptr) + self.__str__ = self.__repr__ + else: + raise ValueError( + "Cannot issue a GenKw from the given keyword config: %s." + % str(gen_kw_config) + ) + + def exportParameters(self, file_name): + """@type: str""" + self._export_parameters(file_name) + + def exportTemplate(self, file_name): + """@type: str""" + self._export_template(file_name) + + def __getitem__(self, key): + """ + @type key: int or str + @rtype: float + """ + do_transform = False + if isinstance(key, str): + if not key in self: + raise KeyError("Key %s does not exist" % (key)) + return self._data_get(key, do_transform) + elif isinstance(key, int): + if not 0 <= key < len(self): + raise IndexError("Index out of range 0 <= %d < %d" % (key, len(self))) + return self._data_iget(key, do_transform) + else: + raise TypeError( + "Illegal type for indexing, must be int or str, got: %s" % (key) + ) + + def __setitem__(self, key, value): + """ + @type key: int or str + @type value: float + """ + if isinstance(key, str): + if not key in self: + raise KeyError("Key %s does not exist" % (key)) + self._data_set(key, value) + elif isinstance(key, int): + if not 0 <= key < len(self): + raise IndexError("Index out of range 0 <= %d < %d" % (key, len(self))) + self._data_iset(key, value) + else: + raise TypeError( + "Illegal type for indexing, must be int or str, got: %s" % (key) + ) + + def items(self): + do_transform = False + v = [] + for index in range(len(self)): + v.append((self._iget_key(index), self._data_iget(index, do_transform))) + return v + + def eclWrite(self, path, filename): + if not path is None: + if not os.path.isdir(path): + raise IOError("The directory:%s does not exist" % path) + + self._ecl_write(path, filename, None) + + def setValues(self, values): + if len(values) == len(self): + if isinstance(values, DoubleVector): + self._set_values(d) + else: + d = DoubleVector() + for (index, v) in enumerate(values): + if isinstance(v, numbers.Number): + d[index] = v + else: + raise TypeError("Values must numeric: %s is invalid" % v) + self._set_values(d) + else: + raise ValueError("Size mismatch between GenKW and values") + + def __len__(self): + """@rtype: int""" + return self._size() + + def __contains__(self, item): + return self._has_key(item) + + def free(self): + self._free() + + def __repr__(self): + return "GenKw(len = %d) at 0x%x" % (len(self), self._address()) diff --git a/res/enkf/data/summary.py b/res/enkf/data/summary.py new file mode 100644 index 00000000000..0c334ce853d --- /dev/null +++ b/res/enkf/data/summary.py @@ -0,0 +1,96 @@ +# Copyright (C) 2018 Equinor ASA, Norway. +# +# The file 'summary.py' 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. +from cwrap import BaseCClass +from res import ResPrototype + + +class Summary(BaseCClass): + TYPE_NAME = "summary" + _alloc = ResPrototype("void* summary_alloc(summary_config)", bind=False) + _free = ResPrototype("void summary_free(summary)") + _iget_value = ResPrototype("double summary_get(summary, int)") + _iset_value = ResPrototype("void summary_set(summary, int, double)") + _length = ResPrototype("int summary_length(summary)") + _get_undef_value = ResPrototype("double summary_undefined_value()", bind=False) + + def __init__(self, config): + c_ptr = self._alloc(config) + self._config = config + super(Summary, self).__init__(c_ptr) + self._undefined_value = self._get_undef_value() + + def __len__(self): + return self._length() + + def __repr__(self): + return "Summary(key=%s, length=%d) %s" % (self.key, len(self), self._ad_str()) + + # The Summary class is intended to contain results loaded from an Eclipse + # formatted summary file, the class has internal functionality for reading + # and interpreting the summary files. In addition it has support for random + # access to the elements with __getitem__() and __setitem__(). For observe + # the following: + # + # 1. The index corresponds to the time axis - i.e. report step in eclipse + # speak. + # + # 2. When using __setitem__() the container will automatically grow to + # the required storage. When growing the underlying storage it will be + # filled with undefined values, and if you later try to access those + # values with __getitem__ you will get a ValueError exception: + # + # summary = Summary( summary_config ) + # summary[10] = 25 + # v = summary[0] -> ValueError("Trying access undefined value") + # + # The access of an undefined value is trapped in Python __getitem__() + # - i.e. it can be bypassed from C. + + def __getitem__(self, index): + if index < 0: + index += len(self) + if index < 0: + raise ValueError("Invalid index") + + if index >= len(self): + raise IndexError( + "Invalid index:%d Valid range: [0,%d>" % (index, len(self)) + ) + + value = self._iget_value(index) + if value == self._undefined_value: + raise ValueError("Trying to access undefined value") + return value + + def __setitem__(self, index, value): + if index < 0: + raise ValueError("Invalid report step") + + self._iset_value(index, value) + + def value(self, report_step): + return self[report_step] + + @property + def config(self): + return self._config + + @property + def key(self): + return self.config.key + + def free(self): + self._free() diff --git a/res/enkf/ecl_config.py b/res/enkf/ecl_config.py new file mode 100644 index 00000000000..f2f9646024b --- /dev/null +++ b/res/enkf/ecl_config.py @@ -0,0 +1,243 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'ecl_config.py' 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. + +from cwrap import BaseCClass +from res import ResPrototype +from ecl.grid import EclGrid +from ecl.summary import EclSum +from ecl.util.util import StringList, CTime +from res.util import UIReturn +from res.enkf import ConfigKeys +import os +from datetime import datetime + + +class EclConfig(BaseCClass): + TYPE_NAME = "ecl_config" + + _alloc = ResPrototype("void* ecl_config_alloc(config_content)", bind=False) + _alloc_full = ResPrototype( + "void* ecl_config_alloc_full( bool, \ + char*, \ + ecl_grid, \ + char*, \ + stringlist, \ + time_t, \ + char*)", + bind=False, + ) + _free = ResPrototype("void ecl_config_free( ecl_config )") + _get_data_file = ResPrototype("char* ecl_config_get_data_file(ecl_config)") + _set_data_file = ResPrototype("void ecl_config_set_data_file(ecl_config , char*)") + _validate_data_file = ResPrototype( + "ui_return_obj ecl_config_validate_data_file(ecl_config , char*)" + ) + _get_gridfile = ResPrototype("char* ecl_config_get_gridfile(ecl_config)") + _set_gridfile = ResPrototype("void ecl_config_set_grid(ecl_config, char*)") + _validate_gridfile = ResPrototype( + "ui_return_obj ecl_config_validate_grid(ecl_config, char*)" + ) + _get_grid = ResPrototype("ecl_grid_ref ecl_config_get_grid(ecl_config)") + _get_refcase_name = ResPrototype("char* ecl_config_get_refcase_name(ecl_config)") + _get_refcase = ResPrototype("ecl_sum_ref ecl_config_get_refcase(ecl_config)") + _load_refcase = ResPrototype("void ecl_config_load_refcase(ecl_config, char*)") + _validate_refcase = ResPrototype( + "ui_return_obj ecl_config_validate_refcase(ecl_config, char*)" + ) + _has_refcase = ResPrototype("bool ecl_config_has_refcase(ecl_config)") + _get_depth_unit = ResPrototype("char* ecl_config_get_depth_unit(ecl_config)") + _get_pressure_unit = ResPrototype("char* ecl_config_get_pressure_unit(ecl_config)") + _get_start_date = ResPrototype("time_t ecl_config_get_start_date(ecl_config)") + _active = ResPrototype("bool ecl_config_active(ecl_config)") + _get_last_history_restart = ResPrototype( + "int ecl_config_get_last_history_restart(ecl_config)" + ) + _get_end_date = ResPrototype("time_t ecl_config_get_end_date(ecl_config)") + _get_num_cpu = ResPrototype("int ecl_config_get_num_cpu(ecl_config)") + + def __init__(self, config_content=None, config_dict=None): + + if config_content is not None and config_dict is not None: + raise ValueError( + "Error: EclConfig can not be instantiated with multiple config objects" + ) + c_ptr = None + if config_dict is None: + c_ptr = self._alloc(config_content) + + if config_dict is not None: + # ECLBASE_KEY + have_eclbase = config_dict.get(ConfigKeys.ECLBASE) is not None + + # DATA_FILE_KEY + data_file = config_dict.get(ConfigKeys.DATA_FILE) + if data_file is not None: + data_file = os.path.realpath(data_file) + if not os.path.isfile(data_file): + raise ValueError("Error: data file is not a file") + + # GRID_KEY + grid = None + grid_file = config_dict.get(ConfigKeys.GRID) + if grid_file is not None: + grid_file = os.path.realpath(grid_file) + if not os.path.isfile(grid_file): + raise ValueError("Error: grid file is not a file") + grid = EclGrid.load_from_file(grid_file) + + # REFCASE_KEY + refcase_default = config_dict.get(ConfigKeys.REFCASE) + if refcase_default is not None: + refcase_default = os.path.realpath(refcase_default) + + # REFCASE_LIST_KEY + refcase_list = StringList() + for refcase in config_dict.get(ConfigKeys.REFCASE_LIST, []): + refcase_list.append(refcase) + + # END_DATE_KEY + end_date = CTime( + datetime.strptime( + config_dict.get(ConfigKeys.END_DATE, "31/12/1969"), "%d/%m/%Y" + ) + ) + + # SCHEDULE_PREDICTION_FILE_KEY + schedule_prediction_file = config_dict.get( + ConfigKeys.SCHEDULE_PREDICTION_FILE + ) + + c_ptr = self._alloc_full( + have_eclbase, + data_file, + grid, + refcase_default, + refcase_list, + end_date, + schedule_prediction_file, + ) + if grid is not None: + grid.convertToCReference(None) + + if c_ptr: + super(EclConfig, self).__init__(c_ptr) + else: + raise RuntimeError("Internal error: Failed constructing EclConfig!") + + def free(self): + self._free() + + def getDataFile(self): + return self._get_data_file() + + def setDataFile(self, datafile): + self._set_data_file(datafile) + + def validateDataFile(self, datafile): + """@rtype: UIReturn""" + return self._validate_data_file(datafile) + + # ----------------------------------------------------------------- + + def get_gridfile(self): + """@rtype: str""" + return self._get_gridfile() + + def set_gridfile(self, gridfile): + self._set_gridfile(gridfile) + + def validateGridFile(self, gridfile): + return self._validate_gridfile(gridfile) + + def getGrid(self): + return self._get_grid() + + def getRefcaseName(self): + return self._get_refcase_name() + + def loadRefcase(self, refcase): + self._load_refcase(refcase) + + def getRefcase(self): + """@rtype: EclSum""" + refcase = self._get_refcase() + if not refcase is None: + refcase.setParent(self) + + return refcase + + def validateRefcase(self, refcase): + return self._validate_refcase(refcase) + + def hasRefcase(self): + """@rtype: bool""" + return self._has_refcase() + + # ----------------------------------------------------------------- + + def getDepthUnit(self): + return self._get_depth_unit() + + def getPressureUnit(self): + return self._get_pressure_unit() + + # ----------------------------------------------------------------- + + def getStartDate(self): + return self._get_start_date() + + def getEndDate(self): + return self._get_end_date() + + def active(self): + """ + Has ECLIPSE been configured?" + """ + return self._active() + + def getLastHistoryRestart(self): + return self._get_last_history_restart() + + @property + def num_cpu(self): + """ + Returns numbers cpu to be used defined in a DATA file + """ + return self._get_num_cpu() + + def __eq__(self, other): + if self.getDataFile() != other.getDataFile(): + return False + + if self.get_gridfile() != other.get_gridfile(): + return False + + if self.getRefcaseName() != other.getRefcaseName(): + return False + + if self.getDepthUnit() != other.getDepthUnit(): + return False + + if self.getPressureUnit() != other.getPressureUnit(): + return False + + if self.getStartDate() != other.getStartDate(): + return False + + if self.getEndDate() != other.getEndDate(): + return False + + return True diff --git a/res/enkf/enkf_defaults.py b/res/enkf/enkf_defaults.py new file mode 100644 index 00000000000..4ca804a304c --- /dev/null +++ b/res/enkf/enkf_defaults.py @@ -0,0 +1,24 @@ +# Copyright (C) 2017 Equinor ASA, Norway. +# +# The file 'enkf_defaults.py' 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. + +from res import ResPrototype + + +class EnkfDefaults: + enkf_defaults_get_default_gen_kw_export_name = ResPrototype( + "char* enkf_defaults_get_default_gen_kw_export_name()", bind=False + ) + DEFAULT_GEN_KW_EXPORT_NAME = enkf_defaults_get_default_gen_kw_export_name() diff --git a/res/enkf/enkf_fs.py b/res/enkf/enkf_fs.py new file mode 100644 index 00000000000..ffc10dfeb52 --- /dev/null +++ b/res/enkf/enkf_fs.py @@ -0,0 +1,158 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'enkf_fs.py' 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. +import sys +from cwrap import BaseCClass +from res import ResPrototype +from res.enkf import TimeMap, StateMap, SummaryKeySet +from res.enkf.enums import EnKFFSType + + +class EnkfFs(BaseCClass): + TYPE_NAME = "enkf_fs" + + _mount = ResPrototype("void* enkf_fs_mount(char* )", bind=False) + _exists = ResPrototype("bool enkf_fs_exists(char*)", bind=False) + _disk_version = ResPrototype("int enkf_fs_disk_version(char*)", bind=False) + _update_disk_version = ResPrototype( + "bool enkf_fs_update_disk_version(char*, int, int)", bind=False + ) + _decref = ResPrototype("int enkf_fs_decref(enkf_fs)") + _incref = ResPrototype("int enkf_fs_incref(enkf_fs)") + _get_refcount = ResPrototype("int enkf_fs_get_refcount(enkf_fs)") + _has_node = ResPrototype( + "bool enkf_fs_has_node(enkf_fs, char*, int, int, int, int)" + ) + _has_vector = ResPrototype( + "bool enkf_fs_has_vector(enkf_fs, char*, int, int, int)" + ) + _get_case_name = ResPrototype("char* enkf_fs_get_case_name(enkf_fs)") + _is_read_only = ResPrototype("bool enkf_fs_is_read_only(enkf_fs)") + _is_running = ResPrototype("bool enkf_fs_is_running(enkf_fs)") + _fsync = ResPrototype("void enkf_fs_fsync(enkf_fs)") + _create = ResPrototype( + "enkf_fs_obj enkf_fs_create_fs(char* , enkf_fs_type_enum , void* , bool)", + bind=False, + ) + _get_time_map = ResPrototype("time_map_ref enkf_fs_get_time_map(enkf_fs)") + _get_state_map = ResPrototype("state_map_ref enkf_fs_get_state_map(enkf_fs)") + _summary_key_set = ResPrototype( + "summary_key_set_ref enkf_fs_get_summary_key_set(enkf_fs)" + ) + + def __init__(self, mount_point): + c_ptr = self._mount(mount_point) + super(EnkfFs, self).__init__(c_ptr) + + def copy(self): + fs = self.createPythonObject(self._address()) + self._incref() + return fs + + # This method will return a new Python object which shares the underlying + # enkf_fs instance as self. The name weakref is used because the Python + # object returned from this method does *not* manipulate the reference + # count of the underlying enkf_fs instance, and specifically it does not + # inhibit destruction of this object. + def weakref(self): + fs = self.createCReference(self._address()) + return fs + + def getTimeMap(self): + """@rtype: TimeMap""" + return self._get_time_map().setParent(self) + + def getStateMap(self): + """@rtype: StateMap""" + return self._get_state_map().setParent(self) + + def getCaseName(self): + """@rtype: str""" + return self._get_case_name() + + def isReadOnly(self): + """@rtype: bool""" + return self._is_read_only() + + def refCount(self): + return self._get_refcount() + + def is_running(self): + return self._is_running() + + @classmethod + def exists(cls, path): + return cls._exists(path) + + @classmethod + def diskVersion(cls, path): + disk_version = cls._disk_version(path) + if disk_version < 0: + raise IOError("No such filesystem: %s" % path) + return disk_version + + @classmethod + def updateVersion(cls, path, src_version, target_version): + return cls._update_disk_version(path, src_version, target_version) + + @classmethod + def createFileSystem(cls, path, mount=False): + assert isinstance(path, str) + fs_type = EnKFFSType.BLOCK_FS_DRIVER_ID + arg = None + fs = cls._create(path, fs_type, arg, mount) + return fs + + # The umount( ) method should not normally be called explicitly by + # downstream code, but in situations where file descriptors is at premium + # it might be beneficial to call it explicitly. In that case it is solely + # the responsability of the calling scope to ensure that it is not called + # repeatedly - that will lead to hard failure! + def umount(self): + if self.isReference(): + raise AssertionError( + "Calling umount() on a reference is an application error" + ) + + if self: + self._decref() + self._invalidateCPointer() + else: + raise AssertionError("Tried to umount for second time - application error") + + def free(self): + if self: + self.umount() + + def __repr__(self): + cn = self.getCaseName() + ad = self._ad_str() + return "EnkfFs(case_name = %s) %s" % (cn, ad) + + def fsync(self): + self._fsync() + + def getSummaryKeySet(self): + """@rtype: SummaryKeySet""" + return self._summary_key_set().setParent(self) + + def realizationList(self, state): + """ + Will return list of realizations with state == the specified state. + @type state: res.enkf.enums.RealizationStateEnum + @rtype: ecl.util.IntVector + """ + state_map = self.getStateMap() + return state_map.realizationList(state) diff --git a/res/enkf/enkf_fs_manager.py b/res/enkf/enkf_fs_manager.py new file mode 100644 index 00000000000..e3d885e6d6e --- /dev/null +++ b/res/enkf/enkf_fs_manager.py @@ -0,0 +1,306 @@ +import os.path +import re + +from cwrap import BaseCClass +from ecl.util.util import StringList, BoolVector +from res import ResPrototype +from res.enkf import ( + EnkfFs, + StateMap, + TimeMap, + RealizationStateEnum, + EnkfInitModeEnum, + EnKFFSType, +) + + +def naturalSortKey(s, _nsre=re.compile("([0-9]+)")): + return [ + int(text) if text.isdigit() else text.lower() for text in re.split(_nsre, s) + ] + + +class FileSystemRotator(object): + def __init__(self, capacity): + super(FileSystemRotator, self).__init__() + self._capacity = capacity + """:type: int""" + self._fs_list = [] + """:type: list of str""" + self._fs_map = {} + """:type: dict[str, EnkfFs]""" + + def __len__(self): + return len(self._fs_list) + + def addFileSystem(self, file_system, full_name): + if self.atCapacity(): + self.dropOldestFileSystem() + + self._fs_list.append(full_name) + self._fs_map[full_name] = file_system + + def dropOldestFileSystem(self): + if len(self._fs_list) > 0: + case_name = self._fs_list[0] + del self._fs_list[0] + del self._fs_map[case_name] + + def atCapacity(self): + return len(self._fs_list) == self._capacity + + def __contains__(self, full_case_name): + return full_case_name in self._fs_list + + def __get_fs(self, name): + fs = self._fs_map[name] + return fs.copy() + + def __getitem__(self, case): + """@rtype: EnkfFs""" + if isinstance(case, str): + return self.__get_fs(case) + elif isinstance(case, int) and 0 <= case < len(self): + case_name = self._fs_list[case] + return self.__get_fs(case_name) + else: + raise IndexError("Value '%s' is not a proper index or case name." % case) + + def umountAll(self): + while len(self._fs_list) > 0: + self.dropOldestFileSystem() + + +# For normal use from ert all filesystems will be located in the same +# folder in the filesystem - corresponding to the ENSPATH setting in +# the config file; in this implementation that setting is stored in +# the @mount_root field. Currently @mount_root is fixed to the value +# returned by EnKFMain.getMountPoint(), but in principle a different +# path could be sent as the the optional second argument to the +# getFS() method. + + +class EnkfFsManager(BaseCClass): + TYPE_NAME = "enkf_fs_manager" + + _get_current_fs = ResPrototype("enkf_fs_obj enkf_main_get_fs_ref(enkf_fs_manager)") + _switch_fs = ResPrototype("void enkf_main_set_fs(enkf_fs_manager, enkf_fs, char*)") + _fs_exists = ResPrototype("bool enkf_main_fs_exists(enkf_fs_manager, char*)") + _alloc_caselist = ResPrototype( + "stringlist_obj enkf_main_alloc_caselist(enkf_fs_manager)" + ) + _ensemble_size = ResPrototype("int enkf_main_get_ensemble_size(enkf_fs_manager)") + + _is_initialized = ResPrototype( + "bool enkf_main_is_initialized(enkf_fs_manager, bool_vector)" + ) + _is_case_initialized = ResPrototype( + "bool enkf_main_case_is_initialized(enkf_fs_manager, char*, bool_vector)" + ) + _initialize_from_scratch = ResPrototype( + "void enkf_main_initialize_from_scratch(enkf_fs_manager, stringlist, ert_run_context)" + ) + _initialize_case_from_existing = ResPrototype( + "void enkf_main_init_case_from_existing(enkf_fs_manager, enkf_fs, int, enkf_fs)" + ) + _custom_initialize_from_existing = ResPrototype( + "void enkf_main_init_current_case_from_existing_custom(enkf_fs_manager, enkf_fs, int, stringlist, bool_vector)" + ) + _initialize_current_case_from_existing = ResPrototype( + "void enkf_main_init_current_case_from_existing(enkf_fs_manager, enkf_fs, int)" + ) + + _alloc_readonly_state_map = ResPrototype( + "state_map_obj enkf_main_alloc_readonly_state_map(enkf_fs_manager, char*)" + ) + _alloc_readonly_time_map = ResPrototype( + "time_map_obj enkf_main_alloc_readonly_time_map(enkf_fs_manager, char*)" + ) + + DEFAULT_CAPACITY = 5 + + def __init__(self, enkf_main, capacity=DEFAULT_CAPACITY): + """ + @type enkf_main: res.enkf.EnKFMain + @type capacity: int + """ + # enkf_main should be an EnKFMain, get the _RealEnKFMain object + real_enkf_main = enkf_main.parent() + + super(EnkfFsManager, self).__init__( + real_enkf_main.from_param(real_enkf_main).value, + parent=real_enkf_main, + is_reference=True, + ) + + self._fs_rotator = FileSystemRotator(capacity) + self._mount_root = real_enkf_main.getMountPoint() + + def __del__(self): + # This object is a reference, so free() won't be called on it + # Any clean-up must be done here + self.umount() + super(EnkfFsManager, self).__del__() + + def _createFullCaseName(self, mount_root, case_name): + return os.path.join(mount_root, case_name) + + # The return value from the getFileSystem will be a weak reference to the + # underlying enkf_fs object. That implies that the fs manager must be in + # scope for the return value to be valid. + def getFileSystem(self, case_name, mount_root=None): + """ + @rtype: EnkfFs + """ + if mount_root is None: + mount_root = self._mount_root + + full_case_name = self._createFullCaseName(mount_root, case_name) + + if not full_case_name in self._fs_rotator: + if not EnkfFs.exists(full_case_name): + if self._fs_rotator.atCapacity(): + self._fs_rotator.dropOldestFileSystem() + + EnkfFs.createFileSystem(full_case_name) + + new_fs = EnkfFs(full_case_name) + self._fs_rotator.addFileSystem(new_fs, full_case_name) + + fs = self._fs_rotator[full_case_name] + + return fs + + def isCaseRunning(self, case_name, mount_root=None): + """Returns true if case is mounted and write_count > 0 + @rtype: bool + """ + if self.isCaseMounted(case_name, mount_root): + case_fs = self.getFileSystem(case_name, mount_root) + return case_fs.is_running() + return False + + def caseExists(self, case_name): + """@rtype: bool""" + return case_name in self.getCaseList() + + def caseHasData(self, case_name): + """@rtype: bool""" + case_has_data = False + state_map = self.getStateMapForCase(case_name) + + for state in state_map: + if state == RealizationStateEnum.STATE_HAS_DATA: + case_has_data = True + + return case_has_data + + def getCurrentFileSystem(self): + """Returns the currently selected file system + @rtype: EnkfFs + """ + current_fs = self._get_current_fs() + case_name = current_fs.getCaseName() + full_name = self._createFullCaseName(self._mount_root, case_name) + + if not full_name in self._fs_rotator: + self._fs_rotator.addFileSystem(current_fs, full_name) + + return self.getFileSystem(case_name, self._mount_root) + + def umount(self): + self._fs_rotator.umountAll() + + def getFileSystemCount(self): + return len(self._fs_rotator) + + def getEnsembleSize(self): + """@rtype: int""" + return self._ensemble_size() + + def switchFileSystem(self, file_system): + """ + @type file_system: EnkfFs + """ + self._switch_fs(file_system, None) + + def isCaseInitialized(self, case): + return self._is_case_initialized(case, None) + + def isInitialized(self): + """@rtype: bool""" + return self._is_initialized(None) # what is the bool_vector mask??? + + def getCaseList(self): + """@rtype: list[str]""" + caselist = [case for case in self._alloc_caselist()] + return sorted(caselist, key=naturalSortKey) + + def customInitializeCurrentFromExistingCase( + self, source_case, source_report_step, member_mask, node_list + ): + """ + @type source_case: str + @type source_report_step: int + @type member_mask: ecl.util.BoolVector + @type node_list: ecl.util.StringList + """ + source_case_fs = self.getFileSystem(source_case) + self._custom_initialize_from_existing( + source_case_fs, source_report_step, node_list, member_mask + ) + + def initializeCurrentCaseFromExisting(self, source_fs, source_report_step): + """ + @type source_fs: EnkfFs + @type source_report_step: int + """ + self._initialize_current_case_from_existing(source_fs, source_report_step) + + def initializeCaseFromExisting(self, source_fs, source_report_step, target_fs): + """ + @type source_fs: EnkfFs + @type source_report_step: int + @type target_fs: EnkfFs + """ + self._initialize_case_from_existing(source_fs, source_report_step, target_fs) + + def initializeFromScratch(self, parameter_list, run_context): + self._initialize_from_scratch(parameter_list, run_context) + + def isCaseMounted(self, case_name, mount_root=None): + """ + @type case_name: str + @type mount_root: str + @rtype: bool + """ + if mount_root is None: + mount_root = self._mount_root + + full_case_name = self._createFullCaseName(mount_root, case_name) + + return full_case_name in self._fs_rotator + + def getStateMapForCase(self, case): + """ + @type case: str + @rtype: StateMap + """ + if self.isCaseMounted(case): + fs = self.getFileSystem(case) + return fs.getStateMap() + else: + return self._alloc_readonly_state_map(case) + + def getTimeMapForCase(self, case): + """ + @type case: str + @rtype: TimeMap + """ + return self._alloc_readonly_time_map(case) + + def isCaseHidden(self, case_name): + """ + @rtype: bool + """ + return case_name.startswith(".") diff --git a/res/enkf/enkf_linalg.py b/res/enkf/enkf_linalg.py new file mode 100644 index 00000000000..9411e594cf7 --- /dev/null +++ b/res/enkf/enkf_linalg.py @@ -0,0 +1,27 @@ +from cwrap import BaseCClass +from res import ResPrototype +from ecl.util.util import DoubleVector +from res.util import Matrix + + +class EnkfLinalg(BaseCClass): + TYPE_NAME = "EnkfLinalg" + + _get_PC = ResPrototype( + "void enkf_linalg_get_PC(matrix, matrix, double, int, matrix, matrix, double_vector)", + bind=False, + ) + + @classmethod + def calculatePrincipalComponents( + cls, S0, D_obs, truncation, ncomp, PC, PC_obs, singular_values + ): + assert isinstance(S0, Matrix) + assert isinstance(D_obs, Matrix) + assert isinstance(truncation, (float, int)) + assert isinstance(ncomp, int) + assert isinstance(PC, Matrix) + assert isinstance(PC_obs, Matrix) + assert isinstance(singular_values, DoubleVector) + + cls._get_PC(S0, D_obs, truncation, ncomp, PC, PC_obs, singular_values) diff --git a/res/enkf/enkf_main.py b/res/enkf/enkf_main.py new file mode 100644 index 00000000000..1b99708cc28 --- /dev/null +++ b/res/enkf/enkf_main.py @@ -0,0 +1,462 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'ecl_kw.py' 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. +import sys +import ctypes, warnings +from os.path import isfile + +from cwrap import BaseCClass +from res import ResPrototype +from res.enkf import ( + AnalysisConfig, + EclConfig, + LocalConfig, + ModelConfig, + EnsembleConfig, + SiteConfig, + ResConfig, + QueueConfig, +) +from res.enkf import EnkfObs, EnKFState, EnkfSimulationRunner, EnkfFsManager +from res.enkf import ErtWorkflowList, HookManager, HookWorkflow, ESUpdate +from res.enkf.enums import EnkfInitModeEnum +from res.enkf.key_manager import KeyManager +from res.util import Log +from res.util.substitution_list import SubstitutionList +from ecl.util.util import rng + + +class EnKFMain(BaseCClass): + + TYPE_NAME = "enkf_main" + + @classmethod + def createPythonObject(cls, c_pointer): + if c_pointer is not None: + real_enkf_main = _RealEnKFMain.createPythonObject(c_pointer) + new_obj = cls.__new__(cls) + EnKFMain._init_from_real_enkf_main(new_obj, real_enkf_main) + EnKFMain._monkey_patch_methods(new_obj, real_enkf_main) + return new_obj + else: + return None + + @classmethod + def createCReference(cls, c_pointer, parent=None): + if c_pointer is not None: + real_enkf_main = _RealEnKFMain.createCReference(c_pointer, parent) + new_obj = cls.__new__(cls) + EnKFMain._init_from_real_enkf_main(new_obj, real_enkf_main) + EnKFMain._monkey_patch_methods(new_obj, real_enkf_main) + return new_obj + else: + return None + + def __init__(self, config, strict=True, verbose=False): + """Initializes an instance of EnkfMain. + + Note: @config is a ResConfig instance holding the configuration. + """ + + real_enkf_main = _RealEnKFMain(config, strict, verbose) + assert isinstance(real_enkf_main, BaseCClass) + self._init_from_real_enkf_main(real_enkf_main) + self._monkey_patch_methods(real_enkf_main) + + def _init_from_real_enkf_main(self, real_enkf_main): + super(EnKFMain, self).__init__( + real_enkf_main.from_param(real_enkf_main).value, + parent=real_enkf_main, + is_reference=True, + ) + + self.__simulation_runner = EnkfSimulationRunner(self) + self.__fs_manager = EnkfFsManager(self) + self.__es_update = ESUpdate(self) + + def _real_enkf_main(self): + return self.parent() + + def getESUpdate(self): + """@rtype: ESUpdate""" + return self.__es_update + + def getEnkfSimulationRunner(self): + """@rtype: EnkfSimulationRunner""" + return self.__simulation_runner + + def getEnkfFsManager(self): + """@rtype: EnkfFsManager""" + return self.__fs_manager + + def umount(self): + if self.__fs_manager is not None: + self.__fs_manager.umount() + + # --- Overridden methods -------------------- + + def _monkey_patch_methods(self, real_enkf_main): + # As a general rule, EnKFMain methods should be implemented on + # _RealEnKFMain because the other references (such as __es_update) + # may need to use them. + # The public methods should be also exposed in this class, forwarding + # the call to the real method on the real_enkf_main object. That's done + # via monkey patching, so we don't need to manually keep the classes + # synchronized + from inspect import getmembers, ismethod + from functools import partial + + methods = getmembers(self._real_enkf_main(), predicate=ismethod) + dont_patch = [name for name, _ in getmembers(BaseCClass)] + for name, method in methods: + if name.startswith("_") or name in dont_patch: + continue # skip private methods + setattr(self, name, method) + + def __repr__(self): + repr = self._real_enkf_main().__repr__() + assert repr.startswith("_RealEnKFMain") + return repr[5:] + + +class _RealEnKFMain(BaseCClass): + """Access to the C EnKFMain interface. + + The python interface of EnKFMain is split between 4 classes, ie + - EnKFMain: main entry point, defined further down + - EnkfSimulationRunner, EnkfFsManager and ESUpdate: access specific + functionalities + EnKFMain owns an instance of each of the last 3 classes. Also, all + of these classes need to access the same underlying C object. + So, in order to avoid circular dependencies, we make _RealEnKF main + the only "owner" of the C object, and all the classes that need to + access it set _RealEnKFMain as parent. + + The situation can be summarized as follows (show only EnkfFSManager, + classes EnkfSimulationRunner and ESUpdate are treated analogously) + ------------------------------------ + | real EnKFMain object in memory | + ------------------------------------ + ^ ^ ^ + | | | + (c_ptr) (c_ptr) | + | | | + _RealEnKFMain | | + ^ ^ | | + | ^--(parent)-- EnKFMain | + | | | + | (owns) | + (parent) | (c_ptr) + | v | + ------------ EnkfFSManager ---- + + """ + + _alloc = ResPrototype("void* enkf_main_alloc(res_config, bool, bool)", bind=False) + + _free = ResPrototype("void enkf_main_free(enkf_main)") + _get_queue_config = ResPrototype( + "queue_config_ref enkf_main_get_queue_config(enkf_main)" + ) + _get_ensemble_size = ResPrototype("int enkf_main_get_ensemble_size( enkf_main )") + _get_ens_config = ResPrototype( + "ens_config_ref enkf_main_get_ensemble_config( enkf_main )" + ) + _get_model_config = ResPrototype( + "model_config_ref enkf_main_get_model_config( enkf_main )" + ) + _get_local_config = ResPrototype( + "local_config_ref enkf_main_get_local_config( enkf_main )" + ) + _get_analysis_config = ResPrototype( + "analysis_config_ref enkf_main_get_analysis_config( enkf_main)" + ) + _get_site_config = ResPrototype( + "site_config_ref enkf_main_get_site_config( enkf_main)" + ) + _get_ecl_config = ResPrototype( + "ecl_config_ref enkf_main_get_ecl_config( enkf_main)" + ) + _get_schedule_prediction_file = ResPrototype( + "char* enkf_main_get_schedule_prediction_file( enkf_main )" + ) + _get_data_kw = ResPrototype("subst_list_ref enkf_main_get_data_kw(enkf_main)") + _clear_data_kw = ResPrototype("void enkf_main_clear_data_kw(enkf_main)") + _add_data_kw = ResPrototype("void enkf_main_add_data_kw(enkf_main, char*, char*)") + _resize_ensemble = ResPrototype("void enkf_main_resize_ensemble(enkf_main, int)") + _get_obs = ResPrototype("enkf_obs_ref enkf_main_get_obs(enkf_main)") + _load_obs = ResPrototype("bool enkf_main_load_obs(enkf_main, char* , bool)") + _get_templates = ResPrototype( + "ert_templates_ref enkf_main_get_templates(enkf_main)" + ) + _get_site_config_file = ResPrototype( + "char* enkf_main_get_site_config_file(enkf_main)" + ) + _get_history_length = ResPrototype("int enkf_main_get_history_length(enkf_main)") + _get_observations = ResPrototype( + "void enkf_main_get_observations(enkf_main, char*, int, long*, double*, double*)" + ) + _get_observation_count = ResPrototype( + "int enkf_main_get_observation_count(enkf_main, char*)" + ) + _have_observations = ResPrototype("bool enkf_main_have_obs(enkf_main)") + _iget_state = ResPrototype("enkf_state_ref enkf_main_iget_state(enkf_main, int)") + _get_workflow_list = ResPrototype( + "ert_workflow_list_ref enkf_main_get_workflow_list(enkf_main)" + ) + _get_hook_manager = ResPrototype( + "hook_manager_ref enkf_main_get_hook_manager(enkf_main)" + ) + _get_user_config_file = ResPrototype( + "char* enkf_main_get_user_config_file(enkf_main)" + ) + _get_mount_point = ResPrototype("char* enkf_main_get_mount_root( enkf_main )") + _export_field = ResPrototype( + "bool enkf_main_export_field(enkf_main, char*, char*, bool_vector, enkf_field_file_format_enum, int)" + ) + _export_field_with_fs = ResPrototype( + "bool enkf_main_export_field_with_fs(enkf_main, char*, char*, bool_vector, enkf_field_file_format_enum, int, enkf_fs_manager)" + ) + _load_from_forward_model = ResPrototype( + "int enkf_main_load_from_forward_model_from_gui(enkf_main, int, bool_vector, enkf_fs)" + ) + _load_from_run_context = ResPrototype( + "int enkf_main_load_from_run_context_from_gui(enkf_main, ert_run_context, enkf_fs)" + ) + _create_run_path = ResPrototype( + "void enkf_main_create_run_path(enkf_main , ert_run_context)" + ) + _submit_simulation = ResPrototype( + "void enkf_main_isubmit_job(enkf_main , run_arg, job_queue)" + ) + _alloc_run_context_ENSEMBLE_EXPERIMENT = ResPrototype( + "ert_run_context_obj enkf_main_alloc_ert_run_context_ENSEMBLE_EXPERIMENT( enkf_main , enkf_fs , bool_vector , int)" + ) + _get_runpath_list = ResPrototype( + "runpath_list_ref enkf_main_get_runpath_list(enkf_main)" + ) + _create_runpath_list = ResPrototype( + "runpath_list_obj enkf_main_alloc_runpath_list(enkf_main)" + ) + _add_node = ResPrototype("void enkf_main_add_node(enkf_main, enkf_config_node)") + _get_res_config = ResPrototype("res_config_ref enkf_main_get_res_config(enkf_main)") + _init_run = ResPrototype("void enkf_main_init_run(enkf_main, ert_run_context)") + _get_shared_rng = ResPrototype("rng_ref enkf_main_get_shared_rng(enkf_main)") + + def __init__(self, config, strict=True, verbose=False): + """Please don't use this class directly. See EnKFMain instead""" + + res_config = self._init_res_config(config) + if res_config is None: + raise TypeError( + "Failed to construct EnKFMain instance due to invalid res_config." + ) + + c_ptr = self._alloc(res_config, strict, verbose) + if c_ptr: + super(_RealEnKFMain, self).__init__(c_ptr) + else: + raise ValueError( + "Failed to construct EnKFMain instance from config %s." % res_config + ) + + self.__key_manager = KeyManager(self) + + def _init_res_config(self, config): + if isinstance(config, ResConfig): + return config + + # The res_config argument can be None; the only reason to + # allow that possibility is to be able to test that the + # site-config loads correctly. + if config is None: + config = ResConfig(None) + config.convertToCReference(self) + return config + + raise TypeError("Expected ResConfig, received: %r" % config) + + def get_queue_config(self): + return self._get_queue_config() + + def getRealisation(self, iens): + """@rtype: EnKFState""" + if 0 <= iens < self.getEnsembleSize(): + return self._iget_state(iens).setParent(self) + else: + raise IndexError( + "iens value:%d invalid Valid range: [0,%d)" + % (iens, self.getEnsembleSize()) + ) + + def free(self): + self._free() + + def __repr__(self): + ens = self.getEnsembleSize() + cfg = self.getUserConfigFile() + cnt = "ensemble_size = %d, config_file = %s" % (ens, cfg) + return self._create_repr(cnt) + + def getEnsembleSize(self): + """@rtype: int""" + return self._get_ensemble_size() + + def resizeEnsemble(self, value): + self._resize_ensemble(value) + + def ensembleConfig(self): + """@rtype: EnsembleConfig""" + return self._get_ens_config().setParent(self) + + def analysisConfig(self): + """@rtype: AnalysisConfig""" + return self._get_analysis_config().setParent(self) + + def getModelConfig(self): + """@rtype: ModelConfig""" + return self._get_model_config().setParent(self) + + def getLocalConfig(self): + """@rtype: LocalConfig""" + config = self._get_local_config().setParent(self) + config.initAttributes( + self.ensembleConfig(), self.getObservations(), self.eclConfig().getGrid() + ) + return config + + def siteConfig(self): + """@rtype: SiteConfig""" + return self._get_site_config().setParent(self) + + def resConfig(self): + return self._get_res_config().setParent(self) + + def eclConfig(self): + """@rtype: EclConfig""" + return self._get_ecl_config().setParent(self) + + def get_schedule_prediction_file(self): + schedule_prediction_file = self._get_schedule_prediction_file() + return schedule_prediction_file + + def getDataKW(self): + """@rtype: SubstitutionList""" + return self._get_data_kw() + + def clearDataKW(self): + self._clear_data_kw() + + def addDataKW(self, key, value): + self._add_data_kw(key, value) + + def getMountPoint(self): + return self._get_mount_point() + + def getObservations(self): + """@rtype: EnkfObs""" + return self._get_obs().setParent(self) + + def have_observations(self): + return self._have_observations() + + def loadObservations(self, obs_config_file, clear=True): + return self._load_obs(obs_config_file, clear) + + def get_templates(self): + return self._get_templates().setParent(self) + + def get_site_config_file(self): + return self._get_site_config_file() + + def getUserConfigFile(self): + """@rtype: str""" + config_file = self._get_user_config_file() + return config_file + + def getHistoryLength(self): + return self._get_history_length() + + def getMemberRunningState(self, ensemble_member): + """@rtype: EnKFState""" + return self._iget_state(ensemble_member).setParent(self) + + def get_observations(self, user_key, obs_count, obs_x, obs_y, obs_std): + return self._get_observations(user_key, obs_count, obs_x, obs_y, obs_std) + + def get_observation_count(self, user_key): + return self._get_observation_count(user_key) + + def getKeyManager(self): + """:rtype: KeyManager""" + return self.__key_manager + + def getWorkflowList(self): + """@rtype: ErtWorkflowList""" + return self._get_workflow_list().setParent(self) + + def getHookManager(self): + """@rtype: HookManager""" + return self._get_hook_manager() + + def exportField( + self, keyword, path, iactive, file_type, report_step, state, enkfFs + ): + """ + @type keyword: str + @type path: str + @type iactive: BoolVector + @type file_type: EnkfFieldFileFormatEnum + @type report_step: int + @type enkfFs: EnkfFs + + """ + assert isinstance(keyword, str) + return self._export_field_with_fs( + keyword, path, iactive, file_type, report_step, state, enkfFs + ) + + def loadFromForwardModel(self, realization, iteration, fs): + """Returns the number of loaded realizations""" + return self._load_from_forward_model(iteration, realization, fs) + + def loadFromRunContext(self, run_context, fs): + """Returns the number of loaded realizations""" + return self._load_from_run_context(run_context, fs) + + def initRun(self, run_context): + self._init_run(run_context) + + def createRunpath(self, run_context): + self._create_run_path(run_context) + + def submitSimulation(self, run_arg, queue): + self._submit_simulation(run_arg, queue) + + def getRunContextENSEMPLE_EXPERIMENT(self, fs, iactive, iteration=0): + return self._alloc_run_context_ENSEMBLE_EXPERIMENT(fs, iactive, iteration) + + def create_runpath_list(self): + return self._create_runpath_list() + + def getRunpathList(self): + return self._get_runpath_list() + + def addNode(self, enkf_config_node): + self._add_node(enkf_config_node) + + def rng(self): + "Will return the random number generator used for updates." + return self._get_shared_rng() diff --git a/res/enkf/enkf_obs.py b/res/enkf/enkf_obs.py new file mode 100644 index 00000000000..3bdd3d5858c --- /dev/null +++ b/res/enkf/enkf_obs.py @@ -0,0 +1,209 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'enkf_obs.py' 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. +import os.path + +from cwrap import BaseCClass +from ecl.util.util import StringList, IntVector +from res.sched import History +from ecl.grid import EclGrid +from ecl.summary import EclSum +from res import ResPrototype +from res.enkf import EnkfFs, LocalObsdataNode, LocalObsdata, MeasData, ObsData +from res.enkf.enums import EnkfObservationImplementationType + +from res.enkf.observations import ObsVector + + +class EnkfObs(BaseCClass): + TYPE_NAME = "enkf_obs" + + _alloc = ResPrototype( + "void* enkf_obs_alloc( history , time_map , ecl_grid , ecl_sum , ens_config )", + bind=False, + ) + _free = ResPrototype("void enkf_obs_free( enkf_obs )") + _get_size = ResPrototype("int enkf_obs_get_size( enkf_obs )") + _valid = ResPrototype("bool enkf_obs_is_valid(enkf_obs)") + _load = ResPrototype("void enkf_obs_load(enkf_obs, char*, double)") + + _clear = ResPrototype("void enkf_obs_clear( enkf_obs )") + _alloc_typed_keylist = ResPrototype( + "stringlist_obj enkf_obs_alloc_typed_keylist(enkf_obs, enkf_obs_impl_type)" + ) + _alloc_matching_keylist = ResPrototype( + "stringlist_obj enkf_obs_alloc_matching_keylist(enkf_obs, char*)" + ) + _has_key = ResPrototype("bool enkf_obs_has_key(enkf_obs, char*)") + _obs_type = ResPrototype("enkf_obs_impl_type enkf_obs_get_type(enkf_obs, char*)") + _get_vector = ResPrototype("obs_vector_ref enkf_obs_get_vector(enkf_obs, char*)") + _iget_vector = ResPrototype("obs_vector_ref enkf_obs_iget_vector(enkf_obs, int)") + _iget_obs_time = ResPrototype("time_t enkf_obs_iget_obs_time(enkf_obs, int)") + _add_obs_vector = ResPrototype("void enkf_obs_add_obs_vector(enkf_obs, obs_vector)") + _get_obs_and_measure_data = ResPrototype( + "void enkf_obs_get_obs_and_measure_data(enkf_obs, enkf_fs, local_obsdata, int_vector, meas_data, obs_data)" + ) + _create_all_active_obs = ResPrototype( + "local_obsdata_obj enkf_obs_alloc_all_active_local_obs( enkf_obs , char*)" + ) + _scale_correlated_std = ResPrototype( + "double enkf_obs_scale_correlated_std( enkf_obs , enkf_fs , int_vector , local_obsdata)" + ) + _local_scale_std = ResPrototype( + "void enkf_obs_local_scale_std( enkf_obs , local_obsdata , double)" + ) + + def __init__( + self, + ensemble_config, + history=None, + external_time_map=None, + grid=None, + refcase=None, + ): + c_ptr = self._alloc(history, external_time_map, grid, refcase, ensemble_config) + super(EnkfObs, self).__init__(c_ptr) + + def __len__(self): + return self._get_size() + + def __contains__(self, key): + return self._has_key(key) + + def __iter__(self): + """@rtype: ObsVector""" + iobs = 0 + while iobs < len(self): + vector = self[iobs] + yield vector + iobs += 1 + + def __getitem__(self, key_or_index): + """@rtype: ObsVector""" + if isinstance(key_or_index, str): + if self.hasKey(key_or_index): + return self._get_vector(key_or_index).setParent(self) + else: + raise KeyError("Unknown key: %s" % key_or_index) + elif isinstance(key_or_index, int): + idx = key_or_index + if idx < 0: + idx += len(self) + if 0 <= idx < len(self): + return self._iget_vector(idx).setParent(self) + else: + raise IndexError( + "Invalid index: %d. Valid range is [0, %d)." + % (key_or_index, len(self)) + ) + else: + raise TypeError( + "Key or index must be of type str or int, not %s." + % str(type(key_or_index)) + ) + + def createLocalObsdata(self, key, add_active_steps=True): + # Use getAllActiveLocalObsdata() + raise NotImplementedError( + "Hmmm C function: enkf_obs_alloc_all_active_local_obs() removed" + ) + + def getAllActiveLocalObsdata(self, key="ALL-OBS"): + return self._create_all_active_obs(key) + + def getTypedKeylist(self, observation_implementation_type): + """ + @type observation_implementation_type: EnkfObservationImplementationType + @rtype: StringList + """ + return self._alloc_typed_keylist(observation_implementation_type) + + def obsType(self, key): + if key in self: + return self._obs_type(key) + else: + raise KeyError("Unknown observation key:%s" % key) + + def getMatchingKeys(self, pattern, obs_type=None): + """ + Will return a list of all the observation keys matching the input + pattern. The matching is based on fnmatch(). + """ + key_list = self._alloc_matching_keylist(pattern) + if obs_type: + new_key_list = [] + for key in key_list: + if self.obsType(key) == obs_type: + new_key_list.append(key) + return new_key_list + else: + return key_list + + def hasKey(self, key): + """@rtype: bool""" + return key in self + + def getObservationTime(self, index): + """@rtype: CTime""" + return self._iget_obs_time(index) + + def addObservationVector(self, observation_vector): + assert isinstance(observation_vector, ObsVector) + + observation_vector.convertToCReference(self) + + self._add_obs_vector(observation_vector) + + def getObservationAndMeasureData( + self, fs, local_obsdata, active_list, meas_data, obs_data + ): + assert isinstance(fs, EnkfFs) + assert isinstance(local_obsdata, LocalObsdata) + assert isinstance(active_list, IntVector) + assert isinstance(meas_data, MeasData) + assert isinstance(obs_data, ObsData) + + self._get_obs_and_measure_data( + fs, local_obsdata, active_list, meas_data, obs_data + ) + + def scaleCorrelatedStd(self, fs, local_obsdata, active_list): + return self._scale_correlated_std(fs, active_list, local_obsdata) + + def localScaleStd(self, local_obsdata, scale_factor): + return self._local_scale_std(local_obsdata, scale_factor) + + @property + def valid(self): + return self._valid() + + def load(self, config_file, std_cutoff=0.0): + if not os.path.isfile(config_file): + raise IOError( + 'The observation config file "%s" does not exist.' % config_file + ) + if not self.valid: + raise ValueError("Invalid enkf_obs instance. Cannot load config file.") + self._load(config_file, std_cutoff) + + def clear(self): + self._clear() + + def free(self): + self._free() + + def __repr__(self): + validity = "valid" if self.valid else "invalid" + return self._create_repr("%s, len=%d" % (validity, len(self))) diff --git a/res/enkf/enkf_simulation_runner.py b/res/enkf/enkf_simulation_runner.py new file mode 100644 index 00000000000..c3e946daf17 --- /dev/null +++ b/res/enkf/enkf_simulation_runner.py @@ -0,0 +1,130 @@ +from cwrap import BaseCClass +from ecl.util.util import BoolVector +from res import ResPrototype +from res.enkf import EnkfFs +from res.enkf import ErtRunContext +from res.enkf import EnKFState +from res.enkf.enums import EnkfInitModeEnum +from res.enkf.enums.realization_state_enum import RealizationStateEnum +from res.job_queue import RunStatusType, JobQueueManager, JobQueueNode, JobStatusType + +from functools import partial +import time +import threading + + +class EnkfSimulationRunner(BaseCClass): + TYPE_NAME = "enkf_simulation_runner" + + _create_run_path = ResPrototype( + "bool enkf_main_create_run_path(enkf_simulation_runner, ert_run_context)" + ) + + def __init__(self, enkf_main): + assert isinstance(enkf_main, BaseCClass) + # enkf_main should be an EnKFMain, get the _RealEnKFMain object + real_enkf_main = enkf_main.parent() + super(EnkfSimulationRunner, self).__init__( + real_enkf_main.from_param(real_enkf_main).value, + parent=real_enkf_main, + is_reference=True, + ) + + def _enkf_main(self): + return self.parent() + + def runSimpleStep(self, job_queue, run_context): + """@rtype: int""" + #### run simplestep #### + self._enkf_main().initRun(run_context) + + if run_context.get_step(): + ecl_config = self._enkf_main().ecl_config.assert_restart() + + #### deselect load and parent failure ##### + iactive = run_context.get_mask() + + run_context.get_sim_fs().getStateMap().deselectMatching( + iactive, + RealizationStateEnum.STATE_LOAD_FAILURE + | RealizationStateEnum.STATE_PARENT_FAILURE, + ) + + #### start queue #### + self.start_queue(run_context, job_queue) + + #### deactivate failed realizations #### + totalOk = 0 + totalFailed = 0 + for i in range(len(run_context)): + if run_context.is_active(i): + run_arg = run_context[i] + if ( + run_arg.run_status == RunStatusType.JOB_LOAD_FAILURE + or run_arg.run_status == RunStatusType.JOB_RUN_FAILURE + ): + run_context.deactivate_realization(i) + totalFailed += 1 + else: + totalOk += 1 + + run_context.get_sim_fs().fsync() + + ## Should be converted tp a looger + if totalFailed == 0: + print("All {} active jobs complete and data loaded.".format(totalOk)) + else: + print("{} active job(s) failed.".format(totalFailed)) + + return totalOk + + def createRunPath(self, run_context): + """@rtype: bool""" + return self._create_run_path(run_context) + + def runEnsembleExperiment(self, job_queue, run_context): + """@rtype: int""" + return self.runSimpleStep(job_queue, run_context) + + @staticmethod + def runWorkflows(runtime, ert): + """:type res.enkf.enum.HookRuntimeEnum""" + hook_manager = ert.getHookManager() + hook_manager.runWorkflows(runtime, ert) + + def start_queue(self, run_context, job_queue): + max_runtime = self._enkf_main().analysisConfig().get_max_runtime() + if max_runtime == 0: + max_runtime = None + + done_callback_function = EnKFState.forward_model_ok_callback + exit_callback_function = EnKFState.forward_model_exit_callback + + # submit jobs + for i in range(len(run_context)): + if not run_context.is_active(i): + continue + run_arg = run_context[i] + job_queue.add_job_from_run_arg( + run_arg, + self._enkf_main().resConfig(), + max_runtime, + done_callback_function, + exit_callback_function, + ) + + job_queue.submit_complete() + queue_evaluators = None + if ( + self._enkf_main().analysisConfig().get_stop_long_running() + and self._enkf_main().analysisConfig().minimum_required_realizations > 0 + ): + queue_evaluators = [ + partial( + job_queue.stop_long_running_jobs, + self._enkf_main().analysisConfig().minimum_required_realizations, + ) + ] + + jqm = JobQueueManager(job_queue, queue_evaluators) + jqm.execute_queue() diff --git a/res/enkf/enkf_state.py b/res/enkf/enkf_state.py new file mode 100644 index 00000000000..7fb2b03cc80 --- /dev/null +++ b/res/enkf/enkf_state.py @@ -0,0 +1,61 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'enkf_state.py' 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. +from cwrap import BaseCClass +from res import ResPrototype +from res.enkf.enums import EnkfInitModeEnum, EnkfVarType + + +class EnKFState(BaseCClass): + TYPE_NAME = "enkf_state" + _free = ResPrototype("void* enkf_state_free( enkf_state )") + _get_ens_config = ResPrototype( + "ens_config_ref enkf_state_get_ensemble_config( enkf_state )" + ) + _initialize = ResPrototype( + "void enkf_state_initialize( enkf_state , enkf_fs , stringlist , enkf_init_mode_enum)" + ) + _forward_model_OK = ResPrototype( + "bool enkf_state_complete_forward_modelOK(res_config, run_arg)", bind=False + ) + _forward_model_EXIT = ResPrototype( + "bool enkf_state_complete_forward_model_EXIT_handler__(run_arg)", bind=False + ) + + def __init__(self): + raise NotImplementedError("Class can not be instantiated directly!") + + def free(self): + self._free() + + def ensembleConfig(self): + """@rtype: EnsembleConfig""" + return self._get_ens_config() + + def initialize( + self, fs, param_list=None, init_mode=EnkfInitModeEnum.INIT_CONDITIONAL + ): + if param_list is None: + ens_config = self.ensembleConfig() + param_list = ens_config.getKeylistFromVarType(EnkfVarType.PARAMETER) + self._initialize(fs, param_list, init_mode) + + @classmethod + def forward_model_exit_callback(cls, args): + return cls._forward_model_EXIT(args[0]) + + @classmethod + def forward_model_ok_callback(cls, args): + return cls._forward_model_OK(args[1], args[0]) diff --git a/res/enkf/ensemble_config.py b/res/enkf/ensemble_config.py new file mode 100644 index 00000000000..c8696093bdf --- /dev/null +++ b/res/enkf/ensemble_config.py @@ -0,0 +1,285 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'ensemble_config.py' 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. +from cwrap import BaseCClass +from ecl.util.util import StringList +from ecl.grid import EclGrid +from ecl.summary import EclSum +from res import ResPrototype +from res.enkf import SummaryKeyMatcher, ConfigKeys +from res.config import ConfigContent +from res.enkf.config import EnkfConfigNode +from res.enkf.enums import EnkfVarType, ErtImplType, LoadFailTypeEnum +import os + + +def _get_abs_path(file): + if file is not None: + file = os.path.realpath(file) + return file + + +def _get_filename(file): + if file is not None: + file = os.path.basename(file) + return file + + +class EnsembleConfig(BaseCClass): + TYPE_NAME = "ens_config" + _alloc = ResPrototype( + "void* ensemble_config_alloc(config_content, ecl_grid, ecl_sum)", bind=False + ) + _alloc_load = ResPrototype( + "void* ensemble_config_alloc_load(char* , ecl_grid, ecl_sum)", bind=False + ) + _alloc_full = ResPrototype("void* ensemble_config_alloc_full(char*)", bind=False) + _free = ResPrototype("void ensemble_config_free( ens_config )") + _has_key = ResPrototype("bool ensemble_config_has_key( ens_config , char* )") + _size = ResPrototype("int ensemble_config_get_size( ens_config)") + _get_node = ResPrototype( + "enkf_config_node_ref ensemble_config_get_node( ens_config , char*)" + ) + _alloc_keylist = ResPrototype( + "stringlist_obj ensemble_config_alloc_keylist( ens_config )" + ) + _add_summary = ResPrototype( + "enkf_config_node_ref ensemble_config_add_summary( ens_config, char*, int)" + ) + _add_gen_kw = ResPrototype( + "enkf_config_node_ref ensemble_config_add_gen_kw( ens_config, char*)" + ) + _add_field = ResPrototype( + "enkf_config_node_ref ensemble_config_add_field( ens_config, char*, ecl_grid)" + ) + _alloc_keylist_from_var_type = ResPrototype( + "stringlist_obj ensemble_config_alloc_keylist_from_var_type(ens_config, enkf_var_type_enum)" + ) + _alloc_keylist_from_impl_type = ResPrototype( + "stringlist_obj ensemble_config_alloc_keylist_from_impl_type(ens_config, ert_impl_type_enum)" + ) + _add_node = ResPrototype( + "void ensemble_config_add_node( ens_config , enkf_config_node )" + ) + _summary_key_matcher = ResPrototype( + "summary_key_matcher_ref ensemble_config_get_summary_key_matcher(ens_config)" + ) + _get_trans_table = ResPrototype("void* ensemble_config_get_trans_table(ens_config)") + _add_summary_full = ResPrototype( + "void ensemble_config_init_SUMMARY_full(ens_config, char*, ecl_sum)" + ) + + def __init__(self, config_content=None, grid=None, refcase=None, config_dict=None): + if config_content is not None and config_dict is not None: + raise ValueError( + "Attempting to create EnsembleConfig object with multiple config objects" + ) + + c_ptr = None + if config_dict is not None: + c_ptr = self._alloc_full(config_dict.get(ConfigKeys.GEN_KW_TAG_FORMAT)) + if c_ptr is None: + raise ValueError( + "Failed to construct EnsembleConfig instance from dict" + ) + + super(EnsembleConfig, self).__init__(c_ptr) + + gen_param_list = config_dict.get(ConfigKeys.GEN_PARAM, []) + for gene_param in gen_param_list: + gen_param_node = EnkfConfigNode.create_gen_param( + gene_param.get(ConfigKeys.NAME), + gene_param.get(ConfigKeys.FORWARD_INIT), + gene_param.get(ConfigKeys.INPUT_FORMAT), + gene_param.get(ConfigKeys.OUTPUT_FORMAT), + gene_param.get(ConfigKeys.INIT_FILES), + gene_param.get(ConfigKeys.ECL_FILE), + gene_param.get(ConfigKeys.MIN_STD), + gene_param.get(ConfigKeys.TEMPLATE), + gene_param.get(ConfigKeys.KEY_KEY), + ) + self.addNode(gen_param_node) + + gen_data_list = config_dict.get(ConfigKeys.GEN_DATA, []) + for gene_data in gen_data_list: + gen_data_node = EnkfConfigNode.create_gen_data_full( + gene_data.get(ConfigKeys.NAME), + gene_data.get(ConfigKeys.RESULT_FILE), + gene_data.get(ConfigKeys.INPUT_FORMAT), + gene_data.get(ConfigKeys.REPORT_STEPS), + gene_data.get(ConfigKeys.ECL_FILE), + gene_data.get(ConfigKeys.INIT_FILES), + gene_data.get(ConfigKeys.TEMPLATE), + gene_data.get(ConfigKeys.KEY_KEY), + ) + self.addNode(gen_data_node) + + gen_kw_list = config_dict.get(ConfigKeys.GEN_KW, []) + for gen_kw in gen_kw_list: + gen_kw_node = EnkfConfigNode.create_gen_kw( + gen_kw.get(ConfigKeys.NAME), + _get_abs_path(gen_kw.get(ConfigKeys.TEMPLATE)), + gen_kw.get(ConfigKeys.OUT_FILE), + _get_abs_path(gen_kw.get(ConfigKeys.PARAMETER_FILE)), + gen_kw.get(ConfigKeys.FORWARD_INIT), + gen_kw.get(ConfigKeys.MIN_STD), + gen_kw.get(ConfigKeys.INIT_FILES), + config_dict.get(ConfigKeys.GEN_KW_TAG_FORMAT), + ) + self.addNode(gen_kw_node) + + surface_list = config_dict.get(ConfigKeys.SURFACE_KEY, []) + for surface in surface_list: + surface_node = EnkfConfigNode.create_surface( + surface.get(ConfigKeys.NAME), + surface.get(ConfigKeys.INIT_FILES), + surface.get(ConfigKeys.OUT_FILE), + surface.get(ConfigKeys.BASE_SURFACE_KEY), + surface.get(ConfigKeys.MIN_STD), + surface.get(ConfigKeys.FORWARD_INIT), + ) + self.addNode(surface_node) + + summary_list = config_dict.get(ConfigKeys.SUMMARY, []) + for a in summary_list: + self.add_summary_full(a, refcase) + + field_list = config_dict.get(ConfigKeys.FIELD_KEY, []) + for field in field_list: + field_node = EnkfConfigNode.create_field( + field.get(ConfigKeys.NAME), + field.get(ConfigKeys.VAR_TYPE), + grid, + self._get_trans_table(), + field.get(ConfigKeys.OUT_FILE), + field.get(ConfigKeys.ENKF_INFILE), + field.get(ConfigKeys.FORWARD_INIT), + field.get(ConfigKeys.INIT_TRANSFORM), + field.get(ConfigKeys.OUTPUT_TRANSFORM), + field.get(ConfigKeys.INPUT_TRANSFORM), + field.get(ConfigKeys.MIN_STD), + field.get(ConfigKeys.MIN_KEY), + field.get(ConfigKeys.MAX_KEY), + field.get(ConfigKeys.INIT_FILES), + ) + self.addNode(field_node) + + schedule_file_list = config_dict.get( + ConfigKeys.SCHEDULE_PREDICTION_FILE, [] + ) + for schedule_file in schedule_file_list: + schedule_file_node = EnkfConfigNode.create_gen_kw( + ConfigKeys.PRED_KEY, + schedule_file.get(ConfigKeys.TEMPLATE), + _get_filename(schedule_file.get(ConfigKeys.TEMPLATE)), + schedule_file.get(ConfigKeys.PARAMETER_KEY), + False, + schedule_file.get(ConfigKeys.MIN_STD), + schedule_file.get(ConfigKeys.INIT_FILES), + config_dict.get(ConfigKeys.GEN_KW_TAG_FORMAT), + ) + self.addNode(schedule_file_node) + + container_list = config_dict.get(ConfigKeys.CONTAINER_KEY, []) + for container in container_list: + container_node = EnkfConfigNode.create_container( + container.get(ConfigKeys.NAME) + ) + for child_key in container.get(ConfigKeys.ARGLIST): + container_node._update_container(self.getNode(child_key)) + self.addNode(container_node) + + return + + c_ptr = self._alloc(config_content, grid, refcase) + if c_ptr is None: + raise ValueError("Failed to construct EnsembleConfig instance") + super(EnsembleConfig, self).__init__(c_ptr) + + def __len__(self): + return self._size() + + def __getitem__(self, key): + """@rtype: EnkfConfigNode""" + if key in self: + return self._get_node(key).setParent(self) + else: + raise KeyError("The key:%s is not in the ensemble configuration" % key) + + def getNode(self, key): + return self[key] + + def alloc_keylist(self): + """@rtype: StringList""" + return self._alloc_keylist() + + def add_summary(self, key): + """@rtype: EnkfConfigNode""" + return self._add_summary(key, 2).setParent(self) + + def add_summary_full(self, key, refcase): + """@rtype: EnkfConfigNode""" + return self._add_summary_full(key, refcase) + + def add_gen_kw(self, key): + """@rtype: EnkfConfigNode""" + return self._add_gen_kw(key).setParent(self) + + def addNode(self, config_node): + assert isinstance(config_node, EnkfConfigNode) + self._add_node(config_node) + config_node.convertToCReference(self) + + def add_field(self, key, eclipse_grid): + """@rtype: EnkfConfigNode""" + return self._add_field(key, eclipse_grid).setParent(self) + + def getKeylistFromVarType(self, var_mask): + """@rtype: StringList""" + assert isinstance(var_mask, EnkfVarType) + return self._alloc_keylist_from_var_type(var_mask) + + def getKeylistFromImplType(self, ert_impl_type): + """@rtype: StringList""" + assert isinstance(ert_impl_type, ErtImplType) + return self._alloc_keylist_from_impl_type(ert_impl_type) + + def __contains__(self, key): + return self._has_key(key) + + def getSummaryKeyMatcher(self): + """@rtype: SummaryKeyMatcher""" + return self._summary_key_matcher() + + def free(self): + self._free() + + def __eq__(self, other): + self_param_list = set(self.alloc_keylist()) + other_param_list = set(other.alloc_keylist()) + if self_param_list != other_param_list: + return False + + for a in self_param_list: + if a in self and a in other: + if self.getNode(a) != other.getNode(a): + return False + else: + return False + + return True + + def __ne__(self, other): + return not self == other diff --git a/res/enkf/enums/__init__.py b/res/enkf/enums/__init__.py new file mode 100644 index 00000000000..b8db1263e1e --- /dev/null +++ b/res/enkf/enums/__init__.py @@ -0,0 +1,29 @@ +from .enkf_field_file_format_enum import EnkfFieldFileFormatEnum +from .load_fail_type_enum import LoadFailTypeEnum +from .enkf_var_type_enum import EnkfVarType +from .enkf_run_enum import EnkfRunType +from .enkf_obs_impl_type_enum import EnkfObservationImplementationType +from .ert_impl_type_enum import ErtImplType +from .enkf_init_modes_enum import EnkfInitModeEnum +from .realization_state_enum import RealizationStateEnum +from .enkf_truncation_type import EnkfTruncationType +from .enkf_fs_type_enum import EnKFFSType +from .gen_data_file_type_enum import GenDataFileType +from .active_mode_enum import ActiveMode +from .hook_runtime_enum import HookRuntime + +__all__ = [ + "EnkfFieldFileFormatEnum", + "LoadFailTypeEnum", + "EnkfVarType", + "EnkfRunType", + "EnkfObservationImplementationType", + "ErtImplType", + "EnkfInitModeEnum", + "RealizationStateEnum", + "EnkfTruncationType", + "EnKFFSType", + "GenDataFileType", + "ActiveMode", + "HookRuntime", +] diff --git a/res/enkf/enums/active_mode_enum.py b/res/enkf/enums/active_mode_enum.py new file mode 100644 index 00000000000..0a7e264cb61 --- /dev/null +++ b/res/enkf/enums/active_mode_enum.py @@ -0,0 +1,28 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'content_type_enum.py' 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. +from cwrap import BaseCEnum + + +class ActiveMode(BaseCEnum): + TYPE_NAME = "active_mode_enum" + ALL_ACTIVE = None + INACTIVE = None + PARTLY_ACTIVE = None + + +ActiveMode.addEnum("ALL_ACTIVE", 1) +ActiveMode.addEnum("INACTIVE", 2) +ActiveMode.addEnum("PARTLY_ACTIVE", 3) diff --git a/res/enkf/enums/enkf_field_file_format_enum.py b/res/enkf/enums/enkf_field_file_format_enum.py new file mode 100644 index 00000000000..674ed116438 --- /dev/null +++ b/res/enkf/enums/enkf_field_file_format_enum.py @@ -0,0 +1,38 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'enkf_field_file_format_enum.py' 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. +from cwrap import BaseCEnum + + +class EnkfFieldFileFormatEnum(BaseCEnum): + TYPE_NAME = "enkf_field_file_format_enum" + UNDEFINED_FORMAT = None + RMS_ROFF_FILE = None + ECL_KW_FILE = None + ECL_KW_FILE_ACTIVE_CELLS = None + ECL_KW_FILE_ALL_CELLS = None + ECL_GRDECL_FILE = None + ECL_FILE = None + FILE_FORMAT_NULL = None + + +EnkfFieldFileFormatEnum.addEnum("UNDEFINED_FORMAT", 0) +EnkfFieldFileFormatEnum.addEnum("RMS_ROFF_FILE", 1) +EnkfFieldFileFormatEnum.addEnum("ECL_KW_FILE", 2) +EnkfFieldFileFormatEnum.addEnum("ECL_KW_FILE_ACTIVE_CELLS", 3) +EnkfFieldFileFormatEnum.addEnum("ECL_KW_FILE_ALL_CELLS", 4) +EnkfFieldFileFormatEnum.addEnum("ECL_GRDECL_FILE", 5) +EnkfFieldFileFormatEnum.addEnum("ECL_FILE", 6) +EnkfFieldFileFormatEnum.addEnum("FILE_FORMAT_NULL", 7) diff --git a/res/enkf/enums/enkf_fs_type_enum.py b/res/enkf/enums/enkf_fs_type_enum.py new file mode 100644 index 00000000000..4b91b454287 --- /dev/null +++ b/res/enkf/enums/enkf_fs_type_enum.py @@ -0,0 +1,26 @@ +# Copyright (C) 2014 Equinor ASA, Norway. +# +# The file 'enkf_fs_type_enum.py' 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. +from cwrap import BaseCEnum + + +class EnKFFSType(BaseCEnum): + TYPE_NAME = "enkf_fs_type_enum" + INVALID_DRIVER_ID = None + BLOCK_FS_DRIVER_ID = None + + +EnKFFSType.addEnum("INVALID_DRIVER_ID", 0) +EnKFFSType.addEnum("BLOCK_FS_DRIVER_ID", 3001) diff --git a/res/enkf/enums/enkf_init_modes_enum.py b/res/enkf/enums/enkf_init_modes_enum.py new file mode 100644 index 00000000000..a307fe11166 --- /dev/null +++ b/res/enkf/enums/enkf_init_modes_enum.py @@ -0,0 +1,28 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'content_type_enum.py' 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. +from cwrap import BaseCEnum + + +class EnkfInitModeEnum(BaseCEnum): + TYPE_NAME = "enkf_init_mode_enum" + INIT_NONE = None + INIT_CONDITIONAL = None + INIT_FORCE = None + + +EnkfInitModeEnum.addEnum("INIT_NONE", 0) +EnkfInitModeEnum.addEnum("INIT_CONDITIONAL", 1) +EnkfInitModeEnum.addEnum("INIT_FORCE", 2) diff --git a/res/enkf/enums/enkf_obs_impl_type_enum.py b/res/enkf/enums/enkf_obs_impl_type_enum.py new file mode 100644 index 00000000000..6bdc482cd7f --- /dev/null +++ b/res/enkf/enums/enkf_obs_impl_type_enum.py @@ -0,0 +1,28 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'enkf_obs_impl_type_enum.py' 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. +from cwrap import BaseCEnum + + +class EnkfObservationImplementationType(BaseCEnum): + TYPE_NAME = "enkf_obs_impl_type" + GEN_OBS = None + SUMMARY_OBS = None + BLOCK_OBS = None + + +EnkfObservationImplementationType.addEnum("GEN_OBS", 1) +EnkfObservationImplementationType.addEnum("SUMMARY_OBS", 2) +EnkfObservationImplementationType.addEnum("BLOCK_OBS", 3) diff --git a/res/enkf/enums/enkf_run_enum.py b/res/enkf/enums/enkf_run_enum.py new file mode 100644 index 00000000000..8955e96ea60 --- /dev/null +++ b/res/enkf/enums/enkf_run_enum.py @@ -0,0 +1,33 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'content_type_enum.py' 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. +from cwrap import BaseCEnum + + +class EnkfRunType(BaseCEnum): + TYPE_NAME = "enkf_run_mode_enum" + ENKF_ASSIMILATION = None + ENSEMBLE_EXPERIMENT = None + SMOOTHER_UPDATE = None + INIT_ONLY = None + CASE_INIT_ONLY = None + + +EnkfRunType.addEnum("ENKF_ASSIMILATION", 1) +EnkfRunType.addEnum("ENSEMBLE_EXPERIMENT", 2) +EnkfRunType.addEnum("SMOOTHER_RUN", 4) +EnkfRunType.addEnum("INIT_ONLY", 8) +EnkfRunType.addEnum("SMOOTHER_UPDATE", 16) +EnkfRunType.addEnum("CASE_INIT_ONLY", 32) diff --git a/res/enkf/enums/enkf_truncation_type.py b/res/enkf/enums/enkf_truncation_type.py new file mode 100644 index 00000000000..720dc23c57d --- /dev/null +++ b/res/enkf/enums/enkf_truncation_type.py @@ -0,0 +1,28 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'content_type_enum.py' 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. +from cwrap import BaseCEnum + + +class EnkfTruncationType(BaseCEnum): + TYPE_NAME = "enkf_truncation_type_enum" + TRUNCATE_NONE = None + TRUNCATE_MIN = None + TRUNCATE_MAX = None + + +EnkfTruncationType.addEnum("TRUNCATE_NONE", 0) +EnkfTruncationType.addEnum("TRUNCATE_MIN", 1) +EnkfTruncationType.addEnum("TRUNCATE_MAX", 2) diff --git a/res/enkf/enums/enkf_var_type_enum.py b/res/enkf/enums/enkf_var_type_enum.py new file mode 100644 index 00000000000..e78115fd796 --- /dev/null +++ b/res/enkf/enums/enkf_var_type_enum.py @@ -0,0 +1,34 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'content_type_enum.py' 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. +from cwrap import BaseCEnum + + +class EnkfVarType(BaseCEnum): + TYPE_NAME = "enkf_var_type_enum" + INVALID_VAR = None + PARAMETER = None + DYNAMIC_RESULT = None + STATIC_STATE = None + INDEX_STATE = None + EXT_PARAMETER = None + + +EnkfVarType.addEnum("INVALID_VAR", 0) +EnkfVarType.addEnum("PARAMETER", 1) +EnkfVarType.addEnum("DYNAMIC_RESULT", 4) +EnkfVarType.addEnum("STATIC_STATE", 8) +EnkfVarType.addEnum("INDEX_STATE", 16) +EnkfVarType.addEnum("EXT_PARAMETER", 32) diff --git a/res/enkf/enums/ert_impl_type_enum.py b/res/enkf/enums/ert_impl_type_enum.py new file mode 100644 index 00000000000..71b4f3565a1 --- /dev/null +++ b/res/enkf/enums/ert_impl_type_enum.py @@ -0,0 +1,42 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'ert_impl_type_enum.py' 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. +from cwrap import BaseCEnum + + +class ErtImplType(BaseCEnum): + TYPE_NAME = "ert_impl_type_enum" + INVALID = None + IMPL_TYPE_OFFSET = None + STATIC = None # MULTZ has been removed & MULTFLT + FIELD = None # WELL has been removed + GEN_KW = None # RELPERM has been removed & HAVANA_FAULT + SUMMARY = None # TPGZONE has been removed + GEN_DATA = None # PILOT_POINT has been removed + SURFACE = None + CONTAINER = None + EXT_PARAM = None + + +ErtImplType.addEnum("INVALID", 0) +ErtImplType.addEnum("IMPL_TYPE_OFFSET", 100) +ErtImplType.addEnum("STATIC", 100) +ErtImplType.addEnum("FIELD", 104) +ErtImplType.addEnum("GEN_KW", 107) +ErtImplType.addEnum("SUMMARY", 110) +ErtImplType.addEnum("GEN_DATA", 113) +ErtImplType.addEnum("SURFACE", 114) +ErtImplType.addEnum("CONTAINER", 115) +ErtImplType.addEnum("EXT_PARAM", 116) diff --git a/res/enkf/enums/gen_data_file_type_enum.py b/res/enkf/enums/gen_data_file_type_enum.py new file mode 100644 index 00000000000..a64422c55d1 --- /dev/null +++ b/res/enkf/enums/gen_data_file_type_enum.py @@ -0,0 +1,32 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'gen_data_file_type_enum.py' 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. +from cwrap import BaseCEnum + + +class GenDataFileType(BaseCEnum): + TYPE_NAME = "gen_data_file_format_type" + GEN_DATA_UNDEFINED = None + ASCII = None # The file is ASCII file with a vector of numbers formatted with "%g" + ASCII_TEMPLATE = None # The data is inserted into a user defined template file. + BINARY_DOUBLE = None # The data is in a binary file with doubles. + BINARY_FLOAT = None # The data is in a binary file with floats. + + +GenDataFileType.addEnum("GEN_DATA_UNDEFINED", 0) +GenDataFileType.addEnum("ASCII", 1) +GenDataFileType.addEnum("ASCII_TEMPLATE", 2) +GenDataFileType.addEnum("BINARY_DOUBLE", 3) +GenDataFileType.addEnum("BINARY_FLOAT", 4) diff --git a/res/enkf/enums/hook_runtime_enum.py b/res/enkf/enums/hook_runtime_enum.py new file mode 100644 index 00000000000..acbd67cfb93 --- /dev/null +++ b/res/enkf/enums/hook_runtime_enum.py @@ -0,0 +1,32 @@ +# Copyright (C) 2016 Equinor ASA, Norway. +# +# The file 'hook_runtime_enum.py' 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. +from cwrap import BaseCEnum + + +class HookRuntime(BaseCEnum): + TYPE_NAME = "hook_runtime_enum" + PRE_SIMULATION = None + POST_SIMULATION = None + PRE_UPDATE = None + POST_UPDATE = None + PRE_FIRST_UPDATE = None + + +HookRuntime.addEnum("PRE_SIMULATION", 0) +HookRuntime.addEnum("POST_SIMULATION", 1) +HookRuntime.addEnum("PRE_UPDATE", 2) +HookRuntime.addEnum("POST_UPDATE", 3) +HookRuntime.addEnum("PRE_FIRST_UPDATE", 4) diff --git a/res/enkf/enums/load_fail_type_enum.py b/res/enkf/enums/load_fail_type_enum.py new file mode 100644 index 00000000000..26251cbe99c --- /dev/null +++ b/res/enkf/enums/load_fail_type_enum.py @@ -0,0 +1,28 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'content_type_enum.py' 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. +from cwrap import BaseCEnum + + +class LoadFailTypeEnum(BaseCEnum): + TYPE_NAME = "load_fail_type" + LOAD_FAIL_SILENT = None + LOAD_FAIL_WARN = None + LOAD_FAIL_EXIT = None + + +LoadFailTypeEnum.addEnum("LOAD_FAIL_SILENT", 0) +LoadFailTypeEnum.addEnum("LOAD_FAIL_WARN", 2) +LoadFailTypeEnum.addEnum("LOAD_FAIL_EXIT", 4) diff --git a/res/enkf/enums/realization_state_enum.py b/res/enkf/enums/realization_state_enum.py new file mode 100644 index 00000000000..cf5c48be310 --- /dev/null +++ b/res/enkf/enums/realization_state_enum.py @@ -0,0 +1,32 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'ert_impl_type_enum.py' 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. +from cwrap import BaseCEnum + + +class RealizationStateEnum(BaseCEnum): + TYPE_NAME = "realisation_state_enum" + STATE_UNDEFINED = None + STATE_INITIALIZED = None + STATE_HAS_DATA = None + STATE_LOAD_FAILURE = None + STATE_PARENT_FAILURE = None + + +RealizationStateEnum.addEnum("STATE_UNDEFINED", 1) +RealizationStateEnum.addEnum("STATE_INITIALIZED", 2) +RealizationStateEnum.addEnum("STATE_HAS_DATA", 4) +RealizationStateEnum.addEnum("STATE_LOAD_FAILURE", 8) +RealizationStateEnum.addEnum("STATE_PARENT_FAILURE", 16) diff --git a/res/enkf/ert_run_context.py b/res/enkf/ert_run_context.py new file mode 100644 index 00000000000..5d914dd25de --- /dev/null +++ b/res/enkf/ert_run_context.py @@ -0,0 +1,210 @@ +# Copyright (C) 2014 Equinor ASA, Norway. +# +# The file 'enkf_fs.py' 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. +from cwrap import BaseCClass + +from ecl.util.util import StringList + +from res.util import PathFormat +from res import ResPrototype +from res.enkf import TimeMap, StateMap, RunArg +from res.enkf.enums import EnkfInitModeEnum + + +class ErtRunContext(BaseCClass): + TYPE_NAME = "ert_run_context" + _alloc = ResPrototype( + "void* ert_run_context_alloc( enkf_run_mode_enum , enkf_init_mode_enum, enkf_fs , enkf_fs, bool_vector, path_fmt ,char*, subst_list, int)", + bind=False, + ) + _alloc_ensemble_experiment = ResPrototype( + "ert_run_context_obj ert_run_context_alloc_ENSEMBLE_EXPERIMENT( enkf_fs, bool_vector, path_fmt ,char*, subst_list, int)", + bind=False, + ) + _alloc_ensemble_smoother = ResPrototype( + "ert_run_context_obj ert_run_context_alloc_SMOOTHER_RUN( enkf_fs , enkf_fs, bool_vector, path_fmt ,char*, subst_list, int)", + bind=False, + ) + _alloc_ensemble_smoother_update = ResPrototype( + "ert_run_context_obj ert_run_context_alloc_SMOOTHER_UPDATE(enkf_fs , enkf_fs )", + bind=False, + ) + _alloc_case_init = ResPrototype( + "ert_run_context_obj ert_run_context_alloc_CASE_INIT(enkf_fs, bool_vector)", + bind=False, + ) + _alloc_runpath_list = ResPrototype( + "stringlist_obj ert_run_context_alloc_runpath_list(bool_vector, path_fmt, subst_list, int)", + bind=False, + ) + _alloc_runpath = ResPrototype( + "char* ert_run_context_alloc_runpath(int, path_fmt, subst_list, int)", + bind=False, + ) + _get_size = ResPrototype("int ert_run_context_get_size( ert_run_context )") + _free = ResPrototype("void ert_run_context_free( ert_run_context )") + _iactive = ResPrototype("bool ert_run_context_iactive( ert_run_context , int)") + _iget = ResPrototype("run_arg_ref ert_run_context_iget_arg( ert_run_context , int)") + _get_id = ResPrototype("char* ert_run_context_get_id( ert_run_context )") + _get_mask = ResPrototype( + "bool_vector_obj ert_run_context_alloc_iactive( ert_run_context )" + ) + _get_iter = ResPrototype("int ert_run_context_get_iter( ert_run_context )") + _get_target_fs = ResPrototype( + "enkf_fs_ref ert_run_context_get_update_target_fs( ert_run_context )" + ) + _get_sim_fs = ResPrototype( + "enkf_fs_ref ert_run_context_get_sim_fs( ert_run_context )" + ) + _get_init_mode = ResPrototype( + "enkf_init_mode_enum ert_run_context_get_init_mode( ert_run_context )" + ) + + _get_step = ResPrototype("int ert_run_context_get_step1(ert_run_context)") + _deactivate_realization = ResPrototype( + "void ert_run_context_deactivate_realization( ert_run_context, int)" + ) + + def __init__( + self, + run_type, + sim_fs, + target_fs, + mask, + path_fmt, + jobname_fmt, + subst_list, + itr, + init_mode=EnkfInitModeEnum.INIT_CONDITIONAL, + ): + c_ptr = self._alloc( + run_type, + init_mode, + sim_fs, + target_fs, + mask, + path_fmt, + jobname_fmt, + subst_list, + itr, + ) + super(ErtRunContext, self).__init__(c_ptr) + + # The C object ert_run_context uses a shared object for the + # path_fmt and subst_list objects. We therefor hold on + # to a reference here - to inhibt Python GC of these objects. + self._path_fmt = path_fmt + self._subst_list = subst_list + + @classmethod + def case_init(cls, sim_fs, mask): + return cls._alloc_case_init(sim_fs, mask) + + @classmethod + def ensemble_experiment(cls, sim_fs, mask, path_fmt, jobname_fmt, subst_list, itr): + run_context = cls._alloc_ensemble_experiment( + sim_fs, mask, path_fmt, jobname_fmt, subst_list, itr + ) + + # The C object ert_run_context uses a shared object for the + # path_fmt and subst_list objects. We therefor hold on + # to a reference here - to inhibt Python GC of these objects. + run_context._path_fmt = path_fmt + run_context._subst_list = subst_list + + return run_context + + @classmethod + def ensemble_smoother( + cls, sim_fs, target_fs, mask, path_fmt, jobname_fmt, subst_list, itr + ): + run_context = cls._alloc_ensemble_smoother( + sim_fs, target_fs, mask, path_fmt, jobname_fmt, subst_list, itr + ) + + # The C object ert_run_context uses a shared object for the + # path_fmt and subst_list objects. We therefor hold on + # to a reference here - to inhibt Python GC of these objects. + run_context._path_fmt = path_fmt + run_context._subst_list = subst_list + + return run_context + + @classmethod + def ensemble_smoother_update(cls, sim_fs, target_fs): + return cls._alloc_ensemble_smoother_update(sim_fs, target_fs) + + def is_active(self, index): + if 0 <= index < len(self): + return self._iactive(index) + else: + raise IndexError( + "Index:%d invalid. Legal range: [0,%d)" % (index, len(self)) + ) + + def __len__(self): + return self._get_size() + + def __getitem__(self, index): + if not isinstance(index, int): + raise TypeError("Invalid type - expetected integer") + + if 0 <= index < len(self): + run_arg = self._iget(index) + return run_arg + else: + raise IndexError( + "Index:%d invalid. Legal range: [0,%d)" % (index, len(self)) + ) + + def free(self): + self._free() + + def __repr__(self): + return "ErtRunContext(size = %d) %s" % (len(self), self._ad_str()) + + @classmethod + def createRunpathList(cls, mask, runpath_fmt, subst_list, iter=0): + """@rtype: ecl.util.stringlist.StringList""" + return cls._alloc_runpath_list(mask, runpath_fmt, subst_list, iter) + + @classmethod + def createRunpath(cls, iens, runpath_fmt, subst_list, iter=0): + """@rtype: str""" + return cls._alloc_runpath(iens, runpath_fmt, subst_list, iter) + + def get_id(self): + return self._get_id() + + def get_mask(self): + return self._get_mask() + + def get_iter(self): + return self._get_iter() + + def get_target_fs(self): + return self._get_target_fs() + + def get_sim_fs(self): + return self._get_sim_fs() + + def get_init_mode(self): + return self._get_init_mode() + + def get_step(self): + return self._get_step() + + def deactivate_realization(self, realization_nr): + self._deactivate_realization(realization_nr) diff --git a/res/enkf/ert_template.py b/res/enkf/ert_template.py new file mode 100644 index 00000000000..2c712eb06e3 --- /dev/null +++ b/res/enkf/ert_template.py @@ -0,0 +1,66 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'ert_template.py' 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. +from cwrap import BaseCClass +from res import ResPrototype + + +class ErtTemplate(BaseCClass): + TYPE_NAME = "ert_template" + + _free = ResPrototype("void ert_template_free( ert_template )") + _get_template_file = ResPrototype( + "char* ert_template_get_template_file(ert_template)" + ) + _get_target_file = ResPrototype("char* ert_template_get_target_file(ert_template)") + _get_arg_list = ResPrototype( + "subst_list_ref ert_template_get_arg_list( ert_template )" + ) + + def __init__(self): + raise NotImplementedError("Class can not be instantiated directly!") + + def get_template_file(self): + """@rtype: str""" + return self._get_template_file() + + def get_target_file(self): + """@rtype: str""" + return self._get_target_file() + + def get_args_as_string(self): + """@rtype: str""" + args_list = self._get_arg_list() + return ", ".join( + ["{}={}".format(key, args_list.get(key)) for key in args_list.keys()] + ) + + def __eq__(self, other): + return ( + self.get_template_file() == other.get_template_file() + and self.get_target_file() == other.get_target_file() + and self.get_args_as_string() == other.get_args_as_string() + ) + + def __ne__(self, other): + return not self == other + + def __repr__(self): + return "ErtTemplate({}, {}, {})".format( + self.get_template_file(), self.get_target_file(), self.get_args_as_string() + ) + + def free(self): + self._free() diff --git a/res/enkf/ert_templates.py b/res/enkf/ert_templates.py new file mode 100644 index 00000000000..63cdafe1237 --- /dev/null +++ b/res/enkf/ert_templates.py @@ -0,0 +1,122 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'ert_templates.py' 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. +from cwrap import BaseCClass +from res import ResPrototype +from res.enkf import ErtTemplate, ConfigKeys +from ecl.util.util import StringList +import os + + +class ErtTemplates(BaseCClass): + TYPE_NAME = "ert_templates" + _alloc = ResPrototype( + "void* ert_templates_alloc( subst_list, config_content )", bind=False + ) + _alloc_default = ResPrototype( + "void* ert_templates_alloc_default( subst_list )", bind=False + ) + _free = ResPrototype("void ert_templates_free( ert_templates )") + _alloc_list = ResPrototype("stringlist_ref ert_templates_alloc_list(ert_templates)") + _get_template = ResPrototype( + "ert_template_ref ert_templates_get_template(ert_templates, char*)" + ) + _clear = ResPrototype("void ert_templates_clear(ert_templates)") + _add_template = ResPrototype( + "ert_template_ref ert_templates_add_template(ert_templates, char*, char*, char*, char*)" + ) + _add_template_unbound = ResPrototype( + "ert_template_ref ert_templates_add_template(ert_templates, char*, char*, char*, char*)", + bind=False, + ) + + def __init__(self, parent_subst, config_content=None, config_dict=None): + if not ((config_content is not None) ^ (config_dict is not None)): + raise ValueError( + "ErtTemplates must be instantiated with exactly one of config_content or config_dict" + ) + + if config_dict is not None: + c_ptr = self._alloc_default(parent_subst) + if c_ptr is None: + raise ValueError("Failed to construct ErtTemplates instance") + super(ErtTemplates, self).__init__(c_ptr) + run_template = config_dict.get(ConfigKeys.RUN_TEMPLATE) + if isinstance(run_template, list): + for template_file_name, target_file, arguments in run_template: + path = config_dict.get(ConfigKeys.CONFIG_DIRECTORY) + if not isinstance(path, str): + raise ValueError( + "ErtTemplates requires {} to be set".format( + ConfigKeys.CONFIG_DIRECTORY + ) + ) + template_path = os.path.normpath( + os.path.join(path, template_file_name) + ) + arguments_string = ", ".join( + ["{}={}".format(key, val) for key, val in arguments] + ) + self._add_template( + None, template_path, target_file, arguments_string + ) + + else: + c_ptr = self._alloc(parent_subst, config_content) + if c_ptr is None: + raise ValueError("Failed to construct ErtTemplates instance") + super(ErtTemplates, self).__init__(c_ptr) + + def getTemplateNames(self): + """@rtype: StringList""" + return self._alloc_list().setParent(self) + + def clear(self): + self._clear() + + def get_template(self, key): + """@rtype: ErtTemplate""" + return self._get_template(key).setParent(self) + + def add_template(self, key, template_file, target_file, arg_string): + """@rtype: ErtTemplate""" + return self._add_template( + key, template_file, target_file, arg_string + ).setParent(self) + + def __eq__(self, other): + if len(self.getTemplateNames()) != len(other.getTemplateNames()): + return False + if not all( + name in self.getTemplateNames() for name in other.getTemplateNames() + ): + return False + for name in self.getTemplateNames(): + if self.get_template(name) != other.get_template(name): + return False + return True + + def __ne__(self, other): + return not self == other + + def __repr__(self): + return "ErtTemplates({})".format( + ", ".join( + x + "=" + str(self.get_template(x)) for x in self.getTemplateNames() + ) + ) + + def free(self): + self._free() diff --git a/res/enkf/ert_workflow_list.py b/res/enkf/ert_workflow_list.py new file mode 100644 index 00000000000..da614e1fb9c --- /dev/null +++ b/res/enkf/ert_workflow_list.py @@ -0,0 +1,203 @@ +from cwrap import BaseCClass +from res import ResPrototype +from ecl.util.util import StringList +from res.util.substitution_list import SubstitutionList +from res.job_queue import Workflow, WorkflowJob, WorkflowJoblist +from res.enkf import ConfigKeys +import os + + +class ErtWorkflowList(BaseCClass): + TYPE_NAME = "ert_workflow_list" + _alloc = ResPrototype( + "void* ert_workflow_list_alloc(subst_list, config_content)", bind=False + ) + _alloc_full = ResPrototype( + "void* ert_workflow_list_alloc_full(subst_list, workflow_joblist)", bind=False + ) + _free = ResPrototype("void ert_workflow_list_free(ert_workflow_list)") + _alloc_namelist = ResPrototype( + "stringlist_obj ert_workflow_list_alloc_namelist(ert_workflow_list)" + ) + _has_workflow = ResPrototype( + "bool ert_workflow_list_has_workflow(ert_workflow_list, char*)" + ) + _get_workflow = ResPrototype( + "workflow_ref ert_workflow_list_get_workflow(ert_workflow_list, char*)" + ) + _add_workflow = ResPrototype( + "workflow_ref ert_workflow_list_add_workflow(ert_workflow_list, char*, char*)" + ) + _get_context = ResPrototype( + "subst_list_ref ert_workflow_list_get_context(ert_workflow_list)" + ) + _add_job = ResPrototype( + "void ert_workflow_list_add_job(ert_workflow_list, char*, char*)" + ) + _has_job = ResPrototype("bool ert_workflow_list_has_job(ert_workflow_list, char*)") + _get_job = ResPrototype( + "workflow_job_ref ert_workflow_list_get_job(ert_workflow_list, char*)" + ) + _get_job_names = ResPrototype( + "stringlist_obj ert_workflow_list_get_job_names(ert_workflow_list)" + ) + + def __init__(self, subst_list=None, config_content=None, config_dict=None): + if subst_list is None: + raise ValueError( + "Failed to construct ErtWorkflowList with no substitution list" + ) + + if config_content is None and config_dict is None: + raise ValueError( + "Failed to construct ErtWorkflowList instance with no config object" + ) + + if config_content is not None and config_dict is not None: + raise ValueError( + "Failed to construct ErtWorkflowList instance with multiple config object" + ) + + c_ptr = None + + if config_content is not None: + c_ptr = self._alloc(subst_list, config_content) + + if config_dict is not None: + workflow_joblist = WorkflowJoblist() + parser = WorkflowJob.configParser() + for job in config_dict.get(ConfigKeys.LOAD_WORKFLOW_JOB, []): + try: + new_job = WorkflowJob.fromFile( + config_file=job[ConfigKeys.PATH], + name=job[ConfigKeys.NAME], + parser=parser, + ) + except: + print( + "WARNING: Unable to create job from {}".format( + job[ConfigKeys.PATH] + ) + ) + continue + if not new_job is None: + workflow_joblist.addJob(new_job) + new_job.convertToCReference(None) + + for job_path in config_dict.get(ConfigKeys.WORKFLOW_JOB_DIRECTORY, []): + if not os.path.isdir(job_path): + print("WARNING: Unable to open job directory {}".format(job_path)) + continue + + files = os.listdir(job_path) + for file_name in files: + full_path = os.path.join(job_path, file_name) + try: + new_job = WorkflowJob.fromFile( + config_file=full_path, parser=parser + ) + workflow_joblist.addJob(new_job) + new_job.convertToCReference(None) + except: + print("WARNING: Unable to create job from {}".format(full_path)) + continue + + workflow_joblist.convertToCReference(None) + + c_ptr = self._alloc_full(subst_list, workflow_joblist) + + if c_ptr is None: + raise ValueError("Failed to construct ErtWorkflowList instance") + + super(ErtWorkflowList, self).__init__(c_ptr) + + if config_dict is not None: + for job in config_dict.get(ConfigKeys.LOAD_WORKFLOW, []): + self.addWorkflow(job[ConfigKeys.PATH], job[ConfigKeys.NAME]) + + def getWorkflowNames(self): + """@rtype: StringList""" + return [name for name in self._alloc_namelist()] + + def __contains__(self, workflow_name): + assert isinstance(workflow_name, str) + return self._has_workflow(workflow_name) + + def __getitem__(self, item): + """@rtype: Workflow""" + if not item in self: + raise KeyError( + "Item '%s' is not in the list of available workflows." % item + ) + + return self._get_workflow(item).setParent(self) + + def getContext(self): + """@rtype: SubstitutionList""" + return self._get_context() + + def __str__(self): + return "ErtWorkflowList with jobs: %s" + str(self.getJobNames()) + + def addWorkflow(self, wf_name, wf_path): + self._add_workflow(wf_name, wf_path).setParent(self) + + def addJob(self, job_name, job_path): + """ + @type job_name: str + @type job_path: str + """ + self._add_job(job_name, job_path) + + def hasJob(self, job_name): + """ + @type job_name: str + @rtype: bool + """ + return self._has_job(job_name) + + def getJob(self, job_name): + """@rtype: WorkflowJob""" + return self._get_job(job_name) + + def getJobNames(self): + """@rtype: StringList""" + return [name for name in self._get_job_names()] + + def getPluginJobs(self): + """@rtype: list of WorkflowJob""" + plugins = [] + for job_name in self.getJobNames(): + job = self.getJob(job_name) + if job.isPlugin(): + plugins.append(job) + return plugins + + def free(self): + self._free() + + def __ne__(self, other): + return not (self == other) + + def __eq__(self, other): + if set(self.getJobNames()) != set(other.getJobNames()): + return False + + for name_self, name_other in zip( + sorted(self.getJobNames()), sorted(other.getJobNames()) + ): + job_self = self.getJob(name_self) + job_other = other.getJob(name_other) + if job_self != job_other: + return False + + if self.getWorkflowNames() != other.getWorkflowNames(): + return False + + for name_self, name_other in zip( + self.getWorkflowNames(), other.getWorkflowNames() + ): + if self[name_self] != other[name_other]: + return False + + return True diff --git a/res/enkf/es_update.py b/res/enkf/es_update.py new file mode 100644 index 00000000000..017f45518ae --- /dev/null +++ b/res/enkf/es_update.py @@ -0,0 +1,44 @@ +from cwrap import BaseCClass +from res import ResPrototype + + +class ESUpdate(BaseCClass): + TYPE_NAME = "es_update" + _smoother_update = ResPrototype( + "bool enkf_main_smoother_update(es_update, enkf_fs, enkf_fs)" + ) + + def __init__(self, enkf_main): + assert isinstance(enkf_main, BaseCClass) + + # enkf_main should be an EnKFMain, get the _RealEnKFMain object + real_enkf_main = enkf_main.parent() + + super(ESUpdate, self).__init__( + real_enkf_main.from_param(real_enkf_main).value, + parent=real_enkf_main, + is_reference=True, + ) + + def _analysis_config(self): + return self.parent().analysisConfig() + + def hasModule(self, name): + """ + Will check if we have analysis module @name. + """ + return self._analysis_config().hasModule(name) + + def getModule(self, name): + if self.hasModule(name): + self._analysis_config().getModule(name) + else: + raise KeyError("No such module:%s " % name) + + def setGlobalStdScaling(self, weight): + self._analysis_config().setGlobalStdScaling(weight) + + def smootherUpdate(self, run_context): + data_fs = run_context.get_sim_fs() + target_fs = run_context.get_target_fs() + return self._smoother_update(data_fs, target_fs) diff --git a/res/enkf/export/__init__.py b/res/enkf/export/__init__.py new file mode 100644 index 00000000000..969624a81a6 --- /dev/null +++ b/res/enkf/export/__init__.py @@ -0,0 +1,19 @@ +from .design_matrix_reader import DesignMatrixReader +from .summary_observation_collector import SummaryObservationCollector +from .summary_collector import SummaryCollector +from .gen_kw_collector import GenKwCollector +from .gen_data_collector import GenDataCollector +from .gen_data_observation_collector import GenDataObservationCollector +from .misfit_collector import MisfitCollector +from .arg_loader import ArgLoader + +__all__ = [ + "DesignMatrixReader", + "SummaryCollector", + "SummaryObservationCollector", + "GenKwCollector", + "MisfitCollector", + "GenDataCollector", + "GenDataObservationCollector", + "ArgLoader", +] diff --git a/res/enkf/export/arg_loader.py b/res/enkf/export/arg_loader.py new file mode 100644 index 00000000000..b8b26c91117 --- /dev/null +++ b/res/enkf/export/arg_loader.py @@ -0,0 +1,43 @@ +import math +from pandas import DataFrame, MultiIndex +import numpy +from res.enkf import ErtImplType, EnKFMain, EnkfFs, RealizationStateEnum, GenKwConfig +from res.enkf.plot_data import EnsemblePlotGenData +from ecl.util.util import BoolVector + + +class ArgLoader(object): + @staticmethod + def load(filename, column_names=None): + rows = 0 + columns = 0 + with open(filename, "r") as fileH: + for line in fileH.readlines(): + rows += 1 + columns = max(columns, len(line.split())) + + if not column_names is None: + if len(column_names) <= columns: + columns = len(column_names) + else: + raise ValueError("To many coloumns in input") + + data = numpy.empty(shape=(rows, columns), dtype=numpy.float64) + data.fill(numpy.nan) + + row = 0 + with open(filename) as fileH: + for line in fileH.readlines(): + tmp = line.split() + print(tmp) + for column in range(columns): + data[row][column] = float(tmp[column]) + row += 1 + + if column_names is None: + column_names = [] + for column in range(columns): + column_names.append("Column%d" % column) + + data_frame = DataFrame(data=data, columns=column_names) + return data_frame diff --git a/res/enkf/export/design_matrix_reader.py b/res/enkf/export/design_matrix_reader.py new file mode 100644 index 00000000000..3c98a857d5a --- /dev/null +++ b/res/enkf/export/design_matrix_reader.py @@ -0,0 +1,14 @@ +import pandas as pd +from pandas import DataFrame + + +class DesignMatrixReader(object): + @staticmethod + def loadDesignMatrix(filename): + """@rtype: DataFrame""" + dm = pd.read_csv(filename, delim_whitespace=True) + """ @type dm: pd.DataFrame """ + dm = dm.rename(columns={dm.columns[0]: "Realization"}) + dm = dm.set_index(["Realization"]) + + return dm diff --git a/res/enkf/export/gen_data_collector.py b/res/enkf/export/gen_data_collector.py new file mode 100644 index 00000000000..e3f23a27466 --- /dev/null +++ b/res/enkf/export/gen_data_collector.py @@ -0,0 +1,48 @@ +import math +from pandas import DataFrame, MultiIndex +import numpy +from res.enkf import ErtImplType, EnKFMain, EnkfFs, RealizationStateEnum, GenKwConfig +from res.enkf.plot_data import EnsemblePlotGenData +from ecl.util.util import BoolVector + + +class GenDataCollector(object): + @staticmethod + def loadGenData(ert, case_name, key, report_step): + """@type ert: EnKFMain + @type case_name: str + @type key: str + @type report_step: int + @rtype: DataFrame + + In the returned dataframe the realisation index runs along the + rows, and the gen_data element index runs vertically along the + columns. + """ + fs = ert.getEnkfFsManager().getFileSystem(case_name) + realizations = fs.realizationList(RealizationStateEnum.STATE_HAS_DATA) + config_node = ert.ensembleConfig().getNode(key) + gen_data_config = config_node.getModelConfig() + + ensemble_data = EnsemblePlotGenData(config_node, fs, report_step) + # The data size and active can only be inferred *after* the EnsembleLoad. + data_size = gen_data_config.getDataSize(report_step) + active_mask = gen_data_config.getActiveMask() + + data_array = numpy.empty( + shape=(data_size, len(realizations)), dtype=numpy.float64 + ) + data_array.fill(numpy.nan) + for realization_index, realization_number in enumerate(realizations): + realization_vector = ensemble_data[realization_number] + + if ( + len(realization_vector) > 0 + ): # Must check because of a bug changing between different case with different states + for data_index in range(data_size): + if active_mask[data_index]: + value = realization_vector[data_index] + data_array[data_index][realization_index] = value + + realizations = numpy.array(realizations) + return DataFrame(data=data_array, columns=realizations) diff --git a/res/enkf/export/gen_data_observation_collector.py b/res/enkf/export/gen_data_observation_collector.py new file mode 100644 index 00000000000..b5a7c9eddb4 --- /dev/null +++ b/res/enkf/export/gen_data_observation_collector.py @@ -0,0 +1,76 @@ +from pandas import DataFrame +from res.enkf import EnKFMain, EnkfFs, EnkfObservationImplementationType + + +class GenDataObservationCollector(object): + @staticmethod + def getAllObservationKeys(ert): + """ + @type ert: EnKFMain + @rtype: list of str + """ + enkf_obs = ert.getObservations() + observation_keys = enkf_obs.getTypedKeylist( + EnkfObservationImplementationType.GEN_OBS + ) + return [key for key in observation_keys] + + @staticmethod + def getObservationKeyForDataKey(ert, data_key, data_report_step): + """ + @type ert: EnKFMain + @rtype: str + """ + observation_key = None + + enkf_obs = ert.getObservations() + for obs_vector in enkf_obs: + if EnkfObservationImplementationType.GEN_OBS: + report_step = obs_vector.firstActiveStep() + key = obs_vector.getDataKey() + + if key == data_key and report_step == data_report_step: + observation_key = obs_vector.getObservationKey() + + return observation_key + + @staticmethod + def loadGenDataObservations(ert, case_name, key): + """ + @type ert: EnKFMain + @type case_name: str + @type key: name of an observation key + @rtype: DataFrame + """ + fs = ert.getEnkfFsManager().getFileSystem(case_name) + + available_observation_keys = GenDataObservationCollector.getAllObservationKeys( + ert + ) + if not key in available_observation_keys: + raise KeyError("Key '%s' is not a valid observation key") + + columns = [key] + std_columns = ["STD_%s" % key] + + enkf_obs = ert.getObservations() + + index_set = set() + obs_vector = enkf_obs[key] + report_step = obs_vector.activeStep() + + obs_node = obs_vector.getNode(report_step) + # """ :type: res.enkf.observations.GenObservation """ + + for obs_index in range(len(obs_node)): + index_set.add(obs_node.getIndex(obs_index)) + + index_list = sorted(list(index_set)) + data = DataFrame(index=index_list, columns=columns + std_columns) + + for obs_index, (value, std) in enumerate(obs_node): + data_index = obs_node.getIndex(obs_index) + data[key][data_index] = value + data["STD_%s" % key][data_index] = std + + return data diff --git a/res/enkf/export/gen_kw_collector.py b/res/enkf/export/gen_kw_collector.py new file mode 100644 index 00000000000..234a5eb078c --- /dev/null +++ b/res/enkf/export/gen_kw_collector.py @@ -0,0 +1,81 @@ +import math +from pandas import DataFrame, MultiIndex +import numpy +from res.enkf import ErtImplType, EnKFMain, EnkfFs, RealizationStateEnum, GenKwConfig +from res.enkf.key_manager import KeyManager +from res.enkf.plot_data import EnsemblePlotGenKW +from ecl.util.util import BoolVector + + +class GenKwCollector(object): + @staticmethod + def createActiveList(ert, fs): + state_map = fs.getStateMap() + ens_mask = BoolVector(False, ert.getEnsembleSize()) + state_map.selectMatching( + ens_mask, + RealizationStateEnum.STATE_INITIALIZED + | RealizationStateEnum.STATE_HAS_DATA, + ) + active_list = BoolVector.createActiveList(ens_mask) + + return [iens for iens in active_list] + + @staticmethod + def getAllGenKwKeys(ert): + """@rtype: list of str""" + key_manager = KeyManager(ert) + return key_manager.genKwKeys() + + @staticmethod + def loadAllGenKwData(ert, case_name, keys=None): + """ + @type ert: EnKFMain + @type case_name: str + @type keys: list of str + @rtype: DataFrame + """ + fs = ert.getEnkfFsManager().getFileSystem(case_name) + + realizations = GenKwCollector.createActiveList(ert, fs) + + gen_kw_keys = GenKwCollector.getAllGenKwKeys(ert) + + if keys is not None: + gen_kw_keys = [ + key for key in keys if key in gen_kw_keys + ] # ignore keys that doesn't exist + + gen_kw_array = numpy.empty( + shape=(len(gen_kw_keys), len(realizations)), dtype=numpy.float64 + ) + gen_kw_array.fill(numpy.nan) + + for column_index, key in enumerate(gen_kw_keys): + key, keyword = key.split(":") + + use_log_scale = False + if key.startswith("LOG10_"): + key = key[6:] + use_log_scale = True + + ensemble_config_node = ert.ensembleConfig().getNode(key) + ensemble_data = EnsemblePlotGenKW(ensemble_config_node, fs) + keyword_index = ensemble_data.getIndexForKeyword(keyword) + + for realization_index, realization_number in enumerate(realizations): + realization_vector = ensemble_data[realization_number] + + value = realization_vector[keyword_index] + + if use_log_scale: + value = math.log10(value) + + gen_kw_array[column_index][realization_index] = value + + gen_kw_data = DataFrame( + data=numpy.transpose(gen_kw_array), index=realizations, columns=gen_kw_keys + ) + gen_kw_data.index.name = "Realization" + + return gen_kw_data diff --git a/res/enkf/export/misfit_collector.py b/res/enkf/export/misfit_collector.py new file mode 100644 index 00000000000..abb3e1a581a --- /dev/null +++ b/res/enkf/export/misfit_collector.py @@ -0,0 +1,56 @@ +from pandas import DataFrame +import numpy +from res.enkf import EnKFMain, EnkfFs, RealizationStateEnum +from res.enkf.key_manager import KeyManager +from ecl.util.util import BoolVector + + +class MisfitCollector(object): + @staticmethod + def createActiveList(ert, fs): + state_map = fs.getStateMap() + ens_mask = BoolVector(False, ert.getEnsembleSize()) + state_map.selectMatching(ens_mask, RealizationStateEnum.STATE_HAS_DATA) + active_list = BoolVector.createActiveList(ens_mask) + + return [iens for iens in active_list] + + @staticmethod + def getAllMisfitKeys(ert, sort_keys=True): + """@rtype: list of str""" + key_manager = KeyManager(ert) + return key_manager.misfitKeys(sort_keys=sort_keys) + + @staticmethod + def loadAllMisfitData(ert, case_name): + """ + @type ert: EnKFMain + @type case_name: str + @rtype: DataFrame + """ + fs = ert.getEnkfFsManager().getFileSystem(case_name) + + realizations = MisfitCollector.createActiveList(ert, fs) + misfit_keys = MisfitCollector.getAllMisfitKeys(ert, sort_keys=False) + misfit_sum_index = len(misfit_keys) - 1 + + misfit_array = numpy.empty( + shape=(len(misfit_keys), len(realizations)), dtype=numpy.float64 + ) + misfit_array.fill(numpy.nan) + misfit_array[misfit_sum_index] = 0.0 + + for column_index, obs_vector in enumerate(ert.getObservations()): + + for realization_index, realization_number in enumerate(realizations): + misfit = obs_vector.getTotalChi2(fs, realization_number) + + misfit_array[column_index][realization_index] = misfit + misfit_array[misfit_sum_index][realization_index] += misfit + + misfit_data = DataFrame( + data=numpy.transpose(misfit_array), index=realizations, columns=misfit_keys + ) + misfit_data.index.name = "Realization" + + return misfit_data diff --git a/res/enkf/export/summary_collector.py b/res/enkf/export/summary_collector.py new file mode 100644 index 00000000000..0063ea392ba --- /dev/null +++ b/res/enkf/export/summary_collector.py @@ -0,0 +1,73 @@ +from pandas import DataFrame, MultiIndex +import numpy +from res.enkf import ErtImplType, EnKFMain, EnkfFs, RealizationStateEnum +from res.enkf.key_manager import KeyManager +from res.enkf.plot_data import EnsemblePlotData +from ecl.util.util import BoolVector + + +class SummaryCollector(object): + @staticmethod + def createActiveList(ert, fs): + state_map = fs.getStateMap() + ens_mask = BoolVector(False, ert.getEnsembleSize()) + state_map.selectMatching(ens_mask, RealizationStateEnum.STATE_HAS_DATA) + active_list = BoolVector.createActiveList(ens_mask) + + return [iens for iens in active_list] + + @staticmethod + def getAllSummaryKeys(ert): + """@rtype: list of str""" + key_manager = KeyManager(ert) + return key_manager.summaryKeys() + + @staticmethod + def loadAllSummaryData(ert, case_name, keys=None): + """ + @type ert: EnKFMain + @type case_name: str + @type keys: list of str + @rtype: DataFrame + """ + fs = ert.getEnkfFsManager().getFileSystem(case_name) + + time_map = fs.getTimeMap() + dates = [time_map[index].datetime() for index in range(1, len(time_map))] + realizations = SummaryCollector.createActiveList(ert, fs) + + summary_keys = SummaryCollector.getAllSummaryKeys(ert) + if keys is not None: + summary_keys = [ + key for key in keys if key in summary_keys + ] # ignore keys that doesn't exist + + summary_array = numpy.empty( + shape=(len(summary_keys), len(realizations) * len(dates)), + dtype=numpy.float64, + ) + summary_array.fill(numpy.nan) + + for key_index, key in enumerate(summary_keys): + ensemble_config_node = ert.ensembleConfig().getNode(key) + ensemble_data = EnsemblePlotData(ensemble_config_node, fs) + summary_row = summary_array[key_index] + + for realization_index, realization_number in enumerate(realizations): + realization_vector = ensemble_data[realization_number] + column_index = realization_index * len(dates) + + for index in range(1, len(realization_vector)): + if realization_vector.isActive(index): + # assert time_map[index] == realization_vector.getTime(index) + # assert time_map[index].datetime() == dates[index - 1] + value = realization_vector.getValue(index) + summary_row[column_index + index - 1] = value + + multi_index = MultiIndex.from_product( + [realizations, dates], names=["Realization", "Date"] + ) + summary_data = DataFrame( + data=numpy.transpose(summary_array), index=multi_index, columns=summary_keys + ) + return summary_data diff --git a/res/enkf/export/summary_observation_collector.py b/res/enkf/export/summary_observation_collector.py new file mode 100644 index 00000000000..e47aea7e83c --- /dev/null +++ b/res/enkf/export/summary_observation_collector.py @@ -0,0 +1,63 @@ +from pandas import DataFrame, MultiIndex +import numpy +from res.enkf import ( + ErtImplType, + EnKFMain, + EnkfFs, + RealizationStateEnum, + EnkfObservationImplementationType, +) +from res.enkf.key_manager import KeyManager +from res.enkf.plot_data import EnsemblePlotData +from ecl.util.util import BoolVector + + +class SummaryObservationCollector(object): + @staticmethod + def getAllObservationKeys(ert): + """ + @type ert: EnKFMain + @rtype: list of str + """ + key_manager = KeyManager(ert) + return key_manager.summaryKeysWithObservations() + + @staticmethod + def loadObservationData(ert, case_name, keys=None): + """ + @type ert: EnKFMain + @type case_name: str + @type keys: list of str + @rtype: DataFrame + """ + observations = ert.getObservations() + history_length = ert.getHistoryLength() + dates = [ + observations.getObservationTime(index).datetime() + for index in range(1, history_length) + ] + summary_keys = SummaryObservationCollector.getAllObservationKeys(ert) + if keys is not None: + summary_keys = [ + key for key in keys if key in summary_keys + ] # ignore keys that doesn't exist + columns = summary_keys + std_columns = ["STD_%s" % key for key in summary_keys] + df = DataFrame(index=dates, columns=columns + std_columns) + for key in summary_keys: + observation_keys = ert.ensembleConfig().getNode(key).getObservationKeys() + for obs_key in observation_keys: + observation_data = observations[obs_key] + for index in range(0, history_length): + if observation_data.isActive(index): + obs_time = observations.getObservationTime(index).datetime() + node = observation_data.getNode(index) + value = node.getValue() + std = node.getStandardDeviation() + df[key][obs_time] = value + df["STD_%s" % key][obs_time] = std + return df + + @classmethod + def summaryKeyHasObservations(cls, ert, key): + return len(ert.ensembleConfig().getNode(key).getObservationKeys()) > 0 diff --git a/res/enkf/forward_load_context.py b/res/enkf/forward_load_context.py new file mode 100644 index 00000000000..70605c70185 --- /dev/null +++ b/res/enkf/forward_load_context.py @@ -0,0 +1,61 @@ +# Copyright (C) 2016 Equinor ASA, Norway. +# +# The file 'forward_load_context.py' 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. + +from res import ResPrototype +from cwrap import BaseCClass + + +# The Python wrapping of the forward_load_context is extremely +# minimal; when creating the Python implementation the only purpose +# was to get an existing test through. + + +class ForwardLoadContext(BaseCClass): + TYPE_NAME = "forward_load_context" + _alloc = ResPrototype( + "void* forward_load_context_alloc( run_arg , bool , ecl_config , char* , stringlist )", + bind=False, + ) + _select_step = ResPrototype( + "void forward_load_context_select_step( forward_load_context , int )" + ) + _get_step = ResPrototype( + "int forward_load_context_get_load_step( forward_load_context)" + ) + _free = ResPrototype("void forward_load_context_free( forward_load_context)") + + def __init__( + self, + run_arg=None, + load_summary=False, + ecl_config=None, + ecl_base=None, + messages=None, + report_step=None, + ): + c_ptr = self._alloc(run_arg, load_summary, ecl_config, ecl_base, messages) + super(ForwardLoadContext, self).__init__(c_ptr) + if not report_step is None: + self.selectStep(report_step) + + def getLoadStep(self): + return self._get_step() + + def selectStep(self, report_step): + self._select_step(report_step) + + def free(self): + self._free() diff --git a/res/enkf/hook_manager.py b/res/enkf/hook_manager.py new file mode 100644 index 00000000000..cc472e295f3 --- /dev/null +++ b/res/enkf/hook_manager.py @@ -0,0 +1,150 @@ +import os +import sys +import ctypes +from cwrap import BaseCClass +from res import ResPrototype +from res.enkf import ConfigKeys +from res.enkf.enums import HookRuntime + + +class HookManager(BaseCClass): + TYPE_NAME = "hook_manager" + + _alloc = ResPrototype( + "void* hook_manager_alloc(ert_workflow_list, config_content)", bind=False + ) + # hook_manager_alloc_full() has char** which cwrap is missing an implementation for + # so we let ctypes implicitly handle the two char** arguments and final int argument + _alloc_full = ResPrototype( + "void* hook_manager_alloc_full(ert_workflow_list, char*)", bind=False + ) + _free = ResPrototype("void hook_manager_free(subst_config)") + _get_runpath_list_file = ResPrototype( + "char* hook_manager_get_runpath_list_file(hook_manager)" + ) + _get_runpath_list = ResPrototype( + "runpath_list_ref hook_manager_get_runpath_list(hook_manager)" + ) + _iget_hook_workflow = ResPrototype( + "hook_workflow_ref hook_manager_iget_hook_workflow(hook_manager, int)" + ) + _size = ResPrototype("int hook_manager_get_size(hook_manager)") + + def __init__(self, workflow_list, config_content=None, config_dict=None): + if not ((config_content is not None) ^ (config_dict is not None)): + raise ValueError("HookManager expects one of config_content or config dict") + + if config_dict is not None: + config_dir = config_dict.get(ConfigKeys.CONFIG_DIRECTORY) + if not isinstance(config_dir, str): + raise ValueError( + "HookManager needs {} to be configured".format( + ConfigKeys.CONFIG_DIRECTORY + ) + ) + + # RUNPATH_FILE # + runpath_file_name = config_dict.get( + ConfigKeys.RUNPATH_FILE, ConfigKeys.RUNPATH_LIST_FILE + ) + runpath_file_path = os.path.normpath( + os.path.join(config_dir, runpath_file_name) + ) + + # HOOK_WORKFLOW + hook_workflow_names = [] + hook_workflow_run_modes = [] + if ConfigKeys.HOOK_WORKFLOW_KEY in config_dict: + for hook_workflow_name, run_mode_name in config_dict[ + ConfigKeys.HOOK_WORKFLOW_KEY + ]: + if run_mode_name not in [ + runtime.name for runtime in HookRuntime.enums() + ]: + raise ValueError( + "Run mode {} not supported".format(run_mode_name) + ) + hook_workflow_names.append(hook_workflow_name) + hook_workflow_run_modes.append(run_mode_name) + + c_ptr = self._alloc_full( + workflow_list, + runpath_file_path, + self._to_c_string_arr(hook_workflow_names), + self._to_c_string_arr(hook_workflow_run_modes), + len(hook_workflow_names), + ) + + else: + c_ptr = self._alloc(workflow_list, config_content) + + if c_ptr is None: + raise ValueError("Failed to construct RNGConfig instance") + + super(HookManager, self).__init__(c_ptr) + + def _to_c_string_arr(self, L): + arr = (ctypes.c_char_p * len(L))() + arr[:] = [s.encode("utf-8") for s in L] + return arr + + def __len__(self): + """@rtype: int""" + return self._size() + + def __repr__(self): + return "HookManager({})".format(", ".join([str(x) for x in self])) + + def __getitem__(self, index): + """@rtype: Hook workflow""" + assert isinstance(index, int) + if index < 0: + index += len(self) + if 0 <= index < len(self): + return self._iget_hook_workflow(index) + else: + raise IndexError("Invalid index. Valid range: [0, %d)." % len(self)) + + def getRunpathListFile(self): + return self._get_runpath_list_file() + + def checkRunpathListFile(self): + """@rtype: bool""" + runpath_list_file = self._get_runpath_list_file() + + if not os.path.exists(runpath_list_file): + sys.stderr.write( + "** Warning: the file: %s with a list of runpath directories was not found - hook workflow will probably fail.\n" + % runpath_list_file + ) + + def getRunpathList(self): + """@rtype: RunpathList""" + return self._get_runpath_list() + + def runWorkflows(self, run_time, ert_self): + + workflow_list = ert_self.getWorkflowList() + for hook_workflow in self: + + if hook_workflow.getRunMode() is not run_time: + continue + + workflow = hook_workflow.getWorkflow() + workflow.run(ert_self, context=workflow_list.getContext()) + + def __eq__(self, other): + if self.getRunpathListFile() != other.getRunpathListFile(): + return False + if len(self) != len(other): + return False + for val in self: + if val not in other: + return False + return True + + def __ne__(self, other): + return not self == other + + def free(self): + self._free() diff --git a/res/enkf/hook_workflow.py b/res/enkf/hook_workflow.py new file mode 100644 index 00000000000..069cdbf1022 --- /dev/null +++ b/res/enkf/hook_workflow.py @@ -0,0 +1,40 @@ +import os +import sys +from cwrap import BaseCClass +from res import ResPrototype +from res.enkf import RunpathList + + +class HookWorkflow(BaseCClass): + TYPE_NAME = "hook_workflow" + + _get_workflow = ResPrototype( + "workflow_ref hook_workflow_get_workflow(hook_workflow)" + ) + _get_runmode = ResPrototype( + "hook_runtime_enum hook_workflow_get_run_mode(hook_workflow)" + ) + + def __init__(self): + raise NotImplementedError("Class can not be instantiated directly!") + + def getWorkflow(self): + """@rtype: Workflow""" + return self._get_workflow() + + def getRunMode(self): + return self._get_runmode() + + def __eq__(self, other): + return ( + self.getRunMode() == other.getRunMode() + and self.getWorkflow().src_file == other.getWorkflow().src_file + ) + + def __ne__(self, other): + return not self == other + + def __repr__(self): + return "HookWorkflow({}, {})".format( + self.getWorkflow().src_file, self.getRunMode() + ) diff --git a/res/enkf/key_manager.py b/res/enkf/key_manager.py new file mode 100644 index 00000000000..b759f774954 --- /dev/null +++ b/res/enkf/key_manager.py @@ -0,0 +1,189 @@ +import weakref + +from res.enkf import ErtImplType, GenKwConfig +from res.enkf.enums import EnkfObservationImplementationType + + +class KeyManager(object): + def __init__(self, ert): + super(KeyManager, self).__init__() + """ + @type ert: res.enkf.EnKFMain + """ + self.__ert_ref = weakref.ref(ert) + + self.__all_keys = None + self.__all_keys_with_observations = None + self.__summary_keys = None + self.__summary_keys_with_observations = None + self.__gen_data_keys = None + self.__gen_data_keys_with_observations = None + self.__gen_kw_keys = None + self.__misfit_keys = None + + def _ert(self): + """:rtype: res.enkf.EnKFMain""" + ert = self.__ert_ref() + if ert is None: + raise RuntimeError("The reference EnKFMain instance has been deleted") + return ert + + def ensembleConfig(self): + """:rtype: res.enkf.EnsembleConfig""" + return self._ert().ensembleConfig() + + def summaryKeys(self): + """:rtype: list of str""" + if self.__summary_keys is None: + self.__summary_keys = sorted( + [ + key + for key in self.ensembleConfig().getKeylistFromImplType( + ErtImplType.SUMMARY + ) + ], + key=lambda k: k.lower(), + ) + + return self.__summary_keys + + def summaryKeysWithObservations(self): + """:rtype: list of str""" + if self.__summary_keys_with_observations is None: + self.__summary_keys_with_observations = sorted( + [ + key + for key in self.summaryKeys() + if len(self.ensembleConfig().getNode(key).getObservationKeys()) > 0 + ], + key=lambda k: k.lower(), + ) + + return self.__summary_keys_with_observations + + def genKwKeys(self): + """:rtype: list of str""" + if self.__gen_kw_keys is None: + gen_kw_keys = self.ensembleConfig().getKeylistFromImplType( + ErtImplType.GEN_KW + ) + gen_kw_keys = [key for key in gen_kw_keys] + + gen_kw_list = [] + for key in gen_kw_keys: + enkf_config_node = self.ensembleConfig().getNode(key) + gen_kw_config = enkf_config_node.getModelConfig() + assert isinstance(gen_kw_config, GenKwConfig) + + for keyword_index, keyword in enumerate(gen_kw_config): + gen_kw_list.append("%s:%s" % (key, keyword)) + + if gen_kw_config.shouldUseLogScale(keyword_index): + gen_kw_list.append("LOG10_%s:%s" % (key, keyword)) + + self.__gen_kw_keys = sorted(gen_kw_list, key=lambda k: k.lower()) + + return self.__gen_kw_keys + + def genDataKeys(self): + """:rtype: list of str""" + if self.__gen_data_keys is None: + gen_data_keys = self.ensembleConfig().getKeylistFromImplType( + ErtImplType.GEN_DATA + ) + gen_data_list = [] + for key in gen_data_keys: + enkf_config_node = self._ert().ensembleConfig().getNode(key) + gen_data_config = enkf_config_node.getDataModelConfig() + + for report_step in gen_data_config.getReportSteps(): + gen_data_list.append("%s@%d" % (key, report_step)) + + self.__gen_data_keys = sorted(gen_data_list, key=lambda k: k.lower()) + + return self.__gen_data_keys + + def genDataKeysWithObservations(self): + """:rtype: list of str""" + if self.__gen_data_keys_with_observations is None: + enkf_obs = self._ert().getObservations() + gen_data_obs_keys = [] + for obs_vector in enkf_obs: + if ( + obs_vector.getImplementationType() + is EnkfObservationImplementationType.GEN_OBS + ): + report_step = obs_vector.activeStep() + key = obs_vector.getDataKey() + + gen_data_key = "%s@%d" % (key, report_step) + if gen_data_key in self.genDataKeys(): + gen_data_obs_keys.append(gen_data_key) + + self.__gen_data_keys_with_observations = gen_data_obs_keys + + return self.__gen_data_keys_with_observations + + def misfitKeys(self, sort_keys=True): + """@rtype: list of str""" + if self.__misfit_keys is None: + keys = [] + for obs_vector in self._ert().getObservations(): + key = "MISFIT:%s" % obs_vector.getObservationKey() + keys.append(key) + + keys.append("MISFIT:TOTAL") + + self.__misfit_keys = ( + sorted(keys, key=lambda k: k.lower()) if sort_keys else keys + ) + + return self.__misfit_keys + + def allDataTypeKeys(self): + """:rtype: list of str""" + if self.__all_keys is None: + self.__all_keys = self.summaryKeys() + self.genKwKeys() + self.genDataKeys() + + return self.__all_keys + + def allDataTypeKeysWithObservations(self): + """:rtype: list of str""" + if self.__all_keys_with_observations is None: + self.__all_keys_with_observations = ( + self.summaryKeysWithObservations() + self.genDataKeysWithObservations() + ) + + return self.__all_keys_with_observations + + def isKeyWithObservations(self, key): + """:rtype: bool""" + return key in self.allDataTypeKeysWithObservations() + + def isSummaryKey(self, key): + """:rtype: bool""" + return key in self.summaryKeys() + + def isGenKwKey(self, key): + """:rtype: bool""" + return key in self.genKwKeys() + + def isGenDataKey(self, key): + """:rtype: bool""" + return key in self.genDataKeys() + + def isMisfitKey(self, key): + """:rtype: bool""" + return key in self.misfitKeys() + + def gen_kw_priors(self): + gen_kw_keys = self.ensembleConfig().getKeylistFromImplType(ErtImplType.GEN_KW) + gen_kw_keys = [key for key in gen_kw_keys] + + all_gen_kw_priors = {} + for key in gen_kw_keys: + enkf_config_node = self.ensembleConfig().getNode(key) + gen_kw_config = enkf_config_node.getModelConfig() + all_gen_kw_priors[key] = gen_kw_config.get_priors() + + return all_gen_kw_priors diff --git a/res/enkf/local_config.py b/res/enkf/local_config.py new file mode 100644 index 00000000000..137569ab53d --- /dev/null +++ b/res/enkf/local_config.py @@ -0,0 +1,191 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'local_config.py' 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. + +from cwrap import BaseCClass +from res import ResPrototype +from res.enkf import LocalUpdateStep +from res.enkf.local_ministep import LocalMinistep +from res.analysis import AnalysisModule + + +class LocalConfig(BaseCClass): + """The LocalConfig class is created as a reference to an existing underlying C + structure by the method EnkFMain.local_config(). When the pointer to the C + local_config_type object has been properly wrapped we 'decorate' the Python + object with references to the ensemble_config , observations and grid. + + This implies that the Python object LocalConfig is richer than the + underlying C object local_config_type; the extra attributes are only used + for validation. + + """ + + TYPE_NAME = "local_config" + + _free = ResPrototype("void local_config_free(local_config)") + _clear = ResPrototype("void local_config_clear(local_config)") + _clear_active = ResPrototype("void local_config_clear_active(local_config)") + _create_ministep = ResPrototype( + "local_ministep_ref local_config_alloc_ministep(local_config, char*, analysis_module)" + ) + _attach_ministep = ResPrototype( + "void local_updatestep_add_ministep(local_updatestep, local_ministep)", + bind=False, + ) + _create_obsdata = ResPrototype( + "void local_config_alloc_obsdata(local_config, char*)" + ) + _create_dataset = ResPrototype( + "void local_config_alloc_dataset(local_config, char*)" + ) + _has_obsdata = ResPrototype("bool local_config_has_obsdata(local_config, char*)") + _has_dataset = ResPrototype("bool local_config_has_dataset(local_config, char*)") + + _get_updatestep = ResPrototype( + "local_updatestep_ref local_config_get_updatestep(local_config)" + ) + _get_ministep = ResPrototype( + "local_ministep_ref local_config_get_ministep(local_config, char*)" + ) + _get_obsdata = ResPrototype( + "local_obsdata_ref local_config_get_obsdata(local_config, char*)" + ) + _copy_obsdata = ResPrototype( + "local_obsdata_ref local_config_alloc_obsdata_copy(local_config, char*, char*)" + ) + _get_dataset = ResPrototype( + "local_dataset_ref local_config_get_dataset(local_config, char*)" + ) + _copy_dataset = ResPrototype( + "local_dataset_ref local_config_alloc_dataset_copy(local_config, char*, char*)" + ) + _smry_fprintf = ResPrototype( + "void local_config_summary_fprintf(local_config, char*)" + ) + + def __init__(self): + raise NotImplementedError("Class can not be instantiated directly!") + + def initAttributes(self, ensemble_config, obs, grid): + self.ensemble_config = ensemble_config + self.obs = obs + self.grid = grid + + def __getObservations(self): + return self.obs + + def __getEnsembleConfig(self): + return self.ensemble_config + + def getGrid(self): + # The grid can be None + return self.grid + + def free(self): + self._free() + + def clear(self): + self._clear() + + def clear_active(self): + self._clear_active() + + def createMinistep(self, mini_step_key, analysis_module=None): + """@rtype: Ministep""" + assert isinstance(mini_step_key, str) + if analysis_module: + assert isinstance(analysis_module, AnalysisModule) + ministep = self._create_ministep(mini_step_key, analysis_module) + if ministep is None: + raise KeyError("Ministep: {} already exists".format(mini_step_key)) + return ministep + + def createObsdata(self, obsdata_key): + """@rtype: Obsdata""" + assert isinstance(obsdata_key, str) + if self._has_obsdata(obsdata_key): + raise ValueError("Tried to add existing observation key:%s " % obsdata_key) + + self._create_obsdata(obsdata_key) + obsdata = self.getObsdata(obsdata_key) + obsdata.initObservations(self.__getObservations()) + return obsdata + + def copyObsdata(self, src_key, target_key): + """@rtype: Obsdata""" + assert isinstance(src_key, str) + assert isinstance(target_key, str) + if not self._has_obsdata(src_key): + raise KeyError(f"The observation set {src_key} does not exist") + + obsdata = self._copy_obsdata(src_key, target_key) + obsdata.initObservations(self.__getObservations()) + return obsdata + + def createDataset(self, dataset_key): + """@rtype: Dataset""" + assert isinstance(dataset_key, str) + if self._has_dataset(dataset_key): + raise ValueError("Tried to add existing data key:%s " % dataset_key) + + self._create_dataset(dataset_key) + data = self.getDataset(dataset_key) + data.initEnsembleConfig(self.__getEnsembleConfig()) + return data + + def copyDataset(self, src_key, target_key): + """@rtype: Dataset""" + assert isinstance(src_key, str) + assert isinstance(target_key, str) + data = self._copy_dataset(src_key, target_key) + data.initEnsembleConfig(self.__getEnsembleConfig()) + return data + + def getUpdatestep(self): + """@rtype: UpdateStep""" + return self._get_updatestep() + + def getMinistep(self, mini_step_key): + """@rtype: Ministep""" + assert isinstance(mini_step_key, str) + return self._get_ministep(mini_step_key) + + def getObsdata(self, obsdata_key): + """@rtype: Obsdata""" + assert isinstance(obsdata_key, str) + return self._get_obsdata(obsdata_key) + + def getDataset(self, dataset_key): + """@rtype: Dataset""" + assert isinstance(dataset_key, str) + return self._get_dataset(dataset_key) + + def attachMinistep(self, update_step, mini_step): + assert isinstance(mini_step, LocalMinistep) + assert isinstance(update_step, LocalUpdateStep) + self._attach_ministep(update_step, mini_step) + + def writeSummaryFile(self, filename): + """ + Writes a summary of the local config object + The summary contains the Obsset with their respective + number of observations and the Datasets with the number of active indices + """ + assert isinstance(filename, str) + self._smry_fprintf(filename) + + def __repr__(self): + return self._create_repr() diff --git a/res/enkf/local_dataset.py b/res/enkf/local_dataset.py new file mode 100644 index 00000000000..87195caa5f2 --- /dev/null +++ b/res/enkf/local_dataset.py @@ -0,0 +1,108 @@ +from cwrap import BaseCClass +from res import ResPrototype +from ecl.grid import EclRegion +from ecl.util.geometry import GeoRegion +from ecl.util.util import StringList + + +class LocalDataset(BaseCClass): + TYPE_NAME = "local_dataset" + + _alloc = ResPrototype("void* local_dataset_alloc(char*)", bind=False) + _size = ResPrototype("int local_dataset_get_size(local_dataset)") + _has_key = ResPrototype("bool local_dataset_has_key(local_dataset, char*)") + _keys = ResPrototype("stringlist_obj local_dataset_alloc_keys(local_dataset)") + _free = ResPrototype("void local_dataset_free(local_dataset)") + _name = ResPrototype("char* local_dataset_get_name(local_dataset)") + _add_node = ResPrototype("void local_dataset_add_node(local_dataset, char*)") + _del_node = ResPrototype("void local_dataset_del_node(local_dataset, char*)") + _get_or_create_row_scaling = ResPrototype( + "row_scaling_ref local_dataset_get_or_create_row_scaling(local_dataset, char*)" + ) + _active_list = ResPrototype( + "active_list_ref local_dataset_get_node_active_list(local_dataset, char*)" + ) + + def __init__(self, name): + raise NotImplementedError("Class can not be instantiated directly!") + + def initEnsembleConfig(self, config): + self.ensemble_config = config + + def __len__(self): + """@rtype: int""" + return self._size() + + def __contains__(self, key): + """@rtype: bool""" + return self._has_key(key) + + def __delitem__(self, key): + assert isinstance(key, str) + if key in self: + self._del_node(key) + else: + raise KeyError('Unknown key "%s"' % key) + + def row_scaling(self, key): + if key not in self: + raise KeyError(f"Unknown key: {key}") + + return self._get_or_create_row_scaling(key) + + def name(self): + return self._name() + + def keys(self): + return self._keys() + + def getName(self): + """@rtype: str""" + return self.name() + + def addNode(self, key): + assert isinstance(key, str) + if key in self.ensemble_config: + if not self._has_key(key): + self._add_node(key) + else: + raise KeyError('Tried to add existing data key "%s".' % key) + else: + raise KeyError('Tried to add data key "%s" not in ensemble.' % key) + + def addNodeWithIndex(self, key, index): + assert isinstance(key, str) + assert isinstance(index, int) + + self.addNode(key) + active_list = self.getActiveList(key) + active_list.addActiveIndex(index) + + def addRegion(self, key, region): + assert isinstance(key, str) + self.addNode(key) + active_list = self.getActiveList(key) + active_region = region.getActiveList() + for i in active_region: + active_list.addActiveIndex(i) + + def addField(self, key, ecl_region): + assert isinstance(ecl_region, EclRegion) + self.addRegion(str(key), ecl_region) + + def addSurface(self, key, geo_region): + assert isinstance(geo_region, GeoRegion) + self.addRegion(str(key), geo_region) + + def getActiveList(self, key): + """@rtype: ActiveList""" + if key in self: + return self._active_list(key) + else: + raise KeyError('Local key "%s" not recognized.' % key) + + def free(self): + self._free() + + def __repr__(self): + return self._create_repr("name=%s, size=%d" % (self.name(), len(self))) diff --git a/res/enkf/local_ministep.py b/res/enkf/local_ministep.py new file mode 100644 index 00000000000..ea91030b054 --- /dev/null +++ b/res/enkf/local_ministep.py @@ -0,0 +1,89 @@ +import ecl.util +from cwrap import BaseCClass +from res import ResPrototype +from res.enkf import LocalObsdata, LocalObsdataNode, LocalDataset, ObsData + + +class LocalMinistep(BaseCClass): + TYPE_NAME = "local_ministep" + + _alloc = ResPrototype("void* local_ministep_alloc(char*)", bind=False) + _add_node = ResPrototype( + "void local_ministep_add_obsdata_node(local_ministep, local_obsdata_node)" + ) + _get_local_obs_data = ResPrototype( + "local_obsdata_ref local_ministep_get_obsdata(local_ministep)" + ) + _get_local_data = ResPrototype( + "local_dataset_ref local_ministep_get_dataset(local_ministep , char*)" + ) + _get_obs_data = ResPrototype( + "obs_data_ref local_ministep_get_obs_data( local_ministep )" + ) + _has_local_data = ResPrototype( + "bool local_ministep_has_dataset(local_ministep , char*)" + ) + _free = ResPrototype("void local_ministep_free(local_ministep)") + _attach_obsdata = ResPrototype( + "void local_ministep_add_obsdata(local_ministep, local_obsdata)" + ) + _attach_dataset = ResPrototype( + "void local_ministep_add_dataset(local_ministep, local_dataset)" + ) + _name = ResPrototype("char* local_ministep_get_name(local_ministep)") + _data_size = ResPrototype("int local_ministep_get_num_dataset(local_ministep)") + + def __init__(self, ministep_key): + raise NotImplementedError("Class can not be instantiated directly!") + + # Will used the data keys; and ignore observation keys. + def __getitem__(self, data_key): + if isinstance(data_key, int): + raise TypeError("Keys must be strings, not int!") + if data_key in self: + return self._get_local_data(data_key) + else: + raise KeyError('No such data key: "%s"' % data_key) + + def __len__(self): + return self._data_size() + + def __contains__(self, data_key): + return self._has_local_data(data_key) + + def addNode(self, node): + assert isinstance(node, LocalObsdataNode) + self._add_node(node) + + def attachObsset(self, obs_set): + assert isinstance(obs_set, LocalObsdata) + self._attach_obsdata(obs_set) + + def attachDataset(self, dataset): + assert isinstance(dataset, LocalDataset) + self._attach_dataset(dataset) + + def getLocalObsData(self): + """@rtype: LocalObsdata""" + return self._get_local_obs_data() + + def name(self): + return self._name() + + def getName(self): + """@rtype: str""" + return self.name() + + def get_obs_data(self): + """@rtype: ObsData""" + return self._get_obs_data() + + def free(self): + self._free() + + def __repr__(self): + return "LocalMinistep(name = %s, len = %d) at 0x%x" % ( + self.name(), + len(self), + self._address(), + ) diff --git a/res/enkf/local_obsdata.py b/res/enkf/local_obsdata.py new file mode 100644 index 00000000000..c377acba084 --- /dev/null +++ b/res/enkf/local_obsdata.py @@ -0,0 +1,171 @@ +from cwrap import BaseCClass +from res import ResPrototype +from res.enkf import LocalObsdataNode + + +class LocalObsdata(BaseCClass): + TYPE_NAME = "local_obsdata" + + _alloc = ResPrototype("void* local_obsdata_alloc(char*)", bind=False) + _free = ResPrototype("void local_obsdata_free(local_obsdata)") + _size = ResPrototype("int local_obsdata_get_size(local_obsdata)") + _has_node = ResPrototype("bool local_obsdata_has_node(local_obsdata, char*)") + _add_node = ResPrototype( + "bool local_obsdata_add_node(local_obsdata, local_obsdata_node)" + ) + _del_node = ResPrototype("void local_obsdata_del_node(local_obsdata, char*)") + _clear = ResPrototype("void local_dataset_clear(local_obsdata)") + _name = ResPrototype("char* local_obsdata_get_name(local_obsdata)") + _iget_node = ResPrototype( + "local_obsdata_node_ref local_obsdata_iget(local_obsdata, int)" + ) + _get_node = ResPrototype( + "local_obsdata_node_ref local_obsdata_get(local_obsdata, char*)" + ) + _copy_active_list = ResPrototype( + "active_list_ref local_obsdata_get_copy_node_active_list(local_obsdata, char*)" + ) + _active_list = ResPrototype( + "active_list_ref local_obsdata_get_node_active_list(local_obsdata, char*)" + ) + + def __init__(self, name, obs=None): + # The obs instance should be a EnkFObs instance; some circular dependency problems + # by importing it right away. It is not really optional, but it is made optional + # here to be able to give a decent error message for old call sites which did not + # supply the obs argument. + if obs is None: + msg = """ + +The LocalObsdata constructor has recently changed, as a second +argument you should pass the EnkFObs instance with all the +observations. You can typically get this instance from the ert main +object as: + + obs = ert.getObservations() + local_obs = LocalObsData("YOUR-KEY" , obs) + +""" + raise Exception(msg) + + assert isinstance(name, str) + + c_ptr = self._alloc(name) + if c_ptr: + super(LocalObsdata, self).__init__(c_ptr) + self.initObservations(obs) + else: + raise ValueError( + 'Unable to construct LocalObsdata with name "%s" from given obs.' % name + ) + + def initObservations(self, obs): + self.obs = obs + + def __len__(self): + """@rtype: int""" + return self._size() + + def __getitem__(self, key): + """@rtype: LocalObsdataNode""" + if isinstance(key, int): + if key < 0: + key += len(self) + if 0 <= key < len(self): + node_ = self._iget_node(key) + node_.setParent(self) + return node_ + else: + raise IndexError("Invalid index, valid range is [0, %d)" % len(self)) + else: + if key in self: + node_ = self._get_node(key) + node_.setParent(self) + return node_ + else: + raise KeyError('Unknown key "%s".' % key) + + def __iter__(self): + cur = 0 + while cur < len(self): + yield self[cur] + cur += 1 + + def __contains__(self, item): + """@rtype: bool""" + if isinstance(item, str): + return self._has_node(item) + elif isinstance(item, LocalObsdataNode): + return self._has_node(item.getKey()) + + return False + + def __delitem__(self, key): + assert isinstance(key, str) + if key in self: + self._del_node(key) + else: + raise KeyError('Unknown key "%s".' % key) + + def addNode(self, key, add_all_timesteps=True): + """@rtype: LocalObsdataNode""" + assert isinstance(key, str) + if key in self.obs: + node = LocalObsdataNode(key, add_all_timesteps) + if node not in self: + node.convertToCReference(self) + self._add_node(node) + return node + else: + raise KeyError("Tried to add existing observation key:%s " % key) + else: + raise KeyError( + "The observation node: %s is not recognized observation key" % key + ) + + def addNodeAndRange(self, key, step_1, step_2): + """@rtype: LocalObsdataNode""" + """ The time range will be removed in the future... """ + assert isinstance(key, str) + assert isinstance(step_1, int) + assert isinstance(step_2, int) + node = self.addNode(key) + node.addRange(step_1, step_2) + return node + + def clear(self): + self._clear() + + def addObsVector(self, obs_vector): + self.addNode(obs_vector.getObservationKey()) + + def name(self): + return self._name() + + def getName(self): + """@rtype: str""" + return self.name() + + def getActiveList(self, key): + """@rtype: ActiveList""" + if key in self: + return self._active_list(key) + else: + raise KeyError('Local key "%s" not recognized.' % key) + + def copy_active_list(self, key): + """@rtype: ActiveList""" + if key in self: + return self._copy_active_list(key) + else: + raise KeyError('Local key "%s" not recognized.' % key) + + def free(self): + self._free() + + def __repr__(self): + return "LocalObsdata(len = %d, name = %s) at 0x%x" % ( + len(self), + self.name(), + self._address(), + ) diff --git a/res/enkf/local_obsdata_node.py b/res/enkf/local_obsdata_node.py new file mode 100644 index 00000000000..add83f35689 --- /dev/null +++ b/res/enkf/local_obsdata_node.py @@ -0,0 +1,74 @@ +from cwrap import BaseCClass +from res import ResPrototype + + +class LocalObsdataNode(BaseCClass): + TYPE_NAME = "local_obsdata_node" + + _alloc = ResPrototype("void* local_obsdata_node_alloc(char* , bool)", bind=False) + _free = ResPrototype("void local_obsdata_node_free(local_obsdata_node)") + _get_key = ResPrototype("char* local_obsdata_node_get_key(local_obsdata_node)") + _add_range = ResPrototype( + "void local_obsdata_node_add_range(local_obsdata_node, int, int)" + ) + _add_step = ResPrototype( + "void local_obsdata_node_add_tstep(local_obsdata_node, int)" + ) + _tstep_active = ResPrototype( + "bool local_obsdata_node_tstep_active(local_obsdata_node, int)" + ) + _all_timestep_active = ResPrototype( + "bool local_obsdata_node_all_timestep_active(local_obsdata_node)" + ) + _set_all_timestep_active = ResPrototype( + "void local_obsdata_node_set_all_timestep_active(local_obsdata_node, bool)" + ) + _get_active_list = ResPrototype( + "active_list_ref local_obsdata_node_get_active_list(local_obsdata_node)" + ) + + def __init__(self, obs_key, all_timestep_active=True): + if isinstance(obs_key, str): + c_ptr = self._alloc(obs_key, all_timestep_active) + if c_ptr: + super(LocalObsdataNode, self).__init__(c_ptr) + else: + raise ArgumentError( + 'Unable to construct LocalObsdataNode with key = "%s".' % obs_key + ) + else: + raise TypeError( + "LocalObsdataNode needs string, not %s." % str(type(obs_key)) + ) + + def key(self): + return self._get_key() + + def getKey(self): + return self.key() + + def addRange(self, step_1, step_2): + assert isinstance(step_1, int) + assert isinstance(step_2, int) + self._add_range(step_1, step_2) + + def addTimeStep(self, step): + self._add_step(step) + + def free(self): + self._free() + + def __repr__(self): + return "LocalObsdataNode(key = %s) %s" % (self.key(), self._ad_str()) + + def tstepActive(self, tstep): + return self._tstep_active(tstep) + + def getActiveList(self): + return self._get_active_list() + + def allTimeStepActive(self): + return self._all_timestep_active() + + def setAllTimeStepActive(self, flag): + return self._set_all_timestep_active(flag) diff --git a/res/enkf/local_updatestep.py b/res/enkf/local_updatestep.py new file mode 100644 index 00000000000..1bd6ec09dfb --- /dev/null +++ b/res/enkf/local_updatestep.py @@ -0,0 +1,50 @@ +from cwrap import BaseCClass +from res import ResPrototype +from res.enkf import LocalMinistep + + +class LocalUpdateStep(BaseCClass): + TYPE_NAME = "local_updatestep" + + _alloc = ResPrototype("void local_updatestep_alloc(char*)", bind=False) + _size = ResPrototype("int local_updatestep_get_num_ministep(local_updatestep)") + _iget_ministep = ResPrototype( + "local_ministep_ref local_updatestep_iget_ministep(local_updatestep, int)" + ) + _free = ResPrototype("void local_updatestep_free(local_updatestep)") + _attach_ministep = ResPrototype( + "void local_updatestep_add_ministep(local_updatestep, local_ministep)" + ) + _name = ResPrototype("char* local_updatestep_get_name(local_updatestep)") + + def __init__(self, updatestep_key): + raise NotImplementedError("Class can not be instantiated directly!") + + def __len__(self): + """@rtype: int""" + return self._size() + + def __getitem__(self, index): + """@rtype: LocalMinistep""" + if not isinstance(index, int): + raise TypeError("Keys must be ints, not %s" % str(type(index))) + if index < 0: + index += len(self) + if 0 <= index < len(self): + return self._iget_ministep(index) + else: + raise IndexError("Invalid index, valid range: [0, %d)" % len(self)) + + def attachMinistep(self, ministep): + assert isinstance(ministep, LocalMinistep) + self._attach_ministep(ministep) + + def name(self): + return self._name() + + def getName(self): + """@rtype: str""" + return self.name() + + def free(self): + self._free(self) diff --git a/res/enkf/log_config.py b/res/enkf/log_config.py new file mode 100644 index 00000000000..49909606687 --- /dev/null +++ b/res/enkf/log_config.py @@ -0,0 +1,102 @@ +# Copyright (C) 2017 Equinor ASA, Norway. +# +# The file 'log_config.py' 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. + +from os.path import isfile, realpath + +from cwrap import BaseCClass + +from res.util.enums import MessageLevelEnum + +from res import ResPrototype +from res.enkf import ConfigKeys + + +class LogConfig(BaseCClass): + TYPE_NAME = "log_config" + + _alloc = ResPrototype("void* log_config_alloc(config_content)", bind=False) + _alloc_load = ResPrototype("void* log_config_alloc_load(char*)", bind=False) + _alloc_full = ResPrototype( + "void* log_config_alloc_full(char*, message_level_enum)", bind=False + ) + _free = ResPrototype("void log_config_free(log_config)") + _log_file = ResPrototype("char* log_config_get_log_file(log_config)") + _log_level = ResPrototype("message_level_enum log_config_get_log_level(log_config)") + + def __init__(self, user_config_file=None, config_content=None, config_dict=None): + configs = sum( + [ + 1 + for x in [user_config_file, config_content, config_dict] + if x is not None + ] + ) + + if configs > 1: + raise IOError( + "Attempting to construct LogConfig with multiple config objects" + ) + + if configs == 0: + raise IOError("Attempting to construct LogConfig with no config objects") + + c_ptr = None + if user_config_file: + if not isfile(user_config_file): + raise IOError('No such configuration file "%s".' % user_config_file) + c_ptr = self._alloc_load(user_config_file) + + if config_content: + c_ptr = self._alloc(config_content) + + if config_dict is not None: + + if ConfigKeys.LOG_FILE in config_dict: + log_file = realpath(config_dict[ConfigKeys.LOG_FILE]) + else: + raise ValueError("No log file provided") + + if ConfigKeys.LOG_LEVEL in config_dict: + message_level = config_dict[ConfigKeys.LOG_LEVEL] + else: + raise ValueError("No log level provided") + + c_ptr = self._alloc_full(log_file, message_level) + + if c_ptr is None: + raise ValueError("Failed to construct LogConfig instance") + super(LogConfig, self).__init__(c_ptr) + + def __repr__(self): + return "LogConfig(log_file=%s, log_level=%r)" % (self.log_file, self.log_level) + + def free(self): + self._free() + + @property + def log_file(self): + return self._log_file() + + @property + def log_level(self): + return self._log_level() + + def __eq__(self, other): + if self.log_file != other.log_file: + return False + if self.log_level != other.log_level: + return False + return True diff --git a/res/enkf/meas_block.py b/res/enkf/meas_block.py new file mode 100644 index 00000000000..28c16aab3c3 --- /dev/null +++ b/res/enkf/meas_block.py @@ -0,0 +1,117 @@ +from cwrap import BaseCClass +from res import ResPrototype +from res.enkf.obs_data import ObsData +from ecl.util.util import IntVector, BoolVector +from res.util import Matrix + + +class MeasBlock(BaseCClass): + TYPE_NAME = "meas_block" + + _alloc = ResPrototype( + "void* meas_block_alloc( char* , bool_vector , int)", bind=False + ) + _free = ResPrototype("void meas_block_free( meas_block )") + _get_active_ens_size = ResPrototype( + "int meas_block_get_active_ens_size( meas_block )" + ) + _get_total_ens_size = ResPrototype( + "int meas_block_get_total_ens_size( meas_block )" + ) + _get_total_obs_size = ResPrototype( + "int meas_block_get_total_obs_size( meas_block )" + ) + _iget_value = ResPrototype("double meas_block_iget( meas_block , int , int)") + _iset_value = ResPrototype( + "void meas_block_iset( meas_block , int , int , double)" + ) + _iget_mean = ResPrototype("double meas_block_iget_ens_mean( meas_block , int )") + _iget_std = ResPrototype("double meas_block_iget_ens_std( meas_block , int )") + _iens_active = ResPrototype("bool meas_block_iens_active( meas_block , int )") + + def __init__(self, obs_key, obs_size, ens_mask): + assert isinstance(ens_mask, BoolVector) + c_ptr = self._alloc(obs_key, ens_mask, obs_size) + if c_ptr: + super(MeasBlock, self).__init__(c_ptr) + else: + raise ValueError("Unable to construct MeasBlock.") + + def __str__(self): + s = "" + for iobs in range(self.getObsSize()): + s += "[" + for iens in range(self.getTotalEnsSize()): + if self.iensActive(iens): + s += "%6.3g " % self[iobs, iens] + else: + s += " X " + + s += "]\n" + return s + + def getObsSize(self): + return self._get_total_obs_size() + + def getActiveEnsSize(self): + return self._get_active_ens_size() + + def getTotalEnsSize(self): + return self._get_total_ens_size() + + def __assert_index(self, index): + if isinstance(index, tuple): + iobs, iens = index + if not 0 <= iobs < self.getObsSize(): + raise IndexError( + "Invalid iobs value:%d Valid range: [0,%d)" + % (iobs, self.getObsSize()) + ) + + if not 0 <= iens < self.getTotalEnsSize(): + raise IndexError( + "Invalid iens value:%d Valid range: [0,%d)" + % (iobs, self.getTotalEnsSize()) + ) + + if not self.iensActive(iens): + raise ValueError( + "Ensemble member:%d is not active - can not be accessed in the MeasBlock()" + % iens + ) + + return iobs, iens + else: + raise TypeError("The index argument must be 2-tuple") + + def __setitem__(self, index, value): + iobs, iens = self.__assert_index(index) + self._iset_value(iens, iobs, value) + + def __getitem__(self, index): + iobs, iens = self.__assert_index(index) + return self._iget_value(iens, iobs) + + def iensActive(self, iens): + return self._iens_active(iens) + + def free(self): + self._free() + + def igetMean(self, iobs): + if 0 <= iobs < self.getObsSize(): + return self._iget_mean(iobs) + else: + raise IndexError( + "Invalid observation index:%d valid range: [0,%d)" + % (iobs, self.getObsSize()) + ) + + def igetStd(self, iobs): + if 0 <= iobs < self.getObsSize(): + return self._iget_std(iobs) + else: + raise IndexError( + "Invalid observation index:%d valid range: [0,%d)" + % (iobs, self.getObsSize()) + ) diff --git a/res/enkf/meas_data.py b/res/enkf/meas_data.py new file mode 100644 index 00000000000..6e4de96fbe3 --- /dev/null +++ b/res/enkf/meas_data.py @@ -0,0 +1,117 @@ +from cwrap import BaseCClass +from res import ResPrototype +from res.enkf.obs_data import ObsData +from ecl.util.util import IntVector +from res.util import Matrix + + +class MeasData(BaseCClass): + TYPE_NAME = "meas_data" + + _alloc = ResPrototype("void* meas_data_alloc(bool_vector)", bind=False) + _free = ResPrototype("void meas_data_free(meas_data)") + _get_active_obs_size = ResPrototype( + "int meas_data_get_active_obs_size(meas_data)" + ) + _get_active_ens_size = ResPrototype( + "int meas_data_get_active_ens_size( meas_data )" + ) + _get_total_ens_size = ResPrototype( + "int meas_data_get_total_ens_size( meas_data )" + ) + _get_num_blocks = ResPrototype("int meas_data_get_num_blocks( meas_data )") + _has_block = ResPrototype("bool meas_data_has_block( meas_data , char* )") + _get_block = ResPrototype("meas_block_ref meas_data_get_block( meas_data , char*)") + _allocS = ResPrototype("matrix_obj meas_data_allocS(meas_data)") + _add_block = ResPrototype( + "meas_block_ref meas_data_add_block(meas_data, char* , int , int)" + ) + _iget_block = ResPrototype("meas_block_ref meas_data_iget_block( meas_data , int)") + + _deactivate_outliers = ResPrototype( + "void enkf_analysis_deactivate_std_zero(obs_data, meas_data)", bind=False + ) + + def __init__(self, ens_mask): + c_ptr = self._alloc(ens_mask) + if c_ptr: + super(MeasData, self).__init__(c_ptr) + else: + raise ValueError( + "Unable to construct MeasData from given ensemble mask of type %s." + % str(type(ens_mask)) + ) + + def __len__(self): + return self._get_num_blocks() + + def __contains__(self, index): + if isinstance(index, str): + return self._has_block(index) + else: + raise TypeError( + 'The in operator expects a string argument, got "%s".' % str(index) + ) + + def __getitem__(self, index): + if isinstance(index, str): + if index in self: + return self._get_block(index) + else: + raise KeyError('The obs block "%s" is not recognized' % index) + elif isinstance(index, int): + if index < 0: + index += len(self) + + if 0 <= index < len(self): + return self._iget_block(index) + else: + raise IndexError( + "Index out of range, should have 0 <= %d < %d." % (index, len(self)) + ) + else: + raise TypeError("The index variable must string or integer") + + def __repr__(self): + fmt = "len = %d, total ens = %d, active obs = %d, active ens = %d" + return self._create_repr( + fmt + % ( + len(self), + self.getTotalEnsSize(), + self.activeObsSize(), + self.getActiveEnsSize(), + ) + ) + + def __str__(self): + return "\n".join([str(block) for block in self]) + + def createS(self): + """@rtype: Matrix""" + S = self._allocS() + if S is None: + raise ValueError( + "Failed to create S active size : [%d,%d]" + % (self.getActiveEnsSize(), self.activeObsSize()) + ) + return S + + def deactivateZeroStdSamples(self, obs_data): + assert isinstance(obs_data, ObsData) + self._deactivate_outliers(obs_data, self) + + def addBlock(self, obs_key, report_step, obs_size): + return self._add_block(obs_key, report_step, obs_size) + + def activeObsSize(self): + return self._get_active_obs_size() + + def getActiveEnsSize(self): + return self._get_active_ens_size() + + def getTotalEnsSize(self): + return self._get_total_ens_size() + + def free(self): + self._free() diff --git a/res/enkf/model_config.py b/res/enkf/model_config.py new file mode 100644 index 00000000000..1cb9e7b96da --- /dev/null +++ b/res/enkf/model_config.py @@ -0,0 +1,355 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'model_config.py' 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. +from cwrap import BaseCClass + +from ecl.summary import EclSum +from ecl.util.util import StringList +from res import ResPrototype +from res.job_queue import ForwardModel, ExtJob, ExtJoblist +from res.sched import HistorySourceEnum +from res.util import PathFormat +from res.enkf import ConfigKeys +from res.enkf.util import TimeMap + +import os + + +class ModelConfig(BaseCClass): + TYPE_NAME = "model_config" + + _alloc = ResPrototype( + "void* model_config_alloc( config_content, \ + char*, ext_joblist, \ + int, ecl_sum)", + bind=False, + ) + _alloc_full = ResPrototype( + "void* model_config_alloc_full( int, \ + int, \ + char*, \ + char*, \ + char*, \ + char*, \ + forward_model, \ + char*, \ + time_map, \ + char*, \ + char*, \ + history_source_enum, \ + ext_joblist, \ + ecl_sum)", + bind=False, + ) + _free = ResPrototype("void model_config_free( model_config )") + _get_forward_model = ResPrototype( + "forward_model_ref model_config_get_forward_model(model_config)" + ) + _get_max_internal_submit = ResPrototype( + "int model_config_get_max_internal_submit(model_config)" + ) + _set_max_internal_submit = ResPrototype( + "void model_config_set_max_internal_submit(model_config, int)" + ) + _get_runpath_as_char = ResPrototype( + "char* model_config_get_runpath_as_char(model_config)" + ) + _select_runpath = ResPrototype( + "bool model_config_select_runpath(model_config, char*)" + ) + _set_runpath = ResPrototype("void model_config_set_runpath(model_config, char*)") + _get_enspath = ResPrototype("char* model_config_get_enspath(model_config)") + _get_history = ResPrototype("history_ref model_config_get_history(model_config)") + _get_history_source = ResPrototype( + "history_source_enum model_config_get_history_source(model_config)" + ) + _select_history = ResPrototype( + "bool model_config_select_history(model_config, history_source_enum, ecl_sum)" + ) + _has_history = ResPrototype("bool model_config_has_history(model_config)") + _gen_kw_export_name = ResPrototype( + "char* model_config_get_gen_kw_export_name(model_config)" + ) + _runpath_requires_iterations = ResPrototype( + "bool model_config_runpath_requires_iter(model_config)" + ) + _get_jobname_fmt = ResPrototype("char* model_config_get_jobname_fmt(model_config)") + _get_runpath_fmt = ResPrototype( + "path_fmt_ref model_config_get_runpath_fmt(model_config)" + ) + _get_num_realizations = ResPrototype( + "int model_config_get_num_realizations(model_config)" + ) + _get_obs_config_file = ResPrototype( + "char* model_config_get_obs_config_file(model_config)" + ) + _get_data_root = ResPrototype("char* model_config_get_data_root(model_config)") + _set_data_root = ResPrototype( + "void model_config_get_data_root(model_config, char*)" + ) + _get_time_map = ResPrototype( + "void model_config_get_external_time_map(model_config)" + ) + + def __init__( + self, + data_root, + joblist, + last_history_restart, + refcase, + config_content=None, + config_dict=None, + is_reference=False, + ): + if config_dict is not None and config_content is not None: + raise ValueError( + "Error: Unable to create ModelConfig with multiple config objects" + ) + + hist_src_enum = ModelConfig._get_history_src_enum(config_dict, config_content) + if hist_src_enum == HistorySourceEnum.SCHEDULE: + raise ValueError( + "{} as {} is not supported".format( + HistorySourceEnum.SCHEDULE, ConfigKeys.HISTORY_SOURCE + ) + ) + + if config_dict is None: + c_ptr = self._alloc( + config_content, data_root, joblist, last_history_restart, refcase + ) + else: + # MAX_RESAMPLE_KEY + max_resample = config_dict.get(ConfigKeys.MAX_RESAMPLE) + + # NUM_REALIZATIONS_KEY + num_realizations = config_dict[ConfigKeys.NUM_REALIZATIONS] + + # RUNPATH_KEY + run_path = config_dict.get(ConfigKeys.RUNPATH) + if run_path is not None: + run_path = os.path.realpath(run_path) + + # DATA_ROOT_KEY + data_root_from_config = config_dict.get(ConfigKeys.DATAROOT) + if data_root_from_config is not None: + data_root = os.path.realpath(data_root_from_config) + + # ENSPATH_KEY + ens_path = config_dict.get(ConfigKeys.ENSPATH) + if ens_path is not None: + ens_path = os.path.realpath(ens_path) + + # JOBNAME_KEY + job_name = config_dict.get(ConfigKeys.JOBNAME) + + # FORWARD_MODEL_KEY + forward_model = ForwardModel(ext_joblist=joblist) + # SIMULATION_JOB_KEY + for job_description in config_dict.get(ConfigKeys.FORWARD_MODEL, []): + job = forward_model.add_job(job_description[ConfigKeys.NAME]) + job.set_private_args_as_string(job_description.get(ConfigKeys.ARGLIST)) + job.convertToCReference(None) + + # SIMULATION_JOB_KEY + for job_description in config_dict.get(ConfigKeys.SIMULATION_JOB, []): + job = forward_model.add_job(job_description[ConfigKeys.NAME]) + job.set_private_args_as_string(job_description.get(ConfigKeys.ARGLIST)) + job.convertToCReference(None) + + # OBS_CONFIG_KEY + obs_config = config_dict.get(ConfigKeys.OBS_CONFIG) + if obs_config is not None: + obs_config = os.path.realpath(obs_config) + + # TIME_MAP_KEY + time_map = None + time_map_file = config_dict.get(ConfigKeys.TIME_MAP) + if time_map_file is not None and not os.path.isfile( + os.path.realpath(time_map_file) + ): + raise ValueError("Error: Time map is not a file") + elif time_map_file is not None: + time_map = TimeMap() + time_map.fload(filename=os.path.realpath(time_map_file)) + + # RFTPATH_KEY + rft_path = config_dict.get(ConfigKeys.RFTPATH) + if rft_path is not None: + rft_path = os.path.realpath(rft_path) + + # GEN_KW_EXPORT_NAME_KEY + gen_kw_export_name = config_dict.get(ConfigKeys.GEN_KW_EXPORT_NAME) + + # HISTORY_SOURCE_KEY + history_source = config_dict.get(ConfigKeys.HISTORY_SOURCE) + + c_ptr = self._alloc_full( + max_resample, + num_realizations, + run_path, + data_root, + ens_path, + job_name, + forward_model, + obs_config, + time_map, + rft_path, + gen_kw_export_name, + history_source, + joblist, + refcase, + ) + + # Fix ownership + forward_model.convertToCReference(None) + if time_map is not None: + time_map.convertToCReference(None) + + if c_ptr is None: + raise ValueError("Failed to construct ModelConfig instance.") + + super(ModelConfig, self).__init__(c_ptr, is_reference=is_reference) + + def hasHistory(self): + return self._has_history() + + def get_history_source(self): + """@rtype: HistorySourceEnum""" + return self._get_history_source() + + def set_history_source(self, history_source, refcase): + """ + @type history_source: HistorySourceEnum + @type refcase: EclSum + @rtype: bool + """ + assert isinstance(history_source, HistorySourceEnum) + assert isinstance(refcase, EclSum) + return self._select_history(history_source, refcase) + + def get_max_internal_submit(self): + """@rtype: int""" + return self._get_max_internal_submit() + + def set_max_internal_submit(self, max_value): + self._get_max_internal_submit(max_value) + + def getForwardModel(self): + """@rtype: ForwardModel""" + return self._get_forward_model().setParent(self) + + def getRunpathAsString(self): + """@rtype: str""" + return self._get_runpath_as_char() + + def selectRunpath(self, path_key): + """@rtype: bool""" + return self._select_runpath(path_key) + + def setRunpath(self, path_format): + self._set_runpath(path_format) + + def free(self): + self._free() + + def getGenKWExportName(self): + """@rtype: str""" + return self._gen_kw_export_name() + + def runpathRequiresIterations(self): + """@rtype: bool""" + return self._runpath_requires_iterations() + + def getJobnameFormat(self): + """@rtype: str""" + return self._get_jobname_fmt() + + @property + def obs_config_file(self): + return self._get_obs_config_file() + + def getEnspath(self): + """@rtype: str""" + return self._get_enspath() + + def getRunpathFormat(self): + """@rtype: PathFormat""" + return self._get_runpath_fmt() + + @property + def num_realizations(self): + return self._get_num_realizations() + + def data_root(self): + return self._get_data_root() + + def get_time_map(self): + return self._get_time_map() + + def __ne__(self, other): + return not self == other + + def __eq__(self, other): + if self.data_root() != other.data_root(): + return False + + if self.num_realizations != other.num_realizations: + return False + + if os.path.realpath(self.obs_config_file) != os.path.realpath( + other.obs_config_file + ): + return False + + if os.path.realpath(self.getEnspath()) != os.path.realpath(other.getEnspath()): + return False + + if self.getRunpathFormat() != other.getRunpathFormat(): + return False + + if self.getJobnameFormat() != other.getJobnameFormat(): + return False + + if os.path.realpath(self.getRunpathAsString()) != os.path.realpath( + other.getRunpathAsString() + ): + return False + + if self.get_max_internal_submit() != other.get_max_internal_submit(): + return False + + if self.getGenKWExportName() != other.getGenKWExportName(): + return False + + if self.get_time_map() != other.get_time_map(): + return False + + if self.getForwardModel() != other.getForwardModel(): + return False + + return True + + @staticmethod + def _get_history_src_enum(config_dict, config_content): + hist_src_enum = None + if config_dict and ConfigKeys.HISTORY_SOURCE in config_dict: + hist_src_enum = config_dict.get(ConfigKeys.HISTORY_SOURCE) + + if config_content and config_content.hasKey(ConfigKeys.HISTORY_SOURCE): + hist_src_str = config_content.getValue(ConfigKeys.HISTORY_SOURCE) + hist_src_enum = HistorySourceEnum.from_string(hist_src_str) + + return hist_src_enum diff --git a/res/enkf/node_id.py b/res/enkf/node_id.py new file mode 100644 index 00000000000..8f89c242b4e --- /dev/null +++ b/res/enkf/node_id.py @@ -0,0 +1,25 @@ +from ctypes import Structure, c_int +from cwrap import Prototype + + +class NodeId(Structure): + """ + NodeId is specified in enkf_types.h + """ + + _fields_ = [("report_step", c_int), ("iens", c_int)] + + def __init__(self, report_step, realization_number): + """ + @type report_step: int + @type realization_number: int + """ + super(NodeId, self).__init__(report_step, realization_number) + + def __repr__(self): + rs = self.report_step + ie = self.iens + return "NodeId(report_step = %d, iens = %d)" % (rs, ie) + + +Prototype.registerType("node_id", NodeId) diff --git a/res/enkf/obs_block.py b/res/enkf/obs_block.py new file mode 100644 index 00000000000..0deaf59b2cd --- /dev/null +++ b/res/enkf/obs_block.py @@ -0,0 +1,87 @@ +from cwrap import BaseCClass +from res import ResPrototype +from res.util import Matrix + + +class ObsBlock(BaseCClass): + TYPE_NAME = "obs_block" + + _alloc = ResPrototype( + "void* obs_block_alloc(char*, int, matrix, bool, double)", bind=False + ) + _free = ResPrototype("void obs_block_free(obs_block)") + _total_size = ResPrototype("int obs_block_get_size( obs_block )") + _active_size = ResPrototype("int obs_block_get_active_size( obs_block )") + _iset = ResPrototype("void obs_block_iset( obs_block , int , double , double)") + _iget_value = ResPrototype("double obs_block_iget_value( obs_block , int)") + _iget_std = ResPrototype("double obs_block_iget_std( obs_block , int)") + _get_obs_key = ResPrototype("char* obs_block_get_key( obs_block )") + _iget_is_active = ResPrototype("bool obs_block_iget_is_active( obs_block , int)") + + def __init__(self, obs_key, obs_size, global_std_scaling=1.0): + error_covar = None + error_covar_owner = False + c_pointer = self._alloc( + obs_key, obs_size, error_covar, error_covar_owner, global_std_scaling + ) + super(ObsBlock, self).__init__(c_pointer) + + def totalSize(self): + return self._total_size() + + def activeSize(self): + return self.active() + + def active(self): + return self._active_size() + + def __len__(self): + """Returns the total size""" + return self.totalSize() + + def is_active(self, index): + return self._iget_is_active(index) + + def get_obs_key(self): + return self._get_obs_key() + + def __setitem__(self, index, value): + if len(value) != 2: + raise TypeError( + "The value argument must be a two element tuple: (value , std)" + ) + d, std = value + + if isinstance(index, int): + if index < 0: + index += len(self) + if 0 <= index < len(self): + self._iset(index, d, std) + else: + raise IndexError( + "Invalid index: %d. Valid range: [0,%d)" % (index, len(self)) + ) + else: + raise TypeError( + "The index item must be integer, not %s." % str(type(index)) + ) + + def __getitem__(self, index): + if isinstance(index, int): + if index < 0: + index += len(self) + if 0 <= index < len(self): + value = self._iget_value(index) + std = self._iget_std(index) + return (value, std) + else: + raise IndexError( + "Invalid index:%d - valid range: [0,%d)" % (index, len(self)) + ) + else: + raise TypeError( + "The index item must be integer, not %s." % str(type(index)) + ) + + def free(self): + self._free() diff --git a/res/enkf/obs_data.py b/res/enkf/obs_data.py new file mode 100644 index 00000000000..0757edcc15e --- /dev/null +++ b/res/enkf/obs_data.py @@ -0,0 +1,114 @@ +# 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. + +from cwrap import BaseCClass +from res import ResPrototype +from res.util import Matrix + + +class ObsData(BaseCClass): + TYPE_NAME = "obs_data" + + _alloc = ResPrototype("void* obs_data_alloc(double)", bind=False) + _free = ResPrototype("void obs_data_free(obs_data)") + _total_size = ResPrototype("int obs_data_get_total_size(obs_data)") + _scale = ResPrototype( + "void obs_data_scale(obs_data, matrix, matrix, matrix, matrix, matrix)" + ) + _scale_matrix = ResPrototype("void obs_data_scale_matrix(obs_data, matrix)") + _scale_Rmatrix = ResPrototype("void obs_data_scale_Rmatrix(obs_data, matrix)") + _iget_value = ResPrototype("double obs_data_iget_value(obs_data, int)") + _iget_std = ResPrototype("double obs_data_iget_std(obs_data, int)") + _add_block = ResPrototype( + "obs_block_ref obs_data_add_block(obs_data , char* , int , matrix , bool)" + ) + _allocdObs = ResPrototype("matrix_obj obs_data_allocdObs(obs_data)") + _allocR = ResPrototype("matrix_obj obs_data_allocR(obs_data)") + _allocD = ResPrototype("matrix_obj obs_data_allocD(obs_data , matrix , matrix)") + _allocE = ResPrototype("matrix_obj obs_data_allocE(obs_data , rng , int)") + _iget_block = ResPrototype("obs_block_ref obs_data_iget_block(obs_data , int )") + _get_num_blocks = ResPrototype("int obs_data_get_num_blocks( obs_data )") + + def __init__(self, global_std_scaling=1.0): + c_pointer = self._alloc(global_std_scaling) + super(ObsData, self).__init__(c_pointer) + + def __len__(self): + """@rtype: int""" + return self._total_size() + + def __getitem__(self, index): + if index < 0: + index += len(self) + + if 0 <= index < len(self): + value = self._iget_value(index) + std = self._iget_std(index) + return (value, std) + + raise IndexError("Invalid index:%d valid range: [0,%d)" % (index, len(self))) + + def __str__(self): + s = "" + for pair in self: + s += "(%g, %g)\n" % pair + return s + + def __repr__(self): + return "ObsData(total_size = %d) at 0x%x" % (len(self), self._address()) + + def addBlock(self, obs_key, obs_size): + error_covar = None + error_covar_owner = False + return self._add_block(obs_key, obs_size, error_covar, error_covar_owner) + + def get_num_blocks(self): + return self._get_num_blocks() + + def get_block(self, index): + return self._iget_block(index) + + def createDObs(self): + """@rtype: Matrix""" + return self._allocdObs() + + def createR(self): + """@rtype: Matrix""" + return self._allocR() + + def createD(self, E, S): + """@rtype: Matrix""" + return self._allocD(E, S) + + def createE(self, rng, active_ens_size): + """@rtype: Matrix""" + return self._allocE(rng, active_ens_size) + + def scaleMatrix(self, m): + self._scale_matrix(m) + + def scaleRMatrix(self, R): + self._scale_Rmatrix(R) + + def scale(self, S, E=None, D=None, R=None, D_obs=None): + assert isinstance(S, Matrix) + for X in (E, D, R, D_obs): + if X is not None: + assert isinstance(X, Matrix) + self._scale(S, E, D, R, D_obs) + + def free(self): + self._free() diff --git a/res/enkf/observations/__init__.py b/res/enkf/observations/__init__.py new file mode 100644 index 00000000000..485088ff710 --- /dev/null +++ b/res/enkf/observations/__init__.py @@ -0,0 +1,5 @@ +from .summary_observation import SummaryObservation +from .gen_observation import GenObservation +from .block_data_config import BlockDataConfig +from .block_observation import BlockObservation +from .obs_vector import ObsVector diff --git a/res/enkf/observations/block_data_config.py b/res/enkf/observations/block_data_config.py new file mode 100644 index 00000000000..d2f546c422a --- /dev/null +++ b/res/enkf/observations/block_data_config.py @@ -0,0 +1,42 @@ +# 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. +from cwrap import BaseCClass +from res.enkf import NodeId, FieldConfig +from res import ResPrototype +import ctypes + + +class BlockDataConfig(BaseCClass): + TYPE_NAME = "block_data_config" + + def __init__(self): + raise NotImplementedError("Cannot instantiate BlockDataConfig!") + + @classmethod + def from_param(cls, instance): + if instance is None: + return ctypes.c_void_p() + elif isinstance(instance, FieldConfig): + return FieldConfig.from_param(instance) + + # The Container class which is used to support summary based + # source in the BLOCK_OBS configuration is not yet supported + # in Python. + + # elif isinstance(instance , ContainerConfig): + # return ContainerConfig.from_param( instance ) + else: + raise ValueError("Currently ONLY field data is supported") diff --git a/res/enkf/observations/block_observation.py b/res/enkf/observations/block_observation.py new file mode 100644 index 00000000000..71b099f5f8e --- /dev/null +++ b/res/enkf/observations/block_observation.py @@ -0,0 +1,111 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'block_obs.py' 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. +from cwrap import BaseCClass +from res import ResPrototype +from res.enkf import NodeId, FieldConfig +from res.enkf.observations import BlockDataConfig + + +class BlockObservation(BaseCClass): + TYPE_NAME = "block_obs" + + _alloc = ResPrototype( + "void* block_obs_alloc( char* , block_data_config , ecl_grid )", bind=False + ) + _free = ResPrototype("void block_obs_free( block_obs )") + _iget_i = ResPrototype("int block_obs_iget_i(block_obs, int)") + _iget_j = ResPrototype("int block_obs_iget_j( block_obs, int)") + _iget_k = ResPrototype("int block_obs_iget_k( block_obs , int)") + _get_size = ResPrototype("int block_obs_get_size( block_obs )") + _get_std = ResPrototype("double block_obs_iget_std( block_obs, int )") + _get_std_scaling = ResPrototype( + "double block_obs_iget_std_scaling( block_obs, int )" + ) + _update_std_scaling = ResPrototype( + "void block_obs_update_std_scale(block_obs , double , active_list)" + ) + _get_value = ResPrototype("double block_obs_iget_value( block_obs, int)") + _get_depth = ResPrototype("double block_obs_iget_depth( block_obs, int)") + _add_field_point = ResPrototype( + "void block_obs_append_field_obs( block_obs, int,int,int,double,double)" + ) + _add_summary_point = ResPrototype( + "void block_obs_append_summary_obs( block_obs, int, int, int, double, double)" + ) + _iget_data = ResPrototype( + "double block_obs_iget_data(block_obs, void*, int, node_id)" + ) + + def __init__(self, obs_key, data_config, grid): + c_ptr = self._alloc(obs_key, data_config, grid) + super(BlockObservation, self).__init__(c_ptr) + + def getCoordinate(self, index): + """@rtype: tuple of (int, int, int)""" + i = self._iget_i(index) + j = self._iget_j(index) + k = self._iget_k(index) + return i, j, k + + def __len__(self): + """@rtype: int""" + return self._get_size() + + def __iter__(self): + cur = 0 + while cur < len(self): + yield cur + cur += 1 + + def addPoint(self, i, j, k, value, std, sum_key=None): + if sum_key is None: + self._add_field_point(i, j, k, value, std) + else: + self._add_summary_point(i, j, k, sum_key, value, std) + + def getValue(self, index): + """@rtype: float""" + return self._get_value(index) + + def getStd(self, index): + """@rtype: float""" + return self._get_std(index) + + def getStdScaling(self, index): + """@rtype: float""" + return self._get_std_scaling(index) + + def updateStdScaling(self, factor, active_list): + self._update_std_scaling(factor, active_list) + + def getDepth(self, index): + """@rtype: float""" + return self._get_depth(index) + + def getData(self, state, obs_index, node_id): + """ + @type state: c_void_p + @type obs_index: int + @type node_id: NodeId + @rtype: float""" + + return self._iget_data(state, obs_index, node_id) + + def free(self): + self._free() + + def __repr__(self): + return "BlockObservation(size = %d) at 0x%x" % (len(self), self._address()) diff --git a/res/enkf/observations/gen_observation.py b/res/enkf/observations/gen_observation.py new file mode 100644 index 00000000000..a155cab1c82 --- /dev/null +++ b/res/enkf/observations/gen_observation.py @@ -0,0 +1,147 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'gen_observation.py' 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. +import ctypes +import os.path + +import numpy as np +from cwrap import BaseCClass +from ecl.util.util import IntVector +from res import ResPrototype +from res.enkf import GenDataConfig + + +class GenObservation(BaseCClass): + TYPE_NAME = "gen_obs" + + _alloc = ResPrototype("void* gen_obs_alloc__(gen_data_config , char*)", bind=False) + _free = ResPrototype("void gen_obs_free(gen_obs)") + _load = ResPrototype("void gen_obs_load_observation(gen_obs , char*)") + _scalar_set = ResPrototype("void gen_obs_set_scalar(gen_obs , double , double)") + _get_std = ResPrototype("double gen_obs_iget_std(gen_obs, int)") + _get_value = ResPrototype("double gen_obs_iget_value(gen_obs, int)") + _get_std_scaling = ResPrototype("double gen_obs_iget_std_scaling(gen_obs, int)") + _get_size = ResPrototype("int gen_obs_get_size(gen_obs)") + _get_data_index = ResPrototype("int gen_obs_get_obs_index(gen_obs, int)") + _load_data_index = ResPrototype("void gen_obs_load_data_index(gen_obs , char*)") + _add_data_index = ResPrototype( + "void gen_obs_attach_data_index(gen_obs , int_vector)" + ) + _update_std_scaling = ResPrototype( + "void gen_obs_update_std_scale(gen_obs , double , active_list)" + ) + _get_value_vector = ResPrototype( + "void gen_obs_load_values(gen_obs, int, double*)" + ) + _get_std_vector = ResPrototype("void gen_obs_load_std(gen_obs, int, double*)") + + def __init__( + self, obs_key, data_config, scalar_value=None, obs_file=None, data_index=None + ): + c_ptr = self._alloc(data_config, obs_key) + if c_ptr: + super(GenObservation, self).__init__(c_ptr) + else: + raise ValueError( + "Unable to construct GenObservation with given obs_key and data_config!" + ) + + if scalar_value is None and obs_file is None: + raise ValueError( + "Exactly one the scalar_value and obs_file arguments must be present" + ) + + if scalar_value is not None and obs_file is not None: + raise ValueError( + "Exactly one the scalar_value and obs_file arguments must be present" + ) + + if obs_file is not None: + if not os.path.isfile(obs_file): + raise IOError( + "The file with observation data:%s does not exist" % obs_file + ) + else: + self._load(obs_file) + else: + obs_value, obs_std = scalar_value + self._scalar_set(obs_value, obs_std) + + if not data_index is None: + if os.path.isfile(data_index): + self._load_data_index(data_index) + else: + index_list = IntVector.active_list(data_index) + self._add_data_index(index_list) + + def __len__(self): + return self._get_size() + + def __getitem__(self, obs_index): + if obs_index < 0: + obs_index += len(self) + + if 0 <= obs_index < len(self): + return (self.getValue(obs_index), self.getStandardDeviation(obs_index)) + else: + raise IndexError("Invalid index. Valid range: [0,%d)" % len(self)) + + def getValue(self, obs_index): + """@rtype: float""" + return self._get_value(obs_index) + + def getStandardDeviation(self, obs_index): + """@rtype: float""" + return self._get_std(obs_index) + + def getStdScaling(self, obs_index): + """@rtype: float""" + return self._get_std_scaling(obs_index) + + def updateStdScaling(self, factor, active_list): + self._update_std_scaling(factor, active_list) + + def get_data_points(self): + np_vector = np.zeros(len(self)) + self._get_value_vector( + len(self), np_vector.ctypes.data_as(ctypes.POINTER(ctypes.c_double)) + ) + return np_vector + + def get_std(self): + np_vector = np.zeros(len(self)) + self._get_std_vector( + len(self), np_vector.ctypes.data_as(ctypes.POINTER(ctypes.c_double)) + ) + return np_vector + + def getSize(self): + """@rtype: float""" + return len(self) + + def getIndex(self, obs_index): + """@rtype: int""" + return self.getDataIndex(obs_index) + + def getDataIndex(self, obs_index): + return self._get_data_index(obs_index) + + def free(self): + self._free() + + def __repr__(self): + si = len(self) + ad = self._ad_str() + return "GenObservation(size = %d) %s" % (si, ad) diff --git a/res/enkf/observations/obs_vector.py b/res/enkf/observations/obs_vector.py new file mode 100644 index 00000000000..c87725f3bbb --- /dev/null +++ b/res/enkf/observations/obs_vector.py @@ -0,0 +1,189 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'obs_vector.py' 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. +from cwrap import BaseCClass +from res import ResPrototype +from res.enkf.config import EnkfConfigNode +from res.enkf.enums import EnkfObservationImplementationType +from res.enkf.observations import BlockObservation, SummaryObservation, GenObservation + + +class ObsVector(BaseCClass): + TYPE_NAME = "obs_vector" + + _alloc = ResPrototype( + "void* obs_vector_alloc(enkf_obs_impl_type, char*, enkf_config_node, int)", + bind=False, + ) + _free = ResPrototype("void obs_vector_free( obs_vector )") + _get_state_kw = ResPrototype("char* obs_vector_get_state_kw( obs_vector )") + _get_key = ResPrototype("char* obs_vector_get_key( obs_vector )") + _iget_node = ResPrototype("void* obs_vector_iget_node( obs_vector, int)") + _get_num_active = ResPrototype("int obs_vector_get_num_active( obs_vector )") + _iget_active = ResPrototype("bool obs_vector_iget_active( obs_vector, int)") + _get_impl_type = ResPrototype( + "enkf_obs_impl_type obs_vector_get_impl_type( obs_vector)" + ) + _install_node = ResPrototype( + "void obs_vector_install_node(obs_vector, int, void*)" + ) + _get_next_active_step = ResPrototype( + "int obs_vector_get_next_active_step(obs_vector, int)" + ) + _has_data = ResPrototype( + "bool obs_vector_has_data(obs_vector , bool_vector , enkf_fs)" + ) + _get_config_node = ResPrototype( + "enkf_config_node_ref obs_vector_get_config_node(obs_vector)" + ) + _get_total_chi2 = ResPrototype( + "double obs_vector_total_chi2(obs_vector, enkf_fs, int)" + ) + _get_obs_key = ResPrototype("char* obs_vector_get_obs_key(obs_vector)") + _get_step_list = ResPrototype("int_vector_ref obs_vector_get_step_list(obs_vector)") + _create_local_node = ResPrototype( + "local_obsdata_node_obj obs_vector_alloc_local_node(obs_vector)" + ) + + def __init__(self, observation_type, observation_key, config_node, num_reports): + """ + @type observation_type: EnkfObservationImplementationType + @type observation_key: str + @type config_node: EnkfConfigNode + @type num_reports: int + """ + assert isinstance(observation_type, EnkfObservationImplementationType) + assert isinstance(observation_key, str) + assert isinstance(config_node, EnkfConfigNode) + assert isinstance(num_reports, int) + c_ptr = self._alloc(observation_type, observation_key, config_node, num_reports) + super(ObsVector, self).__init__(c_ptr) + + def getDataKey(self): + """@rtype: str""" + return self._get_state_kw() + + def getObservationKey(self): + """@rtype: str""" + return self.getKey() + + def getKey(self): + return self._get_key() + + def getObsKey(self): + return self._get_obs_key() + + def getNode(self, index): + """@rtype: SummaryObservation or BlockObservation or GenObservation""" + + pointer = self._iget_node(index) + + node_type = self.getImplementationType() + if node_type == EnkfObservationImplementationType.SUMMARY_OBS: + return SummaryObservation.createCReference(pointer, self) + elif node_type == EnkfObservationImplementationType.BLOCK_OBS: + return BlockObservation.createCReference(pointer, self) + elif node_type == EnkfObservationImplementationType.GEN_OBS: + return GenObservation.createCReference(pointer, self) + else: + raise AssertionError("Node type '%s' currently not supported!" % node_type) + + def __iter__(self): + """Iterate over active report steps; return node""" + cur = -1 + run = True + for step in self.getStepList(): + yield self.getNode(step) + + def getStepList(self): + """ + Will return an IntVector with the active report steps. + """ + return self._get_step_list() + + def activeStep(self): + """Assuming the observation is only active for one report step, this + method will return that report step - if it is active for more + than one report step the method will raise an exception. + """ + step_list = self.getStepList() + if len(step_list) == 1: + return step_list[0] + else: + raise ValueError( + "The activeStep() method can *ONLY* be called for obervations with one active step" + ) + + def firstActiveStep(self): + """@rtype: int""" + step_list = self.getStepList() + if len(step_list) > 0: + return step_list[0] + else: + raise ValueError( + "the firstActiveStep() method cannot be called with no active steps." + ) + + def getActiveCount(self): + """@rtype: int""" + return len(self) + + def __len__(self): + return self._get_num_active() + + def isActive(self, index): + """@rtype: bool""" + return self._iget_active(index) + + def getNextActiveStep(self, previous_step=-1): + """@rtype: int""" + return self._get_next_active_step(previous_step) + + def getImplementationType(self): + """@rtype: EnkfObservationImplementationType""" + return self._get_impl_type() + + def installNode(self, index, node): + assert isinstance(node, SummaryObservation) + node.convertToCReference(self) + self._install_node(index, node.from_param(node)) + + def getConfigNode(self): + """@rtype: EnkfConfigNode""" + return self._get_config_node().setParent(self) + + def createLocalObs(self): + """ + Will create a LocalObsDataNode instance with all timesteps set. + """ + return self._create_local_node() + + def hasData(self, active_mask, fs): + """@rtype: bool""" + return self._has_data(active_mask, fs) + + def free(self): + self._free() + + def __repr__(self): + dk = "data_key = %s" % self.getDataKey() + kk = "key = %s" % self.getKey() + ok = "obs_key = %s" % self.getObsKey() + na = "num_active = %d" % len(self) + return "ObsVector(%s, %s, %s, %s) %s" % (na, kk, ok, dk, self._ad_str()) + + def getTotalChi2(self, fs, realization_number): + """@rtype: float""" + return self._get_total_chi2(fs, realization_number) diff --git a/res/enkf/observations/summary_observation.py b/res/enkf/observations/summary_observation.py new file mode 100644 index 00000000000..bc22161c8a8 --- /dev/null +++ b/res/enkf/observations/summary_observation.py @@ -0,0 +1,103 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'summary_observation.py' 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. + +from cwrap import BaseCClass +from res import ResPrototype + + +class SummaryObservation(BaseCClass): + TYPE_NAME = "summary_obs" + + _alloc = ResPrototype( + "void* summary_obs_alloc(char*, char*, double, double, char*, double)", + bind=False, + ) + _free = ResPrototype("void summary_obs_free(summary_obs)") + _get_value = ResPrototype("double summary_obs_get_value(summary_obs)") + _get_std = ResPrototype("double summary_obs_get_std(summary_obs)") + _get_std_scaling = ResPrototype("double summary_obs_get_std_scaling(summary_obs)") + _get_summary_key = ResPrototype("char* summary_obs_get_summary_key(summary_obs)") + _update_std_scale = ResPrototype( + "void summary_obs_update_std_scale(summary_obs , double , active_list)" + ) + _set_std_scale = ResPrototype( + "void summary_obs_set_std_scale(summary_obs , double)" + ) + + def __init__( + self, + summary_key, + observation_key, + value, + std, + auto_corrf_name=None, + auto_corrf_param=0.0, + ): + assert isinstance(summary_key, str) + assert isinstance(observation_key, str) + assert isinstance(value, float) + assert isinstance(std, float) + + if auto_corrf_name is not None: + assert isinstance(auto_corrf_name, str) + + assert isinstance(auto_corrf_param, float) + c_ptr = self._alloc( + summary_key, observation_key, value, std, auto_corrf_name, auto_corrf_param + ) + if c_ptr: + super(SummaryObservation, self).__init__(c_ptr) + else: + raise ValueError( + "Unable to construct SummaryObservation with given configuration!" + ) + + def getValue(self): + """@rtype: float""" + return self._get_value() + + def getStandardDeviation(self): + """@rtype: float""" + return self._get_std() + + def getStdScaling(self, index=0): + """@rtype: float""" + return self._get_std_scaling() + + def set_std_scaling(self, scaling_factor): + self._set_std_scale(scaling_factor) + + def __len__(self): + return 1 + + def getSummaryKey(self): + """@rtype: str""" + return self._get_summary_key() + + def updateStdScaling(self, factor, active_list): + self._update_std_scale(factor, active_list) + + def free(self): + self._free() + + def __repr__(self): + sk = self.getSummaryKey() + va = self.getValue() + sd = self.getStandardDeviation() + sc = self.getStdScaling() + ad = self._address() + fmt = "SummaryObservation(key = %s, value = %f, std = %f, std_scaling = %f) at 0x%x" + return fmt % (sk, va, sd, sc, ad) diff --git a/res/enkf/plot/__init__.py b/res/enkf/plot/__init__.py new file mode 100644 index 00000000000..a1e0c692936 --- /dev/null +++ b/res/enkf/plot/__init__.py @@ -0,0 +1,9 @@ +from .data_fetcher import DataFetcher +from .observation_data_fetcher import ObservationDataFetcher +from .refcase_data_fetcher import RefcaseDataFetcher +from .ensemble_data_fetcher import EnsembleDataFetcher +from .ensemble_block_data_fetcher import EnsembleBlockDataFetcher +from .block_observation_data_fetcher import BlockObservationDataFetcher +from .ensemble_gen_kw_fetcher import EnsembleGenKWFetcher +from .ensemble_gen_data_fetcher import EnsembleGenDataFetcher +from .observation_gen_data_fetcher import ObservationGenDataFetcher diff --git a/res/enkf/plot/block_observation_data_fetcher.py b/res/enkf/plot/block_observation_data_fetcher.py new file mode 100644 index 00000000000..abb7d631cb3 --- /dev/null +++ b/res/enkf/plot/block_observation_data_fetcher.py @@ -0,0 +1,106 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'block_observation_data_fetcher.py' 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. +from res.enkf import EnkfObservationImplementationType +from res.enkf.observations import BlockObservation +from res.enkf.plot import DataFetcher + + +class BlockObservationDataFetcher(DataFetcher): + def __init__(self, ert): + super(BlockObservationDataFetcher, self).__init__(ert) + self.__selected_report_step_index = None + + def setSelectedReportStepIndex(self, index): + self.__selected_report_step_index = index + + def fetchSupportedKeys(self): + observations = self.ert().getObservations() + string_list = observations.getTypedKeylist( + EnkfObservationImplementationType.BLOCK_OBS + ) + return [key for key in string_list] + + def __fetchObservationData(self, block_observation): + assert isinstance(block_observation, BlockObservation) + + data = { + "continuous": False, + "x": [], + "y": [], + "std": [], + "min_y": None, + "max_y": None, + "min_x": None, + "max_x": None, + } + + for index in block_observation: + std = block_observation.getStd(index) + y = block_observation.getDepth(index) + x = block_observation.getValue(index) + + data["std"].append(std) + data["y"].append(y) + data["x"].append(x) + + adjusted_x = self.adjustX(x, std) + + if data["min_x"] is None or data["min_x"] > adjusted_x: + data["min_x"] = adjusted_x + + if data["max_x"] is None or data["max_x"] < x + std: + data["max_x"] = x + std + + if data["min_y"] is None or data["min_y"] > y: + data["min_y"] = y + + if data["max_y"] is None or data["max_y"] < y: + data["max_y"] = y + + return data + + @staticmethod + def adjustX(x, std): + if x >= 0: + return max(0, x - std) + + return x - std + + def hasData(self, key): + """@rtype: bool""" + observations = self.ert().getObservations() + if not observations.hasKey(key): + return False + + return observations[key].getActiveCount() > 0 + + def fetchData(self, key, case=None): + observations = self.ert().getObservations() + assert observations.hasKey(key) + + observation_vector = observations[key] + + report_step_data = [] + for report_step in observation_vector: + block_observation = observation_vector.getNode(report_step) + data = self.__fetchObservationData(block_observation) + data["report_step"] = report_step + report_step_data.append(data) + + if self.__selected_report_step_index is not None: + return report_step_data[self.__selected_report_step_index] + else: + return report_step_data diff --git a/res/enkf/plot/data_fetcher.py b/res/enkf/plot/data_fetcher.py new file mode 100644 index 00000000000..19dc5bfd94a --- /dev/null +++ b/res/enkf/plot/data_fetcher.py @@ -0,0 +1,26 @@ +from res.enkf.enkf_main import EnKFMain + + +class DataFetcher(object): + def __init__(self, ert): + super(DataFetcher, self).__init__() + assert isinstance(ert, EnKFMain) + self.__ert = ert + self.__supported_keys = None + + def fetchData(self, key, case=None): + raise NotImplementedError() + + def ert(self): + return self.__ert + + def fetchSupportedKeys(self): + raise NotImplementedError() + + def getSupportedKeys(self): + if self.__supported_keys is None: + self.__supported_keys = self.fetchSupportedKeys() + return self.__supported_keys + + def supportsKey(self, key): + return key in self.getSupportedKeys() diff --git a/res/enkf/plot/ensemble_block_data_fetcher.py b/res/enkf/plot/ensemble_block_data_fetcher.py new file mode 100644 index 00000000000..732decaef3a --- /dev/null +++ b/res/enkf/plot/ensemble_block_data_fetcher.py @@ -0,0 +1,115 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'ensemble_block_data_fetcher.py' 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. + +from res.enkf.enums import ErtImplType +from res.enkf.plot import DataFetcher +from res.enkf.plot_data import PlotBlockDataLoader, PlotBlockData + + +class EnsembleBlockDataFetcher(DataFetcher): + def __init__(self, ert): + super(EnsembleBlockDataFetcher, self).__init__(ert) + self.__selected_report_step_index = None + + def __fetchSimulationData(self, block_data): + """ + @type block_data: PlotBlockData + @rtype dict + """ + data = { + "x": [], + "y": [], + "min_x_values": [], + "max_x_values": [], + "min_y": None, + "max_y": None, + "min_x": None, + "max_x": None, + } + + depth_vector = block_data.getDepth() + + for depth in depth_vector: + data["y"].append(depth) + data["min_x_values"].append(None) + data["max_x_values"].append(None) + + min_y = min(data["y"]) + max_y = max(data["y"]) + + if data["min_y"] is None or data["min_y"] > min_y: + data["min_y"] = min_y + + if data["max_y"] is None or data["max_y"] < max_y: + data["max_y"] = max_y + + for block_vector in block_data: + x = [] + data["x"].append(x) + + for index in range(len(block_vector)): + value = block_vector[index] + x.append(value) + if data["min_x"] is None or data["min_x"] > value: + data["min_x"] = value + + if data["max_x"] is None or data["max_x"] < value: + data["max_x"] = value + + if ( + data["min_x_values"][index] is None + or data["min_x_values"][index] > value + ): + data["min_x_values"][index] = value + + if ( + data["max_x_values"][index] is None + or data["max_x_values"][index] < value + ): + data["max_x_values"][index] = value + + return data + + def fetchData(self, key, case=None): + enkf_fs = self.ert().getEnkfFsManager().getFileSystem(case) + observations = self.ert().getObservations() + assert observations.hasKey(key) + + observation_vector = observations[key] + + loader = PlotBlockDataLoader(observation_vector) + + report_step_data = [] + for report_step in observation_vector: + block_data = loader.load(enkf_fs, report_step) + data = self.__fetchSimulationData(block_data) + data["report_step"] = report_step + + report_step_data.append(data) + + if self.__selected_report_step_index is not None: + return report_step_data[self.__selected_report_step_index] + else: + return report_step_data + + def fetchSupportedKeys(self): + string_list = ( + self.ert().ensembleConfig().getKeylistFromImplType(ErtImplType.SUMMARY) + ) + return [key for key in string_list] + + def setSelectedReportStepIndex(self, index): + self.__selected_report_step_index = index diff --git a/res/enkf/plot/ensemble_data_fetcher.py b/res/enkf/plot/ensemble_data_fetcher.py new file mode 100644 index 00000000000..347970619ff --- /dev/null +++ b/res/enkf/plot/ensemble_data_fetcher.py @@ -0,0 +1,80 @@ +from res.enkf import EnsembleConfig +from res.enkf.plot_data import EnsemblePlotData +from res.enkf.enums import ErtImplType +from res.enkf.plot.data_fetcher import DataFetcher + + +class EnsembleDataFetcher(DataFetcher): + def __init__(self, ert): + super(EnsembleDataFetcher, self).__init__(ert) + + def fetchSupportedKeys(self): + """@rtype: list of str""" + return [ + key + for key in self.ert() + .ensembleConfig() + .getKeylistFromImplType(ErtImplType.SUMMARY) + ] + + def getEnsembleConfigNode(self, key): + """@rtype: EnsembleConfig""" + ensemble_config = self.ert().ensembleConfig() + assert key in ensemble_config + return ensemble_config.getNode(key) + + def fetchData(self, key, case=None): + ensemble_config_node = self.getEnsembleConfigNode(key) + enkf_fs = self.ert().getEnkfFsManager().getFileSystem(case) + ensemble_plot_data = EnsemblePlotData(ensemble_config_node, enkf_fs) + + data = { + "x": [], + "y": [], + "min_y_values": [], + "max_y_values": [], + "min_y": None, + "max_y": None, + "min_x": None, + "max_x": None, + } + + time_map = enkf_fs.getTimeMap() + + for index in range(1, len(time_map)): + data["x"].append(time_map[index].ctime()) + data["min_y_values"].append(None) + data["max_y_values"].append(None) + + data["min_x"] = data["x"][0] + data["max_x"] = data["x"][len(data["x"]) - 1] + + for vector in ensemble_plot_data: + y = [] + data["y"].append(y) + + # skip index 0 (not a valid simulation value...) + for index in range(len(vector) - 1): + if vector.isActive(index + 1): + y_value = vector.getValue(index + 1) + y.append(y_value) + + if data["min_y"] is None or data["min_y"] > y_value: + data["min_y"] = y_value + + if data["max_y"] is None or data["max_y"] < y_value: + data["max_y"] = y_value + + if ( + data["min_y_values"][index] is None + or data["min_y_values"][index] > y_value + ): + data["min_y_values"][index] = y_value + + if ( + data["max_y_values"][index] is None + or data["max_y_values"][index] < y_value + ): + data["max_y_values"][index] = y_value + + return data diff --git a/res/enkf/plot/ensemble_gen_data_fetcher.py b/res/enkf/plot/ensemble_gen_data_fetcher.py new file mode 100644 index 00000000000..8a8e020186e --- /dev/null +++ b/res/enkf/plot/ensemble_gen_data_fetcher.py @@ -0,0 +1,72 @@ +# Copyright (C) 2014 Equinor ASA, Norway. +# +# The file 'ensemble_gen_data_fetcher.py' 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. +from res.enkf.plot_data import EnsemblePlotGenData +from res.enkf.plot import DataFetcher + + +class EnsembleGenDataFetcher(DataFetcher): + def __init__(self, ert): + super(EnsembleGenDataFetcher, self).__init__(ert) + + def fetchSupportedKeys(self): + gen_data_list = [] + + return gen_data_list + + def getEnsembleConfigNode(self, key): + """@rtype: EnsembleConfig""" + ensemble_config = self.ert().ensembleConfig() + assert key in ensemble_config + return ensemble_config.getNode(key) + + def fetchData(self, key, case=None): + key, report_step = key.split("@") + report_step = int(report_step) + + ensemble_config_node = self.getEnsembleConfigNode(key) + enkf_fs = self.ert().getEnkfFsManager().getFileSystem(case) + ensemble_plot_gen_data = EnsemblePlotGenData( + ensemble_config_node, enkf_fs, report_step + ) + + data = { + "x": [], + "y": [], + "min_y_values": [value for value in ensemble_plot_gen_data.getMinValues()], + "max_y_values": [value for value in ensemble_plot_gen_data.getMaxValues()], + "min_y": None, + "max_y": None, + "min_x": 0, + "max_x": None, + } + + data["x"] = [index for index in range(len(data["min_y_values"]))] + data["max_x"] = len(data["min_y_values"]) - 1 + + for vector in ensemble_plot_gen_data: + y = [] + data["y"].append(y) + + for value in vector: + y.append(value) + + if data["min_y"] is None or data["min_y"] > value: + data["min_y"] = value + + if data["max_y"] is None or data["max_y"] < value: + data["max_y"] = value + + return data diff --git a/res/enkf/plot/ensemble_gen_kw_fetcher.py b/res/enkf/plot/ensemble_gen_kw_fetcher.py new file mode 100644 index 00000000000..5a110299ec9 --- /dev/null +++ b/res/enkf/plot/ensemble_gen_kw_fetcher.py @@ -0,0 +1,81 @@ +# Copyright (C) 2014 Equinor ASA, Norway. +# +# The file 'ensemble_gen_kw_fetcher.py' 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. +from res.enkf.config.gen_kw_config import GenKwConfig +from res.enkf.enums.ert_impl_type_enum import ErtImplType +from res.enkf.plot import DataFetcher +from res.enkf.plot_data import EnsemblePlotGenKW + + +class EnsembleGenKWFetcher(DataFetcher): + def __init__(self, ert): + super(EnsembleGenKWFetcher, self).__init__(ert) + + def fetchSupportedKeys(self): + gen_kw_keys = ( + self.ert().ensembleConfig().getKeylistFromImplType(ErtImplType.GEN_KW) + ) + gen_kw_list = [] + for key in gen_kw_keys: + enkf_config_node = self.ert().ensembleConfig().getNode(key) + model_config = enkf_config_node.getModelConfig() + assert isinstance(model_config, GenKwConfig) + + model_keys = model_config.getKeyWords() + for key_word in model_keys: + gen_kw_list.append("%s:%s" % (key, key_word)) + + return gen_kw_list + + def getEnsembleConfigNode(self, key): + """@rtype: EnsembleConfig""" + ensemble_config = self.ert().ensembleConfig() + assert key in ensemble_config + return ensemble_config.getNode(key) + + def fetchData(self, key, case=None): + key, keyword = key.split(":") + ensemble_config_node = self.getEnsembleConfigNode(key) + enkf_fs = self.ert().getEnkfFsManager().getFileSystem(case) + ensemble_plot_gen_kw = EnsemblePlotGenKW(ensemble_config_node, enkf_fs) + keyword_index = ensemble_plot_gen_kw.getIndexForKeyword(keyword) + + use_log_scale = ensemble_plot_gen_kw.shouldUseLogScale(keyword_index) + + data = { + "x": [0], + "y": [], + "min_y": None, + "max_y": None, + "min_x": 0, + "max_x": 0, + "use_log_scale": use_log_scale, + } + + for vector in ensemble_plot_gen_kw: + y = [] + data["y"].append(y) + + if len(vector) > keyword_index: + value = vector.getValue(keyword_index) + y.append(value) + + if data["min_y"] is None or data["min_y"] > value: + data["min_y"] = value + + if data["max_y"] is None or data["max_y"] < value: + data["max_y"] = value + + return data diff --git a/res/enkf/plot/observation_data_fetcher.py b/res/enkf/plot/observation_data_fetcher.py new file mode 100644 index 00000000000..a1140e40dd4 --- /dev/null +++ b/res/enkf/plot/observation_data_fetcher.py @@ -0,0 +1,99 @@ +from res.enkf import EnkfObservationImplementationType +from res.enkf.enums import ErtImplType +from res.enkf.plot import DataFetcher + + +class ObservationDataFetcher(DataFetcher): + def __init__(self, ert): + super(ObservationDataFetcher, self).__init__(ert) + + def getObservationKeys(self): + observations = self.ert().getObservations() + keys = observations.getTypedKeylist( + EnkfObservationImplementationType.SUMMARY_OBS + ) + keys = sorted(keys) + return keys + + def fetchSupportedKeys(self): + """@rtype: list of str""" + return sorted( + [ + key + for key in self.ert() + .ensembleConfig() + .getKeylistFromImplType(ErtImplType.SUMMARY) + ] + ) + + def __getObservationData(self, key, data): + observations = self.ert().getObservations() + assert observations.hasKey(key) + + observation_data = observations[key] + active_count = observation_data.getActiveCount() + + history_length = self.ert().getHistoryLength() + for index in range(0, history_length): + if observation_data.isActive(index): + x_value = int(observations.getObservationTime(index).ctime()) + data["x"].append(x_value) + + #: :type: SummaryObservation + node = observation_data.getNode(index) + + y_value = node.getValue() + std = node.getStandardDeviation() + data["y"].append(float(y_value)) + data["std"].append(float(std)) + + if data["min_x"] is None or data["min_x"] > x_value: + data["min_x"] = x_value + + if data["max_x"] is None or data["max_x"] < x_value: + data["max_x"] = x_value + + adjusted_y = self.adjustY(y_value, std) + + if data["min_y"] is None or data["min_y"] > adjusted_y: + data["min_y"] = adjusted_y + + if data["max_y"] is None or data["max_y"] < y_value + std: + data["max_y"] = y_value + std + + if active_count == 1: + data["continuous"] = False + + @staticmethod + def adjustY(y, std): + if y >= 0: + return max(0, y - std) + + return y - std + + def fetchData(self, key, case=None): + obs_keys = self.ert().ensembleConfig().getNode(key).getObservationKeys() + history_length = self.ert().getHistoryLength() + + data = { + "continuous": True, + "x": None, + "y": None, + "std": None, + "min_y": None, + "max_y": None, + "min_x": None, + "max_x": None, + } + + if len(obs_keys) == 0: + return data + + data["x"] = [] + data["y"] = [] + data["std"] = [] + + for obs_key in obs_keys: + self.__getObservationData(obs_key, data) + + return data diff --git a/res/enkf/plot/observation_gen_data_fetcher.py b/res/enkf/plot/observation_gen_data_fetcher.py new file mode 100644 index 00000000000..2ac49e4d2f2 --- /dev/null +++ b/res/enkf/plot/observation_gen_data_fetcher.py @@ -0,0 +1,115 @@ +# Copyright (C) 2014 Equinor ASA, Norway. +# +# The file 'observation_gen_data_fetcher.py' 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. +from res.enkf.enums.ert_impl_type_enum import ErtImplType +from res.enkf.plot import DataFetcher + + +class ObservationGenDataFetcher(DataFetcher): + def __init__(self, ert): + super(ObservationGenDataFetcher, self).__init__(ert) + + def fetchSupportedKeys(self): + gen_data_keys = ( + self.ert().ensembleConfig().getKeylistFromImplType(ErtImplType.GEN_DATA) + ) + gen_data_list = [] + for key in gen_data_keys: + obs_keys = self.ert().ensembleConfig().getNode(key).getObservationKeys() + for obs_key in obs_keys: + obs_vector = self.ert().getObservations()[obs_key] + for report_step in obs_vector.getStepList(): + gen_data_list.append("%s@%d" % (key, report_step)) + + return gen_data_list + + def __getObservationData(self, key, report_step): + data = { + "continuous": True, + "x": [], + "y": [], + "std": [], + "min_y": None, + "max_y": None, + "min_x": None, + "max_x": None, + } + + observations = self.ert().getObservations() + assert observations.hasKey(key) + + gen_obs = observations[key].getNode(report_step) + + size = gen_obs.getSize() + + data["min_x"] = 0 + data["max_x"] = size - 1 + for index in range(0, size): + std = gen_obs.getStandardDeviation(index) + data["std"].append(std) + y_value = gen_obs.getValue(index) + data["y"].append(y_value) + + adjusted_y = self.adjustY(y_value, std) + + if data["min_y"] is None or data["min_y"] > adjusted_y: + data["min_y"] = adjusted_y + + if data["max_y"] is None or data["max_y"] < y_value + std: + data["max_y"] = y_value + std + + obs_index = gen_obs.getIndex(index) + data["x"].append(obs_index) + + return data + + def getObsKeyForKey(self, key, key_report_step): + obs_keys = self.ert().ensembleConfig().getNode(key).getObservationKeys() + for obs_key in obs_keys: + obs_vector = self.ert().getObservations()[obs_key] + for report_step in obs_vector.getStepList(): + if report_step == key_report_step: + return obs_key + + raise UserWarning("Observation key for key '%s' not found!" % key) + + def getAllObsKeysForKey(self, key): + key, report_step = key.split("@") + return self.ert().ensembleConfig().getNode(key).getObservationKeys() + + def hasData(self, key): + """@rtype: bool""" + key, report_step = key.split("@") + observations = self.ert().getObservations() + obs_key = self.getObsKeyForKey(key, int(report_step)) + if not observations.hasKey(obs_key): + return False + + return observations[obs_key].getActiveCount() > 0 + + def fetchData(self, key, case=None): + key, report_step = key.split("@") + + key_report_step = int(report_step) + obs_key = self.getObsKeyForKey(key, key_report_step) + + return self.__getObservationData(obs_key, key_report_step) + + @staticmethod + def adjustY(y, std): + if y >= 0: + return max(0, y - std) + + return y - std diff --git a/res/enkf/plot/refcase_data_fetcher.py b/res/enkf/plot/refcase_data_fetcher.py new file mode 100644 index 00000000000..30db79564b7 --- /dev/null +++ b/res/enkf/plot/refcase_data_fetcher.py @@ -0,0 +1,71 @@ +from ecl.summary import EclSum, EclSumVector, EclSumNode +from res.enkf.enums import ErtImplType +from res.enkf.plot.data_fetcher import DataFetcher + + +class RefcaseDataFetcher(DataFetcher): + def __init__(self, ert): + super(RefcaseDataFetcher, self).__init__(ert) + self.report_times = {} + + def hasRefcase(self): + """@rtype: bool""" + return self.ert().eclConfig().hasRefcase() + + def getRefCase(self): + """@rtype: EclSum""" + return self.ert().eclConfig().getRefcase() + + def getSummaryKeys(self): + """@rtype: StringList""" + return self.ert().ensembleConfig().getKeylistFromImplType(ErtImplType.SUMMARY) + + def fetchData(self, key, case=None): + data = { + "x": None, + "y": None, + "min_y": None, + "max_y": None, + "min_x": None, + "max_x": None, + } + + if not self.hasRefcase(): + return data + + refcase = self.getRefCase() + vector = refcase.get_vector(key, report_only=False) + + data["x"] = [] + data["y"] = [] + + for index in range(1, len(vector)): + node = vector[index] + + x_value = self.getReportStepTimeFromRefcase(refcase, node.report_step) + data["x"].append(int(x_value)) + + if data["min_x"] is None or data["min_x"] > x_value: + data["min_x"] = x_value + + if data["max_x"] is None or data["max_x"] < x_value: + data["max_x"] = x_value + + value = node.value + data["y"].append(float(value)) + + if data["min_y"] is None or data["min_y"] > value: + data["min_y"] = value + + if data["max_y"] is None or data["max_y"] < value: + data["max_y"] = value + + return data + + def getReportStepTimeFromRefcase(self, refcase, report_step): + if not report_step in self.report_times: + self.report_times[report_step] = ( + EclSum.cNamespace().get_report_time(refcase, report_step).ctime() + ) + + return self.report_times[report_step] diff --git a/res/enkf/plot_data/__init__.py b/res/enkf/plot_data/__init__.py new file mode 100644 index 00000000000..e5d7f3d02c4 --- /dev/null +++ b/res/enkf/plot_data/__init__.py @@ -0,0 +1,9 @@ +from .ensemble_plot_data_vector import EnsemblePlotDataVector +from .ensemble_plot_data import EnsemblePlotData +from .plot_block_vector import PlotBlockVector +from .plot_block_data import PlotBlockData +from .plot_block_data_loader import PlotBlockDataLoader +from .ensemble_plot_gen_data_vector import EnsemblePlotGenDataVector +from .ensemble_plot_gen_data import EnsemblePlotGenData +from .ensemble_plot_gen_kw_vector import EnsemblePlotGenKWVector +from .ensemble_plot_gen_kw import EnsemblePlotGenKW diff --git a/res/enkf/plot_data/ensemble_plot_data.py b/res/enkf/plot_data/ensemble_plot_data.py new file mode 100644 index 00000000000..514db020c80 --- /dev/null +++ b/res/enkf/plot_data/ensemble_plot_data.py @@ -0,0 +1,57 @@ +from cwrap import BaseCClass +from res import ResPrototype +from res.enkf.config import EnkfConfigNode +from res.enkf.enkf_fs import EnkfFs +from ecl.util.util import BoolVector + + +class EnsemblePlotData(BaseCClass): + TYPE_NAME = "ensemble_plot_data" + + _alloc = ResPrototype("void* enkf_plot_data_alloc(enkf_config_node)", bind=False) + _load = ResPrototype( + "void enkf_plot_data_load(ensemble_plot_data, enkf_fs, char*, bool_vector)" + ) + _size = ResPrototype("int enkf_plot_data_get_size(ensemble_plot_data)") + _get = ResPrototype( + "ensemble_plot_data_vector_ref enkf_plot_data_iget(ensemble_plot_data, int)" + ) + _free = ResPrototype("void enkf_plot_data_free(ensemble_plot_data)") + + def __init__( + self, ensemble_config_node, file_system=None, user_index=None, input_mask=None + ): + assert isinstance(ensemble_config_node, EnkfConfigNode) + + c_pointer = self._alloc(ensemble_config_node) + super(EnsemblePlotData, self).__init__(c_pointer) + + if not file_system is None: + self.load(file_system, user_index, input_mask) + + def load(self, file_system, user_index=None, input_mask=None): + assert isinstance(file_system, EnkfFs) + if not input_mask is None: + assert isinstance(input_mask, BoolVector) + + self._load(file_system, user_index, input_mask) + + def __len__(self): + """@rtype: int""" + return self._size() + + def __getitem__(self, index): + """@rtype: EnsemblePlotDataVector""" + return self._get(index) + + def __iter__(self): + cur = 0 + while cur < len(self): + yield self[cur] + cur += 1 + + def free(self): + self._free() + + def __repr__(self): + return "EnsemblePlotData(size = %d) %s" % (len(self), self._ad_str()) diff --git a/res/enkf/plot_data/ensemble_plot_data_vector.py b/res/enkf/plot_data/ensemble_plot_data_vector.py new file mode 100644 index 00000000000..61a1bc330d7 --- /dev/null +++ b/res/enkf/plot_data/ensemble_plot_data_vector.py @@ -0,0 +1,40 @@ +from cwrap import BaseCClass +from res import ResPrototype +from ecl.util.util import CTime + + +class EnsemblePlotDataVector(BaseCClass): + TYPE_NAME = "ensemble_plot_data_vector" + + _size = ResPrototype("int enkf_plot_tvector_size(ensemble_plot_data_vector)") + _get_value = ResPrototype( + "double enkf_plot_tvector_iget_value(ensemble_plot_data_vector, int)" + ) + _get_time = ResPrototype( + "time_t enkf_plot_tvector_iget_time(ensemble_plot_data_vector, int)" + ) + _is_active = ResPrototype( + "bool enkf_plot_tvector_iget_active(ensemble_plot_data_vector, int)" + ) + + def __init__(self): + raise NotImplementedError("Class can not be instantiated directly!") + + def __len__(self): + """@rtype: int""" + return self._size() + + def getValue(self, index): + """@rtype: float""" + return self._get_value(index) + + def getTime(self, index): + """@rtype: CTime""" + return self._get_time(index) + + def isActive(self, index): + """@rtype: bool""" + return self._is_active(index) + + def __repr__(self): + return "EnsemblePlotDataVector(size = %d) %s" % (len(self), self._ad_str()) diff --git a/res/enkf/plot_data/ensemble_plot_gen_data.py b/res/enkf/plot_data/ensemble_plot_gen_data.py new file mode 100644 index 00000000000..fe21a384861 --- /dev/null +++ b/res/enkf/plot_data/ensemble_plot_gen_data.py @@ -0,0 +1,91 @@ +# Copyright (C) 2014 Equinor ASA, Norway. +# +# The file 'ensemble_plot_gen_data.py' 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. + +from cwrap import BaseCClass +from res import ResPrototype +from res.enkf.config import EnkfConfigNode +from res.enkf.enkf_fs import EnkfFs +from res.enkf.enums.ert_impl_type_enum import ErtImplType +from ecl.util.util import BoolVector, DoubleVector + + +class EnsemblePlotGenData(BaseCClass): + TYPE_NAME = "ensemble_plot_gen_data" + + _alloc = ResPrototype("void* enkf_plot_gendata_alloc(enkf_config_node)", bind=False) + _size = ResPrototype("int enkf_plot_gendata_get_size(ensemble_plot_gen_data)") + _load = ResPrototype( + "void enkf_plot_gendata_load(ensemble_plot_gen_data, enkf_fs, int, bool_vector)" + ) + _get = ResPrototype( + "ensemble_plot_gen_data_vector_ref enkf_plot_gendata_iget(ensemble_plot_gen_data, int)" + ) + _min_values = ResPrototype( + "double_vector_ref enkf_plot_gendata_get_min_values(ensemble_plot_gen_data)" + ) + _max_values = ResPrototype( + "double_vector_ref enkf_plot_gendata_get_max_values(ensemble_plot_gen_data)" + ) + _free = ResPrototype("void enkf_plot_gendata_free(ensemble_plot_gen_data)") + + def __init__(self, ensemble_config_node, file_system, report_step, input_mask=None): + assert isinstance(ensemble_config_node, EnkfConfigNode) + assert ensemble_config_node.getImplementationType() == ErtImplType.GEN_DATA + + c_ptr = self._alloc(ensemble_config_node) + if c_ptr: + super(EnsemblePlotGenData, self).__init__(c_ptr) + else: + raise ValueError( + "Unable to construct EnsemplePlotGenData from given config node!" + ) + + self.__load(file_system, report_step, input_mask) + + def __load(self, file_system, report_step, input_mask=None): + assert isinstance(file_system, EnkfFs) + if not input_mask is None: + assert isinstance(input_mask, BoolVector) + + self._load(file_system, report_step, input_mask) + + def __len__(self): + """@rtype: int""" + return self._size() + + def __getitem__(self, index): + """@rtype: EnsemblePlotGenDataVector""" + return self._get(index) + + def __iter__(self): + cur = 0 + while cur < len(self): + yield self[cur] + cur += 1 + + def getMaxValues(self): + """@rtype: DoubleVector""" + return self._max_values().setParent(self) + + def getMinValues(self): + """@rtype: DoubleVector""" + return self._min_values().setParent(self) + + def free(self): + self._free() + + def __repr__(self): + return "EnsemblePlotGenData(size = %d) %s" % (len(self), self._ad_str()) diff --git a/res/enkf/plot_data/ensemble_plot_gen_data_vector.py b/res/enkf/plot_data/ensemble_plot_gen_data_vector.py new file mode 100644 index 00000000000..772b2bc8133 --- /dev/null +++ b/res/enkf/plot_data/ensemble_plot_gen_data_vector.py @@ -0,0 +1,53 @@ +# Copyright (C) 2014 Equinor ASA, Norway. +# +# The file 'ensemble_plot_gen_data_vector.py' 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. + +from cwrap import BaseCClass +from res import ResPrototype + + +class EnsemblePlotGenDataVector(BaseCClass): + TYPE_NAME = "ensemble_plot_gen_data_vector" + + _size = ResPrototype( + "int enkf_plot_genvector_get_size(ensemble_plot_gen_data_vector)" + ) + _get_value = ResPrototype( + "double enkf_plot_genvector_iget(ensemble_plot_gen_data_vector, int)" + ) + + def __init__(self): + raise NotImplementedError("Class can not be instantiated directly!") + + def __len__(self): + """@rtype: int""" + return self._size() + + def __repr__(self): + return "EnsemblePlotGenDataVector(size = %d) %s" % (len(self), self._ad_str()) + + def getValue(self, index): + """@rtype: float""" + return self[index] + + def __iter__(self): + cur = 0 + while cur < len(self): + yield self[cur] + cur += 1 + + def __getitem__(self, index): + """@rtype: float""" + return self._get_value(index) diff --git a/res/enkf/plot_data/ensemble_plot_gen_kw.py b/res/enkf/plot_data/ensemble_plot_gen_kw.py new file mode 100644 index 00000000000..71ff0d95a1a --- /dev/null +++ b/res/enkf/plot_data/ensemble_plot_gen_kw.py @@ -0,0 +1,102 @@ +# Copyright (C) 2014 Equinor ASA, Norway. +# +# The file 'ensemble_plot_gen_kw.py' 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. + +from cwrap import BaseCClass +from res import ResPrototype +from res.enkf.config import EnkfConfigNode +from res.enkf.enkf_fs import EnkfFs +from res.enkf.enums.ert_impl_type_enum import ErtImplType +from ecl.util.util import BoolVector +from res.enkf.plot_data import EnsemblePlotGenKWVector + + +class EnsemblePlotGenKW(BaseCClass): + TYPE_NAME = "ensemble_plot_gen_kw" + + _alloc = ResPrototype("void* enkf_plot_gen_kw_alloc(enkf_config_node)", bind=False) + _size = ResPrototype("int enkf_plot_gen_kw_get_size(ensemble_plot_gen_kw)") + _load = ResPrototype( + "void enkf_plot_gen_kw_load(ensemble_plot_gen_kw, enkf_fs, bool, int, bool_vector)" + ) + _get = ResPrototype( + "ensemble_plot_gen_kw_vector_ref enkf_plot_gen_kw_iget(ensemble_plot_gen_kw, int)" + ) + _iget_key = ResPrototype( + "char* enkf_plot_gen_kw_iget_key(ensemble_plot_gen_kw, int)" + ) + _get_keyword_count = ResPrototype( + "int enkf_plot_gen_kw_get_keyword_count(ensemble_plot_gen_kw)" + ) + _should_use_log_scale = ResPrototype( + "bool enkf_plot_gen_kw_should_use_log_scale(ensemble_plot_gen_kw, int)" + ) + _free = ResPrototype("void enkf_plot_gen_kw_free(ensemble_plot_gen_kw)") + + def __init__(self, ensemble_config_node, file_system, input_mask=None): + assert isinstance(ensemble_config_node, EnkfConfigNode) + assert ensemble_config_node.getImplementationType() == ErtImplType.GEN_KW + + c_pointer = self._alloc(ensemble_config_node) + super(EnsemblePlotGenKW, self).__init__(c_pointer) + + self.__load(file_system, input_mask) + + def __load(self, file_system, input_mask=None): + assert isinstance(file_system, EnkfFs) + if not input_mask is None: + assert isinstance(input_mask, BoolVector) + + self._load(file_system, True, 0, input_mask) + + def __len__(self): + """@rtype: int""" + return self._size() + + def __getitem__(self, index): + """@rtype: EnsemblePlotGenKWVector""" + return self._get(index) + + def __iter__(self): + cur = 0 + while cur < len(self): + yield self[cur] + cur += 1 + + def getKeyWordCount(self): + """@rtype: int""" + return self._get_keyword_count() + + def getKeyWordForIndex(self, index): + """@rtype: str""" + return self._iget_key(index) + + def getIndexForKeyword(self, keyword): + """@rtype: int""" + for index in range(self.getKeyWordCount()): + kw = self.getKeyWordForIndex(index) + if kw == keyword: + return index + return None + + def shouldUseLogScale(self, index): + """@rtype: bool""" + return bool(self._should_use_log_scale(index)) + + def free(self): + self._free() + + def __repr__(self): + return "EnsemblePlotGenKW(size = %d) %s" % (len(self), self._ad_str()) diff --git a/res/enkf/plot_data/ensemble_plot_gen_kw_vector.py b/res/enkf/plot_data/ensemble_plot_gen_kw_vector.py new file mode 100644 index 00000000000..654a73911d5 --- /dev/null +++ b/res/enkf/plot_data/ensemble_plot_gen_kw_vector.py @@ -0,0 +1,53 @@ +# Copyright (C) 2014 Equinor ASA, Norway. +# +# The file 'ensemble_plot_gen_kw_vector.py' 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. + +from cwrap import BaseCClass +from res import ResPrototype + + +class EnsemblePlotGenKWVector(BaseCClass): + TYPE_NAME = "ensemble_plot_gen_kw_vector" + + _size = ResPrototype( + "int enkf_plot_gen_kw_vector_get_size(ensemble_plot_gen_kw_vector)" + ) + _get_value = ResPrototype( + "double enkf_plot_gen_kw_vector_iget(ensemble_plot_gen_kw_vector, int)" + ) + + def __init__(self): + raise NotImplementedError("Class can not be instantiated directly!") + + def __len__(self): + """@rtype: int""" + return self._size() + + def getValue(self, index): + """@rtype: float""" + return self[index] + + def __iter__(self): + cur = 0 + while cur < len(self): + yield self[cur] + cur += 1 + + def __getitem__(self, index): + """@rtype: float""" + return self._get_value(index) + + def __repr__(self): + return "EnsemblePlotGenKWVector(size = %d) %s" % (len(self), self._ad_str()) diff --git a/res/enkf/plot_data/plot_block_data.py b/res/enkf/plot_data/plot_block_data.py new file mode 100644 index 00000000000..8321bf78814 --- /dev/null +++ b/res/enkf/plot_data/plot_block_data.py @@ -0,0 +1,40 @@ +from res.enkf.plot_data import PlotBlockVector +from ecl.util.util import DoubleVector + + +class PlotBlockData(object): + def __init__(self, depth_vector): + """ + @type depth_vector: DoubleVector + """ + assert isinstance(depth_vector, DoubleVector) + self.__depth_vector = depth_vector + self.__plot_block_vectors = {} + + def __len__(self): + """@rtype: int""" + return len(self.__plot_block_vectors) + + def __getitem__(self, index): + """ + @type index: int + @rtype: PlotBlockVector + """ + return self.__plot_block_vectors[index] + + def __iter__(self): + cur = 0 + keys = sorted(self.__plot_block_vectors.keys()) + while cur < len(keys): + yield self[keys[cur]] + cur += 1 + + def getDepth(self): + """@rtype: DoubleVector""" + return self.__depth_vector + + def addPlotBlockVector(self, vector): + """ + @type vector: PlotBlockVector + """ + self.__plot_block_vectors[vector.getRealizationNumber()] = vector diff --git a/res/enkf/plot_data/plot_block_data_loader.py b/res/enkf/plot_data/plot_block_data_loader.py new file mode 100644 index 00000000000..20fc9e38673 --- /dev/null +++ b/res/enkf/plot_data/plot_block_data_loader.py @@ -0,0 +1,98 @@ +from res.enkf import RealizationStateEnum, EnkfNode, ErtImplType, NodeId +from res.enkf.plot_data import PlotBlockData, PlotBlockVector +from ecl.util.util import DoubleVector, BoolVector, ThreadPool + + +class PlotBlockDataLoader(object): + def __init__(self, obs_vector): + """ + @type obs_vector: ObsVector + """ + if obs_vector is None: + raise ArgumentError( + "Cannot construct PlotBlockDataLoader without obs_vector. Was None." + ) + super(PlotBlockDataLoader, self).__init__() + self.__obs_vector = obs_vector + self.__permutation_vector = None + + def getBlockObservation(self, report_step): + """@rtype: BlockObservation""" + return self.__obs_vector.getNode(report_step) + + def getDepthValues(self, report_step): + """@rtype: DoubleVector""" + block_obs = self.getBlockObservation(report_step) + + depth = DoubleVector() + for index in block_obs: + value = block_obs.getDepth(index) + depth.append(value) + + return depth + + def load(self, fs, report_step, input_mask=None): + """ + @type fs: EnkfFs + @type report_step: int + @type input_mask: BoolVector + @rtype: PlotBlockData + """ + + state_map = fs.getStateMap() + ensemble_size = len(state_map) + + if not input_mask is None: + mask = BoolVector.copy(input_mask) + else: + mask = BoolVector(False, ensemble_size) + + state_map.selectMatching(mask, RealizationStateEnum.STATE_HAS_DATA) + + depth = self.getDepthValues(report_step) + + self.__permutation_vector = depth.permutationSort() + depth.permute(self.__permutation_vector) + + plot_block_data = PlotBlockData(depth) + + thread_pool = ThreadPool() + for index in range(ensemble_size): + if mask[index]: + thread_pool.addTask( + self.loadVector, plot_block_data, fs, report_step, index + ) + + thread_pool.nonBlockingStart() + thread_pool.join() + + return plot_block_data + + def loadVector(self, plot_block_data, fs, report_step, realization_number): + """ + @type plot_block_data: PlotBlockData + @type fs: EnkfFs + @type report_step: int + @type realization_number: int + @rtype PlotBlockVector + """ + config_node = self.__obs_vector.getConfigNode() + + is_private_container = ( + config_node.getImplementationType() == ErtImplType.CONTAINER + ) + data_node = EnkfNode(config_node, private=is_private_container) + + node_id = NodeId(report_step, realization_number) + + if data_node.tryLoad(fs, node_id): + block_obs = self.getBlockObservation(report_step) + + data = DoubleVector() + for index in range(len(block_obs)): + value = block_obs.getData(data_node.valuePointer(), index, node_id) + data.append(value) + data.permute(self.__permutation_vector) + + plot_block_vector = PlotBlockVector(realization_number, data) + plot_block_data.addPlotBlockVector(plot_block_vector) diff --git a/res/enkf/plot_data/plot_block_vector.py b/res/enkf/plot_data/plot_block_vector.py new file mode 100644 index 00000000000..c1241bc52d6 --- /dev/null +++ b/res/enkf/plot_data/plot_block_vector.py @@ -0,0 +1,35 @@ +from ecl.util.util import DoubleVector + + +class PlotBlockVector(object): + def __init__(self, realization_number, data): + """ + @type realization_number: int + @type data: DoubleVector + """ + super(PlotBlockVector, self).__init__() + + assert isinstance(data, DoubleVector) + + self.__realization_number = realization_number + self.__data = data + + def __len__(self): + """@rtype: int""" + return len(self.__data) + + def __getitem__(self, index): + """@rtype: float""" + assert isinstance(index, int) + return self.__data[index] + + def __iter__(self): + """@rtype: float""" + cur = 0 + while cur < len(self): + yield self[cur] + cur += 1 + + def getRealizationNumber(self): + """@rtype: int""" + return self.__realization_number diff --git a/res/enkf/queue_config.py b/res/enkf/queue_config.py new file mode 100644 index 00000000000..119959cfd99 --- /dev/null +++ b/res/enkf/queue_config.py @@ -0,0 +1,190 @@ +# Copyright (C) 2017 Equinor ASA, Norway. +# +# The file 'site_config.py' 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. + +from cwrap import BaseCClass + +from ecl.util.util import StringList, Hash + +from res import ResPrototype +from res.enkf import ConfigKeys +from res.job_queue import JobQueue, ExtJoblist, Driver + + +class QueueConfig(BaseCClass): + + TYPE_NAME = "queue_config" + + _free = ResPrototype("void queue_config_free( queue_config )") + _alloc_job_queue = ResPrototype( + "job_queue_obj queue_config_alloc_job_queue( queue_config )" + ) + _alloc = ResPrototype("void* queue_config_alloc_load(char*)", bind=False) + _alloc_full = ResPrototype( + "void* queue_config_alloc_full(char*, bool, int, int, queue_driver_enum)", + bind=False, + ) + _alloc_content = ResPrototype( + "void* queue_config_alloc(config_content)", bind=False + ) + _alloc_local_copy = ResPrototype( + "queue_config_obj queue_config_alloc_local_copy( queue_config )" + ) + _has_job_script = ResPrototype("bool queue_config_has_job_script( queue_config )") + _get_job_script = ResPrototype("char* queue_config_get_job_script(queue_config)") + _max_submit = ResPrototype("int queue_config_get_max_submit(queue_config)") + _queue_system = ResPrototype("char* queue_config_get_queue_system(queue_config)") + _queue_driver = ResPrototype( + "driver_ref queue_config_get_queue_driver(queue_config, char*)" + ) + _get_num_cpu = ResPrototype("int queue_config_get_num_cpu(queue_config)") + + _lsf_queue_opt = ResPrototype("char* queue_config_lsf_queue_name()", bind=False) + _lsf_server_opt = ResPrototype("char* queue_config_lsf_server()", bind=False) + _lsf_resource_opt = ResPrototype("char* queue_config_lsf_resource()", bind=False) + _lsf_driver_opt = ResPrototype("char* queue_config_lsf_driver_name()", bind=False) + + def __init__(self, user_config_file=None, config_content=None, config_dict=None): + configs = sum( + [ + 1 + for x in [user_config_file, config_content, config_dict] + if x is not None + ] + ) + + if configs > 1: + raise ValueError( + "Attempting to create QueueConfig object with multiple config objects" + ) + + if configs == 0: + raise ValueError( + "Attempting to create QueueConfig object with no config objects" + ) + + c_ptr = None + if user_config_file is not None: + c_ptr = self._alloc(user_config_file) + + if config_content is not None: + c_ptr = self._alloc_content(config_content) + + if config_dict is not None: + c_ptr = self._alloc_full( + config_dict[ConfigKeys.JOB_SCRIPT], + config_dict[ConfigKeys.USER_MODE], + config_dict[ConfigKeys.MAX_SUBMIT], + config_dict[ConfigKeys.NUM_CPU], + config_dict[ConfigKeys.QUEUE_SYSTEM], + ) + if not c_ptr: + raise ValueError("Unable to create QueueConfig instance") + + super(QueueConfig, self).__init__(c_ptr) + + # Need to create + if config_dict is not None: + queue_options = config_dict.get(ConfigKeys.QUEUE_OPTION) + for option in queue_options: + self.driver.set_option( + option[ConfigKeys.NAME], option[ConfigKeys.VALUE] + ) + + def create_job_queue(self): + queue = JobQueue(self.driver, max_submit=self.max_submit) + return queue + + def create_local_copy(self): + return self._alloc_local_copy() + + def has_job_script(self): + return self._has_job_script() + + def free(self): + self._free() + + @property + def max_submit(self): + return self._max_submit() + + @property + def queue_name(self): + return self.driver.get_option(QueueConfig.LSF_QUEUE_NAME_KEY) + + @property + def queue_system(self): + """The queue system in use, e.g. LSF or LOCAL""" + return self._queue_system() + + @property + def job_script(self): + return self._get_job_script() + + @property + def driver(self): + return self._queue_driver(self.queue_system).setParent(self) + + def _assert_lsf(self, key="driver"): + sys = self.queue_system + if sys != QueueConfig.LSF_KEY: + fmt = "Cannot fetch LSF {key}, current queue is {system}" + raise ValueError(fmt.format(key=key, system=self.queue_system)) + + @property + def _lsf_driver(self): + self._assert_lsf() + driver = self._queue_driver(self.LSF_KEY) + return driver.setParent(self) + + @property + def lsf_resource(self): + self._assert_lsf(key=QueueConfig.LSF_RESOURCE_KEY) + return self._lsf_driver.get_option(self.LSF_RESOURCE_KEY) + + @property + def lsf_server(self): + self._assert_lsf(key=QueueConfig.LSF_SERVER_KEY) + return self._lsf_driver.get_option(self.LSF_SERVER_KEY) + + @property + def num_cpu(self): + return self._get_num_cpu() + + def __eq__(self, other): + + if self.max_submit != other.max_submit: + return False + if self.queue_system != other.queue_system: + return False + if self.num_cpu != other.num_cpu: + return False + if self.job_script != other.job_script: + return False + + if self.queue_system != "LOCAL": + if self.queue_name != other.queue_name: + return False + if self.lsf_resource != other.lsf_resource: + return False + if self.lsf_server != other.lsf_server: + return False + + return True + + LSF_KEY = _lsf_driver_opt() + LSF_QUEUE_NAME_KEY = _lsf_queue_opt() + LSF_RESOURCE_KEY = _lsf_resource_opt() + LSF_SERVER_KEY = _lsf_server_opt() diff --git a/res/enkf/res_config.py b/res/enkf/res_config.py new file mode 100644 index 00000000000..79e928795ea --- /dev/null +++ b/res/enkf/res_config.py @@ -0,0 +1,697 @@ +# Copyright (C) 2017 Equinor ASA, Norway. +# +# The file 'res_config.py' 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. + +import os +from os.path import isfile + +from cwrap import BaseCClass + +from ecl.util.util import StringList +from res import ResPrototype +from res.config import ConfigParser, ConfigContent, ConfigSettings, UnrecognizedEnum + +from res.enkf import ( + SiteConfig, + AnalysisConfig, + SubstConfig, + ModelConfig, + EclConfig, + QueueConfig, + EnsembleConfig, + RNGConfig, + ConfigKeys, + ErtWorkflowList, + HookManager, + ErtTemplates, + LogConfig, +) + + +class ResConfig(BaseCClass): + TYPE_NAME = "res_config" + + _free = ResPrototype("void res_config_free(res_config)") + _alloc_full = ResPrototype( + "void* res_config_alloc_full(" + "char*, " + "char*, " + "subst_config, " + "site_config, " + "rng_config, " + "analysis_config, " + "ert_workflow_list, " + "hook_manager, " + "ert_templates, " + "ecl_config, " + "ens_config, " + "model_config, " + "log_config, " + "queue_config)", + bind=False, + ) + + _alloc_config_content = ResPrototype( + "config_content_ref res_config_alloc_user_content(char*, config_parser)", + bind=False, + ) + _user_config_file = ResPrototype( + "char* res_config_get_user_config_file(res_config)" + ) + _config_path = ResPrototype("char* res_config_get_config_directory(res_config)") + _site_config = ResPrototype( + "site_config_ref res_config_get_site_config(res_config)" + ) + _analysis_config = ResPrototype( + "analysis_config_ref res_config_get_analysis_config(res_config)" + ) + _subst_config = ResPrototype( + "subst_config_ref res_config_get_subst_config(res_config)" + ) + _model_config = ResPrototype( + "model_config_ref res_config_get_model_config(res_config)" + ) + _ecl_config = ResPrototype("ecl_config_ref res_config_get_ecl_config(res_config)") + _ensemble_config = ResPrototype( + "ens_config_ref res_config_get_ensemble_config(res_config)" + ) + _hook_manager = ResPrototype( + "hook_manager_ref res_config_get_hook_manager(res_config)" + ) + _ert_workflow_list = ResPrototype( + "ert_workflow_list_ref res_config_get_workflow_list(res_config)" + ) + _rng_config = ResPrototype("rng_config_ref res_config_get_rng_config(res_config)") + _ert_templates = ResPrototype( + "ert_templates_ref res_config_get_templates(res_config)" + ) + _log_config = ResPrototype("log_config_ref res_config_get_log_config(res_config)") + _queue_config = ResPrototype( + "queue_config_ref res_config_get_queue_config(res_config)" + ) + _init_parser = ResPrototype( + "void res_config_init_config_parser(config_parser)", bind=False + ) + + def __init__( + self, user_config_file=None, config=None, throw_on_error=True, config_dict=None + ): + + _assert_configs = ( + len([x for x in (config, config_dict, user_config_file) if x is not None]) + == 1 + ) + if _assert_configs is False: + raise ValueError( + "Wrong config input - only one config means needs to be provided!" + ) + + self._errors, self._failed_keys = None, None + self._assert_input(user_config_file, config_dict, throw_on_error) + + configs = None + config_dir = None + if config is not None or user_config_file is not None: + configs, config_dir = self._alloc_from_content( + user_config_file=user_config_file, + config=config, + throw_on_error=throw_on_error, + ) + else: + configs, config_dir = self._alloc_from_dict( + config_dict=config_dict, throw_on_error=throw_on_error + ) + + c_ptr = None + + for conf in configs: + conf.convertToCReference(None) + c_ptr = self._alloc_full(config_dir, user_config_file, *configs) + + if c_ptr: + super(ResConfig, self).__init__(c_ptr) + else: + raise ValueError( + "Failed to construct ResConfig instance from %r." + % (user_config_file if user_config_file else config) + ) + + def _assert_input(self, user_config_file, config, throw_on_error): + if config and not isinstance(config, dict): + raise ValueError( + "Expected config to be a dictionary, was %r" % type(config) + ) + + if user_config_file and not isinstance(user_config_file, str): + raise ValueError("Expected user_config_file to be a string.") + + if user_config_file is not None and config is not None: + raise ValueError( + "Expected either user_config_file " + + "or config to be provided, got both!" + ) + + if user_config_file is not None and not isfile(user_config_file): + raise IOError('No such configuration file "%s".' % user_config_file) + + if user_config_file is not None and not throw_on_error: + raise NotImplementedError( + "Disabling exceptions on errors is not " + "available when loading from file." + ) + + # build configs from config file or everest dict + def _alloc_from_content( + self, user_config_file=None, config=None, throw_on_error=True + ): + if user_config_file is not None: + # initialize configcontent if user_file provided + parser = ConfigParser() + config_content = self._alloc_config_content(user_config_file, parser) + config_dir = config_content.getValue(ConfigKeys.CONFIG_DIRECTORY) + else: + config_dir = os.getcwd() + config_content = self._build_config_content(config) + + if self.errors and throw_on_error: + raise ValueError("Error loading configuration: " + str(self._errors)) + + subst_config = SubstConfig(config_content=config_content) + site_config = SiteConfig(config_content=config_content) + rng_config = RNGConfig(config_content=config_content) + analysis_config = AnalysisConfig(config_content=config_content) + ecl_config = EclConfig(config_content=config_content) + log_config = LogConfig(config_content=config_content) + queue_config = QueueConfig(config_content=config_content) + + ert_workflow_list = ErtWorkflowList( + subst_list=subst_config.subst_list, config_content=config_content + ) + + hook_manager = HookManager( + workflow_list=ert_workflow_list, config_content=config_content + ) + + ert_templates = ErtTemplates( + parent_subst=subst_config.subst_list, config_content=config_content + ) + + ensemble_config = EnsembleConfig( + config_content=config_content, + grid=ecl_config.getGrid(), + refcase=ecl_config.getRefcase(), + ) + + model_config = ModelConfig( + data_root=config_dir, + joblist=site_config.get_installed_jobs(), + last_history_restart=ecl_config.getLastHistoryRestart(), + refcase=ecl_config.getRefcase(), + config_content=config_content, + ) + + return [ + subst_config, + site_config, + rng_config, + analysis_config, + ert_workflow_list, + hook_manager, + ert_templates, + ecl_config, + ensemble_config, + model_config, + log_config, + queue_config, + ], config_dir + + # build configs from config dict + def _alloc_from_dict(self, config_dict, throw_on_error=True): + # treat the default config dir + config_dir = os.getcwd() + if ConfigKeys.CONFIG_DIRECTORY in config_dict: + config_dir = config_dict[ConfigKeys.CONFIG_DIRECTORY] + config_dict[ConfigKeys.CONFIG_DIRECTORY] = config_dir + + subst_config = SubstConfig(config_dict=config_dict) + site_config = SiteConfig(config_dict=config_dict) + rng_config = RNGConfig(config_dict=config_dict) + analysis_config = AnalysisConfig(config_dict=config_dict) + ecl_config = EclConfig(config_dict=config_dict) + log_config = LogConfig(config_dict=config_dict) + queue_config = QueueConfig(config_dict=config_dict) + + ert_workflow_list = ErtWorkflowList( + subst_list=subst_config.subst_list, config_dict=config_dict + ) + + hook_manager = HookManager( + workflow_list=ert_workflow_list, config_dict=config_dict + ) + + ert_templates = ErtTemplates( + parent_subst=subst_config.subst_list, config_dict=config_dict + ) + + ensemble_config = EnsembleConfig( + grid=ecl_config.getGrid(), + refcase=ecl_config.getRefcase(), + config_dict=config_dict, + ) + + model_config = ModelConfig( + data_root=config_dir, + joblist=site_config.get_installed_jobs(), + last_history_restart=ecl_config.getLastHistoryRestart(), + refcase=ecl_config.getRefcase(), + config_dict=config_dict, + ) + + return [ + subst_config, + site_config, + rng_config, + analysis_config, + ert_workflow_list, + hook_manager, + ert_templates, + ecl_config, + ensemble_config, + model_config, + log_config, + queue_config, + ], config_dir + + def _extract_defines(self, config): + defines = {} + if ConfigKeys.DEFINES in config: + for key in config[ConfigKeys.DEFINES]: + defines[key] = str(config[ConfigKeys.DEFINES][key]) + + return defines + + def _parse_value(self, value): + if isinstance(value, str): + return value + elif isinstance(value, list): + return [str(elem) for elem in value] + else: + return str(value) + + def _assert_keys(self, mother_key, exp_keys, keys): + if set(exp_keys) != set(keys): + err_msg = "Did expect the keys %r in %s, received %r." + raise ValueError(err_msg % (exp_keys, mother_key, keys)) + + def _extract_internals(self, config): + internal_config = [] + config_dir = os.getcwd() + + if ConfigKeys.INTERNALS in config: + intercon = config[ConfigKeys.INTERNALS] + + dir_key = ConfigKeys.CONFIG_DIRECTORY + if dir_key in intercon: + config_dir = os.path.realpath(intercon[dir_key]) + + internal_filter = [dir_key] + for key, value in intercon.items(): + if key not in internal_filter: + internal_config.append((key, self._parse_value(value))) + + internal_config.append((ConfigKeys.CONFIG_DIRECTORY, config_dir)) + return config_dir, internal_config + + def _extract_queue_system(self, config): + if ConfigKeys.QUEUE_SYSTEM not in config: + return [] + + qc = config[ConfigKeys.QUEUE_SYSTEM] + queue_config = [] + if ConfigKeys.QUEUE_OPTION in qc: + for qo in qc[ConfigKeys.QUEUE_OPTION]: + queue_options = [ + ConfigKeys.DRIVER_NAME, + ConfigKeys.OPTION, + ConfigKeys.VALUE, + ] + + self._assert_keys(ConfigKeys.QUEUE_OPTION, queue_options, qo.keys()) + + value = [str(qo[item]) for item in queue_options] + queue_config.append((ConfigKeys.QUEUE_OPTION, value)) + + queue_system_filter = [ConfigKeys.QUEUE_OPTION] + for key, value in qc.items(): + if not key in queue_system_filter: + queue_config.append((key, self._parse_value(value))) + + return queue_config + + def _extract_install_job(self, config): + if ConfigKeys.INSTALL_JOB not in config: + return [] + + ic = config[ConfigKeys.INSTALL_JOB] + job_config = [] + for job in ic: + job_options = [ConfigKeys.NAME, ConfigKeys.PATH] + + self._assert_keys(ConfigKeys.INSTALL_JOB, job_options, job.keys()) + value = [str(job[item]) for item in job_options] + job_config.append((ConfigKeys.INSTALL_JOB, value)) + + return job_config + + def _extract_simulation_job(self, config): + if ConfigKeys.SIMULATION_JOB not in config: + return [] + + ic = config[ConfigKeys.SIMULATION_JOB] + simulation_job = [] + for job in ic: + arglist = [job[ConfigKeys.NAME]] + if ConfigKeys.ARGLIST in job: + for arg in job[ConfigKeys.ARGLIST]: + arglist.append(str(arg)) + simulation_job.append((ConfigKeys.SIMULATION_JOB, arglist)) + + return simulation_job + + def _extract_forward_model(self, config): + if ConfigKeys.FORWARD_MODEL not in config: + return [] + + ic = config[ConfigKeys.FORWARD_MODEL] + forward_model_job = [] + for job in ic: + forward_model_job.append((ConfigKeys.FORWARD_MODEL, job)) + + return forward_model_job + + def _extract_logging(self, config): + if ConfigKeys.LOGGING not in config: + return [] + + logging_config = [] + for key, value in config[ConfigKeys.LOGGING].items(): + logging_config.append((key, self._parse_value(value))) + + return logging_config + + def _extract_seed(self, config): + if ConfigKeys.SEED not in config: + return [] + + seed_config = [] + for key, value in config[ConfigKeys.SEED].items(): + seed_config.append((key, self._parse_value(value))) + + return seed_config + + def _extract_run_templates(self, config): + if ConfigKeys.RUN_TEMPLATE not in config: + return [] + + template_config = [] + for rt in config[ConfigKeys.RUN_TEMPLATE]: + rt_options = [ConfigKeys.TEMPLATE, ConfigKeys.EXPORT] + + self._assert_keys(ConfigKeys.RUN_TEMPLATE, rt_options, rt.keys()) + + value = [rt[option] for option in rt_options] + template_config.append((ConfigKeys.RUN_TEMPLATE, value)) + + return template_config + + def _extract_gen_kw(self, config): + if ConfigKeys.GEN_KW not in config: + return [] + + gen_kw_config = [] + for gk in config[ConfigKeys.GEN_KW]: + gen_kw_options = [ + ConfigKeys.NAME, + ConfigKeys.TEMPLATE, + ConfigKeys.OUT_FILE, + ConfigKeys.PARAMETER_FILE, + ] + + self._assert_keys(ConfigKeys.GEN_KW, gen_kw_options, gk.keys()) + + value = [gk[item] for item in gen_kw_options] + gen_kw_config.append((ConfigKeys.GEN_KW, value)) + + return gen_kw_config + + def _extract_gen_data(self, config): + if ConfigKeys.GEN_DATA not in config: + return [] + + gen_data_config = [] + for gd in config[ConfigKeys.GEN_DATA]: + req_keys = [ + ConfigKeys.NAME, + ConfigKeys.RESULT_FILE, + ConfigKeys.REPORT_STEPS, + ] + + default_opt = {ConfigKeys.INPUT_FORMAT: "ASCII"} + + if not sorted(req_keys) == sorted(gd.keys()): + err_msg = "Expected keys %r when creating GEN_DATA, received %r" + raise KeyError(err_msg % (req_keys, gd)) + + value = [gd[ConfigKeys.NAME]] + value += ["%s:%s" % (key, gd[key]) for key in req_keys[1:]] + value += ["%s:%s" % (key, val) for key, val in default_opt.items()] + gen_data_config.append((ConfigKeys.GEN_DATA, value)) + + return gen_data_config + + def _extract_simulation(self, config): + if ConfigKeys.SIMULATION not in config: + return [] + + simulation_config = [] + sc = config[ConfigKeys.SIMULATION] + sim_filter = [] + + # Extract queue system + sim_filter.append(ConfigKeys.QUEUE_SYSTEM) + simulation_config += self._extract_queue_system(sc) + + # Extract install job + sim_filter.append(ConfigKeys.INSTALL_JOB) + simulation_config += self._extract_install_job(sc) + + # Extract forward_model + sim_filter.append(ConfigKeys.FORWARD_MODEL) + simulation_config += self._extract_forward_model(sc) + + # Extract simulation_job + sim_filter.append(ConfigKeys.SIMULATION_JOB) + simulation_config += self._extract_simulation_job(sc) + + # Extract logging + sim_filter.append(ConfigKeys.LOGGING) + simulation_config += self._extract_logging(sc) + + # Extract seed + sim_filter.append(ConfigKeys.SEED) + simulation_config += self._extract_seed(sc) + + # Extract run templates + sim_filter.append(ConfigKeys.RUN_TEMPLATE) + simulation_config += self._extract_run_templates(sc) + + # Extract GEN_KW + sim_filter.append(ConfigKeys.GEN_KW) + simulation_config += self._extract_gen_kw(sc) + + # Extract GEN_DATA + sim_filter.append(ConfigKeys.GEN_DATA) + simulation_config += self._extract_gen_data(sc) + + # Others + for key, value in sc.items(): + if not key in sim_filter: + simulation_config.append((key, self._parse_value(value))) + + return simulation_config + + def _extract_config(self, config): + defines = self._extract_defines(config) + key_filter = [ConfigKeys.DEFINES] + + new_config = [] + + # Extract internals + key_filter.append(ConfigKeys.INTERNALS) + config_dir, internal_config = self._extract_internals(config) + new_config += internal_config + + # Extract simulation + key_filter.append(ConfigKeys.SIMULATION) + new_config += self._extract_simulation(config) + + # Unrecognized keys + for key, value in config.items(): + if key not in key_filter: + self._failed_keys[key] = value + + return defines, config_dir, new_config + + def _build_config_content(self, config): + self._failed_keys = {} + defines, config_dir, config_list = self._extract_config(config) + + config_parser = ResConfig.config_parser() + config_content = ConfigContent(None) + config_content.setParser(config_parser) + + # Insert defines + for key in defines: + config_content.add_define(key, defines[key]) + + # Insert key values + if not os.path.exists(config_dir): + raise IOError( + "The configuration direcetory: %s does not exist" % config_dir + ) + + path_elm = config_content.create_path_elm(config_dir) + add_key_value = lambda key, value: config_parser.add_key_value( + config_content, key, StringList([key] + value), path_elm=path_elm + ) + + for key, value in config_list: + if isinstance(value, str): + value = [value] + if not isinstance(value, list): + raise ValueError( + "Expected value to be str or list, was %r" % (type(value)) + ) + + ok = add_key_value(key, value) + if not ok: + self._failed_keys[key] = value + + config_parser.validate(config_content) + self._errors = list(config_content.getErrors()) + + return config_content + + def free(self): + self._free() + + @classmethod + def config_parser(cls): + parser = ConfigParser() + cls._init_parser(parser) + return parser + + @property + def errors(self): + return self._errors + + @property + def failed_keys(self): + return self._failed_keys + + @property + def user_config_file(self): + return self._user_config_file() + + @property + def site_config_file(self): + return self.site_config.config_file + + @property + def site_config(self): + return self._site_config() + + @property + def analysis_config(self): + return self._analysis_config() + + @property + def config_path(self): + return self._config_path() + + @property + def subst_config(self): + return self._subst_config().setParent(self) + + @property + def model_config(self): + return self._model_config() + + @property + def ecl_config(self): + return self._ecl_config() + + @property + def ensemble_config(self): + return self._ensemble_config() + + @property + def hook_manager(self): + return self._hook_manager() + + @property + def ert_workflow_list(self): + return self._ert_workflow_list() + + @property + def rng_config(self): + return self._rng_config() + + @property + def ert_templates(self): + return self._ert_templates() + + @property + def log_config(self): + return self._log_config() + + @property + def queue_config(self): + return self._queue_config() + + def __eq__(self, other): + # compare each config separatelly + config_eqs = ( + (self.subst_config == other.subst_config), + (self.site_config == other.site_config), + (self.rng_config == other.rng_config), + (self.analysis_config == other.analysis_config), + (self.ert_workflow_list == other.ert_workflow_list), + (self.hook_manager == other.hook_manager), + (self.ert_templates == other.ert_templates), + (self.ecl_config == other.ecl_config), + (self.ensemble_config == other.ensemble_config), + (self.model_config == other.model_config), + (self.log_config == other.log_config), + (self.queue_config == other.queue_config), + ) + + if not all(config_eqs): + return False + + return True + + def __ne__(self, other): + return not self == other diff --git a/res/enkf/rng_config.py b/res/enkf/rng_config.py new file mode 100644 index 00000000000..f9d1c607d1f --- /dev/null +++ b/res/enkf/rng_config.py @@ -0,0 +1,72 @@ +# Copyright (C) 2017 Equinor ASA, Norway. +# +# The file 'rng_config.py' 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. + + +from cwrap import BaseCClass + +from res import ResPrototype +from res.enkf import ConfigKeys + + +class RNGConfig(BaseCClass): + TYPE_NAME = "rng_config" + _alloc = ResPrototype("void* rng_config_alloc(config_content)", bind=False) + _alloc_full = ResPrototype("void* rng_config_alloc_full(char*)", bind=False) + _free = ResPrototype("void rng_config_free(rng_config)") + _rng_alg_type = ResPrototype("rng_alg_type_enum rng_config_get_type(rng_config)") + _random_seed = ResPrototype("char* rng_config_get_random_seed(rng_config)") + + def __init__(self, config_content=None, config_dict=None): + if config_content and config_dict: + raise ValueError("RNGConfig can not be instantiated with both config types") + + if not (config_content or config_dict): + raise ValueError( + "RNGConfig can not be instantiated without any config objects" + ) + + if config_content: + c_ptr = self._alloc(config_content) + elif config_dict: + random_seed = config_dict.get(ConfigKeys.RANDOM_SEED) + c_ptr = self._alloc_full(random_seed) + else: + c_ptr = None + + if c_ptr is None: + raise ValueError("Failed to construct RNGConfig instance") + + super(RNGConfig, self).__init__(c_ptr) + + @property + def alg_type(self): + return self._rng_alg_type() + + @property + def random_seed(self): + return self._random_seed() + + def free(self): + self._free() + + def __eq__(self, other): + if self.random_seed != other.random_seed: + return False + + if self.alg_type != other.alg_type: + return False + + return True diff --git a/res/enkf/row_scaling.py b/res/enkf/row_scaling.py new file mode 100644 index 00000000000..6e744cfe96d --- /dev/null +++ b/res/enkf/row_scaling.py @@ -0,0 +1,164 @@ +# Copyright (C) 2020 Equinor ASA, Norway. +# +# The file 'row_scaling.py' 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. + +import numpy as np +import ctypes +from cwrap import BaseCClass +from res import ResPrototype + + +class RowScaling(BaseCClass): + TYPE_NAME = "row_scaling" + + _alloc = ResPrototype("void* row_scaling_alloc()", bind=False) + _free = ResPrototype("void row_scaling_free(row_scaling)") + _size = ResPrototype("int row_scaling_get_size(row_scaling)") + _iset = ResPrototype("double row_scaling_iset(row_scaling, int, double)") + _iget = ResPrototype("double row_scaling_iget(row_scaling, int)") + _clamp = ResPrototype("double row_scaling_clamp(row_scaling, double)") + _assign_double_vector = ResPrototype( + "void row_scaling_assign_double(row_scaling, double*, int)" + ) + _assign_float_vector = ResPrototype( + "void row_scaling_assign_float(row_scaling, float*, int)" + ) + + def __init__(self): + c_ptr = self._alloc() + super(RowScaling, self).__init__(c_ptr) + + def free(self): + self._free() + + def __len__(self): + return self._size() + + def __setitem__(self, index, value): + self._iset(index, value) + + def __getitem__(self, index): + if index < len(self): + return self._iget(index) + + raise IndexError( + "Index: {} outside valid range [0,{}>".format(index, len(self)) + ) + + def clamp(self, value): + return self._clamp(value) + + def assign(self, target_size, func): + """Assign tapering value for all elements. + + The assign() method is the main function used to assign a row scaling + value to be used as tapering in the update. The first argument is the + number of elements in the target parameter, and the second argument is + a callable which will be called with element index as argument. + + In the example below we will assume that tapering is for a field + variable and we will scale the update with the function exp( -r/r0 ) + where r is the distance from some point and r0 is length scale. + + def sqr(x): + return x*x + + def exp_decay(grid, pos, r0, data_index): + x,y,z = grid.get_xyz( active_index = data_index) + r = math.sqrt( sqr(pos[0] - x) + sqr(pos[1] - y) + sqr(pos[2] - z)) + return math.exp( -r/r0 ) + + + ens_config = ert.ensembleConfig() + config_node = ens_config["PORO"] + field_config = config.node.getFieldModelConfig() + grid = ert.eclConfig().getGrid() + pos = grid.get_xyz(ijk=(10,10,1)) + r0 = 10 + + if grid.get_num_active() != field_config.get_data_size(): + raise ValuError("Fatal error - inconsistent field size for: {}".format(config_node.getKey()) + + # Some local configuration boilerplate has been skipped here. + local_config = main.getLocalConfig() + local_data = local_config.createDataset("LOCAL") + row_scaling = local_data.row_scaling("PORO") + row_scaling.assign( field_config.get_data_size(), functools.partial(exp_decay, grid, pos, r0)) + + + In the example below functools.partial() is used to create a callable + which has access to the necessary context, another alternative would be + to use a class instance which implements the __call__() method. + + It is an important point that the assign() method does not have any + context for error checking, i.e. if you call it with an incorrect value + for the size argument things will silently pass initially but might + blow up in the subsequent update step. + + """ + + for index in range(target_size): + self[index] = func(index) + + def assign_vector(self, scaling_vector): + """Assign tapering value for all elements via a vector. + + The assign_vector() function will resize the row_scaling vector to the + number of elements in the scaling_vector, and assign a value to all + elements. + + A typical situation might be to load the scaling data as a EclKW + instance from a grdecl formatted file. Before the assign_vector() can + be called we must transform to an only active elements representation + and use a numpy view: + + # Load scaling vector from grdecl file; typically created with + # geomodelling software. + with open("scaling.grdecl") as fh: + kw_global = EclKW.read_grdecl(fh, "SCALING") + + # Create a ecl_kw copy with only the active elements. + kw_active = grid.compressed_kw_copy(kw_global) + + # Create a numpy view and invoke the assign_vector() function + row_scaling.assign_vector(kw_active.numpy_view()) + + """ + # In current implementation the scaling vector must be of type + # numpy.array(float32/float64); it would possible to also accept ecl_kw + # and call the same underlying C functions. Currently the expected + # approach is rather that calling scope should convert ecl_kw -> numpy. + # + # In addition it would be possible to also accept a general Python + # iterable and repeatedly call __setitem__() from the assign_vector + # implementation. This solution has been avoided has been avoided + # because it would not be very performant. + if isinstance(scaling_vector, np.ndarray): + if scaling_vector.dtype == np.float64: + func = self._assign_double_vector + ptr = scaling_vector.ctypes.data_as(ctypes.POINTER(ctypes.c_double)) + elif scaling_vector.dtype == np.float32: + func = self._assign_float_vector + ptr = scaling_vector.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) + else: + raise TypeError( + f"The scaling_vector must be float32/float64, not {type(scaling_vector.dtype)}" + ) + + func(ptr, len(scaling_vector)) + else: + raise TypeError( + f"The assign_vector method expects a numpy vector as argument, not {type(scaling_vector)}" + ) diff --git a/res/enkf/run_arg.py b/res/enkf/run_arg.py new file mode 100644 index 00000000000..3b160a95da2 --- /dev/null +++ b/res/enkf/run_arg.py @@ -0,0 +1,106 @@ +# Copyright (C) 2014 Equinor ASA, Norway. +# +# The file 'run_arg.py' 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. + +from cwrap import BaseCClass +from res import ResPrototype + + +class RunArg(BaseCClass): + TYPE_NAME = "run_arg" + + _alloc_ENSEMBLE_EXPERIMENT = ResPrototype( + "run_arg_obj run_arg_alloc_ENSEMBLE_EXPERIMENT(char*, enkf_fs, int, int, char*, char*, subst_list)", + bind=False, + ) + _free = ResPrototype("void run_arg_free(run_arg)") + _get_queue_index_safe = ResPrototype("int run_arg_get_queue_index_safe(run_arg)") + _set_queue_index = ResPrototype("void run_arg_set_queue_index(run_arg, int)") + _is_submitted = ResPrototype("bool run_arg_is_submitted(run_arg)") + _get_run_id = ResPrototype("char* run_arg_get_run_id(run_arg)") + _get_geo_id = ResPrototype("int run_arg_get_geo_id(run_arg)") + _set_geo_id = ResPrototype("void run_arg_set_geo_id(run_arg, int)") + _get_runpath = ResPrototype("char* run_arg_get_runpath(run_arg)") + _get_iter = ResPrototype("int run_arg_get_iter(run_arg)") + _get_iens = ResPrototype("int run_arg_get_iens(run_arg)") + _get_status = ResPrototype("int run_arg_get_run_status(run_arg)") + _get_job_name = ResPrototype("char* run_arg_get_job_name(run_arg)") + + def __init__(self): + raise NotImplementedError("Cannot instantiat RunArg directly!") + + @classmethod + def createEnsembleExperimentRunArg( + cls, run_id, fs, iens, runpath, jobname, subst_list, iter=0 + ): + return cls._alloc_ENSEMBLE_EXPERIMENT( + run_id, fs, iens, iter, runpath, jobname, subst_list + ) + + def free(self): + self._free() + + def set_queue_index(self, index): + self._set_queue_index(index) + + def getQueueIndex(self): + qi = self._get_queue_index_safe() + if qi < 0: + raise ValueError("Cannot get queue index before job is submitted.") + return qi + + def isSubmitted(self): + return self._is_submitted() + + def __repr__(self): + if self.isSubmitted(): + su = "submitted" + qi = self.getQueueIndex() + else: + su = "not submitted" + qi = "--" + + return "RunArg(queue_index = %s, %s) %s" % (qi, su, self._ad_str()) + + def get_run_id(self): + return self._get_run_id() + + @property + def geo_id(self): + return self._get_geo_id() + + @geo_id.setter + def geo_id(self, value): + self._set_geo_id(value) + + @property + def runpath(self): + return self._get_runpath() + + @property + def iter_id(self): + return self._get_iter() + + @property + def iens(self): + return self._get_iens() + + @property + def run_status(self): + return self._get_status() + + @property + def job_name(self): + return self._get_job_name() diff --git a/res/enkf/runpath_list.py b/res/enkf/runpath_list.py new file mode 100644 index 00000000000..b18ff0e2578 --- /dev/null +++ b/res/enkf/runpath_list.py @@ -0,0 +1,94 @@ +from collections import namedtuple +from cwrap import BaseCClass +from res import ResPrototype + +RunpathNode = namedtuple( + "RunpathNode", ["realization", "iteration", "runpath", "basename"] +) + + +class RunpathList(BaseCClass): + TYPE_NAME = "runpath_list" + _alloc = ResPrototype("void* runpath_list_alloc(char*)", bind=False) + _free = ResPrototype("void runpath_list_free(runpath_list)") + _add = ResPrototype("void runpath_list_add(runpath_list, int, int, char*, char*)") + _clear = ResPrototype("void runpath_list_clear(runpath_list)") + _size = ResPrototype("int runpath_list_size(runpath_list)") + _iens = ResPrototype("int runpath_list_iget_iens(runpath_list, int)") + _iteration = ResPrototype("int runpath_list_iget_iter(runpath_list, int)") + _runpath = ResPrototype("char* runpath_list_iget_runpath(runpath_list, int)") + _basename = ResPrototype("char* runpath_list_iget_basename(runpath_list, int)") + _export = ResPrototype("void runpath_list_fprintf(runpath_list)") + _load = ResPrototype("bool runpath_list_load(runpath_list)") + + _get_export_file = ResPrototype("char* runpath_list_get_export_file(runpath_list)") + _set_export_file = ResPrototype( + "void runpath_list_set_export_file(runpath_list, char*)" + ) + + def __init__(self, export_file): + c_ptr = self._alloc(export_file) + if c_ptr: + super(RunpathList, self).__init__(c_ptr) + else: + raise IOError( + 'Could not construct RunpathList with export_file "%s".' % export_file + ) + + def __len__(self): + return self._size() + + def __getitem__(self, index): + """@rtype: RunpathNode""" + ls = len(self) + if isinstance(index, int): + idx = index + if idx < 0: + idx += ls + if not 0 <= idx < ls: + raise IndexError("Index not in range: 0 <= %d < %d" % (index, ls)) + realization = self._iens(idx) + iteration = self._iteration(idx) + runpath = self._runpath(idx) + basename = self._basename(idx) + return RunpathNode(realization, iteration, runpath, basename) + elif isinstance(index, slice): + return [self[i] for i in range(*index.indices(ls))] + raise TypeError("List indices must be integers, not %s." % str(type(index))) + + def __iter__(self): + index = 0 + while index < len(self): + yield self[index] + index += 1 + + def getExportFile(self): + return self._get_export_file() + + def setExportFile(self, export_file): + self._set_export_file(export_file) + + def add(self, realization_number, iteration_number, runpath, basename): + """ + @type realization_number: int + @type iteration_number: int + @type runpath: int + @type basename: int + """ + self._add(realization_number, iteration_number, runpath, basename) + + def clear(self): + self._clear() + + def free(self): + self._free() + + def __repr__(self): + return "RunpathList(size = %d) %s" % (len(self), self._ad_str()) + + def export(self): + self._export() + + def load(self): + if not self._load(): + raise IOError("Could not load from:%s" % self._get_export_file()) diff --git a/res/enkf/site_config.py b/res/enkf/site_config.py new file mode 100644 index 00000000000..d491d6b8472 --- /dev/null +++ b/res/enkf/site_config.py @@ -0,0 +1,217 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'site_config.py' 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. + +import os +from cwrap import BaseCClass +from res import ResPrototype +from res.enkf import ConfigKeys +from res.job_queue import ExtJob, ExtJoblist, EnvironmentVarlist + + +class SiteConfig(BaseCClass): + TYPE_NAME = "site_config" + _alloc = ResPrototype("void* site_config_alloc(config_content)", bind=False) + _alloc_full = ResPrototype( + "void* site_config_alloc_full(ext_joblist, env_varlist, int)", bind=False + ) + _alloc_load_user_config = ResPrototype( + "void* site_config_alloc_load_user_config(char*)", bind=False + ) + _free = ResPrototype("void site_config_free( site_config )") + _get_installed_jobs = ResPrototype( + "ext_joblist_ref site_config_get_installed_jobs(site_config)" + ) + _get_license_root_path = ResPrototype( + "char* site_config_get_license_root_path(site_config)" + ) + _set_license_root_path = ResPrototype( + "void site_config_set_license_root_path(site_config, char*)" + ) + _get_location = ResPrototype("char* site_config_get_location()", bind=False) + _get_config_file = ResPrototype("char* site_config_get_config_file(site_config)") + _get_umask = ResPrototype("int site_config_get_umask(site_config)") + + def __init__(self, user_config_file=None, config_content=None, config_dict=None): + + configs = sum( + [ + 1 + for x in [user_config_file, config_content, config_dict] + if x is not None + ] + ) + + if configs > 1: + raise ValueError( + "Attempting to construct SiteConfig with multiple config objects" + ) + + if configs == 0: + raise ValueError( + "Attempting to construct SiteConfig with no config objects" + ) + + c_ptr = None + if user_config_file is not None: + if not os.path.isfile(user_config_file): + raise IOError('No such configuration file "%s".' % user_config_file) + c_ptr = self._alloc_load_user_config(user_config_file) + + elif config_content is not None: + c_ptr = self._alloc(config_content) + + elif config_dict is not None: + __license_root_path = None + if ConfigKeys.LICENSE_PATH in config_dict: + license_root_path = config_dict.get(ConfigKeys.LICENSE_PATH) + license_root_path_site = os.path.realpath(license_root_path) + __license_root_path = os.path.join( + license_root_path_site, os.getenv("USER"), str(os.getpid()) + ) + + # Create joblist + ext_job_list = ExtJoblist() + for job in config_dict.get(ConfigKeys.INSTALL_JOB, []): + if not os.path.isfile(job[ConfigKeys.PATH]): + print( + "WARNING: Unable to locate job file {}".format( + job[ConfigKeys.PATH] + ) + ) + continue + try: + new_job = ExtJob( + config_file=job[ConfigKeys.PATH], + private=False, + name=job[ConfigKeys.NAME], + license_root_path=__license_root_path, + ) + new_job.convertToCReference(None) + ext_job_list.add_job(job[ConfigKeys.NAME], new_job) + except: + print( + "WARNING: Unable to create job from {}".format( + job[ConfigKeys.PATH] + ) + ) + + for job_path in config_dict.get(ConfigKeys.INSTALL_JOB_DIRECTORY, []): + if not os.path.isdir(job_path): + print("WARNING: Unable to locate job directory {}".format(job_path)) + continue + files = os.listdir(job_path) + for file_name in files: + full_path = os.path.join(job_path, file_name) + if os.path.isfile(full_path): + try: + new_job = ExtJob( + config_file=full_path, + private=False, + license_root_path=__license_root_path, + ) + new_job.convertToCReference(None) + ext_job_list.add_job(new_job.name(), new_job) + except: + print( + "WARNING: Unable to create job from {}".format( + full_path + ) + ) + + ext_job_list.convertToCReference(None) + + # Create varlist) + env_var_list = EnvironmentVarlist() + for (var, value) in config_dict.get(ConfigKeys.SETENV, []): + env_var_list[var] = value + + env_var_list.convertToCReference(None) + umask = config_dict.get(ConfigKeys.UMASK) + + c_ptr = self._alloc_full(ext_job_list, env_var_list, umask) + + if c_ptr is None: + raise ValueError("Failed to construct SiteConfig instance.") + + super(SiteConfig, self).__init__(c_ptr) + + def __repr__(self): + return "Site Config {}".format(SiteConfig.getLocation()) + + @property + def config_file(self): + return self._get_config_file() + + def get_installed_jobs(self): + """@rtype: ExtJoblist""" + return self._get_installed_jobs().setParent(self) + + def get_license_root_path(self): + """@rtype: str""" + return self._get_license_root_path() + + def set_license_root_pathmax_submit(self, path): + self._set_license_root_path(path) + + @classmethod + def getLocation(cls): + """@rtype: str""" + return cls._get_location() + + def free(self): + self._free() + + @property + def umask(self): + return self._get_umask() + + def __eq__(self, other): + if self.umask != other.umask: + return False + + self_job_list = self.get_installed_jobs() + other_job_list = other.get_installed_jobs() + + if set(other_job_list.getAvailableJobNames()) != set( + self_job_list.getAvailableJobNames() + ): + return False + + if len(other_job_list.getAvailableJobNames()) != len( + self_job_list.getAvailableJobNames() + ): + return False + + for job_name in other_job_list.getAvailableJobNames(): + + if ( + other_job_list[job_name].get_config_file() + != self_job_list[job_name].get_config_file() + ): + return False + + if ( + other_job_list[job_name].get_stderr_file() + != self_job_list[job_name].get_stderr_file() + ): + return False + + if ( + other_job_list[job_name].get_stdout_file() + != self_job_list[job_name].get_stdout_file() + ): + return False + return True diff --git a/res/enkf/state_map.py b/res/enkf/state_map.py new file mode 100644 index 00000000000..63674138d2a --- /dev/null +++ b/res/enkf/state_map.py @@ -0,0 +1,163 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'enkf_fs.py' 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. +from cwrap import BaseCClass +from res import ResPrototype +from res.enkf.enums import RealizationStateEnum +from ecl.util.util import BoolVector + + +class StateMap(BaseCClass): + TYPE_NAME = "state_map" + + _alloc = ResPrototype("void* state_map_alloc()", bind=False) + _fread = ResPrototype("bool state_map_fread(state_map , char*)") + _fwrite = ResPrototype("void state_map_fwrite(state_map , char*)") + _equal = ResPrototype("bool state_map_equal(state_map , state_map)") + _free = ResPrototype("void state_map_free(state_map)") + _size = ResPrototype("int state_map_get_size(state_map)") + _iget = ResPrototype("realisation_state_enum state_map_iget(state_map, int)") + _iset = ResPrototype("void state_map_iset(state_map, int, realisation_state_enum)") + _select_matching = ResPrototype( + "void state_map_select_matching(state_map, bool_vector, realisation_state_enum)" + ) + _deselect_matching = ResPrototype( + "void state_map_deselect_matching(state_map, bool_vector, realisation_state_enum)" + ) + _is_read_only = ResPrototype("bool state_map_is_readonly(state_map)") + _is_legal_transition = ResPrototype( + "bool state_map_legal_transition(realisation_state_enum, realisation_state_enum)", + bind=False, + ) + + def __init__(self, filename=None): + c_ptr = self._alloc() + super(StateMap, self).__init__(c_ptr) + if filename: + self.load(filename) + + def __len__(self): + """@rtype: int""" + return self._size() + + def __iter__(self): + index = 0 + size = len(self) + + while index < size: + yield self[index] + index += 1 + + def __eq__(self, other): + return self._equal(other) + + def __getitem__(self, index): + """@rtype: RealizationStateEnum""" + if not isinstance(index, int): + raise TypeError("Expected an integer") + + size = len(self) + if index < 0: + index += size + if 0 <= index < size: + return self._iget(index) + raise IndexError("Invalid index. Valid range: [0, %d)" % size) + + def __setitem__(self, index, value): + if self.isReadOnly(): + raise UserWarning("This State Map is read only!") + + if not isinstance(index, int): + raise TypeError("Expected an integer") + + if not isinstance(value, RealizationStateEnum): + raise TypeError("Expected a RealizationStateEnum") + + if index < 0: + index += len(self) + if index < 0: + raise IndexError("Index out of range: %d < 0" % index) + + self._iset(index, value) + + @classmethod + def isLegalTransition(cls, realization_state1, realization_state2): + """@rtype: bool""" + + if not isinstance(realization_state1, RealizationStateEnum) or not isinstance( + realization_state2, RealizationStateEnum + ): + raise TypeError("Expected a RealizationStateEnum") + + return cls._is_legal_transition(realization_state1, realization_state2) + + def isReadOnly(self): + """@rtype: bool""" + return self._is_read_only() + + def selectMatching(self, select_target, select_mask): + """ + @type select_target: BoolVector + @type select_mask: RealizationStateEnum + """ + assert isinstance(select_target, BoolVector) + assert isinstance(select_mask, RealizationStateEnum) + + self._select_matching(select_target, select_mask) + + def deselectMatching(self, select_target, select_mask): + """ + @type select_target: BoolVector + @type select_mask: RealizationStateEnum + """ + assert isinstance(select_target, BoolVector) + assert isinstance(select_mask, RealizationStateEnum) + + self._deselect_matching(select_target, select_mask) + + def realizationList(self, state_value): + """ + Will create a list of all realisations with state equal to state_value. + + @type state_value: RealizationStateEnum + @rtype: ecl.util.IntVector + """ + mask = self.createMask(state_value) + return BoolVector.createActiveList(mask) + + def createMask(self, state_value): + """ + Will create a bool vector of all realisations with state equal to state_value. + + @type state_value: RealizationStateEnum + @rtype: ecl.util.BoolVector + """ + mask = BoolVector(False, len(self)) + self.selectMatching(mask, state_value) + return mask + + def free(self): + self._free() + + def __repr__(self): + ro = "read only" if self.isReadOnly() else "read/write" + return "StateMap(size = %d, %s) %s" % (len(self), ro, self._ad_str()) + + def load(self, filename): + if not self._fread(filename): + raise IOError("Failed to load state map from:%s" % filename) + + def save(self, filename): + self._fwrite(filename) diff --git a/res/enkf/subst_config.py b/res/enkf/subst_config.py new file mode 100644 index 00000000000..ef13e23f3f3 --- /dev/null +++ b/res/enkf/subst_config.py @@ -0,0 +1,163 @@ +# Copyright (C) 2017 Equinor ASA, Norway. +# +# The file 'subst_config.py' 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. + +import os.path + +from cwrap import BaseCClass +from res import ResPrototype +from ecl import EclPrototype +from res.enkf import ConfigKeys +from res.util import SubstitutionList + + +class SubstConfig(BaseCClass): + TYPE_NAME = "subst_config" + _alloc = ResPrototype("void* subst_config_alloc(config_content)", bind=False) + _alloc_full = ResPrototype("void* subst_config_alloc_full(subst_list)", bind=False) + _free = ResPrototype("void subst_config_free(subst_config)") + _get_subst_list = ResPrototype( + "subst_list_ref subst_config_get_subst_list( subst_config )" + ) + _get_num_cpu = EclPrototype("int ecl_util_get_num_cpu(char*)", bind=False) + + def __init__(self, config_content=None, config_dict=None): + if not ((config_content is not None) ^ (config_dict is not None)): + raise ValueError( + "SubstConfig must be instansiated with exactly one of config_content or config_dict" + ) + + if config_dict is not None: + subst_list = SubstitutionList() + + # DIRECTORY # + config_directory = config_dict.get(ConfigKeys.CONFIG_DIRECTORY) + if isinstance(config_directory, str): + subst_list.addItem( + "", + config_directory, + "The current working directory we are running from - the location of the config file.", + ) + subst_list.addItem( + "", + config_directory, + "The current working directory we are running from - the location of the config file.", + ) + else: + raise ValueError( + "{} must be configured".format(ConfigKeys.CONFIG_DIRECTORY) + ) + + # FILE # + filename = config_dict.get(ConfigKeys.CONFIG_FILE_KEY) + if isinstance(filename, str): + subst_list.addItem("", filename) + subst_list.addItem("", os.path.splitext(filename)[0]) + + # CONSTANTS # + constants = config_dict.get(ConfigKeys.DEFINE_KEY) + if isinstance(constants, dict): + for key in constants: + subst_list.addItem(key, constants[key]) + + # DATA_KW + data_kw = config_dict.get(ConfigKeys.DATA_KW_KEY) + if isinstance(data_kw, dict): + for key, value in data_kw.items(): + subst_list.addItem(key, value) + + # RUNPATH_FILE # + runpath_file_name = config_dict.get( + ConfigKeys.RUNPATH_FILE, ConfigKeys.RUNPATH_LIST_FILE + ) + runpath_file_path = os.path.normpath( + os.path.join(config_directory, runpath_file_name) + ) + subst_list.addItem( + "", + runpath_file_path, + "The name of a file with a list of run directories.", + ) + + # Read num_cpu from Eclipse DATA_FILE + if ConfigKeys.DATA_FILE in config_dict: + file_path = os.path.join( + config_directory, config_dict[ConfigKeys.DATA_FILE] + ) + + if os.path.isfile(file_path) and os.access(file_path, os.R_OK): + num_cpu = self._get_num_cpu(file_path) + subst_list.addItem( + "", + "{}".format(num_cpu), + "The number of CPU used for one forward model.", + ) + else: + raise IOError( + "Could not find ECLIPSE data file: {}".format(file_path) + ) + + c_ptr = self._alloc_full(subst_list) + + else: + c_ptr = self._alloc(config_content) + + if c_ptr is None: + raise ValueError("Failed to construct Substonfig instance") + + super(SubstConfig, self).__init__(c_ptr) + + def __getitem__(self, key): + subst_list = self._get_subst_list() + return subst_list[key] + + def __iter__(self): + subst_list = self._get_subst_list() + return iter(subst_list) + + @property + def subst_list(self): + return self._get_subst_list().setParent(self) + + def free(self): + self._free() + + def __eq__(self, other): + list1 = self.subst_list + list2 = other.subst_list + if len(list1.keys()) != len(list2.keys()): + return False + for key in list1.keys(): + val1 = list1.get(key) + val2 = list2.get(key) + if val1 != val2: + return False + + return True + + def __ne__(self, other): + return not self == other + + def __str__(self): + return ( + "[" + + ",\n".join( + [ + "({}, {}, {})".format(key, value, doc) + for key, value, doc in self.subst_list + ] + ) + + "]" + ) diff --git a/res/enkf/summary_key_matcher.py b/res/enkf/summary_key_matcher.py new file mode 100644 index 00000000000..6baebe1d37c --- /dev/null +++ b/res/enkf/summary_key_matcher.py @@ -0,0 +1,49 @@ +from cwrap import BaseCClass +from res import ResPrototype +from ecl.util.util import StringList + + +class SummaryKeyMatcher(BaseCClass): + TYPE_NAME = "summary_key_matcher" + + _alloc = ResPrototype("void* summary_key_matcher_alloc()", bind=False) + _free = ResPrototype("void summary_key_matcher_free(summary_key_matcher)") + _size = ResPrototype("int summary_key_matcher_get_size(summary_key_matcher)") + _add_key = ResPrototype( + "void summary_key_matcher_add_summary_key(summary_key_matcher, char*)" + ) + _match_key = ResPrototype( + "bool summary_key_matcher_match_summary_key(summary_key_matcher, char*)" + ) + _keys = ResPrototype( + "stringlist_obj summary_key_matcher_get_keys(summary_key_matcher)" + ) + _is_required = ResPrototype( + "bool summary_key_matcher_summary_key_is_required(summary_key_matcher, char*)" + ) + + def __init__(self): + c_ptr = self._alloc() + + super(SummaryKeyMatcher, self).__init__(c_ptr) + + def addSummaryKey(self, key): + assert isinstance(key, str) + return self._add_key(key) + + def __len__(self): + return self._size() + + def __contains__(self, key): + return self._match_key(key) + + def isRequired(self, key): + """@rtype: bool""" + return self._is_required(key) + + def keys(self): + """@rtype: StringList""" + return self._keys() + + def free(self): + self._free() diff --git a/res/enkf/summary_key_set.py b/res/enkf/summary_key_set.py new file mode 100644 index 00000000000..76069b7bf16 --- /dev/null +++ b/res/enkf/summary_key_set.py @@ -0,0 +1,56 @@ +from cwrap import BaseCClass +from res import ResPrototype +from ecl.util.util import StringList + + +class SummaryKeySet(BaseCClass): + TYPE_NAME = "summary_key_set" + + _alloc = ResPrototype("void* summary_key_set_alloc()", bind=False) + _alloc_from_file = ResPrototype( + "void* summary_key_set_alloc_from_file(char*, bool)", bind=False + ) + _free = ResPrototype("void summary_key_set_free(summary_key_set)") + _size = ResPrototype("int summary_key_set_get_size(summary_key_set)") + _add_key = ResPrototype( + "bool summary_key_set_add_summary_key(summary_key_set, char*)" + ) + _has_key = ResPrototype( + "bool summary_key_set_has_summary_key(summary_key_set, char*)" + ) + _keys = ResPrototype("stringlist_obj summary_key_set_alloc_keys(summary_key_set)") + _is_read_only = ResPrototype("bool summary_key_set_is_read_only(summary_key_set)") + _fwrite = ResPrototype("void summary_key_set_fwrite(summary_key_set, char*)") + + def __init__(self, filename=None, read_only=False): + if filename is None: + c_ptr = self._alloc() + else: + c_ptr = self._alloc_from_file(filename, read_only) + + super(SummaryKeySet, self).__init__(c_ptr) + + def addSummaryKey(self, key): + assert isinstance(key, str) + return self._add_key(key) + + def __len__(self): + return self._size() + + def __contains__(self, key): + return self._has_key(key) + + def keys(self): + """@rtype: StringList""" + return self._keys() + + def isReadOnly(self): + """@rtype: bool""" + return self._is_read_only() + + def writeToFile(self, filename): + assert isinstance(filename, str) + self._fwrite(filename) + + def free(self): + self._free() diff --git a/res/enkf/util/__init__.py b/res/enkf/util/__init__.py new file mode 100644 index 00000000000..45ad56e0816 --- /dev/null +++ b/res/enkf/util/__init__.py @@ -0,0 +1 @@ +from .time_map import TimeMap diff --git a/res/enkf/util/time_map.py b/res/enkf/util/time_map.py new file mode 100644 index 00000000000..1b108ed32bb --- /dev/null +++ b/res/enkf/util/time_map.py @@ -0,0 +1,206 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'time_map.py' 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. +import os +import errno + +from cwrap import BaseCClass +from res import ResPrototype +from ecl.util.util import CTime + + +class TimeMap(BaseCClass): + TYPE_NAME = "time_map" + + _fread_alloc_readonly = ResPrototype( + "void* time_map_fread_alloc_readonly(char*)", bind=False + ) + _alloc = ResPrototype("void* time_map_alloc()", bind=False) + _load = ResPrototype("bool time_map_fread(time_map , char*)") + _save = ResPrototype("void time_map_fwrite(time_map , char*)") + _fload = ResPrototype("bool time_map_fscanf(time_map , char*)") + _iget_sim_days = ResPrototype("double time_map_iget_sim_days(time_map, int)") + _iget = ResPrototype("time_t time_map_iget(time_map, int)") + _size = ResPrototype("int time_map_get_size(time_map)") + _try_update = ResPrototype("bool time_map_try_update(time_map , int , time_t)") + _is_strict = ResPrototype("bool time_map_is_strict( time_map )") + _set_strict = ResPrototype("void time_map_set_strict( time_map , bool)") + _lookup_time = ResPrototype("int time_map_lookup_time( time_map , time_t)") + _lookup_time_with_tolerance = ResPrototype( + "int time_map_lookup_time_with_tolerance( time_map , time_t , int , int)" + ) + _lookup_days = ResPrototype( + "int time_map_lookup_days( time_map , double)" + ) + _last_step = ResPrototype("int time_map_get_last_step( time_map )") + _upgrade107 = ResPrototype( + "void time_map_summary_upgrade107( time_map , ecl_sum )" + ) + _free = ResPrototype("void time_map_free( time_map )") + + def __init__(self, filename=None): + c_ptr = self._alloc() + super(TimeMap, self).__init__(c_ptr) + if filename: + self.load(filename) + + def load(self, filename): + if os.path.isfile(filename): + self._load(filename) + else: + raise IOError((errno.ENOENT, "File not found: %s" % filename)) + + def fwrite(self, filename): + self._save(filename) + + def fload(self, filename): + """ + Will load a timemap as a formatted file consisting of a list of dates: DD/MM/YYYY + """ + if os.path.isfile(filename): + OK = self._fload(filename) + if not OK: + raise Exception("Error occured when loading timemap from:%s" % filename) + else: + raise IOError((errno.ENOENT, "File not found: %s" % filename)) + + def isStrict(self): + return self._is_strict() + + def setStrict(self, strict): + return self._set_strict(strict) + + def getSimulationDays(self, step): + """@rtype: double""" + if not isinstance(step, int): + raise TypeError("Expected an integer") + + size = len(self) + if step < 0 or step >= size: + raise IndexError("Index out of range: 0 <= %d < %d" % (step, size)) + + return self._iget_sim_days(step) + + def __getitem__(self, index): + """@rtype: CTime""" + if not isinstance(index, int): + raise TypeError("Expected an integer") + + size = len(self) + if index < 0 or index >= size: + raise IndexError("Index out of range: 0 <= %d < %d" % (index, size)) + + return self._iget(index) + + def __setitem__(self, index, time): + self.update(index, time) + + def update(self, index, time): + if self._try_update(index, CTime(time)): + return True + else: + if self.isStrict(): + raise Exception("Tried to update with inconsistent value") + else: + return False + + def __iter__(self): + cur = 0 + while cur < len(self): + yield self[cur] + cur += 1 + + def __contains__(self, time): + index = self._lookup_time(CTime(time)) + if index >= 0: + return True + else: + return False + + def lookupTime(self, time, tolerance_seconds_before=0, tolerance_seconds_after=0): + """Will look up the report step corresponding to input @time. + + If the tolerance arguments tolerance_seconds_before and + tolerance_seconds_after have the default value zero we require + an exact match between input time argument and the content of + the time map. + + If the tolerance arguments are supplied the function will + search through the time_map for the report step closest to the + time argument, which satisfies the tolerance criteria. + + With the call: + + lookupTime( datetime.date(2010,1,10) , 3600*24 , 3600*7) + + We will find the report step in the date interval 2010,1,9 - + 2010,1,17 which is closest to 2010,1,10. The tolerance limits + are inclusive. + + If no report step satisfying the criteria is found a + ValueError exception will be raised. + + """ + if tolerance_seconds_before == 0 and tolerance_seconds_after == 0: + index = self._lookup_time(CTime(time)) + else: + index = self._lookup_time_with_tolerance( + CTime(time), tolerance_seconds_before, tolerance_seconds_after + ) + + if index >= 0: + return index + else: + raise ValueError( + "The time:%s was not found in the time_map instance" % time + ) + + def lookupDays(self, days): + index = self._lookup_days(days) + if index >= 0: + return index + else: + raise ValueError( + "The days: %s was not found in the time_map instance" % days + ) + + def __len__(self): + """@rtype: int""" + return self._size() + + def free(self): + self._free() + + def __repr__(self): + ls = len(self) + la = self.getLastStep() + st = "strict" if self.isStrict() else "not strict" + cnt = "size = %d, last_step = %d, %s" % (ls, la, st) + return self._create_repr(cnt) + + def dump(self): + """ + Will return a list of tuples (step , CTime , days). + """ + step_list = [] + for step, t in enumerate(self): + step_list.append((step, t, self.getSimulationDays(step))) + return step_list + + def getLastStep(self): + return self._last_step() + + def upgrade107(self, refcase): + self._upgrade107(refcase) diff --git a/res/fm/__init__.py b/res/fm/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/res/fm/ecl/__init__.py b/res/fm/ecl/__init__.py new file mode 100644 index 00000000000..b6b7fd063d8 --- /dev/null +++ b/res/fm/ecl/__init__.py @@ -0,0 +1,26 @@ +from .ecl_config import Ecl300Config, Ecl100Config, FlowConfig, EclrunConfig +from .ecl_run import EclRun +from .script import run + + +# This is very deprecated, should rather use the run() method in the script +# module. That will handle argument parsing and so on. +def simulate(simulator, version, data_file, num_cpu=1, check=True): + if simulator == "ecl100": + config = Ecl100Config() + elif simulator == "flow": + config = FlowConfig() + elif simulator == "ecl300": + config = Ecl300Config() + else: + raise Exception("No such simulator: {}".format(simulator)) + + argv = [data_file, "--num-cpu={}".format(num_cpu)] + + if not version is None: + argv.append("--version={}".format(version)) + + if not check: + argv.append("--ignore-errors") + + run(config, argv) diff --git a/res/fm/ecl/ecl100_config.yml b/res/fm/ecl/ecl100_config.yml new file mode 100644 index 00000000000..59138820304 --- /dev/null +++ b/res/fm/ecl/ecl100_config.yml @@ -0,0 +1,60 @@ +# NB: There are inter related dependencies between this file, the EclConfig +# class which essentially internalizes this file and the EclRun class +# which uses the EclConfig class. +# +# The information about eclipse versions installed at a particular site is +# assembled in configuration files like this one, there are separate files for +# ecl100, ecl300 and opm/flow. The file has only one required top level element: +# 'versions' - which describe the versions which are installed. In +# additon you can optionally have a top level element 'env' which is a +# dictionary representing environment variables which should be set before the +# simulator starts. +# +# In the example below the ecl100 simulator is installed in versions '2015.2' +# and '2017.2'. Each version of the program can exist as a scalar single process +# application and a parallell MPI application. When we have drilled all the way +# down through versions['2105.2']['scalar'] the only required +# attribute is 'executable' which is the full path to the executable program. In +# addition you can add an env: setting which will be a dictionary of environment +# variables which will be set before the simulator is invoked. The 2015.2 mpi +# version has an example of such a settings. +# +# If it is an MPI simulation you must in addition to the executable attribute +# also set an 'mpirun' attribute. + +versions: + '2015.2': # without quotes this will be interpreted as a number + scalar: + executable: /path/ecl/2015.2/linux/x86_64/bin/eclipse100 + mpi: + executable: /path/ecl/2015.2/linux/x86_64/bin/eclipse100_mpi + mpirun: /path/ecl/2015.2/intel/bin/mpirun + env: + MPI_ROOT: /path/to/mpi/root + LD_LIBRARY_PATH: /path/to/mpi/root/lib64:$LD_LIBRARY_PATH + + '2017.2': + scalar: + executable: /path/ecl/2017.2/linux/x86_64/bin/eclipse100 + env: + mpi: + executable: /path/ecl/2017.2/linux/x86_64/bin/eclipse100_mpi + mpirun: /path/ecl/2017.2/intel/bin/mpirun + + +# You can have a shared 'env' attribute which is an environment variable map +# which will be set before the simulator starts. If there are env settings in +# the simulator as well they will be merged with these common settings, the +# simulator specific take presedence. + +env: + LM_LICENSE_FILE: license@company.com + F_UFMTENDIAN: big + ARCH: x86_64 + + +eclrun_env: + SLBSLS_LICENSE_FILE: something@yourcompany.com + ECLPATH: /path/to/ecl + PATH: /path/to/ecl/macros + LSB_JOBID: null diff --git a/res/fm/ecl/ecl300_config.yml b/res/fm/ecl/ecl300_config.yml new file mode 100644 index 00000000000..306fbb72307 --- /dev/null +++ b/res/fm/ecl/ecl300_config.yml @@ -0,0 +1,33 @@ +# This is an example configuration file for the local ecl300 installation, see +# the ecl100_config.yml file for more extensive dcoujmentation. + +versions: + '2015.2': # without quotes this will be interpreted as a number + scalar: + executable: /path/ecl/2015.2/linux/x86_64/bin/eclipse300 + mpi: + executable: /path/ecl/2015.2/linux/x86_64/bin/eclipse300_mpi + mpirun: /path/ecl/2015.2/intel/bin/mpirun + env: + MPI_ROOT: /path/to/mpi/root + LD_LIBRARY_PATH: /path/to/mpi/root/lib64:$LD_LIBRARY_PATH + + '2017.2': + scalar: + executable: /path/ecl/2017.2/linux/x86_64/bin/eclipse300 + env: + mpi: + executable: /path/ecl/2017.2/linux/x86_64/bin/eclipse300_mpi + mpirun: /path/ecl/2017.2/intel/bin/mpirun + + +env: + LM_LICENSE_FILE: license@company.com + F_UFMTENDIAN: big + ARCH: x86_64 + +eclrun_env: + SLBSLS_LICENSE_FILE: something@yourcompany.com + ECLPATH: /path/to/ecl + PATH: /path/to/ecl/macros + LSB_JOBID: null diff --git a/res/fm/ecl/ecl_config.py b/res/fm/ecl/ecl_config.py new file mode 100644 index 00000000000..2cd76824552 --- /dev/null +++ b/res/fm/ecl/ecl_config.py @@ -0,0 +1,256 @@ +import os +import yaml +import subprocess +import sys +import re + + +def re_getenv(match_obj): + match_str = match_obj.group(1) + variable = match_str[1:] + return os.getenv(variable, default=match_str) + + +# Small utility function will take a dict as input, and create a new dictionary +# where all $VARIABLE in the values has been replaced with getenv("VARIABLE"). +# Variables which are not recognized are left unchanged. + + +def _replace_env(env): + new_env = {} + for key, value in env.items(): + new_env[key] = re.sub(r"(\$[A-Z0-9_]+)", re_getenv, value) + + return new_env + + +class Keys(object): + default_version = "default_version" + default = "default" + versions = "versions" + env = "env" + mpi = "mpi" + mpirun = "mpirun" + executable = "executable" + scalar = "scalar" + + +class Simulator(object): + """Small 'struct' with the config information for one simulator.""" + + def __init__(self, version, executable, env, mpirun=None): + self.version = version + if not os.access(executable, os.X_OK): + raise OSError( + "The executable: '{}' can not be executed by user".format(executable) + ) + + self.executable = executable + self.env = env + self.mpirun = mpirun + self.name = "simulator" + + if not mpirun is None: + if not os.access(mpirun, os.X_OK): + raise OSError( + "The mpirun argument: '{}' is not executable by user".format( + executable + ) + ) + + def __repr__(self): + mpistring = "" + if self.mpirun: + mpistring = " MPI" + return "{}(version={}, executable={}{})".format( + self.name, self.version, self.executable, mpistring + ) + + +class EclConfig(object): + """Represent the eclipse configuration at a site. + + The EclConfig class internalizes information of where the various eclipse + programs are installed on site, and which environment variables need to be + set before the simulator starts. The class is based on parsing a yaml + formatted configuration file, the source distribution contains commented + example file. + + """ + + def __init__(self, config_file, simulator_name="not_set"): + with open(config_file) as f: + try: + config = yaml.safe_load(f) + except: + raise ValueError("Failed parse: {} as yaml".format(config_file)) + + self._config = config + self._config_file = os.path.abspath(config_file) + self.simulator_name = simulator_name + + def __contains__(self, version): + if version in self._config[Keys.versions]: + return True + + return self.default_version is not None and version in [None, Keys.default] + + def get_eclrun_env(self): + if "eclrun_env" in self._config: + return self._config["eclrun_env"].copy() + return None + + @property + def default_version(self): + return self._config.get(Keys.default_version) + + def _get_version(self, version_arg): + if version_arg in [None, Keys.default]: + version = self.default_version + else: + version = version_arg + + if version is None: + raise Exception( + "The default version has not not been set in the config file:{}".format( + self._config_file + ) + ) + + return version + + def _get_env(self, version, exe_type): + env = {} + env.update(self._config.get(Keys.env, {})) + + version = self._get_version(version) + mpi_sim = self._config[Keys.versions][version][exe_type] + env.update(mpi_sim.get(Keys.env, {})) + return _replace_env(env) + + def _get_sim(self, version, exe_type): + version = self._get_version(version) + d = self._config[Keys.versions][version][exe_type] + if exe_type == Keys.mpi: + mpirun = d[Keys.mpirun] + else: + mpirun = None + return Simulator( + version, d[Keys.executable], self._get_env(version, exe_type), mpirun=mpirun + ) + + def sim(self, version=None): + """Will return a small struct describing the simulator. + + The struct has attributes 'executable' and 'env'. Observe that the + executable path is validated when you instantiate the Simulator object; + so if the executable key in the config file points to non-existing file + you will not get the error before this point. + """ + return self._get_sim(version, Keys.scalar) + + def mpi_sim(self, version=None): + """MPI version of method sim().""" + return self._get_sim(version, Keys.mpi) + + def simulators(self, strict=True): + simulators = [] + for version in self._config[Keys.versions].keys(): + for exe_type in self._config[Keys.versions][version].keys(): + if strict: + sim = self._get_sim(version, exe_type) + else: + try: + sim = self._get_sim(version, exe_type) + except Exception: + sys.stderr.write( + "Failed to create simulator object for: version:{version} {exe_type}\n".format( + version=version, exe_type=exe_type + ) + ) + sim = None + + if sim: + simulators.append(sim) + return simulators + + +class Ecl100Config(EclConfig): + + DEFAULT_CONFIG_FILE = os.path.join(os.path.dirname(__file__), "ecl100_config.yml") + + def __init__(self): + config_file = os.getenv("ECL100_SITE_CONFIG", default=self.DEFAULT_CONFIG_FILE) + super(Ecl100Config, self).__init__(config_file, simulator_name="eclipse") + + +class Ecl300Config(EclConfig): + + DEFAULT_CONFIG_FILE = os.path.join(os.path.dirname(__file__), "ecl300_config.yml") + + def __init__(self): + config_file = os.getenv("ECL300_SITE_CONFIG", default=self.DEFAULT_CONFIG_FILE) + super(Ecl300Config, self).__init__(config_file, simulator_name="e300") + + +class FlowConfig(EclConfig): + + DEFAULT_CONFIG_FILE = os.path.join(os.path.dirname(__file__), "flow_config.yml") + + def __init__(self): + config_file = os.getenv("FLOW_SITE_CONFIG", default=self.DEFAULT_CONFIG_FILE) + super(FlowConfig, self).__init__(config_file, simulator_name="flow") + + +class EclrunConfig: + """This class contains configurations for using the new eclrun binary + for running eclipse. It uses the old configurations classes above to + get the configuration in the ECLX00_SITE_CONFIG files. + """ + + def __init__(self, config, version): + self.simulator_name = config.simulator_name + self.run_env = self._get_run_env(config.get_eclrun_env()) + self.version = version + + def _get_run_env(self, eclrun_env): + if eclrun_env is None: + return None + + env = os.environ.copy() + if "PATH" in eclrun_env: + env["PATH"] = eclrun_env["PATH"] + os.pathsep + env["PATH"] + eclrun_env.pop("PATH") + + for k, v in eclrun_env.copy().items(): + if v is None: + if k in env: + env.pop(k) + eclrun_env.pop(k) + + env.update(eclrun_env) + return env + + def _get_available_eclrun_versions(self): + try: + return ( + subprocess.check_output( + ["eclrun", "--report-versions", self.simulator_name], + env=self.run_env, + ) + .decode("utf-8") + .strip() + .split(" ") + ) + except subprocess.CalledProcessError: + return [] + + def can_use_eclrun(self): + if self.run_env is None: + return False + + ecl_run_versions = self._get_available_eclrun_versions() + if self.version not in ecl_run_versions: + return False + + return True diff --git a/res/fm/ecl/ecl_run.py b/res/fm/ecl/ecl_run.py new file mode 100644 index 00000000000..efd9b4f324c --- /dev/null +++ b/res/fm/ecl/ecl_run.py @@ -0,0 +1,435 @@ +import os.path +import os +import sys +import re +import time +import datetime +import socket +from collections import namedtuple +import subprocess +from contextlib import contextmanager + +try: + from ecl.summary import EclSum +except ImportError: + from ert.ecl import EclSum + +from .ecl_config import EclConfig +from res.util.subprocess import await_process_tee + + +EclipseResult = namedtuple("EclipseResult", "errors bugs") +body_sub_pattern = r"(\s^\s@.+$)*" +date_sub_pattern = r"\s+AT TIME\s+(?P\d+\.\d+)\s+DAYS\s+\((?P(.+)):\s*$" +error_pattern = r"^\s@-- ERROR{}${}".format(date_sub_pattern, body_sub_pattern) + + +def make_LSB_MCPU_machine_list(LSB_MCPU_HOSTS): + host_numcpu_list = LSB_MCPU_HOSTS.split() + host_list = [] + for index in range(len(host_numcpu_list) // 2): + machine = host_numcpu_list[2 * index] + host_numcpu = int(host_numcpu_list[2 * index + 1]) + for icpu in range(host_numcpu): + host_list.append(machine) + return host_list + + +def _expand_SLURM_range(rs): + if "-" in rs: + tmp = rs.split("-") + return range(int(tmp[0]), int(tmp[1]) + 1) + else: + return [int(rs)] + + +def _expand_SLURM_node(node_string): + match_object = re.match(r"(?P[^[]+)\[(?P[-0-9,]+)\]", node_string) + if match_object: + node_list = [] + base = match_object.groupdict()["base"] + range_string = match_object.groupdict()["range"] + for rs in range_string.split(","): + for num in _expand_SLURM_range(rs): + node_list.append("{}{}".format(base, num)) + return node_list + else: + return [node_string] + + +def _expand_SLURM_task_count(task_count_string): + match_object = re.match(r"(?P\d+)(\(x(?P\d+)\))?", task_count_string) + if match_object: + match_dict = match_object.groupdict() + print(match_dict) + count = int(match_dict["count"]) + mult_string = match_dict["mult"] + if mult_string is None: + mult = 1 + else: + mult = int(mult_string) + + return [count] * mult + else: + raise Exception( + "Failed to parse SLURM_TASKS_PER_NODE: {}".format(task_count_string) + ) + + +# The list of available machines/nodes and how many tasks each node should get +# is available in the slurm environment variables SLURM_JOB_NODELIST and +# SLURM_TASKS_PER_NODE. These string variables are in an incredibly compact +# notation, and there are some hoops to expand them. The short description is: +# +# 1. They represent flat lists of hostnames and the number of cpu's on that +# host respectively. +# +# 2. The outer structure is a ',' separated lis. +# +# 3. The items in SLURM_JOB_NODELIST have a compact notation +# base-[n1-n2,n3-n4] which is expanded to the nodelist: [base-n1, +# base-n1+1, base-n1+2, ... , base-n4-1, base-n4] +# +# 4. The SLURM_TASK_PER_NODE items has the compact notation 3(x4) which +# implies that four consecutive nodes (from the expanded +# SLURM_JOB_NODELIST) should have three CPUs each. +# +# For further details see the sbatch manual page. + + +def make_SLURM_machine_list(SLURM_JOB_NODELIST, SLURM_TASKS_PER_NODE): + # We split on ',' - but not on ',' which is inside a [...] + split_re = ",(?![^[]*\\])" + nodelist = [] + for node_string in re.split(split_re, SLURM_JOB_NODELIST): + nodelist += _expand_SLURM_node(node_string) + + task_count_list = [] + for task_count_string in SLURM_TASKS_PER_NODE.split(","): + task_count_list += _expand_SLURM_task_count(task_count_string) + + host_list = [] + for node, count in zip(nodelist, task_count_list): + host_list += [node] * count + + return host_list + + +def make_LSB_machine_list(LSB_HOSTS): + return LSB_HOSTS.split() + + +@contextmanager +def pushd(run_path): + starting_directory = os.getcwd() + os.chdir(run_path) + yield + os.chdir(starting_directory) + + +class EclRun(object): + """Wrapper class to run Eclipse simulations. + + The EclRun class is a small wrapper class which is used to run Eclipse + simulations. It will load a configuration, i.e. where the binary is + installed and so on, from an instance of the EclConfig class. + + The main method is the runEclipse() method which will: + + 1. Set up redirection of the stdxxx file descriptors. + 2. Set the necessary environment variables. + 3. [MPI]: Create machine_file listing the nodes which should be used. + 4. fork+exec to actually run the Eclipse binary. + 5. Parse the output .PRT / .ECLEND file to check for errors. + + If the simulation fails the runEclipse() method will raise an exception. + + The class is called EclRun, and the main focus has been on running Eclipse + simulations, but it should also handle "eclipse-like" simulators, e.g. the + simulator OPM/flow. + + To actually create an executable script based on this class could in it's + simplest form be: + + #!/usr/bin/env python + import sys + from res.fm.ecl import EclRun + + run = EclRun() + run.runEclipse( ) + + + """ + + def __init__( + self, ecl_case, sim, num_cpu=1, check_status=True, summary_conversion=False + ): + self.sim = sim + self.check_status = check_status + self.num_cpu = int(num_cpu) + self.summary_conversion = summary_conversion + + # Dechipher the ecl_case argument. + input_arg = ecl_case + (base, ext) = os.path.splitext(input_arg) + if ext and ext in [".data", ".DATA"]: + data_file = input_arg + else: + if input_arg.islower(): + data_file = input_arg + ".data" + else: + data_file = input_arg + ".DATA" + + if not os.path.isfile(data_file): + raise IOError("No such file: %s" % data_file) + else: + (self.run_path, self.data_file) = os.path.split(data_file) + (self.base_name, ext) = os.path.splitext(self.data_file) + + if self.run_path is None: + self.run_path = os.getcwd() + else: + self.run_path = os.path.abspath(self.run_path) + + def runPath(self): + return self.run_path + + def baseName(self): + return self.base_name + + def numCpu(self): + return self.num_cpu + + def _get_legacy_run_env(self): + my_env = os.environ.copy() + my_env.update(self.sim.env.items()) + return my_env + + def initMPI(self): + + # If the environment variable LSB_MCPU_HOSTS is set we assume the job is + # running on LSF - otherwise we assume it is running on the current host. + # + # If the LSB_MCPU_HOSTS variable is indeed set it will be a string like this: + # + # host1 num_cpu1 host2 num_cpu2 ... + # + # i.e. an alternating list of hostname & number of + # cpu. Alternatively/in addition the environment variable + # LSB_HOSTS can be used. This variable is simply: + # + # host1 host1 host2 host3 + + LSB_MCPU_HOSTS = os.getenv("LSB_MCPU_HOSTS") + LSB_HOSTS = os.getenv("LSB_HOSTS") + + if LSB_MCPU_HOSTS or LSB_HOSTS: + LSB_MCPU_machine_list = make_LSB_MCPU_machine_list(LSB_MCPU_HOSTS) + LSB_machine_list = make_LSB_machine_list(LSB_HOSTS) + + if len(LSB_MCPU_machine_list) == self.num_cpu: + machine_list = LSB_MCPU_machine_list + elif len(LSB_machine_list) == self.num_cpu: + machine_list = LSB_machine_list + else: + raise Exception( + 'LSF / MPI problems. Asked for:%s cpu. LSB_MCPU_HOSTS: "%s" LSB_HOSTS: "%s"' + % (self.num_cpu, LSB_MCPU_HOSTS, LSB_HOSTS) + ) + elif os.getenv("SLURM_JOB_NODELIST"): + machine_list = make_SLURM_machine_list( + os.getenv("SLURM_JOB_NODELIST"), os.getenv("SLURM_TASKS_PER_NODE") + ) + if len(machine_list) != self.num_cpu: + raise Exception( + "SLURM / MPI problems - asked for {} - got {} nodes".format( + self.num_cpu, len(machine_list) + ) + ) + else: + localhost = socket.gethostname() + machine_list = [localhost] * self.num_cpu + + self.machine_file = "%s.mpi" % self.base_name + with open(self.machine_file, "w") as fileH: + for host in machine_list: + fileH.write("%s\n" % host) + + def _get_run_command(self, eclrun_config): + summary_conversion = "yes" if self.summary_conversion else "no" + return [ + "eclrun", + "-v", + eclrun_config.version, + eclrun_config.simulator_name, + "{}.DATA".format(self.base_name), + "--summary-conversion", + summary_conversion, + ] + + def _get_legacy_run_command(self): + if self.num_cpu == 1: + return [self.sim.executable, self.base_name] + else: + self.initMPI() + return [ + self.sim.mpirun, + "-machinefile", + self.machine_file, + "-np", + str(self.num_cpu), + self.sim.executable, + self.base_name, + ] + + def execEclipse(self, eclrun_config=None): + use_eclrun = eclrun_config is not None + log_name = "{}.LOG".format(self.base_name) + + with pushd(self.run_path), open(log_name, "wb") as log_file: + if not os.path.exists(self.data_file): + raise IOError("Can not find data_file:{}".format(self.data_file)) + if not os.access(self.data_file, os.R_OK): + raise OSError("Can not read data file:{}".format(self.data_file)) + + command = ( + self._get_run_command(eclrun_config) + if use_eclrun + else self._get_legacy_run_command() + ) + env = eclrun_config.run_env if use_eclrun else self._get_legacy_run_env() + + process = subprocess.Popen( + command, + env=env, + stdout=subprocess.PIPE, + ) + return await_process_tee(process, sys.stdout, log_file) + + def runEclipse(self, eclrun_config=None): + return_code = self.execEclipse(eclrun_config=eclrun_config) + + OK_file = os.path.join(self.run_path, "%s.OK" % self.base_name) + if not self.check_status: + with open(OK_file, "w") as f: + f.write("ECLIPSE simulation complete - NOT checked for errors.") + else: + if return_code != 0: + raise Exception( + "The eclipse executable exited with error status: %d" + % (return_code) + ) + + self.assertECLEND() + if self.num_cpu > 1: + self.summary_block() + + with open(OK_file, "w") as f: + f.write("ECLIPSE simulation OK") + + def summary_block(self): + case = os.path.join(self.run_path, self.base_name) + start_time = datetime.datetime.now() + prev_len = 0 + while True: + dt = datetime.datetime.now() - start_time + if dt.total_seconds() > 15: + # We have not got a stable summary file after 15 seconds of waiting, + # this eitther implies that something is completely broken or this is + # a NOSIM simulation. Due the possibility of NOSIM solution we just return + # here without signalling an error. + return None + + time.sleep(1) + + try: + ecl_sum = EclSum(case) + except: + continue + + this_len = len(ecl_sum) + if prev_len == 0: + prev_len = this_len + continue + + if prev_len == this_len: + break + + return ecl_sum + + def assertECLEND(self): + result = self.readECLEND() + if result.errors > 0: + error_list = self.parseErrors() + sep = "\n\n...\n\n" + error_msg = sep.join(error_list) + raise Exception( + "Eclipse simulation failed with:%d errors:\n\n%s" + % (result.errors, error_msg) + ) + + if result.bugs > 0: + raise Exception("Eclipse simulation failed with:%d bugs" % result.bugs) + + def readECLEND(self): + error_regexp = re.compile(r"^\s*Errors\s+(\d+)\s*$") + bug_regexp = re.compile(r"^\s*Bugs\s+(\d+)\s*$") + + report_file = os.path.join(self.run_path, "{}.ECLEND".format(self.base_name)) + if not os.path.isfile(report_file): + report_file = os.path.join(self.run_path, "{}.PRT".format(self.base_name)) + + with open(report_file, "r") as fileH: + for line in fileH.readlines(): + error_match = re.match(error_regexp, line) + if error_match: + errors = int(error_match.group(1)) + + bug_match = re.match(bug_regexp, line) + if bug_match: + bugs = int(bug_match.group(1)) + + return EclipseResult(errors=errors, bugs=bugs) + + def parseErrors(self): + prt_file = os.path.join(self.runPath(), "%s.PRT" % self.baseName()) + error_list = [] + error_regexp = re.compile(error_pattern, re.MULTILINE) + with open(prt_file) as f: + content = f.read() + + offset = 0 + while True: + match = error_regexp.search(content[offset:]) + if match: + error_list.append( + content[offset + match.start() : offset + match.end()] + ) + offset += match.end() + else: + break + + return error_list + + @classmethod + def checkCase(cls, refcase, simcase): + ref = EclSum(refcase) + sim = EclSum(simcase) + + if sim.getEndTime() >= ref.getEndTime(): + with open("CHECK_ECLIPSE_RUN.OK", "w") as f: + f.write("OK - the simulation %s was >= %s" % (simcase, refcase)) + + return True + else: + msg = """ +CHECK_ECLIPSE_RUN: Failed +Refcase %s : %s +Simulation %s : %s +""" % ( + refcase, + ref.getEndTime(), + simcase, + sim.getEndTime(), + ) + raise ValueError(msg) diff --git a/res/fm/ecl/script.py b/res/fm/ecl/script.py new file mode 100644 index 00000000000..999b7ee13c0 --- /dev/null +++ b/res/fm/ecl/script.py @@ -0,0 +1,43 @@ +from argparse import ArgumentParser +from .ecl_run import EclRun +from res.fm.ecl.ecl_config import EclrunConfig + + +def run(config, argv): + parser = ArgumentParser() + parser.add_argument("ecl_case") + parser.add_argument("-v", "--version", dest="version", type=str) + parser.add_argument("-n", "--num-cpu", dest="num_cpu", type=int, default=1) + parser.add_argument( + "-i", "--ignore-errors", dest="ignore_errors", action="store_true" + ) + parser.add_argument( + "--summary-conversion", dest="summary_conversion", action="store_true" + ) + + options = parser.parse_args(argv) + + eclrun_config = EclrunConfig(config, options.version) + if eclrun_config.can_use_eclrun(): + run = EclRun( + options.ecl_case, + None, + num_cpu=options.num_cpu, + check_status=not options.ignore_errors, + summary_conversion=options.summary_conversion, + ) + run.runEclipse(eclrun_config=eclrun_config) + else: + if options.num_cpu > 1: + sim = config.mpi_sim(version=options.version) + else: + sim = config.sim(version=options.version) + + run = EclRun( + options.ecl_case, + sim, + num_cpu=options.num_cpu, + check_status=not options.ignore_errors, + summary_conversion=options.summary_conversion, + ) + run.runEclipse() diff --git a/res/fm/rms/__init__.py b/res/fm/rms/__init__.py new file mode 100644 index 00000000000..4b39f10d5e5 --- /dev/null +++ b/res/fm/rms/__init__.py @@ -0,0 +1,29 @@ +from .rms_config import RMSConfig +from .rms_run import RMSRun, RMSRunException + + +def run( + iens, + project, + workflow, + run_path="rms", + target_file=None, + export_path="rmsEXPORT", + import_path="rmsIMPORT", + version=None, + readonly=True, + allow_no_env=False, +): + run_object = RMSRun( + iens, + project, + workflow, + run_path=run_path, + target_file=target_file, + export_path=export_path, + import_path=import_path, + version=version, + readonly=readonly, + allow_no_env=allow_no_env, + ) + run_object.run() diff --git a/res/fm/rms/rms_config.py b/res/fm/rms/rms_config.py new file mode 100644 index 00000000000..9a8c940c2cf --- /dev/null +++ b/res/fm/rms/rms_config.py @@ -0,0 +1,41 @@ +import os +import os.path +import yaml +import shutil + + +class RMSConfig(object): + DEFAULT_CONFIG_FILE = os.path.join(os.path.dirname(__file__), "rms_config.yml") + + def __init__(self): + config_file = os.getenv("RMS_SITE_CONFIG", default=self.DEFAULT_CONFIG_FILE) + with open(config_file) as f: + try: + config = yaml.safe_load(f) + except: + raise ValueError("Failed to parse: {} as yaml".format(config_file)) + + self._config = config + + @property + def executable(self): + exe = self._config["executable"] + if not os.access(exe, os.X_OK): + raise OSError("The executable: {} can not run".format(exe)) + + return exe + + @property + def wrapper(self): + exe = self._config.get("wrapper", None) + if exe is not None and shutil.which(exe) is None: + raise OSError("The executable: {} is not found".format(exe)) + return exe + + @property + def threads(self): + return self._config.get("threads") + + def env(self, version): + env_versions = self._config.get("env", {}) + return env_versions.get(version, {}) diff --git a/res/fm/rms/rms_config.yml b/res/fm/rms/rms_config.yml new file mode 100644 index 00000000000..43189a9e587 --- /dev/null +++ b/res/fm/rms/rms_config.yml @@ -0,0 +1,9 @@ +executable: /path/to/rms + +# Optional keys; if these are not specified default values will be used. +# ----------------------------------------------------------------- + +# The number of threads used by the RMS process for CPU intensive tasks. The +# default number of threads is 1. + +threads: 1 diff --git a/res/fm/rms/rms_run.py b/res/fm/rms/rms_run.py new file mode 100644 index 00000000000..3d0065a0374 --- /dev/null +++ b/res/fm/rms/rms_run.py @@ -0,0 +1,196 @@ +import json +import os +import sys +import os.path +import time +import random +import subprocess + +from contextlib import contextmanager +from .rms_config import RMSConfig + + +@contextmanager +def pushd(path): + cwd0 = os.getcwd() + os.chdir(path) + + yield + + os.chdir(cwd0) + + +class RMSRunException(Exception): + pass + + +class RMSRun(object): + _single_seed_file = "RMS_SEED" + _multi_seed_file = "random.seeds" + _max_seed = 2146483648 + _seed_factor = 7907 + + def __init__( + self, + iens, + project, + workflow, + run_path="rms", + target_file=None, + export_path="rmsEXPORT", + import_path="rmsIMPORT", + version=None, + readonly=True, + allow_no_env=False, + ): + if not os.path.isdir(project): + raise OSError( + "The project:{} does not exist as a directory.".format(project) + ) + + self.config = RMSConfig() + self.project = os.path.abspath(project) + self.workflow = workflow + self.run_path = run_path + self.version = version + self.readonly = readonly + self.import_path = import_path + self.export_path = export_path + self.allow_no_env = allow_no_env + if target_file is None: + self.target_file = None + else: + if os.path.isabs(target_file): + self.target_file = target_file + else: + self.target_file = os.path.join(os.getcwd(), target_file) + + if os.path.isfile(self.target_file): + self.target_file_mtime = os.path.getmtime(self.target_file) + else: + self.target_file_mtime = None + + self.init_seed(iens) + + def init_seed(self, iens): + if "RMS_SEED" in os.environ: + seed = int(os.getenv("RMS_SEED")) + for x in range(iens): + seed *= RMSRun._seed_factor + else: + single_seed_file = os.path.join(self.run_path, RMSRun._single_seed_file) + multi_seed_file = os.path.join(self.run_path, RMSRun._multi_seed_file) + + if os.path.exists(single_seed_file): + # Using existing single seed file + with open(single_seed_file) as fileH: + seed = int(float(fileH.readline())) + elif os.path.exists(multi_seed_file): + with open(multi_seed_file) as fileH: + seed_list = [int(x) for x in fileH.readlines()] + seed = seed_list[iens + 1] + else: + random.seed() + seed = random.randint(0, RMSRun._max_seed) + + self.seed = seed % RMSRun._max_seed + + def run(self): + if not os.path.exists(self.run_path): + os.makedirs(self.run_path) + + self_exe, _ = os.path.splitext(os.path.basename(sys.argv[0])) + exec_env = os.environ.copy() + + config_env = self.config.env(self.version) + if not config_env and not self.allow_no_env: + raise RMSRunException( + f"RMS environment not specified for version: {self.version}" + ) + exec_env_file = "%s_exec_env.json" % self_exe + user_env = {} + if os.path.isfile(exec_env_file): + with open(exec_env_file) as f: + user_env = json.load(f) + for var in set(config_env.keys()) | set(user_env.keys()): + exec_env[var] = ":".join( + filter(None, [user_env.get(var), config_env.get(var)]) + ) + if not exec_env[var].strip(): + exec_env.pop(var) + + with pushd(self.run_path): + fileH = open("RMS_SEED_USED", "a+") + fileH.write( + "%s ... %d\n" + % ( + time.strftime("%d-%m-%Y %H:%M:%S", time.localtime(time.time())), + self.seed, + ) + ) + fileH.close() + + if not os.path.exists(self.export_path): + os.makedirs(self.export_path) + + if not os.path.exists(self.import_path): + os.makedirs(self.import_path) + + exit_status = self.exec_rms(exec_env) + + if exit_status != 0: + raise RMSRunException( + "The RMS run failed with exit status: {}".format(exit_status) + ) + + if self.target_file is None: + return + + if not os.path.isfile(self.target_file): + raise RMSRunException( + "The RMS run did not produce the expected file: {}".format( + self.target_file + ) + ) + + if self.target_file_mtime is None: + return + + if os.path.getmtime(self.target_file) == self.target_file_mtime: + raise RMSRunException( + "The target file:{} is unmodified - interpreted as failure".format( + self.target_file + ) + ) + + def exec_rms(self, exec_env): + args = [self.config.wrapper] if self.config.wrapper is not None else [] + args += [ + self.config.executable, + "-project", + self.project, + "-seed", + str(self.seed), + "-nomesa", + "-export_path", + self.export_path, + "-import_path", + self.import_path, + "-batch", + self.workflow, + ] + + if self.version: + args += ["-v", self.version] + + if self.readonly: + args += ["-readonly"] + + if self.config.threads: + args += ["-threads", str(self.config.threads)] + + if not exec_env: + exec_env = os.environ + + comp_process = subprocess.run(args=args, env=exec_env) + return comp_process.returncode diff --git a/res/fm/shell/__init__.py b/res/fm/shell/__init__.py new file mode 100644 index 00000000000..d1429230405 --- /dev/null +++ b/res/fm/shell/__init__.py @@ -0,0 +1,33 @@ +from .shell import Shell + + +def symlink(target, link_name): + Shell.symlink(target, link_name) + + +def mkdir(path): + Shell.mkdir(path) + + +def move_file(src_file, target): + Shell.moveFile(src_file, target) + + +def delete_file(filename): + Shell.deleteFile(filename) + + +def delete_directory(path): + Shell.deleteDirectory(path) + + +def copy_directory(src_path, target_path): + Shell.copyDirectory(src_path, target_path) + + +def copy_file(src, target=None): + Shell.copyFile(src, target) + + +def careful_copy_file(src, target=None): + Shell.carefulCopyFile(src, target) diff --git a/res/fm/shell/shell.py b/res/fm/shell/shell.py new file mode 100644 index 00000000000..67631f71444 --- /dev/null +++ b/res/fm/shell/shell.py @@ -0,0 +1,200 @@ +import os +import os.path +import shutil +import sys +import distutils.dir_util + + +class Shell(object): + """ + Utility class to simplify common shell operations. + """ + + @staticmethod + def symlink(target, link_name): + """Will create a symbol link 'link_name -> target'. + + If the @link_name already exists as a symbolic link it will be + removed first; if the @link_name exists and is *not* a + symbolic link OSError will be raised. If the @target does not + exists IOError will be raised. + """ + link_path, link_base = os.path.split(link_name) + if len(link_path) == 0: + target_check = target + else: + if not os.path.isdir(link_path): + print("Creating directory for link: %s" % link_path) + os.makedirs(link_path) + target_check = os.path.join(link_path, target) + + if not os.path.exists(target_check): + raise IOError( + "{} (target) and {} (link_name) requested, which implies that {} must exist, but it does not.".format( + target, link_name, target_check + ) + ) + + if os.path.islink(link_name): + os.unlink(link_name) + os.symlink(target, link_name) + print("Linking '%s' -> '%s' [ cwd:%s ]" % (link_name, target, os.getcwd())) + + @staticmethod + def mkdir(path): + if os.path.isdir(path): + print("OK - directory: '%s' already exists" % path) + else: + try: + os.makedirs(path) + print("Created directory: '%s'" % path) + except Exception as e: + # Seems in many cases the directory just suddenly appears; syncronization + # issues? + if not os.path.isdir(path): + msg = 'ERROR: Failed to create directory "%s": %s.' % (path, e) + raise OSError(msg) + + @staticmethod + def moveFile(src_file, target): + """ + Will raise IOError if src_file is not a file. + + """ + if os.path.isfile(src_file): + # shutil.move works best (as unix mv) when target is a file. + if os.path.isdir(target): + target = os.path.join(target, os.path.basename(src_file)) + shutil.move(src_file, target) + else: + raise IOError("Input argument %s is not an existing file" % src_file) + + @staticmethod + def __deleteFile(filename): + stat_info = os.stat(filename) + uid = stat_info.st_uid + if uid == os.getuid(): + os.unlink(filename) + print("Removing file:'%s'" % filename) + else: + sys.stderr.write( + "Sorry you are not owner of file:%s - not deleted\n" % filename + ) + + @staticmethod + def __deleteDirectory(dirname): + stat_info = os.stat(dirname) + uid = stat_info.st_uid + if uid == os.getuid(): + if os.path.islink(dirname): + os.remove(dirname) + print("Removing symbolic link:'%s'" % dirname) + else: + try: + os.rmdir(dirname) + print("Removing directory:'%s'" % dirname) + except OSError as e: + if e.errno == 39: + sys.stderr.write( + "Failed to remove directory:%s - not empty\n" % dirname + ) + else: + raise + else: + sys.stderr.write( + "Sorry you are not owner of directory:%s - not deleted\n" % dirname + ) + + @staticmethod + def deleteFile(filename): + if os.path.exists(filename): + if os.path.isfile(filename): + Shell.__deleteFile(filename) + else: + raise IOError("Entry:'%s' is not a regular file" % filename) + else: + if os.path.islink(filename): + os.remove(filename) + else: + sys.stderr.write("File: '%s' not exist - delete ignored\n" % filename) + + @staticmethod + def deleteDirectory(path): + """ + Will ignore if you are not owner. + """ + if os.path.exists(path): + if os.path.isdir(path): + for root, dirs, files in os.walk( + path, topdown=False, followlinks=False + ): + if not os.path.islink(root): + for file in files: + Shell.__deleteFile(os.path.join(root, file)) + + for dir in dirs: + Shell.__deleteDirectory(os.path.join(root, dir)) + + else: + raise IOError("Entry:'%s' is not a directory" % path) + + Shell.__deleteDirectory(path) + else: + sys.stderr.write("Directory:'%s' not exist - delete ignored\n" % path) + + @staticmethod + def copyDirectory(src_path, target_path): + if os.path.isdir(src_path): + src_basename = os.path.basename(src_path) + target_root, target_basename = os.path.split(target_path) + full_target = os.path.join(target_path, src_basename) + + if target_root: + if not os.path.isdir(target_root): + print("Creating empty folder structure %s" % target_root) + Shell.mkdir(target_root) + + print("Copying directory structure %s -> %s" % (src_path, target_path)) + if os.path.isdir(target_path): + target_path = os.path.join(target_path, src_basename) + distutils.dir_util.copy_tree(src_path, target_path, preserve_times=0) + else: + raise IOError( + "Input argument:'%s' does not correspond to an existing directory" + % src_path + ) + + @staticmethod + def copyFile(src, target=None): + if os.path.isfile(src): + if target is None: + target = os.path.basename(src) + + if os.path.isdir(target): + target_file = os.path.join(target, os.path.basename(src)) + shutil.copyfile(src, target_file) + print("Copying file '%s' -> '%s'" % (src, target_file)) + else: + target_path = os.path.dirname(target) + if target_path: + if not os.path.isdir(target_path): + os.makedirs(target_path) + print("Creating directory '%s' " % target_path) + if os.path.isdir(target): + target_file = os.path.join(target, os.path.basename(src)) + else: + target_file = target + + print("Copying file '%s' -> '%s'" % (src, target_file)) + shutil.copyfile(src, target_file) + else: + raise IOError( + "Input argument:'%s' does not correspond to an existing file" % src + ) + + @staticmethod + def carefulCopyFile(src, target=None): + if os.path.exists(target): + print("File: {} already present - not updated".format(target)) + return + Shell.copyFile(src, target) diff --git a/res/fm/templating/__init__.py b/res/fm/templating/__init__.py new file mode 100644 index 00000000000..1070bac854b --- /dev/null +++ b/res/fm/templating/__init__.py @@ -0,0 +1,6 @@ +from .template_render import render_template +from .template_render import load_data + + +def load_parameters(): + return load_data("parameters.json") diff --git a/res/fm/templating/template_render.py b/res/fm/templating/template_render.py new file mode 100644 index 00000000000..6e52ddb2275 --- /dev/null +++ b/res/fm/templating/template_render.py @@ -0,0 +1,95 @@ +import jinja2 +import json +import os +import yaml + +from res import enkf + +DEFAULT_GEN_KW_EXPORT_NAME = enkf.EnkfDefaults.DEFAULT_GEN_KW_EXPORT_NAME + + +def load_data(filename): + """Will try to load data from @filename first as yaml, and if that fails, + as json. If both fail, a ValueError with both of the error messages will be + raised. + """ + with open(filename) as fin: + try: + return yaml.safe_load(fin) + except Exception as yaml_err: + pass + + try: + return json.load(fin) + except Exception as json_err: + pass + + err_msg = "%s is neither yaml (err_msg=%s) nor json (err_msg=%s)" + raise IOError(err_msg % (filename, str(yaml_err), str(json_err))) + + +def _load_template(template_path): + path, filename = os.path.split(template_path) + return jinja2.Environment( + loader=jinja2.FileSystemLoader(path or "./") + ).get_template(filename) + + +def _generate_file_namespace(filename): + return os.path.splitext(os.path.basename(filename))[0] + + +def _load_input(input_files): + """ + Loads input files (JSON or YAML) and returns the content as dict. + """ + data = {} + for input_file in input_files: + input_namespace = _generate_file_namespace(input_file) + data[input_namespace] = load_data(input_file) + + return data + + +def _assert_input(input_files, template_file, output_file): + """ + validates input for template rendering. + Throws ValueError if input files or template file is not found. + Throws TypeError if output_file is not a string. + """ + for input_file in input_files: + if not os.path.isfile(input_file): + raise ValueError("Input file: %s, does not exist.." % input_file) + + if not os.path.isfile(template_file): + raise ValueError("Template file: %s, does not exist.." % template_file) + + if not isinstance(output_file, str): + raise TypeError("Expected output path to be a string") + + +def render_template(input_files, template_file, output_file): + """ + Will render a jinja2 template file with the parameters given + :param input_files: parameters as a list of JSON or YAML files + :param template_file: template file in jinja2 format + :param output_file: output desitnation for the rendered template file + """ + if isinstance(input_files, str) and input_files: + input_files = (input_files,) + + all_input_files = () + + gen_kw_export_path = DEFAULT_GEN_KW_EXPORT_NAME + ".json" + if os.path.isfile(gen_kw_export_path): + all_input_files += (gen_kw_export_path,) + + if input_files: + all_input_files += tuple(input_files) + + _assert_input(all_input_files, template_file, output_file) + + template = _load_template(template_file) + data = _load_input(all_input_files) + with open(output_file, "w") as fout: + fout.write(template.render(**data)) diff --git a/res/job_queue/__init__.py b/res/job_queue/__init__.py new file mode 100644 index 00000000000..6e46f9b8100 --- /dev/null +++ b/res/job_queue/__init__.py @@ -0,0 +1,98 @@ +# Copyright (C) 2011 Equinor ASA, Norway. +# +# The file '__init__.py' 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. +""" +The job_queue package contains modules and classes for running +external commands. +""" + +# Getting LSF to work properly is quite painful. The situation +# is a mix of build complexity and LSF specific requirements: +# +# 1. The LSF libraries are accessed from the libjob_queue.so +# library, but observe that the dependancy on the liblsf and +# libbat libraries is through dlopen(), i.e. runtime. This module +# will therefor load happily without access to the lsf libraries. +# +# If you at a later stage create a lsf driver the runtime +# environment must be able to locate the liblsf.so, libbat.so and +# libnsl.so shared libraries, either through LD_LIBRARY_PATH or +# other means. +# +# 2. To actually use LSF you need a whole list of environment +# variables to be set: LSF_BINDIR , LSF_LIBDIR , XLDF_UIDDIR , +# LSF_SERVERDIR, LSF_ENVDIR - this is an LSF requirement and not +# related to ERT or the Python bindings. The normal way to +# achieve this is by sourcing a shell script. +# +# If the environment variable LSF_HOME is set we set the +# remaining LSF variables according to: +# +# LSF_BINDIR = $LSF_HOME/bin +# LSF_LIBDIR = $LSF_HOME/lib +# XLSF_UIDDIR = $LSF_HOME/lib/uid +# LSF_SERVERDIR = $LSF_HOME/etc +# LSF_ENVDIR = $LSF_HOME/conf +# PATH = $PATH:$LSF_BINDIR +# +# Observe that none of these variables are modified if they +# already have a value, furthermore it should be observed that +# the use of an LSF_HOME variable is something invented with ERT, +# and not standard LSF approach. + + +import os +from cwrap import Prototype +import res + + +def setenv(var, value): + if not os.getenv(var): + os.environ[var] = value + + +# Set up the full LSF environment - based onf LSF_HOME +LSF_HOME = os.getenv("LSF_HOME") +if LSF_HOME: + setenv("LSF_BINDIR", "%s/bin" % LSF_HOME) + setenv("LSF_LIBDIR", "%s/lib" % LSF_HOME) + setenv("XLSF_UIDDIR", "%s/lib/uid" % LSF_HOME) + setenv("LSF_SERVERDIR", "%s/etc" % LSF_HOME) + setenv("LSF_ENVDIR", "%s/conf" % LSF_HOME) # This is wrong: Equinor: /prog/LSF/conf + +from .job_status_type_enum import JobStatusType +from .run_status_type_enum import RunStatusType +from .thread_status_type_enum import ThreadStatus +from .job import Job +from .driver import Driver, QueueDriverEnum +from .job_queue_node import JobQueueNode +from .queue import JobQueue +from .job_queue_manager import JobQueueManager +from .driver import QueueDriverEnum, Driver, LSFDriver, RSHDriver, LocalDriver +from .ext_job import ExtJob +from .ext_joblist import ExtJoblist +from .environment_varlist import EnvironmentVarlist +from .forward_model import ForwardModel +from .forward_model_status import ForwardModelJobStatus, ForwardModelStatus + +from .ert_script import ErtScript +from .ert_plugin import ErtPlugin, CancelPluginException +from .function_ert_script import FunctionErtScript +from .external_ert_script import ExternalErtScript + +from .workflow_job import WorkflowJob +from .workflow_joblist import WorkflowJoblist +from .workflow import Workflow +from .workflow_runner import WorkflowRunner diff --git a/res/job_queue/driver.py b/res/job_queue/driver.py new file mode 100644 index 00000000000..ae1c7d0d0b6 --- /dev/null +++ b/res/job_queue/driver.py @@ -0,0 +1,149 @@ +# Copyright (C) 2011 Equinor ASA, Norway. +# +# The file 'driver.py' 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. + + +import ctypes +from cwrap import BaseCClass, BaseCEnum +from res import ResPrototype +from res.job_queue import Job + + +class QueueDriverEnum(BaseCEnum): + TYPE_NAME = "queue_driver_enum" + NULL_DRIVER = None + LSF_DRIVER = None + LOCAL_DRIVER = None + RSH_DRIVER = None + TORQUE_DRIVER = None + SLURM_DRIVER = None + + +QueueDriverEnum.addEnum("NULL_DRIVER", 0) +QueueDriverEnum.addEnum("LSF_DRIVER", 1) +QueueDriverEnum.addEnum("LOCAL_DRIVER", 2) +QueueDriverEnum.addEnum("RSH_DRIVER", 3) +QueueDriverEnum.addEnum("TORQUE_DRIVER", 4) +QueueDriverEnum.addEnum("SLURM_DRIVER", 5) + + +LSF_DRIVER = QueueDriverEnum.LSF_DRIVER +RSH_DRIVER = QueueDriverEnum.RSH_DRIVER +LOCAL_DRIVER = QueueDriverEnum.LOCAL_DRIVER +SLURM_DRIVER = QueueDriverEnum.SLURM_DRIVER + + +class Driver(BaseCClass): + TYPE_NAME = "driver" + _alloc = ResPrototype("void* queue_driver_alloc( queue_driver_enum )", bind=False) + _free = ResPrototype("void queue_driver_free( driver )") + _set_option = ResPrototype("void queue_driver_set_option( driver , char* , char*)") + _get_option = ResPrototype("char* queue_driver_get_option(driver, char*)") + _free_job = ResPrototype("void queue_driver_free_job( driver , job )") + _get_status = ResPrototype( + "job_status_type_enum queue_driver_get_status(driver, job)" + ) + _kill_job = ResPrototype("void queue_driver_kill_job( driver , job )") + _get_max_running = ResPrototype("int queue_driver_get_max_running( driver )") + _set_max_running = ResPrototype("void queue_driver_set_max_running( driver , int)") + _get_name = ResPrototype("char* queue_driver_get_name( driver )") + + def __init__(self, driver_type, max_running=1, options=None): + """ + Creates a new driver instance + """ + c_ptr = self._alloc(driver_type) + super(Driver, self).__init__(c_ptr) + if options: + for (key, value) in options: + self.set_option(key, value) + self.set_max_running(max_running) + + def set_option(self, option, value): + """ + Set the driver option @option to @value. + + If the option is succlessfully set the method will return True, + otherwise the method will return False. If the @option is not + recognized the method will return False. The supplied value + should be a string. + """ + return self._set_option(option, str(value)) + + def get_option(self, option_key): + return self._get_option(option_key) + + def is_driver_instance(self): + return True + + def free_job(self, job): + self._free_job(job) + + def get_status(self, job): + return self._get_status(job) + + def kill_job(self, job): + self._kill_job(job) + + def get_max_running(self): + return self._get_max_running() + + def set_max_running(self, max_running): + self._set_max_running(max_running) + + max_running = property(get_max_running, set_max_running) + + @property + def name(self): + return self._get_name() + + def free(self): + self._free() + + +class LSFDriver(Driver): + def __init__( + self, max_running, lsf_server=None, queue="normal", resource_request=None + ): + # The strings should match the available keys given in the + # lsf_driver.h header file. + options = [ + ("LSF_QUEUE", queue), + ("LSF_SERVER", lsf_server), + ("LSF_RESOURCE", resource_request), + ] + Driver.__init__( + self, QueueDriverEnum.LSF_DRIVER, max_running=max_running, options=options + ) + + +class LocalDriver(Driver): + def __init__(self, max_running): + Driver.__init__(self, QueueDriverEnum.LOCAL_DRIVER, max_running, options=[]) + + +class RSHDriver(Driver): + # Changing shell to bash can come in conflict with running ssh + # commands. + + def __init__(self, max_running, rsh_host_list, rsh_cmd="/usr/bin/ssh"): + """ + @rsh_host_list should be a list of tuples like: (hostname , max_running) + """ + + options = [("RSH_CMD", rsh_cmd)] + for (host, host_max) in rsh_host_list: + options.append(("RSH_HOST", "%s:%d" % (host, host_max))) + Driver.__init__(self, QueueDriverEnum.RSH_DRIVER, max_running, options=options) diff --git a/res/job_queue/environment_varlist.py b/res/job_queue/environment_varlist.py new file mode 100644 index 00000000000..7423b3e7c17 --- /dev/null +++ b/res/job_queue/environment_varlist.py @@ -0,0 +1,44 @@ +# Copyright (C) 2017 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. + + +from cwrap import BaseCClass +from res import ResPrototype + + +class EnvironmentVarlist(BaseCClass): + TYPE_NAME = "env_varlist" + + _alloc = ResPrototype("void* env_varlist_alloc()", bind=False) + _free = ResPrototype("void env_varlist_free( env_varlist )") + _setenv = ResPrototype("void env_varlist_setenv(env_varlist, char*, char*)") + _get_size = ResPrototype("int env_varlist_get_size(env_varlist)") + + def __init__(self): + c_ptr = self._alloc() + super(EnvironmentVarlist, self).__init__(c_ptr) + + def __len__(self): + """ + Returns the number of elements. Implements len() + """ + return self._get_size() + + def __setitem__(self, var, value): + self._setenv(var, value) + + def free(self): + self._free() diff --git a/res/job_queue/ert_plugin.py b/res/job_queue/ert_plugin.py new file mode 100644 index 00000000000..b1e2d5b2e53 --- /dev/null +++ b/res/job_queue/ert_plugin.py @@ -0,0 +1,50 @@ +from .ert_script import ErtScript +from threading import Thread +import time + + +class CancelPluginException(Exception): + def __init__(self, cancel_message): + super(CancelPluginException, self).__init__(cancel_message) + + +class ErtPlugin(ErtScript): + def getArguments(self, parent=None): + """@rtype: list""" + return [] + + def getName(self): + """@rtype: str""" + return str(self.__class__) + + def getDescription(self): + """@rtype: str""" + return "No description provided!" + + def checkIfCancelled(self): + if self.isCancelled(): + raise CancelPluginException( + "Plugin '%s' cancelled by user!" % self.getName() + ) + + def startCancellableThread(self, runFunction, cancelFunction): + runFunction.return_value = None + + def runFunctionWrapper(): + runFunction.return_value = runFunction() + + thread = Thread() + thread.run = runFunctionWrapper + thread.start() + + while thread.isAlive(): + if self.isCancelled(): + cancelFunction() + + try: + time.sleep(0.1) + except KeyboardInterrupt: + print("Plugin '%s' cancelled (CTRL+C)" % self.getName()) + self.cancel() + + return runFunction.return_value diff --git a/res/job_queue/ert_script.py b/res/job_queue/ert_script.py new file mode 100644 index 00000000000..a0cdd234e9e --- /dev/null +++ b/res/job_queue/ert_script.py @@ -0,0 +1,151 @@ +import inspect +import sys +import traceback + +import importlib.util + + +class ErtScript(object): + def __init__(self, ert): + """ + @type ert: EnKFMain + """ + super(ErtScript, self).__init__() + + if not hasattr(self, "run"): + raise UserWarning( + "ErtScript implementations must provide a method run(self, ert, ...)" + ) + + self.__verbose = False + self.__ert = ert + + self.__is_cancelled = False + self.__failed = False + self._stdoutdata = "" + self._stderrdata = "" + + @property + def stdoutdata(self): + """@rtype: str""" + if isinstance(self._stdoutdata, bytes): + self._stdoutdata = self._stdoutdata.decode() + return self._stdoutdata + + @property + def stderrdata(self): + """@rtype: str""" + if isinstance(self._stderrdata, bytes): + self._stderrdata = self._stderrdata.decode() + return self._stderrdata + + def isVerbose(self): + return self.__verbose + + def ert(self): + """@rtype: res.enkf.EnKFMain""" + return self.__ert + + def isCancelled(self): + """@rtype: bool""" + return self.__is_cancelled + + def hasFailed(self): + """@rtype: bool""" + return self.__failed + + def cancel(self): + self.__is_cancelled = True + + def cleanup(self): + """ + Override to perform cleanup after a run. + """ + pass + + def initializeAndRun(self, argument_types, argument_values, verbose=False): + """ + @type argument_types: list of type + @type argument_values: list of string + @type verbose: bool + @rtype: unknown + """ + self.__verbose = verbose + self.__failed = False + + arguments = [] + for index, arg_value in enumerate(argument_values): + if index < len(argument_types): + arg_type = argument_types[index] + else: + arg_type = str + + if arg_value is not None: + arguments.append(arg_type(arg_value)) + else: + arguments.append(None) + + try: + return self.run(*arguments) + except AttributeError as e: + if not hasattr(self, "run"): + self.__failed = True + return ( + "Script '%s' has not implemented a 'run' function" + % self.__class__.__name__ + ) + self.outputStackTrace(e) + return None + except KeyboardInterrupt: + return "Script '%s' cancelled (CTRL+C)" % self.__class__.__name__ + except Exception as e: + self.outputStackTrace(e) + return None + finally: + self.cleanup() + + __module_count = ( + 0 # Need to have unique modules in case of identical object naming in scripts + ) + + def outputStackTrace(self, error=None): + stack_trace = error or "".join(traceback.format_exception(*sys.exc_info())) + msg = "The script '{}' caused an error while running:\n{}" + + sys.stderr.write(msg.format(self.__class__.__name__, stack_trace)) + self.__failed = True + + @staticmethod + def loadScriptFromFile(path): + """@rtype: type ErtScript""" + try: + module_name = "ErtScriptModule_%d" % ErtScript.__module_count + ErtScript.__module_count += 1 + + spec = importlib.util.spec_from_file_location(module_name, path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return ErtScript.__findErtScriptImplementations(module) + except Exception as e: + sys.stderr.write("The script '%s' caused an error during load:\n" % path) + traceback.print_exception(sys.exc_info()[0], sys.exc_info()[1], None) + return None + + @staticmethod + def __findErtScriptImplementations(module): + """@rtype: ErtScript""" + result = [] + predicate = ( + lambda member: inspect.isclass(member) + and member.__module__ == module.__name__ + ) + for name, member in inspect.getmembers(module, predicate): + if ErtScript in inspect.getmro(member): + result.append(member) + + if len(result) != 1: + raise UserWarning( + "Must have (only) one implementation of ErtScript in a module!" + ) + + return result[0] diff --git a/res/job_queue/ext_job.py b/res/job_queue/ext_job.py new file mode 100644 index 00000000000..a0b9a32c762 --- /dev/null +++ b/res/job_queue/ext_job.py @@ -0,0 +1,245 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'ext_job.py' 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. +import os.path + +from cwrap import BaseCClass +from res import ResPrototype +from ecl.util.util import StringList, Hash +from res.config import ContentTypeEnum + + +class ExtJob(BaseCClass): + TYPE_NAME = "ext_job" + _alloc = ResPrototype("void* ext_job_alloc(char*,char*, bool)", bind=False) + _fscanf_alloc = ResPrototype( + "void* ext_job_fscanf_alloc(char*, char*, bool, char* , bool)", bind=False + ) + _free = ResPrototype("void ext_job_free( ext_job )") + _get_help_text = ResPrototype("char* ext_job_get_help_text(ext_job)") + _get_name = ResPrototype("char* ext_job_get_name(ext_job)") + _set_private_args_as_string = ResPrototype( + "void ext_job_set_private_args_from_string(ext_job, char*)" + ) + _is_private = ResPrototype("int ext_job_is_private(ext_job)") + _get_config_file = ResPrototype("char* ext_job_get_config_file(ext_job)") + _set_config_file = ResPrototype("void ext_job_set_config_file(ext_job, char*)") + _get_stdin_file = ResPrototype("char* ext_job_get_stdin_file(ext_job)") + _set_stdin_file = ResPrototype("void ext_job_set_stdin_file(ext_job, char*)") + _get_stdout_file = ResPrototype("char* ext_job_get_stdout_file(ext_job)") + _set_stdout_file = ResPrototype("void ext_job_set_stdout_file(ext_job, char*)") + _get_stderr_file = ResPrototype("char* ext_job_get_stderr_file(ext_job)") + _set_stderr_file = ResPrototype("void ext_job_set_stderr_file(ext_job, char*)") + _get_target_file = ResPrototype("char* ext_job_get_target_file(ext_job)") + _set_target_file = ResPrototype("void ext_job_set_target_file(ext_job, char*)") + _get_executable = ResPrototype("char* ext_job_get_executable(ext_job)") + _set_executable = ResPrototype("void ext_job_set_executable(ext_job, char*)") + _get_error_file = ResPrototype("char* ext_job_get_error_file(ext_job)") + _get_start_file = ResPrototype("char* ext_job_get_start_file(ext_job)") + _get_max_running = ResPrototype("int ext_job_get_max_running(ext_job)") + _set_max_running = ResPrototype("void ext_job_set_max_running(ext_job, int)") + _get_max_running_minutes = ResPrototype( + "int ext_job_get_max_running_minutes(ext_job)" + ) + _set_max_running_minutes = ResPrototype( + "void ext_job_set_max_running_minutes(ext_job, int)" + ) + + _min_arg = ResPrototype("int ext_job_get_min_arg(ext_job)") + _max_arg = ResPrototype("int ext_job_get_max_arg(ext_job)") + _arg_type = ResPrototype( + "config_content_type_enum ext_job_iget_argtype(ext_job, int)" + ) + + _get_environment = ResPrototype("string_hash_ref ext_job_get_environment(ext_job)") + _set_environment = ResPrototype( + "void ext_job_add_environment(ext_job, char*, char*)" + ) + _get_license_path = ResPrototype("char* ext_job_get_license_path(ext_job)") + _get_arglist = ResPrototype("stringlist_ref ext_job_get_arglist(ext_job)") + _set_arglist = ResPrototype("void ext_job_set_args(ext_job, stringlist)") + _get_argvalues = ResPrototype("stringlist_ref ext_job_get_argvalues(ext_job)") + _clear_environment = ResPrototype("void ext_job_clear_environment(ext_job)") + _save = ResPrototype("void ext_job_save(ext_job)") + + def __init__( + self, config_file, private, name=None, license_root_path=None, search_PATH=True + ): + if os.path.isfile(config_file): + if name is None: + name = os.path.basename(config_file) + + c_ptr = self._fscanf_alloc( + name, license_root_path, private, config_file, search_PATH + ) + if c_ptr: + super(ExtJob, self).__init__(c_ptr) + else: + raise ValueError( + "Unable to construct ExtJob(name=%s, config_file=%s, private=%s)" + % (name, config_file, private) + ) + else: + raise IOError('No such config file "%s".' % config_file) + + def __repr__(self): + if self._address(): + return self._create_repr( + "{}, config_file = {}".format(self.name(), self.get_config_file()) + ) + else: + return "UNINITIALIZED ExtJob" + + def set_private_args_as_string(self, args): + self._set_private_args_as_string(args) + + def get_help_text(self): + return self._get_help_text() + + def is_private(self): + return self._is_private() + + def get_config_file(self): + return self._get_config_file() + + def set_config_file(self, config_file): + self._set_config_file(config_file) + + def get_stdin_file(self): + return self._get_stdin_file() + + def set_stdin_file(self, filename): + self._set_stdin_file(filename) + + def get_stdout_file(self): + return self._get_stdout_file() + + def set_stdout_file(self, filename): + self._set_stdout_file(filename) + + def get_stderr_file(self): + return self._get_stderr_file() + + def set_stderr_file(self, filename): + self._set_stderr_file(filename) + + def get_target_file(self): + return self._get_target_file() + + def set_target_file(self, filename): + self._set_target_file(filename) + + def get_executable(self): + return self._get_executable() + + def set_executable(self, executable): + self._set_executable(executable) + + def get_max_running(self): + return self._get_max_running() + + def set_max_running(self, max_running): + self._set_max_running(max_running) + + def get_error_file(self): + return self._get_error_file() + + def get_start_file(self): + return self._get_start_file() + + def get_max_running_minutes(self): + return self._get_max_running_minutes() + + def set_max_running_minutes(self, min_value): + self._set_max_running_minutes(min_value) + + @property + def min_arg(self): + return self._min_arg() + + @property + def max_arg(self): + return self._max_arg() + + @property + def arg_types(self): + """@rtype: list of type""" + + result = [] + for index in range(self.max_arg): + result.append(self._arg_type(index)) + + return result + + @staticmethod + def valid_args(arg_types, arg_list, runtime=False): + for index, arg_type in enumerate(arg_types): + arg = arg_list[index] + if not arg_type.valid_string(arg, runtime): + return False + return True + + def get_environment(self): + return self._get_environment() + + def set_environment(self, key, value): + self._set_environment(key, value) + + def get_license_path(self): + return self._get_license_path() + + def get_arglist(self): + # The return type is cast from a to a regular Python list. + return list(self._get_arglist()) + + def get_argvalues(self): + # The return type is cast from a to a regular Python list. + return list(self._get_argvalues()) + + def set_arglist(self, args): + return self._set_arglist(args) + + def clear_environment(self): + self._clear_environment() + + def save(self): + self._save() + + def free(self): + self._free() + + def name(self): + return self._get_name() + + def __ne__(self, other): + return not self == other + + def __eq__(self, other): + if self.name() != other.name(): + return False + + if [x for x in self.get_arglist()] != [x for x in other.get_arglist()]: + return False + + if self.get_config_file() != other.get_config_file(): + return False + + if self.get_stderr_file() != other.get_stderr_file(): + return False + + if self.get_stdout_file() != other.get_stdout_file(): + return False + + return True diff --git a/res/job_queue/ext_joblist.py b/res/job_queue/ext_joblist.py new file mode 100644 index 00000000000..65ee77aa20a --- /dev/null +++ b/res/job_queue/ext_joblist.py @@ -0,0 +1,83 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'ext_joblist.py' 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. +from cwrap import BaseCClass +from res import ResPrototype +from res.job_queue import ExtJob +from ecl.util.util import StringList + + +class ExtJoblist(BaseCClass): + TYPE_NAME = "ext_joblist" + _alloc = ResPrototype("void* ext_joblist_alloc( )", bind=False) + _free = ResPrototype("void ext_joblist_free( ext_joblist )") + _alloc_list = ResPrototype("stringlist_ref ext_joblist_alloc_list(ext_joblist)") + _get_job = ResPrototype("ext_job_ref ext_joblist_get_job(ext_joblist, char*)") + _del_job = ResPrototype("int ext_joblist_del_job(ext_joblist, char*)") + _has_job = ResPrototype("int ext_joblist_has_job(ext_joblist, char*)") + _add_job = ResPrototype("void ext_joblist_add_job(ext_joblist, char*, ext_job)") + _get_jobs = ResPrototype("hash_ref ext_joblist_get_jobs(ext_joblist)") + _size = ResPrototype("int ext_joblist_get_size(ext_joblist)") + + def __init__(self): + c_ptr = self._alloc() + super(ExtJoblist, self).__init__(c_ptr) + + def get_jobs(self): + """@rtype: Hash""" + jobs = self._get_jobs() + jobs.setParent(self) + return jobs + + def __len__(self): + return self._size() + + def __contains__(self, job): + return self._has_job(job) + + def __iter__(self): + names = self.getAvailableJobNames() + for job in names: + yield self[job] + + def __getitem__(self, job): + if job in self: + return self._get_job(job).setParent(self) + + def getAvailableJobNames(self): + """@rtype: StringList""" + return [str(x) for x in self._alloc_list().setParent(self)] + + def del_job(self, job): + return self._del_job(job) + + def has_job(self, job): + return job in self + + def get_job(self, job): + """@rtype: ExtJob""" + return self[job] + + def add_job(self, job_name, new_job): + if not new_job.isReference(): + new_job.convertToCReference(self) + + self._add_job(job_name, new_job) + + def free(self): + self._free() + + def __repr__(self): + return self._create_repr("size=%d, joblist=%s" % (len(self), self.get_jobs())) diff --git a/res/job_queue/external_ert_script.py b/res/job_queue/external_ert_script.py new file mode 100644 index 00000000000..3d59c07a444 --- /dev/null +++ b/res/job_queue/external_ert_script.py @@ -0,0 +1,38 @@ +import codecs +import sys +from subprocess import Popen, PIPE +from res.job_queue import ErtScript + + +class ExternalErtScript(ErtScript): + def __init__(self, ert, executable): + super(ExternalErtScript, self).__init__(ert) + + self.__executable = executable + self.__job = None + + def run(self, *args): + command = [self.__executable] + command.extend([str(arg) for arg in args]) + + self.__job = Popen(command, stdout=PIPE, stderr=PIPE) + + # The job will complete before stdout and stderr is returned + self._stdoutdata, self._stderrdata = self.__job.communicate() + + self._stdoutdata = codecs.decode(self._stdoutdata, "utf8", "replace") + self._stderrdata = codecs.decode(self._stderrdata, "utf8", "replace") + + sys.stdout.write(self._stdoutdata) + + if self.__job.returncode != 0: + raise Exception(self._stderrdata) + + return None + + def cancel(self): + super(ExternalErtScript, self).cancel() + if self.__job is not None: + self.__job.terminate() + + self.__job.kill() diff --git a/res/job_queue/forward_model.py b/res/job_queue/forward_model.py new file mode 100644 index 00000000000..1ab0edb814d --- /dev/null +++ b/res/job_queue/forward_model.py @@ -0,0 +1,91 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'forward_model.py' 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. +from cwrap import BaseCClass +from res import ResPrototype +from res.job_queue import ExtJob, ExtJoblist +from res.job_queue import EnvironmentVarlist +from ecl.util.util import StringList +from res.util.substitution_list import SubstitutionList + + +class ForwardModel(BaseCClass): + TYPE_NAME = "forward_model" + + _alloc = ResPrototype("void* forward_model_alloc(ext_joblist)", bind=False) + _free = ResPrototype("void forward_model_free( forward_model )") + _clear = ResPrototype("void forward_model_clear(forward_model)") + _add_job = ResPrototype("ext_job_ref forward_model_add_job(forward_model, char*)") + _alloc_joblist = ResPrototype( + "stringlist_obj forward_model_alloc_joblist(forward_model)" + ) + _iget_job = ResPrototype("ext_job_ref forward_model_iget_job( forward_model, int)") + _get_length = ResPrototype("int forward_model_get_length(forward_model)") + _formatted_fprintf = ResPrototype( + "void forward_model_formatted_fprintf(forward_model, char*, char*, char*, subst_list, int, env_varlist)" + ) + + def __init__(self, ext_joblist): + c_ptr = self._alloc(ext_joblist) + if c_ptr: + super(ForwardModel, self).__init__(c_ptr) + else: + raise ValueError( + "Failed to construct forward model from provided ext_joblist %s" + % ext_joblist + ) + + def __len__(self): + return self._get_length() + + def joblist(self): + """@rtype: StringList""" + return self._alloc_joblist() + + def iget_job(self, index): + """@rtype: ExtJob""" + return self._iget_job(index).setParent(self) + + def add_job(self, name): + """@rtype: ExtJob""" + return self._add_job(name).setParent(self) + + def clear(self): + self._clear() + + def free(self): + self._free() + + def formatted_fprintf( + self, run_id, path, data_root, global_args, umask, env_varlist + ): + self._formatted_fprintf( + run_id, path, data_root, global_args, umask, env_varlist + ) + + def __repr__(self): + return self._create_repr("joblist=%s" % self.joblist()) + + def get_size(self): + return len(self) + + def __ne__(self, other): + return not self == other + + def __eq__(self, other): + for i in range(len(self)): + if self.iget_job(i) != other.iget_job(i): + return False + return True diff --git a/res/job_queue/forward_model_status.py b/res/job_queue/forward_model_status.py new file mode 100644 index 00000000000..b8ef4bbc2ba --- /dev/null +++ b/res/job_queue/forward_model_status.py @@ -0,0 +1,154 @@ +# Copyright (C) 2018 Equinor ASA, Norway. +# +# The file 'forward_model_status.py' 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. +import os.path +import json +import datetime +import time +import sys +from job_runner.reporting.file import File +from job_runner import JOBS_FILE + + +def _serialize_date(dt): + if dt is None: + return None + + return time.mktime(dt.timetuple()) + + +def _deserialize_date(serial_dt): + if serial_dt is None: + return None + + time_struct = time.localtime(serial_dt) + return datetime.datetime(*time_struct[0:6]) + + +class ForwardModelJobStatus(object): + def __init__( + self, + name, + start_time=None, + end_time=None, + status="Waiting", + error=None, + std_out_file="", + std_err_file="", + current_memory_usage=0, + max_memory_usage=0, + ): + + self.start_time = start_time + self.end_time = end_time + self.name = name + self.status = status + self.error = error + self.std_out_file = std_out_file + self.std_err_file = std_err_file + self.current_memory_usage = current_memory_usage + self.max_memory_usage = max_memory_usage + + @classmethod + def load(cls, job, data, run_path): + start_time = _deserialize_date(data["start_time"]) + end_time = _deserialize_date(data["end_time"]) + name = data["name"] + status = data["status"] + error = data["error"] + current_memory_usage = data["current_memory_usage"] + max_memory_usage = data["max_memory_usage"] + std_err_file = job["stderr"] + std_out_file = job["stdout"] + return cls( + name, + start_time=start_time, + end_time=end_time, + status=status, + error=error, + std_out_file=os.path.join(run_path, std_out_file), + std_err_file=os.path.join(run_path, std_err_file), + current_memory_usage=current_memory_usage, + max_memory_usage=max_memory_usage, + ) + + def __str__(self): + return "name:{} start_time:{} end_time:{} status:{} error:{} ".format( + self.name, self.start_time, self.end_time, self.status, self.error + ) + + def dump_data(self): + return { + "name": self.name, + "status": self.status, + "error": self.error, + "start_time": _serialize_date(self.start_time), + "end_time": _serialize_date(self.end_time), + "stdout": self.std_out_file, + "stderr": self.std_err_file, + "current_memory_usage": self.current_memory_usage, + "max_memory_usage": self.max_memory_usage, + } + + +class ForwardModelStatus(object): + def __init__(self, run_id, start_time, end_time=None): + self.run_id = run_id + self.start_time = start_time + self.end_time = end_time + self._jobs = [] + + @classmethod + def try_load(cls, path): + status_file = os.path.join(path, File.STATUS_json) + jobs_file = os.path.join(path, JOBS_FILE) + + with open(status_file) as status_fp: + status_data = json.load(status_fp) + + with open(jobs_file) as jobs_fp: + job_data = json.load(jobs_fp) + + start_time = _deserialize_date(status_data["start_time"]) + end_time = _deserialize_date(status_data["end_time"]) + status = cls(status_data["run_id"], start_time, end_time=end_time) + + for job, state in zip(job_data["jobList"], status_data["jobs"]): + status.add_job(ForwardModelJobStatus.load(job, state, path)) + + return status + + @classmethod + def load(cls, path, num_retry=10): + sleep_time = 0.10 + attempt = 0 + + while attempt < num_retry: + try: + status = cls.try_load(path) + return status + except (EnvironmentError, ValueError): + attempt += 1 + if attempt < num_retry: + time.sleep(sleep_time) + + return None + + @property + def jobs(self): + return self._jobs + + def add_job(self, job): + self._jobs.append(job) diff --git a/res/job_queue/function_ert_script.py b/res/job_queue/function_ert_script.py new file mode 100644 index 00000000000..7b4c2166bd5 --- /dev/null +++ b/res/job_queue/function_ert_script.py @@ -0,0 +1,63 @@ +import ecl +import cwrap + +from res.job_queue import ErtScript +from ecl.util.util.stringlist import StringList + + +class _NonePrototype(cwrap.Prototype): + lib = cwrap.load(None) + + def __init__(self, prototype, bind=True): + super(_NonePrototype, self).__init__(_NonePrototype.lib, prototype, bind=bind) + + +class FunctionErtScript(ErtScript): + def __init__(self, ert, function_name, argument_types, argument_count): + super(FunctionErtScript, self).__init__(ert) + + parsed_argument_types = [] + + if ert is not None: + self.__function = _NonePrototype( + "void* %s(void*, stringlist)" % function_name + ) + + else: + for arg in argument_types: + if arg is bool: + parsed_argument_types.append("bool") + elif arg is str: + parsed_argument_types.append("char*") + elif arg is int: + parsed_argument_types.append("int") + elif arg is float: + parsed_argument_types.append("float") + else: + raise TypeError("Unknown type: %s" % arg) + + self.__function = _NonePrototype( + "void* %s(%s)" + % (function_name, ", ".join(parsed_argument_types[:argument_count])) + ) + + def run(self, *args): + ert = self.ert() + if ert is None: + # This is usually used for testing purposes without an ert instance + return self.__function(*args) + else: + str_args = StringList() + for arg in args: + str_args.append(arg) + + if hasattr(ert, "from_param"): + pointer = ert.from_param(ert) + else: + pointer = ert # ... + + return self.__function(pointer, str_args) + + def cancel(self): + # job is not cancellable and will just ignore the call + pass diff --git a/res/job_queue/job.py b/res/job_queue/job.py new file mode 100644 index 00000000000..5661c5a5a77 --- /dev/null +++ b/res/job_queue/job.py @@ -0,0 +1,89 @@ +# Copyright (C) 2011 Equinor ASA, Norway. +# +# The file 'job.py' 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. + + +import time +import datetime +from cwrap import BaseCClass +from res.job_queue import JobStatusType + +# This class and the interplay between this class and the Driver and +# JobQueue classes is quite fragile; in particular the Job class +# internalizes a void * pointer to the completely driver specific job +# information - this is way too low level. + + +class Job(BaseCClass): + TYPE_NAME = "job" + + def __init__(self, c_ptr, driver): + self.driver = driver + self.submit_time = datetime.datetime.now() + super(Job, self).__init__(c_ptr) + + def free(self): + pass + + def block(self): + while True: + status = self.status() + if ( + status == JobStatusType.JOB_QUEUE_DONE + or status == JobStatusType.JOB_QUEUE_EXIT + ): + break + else: + time.sleep(1) + + def kill(self): + self.driver.kill_job(self) + + @property + def run_time(self): + td = datetime.datetime.now() - self.submit_time + return td.seconds + td.days * 24 * 3600 + + @property + def status(self): + st = self.driver.get_status(self) + return st + + @property + def running(self): + status = self.driver.get_status(self) + if status == JobStatusType.JOB_QUEUE_RUNNING: + return True + else: + return False + + @property + def pending(self): + status = self.driver.get_status(self) + if status == JobStatusType.JOB_QUEUE_PENDING: + return True + else: + return False + + @property + def complete(self): + status = self.driver.get_status(self) + if ( + status == JobStatusType.JOB_QUEUE_DONE + or status == JobStatusType.JOB_QUEUE_EXIT + ): + return True + else: + return False diff --git a/res/job_queue/job_queue_manager.py b/res/job_queue/job_queue_manager.py new file mode 100644 index 00000000000..7b8df0587b2 --- /dev/null +++ b/res/job_queue/job_queue_manager.py @@ -0,0 +1,94 @@ +# Copyright (C) 2014 Equinor ASA, Norway. +# +# The file 'job_queue_manager.py' 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. +""" +Module implementing a queue for managing external jobs. + +""" +from res.job_queue import JobStatusType +from threading import BoundedSemaphore + +CONCURRENT_INTERNALIZATION = 1 + + +# TODO: there's no need for this class, all the behavior belongs in the queue +# class proper. +class JobQueueManager: + def __init__(self, queue, queue_evaluators=None): + self._queue = queue + self._queue_evaluators = queue_evaluators + self._pool_sema = BoundedSemaphore(value=CONCURRENT_INTERNALIZATION) + + @property + def queue(self): + return self._queue + + def stop_queue(self): + self.queue.kill_all_jobs() + + def getNumRunning(self): + return self.queue.count_status(JobStatusType.JOB_QUEUE_RUNNING) + + def getNumWaiting(self): + return self.queue.count_status(JobStatusType.JOB_QUEUE_WAITING) + + def getNumPending(self): + return self.queue.count_status(JobStatusType.JOB_QUEUE_PENDING) + + def getNumSuccess(self): + return self.queue.count_status(JobStatusType.JOB_QUEUE_SUCCESS) + + def getNumFailed(self): + return self.queue.count_status(JobStatusType.JOB_QUEUE_FAILED) + + def isRunning(self): + return self.queue.is_active() + + def isJobComplete(self, job_index): + return not ( + self.queue.job_list[job_index].is_running() + or self.queue.job_list[job_index].status == JobStatusType.JOB_QUEUE_WAITING + ) + + def isJobRunning(self, job_index): + return self.queue.job_list[job_index].status == JobStatusType.JOB_QUEUE_RUNNING + + def isJobWaiting(self, job_index): + return self.queue.job_list[job_index].status == JobStatusType.JOB_QUEUE_WAITING + + def didJobFail(self, job_index): + return self.queue.job_list[job_index].status == JobStatusType.JOB_QUEUE_FAILED + + def didJobSucceed(self, job_index): + return self.queue.job_list[job_index].status == JobStatusType.JOB_QUEUE_SUCCESS + + def getJobStatus(self, job_index): + # See comment about return type in the prototype section at + # the top of class. + """@rtype: res.job_queue.job_status_type_enum.JobStatusType""" + int_status = self.queue.job_list[job_index].status + return JobStatusType(int_status) + + def __repr__(self): + nw = self.getNumWaiting() + nr = self.getNumRunning() + ns = self.getNumSuccess() + nf = self.getNumFailed() + ir = "running" if self.isRunning() else "not running" + status = "waiting=%d, running=%d, success=%d, failed=%d" % (nw, nr, ns, nf) + return "JobQueueManager(%s, %s)" % (status, ir) + + def execute_queue(self): + self._queue.execute_queue(self._pool_sema, self._queue_evaluators) diff --git a/res/job_queue/job_queue_node.py b/res/job_queue/job_queue_node.py new file mode 100644 index 00000000000..a8745616bb8 --- /dev/null +++ b/res/job_queue/job_queue_node.py @@ -0,0 +1,238 @@ +from cwrap import BaseCClass +from ecl.util.util import StringList +from res import ResPrototype +from res.job_queue import JobStatusType, ThreadStatus +from threading import Thread, Lock + +import time + + +class JobQueueNode(BaseCClass): + TYPE_NAME = "job_queue_node" + + _alloc = ResPrototype( + "void* job_queue_node_alloc_python(char*," + "char*," + "char*," + "int, " + "stringlist," + "int, " + "char*," + "char*," + "char*" + ")", + bind=False, + ) + _free = ResPrototype("void job_queue_node_free(job_queue_node)") + _submit = ResPrototype("int job_queue_node_submit_simple(job_queue_node, driver)") + _kill = ResPrototype("bool job_queue_node_kill_simple(job_queue_node, driver)") + + _get_status = ResPrototype( + "job_status_type_enum job_queue_node_get_status(job_queue_node)" + ) + _update_status = ResPrototype( + "bool job_queue_node_update_status_simple(job_queue_node, driver)" + ) + _set_status = ResPrototype( + "void job_queue_node_set_status(job_queue_node, job_status_type_enum)" + ) + _get_submit_attempt = ResPrototype( + "int job_queue_node_get_submit_attempt(job_queue_node)" + ) + + def __init__( + self, + job_script, + job_name, + run_path, + num_cpu, + status_file, + ok_file, + exit_file, + done_callback_function, + exit_callback_function, + callback_arguments, + max_runtime=None, + callback_timeout=None, + ): + self.done_callback_function = done_callback_function + self.exit_callback_function = exit_callback_function + self.callback_timeout = callback_timeout + self.callback_arguments = callback_arguments + argc = 1 + argv = StringList() + argv.append(run_path) + + self._thread_status = ThreadStatus.READY + self._thread = None + self._mutex = Lock() + + self.run_path = run_path + self._max_runtime = max_runtime + self._start_time = None + self._end_time = None + c_ptr = self._alloc( + job_name, + run_path, + job_script, + argc, + argv, + num_cpu, + ok_file, + status_file, + exit_file, + None, + None, + None, + None, + ) + + if c_ptr is not None: + super(JobQueueNode, self).__init__(c_ptr) + else: + raise ValueError("Unable to create job node object") + + def free(self): + self._free() + + @property + def submit_attempt(self): + return self._get_submit_attempt() + + @property + def status(self): + return self._get_status() + + @property + def thread_status(self): + return self._thread_status + + def submit(self, driver): + self._submit(driver) + + def run_done_callback(self): + callback_status = self.done_callback_function(self.callback_arguments) + + if callback_status: + self._set_status(JobStatusType.JOB_QUEUE_SUCCESS) + else: + self._set_status(JobStatusType.JOB_QUEUE_EXIT) + + return callback_status + + def run_exit_callback(self): + return self.exit_callback_function(self.callback_arguments) + + def is_running(self): + return ( + self.status == JobStatusType.JOB_QUEUE_PENDING + or self.status == JobStatusType.JOB_QUEUE_SUBMITTED + or self.status == JobStatusType.JOB_QUEUE_RUNNING + or self.status == JobStatusType.JOB_QUEUE_UNKNOWN + ) # dont stop monitoring if LSF commands are unavailable + + @property + def runtime(self): + if self._start_time is None: + return 0 + + if self._end_time is None: + return time.time() - self._start_time + + return self._end_time - self._start_time + + def _should_be_killed(self): + return self.thread_status == ThreadStatus.STOPPING or ( + self._max_runtime is not None and self.runtime >= self._max_runtime + ) + + def _job_monitor(self, driver, pool_sema, max_submit): + + self.submit(driver) + self.update_status(driver) + + while self.is_running(): + if ( + self._start_time is None + and self.status == JobStatusType.JOB_QUEUE_RUNNING + ): + self._start_time = time.time() + time.sleep(1) + self.update_status(driver) + if self._should_be_killed(): + self._kill(driver) + if ( + self._max_runtime + and self.runtime >= self._max_runtime + and self.callback_timeout + ): + self.callback_timeout(self.callback_arguments) + + self._end_time = time.time() + + with self._mutex: + if self.status == JobStatusType.JOB_QUEUE_DONE: + with pool_sema: + self.run_done_callback() + + if self.status == JobStatusType.JOB_QUEUE_SUCCESS: + pass + elif self.status == JobStatusType.JOB_QUEUE_EXIT: + if self.submit_attempt < max_submit: + self._set_thread_status(ThreadStatus.READY) + return + else: + self._set_status(JobStatusType.JOB_QUEUE_FAILED) + self.run_exit_callback() + elif self.status == JobStatusType.JOB_QUEUE_IS_KILLED: + pass + else: + self._set_thread_status(ThreadStatus.FAILED) + raise AssertionError( + "Unexpected job status type after running job: {}".format( + self.status + ) + ) + + self._set_thread_status(ThreadStatus.DONE) + + def run(self, driver, pool_sema, max_submit=2): + # Prevent multiple threads working on the same object + self.wait_for() + # Do not start if already kill signal is sent + if self.thread_status == ThreadStatus.STOPPING: + self._set_thread_status(ThreadStatus.DONE) + return + + self._set_thread_status(ThreadStatus.RUNNING) + self._start_time = None + self._thread = Thread( + target=self._job_monitor, args=(driver, pool_sema, max_submit) + ) + self._thread.start() + + def stop(self): + with self._mutex: + if self.thread_status == ThreadStatus.RUNNING: + self._set_thread_status(ThreadStatus.STOPPING) + elif self.thread_status == ThreadStatus.READY: + # clean-up to get the correct status after being stopped by user + self._set_thread_status(ThreadStatus.DONE) + self._set_status(JobStatusType.JOB_QUEUE_FAILED) + + assert self.thread_status in [ + ThreadStatus.DONE, + ThreadStatus.STOPPING, + ThreadStatus.FAILED, + ] + + def wait_for(self): + if self._thread is not None and self._thread.is_alive(): + self._thread.join() + + def update_status(self, driver): + if self.status != JobStatusType.JOB_QUEUE_WAITING: + self._update_status(driver) + + def _set_thread_status(self, new_status): + self._thread_status = new_status diff --git a/res/job_queue/job_status_type_enum.py b/res/job_queue/job_status_type_enum.py new file mode 100644 index 00000000000..ae76877ae9e --- /dev/null +++ b/res/job_queue/job_status_type_enum.py @@ -0,0 +1,58 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'job_status_type_enum.py' 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. +from cwrap import BaseCEnum + + +class JobStatusType(BaseCEnum): + TYPE_NAME = "job_status_type_enum" + JOB_QUEUE_NOT_ACTIVE = None # This value is used in external query routines - for jobs which are (currently) not active. */ + JOB_QUEUE_WAITING = None # A node which is waiting in the internal queue. + JOB_QUEUE_SUBMITTED = None # Internal status: It has has been submitted - the next status update will (should) place it as pending or running. + JOB_QUEUE_PENDING = None # A node which is pending - a status returned by the external system. I.e LSF + JOB_QUEUE_RUNNING = None # The job is running + JOB_QUEUE_DONE = None # The job is done - but we have not yet checked if the target file is produced */ + JOB_QUEUE_EXIT = None # The job has exited - check attempts to determine if we retry or go to complete_fail */ + JOB_QUEUE_IS_KILLED = None # The job has been killed, following a JOB_QUEUE_DO_KILL - can restart. */ + JOB_QUEUE_DO_KILL = None # The the job should be killed, either due to user request, or automated measures - the job can NOT be restarted.. */ + JOB_QUEUE_SUCCESS = None + JOB_QUEUE_RUNNING_DONE_CALLBACK = None + JOB_QUEUE_RUNNING_EXIT_CALLBACK = None + JOB_QUEUE_STATUS_FAILURE = None + JOB_QUEUE_FAILED = None + JOB_QUEUE_DO_KILL_NODE_FAILURE = None + JOB_QUEUE_UNKNOWN = None + + @classmethod + def from_string(cls, string): + return super().from_string(string) + + +JobStatusType.addEnum("JOB_QUEUE_NOT_ACTIVE", 1) +JobStatusType.addEnum("JOB_QUEUE_WAITING", 2) +JobStatusType.addEnum("JOB_QUEUE_SUBMITTED", 4) +JobStatusType.addEnum("JOB_QUEUE_PENDING", 8) +JobStatusType.addEnum("JOB_QUEUE_RUNNING", 16) +JobStatusType.addEnum("JOB_QUEUE_DONE", 32) +JobStatusType.addEnum("JOB_QUEUE_EXIT", 64) +JobStatusType.addEnum("JOB_QUEUE_IS_KILLED", 128) +JobStatusType.addEnum("JOB_QUEUE_DO_KILL", 256) +JobStatusType.addEnum("JOB_QUEUE_SUCCESS", 512) +JobStatusType.addEnum("JOB_QUEUE_RUNNING_DONE_CALLBACK", 1024) +JobStatusType.addEnum("JOB_QUEUE_RUNNING_EXIT_CALLBACK", 2048) +JobStatusType.addEnum("JOB_QUEUE_STATUS_FAILURE", 4096) +JobStatusType.addEnum("JOB_QUEUE_FAILED", 8192) +JobStatusType.addEnum("JOB_QUEUE_DO_KILL_NODE_FAILURE", 16384) +JobStatusType.addEnum("JOB_QUEUE_UNKNOWN", 32768) diff --git a/res/job_queue/queue.py b/res/job_queue/queue.py new file mode 100644 index 00000000000..13047043fc0 --- /dev/null +++ b/res/job_queue/queue.py @@ -0,0 +1,607 @@ +# Copyright (C) 2011 Equinor ASA, Norway. +# +# The file 'job_queue.py' 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. +""" +Module implementing a queue for managing external jobs. + +""" + +import asyncio +import copy +import json +import logging +import time +import ssl +import typing + +import websockets +from websockets.datastructures import Headers +from cloudevents.http import CloudEvent, to_json +from cwrap import BaseCClass +from job_runner import JOBS_FILE, CERT_FILE +from res import ResPrototype +from res.job_queue import JobQueueNode, JobStatusType, ThreadStatus + +logger = logging.getLogger(__name__) + +LONG_RUNNING_FACTOR = 1.25 + + +_FM_STEP_FAILURE = "com.equinor.ert.forward_model_step.failure" +_FM_STEP_PENDING = "com.equinor.ert.forward_model_step.pending" +_FM_STEP_RUNNING = "com.equinor.ert.forward_model_step.running" +_FM_STEP_SUCCESS = "com.equinor.ert.forward_model_step.success" +_FM_STEP_UNKNOWN = "com.equinor.ert.forward_model_step.unknown" +_FM_STEP_WAITING = "com.equinor.ert.forward_model_step.waiting" + + +_queue_state_to_event_type_map = { + "JOB_QUEUE_NOT_ACTIVE": _FM_STEP_WAITING, + "JOB_QUEUE_WAITING": _FM_STEP_WAITING, + "JOB_QUEUE_SUBMITTED": _FM_STEP_WAITING, + "JOB_QUEUE_PENDING": _FM_STEP_PENDING, + "JOB_QUEUE_RUNNING": _FM_STEP_RUNNING, + "JOB_QUEUE_DONE": _FM_STEP_RUNNING, + "JOB_QUEUE_EXIT": _FM_STEP_RUNNING, + "JOB_QUEUE_IS_KILLED": _FM_STEP_FAILURE, + "JOB_QUEUE_DO_KILL": _FM_STEP_FAILURE, + "JOB_QUEUE_SUCCESS": _FM_STEP_SUCCESS, + "JOB_QUEUE_RUNNING_DONE_CALLBACK": _FM_STEP_RUNNING, + "JOB_QUEUE_RUNNING_EXIT_CALLBACK": _FM_STEP_RUNNING, + "JOB_QUEUE_STATUS_FAILURE": _FM_STEP_UNKNOWN, + "JOB_QUEUE_FAILED": _FM_STEP_FAILURE, + "JOB_QUEUE_DO_KILL_NODE_FAILURE": _FM_STEP_FAILURE, + "JOB_QUEUE_UNKNOWN": _FM_STEP_UNKNOWN, +} + + +def _queue_state_event_type(state): + return _queue_state_to_event_type_map[state] + + +class JobQueue(BaseCClass): + # If the queue is created with size == 0 that means that it will + # just grow as needed; for the queue layer to know when to exit + # you must call the function submit_complete() when you have no + # more jobs to submit. + # + # If the number of jobs is known in advance you can create the + # queue with a finite value for size, in that case it is not + # necessary to explitly inform the queue layer when all jobs have + # been submitted. + TYPE_NAME = "job_queue" + _alloc = ResPrototype( + "void* job_queue_alloc( int , char* , char* , char* )", bind=False + ) + _start_user_exit = ResPrototype("bool job_queue_start_user_exit( job_queue )") + _get_user_exit = ResPrototype("bool job_queue_get_user_exit( job_queue )") + _free = ResPrototype("void job_queue_free( job_queue )") + _set_max_running = ResPrototype("void job_queue_set_max_running( job_queue , int)") + _get_max_running = ResPrototype("int job_queue_get_max_running( job_queue )") + _set_max_job_duration = ResPrototype( + "void job_queue_set_max_job_duration( job_queue , int)" + ) + _get_max_job_duration = ResPrototype( + "int job_queue_get_max_job_duration( job_queue )" + ) + _set_driver = ResPrototype("void job_queue_set_driver( job_queue , void* )") + _kill_job = ResPrototype("bool job_queue_kill_job( job_queue , int )") + _start_queue = ResPrototype("void job_queue_run_jobs( job_queue , int , bool)") + _run_jobs = ResPrototype("void job_queue_run_jobs_threaded(job_queue , int , bool)") + _sim_start = ResPrototype("time_t job_queue_iget_sim_start( job_queue , int)") + _iget_driver_data = ResPrototype( + "void* job_queue_iget_driver_data( job_queue , int)" + ) + + _num_running = ResPrototype("int job_queue_get_num_running( job_queue )") + _num_complete = ResPrototype("int job_queue_get_num_complete( job_queue )") + _num_waiting = ResPrototype("int job_queue_get_num_waiting( job_queue )") + _num_pending = ResPrototype("int job_queue_get_num_pending( job_queue )") + + _is_running = ResPrototype("bool job_queue_is_running( job_queue )") + _submit_complete = ResPrototype("void job_queue_submit_complete( job_queue )") + _iget_sim_start = ResPrototype("time_t job_queue_iget_sim_start( job_queue , int)") + _get_active_size = ResPrototype("int job_queue_get_active_size( job_queue )") + _get_pause = ResPrototype("bool job_queue_get_pause(job_queue)") + _set_pause_on = ResPrototype("void job_queue_set_pause_on(job_queue)") + _set_pause_off = ResPrototype("void job_queue_set_pause_off(job_queue)") + _get_max_submit = ResPrototype("int job_queue_get_max_submit(job_queue)") + + _get_job_status = ResPrototype( + "job_status_type_enum job_queue_iget_job_status(job_queue, int)" + ) + + _get_ok_file = ResPrototype("char* job_queue_get_ok_file(job_queue)") + _get_exit_file = ResPrototype("char* job_queue_get_exit_file(job_queue)") + _get_status_file = ResPrototype("char* job_queue_get_status_file(job_queue)") + _add_job = ResPrototype("int job_queue_add_job_node(job_queue, job_queue_node)") + + def __repr__(self): + nrun, ncom, nwait, npend = ( + self._num_running(), + self._num_complete(), + self._num_waiting(), + self._num_pending(), + ) + isrun = "running" if self.isRunning() else "not running" + cnt = "%s, num_running=%d, num_complete=%d, num_waiting=%d, num_pending=%d, active=%d" + return self._create_repr(cnt % (isrun, nrun, ncom, nwait, npend, len(self))) + + def __init__(self, driver, max_submit=2, size=0): + """ + Short doc... + The @max_submit argument says how many times the job be submitted (including a failure) + max_submit = 2: means that we can submit job once more + The @size argument is used to say how many jobs the queue will + run, in total. + size = 0: That means that you do not tell the queue in + advance how many jobs you have. The queue will just run + all the jobs you add, but you have to inform the queue in + some way that all jobs have been submitted. To achieve + this you should call the submit_complete() method when all + jobs have been submitted.# + + size > 0: The queue will know exactly how many jobs to run, + and will continue until this number of jobs have completed + - it is not necessary to call the submit_complete() method + in this case. + """ + + OK_file = "OK" + status_file = "STATUS" + exit_file = "ERROR" + self.job_list = [] + self._stopped = False + c_ptr = self._alloc(max_submit, OK_file, status_file, exit_file) + super(JobQueue, self).__init__(c_ptr) + self.size = size + + self.driver = driver + self._set_driver(driver.from_param(driver)) + self._qindex_to_iens = {} + self._state = [] + + def kill_job(self, queue_index): + """ + Will kill job nr @index. + """ + self._kill_job(queue_index) + + def start(self, blocking=False): + verbose = False + self._run_jobs(self.size, verbose) + + def clear(self): + pass + + def block_waiting(self): + """ + Will block as long as there are waiting jobs. + """ + while self.num_waiting > 0: + time.sleep(1) + + def block(self): + """ + Will block as long as there are running jobs. + """ + while self.isRunning: + time.sleep(1) + + def submit_complete(self): + """ + Method to inform the queue that all jobs have been submitted. + + If the queue has been created with size == 0 the queue has no + way of knowing when all jobs have completed; hence in that + case you must call the submit_complete() method when all jobs + have been submitted. + + If you know in advance exactly how many jobs you will run that + should be specified with the size argument when creating the + queue, in that case it is not necessary to call the + submit_complete() method. + """ + self._submit_complete() + + def isRunning(self): + return self._is_running() + + def num_running(self): + return self._num_running() + + def num_pending(self): + return self._num_pending() + + def num_waiting(self): + return self._num_waiting() + + def num_complete(self): + return self._num_complete() + + def __getitem__(self, index): + idx = index + ls = len(self) + if idx < 0: + idx += ls + if 0 <= idx < ls: + return self._iget_driver_data(idx) + raise IndexError( + "index out of range, was: %d should be in [0, %d)" % (index, ls) + ) + + def exists(self, index): + return self[index] + + def get_max_running(self): + return self.driver.get_max_running() + + def set_max_running(self, max_running): + self.driver.set_max_running(max_running) + + def get_max_job_duration(self): + return self._get_max_job_duration() + + def set_max_job_duration(self, max_duration): + self._set_max_job_duration(max_duration) + + @property + def max_submit(self): + return self._get_max_submit() + + def killAllJobs(self): + # The queue will not set the user_exit flag before the + # queue is in a running state. If the queue does not + # change to running state within a timeout the C function + # will return False, and that False value is just passed + # along. + user_exit = self._start_user_exit() + if user_exit: + while self.isRunning(): + time.sleep(0.1) + return True + else: + return False + + def igetSimStart(self, job_index): + return self._iget_sim_start(self, job_index) + + def getUserExit(self): + # Will check if a user_exit has been initated on the job. The + # queue can be queried about this status until a + # job_queue_reset() call is invoked, and that should not be + # done before the queue is recycled to run another batch of + # simulations. + return self._get_user_exit() + + def set_pause_on(self): + self._set_pause_on() + + def set_pause_off(self): + self._set_pause_off() + + def free(self): + self._free() + + def __len__(self): + return self._get_active_size() + + def getJobStatus(self, job_number): + """@rtype: JobStatusType""" + return self._get_job_status(job_number) + + def is_active(self): + for job in self.job_list: + if ( + job.thread_status == ThreadStatus.READY + or job.thread_status == ThreadStatus.RUNNING + or job.thread_status == ThreadStatus.STOPPING + ): + return True + return False + + def fetch_next_waiting(self): + for job in self.job_list: + if job.thread_status == ThreadStatus.READY: + return job + return None + + def count_status(self, status): + return len([job for job in self.job_list if job.status == status]) + + @property + def stopped(self): + return self._stopped + + def kill_all_jobs(self): + self._stopped = True + + @property + def queue_size(self): + return len(self.job_list) + + @property + def ok_file(self): + return self._get_ok_file() + + @property + def exit_file(self): + return self._get_exit_file() + + @property + def status_file(self): + return self._get_status_file() + + def add_job(self, job, iens): + job.convertToCReference(None) + queue_index = self._add_job(job) + self.job_list.append(job) + self._qindex_to_iens[queue_index] = iens + self._state.append(job.status.value) + assert len(self._state) - 1 == queue_index + return queue_index + + def count_running(self): + return sum(job.thread_status == ThreadStatus.RUNNING for job in self.job_list) + + def max_running(self): + if self.get_max_running() == 0: + return len(self.job_list) + else: + return self.get_max_running() + + def available_capacity(self): + return not self.stopped and self.count_running() < self.max_running() + + def stop_jobs(self): + for job in self.job_list: + job.stop() + while self.is_active(): + time.sleep(1) + + async def stop_jobs_async(self): + for job in self.job_list: + job.stop() + while self.is_active(): + await asyncio.sleep(1) + + def assert_complete(self): + for job in self.job_list: + if job.thread_status != ThreadStatus.DONE: + msg = "Unexpected job status type after running job: {} with thread status: {}" + raise AssertionError(msg.format(job.status, job.thread_status)) + + def launch_jobs(self, pool_sema): + # Start waiting jobs + while self.available_capacity(): + job = self.fetch_next_waiting() + if job is None: + break + job.run( + driver=self.driver, + pool_sema=pool_sema, + max_submit=self.max_submit, + ) + + def execute_queue(self, pool_sema, evaluators): + while self.is_active() and not self.stopped: + self.launch_jobs(pool_sema) + + time.sleep(1) + + if evaluators is not None: + for func in evaluators: + func() + self._transition() + + if self.stopped: + self.stop_jobs() + + self.assert_complete() + self._transition() + + @staticmethod + def _translate_change_to_cloudevent(ee_id, real_id, status): + return CloudEvent( + { + "type": _queue_state_event_type(status), + "source": f"/ert/ee/{ee_id}/real/{real_id}/step/{0}", + "datacontenttype": "application/json", + }, + { + "queue_event_type": status, + }, + ) + + @staticmethod + async def _publish_changes(ee_id, changes, websocket): + events = [ + JobQueue._translate_change_to_cloudevent(ee_id, real_id, status) + for real_id, status in changes.items() + ] + for event in events: + await websocket.send(to_json(event)) + + async def execute_queue_async( + self, ws_uri, ee_id, pool_sema, evaluators, cert=None, token=None + ): + 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 ws_uri.startswith("wss") else None + headers = Headers() + if token is not None: + headers["token"] = token + async with websockets.connect( + ws_uri, ssl=ssl_context, extra_headers=headers + ) as websocket: + await JobQueue._publish_changes(ee_id, self.snapshot(), websocket) + + try: + while self.is_active() and not self.stopped: + self.launch_jobs(pool_sema) + + await asyncio.sleep(1) + + if evaluators is not None: + for func in evaluators: + func() + + await JobQueue._publish_changes( + ee_id, self._changes_after_transition(), websocket + ) + except asyncio.CancelledError: + if self.stopped: + logger.debug( + "observed that the queue had stopped after cancellation, stopping jobs..." + ) + self.stop_jobs() + logger.debug("jobs now stopped (after cancellation)") + raise + + if self.stopped: + logger.debug("observed that the queue had stopped, stopping jobs...") + await self.stop_jobs_async() + logger.debug("jobs now stopped") + self.assert_complete() + self._transition() + await JobQueue._publish_changes(ee_id, self.snapshot(), websocket) + + def add_job_from_run_arg(self, run_arg, res_config, max_runtime, ok_cb, exit_cb): + job_name = run_arg.job_name + run_path = run_arg.runpath + job_script = res_config.queue_config.job_script + num_cpu = res_config.queue_config.num_cpu + if num_cpu == 0: + num_cpu = res_config.ecl_config.num_cpu + + job = JobQueueNode( + job_script=job_script, + job_name=job_name, + run_path=run_path, + num_cpu=num_cpu, + status_file=self.status_file, + ok_file=self.ok_file, + exit_file=self.exit_file, + done_callback_function=ok_cb, + exit_callback_function=exit_cb, + callback_arguments=[run_arg, res_config], + max_runtime=max_runtime, + ) + + if job is None: + return + run_arg._set_queue_index(self.add_job(job, run_arg.iens)) + + def add_ee_stage(self, stage, callback_timeout=None): + job = JobQueueNode( + job_script=stage.get_job_script(), + job_name=stage.get_job_name(), + run_path=stage.get_run_path(), + num_cpu=stage.get_num_cpu(), + status_file=self.status_file, + ok_file=self.ok_file, + exit_file=self.exit_file, + done_callback_function=stage.get_done_callback(), + exit_callback_function=stage.get_exit_callback(), + callback_arguments=stage.get_callback_arguments(), + max_runtime=stage.get_max_runtime(), + callback_timeout=callback_timeout, + ) + if job is None: + raise ValueError("JobQueueNode constructor created None job") + + iens = stage.get_run_arg().iens + stage.get_run_arg()._set_queue_index(self.add_job(job, iens)) + + def stop_long_running_jobs(self, minimum_required_realizations): + finished_realizations = self.count_status(JobStatusType.JOB_QUEUE_DONE) + if finished_realizations < minimum_required_realizations: + return + + completed_jobs = [ + job for job in self.job_list if job.status == JobStatusType.JOB_QUEUE_DONE + ] + average_runtime = sum([job.runtime for job in completed_jobs]) / float( + len(completed_jobs) + ) + + for job in self.job_list: + if job.runtime > LONG_RUNNING_FACTOR * average_runtime: + job.stop() + + def _transition( + self, + ) -> typing.Tuple[typing.List[JobStatusType], typing.List[JobStatusType]]: + """Transition to a new state, return both old and new state.""" + new_state = [job.status.value for job in self.job_list] + old_state = copy.copy(self._state) + self._state = new_state + return old_state, new_state + + def _diff_states( + self, + old_state: typing.List[JobStatusType], + new_state: typing.List[JobStatusType], + ) -> typing.Dict[int, str]: + """Return the diff between old_state and new_state.""" + changes = {} + + diff = list(map(lambda s: s[0] == s[1], zip(old_state, new_state))) + if len(diff) > 0: + for q_index, equal in enumerate(diff): + if not equal: + st = str(JobStatusType(new_state[q_index])) + changes[self._qindex_to_iens[q_index]] = st + return changes + + def _changes_after_transition(self) -> typing.Dict[int, str]: + old_state, new_state = self._transition() + return self._diff_states(old_state, new_state) + + def snapshot(self) -> typing.Optional[typing.Dict[int, str]]: + """Return the whole state, or None if there was no snapshot.""" + snapshot = {} + for q_index, state_val in enumerate(self._state): + st = str(JobStatusType(state_val)) + try: + snapshot[self._qindex_to_iens[q_index]] = st + except KeyError as e: + logger.debug(f"differ could produce no snapshot due to {e}") + return None + return snapshot + + def add_ensemble_evaluator_information_to_jobs_file( + self, ee_id, dispatch_url, cert, token + ): + for q_index, q_node in enumerate(self.job_list): + if cert is not None: + cert_path = f"{q_node.run_path}/{CERT_FILE}" + with open(cert_path, "w") as cert_file: + cert_file.write(cert) + with open(f"{q_node.run_path}/{JOBS_FILE}", "r+") as jobs_file: + data = json.load(jobs_file) + + data["ee_id"] = ee_id + data["real_id"] = self._qindex_to_iens[q_index] + data["step_id"] = 0 + data["dispatch_url"] = dispatch_url + data["ee_token"] = token + data["ee_cert_path"] = cert_path if cert is not None else None + + jobs_file.seek(0) + jobs_file.truncate() + json.dump(data, jobs_file, indent=4) diff --git a/res/job_queue/run_status_type_enum.py b/res/job_queue/run_status_type_enum.py new file mode 100644 index 00000000000..51deb846e6a --- /dev/null +++ b/res/job_queue/run_status_type_enum.py @@ -0,0 +1,31 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'job_status_type_enum.py' 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. +from cwrap import BaseCEnum + + +class RunStatusType(BaseCEnum): + TYPE_NAME = "run_status_type_enum" + + JOB_LOAD_FAILURE = None + JOB_RUN_FAILURE = None + + @classmethod + def from_string(cls, string): + pass + + +RunStatusType.addEnum("JOB_RUN_FAILURE", 2) +RunStatusType.addEnum("JOB_LOAD_FAILURE", 3) diff --git a/res/job_queue/thread_status_type_enum.py b/res/job_queue/thread_status_type_enum.py new file mode 100644 index 00000000000..ea93b9d65de --- /dev/null +++ b/res/job_queue/thread_status_type_enum.py @@ -0,0 +1,23 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'thread_status_type_enum.py' 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. + + +class ThreadStatus: + READY = 1 + RUNNING = 2 + FAILED = 3 + DONE = 4 + STOPPING = 5 diff --git a/res/job_queue/workflow.py b/res/job_queue/workflow.py new file mode 100644 index 00000000000..405a19fc794 --- /dev/null +++ b/res/job_queue/workflow.py @@ -0,0 +1,143 @@ +import time +import sys +from res.config import ConfigError +from cwrap import BaseCClass +from res import ResPrototype +from res.job_queue import WorkflowJoblist, WorkflowJob +from res.util.substitution_list import SubstitutionList +import os +from ert_logger import log_message + + +class Workflow(BaseCClass): + TYPE_NAME = "workflow" + _alloc = ResPrototype("void* workflow_alloc(char*, workflow_joblist)", bind=False) + _free = ResPrototype("void workflow_free(workflow)") + _count = ResPrototype("int workflow_size(workflow)") + _iget_job = ResPrototype("workflow_job_ref workflow_iget_job(workflow, int)") + _iget_args = ResPrototype("stringlist_ref workflow_iget_arguments(workflow, int)") + + _try_compile = ResPrototype("bool workflow_try_compile(workflow, subst_list)") + _get_last_error = ResPrototype("config_error_ref workflow_get_last_error(workflow)") + _get_src_file = ResPrototype("char* worflow_get_src_file(workflow)") + + def __init__(self, src_file, job_list): + """ + @type src_file: str + @type job_list: WorkflowJoblist + """ + c_ptr = self._alloc(src_file, job_list) + super(Workflow, self).__init__(c_ptr) + + self.__running = False + self.__cancelled = False + self.__current_job = None + self.__status = {} + + def __len__(self): + return self._count() + + def __getitem__(self, index): + """ + @type index: int + @rtype: tuple of (WorkflowJob, arguments) + """ + job = self._iget_job(index) + args = self._iget_args(index) + return job, args + + def __iter__(self): + for index in range(len(self)): + yield self[index] + + @property + def src_file(self): + return self._get_src_file() + + @staticmethod + def _log_workflow_job_usage(job_name): + payload = { + "subsystem": "ert_workflow", + "ert_job": job_name, + "cwd": os.getcwd(), + } + log_message(payload) + + def run(self, ert, verbose=False, context=None): + """ + @type ert: res.enkf.enkf_main.EnKFMain + @type verbose: bool + @type context: SubstitutionList + @rtype: bool + """ + # Reset status + self.__status = {} + self.__running = True + success = self._try_compile(context) + if not success: + msg = ( + "** Warning: The workflow file {} is not valid - " + "make sure the workflow jobs are defined accordingly\n" + ) + sys.stderr.write(msg.format(self.src_file)) + + self.__running = False + return False + + for job, args in self: + self.__current_job = job + if not self.__cancelled: + self._log_workflow_job_usage(job.name()) + + return_value = job.run(ert, args, verbose) + self.__status[job.name()] = { + "stdout": job.stdoutdata(), + "stderr": job.stderrdata(), + "completed": not job.hasFailed(), + "return": return_value, + } + + self.__current_job = None + self.__running = False + return success + + def free(self): + self._free() + + def isRunning(self): + return self.__running + + def cancel(self): + if self.__current_job is not None: + self.__current_job.cancel() + + self.__cancelled = True + + def isCancelled(self): + return self.__cancelled + + def wait(self): + while self.isRunning(): + time.sleep(1) + + def getLastError(self): + """@rtype: ConfigError""" + return self._get_last_error() + + def getJobsReport(self): + """@rtype: {dict}""" + return self.__status + + @classmethod + def createCReference(cls, c_pointer, parent=None): + workflow = super(Workflow, cls).createCReference(c_pointer, parent) + workflow.__running = False + workflow.__cancelled = False + workflow.__current_job = None + return workflow + + def __ne__(self, other): + return not (self == other) + + def __eq__(self, other): + return os.path.realpath(self.src_file) == os.path.realpath(other.src_file) diff --git a/res/job_queue/workflow_job.py b/res/job_queue/workflow_job.py new file mode 100644 index 00000000000..823687ce3f5 --- /dev/null +++ b/res/job_queue/workflow_job.py @@ -0,0 +1,238 @@ +import os + +from cwrap import BaseCClass +from res import ResPrototype +from res.job_queue import ErtScript, FunctionErtScript, ErtPlugin, ExternalErtScript +from res.config import ContentTypeEnum + + +class WorkflowJob(BaseCClass): + TYPE_NAME = "workflow_job" + _alloc = ResPrototype("void* workflow_job_alloc(char*, bool)", bind=False) + _alloc_parser = ResPrototype( + "config_parser_obj workflow_job_alloc_config( )", bind=False + ) + _alloc_from_file = ResPrototype( + "workflow_job_obj workflow_job_config_alloc( char* , config_parser , char*)", + bind=False, + ) + _free = ResPrototype("void workflow_job_free(workflow_job)") + _name = ResPrototype("char* workflow_job_get_name(workflow_job)") + _internal = ResPrototype("bool workflow_job_internal(workflow_job)") + _is_internal_script = ResPrototype( + "bool workflow_job_is_internal_script(workflow_job)" + ) + _get_internal_script = ResPrototype( + "char* workflow_job_get_internal_script_path(workflow_job)" + ) + _get_function = ResPrototype("char* workflow_job_get_function(workflow_job)") + _get_module = ResPrototype("char* workflow_job_get_module(workflow_job)") + _get_executable = ResPrototype("char* workflow_job_get_executable(workflow_job)") + _min_arg = ResPrototype("int workflow_job_get_min_arg(workflow_job)") + _max_arg = ResPrototype("int workflow_job_get_max_arg(workflow_job)") + _arg_type = ResPrototype( + "config_content_type_enum workflow_job_iget_argtype(workflow_job, int)" + ) + + @classmethod + def configParser(cls): + return cls._alloc_parser() + + @classmethod + def fromFile(cls, config_file, name=None, parser=None): + if os.path.isfile(config_file) and os.access(config_file, os.R_OK): + if parser is None: + parser = cls.configParser() + + if name is None: + name = os.path.basename(config_file) + + # NB: Observe argument reoredring. + return cls._alloc_from_file(name, parser, config_file) + else: + raise IOError("Could not open config_file:%s" % config_file) + + def __init__(self, name, internal=True): + c_ptr = self._alloc(name, internal) + super(WorkflowJob, self).__init__(c_ptr) + + self.__script = None + """ :type: ErtScript """ + self.__running = False + + def isInternal(self): + """@rtype: bool""" + return self._internal() + + def name(self): + """@rtype: str""" + return self._name() + + def minimumArgumentCount(self): + """@rtype: int""" + return self._min_arg() + + def maximumArgumentCount(self): + """@rtype: int""" + return self._max_arg() + + def functionName(self): + """@rtype: str""" + return self._get_function() + + def module(self): + """@rtype: str""" + return self._get_module() + + def executable(self): + """@rtype: str""" + return self._get_executable() + + def isInternalScript(self): + """@rtype: bool""" + return self._is_internal_script() + + def getInternalScriptPath(self): + """@rtype: str""" + return self._get_internal_script() + + def isPlugin(self): + """@rtype: bool""" + if self.isInternalScript(): + script_obj = ErtScript.loadScriptFromFile(self.getInternalScriptPath()) + return script_obj is not None and issubclass(script_obj, ErtPlugin) + + return False + + def argumentTypes(self): + """@rtype: list of type""" + + result = [] + for index in range(self.maximumArgumentCount()): + t = self._arg_type(index) + if t == ContentTypeEnum.CONFIG_BOOL: + result.append(bool) + elif t == ContentTypeEnum.CONFIG_FLOAT: + result.append(float) + elif t == ContentTypeEnum.CONFIG_INT: + result.append(int) + elif t == ContentTypeEnum.CONFIG_STRING: + result.append(str) + else: + result.append(NoneType) + + return result + + def run(self, ert, arguments, verbose=False): + """ + @type ert: res.enkf.enkf_main.EnKFMain + @type arguments: list of str + @type verbose: bool + @rtype: ctypes.c_void_p + """ + self.__running = True + + min_arg = self.minimumArgumentCount() + if min_arg > 0 and len(arguments) < min_arg: + raise UserWarning( + "The job: %s requires at least %d arguments, %d given." + % (self.name(), min_arg, len(arguments)) + ) + + max_arg = self.maximumArgumentCount() + if 0 < max_arg < len(arguments): + raise UserWarning( + "The job: %s can only have %d arguments, %d given." + % (self.name(), max_arg, len(arguments)) + ) + + if self.isInternalScript(): + script_obj = ErtScript.loadScriptFromFile(self.getInternalScriptPath()) + self.__script = script_obj(ert) + result = self.__script.initializeAndRun( + self.argumentTypes(), arguments, verbose=verbose + ) + + elif self.isInternal() and not self.isInternalScript(): + self.__script = FunctionErtScript( + ert, + self.functionName(), + self.argumentTypes(), + argument_count=len(arguments), + ) + result = self.__script.initializeAndRun( + self.argumentTypes(), arguments, verbose=verbose + ) + + elif not self.isInternal(): + self.__script = ExternalErtScript(ert, self.executable()) + result = self.__script.initializeAndRun( + self.argumentTypes(), arguments, verbose=verbose + ) + + else: + raise UserWarning("Unknown script type!") + + self.__running = False + return result + + def cancel(self): + if self.__script is not None: + self.__script.cancel() + + def isRunning(self): + return self.__running + + def isCancelled(self): + return self.__script.isCancelled() + + def hasFailed(self): + """@rtype: bool""" + return self.__script.hasFailed() + + def free(self): + self._free() + + def stdoutdata(self): + """@rtype: str""" + return self.__script.stdoutdata + + def stderrdata(self): + """@rtype: str""" + return self.__script.stderrdata + + @classmethod + def createCReference(cls, c_pointer, parent=None): + workflow = super(WorkflowJob, cls).createCReference(c_pointer, parent) + workflow.__script = None + workflow.__running = False + return workflow + + def __ne__(self, other): + return not (self == other) + + def __eq__(self, other): + + if self.executable() != other.executable(): + return False + + if self._is_internal_script() != other._is_internal_script(): + return False + + if self._is_internal_script(): + if self._get_internal_script() != other._get_internal_script(): + return False + + if self._name() != other._name(): + return False + + if self._min_arg() != other._min_arg(): + return False + + if self._max_arg() != other._max_arg(): + return False + + if self._get_module() != other._get_module(): + return False + + return True diff --git a/res/job_queue/workflow_joblist.py b/res/job_queue/workflow_joblist.py new file mode 100644 index 00000000000..c7975125275 --- /dev/null +++ b/res/job_queue/workflow_joblist.py @@ -0,0 +1,70 @@ +import os +from cwrap import BaseCClass +from res import ResPrototype +from res.job_queue import WorkflowJob + + +class WorkflowJoblist(BaseCClass): + TYPE_NAME = "workflow_joblist" + _alloc = ResPrototype("void* workflow_joblist_alloc()", bind=False) + _free = ResPrototype("void workflow_joblist_free(workflow_joblist)") + _add_job = ResPrototype( + "void workflow_joblist_add_job(workflow_joblist, workflow_job)" + ) + _add_job_from_file = ResPrototype( + "bool workflow_joblist_add_job_from_file(workflow_joblist, char*, char*)" + ) + _has_job = ResPrototype( + "bool workflow_joblist_has_job(workflow_joblist, char*)" + ) + _get_job = ResPrototype( + "workflow_job_ref workflow_joblist_get_job(workflow_joblist, char*)" + ) + _count = ResPrototype( + "workflow_job_ref workflow_joblist_get_job(workflow_joblist, char*)" + ) + + def __init__(self): + c_ptr = self._alloc() + super(WorkflowJoblist, self).__init__(c_ptr) + + def addJob(self, job): + """@type job: WorkflowJob""" + job.convertToCReference(self) + self._add_job(job) + + def addJobFromFile(self, name, filepath): + """ + @type name: str + @type filepath: str + @rtype: bool + """ + if not os.path.exists(filepath): + raise UserWarning("Job file '%s' does not exist!" % filepath) + + return self._add_job_from_file(name, filepath) + + def __contains__(self, item): + """ + @type item: str or WorkflowJob + @rtype: bool + """ + + if isinstance(item, WorkflowJob): + item = item.name() + + return self._has_job(item) + + def __getitem__(self, item): + """ + @type item: str + @rtype: WorkflowJob + """ + + if not item in self: + return None + + return self._get_job(item) + + def free(self): + self._free() diff --git a/res/job_queue/workflow_runner.py b/res/job_queue/workflow_runner.py new file mode 100644 index 00000000000..8daf2b6d710 --- /dev/null +++ b/res/job_queue/workflow_runner.py @@ -0,0 +1,89 @@ +from res.job_queue import Workflow +from res.util.substitution_list import SubstitutionList +from concurrent import futures + + +class WorkflowRunner(object): + def __init__(self, workflow, ert=None, context=None): + """ + @type workflow: Workflow + @type ert: res.enkf.EnKFMain + @type context: SubstitutionList + """ + super(WorkflowRunner, self).__init__() + + self.__workflow = workflow + self.__ert = ert + + if context is None: + context = SubstitutionList() + + self.__context = context + self.__workflow_result = None + self._workflow_executor = futures.ThreadPoolExecutor(max_workers=1) + self._workflow_job = None + + def __enter__(self): + self.run() + return self + + def __exit__(self, type, value, traceback): + self.wait() + + def run(self): + if self.isRunning(): + raise AssertionError("An instance of workflow is already running!") + else: + self._workflow_job = self._workflow_executor.submit(self.__runWorkflow) + + def __runWorkflow(self): + self.__workflow_result = self.__workflow.run(self.__ert, context=self.__context) + + def isRunning(self): + """@rtype: bool""" + if self.__workflow.isRunning(): + return True + + # Completion of _workflow does not indicate that __workflow_result is + # set. Check future status, since __workflow_result follows future + # completion. + return self._workflow_job is not None and not self._workflow_job.done() + + def isCancelled(self): + """@rtype: bool""" + return self.__workflow.isCancelled() + + def cancel(self): + if self.isRunning(): + self.__workflow.cancel() + self.wait() + + def exception(self): + if self._workflow_job is not None: + return self._workflow_job._exception + return None + + def wait(self): + # This returns a tuple (done, pending), since we run only one job we don't need to use it + _, _ = futures.wait( + [self._workflow_job], timeout=None, return_when=futures.FIRST_EXCEPTION + ) + + def workflowResult(self): + """@rtype: bool or None""" + return self.__workflow_result + + def workflowReport(self): + """@rtype: {dict}""" + return self.__workflow.getJobsReport() + + def workflowError(self): + """@rtype: str""" + error = self.__workflow.getLastError() + + error_message = "" + + for error_line in error: + error_message += error_line + "\n" + + return error_message diff --git a/res/sched/__init__.py b/res/sched/__init__.py new file mode 100644 index 00000000000..8fbdb39904d --- /dev/null +++ b/res/sched/__init__.py @@ -0,0 +1,24 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file '__init__.py' 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. +import res +import ecl +import ecl.util +import ecl.util.geometry + +from cwrap import Prototype + +from .history_source_enum import HistorySourceEnum +from .history import History diff --git a/res/sched/history.py b/res/sched/history.py new file mode 100644 index 00000000000..0e9a2839095 --- /dev/null +++ b/res/sched/history.py @@ -0,0 +1,69 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'history.py' 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. + +from cwrap import BaseCClass +from res import ResPrototype +from res.sched import HistorySourceEnum +from ecl.summary import EclSum + + +class History(BaseCClass): + TYPE_NAME = "history" + + _alloc_from_refcase = ResPrototype( + "void* history_alloc_from_refcase(ecl_sum, bool)", bind=False + ) + _get_source_string = ResPrototype( + "char* history_get_source_string(history_source_enum)", bind=False + ) + _free = ResPrototype("void history_free( history )") + + def __init__(self, refcase=None, use_history=False, sched_file=None): + """ + @type refcase: EclSum + @type use_history: bool + @rtype: HistoryType + """ + if sched_file is not None: + raise ValueError("Cannot create history from sched_file.") + + if refcase is None: + ValueError("Refcase cannot be None when creating a History.") + + self._init_from = "refcase" + self._init_val = str(refcase) + c_ptr = self._alloc_from_refcase(refcase, use_history) + if c_ptr: + super(History, self).__init__(c_ptr) + else: + raise ValueError("Invalid input. Failed to create History.") + + @staticmethod + def get_source_string(history_source_type): + """ + @type history_source_type: HistorySourceEnum + @rtype: str + """ + return History._get_source_string(history_source_type) + + def free(self): + self._free(self) + + def __repr__(self): + fr = self._init_from + va = self._init_val + ad = self._ad_str() + return "History(init_from = %s: %s) %s" % (fr, va, ad) diff --git a/res/sched/history_source_enum.py b/res/sched/history_source_enum.py new file mode 100644 index 00000000000..c238d01e3c2 --- /dev/null +++ b/res/sched/history_source_enum.py @@ -0,0 +1,15 @@ +from cwrap import BaseCEnum + + +class HistorySourceEnum(BaseCEnum): + TYPE_NAME = "history_source_enum" + SCHEDULE = None + REFCASE_SIMULATED = None + REFCASE_HISTORY = None + HISTORY_SOURCE_INVALID = None + + +HistorySourceEnum.addEnum("SCHEDULE", 0) +HistorySourceEnum.addEnum("REFCASE_SIMULATED", 1) +HistorySourceEnum.addEnum("REFCASE_HISTORY", 2) +HistorySourceEnum.addEnum("HISTORY_SOURCE_INVALID", 10) diff --git a/res/simulator/__init__.py b/res/simulator/__init__.py new file mode 100644 index 00000000000..b591fa5216c --- /dev/null +++ b/res/simulator/__init__.py @@ -0,0 +1,3 @@ +from .batch_simulator import BatchSimulator +from .batch_simulator_context import BatchContext +from .simulation_context import SimulationContext diff --git a/res/simulator/batch_simulator.py b/res/simulator/batch_simulator.py new file mode 100644 index 00000000000..2c9236cfb55 --- /dev/null +++ b/res/simulator/batch_simulator.py @@ -0,0 +1,206 @@ +from ecl.util.util import BoolVector + +from res.enkf import ResConfig, EnKFMain, EnkfConfigNode, EnkfNode, NodeId +from .batch_simulator_context import BatchContext + + +def _slug(entity): + entity = " ".join(str(entity).split()) + return "".join([x if x.isalnum() else "_" for x in entity.strip()]) + + +class BatchSimulator(object): + def __init__(self, res_config, controls, results, callback=None): + """Will create simulator which can be used to run multiple simulations. + + The @res_config argument should be a ResConfig object, representing the + fully configured state of libres. + + + The @controls argument configures which parameters the simulator should + get when actually simulating. The @controls argument should be a + dictionary like this : + + controls = { + "cmode": ["Well", "Group"], + "order": + "W" : ["01", "02", "03"] + } + + In this example, the first group of controls "cmode" includes two + controls, called "Well" and "Group". The second group of controls + "order" has one control "W" with three suffixes. + Note that: + - Either no variable in a group has suffixes or all the variables in + the group have suffixes. + - Suffixes must be specified as non-empty collections of strings. + - No duplicate groups/controls/suffixes are allowed + + When actually simulating, these values will be written to json files + looking like this: + + cmode.json = {"Well": 1.0, "Group": 2.0} + order.json = { + "W": + "01": 0.3, + "02": 1.0, + "03": 4.2 + } + + When later invoking the start() method the simulator expects to get + values for all parameters configured with the @controls argument, + otherwise an exception will be raised. + Internally in libres code the controls will be implemented as + 'ext_param' instances. + + + The @results argument is a list of keys of results which the simulator + expects to be generated by the forward model. If argument @results + looks like: + + results = ["CMODE", "order"] + + The simulator will look for the files 'CMODE_0' and 'order_0' in the + simulation folder. If those files are not produced by the simulator an + exception will be raised. + + The optional argument callback can be used to provide a callable + which will be called as: + + callback(run_context) + + When the simulator has started. For the callable passed as + callback you are encouraged to use the future proof signature: + + def callback(*args, **kwargs): + .... + + """ + if not isinstance(res_config, ResConfig): + raise ValueError("The first argument must be valid ResConfig instance") + + self.res_config = res_config + self.ert = EnKFMain(self.res_config) + self.control_keys = set(controls.keys()) + self.result_keys = set(results) + self.callback = callback + + ens_config = self.res_config.ensemble_config + for control_name, variables in controls.items(): + ens_config.addNode(EnkfConfigNode.create_ext_param(control_name, variables)) + + for key in results: + ens_config.addNode(EnkfConfigNode.create_gen_data(key, "{}_%d".format(key))) + + def _setup_sim(self, sim_id, controls, file_system): + def _set_ext_param(ext_param, key, assignment): + if isinstance(assignment, dict): # handle suffixes + suffixes = ext_param.config[key] + if len(assignment) != len(suffixes): + raise KeyError( + "Key {} is missing values for these suffixes: {}".format( + key, set(suffixes).difference(set(assignment.keys())) + ) + ) + for suffix, value in assignment.items(): + ext_node[key, suffix] = value + else: # assume assignment is a single numerical value + ext_node[key] = assignment + + node_id = NodeId(0, sim_id) + if set(controls.keys()) != self.control_keys: + err_msg = "Mismatch between initialized and provided control names." + raise KeyError(err_msg) + + for control_name, control in controls.items(): + ens_config = self.res_config.ensemble_config + node = EnkfNode(ens_config[control_name]) + ext_node = node.as_ext_param() + if len(ext_node) != len(control.keys()): + raise KeyError( + ("Expected {} variables for control {}, " "received {}.").format( + len(ext_node), control_name, len(control.keys()) + ) + ) + for var_name, var_setting in control.items(): + _set_ext_param(ext_node, var_name, var_setting) + node.save(file_system, node_id) + + def start(self, case_name, case_data): + """Start batch simulation, return a simulation context + + The start method will submit simulations to the queue system and then + return a BatchContext handle which can be used to query for simulation + status and results. The @case_name argument should just be string which + will be used as name for the storage of these simulations in the + system. The @controls argument is the set of control values, and the + corresponding ID of the external realisation used for the simulations. + The @control argument must match the control argument used when the + simulator was instantiated. Assuming the following @control argument + was passed to simulator construction: + + controls = { + "cmode": ["Well", "Group"], + "order": + "W" : ["01", "02", "03"] + } + + Then the following @case_data argument can be used in the start method + to simulate four simulations: + + [ + (1, + { + "cmode": {"Well": 2, "Group": 2}, + "order": { + "W": + "01": 2, + "02": 2, + "03": 5}, + }), + (1, + { + "cmode": {"Well": 1, "Group": 3}, + "order": {"W": ...}, + }), + (1, + { + "cmode": {"Well": 1, "Group": 7}, + "order": {"W": ...}, + }), + (2, + { + "cmode": {"Well": 1, "Group": -1}, + "order": {"W": ...}, + }), + ] + + The first integer argument in the tuple is the realisation id, so this + simulation batch will consist of a total of four simulations, where the + first three are based on realisation 1, and the last is based on + realisation 2. + + Observe that only one BatchSimulator should actually be running at a + time, so when you have called the 'start' method you need to let that + batch complete before you start a new batch. + """ + + self.ert.addDataKW("", _slug(case_name)) + file_system = self.ert.getEnkfFsManager().getFileSystem(case_name) + for sim_id, (geo_id, controls) in enumerate(case_data): + assert isinstance(geo_id, int) + self._setup_sim(sim_id, controls, file_system) + + # The input should be validated before we instantiate the BatchContext + # object, at that stage a job_queue object with multiple threads is + # started, and things will typically be in a quite sorry state if an + # exception occurs. + itr = 0 + mask = BoolVector(default_value=True, initial_size=len(case_data)) + sim_context = BatchContext( + self.result_keys, self.ert, file_system, mask, itr, case_data + ) + + if self.callback: + self.callback(sim_context) + return sim_context diff --git a/res/simulator/batch_simulator_context.py b/res/simulator/batch_simulator_context.py new file mode 100644 index 00000000000..3d6791b9c9a --- /dev/null +++ b/res/simulator/batch_simulator_context.py @@ -0,0 +1,96 @@ +import time +from collections import namedtuple +from res.enkf import NodeId, EnkfNode +import logging +import numpy as np +from .simulation_context import SimulationContext + +Status = namedtuple("Status", "waiting pending running complete failed") + + +class BatchContext(SimulationContext): + def __init__(self, result_keys, ert, fs, mask, itr, case_data): + """ + Handle which can be used to query status and results for batch simulation. + """ + super(BatchContext, self).__init__(ert, fs, mask, itr, case_data) + self.result_keys = result_keys + self.res_config = ert.resConfig() + + def join(self): + """ + Will block until the simulation is complete. + """ + while self.running(): + time.sleep(1) + + def running(self): + return self.isRunning() + + @property + def status(self): + """ + Will return the state of the simulations. + """ + return Status( + running=self.getNumRunning(), + waiting=self.getNumWaiting(), + pending=self.getNumPending(), + complete=self.getNumSuccess(), + failed=self.getNumFailed(), + ) + + def results(self): + """Will return the results of the simulations. + + Observe that this function will raise RuntimeError if the simulations + have not been completed. To be certain that the simulations have + completed you can call the join() method which will block until all + simulations have completed. + + The function will return all the results which were configured with the + @results when the simulator was created. The results will come as a + list of dictionaries of arrays of double values, i.e. if the @results + argument was: + + results = ["CMODE", "order"] + + when the simulator was created the results will be returned as: + + + [ {"CMODE" : [1,2,3], "order" : [1,1,3]}, + {"CMODE" : [1,4,1], "order" : [0,7,8]}, + None, + {"CMODE" : [6,1,0], "order" : [0,0,8]} ] + + For a simulation which consist of a total of four simulations, where the + None value indicates that the simulator was unable to compute a request. + The order of the list corresponds to case_data provided in the start + call. + + """ + if self.running(): + raise RuntimeError( + "Simulations are still running - need to wait before gettting results" + ) + + res = [] + nodes = [ + EnkfNode(self.res_config.ensemble_config[key]) for key in self.result_keys + ] + for sim_id in range(len(self)): + node_id = NodeId(0, sim_id) + if not self.didRealizationSucceed(sim_id): + logging.error( + "Simulation %d (node %s) failed." % (sim_id, str(node_id)) + ) + res.append(None) + continue + d = {} + for node in nodes: + node.load(self.get_sim_fs(), node_id) + data = node.asGenData().getData() + d[node.name()] = np.array(data) + res.append(d) + + return res diff --git a/res/simulator/simulation_context.py b/res/simulator/simulation_context.py new file mode 100644 index 00000000000..7e37751764f --- /dev/null +++ b/res/simulator/simulation_context.py @@ -0,0 +1,180 @@ +from res.job_queue import JobQueueManager, ForwardModelStatus +from res.enkf import ErtRunContext, EnkfSimulationRunner +from res.enkf.enums import EnkfRunType, HookRuntime +from threading import Thread +from time import sleep + + +class SimulationContext(object): + def __init__(self, ert, sim_fs, mask, itr, case_data): + self._ert = ert + """ :type: res.enkf.EnKFMain """ + max_runtime = ert.analysisConfig().get_max_runtime() + self._mask = mask + + job_queue = ert.get_queue_config().create_job_queue() + job_queue.set_max_job_duration(max_runtime) + self._queue_manager = JobQueueManager(job_queue) + + subst_list = self._ert.getDataKW() + path_fmt = self._ert.getModelConfig().getRunpathFormat() + jobname_fmt = self._ert.getModelConfig().getJobnameFormat() + + self._run_context = ErtRunContext( + EnkfRunType.ENSEMBLE_EXPERIMENT, + sim_fs, + None, + mask, + path_fmt, + jobname_fmt, + subst_list, + itr, + ) + # fill in the missing geo_id data + for sim_id, (geo_id, _) in enumerate(case_data): + if mask[sim_id]: + run_arg = self._run_context[sim_id] + run_arg.geo_id = geo_id + + self._ert.getEnkfSimulationRunner().createRunPath(self._run_context) + EnkfSimulationRunner.runWorkflows(HookRuntime.PRE_SIMULATION, self._ert) + self._sim_thread = self._run_simulations_simple_step() + + # Wait until the queue is active before we finish the creation + # to ensure sane job status while running + while self.isRunning() and not self._queue_manager.isRunning(): + sleep(0.1) + + def get_run_args(self, iens): + """ + raises an exception if no iens simulation found + + :param iens: realization number + :return: run_args for the realization + """ + for run_arg in self._run_context: + if run_arg is not None and run_arg.iens == iens: + return run_arg + raise KeyError("No such simulation: %s" % iens) + + def _run_simulations_simple_step(self): + sim_thread = Thread( + target=lambda: self._ert.getEnkfSimulationRunner().runSimpleStep( + self._queue_manager.queue, self._run_context + ) + ) + sim_thread.start() + return sim_thread + + def __len__(self): + return self._mask.count() + + def isRunning(self): + # TODO: Should separate between running jobs and having loaded all data + return self._sim_thread.is_alive() or self._queue_manager.isRunning() + + def getNumPending(self): + return self._queue_manager.getNumPending() + + def getNumRunning(self): + return self._queue_manager.getNumRunning() + + def getNumSuccess(self): + return self._queue_manager.getNumSuccess() + + def getNumFailed(self): + return self._queue_manager.getNumFailed() + + def getNumWaiting(self): + return self._queue_manager.getNumWaiting() + + def didRealizationSucceed(self, iens): + queue_index = self.get_run_args(iens).getQueueIndex() + return self._queue_manager.didJobSucceed(queue_index) + + def didRealizationFail(self, iens): + # For the purposes of this class, a failure should be anything (killed job, etc) that is not an explicit success. + return not self.didRealizationSucceed(iens) + + def isRealizationQueued(self, iens): + # an exception will be raised if it's not queued + self.get_run_args(iens) + return True + + def isRealizationFinished(self, iens): + run_arg = self.get_run_args(iens) + + if run_arg.isSubmitted(): + queue_index = run_arg.getQueueIndex() + return self._queue_manager.isJobComplete(queue_index) + else: + return False + + def __repr__(self): + running = "running" if self.isRunning() else "not running" + numRunn = self.getNumRunning() + numSucc = self.getNumSuccess() + numFail = self.getNumFailed() + numWait = self.getNumWaiting() + fmt = "%s, #running = %d, #success = %d, #failed = %d, #waiting = %d" + fmt = fmt % (running, numRunn, numSucc, numFail, numWait) + return "SimulationContext(%s)" % fmt + + def get_sim_fs(self): + return self._run_context.get_sim_fs() + + def get_run_context(self): + return self._run_context + + def stop(self): + self._queue_manager.stop_queue() + self._sim_thread.join() + + def job_progress(self, iens): + """Will return a detailed progress of the job. + + The progress report is obtained by reading a file from the filesystem, + that file is typically created by another process running on another + machine, and reading might fail due to NFS issues, simultanoues write + and so on. If loading valid json fails the function will sleep 0.10 + seconds and retry - eventually giving up and returning None. Also for + jobs which have not yet started the method will return None. + + When the method succeeds in reading the progress file from the file + system the return value will be an object with properties like this:| + + progress.start_time + progress.end_time + progress.run_id + progress.jobs =[ (job1.name, job1.start_time, job1.end_time, job1.status, job1.error_msg), + (job2.name, job2.start_time, job2.end_time, job2.status, job2.error_msg), + .... + (jobN.name, jobN.start_time, jobN.end_time, jobN.status, jobN.error_msg) ] + + """ + run_arg = self.get_run_args(iens) + + try: + # will throw if not yet submitted (is in a limbo state) + queue_index = run_arg.getQueueIndex() + except ValueError: + return None + if self._queue_manager.isJobWaiting(queue_index): + return None + + return ForwardModelStatus.load(run_arg.runpath) + + def run_path(self, iens): + """ + Will return the path to the simulation. + """ + return self.get_run_args(iens).runpath + + def job_status(self, iens): + """Will query the queue system for the status of the job.""" + run_arg = self.get_run_args(iens) + try: + queue_index = run_arg.getQueueIndex() + except ValueError: + return None + return self._queue_manager.getJobStatus(queue_index) diff --git a/res/test/__init__.py b/res/test/__init__.py new file mode 100644 index 00000000000..6055969c54c --- /dev/null +++ b/res/test/__init__.py @@ -0,0 +1 @@ +from .ert_test_context import ErtTestContext, ErtTest diff --git a/res/test/ert_test_context.py b/res/test/ert_test_context.py new file mode 100644 index 00000000000..d68b5e84931 --- /dev/null +++ b/res/test/ert_test_context.py @@ -0,0 +1,130 @@ +# Copyright (C) 2013 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. +import os.path + +from ecl.util.test import TestArea + +from cwrap import BaseCClass +from res import ResPrototype +from res.enkf import EnKFMain, ResConfig + + +class ErtTest(BaseCClass): + TYPE_NAME = "ert_test" + + _alloc = ResPrototype( + "void* ert_test_context_alloc_python( test_area , res_config)", bind=False + ) + _free = ResPrototype("void ert_test_context_free( ert_test )") + _get_cwd = ResPrototype("char* ert_test_context_get_cwd( ert_test )") + _get_enkf_main = ResPrototype("enkf_main_ref ert_test_context_get_main( ert_test )") + + def __init__( + self, test_name, model_config=None, config_dict=None, store_area=False + ): + if model_config is None and config_dict is None: + raise ValueError("Must supply either model_config or config_dict argument") + + work_area = TestArea(test_name, store_area=store_area) + work_area.convertToCReference(self) + + if model_config: + work_area.copy_parent_content(model_config) + res_config = ResConfig(user_config_file=os.path.basename(model_config)) + else: + work_area.copy_directory_content(work_area.get_original_cwd()) + res_config = ResConfig(config=config_dict) + + res_config.convertToCReference(self) + c_ptr = self._alloc(work_area, res_config) + super(ErtTest, self).__init__(c_ptr) + + self.__ert = None + + def getErt(self): + """@rtype: EnKFMain""" + if self.__ert is None: + self.__ert = self._get_enkf_main() + + return self.__ert + + def free(self): + ert = self.getErt() + ert.umount() + self._free() + + def installWorkflowJob(self, job_name, job_path): + """@rtype: bool""" + if os.path.exists(job_path) and os.path.isfile(job_path): + ert = self.getErt() + workflow_list = ert.getWorkflowList() + + workflow_list.addJob(job_name, job_path) + else: + raise IOError("Could not load workflowjob from:%s" % job_path) + + def runWorkflowJob(self, job_name, *arguments): + """@rtype: bool""" + ert = self.getErt() + workflow_list = ert.getWorkflowList() + + if workflow_list.hasJob(job_name): + job = workflow_list.getJob(job_name) + job.run(ert, [arg for arg in arguments]) + return True + else: + return False + + def getCwd(self): + """ + Returns the current working directory of this context. + @rtype: string + """ + return self._get_cwd() + + +class ErtTestContext(object): + def __init__( + self, test_name, model_config=None, config_dict=None, store_area=False + ): + self.__test_name = test_name + self.__model_config = model_config + self.__store_area = store_area + self.__config_dict = config_dict + self.__test_context = ErtTest( + self.__test_name, + model_config=self.__model_config, + config_dict=config_dict, + store_area=self.__store_area, + ) + + def __enter__(self): + """@rtype: ErtTest""" + return self.__test_context + + def __exit__(self, exc_type, exc_val, exc_tb): + del self.__test_context + return False + + def getErt(self): + return self.__test_context.getErt() + + def getCwd(self): + """ + Returns the current working directory of this context. + @rtype: string + """ + return self.__test_context.getCwd() diff --git a/res/test/synthesizer/__init__.py b/res/test/synthesizer/__init__.py new file mode 100644 index 00000000000..79d4eea1282 --- /dev/null +++ b/res/test/synthesizer/__init__.py @@ -0,0 +1,4 @@ +from .prime_generator import PrimeGenerator +from .perlin import PerlinNoise +from .shaped_perlin import ShapedNoise, ShapeFunction, ShapeCreator +from .oil_simulator import OilSimulator diff --git a/res/test/synthesizer/oil_simulator.py b/res/test/synthesizer/oil_simulator.py new file mode 100644 index 00000000000..cbbf71359ea --- /dev/null +++ b/res/test/synthesizer/oil_simulator.py @@ -0,0 +1,207 @@ +from . import ShapeFunction, ShapeCreator + + +class OilSimulator(object): + OPR_SHAPE = ShapeFunction([0.0, 0.2, 0.5, 0.7, 1.0], [0.0, 0.7, 0.2, 0.1, 0.01]) + GPR_SHAPE = ShapeFunction([0.0, 0.2, 0.5, 0.7, 1.0], [0.0, 0.5, 0.7, 0.7, 0.3]) + WPR_SHAPE = ShapeFunction([0.0, 0.2, 0.5, 0.7, 1.0], [0.0, 0.01, 0.3, 0.7, 1]) + BPR_SHAPE = ShapeFunction([0.0, 0.2, 0.5, 0.7, 1.0], [1.0, 0.7, 0.5, 0.3, 0.1]) + + O_DIVERGENCE = ShapeFunction([0.0, 0.5, 0.7, 0.9, 1.0], [0.0, 0.5, 0.3, 0.1, 0.01]) + G_DIVERGENCE = ShapeFunction([0.0, 0.5, 0.7, 0.9, 1.0], [0.0, 0.1, 0.3, 0.2, 0.1]) + W_DIVERGENCE = ShapeFunction([0.0, 0.5, 0.7, 0.9, 1.0], [0.0, 0.1, 0.3, 0.2, 0.01]) + B_DIVERGENCE = ShapeFunction([0.0, 0.5, 0.7, 0.9, 1.0], [0.0, 0.1, 0.2, 0.3, 0.5]) + + def __init__(self, ooip=2000, goip=2500, woip=2250): + self._foip = self.ooip = ooip + self._fgip = self.goip = goip + self._fwip = self.woip = woip + + self._oprFunc = {} + self._gprFunc = {} + self._wprFunc = {} + self._bprFunc = {} + self._current_step = 0 + + self._fopt = 0.0 + self._fopr = 0.0 + self._fgpt = 0.0 + self._fgpr = 0.0 + self._fwpt = 0.0 + self._fwpr = 0.0 + + self._fgor = 0.0 + self._fwct = 0.0 + + self._wells = {} + self._bpr = {} + + def addWell( + self, name, seed, persistence=0.2, octaves=8, divergence_scale=1.0, offset=0.0 + ): + oil_div = OilSimulator.O_DIVERGENCE.scaledCopy(divergence_scale) + gas_div = OilSimulator.G_DIVERGENCE.scaledCopy(divergence_scale) + water_div = OilSimulator.W_DIVERGENCE.scaledCopy(divergence_scale) + self._oprFunc[name] = ShapeCreator.createNoiseFunction( + OilSimulator.OPR_SHAPE, + oil_div, + seed, + persistence=persistence, + octaves=octaves, + cutoff=0.0, + offset=offset, + ) + self._gprFunc[name] = ShapeCreator.createNoiseFunction( + OilSimulator.GPR_SHAPE, + gas_div, + seed * 7, + persistence=persistence * 3.5, + octaves=octaves / 2, + cutoff=0.0, + offset=offset, + ) + self._wprFunc[name] = ShapeCreator.createNoiseFunction( + OilSimulator.WPR_SHAPE, + water_div, + seed * 11, + persistence=persistence, + octaves=octaves, + cutoff=0.0, + offset=offset, + ) + + self._wells[name] = { + "opr": 0.0, + "opt": 0.0, + "gpr": 0.0, + "gpt": 0.0, + "wpr": 0.0, + "wpt": 0.0, + } + + def addBlock(self, name, seed, persistence=0.2): + self._bprFunc[name] = ShapeCreator.createNoiseFunction( + OilSimulator.BPR_SHAPE, + OilSimulator.B_DIVERGENCE, + seed, + persistence=persistence, + cutoff=0.0, + ) + self._bpr[name] = 0.0 + + def step(self, scale=1.0): + self._fopr = 0.0 + self._fgpr = 0.0 + self._fwpr = 0.0 + self._fgor = 0.0 + self._fwct = 0.0 + for key in self._wells: + oprFunction = self._oprFunc[key] + gprFunction = self._gprFunc[key] + wprFunction = self._wprFunc[key] + opr_value = oprFunction(self._current_step, scale) + gpr_value = gprFunction(self._current_step, scale) + wpr_value = wprFunction(self._current_step, scale) + + well = self._wells[key] + if self._foip > 0.0: + well["opr"] = opr_value + well["opt"] += opr_value + self._fopr += opr_value + + if self._fgip > 0.0: + well["gpr"] = gpr_value + well["gpt"] += gpr_value + self._fgpr += gpr_value + + if self._fwip > 0.0: + well["wpr"] = wpr_value + well["wpt"] += wpr_value + self._fwpr += wpr_value + + self._fgor += self.gor(key) + self._fwct += self.wct(key) + + self._foip -= self._fopr + self._fgip -= self._fgpr + self._fwip -= self._fwpr + + if self._foip < 0.0: + self._foip = 0.0 # This may lead to the total (FOPT) larger than OOIP + + if self._fgip < 0.0: + self._fgip = 0.0 + + if self._fwip < 0.0: + self._fwip = 0.0 + + self._fopt += self._fopr + self._fgpt += self._fgpr + self._fwpt += self._fwpr + + self._fgor /= len(self._wells) + self._fwct /= len(self._wells) + + for key in self._bpr: + bprFunction = self._bprFunc[key] + self._bpr[key] = bprFunction(self._current_step, scale) + + self._current_step += 1 + + def fopt(self): + return self._fopt + + def fopr(self): + return self._fopr + + def fgpt(self): + return self._fgpt + + def fgpr(self): + return self._fgpr + + def fwpt(self): + return self._fwpt + + def fwpr(self): + return self._fwpr + + def fgor(self): + return self._fgor + + def fwct(self): + return self._fwct + + def foip(self): + return self._foip + + def fgip(self): + return self._fgip + + def fwip(self): + return self._fwip + + def opr(self, well_name): + return self._wells[well_name]["opr"] + + def gpr(self, well_name): + return self._wells[well_name]["gpr"] + + def wpr(self, well_name): + return self._wells[well_name]["wpr"] + + def wct(self, well_name): + opr = self.opr(well_name) + wpr = self.wpr(well_name) + opr = max(opr, 0.1) + return wpr / (wpr + opr) if (wpr + opr) > 0.0 else 0.0 + + def gor(self, well_name): + opr = self.opr(well_name) + gpr = self.gpr(well_name) + opr = max(opr, 0.1) + gpr = max(gpr, 0.1) + return gpr / opr + + def bpr(self, block_name): + return self._bpr[block_name] diff --git a/res/test/synthesizer/perlin.py b/res/test/synthesizer/perlin.py new file mode 100644 index 00000000000..043b951eff3 --- /dev/null +++ b/res/test/synthesizer/perlin.py @@ -0,0 +1,65 @@ +import math + +from .prime_generator import PrimeGenerator + + +class PerlinNoise(object): + def __init__(self, persistence=0.5, number_of_octaves=4, prime_generator=None): + self.persistence = persistence + self.number_of_octaves = number_of_octaves + + self.octave_primes = ( + prime_generator if prime_generator is not None else PrimeGenerator() + ) + + def cosineInterpolation(self, a, b, x): + ft = x * 3.1415927 + f = (1.0 - math.cos(ft)) * 0.5 + return a * (1 - f) + b * f + + MAX_INT = (1 << 31) - 1 + + def noise(self, x, perturbation): + x += perturbation + x = ((x << 13) & PerlinNoise.MAX_INT) ^ x + x = (x * (x * x * 15731 + 789221) + 1376312589) & PerlinNoise.MAX_INT + return 1.0 - x / 1073741824.0 + + def smoothedNoise(self, x, perturbation): + return ( + self.noise(x, perturbation) / 2.0 + + self.noise(x - 1, perturbation) / 4.0 + + self.noise(x + 1, perturbation) / 4.0 + ) + + def interpolatedNoise(self, x, octave_number): + int_x = int(x) + frac_x = x - int_x + + perturbation = self.octave_primes[octave_number] + + v1 = self.smoothedNoise(int_x, perturbation) + v2 = self.smoothedNoise(int_x + 1, perturbation) + + return self.cosineInterpolation(v1, v2, frac_x) + + def perlinNoise1D(self, x): + total = 0.0 + + for octave in range(int(self.number_of_octaves) - 1): + frequency = math.pow(2, octave) + amplitude = math.pow(self.persistence, octave) + + total += ( + self.interpolatedNoise(x * frequency, octave_number=octave) * amplitude + ) + + return total + + def __getitem__(self, x): + """:rtype: float""" + return self.perlinNoise1D(x * 10.0) + + def __call__(self, x): + """:rtype: float""" + return self[x] diff --git a/res/test/synthesizer/prime_generator.py b/res/test/synthesizer/prime_generator.py new file mode 100644 index 00000000000..94689948f13 --- /dev/null +++ b/res/test/synthesizer/prime_generator.py @@ -0,0 +1,43 @@ +import random, numbers + + +def rwh_primes2(n): + # http://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n-in-python/3035188#3035188 + """Input n>=6, Returns a list of primes, 2 <= p < n""" + correction = n % 6 > 1 + n = {0: n, 1: n - 1, 2: n + 4, 3: n + 3, 4: n + 2, 5: n + 1}[n % 6] + sieve = [True] * (n // 3) + sieve[0] = False + for i in range(int(n ** 0.5) // 3 + 1): + if sieve[i]: + k = 3 * i + 1 | 1 + sieve[((k * k) // 3) :: 2 * k] = [False] * ( + (n // 6 - (k * k) // 6 - 1) // k + 1 + ) + sieve[(k * k + 4 * k - 2 * k * (i & 1)) // 3 :: 2 * k] = [False] * ( + (n // 6 - (k * k + 4 * k - 2 * k * (i & 1)) // 6 - 1) // k + 1 + ) + return [2, 3] + [3 * i + 1 | 1 for i in range(1, n // 3 - correction) if sieve[i]] + + +class PrimeGenerator(object): + LIST_OF_PRIMES = rwh_primes2(10000) + + def __init__(self, seed=None): + self.__primes = {} + self.__random = random.Random(seed) + random.seed(seed) + + def __getitem__(self, index): + if not isinstance(index, numbers.Rational) or index < 0: + raise IndexError("Index must be a positive integer: %d" % index) + + if not index in self.__primes: + p1 = self.randomPrime() + self.__primes[index] = p1 + + return self.__primes[index] + + def randomPrime(self): + random_index = self.__random.randint(0, len(PrimeGenerator.LIST_OF_PRIMES) - 1) + return PrimeGenerator.LIST_OF_PRIMES[random_index] diff --git a/res/test/synthesizer/shaped_perlin.py b/res/test/synthesizer/shaped_perlin.py new file mode 100644 index 00000000000..1fcee24b498 --- /dev/null +++ b/res/test/synthesizer/shaped_perlin.py @@ -0,0 +1,143 @@ +import math +from . import PrimeGenerator, PerlinNoise + + +class Interpolator(object): + def __init__(self, x, y): + self.x = x + self.y = y + + assert len(x) == len(y) + + def __call__(self, x): + if x <= self.x[0]: + y = self.y[0] + elif x >= self.x[len(self.x) - 1]: + y = self.y[len(self.x) - 1] + else: + y = None + for i in range(len(self.x) - 1): + if self.x[i] <= x < self.x[i + 1]: + x_diff = self.x[i + 1] - self.x[i] + frac_x = (x - self.x[i]) / x_diff + y = self.cosineInterpolation(self.y[i], self.y[i + 1], frac_x) + break + + return y + + def cosineInterpolation(self, a, b, x): + ft = x * 3.1415927 + f = (1.0 - math.cos(ft)) * 0.5 + return a * (1 - f) + b * f + + +class ShapeFunction(object): + def __init__(self, x, y, scale=1.0): + self.scale = scale + self.interpolator = Interpolator(x, y) + + def __call__(self, x): + return self.interpolator(x) * self.scale + + def scaledCopy(self, scale=1.0): + return ShapeFunction(self.interpolator.x, self.interpolator.y, scale) + + +class ConstantShapeFunction(ShapeFunction): + def __init__(self, value): + super(ConstantShapeFunction, self).__init__([0.0], [value]) + + +class ShapedNoise(object): + def __init__( + self, noiseFunction, shapeFunction, divergenceFunction, offset=0.0, cutoff=None + ): + self.shapeFunction = shapeFunction + self.divergenceFunction = divergenceFunction + self.noiseFunction = noiseFunction + self.offset = offset + self.cutoff = cutoff + + def __call__(self, x, scale=1.0): + scaled_x = x * scale + result = self.shapeFunction(scaled_x) + self.noiseFunction( + scaled_x + ) * self.divergenceFunction(scaled_x) + result += self.offset + if self.cutoff is not None: + result = max(result, self.cutoff) + return result + + +class ShapeCreator(object): + @staticmethod + def createShapeFunction(count=1000, persistence=0.2, octaves=8, seed=1): + """@rtype: ShapeFunction""" + prime_generator = PrimeGenerator(seed=seed) + perlininator = PerlinNoise( + persistence=persistence, + number_of_octaves=octaves, + prime_generator=prime_generator, + ) + + x_values = [x / float(count) for x in range(count)] + y_values = [perlininator(x) for x in x_values] + + return ShapeFunction(x_values, y_values) + + @staticmethod + def createShapedPerlinFunction( + divergence_x, + divergence_y, + shape_seed=None, + perlin_seed=None, + count=1000, + persistence=0.2, + octaves=8, + offset=0.0, + cutoff=None, + ): + """@rtype: ShapedNoise""" + shapeFunction = ShapeCreator.createShapeFunction( + count, persistence, octaves, shape_seed + ) + divergenceFunction = ShapeFunction(divergence_x, divergence_y) + prime_generator = PrimeGenerator(perlin_seed) + perlin_noise = PerlinNoise(persistence, octaves, prime_generator) + + return ShapedNoise( + perlin_noise, + shapeFunction, + divergenceFunction, + offset=offset, + cutoff=cutoff, + ) + + @staticmethod + def createNoiseFunction( + shapeFunction=None, + divergenceFunction=None, + seed=None, + persistence=0.2, + octaves=8, + offset=0.0, + cutoff=None, + ): + """@rtype: ShapedNoise""" + if shapeFunction is None: + shapeFunction = ConstantShapeFunction(0.0) + + if divergenceFunction is None: + divergenceFunction = ConstantShapeFunction(1.0) + + prime_generator = PrimeGenerator(seed) + perlin_noise = PerlinNoise(persistence, octaves, prime_generator) + + noise = ShapedNoise( + perlin_noise, + shapeFunction, + divergenceFunction, + offset=offset, + cutoff=cutoff, + ) + return noise diff --git a/res/util/__init__.py b/res/util/__init__.py new file mode 100644 index 00000000000..8c234469ce1 --- /dev/null +++ b/res/util/__init__.py @@ -0,0 +1,27 @@ +# Copyright (C) 2017 Equinor ASA, Norway. +# +# The file '__init__.py' 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. +from cwrap import Prototype +import res + +from .substitution_list import SubstitutionList +from .enums import LLSQResultEnum +from .log import Log +from .res_version import ResVersion +from .res_log import ResLog +from .ui_return import UIReturn +from .path_format import PathFormat +from .matrix import Matrix +from .stat import polyfit diff --git a/res/util/enums/__init__.py b/res/util/enums/__init__.py new file mode 100644 index 00000000000..2ce398193a6 --- /dev/null +++ b/res/util/enums/__init__.py @@ -0,0 +1,3 @@ +from .ui_return_status_enum import UIReturnStatusEnum +from .message_level_enum import MessageLevelEnum +from .llsq_result_enum import LLSQResultEnum diff --git a/res/util/enums/llsq_result_enum.py b/res/util/enums/llsq_result_enum.py new file mode 100644 index 00000000000..7d33d3eacde --- /dev/null +++ b/res/util/enums/llsq_result_enum.py @@ -0,0 +1,13 @@ +from cwrap import BaseCEnum + + +class LLSQResultEnum(BaseCEnum): + TYPE_NAME = "llsq_result_enum" + LLSQ_SUCCESS = None + LLSQ_INVALID_DIM = None + LLSQ_UNDETERMINED = None + + +LLSQResultEnum.addEnum("LLSQ_SUCCESS", 0) +LLSQResultEnum.addEnum("LLSQ_INVALID_DIM", 1) +LLSQResultEnum.addEnum("LLSQ_UNDETERMINED", 2) diff --git a/res/util/enums/message_level_enum.py b/res/util/enums/message_level_enum.py new file mode 100644 index 00000000000..b05b1c68694 --- /dev/null +++ b/res/util/enums/message_level_enum.py @@ -0,0 +1,37 @@ +from cwrap import BaseCEnum + + +class MessageLevelEnum(BaseCEnum): + TYPE_NAME = "message_level_enum" + + LOG_CRITICAL = None + LOG_ERROR = None + LOG_WARNING = None + LOG_INFO = None + LOG_DEBUG = None + + @classmethod + def to_enum(cls, val): + if isinstance(val, MessageLevelEnum): + return val + if not isinstance(val, int): + raise TypeError("Cannot convert %s to MessageLevelEnum." % type(val)) + if val is None: + return MessageLevelEnum.LOG_WARNING + if val >= 50: + return MessageLevelEnum.LOG_CRITICAL + if val >= 40: + return MessageLevelEnum.LOG_ERROR + if val >= 30: + return MessageLevelEnum.LOG_WARNING + if val >= 20: + return MessageLevelEnum.LOG_INFO + # default level for any values <= 10 + return MessageLevelEnum.LOG_DEBUG + + +MessageLevelEnum.addEnum("LOG_CRITICAL", 50) +MessageLevelEnum.addEnum("LOG_ERROR", 40) +MessageLevelEnum.addEnum("LOG_WARNING", 30) +MessageLevelEnum.addEnum("LOG_INFO", 20) +MessageLevelEnum.addEnum("LOG_DEBUG", 10) diff --git a/res/util/enums/ui_return_status_enum.py b/res/util/enums/ui_return_status_enum.py new file mode 100644 index 00000000000..5c810ec127e --- /dev/null +++ b/res/util/enums/ui_return_status_enum.py @@ -0,0 +1,11 @@ +from cwrap import BaseCEnum + + +class UIReturnStatusEnum(BaseCEnum): + TYPE_NAME = "ui_return_status" + UI_RETURN_OK = None + UI_RETURN_FAIL = None + + +UIReturnStatusEnum.addEnum("UI_RETURN_OK", 1) +UIReturnStatusEnum.addEnum("UI_RETURN_FAIL", 2) diff --git a/res/util/log.py b/res/util/log.py new file mode 100644 index 00000000000..08f94cc5b19 --- /dev/null +++ b/res/util/log.py @@ -0,0 +1,41 @@ +# Copyright (C) 2012 Equinor ASA, Norway. +# +# The file 'log.py' 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. + +from cwrap import BaseCClass + +from res import ResPrototype +from res.util.enums import MessageLevelEnum + + +class Log(BaseCClass): + _open_log = ResPrototype( + "void* log_open_file(char*, message_level_enum)", bind=False + ) + _get_filename = ResPrototype("char* log_get_filename(log)") + _set_level = ResPrototype("void log_set_level(log, message_level_enum)") + + def __init__(self, log_filename, log_level): + c_ptr = self._open_log(log_filename, log_level) + if c_ptr: + super(Log, self).__init__(c_ptr) + else: + raise IOError("Failed to open log handle at:%s" % log_filename) + + def get_filename(self): + return self._get_filename() + + def set_level(self, level): + self._set_level(self, level) diff --git a/res/util/matrix.py b/res/util/matrix.py new file mode 100644 index 00000000000..9ef59d2f0f9 --- /dev/null +++ b/res/util/matrix.py @@ -0,0 +1,232 @@ +# Copyright (C) 2011 Equinor ASA, Norway. +# +# The file 'matrix.py' 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. + + +# The Matrix class implemented here wraps the C matrix implementation +# in matrix.c from the libutil library. The C matrix implementation +# has the very limited ambition of just barely satisfying the matrix +# needs of the EnKF algorithm, i.e. for general linear algebra +# applications you will probably be better served by a more complete +# matrix library. This applies even more so to this Python +# implementation; it is only here facilitate use of C libraries which +# expect a matrix instance as input (i.e. the LARS estimator). For +# general linear algebra in Python the numpy library is a natural +# choice. + + +from cwrap import BaseCClass, CFILE +from res import ResPrototype + + +class Matrix(BaseCClass): + _matrix_alloc = ResPrototype("void* matrix_alloc(int, int )", bind=False) + _matrix_alloc_identity = ResPrototype( + "matrix_obj matrix_alloc_identity( int )", bind=False + ) + _alloc_transpose = ResPrototype("matrix_obj matrix_alloc_transpose(matrix)") + _inplace_transpose = ResPrototype("void matrix_inplace_transpose(matrix)") + _copy = ResPrototype("matrix_obj matrix_alloc_copy(matrix)") + _sub_copy = ResPrototype( + "matrix_obj matrix_alloc_sub_copy(matrix, int , int , int , int)" + ) + _free = ResPrototype("void matrix_free(matrix)") + _iget = ResPrototype("double matrix_iget( matrix , int , int )") + _iset = ResPrototype("void matrix_iset( matrix , int , int , double)") + _set_all = ResPrototype("void matrix_scalar_set( matrix , double)") + _scale_column = ResPrototype("void matrix_scale_column(matrix , int , double)") + _scale_row = ResPrototype("void matrix_scale_row(matrix , int , double)") + _copy_column = ResPrototype( + "void matrix_copy_column(matrix , matrix , int , int)", bind=False + ) + _rows = ResPrototype("int matrix_get_rows(matrix)") + _columns = ResPrototype("int matrix_get_columns(matrix)") + _equal = ResPrototype("bool matrix_equal(matrix, matrix)") + _pretty_print = ResPrototype("void matrix_pretty_print(matrix, char*, char*)") + _fprint = ResPrototype("void matrix_fprintf(matrix, char*, FILE)") + _random_init = ResPrototype("void matrix_random_init(matrix, rng)") + _dump_csv = ResPrototype("void matrix_dump_csv(matrix, char*)") + + # Requires BLAS. If the library does not have the + # matrix_alloc_matmul() function the prototype will have _func = + # None, and NotImplementedError( ) will be raised int the + # __call__() method if we try to use this function. + try: + _alloc_matmul = ResPrototype( + "matrix_obj matrix_alloc_matmul(matrix, matrix)", bind=False + ) + except AttributeError: + _alloc_matmul = None + + # Requires BLAS! + @classmethod + def matmul(cls, m1, m2): + """ + Will return a new matrix which is matrix product of m1 and m2. + """ + if m1.columns() == m2.rows(): + return cls._alloc_matmul(m1, m2) + else: + raise ValueError("Matrix size mismatch") + + def __init__(self, rows, columns, value=0): + c_ptr = self._matrix_alloc(rows, columns) + super(Matrix, self).__init__(c_ptr) + self.setAll(value) + + def copy(self): + return self._copy() + + @classmethod + def identity(cls, dim): + """Returns a dim x dim identity matrix.""" + if dim < 1: + raise ValueError( + "Identity matrix must have positive size, %d not allowed." % dim + ) + return cls._matrix_alloc_identity(dim) + + def subCopy(self, row_offset, column_offset, rows, columns): + if row_offset < 0 or row_offset >= self.rows(): + raise ValueError("Invalid row offset") + + if column_offset < 0 or column_offset >= self.columns(): + raise ValueError("Invalid column offset") + + if row_offset + rows > self.rows(): + raise ValueError("Invalid rows") + + if column_offset + columns > self.columns(): + raise ValueError("Invalid columns") + + return self._sub_copy(row_offset, column_offset, rows, columns) + + def __str__(self): + s = "" + for i in range(self.rows()): + s += "[" + for j in range(self.columns()): + d = self._iget(i, j) + s += "%6.3g " % d + s += "]\n" + return s + + def __getitem__(self, index_tuple): + if not 0 <= index_tuple[0] < self.rows(): + raise IndexError("Expected 0 <= %d < %d" % (index_tuple[0], self.rows())) + + if not 0 <= index_tuple[1] < self.columns(): + raise IndexError("Expected 0 <= %d < %d" % (index_tuple[1], self.columns())) + + return self._iget(index_tuple[0], index_tuple[1]) + + def __setitem__(self, index_tuple, value): + if not 0 <= index_tuple[0] < self.rows(): + raise IndexError("Expected 0 <= %d < %d" % (index_tuple[0], self.rows())) + + if not 0 <= index_tuple[1] < self.columns(): + raise IndexError("Expected 0 <= %d < %d" % (index_tuple[1], self.columns())) + + return self._iset(index_tuple[0], index_tuple[1], value) + + def dims(self): + return self._rows(), self._columns() + + def rows(self): + """@rtype: int""" + return self._rows() + + def transpose(self, inplace=False): + """ + Will transpose the matrix. By default a transposed copy is returned. + """ + if inplace: + self._inplace_transpose() + return self + else: + return self._alloc_transpose() + + def columns(self): + """@rtype: int""" + return self._columns() + + def __eq__(self, other): + assert isinstance(other, Matrix) + return self._equal(other) + + def scaleColumn(self, column, factor): + if not 0 <= column < self.columns(): + raise IndexError( + "Expected column: [0,%d) got:%d" % (self.columns(), column) + ) + self._scale_column(column, factor) + + def scaleRow(self, row, factor): + if not 0 <= row < self.rows(): + raise IndexError("Expected row: [0,%d) got:%d" % (self.rows(), row)) + self._scale_row(row, factor) + + def setAll(self, value): + self._set_all(value) + + def copyColumn(self, target_column, src_column): + columns = self.columns() + if not 0 <= src_column < columns: + raise ValueError("src column:%d invalid" % src_column) + + if not 0 <= target_column < columns: + raise ValueError("target column:%d invalid" % target_column) + + if src_column != target_column: + # The underlying C function accepts column copy between matrices. + Matrix._copy_column(self, self, target_column, src_column) + + def dumpCSV(self, filename): + self._dump_csv(filename) + + def prettyPrint(self, name, fmt="%6.3g"): + self._pretty_print(name, fmt) + + def fprint(self, fileH, fmt="%g "): + """Will print ASCII representation of matrix. + + The fileH argument should point to an open Python + filehandle. If you supply a fmt string it is important that it + contains a separator, otherwise you might risk that elements + overlap in the output. For the matrix: + + [0 1 2] + m = [3 4 5] + [6 7 8] + + The code: + + with open("matrix.txt" , "w") as f: + m.fprintf( f ) + + The file 'matrix.txt' will look like: + + 0 1 2 + 3 4 5 + 6 7 8 + + """ + self._fprint(fmt, CFILE(fileH)) + + def randomInit(self, rng): + self._random_init(rng) + + def free(self): + self._free() diff --git a/res/util/path_format.py b/res/util/path_format.py new file mode 100644 index 00000000000..e1c6f35dc85 --- /dev/null +++ b/res/util/path_format.py @@ -0,0 +1,49 @@ +# Copyright (C) 2017 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. + +from cwrap import BaseCClass +from res import ResPrototype +import os + +# The path_fmt implementation hinges strongly on variable length +# argument lists in C not clear if/how that maps over to Python, +# this Python class therefor has *very* limited functionality. + + +class PathFormat(BaseCClass): + TYPE_NAME = "path_fmt" + _alloc = ResPrototype("void* path_fmt_alloc_directory_fmt(char*)", bind=False) + _str = ResPrototype("char* path_fmt_get_fmt(path_fmt)") + _free = ResPrototype("void path_fmt_free(path_fmt)") + + def __init__(self, path_fmt): + c_ptr = self._alloc(path_fmt) + if c_ptr: + super(PathFormat, self).__init__(c_ptr) + else: + raise ValueError('Unable to construct path format "%s"' % path_fmt) + + def __repr__(self): + return self._create_repr("fmt=%s" % self._str()) + + def free(self): + self._free() + + def __ne__(self, other): + return not self == other + + def __eq__(self, other): + return os.path.realpath(self._str()) == os.path.realpath(other._str()) diff --git a/res/util/res_log.py b/res/util/res_log.py new file mode 100644 index 00000000000..040b86b1181 --- /dev/null +++ b/res/util/res_log.py @@ -0,0 +1,27 @@ +from res import ResPrototype +from res.util.enums import MessageLevelEnum + + +class ResLog(object): + _init = ResPrototype( + "bool res_log_init_log(message_level_enum, char*, bool)", bind=False + ) + _write_log = ResPrototype( + "void res_log_add_message(message_level_enum, char*)", bind=False + ) + _get_filename = ResPrototype("char* res_log_get_filename()", bind=False) + + @classmethod + def init(cls, log_level, log_filename, verbose): + if not cls._init(MessageLevelEnum.to_enum(log_level), log_filename, verbose): + raise IOError("Failed to open log handle for file:%s" % log_filename) + + @classmethod + def log(cls, log_level, message): + """Higher log_level indicates higher importance""" + cls._write_log(MessageLevelEnum.to_enum(log_level), message) + + @classmethod + def getFilename(cls): + """@rtype: string""" + return cls._get_filename() diff --git a/res/util/res_version.py b/res/util/res_version.py new file mode 100644 index 00000000000..ff1fddb442c --- /dev/null +++ b/res/util/res_version.py @@ -0,0 +1,19 @@ +from ecl.util.util import Version +from res import ResPrototype + + +class ResVersion(Version): + _build_time = ResPrototype("char* res_version_get_build_time()", bind=False) + _git_commit = ResPrototype("char* res_version_get_git_commit()", bind=False) + _major_version = ResPrototype("int res_version_get_major_version()", bind=False) + _minor_version = ResPrototype("int res_version_get_minor_version()", bind=False) + _micro_version = ResPrototype("char* res_version_get_micro_version()", bind=False) + _is_devel = ResPrototype("bool res_version_is_devel_version()", bind=False) + + def __init__(self): + major = self._major_version() + minor = self._minor_version() + micro = self._micro_version() + git_commit = self._git_commit() + build_time = self._build_time() + super(ResVersion, self).__init__(major, minor, micro, git_commit, build_time) diff --git a/res/util/stat.py b/res/util/stat.py new file mode 100644 index 00000000000..796a2812482 --- /dev/null +++ b/res/util/stat.py @@ -0,0 +1,82 @@ +# Copyright (C) 2011 Equinor ASA, Norway. +# +# The file 'stat.py' 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. +try: + from collections.abc import Sequence +except ImportError: + from collections import Sequence + +from cwrap import PrototypeError +from res import ResPrototype +from ecl import EclPrototype +from res.util import LLSQResultEnum +from res.util import Matrix + +try: + _polyfit = ResPrototype( + "llsq_result_enum matrix_stat_polyfit(matrix, matrix, matrix, matrix)" + ) +except PrototypeError: + _polyfit = None # type: ignore[assignment] + + +def polyfit(n, x, y, s=None): + """ + @type n: int + @type x: Matrix or Sequence or DoubleVector + @type y: Matrix or Sequence or DoubleVector + @type s: Matrix or Sequence or DoubleVector or None + @return: tuple + """ + if _polyfit is None: + raise NotImplementedError( + "Sorry - your ert distribution has been built without lapack support" + ) + + if isinstance(x, Matrix): + xm = x + else: + xm = Matrix(len(x), 1) + for i in range(len(x)): + xm[i, 0] = x[i] + + if isinstance(y, Matrix): + ym = y + else: + ym = Matrix(len(y), 1) + for i in range(len(y)): + ym[i, 0] = y[i] + + if s: + if isinstance(s, Matrix): + sm = s + else: + sm = Matrix(len(s), 1) + for i in range(len(s)): + sm[i, 0] = s[i] + else: + sm = s + + beta = Matrix(n, 1) + res = _polyfit(beta, xm, ym, sm) + + if not res == LLSQResultEnum.LLSQ_SUCCESS: + raise Exception("Linear Least Squares Estimator failed?") + + l = [] + for i in range(n): + l.append(beta[i, 0]) + + return tuple(l) diff --git a/res/util/subprocess.py b/res/util/subprocess.py new file mode 100644 index 00000000000..c57f20b49ee --- /dev/null +++ b/res/util/subprocess.py @@ -0,0 +1,47 @@ +# Copyright (C) 2019 Equinor ASA, Norway. +# +# The file 'subprocess.py' 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. + + +import os + + +def await_process_tee(process, *out_files): + """Wait for process to finish, "tee"-ing the subprocess' stdout into all the + given file objects. + + NB: We aren't checking if `os.write` succeeds. It succeeds if its return + value matches `len(bytes_)`. In other cases we might want to do something + smart, such as retry or raise an error. At the time of writing it is + uncertain what we should do, and it is assumed that data loss is acceptable. + + """ + out_fds = [f.fileno() for f in out_files] + process_fd = process.stdout.fileno() + + while True: + while True: + bytes_ = os.read(process_fd, 4096) + if bytes_ == b"": # check EOF + break + for fd in out_fds: + os.write(fd, bytes_) + + # Check if process terminated + if process.poll() is not None: + break + process.stdout.close() + + return process.returncode diff --git a/res/util/substitution_list.py b/res/util/substitution_list.py new file mode 100644 index 00000000000..de23ef56b30 --- /dev/null +++ b/res/util/substitution_list.py @@ -0,0 +1,84 @@ +from cwrap import BaseCClass +from res import ResPrototype + + +class SubstitutionList(BaseCClass): + TYPE_NAME = "subst_list" + + _alloc = ResPrototype("void* subst_list_alloc(void*)", bind=False) + _free = ResPrototype("void subst_list_free(subst_list)") + _size = ResPrototype("int subst_list_get_size(subst_list)") + _iget_key = ResPrototype("char* subst_list_iget_key(subst_list, int)") + _iget_value = ResPrototype("char* subst_list_iget_value(subst_list, int)") + _get_value = ResPrototype("char* subst_list_get_value(subst_list, char*)") + _has_key = ResPrototype("bool subst_list_has_key(subst_list, char*)") + _get_doc = ResPrototype("char* subst_list_get_doc_string(subst_list, char*)") + _append_copy = ResPrototype( + "void subst_list_append_copy(subst_list, char*, char*, char*)" + ) + + def __init__(self): + c_ptr = self._alloc(None) + + if c_ptr: + super(SubstitutionList, self).__init__(c_ptr) + else: + raise ValueError("Failed to construct subst_list instance.") + + def __len__(self): + return self._size() + + def addItem(self, key, value, doc_string=""): + self._append_copy(key, value, doc_string) + + def keys(self): + key_list = [] + for i in range(len(self)): + key_list.append(self._iget_key(i)) + return key_list + + def __iter__(self): + index = 0 + keys = self.keys() + for index in range(len(self)): + key = keys[index] + yield (key, self[key], self.doc(key)) + + def __contains__(self, key): + if not isinstance(key, str): + return False + return self._has_key(key) + + def __getitem__(self, key): + if key in self: + return self._get_value(key) + else: + raise KeyError("No such key:%s" % key) + + def get(self, key, default=None): + return self[key] if key in self else default + + def doc(self, key): + if key in self: + return self._get_doc(key) + else: + raise KeyError("No such key:%s" % key) + + def indexForKey(self, key): + if not key in self: + raise KeyError("Key '%s' not in substitution list!" % key) + + for index, key_val_doc in enumerate(self): + if key == key_val_doc[0]: + return index + + return None # Should never happen! + + def free(self): + self._free() + + def __repr__(self): + return self._create_repr("len=%d" % len(self)) + + def __str__(self): + return "SubstitutionList{%s}" % ", ".join(map(str, self.keys())) diff --git a/res/util/ui_return.py b/res/util/ui_return.py new file mode 100644 index 00000000000..c28226b3406 --- /dev/null +++ b/res/util/ui_return.py @@ -0,0 +1,106 @@ +# Copyright (C) 2013 Equinor ASA, Norway. +# +# The file 'ui_return.py' 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. + +from cwrap import BaseCClass +from res import ResPrototype +from .enums import UIReturnStatusEnum + + +class UIReturn(BaseCClass): + TYPE_NAME = "ui_return" + + _alloc = ResPrototype("void* ui_return_alloc( ui_return_status )", bind=False) + _free = ResPrototype("void ui_return_free(ui_return)") + _get_status = ResPrototype("ui_return_status ui_return_get_status(ui_return)") + _get_help = ResPrototype("char* ui_return_get_help(ui_return)") + _add_help = ResPrototype("bool ui_return_add_help(ui_return, char*)") + _add_error = ResPrototype("bool ui_return_add_error(ui_return, char*)") + _num_error = ResPrototype("int ui_return_get_error_count(ui_return)") + _last_error = ResPrototype("char* ui_return_get_last_error(ui_return)") + _first_error = ResPrototype("char* ui_return_get_first_error(ui_return)") + _iget_error = ResPrototype("char* ui_return_iget_error(ui_return , int)") + + def __init__(self, status): + c_ptr = self._alloc(status) + if c_ptr: + super(UIReturn, self).__init__(c_ptr) + else: + raise ValueError( + "Unable to construct UIReturn with status = %s" % str(status) + ) + + # For python 3, corresponds to __nonzero__ + def __bool__(self): + return self.status() == UIReturnStatusEnum.UI_RETURN_OK + + # For python 2 + def __nonzero__(self): + return self.__bool__() + + def __len__(self): + return self._num_error() + + def __getitem__(self, index): + if isinstance(index, int): + if index < 0: + index += len(self) + if 0 <= index < len(self): + return self._iget_error(index) + else: + raise IndexError("Invalid index. Valid range: [0, %d)" % len(self)) + else: + raise TypeError("Lookup type must be integer") + + def iget_error(self, index): + return self[index] + + def help_text(self): + help_text = self._get_help() + if help_text: + return help_text + else: + return "" + + def add_help(self, help_text): + self._add_help(help_text) + + def status(self): + return self._get_status() + + def __assert_error(self): + if self.status() == UIReturnStatusEnum.UI_RETURN_OK: + raise ValueError("Can not add error messages to object in state RETURN_OK") + + def add_error(self, error): + self.__assert_error() + self._add_error(error) + + def last_error(self): + self.__assert_error() + return self._last_error() + + def first_error(self): + self.__assert_error() + return self._first_error() + + def free(self): + self._free() + + def __repr__(self): + ec = len(self) + st = self.status() + ad = self._ad_str() + return "UIReturn(error_count = %d, status = %s) %s" % (ec, st, ad) diff --git a/setup.py b/setup.py index 4f6555d5b7c..36309bbee75 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,20 @@ -from setuptools import setup, find_packages +import os +from setuptools import find_packages +from skbuild import setup +from setuptools_scm import get_version -import os +def get_ecl_include(): + from ecl import get_include + + return get_include() + + +def get_data_files(): + data_files = [] + for root, _, files in os.walk("share/ert"): + data_files.append((root, [os.path.join(root, name) for name in files])) + return data_files def package_files(directory): @@ -14,24 +27,44 @@ def package_files(directory): extra_files = package_files("ert_gui/resources/") logging_configuration = package_files("ert_logging/") +ert3_example_files = package_files("ert3_examples/") + + +with open("README.md") as f: + long_description = f.read() + +packages = find_packages( + exclude=["*.tests", "*.tests.*", "tests.*", "tests", "tests*", "libres"], +) + +# Given this unusual layout where we cannot fall back on a "root package", +# package_dir is built manually from libres_packages. +res_files = get_data_files() setup( name="ert", author="Equinor ASA", author_email="fg_sib-scout@equinor.com", + description="Ensemble based Reservoir Tool (ERT)", use_scm_version={"root": ".", "write_to": "ert_shared/version.py"}, - entry_points={ - "console_scripts": [ - "ert3=ert3.console:main", - "ert=ert_shared.main:main", - ] + long_description=long_description, + long_description_content_type="text/markdown", + url="https://github.com/equinor/ert", + packages=packages, + package_data={ + "ert_gui": extra_files, + "ert_logging": logging_configuration, + "ert3_examples": ert3_example_files, + "res": [ + "fm/rms/rms_config.yml", + "fm/ecl/ecl300_config.yml", + "fm/ecl/ecl100_config.yml", + ], }, - packages=find_packages(exclude=["tests*"]), - package_data={"ert_gui": extra_files, "ert_logging": logging_configuration}, include_package_data=True, - license="Open Source", - long_description=open("README.md").read(), - long_description_content_type="text/markdown", + data_files=res_files, + license="GPL-3.0", + platforms="any", install_requires=[ "aiofiles", "aiohttp", @@ -43,11 +76,12 @@ def package_files(directory): "cloudpickle", "console-progressbar==1.1.2", "cryptography", + "cwrap", "dask_jobqueue", "decorator", "deprecation", "dnspython >= 2", - "equinor-libres >= 11.0.0b1", + "ecl", "fastapi", "graphlib_backport; python_version < '3.9'", "jinja2", @@ -56,12 +90,14 @@ def package_files(directory): "pandas", "pluggy", "prefect", + "psutil", "pydantic >= 1.8.1", "PyQt5", "pyrsistent", "python-dateutil", "pyyaml", "qtpy", + "requests", "scipy", "semeio", "sqlalchemy", @@ -74,10 +110,24 @@ def package_files(directory): "ert-storage==0.1.7", ], }, - zip_safe=False, - tests_require=["pytest", "mock"], - test_suite="tests", setup_requires=["pytest-runner", "setuptools_scm"], + entry_points={ + "console_scripts": [ + "ert3=ert3.console:main", + "ert=ert_shared.main:main", + "job_dispatch.py = job_runner.job_dispatch:main", + ] + }, + cmake_args=[ + "-DRES_VERSION=" + get_version(), + "-DECL_INCLUDE_DIRS=" + get_ecl_include(), + # we can safely pass OSX_DEPLOYMENT_TARGET as it's ignored on + # everything not OS X. We depend on C++11, which makes our minimum + # supported OS X release 10.9 + "-DCMAKE_OSX_DEPLOYMENT_TARGET=10.9", + f"-DCMAKE_INSTALL_LIBDIR=res/.libs", + ], + cmake_source_dir="libres/", classifiers=[ "Development Status :: 5 - Production/Stable", "Environment :: Other Environment", @@ -92,4 +142,5 @@ def package_files(directory): "Topic :: Scientific/Engineering", "Topic :: Scientific/Engineering :: Physics", ], + test_suite="tests", ) diff --git a/share/ert/forward-models/docs/description/CAREFUL_COPY_FILE.rst b/share/ert/forward-models/docs/description/CAREFUL_COPY_FILE.rst new file mode 100644 index 00000000000..0fda1af3c4b --- /dev/null +++ b/share/ert/forward-models/docs/description/CAREFUL_COPY_FILE.rst @@ -0,0 +1,7 @@ + +The :code:`CAREFUL_COPY_FILE` job will copy a file. If the :code:`` +argument has a directory component, that directory will be created. +This is an extension of the normal :code:`cp` command +which will *not* create directories in this way. +This job superseded an older version called :code:`CAREFULL_COPY` +and should be used instead. diff --git a/share/ert/forward-models/docs/description/COPY_DIRECTORY.rst b/share/ert/forward-models/docs/description/COPY_DIRECTORY.rst new file mode 100644 index 00000000000..c495f44b091 --- /dev/null +++ b/share/ert/forward-models/docs/description/COPY_DIRECTORY.rst @@ -0,0 +1,5 @@ +The job copies the directory :code:`` to the target :code:``. If +:code:`` points to a non-existing directory structure, it will be +created first. If the :code:`` folder already exist it creates a new directory within the existing one. +E.g. :code:`COPY_DIRECTORY (=foo, =bar)` creates :code:`bar/foo` if the directory +:code:`bar` already exists. If :code:`bar` does not exist it becomes a copy of :code:`foo`. diff --git a/share/ert/forward-models/docs/description/COPY_FILE.rst b/share/ert/forward-models/docs/description/COPY_FILE.rst new file mode 100644 index 00000000000..0864a0daf2c --- /dev/null +++ b/share/ert/forward-models/docs/description/COPY_FILE.rst @@ -0,0 +1,5 @@ + +The :code:`COPY_FILE` job will copy a file. If the :code:`` +argument has a directory component, that directory will be created. +This is an extension of the normal :code:`cp` command +which will *not* create directories in this way. diff --git a/share/ert/forward-models/docs/description/DELETE_DIRECTORY.rst b/share/ert/forward-models/docs/description/DELETE_DIRECTORY.rst new file mode 100644 index 00000000000..daa184b3278 --- /dev/null +++ b/share/ert/forward-models/docs/description/DELETE_DIRECTORY.rst @@ -0,0 +1,6 @@ +The :code:`DELETE_DIRECTORY` job will recursively remove a directory +and all the files in the directory. Like the :code:`DELETE_FILE` job +it will *only* delete files and directories which are owned by the +current user. If one delete operation fails the job will continue, but +unless all delete calls succeed (parts of) the directory structure +will remain. diff --git a/share/ert/forward-models/docs/description/DELETE_FILE.rst b/share/ert/forward-models/docs/description/DELETE_FILE.rst new file mode 100644 index 00000000000..e2e1f3990c2 --- /dev/null +++ b/share/ert/forward-models/docs/description/DELETE_FILE.rst @@ -0,0 +1,6 @@ +The :code:`DELETE_FILE` job will *only* remove files which are owned +by the current user, *even if* file system permissions would have +allowed the delete operation to proceed. The :code:`DELETE_FILE` will +*not* delete a directory, and if presented with a symbolic link it +will *only* delete the link, and not the target. + diff --git a/share/ert/forward-models/docs/description/MAKE_DIRECTORY.rst b/share/ert/forward-models/docs/description/MAKE_DIRECTORY.rst new file mode 100644 index 00000000000..cda996ef999 --- /dev/null +++ b/share/ert/forward-models/docs/description/MAKE_DIRECTORY.rst @@ -0,0 +1,2 @@ +Will create the directory :code:``, with all sub +directories. diff --git a/share/ert/forward-models/docs/description/MAKE_SYMLINK.rst b/share/ert/forward-models/docs/description/MAKE_SYMLINK.rst new file mode 100644 index 00000000000..d6c06ec7997 --- /dev/null +++ b/share/ert/forward-models/docs/description/MAKE_SYMLINK.rst @@ -0,0 +1,2 @@ +Will create a symbolic link with name :code:`` which points to +:code:``. If :code:`` already exists, it will be updated. diff --git a/share/ert/forward-models/docs/description/MOVE_FILE.rst b/share/ert/forward-models/docs/description/MOVE_FILE.rst new file mode 100644 index 00000000000..29d58d23324 --- /dev/null +++ b/share/ert/forward-models/docs/description/MOVE_FILE.rst @@ -0,0 +1,3 @@ +The :code:`MOVE_FILE` job will move file to target directory. +If file already exists, this job will move file to the target directory +and then replace the existing file. diff --git a/share/ert/forward-models/docs/description/RMS.rst b/share/ert/forward-models/docs/description/RMS.rst new file mode 100644 index 00000000000..68de293c2cc --- /dev/null +++ b/share/ert/forward-models/docs/description/RMS.rst @@ -0,0 +1,5 @@ +Forward model for running a given workflow in an existing RMS-project. + +The forward model requires explicit knowledge of the version used to +produce the RMS project loaded. The Python environment is adapted to +work with Python inside RMS. diff --git a/share/ert/forward-models/docs/description/SYMLINK.rst b/share/ert/forward-models/docs/description/SYMLINK.rst new file mode 100644 index 00000000000..d6c06ec7997 --- /dev/null +++ b/share/ert/forward-models/docs/description/SYMLINK.rst @@ -0,0 +1,2 @@ +Will create a symbolic link with name :code:`` which points to +:code:``. If :code:`` already exists, it will be updated. diff --git a/share/ert/forward-models/docs/description/TEMPLATE_RENDER.rst b/share/ert/forward-models/docs/description/TEMPLATE_RENDER.rst new file mode 100644 index 00000000000..628ee76c7d1 --- /dev/null +++ b/share/ert/forward-models/docs/description/TEMPLATE_RENDER.rst @@ -0,0 +1,3 @@ +Loads the data from each file (:code:`some/path/filename.xxx`) in :code:`INPUT_FILES` +and exposes it as the variable :code:`filename`. It then loads the Jinja2 +template :code:`TEMPLATE_FILE` and dumps the rendered result to :code:`OUTPUT`. diff --git a/share/ert/forward-models/docs/examples/CAREFUL_COPY_FILE.rst b/share/ert/forward-models/docs/examples/CAREFUL_COPY_FILE.rst new file mode 100644 index 00000000000..aa9cbe9b68b --- /dev/null +++ b/share/ert/forward-models/docs/examples/CAREFUL_COPY_FILE.rst @@ -0,0 +1,3 @@ +.. code-block:: bash + + FORWARD_MODEL CAREFUL_COPY_FILE(=file1, =path/to/directory/file1) diff --git a/share/ert/forward-models/docs/examples/COPY_FILE.rst b/share/ert/forward-models/docs/examples/COPY_FILE.rst new file mode 100644 index 00000000000..7ae76c3fab9 --- /dev/null +++ b/share/ert/forward-models/docs/examples/COPY_FILE.rst @@ -0,0 +1,3 @@ +.. code-block:: bash + + FORWARD_MODEL COPY_FILE(=file1, =path/to/directory/file1) diff --git a/share/ert/forward-models/docs/examples/ECLIPSE100.rst b/share/ert/forward-models/docs/examples/ECLIPSE100.rst new file mode 100644 index 00000000000..27149218556 --- /dev/null +++ b/share/ert/forward-models/docs/examples/ECLIPSE100.rst @@ -0,0 +1,13 @@ +The version, number of cpu, and whether or not to ignore errors and whether or not to produce `YOUR_CASE_NAME.h5` output files can +be configured in the configuration file when adding the job, as such: + +.. code-block:: bash + + FORWARD_MODEL ECLIPSE100(, =xxxx, ={"--ignore-errors", "--summary-conversion"}) + +The :code:`OPTS` argument is optional and can be removed, fully or partially. +In absence of :code:`--ignore-errors` eclipse will fail on errors. +Adding the flag :code:`--ignore-errors` will result in eclipse ignoring errors. + +And in absence of :code:`--summary-conversions` eclipse will run without producing `YOUR_CASE_NAME.h5` output files. +Add flag :code:`--summary-conversions` to produce `YOUR_CASE_NAME.h5` output files. \ No newline at end of file diff --git a/share/ert/forward-models/docs/examples/ECLIPSE300.rst b/share/ert/forward-models/docs/examples/ECLIPSE300.rst new file mode 100644 index 00000000000..3cd58dab724 --- /dev/null +++ b/share/ert/forward-models/docs/examples/ECLIPSE300.rst @@ -0,0 +1,9 @@ +The version, number of cpu and whether or not to ignore errors can +be configured in the configuration file when adding the job, as such: + +.. code-block:: bash + + FORWARD_MODEL ECLIPSE300(, =xxxx, ="--ignore-errors") + +The :code:`OPTS` argument is optional and can be removed, thus running eclipse +without ignoring errors diff --git a/share/ert/forward-models/docs/examples/FLOW.rst b/share/ert/forward-models/docs/examples/FLOW.rst new file mode 100644 index 00000000000..b2801eea52d --- /dev/null +++ b/share/ert/forward-models/docs/examples/FLOW.rst @@ -0,0 +1,10 @@ +The version, number of cpu and whether or not to ignore errors can +be configured in the configuration file when adding the job, as such: + +.. code-block:: bash + + FORWARD_MODEL FLOW(, =xxxx, ="--ignore-errors") + +The :code:`OPTS` argument is optional and can be removed, thus running flow +without ignoring errors. + diff --git a/share/ert/forward-models/docs/examples/RMS.rst b/share/ert/forward-models/docs/examples/RMS.rst new file mode 100644 index 00000000000..f1d56bd7fc4 --- /dev/null +++ b/share/ert/forward-models/docs/examples/RMS.rst @@ -0,0 +1,43 @@ +Running the forward model +######################### + +RMS is usually incorporated in ERT configurations using statements like + +.. code-block:: bash + + DEFINE reek.rms11.0.1 + DEFINE 11.0.1 + DEFINE MAIN_WORKFLOW + FORWARD_MODEL RMS(=, =, =/../../rms/model/) + +RMS script documentation +######################## + +The script must be invoked with minimum three positional arguments: + +Positional arguments: + IENS + Realization number + RMS_PROJECT + The RMS project we are running + RMS_WORKFLOW + The rms workflow we intend to run + +Optional arguments: + -r, --run-path RUN_PATH + The directory which will be used as cwd when running + rms + -t, --target-file TARGET_FILE + name of file which should be created/updated by rms + -i, --import-path IMPORT_PATH + the prefix of all relative paths when rms is importing + -e, --export-path EXPORT_PATH + the prefix of all relative paths when rms is exporting + -v, --version VERSION + the prefix of all relative paths when rms is exporting + -a, --allow-no-env + Boolean flag to allow RMS to run without a site configured + environment. WARNING: This is typically a sign of using an + unsupported version of RMS and the configuration of the + environment is now entirely on the shoulders of the user. + Consider contacting your system administrators. diff --git a/share/ert/forward-models/docs/examples/TEMPLATE_RENDER.rst b/share/ert/forward-models/docs/examples/TEMPLATE_RENDER.rst new file mode 100644 index 00000000000..b9b797d4d3a --- /dev/null +++ b/share/ert/forward-models/docs/examples/TEMPLATE_RENDER.rst @@ -0,0 +1,25 @@ +Given an input file :code:`my_input.json`: + +.. code-block:: json + + { + "my_variable": "my_value" + } + +And a template file :code:`tmpl.jinja`: + +.. code-block:: bash + + This is written in my file together with {{my_input.my_variable}} + +This job will produce an output file: + +.. code-block:: bash + + This is written in my file together with my_value + +By invoking the :code:`FORWARD_MODEL` as such: + +.. code-block:: bash + + FORWARD_MODEL TEMPLATE_RENDER(=my_input.json, =tmpl.jinja, =output_file) diff --git a/share/ert/forward-models/old_style/CAREFUL_COPY_FILE b/share/ert/forward-models/old_style/CAREFUL_COPY_FILE new file mode 100644 index 00000000000..e81d11b7562 --- /dev/null +++ b/share/ert/forward-models/old_style/CAREFUL_COPY_FILE @@ -0,0 +1,9 @@ +STDOUT careful_copy_file.stdout +STDERR careful_copy_file.stderr +EXECUTABLE ../../shell_scripts/careful_copy_file +ARGLIST + +MIN_ARG 2 +MAX_ARG 2 +ARG_TYPE 0 STRING +ARG_TYPE 1 STRING diff --git a/share/ert/forward-models/old_style/COPY_DIRECTORY b/share/ert/forward-models/old_style/COPY_DIRECTORY new file mode 100644 index 00000000000..37f7eda3642 --- /dev/null +++ b/share/ert/forward-models/old_style/COPY_DIRECTORY @@ -0,0 +1,10 @@ +STDERR copy_directory.stderr +STDOUT copy_directory.stdout +EXECUTABLE ../../shell_scripts/copy_directory +ARGLIST + +MIN_ARG 2 +MAX_ARG 2 +ARG_TYPE 0 STRING +ARG_TYPE 1 STRING + diff --git a/share/ert/forward-models/old_style/COPY_FILE b/share/ert/forward-models/old_style/COPY_FILE new file mode 100644 index 00000000000..d4d366d8d8d --- /dev/null +++ b/share/ert/forward-models/old_style/COPY_FILE @@ -0,0 +1,9 @@ +STDOUT copy_file.stdout +STDERR copy_file.stderr +EXECUTABLE ../../shell_scripts/copy_file +ARGLIST + +MIN_ARG 2 +MAX_ARG 2 +ARG_TYPE 0 STRING +ARG_TYPE 1 STRING diff --git a/share/ert/forward-models/old_style/DELETE_DIRECTORY b/share/ert/forward-models/old_style/DELETE_DIRECTORY new file mode 100644 index 00000000000..ce4b08a6c5e --- /dev/null +++ b/share/ert/forward-models/old_style/DELETE_DIRECTORY @@ -0,0 +1,9 @@ +STDERR delete_dir.stderr +STDOUT delete_dir.stdout + +EXECUTABLE ../../shell_scripts/delete_directory +ARGLIST + +MIN_ARG 1 +MAX_ARG 1 +ARG_TYPE 0 STRING diff --git a/share/ert/forward-models/old_style/DELETE_FILE b/share/ert/forward-models/old_style/DELETE_FILE new file mode 100644 index 00000000000..4bfbf59da49 --- /dev/null +++ b/share/ert/forward-models/old_style/DELETE_FILE @@ -0,0 +1,9 @@ +STDERR delete_file.stderr +STDOUT delete_file.stdout + +EXECUTABLE ../../shell_scripts/delete_file +ARGLIST + +MIN_ARG 1 +MAX_ARG 1 +ARG_TYPE 0 STRING diff --git a/share/ert/forward-models/old_style/ECLIPSE100 b/share/ert/forward-models/old_style/ECLIPSE100 new file mode 100644 index 00000000000..372c5d8419c --- /dev/null +++ b/share/ert/forward-models/old_style/ECLIPSE100 @@ -0,0 +1,5 @@ +EXECUTABLE ../res/script/ecl100 +DEFAULT version +DEFAULT 1 +DEFAULT "" +ARGLIST -v -n diff --git a/share/ert/forward-models/old_style/ECLIPSE300 b/share/ert/forward-models/old_style/ECLIPSE300 new file mode 100644 index 00000000000..95123b122df --- /dev/null +++ b/share/ert/forward-models/old_style/ECLIPSE300 @@ -0,0 +1,6 @@ +EXECUTABLE ../res/script/ecl300 +DEFAULT version +DEFAULT 1 +DEFAULT "" +ARGLIST -v -n + diff --git a/share/ert/forward-models/old_style/FLOW b/share/ert/forward-models/old_style/FLOW new file mode 100644 index 00000000000..a6f38fefdde --- /dev/null +++ b/share/ert/forward-models/old_style/FLOW @@ -0,0 +1,5 @@ +EXECUTABLE ../res/script/flow +DEFAULT default +DEFAULT 1 +DEFAULT "" +ARGLIST -v -n diff --git a/share/ert/forward-models/old_style/MAKE_DIRECTORY b/share/ert/forward-models/old_style/MAKE_DIRECTORY new file mode 100644 index 00000000000..deb334546b7 --- /dev/null +++ b/share/ert/forward-models/old_style/MAKE_DIRECTORY @@ -0,0 +1,9 @@ +STDERR make_directory.stderr +STDOUT make_directory.stdout + +ARGLIST +EXECUTABLE ../../shell_scripts/make_directory + +MIN_ARG 1 +MAX_ARG 1 +ARG_TYPE 0 STRING diff --git a/share/ert/forward-models/old_style/MAKE_SYMLINK b/share/ert/forward-models/old_style/MAKE_SYMLINK new file mode 100644 index 00000000000..a0e6432f5e5 --- /dev/null +++ b/share/ert/forward-models/old_style/MAKE_SYMLINK @@ -0,0 +1,12 @@ +-- The SYMLINK and MAKE_SYMLINK jobs are aliases to the same +-- underlying script. +STDERR make_symlink.stderr +STDOUT make_symlink.stdout + +EXECUTABLE ../../shell_scripts/symlink +ARGLIST + +MIN_ARG 2 +MAX_ARG 2 +ARG_TYPE 0 STRING +ARG_TYPE 1 STRING diff --git a/share/ert/forward-models/old_style/MOVE_FILE b/share/ert/forward-models/old_style/MOVE_FILE new file mode 100644 index 00000000000..3223f1b18bc --- /dev/null +++ b/share/ert/forward-models/old_style/MOVE_FILE @@ -0,0 +1,10 @@ +STDERR move_file.stderr +STDOUT move_file.stdout + +EXECUTABLE ../../shell_scripts/move_file +ARGLIST + +MIN_ARG 2 +MAX_ARG 2 +ARG_TYPE 0 STRING +ARG_TYPE 1 STRING diff --git a/share/ert/forward-models/old_style/RMS b/share/ert/forward-models/old_style/RMS new file mode 100644 index 00000000000..cefa3d90c2d --- /dev/null +++ b/share/ert/forward-models/old_style/RMS @@ -0,0 +1,11 @@ +EXECUTABLE ../res/script/rms + +DEFAULT ./ +DEFAULT ./ +DEFAULT rms/model +DEFAULT "" +TARGET_FILE +ARGLIST -r -t -i -v -e + +EXEC_ENV PYTHONPATH +EXEC_ENV PATH_PREFIX diff --git a/share/ert/forward-models/old_style/SYMLINK b/share/ert/forward-models/old_style/SYMLINK new file mode 100644 index 00000000000..a0e6432f5e5 --- /dev/null +++ b/share/ert/forward-models/old_style/SYMLINK @@ -0,0 +1,12 @@ +-- The SYMLINK and MAKE_SYMLINK jobs are aliases to the same +-- underlying script. +STDERR make_symlink.stderr +STDOUT make_symlink.stdout + +EXECUTABLE ../../shell_scripts/symlink +ARGLIST + +MIN_ARG 2 +MAX_ARG 2 +ARG_TYPE 0 STRING +ARG_TYPE 1 STRING diff --git a/share/ert/forward-models/old_style/TEMPLATE_RENDER b/share/ert/forward-models/old_style/TEMPLATE_RENDER new file mode 100644 index 00000000000..6c465aeacfc --- /dev/null +++ b/share/ert/forward-models/old_style/TEMPLATE_RENDER @@ -0,0 +1,2 @@ +EXECUTABLE ../templating/script/template_render +ARGLIST -i -o -t diff --git a/share/ert/forward-models/res/eclipse100 b/share/ert/forward-models/res/eclipse100 new file mode 100644 index 00000000000..1dfed5518dd --- /dev/null +++ b/share/ert/forward-models/res/eclipse100 @@ -0,0 +1 @@ +EXECUTABLE script/ecl100 diff --git a/share/ert/forward-models/res/eclipse300 b/share/ert/forward-models/res/eclipse300 new file mode 100644 index 00000000000..ec71d6390a9 --- /dev/null +++ b/share/ert/forward-models/res/eclipse300 @@ -0,0 +1 @@ +EXECUTABLE script/ecl300 diff --git a/share/ert/forward-models/res/flow b/share/ert/forward-models/res/flow new file mode 100644 index 00000000000..1d653031981 --- /dev/null +++ b/share/ert/forward-models/res/flow @@ -0,0 +1 @@ +EXECUTABLE script/flow diff --git a/share/ert/forward-models/res/rms b/share/ert/forward-models/res/rms new file mode 100644 index 00000000000..2da059aafd9 --- /dev/null +++ b/share/ert/forward-models/res/rms @@ -0,0 +1 @@ +EXECUTABLE script/rms diff --git a/share/ert/forward-models/res/script/ecl100 b/share/ert/forward-models/res/script/ecl100 new file mode 100755 index 00000000000..fe9894a90fe --- /dev/null +++ b/share/ert/forward-models/res/script/ecl100 @@ -0,0 +1,6 @@ +#!/usr/bin/env python +import sys +from res.fm.ecl import run, Ecl100Config + +config = Ecl100Config() +run(config, [arg for arg in sys.argv[1:] if len(arg) > 0]) diff --git a/share/ert/forward-models/res/script/ecl300 b/share/ert/forward-models/res/script/ecl300 new file mode 100755 index 00000000000..a0db696224a --- /dev/null +++ b/share/ert/forward-models/res/script/ecl300 @@ -0,0 +1,6 @@ +#!/usr/bin/env python +import sys +from res.fm.ecl import run, Ecl300Config + +config = Ecl300Config() +run(config, [arg for arg in sys.argv[1:] if len(arg) > 0]) diff --git a/share/ert/forward-models/res/script/flow b/share/ert/forward-models/res/script/flow new file mode 100755 index 00000000000..14dc264a754 --- /dev/null +++ b/share/ert/forward-models/res/script/flow @@ -0,0 +1,6 @@ +#!/usr/bin/env python +import sys +from res.fm.ecl import run, FlowConfig + +config = FlowConfig() +run(config, [arg for arg in sys.argv[1:] if len(arg) > 0]) diff --git a/share/ert/forward-models/res/script/rms b/share/ert/forward-models/res/script/rms new file mode 100755 index 00000000000..63f6049b01a --- /dev/null +++ b/share/ert/forward-models/res/script/rms @@ -0,0 +1,91 @@ +#!/usr/bin/env python +import argparse +from res.fm.rms import run +import sys + + +def _build_argument_parser(): + description = "Wrapper script to run rms." + usage = ( + "The script must be invoked with minimum three positional arguments:\n\n" + " rms iens project workflow \n\n" + "Optional arguments supported: \n" + " target file [-t][--target-file]\n" + " run path [-r][--run-path] default=rms/model\n" + " import path [-i][--import-path] default=./ \n" + " export path [-e][--export-path] default=./ \n" + " version [-v][--version]\n" + ) + parser = argparse.ArgumentParser(description=description, usage=usage) + parser.add_argument( + "iens", + type=int, + help="Realization number", + ) + parser.add_argument( + "project", + help="The RMS project we are running", + ) + parser.add_argument( + "workflow", + help="The rms workflow we intend to run", + ) + parser.add_argument( + "-r", + "--run-path", + default="rms/model", + help="The directory which will be used as cwd when running rms", + ) + parser.add_argument( + "-t", + "--target-file", + default=None, + help="name of file which should be created/updated by rms", + ) + parser.add_argument( + "-i", + "--import-path", + default="./", + help="the prefix of all relative paths when rms is importing", + ) + parser.add_argument( + "-e", + "--export-path", + default="./", + help="the prefix of all relative paths when rms is exporting", + ) + parser.add_argument( + "-v", + "--version", + default=None, + help="the prefix of all relative paths when rms is exporting", + ) + parser.add_argument( + "-a", + "--allow-no-env", + action="store_true", + help="Allow RMS to run without a site configured environment", + ) + return parser + + +# The first three arguments; iens, project and workflow are positional +# and *must* be supplied. The run_path and target_file arguments are optional. + +if __name__ == "__main__": + # old style jobs pass inn empty arguments as "" and causes argparse to fail + sys.argv = [arg for arg in sys.argv if arg != ""] + arg_parser = _build_argument_parser() + args = arg_parser.parse_args() + + run( + args.iens, + args.project, + args.workflow, + run_path=args.run_path, + target_file=args.target_file, + import_path=args.import_path, + export_path=args.export_path, + version=args.version, + allow_no_env=args.allow_no_env, + ) diff --git a/share/ert/forward-models/shell/careful_copy_file b/share/ert/forward-models/shell/careful_copy_file new file mode 100644 index 00000000000..53704ff2a16 --- /dev/null +++ b/share/ert/forward-models/shell/careful_copy_file @@ -0,0 +1 @@ +EXECUTABLE ../../shell_scripts/careful_copy_file diff --git a/share/ert/forward-models/shell/copy_directory b/share/ert/forward-models/shell/copy_directory new file mode 100644 index 00000000000..097e390307e --- /dev/null +++ b/share/ert/forward-models/shell/copy_directory @@ -0,0 +1 @@ +EXECUTABLE ../../shell_scripts/copy_directory diff --git a/share/ert/forward-models/shell/copy_file b/share/ert/forward-models/shell/copy_file new file mode 100644 index 00000000000..2fc0901dae5 --- /dev/null +++ b/share/ert/forward-models/shell/copy_file @@ -0,0 +1 @@ +EXECUTABLE ../../shell_scripts/copy_file diff --git a/share/ert/forward-models/shell/delete_directory b/share/ert/forward-models/shell/delete_directory new file mode 100644 index 00000000000..8666aa26cd4 --- /dev/null +++ b/share/ert/forward-models/shell/delete_directory @@ -0,0 +1 @@ +EXECUTABLE ../../shell_scripts/delete_directory diff --git a/share/ert/forward-models/shell/delete_file b/share/ert/forward-models/shell/delete_file new file mode 100644 index 00000000000..7c7eccaee34 --- /dev/null +++ b/share/ert/forward-models/shell/delete_file @@ -0,0 +1 @@ +EXECUTABLE ../../shell_scripts/delete_file diff --git a/share/ert/forward-models/shell/make_directory b/share/ert/forward-models/shell/make_directory new file mode 100644 index 00000000000..c44a93f316a --- /dev/null +++ b/share/ert/forward-models/shell/make_directory @@ -0,0 +1 @@ +EXECUTABLE ../../shell_scripts/make_directory diff --git a/share/ert/forward-models/shell/make_symlink b/share/ert/forward-models/shell/make_symlink new file mode 100644 index 00000000000..8153f279aeb --- /dev/null +++ b/share/ert/forward-models/shell/make_symlink @@ -0,0 +1 @@ +EXECUTABLE ../../shell_scripts/symlink diff --git a/share/ert/forward-models/shell/move_file b/share/ert/forward-models/shell/move_file new file mode 100644 index 00000000000..5c6c032f998 --- /dev/null +++ b/share/ert/forward-models/shell/move_file @@ -0,0 +1 @@ +EXECUTABLE ../../shell_scripts/move_file diff --git a/share/ert/forward-models/shell/symlink b/share/ert/forward-models/shell/symlink new file mode 100644 index 00000000000..8153f279aeb --- /dev/null +++ b/share/ert/forward-models/shell/symlink @@ -0,0 +1 @@ +EXECUTABLE ../../shell_scripts/symlink diff --git a/share/ert/forward-models/templating/script/template_render b/share/ert/forward-models/templating/script/template_render new file mode 100755 index 00000000000..4682393d2ae --- /dev/null +++ b/share/ert/forward-models/templating/script/template_render @@ -0,0 +1,59 @@ +#!/usr/bin/env python +import sys +import argparse +from res.fm.templating import render_template + + +def _build_argument_parser(): + description = """ +Loads the data from each file ("some/path/filename.xxx") in INPUT_FILES +and exposes it as the variable "filename". It then loads the Jinja2 +template TEMPLATE_FILE and dumps the rendered result to OUTPUT. + +Example: +Given an input file my_input.json: + +{ + my_variable: my_value +} + +And a template file tmpl.jinja: + +This is written in my file together with {{my_input.my_variable}} + +This job will produce an output file: + +This is written in my file together with my_value +""" + + parser = argparse.ArgumentParser(description=description) + parser.add_argument( + "--output_file", + "-o", + required=True, + help="the output file", + ) + parser.add_argument( + "--template_file", + "-t", + required=True, + help="the jinja2 template file", + ) + parser.add_argument( + "--input_files", + "-i", + nargs="+", + help="list of json and yaml input files", + ) + return parser + + +if __name__ == "__main__": + arg_parser = _build_argument_parser() + args = arg_parser.parse_args() + + render_template( + args.input_files, + args.template_file, + args.output_file, + ) diff --git a/share/ert/forward-models/templating/template_render b/share/ert/forward-models/templating/template_render new file mode 100644 index 00000000000..9b78808ed61 --- /dev/null +++ b/share/ert/forward-models/templating/template_render @@ -0,0 +1 @@ +EXECUTABLE script/template_render diff --git a/share/ert/gui/doc/documentation.txt b/share/ert/gui/doc/documentation.txt new file mode 100644 index 00000000000..7a25a71ffc4 --- /dev/null +++ b/share/ert/gui/doc/documentation.txt @@ -0,0 +1,70 @@ +ERTGUI core documentation + + Codebase in C Codebase in Python + ++--------------------+ +-------------------------+ +| ERT |/------------\| ErtWrapper | +| shared C library |\------------/| ctypes wrapper | ++--------------------+ | (C <-> Python comm.) | + +-------------------------+ + /\ + || + || + \/ + +-------------------------+ + | ContentModel | + | Class for fetching and | + | updating data in ERT | + +-------------------------+ + /\ + || + || + \/ + +-------------------------+ + | Widgets | + | GUI components that | + | enables presentation | + | and modification of | + | data in ERT | + +-------------------------+ + + +ERT: + the base software which the GUI controls. + +ErtWrapper: + this class simplifies the link between Python code and the exposed + functions of the shared library of ERT. + All communication between ERT and the GUI is performed through this + class and type safety of the functions in the shared library are + provided through a prototyping functionality. + +ContentModel: + This class is a base class for all widgets and algorithms that need + to communicate with ERT. Several abstract functions are defined that + must/can be implemented: + * initialize(model): typically used for prototyping of functions + * getter(model): used to fetch data from ERT + * setter(model, value): used for updating data values + * insert(model, value): used for adding data values + * remove(model, value): used for removing data values + + The program should never call these functions directly but use: + * getFromModel(): which will call the getter automatically + * updateContent(value, operation = UPDATE|INSERT|REMOVE): + which will call setter/insert/remove respectively automatically. + + A final function that must be implemented is fetchContent(). This function + will automatically be called when the model (ERT) is ready or has been + changed. This is also safe to be called by the program. Typical use of this + function is to call getFromModel() which returns the data generated by the + implemented getter function and then populate the widgets with the data. + + Whenever changes are made to the GUI ERT should be updated by calls + to updateContent(). + +Widgets: + Several widgets have been implemented. Most of these subclass HelpedWidget + which subclass ContentModel and provide built-in help functionality. + + diff --git a/share/ert/gui/doc/info.txt b/share/ert/gui/doc/info.txt new file mode 100644 index 00000000000..9dcfd7b01e0 --- /dev/null +++ b/share/ert/gui/doc/info.txt @@ -0,0 +1,237 @@ +forward_model: +-------------- + +get: +---- +model_config = enkf_main_get_model_config( enkf_main ) +forward_model = model_config_get_forward_model( model_config ) +names = forward_model_alloc_joblist( forward_model ) <- Allokerer en stringlist() med navnene på jobbene. + + + +set +--- +forward_model_clear( forward_model ) + +forward_model_add_job( forward_model , job_name ); +forward_model_iset_job_arg( forward_model , job_nr , key , value); <- Set argument - observe that the jobs are identified with the index in + the forward model, and not by name. + +more +---- +s = enkf_main_get_site_config( enkf_main ) +joblist = site_config_get_installed_jobs( s ); +job_names = ext_joblist_alloc_list( joblist ); <- Stringlist + + + +forward_model_get_job( forward_model , ?NAME?) +ext_joblist_get_job( joblist , ?NAME?) + +ext_job = forward_model_iget_job( forward_model , job_nr ) +arg_string = ext_job_get_private_args_as_string( ext_job ); +ext_job_set_private_args_from_string( ext_job , "KEY1=Value1 , Key2=value2" ) + + +ext_job_get_help_text( job ); + + +-------------------------------------- + + +LOG: + +Get +--- +log_file = enkf_main_get_log_file( enkf_main ) +log_level = enkf_main_get_log_level( enkf_main ) + + +Set +--- +enkf_main_set_log_file( enkf_main , "log_file" ) +enkf_main_set_log_level( enkf_main , log_level ) + + +analysis_config = enkf_main_get_analysis_config( enkf_main ) +log_path = analysis_config_get_log_path( analysis_config ); +analysis_config_set_log_path( analysis_config , log_path ); + + + + +INITIALIZED +----------- + +enkf_main_is_initialized(enkf_main , active_mask); + +active_mask er nå en vector av typen: bool_vector som er implementert i +libutil. Dersom alle elemementene er aktive kan du sende inn None / +NULL, ellers må allokere og initialisere en active_mask instans: + +mask = bool_vector_alloc(0 , false) +for index in selectedMembers: + bool_vector_iset( mask , index , true ) + +enkf_main_is_initialized( enkf_main , mask ) +bool_vector_free( mask ) + + + + +TEMPLATES: +---------- +Get: +---- +templates = enkf_main_get_templates( enkf_main ); +s = ert_templates_alloc_list( templates ); <- Returnerer en stringlist med navn +template = ert_templates_get_template( templates , "Navn") +template_file = ert_template_get_template_file( template ); +target_file = ert_template_get_target_file( template ); +arg_string = ert_template_get_args_as_string( template ) + + +Set: (kan enten gjøres med manuelle add / del eller ved hjelp av clear og add). +---- +templates = enkf_main_get_templates( enkf_main ); +ert_templates_clear( templates ); +ert_templates_del_template( templates , "Key"); +template = ert_templates_add_template( templates , "Key", "Template_file" , "target_file" , arg_string /* Can be None / NULL */); + +template = ert_templates_get_template( templates , Key) +ert_template_set_target_file(template , target_file ); +ert_template_set_template_file(template , template_file ); +ert_template_set_args_as_string(template , arg_string); + + + + + +INITIALIZATION +-------------- + +enkf_main_initialize_from_existing__( enkf_main , source_case , source_step , source_state , + iens_mask , ranking_key /* Will be NULL*/ , + node_list); + + + +node_list ~ stringlist med de valgt nodene. + +iens_mask = bool_vector_alloc(0 , false); <- Allokereres med false som default. +iens_mask ~ bool_vector hvor alle de aktive indeksene er satt til true + + + +SCHEDULE_PREDICTION_FILE +------------------------ +Get: enkf_main_get_schedule_prediction_file( enkf_main ) +Set: enkf_main_set_schedule_prediction_file( enkf_main , filename ) + + +REFCASE +------- +Get: +ecle_config = enkf_main_get_ecl_config( enkf_main ); +refacse = ecl_config_get_refcase_name( ecl_config ) + +Set: +enkf_main_set_refcase( enkf_main , refcase ) ???????????????????? Ikkje load? + + + +HISTORY_SOURCE +-------------- +Get: +m = enkf_main_get_model_config( enkf_main ) +history_source = model_config_get_history_source( m ) <- History_source ~ enum history_source_type fra libsched/src/history.y + + +Set: +m = enkf_main_get_model_config( enkf_main ) +model_config_get_history_source( m , source) <- NB Hvis source har en av verdiene REFCASE_HISTORY / REFCASE_SMULATED du maa satt ett REFACSE forst. + + + + +CASE_TABLE +---------- +model_config = enkf_main_get_model_config( enkf_main ); + +get: model_config_get_case_table_file( model_config ) +set: enkf_main_set_case_table( model_config , filename ); <- File must exist + + + +REFCASE - Plotting: +------------------ +Get: + +ecl_config = enkf_main_get_ecl_config( enkf_main ) +ecl_sum = ecl_config_get_refcase( ecl_config ) + +Plotting: + + 1. Sjekk om refcase har variabel: + + ecl_sum_has_general_var( ecl_sum , key) + + hvor 'key' er en summary key, som for eksempel "WWCT:OP_1". Dersom denne funksjonen returnerer true, maa du + sporre ecl_sum som en "oversettelse" fra key til en integer index, og siden bruke den indeksen: + + key_index = ecl_sum_get_general_var_index( ecl_sum , key ); + + + 2. Alloker en tidsakse: + + time_vector = ecl_sum_alloc_time_vector( ecl_sum , true /* Report_only kan vre true */); + + Denne kan selvflgelig gjenbrukes mellom flere summary plott. + + 3. Alloker datavektor + + data_vektor1 = ecl_sum_alloc_data_vector( ecl_sum , key_index1 , true ); + + + Da har du x og y i en hhv time_t_vector og double_vector - en av joakims hjemmelade datastrukturer: + + size: xxx_vector_get_size( ) + get1: xxx_vector_iget( ) + get2: xxx_vector_get_ptr() <- Returnerer en peker til underliggende data. + + + 4. xxx_vector_free () naar du er ferdi. + + +5. (Ekstraoppgave:) en ecl_sum instans kan også loades fra filsystemet (helt utenfor ERT sin database) med + + ecl_sum = ecl_sum_fread_alloc_case( "Navn på en ECLIPSE.DATA fil" ) + +--------------------- + +Gen_data: +size: +----- +ensemble_config = enkf_main_get_ensemble_config( enkf_main ) +config_node = ensemble_config_get_node( ensemble_config , "KEY") +gen_data_config = enkf_config_node_get_ref( config_node ) + +size = gen_date_config_get_initial_size( gen_data_config ) + +Value: +----- +value = enkf_node_user_get( enkf_node , "KEY:tall" , &valid) <----- NB Det er helt avgjørende å sjekke at valid returnerer true, + gen_data typen er (altfor) fleksibel, og kan endre størrelse runtime. + At du har sjekket initiell størrelse er derfor desverre ikke tilstrekkelig + til å validere input. + + +---------------- +Saving: + +1. Sette config file: enkf_main_set_user_config_file( enkf_main , "config_file" ) [Denne trengs for "Save AS"] +2. enkf_main_fprintf_config( enkf_main ); + + + + \ No newline at end of file diff --git a/share/ert/gui/doc/initializers b/share/ert/gui/doc/initializers new file mode 100644 index 00000000000..26ec3d9224f --- /dev/null +++ b/share/ert/gui/doc/initializers @@ -0,0 +1,201 @@ + +mask = util.bool_vector_alloc(ens_size , false) +# Første arg: størrelse +# Andre arg: init verdi. + +util.bool_vector_iset( mask , 67 , true ) +# bool_ptr = util.bool_vector_get_ptr( mask ) +# Send inn int_ptr +.... +#util.bool_vector_free( mask ) + + + + + + + +#Missing initializer: job_script +# s = enkf_main_get_site_config( enkf_main ) +# site_config_get_job_script( s ); +# site_config_set_job_script( s , EXECUTABLE_FILE ); + + + + +Missing initializer: install_job + s = enkf_main_get_site_config( enkf_main ); + site_config_install_job( s , JOB_KEY , EXISTING_FILE ); <- Should have a GUI option for creating the + config file EXISTING_FILE + +INSTALL_JOB: +----------- +1. Denne bør endre navn til "Private jobs" + +2. Her blir get/set ganske komplisert - det er to grunner til det: + + a) Listen over installerte jobber inneholder både globale jobber, + som brukeren ikke har anledning til å manipulere, og "Private" + jobber som brukeren har installert og således må få lov til å + manipulere. + + b) Foreløpig er det bare basert en config_file, men når vi først + lager GUI ville det være veldig naturlig å lage mulighet for å + konfigurere en jobb via GUI. + +3. Det opprinnelige GUI panelet består av to felt. Kutt ut det ene + feltet, men la det sprette opp ett vindu når man legger til en + jobb. + + +Get: +s = enkf_main_get_site_config( enkf_main ) +jl = site_config_get_installed_jobs( s ) +h = ext_joblist_get_jobs( jl ) <- Nå er h en hash tabell. + +#Itererer over hash_tabellen: +python_joblist = [] +job = hash_get( h , job_name ); +if ext_job_is_private( job ): <- Denne funksjonen er i libjob_queue + python_joblist.append( job_name ) + +OK : python_joblist skal inneholde de job_navnene som skal + vises i GUI. + + + +Set: Her tror jeg det er enklest å respondere eksplisitt på add_job og + del_job events, istedet for å ta en batch prosess til slutt + +add_job: +-------- +job = ext_job_fscanf_alloc( job_name , site_config_get_license_root_path__( s ) , job_config_file ) # job_name and job_config_file from GUI. +ext_joblist_add_job( jl , job ) + + +del_job +------- +ext_joblist_del_job( jl , job_name ) + + + + + + +#Missing initializer: num_realizations +# enkf_main_get_ensemble_size( enkf_main ); +# enkf_main_resize_ensemble( enkf_main ); <- Denne kan potensielt ta en del tid, og + bør være beskyttet av en APPLY knapp. + + +Missing initializer: parameters +Missing initializer: param_type +Missing initializer: param_min +Missing initializer: param_max +Missing initializer: param_init +Missing initializer: param_output +Missing initializer: param_init_files +Missing initializer: param_file_generated_by_enkf +Missing initializer: param_file_loaded_by_enkf +Missing initializer: history_source +Missing initializer: obs_config + + + +#Missing initializer: max_submit +# s = enkf_main_get_site_config( enkf_main ); +# site_config_get_max_submit( s ); +# site_config_set_max_submit( s , max_submit ); + + + + +#Missing initializer: max_resample +# m = enkf_main_get_model_config( enkf_main ); +# model_config_get_max_resample( m ); +# model_config_set_max_resample( m , max_resample ); + + + +Missing initializer: forward_model +Missing initializer: case_table +Missing initializer: license_path + +Missing initializer: runpath +# m = enkf_main_get_model_config( enkf_main ) +# model_config_get_runpath_fmt( m ) +# model_config_set_runpath_fmt( m , "/path/to/run%d" ) + + + +Missing initializer: pre_clear_runpath +Missing initializer: delete_runpath +Missing initializer: keep_runpath +Missing initializer: run_template +Missing initializer: dbase_type +Missing initializer: enspath +Missing initializer: select_case + + +#Missing initializer: log_file +#Missing initializer: log_level +# lg = enkf_main_get_logh( enkf_main ); +# log_reset_filename( lg , "/some/path/to/FILENAME" ); +# log_get_filename( lg ); +# +# log_get_level( lg ); +# log_set_level( lg , LOG_LEVEL) + + + +Missing initializer: update_log_path + + + + +#Get: s = enkf_main_get_data_kw( enkf_main ) +# subst_list_get_size( s ) +# subst_list_iget_key( s ) +# subst_list_iget_value( s ) +#Set: enkf_main_clear_data_kw( enkf_main ) +# enkf_main_add_data_kw( enkf_main , key , value ) + +#Get: m = enkf_main_get_model_config( enkf_main ) +# model_config_get_enkf_sched_file( m ) +#Set: model_config_set_enkf_sched_file( m , "FILENAME" ) + +#Get l = enkf_main_get_local_config( enkf_main ); +# s = local_config_get_config_files( l ) # Stringlist +#Set local_config_clear_config_files( l ) +# local_config_add_config_file(l , "FILENAME") + +#Get s = enkf_main_get_site_config( enkf_main ) +# site_config_get_max_running_(lsf|rsh|local)( s ) +#Set site_config_get_max_running_(lsf|rsh|local)( s , value ) + +#Get s = enkf_main_get_site_config( enkf_main ) +# h = site_config_get_rsh_host_list( s ) +# Iterer over hash - men bruk hash_get_int() for aa faa antall jobber en host kan ta. +#Set site_config_clear_rsh_host_list( s ) +# site_config_add_rsh_host( s , host_name , max_running ) + +#Get s = enkf_main_get_site_config( enkf_main ) +# queue_name = site_config_get_lsf_queue( s ) +#Set site_config_set_lsf_queue( s , "NORMAL" ) + +# site_config_set_job_queue( s , "LOCAL|LSF|RSH" ); +# site_config_get_job_queue_name( s ); + +#self.job_script = "..." +#self.setenv = [["LSF_BINDIR", "/prog/LSF/7.0/linux2.6-glibc2.3-x86_64/bin"], ["LSF_LIBDIR", "/prog/LSF/7.0/linux2.6-glibc2.3-x86_64/lib"]] +#Get: s = enkf_main_get_site_config( enkf_main ) +# h = site_config_get_env_hash( s ) +#Set site_config_clear_env( s ) +# site_config_setenv( s , var , value ) + +#self.update_path = [["PATH", "/prog/LSF/7.0/linux2.6-glibc2.3-x86_64/bin"], ["LD_LIBRARY_PATH", "/prog/LSF/7.0/linux2.6-glibc2.3-x86_64/lib"]] +#Get: s = enkf_main_get_site_config( enkf_main ) +# pathlist = site_config_get_path_variables( s ) +# valuelist = site_config_get_path_values( s ) +#Set: site_config_clear_pathvar( s ) +# site_config_update_pathvar( s , path , value ); diff --git a/share/ert/gui/doc/run_info b/share/ert/gui/doc/run_info new file mode 100644 index 00000000000..3c7280a722c --- /dev/null +++ b/share/ert/gui/doc/run_info @@ -0,0 +1,36 @@ +state = enkf_main_iget_state( enkf_main , iens ); +status = enkf_state_get_run_status( state ); +sim_start = enkf_state_get_start_time( state ); +submit_time = enkf_state_get_submit_time( state ); + + +Queue running: +------------- +s = enkf_main_get_site_config( enkf_main ) +site_config_queue_is_running( s ) + + +Kill simulation +--------------- +enkf_state_kill_simulation( state ) + + +Restart simulation +------------------ +enkf_state_restart_simulation( state , resample ) /* Resample er en bool - True: Kjør ny initialisering */ + + + + +Main queue pausing: +------------------- +job_queue_get_pause( jq ) + +job_queue_set_pause_on( jq ) + +job_queue_set_pause_off( jp ) + + + + + diff --git a/share/ert/gui/doc/tmp_info.txt b/share/ert/gui/doc/tmp_info.txt new file mode 100644 index 00000000000..bc63f69f66e --- /dev/null +++ b/share/ert/gui/doc/tmp_info.txt @@ -0,0 +1,69 @@ +GEN_DATA (Det er ikke skille p GEN_DATA og GEN_PARAM - alt er GEN_DATA) +-------- + +1. Opprette en ny: ensemble_config_add_gen_data( ensemble_config , new_key ); + + +2 Get: + + gen_data_config = enkf_config_node_get_ref( config_node ); + + output_format = gen_data_config_get_output_format( gen_data_config ); <- Returverdien her er en enum: gen_data_file_format_type, + input_format = gen_data_config_get_input_format( gen_data_config ); definert i gen_data_config.h + template_file = gen_data_config_get_template_file( gen_data_config ); + template_key = gen_data_config_get_template_key( gen_data_config ); + init_file_fmt = gen_data_config_get_init_file_fmt( gen_data_config ); + ..... + enkf_config_node_get_enkf_outfile( config_node ) -> Include file (parameter/general) + enkf_config_node_get_min_std_file( config_node ) -> Min. std. (parameter/general) + enkf_config_node_get_enkf_infile( config_node ) -> File loaded by EnKF (general) + + + +3. Update: + + enkf_config_node_update_gen_data( config_node , input_format , output_format , /* Enum: gen_data_file_format_type */ + init_file_format , + template_file , + template_key , + enkf_outfile_fmt , + enkf_infile_fmt , + min_std_file ); + + Legg merke til at det er ganske MAANGE innbyrdes krav mellom de + forskjellige variablene som maa vaere tilfredsstilt; dette er + "dokumentert" i funksjonene enkf_config_node_update_gen_data() og + gen_data_config_update(). + + + + + +OBS_CONFIG: +---------- + +Get: +---- +obs = enkf_main_get_obs( enkf_main ); +enkf_obs_get_config_file( obs ); + + +Set: +enkf_main_load_obs( enkf_main , obs_config_file ); + + + + + +KEEP_RUNPATH / DELETE_RUNPATH +----------------------------- + +Denne variabelen er definert som en streng i tui - jeg foreslaar at du +heller bruker en tilsvarende tilnaerming som naar vi skal velge hvilke +realisasjoner som skal simuleres paa, hver av de smaa boksene skal ha +tre mulige verdier, tilsvarende enum keep_runpath_type i enkf_types.h. + +Deretter: + +enkf_main_iget_keep_runpath( enkf_main , index ); +enkf_main_iset_keep_runpath( enkf_main , index , enum_value ); diff --git a/share/ert/gui/help/config/analysis/analysis_module.html b/share/ert/gui/help/config/analysis/analysis_module.html new file mode 100644 index 00000000000..f0b8207c576 --- /dev/null +++ b/share/ert/gui/help/config/analysis/analysis_module.html @@ -0,0 +1 @@ +Select which type of ensemble smoother to use for perform updates \ No newline at end of file diff --git a/share/ert/gui/help/config/analysis/iterated_analysis_module.html b/share/ert/gui/help/config/analysis/iterated_analysis_module.html new file mode 100644 index 00000000000..adea08022ed --- /dev/null +++ b/share/ert/gui/help/config/analysis/iterated_analysis_module.html @@ -0,0 +1 @@ +Select which type of iterated ensemble smoother to use for perform updates \ No newline at end of file diff --git a/share/ert/gui/help/config/init/history_length.html b/share/ert/gui/help/config/init/history_length.html new file mode 100644 index 00000000000..df8285cb428 --- /dev/null +++ b/share/ert/gui/help/config/init/history_length.html @@ -0,0 +1 @@ +The timestep of the source case from which the initial values are copied \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/add_static_kw.html b/share/ert/gui/help/config/keywords/add_static_kw.html new file mode 100644 index 00000000000..c78e0cbbc31 --- /dev/null +++ b/share/ert/gui/help/config/keywords/add_static_kw.html @@ -0,0 +1,15 @@ +The restart files from ECLIPSE are organized by keywords, which are of three different types: +
    +
  1. Keywords containing the dynamic solution, e.g. pressure and saturations.
  2. +
  3. Keywords containing various types of header information which is needed for a restart.
  4. +
  5. Keywords containing various types of diagnostic information which is not needed for a restart.
  6. +
+ +Keywords in category 2 and 3 are referred to as static keywords. To be able to restart ECLIPSE, the ERT application has to store the keywords in category 2, whereas keywords in category 3 can safely be dropped. To determine whether a particular keyword is in category 2 or 3 ERT considers an internal list of keywords. The current list contains the keywords: + +
INTEHEAD LOGIHEAD DOUBHEAD IGRP SGRP XGRP ZGRP IWEL SWEL XWEL ZWEL
+ ICON SCON XCON HIDDEN STARTSOL PRESSURE SWAT SGAS RS RV ENDSOL ICAQNUM ICAQ IAAQ
+ SCAQNUM SCAQ SAAQ ACAQNUM ACAQ XAAQ
+ ISEG ILBS ILBR RSEG ISTHW ISTHG
+
+Here you can dynamically add to this list. The magic string __ALL__ will add all static keywords, but use of this option is strongly discouraged, as it wastes a lot of disk space. diff --git a/share/ert/gui/help/config/keywords/analysis_copy.html b/share/ert/gui/help/config/keywords/analysis_copy.html new file mode 100644 index 00000000000..bb65f45d449 --- /dev/null +++ b/share/ert/gui/help/config/keywords/analysis_copy.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/analysis_enkf_update.html b/share/ert/gui/help/config/keywords/analysis_enkf_update.html new file mode 100644 index 00000000000..ba920573985 --- /dev/null +++ b/share/ert/gui/help/config/keywords/analysis_enkf_update.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/analysis_load.html b/share/ert/gui/help/config/keywords/analysis_load.html new file mode 100644 index 00000000000..0439e95c261 --- /dev/null +++ b/share/ert/gui/help/config/keywords/analysis_load.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/analysis_select.html b/share/ert/gui/help/config/keywords/analysis_select.html new file mode 100644 index 00000000000..5be06c89cb6 --- /dev/null +++ b/share/ert/gui/help/config/keywords/analysis_select.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/analysis_set_var.html b/share/ert/gui/help/config/keywords/analysis_set_var.html new file mode 100644 index 00000000000..7513aae255a --- /dev/null +++ b/share/ert/gui/help/config/keywords/analysis_set_var.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/analysis_update.html b/share/ert/gui/help/config/keywords/analysis_update.html new file mode 100644 index 00000000000..be8ec82a195 --- /dev/null +++ b/share/ert/gui/help/config/keywords/analysis_update.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/case_table.html b/share/ert/gui/help/config/keywords/case_table.html new file mode 100644 index 00000000000..a0597c48440 --- /dev/null +++ b/share/ert/gui/help/config/keywords/case_table.html @@ -0,0 +1 @@ +For running sensitivies you can give the cases descriptive names. diff --git a/share/ert/gui/help/config/keywords/create_case.html b/share/ert/gui/help/config/keywords/create_case.html new file mode 100644 index 00000000000..7a634a6b3af --- /dev/null +++ b/share/ert/gui/help/config/keywords/create_case.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/data_file.html b/share/ert/gui/help/config/keywords/data_file.html new file mode 100644 index 00000000000..280a668f084 --- /dev/null +++ b/share/ert/gui/help/config/keywords/data_file.html @@ -0,0 +1 @@ +The ECLIPSE data file used to control the simulations. The standard ECLIPSE data file needs to be modified in some ways in order to work with ERT. diff --git a/share/ert/gui/help/config/keywords/data_kw.html b/share/ert/gui/help/config/keywords/data_kw.html new file mode 100644 index 00000000000..50e7d08a0dc --- /dev/null +++ b/share/ert/gui/help/config/keywords/data_kw.html @@ -0,0 +1 @@ +Using Data keywords you can assign arbitrary (Keyword , Value) pairs where all occurences of <Keyword> in the ECLIPSE Data file will be replaced by Value. diff --git a/share/ert/gui/help/config/keywords/data_ranking.html b/share/ert/gui/help/config/keywords/data_ranking.html new file mode 100644 index 00000000000..9336015252f --- /dev/null +++ b/share/ert/gui/help/config/keywords/data_ranking.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/define.html b/share/ert/gui/help/config/keywords/define.html new file mode 100644 index 00000000000..b1f1a19e61a --- /dev/null +++ b/share/ert/gui/help/config/keywords/define.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/delete_runpath.html b/share/ert/gui/help/config/keywords/delete_runpath.html new file mode 100644 index 00000000000..397ba46143f --- /dev/null +++ b/share/ert/gui/help/config/keywords/delete_runpath.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/eclbase.html b/share/ert/gui/help/config/keywords/eclbase.html new file mode 100644 index 00000000000..e4ba02e9929 --- /dev/null +++ b/share/ert/gui/help/config/keywords/eclbase.html @@ -0,0 +1,5 @@ +The basename used for the ECLIPSE simulations. The name should contain a %d-specifier, which will be replazed by the realization number when running ECLIPSE.
+ +Example: BASE_%04d will be replaced by BASE_0007 for realization number 7.
+ +Note that due to limitations in ECLIPSE, the basename must be in strictly upper or lower case. diff --git a/share/ert/gui/help/config/keywords/end_date.html b/share/ert/gui/help/config/keywords/end_date.html new file mode 100644 index 00000000000..13ea1a5f5bd --- /dev/null +++ b/share/ert/gui/help/config/keywords/end_date.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/enkf_alpha.html b/share/ert/gui/help/config/keywords/enkf_alpha.html new file mode 100644 index 00000000000..5534d25dc10 --- /dev/null +++ b/share/ert/gui/help/config/keywords/enkf_alpha.html @@ -0,0 +1 @@ +A parameter controlling outlier behaviour in the EnKF algorithm. The default value is 1.50. diff --git a/share/ert/gui/help/config/keywords/enkf_bootstrap.html b/share/ert/gui/help/config/keywords/enkf_bootstrap.html new file mode 100644 index 00000000000..62c4d5e1e00 --- /dev/null +++ b/share/ert/gui/help/config/keywords/enkf_bootstrap.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/enkf_force_ncomp.html b/share/ert/gui/help/config/keywords/enkf_force_ncomp.html new file mode 100644 index 00000000000..bff2df93668 --- /dev/null +++ b/share/ert/gui/help/config/keywords/enkf_force_ncomp.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/enkf_merge_observations.html b/share/ert/gui/help/config/keywords/enkf_merge_observations.html new file mode 100644 index 00000000000..274c5023ea4 --- /dev/null +++ b/share/ert/gui/help/config/keywords/enkf_merge_observations.html @@ -0,0 +1 @@ +If you use the ENKF schedule file option to jump over several dates at a time you can choose whether you want to use all the observations in between, or just the final. Select Perform merge if you want all observations to be used, unselect to use only the final observation. diff --git a/share/ert/gui/help/config/keywords/enkf_mode.html b/share/ert/gui/help/config/keywords/enkf_mode.html new file mode 100644 index 00000000000..3a31f07936b --- /dev/null +++ b/share/ert/gui/help/config/keywords/enkf_mode.html @@ -0,0 +1 @@ +Here you select which EnKF algorithm to use. Use ENKF_STANDARD for the original EnKF algorithm, or ENKF_SQRT for the square root scheme. diff --git a/share/ert/gui/help/config/keywords/enkf_ncomp.html b/share/ert/gui/help/config/keywords/enkf_ncomp.html new file mode 100644 index 00000000000..438e8b761d5 --- /dev/null +++ b/share/ert/gui/help/config/keywords/enkf_ncomp.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/enkf_pen_press.html b/share/ert/gui/help/config/keywords/enkf_pen_press.html new file mode 100644 index 00000000000..7385cf52925 --- /dev/null +++ b/share/ert/gui/help/config/keywords/enkf_pen_press.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/enkf_rerun.html b/share/ert/gui/help/config/keywords/enkf_rerun.html new file mode 100644 index 00000000000..3f7b3ec169d --- /dev/null +++ b/share/ert/gui/help/config/keywords/enkf_rerun.html @@ -0,0 +1 @@ +Select if the simulation should be started from time zero after each update. diff --git a/share/ert/gui/help/config/keywords/enkf_truncation.html b/share/ert/gui/help/config/keywords/enkf_truncation.html new file mode 100644 index 00000000000..cc87ad385fe --- /dev/null +++ b/share/ert/gui/help/config/keywords/enkf_truncation.html @@ -0,0 +1 @@ +Cutoff used on singular value spectrum. Default value: 0.99. diff --git a/share/ert/gui/help/config/keywords/ensemble_run.html b/share/ert/gui/help/config/keywords/ensemble_run.html new file mode 100644 index 00000000000..d0c173a933d --- /dev/null +++ b/share/ert/gui/help/config/keywords/ensemble_run.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/enspath.html b/share/ert/gui/help/config/keywords/enspath.html new file mode 100644 index 00000000000..cc2d44680e9 --- /dev/null +++ b/share/ert/gui/help/config/keywords/enspath.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/export_field.html b/share/ert/gui/help/config/keywords/export_field.html new file mode 100644 index 00000000000..e1cf6dddec4 --- /dev/null +++ b/share/ert/gui/help/config/keywords/export_field.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/export_field_ecl_grdecl.html b/share/ert/gui/help/config/keywords/export_field_ecl_grdecl.html new file mode 100644 index 00000000000..256e994c047 --- /dev/null +++ b/share/ert/gui/help/config/keywords/export_field_ecl_grdecl.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/export_field_rms_roff.html b/share/ert/gui/help/config/keywords/export_field_rms_roff.html new file mode 100644 index 00000000000..69b24e56a47 --- /dev/null +++ b/share/ert/gui/help/config/keywords/export_field_rms_roff.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/export_ranking.html b/share/ert/gui/help/config/keywords/export_ranking.html new file mode 100644 index 00000000000..82e3807b973 --- /dev/null +++ b/share/ert/gui/help/config/keywords/export_ranking.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/field.html b/share/ert/gui/help/config/keywords/field.html new file mode 100644 index 00000000000..7002f52e4e6 --- /dev/null +++ b/share/ert/gui/help/config/keywords/field.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/forward_model.html b/share/ert/gui/help/config/keywords/forward_model.html new file mode 100644 index 00000000000..ef7799a67be --- /dev/null +++ b/share/ert/gui/help/config/keywords/forward_model.html @@ -0,0 +1 @@ +The Forward model defines how the simulations are executed. E.g., which version of ECLIPSE to use, which rel.perm script to run, which rock physics model to use etc. diff --git a/share/ert/gui/help/config/keywords/gen_data.html b/share/ert/gui/help/config/keywords/gen_data.html new file mode 100644 index 00000000000..4225d803d46 --- /dev/null +++ b/share/ert/gui/help/config/keywords/gen_data.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/gen_kw.html b/share/ert/gui/help/config/keywords/gen_kw.html new file mode 100644 index 00000000000..92666c8c313 --- /dev/null +++ b/share/ert/gui/help/config/keywords/gen_kw.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/gen_param.html b/share/ert/gui/help/config/keywords/gen_param.html new file mode 100644 index 00000000000..d8ac1acef5a --- /dev/null +++ b/share/ert/gui/help/config/keywords/gen_param.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/grid.html b/share/ert/gui/help/config/keywords/grid.html new file mode 100644 index 00000000000..163472e1888 --- /dev/null +++ b/share/ert/gui/help/config/keywords/grid.html @@ -0,0 +1 @@ +This should point to an existing GRID/EGRID-file belonging to your ECLIPSE-model. diff --git a/share/ert/gui/help/config/keywords/history_source.html b/share/ert/gui/help/config/keywords/history_source.html new file mode 100644 index 00000000000..eddb472db14 --- /dev/null +++ b/share/ert/gui/help/config/keywords/history_source.html @@ -0,0 +1,9 @@ +ERT will read the historic rates from the Schedule file, but it is also possible to use historic data from another ECLIPSE run (e.g if historic data do not exist in the Schedule file). +
+
SCHEDULE +
ERT will use the historic data provided in the Schedule file. +
REFCASE_SIMULATED +
ERT will use simulated values from another ECLIPSE run (a Reference case) as historic data. +
REFCASE_HISTORY +
ERT will use historic data from a reference case. +
diff --git a/share/ert/gui/help/config/keywords/image_type.html b/share/ert/gui/help/config/keywords/image_type.html new file mode 100644 index 00000000000..012875d2255 --- /dev/null +++ b/share/ert/gui/help/config/keywords/image_type.html @@ -0,0 +1 @@ +In which format should the plots created by PLPLOT be saved (default: png). diff --git a/share/ert/gui/help/config/keywords/image_viewer.html b/share/ert/gui/help/config/keywords/image_viewer.html new file mode 100644 index 00000000000..422c6d74173 --- /dev/null +++ b/share/ert/gui/help/config/keywords/image_viewer.html @@ -0,0 +1,5 @@ +Here you may decide which application to use to view the plots. The default is a program called /usr/bin/display. +
+
+In order to avoid the plots from flashing in your face as they are created, set Image viewer to /d/proj/bg/enkf/bin/noplot.sh +. diff --git a/share/ert/gui/help/config/keywords/init_case_from_existing.html b/share/ert/gui/help/config/keywords/init_case_from_existing.html new file mode 100644 index 00000000000..ba679851bfb --- /dev/null +++ b/share/ert/gui/help/config/keywords/init_case_from_existing.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/init_misfit_table.html b/share/ert/gui/help/config/keywords/init_misfit_table.html new file mode 100644 index 00000000000..a242e1e5cee --- /dev/null +++ b/share/ert/gui/help/config/keywords/init_misfit_table.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/init_section.html b/share/ert/gui/help/config/keywords/init_section.html new file mode 100644 index 00000000000..cd2dacddcca --- /dev/null +++ b/share/ert/gui/help/config/keywords/init_section.html @@ -0,0 +1,6 @@ +The Init section is used to handle initialization of the ECLIPSE run. +This can be used in two different ways: +
    +
  • If it is set to the name of an existing file, the contents of this file will be used for the initialization. +
  • If it is set to the name of a non-existing file, it will be assumed that a file with this name in the simulation folder will be generated when simulations are submitted, either by the ERT application itself, or by some job installed by the user. This generated file will then be used by ECLIPSE for initialization. +
diff --git a/share/ert/gui/help/config/keywords/install_job.html b/share/ert/gui/help/config/keywords/install_job.html new file mode 100644 index 00000000000..8c97722da7e --- /dev/null +++ b/share/ert/gui/help/config/keywords/install_job.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/job_script.html b/share/ert/gui/help/config/keywords/job_script.html new file mode 100644 index 00000000000..bb05afd9a46 --- /dev/null +++ b/share/ert/gui/help/config/keywords/job_script.html @@ -0,0 +1,7 @@ +Running the forward model from enkf is a multi-level process which can be summarized as follows: +
    +
  1. A Python module called jobs.py is written and stored in the directory where the forward simulation is run. This module contains a list of job-elements, where each element is a Python representation of the code entered when installing the job.
  2. +
  3. The ERT application submits a Python script to the EnKF queue system, this script then loads the jobs.py module to find out which programs to run, and how to run them.
  4. +
  5. The Job script starts and monitors the individual jobs in the jobs.py module.
  6. +
+Job script should point at the Python script which is managing the forward model. diff --git a/share/ert/gui/help/config/keywords/jobname.html b/share/ert/gui/help/config/keywords/jobname.html new file mode 100644 index 00000000000..c1b4e7290e7 --- /dev/null +++ b/share/ert/gui/help/config/keywords/jobname.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/keywords.txt b/share/ert/gui/help/config/keywords/keywords.txt new file mode 100644 index 00000000000..4fd8a109add --- /dev/null +++ b/share/ert/gui/help/config/keywords/keywords.txt @@ -0,0 +1,81 @@ +data_file http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#DATA_FILE +eclbase http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#ECLBASE +jobname http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#JOBNAME +grid http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#GRID +num_realizations http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#NUM_REALIZATIONS +data_kw http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#DATA_KW +delete_runpath http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#DELETE_RUNPATH +enkf_sched_file http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#ENKF_SCHED_FILE +end_date http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#END_DATE +enspath http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#ENSPATH +select_case http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#SELECT_CASE +history_source http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#HISTORY_SOURCE +refcase http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#REFCASE +install_job http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#INSTALL_JOB +keep_runpath http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#KEEP_RUNPATH +obs_config http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#OBS_CONFIG +result_path http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#RESULT_PATH +runpath http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#RUNPATH +runpath_file http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#RUNPATH_FILE +min_realizations http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#MIN_REALIZATIONS +stop_long_running http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#STOP_LONG_RUNNING +max_runtime http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#MAX_RUNTIME +field http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#FIELD +gen_data http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#GEN_DATA +gen_kw http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#GEN_KW +gen_param http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#GEN_PARAM +surface http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#SURFACE +summary http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#SUMMARY +enkf_alpha http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#ENKF_ALPHA +enkf_bootstrap http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#ENKF_BOOTSTRAP +enkf_force_ncomp http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#ENKF_FORCE_NCOMP +enkf_pen_press http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#ENKF_PEN_PRESS +enkf_mode http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#ENKF_MODE +enkf_merge_observations http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#ENKF_MERGE_OBSERVATIONS +enkf_ncomp http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#ENKF_NCOMP +enkf_rerun http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#ENKF_RERUN +enkf_truncation http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#ENKF_TRUNCATION +update_log_path http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#UPDATE_LOG_PATH +analysis_load http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#ANALYSIS_LOAD +analysis_select http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#ANALYSIS_SELECT +analysis_set_var http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#ANALYSIS_SET_VAR +analysis_copy http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#ANALYSIS_COPY +define http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#DEFINE +schedule_prediction_file http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#SCHEDULE_PREDICTION_FILE +forward_model http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#FORWARD_MODEL +job_script http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#JOB_SCRIPT +queue_system http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#QUEUE_SYSTEM +lsf_server http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#LSF_SERVER +lsf_queue http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#LSF_QUEUE +rsh_host http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#RSH_HOST +rsh_command http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#RSH_COMMAND +image_viewer http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#IMAGE_VIEWER +image_type http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#IMAGE_TYPE +plot_driver http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#PLOT_DRIVER +plot_errorbar http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#PLOT_ERRORBAR +plot_errorbar_max http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#PLOT_ERRORBAR_MAX +plot_height http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#PLOT_HEIGHT +plot_refcase http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#PLOT_REFCASE +refcase_list http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#REFCASE_LIST +plot_path http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#PLOT_PATH +plot_width http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#PLOT_WIDTH +rftpath http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#RFTPATH +select_case http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#SELECT_CASE +create_case http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#CREATE_CASE +init_case_from_existing http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#INIT_CASE_FROM_EXISTING +export_field http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#EXPORT_FIELD +export_field_rms_roff http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#EXPORT_FIELD_RMS_ROFF +export_field_ecl_grdecl http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#EXPORT_FIELD_ECL_GRDECL +analysis_update http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#ANALYSIS_UPDATE +analysis_enkf_update http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#ANALYSIS_ENKF_UPDATE +run_smoother http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#RUN_SMOOTHER +run_smoother_with_iter http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#RUN_SMOOTHER_WITH_ITER +ensemble_run http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#ENSEMBLE_RUN +load_results http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#LOAD_RESULTS +load_results_iter http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#LOAD_RESULTS_ITER +observation_ranking http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#OBSERVATION_RANKING +data_ranking http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#DATA_RANKING +export_ranking http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#EXPORT_RANKING +init_misfit_table http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#INIT_MISFIT_TABLE +setenv http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#SETENV +update_path http://ert.nr.no/ert/index.php/Creating_a_configuration_file_for_ERT#UPDATE_PATH \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/license_path.html b/share/ert/gui/help/config/keywords/license_path.html new file mode 100644 index 00000000000..cd8d4a08f31 --- /dev/null +++ b/share/ert/gui/help/config/keywords/license_path.html @@ -0,0 +1 @@ +The path where the ERT-licenses for the different programs (such as RMS) are stored. diff --git a/share/ert/gui/help/config/keywords/load_results.html b/share/ert/gui/help/config/keywords/load_results.html new file mode 100644 index 00000000000..2def17d6d95 --- /dev/null +++ b/share/ert/gui/help/config/keywords/load_results.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/load_results_iter.html b/share/ert/gui/help/config/keywords/load_results_iter.html new file mode 100644 index 00000000000..11a79d6e596 --- /dev/null +++ b/share/ert/gui/help/config/keywords/load_results_iter.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/local_config.html b/share/ert/gui/help/config/keywords/local_config.html new file mode 100644 index 00000000000..cfa726e6db3 --- /dev/null +++ b/share/ert/gui/help/config/keywords/local_config.html @@ -0,0 +1 @@ +This can point to a file with configuration information for local analysis. diff --git a/share/ert/gui/help/config/keywords/log_file.html b/share/ert/gui/help/config/keywords/log_file.html new file mode 100644 index 00000000000..2e2eb3503b5 --- /dev/null +++ b/share/ert/gui/help/config/keywords/log_file.html @@ -0,0 +1 @@ +The name of the log file. diff --git a/share/ert/gui/help/config/keywords/log_level.html b/share/ert/gui/help/config/keywords/log_level.html new file mode 100644 index 00000000000..dd3c264cbcb --- /dev/null +++ b/share/ert/gui/help/config/keywords/log_level.html @@ -0,0 +1 @@ +The amount of logging. diff --git a/share/ert/gui/help/config/keywords/lsf_queue.html b/share/ert/gui/help/config/keywords/lsf_queue.html new file mode 100644 index 00000000000..7007bb48e0a --- /dev/null +++ b/share/ert/gui/help/config/keywords/lsf_queue.html @@ -0,0 +1,2 @@ +Select which LSF queue to use. + diff --git a/share/ert/gui/help/config/keywords/lsf_resources.html b/share/ert/gui/help/config/keywords/lsf_resources.html new file mode 100644 index 00000000000..266ae8a7503 --- /dev/null +++ b/share/ert/gui/help/config/keywords/lsf_resources.html @@ -0,0 +1 @@ +Specify special LSF requirements here. diff --git a/share/ert/gui/help/config/keywords/lsf_server.html b/share/ert/gui/help/config/keywords/lsf_server.html new file mode 100644 index 00000000000..fd9aac3cc2c --- /dev/null +++ b/share/ert/gui/help/config/keywords/lsf_server.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/max_resample.html b/share/ert/gui/help/config/keywords/max_resample.html new file mode 100644 index 00000000000..0e22f3505e6 --- /dev/null +++ b/share/ert/gui/help/config/keywords/max_resample.html @@ -0,0 +1 @@ +How many times ERT should resample & retry a simulation. diff --git a/share/ert/gui/help/config/keywords/max_running_local.html b/share/ert/gui/help/config/keywords/max_running_local.html new file mode 100644 index 00000000000..2fe1a41c973 --- /dev/null +++ b/share/ert/gui/help/config/keywords/max_running_local.html @@ -0,0 +1 @@ +Controls the maximum number of simultaneous jobs running when using the LOCAL option. It is strongly recommended to not let this number exceed the number of processors on the workstation used. diff --git a/share/ert/gui/help/config/keywords/max_running_lsf.html b/share/ert/gui/help/config/keywords/max_running_lsf.html new file mode 100644 index 00000000000..607149cd96f --- /dev/null +++ b/share/ert/gui/help/config/keywords/max_running_lsf.html @@ -0,0 +1 @@ +Controls the maximum number of simultaneous jobs submitted to the LSF queue when using the LSF option. diff --git a/share/ert/gui/help/config/keywords/max_running_rsh.html b/share/ert/gui/help/config/keywords/max_running_rsh.html new file mode 100644 index 00000000000..a7d64dcd5c1 --- /dev/null +++ b/share/ert/gui/help/config/keywords/max_running_rsh.html @@ -0,0 +1 @@ +Controls the maximum number of simultaneous jobs running when using the RSH option. If this number exceeds the total capacity defined in the RSH Host List, it will automatically be truncated to that capacity. diff --git a/share/ert/gui/help/config/keywords/max_runtime.html b/share/ert/gui/help/config/keywords/max_runtime.html new file mode 100644 index 00000000000..3e1de4739b9 --- /dev/null +++ b/share/ert/gui/help/config/keywords/max_runtime.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/max_submit.html b/share/ert/gui/help/config/keywords/max_submit.html new file mode 100644 index 00000000000..3fcc40c76c0 --- /dev/null +++ b/share/ert/gui/help/config/keywords/max_submit.html @@ -0,0 +1 @@ +How many times the queue system should retry a simulation. diff --git a/share/ert/gui/help/config/keywords/min_realizations.html b/share/ert/gui/help/config/keywords/min_realizations.html new file mode 100644 index 00000000000..4f540357178 --- /dev/null +++ b/share/ert/gui/help/config/keywords/min_realizations.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/num_realizations.html b/share/ert/gui/help/config/keywords/num_realizations.html new file mode 100644 index 00000000000..2b941723248 --- /dev/null +++ b/share/ert/gui/help/config/keywords/num_realizations.html @@ -0,0 +1 @@ +The number of members/realizations in your ensemble, i.e the ensemble size. diff --git a/share/ert/gui/help/config/keywords/obs_config.html b/share/ert/gui/help/config/keywords/obs_config.html new file mode 100644 index 00000000000..267ab3e64b9 --- /dev/null +++ b/share/ert/gui/help/config/keywords/obs_config.html @@ -0,0 +1 @@ +This should point to an observation file, a file defining observations and associated uncertainties. It is optional, but strongly recommended to provide an observation file. diff --git a/share/ert/gui/help/config/keywords/observation_ranking.html b/share/ert/gui/help/config/keywords/observation_ranking.html new file mode 100644 index 00000000000..be2ff2fe016 --- /dev/null +++ b/share/ert/gui/help/config/keywords/observation_ranking.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/plot_driver.html b/share/ert/gui/help/config/keywords/plot_driver.html new file mode 100644 index 00000000000..249a95f9e07 --- /dev/null +++ b/share/ert/gui/help/config/keywords/plot_driver.html @@ -0,0 +1,7 @@ +The Plot Driver is the sub system used for creating plots. +
+
PLPLOT: +
This is the default system. All the other options regarding plotting are sub options which are only relevant when you are using this. +
TEXT: +
This will not produce any plots, just textfiles which can be used for plotting with your favorite plotting program. This is particularly relevant if you have some special requirements to the plots. +
diff --git a/share/ert/gui/help/config/keywords/plot_errorbar.html b/share/ert/gui/help/config/keywords/plot_errorbar.html new file mode 100644 index 00000000000..0568bb659da --- /dev/null +++ b/share/ert/gui/help/config/keywords/plot_errorbar.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/plot_errorbar_max.html b/share/ert/gui/help/config/keywords/plot_errorbar_max.html new file mode 100644 index 00000000000..011fec7881a --- /dev/null +++ b/share/ert/gui/help/config/keywords/plot_errorbar_max.html @@ -0,0 +1,5 @@ +When plotting summary vectors for which observations have been 'installed' with the Observation config, ERT will plot the observed values. If the number of observations is less than Errorbar max, ERT will use errorbars to show the observed values, otherwise it will use two dashed lines indicating +/- one standard deviation. +
+To ensure that you always get errorbars you can set Errorbar max to a very large value. On the other hand, setting Errorbar max to zero will ensure that ERT always plots observation uncertainty using dashed lines of +/- one standard deviation. +
+The setting here will also affect the output when you are using the TEXT driver to plot. diff --git a/share/ert/gui/help/config/keywords/plot_height.html b/share/ert/gui/help/config/keywords/plot_height.html new file mode 100644 index 00000000000..e33e7dfe747 --- /dev/null +++ b/share/ert/gui/help/config/keywords/plot_height.html @@ -0,0 +1,2 @@ +When the PLPLOT driver creates a plot file, it will have the height (in pixels) given by the PLOT_HEIGHT keyword. +The default value for PLOT_HEIGHT is 1024 pixels. diff --git a/share/ert/gui/help/config/keywords/plot_path.html b/share/ert/gui/help/config/keywords/plot_path.html new file mode 100644 index 00000000000..f40b2a22c53 --- /dev/null +++ b/share/ert/gui/help/config/keywords/plot_path.html @@ -0,0 +1,3 @@ +The plotting engine creates files with plots, they are stored in a directory. +You can tell what that directory should be. +Observe that the current casename automatically will be appended to the plot path. diff --git a/share/ert/gui/help/config/keywords/plot_refcase.html b/share/ert/gui/help/config/keywords/plot_refcase.html new file mode 100644 index 00000000000..a7d3d67fb98 --- /dev/null +++ b/share/ert/gui/help/config/keywords/plot_refcase.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/plot_width.html b/share/ert/gui/help/config/keywords/plot_width.html new file mode 100644 index 00000000000..281295d50be --- /dev/null +++ b/share/ert/gui/help/config/keywords/plot_width.html @@ -0,0 +1,2 @@ +When the PLPLOT driver creates a plot file, it will have the width (in pixels) given by the PLOT_WIDTH keyword. +The default value for PLOT_WIDTH is 1024 pixels. diff --git a/share/ert/gui/help/config/keywords/pre_clear_runpath.html b/share/ert/gui/help/config/keywords/pre_clear_runpath.html new file mode 100644 index 00000000000..a67c3348618 --- /dev/null +++ b/share/ert/gui/help/config/keywords/pre_clear_runpath.html @@ -0,0 +1 @@ +Select Perform pre clear if you would like the runpath to be cleared before initilaizing. diff --git a/share/ert/gui/help/config/keywords/queue_system.html b/share/ert/gui/help/config/keywords/queue_system.html new file mode 100644 index 00000000000..d23625073f4 --- /dev/null +++ b/share/ert/gui/help/config/keywords/queue_system.html @@ -0,0 +1,8 @@ +Controls where the simulation jobs are executed. +
+
LSF: Submits the jobs to the LSF cluster at your location. +
RSH: Submits the jobs to a defined set of workstations. +
LOCAL: Submits the jobs to your local workstation. +
+
+More information in the wiki. diff --git a/share/ert/gui/help/config/keywords/refcase.html b/share/ert/gui/help/config/keywords/refcase.html new file mode 100644 index 00000000000..519d42c6548 --- /dev/null +++ b/share/ert/gui/help/config/keywords/refcase.html @@ -0,0 +1 @@ +Here you may supply ERT with a reference case, i.e an existing ECLIPSE data file, which can be used as observations. diff --git a/share/ert/gui/help/config/keywords/refcase_list.html b/share/ert/gui/help/config/keywords/refcase_list.html new file mode 100644 index 00000000000..9fa95da1129 --- /dev/null +++ b/share/ert/gui/help/config/keywords/refcase_list.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/rerun_start.html b/share/ert/gui/help/config/keywords/rerun_start.html new file mode 100644 index 00000000000..db11b245c12 --- /dev/null +++ b/share/ert/gui/help/config/keywords/rerun_start.html @@ -0,0 +1 @@ +If using EnKF rerun, select here which time step to start from after each update. diff --git a/share/ert/gui/help/config/keywords/result_path.html b/share/ert/gui/help/config/keywords/result_path.html new file mode 100644 index 00000000000..5ca7fa52415 --- /dev/null +++ b/share/ert/gui/help/config/keywords/result_path.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/rftpath.html b/share/ert/gui/help/config/keywords/rftpath.html new file mode 100644 index 00000000000..ca1df0aa928 --- /dev/null +++ b/share/ert/gui/help/config/keywords/rftpath.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/rsh_command.html b/share/ert/gui/help/config/keywords/rsh_command.html new file mode 100644 index 00000000000..e0e268f88c3 --- /dev/null +++ b/share/ert/gui/help/config/keywords/rsh_command.html @@ -0,0 +1 @@ +The name of the executable used to invoke remote shell operations. Will typically be either rsh or ssh. The command given to RSH Command must either be in PATH or an absolute path. diff --git a/share/ert/gui/help/config/keywords/rsh_host.html b/share/ert/gui/help/config/keywords/rsh_host.html new file mode 100644 index 00000000000..c8413d61802 --- /dev/null +++ b/share/ert/gui/help/config/keywords/rsh_host.html @@ -0,0 +1,6 @@ +To use the RSH queue system you must first set a list of computers which ERT can use for running jobs. The first column takes the computer names, the second takes the number of jobs for each computer.
+ +When using the RSH option, note the following:
    +
  1. You must have passwordless login to the computers listed in the Host List.
  2. +
  3. ERT will not consider the total load on the various computers; if having said a computer can take two jobs, it will get two jobs, irrespective of the existing load.
  4. +
diff --git a/share/ert/gui/help/config/keywords/run_smoother.html b/share/ert/gui/help/config/keywords/run_smoother.html new file mode 100644 index 00000000000..5d61059e823 --- /dev/null +++ b/share/ert/gui/help/config/keywords/run_smoother.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/run_smoother_with_iter.html b/share/ert/gui/help/config/keywords/run_smoother_with_iter.html new file mode 100644 index 00000000000..f71187c49e0 --- /dev/null +++ b/share/ert/gui/help/config/keywords/run_smoother_with_iter.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/run_template.html b/share/ert/gui/help/config/keywords/run_template.html new file mode 100644 index 00000000000..113f2834547 --- /dev/null +++ b/share/ert/gui/help/config/keywords/run_template.html @@ -0,0 +1 @@ +You may use this option to install arbitrary files in the runpath directory. diff --git a/share/ert/gui/help/config/keywords/runpath.html b/share/ert/gui/help/config/keywords/runpath.html new file mode 100644 index 00000000000..7f30da7d36a --- /dev/null +++ b/share/ert/gui/help/config/keywords/runpath.html @@ -0,0 +1,12 @@ +The Runpath should give the name of the folders where the +ECLIPSE simulations are to be executed. It should contain at least one +%d-specifier, which will be replaced by the realization number when +ERT creates the folders.

+ +Optionally, the Runpath can contain two more %d specifers, which will +be replaced by the first and last report step of the simulation. By +default, Runpath is set to "simulations/realization%d".

+ +When running with LSF it is essential that the Runpath points +to a network disk which is accessible with the same path from all the +nodes in the cluster. diff --git a/share/ert/gui/help/config/keywords/runpath_file.html b/share/ert/gui/help/config/keywords/runpath_file.html new file mode 100644 index 00000000000..e5839c9695e --- /dev/null +++ b/share/ert/gui/help/config/keywords/runpath_file.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/schedule_file.html b/share/ert/gui/help/config/keywords/schedule_file.html new file mode 100644 index 00000000000..2ea01382cd2 --- /dev/null +++ b/share/ert/gui/help/config/keywords/schedule_file.html @@ -0,0 +1 @@ +A text file containing the SCHEDULE section of the ECLIPSE data file. The Schedule file is used to control the ECLIPSE simulations. diff --git a/share/ert/gui/help/config/keywords/schedule_prediction_file.html b/share/ert/gui/help/config/keywords/schedule_prediction_file.html new file mode 100644 index 00000000000..9d9ee7bb4bf --- /dev/null +++ b/share/ert/gui/help/config/keywords/schedule_prediction_file.html @@ -0,0 +1 @@ +This is the name of a schedule prediction file. It may contain a %d-specifier to get different files for different members. Observe that the ECLIPSE datafile should include only one schedule file, even if you are doing predictions. diff --git a/share/ert/gui/help/config/keywords/select_case.html b/share/ert/gui/help/config/keywords/select_case.html new file mode 100644 index 00000000000..68f628f3c38 --- /dev/null +++ b/share/ert/gui/help/config/keywords/select_case.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/setenv.html b/share/ert/gui/help/config/keywords/setenv.html new file mode 100644 index 00000000000..b22ce6a2d0a --- /dev/null +++ b/share/ert/gui/help/config/keywords/setenv.html @@ -0,0 +1,4 @@ +You can use setenv to alter the unix environment ERT is running +in. This is probably most relevant for setting up the environment for +the external jobs invoked by ERT. It is possible to use $VAR to refer +to an existing environment variable. diff --git a/share/ert/gui/help/config/keywords/stop_long_running.html b/share/ert/gui/help/config/keywords/stop_long_running.html new file mode 100644 index 00000000000..0abd696273d --- /dev/null +++ b/share/ert/gui/help/config/keywords/stop_long_running.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/summary.html b/share/ert/gui/help/config/keywords/summary.html new file mode 100644 index 00000000000..8083f8729b2 --- /dev/null +++ b/share/ert/gui/help/config/keywords/summary.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/surface.html b/share/ert/gui/help/config/keywords/surface.html new file mode 100644 index 00000000000..c26f69080a9 --- /dev/null +++ b/share/ert/gui/help/config/keywords/surface.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/update_log_path.html b/share/ert/gui/help/config/keywords/update_log_path.html new file mode 100644 index 00000000000..c321b261c8a --- /dev/null +++ b/share/ert/gui/help/config/keywords/update_log_path.html @@ -0,0 +1 @@ +Click here to view help on the wiki page. \ No newline at end of file diff --git a/share/ert/gui/help/config/keywords/update_path.html b/share/ert/gui/help/config/keywords/update_path.html new file mode 100644 index 00000000000..99db43fb904 --- /dev/null +++ b/share/ert/gui/help/config/keywords/update_path.html @@ -0,0 +1 @@ +The Update path option will prepend a new element to an existing PATH variable. diff --git a/share/ert/gui/help/config/run/runpath.html b/share/ert/gui/help/config/run/runpath.html new file mode 100644 index 00000000000..36685bc4661 --- /dev/null +++ b/share/ert/gui/help/config/run/runpath.html @@ -0,0 +1,3 @@ +basic runpath info + +Click here diff --git a/share/ert/gui/help/config/simulation/active_realizations.html b/share/ert/gui/help/config/simulation/active_realizations.html new file mode 100644 index 00000000000..adff4f4ce72 --- /dev/null +++ b/share/ert/gui/help/config/simulation/active_realizations.html @@ -0,0 +1 @@ +These are the realizations that will be used to perform simulations. For example, if "Number of realizations:50 and Active realizations is 0-9", then only realizations 0,1,2,3,...,9 will be used to perform simulations while realizations 10,11, 12,...,49 will be excluded. \ No newline at end of file diff --git a/share/ert/gui/help/config/simulation/analysis_module.html b/share/ert/gui/help/config/simulation/analysis_module.html new file mode 100644 index 00000000000..a0597c48440 --- /dev/null +++ b/share/ert/gui/help/config/simulation/analysis_module.html @@ -0,0 +1 @@ +For running sensitivies you can give the cases descriptive names. diff --git a/share/ert/gui/help/config/simulation/iterated_target_case_format.html b/share/ert/gui/help/config/simulation/iterated_target_case_format.html new file mode 100644 index 00000000000..de622b84188 --- /dev/null +++ b/share/ert/gui/help/config/simulation/iterated_target_case_format.html @@ -0,0 +1 @@ +The "Iterated Ensemble Smoother" creates multiple cases for the different iterations. The case names will follow the specified format. For example, "Target case format: iter_%d" will generate cases with the names iter_0, iter_1, iter_2, iter_3, .... \ No newline at end of file diff --git a/share/ert/gui/help/config/simulation/iteration_weights.html b/share/ert/gui/help/config/simulation/iteration_weights.html new file mode 100644 index 00000000000..7e6a9edf4f9 --- /dev/null +++ b/share/ert/gui/help/config/simulation/iteration_weights.html @@ -0,0 +1,7 @@ +

+ The normalized weights are the actual α values used in the scaling of the standard deviation. +

+

+ Example Custom Relative Weights: '8,4,2,1'. This means Multiple Data Assimilation Ensemble Smoother will half the + weight applied to the Observation Errors from one iteration to the next across 4 iterations. +

diff --git a/share/ert/gui/help/config/simulation/number_of_iterations.html b/share/ert/gui/help/config/simulation/number_of_iterations.html new file mode 100644 index 00000000000..5d649052ffa --- /dev/null +++ b/share/ert/gui/help/config/simulation/number_of_iterations.html @@ -0,0 +1 @@ +Specify the number of times to perform updates/iterations. In general, the more updates the better, however, this could be time consuming. The default value is 4. \ No newline at end of file diff --git a/share/ert/gui/help/config/simulation/sensitivity_parameter_constant_value.html b/share/ert/gui/help/config/simulation/sensitivity_parameter_constant_value.html new file mode 100644 index 00000000000..792bce12bb6 --- /dev/null +++ b/share/ert/gui/help/config/simulation/sensitivity_parameter_constant_value.html @@ -0,0 +1 @@ +Constant value used when the parameter is kept constant in the sensitivity study. diff --git a/share/ert/gui/help/config/simulation/sensitivity_parameter_is_included.html b/share/ert/gui/help/config/simulation/sensitivity_parameter_is_included.html new file mode 100644 index 00000000000..549294c6e86 --- /dev/null +++ b/share/ert/gui/help/config/simulation/sensitivity_parameter_is_included.html @@ -0,0 +1 @@ +Check this box if you want the parameter to be included in the sensitivity study. diff --git a/share/ert/gui/help/config/simulation/sensitivity_target_case_format.html b/share/ert/gui/help/config/simulation/sensitivity_target_case_format.html new file mode 100644 index 00000000000..b602cba8dd4 --- /dev/null +++ b/share/ert/gui/help/config/simulation/sensitivity_target_case_format.html @@ -0,0 +1 @@ +The "Sensitivity Study" creates a case for each parameter that is tested. The case names will follow the specified format. For example, "Target case format: iter_%s" and parameter name trans_mult will generate cases with the names iter_trans_mult. diff --git a/share/ert/gui/help/config/simulation/target_case.html b/share/ert/gui/help/config/simulation/target_case.html new file mode 100644 index 00000000000..b3f6cb2063b --- /dev/null +++ b/share/ert/gui/help/config/simulation/target_case.html @@ -0,0 +1 @@ +This is the name of the case where the results for the updated parameters will be stored \ No newline at end of file diff --git a/share/ert/gui/help/init/case_list.html b/share/ert/gui/help/init/case_list.html new file mode 100644 index 00000000000..21d1bbc77b7 --- /dev/null +++ b/share/ert/gui/help/init/case_list.html @@ -0,0 +1 @@ +List of available cases. diff --git a/share/ert/gui/help/init/current_case_selection.html b/share/ert/gui/help/init/current_case_selection.html new file mode 100644 index 00000000000..fbcc72af963 --- /dev/null +++ b/share/ert/gui/help/init/current_case_selection.html @@ -0,0 +1 @@ +The case to initialize and use for simulation. diff --git a/share/ert/gui/help/init/initialize_from_existing.html b/share/ert/gui/help/init/initialize_from_existing.html new file mode 100644 index 00000000000..c05dccad1ba --- /dev/null +++ b/share/ert/gui/help/init/initialize_from_existing.html @@ -0,0 +1 @@ +Click on this button to perform the initialization process \ No newline at end of file diff --git a/share/ert/gui/help/init/initialize_from_scratch.html b/share/ert/gui/help/init/initialize_from_scratch.html new file mode 100644 index 00000000000..c05dccad1ba --- /dev/null +++ b/share/ert/gui/help/init/initialize_from_scratch.html @@ -0,0 +1 @@ +Click on this button to perform the initialization process \ No newline at end of file diff --git a/share/ert/gui/help/init/select_case_for_info.html b/share/ert/gui/help/init/select_case_for_info.html new file mode 100644 index 00000000000..f27d05c5731 --- /dev/null +++ b/share/ert/gui/help/init/select_case_for_info.html @@ -0,0 +1 @@ +The case you want to view information on \ No newline at end of file diff --git a/share/ert/gui/help/init/select_members.html b/share/ert/gui/help/init/select_members.html new file mode 100644 index 00000000000..616ac700be1 --- /dev/null +++ b/share/ert/gui/help/init/select_members.html @@ -0,0 +1 @@ +Select which members of realizations to initialize \ No newline at end of file diff --git a/share/ert/gui/help/init/select_parameters.html b/share/ert/gui/help/init/select_parameters.html new file mode 100644 index 00000000000..ddb2f22741c --- /dev/null +++ b/share/ert/gui/help/init/select_parameters.html @@ -0,0 +1 @@ +Select which parameters you want to initialize \ No newline at end of file diff --git a/share/ert/gui/help/init/selected_case_info.html b/share/ert/gui/help/init/selected_case_info.html new file mode 100644 index 00000000000..ba7763102b7 --- /dev/null +++ b/share/ert/gui/help/init/selected_case_info.html @@ -0,0 +1 @@ +Information for the selected case \ No newline at end of file diff --git a/share/ert/gui/help/init/source_case.html b/share/ert/gui/help/init/source_case.html new file mode 100644 index 00000000000..5eec505e795 --- /dev/null +++ b/share/ert/gui/help/init/source_case.html @@ -0,0 +1 @@ +The case from which the initial values are copied \ No newline at end of file diff --git a/share/ert/gui/help/run/simulation_mode.html b/share/ert/gui/help/run/simulation_mode.html new file mode 100644 index 00000000000..3be738a9b68 --- /dev/null +++ b/share/ert/gui/help/run/simulation_mode.html @@ -0,0 +1,4 @@ +Here you can select which type of simulation to run.
+1. Ensemble Experiment: this will run simulations without performing any updates on the parameters.
+2. Ensemble Smoother: this will run simulations while performing one update on the parameters by using the ensemble smoother algorithm.
+3. Iterated Ensemble Smoother: this will run simulations while performing multiple updates on the parameters by using the iterated ensemble smoother algorithm. diff --git a/share/ert/gui/help/run/start_simulation.html b/share/ert/gui/help/run/start_simulation.html new file mode 100644 index 00000000000..cd0892535e4 --- /dev/null +++ b/share/ert/gui/help/run/start_simulation.html @@ -0,0 +1 @@ +Click on this button to start running simulations diff --git a/share/ert/gui/help/run/workflow.html b/share/ert/gui/help/run/workflow.html new file mode 100644 index 00000000000..986b0130b18 --- /dev/null +++ b/share/ert/gui/help/run/workflow.html @@ -0,0 +1 @@ +Select the workflow to run diff --git a/share/ert/gui/help/template.html b/share/ert/gui/help/template.html new file mode 100644 index 00000000000..d11776e51a4 --- /dev/null +++ b/share/ert/gui/help/template.html @@ -0,0 +1 @@ +%s \ No newline at end of file diff --git a/share/ert/gui/help/tools/export.html b/share/ert/gui/help/tools/export.html new file mode 100644 index 00000000000..5a3f2061d7f --- /dev/null +++ b/share/ert/gui/help/tools/export.html @@ -0,0 +1 @@ +This will open the "Export Data to Other Formats" dialog window where you can export data stored in ERT to formats usable by other applications diff --git a/share/ert/gui/help/tools/help.html b/share/ert/gui/help/tools/help.html new file mode 100644 index 00000000000..ecbb1741735 --- /dev/null +++ b/share/ert/gui/help/tools/help.html @@ -0,0 +1 @@ +This will open the "Help" window where help information are displayed for various functionality diff --git a/share/ert/gui/help/tools/ide.html b/share/ert/gui/help/tools/ide.html new file mode 100644 index 00000000000..c04f7eca8b1 --- /dev/null +++ b/share/ert/gui/help/tools/ide.html @@ -0,0 +1 @@ +This will open the "Configuration" dialog window where you can set the values for various keywords and parameters in ERT diff --git a/share/ert/gui/help/tools/manage_cases.html b/share/ert/gui/help/tools/manage_cases.html new file mode 100644 index 00000000000..6420f762a18 --- /dev/null +++ b/share/ert/gui/help/tools/manage_cases.html @@ -0,0 +1 @@ +This will open the "Manage Cases" dialog window where you can create, delete, and initialize cases diff --git a/share/ert/gui/help/tools/plot.html b/share/ert/gui/help/tools/plot.html new file mode 100644 index 00000000000..5110a84c4d2 --- /dev/null +++ b/share/ert/gui/help/tools/plot.html @@ -0,0 +1 @@ +This will open the "Plotting" page where you can view the plot profiles for various data types diff --git a/share/ert/gui/help/tools/workflows.html b/share/ert/gui/help/tools/workflows.html new file mode 100644 index 00000000000..dcf597397be --- /dev/null +++ b/share/ert/gui/help/tools/workflows.html @@ -0,0 +1 @@ +This will open the "Workflow" dialog window where you can select and run workflows which you have configured in ERT diff --git a/share/ert/gui/help/welcome_to_ert.html b/share/ert/gui/help/welcome_to_ert.html new file mode 100644 index 00000000000..8d6993316db --- /dev/null +++ b/share/ert/gui/help/welcome_to_ert.html @@ -0,0 +1,17 @@ +
+ _________________________________
+/                                 \
+|    ______   ______   _______    |
+|   |  ____| |  __  \ |__   __|   |
+|   | |__    | |__) |    | |      |
+|   |  __|   |  _  /     | |      |
+|   | |____  | | \ \     | |      |
+|   |______| |_|  \_\    |_|      |
+|                                 |
+|  Ensemble based Reservoir Tool  |
+\_________________________________/
+
+
+

+Welcome to ERT +

diff --git a/share/ert/gui/img/add.png b/share/ert/gui/img/add.png new file mode 100644 index 00000000000..4aadc248dbc Binary files /dev/null and b/share/ert/gui/img/add.png differ diff --git a/share/ert/gui/img/application/window_icon.png b/share/ert/gui/img/application/window_icon.png new file mode 100755 index 00000000000..a2dcd681bf1 Binary files /dev/null and b/share/ert/gui/img/application/window_icon.png differ diff --git a/share/ert/gui/img/application/window_icon_cutout.png b/share/ert/gui/img/application/window_icon_cutout.png new file mode 100755 index 00000000000..2001cb52c8e Binary files /dev/null and b/share/ert/gui/img/application/window_icon_cutout.png differ diff --git a/share/ert/gui/img/calendar.png b/share/ert/gui/img/calendar.png new file mode 100644 index 00000000000..f404e21eb8f Binary files /dev/null and b/share/ert/gui/img/calendar.png differ diff --git a/share/ert/gui/img/checked.png b/share/ert/gui/img/checked.png new file mode 100644 index 00000000000..8ca5395d729 Binary files /dev/null and b/share/ert/gui/img/checked.png differ diff --git a/share/ert/gui/img/copy_from.png b/share/ert/gui/img/copy_from.png new file mode 100644 index 00000000000..df05318c946 Binary files /dev/null and b/share/ert/gui/img/copy_from.png differ diff --git a/share/ert/gui/img/copy_to.png b/share/ert/gui/img/copy_to.png new file mode 100644 index 00000000000..4af7902c39c Binary files /dev/null and b/share/ert/gui/img/copy_to.png differ diff --git a/share/ert/gui/img/ide/chart_curve_add.png b/share/ert/gui/img/ide/chart_curve_add.png new file mode 100755 index 00000000000..45b61c5656a Binary files /dev/null and b/share/ert/gui/img/ide/chart_curve_add.png differ diff --git a/share/ert/gui/img/ide/cog.png b/share/ert/gui/img/ide/cog.png new file mode 100755 index 00000000000..d53ebf1c3d8 Binary files /dev/null and b/share/ert/gui/img/ide/cog.png differ diff --git a/share/ert/gui/img/ide/cog_edit.png b/share/ert/gui/img/ide/cog_edit.png new file mode 100755 index 00000000000..7b7cb83683a Binary files /dev/null and b/share/ert/gui/img/ide/cog_edit.png differ diff --git a/share/ert/gui/img/ide/control_play_blue.png b/share/ert/gui/img/ide/control_play_blue.png new file mode 100755 index 00000000000..a1f7345f371 Binary files /dev/null and b/share/ert/gui/img/ide/control_play_blue.png differ diff --git a/share/ert/gui/img/ide/database_gear.png b/share/ert/gui/img/ide/database_gear.png new file mode 100755 index 00000000000..373a0adb0c3 Binary files /dev/null and b/share/ert/gui/img/ide/database_gear.png differ diff --git a/share/ert/gui/img/ide/disk.png b/share/ert/gui/img/ide/disk.png new file mode 100755 index 00000000000..3a9dcf37ec2 Binary files /dev/null and b/share/ert/gui/img/ide/disk.png differ diff --git a/share/ert/gui/img/ide/gear_in_play.png b/share/ert/gui/img/ide/gear_in_play.png new file mode 100755 index 00000000000..6f73cbb4d94 Binary files /dev/null and b/share/ert/gui/img/ide/gear_in_play.png differ diff --git a/share/ert/gui/img/ide/help.png b/share/ert/gui/img/ide/help.png new file mode 100755 index 00000000000..68f51bac41f Binary files /dev/null and b/share/ert/gui/img/ide/help.png differ diff --git a/share/ert/gui/img/ide/loading.gif b/share/ert/gui/img/ide/loading.gif new file mode 100755 index 00000000000..90f28cbdbb3 Binary files /dev/null and b/share/ert/gui/img/ide/loading.gif differ diff --git a/share/ert/gui/img/ide/plugin.png b/share/ert/gui/img/ide/plugin.png new file mode 100755 index 00000000000..c1ee68def07 Binary files /dev/null and b/share/ert/gui/img/ide/plugin.png differ diff --git a/share/ert/gui/img/ide/readme-fatcow.txt b/share/ert/gui/img/ide/readme-fatcow.txt new file mode 100755 index 00000000000..1f3e7162867 --- /dev/null +++ b/share/ert/gui/img/ide/readme-fatcow.txt @@ -0,0 +1,21 @@ +FatCow Farm Fresh Icons + +03/29/2013, v3.5.0, 9.12 Mb + +© Copyright 2013 FatCow Web Hosting. All rights reserved. + +These icons are licensed under a Creative Commons +Attribution 3.0 License. +http://creativecommons.org/licenses/by/3.0/us/ + +We are unavailable for custom icon design work. But we +plan to draw 500 more metaphors and suggestions are welcome. +https://twitter.com/FatCow http://www.facebook.com/FatCow +or Google+ https://plus.google.com/101826539018374982925 + +------------------------------------------------------------ + +All other trademarks and copyrights +are property of their respective owners. + +------------------------------------------------------------ \ No newline at end of file diff --git a/share/ert/gui/img/ide/save_as.png b/share/ert/gui/img/ide/save_as.png new file mode 100755 index 00000000000..915e280c54f Binary files /dev/null and b/share/ert/gui/img/ide/save_as.png differ diff --git a/share/ert/gui/img/ide/small/add.png b/share/ert/gui/img/ide/small/add.png new file mode 100755 index 00000000000..0ea124a7447 Binary files /dev/null and b/share/ert/gui/img/ide/small/add.png differ diff --git a/share/ert/gui/img/ide/small/chart_curve_go.png b/share/ert/gui/img/ide/small/chart_curve_go.png new file mode 100755 index 00000000000..aa7dc60db96 Binary files /dev/null and b/share/ert/gui/img/ide/small/chart_curve_go.png differ diff --git a/share/ert/gui/img/ide/small/cog_edit.png b/share/ert/gui/img/ide/small/cog_edit.png new file mode 100755 index 00000000000..dc0a35ee5e3 Binary files /dev/null and b/share/ert/gui/img/ide/small/cog_edit.png differ diff --git a/share/ert/gui/img/ide/small/delete.png b/share/ert/gui/img/ide/small/delete.png new file mode 100755 index 00000000000..ace289edd9f Binary files /dev/null and b/share/ert/gui/img/ide/small/delete.png differ diff --git a/share/ert/gui/img/ide/small/folder.png b/share/ert/gui/img/ide/small/folder.png new file mode 100755 index 00000000000..f1ed9abe033 Binary files /dev/null and b/share/ert/gui/img/ide/small/folder.png differ diff --git a/share/ert/gui/img/ide/table_export.png b/share/ert/gui/img/ide/table_export.png new file mode 100755 index 00000000000..a96837ed1e4 Binary files /dev/null and b/share/ert/gui/img/ide/table_export.png differ diff --git a/share/ert/gui/img/ide/table_import.png b/share/ert/gui/img/ide/table_import.png new file mode 100755 index 00000000000..5c25faddacc Binary files /dev/null and b/share/ert/gui/img/ide/table_import.png differ diff --git a/share/ert/gui/img/ide/to_do_list_checked_1.png b/share/ert/gui/img/ide/to_do_list_checked_1.png new file mode 100755 index 00000000000..e0d6504752e Binary files /dev/null and b/share/ert/gui/img/ide/to_do_list_checked_1.png differ diff --git a/share/ert/gui/img/ide/transform_scale.png b/share/ert/gui/img/ide/transform_scale.png new file mode 100755 index 00000000000..a5956ba7a3d Binary files /dev/null and b/share/ert/gui/img/ide/transform_scale.png differ diff --git a/share/ert/gui/img/ide/widgets.png b/share/ert/gui/img/ide/widgets.png new file mode 100755 index 00000000000..73e67cf938f Binary files /dev/null and b/share/ert/gui/img/ide/widgets.png differ diff --git a/share/ert/gui/img/notchecked.png b/share/ert/gui/img/notchecked.png new file mode 100644 index 00000000000..11765214ba8 Binary files /dev/null and b/share/ert/gui/img/notchecked.png differ diff --git a/share/ert/gui/img/page_copy.png b/share/ert/gui/img/page_copy.png new file mode 100644 index 00000000000..a61ec134324 Binary files /dev/null and b/share/ert/gui/img/page_copy.png differ diff --git a/share/ert/gui/img/redo.png b/share/ert/gui/img/redo.png new file mode 100644 index 00000000000..bb7673c827d Binary files /dev/null and b/share/ert/gui/img/redo.png differ diff --git a/share/ert/gui/img/remove.png b/share/ert/gui/img/remove.png new file mode 100644 index 00000000000..5be9040ec94 Binary files /dev/null and b/share/ert/gui/img/remove.png differ diff --git a/share/ert/gui/img/remove_favorite.png b/share/ert/gui/img/remove_favorite.png new file mode 100644 index 00000000000..e716db68caf Binary files /dev/null and b/share/ert/gui/img/remove_favorite.png differ diff --git a/share/ert/gui/img/undo.png b/share/ert/gui/img/undo.png new file mode 100644 index 00000000000..0c196ec096e Binary files /dev/null and b/share/ert/gui/img/undo.png differ diff --git a/share/ert/gui/img/update.png b/share/ert/gui/img/update.png new file mode 100644 index 00000000000..b7639f15582 Binary files /dev/null and b/share/ert/gui/img/update.png differ diff --git a/share/ert/shell_scripts/careful_copy_file b/share/ert/shell_scripts/careful_copy_file new file mode 100755 index 00000000000..fa075e3b944 --- /dev/null +++ b/share/ert/shell_scripts/careful_copy_file @@ -0,0 +1,11 @@ +#!/usr/bin/env python +import sys, os +from res.fm.shell import careful_copy_file + +if __name__ == "__main__": + src = sys.argv[1] + if len(sys.argv) > 2: + target = sys.argv[2] + careful_copy_file(src, target) + else: + careful_copy_file(src) diff --git a/share/ert/shell_scripts/copy_directory b/share/ert/shell_scripts/copy_directory new file mode 100755 index 00000000000..13a4bb7a7c0 --- /dev/null +++ b/share/ert/shell_scripts/copy_directory @@ -0,0 +1,10 @@ +#!/usr/bin/env python +import sys +from res.fm.shell import copy_directory + + +if __name__ == "__main__": + src_path = sys.argv[1] + target_path = sys.argv[2] + + copy_directory(src_path, target_path) diff --git a/share/ert/shell_scripts/copy_file b/share/ert/shell_scripts/copy_file new file mode 100755 index 00000000000..6470ea4d67c --- /dev/null +++ b/share/ert/shell_scripts/copy_file @@ -0,0 +1,11 @@ +#!/usr/bin/env python +import sys +from res.fm.shell import copy_file + +if __name__ == "__main__": + src = sys.argv[1] + if len(sys.argv) > 2: + target = sys.argv[2] + copy_file(src, target) + else: + copy_file(src) diff --git a/share/ert/shell_scripts/delete_directory b/share/ert/shell_scripts/delete_directory new file mode 100755 index 00000000000..2ef395ce1c6 --- /dev/null +++ b/share/ert/shell_scripts/delete_directory @@ -0,0 +1,8 @@ +#!/usr/bin/env python +import sys +from res.fm.shell import delete_directory + + +if __name__ == "__main__": + for d in sys.argv[1:]: + delete_directory(d) diff --git a/share/ert/shell_scripts/delete_file b/share/ert/shell_scripts/delete_file new file mode 100755 index 00000000000..f186fdee500 --- /dev/null +++ b/share/ert/shell_scripts/delete_file @@ -0,0 +1,8 @@ +#!/usr/bin/env python +import sys +from res.fm.shell import delete_file + + +if __name__ == "__main__": + for file in sys.argv[1:]: + delete_file(file) diff --git a/share/ert/shell_scripts/make_directory b/share/ert/shell_scripts/make_directory new file mode 100755 index 00000000000..300678928b6 --- /dev/null +++ b/share/ert/shell_scripts/make_directory @@ -0,0 +1,8 @@ +#!/usr/bin/env python +import sys +from res.fm.shell import mkdir + + +if __name__ == "__main__": + path = sys.argv[1] + mkdir(path) diff --git a/share/ert/shell_scripts/move_file b/share/ert/shell_scripts/move_file new file mode 100755 index 00000000000..5d219acff6c --- /dev/null +++ b/share/ert/shell_scripts/move_file @@ -0,0 +1,10 @@ +#!/usr/bin/env python +import sys +from res.fm.shell import move_file + + +if __name__ == "__main__": + src = sys.argv[1] + target = sys.argv[2] + + move_file(src, target) diff --git a/share/ert/shell_scripts/symlink b/share/ert/shell_scripts/symlink new file mode 100755 index 00000000000..eb954073f2a --- /dev/null +++ b/share/ert/shell_scripts/symlink @@ -0,0 +1,9 @@ +#!/usr/bin/env python +import sys +from res.fm.shell import symlink + + +if __name__ == "__main__": + target = sys.argv[1] + link_name = sys.argv[2] + symlink(target, link_name) diff --git a/share/ert/site-config b/share/ert/site-config new file mode 100644 index 00000000000..3d896bd4bda --- /dev/null +++ b/share/ert/site-config @@ -0,0 +1,15 @@ +-- This is a very basic site config file, mainly used for testing. + +WORKFLOW_JOB_DIRECTORY workflows/jobs/shell +WORKFLOW_JOB_DIRECTORY workflows/jobs/internal/config +WORKFLOW_JOB_DIRECTORY workflows/jobs/internal-gui/config + +JOB_SCRIPT job_dispatch.py +INSTALL_JOB_DIRECTORY forward-models/res +INSTALL_JOB_DIRECTORY forward-models/shell +INSTALL_JOB_DIRECTORY forward-models/templating +INSTALL_JOB_DIRECTORY forward-models/old_style + +QUEUE_OPTION LOCAL MAX_RUNNING 1 + +ANALYSIS_LOAD RML_ENKF rml_enkf.so diff --git a/share/ert/workflows/jobs/internal-gui/config/CREATE_CASE b/share/ert/workflows/jobs/internal-gui/config/CREATE_CASE new file mode 100644 index 00000000000..6875fee2d04 --- /dev/null +++ b/share/ert/workflows/jobs/internal-gui/config/CREATE_CASE @@ -0,0 +1,6 @@ +INTERNAL True +SCRIPT ../scripts/create_case.py +MIN_ARG 1 +MAX_ARG 1 +ARG_TYPE 0 STRING + diff --git a/share/ert/workflows/jobs/internal-gui/config/CSV_EXPORT b/share/ert/workflows/jobs/internal-gui/config/CSV_EXPORT new file mode 100644 index 00000000000..63487d7d9a6 --- /dev/null +++ b/share/ert/workflows/jobs/internal-gui/config/CSV_EXPORT @@ -0,0 +1,9 @@ +INTERNAL True +SCRIPT ../scripts/csv_export.py +MIN_ARG 1 +MAX_ARG 5 +ARG_TYPE 0 STRING +ARG_TYPE 1 STRING +ARG_TYPE 2 STRING +ARG_TYPE 3 BOOL + diff --git a/share/ert/workflows/jobs/internal-gui/config/EXPORT_MISFIT_DATA b/share/ert/workflows/jobs/internal-gui/config/EXPORT_MISFIT_DATA new file mode 100644 index 00000000000..c4aac369aae --- /dev/null +++ b/share/ert/workflows/jobs/internal-gui/config/EXPORT_MISFIT_DATA @@ -0,0 +1,6 @@ +INTERNAL True +SCRIPT ../scripts/export_misfit_data.py +MIN_ARG 0 +MAX_ARG 1 +ARG_TYPE 0 STRING + diff --git a/share/ert/workflows/jobs/internal-gui/config/GEN_DATA_RFT_CSV_EXPORT b/share/ert/workflows/jobs/internal-gui/config/GEN_DATA_RFT_CSV_EXPORT new file mode 100644 index 00000000000..c3012b7dce1 --- /dev/null +++ b/share/ert/workflows/jobs/internal-gui/config/GEN_DATA_RFT_CSV_EXPORT @@ -0,0 +1,8 @@ +INTERNAL True +SCRIPT ../scripts/gen_data_rft_export.py +MIN_ARG 2 +MAX_ARG 5 +ARG_TYPE 0 STRING +ARG_TYPE 1 STRING +ARG_TYPE 2 STRING +ARG_TYPE 3 BOOL diff --git a/share/ert/workflows/jobs/internal-gui/config/INIT_CASE_FROM_EXISTING b/share/ert/workflows/jobs/internal-gui/config/INIT_CASE_FROM_EXISTING new file mode 100644 index 00000000000..4c37a13fca9 --- /dev/null +++ b/share/ert/workflows/jobs/internal-gui/config/INIT_CASE_FROM_EXISTING @@ -0,0 +1,7 @@ +INTERNAL True +SCRIPT ../scripts/init_case_from_existing.py +MIN_ARG 1 +MAX_ARG 2 +ARG_TYPE 0 STRING +ARG_TYPE 1 STRING + diff --git a/share/ert/workflows/jobs/internal-gui/config/SELECT_CASE b/share/ert/workflows/jobs/internal-gui/config/SELECT_CASE new file mode 100644 index 00000000000..bdd49d88882 --- /dev/null +++ b/share/ert/workflows/jobs/internal-gui/config/SELECT_CASE @@ -0,0 +1,6 @@ +INTERNAL True +SCRIPT ../scripts/select_case.py +MIN_ARG 1 +MAX_ARG 1 +ARG_TYPE 0 STRING + diff --git a/share/ert/workflows/jobs/internal-gui/config/UPDATE_RUNPATH_LIST b/share/ert/workflows/jobs/internal-gui/config/UPDATE_RUNPATH_LIST new file mode 100644 index 00000000000..dbaa256d05b --- /dev/null +++ b/share/ert/workflows/jobs/internal-gui/config/UPDATE_RUNPATH_LIST @@ -0,0 +1,6 @@ +INTERNAL True +SCRIPT ../scripts/update_runpath_list.py +MIN_ARG 0 +MAX_ARG 1 +ARG_TYPE 0 STRING + diff --git a/share/ert/workflows/jobs/internal-gui/scripts/create_case.py b/share/ert/workflows/jobs/internal-gui/scripts/create_case.py new file mode 100644 index 00000000000..76da781f4e3 --- /dev/null +++ b/share/ert/workflows/jobs/internal-gui/scripts/create_case.py @@ -0,0 +1,7 @@ +from res.enkf import ErtScript + + +class CreateCaseJob(ErtScript): + def run(self, case_name): + ert = self.ert() + fs = ert.getEnkfFsManager().getFileSystem(case_name) diff --git a/share/ert/workflows/jobs/internal-gui/scripts/csv_export.py b/share/ert/workflows/jobs/internal-gui/scripts/csv_export.py new file mode 100644 index 00000000000..85fdf7d6347 --- /dev/null +++ b/share/ert/workflows/jobs/internal-gui/scripts/csv_export.py @@ -0,0 +1,228 @@ +import os +import re + +import pandas +import sys + +try: + from PyQt4.QtGui import QCheckBox +except ImportError: + from PyQt5.QtWidgets import QCheckBox + + +from res.enkf import ErtPlugin, CancelPluginException +from res.enkf.export import ( + SummaryCollector, + GenKwCollector, + MisfitCollector, + DesignMatrixReader, +) +from ert_gui.ertwidgets.customdialog import CustomDialog +from ert_gui.ertwidgets.listeditbox import ListEditBox +from ert_gui.ertwidgets.models.path_model import PathModel +from ert_gui.ertwidgets.pathchooser import PathChooser + + +class CSVExportJob(ErtPlugin): + """ + Export of summary, misfit, design matrix data and gen kw into a single CSV file. + + The script expects a single argument: + + output_file: this is the path to the file to output the CSV data to + + Optional arguments: + + case_list: a comma separated list of cases to export (no spaces allowed) + if no list is provided the current case is exported + a single * can be used to export all cases + + design_matrix: a path to a file containing the design matrix + + infer_iteration: If True the script will try to infer the iteration number by looking at the suffix of the case name + (i.e. default_2 = iteration 2) + If False the script will use the ordering of the case list: the first item will be iteration 0, + the second item will be iteration 1... + + The script also looks for default values for output path and design matrix path to present in the GUI. These can + be specified with DATA_KW keyword in the config file: + DATA_KW CSV_OUTPUT_PATH + DATA_KW DESIGN_MATRIX_PATH + """ + + INFER_HELP = ( + "" + "If this is checked the iteration number will be inferred from the name i.e.:" + "
    " + "
  • case_name -> iteration: 0
  • " + "
  • case_name_0 -> iteration: 0
  • " + "
  • case_name_2 -> iteration: 2
  • " + "
  • case_0, case_2, case_5 -> iterations: 0, 2, 5
  • " + "
" + "Leave this unchecked to set iteration number to the order of the listed cases:" + "
  • case_0, case_2, case_5 -> iterations: 0, 1, 2
" + "
" + "" + ) + + def getName(self): + return "CSV Export" + + def getDescription(self): + return "Export GenKW, design matrix, misfit data and summary data into a single CSV file." + + def inferIterationNumber(self, case_name): + pattern = re.compile("_([0-9]+$)") + match = pattern.search(case_name) + + if match is not None: + return int(match.group(1)) + return 0 + + def run( + self, + output_file, + case_list=None, + design_matrix_path=None, + infer_iteration=True, + drop_const_cols=False, + ): + cases = [] + + if case_list is not None: + if case_list.strip() == "*": + cases = self.getAllCaseList() + else: + cases = case_list.split(",") + + if case_list is None or len(cases) == 0: + cases = [self.ert().getEnkfFsManager().getCurrentFileSystem().getCaseName()] + + if design_matrix_path is not None: + if not os.path.exists(design_matrix_path): + raise UserWarning("The design matrix file does not exists!") + + if not os.path.isfile(design_matrix_path): + raise UserWarning("The design matrix is not a file!") + + data = pandas.DataFrame() + + for index, case in enumerate(cases): + case = case.strip() + + if not self.ert().getEnkfFsManager().caseExists(case): + raise UserWarning("The case '%s' does not exist!" % case) + + if not self.ert().getEnkfFsManager().caseHasData(case): + raise UserWarning("The case '%s' does not have any data!" % case) + + if infer_iteration: + iteration_number = self.inferIterationNumber(case) + else: + iteration_number = index + + case_data = GenKwCollector.loadAllGenKwData(self.ert(), case) + + if design_matrix_path is not None: + design_matrix_data = DesignMatrixReader.loadDesignMatrix( + design_matrix_path + ) + if not design_matrix_data.empty: + case_data = case_data.join(design_matrix_data, how="outer") + + misfit_data = MisfitCollector.loadAllMisfitData(self.ert(), case) + if not misfit_data.empty: + case_data = case_data.join(misfit_data, how="outer") + + summary_data = SummaryCollector.loadAllSummaryData(self.ert(), case) + if not summary_data.empty: + case_data = case_data.join(summary_data, how="outer") + else: + case_data["Date"] = None + case_data.set_index(["Date"], append=True, inplace=True) + + case_data["Iteration"] = iteration_number + case_data["Case"] = case + case_data.set_index(["Case", "Iteration"], append=True, inplace=True) + + data = pandas.concat([data, case_data]) + + data = data.reorder_levels(["Realization", "Iteration", "Date", "Case"]) + if drop_const_cols: + data = data.loc[:, (data != data.iloc[0]).any()] + + data.to_csv(output_file) + + export_info = "Exported %d rows and %d columns to %s." % ( + len(data.index), + len(data.columns), + output_file, + ) + return export_info + + def getArguments(self, parent=None): + description = "The CSV export requires some information before it starts:" + dialog = CustomDialog("CSV Export", description, parent) + + default_csv_output_path = self.getDataKWValue( + "CSV_OUTPUT_PATH", default="output.csv" + ) + output_path_model = PathModel(default_csv_output_path) + output_path_chooser = PathChooser(output_path_model) + + design_matrix_default = self.getDataKWValue("DESIGN_MATRIX_PATH", default="") + design_matrix_path_model = PathModel( + design_matrix_default, is_required=False, must_exist=True + ) + design_matrix_path_chooser = PathChooser(design_matrix_path_model) + + list_edit = ListEditBox(self.getAllCaseList()) + + infer_iteration_check = QCheckBox() + infer_iteration_check.setChecked(True) + infer_iteration_check.setToolTip(CSVExportJob.INFER_HELP) + + drop_const_columns_check = QCheckBox() + drop_const_columns_check.setChecked(False) + drop_const_columns_check.setToolTip( + "If checked, exclude columns whose value is the same for every entry" + ) + + dialog.addLabeledOption("Output file path", output_path_chooser) + dialog.addLabeledOption("Design Matrix path", design_matrix_path_chooser) + dialog.addLabeledOption("List of cases to export", list_edit) + dialog.addLabeledOption("Infer iteration number", infer_iteration_check) + dialog.addLabeledOption("Drop constant columns", drop_const_columns_check) + + dialog.addButtons() + + success = dialog.showAndTell() + + if success: + design_matrix_path = design_matrix_path_model.getPath() + if design_matrix_path.strip() == "": + design_matrix_path = None + + case_list = ",".join(list_edit.getItems()) + + return [ + output_path_model.getPath(), + case_list, + design_matrix_path, + infer_iteration_check.isChecked(), + drop_const_columns_check.isChecked(), + ] + + raise CancelPluginException("User cancelled!") + + def getDataKWValue(self, name, default): + data_kw = self.ert().getDataKW() + if name in data_kw: + return data_kw[data_kw.indexForKey(name)][1] + return default + + def getAllCaseList(self): + fs_manager = self.ert().getEnkfFsManager() + all_case_list = fs_manager.getCaseList() + all_case_list = [case for case in all_case_list if fs_manager.caseHasData(case)] + return all_case_list diff --git a/share/ert/workflows/jobs/internal-gui/scripts/export_misfit_data.py b/share/ert/workflows/jobs/internal-gui/scripts/export_misfit_data.py new file mode 100644 index 00000000000..0b9abef197f --- /dev/null +++ b/share/ert/workflows/jobs/internal-gui/scripts/export_misfit_data.py @@ -0,0 +1,77 @@ +from collections import OrderedDict +import os +from res.enkf import ErtScript, RealizationStateEnum +from ecl.util.util import BoolVector + +""" +This job exports misfit data into a chosen file or to the default gen_kw export file (parameters.txt) +""" + + +class ExportMisfitDataJob(ErtScript): + def run(self, target_file=None): + ert = self.ert() + fs = ert.getEnkfFsManager().getCurrentFileSystem() + + if target_file is None: + target_file = ert.getModelConfig().getGenKWExportFile() + + runpath_list = ert.getHookManager().getRunpathList() + + active_list = self.createActiveList(fs) + + for runpath_node in runpath_list: + if runpath_node.realization in active_list: + + if not os.path.exists(runpath_node.runpath): + os.makedirs(runpath_node.runpath) + + target_path = os.path.join(runpath_node.runpath, target_file) + + parameters = self.parseTargetFile(target_path) + + misfit_sum = 0.0 + for obs_vector in ert.getObservations(): + misfit = obs_vector.getTotalChi2(fs, runpath_node.realization) + + key = "MISFIT:%s" % obs_vector.getObservationKey() + parameters[key] = misfit + + misfit_sum += misfit + + parameters["MISFIT:TOTAL"] = misfit_sum + + self.dumpParametersToTargetFile(parameters, target_path) + + def parseTargetFile(self, target_path): + parameters = OrderedDict() + + if os.path.exists(target_path) and os.path.isfile(target_path): + with open(target_path, "r") as input_file: + lines = input_file.readlines() + + for line in lines: + tokens = line.split() + + if len(tokens) == 2: + parameters[tokens[0]] = tokens[1] + else: + raise UserWarning( + "The file '%s' contains errors. Expected format for each line: KEY VALUE" + % target_path + ) + + return parameters + + def dumpParametersToTargetFile(self, parameters, target_path): + with open(target_path, "w") as output: + for key in parameters: + output.write("%s %s\n" % (key, parameters[key])) + + def createActiveList(self, fs): + state_map = fs.getStateMap() + ens_mask = BoolVector(False, self.ert().getEnsembleSize()) + state_map.selectMatching(ens_mask, RealizationStateEnum.STATE_HAS_DATA) + active_list = BoolVector.createActiveList(ens_mask) + + return active_list diff --git a/share/ert/workflows/jobs/internal-gui/scripts/gen_data_rft_export.py b/share/ert/workflows/jobs/internal-gui/scripts/gen_data_rft_export.py new file mode 100644 index 00000000000..49a1329a5ea --- /dev/null +++ b/share/ert/workflows/jobs/internal-gui/scripts/gen_data_rft_export.py @@ -0,0 +1,246 @@ +import os +import re + +import numpy +import pandas +import sys + +try: + from PyQt4.QtGui import QCheckBox +except ImportError: + from PyQt5.QtWidgets import QCheckBox + +from ecl.rft import WellTrajectory +from res.enkf import ErtPlugin, CancelPluginException +from res.enkf import RealizationStateEnum +from res.enkf.enums import EnkfObservationImplementationType +from res.enkf.export import GenDataCollector, ArgLoader +from ert_gui.ertwidgets.customdialog import CustomDialog +from ert_gui.ertwidgets.listeditbox import ListEditBox +from ert_gui.ertwidgets.models.path_model import PathModel +from ert_gui.ertwidgets.pathchooser import PathChooser + + +class GenDataRFTCSVExportJob(ErtPlugin): + """Export of GEN_DATA based rfts to a CSV file. The csv file will in + addition contain the depth as duplicated seperate row. + + The script expects four arguments: + + output_file: this is the path to the file to output the CSV data to + + key: this is the ert GEN_DATA key used for this particular RFT. + + report_step: This is the report step configured in the ert + configuration file for this RFT. + + trajectory_file: This is the the file containing the + + Optional arguments: + + case_list: a comma separated list of cases to export (no spaces allowed) + if no list is provided the current case is exported + + infer_iteration: If True the script will try to infer the iteration number by looking at the suffix of the case name + (i.e. default_2 = iteration 2) + If False the script will use the ordering of the case list: the first item will be iteration 0, + the second item will be iteration 1... + """ + + INFER_HELP = ( + "" + "If this is checked the iteration number will be inferred from the name i.e.:" + "
    " + "
  • case_name -> iteration: 0
  • " + "
  • case_name_0 -> iteration: 0
  • " + "
  • case_name_2 -> iteration: 2
  • " + "
  • case_0, case_2, case_5 -> iterations: 0, 2, 5
  • " + "
" + "Leave this unchecked to set iteration number to the order of the listed cases:" + "
  • case_0, case_2, case_5 -> iterations: 0, 1, 2
" + "
" + "" + ) + + def getName(self): + return "GEN_DATA RFT CSV Export" + + def getDescription(self): + return "Export gen_data RFT results into a single CSV file." + + def inferIterationNumber(self, case_name): + pattern = re.compile("_([0-9]+$)") + match = pattern.search(case_name) + + if match is not None: + return int(match.group(1)) + return 0 + + def run( + self, + output_file, + trajectory_path, + case_list=None, + infer_iteration=True, + drop_const_cols=False, + ): + """The run method will export the RFT's for all wells and all cases. + + The successfull operation of this method hinges on two naming + conventions: + + 1. All the GEN_DATA RFT observations have key RFT_$WELL + 2. The trajectory files are in $trajectory_path/$WELL.txt or $trajectory_path/$WELL_R.txt + + """ + + wells = set() + obs_pattern = "RFT_*" + enkf_obs = self.ert().getObservations() + obs_keys = enkf_obs.getMatchingKeys( + obs_pattern, obs_type=EnkfObservationImplementationType.GEN_OBS + ) + + cases = [] + if case_list is not None: + cases = case_list.split(",") + + if case_list is None or len(cases) == 0: + cases = [self.ert().getEnkfFsManager().getCurrentFileSystem().getCaseName()] + + data_frame = pandas.DataFrame() + for index, case in enumerate(cases): + case = case.strip() + case_frame = pandas.DataFrame() + + if not self.ert().getEnkfFsManager().caseExists(case): + raise UserWarning("The case '%s' does not exist!" % case) + + if not self.ert().getEnkfFsManager().caseHasData(case): + raise UserWarning("The case '%s' does not have any data!" % case) + + if infer_iteration: + iteration_number = self.inferIterationNumber(case) + else: + iteration_number = index + + for obs_key in obs_keys: + well = obs_key.replace("RFT_", "") + wells.add(well) + obs_vector = enkf_obs[obs_key] + data_key = obs_vector.getDataKey() + report_step = obs_vector.activeStep() + obs_node = obs_vector.getNode(report_step) + + rft_data = GenDataCollector.loadGenData( + self.ert(), case, data_key, report_step + ) + fs = self.ert().getEnkfFsManager().getFileSystem(case) + realizations = fs.realizationList(RealizationStateEnum.STATE_HAS_DATA) + + # Trajectory + trajectory_file = os.path.join(trajectory_path, "%s.txt" % well) + if not os.path.isfile(trajectory_file): + trajectory_file = os.path.join(trajectory_path, "%s_R.txt" % well) + + trajectory = WellTrajectory(trajectory_file) + arg = ArgLoader.load( + trajectory_file, column_names=["utm_x", "utm_y", "md", "tvd"] + ) + tvd_arg = arg["tvd"] + data_size = len(tvd_arg) + + # Observations + obs = numpy.empty(shape=(data_size, 2), dtype=numpy.float64) + obs.fill(numpy.nan) + for obs_index in range(len(obs_node)): + data_index = obs_node.getDataIndex(obs_index) + value = obs_node.getValue(obs_index) + std = obs_node.getStandardDeviation(obs_index) + obs[data_index, 0] = value + obs[data_index, 1] = std + + for iens in realizations: + realization_frame = pandas.DataFrame( + data={ + "TVD": tvd_arg, + "Pressure": rft_data[iens], + "ObsValue": obs[:, 0], + "ObsStd": obs[:, 1], + }, + columns=["TVD", "Pressure", "ObsValue", "ObsStd"], + ) + + realization_frame["Realization"] = iens + realization_frame["Well"] = well + realization_frame["Case"] = case + realization_frame["Iteration"] = iteration_number + + case_frame = case_frame.append(realization_frame) + + data_frame = data_frame.append(case_frame) + + data_frame.set_index(["Realization", "Well", "Case", "Iteration"], inplace=True) + if drop_const_cols: + data_frame = data_frame.loc[:, (data_frame != data_frame.iloc[0]).any()] + + data_frame.to_csv(output_file) + export_info = "Exported RFT information for wells: %s to: %s " % ( + ", ".join(list(wells)), + output_file, + ) + return export_info + + def getArguments(self, parent=None): + description = ( + "The GEN_DATA RFT CSV export requires some information before it starts:" + ) + dialog = CustomDialog("Robust CSV Export", description, parent) + + output_path_model = PathModel("output.csv") + output_path_chooser = PathChooser(output_path_model) + + trajectory_model = PathModel( + "wellpath", must_be_a_directory=True, must_be_a_file=False, must_exist=True + ) + trajectory_chooser = PathChooser(trajectory_model) + + fs_manager = self.ert().getEnkfFsManager() + all_case_list = fs_manager.getCaseList() + all_case_list = [case for case in all_case_list if fs_manager.caseHasData(case)] + list_edit = ListEditBox(all_case_list) + + infer_iteration_check = QCheckBox() + infer_iteration_check.setChecked(True) + infer_iteration_check.setToolTip(GenDataRFTCSVExportJob.INFER_HELP) + + drop_const_columns_check = QCheckBox() + drop_const_columns_check.setChecked(False) + drop_const_columns_check.setToolTip( + "If checked, exclude columns whose value is the same for every entry" + ) + + dialog.addLabeledOption("Output file path", output_path_chooser) + dialog.addLabeledOption("Trajectory file", trajectory_chooser) + dialog.addLabeledOption("List of cases to export", list_edit) + dialog.addLabeledOption("Infer iteration number", infer_iteration_check) + dialog.addLabeledOption("Drop constant columns", drop_const_columns_check) + + dialog.addButtons() + + success = dialog.showAndTell() + + if success: + case_list = ",".join(list_edit.getItems()) + try: + return [ + output_path_model.getPath(), + trajectory_model.getPath(), + case_list, + infer_iteration_check.isChecked(), + drop_const_columns_check.isChecked(), + ] + except ValueError: + pass + + raise CancelPluginException("User cancelled!") diff --git a/share/ert/workflows/jobs/internal-gui/scripts/init_case_from_existing.py b/share/ert/workflows/jobs/internal-gui/scripts/init_case_from_existing.py new file mode 100644 index 00000000000..ec716ab2e66 --- /dev/null +++ b/share/ert/workflows/jobs/internal-gui/scripts/init_case_from_existing.py @@ -0,0 +1,15 @@ +from res.enkf import ErtScript + + +class InitCaseFromExistingJob(ErtScript): + def run(self, source_case, target_case=None): + ert = self.ert() + source_fs = ert.getEnkfFsManager().getFileSystem(source_case) + + if target_case is None: + target_fs = ert.getEnkfFsManager().getCurrentFileSystem() + + else: + target_fs = ert.getEnkfFsManager().getFileSystem(target_case) + + ert.getEnkfFsManager().initializeCaseFromExisting(source_fs, 0, target_fs) diff --git a/share/ert/workflows/jobs/internal-gui/scripts/select_case.py b/share/ert/workflows/jobs/internal-gui/scripts/select_case.py new file mode 100644 index 00000000000..644eb1820c2 --- /dev/null +++ b/share/ert/workflows/jobs/internal-gui/scripts/select_case.py @@ -0,0 +1,8 @@ +from res.enkf import ErtScript + + +class SelectCaseJob(ErtScript): + def run(self, case_name): + ert = self.ert() + fs = ert.getEnkfFsManager().getFileSystem(case_name) + ert.getEnkfFsManager().switchFileSystem(fs) diff --git a/share/ert/workflows/jobs/internal-gui/scripts/update_runpath_list.py b/share/ert/workflows/jobs/internal-gui/scripts/update_runpath_list.py new file mode 100644 index 00000000000..2c6a2a5bf82 --- /dev/null +++ b/share/ert/workflows/jobs/internal-gui/scripts/update_runpath_list.py @@ -0,0 +1,38 @@ +from res.enkf import ErtScript + +""" +This job is useful if you are running a workflow that requires the hook_manager runpath_list +to be populated but your are not running any simulations. +""" + + +class UpdateRunpathListJob(ErtScript): + def run(self): + ert = self.ert() + + realization_count = ert.getEnsembleSize() + iteration = 0 + + ecl_config = ert.eclConfig() + model_config = ert.getModelConfig() + basename_fmt = ecl_config.getEclBase() + runpath_fmt = model_config.getRunpathAsString() + hook_manager = ert.getHookManager() + + runpath_list = hook_manager.getRunpathList() + + runpath_list.clear() + + for realization_number in range(realization_count): + + if basename_fmt is not None: + basename = basename_fmt % realization_number + else: + raise UserWarning("EclBase not set!") + + if model_config.runpathRequiresIterations(): + runpath = runpath_fmt % (realization_number, iteration) + else: + runpath = runpath_fmt % realization_number + + runpath_list.add(realization_number, iteration, runpath, basename) diff --git a/share/ert/workflows/jobs/internal-tui/config/CREATE_CASE b/share/ert/workflows/jobs/internal-tui/config/CREATE_CASE new file mode 100644 index 00000000000..e59df565d80 --- /dev/null +++ b/share/ert/workflows/jobs/internal-tui/config/CREATE_CASE @@ -0,0 +1,6 @@ +INTERNAL True +FUNCTION enkf_main_create_case_JOB +MIN_ARG 1 +MAX_ARG 1 +ARG_TYPE 0 STRING + diff --git a/share/ert/workflows/jobs/internal-tui/config/INIT_CASE_FROM_EXISTING b/share/ert/workflows/jobs/internal-tui/config/INIT_CASE_FROM_EXISTING new file mode 100644 index 00000000000..f9a1ba7a5e0 --- /dev/null +++ b/share/ert/workflows/jobs/internal-tui/config/INIT_CASE_FROM_EXISTING @@ -0,0 +1,7 @@ +INTERNAL True +FUNCTION enkf_main_init_case_from_existing_JOB +MIN_ARG 1 +MAX_ARG 2 +ARG_TYPE 0 STRING +ARG_TYPE 1 STRING + diff --git a/share/ert/workflows/jobs/internal-tui/config/PLOT_ALL_SUMMARY b/share/ert/workflows/jobs/internal-tui/config/PLOT_ALL_SUMMARY new file mode 100644 index 00000000000..76ce548a86c --- /dev/null +++ b/share/ert/workflows/jobs/internal-tui/config/PLOT_ALL_SUMMARY @@ -0,0 +1,5 @@ +INTERNAL True +FUNCTION enkf_tui_plot_all_summary_JOB +MIN_ARG 0 + + diff --git a/share/ert/workflows/jobs/internal-tui/config/SELECT_CASE b/share/ert/workflows/jobs/internal-tui/config/SELECT_CASE new file mode 100644 index 00000000000..3d77f143695 --- /dev/null +++ b/share/ert/workflows/jobs/internal-tui/config/SELECT_CASE @@ -0,0 +1,6 @@ +INTERNAL True +FUNCTION enkf_main_select_case_JOB +MIN_ARG 1 +MAX_ARG 1 +ARG_TYPE 0 STRING + diff --git a/share/ert/workflows/jobs/internal/config/ANALYSIS_UPDATE b/share/ert/workflows/jobs/internal/config/ANALYSIS_UPDATE new file mode 100644 index 00000000000..186f1d71223 --- /dev/null +++ b/share/ert/workflows/jobs/internal/config/ANALYSIS_UPDATE @@ -0,0 +1,6 @@ +INTERNAL True +FUNCTION enkf_main_analysis_update_JOB +MIN_ARG 2 +MAX_ARG 2 +ARG_TYPE 0 STRING +ARG_TYPE 1 STRING diff --git a/share/ert/workflows/jobs/internal/config/DATA_RANKING b/share/ert/workflows/jobs/internal/config/DATA_RANKING new file mode 100644 index 00000000000..514aac9c2ef --- /dev/null +++ b/share/ert/workflows/jobs/internal/config/DATA_RANKING @@ -0,0 +1,8 @@ +INTERNAL True +FUNCTION enkf_main_rank_on_data_JOB +MIN_ARG 3 +MAX_ARG 4 +ARG_TYPE 0 STRING +ARG_TYPE 1 STRING +ARG_TYPE 2 BOOL +ARG_TYPE 3 INT diff --git a/share/ert/workflows/jobs/internal/config/EXIT_ERT b/share/ert/workflows/jobs/internal/config/EXIT_ERT new file mode 100644 index 00000000000..ef2e4b9d1e6 --- /dev/null +++ b/share/ert/workflows/jobs/internal/config/EXIT_ERT @@ -0,0 +1,4 @@ +INTERNAL True +FUNCTION enkf_main_exit_JOB +MIN_ARG 0 +MAX_ARG 0 diff --git a/share/ert/workflows/jobs/internal/config/EXPORT_FIELD b/share/ert/workflows/jobs/internal/config/EXPORT_FIELD new file mode 100644 index 00000000000..ca01bc461a9 --- /dev/null +++ b/share/ert/workflows/jobs/internal/config/EXPORT_FIELD @@ -0,0 +1,7 @@ +INTERNAL True +FUNCTION enkf_main_export_field_JOB +MIN_ARG 3 +ARG_TYPE 0 STRING +ARG_TYPE 1 STRING +ARG_TYPE 2 INT +ARG_TYPE 3 STRING diff --git a/share/ert/workflows/jobs/internal/config/EXPORT_FIELD_ECL_GRDECL b/share/ert/workflows/jobs/internal/config/EXPORT_FIELD_ECL_GRDECL new file mode 100644 index 00000000000..75e0020f1e3 --- /dev/null +++ b/share/ert/workflows/jobs/internal/config/EXPORT_FIELD_ECL_GRDECL @@ -0,0 +1,7 @@ +INTERNAL True +FUNCTION enkf_main_export_field_to_ECL_JOB +MIN_ARG 3 +ARG_TYPE 0 STRING +ARG_TYPE 1 STRING +ARG_TYPE 2 INT +ARG_TYPE 3 STRING diff --git a/share/ert/workflows/jobs/internal/config/EXPORT_FIELD_RMS_ROFF b/share/ert/workflows/jobs/internal/config/EXPORT_FIELD_RMS_ROFF new file mode 100644 index 00000000000..cfc6a2f4f58 --- /dev/null +++ b/share/ert/workflows/jobs/internal/config/EXPORT_FIELD_RMS_ROFF @@ -0,0 +1,7 @@ +INTERNAL True +FUNCTION enkf_main_export_field_to_RMS_JOB +MIN_ARG 3 +ARG_TYPE 0 STRING +ARG_TYPE 1 STRING +ARG_TYPE 2 INT +ARG_TYPE 3 STRING diff --git a/share/ert/workflows/jobs/internal/config/EXPORT_RANKING b/share/ert/workflows/jobs/internal/config/EXPORT_RANKING new file mode 100644 index 00000000000..0ef06146091 --- /dev/null +++ b/share/ert/workflows/jobs/internal/config/EXPORT_RANKING @@ -0,0 +1,7 @@ +INTERNAL True +FUNCTION enkf_main_export_ranking_JOB +MIN_ARG 2 +MAX_ARG 2 +ARG_TYPE 0 STRING +ARG_TYPE 1 STRING + diff --git a/share/ert/workflows/jobs/internal/config/EXPORT_RUNPATH b/share/ert/workflows/jobs/internal/config/EXPORT_RUNPATH new file mode 100644 index 00000000000..753f9e9c9fd --- /dev/null +++ b/share/ert/workflows/jobs/internal/config/EXPORT_RUNPATH @@ -0,0 +1,3 @@ +INTERNAL True +FUNCTION enkf_main_export_runpath_file_JOB + diff --git a/share/ert/workflows/jobs/internal/config/INIT_MISFIT_TABLE b/share/ert/workflows/jobs/internal/config/INIT_MISFIT_TABLE new file mode 100644 index 00000000000..a798874f592 --- /dev/null +++ b/share/ert/workflows/jobs/internal/config/INIT_MISFIT_TABLE @@ -0,0 +1,4 @@ +INTERNAL True +FUNCTION enkf_main_init_misfit_table_JOB +MIN_ARG 0 + diff --git a/share/ert/workflows/jobs/internal/config/LOAD_RESULTS b/share/ert/workflows/jobs/internal/config/LOAD_RESULTS new file mode 100644 index 00000000000..8be9a06ecd9 --- /dev/null +++ b/share/ert/workflows/jobs/internal/config/LOAD_RESULTS @@ -0,0 +1,3 @@ +INTERNAL True +FUNCTION enkf_main_load_results_JOB + diff --git a/share/ert/workflows/jobs/internal/config/LOAD_RESULTS_ITER b/share/ert/workflows/jobs/internal/config/LOAD_RESULTS_ITER new file mode 100644 index 00000000000..e81802426ef --- /dev/null +++ b/share/ert/workflows/jobs/internal/config/LOAD_RESULTS_ITER @@ -0,0 +1,5 @@ +INTERNAL True +FUNCTION enkf_main_load_results_iter_JOB +MIN_ARG 1 +ARG_TYPE 0 INT + diff --git a/share/ert/workflows/jobs/internal/config/OBSERVATION_RANKING b/share/ert/workflows/jobs/internal/config/OBSERVATION_RANKING new file mode 100644 index 00000000000..23b8402bb68 --- /dev/null +++ b/share/ert/workflows/jobs/internal/config/OBSERVATION_RANKING @@ -0,0 +1,4 @@ +INTERNAL True +FUNCTION enkf_main_rank_on_observations_JOB +MIN_ARG 1 +ARG_TYPE 0 STRING diff --git a/share/ert/workflows/jobs/internal/config/PRE_SIMULATION_COPY b/share/ert/workflows/jobs/internal/config/PRE_SIMULATION_COPY new file mode 100644 index 00000000000..55768d16057 --- /dev/null +++ b/share/ert/workflows/jobs/internal/config/PRE_SIMULATION_COPY @@ -0,0 +1,6 @@ +INTERNAL True +FUNCTION enkf_main_pre_simulation_copy_JOB +MIN_ARG 1 +MAX_ARG 2 +ARG_TYPE 0 STRING +ARG_TYPE 1 STRING diff --git a/share/ert/workflows/jobs/internal/config/STD_SCALE_CORRELATED_OBS b/share/ert/workflows/jobs/internal/config/STD_SCALE_CORRELATED_OBS new file mode 100644 index 00000000000..10baf753132 --- /dev/null +++ b/share/ert/workflows/jobs/internal/config/STD_SCALE_CORRELATED_OBS @@ -0,0 +1,3 @@ +INTERNAL True +FUNCTION enkf_main_std_scale_correlated_obs_JOB + diff --git a/share/ert/workflows/jobs/shell/CAREFUL_COPY_FILE b/share/ert/workflows/jobs/shell/CAREFUL_COPY_FILE new file mode 100644 index 00000000000..cddfcdf26bc --- /dev/null +++ b/share/ert/workflows/jobs/shell/CAREFUL_COPY_FILE @@ -0,0 +1,2 @@ +INTERNAL FALSE +EXECUTABLE ../../../shell_scripts/careful_copy_file diff --git a/share/ert/workflows/jobs/shell/COPY_DIRECTORY b/share/ert/workflows/jobs/shell/COPY_DIRECTORY new file mode 100644 index 00000000000..43cb048e36f --- /dev/null +++ b/share/ert/workflows/jobs/shell/COPY_DIRECTORY @@ -0,0 +1,2 @@ +INTERNAL FALSE +EXECUTABLE ../../../shell_scripts/copy_directory diff --git a/share/ert/workflows/jobs/shell/COPY_FILE b/share/ert/workflows/jobs/shell/COPY_FILE new file mode 100644 index 00000000000..803e8bb01de --- /dev/null +++ b/share/ert/workflows/jobs/shell/COPY_FILE @@ -0,0 +1,2 @@ +INTERNAL FALSE +EXECUTABLE ../../../shell_scripts/copy_file diff --git a/share/ert/workflows/jobs/shell/DELETE_DIRECTORY b/share/ert/workflows/jobs/shell/DELETE_DIRECTORY new file mode 100644 index 00000000000..1ac2a1edf5a --- /dev/null +++ b/share/ert/workflows/jobs/shell/DELETE_DIRECTORY @@ -0,0 +1,2 @@ +INTERNAL FALSE +EXECUTABLE ../../../shell_scripts/delete_directory diff --git a/share/ert/workflows/jobs/shell/DELETE_FILE b/share/ert/workflows/jobs/shell/DELETE_FILE new file mode 100644 index 00000000000..0f5f60d20cb --- /dev/null +++ b/share/ert/workflows/jobs/shell/DELETE_FILE @@ -0,0 +1,2 @@ +INTERNAL FALSE +EXECUTABLE ../../../shell_scripts/delete_file diff --git a/share/ert/workflows/jobs/shell/MAKE_DIRECTORY b/share/ert/workflows/jobs/shell/MAKE_DIRECTORY new file mode 100644 index 00000000000..c0e2b31d8b5 --- /dev/null +++ b/share/ert/workflows/jobs/shell/MAKE_DIRECTORY @@ -0,0 +1,2 @@ +INTERNAL FALSE +EXECUTABLE ../../../shell_scripts/make_directory diff --git a/share/ert/workflows/jobs/shell/MAKE_SYMLINK b/share/ert/workflows/jobs/shell/MAKE_SYMLINK new file mode 100644 index 00000000000..d719e392b3e --- /dev/null +++ b/share/ert/workflows/jobs/shell/MAKE_SYMLINK @@ -0,0 +1,2 @@ +INTERNAL FALSE +EXECUTABLE ../../../shell_scripts/symlink diff --git a/share/ert/workflows/jobs/shell/MOVE_FILE b/share/ert/workflows/jobs/shell/MOVE_FILE new file mode 100644 index 00000000000..8ce642d1f57 --- /dev/null +++ b/share/ert/workflows/jobs/shell/MOVE_FILE @@ -0,0 +1,2 @@ +INTERNAL FALSE +EXECUTABLE ../../../shell_scripts/move_file diff --git a/share/ert/workflows/jobs/shell/SYMLINK b/share/ert/workflows/jobs/shell/SYMLINK new file mode 100644 index 00000000000..d719e392b3e --- /dev/null +++ b/share/ert/workflows/jobs/shell/SYMLINK @@ -0,0 +1,2 @@ +INTERNAL FALSE +EXECUTABLE ../../../shell_scripts/symlink