Skip to content

Commit a6ef9c0

Browse files
Add statistical surfaces
1 parent b366429 commit a6ef9c0

23 files changed

+628
-239
lines changed

backend/src/backend/primary/routers/surface/router.py

+87-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
from . import converters
2222
from . import schemas
2323

24+
import numpy as np
25+
2426
LOGGER = logging.getLogger(__name__)
2527

2628
router = APIRouter()
@@ -215,8 +217,92 @@ async def get_property_surface_resampled_to_statistical_static_surface(
215217
@router.post("/well_intersection_reals_from_user_session")
216218
async def well_intersection_reals_from_user_session(
217219
request: Request,
218-
polyline: schemas.FencePolyline = Body(embed=True),
220+
ensemble_ident: schemas.EnsembleIdent = Body(embed=True),
221+
realizations_surface_set_spec: schemas.RealizationsSurfaceSetSpec = Body(embed=True),
222+
surface_fence_spec: schemas.SurfaceFenceSpec = Body(embed=True),
219223
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
220224
) -> List[schemas.SurfaceIntersectionPoints]:
221225
response = await proxy_to_user_session(request, authenticated_user)
222226
return response
227+
228+
229+
@router.post("/well_intersection_statistics")
230+
async def well_intersection_statistics(
231+
request: Request,
232+
ensemble_ident: schemas.EnsembleIdent = Body(embed=True),
233+
statistical_surface_set_spec: schemas.StatisticalSurfaceSetSpec = Body(embed=True),
234+
surface_fence_spec: schemas.SurfaceFenceSpec = Body(embed=True),
235+
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
236+
) -> List[schemas.SurfaceIntersectionPoints]:
237+
access = await SurfaceAccess.from_case_uuid(
238+
authenticated_user.get_sumo_access_token(), ensemble_ident.case_uuid, ensemble_ident.ensemble_name
239+
)
240+
surfaces = []
241+
242+
async def fetch_surface(statistic, surface_name):
243+
surface = await access.get_statistical_surface_data_async(
244+
statistic_function=StatisticFunction.from_string_value(statistic),
245+
name=surface_name,
246+
attribute=statistical_surface_set_spec.surface_attribute,
247+
realizations=statistical_surface_set_spec.realization_nums,
248+
)
249+
surface.name = surface_name + "_" + statistic
250+
return surface
251+
252+
async def fetch_all_surfaces():
253+
tasks = []
254+
for statistic in statistical_surface_set_spec.statistic_function:
255+
for surface_name in statistical_surface_set_spec.surface_names:
256+
# Create a task for each combination of statistic and surface_name
257+
task = fetch_surface(statistic, surface_name)
258+
tasks.append(task)
259+
260+
# Run all the tasks concurrently
261+
tmp_surfaces = await asyncio.gather(*tasks)
262+
return tmp_surfaces
263+
264+
# for statistic in statistical_surface_set_spec.statistic_function:
265+
# for surface_name in statistical_surface_set_spec.surface_names:
266+
# surface = await access.get_statistical_surface_data_async(
267+
# statistic_function=StatisticFunction.from_string_value(statistic),
268+
# name=surface_name,
269+
# attribute=statistical_surface_set_spec.surface_attribute,
270+
# realizations=statistical_surface_set_spec.realization_nums,
271+
# )
272+
# surface.name = surface_name + "_" + statistic
273+
# surfaces.append(surface)
274+
surfaces = await fetch_all_surfaces()
275+
276+
fence_arr = np.array(
277+
[
278+
surface_fence_spec.x_points,
279+
surface_fence_spec.y_points,
280+
np.zeros(len(surface_fence_spec.y_points)),
281+
surface_fence_spec.cum_length,
282+
]
283+
).T
284+
intersections = await make_intersections(surfaces, fence_arr)
285+
print(intersections)
286+
return intersections
287+
288+
289+
from concurrent.futures import ThreadPoolExecutor
290+
import asyncio
291+
292+
293+
async def make_intersections(surfaces, fence_arr):
294+
def make_intersection(surf):
295+
line = surf.get_randomline(fence_arr)
296+
intersection = schemas.SurfaceIntersectionPoints(
297+
name=f"{surf.name}",
298+
cum_length=line[:, 0].tolist(),
299+
z_array=line[:, 1].tolist(),
300+
)
301+
return intersection
302+
303+
loop = asyncio.get_running_loop()
304+
305+
with ThreadPoolExecutor() as executor:
306+
tasks = [loop.run_in_executor(executor, make_intersection, surf) for surf in surfaces]
307+
intersections = await asyncio.gather(*tasks)
308+
return intersections

backend/src/backend/primary/routers/surface/schemas.py

+23-10
Original file line numberDiff line numberDiff line change
@@ -70,18 +70,31 @@ class SurfaceData(BaseModel):
7070
values_b64arr: B64FloatArray
7171

7272

73-
class FencePolyline(BaseModel):
74-
case_uuid: str
75-
ensemble_name: str
76-
realization_nums: list[int] | None
77-
names: list[str]
78-
attribute: str
73+
class SurfaceIntersectionPoints(BaseModel):
74+
name: str
75+
z_array: list[float]
76+
cum_length: list[float]
77+
78+
79+
class SurfaceFenceSpec(BaseModel):
7980
x_points: list[float]
8081
y_points: list[float]
8182
cum_length: list[float]
8283

8384

84-
class SurfaceIntersectionPoints(BaseModel):
85-
name: str
86-
z_array: list[float]
87-
cum_length: list[float]
85+
class RealizationsSurfaceSetSpec(BaseModel):
86+
realization_nums: list[int]
87+
surface_names: list[str]
88+
surface_attribute: str
89+
90+
91+
class EnsembleIdent(BaseModel):
92+
case_uuid: str
93+
ensemble_name: str
94+
95+
96+
class StatisticalSurfaceSetSpec(BaseModel):
97+
surface_names: list[str]
98+
surface_attribute: str
99+
realization_nums: list[int]
100+
statistic_function: list[SurfaceStatisticFunction]

backend/src/backend/user_session/routers/surface/router.py

+18-12
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,21 @@ async def well_intersection_reals_from_user_session(
7171
authenticated_user: AuthenticatedUser = Depends(AuthHelper.get_authenticated_user),
7272
) -> List[schemas.SurfaceIntersectionPoints]:
7373
body = await request.json()
74-
polyline = schemas.FencePolyline(**body.get("polyline"))
74+
75+
ensemble_ident = schemas.EnsembleIdent(**body.get("ensemble_ident"))
76+
realization_surface_set_spec = schemas.RealizationsSurfaceSetSpec(**body.get("realizations_surface_set_spec"))
77+
surface_fence_spec = schemas.SurfaceFenceSpec(**body.get("surface_fence_spec"))
7578
timer = PerfTimer()
7679
# Config
77-
case_uuid = polyline.case_uuid
78-
snames = polyline.names
79-
sattr = polyline.attribute
80-
ensemble_name = polyline.ensemble_name
80+
case_uuid = ensemble_ident.case_uuid
81+
snames = realization_surface_set_spec.surface_names
82+
sattr = realization_surface_set_spec.surface_attribute
83+
ensemble_name = ensemble_ident.ensemble_name
84+
realization_nums = realization_surface_set_spec.realization_nums
8185
# Get uuids
82-
uuids = get_uuids(case_uuid, ensemble_name, polyline, snames, sattr, authenticated_user.get_sumo_access_token())
86+
uuids = get_uuids(
87+
case_uuid, ensemble_name, realization_nums, snames, sattr, authenticated_user.get_sumo_access_token()
88+
)
8389
elapsed_meta = timer.lap_ms()
8490

8591
# Check if cached
@@ -111,10 +117,10 @@ async def well_intersection_reals_from_user_session(
111117
# Intersect
112118
fence_arr = np.array(
113119
[
114-
polyline.x_points,
115-
polyline.y_points,
116-
np.zeros(len(polyline.y_points)),
117-
polyline.cum_length,
120+
surface_fence_spec.x_points,
121+
surface_fence_spec.y_points,
122+
np.zeros(len(surface_fence_spec.y_points)),
123+
surface_fence_spec.cum_length,
118124
]
119125
).T
120126
intersections = await make_intersections(surfaces, fence_arr)
@@ -179,14 +185,14 @@ def make_intersection(surf):
179185
return intersections
180186

181187

182-
def get_uuids(case_uuid, ensemble_name, polyline, snames, sattr, bearer_token):
188+
def get_uuids(case_uuid, ensemble_name, realization_nums, snames, sattr, bearer_token):
183189
sumo_client = SumoClient(env="prod", token=bearer_token, interactive=False)
184190
case_collection = CaseCollection(sumo_client).filter(uuid=case_uuid)
185191
case = case_collection[0]
186192
surface_collection = case.surfaces.filter(
187193
iteration=ensemble_name,
188194
name=snames,
189195
tagname=sattr,
190-
realization=polyline.realization_nums,
196+
realization=realization_nums,
191197
)
192198
return [surf.uuid for surf in surface_collection]

backend/src/services/sumo_access/surface_access.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ async def get_statistical_surface_data_async(
144144
name: str,
145145
attribute: str,
146146
time_or_interval_str: Optional[str] = None,
147+
realizations: Optional[List[int]] = None,
147148
) -> Optional[xtgeo.RegularSurface]:
148149
"""
149150
Compute statistic and return surface data
@@ -179,15 +180,16 @@ async def get_statistical_surface_data_async(
179180
name=name,
180181
tagname=attribute,
181182
time=time_filter,
183+
realization=realizations,
182184
)
183-
185+
if not realizations:
186+
realizations = await surface_collection.realizations_async
184187
surf_count = await surface_collection.length_async()
185188
if surf_count == 0:
186189
LOGGER.warning(f"No statistical surfaces found in Sumo for {addr_str}")
187190
return None
188191
et_locate_ms = timer.lap_ms()
189192

190-
realizations = await surface_collection.realizations_async
191193
et_collect_reals_ms = timer.lap_ms()
192194

193195
xtgeo_surf = await _compute_statistical_surface_async(statistic_function, surface_collection)

frontend/src/api/index.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,17 @@ export { B64FloatArray as B64FloatArray_api } from './models/B64FloatArray';
1313
export { B64UintArray as B64UintArray_api } from './models/B64UintArray';
1414
export type { Body_get_realizations_response as Body_get_realizations_response_api } from './models/Body_get_realizations_response';
1515
export type { Body_well_intersection_reals_from_user_session as Body_well_intersection_reals_from_user_session_api } from './models/Body_well_intersection_reals_from_user_session';
16+
export type { Body_well_intersection_statistics as Body_well_intersection_statistics_api } from './models/Body_well_intersection_statistics';
1617
export type { CaseInfo as CaseInfo_api } from './models/CaseInfo';
1718
export type { Completions as Completions_api } from './models/Completions';
1819
export type { EnsembleDetails as EnsembleDetails_api } from './models/EnsembleDetails';
20+
export type { EnsembleIdent as EnsembleIdent_api } from './models/EnsembleIdent';
1921
export type { EnsembleInfo as EnsembleInfo_api } from './models/EnsembleInfo';
2022
export type { EnsembleParameter as EnsembleParameter_api } from './models/EnsembleParameter';
2123
export type { EnsembleParameterDescription as EnsembleParameterDescription_api } from './models/EnsembleParameterDescription';
2224
export type { EnsembleScalarResponse as EnsembleScalarResponse_api } from './models/EnsembleScalarResponse';
2325
export type { EnsembleSensitivity as EnsembleSensitivity_api } from './models/EnsembleSensitivity';
2426
export type { EnsembleSensitivityCase as EnsembleSensitivityCase_api } from './models/EnsembleSensitivityCase';
25-
export type { FencePolyline as FencePolyline_api } from './models/FencePolyline';
2627
export type { FieldInfo as FieldInfo_api } from './models/FieldInfo';
2728
export { Frequency as Frequency_api } from './models/Frequency';
2829
export type { GraphUserPhoto as GraphUserPhoto_api } from './models/GraphUserPhoto';
@@ -36,19 +37,22 @@ export type { PolygonData as PolygonData_api } from './models/PolygonData';
3637
export { PolygonsAttributeType as PolygonsAttributeType_api } from './models/PolygonsAttributeType';
3738
export type { PolygonsMeta as PolygonsMeta_api } from './models/PolygonsMeta';
3839
export type { PvtData as PvtData_api } from './models/PvtData';
40+
export type { RealizationsSurfaceSetSpec as RealizationsSurfaceSetSpec_api } from './models/RealizationsSurfaceSetSpec';
3941
export type { RftInfo as RftInfo_api } from './models/RftInfo';
4042
export type { RftObservation as RftObservation_api } from './models/RftObservation';
4143
export type { RftObservations as RftObservations_api } from './models/RftObservations';
4244
export type { RftRealizationData as RftRealizationData_api } from './models/RftRealizationData';
4345
export type { SeismicCubeMeta as SeismicCubeMeta_api } from './models/SeismicCubeMeta';
4446
export { SensitivityType as SensitivityType_api } from './models/SensitivityType';
47+
export type { StatisticalSurfaceSetSpec as StatisticalSurfaceSetSpec_api } from './models/StatisticalSurfaceSetSpec';
4548
export { StatisticFunction as StatisticFunction_api } from './models/StatisticFunction';
4649
export type { StatisticValueObject as StatisticValueObject_api } from './models/StatisticValueObject';
4750
export { StratigraphicFeature as StratigraphicFeature_api } from './models/StratigraphicFeature';
4851
export type { SummaryVectorDateObservation as SummaryVectorDateObservation_api } from './models/SummaryVectorDateObservation';
4952
export type { SummaryVectorObservations as SummaryVectorObservations_api } from './models/SummaryVectorObservations';
5053
export { SurfaceAttributeType as SurfaceAttributeType_api } from './models/SurfaceAttributeType';
5154
export type { SurfaceData as SurfaceData_api } from './models/SurfaceData';
55+
export type { SurfaceFenceSpec as SurfaceFenceSpec_api } from './models/SurfaceFenceSpec';
5256
export type { SurfaceIntersectionPoints as SurfaceIntersectionPoints_api } from './models/SurfaceIntersectionPoints';
5357
export type { SurfaceMeta as SurfaceMeta_api } from './models/SurfaceMeta';
5458
export { SurfaceStatisticFunction as SurfaceStatisticFunction_api } from './models/SurfaceStatisticFunction';

frontend/src/api/models/Body_well_intersection_reals_from_user_session.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22
/* tslint:disable */
33
/* eslint-disable */
44

5-
import type { FencePolyline } from './FencePolyline';
5+
import type { EnsembleIdent } from './EnsembleIdent';
6+
import type { RealizationsSurfaceSetSpec } from './RealizationsSurfaceSetSpec';
7+
import type { SurfaceFenceSpec } from './SurfaceFenceSpec';
68

79
export type Body_well_intersection_reals_from_user_session = {
8-
polyline: FencePolyline;
10+
ensemble_ident: EnsembleIdent;
11+
realizations_surface_set_spec: RealizationsSurfaceSetSpec;
12+
surface_fence_spec: SurfaceFenceSpec;
913
};
1014

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/* istanbul ignore file */
2+
/* tslint:disable */
3+
/* eslint-disable */
4+
5+
import type { EnsembleIdent } from './EnsembleIdent';
6+
import type { StatisticalSurfaceSetSpec } from './StatisticalSurfaceSetSpec';
7+
import type { SurfaceFenceSpec } from './SurfaceFenceSpec';
8+
9+
export type Body_well_intersection_statistics = {
10+
ensemble_ident: EnsembleIdent;
11+
statistical_surface_set_spec: StatisticalSurfaceSetSpec;
12+
surface_fence_spec: SurfaceFenceSpec;
13+
};
14+
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/* istanbul ignore file */
2+
/* tslint:disable */
3+
/* eslint-disable */
4+
5+
export type EnsembleIdent = {
6+
case_uuid: string;
7+
ensemble_name: string;
8+
};
9+

frontend/src/api/models/FencePolyline.ts

-15
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/* istanbul ignore file */
2+
/* tslint:disable */
3+
/* eslint-disable */
4+
5+
export type RealizationsSurfaceSetSpec = {
6+
realization_nums: Array<number>;
7+
surface_names: Array<string>;
8+
surface_attribute: string;
9+
};
10+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/* istanbul ignore file */
2+
/* tslint:disable */
3+
/* eslint-disable */
4+
5+
import type { SurfaceStatisticFunction } from './SurfaceStatisticFunction';
6+
7+
export type StatisticalSurfaceSetSpec = {
8+
surface_names: Array<string>;
9+
surface_attribute: string;
10+
realization_nums: Array<number>;
11+
statistic_function: Array<SurfaceStatisticFunction>;
12+
};
13+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/* istanbul ignore file */
2+
/* tslint:disable */
3+
/* eslint-disable */
4+
5+
export type SurfaceFenceSpec = {
6+
x_points: Array<number>;
7+
y_points: Array<number>;
8+
cum_length: Array<number>;
9+
};
10+

frontend/src/api/services/SurfaceService.ts

+21
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
/* tslint:disable */
33
/* eslint-disable */
44
import type { Body_well_intersection_reals_from_user_session } from '../models/Body_well_intersection_reals_from_user_session';
5+
import type { Body_well_intersection_statistics } from '../models/Body_well_intersection_statistics';
56
import type { SurfaceData } from '../models/SurfaceData';
67
import type { SurfaceIntersectionPoints } from '../models/SurfaceIntersectionPoints';
78
import type { SurfaceMeta } from '../models/SurfaceMeta';
@@ -218,4 +219,24 @@ export class SurfaceService {
218219
});
219220
}
220221

222+
/**
223+
* Well Intersection Statistics
224+
* @param requestBody
225+
* @returns SurfaceIntersectionPoints Successful Response
226+
* @throws ApiError
227+
*/
228+
public wellIntersectionStatistics(
229+
requestBody: Body_well_intersection_statistics,
230+
): CancelablePromise<Array<SurfaceIntersectionPoints>> {
231+
return this.httpRequest.request({
232+
method: 'POST',
233+
url: '/surface/well_intersection_statistics',
234+
body: requestBody,
235+
mediaType: 'application/json',
236+
errors: {
237+
422: `Validation Error`,
238+
},
239+
});
240+
}
241+
221242
}

0 commit comments

Comments
 (0)