Skip to content

Commit 2ee754a

Browse files
authored
Merge pull request #1664 from knutfrode/dev
[run-ex] Extracted coastline_crossing from stranding algorithm as a s…
2 parents 645eaba + c59e79f commit 2ee754a

2 files changed

Lines changed: 86 additions & 30 deletions

File tree

opendrift/models/basemodel/__init__.py

Lines changed: 70 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,55 @@
6969
Mode = Enum('Mode', ['Config', 'Ready', 'Run', 'Result'])
7070
rl = roaring_landmask.RoaringLandmask.new()
7171

72+
def coastline_crossing(lon1, lat1, lon2, lat2, step_degrees, land_side=True):
73+
"""Return the coastline crossing points between positions in water (lon1,lat1) and positions on land (lon2, lat2).
74+
75+
This function uses the RoaringLandmask to find the coastline crossing points,
76+
but can be generalised to any landmask later.
77+
78+
Args:
79+
lon1, lat1: Coordinates of first point
80+
lon2, lat2: Coordinates of second point
81+
precision: Precision for coastline approximation, in degrees
82+
land_side: If True, return the last position in water and first position on land.
83+
84+
Returns:
85+
lon, lat
86+
Last position in water (if land_side is False) or first position on land (if land_side is True (default)) along transect
87+
"""
88+
89+
lon1 = np.atleast_1d(lon1)
90+
lat1 = np.atleast_1d(lat1)
91+
lon2 = np.atleast_1d(lon2)
92+
lat2 = np.atleast_1d(lat2)
93+
if land_side is True:
94+
lon_c = lon2
95+
lat_c = lat2
96+
else:
97+
lon_c = lon1
98+
lat_c = lat1
99+
for i, (lon1, lat1, lon2, lat2) in enumerate(zip(lon1, lat1, lon2, lat2)):
100+
x_degree_diff = np.abs(lon2 - lon1)
101+
y_degree_diff = np.abs(lat2 - lat1)
102+
if x_degree_diff == 0 and y_degree_diff == 0:
103+
continue
104+
x_samples = np.floor(x_degree_diff / step_degrees).astype(np.int64) if x_degree_diff > step_degrees else 1
105+
x = np.linspace(lon1, lon2, x_samples)
106+
y_samples = np.floor(y_degree_diff/ step_degrees).astype(np.int64) if y_degree_diff > step_degrees else 1
107+
y = np.linspace(lat1, lat2, y_samples)
108+
109+
xx, yy = np.meshgrid(x,y)
110+
xx, yy = xx.ravel(), yy.ravel()
111+
112+
rl_mask = rl.contains_many(xx, yy)
113+
if np.any(rl_mask):
114+
index = np.argmax(rl_mask)
115+
if land_side is False:
116+
index = np.maximum(0, index-1)
117+
lon_c[i] = xx[index]
118+
lat_c[i] = yy[index]
119+
return lon_c, lat_c
120+
72121
def require_mode(mode: Union[Mode, List[Mode]], post_next_mode=False, error=None):
73122
if not isinstance(mode, list):
74123
mode = [mode]
@@ -643,34 +692,14 @@ def interact_with_coastline(self, final=False):
643692
if not coastline_approximation_precision:
644693
return
645694

646-
for on_land_id, on_land_prev_id in zip(on_land, self.elements.ID[on_land]):
647-
lon = self.elements.lon[on_land_id]
648-
lat = self.elements.lat[on_land_id]
649-
prev_lon = self._elements_previous.lon[on_land_prev_id].data
650-
prev_lat = self._elements_previous.lat[on_land_prev_id].data
651-
step_degrees = float(coastline_approximation_precision)
652-
653-
x_degree_diff = np.abs(prev_lon - lon)
654-
y_degree_diff = np.abs(prev_lat - lat)
655-
if x_degree_diff == 0 and y_degree_diff == 0:
656-
continue
657-
x_samples = np.floor(x_degree_diff / step_degrees).astype(np.int64) if x_degree_diff > step_degrees else 1
658-
x = np.linspace(prev_lon, lon, x_samples)
659-
660-
y_samples = np.floor(y_degree_diff/ step_degrees).astype(np.int64) if y_degree_diff > step_degrees else 1
661-
y = np.linspace(prev_lat, lat, y_samples)
662-
663-
xx, yy = np.meshgrid(x,y)
664-
xx, yy = xx.ravel(), yy.ravel()
665-
666-
rl_mask = rl.contains_many(xx, yy)
667-
if np.any(rl_mask):
668-
index = np.argmax(rl_mask)
669-
new_lon = xx[index]
670-
new_lat = yy[index]
671-
672-
self.elements.lon[on_land_id] = new_lon
673-
self.elements.lat[on_land_id] = new_lat
695+
self.elements.lon[on_land], self.elements.lat[on_land] = coastline_crossing(
696+
self._elements_previous.lon[on_land],
697+
self._elements_previous.lat[on_land],
698+
self.elements.lon[on_land],
699+
self.elements.lat[on_land],
700+
coastline_approximation_precision,
701+
land_side=True
702+
)
674703

675704
self.environment.land_binary_mask[on_land] = 0
676705

@@ -687,8 +716,19 @@ def interact_with_coastline(self, final=False):
687716
logger.debug('%s elements hit coastline, '
688717
'moving back to water' % len(on_land))
689718
on_land_ID = self.elements.ID[on_land]
690-
self.elements.lon[on_land] = self._elements_previous.lon[on_land_ID]
691-
self.elements.lat[on_land] = self._elements_previous.lat[on_land_ID]
719+
720+
if not coastline_approximation_precision:
721+
self.elements.lon[on_land] = self._elements_previous.lon[on_land_ID]
722+
self.elements.lat[on_land] = self._elements_previous.lat[on_land_ID]
723+
else:
724+
self.elements.lon[on_land], self.elements.lat[on_land] = coastline_crossing(
725+
self._elements_previous.lon[on_land],
726+
self._elements_previous.lat[on_land],
727+
self.elements.lon[on_land],
728+
self.elements.lat[on_land],
729+
coastline_approximation_precision,
730+
land_side=False
731+
)
692732
self.environment.land_binary_mask[on_land] = 0
693733

694734
def interact_with_seafloor(self):

tests/models/test_stranding.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,22 @@ def test_stranding_approximation(self):
160160
self.assertAlmostEqual(o.elements_deactivated.lon[0], 5.066, 3)
161161
self.assertAlmostEqual(o2.elements_deactivated.lon[0], 5.051, 3)
162162

163+
def test_previous_approximation(self):
164+
o = OceanDrift(loglevel=0)
165+
o.set_config('general:coastline_action', 'previous')
166+
o.set_config('environment:constant:x_sea_water_velocity', 1)
167+
o.set_config('general:coastline_approximation_precision', None)
168+
o.seed_elements(lon=4.55, lat=60, time=datetime.now())
169+
o.run(steps=10)
170+
self.assertAlmostEqual(o.elements.lon[0], 5.00161, 4)
171+
o2 = OceanDrift(loglevel=0)
172+
o2.set_config('general:coastline_action', 'previous')
173+
o2.set_config('environment:constant:x_sea_water_velocity', 1)
174+
o2.set_config('general:coastline_approximation_precision', .001)
175+
o2.seed_elements(lon=4.55, lat=60, time=datetime.now())
176+
o2.run(steps=10)
177+
self.assertAlmostEqual(o2.elements.lon[0], 5.1152, 3)
178+
163179

164180
if __name__ == '__main__':
165181
unittest.main()

0 commit comments

Comments
 (0)