From 67919219e91cce25a984099ab2a7e6e8cef45794 Mon Sep 17 00:00:00 2001 From: "Oddvar Lia (ST MSU GEO)" Date: Tue, 24 Oct 2023 09:17:59 +0200 Subject: [PATCH] Replaced gaussianfft as python module for simulation of gaussian fields with gstools. --- test_requirements.txt | 2 + tests/jobs/localisation/example_case/README | 104 ++-- .../example_case/example_case.yml | 44 -- .../example_case/scripts/FM_SIM_FIELD | 2 +- .../example_case/scripts/common_functions.py | 511 +++++++++--------- .../scripts/import_field_parameters.py | 97 +--- .../scripts/import_field_parameters_local.py | 95 +--- .../scripts/import_fields_to_rms.py | 89 +++ .../import_upscaled_field_parameters.py | 94 +--- .../import_upscaled_field_parameters_local.py | 96 +--- .../example_case/scripts/init_test_case.py | 111 ++-- .../example_case/scripts/sim_fields.py | 66 +-- .../localisation/example_case/sim_field.ert | 3 +- .../example_case/sim_field_local.ert | 3 +- 14 files changed, 499 insertions(+), 818 deletions(-) delete mode 100644 tests/jobs/localisation/example_case/example_case.yml create mode 100644 tests/jobs/localisation/example_case/scripts/import_fields_to_rms.py diff --git a/test_requirements.txt b/test_requirements.txt index 8bfdb402f..58c9bd095 100644 --- a/test_requirements.txt +++ b/test_requirements.txt @@ -8,3 +8,5 @@ pytest-httpserver pytest-snapshot rstcheck-core<=1.0.3 xlwt +gstools +gaussianfft diff --git a/tests/jobs/localisation/example_case/README b/tests/jobs/localisation/example_case/README index 1c4dcfa8d..00e07805a 100644 --- a/tests/jobs/localisation/example_case/README +++ b/tests/jobs/localisation/example_case/README @@ -1,60 +1,44 @@ -Directory for test case for non-adaptive localisation (LOCALISATION_JOB) -Main components: -ERT config file: sim_field.ert - -The ERT model depends on: -scripts/sim_fields.py -scripts/common_functions.py - -Example of optional input config scripts to -scripts/init_test_case.py and scripts/sim_fields.py: -example_case.yml -modify_variogram.yml - -ERT keyword input: -GRID: Input grid is made by scripts/init_test_case.py -OBS_CONFIG: Observations are extracted from an upscaled realization made by scripts/init_test_case.py -GEN_DATA : Prediction of observables made by forward model SIM_FIELD which runs scripts/sim_fields.py -FIELD: Prior realizations of field made by forward model SIM_FIELD which runs scripts/sim_fields.py - -Other ERT input files: -time_map.txt -localisation.wf - -Other files: -randomseeds.txt - Not used by ERT, but by sim_fields.py - This file can be generated by scripts/init_test_case.py -UpscaledGrid.EGRID - Not used by ERT, but by scripts/sim_fields.py - This file can be generated by scripts/init_test_case.py - -Typical workflow: -1. Prepare ERT config input by running scripts/init_test_case.py. If run without any arguments, a set of hardcoded - default settings are used. Optionally add one argument (a yml file where the settings for the test case can be modified. - See example of two such yml files: example_case.yml containing all available settings and modify_variogram.yml - where only a few parameters are modified and the rest of the settings are using default values. - Edit the input yml file to change settings. -2. Directories for observations are created automatically according to the default (or modified) settings. -3. Make the directory init_files if not existing. -4. Run the script init_test_case.py located under scripts from the example_case dirctory. - Run it with or without one argument which is a specification of settings. - Default settings are used for settings not specified. The file can be ommited if one want to use the default - settings for all parameters. Example file containing all possible settings is the file example_case.yml -5. If non-default settings are specified and the init_test_case.py is run with an yml file as argument, also the ERT forward - model SIM_FIELD need the same input as the third argument for the script sim_fields.py - The first two arguments are iteration and realisation number. Edit FM_SIM_FIELD and specify wanted settings file (yml file). -5. Activate/not activate localisation in ERT config file (HOOK_WORKFLOW LOAD_WORKFLOW for localisation) -6. Now ready to run ERT. - - - -What the script sim_fields.py do: -1. Get iteration and realisation number from ERT using ERT environment variables _ERT_ITERATION_NUMBER and _ERT_REALIZATION_NUMBER - If running with old komodo version, the script will require iteration and realisation_number as command line input and optionally the yml settings file. -2. If iteration = 0 then - simulate field and export to file to be used in FIELD keyword in ERT config file. - upscale field and optionally export to file for QC purpose - else - import updated field from ERT - upscale field and optionally export file for QC purpose -3. Extract predicted values of observables from upscaled field (values for some selected grid cells related to the upscaled grid) and write GEN_DATA files -4. Optionally write some files for QC purpose. +## Directory for Test Case for Non-Adaptive Localisation (`LOCALISATION_JOB`) +​ +### Main Components: +​ +- **ERT config file:** `sim_field.ert` +​ + The ERT model depends on: + - `scripts/sim_fields.py` + - `scripts/common_functions.py` +​ +​ +- **ERT Keyword Input:** + - **GRID:** Input grid is made by `scripts/init_test_case.py` + - **OBS_CONFIG:** Observations are extracted from an upscaled realization made by `scripts/init_test_case.py` + - **GEN_DATA:** Prediction of observables made by forward model `SIM_FIELD` which runs `scripts/sim_fields.py` + - **FIELD:** Prior realizations of field made by forward model `SIM_FIELD` which runs `scripts/sim_fields.py` +​ +- **Other ERT Input Files:** + - `time_map.txt` + - `localisation.wf` +​ +- **Other Files:** + - `randomseeds.txt`: Not used by ERT, but by `sim_fields.py`. This file can be generated by `scripts/init_test_case.py` + - `UpscaledGrid.EGRID`: Not used by ERT, but by `sim_fields.py`. This file can be generated by `scripts/init_test_case.py` +​ +### Typical Workflow: +​ +1. **Preparation:** Prepare ERT config input by running `scripts/init_test_case.py`. +2. Directories for observations are created automatically according to the default settings. +3. Make the directory `init_files` if not existing. +4. Activate or deactivate localisation in ERT config file (`HOOK_WORKFLOW LOAD_WORKFLOW` for localisation). +5. Now ready to run ERT. +​ +### What the Script `sim_fields.py` Does: +​ +1. Get iteration and realisation number from ERT using ERT environment variables `_ERT_ITERATION_NUMBER` and `_ERT_REALIZATION_NUMBER`. If running with an old Komodo version, the script will require iteration and realisation_number as command line input. +2. If iteration = 0, then: + - Simulate field and export to file to be used in `FIELD` keyword in ERT config file. + - Upscale field and optionally export to file for QC purpose. + Else: + - Import updated field from ERT. + - Upscale field and optionally export file for QC purpose. +3. Extract predicted values of observables from upscaled field (values for selected grid cells related to the upscaled grid) and write `GEN_DATA` files. +4. Optionally write some files for QC purpose.Directory for test case for non-adaptive localisation (LOCALISATION_JOB) diff --git a/tests/jobs/localisation/example_case/example_case.yml b/tests/jobs/localisation/example_case/example_case.yml deleted file mode 100644 index f0418b998..000000000 --- a/tests/jobs/localisation/example_case/example_case.yml +++ /dev/null @@ -1,44 +0,0 @@ -settings: - grid_size: - xsize: 7500.0 - ysize: 12500.0 - zsize: 50.0 - use_eclipse_grid_index_origo: "eclipse" - - field: - name: "FIELDPARAM" - initial_file_name: "init_files/FieldParam.roff" - updated_file_name: "FieldParam.roff" - seed_file: "randomseeds.txt" - variogram: "gaussian" - correlation_range: [5000.0, 3000.0, 2.0] - correlation_azimuth: 45.0 - correlation_dip: 0.0 - trend_use: False - trend_params: [ 1.0, -1.0 ] - trend_relstd: 0.15 - grid_dimension: [150, 250, 1] - grid_file_name: "GRID.EGRID" - - response: - grid_dimension: [15, 25, 1] - upscaled_file_name: "Upscaled.roff" - grid_file_name: "UpscaleGrid.EGRID" - response_function: "average" - gen_data_file_name: "UpscaledField_0.txt" - calculate_all_cells: True - - observation: - directory: "observations" - file_name: "observations.obs" - data_dir: "obs_data" - 3D_param_file_name: "init_files/UpscaledObsField.roff" - rel_error: 0.10 - min_abs_error: 0.01 - selected_grid_cells: - - [5, 10, 1] - - [10, 20, 1] - - optional: - write_upscaled_to_file: True - write_obs_pred_diff_field_file: True diff --git a/tests/jobs/localisation/example_case/scripts/FM_SIM_FIELD b/tests/jobs/localisation/example_case/scripts/FM_SIM_FIELD index 1611d40ff..668923bcf 100644 --- a/tests/jobs/localisation/example_case/scripts/FM_SIM_FIELD +++ b/tests/jobs/localisation/example_case/scripts/FM_SIM_FIELD @@ -1,6 +1,6 @@ EXECUTABLE ./sim_fields.py -ARGLIST +ARGLIST STDERR sim_fields.stderr STDOUT sim_fields.stdout diff --git a/tests/jobs/localisation/example_case/scripts/common_functions.py b/tests/jobs/localisation/example_case/scripts/common_functions.py index 45d63bd7b..df484c3ab 100644 --- a/tests/jobs/localisation/example_case/scripts/common_functions.py +++ b/tests/jobs/localisation/example_case/scripts/common_functions.py @@ -1,222 +1,152 @@ """ Common functions used by the scripts: init_test_case.py and sim_field.py """ -import copy +import math +from dataclasses import dataclass +from typing import Tuple +import gstools as gs import numpy as np -import yaml - -import xtgeo # isort: skip -import gaussianfft as sim # isort: skip - - -# NOTE: xtgeo MUST be imported BEFORE gaussianfft -# The reason is that xtgeo has functions importing roxar API and -# even though this is not used, the code will crash with core dump -# since Roxar API and gaussianfft both use Boost to wrap C++ code -# into python functions but Roxar API and gaussianfft uses two -# slightly different versions of Boost. -# The gaussianfft module uses version 1.76 which is newer than -# version 1.74 from Roxar API (and indirectly xtgeo) -# which may explain why it works importing gaussianfft after -# xtgeo (and Roxar API module roxar). We don't know exactly the reason -# for the core dump with wrong sequence of the import's, but probably -# it is due to some initialization related to the Boost library and -# Boost version 1.74.0 is not compatible with any initialization done by -# version 1.76. -# -# The message related to Boost and RMS (and then indirectly also xtgeo) -# from Aspentech Support is this: -# "The current version of boost is 1.74.0 and this version has been used -# since RMS 12.1, and is still used in RMS V14.0.1 and V14.1. -# Boost version 1.81.0 will be available in version 14.2 or version 15. -# Thomas also write "Last time I checked, boost did not provide any -# compatibility guarantees, so it's not expected to work if you -# mix two different boost versions in the same process -# by loading python modules into RMS that uses other versions." - -# The current combination of gaussianfft and xtgeo (or RMS Roxar API) -# will work if xtgeo is imported first. But this may change later. -# The plan for gaussianfft is to ensure correct sequence of import by -# importing xtgeo (and when running from RMS, also roxar) in -# gaussianfft before calling any functions from the gaussianfft module. -# In this case it should work for the end user regardless of which sequence -# it is imported. The most robust solution would be to generate gaussianfft -# to use exactly the same. - +import xtgeo # pylint: disable=missing-function-docstring, too-many-locals, invalid-name -# pylint: disable=bare-except, raise-missing-from -# pylint: disable= redefined-outer-name, too-many-nested-blocks +# pylint: disable=raise-missing-from +# pylint: disable=too-many-nested-blocks -def specify_settings(spec_dict=None, yml_file_name=None): +# Settings for the test case in the following dataclasses +@dataclass +class GridSize: """ - grid_size - length, width, thickness of a box containing the field - Same size is used for both fine scale grid with - the simulated field and the coarse scale grid - containing upscaled values of the simulated field. - - field - Define the dimension (number of grid cells) for fine scale grid, - name of output files and specification of model parameters for - simulation of gaussian field with option to use linear trend. - Relative standard deviation specify standard deviation of - gaussian residual field relative to the trends span of value - (max trend value - min trend value) - - response - Specify the coarse grid dimensions, name of file and type - of average operation to calculated upscaled values that - are predictions of observations of the same grid cells. - Which cell indices are observed are specified in - observation settings. - - observation - Specify name of files for generated observations - and also which grid cells from coarse grid is used - as observables. - (Cells that have values that are used as observations) - - optional - Specify if some optional files should be - written or not (for QC purpose) + Length, width, thickness of a box containing the field + Same size is used for both fine scale grid with + the simulated field and the coarse scale grid + containing upscaled values of the simulated field. """ - default_settings = { - "grid_size": { - "xsize": 7500.0, - "ysize": 12500.0, - "zsize": 50.0, - "use_eclipse_grid_index_origo": True, - }, - "field": { - "name": "FIELDPARAM", - "initial_file_name": "init_files/FieldParam.roff", - "updated_file_name": "FieldParam.roff", - "seed_file": "randomseeds.txt", - "variogram": "gaussian", - "correlation_range": [5000.0, 3000.0, 2.0], - "correlation_azimuth": 45.0, - "correlation_dip": 0.0, - "trend_use": 0, - "trend_params": [1.0, -1.0], - "trend_relstd": 0.05, - "grid_dimension": [150, 250, 1], - "grid_file_name": "GRID.EGRID", - }, - "response": { - "grid_dimension": [15, 25, 1], - "upscaled_file_name": "Upscaled.roff", - "grid_file_name": "UpscaleGrid.EGRID", - "response_function": "average", - "gen_data_file_name": "UpscaledField_0.txt", - "calculate_all_cells": True, - }, - "observation": { - "directory": "observations", - "file_name": "observations.obs", - "data_dir": "obs_data", - "3D_param_file_name": "init_files/UpscaledObsField.roff", - "rel_error": 0.10, - "min_abs_error": 0.01, - "selected_grid_cells": [ - [5, 10, 1], - [10, 20, 1], - ], - # "selected_grid_cells":[ - # [15, 1, 1], - # [15, 25, 1], - # [ 1, 25, 1], - # [ 1, 1, 1], - # [ 3, 3, 1], - # [ 5, 23, 1], - # [11, 4, 1], - # [ 3, 12, 1], - # [13, 18, 1], - # ], - }, - "optional": { - "write_upscaled_to_file": True, - "write_obs_pred_diff_field_file": True, - }, - } - - settings = copy.deepcopy(default_settings) - if spec_dict is not None: - main_key = "settings" - if main_key not in spec_dict: - raise KeyError(f"Missing main keyword {main_key} in file: {yml_file_name}") - settings_yml_dict = spec_dict[main_key] - valid_keys1 = default_settings.keys() - for key1 in settings_yml_dict.keys(): - if key1 in valid_keys1: - valid_keys2 = default_settings[key1].keys() - for key2 in settings_yml_dict[key1].keys(): - if key2 in valid_keys2: - print( - f"Modifying default setting for keyword '{key2}' " - f"under '{key1}' in file {yml_file_name} " - ) - settings[key1][key2] = settings_yml_dict[key1][key2] - else: - raise KeyError( - f"Unknown keyword '{key2}' specified under keyword '{key1}'" - f" in {yml_file_name}" - ) - else: - raise KeyError( - f"Unknown keyword '{key1}' specified under keyword '{main_key}'" - f" in {yml_file_name}" - ) - return settings - - -# def update_settings(settings_dict, spec_dict, main_key, sub_keys): -# if main_key in spec_dict: -# main_key_dict = spec_dict[main_key] -# print(f"main_key_dict: {main_key_dict}") -# if main_key_dict is not None: -# print(f"sub_keys: {sub_keys}") -# for key in sub_keys: -# if key in main_key_dict: -# print(f"Settings updated for:{main_key} with sub key: {key} ") -# settings_dict[key] = main_key_dict[key] -# return settings_dict - - -def read_test_config(config_file_name): - if config_file_name is None: - spec_dict = None - else: - words = config_file_name.split(".") - if words[-1].upper() not in ["YML", "YAML"]: - raise IOError( - f"Expecting input yaml file as last argument for script {__file__}. " - f"Got the file {config_file_name} " - ) - with open(config_file_name, "r", encoding="utf-8") as yml_file: - spec_dict = yaml.safe_load(yml_file) - return specify_settings(spec_dict, config_file_name) + xsize: float = 7500.0 + ysize: float = 12500.0 + zsize: float = 50.0 + use_eclipse_grid_index_origo: bool = True + + +@dataclass +class Field: + """ + Define the dimension (number of grid cells) for fine scale grid, + name of output files and specification of model parameters for + simulation of gaussian field with option to use linear trend. + Relative standard deviation specify standard deviation of + gaussian residual field relative to the trends span of value + (max trend value - min trend value) + """ + + # pylint: disable=too-many-instance-attributes + name: str = "FIELDPARAM" + algorithm: str = "gstools" + initial_file_name: str = "init_files/FieldParam.roff" + updated_file_name: str = "FieldParam.roff" + seed_file: str = "randomseeds.txt" + variogram: str = "exponential" + correlation_range: Tuple[float] = (5000.0, 3000.0, 2.0) + correlation_azimuth: float = 45.0 + correlation_dip: float = 0.0 + correlation_exponent: float = 1.9 + trend_use: bool = False + trend_params: Tuple[float] = (1.0, -1.0) + trend_relstd: float = 0.05 + grid_dimension: Tuple[int] = (150, 250, 1) + grid_file_name: str = "GRID.EGRID" + + +@dataclass +class Response: + """ + Specify the coarse grid dimensions, name of file and type + of average operation to calculated upscaled values that + are predictions of observations of the same grid cells. + Which cell indices are observed are specified in + observation settings. + """ + + grid_dimension: Tuple[int] = (15, 25, 1) + upscaled_file_name: str = "Upscaled.roff" + grid_file_name: str = "UpscaleGrid.EGRID" + response_function: str = "average" + gen_data_file_name: str = "UpscaledField_0.txt" + calculate_all_cells: bool = True + + +@dataclass +class Observation: + """ + Specify name of files for generated observations + and also which grid cells from coarse grid is used + as observables. (Cells that have values that are used as observations) + """ + + directory: str = "observations" + file_name: str = "observations.obs" + data_dir: str = "obs_data" + param_file_name: str = "init_files/UpscaledObsField.roff" + rel_error: float = 0.10 + min_abs_error: float = 0.01 + selected_grid_cells: Tuple[Tuple[int]] = ((5, 10, 1), (10, 20, 1)) + + +@dataclass +class Optional: + """ + Specify if some optional files should be + written or not (for QC purpose). + """ + + write_upscaled_to_file: bool = True + write_obs_pred_diff_field_file: bool = True + + +@dataclass +class Settings: + """ + Settings for the test case + """ + + grid_size: GridSize = GridSize() + field: Field = Field() + response: Response = Response() + observation: Observation = Observation() + optional: Optional = Optional() + +settings = Settings() -def generate_field_and_upscale(settings, real_number): - seed_file_name = settings["field"]["seed_file"] - relative_std = settings["field"]["trend_relstd"] - use_trend = settings["field"]["trend_use"] +def generate_field_and_upscale(real_number): + seed_file_name = settings.field.seed_file + relative_std = settings.field.trend_relstd + use_trend = settings.field.trend_use + algorithm_method = settings.field.algorithm start_seed = get_seed(seed_file_name, real_number) - residual_field = simulate_field(settings, start_seed) + if algorithm_method == "gstools": + print(f"Use algorithm: {algorithm_method}") + residual_field = simulate_field_using_gstools(start_seed) + else: + print("Use algorithm: gaussianfft") + residual_field = simulate_field(start_seed) if use_trend == 1: - trend_field = trend(settings) + trend_field = trend() field3D = trend_field + relative_std * residual_field + else: field3D = residual_field # Write field parameter for fine scale grid - field_object = export_field(settings, field3D) + field_object = export_field(field3D) field_values = field_object.values # Calculate upscaled values for selected coarse grid cells upscaled_values = upscaling( field_values, - settings, write_field=True, iteration=0, ) @@ -228,18 +158,25 @@ def get_seed(seed_file_name, r_number): lines = file.readlines() try: seed_value = int(lines[r_number - 1]) - except: # noqa: E722 - raise IOError("Seed value not found for realization {r_number} ") + except IndexError as exc: + raise IOError("Seed value not found for realization {r_number} ") from exc + except ValueError as exc: + raise IOError( + "Invalid seed value in file for realization{r_number}" + ) from exc return seed_value -def upscaling(field_values, settings, write_field=True, iteration=0): - response_function_name = settings["response"]["response_function"] - upscaled_file_name = settings["response"]["upscaled_file_name"] - NX, NY, NZ = settings["response"]["grid_dimension"] - calculate_all = settings["response"]["calculate_all_cells"] +def upscaling(field_values, write_field=True, iteration=0): + """ + Calculate upscaled values and optionally write upscaled values to file. + Return upscaled values + """ + response_function_name = settings.response.response_function - coarse_cell_index_list = settings["observation"]["selected_grid_cells"] + NX, NY, NZ = settings.response.grid_dimension + calculate_all = settings.response.calculate_all_cells + coarse_cell_index_list = settings.observation.selected_grid_cells upscaled_values = np.zeros((NX, NY, NZ), dtype=np.float32, order="F") upscaled_values[:, :, :] = -999 @@ -248,11 +185,12 @@ def upscaling(field_values, settings, write_field=True, iteration=0): field_values, coarse_cell_index_list, upscaled_values, use_all=calculate_all ) - if iteration == 0: - upscaled_file_name = "init_files/" + upscaled_file_name - if write_field: + upscaled_file_name = settings.response.upscaled_file_name + if iteration == 0: + upscaled_file_name = "init_files/" + upscaled_file_name write_upscaled_field(upscaled_values, upscaled_file_name) + return upscaled_values @@ -361,30 +299,15 @@ def upscale_average( return upscaled_values -def write_prediction_gen_data(upscaled_values, settings): - cell_indx_list = settings["observation"]["selected_grid_cells"] - response_file_name = settings["response"]["gen_data_file_name"] - print(f"Write GEN_DATA file with prediction of observations: {response_file_name}") - with open(response_file_name, "w", encoding="utf8") as file: - # NOTE: The sequence of values must be the same as for the observations - for indices in cell_indx_list: - Iindx = indices[0] - 1 - Jindx = indices[1] - 1 - Kindx = indices[2] - 1 - value = upscaled_values[Iindx, Jindx, Kindx] - print(f"Prediction of obs for {Iindx+1},{Jindx+1},{Kindx+1}: {value}") - file.write(f"{value}\n") - - -def trend(settings): +def trend(): """ Return 3D numpy array with values following a linear trend scaled to take values between 0 and 1. """ - nx, ny, nz = settings["field"]["grid_dimension"] - xsize = settings["grid_size"]["xsize"] - ysize = settings["grid_size"]["ysize"] - a, b = settings["field"]["trend_params"] + nx, ny, nz = settings.field.grid_dimension + xsize = settings.grid_size.xsize + ysize = settings.grid_size.ysize + a, b = settings.field.trend_params x0 = 0.0 y0 = 0.0 @@ -409,22 +332,25 @@ def trend(settings): return val_normalized -def simulate_field(settings, start_seed): - # pylint: disable=no-member, - variogram_name = settings["field"]["variogram"] - corr_ranges = settings["field"]["correlation_range"] +def simulate_field(start_seed): + # pylint: disable=no-member,import-outside-toplevel + + import gaussianfft as sim # isort: skip + + variogram_name = settings.field.variogram + corr_ranges = settings.field.correlation_range + azimuth = settings.field.correlation_azimuth + dip = settings.field.correlation_dip + alpha = settings.field.correlation_exponent + nx, ny, nz = settings.field.grid_dimension + xsize = settings.grid_size.xsize + ysize = settings.grid_size.ysize + zsize = settings.grid_size.zsize + xrange = corr_ranges[0] yrange = corr_ranges[1] zrange = corr_ranges[2] - azimuth = settings["field"]["correlation_azimuth"] - dip = settings["field"]["correlation_dip"] - - nx, ny, nz = settings["field"]["grid_dimension"] - xsize = settings["grid_size"]["xsize"] - ysize = settings["grid_size"]["ysize"] - zsize = settings["grid_size"]["zsize"] - dx = xsize / nx dy = ysize / ny dz = zsize / nz @@ -439,6 +365,7 @@ def simulate_field(settings, start_seed): depth_range=zrange, azimuth=azimuth - 90, dip=dip, + power=alpha, ) print(f"Simulate field with size: nx={nx},ny={ny} ") @@ -447,11 +374,82 @@ def simulate_field(settings, start_seed): return field -def export_field(settings, field3D): - # Export initial ensemble field - nx, ny, nz = settings["field"]["grid_dimension"] - field_name = settings["field"]["name"] - field_file_name = settings["field"]["initial_file_name"] +def simulate_field_using_gstools(start_seed): + # pylint: disable=no-member, + + variogram_name = settings.field.variogram + corr_ranges = settings.field.correlation_range + azimuth = settings.field.correlation_azimuth + xrange = corr_ranges[0] + yrange = corr_ranges[1] + zrange = corr_ranges[2] + + nx, ny, nz = settings.field.grid_dimension + xsize = settings.grid_size.xsize + ysize = settings.grid_size.ysize + zsize = settings.grid_size.zsize + + dx = xsize / nx + dy = ysize / ny + dz = zsize / nz + + x = np.arange(0.5 * dx, xsize, dx) + y = np.arange(0.5 * dy, ysize, dy) + z = np.arange(0.5 * dz, zsize, dz) + # Rescale factor is set to: + # sqrt(3.0) for gaussian correlation functions, + # 3.0 for exponetial correlation function, + # pow(3.0, 1/alpha) for general exponential correlation function + # with exponent alpha + # to ensure the correlation function have the same definition of correlation + # lenght as is used in RMS and gaussianfft algorithm. + print(f"Variogram name: {variogram_name}") + if variogram_name.upper() == "GAUSSIAN": + model = gs.Gaussian( + dim=3, + var=1.0, + len_scale=[xrange, yrange, zrange], + angles=np.pi * (0.5 - azimuth / 180.0), + rescale=math.sqrt(3), + ) + elif variogram_name.upper() == "EXPONENTIAL": + model = gs.Exponential( + dim=3, + var=1.0, + len_scale=[xrange, yrange, zrange], + angles=np.pi * (0.5 - azimuth / 180.0), + rescale=3, + ) + else: + raise ValueError(f"Unknown variogram type: {variogram_name} ") + + print(f"Start seed: {start_seed}") + print(f"Simulate field with size: nx={nx},ny={ny} nz={nz} ") + srf = gs.SRF(model, seed=start_seed) + field_srf = srf.structured([x, y, z], store="Field") + # print(f"Field: {srf.field_names} ") + # print(f"Field shape: {srf.field_shape} ") + # print(f"Field type name: {srf.name} ") + # print(f"Field nugget: {srf.nugget} ") + # print(f"Field opt arg: {srf.opt_arg}") + field = field_srf.reshape((nx, ny, nz), order="F") + if settings.grid_size.use_eclipse_grid_index_origo: + field_result = np.zeros((nx, ny, nz), dtype=np.float32) + j_indices = -np.arange(ny) + ny - 1 + field_result[:, j_indices, :] = field[:, :, :] + return field_result + + return field + + +def export_field(field3D): + """ + Export initial realization of field to roff format + """ + + nx, ny, nz = settings.field.grid_dimension + field_name = settings.field.name + field_file_name = settings.field.initial_file_name field_object = xtgeo.grid3d.GridProperty( ncol=nx, nrow=ny, nlay=nz, values=field3D, discrete=False, name=field_name @@ -462,23 +460,37 @@ def export_field(settings, field3D): return field_object -def read_field_from_file(settings): - input_file_name = settings["field"]["updated_file_name"] - name = settings["field"]["name"] +def read_field_from_file(): + """ + Read field from roff formatted file and return xtgeo property object + """ + + input_file_name = settings.field.updated_file_name + name = settings.field.name field_object = xtgeo.gridproperty_from_file( input_file_name, fformat="roff", name=name ) return field_object -def read_obs_field_from_file(settings): - input_file_name = settings["observation"]["3D_param_file_name"] +def read_obs_field_from_file(): + """ + Read field parameter containing parameter with observed values + for selected grid cells + """ + + input_file_name = settings.observation.param_file_name obs_field_object = xtgeo.gridproperty_from_file(input_file_name, fformat="roff") return obs_field_object -def read_upscaled_field_from_file(settings, iteration): - input_file_name = settings["response"]["upscaled_file_name"] +def read_upscaled_field_from_file(iteration): + """ + Read upscaled field parameter either from initial ensemble or updated ensemble. + Return xtgeo property object + """ + + input_file_name = settings.response.upscaled_file_name if iteration == 0: filename = "init_files/" + input_file_name else: @@ -488,6 +500,11 @@ def read_upscaled_field_from_file(settings, iteration): def write_obs_pred_diff_field(upscaled_field_object, observation_field_object): + """ + Get xtgeo property objects for predicted values for observables + and observation values. + Write file with difference as roff formatted file. + """ nx, ny, nz = upscaled_field_object.dimensions values_diff = upscaled_field_object.values - observation_field_object.values diff --git a/tests/jobs/localisation/example_case/scripts/import_field_parameters.py b/tests/jobs/localisation/example_case/scripts/import_field_parameters.py index 3b2ff6472..959fcfa1b 100644 --- a/tests/jobs/localisation/example_case/scripts/import_field_parameters.py +++ b/tests/jobs/localisation/example_case/scripts/import_field_parameters.py @@ -1,103 +1,18 @@ """ Import field parameters into RMS (Must be included as python job -in RMS workflow and edited to fit your scratch directory) +in RMS workflow and edited to fit your scratch directory). +Variable project is defined when running within RMS, +but not outside since it refers to Roxar API """ -from pathlib import Path - -import xtgeo +# pylint: disable=import-error, undefined-variable +from import_fields_to_rms import import_from_scratch_directory SCRATCH = "/scratch/fmu/olia/sim_field/" -# SCRATCH = "/scratch/fmu/olia/sim_field_local/" CASE_NAME = "original" -# CASE_NAME = "local" - - -# pylint: disable=undefined-variable, bare-except,duplicate-code PRJ = project # noqa: 821 - GRID_MODEL_NAME = "GRID" FIELD_NAMES = [ "FieldParam", ] - -def main(): - """ - Import files with initial ensemble and updated fields into RMS project - """ - xtgeo.grid_from_roxar(PRJ, GRID_MODEL_NAME, PRJ.current_realisation) - - path = Path(SCRATCH) - if not path.exists(): - raise IOError(f"File path: {SCRATCH} does not exist. ") - - real = PRJ.current_realisation - print("\n") - print(f"Realization: {real} ") - for name in FIELD_NAMES: - for iteration in [0, 3]: - print(f"Iteration: {iteration}") - if iteration == 0: - name_with_iter = name + "_" + CASE_NAME + "_" + str(iteration) - path = ( - SCRATCH - + "realization-" - + str(real) - + "/iter-" - + str(iteration) - + "/init_files/" - ) - file_name = path + name + ".roff" - print(f"File name: {file_name} ") - - try: - property0 = xtgeo.gridproperty_from_file(file_name, "roff") - print( - f"Import property {property0.name} from file" - f" {file_name} into {name_with_iter} " - ) - property0.to_roxar( - PRJ, GRID_MODEL_NAME, name_with_iter, realisation=real - ) - except: # noqa: E722 - print(f"Skip realization: {real} for iteration: {iteration} ") - elif iteration == 3: - name_with_iter = name + "_" + CASE_NAME + "_" + str(iteration) - path = ( - SCRATCH - + "realization-" - + str(real) - + "/iter-" - + str(iteration) - + "/" - ) - file_name = path + name + ".roff" - print(f"File name: {file_name} ") - - try: - property3 = xtgeo.gridproperty_from_file(file_name, "roff") - print( - f"Import property {property3.name} for iteration {iteration} " - f"from file {file_name} into {name_with_iter} " - ) - property3.to_roxar( - PRJ, GRID_MODEL_NAME, name_with_iter, realisation=real - ) - except: # noqa: E722 - print(f"Skip realization: {real} for iteration: {iteration} ") - try: - diff_property = property0 - diff_property.values = property3.values - property0.values - name_diff = name + "_" + CASE_NAME + "_diff" - print( - f"Calculate difference between iteration 3 and 0: {name_diff}" - ) - diff_property.to_roxar( - PRJ, GRID_MODEL_NAME, name_diff, realisation=real - ) - except: # noqa: E722 - print(f"Skip difference for realisation: {real} ") - - -if __name__ == "__main__": - main() +import_from_scratch_directory(PRJ, GRID_MODEL_NAME, FIELD_NAMES, CASE_NAME, SCRATCH) diff --git a/tests/jobs/localisation/example_case/scripts/import_field_parameters_local.py b/tests/jobs/localisation/example_case/scripts/import_field_parameters_local.py index 1c71ee10d..635858faf 100644 --- a/tests/jobs/localisation/example_case/scripts/import_field_parameters_local.py +++ b/tests/jobs/localisation/example_case/scripts/import_field_parameters_local.py @@ -1,103 +1,18 @@ """ Import field parameters into RMS (Must be included as python job in RMS workflow and edited to fit your scratch directory) +Variable project is defined when running within RMS, +but not outside since it refers to Roxar API. """ -from pathlib import Path +# pylint: disable=import-error, undefined-variable +from import_fields_to_rms import import_from_scratch_directory -import xtgeo - -# SCRATCH = "/scratch/fmu/olia/sim_field/" SCRATCH = "/scratch/fmu/olia/sim_field_local/" -# CASE_NAME = "original" CASE_NAME = "local" - - -# pylint: disable=undefined-variable, bare-except,duplicate-code PRJ = project # noqa: 821 - GRID_MODEL_NAME = "GRID" FIELD_NAMES = [ "FieldParam", ] - -def main(): - """ - Import files with initial ensemble and updated fields into RMS project - """ - xtgeo.grid_from_roxar(PRJ, GRID_MODEL_NAME, PRJ.current_realisation) - - path = Path(SCRATCH) - if not path.exists(): - raise IOError(f"File path: {SCRATCH} does not exist. ") - - real = PRJ.current_realisation - print("\n") - print(f"Realization: {real} ") - for name in FIELD_NAMES: - for iteration in [0, 3]: - print(f"Iteration: {iteration}") - if iteration == 0: - name_with_iter = name + "_" + CASE_NAME + "_" + str(iteration) - path = ( - SCRATCH - + "realization-" - + str(real) - + "/iter-" - + str(iteration) - + "/init_files/" - ) - file_name = path + name + ".roff" - print(f"File name: {file_name} ") - - try: - property0 = xtgeo.gridproperty_from_file(file_name, "roff") - print( - f"Import property {property0.name} from file" - f" {file_name} into {name_with_iter} " - ) - property0.to_roxar( - PRJ, GRID_MODEL_NAME, name_with_iter, realisation=real - ) - except: # noqa: E722 - print(f"Skip realization: {real} for iteration: {iteration} ") - elif iteration == 3: - name_with_iter = name + "_" + CASE_NAME + "_" + str(iteration) - path = ( - SCRATCH - + "realization-" - + str(real) - + "/iter-" - + str(iteration) - + "/" - ) - file_name = path + name + ".roff" - print(f"File name: {file_name} ") - - try: - property3 = xtgeo.gridproperty_from_file(file_name, "roff") - print( - f"Import property {property3.name} for iteration {iteration} " - f"from file {file_name} into {name_with_iter} " - ) - property3.to_roxar( - PRJ, GRID_MODEL_NAME, name_with_iter, realisation=real - ) - except: # noqa: E722 - print(f"Skip realization: {real} for iteration: {iteration} ") - try: - diff_property = property0 - diff_property.values = property3.values - property0.values - name_diff = name + "_" + CASE_NAME + "_diff" - print( - f"Calculate difference between iteration 3 and 0: {name_diff}" - ) - diff_property.to_roxar( - PRJ, GRID_MODEL_NAME, name_diff, realisation=real - ) - except: # noqa: E722 - print(f"Skip difference for realisation: {real} ") - - -if __name__ == "__main__": - main() +import_from_scratch_directory(PRJ, GRID_MODEL_NAME, FIELD_NAMES, CASE_NAME, SCRATCH) diff --git a/tests/jobs/localisation/example_case/scripts/import_fields_to_rms.py b/tests/jobs/localisation/example_case/scripts/import_fields_to_rms.py new file mode 100644 index 000000000..3f2a10bd3 --- /dev/null +++ b/tests/jobs/localisation/example_case/scripts/import_fields_to_rms.py @@ -0,0 +1,89 @@ +""" +Import field parameters from scratch disk into RMS project. +This script should be imported into RMS but requires pythonpath +to be set in the RMS project to the scripts directory. +""" +from pathlib import Path + +import xtgeo + + +# pylint: disable=bare-except +def import_from_scratch_directory( + project, grid_model_name, field_names, case_name, scratch_dir +): + """ + Import files with initial ensemble and updated fields into RMS project + """ + xtgeo.grid_from_roxar(project, grid_model_name, project.current_realisation) + + path = Path(scratch_dir) + if not path.exists(): + raise IOError(f"File path: {scratch_dir} does not exist. ") + + real = project.current_realisation + print("\n") + print(f"Realization: {real} ") + for name in field_names: + for iteration in [0, 3]: + print(f"Iteration: {iteration}") + if iteration == 0: + name_with_iter = name + "_" + case_name + "_" + str(iteration) + path = ( + scratch_dir + + "realization-" + + str(real) + + "/iter-" + + str(iteration) + + "/init_files/" + ) + file_name = path + name + ".roff" + print(f"File name: {file_name} ") + + try: + property0 = xtgeo.gridproperty_from_file(file_name, "roff") + print( + f"Import property {property0.name} from file" + f" {file_name} into {name_with_iter} " + ) + property0.to_roxar( + project, grid_model_name, name_with_iter, realisation=real + ) + except: # noqa: E722 + print(f"Skip realization: {real} for iteration: {iteration} ") + elif iteration == 3: + name_with_iter = name + "_" + case_name + "_" + str(iteration) + path = ( + scratch_dir + + "realization-" + + str(real) + + "/iter-" + + str(iteration) + + "/" + ) + file_name = path + name + ".roff" + print(f"File name: {file_name} ") + + try: + property3 = xtgeo.gridproperty_from_file(file_name, "roff") + print( + f"Import property {property3.name} for iteration {iteration} " + f"from file {file_name} into {name_with_iter} " + ) + property3.to_roxar( + project, grid_model_name, name_with_iter, realisation=real + ) + except: # noqa: E722 + print(f"Skip realization: {real} for iteration: {iteration} ") + try: + diff_property = property0 + diff_property.values = property3.values - property0.values + name_diff = name + "_" + case_name + "_diff" + print( + f"Calculate difference between iteration 3 and 0: {name_diff}" + ) + diff_property.to_roxar( + project, grid_model_name, name_diff, realisation=real + ) + except: # noqa: E722 + print(f"Skip difference for realisation: {real} ") diff --git a/tests/jobs/localisation/example_case/scripts/import_upscaled_field_parameters.py b/tests/jobs/localisation/example_case/scripts/import_upscaled_field_parameters.py index 207d6c0c5..6e3267990 100644 --- a/tests/jobs/localisation/example_case/scripts/import_upscaled_field_parameters.py +++ b/tests/jobs/localisation/example_case/scripts/import_upscaled_field_parameters.py @@ -1,103 +1,19 @@ -# Import field parameters """ Import upscaled field parameters into RMS (Must be included as python job in RMS workflow and edited to fit your scratch directory) +Variable project is defined when running within RMS, +but not outside since it refers to Roxar API """ -from pathlib import Path +# pylint: disable=import-error, undefined-variable +from import_fields_to_rms import import_from_scratch_directory -import xtgeo - -# Set scratch file directory for the case and name of case (one short word) SCRATCH = "/scratch/fmu/olia/sim_field/" -# SCRATCH = "/scratch/fmu/olia/sim_field_local/" CASE_NAME = "original" -# CASE_NAME = "local" - -# pylint: disable=undefined-variable, bare-except PRJ = project # noqa: 821 - GRID_MODEL_NAME = "UpscaleGrid" FIELD_NAMES = [ "Upscaled", ] - -def main(): - """ - Import files with upscaled initial ensemble and updated fields into RMS - """ - - xtgeo.grid_from_roxar(PRJ, GRID_MODEL_NAME, PRJ.current_realisation) - - path = Path(SCRATCH) - if not path.exists(): - raise IOError(f"File path: {SCRATCH} does not exist. ") - - real = PRJ.current_realisation - print("\n") - print(f"Realization: {real} ") - for name in FIELD_NAMES: - for iteration in [0, 3]: - print(f"Iteration: {iteration}") - name_with_iter = name + "_" + CASE_NAME + "_" + str(iteration) - if iteration == 0: - path = ( - SCRATCH - + "realization-" - + str(real) - + "/iter-" - + str(iteration) - + "/init_files/" - ) - file_name = path + name + ".roff" - print(f"File name: {file_name} ") - try: - property0 = xtgeo.gridproperty_from_file(file_name, "roff") - print( - f"Import property {property0.name} from file " - f"{file_name} into {name_with_iter} " - ) - property0.to_roxar( - PRJ, GRID_MODEL_NAME, name_with_iter, realisation=real - ) - except: # noqa: E722 - print(f"Skip realization: {real} for iteration: {iteration} ") - elif iteration == 3: - path = ( - SCRATCH - + "realization-" - + str(real) - + "/iter-" - + str(iteration) - + "/" - ) - file_name = path + name + ".roff" - print(f"File name: {file_name} ") - try: - property3 = xtgeo.gridproperty_from_file(file_name, "roff") - print( - f"Import property {property3.name} for iteration {iteration} " - f"from file {file_name} into {name_with_iter} " - ) - property3.to_roxar( - PRJ, GRID_MODEL_NAME, name_with_iter, realisation=real - ) - except: # noqa: E722 - print(f"Skip realization: {real} for iteration: {iteration} ") - try: - diff_property = property0 - diff_property.values = property3.values - property0.values - name_diff = name + "_" + CASE_NAME + "_diff" - print( - f"Calculate difference between iteration 3 and 0: {name_diff}" - ) - diff_property.to_roxar( - PRJ, GRID_MODEL_NAME, name_diff, realisation=real - ) - except: # noqa: E722 - print(f"Skip difference for realisation: {real} ") - - -if __name__ == "__main__": - main() +import_from_scratch_directory(PRJ, GRID_MODEL_NAME, FIELD_NAMES, CASE_NAME, SCRATCH) diff --git a/tests/jobs/localisation/example_case/scripts/import_upscaled_field_parameters_local.py b/tests/jobs/localisation/example_case/scripts/import_upscaled_field_parameters_local.py index b2d0f20a2..21faac4d8 100644 --- a/tests/jobs/localisation/example_case/scripts/import_upscaled_field_parameters_local.py +++ b/tests/jobs/localisation/example_case/scripts/import_upscaled_field_parameters_local.py @@ -1,103 +1,19 @@ -# Import field parameters """ -Import upscaled field parameters into RMS (Must be included as python job +Import field parameters into RMS (Must be included as python job in RMS workflow and edited to fit your scratch directory) +Variable project is defined when running within RMS, +but not outside since it refers to Roxar API """ -from pathlib import Path +# pylint: disable=import-error, undefined-variable +from import_fields_to_rms import import_from_scratch_directory -import xtgeo - -# Set scratch file directory for the case and name of case (one short word) -# SCRATCH = "/scratch/fmu/olia/sim_field/" SCRATCH = "/scratch/fmu/olia/sim_field_local/" -# CASE_NAME = "original" CASE_NAME = "local" - -# pylint: disable=undefined-variable, bare-except PRJ = project # noqa: 821 - GRID_MODEL_NAME = "UpscaleGrid" FIELD_NAMES = [ "Upscaled", ] - -def main(): - """ - Import files with upscaled initial ensemble and updated fields into RMS - """ - - xtgeo.grid_from_roxar(PRJ, GRID_MODEL_NAME, PRJ.current_realisation) - - path = Path(SCRATCH) - if not path.exists(): - raise IOError(f"File path: {SCRATCH} does not exist. ") - - real = PRJ.current_realisation - print("\n") - print(f"Realization: {real} ") - for name in FIELD_NAMES: - for iteration in [0, 3]: - print(f"Iteration: {iteration}") - name_with_iter = name + "_" + CASE_NAME + "_" + str(iteration) - if iteration == 0: - path = ( - SCRATCH - + "realization-" - + str(real) - + "/iter-" - + str(iteration) - + "/init_files/" - ) - file_name = path + name + ".roff" - print(f"File name: {file_name} ") - try: - property0 = xtgeo.gridproperty_from_file(file_name, "roff") - print( - f"Import property {property0.name} from file " - f"{file_name} into {name_with_iter} " - ) - property0.to_roxar( - PRJ, GRID_MODEL_NAME, name_with_iter, realisation=real - ) - except: # noqa: E722 - print(f"Skip realization: {real} for iteration: {iteration} ") - elif iteration == 3: - path = ( - SCRATCH - + "realization-" - + str(real) - + "/iter-" - + str(iteration) - + "/" - ) - file_name = path + name + ".roff" - print(f"File name: {file_name} ") - try: - property3 = xtgeo.gridproperty_from_file(file_name, "roff") - print( - f"Import property {property3.name} for iteration {iteration} " - f"from file {file_name} into {name_with_iter} " - ) - property3.to_roxar( - PRJ, GRID_MODEL_NAME, name_with_iter, realisation=real - ) - except: # noqa: E722 - print(f"Skip realization: {real} for iteration: {iteration} ") - try: - diff_property = property0 - diff_property.values = property3.values - property0.values - name_diff = name + "_" + CASE_NAME + "_diff" - print( - f"Calculate difference between iteration 3 and 0: {name_diff}" - ) - diff_property.to_roxar( - PRJ, GRID_MODEL_NAME, name_diff, realisation=real - ) - except: # noqa: E722 - print(f"Skip difference for realisation: {real} ") - - -if __name__ == "__main__": - main() +import_from_scratch_directory(PRJ, GRID_MODEL_NAME, FIELD_NAMES, CASE_NAME, SCRATCH) diff --git a/tests/jobs/localisation/example_case/scripts/init_test_case.py b/tests/jobs/localisation/example_case/scripts/init_test_case.py index 5cd265850..68276e35e 100755 --- a/tests/jobs/localisation/example_case/scripts/init_test_case.py +++ b/tests/jobs/localisation/example_case/scripts/init_test_case.py @@ -6,24 +6,22 @@ import math import os import random -import sys - -import xtgeo # pylint: disable=import-error - +import xtgeo +from common_functions import generate_field_and_upscale, settings, write_upscaled_field # pylint: disable=too-many-arguments,invalid-name,missing-function-docstring # pylint: disable=too-many-locals,redefined-outer-name def generate_seed_file( - settings, start_seed: int = 9828862224, number_of_seeds: int = 1000, ): # pylint: disable=unused-variable - seed_file_name = settings["field"]["seed_file"] + + seed_file_name = settings.field.seed_file print(f"Generate random seed file: {seed_file_name}") random.seed(start_seed) with open(seed_file_name, "w", encoding="utf8") as file: @@ -31,15 +29,15 @@ def generate_seed_file( file.write(f"{random.randint(1, 999999999)}\n") -def obs_positions(settings): - NX, NY, _ = settings["response"]["grid_dimension"] - use_eclipse_origo = settings["grid_size"]["use_eclipse_grid_index_origo"] +def obs_positions(): + NX, NY, _ = settings.response.grid_dimension + use_eclipse_origo = settings.grid_size.use_eclipse_grid_index_origo - xsize = settings["grid_size"]["xsize"] - ysize = settings["grid_size"]["ysize"] + xsize = settings.grid_size.xsize + ysize = settings.grid_size.ysize dx = xsize / NX dy = ysize / NY - cell_indx_list = settings["observation"]["selected_grid_cells"] + cell_indx_list = settings.observation.selected_grid_cells if use_eclipse_origo: print("Grid index origin: Eclipse standard") else: @@ -65,18 +63,17 @@ def obs_positions(settings): def write_localisation_config( - settings, config_file_name="local_config.yml", write_scaling=True, ): - obs_index_list = settings["observation"]["selected_grid_cells"] - field_name = settings["field"]["name"] - corr_ranges = settings["field"]["correlation_range"] - azimuth = settings["field"]["correlation_azimuth"] + obs_index_list = settings.observation.selected_grid_cells + field_name = settings.field.name + corr_ranges = settings.field.correlation_range + azimuth = settings.field.correlation_azimuth space = " " * 2 space2 = " " * 4 space3 = " " * 6 - positions = obs_positions(settings) + positions = obs_positions() print(f"Write localisation config file: {config_file_name}") with open(config_file_name, "w", encoding="utf8") as file: file.write("log_level: 3\n") @@ -99,13 +96,13 @@ def write_localisation_config( file.write(f"{space3}ref_point: [ {pos[0]}, {pos[1]} ]\n") -def write_gen_obs(upscaled_values, settings): - observation_dir = settings["observation"]["directory"] - obs_file_name = settings["observation"]["file_name"] - obs_data_dir = settings["observation"]["data_dir"] - cell_indx_list = settings["observation"]["selected_grid_cells"] - rel_err = settings["observation"]["rel_error"] - min_err = settings["observation"]["min_abs_error"] +def write_gen_obs(upscaled_values): + observation_dir = settings.observation.directory + obs_file_name = settings.observation.file_name + obs_data_dir = settings.observation.data_dir + cell_indx_list = settings.observation.selected_grid_cells + rel_err = settings.observation.rel_error + min_err = settings.observation.min_abs_error if not os.path.exists(observation_dir): print(f"Create directory: {observation_dir} ") os.makedirs(observation_dir) @@ -152,13 +149,13 @@ def write_gen_obs(upscaled_values, settings): data_file.write(f"{value} {value_err}\n") -def create_grid(settings): - grid_file_name = settings["field"]["grid_file_name"] - nx, ny, nz = settings["field"]["grid_dimension"] - xsize = settings["grid_size"]["xsize"] - ysize = settings["grid_size"]["ysize"] - zsize = settings["grid_size"]["zsize"] - if settings["grid_size"]["use_eclipse_grid_index_origo"]: +def create_grid(): + grid_file_name = settings.field.grid_file_name + nx, ny, nz = settings.field.grid_dimension + xsize = settings.grid_size.xsize + ysize = settings.grid_size.ysize + zsize = settings.grid_size.zsize + if settings.grid_size.use_eclipse_grid_index_origo: flip = -1 x0 = 0.0 y0 = ysize @@ -186,13 +183,13 @@ def create_grid(settings): return grid_object -def create_upscaled_grid(settings): - grid_file_name = settings["response"]["grid_file_name"] - nx, ny, nz = settings["response"]["grid_dimension"] - xsize = settings["grid_size"]["xsize"] - ysize = settings["grid_size"]["ysize"] - zsize = settings["grid_size"]["zsize"] - if settings["grid_size"]["use_eclipse_grid_index_origo"]: +def create_upscaled_grid(): + grid_file_name = settings.response.grid_file_name + nx, ny, nz = settings.response.grid_dimension + xsize = settings.grid_size.xsize + ysize = settings.grid_size.ysize + zsize = settings.grid_size.zsize + if settings.grid_size.use_eclipse_grid_index_origo: flip = -1 x0 = 0.0 y0 = ysize @@ -220,62 +217,46 @@ def create_upscaled_grid(settings): return grid_object -def main(config_file_name): +def main(): """ Initialize seed file, grid files, observation files and localisation config file """ - # pylint: disable=import-outside-toplevel - from common_functions import ( - generate_field_and_upscale, - read_test_config, - write_upscaled_field, - ) - - # Settings are specified here - settings = read_test_config(config_file_name) # Create seed file - generate_seed_file(settings) + generate_seed_file() # Create grid for the field parameter - create_grid(settings) + create_grid() # Create coarse grid to be used in QC of upscaled field parameter - create_upscaled_grid(settings) + create_upscaled_grid() print("Generate field parameter and upscale this.") print( - f"The upscaled field {settings['observation']['3D_param_file_name']} " + f"The upscaled field {settings.observation.param_file_name} " "is used when extracting observations." ) # Simulate field (with trend) real_number = 0 - upscaled_values = generate_field_and_upscale(settings, real_number) + upscaled_values = generate_field_and_upscale(real_number) # Create observations by extracting from existing upscaled field - write_gen_obs(upscaled_values, settings) + write_gen_obs(upscaled_values) # Write upscaled field used as truth realisation write_upscaled_field( upscaled_values, - settings["observation"]["3D_param_file_name"], - selected_cell_index_list=settings["observation"]["selected_grid_cells"], + settings.observation.param_file_name, + selected_cell_index_list=settings.observation.selected_grid_cells, ) # Write file for non-adaptive localisation using distance based localisation write_localisation_config( - settings, config_file_name="local_config.yml", write_scaling=True, ) if __name__ == "__main__": - config_file_name = None - if len(sys.argv) < 2: - print("Use default settings") - else: - config_file_name = sys.argv[1] - print(f"Read modified settings from file: {config_file_name} ") - main(config_file_name) + main() diff --git a/tests/jobs/localisation/example_case/scripts/sim_fields.py b/tests/jobs/localisation/example_case/scripts/sim_fields.py index 8f95f2a9b..511748471 100755 --- a/tests/jobs/localisation/example_case/scripts/sim_fields.py +++ b/tests/jobs/localisation/example_case/scripts/sim_fields.py @@ -11,16 +11,19 @@ generate_field_and_upscale, read_field_from_file, read_obs_field_from_file, - read_test_config, read_upscaled_field_from_file, + settings, upscaling, write_obs_pred_diff_field, ) -def write_prediction_gen_data(upscaled_values, settings): - cell_indx_list = settings["observation"]["selected_grid_cells"] - response_file_name = settings["response"]["gen_data_file_name"] +def write_prediction_gen_data(upscaled_values): + """ + Write GEN_DATA file with predicted values of observables (selected upscaled values) + """ + cell_indx_list = settings.observation.selected_grid_cells + response_file_name = settings.response.gen_data_file_name print(f"Write GEN_DATA file with prediction of observations: {response_file_name}") with open(response_file_name, "w", encoding="utf8") as file: # NOTE: The sequence of values must be the same as for the observations @@ -64,65 +67,50 @@ def get_iteration_and_real_number(argv): return iteration, real_number -def main(config_file_name, iteration, real_number): - # pylint: disable=too-many-arguments +def main(iteration, real_number): """ - Specify settings for fine grid and model parameters for simulating a - field on fine grid. - Specify settings for coarse grid. - Specify settings for synthetic observations extracted from upscaled - field values from coarse grid. - Simulate a field on fine grid. - Export the fine grid and the field for the fine grid to files to be used by ERT. - Upscale the fine grid field to a coarse grid field which is used as - response variables here. - Option to extract synthetic observations for upscaled field parameters - and generate ERT observation files. - Options to generate a sequence of random seeds to make the simulations repeatable. - + For iteration = 0: + - simulate field, export to file as initial ensemble realization + - upscale and extract predicted values for observables + (selected coarse grid cell values) + For iteration > 0: + - Import updated field from ERT. + - upscale and extract predicted values for observables + (selected coarse grid cell values) """ # NOTE: Both the fine scale grid with simulated field values # and the coarse grid with upscaled values must have Eclipse grid index origin - # Settings are specified here - - settings = read_test_config(config_file_name) - if iteration == 0: print(f"Generate new field parameter realization:{real_number} ") # Simulate field (with trend) - upscaled_values = generate_field_and_upscale(settings, real_number) + upscaled_values = generate_field_and_upscale(real_number) else: print(f"Import updated field parameter realization: {real_number} ") - field_object = read_field_from_file(settings) + field_object = read_field_from_file() field_values = field_object.values # Calculate upscaled values for selected coarse grid cells upscaled_values = upscaling( field_values, - settings, - write_field=settings["optional"]["write_upscaled_to_file"], + write_field=settings.optional.write_upscaled_to_file, iteration=iteration, ) # Write GEN_DATA file - write_prediction_gen_data(upscaled_values, settings) + write_prediction_gen_data(upscaled_values) # Optional output - if settings["optional"]["write_obs_pred_diff_field_file"]: - obs_field_object = read_obs_field_from_file(settings) - upscaled_field_object = read_upscaled_field_from_file(settings, iteration) + if settings.optional.write_obs_pred_diff_field_file: + obs_field_object = read_obs_field_from_file() + upscaled_field_object = read_upscaled_field_from_file(iteration) write_obs_pred_diff_field(upscaled_field_object, obs_field_object) if __name__ == "__main__": + # Command line arguments are required for old version of + # ERT to get iteration and realization number, + # but for ERT version 5.0 and newer, environment variables are used to get them. iteration, real_number = get_iteration_and_real_number(sys.argv) - config_file_name = None - if len(sys.argv) < 4: - print("Use default settings") - else: - config_file_name = sys.argv[3] - print(f"Read modified settings from file: {config_file_name} ") - - main(config_file_name, iteration, real_number) + main(iteration, real_number) diff --git a/tests/jobs/localisation/example_case/sim_field.ert b/tests/jobs/localisation/example_case/sim_field.ert index 99302a6d6..1020ac549 100644 --- a/tests/jobs/localisation/example_case/sim_field.ert +++ b/tests/jobs/localisation/example_case/sim_field.ert @@ -48,7 +48,8 @@ FORWARD_MODEL COPY_FILE(=/, ==/init_files/UpscaledObsField.roff, =/init_files/UpscaledObsField.roff) -- The main forward model simulating gaussian field with trend, and upscale -FORWARD_MODEL SIM_FIELD(=, =) +-- FORWARD_MODEL SIM_FIELD(=, =) +FORWARD_MODEL SIM_FIELD GRID /GRID.EGRID -- Necessary for AHM using field parameters diff --git a/tests/jobs/localisation/example_case/sim_field_local.ert b/tests/jobs/localisation/example_case/sim_field_local.ert index 9e0993793..35581db14 100644 --- a/tests/jobs/localisation/example_case/sim_field_local.ert +++ b/tests/jobs/localisation/example_case/sim_field_local.ert @@ -2,6 +2,7 @@ DEFINE $USER DEFINE /scratch/fmu DEFINE sim_field_local DEFINE randomseeds.txt +DEFINE /example_case.yml INSTALL_JOB SIM_FIELD scripts/FM_SIM_FIELD ----------------------------------------------------- -- Observations @@ -47,7 +48,7 @@ FORWARD_MODEL COPY_FILE(=/, ==/init_files/UpscaledObsField.roff, =/init_files/UpscaledObsField.roff) -- The main forward model simulating gaussian field with trend, and upscale -FORWARD_MODEL SIM_FIELD(=, =) +FORWARD_MODEL SIM_FIELD GRID /GRID.EGRID -- Necessary for AHM using field parameters