Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
HansKallekleiv committed Nov 7, 2024
1 parent f78f275 commit ce735d7
Show file tree
Hide file tree
Showing 25 changed files with 1,178 additions and 52 deletions.
40 changes: 30 additions & 10 deletions backend_py/primary/primary/routers/relperm/converters.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,37 @@
import base64

import numpy as np
import xtgeo
from numpy.typing import NDArray
from webviz_pkg.core_utils.b64 import b64_encode_float_array_as_float32

from primary.services.sumo_access.relperm_access import RelPermAccess, RelPermTableInfo
from primary.services.relperm_assembler.relperm_assembler import (
RelPermTableInfo,
RelPermSaturationAxis,
RelPermRealizationData,
)

from . import schemas


def to_api_relperm_table_info(table_info: RelPermTableInfo) -> schemas.RelPermTableInfo:


return schemas.RelPermTableInfo(
table_name=table_info.table_name,
column_names=table_info.column_names)
saturation_axes=[to_api_relperm_saturation_axis(axis) for axis in table_info.saturation_axes],
satnums=table_info.satnums,
)


def to_api_relperm_saturation_axis(axis: RelPermSaturationAxis) -> schemas.RelPermSaturationAxis:

return schemas.RelPermSaturationAxis(
saturation_name=axis.saturation_name,
relperm_curve_names=axis.relperm_curve_names,
capillary_pressure_curve_names=axis.capillary_pressure_curve_names,
)


def to_api_relperm_ensemble_data(data: RelPermRealizationData) -> schemas.RelPermRealizationData:

return schemas.RelPermRealizationData(
saturation_axis_data=data.saturation_axis_data,
satnum_data=[
schemas.RelPermSatNumData(satnum=satnum_data.satnum, relperm_curves_data=satnum_data.relperm_curves_data)
for satnum_data in data.satnum_data
],
realization=data.realization,
)
53 changes: 45 additions & 8 deletions backend_py/primary/primary/routers/relperm/router.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import logging
from typing import Annotated, List
from typing import Annotated, List, Any

from fastapi import APIRouter, Depends, Query

from primary.auth.auth_helper import AuthHelper
from primary.services.sumo_access.relperm_access import RelPermAccess
from primary.services.relperm_assembler.relperm_assembler import RelPermAssembler
from primary.services.utils.authenticated_user import AuthenticatedUser

from . import schemas
Expand All @@ -15,13 +16,49 @@
router = APIRouter()


@router.get("/table_definition")
async def get_table_definition(
@router.get("/table_names")
async def get_table_names(
authenticated_user: Annotated[AuthenticatedUser, Depends(AuthHelper.get_authenticated_user)],
case_uuid: Annotated[str, Query(description="Sumo case uuid")],
ensemble_name: Annotated[str, Query(description="Ensemble name")],
) -> List[schemas.RelPermTableInfo]:
access = await RelPermAccess.from_case_uuid_async(authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name)
relperm_tables_info = await access.get_relperm_tables_info()
return [converters.to_api_relperm_table_info(table_info) for table_info in relperm_tables_info]

) -> List[str]:
access = await RelPermAccess.from_case_uuid_async(
authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name
)
return await access.get_relperm_table_names()


@router.get("/table_info")
async def get_table_info(
authenticated_user: Annotated[AuthenticatedUser, Depends(AuthHelper.get_authenticated_user)],
case_uuid: Annotated[str, Query(description="Sumo case uuid")],
ensemble_name: Annotated[str, Query(description="Ensemble name")],
table_name: Annotated[str, Query(description="Table name")],
) -> schemas.RelPermTableInfo:
access = await RelPermAccess.from_case_uuid_async(
authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name
)
assembler = RelPermAssembler(access)
relperm_table_info = await assembler.get_relperm_table_info(table_name)

return converters.to_api_relperm_table_info(relperm_table_info)


@router.get("/saturation_and_curve_data")
async def get_saturation_and_curve_data(
authenticated_user: Annotated[AuthenticatedUser, Depends(AuthHelper.get_authenticated_user)],
case_uuid: Annotated[str, Query(description="Sumo case uuid")],
ensemble_name: Annotated[str, Query(description="Ensemble name")],
table_name: Annotated[str, Query(description="Table name")],
saturation_axis_name: Annotated[str, Query(description="Saturation axis name")],
curve_names: Annotated[List[str], Query(description="Curve names")],
satnums: Annotated[List[int], Query(description="Satnums")],
) -> List[schemas.RelPermRealizationData]:

access = await RelPermAccess.from_case_uuid_async(
authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name
)
assembler = RelPermAssembler(access)
relperm_data = await assembler.get_relperm_ensemble_data(table_name, saturation_axis_name, curve_names, satnums)

return [converters.to_api_relperm_ensemble_data(data) for data in relperm_data]
21 changes: 20 additions & 1 deletion backend_py/primary/primary/routers/relperm/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,25 @@

from pydantic import BaseModel


class RelPermSaturationAxis(BaseModel):
saturation_name: str
relperm_curve_names: List[str]
capillary_pressure_curve_names: List[str]


class RelPermTableInfo(BaseModel):
table_name: str
column_names: List[str]
saturation_axes: List[RelPermSaturationAxis]
satnums: List[int]


class RelPermSatNumData(BaseModel):
satnum: int
relperm_curves_data: List[List[float]]


class RelPermRealizationData(BaseModel):
saturation_axis_data: List[float]
satnum_data: List[RelPermSatNumData]
realization: int
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
from enum import Enum
from typing import List
import logging
from dataclasses import dataclass
import numpy as np
from scipy.interpolate import interp1d
import polars as pl
from primary.services.sumo_access.relperm_access import RelPermAccess
from primary.services.service_exceptions import (
Service,
NoDataError,
InvalidDataError,
)

LOGGER = logging.getLogger(__name__)


class RelPermFamily(str, Enum):
"""Enumeration of relative permeability keyword families"""

FAMILY_1 = "family_1" # SWOF, SGOF, SLGOF family
FAMILY_2 = "family_2" # SWFN, SGFN, SOF3 family


RELPERM_FAMILIES = {
1: ["SWOF", "SGOF", "SLGOF"],
2: ["SWFN", "SGFN", "SOF3"],
}


@dataclass
class RelPermSaturationAxis:
saturation_name: str
relperm_curve_names: List[str]
capillary_pressure_curve_names: List[str]


@dataclass
class RelPermTableInfo:
table_name: str
saturation_axes: List[RelPermSaturationAxis]
satnums: List[int]


@dataclass
class RelPermSatNumData:
satnum: int
relperm_curves_data: List[List[float]]


@dataclass
class RelPermRealizationData:
saturation_axis_data: List[float]
satnum_data: List[RelPermSatNumData]
realization: int


class RelPermAssembler:
def __init__(self, relperm_access: RelPermAccess):
self._relperm_access = relperm_access

async def get_relperm_table_info(self, relperm_table_name: str):
single_realization_table = await self._relperm_access.get_single_realization_table(relperm_table_name)
table_columns = single_realization_table.columns
satnums = extract_satnums_from_relperm_table(single_realization_table)
all_keywords = extract_keywords_from_relperm_table(single_realization_table)
family = extract_familiy_info_from_keywords(all_keywords)
saturation_infos = extract_saturation_axes_from_relperm_table(table_columns, family)

return RelPermTableInfo(
table_name=relperm_table_name, saturation_axes=saturation_infos, satnums=sorted(satnums)
)

async def get_relperm_ensemble_data(
self, relperm_table_name: str, saturation_axis_name: str, curve_names: List[str], satnums: List[int]
) -> List[RelPermRealizationData]:
realizations_table: pl.DataFrame = await self._relperm_access.get_relperm_table(relperm_table_name)
table_columns = realizations_table.columns

if saturation_axis_name not in table_columns:
raise NoDataError(
f"Saturation axis {saturation_axis_name} not found in table {relperm_table_name}",
Service.GENERAL,
)

for curve_name in curve_names:
if curve_name not in table_columns:
raise NoDataError(
f"Curve {curve_name} not found in saturation axis {saturation_axis_name} in table {relperm_table_name}",
Service.GENERAL,
)

columns_to_use = [saturation_axis_name] + curve_names + ["REAL", "SATNUM"]
filtered_table = (
realizations_table.select(columns_to_use)
.filter((realizations_table["SATNUM"].cast(pl.Int32).is_in(satnums)))
.drop_nulls()
.sort(saturation_axis_name)
)
shared_saturation_axis = np.linspace(0, 1, 100)
real_data: List[RelPermRealizationData] = []
for _real, real_table in filtered_table.group_by("REAL"):
satnum_data = []
for _satnum, satnum_table in real_table.group_by("SATNUM"):
table_to = satnum_table.sort(saturation_axis_name)
original_saturation = table_to[saturation_axis_name].to_numpy()

# Interpolate to get shared axis
interpolated_curves = []
for curve_name in curve_names:
original_values = table_to[curve_name]

interpolator = interp1d(
original_saturation,
original_values,
kind="cubic",
bounds_error=False,
fill_value=(original_values[0], original_values[-1]),
)

# Interpolate to shared axis
interpolated_values = interpolator(shared_saturation_axis)
interpolated_curves.append(interpolated_values.tolist())
satnum_data.append(
RelPermSatNumData(
satnum=table_to["SATNUM"][0],
relperm_curves_data=[table_to[curve_name].to_list() for curve_name in curve_names],
)
)
real_data.append(
RelPermRealizationData(
saturation_axis_data=table_to[saturation_axis_name].to_list(),
satnum_data=satnum_data,
realization=table_to["REAL"][0],
)
)
return real_data


def extract_keywords_from_relperm_table(relperm_table: pl.DataFrame) -> List[str]:
return relperm_table["KEYWORD"].unique().to_list()


def extract_satnums_from_relperm_table(relperm_table: pl.DataFrame) -> List[int]:
return relperm_table["SATNUM"].cast(pl.Int32).unique().to_list()


def extract_familiy_info_from_keywords(keywords: List[str]) -> RelPermFamily:

if any(keyword in RELPERM_FAMILIES[1] for keyword in keywords):
if any(keyword in RELPERM_FAMILIES[2] for keyword in keywords):
raise InvalidDataError(
"Mix of keyword family 1 and 2, currently only support one family at this time.",
Service.GENERAL,
)
return RelPermFamily.FAMILY_1

elif not all(keyword in RELPERM_FAMILIES[2] for keyword in keywords):
raise InvalidDataError(
"Unrecognized saturation table keyword in data. This should not occur unless "
"there has been changes to res2df. Update of this plugin might be required.",
Service.GENERAL,
)
else:
return RelPermFamily.FAMILY_2


def extract_saturation_axes_from_relperm_table(
relperm_table_columns: List[str], relperm_family: RelPermFamily
) -> List[RelPermSaturationAxis]:
saturation_infos = []
if relperm_family == RelPermFamily.FAMILY_1:
if "SW" in relperm_table_columns:
saturation_infos.append(
RelPermSaturationAxis(
saturation_name="SW",
relperm_curve_names=[
curve_name for curve_name in ["KRWO", "KRW"] if curve_name in relperm_table_columns
],
capillary_pressure_curve_names=[
curve_name for curve_name in ["PCOW"] if curve_name in relperm_table_columns
],
)
)
if "SG" in relperm_table_columns:
saturation_infos.append(
RelPermSaturationAxis(
saturation_name="SG",
relperm_curve_names=[
curve_name for curve_name in ["KRG", "KROG"] if curve_name in relperm_table_columns
],
capillary_pressure_curve_names=[
curve_name for curve_name in ["PCOG"] if curve_name in relperm_table_columns
],
)
)

if relperm_family == RelPermFamily.FAMILY_2:
if "SW" in relperm_table_columns:
saturation_infos.append(
RelPermSaturationAxis(
saturation_name="SW",
relperm_curve_names=[curve_name for curve_name in ["KRW"] if curve_name in relperm_table_columns],
capillary_pressure_curve_names=[
curve_name for curve_name in ["PCOW"] if curve_name in relperm_table_columns
],
)
)
if "SG" in relperm_table_columns:
saturation_infos.append(
RelPermSaturationAxis(
saturation_name="SG",
relperm_curve_names=[curve_name for curve_name in ["KRG"] if curve_name in relperm_table_columns],
capillary_pressure_curve_names=[
curve_name for curve_name in ["PCOG"] if curve_name in relperm_table_columns
],
)
)
if "SO" in relperm_table_columns:
saturation_infos.append(
RelPermSaturationAxis(
saturation_name="SO",
relperm_curve_names=[
curve_name for curve_name in ["KROW", "KROG"] if curve_name in relperm_table_columns
],
capillary_pressure_curve_names=[],
)
)
return saturation_infos
Loading

0 comments on commit ce735d7

Please sign in to comment.