Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import logging
from typing import List, Optional
from typing import List, Optional, Tuple

import pandas as pd
import pyarrow as pa
Expand All @@ -12,7 +12,6 @@
from .sumo_client_factory import create_sumo_client
from .parameter_types import (
EnsembleParameter,
EnsembleParameters,
EnsembleSensitivity,
EnsembleSensitivityCase,
SensitivityType,
Expand All @@ -35,7 +34,7 @@ def from_ensemble_name(cls, access_token: str, case_uuid: str, ensemble_name: st
sumo_client = create_sumo_client(access_token)
return cls(sumo_client=sumo_client, case_uuid=case_uuid, ensemble_name=ensemble_name)

async def get_parameters_and_sensitivities_async(self) -> EnsembleParameters:
async def get_parameters_and_sensitivities_async(self) -> Tuple[List[EnsembleParameter], List[EnsembleSensitivity]]:
"""Retrieve parameters for an ensemble"""
perf_metrics = PerfMetrics()

Expand Down Expand Up @@ -77,20 +76,17 @@ async def get_parameters_and_sensitivities_async(self) -> EnsembleParameters:
f"ParameterAccess.get_parameters_and_sensitivities_async() took: {perf_metrics.to_string()}, {self._case_uuid=}, {self._ensemble_name=}"
)

return EnsembleParameters(
parameters=ensemble_parameters,
sensitivities=sensitivities,
)
return ensemble_parameters, sensitivities

async def get_parameter_async(self, parameter_name: str) -> EnsembleParameter:
"""Retrieve a single parameter for an ensemble"""
parameters = await self.get_parameters_and_sensitivities_async()
return next(parameter for parameter in parameters.parameters if parameter.name == parameter_name)
parameters, _ = await self.get_parameters_and_sensitivities_async()
return next(parameter for parameter in parameters if parameter.name == parameter_name)


def create_ensemble_sensitivities(
sumo_ensemble_parameters: List[EnsembleParameter],
) -> Optional[List[EnsembleSensitivity]]:
) -> List[EnsembleSensitivity]:
"""Extract sensitivities from a list of SumoEnsembleParameter objects"""
sensitivities = []

Expand All @@ -103,7 +99,7 @@ def create_ensemble_sensitivities(
None,
)
if sens_case_parameter is None or sens_name_parameter is None:
return None
return []
df = pd.DataFrame(
{
"name": sens_name_parameter.values,
Expand All @@ -119,7 +115,7 @@ def create_ensemble_sensitivities(
cases=create_ensemble_sensitivity_cases(group),
)
)
return sensitivities if sensitivities else None
return sensitivities


def find_sensitivity_type(sens_case_names: List[str]) -> SensitivityType:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,3 @@ class EnsembleSensitivity(BaseModel):
name: str
type: SensitivityType
cases: List[EnsembleSensitivityCase]


class EnsembleParameters(BaseModel): # Find a better name
"""Description/data for all parameters in an ensemble
type: "sensitivity" | "historymatch" | "prediction ??"
"""

parameters: List[EnsembleParameter]
sensitivities: Optional[List[EnsembleSensitivity]] = None
47 changes: 47 additions & 0 deletions backend_py/primary/primary/routers/parameters/converters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from typing import List

from webviz_services.sumo_access.parameter_types import EnsembleParameter, EnsembleSensitivity
from . import schemas


def to_api_parameters(parameters: List[EnsembleParameter]) -> List[schemas.EnsembleParameter]:
"""
Convert Sumo ensemble parameters to API ensemble parameters
"""
api_parameters = []
for parameter in parameters:
api_parameters.append(
schemas.EnsembleParameter(
name=parameter.name,
isLogarithmic=parameter.is_logarithmic,
isDiscrete=parameter.is_discrete,
isConstant=parameter.is_constant,
groupName=parameter.group_name,
descriptiveName=parameter.descriptive_name,
realizations=parameter.realizations,
values=parameter.values,
)
)
return api_parameters


def to_api_sensitivities(sensitivities: List[EnsembleSensitivity]) -> List[schemas.EnsembleSensitivity]:
"""
Convert Sumo ensemble sensitivities to API ensemble sensitivities
"""
api_sensitivities = []
for sensitivity in sensitivities:
api_sensitivities.append(
schemas.EnsembleSensitivity(
name=sensitivity.name,
type=schemas.SensitivityType(sensitivity.type.value),
cases=[
schemas.EnsembleSensitivityCase(
name=case.name,
realizations=case.realizations,
)
for case in sensitivity.cases
],
)
)
return api_sensitivities
91 changes: 10 additions & 81 deletions backend_py/primary/primary/routers/parameters/router.py
Original file line number Diff line number Diff line change
@@ -1,100 +1,29 @@
import logging
from typing import List, Optional, Literal
from typing import List, Tuple

from fastapi import APIRouter, Depends, Query

from primary.auth.auth_helper import AuthHelper
from webviz_services.sumo_access.parameter_access import ParameterAccess
from webviz_services.sumo_access.parameter_types import EnsembleParameter, EnsembleSensitivity
from webviz_services.utils.authenticated_user import AuthenticatedUser

from . import schemas
from . import schemas, converters

LOGGER = logging.getLogger(__name__)

router = APIRouter()


@router.get("/parameter_names_and_description/")
async def get_parameter_names_and_description(
# fmt:off
@router.get("/parameters_and_sensitivities/")
async def get_parameters_and_sensitivities(
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
case_uuid: str = Query(description="Sumo case uuid"),
ensemble_name: str = Query(description="Ensemble name"),
exclude_all_values_constant: bool = Query(True, description="Exclude all parameters where all values are the same value"),
sort_order: Literal["alphabetically", "standard_deviation"] = Query("alphabetically", description="Sort order"),
# fmt:on
) -> List[schemas.EnsembleParameterDescription]:
"""Retrieve parameter names and description for an ensemble"""
) -> Tuple[List[schemas.EnsembleParameter], List[schemas.EnsembleSensitivity]]:
access = ParameterAccess.from_ensemble_name(authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name)
parameters = (await access.get_parameters_and_sensitivities_async()).parameters
if exclude_all_values_constant:
parameters = [p for p in parameters if not p.is_constant]
if sort_order == "alphabetically":
parameters = sorted(parameters, key=lambda p: p.name.lower())
# temporary
# parameters = [p for p in parameters if p.group_name and "GLOBVAR" in p.group_name]
return [
schemas.EnsembleParameterDescription(
name=parameter.name,
descriptive_name=parameter.descriptive_name,
group_name=parameter.group_name,
is_discrete=parameter.is_discrete,
)
for parameter in parameters
]
parameters, sensitivities = await access.get_parameters_and_sensitivities_async()


@router.get("/parameter/")
async def get_parameter(
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
case_uuid: str = Query(description="Sumo case uuid"),
ensemble_name: str = Query(description="Ensemble name"),
parameter_name: str = Query(description="Parameter name"),
) -> Optional[EnsembleParameter]:
"""Get a parameter in a given Sumo ensemble"""

access = ParameterAccess.from_ensemble_name(authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name)
parameters = (await access.get_parameters_and_sensitivities_async()).parameters
for parameter in parameters:
if parameter.name == parameter_name:
return parameter
return None


@router.get("/parameters/")
async def get_parameters(
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
case_uuid: str = Query(description="Sumo case uuid"),
ensemble_name: str = Query(description="Ensemble name"),
) -> List[EnsembleParameter]:
access = ParameterAccess.from_ensemble_name(authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name)
parameters = (await access.get_parameters_and_sensitivities_async()).parameters
return [parameter for parameter in parameters]


@router.get("/is_sensitivity_run/")
async def get_is_sensitivity_run(
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
case_uuid: str = Query(description="Sumo case uuid"),
ensemble_name: str = Query(description="Ensemble name"),
) -> bool:
"""Check if a given Sumo ensemble is a sensitivity run"""

access = ParameterAccess.from_ensemble_name(authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name)
parameters = await access.get_parameters_and_sensitivities_async()
return parameters.sensitivities is not None


@router.get("/sensitivities/")
async def get_sensitivities(
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
case_uuid: str = Query(description="Sumo case uuid"),
ensemble_name: str = Query(description="Ensemble name"),
) -> List[EnsembleSensitivity]:
"""Get sensitivities in a given Sumo ensemble"""

access = ParameterAccess.from_ensemble_name(authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name)

sensitivities = (await access.get_parameters_and_sensitivities_async()).sensitivities
return sensitivities if sensitivities else []
return (
converters.to_api_parameters(parameters),
converters.to_api_sensitivities(sensitivities),
)
37 changes: 32 additions & 5 deletions backend_py/primary/primary/routers/parameters/schemas.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,37 @@
from typing import Optional
from typing import List, Optional, Union
from enum import Enum

from pydantic import BaseModel


class EnsembleParameterDescription(BaseModel):
class EnsembleParameter(BaseModel):
"""Description/data for a single parameter in an ensemble"""

name: str
isLogarithmic: bool
isDiscrete: bool # values are string or integer
isConstant: bool # all values are equal
groupName: Optional[str] = None
descriptiveName: Optional[str] = None
realizations: List[int]
values: Union[List[float], List[int], List[str]]


class SensitivityType(str, Enum):
MONTECARLO = "montecarlo"
SCENARIO = "scenario"


class EnsembleSensitivityCase(BaseModel):
"""Description/data for a single sensitivity case in an ensemble"""

name: str
realizations: List[int]


class EnsembleSensitivity(BaseModel):
"""Description/data for a single sensitivity in an ensemble"""

name: str
group_name: Optional[str] = None
descriptive_name: Optional[str] = None
is_discrete: bool
type: SensitivityType
cases: List[EnsembleSensitivityCase]
2 changes: 1 addition & 1 deletion backend_py/primary/primary/routers/timeseries/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,7 @@ async def get_statistical_vector_data_per_sensitivity(
parameter_access = ParameterAccess.from_ensemble_name(
authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name
)
sensitivities = (await parameter_access.get_parameters_and_sensitivities_async()).sensitivities
_, sensitivities = await parameter_access.get_parameters_and_sensitivities_async()

service_freq = Frequency.from_string_value(resampling_frequency.value)
service_stat_funcs_to_compute = converters.to_service_statistic_functions(statistic_functions)
Expand Down
Loading
Loading