ladim 2.0.3__tar.gz → 2.0.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. {ladim-2.0.3 → ladim-2.0.5}/PKG-INFO +2 -2
  2. {ladim-2.0.3 → ladim-2.0.5}/ladim/__init__.py +1 -1
  3. {ladim-2.0.3 → ladim-2.0.5}/ladim/__main__.py +1 -1
  4. {ladim-2.0.3 → ladim-2.0.5}/ladim/config.py +14 -2
  5. {ladim-2.0.3 → ladim-2.0.5}/ladim/forcing.py +2 -0
  6. {ladim-2.0.3 → ladim-2.0.5}/ladim/grid.py +14 -4
  7. {ladim-2.0.3 → ladim-2.0.5}/ladim/gridforce/zROMS.py +55 -33
  8. {ladim-2.0.3 → ladim-2.0.5}/ladim/output.py +0 -1
  9. {ladim-2.0.3 → ladim-2.0.5}/ladim.egg-info/PKG-INFO +2 -2
  10. {ladim-2.0.3 → ladim-2.0.5}/tests/test_config.py +5 -1
  11. {ladim-2.0.3 → ladim-2.0.5}/LICENSE +0 -0
  12. {ladim-2.0.3 → ladim-2.0.5}/README.md +0 -0
  13. {ladim-2.0.3 → ladim-2.0.5}/ladim/gridforce/ROMS.py +0 -0
  14. {ladim-2.0.3 → ladim-2.0.5}/ladim/gridforce/__init__.py +0 -0
  15. {ladim-2.0.3 → ladim-2.0.5}/ladim/gridforce/analytical.py +0 -0
  16. {ladim-2.0.3 → ladim-2.0.5}/ladim/ibms/__init__.py +0 -0
  17. {ladim-2.0.3 → ladim-2.0.5}/ladim/ibms/light.py +0 -0
  18. {ladim-2.0.3 → ladim-2.0.5}/ladim/main.py +0 -0
  19. {ladim-2.0.3 → ladim-2.0.5}/ladim/model.py +0 -0
  20. {ladim-2.0.3 → ladim-2.0.5}/ladim/plugins/__init__.py +0 -0
  21. {ladim-2.0.3 → ladim-2.0.5}/ladim/release.py +0 -0
  22. {ladim-2.0.3 → ladim-2.0.5}/ladim/sample.py +0 -0
  23. {ladim-2.0.3 → ladim-2.0.5}/ladim/solver.py +0 -0
  24. {ladim-2.0.3 → ladim-2.0.5}/ladim/state.py +0 -0
  25. {ladim-2.0.3 → ladim-2.0.5}/ladim/tracker.py +0 -0
  26. {ladim-2.0.3 → ladim-2.0.5}/ladim/utilities.py +0 -0
  27. {ladim-2.0.3 → ladim-2.0.5}/ladim.egg-info/SOURCES.txt +0 -0
  28. {ladim-2.0.3 → ladim-2.0.5}/ladim.egg-info/dependency_links.txt +0 -0
  29. {ladim-2.0.3 → ladim-2.0.5}/ladim.egg-info/entry_points.txt +0 -0
  30. {ladim-2.0.3 → ladim-2.0.5}/ladim.egg-info/requires.txt +0 -0
  31. {ladim-2.0.3 → ladim-2.0.5}/ladim.egg-info/top_level.txt +0 -0
  32. {ladim-2.0.3 → ladim-2.0.5}/postladim/__init__.py +0 -0
  33. {ladim-2.0.3 → ladim-2.0.5}/postladim/cellcount.py +0 -0
  34. {ladim-2.0.3 → ladim-2.0.5}/postladim/kde_plot.py +0 -0
  35. {ladim-2.0.3 → ladim-2.0.5}/postladim/particlefile.py +0 -0
  36. {ladim-2.0.3 → ladim-2.0.5}/postladim/variable.py +0 -0
  37. {ladim-2.0.3 → ladim-2.0.5}/pyproject.toml +0 -0
  38. {ladim-2.0.3 → ladim-2.0.5}/setup.cfg +0 -0
  39. {ladim-2.0.3 → ladim-2.0.5}/tests/test_forcing.py +0 -0
  40. {ladim-2.0.3 → ladim-2.0.5}/tests/test_ladim.py +0 -0
  41. {ladim-2.0.3 → ladim-2.0.5}/tests/test_model.py +0 -0
  42. {ladim-2.0.3 → ladim-2.0.5}/tests/test_output.py +0 -0
  43. {ladim-2.0.3 → ladim-2.0.5}/tests/test_release.py +0 -0
  44. {ladim-2.0.3 → ladim-2.0.5}/tests/test_solver.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: ladim
3
- Version: 2.0.3
3
+ Version: 2.0.5
4
4
  Summary: Lagrangian Advection and Diffusion Model
5
5
  Home-page: https://github.com/pnsaevik/ladim
6
6
  Author: Bjørn Ådlandsvik
@@ -1,3 +1,3 @@
1
- __version__ = '2.0.3'
1
+ __version__ = '2.0.5'
2
2
 
3
3
  from .main import main, run
@@ -1,2 +1,2 @@
1
1
  from . import run
2
- run()
2
+ run()
@@ -59,15 +59,21 @@ def dict_get_single(d, item):
59
59
 
60
60
 
61
61
  def convert_1_to_2(c):
62
-
63
62
  out = {}
64
63
 
64
+ # If any of the top-level attribute values in `c` are None, they should be
65
+ # converted to empty dicts
66
+ top_level_nones = [k for k in c if c[k] is None]
67
+ c = c.copy()
68
+ for k in top_level_nones:
69
+ c[k] = dict()
70
+
65
71
  # Read timedelta
66
72
  dt_sec = None
67
73
  if 'numerics' in c:
68
74
  if 'dt' in c['numerics']:
69
75
  dt_value, dt_unit = c['numerics']['dt']
70
- dt_sec = np.timedelta64(dt_value, dt_unit).astype('timedelta64[s]').astype('int64')
76
+ dt_sec = int(np.timedelta64(dt_value, dt_unit).astype('timedelta64[s]').astype(int))
71
77
 
72
78
  out['version'] = 2
73
79
 
@@ -80,13 +86,17 @@ def convert_1_to_2(c):
80
86
 
81
87
  out['grid'] = {}
82
88
  out['grid']['file'] = dict_get(c, [
89
+ 'gridforce.first_file',
83
90
  'files.grid_file', 'gridforce.grid_file',
84
91
  'files.input_file', 'gridforce.input_file'])
85
92
  out['grid']['legacy_module'] = dict_get(c, 'gridforce.module', '') + '.Grid'
86
93
  out['grid']['start_time'] = np.datetime64(dict_get(c, 'time_control.start_time', '1970'), 's')
94
+ out['grid']['subgrid'] = dict_get(c, 'gridforce.subgrid', None)
87
95
 
88
96
  out['forcing'] = {}
89
97
  out['forcing']['file'] = dict_get(c, ['gridforce.input_file', 'files.input_file'])
98
+ out['forcing']['first_file'] = dict_get(c, 'gridforce.first_file', "")
99
+ out['forcing']['last_file'] = dict_get(c, 'gridforce.last_file', "")
90
100
  out['forcing']['legacy_module'] = dict_get(c, 'gridforce.module', '') + '.Forcing'
91
101
  out['forcing']['start_time'] = np.datetime64(dict_get(c, 'time_control.start_time', '1970'), 's')
92
102
  out['forcing']['stop_time'] = np.datetime64(dict_get(c, 'time_control.stop_time', '1970'), 's')
@@ -133,6 +143,8 @@ def convert_1_to_2(c):
133
143
  if 'ibm' in c:
134
144
  out['ibm']['module'] = 'ladim.ibms.LegacyIBM'
135
145
  out['ibm']['legacy_module'] = dict_get(c, ['ibm.ibm_module', 'ibm.module'])
146
+ if out['ibm']['legacy_module'] == 'ladim.ibms.ibm_salmon_lice':
147
+ out['ibm']['legacy_module'] = 'ladim_plugins.salmon_lice'
136
148
  out['ibm']['conf'] = {}
137
149
  out['ibm']['conf']['dt'] = dt_sec
138
150
  out['ibm']['conf']['output_instance'] = dict_get(c, 'output_variables.instance', [])
@@ -45,6 +45,8 @@ class RomsForcing(Forcing):
45
45
  legacy_conf = dict(
46
46
  gridforce=dict(
47
47
  input_file=file,
48
+ first_file=conf.get('first_file', ""),
49
+ last_file=conf.get('last_file', ""),
48
50
  ),
49
51
  ibm_forcing=conf.get('ibm_forcing', []),
50
52
  start_time=conf.get('start_time', None),
@@ -22,18 +22,28 @@ class Grid(Module):
22
22
 
23
23
 
24
24
  class RomsGrid(Grid):
25
- def __init__(self, model: Model, **conf):
25
+ def __init__(
26
+ self,
27
+ model: Model,
28
+ file: str,
29
+ start_time=None,
30
+ subgrid=None,
31
+ legacy_module='ladim.gridforce.ROMS.Grid',
32
+ **_,
33
+ ):
26
34
  super().__init__(model)
27
35
 
28
36
  legacy_conf = dict(
29
37
  gridforce=dict(
30
- input_file=conf['file'],
38
+ input_file=file,
31
39
  ),
32
- start_time=conf.get('start_time', None),
40
+ start_time=start_time,
33
41
  )
42
+ if subgrid is not None:
43
+ legacy_conf['gridforce']['subgrid'] = subgrid
34
44
 
35
45
  from .model import load_class
36
- LegacyGrid = load_class(conf.get('legacy_module', 'ladim.gridforce.ROMS.Grid'))
46
+ LegacyGrid = load_class(legacy_module)
37
47
 
38
48
  # Allow gridforce module in current directory
39
49
  import sys
@@ -15,7 +15,7 @@ import glob
15
15
  import logging
16
16
  import numpy as np
17
17
  from netCDF4 import Dataset, num2date
18
- from ladim.sample import sample2D
18
+ from ladim.sample import sample2D, bilin_inv
19
19
 
20
20
 
21
21
  logger = logging.getLogger(__name__)
@@ -107,11 +107,6 @@ class Grid:
107
107
  self.lon = ncid.variables["lon_rho"][self.J, self.I]
108
108
  self.lat = ncid.variables["lat_rho"][self.J, self.I]
109
109
 
110
- # self.z_r = sdepth(self.H, self.hc, self.Cs_r,
111
- # stagger='rho', Vtransform=self.Vtransform)
112
- # self.z_w = sdepth(self.H, self.hc, self.Cs_w,
113
- # stagger='w', Vtransform=self.Vtransform)
114
-
115
110
  # Land masks at u- and v-points
116
111
  M = self.M
117
112
  Mu = np.zeros((self.jmax, self.imax + 1), dtype=int)
@@ -137,6 +132,10 @@ class Grid:
137
132
  I = X.round().astype(int) - self.i0
138
133
  J = Y.round().astype(int) - self.j0
139
134
 
135
+ # Constrain to valid indices
136
+ I = np.minimum(np.maximum(I, 0), self.dx.shape[-1] - 2)
137
+ J = np.minimum(np.maximum(J, 0), self.dx.shape[-2] - 2)
138
+
140
139
  # Metric is conform for PolarStereographic
141
140
  A = self.dx[J, I]
142
141
  return A, A
@@ -145,30 +144,39 @@ class Grid:
145
144
  """Return the depth of grid cells"""
146
145
  I = X.round().astype(int) - self.i0
147
146
  J = Y.round().astype(int) - self.j0
147
+ I = np.minimum(np.maximum(I, 0), self.H.shape[1] - 1)
148
+ J = np.minimum(np.maximum(J, 0), self.H.shape[0] - 1)
148
149
  return self.H[J, I]
149
150
 
150
151
  def lonlat(self, X, Y, method="bilinear"):
151
152
  """Return the longitude and latitude from grid coordinates"""
152
153
  if method == "bilinear": # More accurate
153
154
  return self.xy2ll(X, Y)
154
- else: # containing grid cell, less accurate
155
- I = X.round().astype("int") - self.i0
156
- J = Y.round().astype("int") - self.j0
157
- return self.lon[J, I], self.lat[J, I]
158
-
155
+ # else: containing grid cell, less accurate
156
+ I = X.round().astype("int") - self.i0
157
+ J = Y.round().astype("int") - self.j0
158
+ I = np.minimum(np.maximum(I, 0), self.lon.shape[1] - 1)
159
+ J = np.minimum(np.maximum(J, 0), self.lon.shape[0] - 1)
160
+ return self.lon[J, I], self.lat[J, I]
161
+
162
+ def ingrid(self, X: np.ndarray, Y: np.ndarray) -> np.ndarray:
163
+ """Returns True for points inside the subgrid"""
159
164
  return (
160
- sample2D(self.lon, X - self.i0, Y - self.j0),
161
- sample2D(self.lat, X - self.i0, Y - self.j0),
165
+ (self.xmin + 0.5 < X)
166
+ & (X < self.xmax - 0.5)
167
+ & (self.ymin + 0.5 < Y)
168
+ & (Y < self.ymax - 0.5)
162
169
  )
163
170
 
164
- def ingrid(self, X, Y):
165
- """Returns True for points inside the subgrid"""
166
- return (self.xmin < X) & (X < self.xmax) & (self.ymin < Y) & (Y < self.ymax)
167
-
168
171
  def onland(self, X, Y):
169
172
  """Returns True for points on land"""
170
173
  I = X.round().astype(int) - self.i0
171
174
  J = Y.round().astype(int) - self.j0
175
+
176
+ # Constrain to valid indices
177
+ I = np.minimum(np.maximum(I, 0), self.M.shape[-1] - 1)
178
+ J = np.minimum(np.maximum(J, 0), self.M.shape[-2] - 1)
179
+
172
180
  return self.M[J, I] < 1
173
181
 
174
182
  # Error if point outside
@@ -176,6 +184,11 @@ class Grid:
176
184
  """Returns True for points at sea"""
177
185
  I = X.round().astype(int) - self.i0
178
186
  J = Y.round().astype(int) - self.j0
187
+
188
+ # Constrain to valid indices
189
+ I = np.minimum(np.maximum(I, 0), self.M.shape[-1] - 1)
190
+ J = np.minimum(np.maximum(J, 0), self.M.shape[-2] - 1)
191
+
179
192
  return self.M[J, I] > 0
180
193
 
181
194
  def xy2ll(self, X, Y):
@@ -205,7 +218,7 @@ class Forcing:
205
218
  logger.info("Initiating forcing")
206
219
 
207
220
  self._grid = grid # Get the grid object, make private?
208
-
221
+ # self.config = config["gridforce"]
209
222
  self.ibm_forcing = config["ibm_forcing"]
210
223
 
211
224
  # Test for glob, use MFDataset if needed
@@ -324,6 +337,7 @@ class Forcing:
324
337
  # --------------
325
338
  # prestep = last forcing step < 0
326
339
  #
340
+
327
341
  V = [step for step in steps if step < 0]
328
342
  if V: # Forcing available before start time
329
343
  prestep = max(V)
@@ -345,11 +359,12 @@ class Forcing:
345
359
 
346
360
  elif steps[0] == 0:
347
361
  # Simulation start at first forcing time
362
+ # Runge-Kutta needs dU and dV in this case as well
348
363
  self.U, self.V = self._read_velocity(0)
349
364
  self.Unew, self.Vnew = self._read_velocity(steps[1])
350
365
  self.dU = (self.Unew - self.U) / steps[1]
351
366
  self.dV = (self.Vnew - self.V) / steps[1]
352
- # Syncronize
367
+ # Synchronize with start time
353
368
  self.Unew = self.U
354
369
  self.Vnew = self.V
355
370
  # Extrapolate to time step = -1
@@ -372,7 +387,7 @@ class Forcing:
372
387
  def update(self, t):
373
388
  """Update the fields to time step t"""
374
389
 
375
- # Read from config
390
+ # Read from config?
376
391
  interpolate_velocity_in_time = True
377
392
  interpolate_ibm_forcing_in_time = False
378
393
 
@@ -413,7 +428,7 @@ class Forcing:
413
428
 
414
429
  # Handle file opening/closing
415
430
  # Always read velocity before other fields
416
- logger.debug("Reading velocity for time step = {}".format(n))
431
+ logger.info("Reading velocity for time step = {}".format(n))
417
432
  first = True
418
433
  if first: # Open file initiallt
419
434
  self._nc = Dataset(self._files[self.file_idx[n]])
@@ -430,6 +445,7 @@ class Forcing:
430
445
  # Read the velocity
431
446
  U = self._nc.variables["u"][frame, :, self._grid.Ju, self._grid.Iu]
432
447
  V = self._nc.variables["v"][frame, :, self._grid.Jv, self._grid.Iv]
448
+
433
449
  # Scale if needed
434
450
  # Assume offset = 0 for velocity
435
451
  if self.scaled["U"]:
@@ -446,7 +462,6 @@ class Forcing:
446
462
 
447
463
  def _read_field(self, name, n):
448
464
  """Read a 3D field"""
449
- # print("IBM-forcing:", name)
450
465
  frame = self.frame_idx[n]
451
466
  F = self._nc.variables[name][frame, :, self._grid.J, self._grid.I]
452
467
  if self.scaled[name]:
@@ -579,7 +594,7 @@ def sdepth(H, Hc, C, stagger="rho", Vtransform=1):
579
594
  """
580
595
  H = np.asarray(H)
581
596
  Hshape = H.shape # Save the shape of H
582
- H = H.ravel() # and make H 1D for easy shape manipulation
597
+ H = H.ravel() # and make H 1D for easy shape maniplation
583
598
  C = np.asarray(C)
584
599
  N = len(C)
585
600
  outshape = (N,) + Hshape # Shape of output
@@ -595,13 +610,13 @@ def sdepth(H, Hc, C, stagger="rho", Vtransform=1):
595
610
  B = np.outer(C, H)
596
611
  return (A + B).reshape(outshape)
597
612
 
598
- elif Vtransform == 2: # New transform by Shchepetkin
613
+ if Vtransform == 2: # New transform by Shchepetkin
599
614
  N = Hc * S[:, None] + np.outer(C, H)
600
615
  D = 1.0 + Hc / H
601
616
  return (N / D).reshape(outshape)
602
617
 
603
- else:
604
- raise ValueError("Unknown Vtransform")
618
+ # else:
619
+ raise ValueError("Unknown Vtransform")
605
620
 
606
621
 
607
622
  # ------------------------
@@ -660,13 +675,15 @@ def sample3D(F, X, Y, K, A, method="bilinear"):
660
675
 
661
676
  """
662
677
 
663
- # print('sample3D: method =', method)
664
-
665
- # sjekk om 1-A eller A
666
678
  if method == "bilinear":
667
679
  # Find rho-point as lower left corner
668
680
  I = X.astype("int")
669
681
  J = Y.astype("int")
682
+
683
+ # Constrain to valid indices
684
+ I = np.minimum(np.maximum(I, 0), F.shape[-1] - 2)
685
+ J = np.minimum(np.maximum(J, 0), F.shape[-2] - 2)
686
+
670
687
  P = X - I
671
688
  Q = Y - J
672
689
  W000 = (1 - P) * (1 - Q) * A
@@ -689,10 +706,15 @@ def sample3D(F, X, Y, K, A, method="bilinear"):
689
706
  + W111 * F[K - 1, J + 1, I + 1]
690
707
  )
691
708
 
692
- else: # method == 'nearest'
693
- I = X.round().astype("int")
694
- J = Y.round().astype("int")
695
- return F[K, J, I]
709
+ # else: method == 'nearest'
710
+ I = X.round().astype("int")
711
+ J = Y.round().astype("int")
712
+
713
+ # Constrain to valid indices
714
+ I = np.minimum(np.maximum(I, 0), F.shape[-1] - 1)
715
+ J = np.minimum(np.maximum(J, 0), F.shape[-2] - 1)
716
+
717
+ return F[K, J, I]
696
718
 
697
719
 
698
720
  def sample3DUV(U, V, X, Y, K, A, method="bilinear"):
@@ -242,5 +242,4 @@ def create_netcdf_file(fname: str, formats: dict[str, OutputFormat], diskless=Fa
242
242
  dset.variables[varname].set_auto_mask(False)
243
243
  dset.variables[varname].setncatts(item.attributes)
244
244
 
245
-
246
245
  return dset
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: ladim
3
- Version: 2.0.3
3
+ Version: 2.0.5
4
4
  Summary: Lagrangian Advection and Diffusion Model
5
5
  Home-page: https://github.com/pnsaevik/ladim
6
6
  Author: Bjørn Ådlandsvik
@@ -37,6 +37,8 @@ class Test_convert_1_to_2:
37
37
  'forcing': {
38
38
  'dt': 60,
39
39
  'file': '../forcing*.nc',
40
+ 'first_file': '',
41
+ 'last_file': '',
40
42
  'ibm_forcing': [],
41
43
  'legacy_module': 'ladim.gridforce.ROMS.Forcing',
42
44
  'start_time': np.datetime64('2015-09-07T01:00:00'),
@@ -44,7 +46,9 @@ class Test_convert_1_to_2:
44
46
  'grid': {
45
47
  'file': '../forcing*.nc',
46
48
  'legacy_module': 'ladim.gridforce.ROMS.Grid',
47
- 'start_time': np.datetime64('2015-09-07T01:00:00')},
49
+ 'start_time': np.datetime64('2015-09-07T01:00:00'),
50
+ 'subgrid': None,
51
+ },
48
52
  'ibm': {},
49
53
  'output': {
50
54
  'file': 'out.nc',
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes