Skip to content

Commit

Permalink
Seismic intersection
Browse files Browse the repository at this point in the history
  • Loading branch information
HansKallekleiv committed Sep 1, 2023
1 parent 7fc8161 commit 95652b0
Show file tree
Hide file tree
Showing 73 changed files with 21,054 additions and 1,578 deletions.
5 changes: 5 additions & 0 deletions backend/src/backend/primary/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from fastapi.responses import ORJSONResponse
from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware


from src.backend.shared_middleware import add_shared_middlewares
from src.backend.auth.auth_helper import AuthHelper
from .routers.explore import router as explore_router
Expand All @@ -20,6 +21,8 @@
from .routers.well_completion.router import router as well_completion_router
from .routers.well.router import router as well_router
from .routers.surface_polygons.router import router as surface_polygons_router
from .routers.seismic.router import router as seismic_router
from .routers.intersection.router import router as intersection_router

logging.basicConfig(
level=logging.WARNING,
Expand Down Expand Up @@ -56,6 +59,8 @@ def custom_generate_unique_id(route: APIRoute) -> str:
app.include_router(well_completion_router, prefix="/well_completion", tags=["well_completion"])
app.include_router(well_router, prefix="/well", tags=["well"])
app.include_router(surface_polygons_router, prefix="/surface_polygons", tags=["surface_polygons"])
app.include_router(seismic_router, prefix="/seismic", tags=["seismic"])
app.include_router(intersection_router, prefix="/intersection", tags=["intersection"])

authHelper = AuthHelper()
app.include_router(authHelper.router)
Expand Down
5 changes: 4 additions & 1 deletion backend/src/backend/primary/routers/grid/router.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from fastapi import APIRouter, Depends
from starlette.requests import Request
from typing import Any, List

import orjson as json
import numpy as np
import xtgeo
from fastapi import APIRouter, Depends, HTTPException, Query, Response
from src.services.utils.authenticated_user import AuthenticatedUser
from src.backend.auth.auth_helper import AuthHelper
Expand All @@ -10,6 +12,7 @@
from src.services.sumo_access.grid_access import GridAccess
from .schemas import GridSurface, B64EncodedNumpyArray, GridIntersection


router = APIRouter()


Expand Down
Empty file.
131 changes: 131 additions & 0 deletions backend/src/backend/primary/routers/intersection/router.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import logging
from typing import List
import numpy as np
from fastapi import APIRouter, Depends, HTTPException, Query, Body
import orjson as json

from src.services.sumo_access import SurfaceAccess, SeismicAccess, GridAccess
from src.services.oneseismic_access.vds_access import VdsAccess

from src.services.utils.authenticated_user import AuthenticatedUser

from src.backend.auth.auth_helper import AuthHelper
from . import schemas


router = APIRouter()


@router.post("/surfaces/")
def get_surfaces(
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
case_uuid: str = Query(description="Sumo case uuid"),
ensemble_name: str = Query(description="Ensemble name"),
realization_num: int = Query(description="Realization number"),
names: List[str] = Query(description="Surface names"),
attribute: str = Query(description="Surface attribute"),
cutting_plane: schemas.CuttingPlane = Body(alias="cuttingPlane", embed=True),
) -> List[schemas.SurfaceIntersectionData]:
access = SurfaceAccess(authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name)
intersections = []

fence_arr = np.array(
[cutting_plane.x_arr, cutting_plane.y_arr, np.zeros(len(cutting_plane.y_arr)), cutting_plane.h_arr]
).T

for name in names:
try:
xtgeo_surf = access.get_static_surf(real_num=realization_num, name=name, attribute=attribute)
line = xtgeo_surf.get_randomline(fence_arr)
intersections.append(
schemas.SurfaceIntersectionData(name=f"{name}", hlen_arr=line[:, 0].tolist(), z_arr=line[:, 1].tolist())
)
except AttributeError:
print(f"Could not find surface {name} with attribute {attribute}-{realization_num}")

return intersections


@router.post("/grid_parameter/")
async def get_grid_parameter(
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
case_uuid: str = Query(description="Sumo case uuid"),
ensemble_name: str = Query(description="Ensemble name"),
grid_name: str = Query(description="Grid name"),
parameter_name: str = Query(description="Grid parameter"),
realization: int = Query(description="Realization"),
cutting_plane: schemas.CuttingPlane = Body(alias="cuttingPlane", embed=True),
) -> schemas.CubeIntersectionData:
"""Get a grid parameter"""
grid_access = GridAccess(authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name)
grid_geometry = grid_access.get_grid_geometry(grid_name, int(realization))
grid_property = grid_access.get_grid_parameter(grid_name, parameter_name, int(realization))
grid_property.name = parameter_name
grid_geometry._filesrc = "grid"
grid_property._filesrc = "gridprop"
grid_geometry.append_prop(grid_property)
print("Starting to get random line")
from src.services.utils.perf_timer import PerfTimer

timer = PerfTimer()

fence_arr = np.array(
[cutting_plane.x_arr, cutting_plane.y_arr, np.zeros(len(cutting_plane.y_arr)), cutting_plane.h_arr]
).T
hmin, hmax, vmin, vmax, ndarray2d = grid_geometry.get_randomline(fence_arr, parameter_name)
print("Got random line", timer.lap_s())
print(np.nanmin(ndarray2d), np.nanmax(ndarray2d))
# ndarray2d = np.clip(ndarray2d, 0.25, 0.35)

return schemas.CubeIntersectionData(
xy_arr_string=json.dumps(ndarray2d.tolist()),
z_arr_string=json.dumps(np.linspace(vmin, vmax, len(ndarray2d)).tolist()),
)


@router.post("/seismic/")
def get_seismic(
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
case_uuid: str = Query(description="Sumo case uuid"),
ensemble_name: str = Query(description="Ensemble name"),
realization_num: int = Query(description="Realization number"),
seismic_cube_attribute: str = Query(description="Seismic cube attribute"),
seismic_timestamp_or_timestep: str = Query(description="Timestamp or timestep"),
observed: bool = Query(description="Observed or simulated"),
cutting_plane: schemas.CuttingPlane = Body(alias="cuttingPlane", embed=True),
) -> schemas.CubeIntersectionData:
seismic_access = SeismicAccess(authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name)
timestamp = None
timestep = None
if "--" in seismic_timestamp_or_timestep:
timestep = seismic_timestamp_or_timestep
else:
timestamp = seismic_timestamp_or_timestep

try:
vds_handle = seismic_access.get_vds_handle(
realization=realization_num,
iteration=ensemble_name,
cube_tagname=seismic_cube_attribute,
timestep=timestep,
timestamp=timestamp,
observed=observed,
)
except ValueError as err:
raise HTTPException(status_code=404, detail=str(err)) from err

vdsaccess = VdsAccess(vds_handle)

vals, meta = vdsaccess.get_fence(
coordinate_system="cdp", coordinates=[[x, y] for x, y in zip(cutting_plane.x_arr, cutting_plane.y_arr)]
)

meta = vdsaccess.get_metadata()
tvd_meta = meta.get("axis")[2]

tvd_values = np.linspace(tvd_meta["min"], tvd_meta["max"], tvd_meta["samples"])

return schemas.CubeIntersectionData(
xy_arr_string=json.dumps(vals.T.tolist()), # pylint: disable=no-member
z_arr_string=json.dumps(tvd_values.tolist()), # pylint: disable=no-member
)
24 changes: 24 additions & 0 deletions backend/src/backend/primary/routers/intersection/schemas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from typing import List

from pydantic import BaseModel


class CuttingPlane(BaseModel):
x_arr: List[float]
y_arr: List[float]
h_arr: List[float]


class SurfaceIntersectionData(BaseModel):
name: str
z_arr: List[float]
hlen_arr: List[float]
unit: str = "m"
depthReference: str = "MSL"
context: str = "depth"
stratigraphicalInterval: bool = True


class CubeIntersectionData(BaseModel):
xy_arr_string: str
z_arr_string: str
Empty file.
118 changes: 118 additions & 0 deletions backend/src/backend/primary/routers/seismic/router.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import logging
from typing import List

import numpy as np
import orjson as json
from fastapi import APIRouter, Depends, HTTPException, Query

from src.services.sumo_access.surface_access import SurfaceAccess
from src.services.sumo_access.seismic_access import SeismicAccess
from src.services.utils.authenticated_user import AuthenticatedUser
from src.services.utils.perf_timer import PerfTimer
from src.backend.auth.auth_helper import AuthHelper

from src.services.sumo_access.seismic_types import SeismicCubeSchema
from src.services.oneseismic_access.vds_access import VdsAccess


from . import schemas

LOGGER = logging.getLogger(__name__)

router = APIRouter()


@router.get("/seismic_3dsurvey_directory/")
def get_seismic_3dsurvey_directory(
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
case_uuid: str = Query(description="Sumo case uuid"),
ensemble_name: str = Query(description="Ensemble name"),
) -> schemas.Seismic3DSurveyDirectory:
"""
Get a directory of seismic 3D surveys.
"""
access = SeismicAccess(authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name)
seismic_dir = access.get_seismic_3dsurvey_directory()
return seismic_dir


@router.get("/seismic_4dsurvey_directory/")
def get_seismic_4dsurvey_directory(
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
case_uuid: str = Query(description="Sumo case uuid"),
ensemble_name: str = Query(description="Ensemble name"),
) -> schemas.Seismic4DSurveyDirectory:
"""
Get a directory of seismic 4D surveys.
"""
access = SeismicAccess(authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name)
seismic_dir = access.get_seismic_4dsurvey_directory()
return seismic_dir


@router.get("/get_seismic_attribute_near_surface/")
def get_seismic_attribute_near_surface(
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
case_uuid: str = Query(description="Sumo case uuid"),
ensemble_name: str = Query(description="Ensemble name"),
realization_num: int = Query(description="Realization number"),
seismic_cube_attribute: str = Query(description="Seismic cube attribute"),
seismic_timestamp_or_timestep: str = Query(description="Timestamp or timestep"),
surface_name: str = Query(description="Surface name"),
surface_attribute: str = Query(description="Surface attribute"),
) -> None:
"""
Get a directory of surface names, attributes and time/interval strings for simulated dynamic surfaces.
"""
seismic_access = SeismicAccess(authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name)

timestamp = None
timestep = None
if "--" in seismic_timestamp_or_timestep:
timestep = seismic_timestamp_or_timestep
else:
timestamp = seismic_timestamp_or_timestep
vds_handle = seismic_access.get_vds_handle(
realization=1,
iteration=ensemble_name,
cube_tagname=seismic_cube_attribute,
timestep=timestep,
timestamp=timestamp,
)

surface_access = SurfaceAccess(authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name)
xtg_surf = surface_access.get_static_surf(real_num=1, name=surface_name, attribute=surface_attribute).copy()

vdsaccess = VdsAccess(vds_handle)
seismic_values = vdsaccess.get_surface_values(xtgeo_surf=xtg_surf, above=5, below=5, attribute="mean")

return None


@router.get("/get_slice/")
def get_slice(
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
case_uuid: str = Query(description="Sumo case uuid"),
ensemble_name: str = Query(description="Ensemble name"),
realization_num: int = Query(description="Realization number"),
seismic_cube_attribute: str = Query(description="Seismic cube attribute"),
seismic_time_string: str = Query(description="Timestamp or timestep"),
direction: str = Query(description="Sumo case uuid"),
lineno: int = Query(description="Sumo case uuid"),
) -> None:
seismic_access = SeismicAccess(authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name)
timestamp = None
timestep = None
if "--" in seismic_time_string:
timestep = seismic_time_string
else:
timestamp = seismic_time_string
vds_handle = seismic_access.get_vds_handle(
realization=realization_num,
iteration=ensemble_name,
cube_tagname=seismic_cube_attribute,
timestep=timestep,
timestamp=timestamp,
)
vdsaccess = VdsAccess(vds_handle)
vdsaccess.get_slice(lineno=lineno, direction=direction)
20 changes: 20 additions & 0 deletions backend/src/backend/primary/routers/seismic/schemas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from typing import List
from pydantic import BaseModel


class Seismic3DSurveyDirectory(BaseModel):
attributes: List[str]
timestamps: List[str]

@classmethod
def create_empty(cls) -> "Seismic3DSurveyDirectory":
return cls(attributes=[], timestamps=[])


class Seismic4DSurveyDirectory(BaseModel):
attributes: List[str]
intervals: List[str]

@classmethod
def create_empty(cls) -> Seismic3DSurveyDirectory:
return cls(attributes=[], intervals=[])
8 changes: 6 additions & 2 deletions backend/src/backend/primary/routers/surface/router.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import logging
from typing import List

from fastapi import APIRouter, Depends, HTTPException, Query
import numpy as np
from fastapi import APIRouter, Depends, HTTPException, Query, Body
import json

from src.services.sumo_access.surface_access import SurfaceAccess
from src.services.sumo_access.iteration_inspector import IterationInspector
from src.services.sumo_access.case_inspector import CaseInspector
from src.services.utils.statistic_function import StatisticFunction
from src.services.utils.authenticated_user import AuthenticatedUser
from src.services.utils.perf_timer import PerfTimer
Expand All @@ -12,6 +15,7 @@

from . import converters
from . import schemas
from src.services.sumo_access.generic_types import SumoContent

LOGGER = logging.getLogger(__name__)

Expand Down
9 changes: 9 additions & 0 deletions backend/src/backend/primary/routers/well/converters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from typing import List

from . import schemas


def to_api_wellbore_picks_data(
wellborePicks: List[schemas.WellBorePick], stratUnits: List[schemas.StratigraphicUnit]
) -> schemas.WellBorePicksAndStratUnits:
return schemas.WellBorePicksAndStratUnits(picks=wellborePicks, strat_units=stratUnits)
Loading

0 comments on commit 95652b0

Please sign in to comment.