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

Adjustments to backend (and related required changes to frontend) in preparation of 2D viewer #808

Merged
merged 7 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
3 changes: 3 additions & 0 deletions backend_py/primary/primary/routers/explore.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class EnsembleInfo(BaseModel):
class EnsembleDetails(BaseModel):
name: str
field_identifier: str
stratigraphic_column_identifier: str
case_name: str
case_uuid: str
realizations: Sequence[int]
Expand Down Expand Up @@ -91,6 +92,7 @@ async def get_ensemble_details(
case_name = await case_inspector.get_case_name_async()
realizations = await case_inspector.get_realizations_in_iteration_async(ensemble_name)
field_identifiers = await case_inspector.get_field_identifiers_async()
stratigraphic_column_identifier = await case_inspector.get_stratigraphic_column_identifier_async()

if len(field_identifiers) != 1:
raise NotImplementedError("Multiple field identifiers not supported")
Expand All @@ -101,4 +103,5 @@ async def get_ensemble_details(
case_uuid=case_uuid,
realizations=realizations,
field_identifier=field_identifiers[0],
stratigraphic_column_identifier=stratigraphic_column_identifier,
)
13 changes: 7 additions & 6 deletions backend_py/primary/primary/routers/polygons/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
from webviz_pkg.core_utils.perf_timer import PerfTimer

from primary.auth.auth_helper import AuthHelper
from primary.services.smda_access.mocked_drogon_smda_access import _mocked_stratigraphy_access
from primary.services.smda_access.stratigraphy_access import StratigraphyAccess
from primary.services.smda_access.drogon import DrogonSmdaAccess
from primary.services.smda_access import SmdaAccess
from primary.services.smda_access.stratigraphy_utils import sort_stratigraphic_names_by_hierarchy
from primary.services.sumo_access.case_inspector import CaseInspector
from primary.services.sumo_access.polygons_access import PolygonsAccess
Expand Down Expand Up @@ -34,14 +34,15 @@ async def get_polygons_directory(
polygons_dir = await access.get_polygons_directory_async()

case_inspector = CaseInspector.from_case_uuid(authenticated_user.get_sumo_access_token(), case_uuid)
field_identifiers = await case_inspector.get_field_identifiers_async()
strat_column_identifier = await case_inspector.get_stratigraphic_column_identifier_async()
strat_access: Union[StratigraphyAccess, _mocked_stratigraphy_access.StratigraphyAccess]
smda_access: Union[SmdaAccess, DrogonSmdaAccess]

if strat_column_identifier == "DROGON_HAS_NO_STRATCOLUMN":
strat_access = _mocked_stratigraphy_access.StratigraphyAccess(authenticated_user.get_smda_access_token())
smda_access = DrogonSmdaAccess()
else:
strat_access = StratigraphyAccess(authenticated_user.get_smda_access_token())
strat_units = await strat_access.get_stratigraphic_units(strat_column_identifier)
smda_access = SmdaAccess(authenticated_user.get_smda_access_token(), field_identifier=field_identifiers[0])
strat_units = await smda_access.get_stratigraphic_units(strat_column_identifier)
sorted_stratigraphic_surfaces = sort_stratigraphic_names_by_hierarchy(strat_units)

return converters.to_api_polygons_directory(polygons_dir, sorted_stratigraphic_surfaces)
Expand Down
1 change: 1 addition & 0 deletions backend_py/primary/primary/routers/polygons/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class PolygonsAttributeType(str, Enum):
PINCHOUT = "pinchout" # Values are pinchouts
SUBCROP = "subcrop" # Values are subcrops
FAULT_LINES = "fault_lines" # Values are fault lines
NAMED_AREA = "named_area" # Values are named areas, e.g. CCS containment polygons


class PolygonsMeta(BaseModel):
Expand Down
50 changes: 50 additions & 0 deletions backend_py/primary/primary/routers/seismic/converters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from typing import List

import orjson
import numpy as np
import xtgeo

from . import schemas


def surface_to_float32_array(values: np.ndarray) -> List[float]:
values = values.astype(np.float32)
np.ma.set_fill_value(values, np.nan)
values = np.ma.filled(values)

# Rotate 90 deg left.
# This will cause the width of to run along the X axis
# and height of along Y axis (starting from bottom.)
values = np.rot90(values)

return values.flatten().tolist()


def to_api_surface_data(
xtgeo_surf: xtgeo.RegularSurface, property_values: np.ndarray
) -> schemas.SurfaceMeshAndProperty:
"""
Create API SurfaceData from xtgeo regular surface
"""
float32_mesh = surface_to_float32_array(xtgeo_surf.values)
float32_property = surface_to_float32_array(property_values)

return schemas.SurfaceMeshAndProperty(
x_ori=xtgeo_surf.xori,
y_ori=xtgeo_surf.yori,
x_count=xtgeo_surf.ncol,
y_count=xtgeo_surf.nrow,
x_inc=xtgeo_surf.xinc,
y_inc=xtgeo_surf.yinc,
x_min=xtgeo_surf.xmin,
x_max=xtgeo_surf.xmax,
y_min=xtgeo_surf.ymin,
y_max=xtgeo_surf.ymax,
mesh_value_min=xtgeo_surf.values.min(),
mesh_value_max=xtgeo_surf.values.max(),
property_value_min=property_values.min(),
property_value_max=property_values.max(),
rot_deg=xtgeo_surf.rotation,
mesh_data=orjson.dumps(float32_mesh).decode("utf-8"), # pylint: disable=maybe-no-member
property_data=orjson.dumps(float32_property).decode("utf-8"), # pylint: disable=maybe-no-member
)
20 changes: 20 additions & 0 deletions backend_py/primary/primary/routers/seismic/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,23 @@ class SeismicFenceData(BaseModel):
num_samples_per_trace: int
min_fence_depth: float
max_fence_depth: float


class SurfaceMeshAndProperty(BaseModel):
x_ori: float
y_ori: float
x_count: int
y_count: int
x_inc: float
y_inc: float
x_min: float
x_max: float
y_min: float
y_max: float
mesh_value_min: float
mesh_value_max: float
property_value_min: float
property_value_max: float
rot_deg: float
mesh_data: str
property_data: str
20 changes: 20 additions & 0 deletions backend_py/primary/primary/routers/surface/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from primary.services.utils.surface_intersect_with_polyline import XtgeoSurfaceIntersectionResult
from primary.services.utils.surface_to_float32 import surface_to_float32_numpy_array
from primary.services.utils.surface_to_png import surface_to_png_bytes_optimized
from primary.services.smda_access import StratigraphicUnit

from . import schemas

Expand Down Expand Up @@ -174,3 +175,22 @@ def to_api_surface_intersection(
z_points=xtgeo_surface_intersection.zval,
cum_lengths=xtgeo_surface_intersection.distance,
)


def to_api_stratigraphic_unit(
stratigraphic_unit: StratigraphicUnit,
) -> schemas.StratigraphicUnit:
return schemas.StratigraphicUnit(
identifier=stratigraphic_unit.identifier,
top=stratigraphic_unit.top,
base=stratigraphic_unit.base,
stratUnitLevel=stratigraphic_unit.strat_unit_level,
stratUnitType=stratigraphic_unit.strat_unit_type,
topAge=stratigraphic_unit.top_age,
baseAge=stratigraphic_unit.base_age,
stratUnitParent=stratigraphic_unit.strat_unit_parent,
colorR=stratigraphic_unit.color_r,
colorG=stratigraphic_unit.color_g,
colorB=stratigraphic_unit.color_b,
lithologyType=stratigraphic_unit.lithology_type,
)
31 changes: 25 additions & 6 deletions backend_py/primary/primary/routers/surface/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

from primary.services.sumo_access.case_inspector import CaseInspector
from primary.services.sumo_access.surface_access import SurfaceAccess
from primary.services.smda_access.stratigraphy_access import StratigraphyAccess, StratigraphicUnit
from primary.services.smda_access import SmdaAccess, StratigraphicUnit
from primary.services.smda_access.stratigraphy_utils import sort_stratigraphic_names_by_hierarchy
from primary.services.smda_access.mocked_drogon_smda_access import _mocked_stratigraphy_access
from primary.services.smda_access.drogon import DrogonSmdaAccess
from primary.services.utils.statistic_function import StatisticFunction
from primary.services.utils.surface_intersect_with_polyline import intersect_surface_with_polyline
from primary.services.utils.authenticated_user import AuthenticatedUser
Expand Down Expand Up @@ -295,22 +295,41 @@ async def get_misfit_surface_data(
raise HTTPException(status.HTTP_501_NOT_IMPLEMENTED)


@router.get("/stratigraphic_units")
async def get_stratigraphic_units(
# fmt:off
response: Response,
authenticated_user: Annotated[AuthenticatedUser, Depends(AuthHelper.get_authenticated_user)],
case_uuid: Annotated[str, Query(description="Sumo case uuid")],
# fmt:on
) -> list[schemas.StratigraphicUnit]:
perf_metrics = ResponsePerfMetrics(response)

strat_units = await _get_stratigraphic_units_for_case_async(authenticated_user, case_uuid)
api_strat_units = [converters.to_api_stratigraphic_unit(strat_unit) for strat_unit in strat_units]

LOGGER.info(f"Got stratigraphic units in: {perf_metrics.to_string()}")

return api_strat_units


async def _get_stratigraphic_units_for_case_async(
authenticated_user: AuthenticatedUser, case_uuid: str
) -> list[StratigraphicUnit]:
perf_metrics = PerfMetrics()

case_inspector = CaseInspector.from_case_uuid(authenticated_user.get_sumo_access_token(), case_uuid)
field_identifiers = await case_inspector.get_field_identifiers_async()
strat_column_identifier = await case_inspector.get_stratigraphic_column_identifier_async()
perf_metrics.record_lap("get-strat-ident")

strat_access: StratigraphyAccess | _mocked_stratigraphy_access.StratigraphyAccess
smda_access: SmdaAccess | DrogonSmdaAccess
if strat_column_identifier == "DROGON_HAS_NO_STRATCOLUMN":
strat_access = _mocked_stratigraphy_access.StratigraphyAccess(authenticated_user.get_smda_access_token())
smda_access = DrogonSmdaAccess()
else:
strat_access = StratigraphyAccess(authenticated_user.get_smda_access_token())
smda_access = SmdaAccess(authenticated_user.get_smda_access_token(), field_identifier=field_identifiers[0])

strat_units = await strat_access.get_stratigraphic_units(strat_column_identifier)
strat_units = await smda_access.get_stratigraphic_units(strat_column_identifier)
perf_metrics.record_lap("get-strat-units")

LOGGER.info(f"Got stratigraphic units for case in : {perf_metrics.to_string()}")
Expand Down
15 changes: 15 additions & 0 deletions backend_py/primary/primary/routers/surface/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,18 @@ class SurfaceRealizationSampleValues(BaseModel):
class PointSetXY(BaseModel):
x_points: list[float]
y_points: list[float]


class StratigraphicUnit(BaseModel):
identifier: str
top: str
base: str
stratUnitLevel: int
stratUnitType: str
topAge: int | float
baseAge: int | float
stratUnitParent: str | None
colorR: int
colorG: int
colorB: int
lithologyType: int | float | str = "unknown"
3 changes: 3 additions & 0 deletions backend_py/primary/primary/routers/well/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def convert_wellbore_pick_to_schema(wellbore_pick: WellborePick) -> schemas.Well
md=wellbore_pick.md,
mdMsl=wellbore_pick.md_msl,
uniqueWellboreIdentifier=wellbore_pick.unique_wellbore_identifier,
wellboreUuid=wellbore_pick.wellbore_uuid,
pickIdentifier=wellbore_pick.pick_identifier,
confidence=wellbore_pick.confidence,
depthReferencePoint=wellbore_pick.depth_reference_point,
Expand Down Expand Up @@ -57,6 +58,8 @@ def convert_wellbore_header_to_schema(
wellNorthing=drilled_wellbore_header.well_northing,
depthReferencePoint=drilled_wellbore_header.depth_reference_point,
depthReferenceElevation=drilled_wellbore_header.depth_reference_elevation,
wellborePurpose=(drilled_wellbore_header.wellbore_purpose if drilled_wellbore_header.wellbore_purpose else ""),
wellboreStatus=drilled_wellbore_header.wellbore_status if drilled_wellbore_header.wellbore_status else "",
)


Expand Down
Loading
Loading