Skip to content

Commit 3d0bcde

Browse files
updated map code to instantly refloat when refloat_halflife == 0.
1 parent 922fea7 commit 3d0bcde

File tree

2 files changed

+217
-21
lines changed

2 files changed

+217
-21
lines changed

py_gnome/gnome/maps/map.py

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,8 @@
99
As well as a few implementations of actual maps
1010
"""
1111

12-
# This is a re-write of the C++ raster map approach
13-
14-
# Features:
15-
# - Map now handles spillable area and map bounds as polygons
12+
# RasterMap is the primary implimentation:
13+
# - Map handles spillable area and map bounds as polygons
1614
# - raster is the same aspect ratio as the land
1715
# - internally, raster is a numpy array
1816
# - land raster is only as big as the land -- if the map bounds are bigger,
@@ -22,7 +20,6 @@
2220
# - Perhaps we just use non-projected coordinates for the raster map?
2321
# It makes for a little less computation at every step.
2422

25-
2623
import os
2724
import math
2825
import warnings
@@ -104,13 +101,13 @@ class GnomeMap(GnomeId):
104101
"""
105102
The very simplest map for GNOME -- all water
106103
with only a bounding box for the map bounds.
107-
"""
104+
105+
Also the base class for all maps.
108106
# This also serves as a description of the interface and
109107
# base class for more complex maps
108+
"""
110109

111110
_schema = GnomeMapSchema
112-
113-
refloat_halflife = None # note -- no land, so never used
114111
_ref_as = 'map'
115112

116113
def __init__(self,
@@ -154,6 +151,14 @@ def __init__(self,
154151
self.spillable_area = spillable_area
155152
self.land_polys = land_polys
156153

154+
@property
155+
def refloat_halflife(self):
156+
return self._refloat_halflife / self.seconds_in_hour
157+
158+
@refloat_halflife.setter
159+
def refloat_halflife(self, value):
160+
self._refloat_halflife = value * self.seconds_in_hour
161+
157162
def __add__(self, other):
158163
# Just so pyghnome users will get a helpful message
159164
# if the do: model.map += a_map
@@ -868,13 +873,13 @@ def raster(self, arr):
868873
self._raster = np.ascontiguousarray(arr)
869874
self.build_coarser_rasters()
870875

871-
@property
872-
def refloat_halflife(self):
873-
return self._refloat_halflife / self.seconds_in_hour
876+
# @property
877+
# def refloat_halflife(self):
878+
# return self._refloat_halflife / self.seconds_in_hour
874879

875-
@refloat_halflife.setter
876-
def refloat_halflife(self, value):
877-
self._refloat_halflife = value * self.seconds_in_hour
880+
# @refloat_halflife.setter
881+
# def refloat_halflife(self, value):
882+
# self._refloat_halflife = value * self.seconds_in_hour
878883

879884
@property
880885
def approximate_raster_interval(self):
@@ -1035,6 +1040,14 @@ def beach_elements(self, sc, model_time=None):
10351040
last_water_positions[beached, :2] = \
10361041
self.projection.to_lonlat(last_water_pos_pixel[beached, :2])
10371042

1043+
# zero half-life will not stick at all.
1044+
# so put the back to last water position right away.
1045+
if self.refloat_halflife == 0.0:
1046+
# set the beached elements to the last water position
1047+
next_pos[beached, :2] = last_water_positions[beached, :2]
1048+
# set them back to in_water
1049+
status_codes[beached] = oil_status.in_water
1050+
10381051
self._set_off_map_status(sc)
10391052

10401053
# # todo: need a prepare_for_model_run() so map adds these keys to
@@ -1130,16 +1143,18 @@ def to_pixel_array(self, coords):
11301143
return self.projection.to_pixel(coords)
11311144

11321145

1146+
# Fixme -- use utilities.convert_longitude
1147+
11331148
def ShiftLon360(points):
11341149
try:
1135-
points[points[:,0]<0,0] = points[:,0]+360
1150+
points[points[:,0] < 0, 0] = points[:, 0] + 360
11361151
except ValueError:
11371152
pass
11381153
return points
11391154

11401155
def ShiftLon180(points):
11411156
try:
1142-
points[points[:,0]>180,0] = points[:,0]-360
1157+
points[points[:,0] > 180, 0] = points[:, 0] - 360
11431158
except ValueError:
11441159
pass
11451160
return points
@@ -1157,6 +1172,7 @@ def __init__(self,
11571172
raster_size=4096 * 4096,
11581173
map_bounds=None,
11591174
spillable_area=None,
1175+
land_polys=None,
11601176
shift_lons=0,
11611177
**kwargs):
11621178
"""
@@ -1177,7 +1193,9 @@ def __init__(self,
11771193
180, or 360 are valid inputs
11781194
:type shiftLons: integer
11791195
1180-
Optional arguments (kwargs):
1196+
:param shift_lons=0: Whether to shift the longitude reference frame:
1197+
Accepted values:
1198+
0: do nothing. 180: shift to -180--180. 360: shift to 0--360
11811199
11821200
:param refloat_halflife: the half-life (in hours) for the re-floating.
11831201
@@ -1186,6 +1204,8 @@ def __init__(self,
11861204
11871205
:param spillable_area: The polygon bounding the spillable_area
11881206
1207+
Optional arguments (kwargs):
1208+
11891209
:param id: unique ID of the object. Using UUID as a string.
11901210
This is only used when loading object from save file.
11911211
:type id: string
@@ -1208,7 +1228,7 @@ def __init__(self,
12081228
land_polys = PolygonSet() # and lakes....
12091229
spillable_area_bna = PolygonSet()
12101230

1211-
#add if based on input param
1231+
# add if based on input param
12121232
tf = ShiftLon360 if shift_lons == 360 else ShiftLon180 if shift_lons == 180 else None
12131233
if tf is not None:
12141234
polygons.TransformData(tf)
@@ -1257,7 +1277,7 @@ def __init__(self,
12571277
# get the raster as a numpy array:
12581278
raster, projection = self.build_raster(land_polys, BB)
12591279

1260-
super(MapFromBNA, self).__init__(
1280+
super().__init__(
12611281
raster=raster,
12621282
projection=projection,
12631283
map_bounds=map_bounds,
@@ -1299,7 +1319,6 @@ def build_raster(self, land_polys=None, BB=None):
12991319

13001320
# draw the land to the background
13011321
for poly in land_polys:
1302-
# fixme -- this should be something like "land"
13031322
if poly.metadata[2] == '1':
13041323
canvas.draw_polygon(poly,
13051324
line_color='land',
@@ -1391,6 +1410,8 @@ def to_geojson(self):
13911410
class MapFromUGrid(RasterMap):
13921411
"""
13931412
A raster land-water map, created from netcdf File of a UGrid
1413+
1414+
NOTE: not complete or well tested
13941415
"""
13951416
_schema = MapFromUGridSchema
13961417

py_gnome/tests/unit_tests/test_maps/test_map.py

Lines changed: 176 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,7 @@ def test_refloat_some_onland(self):
494494

495495
class Test_MapfromBNA:
496496

497-
print("instaniating map:", testbnamap)
497+
print("instantiating map:", testbnamap)
498498
# NOTE: this is a pretty course map -- for testing
499499
bna_map = MapFromBNA(testbnamap, refloat_halflife=6, raster_size=1000)
500500

@@ -910,6 +910,181 @@ def test_bna_no_map_bounds():
910910
(6., 10.),
911911
])
912912

913+
class TestZeroRefloat():
914+
"""
915+
some tests for setting refloat to zero
916+
917+
Depending on the flag set -- it should
918+
either refloat them all at the next timestep, or right away
919+
920+
"""
921+
# a very simple raster:
922+
(w, h) = (20, 10)
923+
raster = np.zeros((w, h), dtype=np.uint8)
924+
# a single skinny vertical line:
925+
raster[10, :] = 1
926+
927+
# old style no longer exists ...
928+
# def test_zero_refloat_old_style(self):
929+
# """
930+
# test a few LEs
931+
# """
932+
# time_step = 900 # 15 minutes in seconds
933+
# gmap = RasterMap(refloat_halflife=0,
934+
# raster=self.raster,
935+
# map_bounds=((-50, -30), (-50, 30),
936+
# (50, 30), (50, -30)),
937+
# projection=NoProjection())
938+
939+
# gmap.instant_refloat=False
940+
# # all moving left to right:
941+
942+
# spill = sample_sc_release(2)
943+
944+
# spill['positions'] = np.array(((5.0, 5.0, 0.),
945+
# (5.0, 7.0, 0.),
946+
# ),
947+
# dtype=np.float64)
948+
# spill['next_positions'] = np.array(((15.0, 5.0, 0.),
949+
# (15.0, 7.0, 0.),
950+
# ),
951+
# dtype=np.float64)
952+
953+
# gmap.beach_elements(spill)
954+
955+
# pos = spill['positions']
956+
# npos = spill['next_positions']
957+
# lwp = spill['last_water_positions']
958+
# sc = spill['status_codes']
959+
960+
# print("after beaching")
961+
# print("before refloating")
962+
963+
# print(f"{pos=}")
964+
# print(f"{npos=}")
965+
# print(f"{lwp=}")
966+
# print(f"{sc=}")
967+
968+
# # elements should be beached
969+
# assert np.all(sc == oil_status.on_land)
970+
# # last water position should be set to right before the land
971+
# assert np.all(lwp == [[9., 5., 0.],
972+
# [9., 7., 0.]])
973+
# # next positions are on land
974+
# assert np.all(npos == [[10., 5., 0.],
975+
# [10., 7., 0.]])
976+
977+
# # positions are unchanged
978+
# assert np.all(pos == [[5., 5., 0.],
979+
# [5., 7., 0.]])
980+
981+
# gmap.refloat_elements(spill, time_step)
982+
983+
# pos2 = spill['positions']
984+
# npos2 = spill['next_positions']
985+
# lwp2 = spill['last_water_positions']
986+
# sc2 = spill['status_codes']
987+
988+
# print("after refloating")
989+
# print(f"{pos2=}")
990+
# print(f"{npos2=}")
991+
# print(f"{lwp2=}")
992+
# print(f"{sc2=}")
993+
994+
# # after refloating:
995+
# # elements should not be beached
996+
# assert np.all(sc2 == oil_status.in_water)
997+
# # last water position should still be set to right before the land
998+
# assert np.all(lwp2 == [[9., 5., 0.],
999+
# [9., 7., 0.]])
1000+
# # next positions are irrelevent
1001+
# # assert np.all(npos2 == [[10., 5., 0.],
1002+
# # [10., 7., 0.]])
1003+
1004+
# # positions are at last water position
1005+
# assert np.all(pos2 == [[9., 5., 0.],
1006+
# [9., 7., 0.]])
1007+
1008+
def test_zero_refloat_instant_refloat(self):
1009+
"""
1010+
test a few LEs
1011+
"""
1012+
time_step = 900 # 15 minutes in seconds
1013+
gmap = RasterMap(refloat_halflife=0,
1014+
raster=self.raster,
1015+
map_bounds=((-50, -30), (-50, 30),
1016+
(50, 30), (50, -30)),
1017+
projection=NoProjection())
1018+
1019+
gmap.instant_refloat=True
1020+
1021+
# all moving left to right:
1022+
1023+
spill = sample_sc_release(2)
1024+
1025+
spill['positions'] = np.array(((5.0, 5.0, 0.),
1026+
(5.0, 7.0, 0.),
1027+
),
1028+
dtype=np.float64)
1029+
spill['next_positions'] = np.array(((15.0, 5.0, 0.),
1030+
(15.0, 7.0, 0.),
1031+
),
1032+
dtype=np.float64)
1033+
1034+
gmap.beach_elements(spill)
1035+
1036+
pos = spill['positions']
1037+
npos = spill['next_positions']
1038+
lwp = spill['last_water_positions']
1039+
sc = spill['status_codes']
1040+
1041+
print("after beaching")
1042+
print("before refloating")
1043+
1044+
print(f"{pos=}")
1045+
print(f"{npos=}")
1046+
print(f"{lwp=}")
1047+
print(f"{sc=}")
1048+
1049+
# no elements should be beached
1050+
assert np.all(sc == oil_status.in_water)
1051+
# last water position should be set to right before the land
1052+
assert np.all(lwp == [[9., 5., 0.],
1053+
[9., 7., 0.]])
1054+
# next positions are at last water position
1055+
assert np.all(npos == [[9., 5., 0.],
1056+
[9., 7., 0.]])
1057+
# positions are unchanged
1058+
assert np.all(pos == [[5., 5., 0.],
1059+
[5., 7., 0.]])
1060+
1061+
# refloat should not make a difference:
1062+
gmap.refloat_elements(spill, time_step)
1063+
1064+
pos2 = spill['positions']
1065+
npos2 = spill['next_positions']
1066+
lwp2 = spill['last_water_positions']
1067+
sc2 = spill['status_codes']
1068+
1069+
print("after refloating")
1070+
print(f"{pos2=}")
1071+
print(f"{npos2=}")
1072+
print(f"{lwp2=}")
1073+
print(f"{sc2=}")
1074+
1075+
# after refloating: -- no difference if none beached
1076+
# no elements should be beached
1077+
assert np.all(sc2 == oil_status.in_water)
1078+
# last water position should be set to right before the land
1079+
assert np.all(lwp2 == [[9., 5., 0.],
1080+
[9., 7., 0.]])
1081+
# next positions are at last water position
1082+
assert np.all(npos2 == [[9., 5., 0.],
1083+
[9., 7., 0.]])
1084+
# positions are unchanged
1085+
assert np.all(pos2 == [[5., 5., 0.],
1086+
[5., 7., 0.]])
1087+
9131088

9141089
class Test_lake():
9151090
"""

0 commit comments

Comments
 (0)