Skip to content

Commit

Permalink
Consistent client-side handling of timestamps (equinor#244)
Browse files Browse the repository at this point in the history
  • Loading branch information
sigurdp authored Sep 1, 2023
1 parent 451b4ce commit 4f358f7
Show file tree
Hide file tree
Showing 32 changed files with 603 additions and 525 deletions.
98 changes: 49 additions & 49 deletions backend/src/backend/primary/routers/correlations/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,59 +18,59 @@
router = APIRouter()


@router.get("/correlate_parameters_with_timeseries/")
def correlate_parameters_with_timeseries(
# fmt:off
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
case_uuid: str = Query(description="Sumo case uuid"),
ensemble_name: str = Query(description="Ensemble name"),
vector_name: str = Query(description="Name of the vector"),
timestep: datetime.datetime = Query(description= "Timestep"),
# realizations: Optional[Sequence[int]] = Query(None, description="Optional list of realizations to include. If not specified, all realizations will be returned."),
# parameter_names: Optional[List[str]] = Query(None, description="Optional subset of parameters to correlate. Default are all parameters.")
# fmt:on
) -> EnsembleCorrelations:
"""Get parameter correlations for a timeseries at a given timestep"""
# @router.get("/correlate_parameters_with_timeseries/")
# def correlate_parameters_with_timeseries(
# # fmt:off
# authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
# case_uuid: str = Query(description="Sumo case uuid"),
# ensemble_name: str = Query(description="Ensemble name"),
# vector_name: str = Query(description="Name of the vector"),
# timestep: datetime.datetime = Query(description= "Timestep"),
# # realizations: Optional[Sequence[int]] = Query(None, description="Optional list of realizations to include. If not specified, all realizations will be returned."),
# # parameter_names: Optional[List[str]] = Query(None, description="Optional subset of parameters to correlate. Default are all parameters.")
# # fmt:on
# ) -> EnsembleCorrelations:
# """Get parameter correlations for a timeseries at a given timestep"""

summary_access = SummaryAccess(authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name)
parameter_access = ParameterAccess(
authenticated_user.get_sumo_access_token(),
case_uuid=case_uuid,
iteration_name=ensemble_name,
)
# summary_access = SummaryAccess(authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name)
# parameter_access = ParameterAccess(
# authenticated_user.get_sumo_access_token(),
# case_uuid=case_uuid,
# iteration_name=ensemble_name,
# )

ensemble_response = summary_access.get_vector_values_at_timestep(
vector_name=vector_name, timestep=timestep, realizations=None
)
parameters = parameter_access.get_parameters_and_sensitivities()
# ensemble_response = summary_access.get_vector_values_at_timestep(
# vector_name=vector_name, timestep=timestep, realizations=None
# )
# parameters = parameter_access.get_parameters_and_sensitivities()

return correlate_parameters_with_response(parameters.parameters, ensemble_response)
# return correlate_parameters_with_response(parameters.parameters, ensemble_response)


@router.get("/correlate_parameters_with_inplace_volumes/")
def correlate_parameters_with_inplace_volumes(
# fmt:off
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
case_uuid: str = Query(description="Sumo case uuid"),
ensemble_name: str = Query(description="Ensemble name"),
table_name: str = Query(description="Table name"),
response_name:str = Query(description="Response name"),
# categorical_filter:Optional[List[InplaceVolumetricsCategoricalMetaData]] = None,
# realizations: Optional[Sequence[int]] = Query(None, description="Optional list of realizations to include. If not specified, all realizations will be returned."),
# parameter_names: Optional[List[str]] = Query(None, description="Optional subset of parameters to correlate. Default are all parameters.")
# fmt:on
) -> EnsembleCorrelations:
"""Get parameter correlations for an inplace volumetrics response"""
# @router.get("/correlate_parameters_with_inplace_volumes/")
# def correlate_parameters_with_inplace_volumes(
# # fmt:off
# authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
# case_uuid: str = Query(description="Sumo case uuid"),
# ensemble_name: str = Query(description="Ensemble name"),
# table_name: str = Query(description="Table name"),
# response_name:str = Query(description="Response name"),
# # categorical_filter:Optional[List[InplaceVolumetricsCategoricalMetaData]] = None,
# # realizations: Optional[Sequence[int]] = Query(None, description="Optional list of realizations to include. If not specified, all realizations will be returned."),
# # parameter_names: Optional[List[str]] = Query(None, description="Optional subset of parameters to correlate. Default are all parameters.")
# # fmt:on
# ) -> EnsembleCorrelations:
# """Get parameter correlations for an inplace volumetrics response"""

inplace_access = InplaceVolumetricsAccess(authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name)
parameter_access = ParameterAccess(
authenticated_user.get_sumo_access_token(),
case_uuid=case_uuid,
iteration_name=ensemble_name,
)
ensemble_response = inplace_access.get_response(
table_name, response_name, categorical_filters=None, realizations=None
)
parameters = parameter_access.get_parameters_and_sensitivities()
# inplace_access = InplaceVolumetricsAccess(authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name)
# parameter_access = ParameterAccess(
# authenticated_user.get_sumo_access_token(),
# case_uuid=case_uuid,
# iteration_name=ensemble_name,
# )
# ensemble_response = inplace_access.get_response(
# table_name, response_name, categorical_filters=None, realizations=None
# )
# parameters = parameter_access.get_parameters_and_sensitivities()

return correlate_parameters_with_response(parameters.parameters, ensemble_response)
# return correlate_parameters_with_response(parameters.parameters, ensemble_response)
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def to_api_vector_statistic_data(

ret_data = schemas.VectorStatisticData(
realizations=vector_statistics.realizations,
timestamps=vector_statistics.timestamps,
timestamps_utc_ms=vector_statistics.timestamps_utc_ms,
value_objects=value_objects,
unit=vector_metadata.unit,
is_rate=vector_metadata.is_rate,
Expand Down
28 changes: 13 additions & 15 deletions backend/src/backend/primary/routers/timeseries/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def get_realizations_vector_data(
ret_arr.append(
schemas.VectorRealizationData(
realization=vec.realization,
timestamps=vec.timestamps,
timestamps_utc_ms=vec.timestamps_utc_ms,
values=vec.values,
unit=vec.metadata.unit,
is_rate=vec.metadata.is_rate,
Expand All @@ -77,15 +77,15 @@ def get_realizations_vector_data(
return ret_arr


@router.get("/timesteps/")
def get_timesteps(
@router.get("/timestamps_list/")
def get_timestamps_list(
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
case_uuid: str = Query(description="Sumo case uuid"),
ensemble_name: str = Query(description="Ensemble name"),
resampling_frequency: Optional[schemas.Frequency] = Query(None, description="Resampling frequency"),
# realizations: Union[Sequence[int], None] = Query(None, description="Optional list of realizations to include"),
) -> List[datetime.datetime]:
"""Get the intersection of available timesteps.
) -> List[int]:
"""Get the intersection of available timestamps.
Note that when resampling_frequency is None, the pure intersection of the
stored raw dates will be returned. Thus the returned list of dates will not include
dates from long running realizations.
Expand All @@ -94,7 +94,7 @@ def get_timesteps(
"""
access = SummaryAccess(authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name)
sumo_freq = Frequency.from_string_value(resampling_frequency.value if resampling_frequency else "dummy")
return access.get_timesteps(resampling_frequency=sumo_freq)
return access.get_timestamps(resampling_frequency=sumo_freq)


@router.get("/historical_vector_data/")
Expand All @@ -118,7 +118,7 @@ def get_historical_vector_data(
raise HTTPException(status_code=404, detail="Could not get historical vector")

return schemas.VectorHistoricalData(
timestamps=sumo_hist_vec.timestamps,
timestamps_utc_ms=sumo_hist_vec.timestamps_utc_ms,
values=sumo_hist_vec.values,
unit=sumo_hist_vec.metadata.unit,
is_rate=sumo_hist_vec.metadata.is_rate,
Expand Down Expand Up @@ -202,7 +202,7 @@ def get_statistical_vector_data_per_sensitivity(
sensitivity_name=sensitivity.name,
sensitivity_case=case.name,
realizations=statistic_data.realizations,
timestamps=statistic_data.timestamps,
timestamps_utc_ms=statistic_data.timestamps_utc_ms,
value_objects=statistic_data.value_objects,
unit=statistic_data.unit,
is_rate=statistic_data.is_rate,
Expand All @@ -211,22 +211,20 @@ def get_statistical_vector_data_per_sensitivity(
return ret_data


@router.get("/realization_vector_at_timestep/")
def get_realization_vector_at_timestep(
@router.get("/realization_vector_at_timestamp/")
def get_realization_vector_at_timestamp(
# fmt:off
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
case_uuid: str = Query(description="Sumo case uuid"),
ensemble_name: str = Query(description="Ensemble name"),
vector_name: str = Query(description="Name of the vector"),
timestep: datetime.datetime = Query(description= "Timestep"),
timestamp_utc_ms: int = Query(description= "Timestamp in ms UTC to query vectors at"),
# realizations: Optional[Sequence[int]] = Query(None, description="Optional list of realizations to include. If not specified, all realizations will be returned."),
# fmt:on
) -> EnsembleScalarResponse:
"""Get parameter correlations for a timeseries at a given timestep"""

summary_access = SummaryAccess(authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name)
ensemble_response = summary_access.get_vector_values_at_timestep(
vector_name=vector_name, timestep=timestep, realizations=None
ensemble_response = summary_access.get_vector_values_at_timestamp(
vector_name=vector_name, timestamp_utc_ms=timestamp_utc_ms, realizations=None
)
return ensemble_response

Expand Down
8 changes: 4 additions & 4 deletions backend/src/backend/primary/routers/timeseries/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ class VectorDescription(BaseModel):


class VectorHistoricalData(BaseModel):
timestamps: List[datetime.datetime]
timestamps_utc_ms: List[int]
values: List[float]
unit: str
is_rate: bool


class VectorRealizationData(BaseModel):
realization: int
timestamps: List[datetime.datetime]
timestamps_utc_ms: List[int]
values: List[float]
unit: str
is_rate: bool
Expand All @@ -50,15 +50,15 @@ class StatisticValueObject(BaseModel):

class VectorStatisticData(BaseModel):
realizations: List[int]
timestamps: List[datetime.datetime]
timestamps_utc_ms: List[int]
value_objects: List[StatisticValueObject]
unit: str
is_rate: bool


class VectorStatisticSensitivityData(BaseModel):
realizations: List[int]
timestamps: List[datetime.datetime]
timestamps_utc_ms: List[int]
value_objects: List[StatisticValueObject]
unit: str
is_rate: bool
Expand Down
5 changes: 2 additions & 3 deletions backend/src/services/summary_vector_statistics.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import datetime
from typing import Dict, List, Optional, Sequence

import numpy as np
Expand All @@ -15,7 +14,7 @@

class VectorStatistics(BaseModel):
realizations: List[int]
timestamps: List[datetime.datetime]
timestamps_utc_ms: List[int]
values_dict: Dict[StatisticFunction, List[float]]


Expand Down Expand Up @@ -106,7 +105,7 @@ def compute_vector_statistics(

ret_data = VectorStatistics(
realizations=unique_realizations,
timestamps=statistics_table["DATE"].to_numpy().astype(datetime.datetime).tolist(),
timestamps_utc_ms=statistics_table["DATE"].to_numpy().astype(int).tolist(),
values_dict=values_dict,
)

Expand Down
20 changes: 11 additions & 9 deletions backend/src/services/sumo_access/summary_access.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import datetime
import logging
from io import BytesIO
from typing import List, Optional, Sequence, Tuple, Set
Expand Down Expand Up @@ -143,7 +142,7 @@ def get_vector(
ret_arr.append(
RealizationVector(
realization=real,
timestamps=date_np_arr.astype(datetime.datetime).tolist(),
timestamps_utc_ms=date_np_arr.astype(int).tolist(),
values=value_np_arr.tolist(),
metadata=vector_metadata,
)
Expand Down Expand Up @@ -199,41 +198,44 @@ def get_matching_historical_vector(
)

return HistoricalVector(
timestamps=date_np_arr.astype(datetime.datetime).tolist(),
timestamps_utc_ms=date_np_arr.astype(int).tolist(),
values=value_np_arr.tolist(),
metadata=vector_metadata,
)

def get_vector_values_at_timestep(
def get_vector_values_at_timestamp(
self,
vector_name: str,
timestep: datetime.datetime,
timestamp_utc_ms: int,
realizations: Optional[Sequence[int]] = None,
) -> EnsembleScalarResponse:
table, _ = self.get_vector_table(vector_name, resampling_frequency=None, realizations=realizations)

if realizations is not None:
mask = pc.is_in(table["REAL"], value_set=pa.array(realizations))
table = table.filter(mask)
mask = pc.is_in(table["DATE"], value_set=pa.array([timestep]))
mask = pc.is_in(table["DATE"], value_set=pa.array([timestamp_utc_ms]))
table = table.filter(mask)

return EnsembleScalarResponse(
realizations=table["REAL"].to_pylist(),
values=table[vector_name].to_pylist(),
)

def get_timesteps(
def get_timestamps(
self,
resampling_frequency: Optional[Frequency] = None,
) -> List[datetime.datetime]:
) -> List[int]:
"""
Get list of available timestamps in ms UTC
"""
table, _ = self.get_vector_table(
self.get_available_vectors()[0].name,
resampling_frequency=resampling_frequency,
realizations=None,
)

return pc.unique(table.column("DATE")).to_pylist()
return pc.unique(table.column("DATE")).to_numpy().astype(int).tolist()


def _load_arrow_table_for_from_sumo(case: Case, iteration_name: str, vector_name: str) -> Optional[pa.Table]:
Expand Down
5 changes: 2 additions & 3 deletions backend/src/services/sumo_access/summary_types.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import datetime
from enum import Enum
from typing import List, Optional

Expand Down Expand Up @@ -36,12 +35,12 @@ class VectorMetadata(BaseModel):

class RealizationVector(BaseModel):
realization: int
timestamps: List[datetime.datetime]
timestamps_utc_ms: List[int]
values: List[float]
metadata: VectorMetadata


class HistoricalVector(BaseModel):
timestamps: List[datetime.datetime]
timestamps_utc_ms: List[int]
values: List[float]
metadata: VectorMetadata
3 changes: 0 additions & 3 deletions frontend/src/api/ApiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import type { BaseHttpRequest } from './core/BaseHttpRequest';
import type { OpenAPIConfig } from './core/OpenAPI';
import { AxiosHttpRequest } from './core/AxiosHttpRequest';

import { CorrelationsService } from './services/CorrelationsService';
import { DefaultService } from './services/DefaultService';
import { ExploreService } from './services/ExploreService';
import { GridService } from './services/GridService';
Expand All @@ -22,7 +21,6 @@ type HttpRequestConstructor = new (config: OpenAPIConfig) => BaseHttpRequest;

export class ApiService {

public readonly correlations: CorrelationsService;
public readonly default: DefaultService;
public readonly explore: ExploreService;
public readonly grid: GridService;
Expand Down Expand Up @@ -50,7 +48,6 @@ export class ApiService {
ENCODE_PATH: config?.ENCODE_PATH,
});

this.correlations = new CorrelationsService(this.request);
this.default = new DefaultService(this.request);
this.explore = new ExploreService(this.request);
this.grid = new GridService(this.request);
Expand Down
2 changes: 0 additions & 2 deletions frontend/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ export type { Body_get_realizations_response as Body_get_realizations_response_a
export type { CaseInfo as CaseInfo_api } from './models/CaseInfo';
export type { Completions as Completions_api } from './models/Completions';
export type { DynamicSurfaceDirectory as DynamicSurfaceDirectory_api } from './models/DynamicSurfaceDirectory';
export type { EnsembleCorrelations as EnsembleCorrelations_api } from './models/EnsembleCorrelations';
export type { EnsembleDetails as EnsembleDetails_api } from './models/EnsembleDetails';
export type { EnsembleInfo as EnsembleInfo_api } from './models/EnsembleInfo';
export type { EnsembleParameter as EnsembleParameter_api } from './models/EnsembleParameter';
Expand Down Expand Up @@ -55,7 +54,6 @@ export type { WellCompletionUnits as WellCompletionUnits_api } from './models/We
export type { WellCompletionWell as WellCompletionWell_api } from './models/WellCompletionWell';
export type { WellCompletionZone as WellCompletionZone_api } from './models/WellCompletionZone';

export { CorrelationsService } from './services/CorrelationsService';
export { DefaultService } from './services/DefaultService';
export { ExploreService } from './services/ExploreService';
export { GridService } from './services/GridService';
Expand Down
9 changes: 0 additions & 9 deletions frontend/src/api/models/EnsembleCorrelations.ts

This file was deleted.

Loading

0 comments on commit 4f358f7

Please sign in to comment.