From 883c2692bf366066ee31c286cb5a6f180b439f4b Mon Sep 17 00:00:00 2001 From: "Oddvar Lia (ST MSU GEO)" Date: Tue, 19 Nov 2024 12:38:07 +0100 Subject: [PATCH] Added generate_petro_jobs_for_field_update and tests Add testdata for generate_petro_jobs_for_field_update. Add documentation Update doc index Added description of use --- docs/generate_petro_jobs_for_field_update.rst | 92 +++ docs/index.rst | 1 + src/fmu/tools/rms/__init__.py | 9 +- .../generate_petro_jobs_for_field_update.py | 534 ++++++++++++++++++ .../F1_multi_zone_petro.txt | 174 ++++++ .../F1_single_zone_petro.txt | 90 +++ .../F2_multi_zone_petro.txt | 174 ++++++ .../F2_single_zone_petro.txt | 90 +++ .../F3_multi_zone_petro.txt | 174 ++++++ .../F3_single_zone_petro.txt | 90 +++ .../generate_original_multi_zone_job.yml | 31 + .../generate_original_single_zone_job.yml | 20 + .../generate_petro_multi_zone_jobs.yml | 25 + .../generate_petro_single_zone_jobs.yml | 14 + .../multizonefacies.roff | Bin 0 -> 528 bytes .../generate_jobs_testdata/multizonegrid.roff | Bin 0 -> 3697 bytes .../singlezonefacies.roff | Bin 0 -> 503 bytes .../singlezonegrid.roff | Bin 0 -> 3202 bytes ...st_generate_petro_jobs_for_field_update.py | 403 +++++++++++++ 19 files changed, 1920 insertions(+), 1 deletion(-) create mode 100644 docs/generate_petro_jobs_for_field_update.rst create mode 100644 src/fmu/tools/rms/generate_petro_jobs_for_field_update.py create mode 100644 tests/rms/generate_jobs_testdata/F1_multi_zone_petro.txt create mode 100644 tests/rms/generate_jobs_testdata/F1_single_zone_petro.txt create mode 100644 tests/rms/generate_jobs_testdata/F2_multi_zone_petro.txt create mode 100644 tests/rms/generate_jobs_testdata/F2_single_zone_petro.txt create mode 100644 tests/rms/generate_jobs_testdata/F3_multi_zone_petro.txt create mode 100644 tests/rms/generate_jobs_testdata/F3_single_zone_petro.txt create mode 100644 tests/rms/generate_jobs_testdata/generate_original_multi_zone_job.yml create mode 100644 tests/rms/generate_jobs_testdata/generate_original_single_zone_job.yml create mode 100644 tests/rms/generate_jobs_testdata/generate_petro_multi_zone_jobs.yml create mode 100644 tests/rms/generate_jobs_testdata/generate_petro_single_zone_jobs.yml create mode 100644 tests/rms/generate_jobs_testdata/multizonefacies.roff create mode 100644 tests/rms/generate_jobs_testdata/multizonegrid.roff create mode 100644 tests/rms/generate_jobs_testdata/singlezonefacies.roff create mode 100644 tests/rms/generate_jobs_testdata/singlezonegrid.roff create mode 100644 tests/rms/test_generate_petro_jobs_for_field_update.py diff --git a/docs/generate_petro_jobs_for_field_update.rst b/docs/generate_petro_jobs_for_field_update.rst new file mode 100644 index 00000000..1eb3f236 --- /dev/null +++ b/docs/generate_petro_jobs_for_field_update.rst @@ -0,0 +1,92 @@ +rms.generate_petro_jobs +=========================== + +When running FMU project where field parameters for both facies and +petrophysical properties is updated in ERT simultaneously, +some adjustments are needed in the RMS project to support this type +of workflow. It is necessary to have one petrosim job per facies. +To simplify the work with the RMS project, the function +*generate_petro_jobs* from *fmu.tools* +can be used in a python job in RMS. +It requires a small configuration file and will then +read an existing petrosim job from the RMS project and generate one new +petrosim job per facies. The new jobs are ready to be used +(put into the RMS workflow) and they will use the same model parameters +for the petrophysical properties as the original (existing) job, +but only for one facies. + +The function *generate_petro_jobs* will modify +your RMS project when run from your RMS project +by adding new petrosim jobs, one per facies as specified in the +configuration file for the script. The configuration file is a yaml format +file defining which grid model and which facies 3D parameter to use and the +name of the original petrosim job. For each zone and each facies per zone +a list is specified of which petrophysical parameters to use in the new +petrosim jobs that are generated. + + +Usage +^^^^^ +* Import the function *generate_petro_jobs* into a python job in RMS. + Specify a configuration file and specify the name of this configuration + file in the python job. + +* Run the python job in RMS to generate the new petrosim jobs. + +* Finally, update the workflowin RMS by using the generated jobs. + +Example of python script in RMS + +.. code-block:: python + + from fmu.tools.rms import generate_petro_jobs + + CONFIG_FILE = "generate_petro_jobs.yml" + if __name__ == "__main__": + generate_petro_jobs(CONFIG_FILE) + +Example of configuration file for multi zone grid in RMS + +.. code-block:: yaml + + # Name of grid model for the petrophysics jobs + grid_name: MultiZoneBox + + # Name of original petro job using facies realization as input + original_job_name: original_multi_zone + + # Use empty string as zone name for single zone grids and zone name + # for multizone grids. + # For each zone, specify facies and for each facies specify + # petro variables to be used as field parameters in ERT update + + # Example for a multizone grid with three zones: + used_petro_var: + Zone1: + F1: [P1, P2] + F2: [P1, P2] + F3: [P1, P2] + Zone2: + F1: [P1, P2] + F2: [P1, P2] + F3: [P1, P2] + +Example of configuration file for single zone grid in RMS + +.. code-block:: yaml + + # Name of grid model for the petrophysics jobs + grid_name: SingleZoneBox + + # Name of original petro job using facies realization as input + original_job_name: original_single_zone + + # Use empty string as zone name for single zone grids and zone name + # for multizone grids. + # For each zone, specify facies and for each facies specify + # petro variables to be used as field parameters in ERT update + used_petro_var: + default: + F1: [P1, P2] + F2: [P1, P2] + F3: [P1, P2] diff --git a/docs/index.rst b/docs/index.rst index fe2f7785..22c6a21a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -11,6 +11,7 @@ Contents: qcforward qcproperties qcreset + generate_petro_jobs_for_field_update properties domain_conversion create_rft_ertobs diff --git a/src/fmu/tools/rms/__init__.py b/src/fmu/tools/rms/__init__.py index 847641b2..0f2d6419 100644 --- a/src/fmu/tools/rms/__init__.py +++ b/src/fmu/tools/rms/__init__.py @@ -2,7 +2,14 @@ Processing of volumetrics from RMS """ +from .generate_petro_jobs_for_field_update import ( + main as generate_petro_jobs, +) from .import_localmodules import import_localmodule from .volumetrics import rmsvolumetrics_txt2df -__all__ = ["rmsvolumetrics_txt2df", "import_localmodule"] +__all__ = [ + "rmsvolumetrics_txt2df", + "import_localmodule", + "generate_petro_jobs", +] diff --git a/src/fmu/tools/rms/generate_petro_jobs_for_field_update.py b/src/fmu/tools/rms/generate_petro_jobs_for_field_update.py new file mode 100644 index 00000000..e1eec0c4 --- /dev/null +++ b/src/fmu/tools/rms/generate_petro_jobs_for_field_update.py @@ -0,0 +1,534 @@ +""" +Description: + Implementation of a function to create new petrosim jobs, one per facies + given an existing petrosim job using facies realization as input. + +Summary: + + The current script is made to simplify the preparation step of the RMS project by + creating the necessary petrosim jobs for a workflow supporting simultaneous update + of both facies and petrophysical properties in ERT. +""" + +import copy +import json +import pprint +import warnings + +import yaml + +try: + import _roxar # type: ignore +except ModuleNotFoundError: + try: + import _rmsapi as _roxar # type: ignore + import roxar.jobs # type: ignore + import roxar.jobs.Job # type: ignore + except ModuleNotFoundError: + warnings.warn("This script only supports interactive RMS usage", UserWarning) +from typing import Dict, List, Tuple, no_type_check + +# Fixed global variables +GRID_MODELS = "Grid models" +GRID = "Grid" +JOB_TYPE = "Petrophysical Modeling" +PP = pprint.PrettyPrinter(depth=7) + + +def main(config_file: str, debug: bool = False, report_unused: bool = False) -> None: + """Generate new RMS petrosim jobs from an original petrosim job. + + Description + + This function can be used to generate new petrosim jobs in RMS from an existing + petrosim job. The input (original) petrosim job is assumed to be a job for + either a single zone or multi zone grid where the petrophysical properties + are conditioned to an existing facies realization. + The new generated petrosim jobs will not use facies realization as input, + but assume that all grid cells belongs to the same facies. + Hence, if the original petrosim job specify model parameters for + petrophysical properties conditioned to a facies + realization with N different facies, this script can generate up to N new + petrosim jobs, one per facies the user wants to use in field parameter + updating in ERT. + + Input + + An existing RMS project with a petrophysical job conditioning petrophysical + properties on an existing facies realization. + + A yaml format configuration file specifying necessary input to the function. + This includes name of original petrosim job, grid model name and for each zone + and each facies which petrophysical variables to use in the new petrosim jobs + that are created. It is possible to not use all petrophysical variables in the + original job and this can vary from facies to facies and zone to zone. + + Output + + A set of new petrosim jobs, one per facies that is specified in at least + one of the zones. For a multi zone grid, each of the new petrosim jobs will + contain specification of the petrophysical properties that is to be modelled + for each zone for a given facies. The new jobs will get a name reflecting which + facies it belongs to. + + How to use the new petrosim jobs in the RMS workflow + + When initial ensemble is created (ERT iteration = 0) + + The RMS workflow should first use the original petrosim job to generate + realizations of petrophysical properties as usual when not all of the + petrophysical properties are going to be used as field parameters in RMS. + If all petrophysical properties are going to be updated as field parameters + in ERT, it is not necessary to do this step. + Add all the new petrosim jobs (one per facies) into the workflow in RMS. + Copy the petrophysical property realizations for each facies from the + geomodel grid into the ERTBOX grid used for field parameter update in ERT. + Export the petrophysical properties for each of the facies to ERT in + ROFF format. Use a script that take as input the facies realization and + the petrophysical properties per facies and use the facies realization + as filter to select which values to copy from the petrophysical realizations + to put into the petrophysical property parameter that was generated by + the original job. This will overwrite the petrophysical properties + in the realization from the original job for those + parameters that are used as field parameters to be updated by ERT. + The other petrophysical parameters generated by the original job but not + used as field parameters in ERT will be untouched. + + When updated ensemble is fetched from ERT (ERT iteration > 0): + + Run the original petrosim job to generate all petrophysical parameters + in the geogrid. Import the updated field parameters for petrophysical + properties per facies into ERTBOX grid. + Copy the petrophysical property parameters that were updated as + field parameters by ERT into geomodel grid from the ERTBOX grid. + This operation is the same as the last step + when initial ensemble realization was created. This step will then + overwrite the petrophysical property parameters already created by + the original job by the new values updated by ERT. All petrophysical + property parameters not updated as field parameters by ERT will be + untouched and will therefore have the same values as they + had after the original petrosim job was run. + + + Summary + + The current function is made to simplify the preparation step of the + RMS project by creating the necessary petrosim jobs for a workflow + supporting simultaneous update of both facies and petrophysical + properties in ERT. + + Usage of this function + + Input + Name of config file with specification of which field parameters + to simulate per zone per facies. + + Optional parameters + debug info (True/False), + report field parameters not used (True/False) + + Output + A set of new petro sim jobs to appear in RMS project. + + """ + + spec_dict = read_specification_file(config_file) + create_new_petro_job_per_facies( + spec_dict, debug_print=debug, report_unused=report_unused + ) + + +@no_type_check +def check_rms_project(project): + if not isinstance(project, _roxar.Project): # type: ignore + raise RuntimeError("This run must be ran in an RoxAPI environment!") + + +def read_specification_file(config_file_name: str, check: bool = True) -> Dict: + with open(config_file_name, encoding="utf-8") as yml_file: + spec_dict = yaml.safe_load(yml_file) + if check and (not check_specification(spec_dict)): + raise ValueError(f"Errors found in {config_file_name}") + return spec_dict + + +def check_specification(spec_dict: Dict) -> bool: + # Check that the same petro variables are specified for + # all zones using the same facies. This means that for instance + # if the case has zone A,B,C and facies F1 is present in zone A and C + # and zone A for facies F1 has petro variables P1, P2, then + # zone C which also has facies F1 must use petro variables P1, P2 + # for facies F1. + used_petro_var_dict = spec_dict["used_petro_var"] + ok = True + if len(used_petro_var_dict) > 1: + for zone_name1, zone_dict1 in used_petro_var_dict.items(): + for zone_name2, zone_dict2 in used_petro_var_dict.items(): + if zone_name1 != zone_name2: + for facies_name1, petro_list1 in zone_dict1.items(): + petro_set1 = set(petro_list1) + for facies_name2, petro_list2 in zone_dict2.items(): + petro_set2 = set(petro_list2) + if ( + facies_name1 == facies_name2 + and petro_set1 != petro_set2 + ): + print( + "Petro variables to use must be " + "the same for all zones for facies: " + f"{facies_name1}" + ) + print( + f" This is specified for zone {zone_name1}:" + f" {petro_list1}" + ) + print( + f" This is specified for zone {zone_name2}:" + f" {petro_list2}" + ) + ok = False + break + if not ok: + break + if not ok: + break + if not ok: + break + return ok + + +def define_new_variable_names_and_correlation_matrix( + orig_var_names: List[str], + facies_name: str, + new_var_names: List[str], + orig_corr_matrix: List[List[float]], +) -> Tuple[List[str], List[List[float]]]: + nvar_new = len(new_var_names) + if nvar_new == 1: + # No correlation matrix + return new_var_names, [] + + # Order the var_names_to_keep list to be in same sequence + # as orig_var_names for those variables that are common with the var_names_to_keep + # Example: the keep list is: ["A", "C", "B"] + # and the original var name list + # is ["A", "D", "F", "B", "C"] + # The sorted keep list should be: ["A", "B", "C"] + + sorted_new_var_names = sort_new_var_names( + orig_var_names, facies_name, new_var_names + ) + new_corr_matrix = [] + index_in_orig_var = [] + for i, var_name in enumerate(orig_var_names): + name = set_new_var_name(facies_name, var_name) + if name in new_var_names: + index_in_orig_var.append(i) + + for row in range(nvar_new): + row_list = [] + for col in range(row): + row_list.append( + orig_corr_matrix[index_in_orig_var[row]][index_in_orig_var[col]] + ) + new_corr_matrix.append(row_list) + + return sorted_new_var_names, new_corr_matrix + + +def sort_new_var_names( + original_variable_names: List[str], facies_name: str, new_variable_names: List[str] +) -> List[str]: + sorted_keep_list = [] + for varname in original_variable_names: + name = set_new_var_name(facies_name, varname) + if name in new_variable_names: + sorted_keep_list.append(name) + return sorted_keep_list + + +def get_original_job_settings( + owner_string_list: List[str], job_type: str, job_name: str +) -> dict: + original_job = roxar.jobs.Job.get_job(owner_string_list, job_type, job_name) + return original_job.get_arguments(skip_defaults=False) + + +def create_copy_of_job( + owner_string_list: List[str], + job_type: str, + original_job_arguments: Dict, + new_job_name: str, +): + new_job = roxar.jobs.Job.create(owner_string_list, job_type, new_job_name) + new_job.set_arguments(original_job_arguments) + return new_job + + +def get_zone_names_per_facies(used_petro_per_zone_per_facies_dict: Dict) -> Dict: + zone_names_with_facies_dict: Dict[str, List[str]] = {} + for zone_name, facies_dict in used_petro_per_zone_per_facies_dict.items(): + for facies_name, _ in facies_dict.items(): + if facies_name not in zone_names_with_facies_dict: + zone_names_with_facies_dict[facies_name] = [] + zone_names_with_facies_dict[facies_name].append(zone_name) + return zone_names_with_facies_dict + + +def get_used_petro_names(used_petro_per_zone_per_facies_dict: Dict) -> List[str]: + all_petro_var_list = [] + for _, petro_per_facies_dict in used_petro_per_zone_per_facies_dict.items(): + for _, petro_list in petro_per_facies_dict.items(): + for petro_name in petro_list: + if petro_name not in all_petro_var_list: + all_petro_var_list.append(petro_name) + return all_petro_var_list + + +def set_new_var_name(facies_name: str, petro_name: str): + return facies_name + "_" + petro_name + + +def report_unused_fields( + owner_string_list: List[str], + job_name: str, + used_petro_per_zone_per_facies_dict: Dict, +): + job_arguments = get_original_job_settings(owner_string_list, JOB_TYPE, job_name) + zone_models_list = job_arguments["Zone Models"] + # First report which field parameters from original model + # that is not specified to be used + print("Report of unused petrophysical variables in generated jobs:") + for zone_model in zone_models_list: + zone_name = zone_model["ZoneName"] + if zone_name not in used_petro_per_zone_per_facies_dict: + print(f" No field parameters are used from zone: {zone_name}") + else: + petro_per_facies_dict = used_petro_per_zone_per_facies_dict[zone_name] + facies_models_list = zone_model["Facies Models"] + for facies_model in facies_models_list: + facies_name = facies_model["FaciesName"] + petro_model_list = facies_model["Variable Models"] + if facies_name not in petro_per_facies_dict: + print( + " No field parameters are used for facies " + f"{facies_name} for zone {zone_name}" + ) + else: + petro_list = petro_per_facies_dict[facies_name] + for petro_model in petro_model_list: + var_name = petro_model["VariableName"] + if var_name not in petro_list: + print( + f" Field parameter {var_name} is not used " + f"for facies {facies_name} for zone {zone_name}" + ) + print("") + + +def check_consistency( + owner_string_list: List[str], + job_name: str, + used_petro_per_zone_per_facies_dict: Dict, + report_unused: bool = True, +) -> None: + # Check if there are specified field parameters which does not exist + # in the original job and report errors if this is the case + job_arguments = get_original_job_settings(owner_string_list, JOB_TYPE, job_name) + zone_models_list = job_arguments["Zone Models"] + specified_petro_var_list = get_used_petro_names(used_petro_per_zone_per_facies_dict) + err_list = [] + for specified_petro_var in specified_petro_var_list: + found = False + for zone_model in zone_models_list: + facies_models_list = zone_model["Facies Models"] + for facies_model in facies_models_list: + petro_model_list = facies_model["Variable Models"] + for petro_model in petro_model_list: + if specified_petro_var == petro_model["VariableName"]: + found = True + break + if found: + break + if found: + break + if not found: + err_list.append(specified_petro_var) + if len(err_list) > 0: + print("Error in specification of used petrophysical variables.") + print("Unknown petrophysical variables:") + for name in err_list: + print(f"{name}") + raise ValueError("Unknown petrophysical variable names are specified.") + + +def create_new_petro_job_per_facies( + spec_dict: Dict, debug_print: bool = False, report_unused: bool = False +) -> List[str]: + grid_name = spec_dict["grid_name"] + original_job_name = spec_dict["original_job_name"] + used_petro_per_zone_per_facies_dict = spec_dict["used_petro_var"] + + if len(used_petro_per_zone_per_facies_dict.keys()) == 1: + # Only one zone, set zone name to empty + zone_name = list(used_petro_per_zone_per_facies_dict.keys())[0] + tmp: Dict[str, Dict] = {} + tmp[""] = used_petro_per_zone_per_facies_dict[zone_name] + used_petro_per_zone_per_facies_dict = tmp + + # Original job parameter setting + owner_string_list = [GRID_MODELS, grid_name, GRID] + if report_unused: + report_unused_fields( + owner_string_list, original_job_name, used_petro_per_zone_per_facies_dict + ) + + check_consistency( + owner_string_list, + original_job_name, + used_petro_per_zone_per_facies_dict, + report_unused=report_unused, + ) + orig_job_arguments = get_original_job_settings( + owner_string_list, JOB_TYPE, original_job_name + ) + + zone_names_per_facies_dict = get_zone_names_per_facies( + used_petro_per_zone_per_facies_dict + ) + + # for each facies used in any zone, find the zone models having the facies + original_zone_models_list = orig_job_arguments["Zone Models"] + new_job_name_list = [] + for facies_name, zone_name_list in zone_names_per_facies_dict.items(): + if debug_print: + print(f"Facies: {facies_name}") + new_job_arguments_current = copy.deepcopy(orig_job_arguments) + # Remove unused keys or keys that should be set to default for new job + del new_job_arguments_current["InputFaciesProperty"] + del new_job_arguments_current["PrefixOutputName"] + + # Only keep specification for zones having the facies + new_job_arguments_current["Zone Models"] = [] + new_variable_names_all_zones = [] + for zone_model_dict in original_zone_models_list: + zone_name = zone_model_dict["ZoneName"] + if zone_name in zone_name_list: + zone_model_dict_current = copy.deepcopy(zone_model_dict) + new_job_arguments_current["Zone Models"].append(zone_model_dict_current) + # for this zone model remove all specifications not relevant + # for current facies_name + + used_petro_var_list = used_petro_per_zone_per_facies_dict[zone_name][ + facies_name + ] + + # Loop over facies for this zone and keep only current facies + tmp_list = zone_model_dict_current["Facies Models"] + current_facies_model_dict = None + for facies_model_dict in tmp_list: + if facies_model_dict["FaciesName"] == facies_name: + current_facies_model_dict = facies_model_dict + break + # Here at least one facies model must exist in the tmp_list, + # if not there is a consistency error in input dictionary + # used_petro_per_zone_per_facies_dict related to the + # specified original job. + if current_facies_model_dict is None: + raise ValueError( + "There are some facies name errors in input dict" + " 'used_petro_per_zone_per_facies_dict'. " + "Check consistency with original job " + f"'{original_job_name}' and facies name '{facies_name}'" + ) + # Assign current facies model as only entry in the list + zone_model_dict_current["Facies Models"] = [current_facies_model_dict] + + # Loop over petro variable for this facies model and only keep + # the petro variables specified to be used + new_petro_variable_model_list = [] + new_variable_names = [] + current_petro_variable_model_list = current_facies_model_dict[ + "Variable Models" + ] + for petro_variable_dict in current_petro_variable_model_list: + petro_name = petro_variable_dict["VariableName"] + if petro_name in used_petro_var_list: + # Set a new name of the petro variables to keep + new_petro_name = set_new_var_name(facies_name, petro_name) + petro_variable_dict["VariableName"] = new_petro_name + new_variable_names.append(new_petro_name) + new_petro_variable_model_list.append(petro_variable_dict) + current_facies_model_dict["Variable Models"] = ( + new_petro_variable_model_list + ) + + original_variable_names = orig_job_arguments["VariableNames"] + # Only one element in this always ?????????? + original_corr_model_dict = current_facies_model_dict[ + "Correlation Model" + ][0] + + original_corr_matrix = original_corr_model_dict["CorrelationMatrix"] + new_variable_names, new_corr_matrix = ( + define_new_variable_names_and_correlation_matrix( + original_variable_names, + facies_name, + new_variable_names, + original_corr_matrix, + ) + ) + + original_corr_model_dict["CorrelationMatrix"] = new_corr_matrix + # Add variable names for current zone which is not already + # within the list + for var_name in new_variable_names: + if var_name not in new_variable_names_all_zones: + new_variable_names_all_zones.append(var_name) + + new_job_arguments_current["VariableNames"] = new_variable_names_all_zones + + new_job_name = facies_name + "_petro" + new_job_name = new_job_name.lower() + new_job_name_list.append(new_job_name) + print(f"Create job: {new_job_name}") + if debug_print: + print("-" * 100) + PP.pprint(new_job_arguments_current) + print("-" * 100) + new_job = create_copy_of_job( + owner_string_list, JOB_TYPE, new_job_arguments_current, new_job_name + ) + ok, err_msg_list, warn_msg_list = roxar.jobs.Job.check(new_job) + if not ok: + print("Error messages from created job object:") + for err_msg in err_msg_list: + print(f"{err_msg}") + print("\n") + print("Warnings from created job object:") + for warn_msg in warn_msg_list: + print(f"{warn_msg}") + print(f"\nThe job with name {new_job_name} is not saved.") + else: + print(f"Save new job: {new_job_name}") + new_job.save() + return new_job_name_list + + +def write_petro_job_to_file( + owner_string_list: List[str], job_type: str, job_name: str, filename: str +) -> None: + job_instance = roxar.jobs.Job.get_job( + owner=owner_string_list, type=job_type, name=job_name + ) + arguments = job_instance.get_arguments(True) + + print(f"Write file: {filename}") + with open(filename, "w") as outfile: + outfile.write(json.dumps(arguments, sort_keys=True, indent=3)) + outfile.write("\n") + + +if __name__ == "__main__": + config_file = "generate_petro_jobs.yml" + main(config_file) diff --git a/tests/rms/generate_jobs_testdata/F1_multi_zone_petro.txt b/tests/rms/generate_jobs_testdata/F1_multi_zone_petro.txt new file mode 100644 index 00000000..d0beeb43 --- /dev/null +++ b/tests/rms/generate_jobs_testdata/F1_multi_zone_petro.txt @@ -0,0 +1,174 @@ +{ + "Algorithm": "SIMULATION", + "ConditionOnBlockedWells": false, + "VariableNames": [ + "F1_P1", + "F1_P2" + ], + "Zone Models": [ + { + "Facies Models": [ + { + "Correlation Model": [ + { + "CorrelationMatrix": [ + [], + [ + 0.5 + ] + ] + } + ], + "FaciesName": "F1", + "Variable Models": [ + { + "Transform Sequence": [ + { + "EstimationMode": "FIXED", + "Mean": [ + { + "Automated": false, + "Mean": 0.30000001192092896, + "SequenceNumber": 1 + } + ], + "Truncate": [ + {} + ], + "WeightLog": "- none -" + } + ], + "VariableName": "F1_P1", + "Variogram Models": [ + { + "GeneralExponentialPower": 1.7999999523162842, + "Mode": "STANDARD", + "RangeAzimuth": 2500.0, + "RangeVertical": 25.0, + "TextureRoughness": 0.7999999523162842, + "Type": "GENERAL_EXPONENTIAL", + "VariogramSill": 0.029999999329447746, + "VariogramSillType": "CONSTANT" + } + ] + }, + { + "Transform Sequence": [ + { + "EstimationMode": "FIXED", + "Mean": [ + { + "Automated": false, + "Mean": 0.25, + "SequenceNumber": 1 + } + ], + "Truncate": [ + {} + ], + "WeightLog": "- none -" + } + ], + "VariableName": "F1_P2", + "Variogram Models": [ + { + "GeneralExponentialPower": 1.7999999523162842, + "Mode": "STANDARD", + "RangeAzimuth": 2500.0, + "RangeVertical": 25.0, + "TextureRoughness": 0.7999999523162842, + "Type": "GENERAL_EXPONENTIAL", + "VariogramSill": 0.02500000037252903, + "VariogramSillType": "CONSTANT" + } + ] + } + ] + } + ], + "ZoneName": "Zone1" + }, + { + "Facies Models": [ + { + "Correlation Model": [ + { + "CorrelationMatrix": [ + [], + [ + 0.5 + ] + ] + } + ], + "FaciesName": "F1", + "Variable Models": [ + { + "Transform Sequence": [ + { + "EstimationMode": "FIXED", + "Mean": [ + { + "Automated": false, + "Mean": 0.30000001192092896, + "SequenceNumber": 1 + } + ], + "Truncate": [ + {} + ], + "WeightLog": "- none -" + } + ], + "VariableName": "F1_P1", + "Variogram Models": [ + { + "GeneralExponentialPower": 1.7999999523162842, + "Mode": "STANDARD", + "RangeAzimuth": 2500.0, + "RangeVertical": 25.0, + "TextureRoughness": 0.7999999523162842, + "Type": "GENERAL_EXPONENTIAL", + "VariogramSill": 0.029999999329447746, + "VariogramSillType": "CONSTANT" + } + ] + }, + { + "Transform Sequence": [ + { + "EstimationMode": "FIXED", + "Mean": [ + { + "Automated": false, + "Mean": 0.25, + "SequenceNumber": 1 + } + ], + "Truncate": [ + {} + ], + "WeightLog": "- none -" + } + ], + "VariableName": "F1_P2", + "Variogram Models": [ + { + "GeneralExponentialPower": 1.7999999523162842, + "Mode": "STANDARD", + "RangeAzimuth": 2500.0, + "RangeVertical": 25.0, + "TextureRoughness": 0.7999999523162842, + "Type": "GENERAL_EXPONENTIAL", + "VariogramSill": 0.02500000037252903, + "VariogramSillType": "CONSTANT" + } + ] + } + ] + } + ], + "ZoneName": "Zone2" + } + ] +} diff --git a/tests/rms/generate_jobs_testdata/F1_single_zone_petro.txt b/tests/rms/generate_jobs_testdata/F1_single_zone_petro.txt new file mode 100644 index 00000000..ec81cdcd --- /dev/null +++ b/tests/rms/generate_jobs_testdata/F1_single_zone_petro.txt @@ -0,0 +1,90 @@ +{ + "ConditionOnBlockedWells": false, + "VariableNames": [ + "F1_P1", + "F1_P2" + ], + "Zone Models": [ + { + "Facies Models": [ + { + "Correlation Model": [ + { + "CorrelationMatrix": [ + [], + [ + 0.5 + ] + ] + } + ], + "FaciesName": "F1", + "Variable Models": [ + { + "Transform Sequence": [ + { + "EstimationMode": "FIXED", + "Mean": [ + { + "Automated": false, + "Mean": 0.30000001192092896, + "SequenceNumber": 1 + } + ], + "Truncate": [ + {} + ], + "WeightLog": "- none -" + } + ], + "VariableName": "F1_P1", + "Variogram Models": [ + { + "GeneralExponentialPower": 1.7999999523162842, + "Mode": "STANDARD", + "RangeAzimuth": 2500.0, + "RangeVertical": 25.0, + "TextureRoughness": 0.7999999523162842, + "Type": "GENERAL_EXPONENTIAL", + "VariogramSill": 9.999999747378752e-05, + "VariogramSillType": "CONSTANT" + } + ] + }, + { + "Transform Sequence": [ + { + "EstimationMode": "FIXED", + "Mean": [ + { + "Automated": false, + "Mean": 0.25, + "SequenceNumber": 1 + } + ], + "Truncate": [ + {} + ], + "WeightLog": "- none -" + } + ], + "VariableName": "F1_P2", + "Variogram Models": [ + { + "GeneralExponentialPower": 1.7999999523162842, + "Mode": "STANDARD", + "RangeAzimuth": 2500.0, + "RangeVertical": 25.0, + "TextureRoughness": 0.7999999523162842, + "Type": "GENERAL_EXPONENTIAL", + "VariogramSill": 9.999999747378752e-05, + "VariogramSillType": "CONSTANT" + } + ] + } + ] + } + ] + } + ] +} diff --git a/tests/rms/generate_jobs_testdata/F2_multi_zone_petro.txt b/tests/rms/generate_jobs_testdata/F2_multi_zone_petro.txt new file mode 100644 index 00000000..74589c7b --- /dev/null +++ b/tests/rms/generate_jobs_testdata/F2_multi_zone_petro.txt @@ -0,0 +1,174 @@ +{ + "Algorithm": "SIMULATION", + "ConditionOnBlockedWells": false, + "VariableNames": [ + "F2_P1", + "F2_P2" + ], + "Zone Models": [ + { + "Facies Models": [ + { + "Correlation Model": [ + { + "CorrelationMatrix": [ + [], + [ + 0.5 + ] + ] + } + ], + "FaciesName": "F2", + "Variable Models": [ + { + "Transform Sequence": [ + { + "EstimationMode": "FIXED", + "Mean": [ + { + "Automated": false, + "Mean": 0.10000000149011612, + "SequenceNumber": 1 + } + ], + "Truncate": [ + {} + ], + "WeightLog": "- none -" + } + ], + "VariableName": "F2_P1", + "Variogram Models": [ + { + "GeneralExponentialPower": 1.7999999523162842, + "Mode": "STANDARD", + "RangeAzimuth": 2500.0, + "RangeVertical": 25.0, + "TextureRoughness": 0.7999999523162842, + "Type": "GENERAL_EXPONENTIAL", + "VariogramSill": 0.009999999776482582, + "VariogramSillType": "CONSTANT" + } + ] + }, + { + "Transform Sequence": [ + { + "EstimationMode": "FIXED", + "Mean": [ + { + "Automated": false, + "Mean": 0.10000000149011612, + "SequenceNumber": 1 + } + ], + "Truncate": [ + {} + ], + "WeightLog": "- none -" + } + ], + "VariableName": "F2_P2", + "Variogram Models": [ + { + "GeneralExponentialPower": 1.7999999523162842, + "Mode": "STANDARD", + "RangeAzimuth": 2500.0, + "RangeVertical": 25.0, + "TextureRoughness": 0.7999999523162842, + "Type": "GENERAL_EXPONENTIAL", + "VariogramSill": 0.019999999552965164, + "VariogramSillType": "CONSTANT" + } + ] + } + ] + } + ], + "ZoneName": "Zone1" + }, + { + "Facies Models": [ + { + "Correlation Model": [ + { + "CorrelationMatrix": [ + [], + [ + 0.5 + ] + ] + } + ], + "FaciesName": "F2", + "Variable Models": [ + { + "Transform Sequence": [ + { + "EstimationMode": "FIXED", + "Mean": [ + { + "Automated": false, + "Mean": 0.10000000149011612, + "SequenceNumber": 1 + } + ], + "Truncate": [ + {} + ], + "WeightLog": "- none -" + } + ], + "VariableName": "F2_P1", + "Variogram Models": [ + { + "GeneralExponentialPower": 1.7999999523162842, + "Mode": "STANDARD", + "RangeAzimuth": 2500.0, + "RangeVertical": 25.0, + "TextureRoughness": 0.7999999523162842, + "Type": "GENERAL_EXPONENTIAL", + "VariogramSill": 0.009999999776482582, + "VariogramSillType": "CONSTANT" + } + ] + }, + { + "Transform Sequence": [ + { + "EstimationMode": "FIXED", + "Mean": [ + { + "Automated": false, + "Mean": 0.10000000149011612, + "SequenceNumber": 1 + } + ], + "Truncate": [ + {} + ], + "WeightLog": "- none -" + } + ], + "VariableName": "F2_P2", + "Variogram Models": [ + { + "GeneralExponentialPower": 1.7999999523162842, + "Mode": "STANDARD", + "RangeAzimuth": 2500.0, + "RangeVertical": 25.0, + "TextureRoughness": 0.7999999523162842, + "Type": "GENERAL_EXPONENTIAL", + "VariogramSill": 0.019999999552965164, + "VariogramSillType": "CONSTANT" + } + ] + } + ] + } + ], + "ZoneName": "Zone2" + } + ] +} diff --git a/tests/rms/generate_jobs_testdata/F2_single_zone_petro.txt b/tests/rms/generate_jobs_testdata/F2_single_zone_petro.txt new file mode 100644 index 00000000..94c1b37a --- /dev/null +++ b/tests/rms/generate_jobs_testdata/F2_single_zone_petro.txt @@ -0,0 +1,90 @@ +{ + "ConditionOnBlockedWells": false, + "VariableNames": [ + "F2_P1", + "F2_P2" + ], + "Zone Models": [ + { + "Facies Models": [ + { + "Correlation Model": [ + { + "CorrelationMatrix": [ + [], + [ + 0.5 + ] + ] + } + ], + "FaciesName": "F2", + "Variable Models": [ + { + "Transform Sequence": [ + { + "EstimationMode": "FIXED", + "Mean": [ + { + "Automated": false, + "Mean": 0.15000000596046448, + "SequenceNumber": 1 + } + ], + "Truncate": [ + {} + ], + "WeightLog": "- none -" + } + ], + "VariableName": "F2_P1", + "Variogram Models": [ + { + "GeneralExponentialPower": 1.7999999523162842, + "Mode": "STANDARD", + "RangeAzimuth": 2500.0, + "RangeVertical": 25.0, + "TextureRoughness": 0.7999999523162842, + "Type": "GENERAL_EXPONENTIAL", + "VariogramSill": 9.999999747378752e-05, + "VariogramSillType": "CONSTANT" + } + ] + }, + { + "Transform Sequence": [ + { + "EstimationMode": "FIXED", + "Mean": [ + { + "Automated": false, + "Mean": 0.10000000149011612, + "SequenceNumber": 1 + } + ], + "Truncate": [ + {} + ], + "WeightLog": "- none -" + } + ], + "VariableName": "F2_P2", + "Variogram Models": [ + { + "GeneralExponentialPower": 1.7999999523162842, + "Mode": "STANDARD", + "RangeAzimuth": 2500.0, + "RangeVertical": 25.0, + "TextureRoughness": 0.7999999523162842, + "Type": "GENERAL_EXPONENTIAL", + "VariogramSill": 9.999999747378752e-05, + "VariogramSillType": "CONSTANT" + } + ] + } + ] + } + ] + } + ] +} diff --git a/tests/rms/generate_jobs_testdata/F3_multi_zone_petro.txt b/tests/rms/generate_jobs_testdata/F3_multi_zone_petro.txt new file mode 100644 index 00000000..0cf19a38 --- /dev/null +++ b/tests/rms/generate_jobs_testdata/F3_multi_zone_petro.txt @@ -0,0 +1,174 @@ +{ + "Algorithm": "SIMULATION", + "ConditionOnBlockedWells": false, + "VariableNames": [ + "F3_P1", + "F3_P2" + ], + "Zone Models": [ + { + "Facies Models": [ + { + "Correlation Model": [ + { + "CorrelationMatrix": [ + [], + [ + 0.5 + ] + ] + } + ], + "FaciesName": "F3", + "Variable Models": [ + { + "Transform Sequence": [ + { + "EstimationMode": "FIXED", + "Mean": [ + { + "Automated": false, + "Mean": 0.05000000074505806, + "SequenceNumber": 1 + } + ], + "Truncate": [ + {} + ], + "WeightLog": "- none -" + } + ], + "VariableName": "F3_P1", + "Variogram Models": [ + { + "GeneralExponentialPower": 1.7999999523162842, + "Mode": "STANDARD", + "RangeAzimuth": 2500.0, + "RangeVertical": 25.0, + "TextureRoughness": 0.7999999523162842, + "Type": "GENERAL_EXPONENTIAL", + "VariogramSill": 0.009999999776482582, + "VariogramSillType": "CONSTANT" + } + ] + }, + { + "Transform Sequence": [ + { + "EstimationMode": "FIXED", + "Mean": [ + { + "Automated": false, + "Mean": 0.07000000029802322, + "SequenceNumber": 1 + } + ], + "Truncate": [ + {} + ], + "WeightLog": "- none -" + } + ], + "VariableName": "F3_P2", + "Variogram Models": [ + { + "GeneralExponentialPower": 1.7999999523162842, + "Mode": "STANDARD", + "RangeAzimuth": 2500.0, + "RangeVertical": 25.0, + "TextureRoughness": 0.7999999523162842, + "Type": "GENERAL_EXPONENTIAL", + "VariogramSill": 0.014999999664723873, + "VariogramSillType": "CONSTANT" + } + ] + } + ] + } + ], + "ZoneName": "Zone1" + }, + { + "Facies Models": [ + { + "Correlation Model": [ + { + "CorrelationMatrix": [ + [], + [ + 0.5 + ] + ] + } + ], + "FaciesName": "F3", + "Variable Models": [ + { + "Transform Sequence": [ + { + "EstimationMode": "FIXED", + "Mean": [ + { + "Automated": false, + "Mean": 0.05000000074505806, + "SequenceNumber": 1 + } + ], + "Truncate": [ + {} + ], + "WeightLog": "- none -" + } + ], + "VariableName": "F3_P1", + "Variogram Models": [ + { + "GeneralExponentialPower": 1.7999999523162842, + "Mode": "STANDARD", + "RangeAzimuth": 2500.0, + "RangeVertical": 25.0, + "TextureRoughness": 0.7999999523162842, + "Type": "GENERAL_EXPONENTIAL", + "VariogramSill": 0.009999999776482582, + "VariogramSillType": "CONSTANT" + } + ] + }, + { + "Transform Sequence": [ + { + "EstimationMode": "FIXED", + "Mean": [ + { + "Automated": false, + "Mean": 0.07000000029802322, + "SequenceNumber": 1 + } + ], + "Truncate": [ + {} + ], + "WeightLog": "- none -" + } + ], + "VariableName": "F3_P2", + "Variogram Models": [ + { + "GeneralExponentialPower": 1.7999999523162842, + "Mode": "STANDARD", + "RangeAzimuth": 2500.0, + "RangeVertical": 25.0, + "TextureRoughness": 0.7999999523162842, + "Type": "GENERAL_EXPONENTIAL", + "VariogramSill": 0.014999999664723873, + "VariogramSillType": "CONSTANT" + } + ] + } + ] + } + ], + "ZoneName": "Zone2" + } + ] +} diff --git a/tests/rms/generate_jobs_testdata/F3_single_zone_petro.txt b/tests/rms/generate_jobs_testdata/F3_single_zone_petro.txt new file mode 100644 index 00000000..a9b256b3 --- /dev/null +++ b/tests/rms/generate_jobs_testdata/F3_single_zone_petro.txt @@ -0,0 +1,90 @@ +{ + "ConditionOnBlockedWells": false, + "VariableNames": [ + "F3_P1", + "F3_P2" + ], + "Zone Models": [ + { + "Facies Models": [ + { + "Correlation Model": [ + { + "CorrelationMatrix": [ + [], + [ + 0.5 + ] + ] + } + ], + "FaciesName": "F3", + "Variable Models": [ + { + "Transform Sequence": [ + { + "EstimationMode": "FIXED", + "Mean": [ + { + "Automated": false, + "Mean": 0.019999999552965164, + "SequenceNumber": 1 + } + ], + "Truncate": [ + {} + ], + "WeightLog": "- none -" + } + ], + "VariableName": "F3_P1", + "Variogram Models": [ + { + "GeneralExponentialPower": 1.7999999523162842, + "Mode": "STANDARD", + "RangeAzimuth": 2500.0, + "RangeVertical": 25.0, + "TextureRoughness": 0.7999999523162842, + "Type": "GENERAL_EXPONENTIAL", + "VariogramSill": 9.999999747378752e-05, + "VariogramSillType": "CONSTANT" + } + ] + }, + { + "Transform Sequence": [ + { + "EstimationMode": "FIXED", + "Mean": [ + { + "Automated": false, + "Mean": 0.03999999910593033, + "SequenceNumber": 1 + } + ], + "Truncate": [ + {} + ], + "WeightLog": "- none -" + } + ], + "VariableName": "F3_P2", + "Variogram Models": [ + { + "GeneralExponentialPower": 1.7999999523162842, + "Mode": "STANDARD", + "RangeAzimuth": 2500.0, + "RangeVertical": 25.0, + "TextureRoughness": 0.7999999523162842, + "Type": "GENERAL_EXPONENTIAL", + "VariogramSill": 9.999999747378752e-05, + "VariogramSillType": "CONSTANT" + } + ] + } + ] + } + ] + } + ] +} diff --git a/tests/rms/generate_jobs_testdata/generate_original_multi_zone_job.yml b/tests/rms/generate_jobs_testdata/generate_original_multi_zone_job.yml new file mode 100644 index 00000000..13a5aec4 --- /dev/null +++ b/tests/rms/generate_jobs_testdata/generate_original_multi_zone_job.yml @@ -0,0 +1,31 @@ +grid_name: MultiZoneBox +grid_file_name: multizonegrid.roff +facies_real_name: Facies +facies_file_name: multizonefacies.roff +original_job_name: original_multi_zone +# mode is either: simulation or prediction +mode: simulation +original_job_settings: + zones: + Zone1: + F1: + P1: [0.3, 0.03] # [mean, stdev] + P2: [0.25, 0.025] + F2: + P1: [0.1, 0.01] + P2: [0.1, 0.02] + F3: + P1: [0.05, 0.01] + P2: [0.07, 0.015] + Zone2: + F1: + P1: [0.3, 0.03] + P2: [0.25, 0.025] + F2: + P1: [0.1, 0.01] + P2: [0.1, 0.02] + F3: + P1: [0.05, 0.01] + P2: [0.07, 0.015] + + diff --git a/tests/rms/generate_jobs_testdata/generate_original_single_zone_job.yml b/tests/rms/generate_jobs_testdata/generate_original_single_zone_job.yml new file mode 100644 index 00000000..65ef8ee4 --- /dev/null +++ b/tests/rms/generate_jobs_testdata/generate_original_single_zone_job.yml @@ -0,0 +1,20 @@ +grid_name: SingleZoneBox +grid_file_name: singlezonegrid.roff +facies_real_name: Facies +facies_file_name: singlezonefacies.roff +original_job_name: original_single_zone +# mode is either: simulation or prediction +mode: prediction +original_job_settings: + zones: + "": + F1: + P1: [0.3, 0.0001] # [mean, stdev] + P2: [0.25, 0.0001] + F2: + P1: [0.15, 0.0001] + P2: [0.1, 0.0001] + F3: + P1: [0.02, 0.0001] + P2: [0.04, 0.0001] + diff --git a/tests/rms/generate_jobs_testdata/generate_petro_multi_zone_jobs.yml b/tests/rms/generate_jobs_testdata/generate_petro_multi_zone_jobs.yml new file mode 100644 index 00000000..ebc4e809 --- /dev/null +++ b/tests/rms/generate_jobs_testdata/generate_petro_multi_zone_jobs.yml @@ -0,0 +1,25 @@ +# Name of grid model for the petrophysics jobs +grid_name: MultiZoneBox + +# Name of original petro job using facies realization as input +original_job_name: original_multi_zone + +# Use empty string as zone name for single zone grids and zone name for multizone grids. +# For each zone, specify facies and for each facies specify +# petro variables to be used as field parameters in ERT update + +# Example for a multizone grid with three zones: +used_petro_var: + Zone1: + F1: [P1, P2] + F2: [P1, P2] + F3: [P1, P2] + Zone2: + F1: [P1, P2] + F2: [P1, P2] + F3: [P1, P2] + + + + + diff --git a/tests/rms/generate_jobs_testdata/generate_petro_single_zone_jobs.yml b/tests/rms/generate_jobs_testdata/generate_petro_single_zone_jobs.yml new file mode 100644 index 00000000..42b10dac --- /dev/null +++ b/tests/rms/generate_jobs_testdata/generate_petro_single_zone_jobs.yml @@ -0,0 +1,14 @@ +# Name of grid model for the petrophysics jobs +grid_name: SingleZoneBox + +# Name of original petro job using facies realization as input +original_job_name: original_single_zone + +# Use default as zone name for single zone grids and zone name for multizone grids. +# For each zone, specify facies and for each facies specify +# petro variables to be used as field parameters in ERT update +used_petro_var: + default: + F1: [P1, P2] + F2: [P1, P2] + F3: [P1, P2] diff --git a/tests/rms/generate_jobs_testdata/multizonefacies.roff b/tests/rms/generate_jobs_testdata/multizonefacies.roff new file mode 100644 index 0000000000000000000000000000000000000000..f167bd67c5ce78b82cb379dd3fd4166e8da38830 GIT binary patch literal 528 zcmZ9I-A=z&%^ z<#km;GzVaf)b(Wg#WV2g1-JVQ3+7@YUk~`J8=~@Mx z+6KzeR8B!^9Mro`7mw5qemWQffW`x5eTeJsXxO%Cv$PsCBxYJxPDAbLOtm^wo2|_y z)#`k;Ha|ND+B7LUH6GLt&{hv~3+FI14cAT$exoZ2Z>DW+h_di8<}RG)aC2`c@5A`< zZq6!GWQ5boa^>JLma5PWZeL!!Q950A-n`skmYY057caGkT9GV19c!I`wubC%k@tKGGi#%VXg(ql^7asDxmORWuJ~H_EntX<}(|Jkz2j5A*o^%yVfGxtJ$tjQ5&(f)<~7ywCL^Ej&R>Jg;ZY z{IM59!n17S7s> LNOazkKPEo`?o2y;kD@Ux<`xz#ob)%d2w5m zO6#S*UzQ6X+lYjGlk%w2;ivPE@_NzijOCl0GkRk>vSKxi9Qz7a6WzZ3weOJqAGCzHvVF0}3BsZCb%hR#ycO4hrD)9s0*qivDqCb?ainW~n- zsTNR;hH?s0o2`I04XRFqC-oDw)x+GvDXdJ>wNryUn>M9Fmh9?-f!c|6%UNGKA zHR`!F+FnnMEJHm5cwD`>9GtA>P^90R#;UQmmNDEKUQlEKz9bTU|=k^wbCuq!j%{)Pi&phOx z=YEkEo}f7|Jmh;Vb(n{IWbpl(c!6ul{R$fMUNcY7;xo@UUMoD55gzhoeaIJ{DbZ4g zd3=55xv+p-%o8-`y=I=E#b+Mx^ZJk$o}eYq_cPb=_m9`i6Ex`~Z<^o)aI zGfUmF>A@@9if8_CltvDTj@YH6`f^?T|F`EwpB}~doQTK9#>+uG-i|*X#Dn(qfpK#E zv9BNXi+beyg8ew``EmFEu)L%B6aM`@OluR5c<+XLB;LbExL^LY^*`?Z|IhPB?=w|* fX-D&CWi8J$@L!eJ@Fg+&TKbFgXhNMg None: + new_variable_names, new_lower_corr_mat = ( + define_new_variable_names_and_correlation_matrix( + original_variable_names, + facies_name, + new_variable_names_input, + original_lower_corr_mat, + ) + ) + + print(f"Original var names: {original_variable_names}") + print(f"New var names: {new_variable_names}") + print(f"Original correlations: {original_lower_corr_mat}") + print(f"New correlations: {new_lower_corr_mat}") + + # Compare lists with reference + l1 = new_variable_names + l2 = reference_var_names + res = [x for x in l1 + l2 if x not in l1 or x not in l2] + assert len(res) == 0 + + # Compare corr matrix with reference + nrows = len(new_lower_corr_mat) + assert nrows == len(reference_corr_mat) + for j in range(nrows): + ncol = len(new_lower_corr_mat[j]) + assert ncol == len(reference_corr_mat[j]) + for i in range(ncol): + assert new_lower_corr_mat[j][i] == reference_corr_mat[j][i] + + +def create_original_petro_job(spec_dict: Dict) -> str: + grid_name = spec_dict["grid_name"] + owner_string_list = ["Grid models", grid_name, "Grid"] + job_name = spec_dict["original_job_name"] + petro_job = roxar.jobs.Job.create( + owner=owner_string_list, type=JOB_TYPE, name=job_name + ) + roxar.jobs.Job.license_checkout(job_types=[JOB_TYPE]) + job_arguments = define_setting_for_original_job(spec_dict) + petro_job.set_arguments(job_arguments) + checked_ok, err_msg_list, warn_msg_list = petro_job.check(job_arguments) + if not checked_ok: + print("Errors when checking job arguments:") + for s in err_msg_list: + print(s) + print("Warnings when checking job arguments:") + for s in warn_msg_list: + print(s) + raise ValueError("Errors in arguments for job") + print(f"Create original petrophysics job: {job_name}") + petro_job.save() + return job_name + + +def define_setting_for_original_job(spec_dict: Dict) -> Dict: + # Use specified zones, facies and variable names and selected model parameters, + # but assign default to all other settings + facies_real_name = spec_dict["facies_real_name"] + grid_name = spec_dict["grid_name"] + original_job_name = spec_dict["original_job_name"] + original_job_settings = spec_dict["original_job_settings"] + simulation_mode = spec_dict["mode"] + simulation_mode = simulation_mode.upper() + zone_dict = original_job_settings["zones"] + zone_model_list = [] + common_petro_set = None + common_petro_list = [] + for zone_name, facies_dict in zone_dict.items(): + facies_model_list = [] + for facies_name, petro_dict in facies_dict.items(): + var_model_list = [] + petro_list = list(petro_dict.keys()) + if common_petro_set is None: + common_petro_set = set(petro_list) + common_petro_list = copy.copy(petro_list) + else: + if len(common_petro_set.difference(set(petro_list))) > 0: + raise ValueError( + "Expecting same set of petro variables " + "for all facies and all zones in original petro " + f"job {original_job_name}" + ) + for petro_name, petro_model_param_list in petro_dict.items(): + model_param_mean = petro_model_param_list[0] + model_param_stdev = petro_model_param_list[1] + var_model_list.append( + { + "ModelingMode": "PREDICTION/SIMULATION", + "VariableName": petro_name, + "Variogram Models": [ + { + "Mode": "STANDARD", + "RangeAzimuth": 2500.0, + "RangeAzimuthNormal": 1000, + "RangeVertical": 25.0, + "Type": "GENERAL_EXPONENTIAL", + "GeneralExponentialPower": 1.8, + "VariogramSillType": "CONSTANT", + "VariogramSill": model_param_stdev, + }, + ], + "Transform Sequence": [ + { + "EstimationMode": "FIXED", + "WeightLog": "- none -", + "Truncate": [ + { + "Active": True, + "AppliedTo": "INPUT_AND_OUTPUT", + "Automated": False, + "Mode": "MIN", + "Min": 0, + "SequenceNumber": 0, + } + ], + "Mean": [ + { + "Active": True, + "Automated": False, + "Mean": model_param_mean, + "SequenceNumber": 1, + } + ], + }, + ], + } + ) + corr_model = [{"CorrelationMatrix": [[], [0.5]]}] + facies_model_list.append( + { + "FaciesName": facies_name, + "Variable Models": var_model_list, + "Correlation Model": corr_model, + } + ) + zone_model_list.append( + {"ZoneName": zone_name, "Facies Models": facies_model_list} + ) + + return { + "Algorithm": simulation_mode, + "ConditionOnBlockedWells": False, + "InputFaciesProperty": ["Grid models", grid_name, facies_real_name], + "VariableNames": common_petro_list, + "Zone Models": zone_model_list, + "PrefixOutputName": "original", + } + + +def rename_subgrids(xtgeo_grd: xtgeo.Grid) -> None: + nsubgrids = len(list(xtgeo_grd.subgrids.keys())) + subgrid_names = [] + for i in range(nsubgrids): + name_of_subgrid = f"Zone{i+1}" + subgrid_names.append(name_of_subgrid) + xtgeo_grd.rename_subgrids(subgrid_names) + + +# Here the temporary RMS project is created. It contains a grid, +# a facies parameter and an original petrophysical job +def create_project(): + """Create a tmp RMS project for testing, populate with basic data. + + After the yield command, the teardown phase will remove the tmp RMS project. + """ + prj1 = str(PRJ) + + print("\n******** Setup RMS project!\n") + if isdir(prj1): + print("Remove existing project! (1)") + shutil.rmtree(prj1) + + project = roxar.Project.create() + + rox = xtgeo.RoxUtils(project) + print("Roxar version is", rox.roxversion) + print("RMS version is", rox.rmsversion(rox.roxversion)) + assert "1." in rox.roxversion + + # Read specification for original petrophysical job + # using facies realization as input for single zone grid + spec_original_dict = read_specification_file( + CONFIG_FILE_ORIGINAL_SINGLE_ZONE_JOB, check=False + ) + rms_grid_name = spec_original_dict["grid_name"] + rms_facies_real_name = spec_original_dict["facies_real_name"] + grid_file_name = spec_original_dict["grid_file_name"] + facies_file_name = spec_original_dict["facies_file_name"] + + # populate with grid and props + grid_data = REFERENCE_DIR / grid_file_name + grd = xtgeo.grid_from_file(grid_data, fformat="roff") + grd.to_roxar(project, rms_grid_name) + facies_data = REFERENCE_DIR / facies_file_name + facies = xtgeo.gridproperty_from_file( + facies_data, name=rms_facies_real_name, fformat="roff" + ) + facies.to_roxar(project, rms_grid_name, rms_facies_real_name) + + job_name = create_original_petro_job(spec_original_dict) + petro_job = roxar.jobs.Job.get_job(OWNER_STRING_SINGLE_ZONE, JOB_TYPE, job_name) + print(f"Run {job_name}") + petro_job.execute() + + # Read specification for original petrophysical job + # using facies realization as input for multi zone grid + spec_original_dict = read_specification_file( + CONFIG_FILE_ORIGINAL_MULTI_ZONE_JOB, check=False + ) + rms_grid_name = spec_original_dict["grid_name"] + rms_facies_real_name = spec_original_dict["facies_real_name"] + grid_file_name = spec_original_dict["grid_file_name"] + facies_file_name = spec_original_dict["facies_file_name"] + + # populate with grid and props + grid_data = REFERENCE_DIR / grid_file_name + grd = xtgeo.grid_from_file(grid_data, fformat="roff") + rename_subgrids(grd) + grd.to_roxar(project, rms_grid_name) + facies_data = REFERENCE_DIR / facies_file_name + facies = xtgeo.gridproperty_from_file( + facies_data, name=rms_facies_real_name, fformat="roff" + ) + facies.to_roxar(project, rms_grid_name, rms_facies_real_name) + + job_name = create_original_petro_job(spec_original_dict) + petro_job = roxar.jobs.Job.get_job(OWNER_STRING_MULTI_ZONE, JOB_TYPE, job_name) + print(f"Run {job_name}") + petro_job.execute() + + project.save_as(prj1) + return project + + +# Now run the script to generate one petro job per facies +# using the same settings (same model parameters) +# as the original petro job for the +# petrophysical properties. +@pytest.mark.skipunlessroxar +def test_generate_jobs() -> None: + """Test generate_petro_jobs_for_field_update""" + + project = create_project() + check_rms_project(project) + + # Now run script to generate one petro job per facies for single zone grid + spec_case = read_specification_file(CONFIG_FILE_SINGLE_ZONE) + job_name_list = create_new_petro_job_per_facies(spec_case) + for n, job_name in enumerate(job_name_list): + filename = RESULTDIR / Path(job_name + "_single.txt") + reference_filename = REFERENCE_FILES_SINGLE_ZONE_GRID[n] + write_petro_job_to_file(OWNER_STRING_SINGLE_ZONE, JOB_TYPE, job_name, filename) + # Compare text files with job parameters with reference for single zone jobs + check = filecmp.cmp(filename, reference_filename) + if check: + print("Check OK for single zone grid") + assert check + + for n, job_name in enumerate(job_name_list): + petro_job = roxar.jobs.Job.get_job(OWNER_STRING_SINGLE_ZONE, JOB_TYPE, job_name) + print(f"Run {job_name}") + petro_job.execute() + + # Now run script to generate one petro job per facies for single zone grid + spec_case = read_specification_file(CONFIG_FILE_MULTI_ZONE) + job_name_list = create_new_petro_job_per_facies(spec_case) + for n, job_name in enumerate(job_name_list): + filename = RESULTDIR / Path(job_name + "_multi.txt") + reference_filename = REFERENCE_FILES_MULTI_ZONE_GRID[n] + write_petro_job_to_file(OWNER_STRING_MULTI_ZONE, JOB_TYPE, job_name, filename) + # Compare text files with job parameters with reference for single zone jobs + check = filecmp.cmp(filename, reference_filename) + if check: + print("Check OK for multi zone grid") + assert check + + for n, job_name in enumerate(job_name_list): + petro_job = roxar.jobs.Job.get_job(OWNER_STRING_MULTI_ZONE, JOB_TYPE, job_name) + print(f"Run {job_name}") + petro_job.execute() + + project.save() + project.close() + if REMOVE_RMS_PROJECT_AFTER_TEST: + print("\n******* Teardown RMS project!\n") + if isdir(PRJ): + print("Remove existing project!") + shutil.rmtree(PRJ) + if isdir(RESULTDIR): + print("Remove temporary files") + shutil.rmtree(RESULTDIR)