14
14
#
15
15
# Copyright 2015, 2023, Knut-Frode Dagestad, MET Norway
16
16
# Copyright 2024, Lenny Hucher, NERSC, Norway
17
- # Copyright 2023, 2024, Achref Othmani, NERSC, Norway
17
+ # Copyright 2023, 2024, 2025 Achref Othmani, NERSC, Norway
18
18
19
19
"""
20
20
This code is initiated from the following reference with posterior modifications.
34
34
35
35
36
36
# Constants
37
- rho_water = 1027 # Density of water (kg/m^3)
38
- rho_air = 1.293 # Density of air (kg/m^3)
39
- rho_ice = 917 # Density of ice (kg/m^3)
40
- rho_iceb = 900 # Density of iceberg (kg/m^3)
41
- g = 9.81 # Acceleration due to gravity in m/s²
42
- omega = 7.2921e-5 # Angular frequency (rad/s)
43
- csi = 1 # Sea ice coefficient of resistance
44
- wave_drag_coef = 0.3 # Wave drag coefficient
37
+ rho_water = 1027 # Density of water (kg/m^3)
38
+ rho_air = 1.293 # Density of air (kg/m^3)
39
+ rho_ice = 917 # Density of ice (kg/m^3)
40
+ rho_iceb = 900 # Density of iceberg (kg/m^3)
41
+ g = 9.81 # Acceleration due to gravity in m/s²
42
+ omega = 7.2921e-5 # Angular frequency (rad/s)
43
+ csi = 1 # Sea ice coefficient of resistance
44
+ wave_drag_coef = 0.3 # Wave drag coefficient
45
45
46
46
47
47
48
48
class IcebergObj (LagrangianArray ):
49
- """ Extending Lagrangian3DArray with relevant properties for an Iceberg """
50
-
49
+ """ Extending LagrangianArray with relevant properties for an Iceberg """
51
50
variables = LagrangianArray .add_variables ([
52
51
('sail' , {'dtype' : np .float32 ,
53
52
'units' : 'm' ,
@@ -69,7 +68,7 @@ class IcebergObj(LagrangianArray):
69
68
'default' : 30 ,
70
69
'description' : 'Width of iceberg)' ,
71
70
'level' : CONFIG_LEVEL_ESSENTIAL }),
72
- ('weight_coeff' , {'dtype' : np .float32 , # Relative to the shape of iceberg (e.g. 1 for tabular; 0.3 for pinnacle: It affects the mass only ! )
71
+ ('weight_coeff' , {'dtype' : np .float32 , # Relative to the shape of iceberg (e.g. 1 for tabular; 0.3 for pinnacle: It affects the mass only)
73
72
'units' : '1' ,
74
73
'default' : 1 }),
75
74
('water_drag_coeff' , {'dtype' : np .float32 , # Ocean drag coeff.
@@ -133,8 +132,8 @@ def wave_radiation_force(rho_water, wave_height, wave_direction, iceb_length):
133
132
wave_direction : Wave direction
134
133
iceb_length : Iceberg's length
135
134
"""
136
- F_wave_x = (0.5 * rho_water * wave_drag_coef * g * iceb_length * (wave_height / 2 ) ** 2 * np .sin (np .deg2rad (( wave_direction + 180 ) % 360 )))
137
- F_wave_y = (0.5 * rho_water * wave_drag_coef * g * iceb_length * (wave_height / 2 ) ** 2 * np .cos (np .deg2rad (( wave_direction + 180 ) % 360 )))
135
+ F_wave_x = (0.5 * rho_water * wave_drag_coef * g * iceb_length * (wave_height / 2 ) ** 2 * np .sin (np .deg2rad (wave_direction )))
136
+ F_wave_y = (0.5 * rho_water * wave_drag_coef * g * iceb_length * (wave_height / 2 ) ** 2 * np .cos (np .deg2rad (wave_direction )))
138
137
return np .array ([F_wave_x , F_wave_y ])
139
138
140
139
@@ -276,6 +275,7 @@ class OpenBerg(OpenDriftSimulation):
276
275
"x_sea_water_velocity" : {"fallback" : None , "profiles" : True },
277
276
"y_sea_water_velocity" : {"fallback" : None , "profiles" : True },
278
277
"sea_floor_depth_below_sea_level" : {"fallback" : 10000 },
278
+ 'sea_surface_height' : {'fallback' : 0 },
279
279
"sea_surface_x_slope" : {"fallback" : 0 },
280
280
"sea_surface_y_slope" : {"fallback" : 0 },
281
281
"x_wind" : {"fallback" : None , "important" : True },
@@ -300,12 +300,21 @@ def get_profile_masked(self, variable):
300
300
"""
301
301
draft = self .elements .draft
302
302
profile = self .environment_profiles [variable ]
303
- z = self .environment_profiles ["z" ]
303
+ z = self .environment_profiles ["z" ]
304
+ if profile .ndim == 1 :
305
+ profile = profile [np .newaxis , :]
304
306
if z is None or (len (z ) == 1 and z [0 ] is None ):
305
- z = np .zeros_like (profile )
306
- mask = draft [:, np .newaxis ] < - z
307
- mask [np .argmax (mask , axis = 0 ), np .arange (mask .shape [1 ])] = False
308
- return np .ma .masked_array (profile , mask .T , fill_value = np .nan )
307
+ z = np .zeros (profile .shape [0 ])
308
+ z = np .atleast_1d (z )
309
+ if z .ndim == 1 :
310
+ z = z [:, np .newaxis ]
311
+ draft = np .atleast_1d (draft )
312
+ mask = draft [np .newaxis , :] < - z
313
+ if mask .shape [0 ] > 1 :
314
+ mask [np .argmax (mask , axis = 0 ), np .arange (mask .shape [1 ])] = False
315
+ assert profile .shape == mask .shape , f"Incompatible shapes: profile { profile .shape } , mask { mask .shape } "
316
+
317
+ return np .ma .masked_array (profile , mask , fill_value = np .nan )
309
318
310
319
311
320
def get_basal_env (self , variable ):
@@ -327,7 +336,7 @@ def __init__(self, *args, **kwargs):
327
336
},
328
337
'drift:stokes_drift' :{
329
338
'type' : 'bool' ,
330
- 'default' : True ,
339
+ 'default' : False ,
331
340
'description' : 'If True, stokes drift force is added' ,
332
341
'level' : CONFIG_LEVEL_BASIC
333
342
},
@@ -339,7 +348,7 @@ def __init__(self, *args, **kwargs):
339
348
},
340
349
'drift:sea_surface_slope' :{
341
350
'type' : 'bool' ,
342
- 'default' : True ,
351
+ 'default' : False ,
343
352
'description' : 'If True, sea surface slope force is added' ,
344
353
'level' : CONFIG_LEVEL_BASIC ,
345
354
},
@@ -403,6 +412,7 @@ def advect_iceberg(self):
403
412
rho_water = PhysicsMethods .sea_water_density (T , S )
404
413
sea_slope_x = self .environment .sea_surface_x_slope
405
414
sea_slope_y = self .environment .sea_surface_y_slope
415
+ sea_surface_height = self .environment .sea_surface_height
406
416
wave_height = self .environment .sea_surface_wave_significant_height
407
417
wave_direction = self .environment .sea_surface_wave_from_direction
408
418
sea_ice_thickness = self .environment .sea_ice_thickness
@@ -438,7 +448,7 @@ def advect_iceberg(self):
438
448
vprof_mean_inter = (vprof [1 :] + vprof [:- 1 ]) / 2
439
449
mask = mask [:- 1 ]
440
450
thickness_reshaped = np .tile (thickness , (1 , mask .shape [1 ]))
441
- thickness_reshaped [ mask ] = np .nan
451
+ thickness_reshaped = np .where ( mask , np . nan , thickness_reshaped )
442
452
umean = np .nansum (thickness_reshaped * uprof_mean_inter , axis = 0 ) / np .nansum (thickness_reshaped , axis = 0 )
443
453
vmean = np .nansum (thickness_reshaped * vprof_mean_inter , axis = 0 ) / np .nansum (thickness_reshaped , axis = 0 )
444
454
water_vel = np .array ([umean , vmean ])
@@ -471,11 +481,29 @@ def dynamic(t,iceb_vel, water_vel, wind_vel, wave_height, wave_direction, Ao,
471
481
V0 = advect_iceberg_no_acc (f , water_vel , wind_vel ) # Approximation of the solution of the dynamic equation for the iceberg velocity
472
482
V0 [:, sea_ice_conc >= 0.9 ] = sea_ice_vel [:, sea_ice_conc >= 0.9 ] # With this criterium, the iceberg moves with the sea ice
473
483
V0 = V0 .flatten () # V0 needs to be 1D
474
- hwall = draft - water_depth
484
+
485
+ effective_water_depth = water_depth + sea_surface_height
486
+ hwall = draft - effective_water_depth
475
487
grounded = np .logical_and (hwall >= 0 , grounding )
476
- if any (grounded ) and grounding :
477
- logger .debug (f"Grounding condition : Icebergs grounded = { len (hwall [hwall > 0 ])} , hwall={ np .round (hwall [hwall > 0 ],3 )} meters" )
478
- self .elements .moving [grounded ] = 0 # Grounded icebergs shall not move, also not with diffusivity
488
+ if grounding :
489
+ # Determine which icebergs are grounded
490
+ if np .any (grounded ):
491
+ logger .debug (f"Grounding condition: Icebergs grounded = { np .sum (grounded )} , "
492
+ f"hwall = { np .round (hwall [grounded ], 3 )} meters" )
493
+ # Grounded icebergs stop moving
494
+ self .elements .moving [grounded ] = 0
495
+ else :
496
+ logger .debug ("No grounded icebergs detected in this timestep" )
497
+
498
+ # Check for Degrounding regardless of whether grounding occurred now
499
+ degrounding = np .logical_and (self .elements .moving == 0 , hwall < 0 )
500
+ if np .any (degrounding ):
501
+ logger .debug (f"Degrounding condition: Icebergs degrounded = { np .sum (degrounding )} , "
502
+ f"hwall = { np .round (hwall [degrounding ], 3 )} meters" )
503
+ # Degrounded icebergs start moving again
504
+ self .elements .moving [degrounding ] = 1
505
+ else :
506
+ logger .debug ("Grounding process disabled in configuration" )
479
507
480
508
sol = solve_ivp (dynamic , [0 , self .time_step .total_seconds ()], V0 ,
481
509
args = (water_vel , wind_vel , wave_height , wave_direction , Ao , Aa , rho_water ,
0 commit comments