-
Notifications
You must be signed in to change notification settings - Fork 107
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
30 changed files
with
576 additions
and
120 deletions.
There are no files selected for viewing
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from ._session import ConnInfo | ||
from .async_client import AsyncClient | ||
from .client import Client | ||
|
||
__all__ = ["AsyncClient", "Client", "ConnInfo"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import json | ||
import os | ||
from pathlib import Path | ||
from typing import Optional | ||
|
||
from pydantic import BaseModel, ValidationError | ||
|
||
|
||
class ConnInfo(BaseModel): | ||
base_url: str | ||
auth_token: Optional[str] = None | ||
|
||
|
||
ENV_VAR = "ERT_STORAGE_CONNECTION_STRING" | ||
|
||
# Avoid searching for the connection information on every request. We assume | ||
# that a single client process will only ever want to connect to a single ERT | ||
# Storage server during its lifetime, so we don't provide an API for managing | ||
# this cache. | ||
_CACHED_CONN_INFO: Optional[ConnInfo] = None | ||
|
||
|
||
def find_conn_info() -> ConnInfo: | ||
""" | ||
The base url and auth token are read from either: | ||
The file `storage_server.json`, starting from the current working directory | ||
or the environment variable `ERT_STORAGE_CONNECTION_STRING` | ||
In both cases the configuration is represented by JSON representation of the | ||
`ConnInfo` pydantic model. | ||
In the event that nothing is found, a RuntimeError is raised. | ||
""" | ||
global _CACHED_CONN_INFO | ||
if _CACHED_CONN_INFO is not None: | ||
return _CACHED_CONN_INFO | ||
|
||
conn_str = os.environ.get(ENV_VAR) | ||
|
||
# This could be an empty string rather than None, as by the shell | ||
# invocation: env ERT_STORAGE_CONNECTION_STRING= python | ||
if not conn_str: | ||
# Look for `storage_server.json` from cwd up to root. | ||
root = Path("/") | ||
path = Path.cwd() | ||
while path != root: | ||
try: | ||
conn_str = (path / "storage_server.json").read_text() | ||
break | ||
except FileNotFoundError: | ||
path = path.parent | ||
|
||
if not conn_str: | ||
raise RuntimeError("No Storage connection configuration found") | ||
|
||
try: | ||
conn_info = ConnInfo.parse_obj(json.loads(conn_str)) | ||
_CACHED_CONN_INFO = conn_info | ||
return conn_info | ||
except json.JSONDecodeError: | ||
raise RuntimeError("Invalid storage conneciton configuration") | ||
except ValidationError: | ||
raise RuntimeError("Invalid storage conneciton configuration") | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
from typing import Optional | ||
|
||
import httpx | ||
|
||
from ._session import ConnInfo, find_conn_info | ||
|
||
|
||
class AsyncClient(httpx.AsyncClient): | ||
""" | ||
Wrapper class for httpx.AsyncClient that provides a user-friendly way to | ||
interact with ERT Storage's API | ||
""" | ||
|
||
def __init__(self, conn_info: Optional[ConnInfo] = None) -> None: | ||
if conn_info is None: | ||
conn_info = find_conn_info() | ||
|
||
headers = {} | ||
if conn_info.auth_token is not None: | ||
headers = {"Token": conn_info.auth_token} | ||
|
||
super().__init__(base_url=conn_info.base_url, headers=headers) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
from typing import Optional | ||
|
||
import httpx | ||
|
||
from ._session import ConnInfo, find_conn_info | ||
|
||
|
||
class Client(httpx.Client): | ||
""" | ||
Wrapper class for httpx.Client that provides a user-friendly way to | ||
interact with ERT Storage's API | ||
""" | ||
|
||
def __init__(self, conn_info: Optional[ConnInfo] = None) -> None: | ||
if conn_info is None: | ||
conn_info = find_conn_info() | ||
|
||
headers = {} | ||
if conn_info.auth_token is not None: | ||
headers = {"Token": conn_info.auth_token} | ||
|
||
super().__init__(base_url=conn_info.base_url, headers=headers) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
from typing import List, Mapping | ||
|
||
import numpy as np | ||
import pandas as pd | ||
|
||
|
||
def _calculate_misfit( | ||
obs_value: np.ndarray, response_value: np.ndarray, obs_std: np.ndarray | ||
) -> List[float]: | ||
difference = response_value - obs_value | ||
misfit = (difference / obs_std) ** 2 | ||
return (misfit * np.sign(difference)).tolist() | ||
|
||
|
||
def calculate_misfits_from_pandas( | ||
reponses_dict: Mapping[int, pd.DataFrame], | ||
observation: pd.DataFrame, | ||
summary_misfits: bool = False, | ||
) -> pd.DataFrame: | ||
""" | ||
Compute misfits from reponses_dict (real_id, values in dataframe) | ||
and observation | ||
""" | ||
misfits_dict = {} | ||
for realization_index in reponses_dict: | ||
misfits_dict[realization_index] = _calculate_misfit( | ||
observation["values"], | ||
reponses_dict[realization_index].loc[:, observation.index].values.flatten(), | ||
observation["errors"], | ||
) | ||
|
||
df = pd.DataFrame(data=misfits_dict, index=observation.index) | ||
if summary_misfits: | ||
df = pd.DataFrame([df.abs().sum(axis=0)], columns=df.columns, index=[0]) | ||
return df.T |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
from typing import Any | ||
|
||
from fastapi import status | ||
|
||
|
||
class ErtStorageError(RuntimeError): | ||
""" | ||
Base error class for all the rest of errors | ||
""" | ||
|
||
__status_code__ = status.HTTP_200_OK | ||
|
||
def __init__(self, message: str, **kwargs: Any): | ||
super().__init__(message, kwargs) | ||
|
||
|
||
class NotFoundError(ErtStorageError): | ||
__status_code__ = status.HTTP_404_NOT_FOUND | ||
|
||
|
||
class ConflictError(ErtStorageError): | ||
__status_code__ = status.HTTP_409_CONFLICT | ||
|
||
|
||
class ExpectationError(ErtStorageError): | ||
__status_code__ = status.HTTP_417_EXPECTATION_FAILED | ||
|
||
|
||
class UnprocessableError(ErtStorageError): | ||
__status_code__ = status.HTTP_422_UNPROCESSABLE_ENTITY |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
from typing import Any, List, Mapping, Optional | ||
from uuid import UUID | ||
|
||
from pydantic import BaseModel, Field, model_validator | ||
|
||
|
||
class _Ensemble(BaseModel): | ||
size: int | ||
parameter_names: List[str] | ||
response_names: List[str] | ||
active_realizations: List[int] = [] | ||
|
||
|
||
class EnsembleIn(_Ensemble): | ||
update_id: Optional[UUID] = None | ||
userdata: Mapping[str, Any] = {} | ||
|
||
@model_validator | ||
def _check_names_no_overlap(cls, values: Mapping[str, Any]) -> Mapping[str, Any]: | ||
""" | ||
Verify that `parameter_names` and `response_names` don't overlap. Ie, no | ||
record can be both a parameter and a response. | ||
""" | ||
if not set(values["parameter_names"]).isdisjoint(set(values["response_names"])): | ||
raise ValueError("parameters and responses cannot have a name in common") | ||
return values | ||
|
||
|
||
class EnsembleOut(_Ensemble): | ||
id: UUID | ||
children: List[UUID] = Field(alias="child_ensemble_ids") | ||
parent: Optional[UUID] = Field(alias="parent_ensemble_id") | ||
experiment_id: Optional[UUID] = None | ||
userdata: Mapping[str, Any] | ||
|
||
class Config: | ||
orm_mode = True |
Oops, something went wrong.