Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rft improvements #774

Merged
merged 5 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions backend_py/primary/primary/routers/rft/converters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from primary.services.sumo_access.rft_types import RftTableDefinition

from . import schemas


def to_api_table_definition(
table_definition: RftTableDefinition,
) -> schemas.RftTableDefinition:
"""Converts the table definitions from the sumo service to the API format"""
return schemas.RftTableDefinition(
response_names=table_definition.response_names,
well_infos=[
schemas.RftWellInfo(
well_name=well_info.well_name,
timestamps_utc_ms=well_info.timestamps_utc_ms,
)
for well_info in table_definition.well_infos
],
)
15 changes: 6 additions & 9 deletions backend_py/primary/primary/routers/rft/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,23 @@
from primary.services.utils.authenticated_user import AuthenticatedUser

from . import schemas
from . import converters

LOGGER = logging.getLogger(__name__)

router = APIRouter()


@router.get("/rft_info")
async def get_rft_info(
@router.get("/table_definition")
async def get_table_definition(
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.RftInfo]:
) -> schemas.RftTableDefinition:
access = await RftAccess.from_case_uuid_async(authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name)
rft_well_list = await access.get_rft_info()
rft_table_def = await access.get_rft_info()

ret_rft_well_list: list[schemas.RftInfo] = []
for rftinfo in rft_well_list:
ret_rft_well_list.append(schemas.RftInfo.model_validate(rftinfo.model_dump()))

return ret_rft_well_list
return converters.to_api_table_definition(rft_table_def)


@router.get("/realization_data")
Expand Down
7 changes: 6 additions & 1 deletion backend_py/primary/primary/routers/rft/schemas.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
from pydantic import BaseModel


class RftInfo(BaseModel):
class RftWellInfo(BaseModel):
well_name: str
timestamps_utc_ms: list[int]


class RftTableDefinition(BaseModel):
response_names: list[str]
well_infos: list[RftWellInfo]


class RftRealizationData(BaseModel):
well_name: str
realization: int
Expand Down
62 changes: 44 additions & 18 deletions backend_py/primary/primary/services/sumo_access/rft_access.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,22 @@
from fmu.sumo.explorer.objects import Case, TableCollection
from webviz_pkg.core_utils.perf_timer import PerfTimer

from primary.services.service_exceptions import (
Service,
NoDataError,
InvalidDataError,
MultipleDataMatchesError,
)

from ._helpers import create_sumo_client, create_sumo_case_async
from .rft_types import RftInfo, RftRealizationData
from .rft_types import RftTableDefinition, RftWellInfo, RftRealizationData

LOGGER = logging.getLogger(__name__)


ALLOWED_RFT_RESPONSE_NAMES = ["PRESSURE", "SGAS", "SWAT", "SOIL"]


class RftAccess:
def __init__(self, case: Case, iteration_name: str):
self._case: Case = case
Expand All @@ -26,18 +36,24 @@ async def from_case_uuid_async(cls, access_token: str, case_uuid: str, iteration
case: Case = await create_sumo_case_async(client=sumo_client, case_uuid=case_uuid, want_keepalive_pit=False)
return RftAccess(case=case, iteration_name=iteration_name)

async def get_rft_info(self) -> list[RftInfo]:
table = await get_concatenated_rft_table(self._case, self._iteration_name, column_names=["PRESSURE"])
rft_well_infos: list[RftInfo] = []
async def get_rft_info(self) -> RftTableDefinition:
rft_table_collection = await get_rft_table_collection(self._case, self._iteration_name, column_name=None)

columns = await rft_table_collection.columns_async
available_response_names = [col for col in columns if col in ALLOWED_RFT_RESPONSE_NAMES]
table = await get_concatenated_rft_table(
self._case, self._iteration_name, column_names=available_response_names
)
rft_well_infos: list[RftWellInfo] = []
well_names = table["WELL"].unique().tolist()

for well_name in well_names:
well_table = table.filter(pc.equal(table["WELL"], well_name))
timestamps_utc_ms = sorted(list(set(well_table["DATE"].to_numpy().astype(int).tolist())))

rft_well_infos.append(RftInfo(well_name=well_name, timestamps_utc_ms=timestamps_utc_ms))
rft_well_infos.append(RftWellInfo(well_name=well_name, timestamps_utc_ms=timestamps_utc_ms))

return rft_well_infos
return RftTableDefinition(response_names=available_response_names, well_infos=rft_well_infos)

async def get_rft_well_realization_data(
self,
Expand Down Expand Up @@ -112,7 +128,10 @@ async def _load_arrow_table_for_from_sumo(case: Case, iteration_name: str, colum
if await rft_table_collection.length_async() == 0:
return None
if await rft_table_collection.length_async() > 1:
raise ValueError(f"Multiple tables found for vector {column_name=}")
raise MultipleDataMatchesError(
f"Multiple rft tables found in case={case}, iteration={iteration_name}: {column_name=}",
Service.SUMO,
)

sumo_table = await rft_table_collection.getitem_async(0)
# print(f"{sumo_table.format=}")
Expand All @@ -129,26 +148,29 @@ async def _load_arrow_table_for_from_sumo(case: Case, iteration_name: str, colum

# Verify that we got the expected columns
if not "DATE" in table.column_names:
raise ValueError("Table does not contain a DATE column")
raise InvalidDataError("Table does not contain a DATE column", Service.SUMO)

if not "REAL" in table.column_names:
raise ValueError("Table does not contain a REAL column")
raise InvalidDataError("Table does not contain a REAL column", Service.SUMO)
if not column_name in table.column_names:
raise ValueError(f"Table does not contain a {column_name} column")
raise InvalidDataError(f"Table does not contain a {column_name} column", Service.SUMO)
if table.num_columns != 4:
raise ValueError("Table should contain exactly 4 columns")
raise InvalidDataError("Table should contain exactly 4 columns", Service.SUMO)

# Verify that we got the expected columns
if sorted(table.column_names) != sorted(["DATE", "REAL", "WELL", column_name]):
raise ValueError(f"Unexpected columns in table {table.column_names=}")
raise InvalidDataError(f"Unexpected columns in table {table.column_names=}", Service.SUMO)

# Verify that the column datatypes are as we expect
schema = table.schema
if schema.field("DATE").type != pa.timestamp("ms"):
raise ValueError(f"Unexpected type for DATE column {schema.field('DATE').type=}")
raise InvalidDataError(f"Unexpected type for DATE column {schema.field('DATE').type=}", Service.SUMO)
if schema.field("REAL").type != pa.int16():
raise ValueError(f"Unexpected type for REAL column {schema.field('REAL').type=}")
raise InvalidDataError(f"Unexpected type for REAL column {schema.field('REAL').type=}", Service.SUMO)
if schema.field(column_name).type != pa.float32():
raise ValueError(f"Unexpected type for {column_name} column {schema.field(column_name).type=}")
raise InvalidDataError(
f"Unexpected type for {column_name} column {schema.field(column_name).type=}", Service.SUMO
)

LOGGER.debug(
f"Loaded arrow table from Sumo in: {timer.elapsed_ms()}ms ("
Expand All @@ -170,7 +192,6 @@ async def get_rft_table_collection(
iteration=iteration_name,
)
table_names = await rft_table_collection.names_async
print(table_names)
rft_table_collection = case.tables.filter(
aggregation="collection",
tagname="rft",
Expand All @@ -179,8 +200,13 @@ async def get_rft_table_collection(
)
table_names = await rft_table_collection.names_async
if len(table_names) == 0:
raise ValueError("No rft table collections found")
raise NoDataError(
f"No rft table collections found in case={case.uuid}, iteration={iteration_name}", Service.SUMO
)
if len(table_names) == 1:
return rft_table_collection

raise ValueError(f"Multiple rft table collections found: {table_names}. Expected only one.")
raise MultipleDataMatchesError(
f"Multiple rft table collections found in case={case.uuid}, iteration={iteration_name}: {table_names=}",
Service.SUMO,
)
7 changes: 6 additions & 1 deletion backend_py/primary/primary/services/sumo_access/rft_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@ class RftSumoTableSchema(BaseModel):
column_names: list[str]


class RftInfo(BaseModel):
class RftWellInfo(BaseModel):
well_name: str
timestamps_utc_ms: list[int]


class RftTableDefinition(BaseModel):
response_names: list[str]
well_infos: list[RftWellInfo]


class RftRealizationData(BaseModel):
well_name: str
realization: int
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,11 @@ export type { PolygonsMeta as PolygonsMeta_api } from './models/PolygonsMeta';
export type { PolylineIntersection as PolylineIntersection_api } from './models/PolylineIntersection';
export type { PvtData as PvtData_api } from './models/PvtData';
export type { RepeatedTableColumnData as RepeatedTableColumnData_api } from './models/RepeatedTableColumnData';
export type { RftInfo as RftInfo_api } from './models/RftInfo';
export type { RftObservation as RftObservation_api } from './models/RftObservation';
export type { RftObservations as RftObservations_api } from './models/RftObservations';
export type { RftRealizationData as RftRealizationData_api } from './models/RftRealizationData';
export type { RftTableDefinition as RftTableDefinition_api } from './models/RftTableDefinition';
export type { RftWellInfo as RftWellInfo_api } from './models/RftWellInfo';
export type { SeismicCubeMeta as SeismicCubeMeta_api } from './models/SeismicCubeMeta';
export type { SeismicFenceData as SeismicFenceData_api } from './models/SeismicFenceData';
export type { SeismicFencePolyline as SeismicFencePolyline_api } from './models/SeismicFencePolyline';
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/api/models/RftTableDefinition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* generated using openapi-typescript-codegen -- do no edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { RftWellInfo } from './RftWellInfo';
export type RftTableDefinition = {
response_names: Array<string>;
well_infos: Array<RftWellInfo>;
};

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type RftInfo = {
export type RftWellInfo = {
well_name: string;
timestamps_utc_ms: Array<number>;
};
Expand Down
12 changes: 6 additions & 6 deletions frontend/src/api/services/RftService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,26 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { RftInfo } from '../models/RftInfo';
import type { RftRealizationData } from '../models/RftRealizationData';
import type { RftTableDefinition } from '../models/RftTableDefinition';
import type { CancelablePromise } from '../core/CancelablePromise';
import type { BaseHttpRequest } from '../core/BaseHttpRequest';
export class RftService {
constructor(public readonly httpRequest: BaseHttpRequest) {}
/**
* Get Rft Info
* Get Table Definition
* @param caseUuid Sumo case uuid
* @param ensembleName Ensemble name
* @returns RftInfo Successful Response
* @returns RftTableDefinition Successful Response
* @throws ApiError
*/
public getRftInfo(
public getTableDefinition(
caseUuid: string,
ensembleName: string,
): CancelablePromise<Array<RftInfo>> {
): CancelablePromise<RftTableDefinition> {
return this.httpRequest.request({
method: 'GET',
url: '/rft/rft_info',
url: '/rft/table_definition',
query: {
'case_uuid': caseUuid,
'ensemble_name': ensembleName,
Expand Down
36 changes: 30 additions & 6 deletions frontend/src/modules/Rft/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,40 @@
import { RftRealizationData_api } from "@api";
import { InterfaceInitialization } from "@framework/UniDirectionalModuleComponentsInterface";
import { UseQueryResult } from "@tanstack/react-query";

import { rftWellAddressAtom } from "./settings/atoms/baseAtoms";
import { RftWellAddress } from "./typesAndEnums";

type SettingsToViewInterface = { rftWellAddress: RftWellAddress | null };
import { validRealizationNumbersAtom } from "./settings/atoms/baseAtoms";
import {
selectedRftResponseNameAtom,
selectedRftTimestampsUtcMsAtom,
selectedRftWellNameAtom,
} from "./settings/atoms/derivedAtoms";
import { rftRealizationDataQueryAtom } from "./settings/atoms/queryAtoms";

type SettingsToViewInterface = {
rftDataQuery: UseQueryResult<RftRealizationData_api[], Error>;
wellName: string | null;
responseName: string | null;
timeStampsUtcMs: number | null;
realizationNums: number[] | null;
};
export type Interfaces = {
settingsToView: SettingsToViewInterface;
};

export const settingsToViewInterfaceInitialization: InterfaceInitialization<SettingsToViewInterface> = {
rftWellAddress: (get) => {
return get(rftWellAddressAtom);
rftDataQuery: (get) => {
return get(rftRealizationDataQueryAtom);
},
wellName: (get) => {
return get(selectedRftWellNameAtom);
},
responseName: (get) => {
return get(selectedRftResponseNameAtom);
},
timeStampsUtcMs: (get) => {
return get(selectedRftTimestampsUtcMsAtom);
},
realizationNums: (get) => {
return get(validRealizationNumbersAtom);
},
};
52 changes: 0 additions & 52 deletions frontend/src/modules/Rft/queryHooks.ts

This file was deleted.

Loading
Loading