Source code for dataio.export.rms.simulator_fipregions_mapping

from __future__ import annotations

from pathlib import Path
from typing import Any, Final

import pyarrow as pa
import xtgeo

from fmu.dataio._export import ExportConfig, export_with_metadata
from fmu.dataio._logging import null_logger
from fmu.dataio.export._base import SimpleExportBase
from fmu.dataio.export._export_result import ExportResult, ExportResultItem
from fmu.datamodels import SimulatorFipregionsMappingResult
from fmu.datamodels.common.enums import Classification
from fmu.datamodels.fmu_results.enums import Content
from fmu.datamodels.standard_results.enums import (
    SimulatorFipregionsMapping,
    StandardResultName,
)

_logger: Final = null_logger(__name__)

FIPNAME: Final = "FIPNUM"


class _ExportFipZoneRegionMapping(SimpleExportBase):
    def __init__(self, mapping_table: pa.Table) -> None:
        super().__init__()

        self._mapping_table = mapping_table

    def _get_export_config(self) -> ExportConfig:
        """Export config for the standard result."""

        return (
            ExportConfig.builder()
            .content(Content.mapping)
            .file_config(
                name=FIPNAME,
                subfolder=StandardResultName.simulator_fipregions_mapping.value,
            )
            .access(Classification.internal, rep_include=False)
            .global_config(self._config)
            .standard_result(StandardResultName.simulator_fipregions_mapping)
            .table_config(table_index=SimulatorFipregionsMapping.index_columns())
            .build()
        )

    def _export_data_as_standard_result(self) -> ExportResult:
        export_config = self._get_export_config()

        absolute_export_path = export_with_metadata(export_config, self._mapping_table)
        _logger.debug("Fip mapping table exported to: %s", absolute_export_path)

        return ExportResult(
            items=[ExportResultItem(absolute_path=Path(absolute_export_path))],
        )

    def _validate_data_pre_export(self) -> None:
        """Data validations before export."""
        SimulatorFipregionsMappingResult.model_validate(self._mapping_table.to_pylist())


def _create_fipnum_from_region_and_zone(
    zone: xtgeo.GridProperty, region: xtgeo.GridProperty
) -> tuple[xtgeo.GridProperty, pa.Table]:
    """
    Create a FIPNUM property with a unique value per region / zone combination.
    The mappings from FIPNUM value to corresponding zone and region names are
    collected and returned as a table.
    """

    fipnum = xtgeo.GridProperty(zone, discrete=True, values=0)

    mapping = []
    fipvalue = 1
    for zonecode, zonename in zone.codes.items():
        for regcode, regname in region.codes.items():
            cell_filter = (region.values == regcode) & (zone.values == zonecode)
            fipnum.values[cell_filter] = fipvalue

            fipnum.codes[fipvalue] = f"{regname}_{zonename}"

            mapping.append({FIPNAME: fipvalue, "REGION": regname, "ZONE": zonename})

            fipvalue += 1

    return fipnum, pa.Table.from_pylist(mapping)


def _load_discrete_gridproperty(
    project: Any, grid_name: str, property_name: str
) -> xtgeo.GridProperty:
    """Load a grid property from RMS and validate that it is discrete."""
    prop = xtgeo.gridproperty_from_roxar(project, grid_name, property_name)

    if not prop.isdiscrete:
        raise ValueError(f"The property {property_name} must be discrete.")

    return prop


def _create_fipnum_in_project(
    project: Any, grid_name: str, region: str, zone: str
) -> pa.Table:
    """Create the FIPNUM property in the RMS project and return the mapping table."""
    region = _load_discrete_gridproperty(project, grid_name, region)
    zone = _load_discrete_gridproperty(project, grid_name, zone)

    fipnum, mapping_table = _create_fipnum_from_region_and_zone(zone, region)

    fipnum.to_roxar(project, grid_name, FIPNAME)

    return mapping_table


[docs] def create_fipnum_property( project: Any, grid_name: str, region: str, zone: str ) -> ExportResult: """Simplified interface for creating a FIPNUM property in RMS and exporting the corresponding zone / region mappings. The FIPNUM property will have a unique value per region / zone combination and the mapping between a FIPNUM value and the corresponding region and zone names will be exported as a standard result 'simulator_fipregions_mapping'. Args: project: The 'magic' project variable in RMS. grid_name: Name of the grid in RMS. region: Name of the region property in the grid model. zone: Name of the zone property in the grid model. Examples: Example usage in an RMS script:: from fmu.dataio.export.rms import create_fipnum_property export_results = create_fipnum_property( project, grid_name="Simgrid", region="Region", zone="Zone" ) for result in export_results.items: print(f"Fipnum mappings are exported to {result.absolute_path}") """ mapping_table = _create_fipnum_in_project(project, grid_name, region, zone) return _ExportFipZoneRegionMapping(mapping_table).export()