Skip to content

Commit c1021bf

Browse files
committed
Added distance-based localization functions for 2D fields
added snapshot test Functions: - calc_rho_for_2d_grid_layer to calculate elements of the RHO matrix.
1 parent 9d7361e commit c1021bf

3 files changed

Lines changed: 209 additions & 1 deletion

File tree

src/ert/config/surface_config.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010
from pydantic import field_serializer
1111
from surfio import IrapHeader, IrapSurface
1212

13+
from ert.field_utils import (
14+
calc_rho_for_2d_grid_layer,
15+
transform_local_ellipse_angle_to_local_coords,
16+
transform_positions_to_local_field_coordinates,
17+
)
1318
from ert.substitutions import substitute_runpath_name
1419

1520
from ._str_to_bool import str_to_bool
@@ -248,3 +253,55 @@ def load_parameter_graph(self) -> nx.Graph[int]:
248253
this flattening process"""
249254

250255
return create_flattened_cube_graph(px=self.ncol, py=self.nrow, pz=1)
256+
257+
def calc_rho_for_2d_grid_layer(
258+
self,
259+
obs_xpos: npt.NDArray[np.float64],
260+
obs_ypos: npt.NDArray[np.float64],
261+
obs_main_range: npt.NDArray[np.float64],
262+
obs_perp_range: npt.NDArray[np.float64],
263+
obs_anisotropy_angle: npt.NDArray[np.float64],
264+
) -> npt.NDArray[np.float64]:
265+
"""Function to calculate scaling values to be used in the RHO matrix
266+
for distance-based localization.
267+
268+
Args:
269+
obs_xpos: x-coordinates in global coordinates of observations
270+
obs_ypos: y-coordinates in global coordinates of observations
271+
obs_main_range: Size of influence ellipse main principal direction.
272+
obs_perp_range: Size of influence ellipse second principal direction.
273+
obs_anisotropy_angle: Rotation angle anticlock wise of main principal
274+
direction of influence ellipse relative to global coordinate
275+
system's x-axis.
276+
277+
Returns:
278+
Scaling values (elements of the RHO matrix) as a numpy array
279+
of shape=(nx,ny,nobservations)
280+
281+
"""
282+
# Transform observation positions to local surface coordinates
283+
xpos, ypos = transform_positions_to_local_field_coordinates(
284+
(self.xori, self.yori), self.rotation, obs_xpos, obs_ypos
285+
)
286+
# Transform ellipse orientation to local surface coordinates
287+
rotation_angle_of_localization_ellipse = (
288+
transform_local_ellipse_angle_to_local_coords(
289+
self.rotation, obs_anisotropy_angle
290+
)
291+
)
292+
293+
# Assume the coordinate system is not flipped.
294+
# This means the right_handed_grid_indexing is False
295+
assert self.yflip == 1
296+
return calc_rho_for_2d_grid_layer(
297+
self.ncol,
298+
self.nrow,
299+
self.xinc,
300+
self.yinc,
301+
xpos,
302+
ypos,
303+
obs_main_range,
304+
obs_perp_range,
305+
rotation_angle_of_localization_ellipse,
306+
right_handed_grid_indexing=False,
307+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
[[[0.50498824 0.00651928 0. ... 0.99997919 0. 0. ]
2+
[0.54097227 0.00890534 0. ... 0.99786346 0. 0. ]
3+
[0.57488894 0.01182687 0. ... 0.99172024 0. 0. ]
4+
...
5+
[0. 0. 0.06072022 ... 0. 0. 0.99464177]
6+
[0. 0. 0.05473404 ... 0. 0. 0.99848823]
7+
[0. 0. 0.04896573 ... 0. 0. 0.99964644]]
8+
9+
[[0.54097227 0.00602875 0. ... 0.99786346 0. 0. ]
10+
[0.57926199 0.00832618 0. ... 0.99981325 0. 0. ]
11+
[0.61536138 0.01116308 0. ... 0.99761887 0. 0. ]
12+
...
13+
[0. 0. 0.06174645 ... 0. 0. 0.99613059]
14+
[0. 0. 0.0554226 ... 0. 0. 0.99685459]
15+
[0. 0. 0.04936423 ... 0. 0. 0.9948815 ]]
16+
17+
[[0.57488894 0.00553249 0. ... 0.99172024 0. 0. ]
18+
[0.61536138 0.00773125 0. ... 0.99761887 0. 0. ]
19+
[0.65353538 0.01047068 0. ... 0.99948267 0. 0. ]
20+
...
21+
[0. 0. 0.06243868 ... 0. 0. 0.99136881]
22+
[0. 0. 0.05580123 ... 0. 0. 0.98902204]
23+
[0. 0. 0.04947859 ... 0. 0. 0.98407544]]
24+
25+
...
26+
27+
[[0. 0. 0.04947859 ... 0. 0.46753824 0. ]
28+
[0. 0. 0.05580123 ... 0. 0.82297363 0. ]
29+
[0. 0. 0.06243868 ... 0. 0.99434669 0. ]
30+
...
31+
[0. 0.01047068 0. ... 0.18232878 0. 0. ]
32+
[0. 0.00773125 0. ... 0.16524114 0. 0. ]
33+
[0. 0.00553249 0. ... 0.14890707 0. 0. ]]
34+
35+
[[0. 0. 0.04936423 ... 0. 0.82503208 0. ]
36+
[0. 0. 0.0554226 ... 0. 0.99794506 0. ]
37+
[0. 0. 0.06174645 ... 0. 0.82297363 0. ]
38+
...
39+
[0. 0.01116308 0. ... 0.19644278 0. 0. ]
40+
[0. 0.00832618 0. ... 0.17895554 0. 0. ]
41+
[0. 0.00602875 0. ... 0.16213323 0. 0. ]]
42+
43+
[[0. 0. 0.04896573 ... 0. 0.99976955 0. ]
44+
[0. 0. 0.05473404 ... 0. 0.82503208 0. ]
45+
[0. 0. 0.06072022 ... 0. 0.46753824 0. ]
46+
...
47+
[0. 0.01182687 0. ... 0.21060644 0. 0. ]
48+
[0. 0.00890534 0. ... 0.19282388 0. 0. ]
49+
[0. 0.00651928 0. ... 0.17560682 0. 0. ]]]

tests/ert/unit_tests/config/test_surface_config.py

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import numpy as np
55
import pytest
66
import xtgeo
7-
from surfio import IrapSurface
7+
from surfio import IrapHeader, IrapSurface
88

99
from ert.config import ConfigValidationError, SurfaceConfig
1010
from ert.config.parameter_config import InvalidParameterFile
@@ -339,3 +339,105 @@ def test_surface_create_storage_datasets_raises_surface_mismatch_error_when_the_
339339
)
340340
with pytest.raises(InvalidParameterFile, match=expected_error_msg):
341341
next(storage_dataset_iterator)
342+
343+
344+
@pytest.fixture
345+
def surface_for_dl():
346+
# Return a SurfaceConfig object for test purpose
347+
348+
# Create a synthetic surface grid with rotation
349+
nx = 100
350+
ny = 120
351+
xori = 1000.0
352+
yori = 2000.0
353+
xsize = 500.0
354+
ysize = 600.0
355+
xinc = xsize / nx
356+
yinc = ysize / ny
357+
rotation = 0.0
358+
return SurfaceConfig(
359+
type="surface",
360+
name="MySurface",
361+
forward_init=True,
362+
update=True,
363+
ncol=nx,
364+
nrow=ny,
365+
xori=xori,
366+
yori=yori,
367+
xinc=xinc,
368+
yinc=yinc,
369+
rotation=rotation,
370+
yflip=1,
371+
forward_init_file="dummy.txt",
372+
output_file=Path("dummy.txt"),
373+
base_surface_path="dummy.txt",
374+
)
375+
376+
377+
@pytest.mark.parametrize(
378+
"xpos, ypos, main_range, perp_range, anisotropy_angle",
379+
[
380+
(
381+
[1050.0, 1250.0, 1250.0, 1250.0, 1000.0, 1500.0, 1000.0], # xpos
382+
[2050.0, 2300.0, 2300.0, 2300.0, 2000.0, 2000.0, 2600.0], # ypos
383+
[100.0, 300.0, 300.0, 600.0, 1000.0, 300.0, 300.0], # main_range
384+
[100.0, 100.0, 100.0, 200.0, 100.0, 10.0, 100.0], # perp_range
385+
[0.0, 35.0, 135.0, -135.0, 45.0, -45.0, -60.0], # angle
386+
),
387+
],
388+
)
389+
def test_calc_rho_for_2d_grid_layer(
390+
snapshot,
391+
surface_for_dl,
392+
xpos: list[float],
393+
ypos: list[float],
394+
main_range: list[float],
395+
perp_range: list[float],
396+
anisotropy_angle: list[float],
397+
):
398+
write_surface_file = False
399+
400+
xposition = np.array(xpos)
401+
yposition = np.array(ypos)
402+
mainrange = np.array(main_range)
403+
perprange = np.array(perp_range)
404+
angles = np.array(anisotropy_angle)
405+
406+
# Dimension of rho_for_one_grid_layer is (nx,ny,nobs)
407+
rho_for_one_grid_layer = surface_for_dl.calc_rho_for_2d_grid_layer(
408+
xposition,
409+
yposition,
410+
mainrange,
411+
perprange,
412+
angles,
413+
)
414+
# Ensure -0 and +0 will be 0
415+
rho_for_one_grid_layer = np.where(
416+
rho_for_one_grid_layer == 0, 0.0, rho_for_one_grid_layer
417+
)
418+
419+
snapshot.assert_match(
420+
str(rho_for_one_grid_layer) + "\n", "testdata_rho_for_one_grid_layer.txt"
421+
)
422+
if write_surface_file:
423+
# Write surface of rho for visualization
424+
425+
for obs_indx in range(len(xposition)):
426+
surf = IrapSurface(
427+
header=IrapHeader(
428+
ncol=surface_for_dl.ncol,
429+
nrow=surface_for_dl.nrow,
430+
xori=surface_for_dl.xori,
431+
yori=surface_for_dl.yori,
432+
xinc=surface_for_dl.xinc,
433+
yinc=surface_for_dl.yinc,
434+
rot=surface_for_dl.rotation,
435+
xrot=surface_for_dl.xori,
436+
yrot=surface_for_dl.yori,
437+
),
438+
values=rho_for_one_grid_layer[:, :, obs_indx],
439+
)
440+
441+
file_path = Path("tmp_2d_rho_for_obs_" + str(obs_indx) + ".txt")
442+
print(f"Write file: {file_path}")
443+
surf.to_ascii_file(file_path)

0 commit comments

Comments
 (0)