Skip to content

Commit e18bc35

Browse files
committed
Changes to seismic access and router and layer framework
1 parent 93303d5 commit e18bc35

File tree

69 files changed

+2152
-336
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+2152
-336
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from pydantic import BaseModel
2+
3+
4+
class BoundingBox3d(BaseModel):
5+
"""Bounding box for a 3D grid geometry"""
6+
7+
xmin: float
8+
ymin: float
9+
zmin: float
10+
xmax: float
11+
ymax: float
12+
zmax: float

backend_py/primary/primary/routers/grid3d/schemas.py

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from pydantic import BaseModel
44
from webviz_pkg.core_utils.b64 import B64FloatArray, B64UintArray
55

6+
from .._shared.schemas import BoundingBox3d
7+
68

79
# Rename?
810
class Grid3dGeometry(BaseModel):
@@ -26,17 +28,6 @@ class Grid3dMappedProperty(BaseModel):
2628
max_grid_prop_value: float
2729

2830

29-
class BoundingBox3d(BaseModel):
30-
"""Bounding box for a 3D grid geometry"""
31-
32-
xmin: float
33-
ymin: float
34-
zmin: float
35-
xmax: float
36-
ymax: float
37-
zmax: float
38-
39-
4031
class Grid3dZone(BaseModel):
4132
"""Named subset of 3D grid layers (Zone)"""
4233

Lines changed: 54 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,66 @@
1-
from typing import List
2-
3-
import orjson
41
import numpy as np
5-
import xtgeo
2+
from numpy.typing import NDArray
3+
from webviz_pkg.core_utils.b64 import b64_encode_float_array_as_float32
64

5+
from primary.services.vds_access.response_types import VdsSliceMetadata
6+
from primary.services.sumo_access.seismic_types import SeismicCubeMeta
77
from . import schemas
88

99

10-
def surface_to_float32_array(values: np.ndarray) -> List[float]:
11-
values = values.astype(np.float32)
12-
np.ma.set_fill_value(values, np.nan)
13-
values = np.ma.filled(values)
14-
15-
# Rotate 90 deg left.
16-
# This will cause the width of to run along the X axis
17-
# and height of along Y axis (starting from bottom.)
18-
values = np.rot90(values)
10+
def to_api_vds_cube_meta(seismic_meta: SeismicCubeMeta) -> schemas.SeismicCubeMeta:
11+
"""
12+
Create API SeismicCubeMeta from VdsSliceMetadata
13+
"""
1914

20-
return values.flatten().tolist()
15+
return schemas.SeismicCubeMeta(
16+
seismicAttribute=seismic_meta.seismic_attribute,
17+
unit=seismic_meta.unit,
18+
isoDateOrInterval=seismic_meta.iso_date_or_interval,
19+
isObservation=seismic_meta.is_observation,
20+
isDepth=seismic_meta.is_depth,
21+
bbox=schemas.BoundingBox3d(
22+
xmin=seismic_meta.bbox.xmin,
23+
ymin=seismic_meta.bbox.ymin,
24+
zmin=seismic_meta.bbox.zmin,
25+
xmax=seismic_meta.bbox.xmax,
26+
ymax=seismic_meta.bbox.ymax,
27+
zmax=seismic_meta.bbox.zmax,
28+
),
29+
spec=schemas.SeismicCubeSpec(
30+
numCols=seismic_meta.spec.num_cols,
31+
numRows=seismic_meta.spec.num_rows,
32+
numLayers=seismic_meta.spec.num_layers,
33+
xOrigin=seismic_meta.spec.x_origin,
34+
yOrigin=seismic_meta.spec.y_origin,
35+
zOrigin=seismic_meta.spec.z_origin,
36+
xInc=seismic_meta.spec.x_inc,
37+
yInc=seismic_meta.spec.y_inc,
38+
zInc=seismic_meta.spec.z_inc,
39+
yFlip=seismic_meta.spec.y_flip,
40+
zFlip=seismic_meta.spec.z_flip,
41+
rotation=seismic_meta.spec.rotation,
42+
),
43+
)
2144

2245

23-
def to_api_surface_data(
24-
xtgeo_surf: xtgeo.RegularSurface, property_values: np.ndarray
25-
) -> schemas.SurfaceMeshAndProperty:
46+
def to_api_vds_slice_data(
47+
flattened_slice_traces_array: NDArray[np.float32], metadata: VdsSliceMetadata
48+
) -> schemas.SeismicSliceData:
2649
"""
27-
Create API SurfaceData from xtgeo regular surface
50+
Create API SeismicCrosslineData from VdsSliceMetadata and flattened slice traces array
2851
"""
29-
float32_mesh = surface_to_float32_array(xtgeo_surf.values)
30-
float32_property = surface_to_float32_array(property_values)
3152

32-
return schemas.SurfaceMeshAndProperty(
33-
x_ori=xtgeo_surf.xori,
34-
y_ori=xtgeo_surf.yori,
35-
x_count=xtgeo_surf.ncol,
36-
y_count=xtgeo_surf.nrow,
37-
x_inc=xtgeo_surf.xinc,
38-
y_inc=xtgeo_surf.yinc,
39-
x_min=xtgeo_surf.xmin,
40-
x_max=xtgeo_surf.xmax,
41-
y_min=xtgeo_surf.ymin,
42-
y_max=xtgeo_surf.ymax,
43-
mesh_value_min=xtgeo_surf.values.min(),
44-
mesh_value_max=xtgeo_surf.values.max(),
45-
property_value_min=property_values.min(),
46-
property_value_max=property_values.max(),
47-
rot_deg=xtgeo_surf.rotation,
48-
mesh_data=orjson.dumps(float32_mesh).decode("utf-8"), # pylint: disable=maybe-no-member
49-
property_data=orjson.dumps(float32_property).decode("utf-8"), # pylint: disable=maybe-no-member
53+
return schemas.SeismicSliceData(
54+
slice_traces_b64arr=b64_encode_float_array_as_float32(flattened_slice_traces_array),
55+
bbox_utm=metadata.geospatial,
56+
u_min=metadata.x["min"],
57+
u_max=metadata.x["max"],
58+
u_num_samples=metadata.x["samples"],
59+
u_unit=metadata.x["unit"],
60+
v_min=metadata.y["min"],
61+
v_max=metadata.y["max"],
62+
v_num_samples=metadata.y["samples"],
63+
v_unit=metadata.y["unit"],
64+
value_min=np.nanmin(flattened_slice_traces_array),
65+
value_max=np.nanmax(flattened_slice_traces_array),
5066
)

backend_py/primary/primary/routers/seismic/router.py

Lines changed: 120 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
from primary.services.vds_access.request_types import VdsCoordinates, VdsCoordinateSystem
1111
from primary.services.vds_access.response_types import VdsMetadata
1212
from primary.services.vds_access.vds_access import VdsAccess
13-
1413
from . import schemas
14+
from . import converters
1515

1616
LOGGER = logging.getLogger(__name__)
1717

@@ -32,11 +32,128 @@ async def get_seismic_cube_meta_list(
3232
)
3333
seismic_cube_meta_list = await access.get_seismic_cube_meta_list_async()
3434
try:
35-
return [schemas.SeismicCubeMeta(**meta.__dict__) for meta in seismic_cube_meta_list]
35+
return [converters.to_api_vds_cube_meta(meta) for meta in seismic_cube_meta_list]
3636
except ValueError as exc:
3737
raise HTTPException(status_code=400, detail=str(exc)) from exc
3838

3939

40+
@router.get("/get_inline_slice/")
41+
async def get_inline_slice(
42+
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
43+
case_uuid: str = Query(description="Sumo case uuid"),
44+
ensemble_name: str = Query(description="Ensemble name"),
45+
realization_num: int = Query(description="Realization number"),
46+
seismic_attribute: str = Query(description="Seismic cube attribute"),
47+
time_or_interval_str: str = Query(description="Timestamp or timestep"),
48+
observed: bool = Query(description="Observed or simulated"),
49+
inline_no: int = Query(description="Inline number"),
50+
) -> schemas.SeismicSliceData:
51+
"""Get a seismic inline from a seismic cube."""
52+
seismic_access = await SeismicAccess.from_case_uuid_async(
53+
authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name
54+
)
55+
56+
vds_handle: Optional[VdsHandle] = None
57+
try:
58+
vds_handle = await seismic_access.get_vds_handle_async(
59+
realization=realization_num,
60+
seismic_attribute=seismic_attribute,
61+
time_or_interval_str=time_or_interval_str,
62+
observed=observed,
63+
)
64+
except ValueError as err:
65+
raise HTTPException(status_code=404, detail=str(err)) from err
66+
67+
if vds_handle is None:
68+
raise HTTPException(status_code=404, detail="Vds handle not found")
69+
70+
vds_access = VdsAccess(sas_token=vds_handle.sas_token, vds_url=vds_handle.vds_url)
71+
72+
flattened_slice_traces_array, metadata = await vds_access.get_inline_slice(line_no=inline_no)
73+
74+
return converters.to_api_vds_slice_data(
75+
flattened_slice_traces_array=flattened_slice_traces_array, metadata=metadata
76+
)
77+
78+
79+
@router.get("/get_crossline_slice/")
80+
async def get_crossline_slice(
81+
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
82+
case_uuid: str = Query(description="Sumo case uuid"),
83+
ensemble_name: str = Query(description="Ensemble name"),
84+
realization_num: int = Query(description="Realization number"),
85+
seismic_attribute: str = Query(description="Seismic cube attribute"),
86+
time_or_interval_str: str = Query(description="Timestamp or timestep"),
87+
observed: bool = Query(description="Observed or simulated"),
88+
crossline_no: int = Query(description="Crossline number"),
89+
) -> schemas.SeismicSliceData:
90+
"""Get a seismic crossline from a seismic cube."""
91+
seismic_access = await SeismicAccess.from_case_uuid_async(
92+
authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name
93+
)
94+
95+
vds_handle: Optional[VdsHandle] = None
96+
try:
97+
vds_handle = await seismic_access.get_vds_handle_async(
98+
realization=realization_num,
99+
seismic_attribute=seismic_attribute,
100+
time_or_interval_str=time_or_interval_str,
101+
observed=observed,
102+
)
103+
except ValueError as err:
104+
raise HTTPException(status_code=404, detail=str(err)) from err
105+
106+
if vds_handle is None:
107+
raise HTTPException(status_code=404, detail="Vds handle not found")
108+
109+
vds_access = VdsAccess(sas_token=vds_handle.sas_token, vds_url=vds_handle.vds_url)
110+
111+
flattened_slice_traces_array, metadata = await vds_access.get_crossline_slice(line_no=crossline_no)
112+
113+
return converters.to_api_vds_slice_data(
114+
flattened_slice_traces_array=flattened_slice_traces_array, metadata=metadata
115+
)
116+
117+
118+
@router.get("/get_depth_slice/")
119+
async def get_depth_slice(
120+
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
121+
case_uuid: str = Query(description="Sumo case uuid"),
122+
ensemble_name: str = Query(description="Ensemble name"),
123+
realization_num: int = Query(description="Realization number"),
124+
seismic_attribute: str = Query(description="Seismic cube attribute"),
125+
time_or_interval_str: str = Query(description="Timestamp or timestep"),
126+
observed: bool = Query(description="Observed or simulated"),
127+
depth: int = Query(description="depth"),
128+
) -> schemas.SeismicSliceData:
129+
"""Get a seismic depth slice from a seismic cube."""
130+
seismic_access = await SeismicAccess.from_case_uuid_async(
131+
authenticated_user.get_sumo_access_token(), case_uuid, ensemble_name
132+
)
133+
134+
vds_handle: Optional[VdsHandle] = None
135+
try:
136+
vds_handle = await seismic_access.get_vds_handle_async(
137+
realization=realization_num,
138+
seismic_attribute=seismic_attribute,
139+
time_or_interval_str=time_or_interval_str,
140+
observed=observed,
141+
)
142+
except ValueError as err:
143+
raise HTTPException(status_code=404, detail=str(err)) from err
144+
145+
if vds_handle is None:
146+
raise HTTPException(status_code=404, detail="Vds handle not found")
147+
148+
vds_access = VdsAccess(sas_token=vds_handle.sas_token, vds_url=vds_handle.vds_url)
149+
150+
flattened_slice_traces_array, metadata = await vds_access.get_depth_slice(depth=depth)
151+
152+
return converters.to_api_vds_slice_data(
153+
flattened_slice_traces_array=flattened_slice_traces_array, metadata=metadata
154+
)
155+
156+
40157
@router.post("/get_seismic_fence/")
41158
async def post_get_seismic_fence(
42159
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
@@ -86,8 +203,8 @@ async def post_get_seismic_fence(
86203
coordinates=VdsCoordinates(polyline.x_points, polyline.y_points),
87204
coordinate_system=VdsCoordinateSystem.CDP,
88205
)
89-
90206
meta: VdsMetadata = await vds_access.get_metadata_async()
207+
91208
if len(meta.axis) != 3:
92209
raise ValueError(f"Expected 3 axes, got {len(meta.axis)}")
93210
depth_axis_meta = meta.axis[2]

backend_py/primary/primary/routers/seismic/schemas.py

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,40 @@
33
from pydantic import BaseModel
44
from webviz_pkg.core_utils.b64 import B64FloatArray
55

6+
from .._shared.schemas import BoundingBox3d
7+
8+
9+
class SeismicCubeSpec(BaseModel):
10+
"""
11+
Specification for a seismic cube.
12+
"""
13+
14+
numCols: int
15+
numRows: int
16+
numLayers: int
17+
xOrigin: float
18+
yOrigin: float
19+
zOrigin: float
20+
xInc: float
21+
yInc: float
22+
zInc: float
23+
yFlip: int
24+
zFlip: int
25+
rotation: float
26+
627

728
class SeismicCubeMeta(BaseModel):
8-
seismic_attribute: str
9-
iso_date_or_interval: str
10-
is_observation: bool
11-
is_depth: bool
29+
"""
30+
Metadata for a seismic cube.
31+
"""
32+
33+
seismicAttribute: str
34+
unit: str
35+
isoDateOrInterval: str
36+
isObservation: bool
37+
isDepth: bool
38+
bbox: BoundingBox3d
39+
spec: SeismicCubeSpec
1240

1341

1442
class SeismicFencePolyline(BaseModel):
@@ -60,21 +88,16 @@ class SeismicFenceData(BaseModel):
6088
max_fence_depth: float
6189

6290

63-
class SurfaceMeshAndProperty(BaseModel):
64-
x_ori: float
65-
y_ori: float
66-
x_count: int
67-
y_count: int
68-
x_inc: float
69-
y_inc: float
70-
x_min: float
71-
x_max: float
72-
y_min: float
73-
y_max: float
74-
mesh_value_min: float
75-
mesh_value_max: float
76-
property_value_min: float
77-
property_value_max: float
78-
rot_deg: float
79-
mesh_data: str
80-
property_data: str
91+
class SeismicSliceData(BaseModel):
92+
slice_traces_b64arr: B64FloatArray
93+
bbox_utm: List[List[float]]
94+
u_min: int
95+
u_max: int
96+
u_num_samples: int
97+
u_unit: str
98+
v_min: float
99+
v_max: float
100+
v_num_samples: int
101+
v_unit: str
102+
value_min: float
103+
value_max: float

0 commit comments

Comments
 (0)