ladim 2.0.2__py3-none-any.whl → 2.0.4__py3-none-any.whl
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.
- ladim/__init__.py +1 -1
- ladim/config.py +108 -80
- ladim/gridforce/ROMS.py +14 -0
- ladim/gridforce/zROMS.py +71 -38
- ladim/main.py +3 -1
- ladim/model.py +23 -1
- {ladim-2.0.2.dist-info → ladim-2.0.4.dist-info}/METADATA +1 -1
- {ladim-2.0.2.dist-info → ladim-2.0.4.dist-info}/RECORD +12 -12
- {ladim-2.0.2.dist-info → ladim-2.0.4.dist-info}/LICENSE +0 -0
- {ladim-2.0.2.dist-info → ladim-2.0.4.dist-info}/WHEEL +0 -0
- {ladim-2.0.2.dist-info → ladim-2.0.4.dist-info}/entry_points.txt +0 -0
- {ladim-2.0.2.dist-info → ladim-2.0.4.dist-info}/top_level.txt +0 -0
ladim/__init__.py
CHANGED
ladim/config.py
CHANGED
|
@@ -28,98 +28,126 @@ def configure(module_conf):
|
|
|
28
28
|
|
|
29
29
|
def _versioned_configure(config_dict):
|
|
30
30
|
if config_dict['version'] == 1:
|
|
31
|
-
config_dict =
|
|
31
|
+
config_dict = convert_1_to_2(config_dict)
|
|
32
32
|
|
|
33
33
|
return config_dict
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
def
|
|
36
|
+
def dict_get(d, items, default=None):
|
|
37
|
+
if isinstance(items, str):
|
|
38
|
+
items = [items]
|
|
39
|
+
|
|
40
|
+
for item in items:
|
|
41
|
+
try:
|
|
42
|
+
return dict_get_single(d, item)
|
|
43
|
+
except KeyError:
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
return default
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def dict_get_single(d, item):
|
|
50
|
+
tokens = str(item).split(sep='.')
|
|
51
|
+
sub_dict = d
|
|
52
|
+
for t in tokens:
|
|
53
|
+
if t in sub_dict:
|
|
54
|
+
sub_dict = sub_dict[t]
|
|
55
|
+
else:
|
|
56
|
+
raise KeyError
|
|
57
|
+
|
|
58
|
+
return sub_dict
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def convert_1_to_2(c):
|
|
62
|
+
out = {}
|
|
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
|
+
|
|
37
71
|
# Read timedelta
|
|
38
|
-
|
|
39
|
-
|
|
72
|
+
dt_sec = None
|
|
73
|
+
if 'numerics' in c:
|
|
74
|
+
if 'dt' in c['numerics']:
|
|
75
|
+
dt_value, dt_unit = c['numerics']['dt']
|
|
76
|
+
dt_sec = np.timedelta64(dt_value, dt_unit).astype('timedelta64[s]').astype('int64')
|
|
77
|
+
|
|
78
|
+
out['version'] = 2
|
|
79
|
+
|
|
80
|
+
out['solver'] = {}
|
|
81
|
+
out['solver']['start'] = dict_get(c, 'time_control.start_time')
|
|
82
|
+
out['solver']['stop'] = dict_get(c, 'time_control.stop_time')
|
|
83
|
+
out['solver']['step'] = dt_sec
|
|
84
|
+
out['solver']['seed'] = dict_get(c, 'numerics.seed')
|
|
85
|
+
out['solver']['order'] = ['release', 'forcing', 'output', 'tracker', 'ibm', 'state']
|
|
86
|
+
|
|
87
|
+
out['grid'] = {}
|
|
88
|
+
out['grid']['file'] = dict_get(c, [
|
|
89
|
+
'files.grid_file', 'gridforce.grid_file',
|
|
90
|
+
'files.input_file', 'gridforce.input_file'])
|
|
91
|
+
out['grid']['legacy_module'] = dict_get(c, 'gridforce.module', '') + '.Grid'
|
|
92
|
+
out['grid']['start_time'] = np.datetime64(dict_get(c, 'time_control.start_time', '1970'), 's')
|
|
93
|
+
|
|
94
|
+
out['forcing'] = {}
|
|
95
|
+
out['forcing']['file'] = dict_get(c, ['gridforce.input_file', 'files.input_file'])
|
|
96
|
+
out['forcing']['legacy_module'] = dict_get(c, 'gridforce.module', '') + '.Forcing'
|
|
97
|
+
out['forcing']['start_time'] = np.datetime64(dict_get(c, 'time_control.start_time', '1970'), 's')
|
|
98
|
+
out['forcing']['stop_time'] = np.datetime64(dict_get(c, 'time_control.stop_time', '1970'), 's')
|
|
99
|
+
out['forcing']['dt'] = dt_sec
|
|
100
|
+
out['forcing']['ibm_forcing'] = dict_get(c, 'gridforce.ibm_forcing', [])
|
|
40
101
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
102
|
+
out['output'] = {}
|
|
103
|
+
out['output']['file'] = dict_get(c, 'files.output_file')
|
|
104
|
+
out['output']['frequency'] = dict_get(c, 'output_variables.outper')
|
|
105
|
+
out['output']['variables'] = {}
|
|
106
|
+
|
|
107
|
+
# Convert output variable format spec
|
|
108
|
+
outvar_names = dict_get(c, 'output_variables.particle', []).copy()
|
|
109
|
+
outvar_names += dict_get(c, 'output_variables.instance', [])
|
|
44
110
|
for v in outvar_names:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
111
|
+
out['output']['variables'][v] = c['output_variables'][v].copy()
|
|
112
|
+
if v == 'release_time' and 'units' in c['output_variables'][v]:
|
|
113
|
+
out['output']['variables'][v]['units'] = 'seconds since 1970-01-01'
|
|
114
|
+
for v in dict_get(c, 'output_variables.particle', []):
|
|
115
|
+
out['output']['variables'][v]['kind'] = 'initial'
|
|
116
|
+
|
|
117
|
+
out['tracker'] = {}
|
|
118
|
+
out['tracker']['method'] = dict_get(c, 'numerics.advection')
|
|
119
|
+
out['tracker']['diffusion'] = dict_get(c, 'numerics.diffusion')
|
|
50
120
|
|
|
51
121
|
# Read release config
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
del relconf['frequency']
|
|
64
|
-
ibmvars = c.get('state', dict()).get('ibm_variables', [])
|
|
65
|
-
ibmvars += c.get('ibm', dict()).get('variables', [])
|
|
66
|
-
relconf['defaults'] = {
|
|
122
|
+
out['release'] = {}
|
|
123
|
+
out['release']['file'] = dict_get(c, 'files.particle_release_file')
|
|
124
|
+
out['release']['colnames'] = dict_get(c, 'particle_release.variables', [])
|
|
125
|
+
if dict_get(c, 'particle_release.release_type', '') == 'continuous':
|
|
126
|
+
out['release']['frequency'] = dict_get(c, 'particle_release.release_frequency', [0, 's'])
|
|
127
|
+
out['release']['formats'] = {
|
|
128
|
+
c.get('particle_release', {})[v]: v
|
|
129
|
+
for v in dict_get(c, 'particle_release.variables', [])
|
|
130
|
+
if v in c.get('particle_release', {}).keys()
|
|
131
|
+
}
|
|
132
|
+
out['release']['defaults'] = {
|
|
67
133
|
k: np.float64(0)
|
|
68
|
-
for k in
|
|
69
|
-
if k not in
|
|
134
|
+
for k in dict_get(c, 'state.ibm_variables', []) + dict_get(c, 'ibm.variables', [])
|
|
135
|
+
if k not in out['release']['colnames']
|
|
70
136
|
}
|
|
71
137
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
output_instance=c.get('output_variables', {}).get('instance', []),
|
|
83
|
-
nc_attributes={k: v for k, v in outvars.items()}
|
|
84
|
-
)
|
|
85
|
-
ibmconf['conf']['ibm'] = {
|
|
138
|
+
out['ibm'] = {}
|
|
139
|
+
if 'ibm' in c:
|
|
140
|
+
out['ibm']['module'] = 'ladim.ibms.LegacyIBM'
|
|
141
|
+
out['ibm']['legacy_module'] = dict_get(c, ['ibm.ibm_module', 'ibm.module'])
|
|
142
|
+
if out['ibm']['legacy_module'] == 'ladim.ibms.ibm_salmon_lice':
|
|
143
|
+
out['ibm']['legacy_module'] = 'ladim_plugins.salmon_lice'
|
|
144
|
+
out['ibm']['conf'] = {}
|
|
145
|
+
out['ibm']['conf']['dt'] = dt_sec
|
|
146
|
+
out['ibm']['conf']['output_instance'] = dict_get(c, 'output_variables.instance', [])
|
|
147
|
+
out['ibm']['conf']['nc_attributes'] = {
|
|
86
148
|
k: v
|
|
87
|
-
for k, v in
|
|
88
|
-
if k != 'ibm_module'
|
|
149
|
+
for k, v in out['output']['variables'].items()
|
|
89
150
|
}
|
|
151
|
+
out['ibm']['conf']['ibm'] = {k: v for k, v in c['ibm'].items() if k != 'ibm_module'}
|
|
90
152
|
|
|
91
|
-
|
|
92
|
-
version=2,
|
|
93
|
-
solver=dict(
|
|
94
|
-
start=c['time_control']['start_time'],
|
|
95
|
-
stop=c['time_control']['stop_time'],
|
|
96
|
-
step=dt_sec,
|
|
97
|
-
seed=c['numerics'].get('seed', None),
|
|
98
|
-
order=['release', 'forcing', 'output', 'tracker', 'ibm', 'state'],
|
|
99
|
-
),
|
|
100
|
-
grid=dict(
|
|
101
|
-
file=c['gridforce']['input_file'],
|
|
102
|
-
legacy_module=c['gridforce']['module'] + '.Grid',
|
|
103
|
-
start_time=np.datetime64(c['time_control']['start_time'], 's'),
|
|
104
|
-
),
|
|
105
|
-
forcing=dict(
|
|
106
|
-
file=c['gridforce']['input_file'],
|
|
107
|
-
legacy_module=c['gridforce']['module'] + '.Forcing',
|
|
108
|
-
start_time=np.datetime64(c['time_control']['start_time'], 's'),
|
|
109
|
-
stop_time=np.datetime64(c['time_control']['stop_time'], 's'),
|
|
110
|
-
dt=dt_sec,
|
|
111
|
-
ibm_forcing=c['gridforce'].get('ibm_forcing', []),
|
|
112
|
-
),
|
|
113
|
-
release=relconf,
|
|
114
|
-
output=dict(
|
|
115
|
-
file=c['files']['output_file'],
|
|
116
|
-
frequency=c['output_variables']['outper'],
|
|
117
|
-
variables=outvars,
|
|
118
|
-
),
|
|
119
|
-
tracker=dict(
|
|
120
|
-
method=c['numerics']['advection'],
|
|
121
|
-
diffusion=c['numerics']['diffusion'],
|
|
122
|
-
),
|
|
123
|
-
ibm=ibmconf,
|
|
124
|
-
)
|
|
125
|
-
return config_dict
|
|
153
|
+
return out
|
ladim/gridforce/ROMS.py
CHANGED
|
@@ -182,6 +182,8 @@ class Grid:
|
|
|
182
182
|
"""Return the depth of grid cells"""
|
|
183
183
|
I = X.round().astype(int) - self.i0
|
|
184
184
|
J = Y.round().astype(int) - self.j0
|
|
185
|
+
I = np.minimum(np.maximum(I, 0), self.H.shape[1] - 1)
|
|
186
|
+
J = np.minimum(np.maximum(J, 0), self.H.shape[0] - 1)
|
|
185
187
|
return self.H[J, I]
|
|
186
188
|
|
|
187
189
|
def lonlat(self, X, Y, method="bilinear"):
|
|
@@ -191,6 +193,8 @@ class Grid:
|
|
|
191
193
|
# else: containing grid cell, less accurate
|
|
192
194
|
I = X.round().astype("int") - self.i0
|
|
193
195
|
J = Y.round().astype("int") - self.j0
|
|
196
|
+
I = np.minimum(np.maximum(I, 0), self.lon.shape[1] - 1)
|
|
197
|
+
J = np.minimum(np.maximum(J, 0), self.lon.shape[0] - 1)
|
|
194
198
|
return self.lon[J, I], self.lat[J, I]
|
|
195
199
|
|
|
196
200
|
def ingrid(self, X: np.ndarray, Y: np.ndarray) -> np.ndarray:
|
|
@@ -206,6 +210,11 @@ class Grid:
|
|
|
206
210
|
"""Returns True for points on land"""
|
|
207
211
|
I = X.round().astype(int) - self.i0
|
|
208
212
|
J = Y.round().astype(int) - self.j0
|
|
213
|
+
|
|
214
|
+
# Constrain to valid indices
|
|
215
|
+
I = np.minimum(np.maximum(I, 0), self.M.shape[-1] - 1)
|
|
216
|
+
J = np.minimum(np.maximum(J, 0), self.M.shape[-2] - 1)
|
|
217
|
+
|
|
209
218
|
return self.M[J, I] < 1
|
|
210
219
|
|
|
211
220
|
# Error if point outside
|
|
@@ -802,6 +811,11 @@ def sample3D(F, X, Y, K, A, method="bilinear"):
|
|
|
802
811
|
# else: method == 'nearest'
|
|
803
812
|
I = X.round().astype("int")
|
|
804
813
|
J = Y.round().astype("int")
|
|
814
|
+
|
|
815
|
+
# Constrain to valid indices
|
|
816
|
+
I = np.minimum(np.maximum(I, 0), F.shape[-1] - 1)
|
|
817
|
+
J = np.minimum(np.maximum(J, 0), F.shape[-2] - 1)
|
|
818
|
+
|
|
805
819
|
return F[K, J, I]
|
|
806
820
|
|
|
807
821
|
|
ladim/gridforce/zROMS.py
CHANGED
|
@@ -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__)
|
|
@@ -37,12 +37,23 @@ class Grid:
|
|
|
37
37
|
def __init__(self, config):
|
|
38
38
|
|
|
39
39
|
logger.info("Initializing zROMS grid object")
|
|
40
|
+
|
|
41
|
+
# Grid file
|
|
42
|
+
if "grid_file" in config["gridforce"]:
|
|
43
|
+
grid_file = config["gridforce"]["grid_file"]
|
|
44
|
+
elif "input_file" in config["gridforce"]:
|
|
45
|
+
files = glob.glob(config["gridforce"]["input_file"])
|
|
46
|
+
files.sort()
|
|
47
|
+
grid_file = files[0]
|
|
48
|
+
else:
|
|
49
|
+
logger.error("No grid file specified")
|
|
50
|
+
raise SystemExit(1)
|
|
51
|
+
|
|
40
52
|
try:
|
|
41
|
-
ncid = Dataset(
|
|
53
|
+
ncid = Dataset(grid_file)
|
|
54
|
+
ncid.set_auto_mask(False)
|
|
42
55
|
except OSError:
|
|
43
|
-
logger.error(
|
|
44
|
-
"Grid file {} not found".format(config["gridforce"]["grid_file"])
|
|
45
|
-
)
|
|
56
|
+
logger.error("Could not open grid file " + grid_file)
|
|
46
57
|
raise SystemExit(1)
|
|
47
58
|
|
|
48
59
|
# Subgrid, only considers internal grid cells
|
|
@@ -96,11 +107,6 @@ class Grid:
|
|
|
96
107
|
self.lon = ncid.variables["lon_rho"][self.J, self.I]
|
|
97
108
|
self.lat = ncid.variables["lat_rho"][self.J, self.I]
|
|
98
109
|
|
|
99
|
-
# self.z_r = sdepth(self.H, self.hc, self.Cs_r,
|
|
100
|
-
# stagger='rho', Vtransform=self.Vtransform)
|
|
101
|
-
# self.z_w = sdepth(self.H, self.hc, self.Cs_w,
|
|
102
|
-
# stagger='w', Vtransform=self.Vtransform)
|
|
103
|
-
|
|
104
110
|
# Land masks at u- and v-points
|
|
105
111
|
M = self.M
|
|
106
112
|
Mu = np.zeros((self.jmax, self.imax + 1), dtype=int)
|
|
@@ -126,6 +132,10 @@ class Grid:
|
|
|
126
132
|
I = X.round().astype(int) - self.i0
|
|
127
133
|
J = Y.round().astype(int) - self.j0
|
|
128
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
|
+
|
|
129
139
|
# Metric is conform for PolarStereographic
|
|
130
140
|
A = self.dx[J, I]
|
|
131
141
|
return A, A
|
|
@@ -134,30 +144,39 @@ class Grid:
|
|
|
134
144
|
"""Return the depth of grid cells"""
|
|
135
145
|
I = X.round().astype(int) - self.i0
|
|
136
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)
|
|
137
149
|
return self.H[J, I]
|
|
138
150
|
|
|
139
151
|
def lonlat(self, X, Y, method="bilinear"):
|
|
140
152
|
"""Return the longitude and latitude from grid coordinates"""
|
|
141
153
|
if method == "bilinear": # More accurate
|
|
142
154
|
return self.xy2ll(X, Y)
|
|
143
|
-
else:
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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"""
|
|
148
164
|
return (
|
|
149
|
-
|
|
150
|
-
|
|
165
|
+
(self.xmin + 0.5 < X)
|
|
166
|
+
& (X < self.xmax - 0.5)
|
|
167
|
+
& (self.ymin + 0.5 < Y)
|
|
168
|
+
& (Y < self.ymax - 0.5)
|
|
151
169
|
)
|
|
152
170
|
|
|
153
|
-
def ingrid(self, X, Y):
|
|
154
|
-
"""Returns True for points inside the subgrid"""
|
|
155
|
-
return (self.xmin < X) & (X < self.xmax) & (self.ymin < Y) & (Y < self.ymax)
|
|
156
|
-
|
|
157
171
|
def onland(self, X, Y):
|
|
158
172
|
"""Returns True for points on land"""
|
|
159
173
|
I = X.round().astype(int) - self.i0
|
|
160
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
|
+
|
|
161
180
|
return self.M[J, I] < 1
|
|
162
181
|
|
|
163
182
|
# Error if point outside
|
|
@@ -165,6 +184,11 @@ class Grid:
|
|
|
165
184
|
"""Returns True for points at sea"""
|
|
166
185
|
I = X.round().astype(int) - self.i0
|
|
167
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
|
+
|
|
168
192
|
return self.M[J, I] > 0
|
|
169
193
|
|
|
170
194
|
def xy2ll(self, X, Y):
|
|
@@ -194,7 +218,7 @@ class Forcing:
|
|
|
194
218
|
logger.info("Initiating forcing")
|
|
195
219
|
|
|
196
220
|
self._grid = grid # Get the grid object, make private?
|
|
197
|
-
|
|
221
|
+
# self.config = config["gridforce"]
|
|
198
222
|
self.ibm_forcing = config["ibm_forcing"]
|
|
199
223
|
|
|
200
224
|
# Test for glob, use MFDataset if needed
|
|
@@ -247,7 +271,7 @@ class Forcing:
|
|
|
247
271
|
num_frames = [] # Available time frames in each file
|
|
248
272
|
# change_times = [] # Times for change of file
|
|
249
273
|
for fname in files:
|
|
250
|
-
|
|
274
|
+
logging.info(f'Load {fname}')
|
|
251
275
|
with Dataset(fname) as nc:
|
|
252
276
|
# new_times = nc.variables['ocean_time'][:]
|
|
253
277
|
new_times = nc.variables["time"][:]
|
|
@@ -313,6 +337,7 @@ class Forcing:
|
|
|
313
337
|
# --------------
|
|
314
338
|
# prestep = last forcing step < 0
|
|
315
339
|
#
|
|
340
|
+
|
|
316
341
|
V = [step for step in steps if step < 0]
|
|
317
342
|
if V: # Forcing available before start time
|
|
318
343
|
prestep = max(V)
|
|
@@ -334,11 +359,12 @@ class Forcing:
|
|
|
334
359
|
|
|
335
360
|
elif steps[0] == 0:
|
|
336
361
|
# Simulation start at first forcing time
|
|
362
|
+
# Runge-Kutta needs dU and dV in this case as well
|
|
337
363
|
self.U, self.V = self._read_velocity(0)
|
|
338
364
|
self.Unew, self.Vnew = self._read_velocity(steps[1])
|
|
339
365
|
self.dU = (self.Unew - self.U) / steps[1]
|
|
340
366
|
self.dV = (self.Vnew - self.V) / steps[1]
|
|
341
|
-
#
|
|
367
|
+
# Synchronize with start time
|
|
342
368
|
self.Unew = self.U
|
|
343
369
|
self.Vnew = self.V
|
|
344
370
|
# Extrapolate to time step = -1
|
|
@@ -361,7 +387,7 @@ class Forcing:
|
|
|
361
387
|
def update(self, t):
|
|
362
388
|
"""Update the fields to time step t"""
|
|
363
389
|
|
|
364
|
-
# Read from config
|
|
390
|
+
# Read from config?
|
|
365
391
|
interpolate_velocity_in_time = True
|
|
366
392
|
interpolate_ibm_forcing_in_time = False
|
|
367
393
|
|
|
@@ -402,7 +428,7 @@ class Forcing:
|
|
|
402
428
|
|
|
403
429
|
# Handle file opening/closing
|
|
404
430
|
# Always read velocity before other fields
|
|
405
|
-
logger.
|
|
431
|
+
logger.info("Reading velocity for time step = {}".format(n))
|
|
406
432
|
first = True
|
|
407
433
|
if first: # Open file initiallt
|
|
408
434
|
self._nc = Dataset(self._files[self.file_idx[n]])
|
|
@@ -419,6 +445,7 @@ class Forcing:
|
|
|
419
445
|
# Read the velocity
|
|
420
446
|
U = self._nc.variables["u"][frame, :, self._grid.Ju, self._grid.Iu]
|
|
421
447
|
V = self._nc.variables["v"][frame, :, self._grid.Jv, self._grid.Iv]
|
|
448
|
+
|
|
422
449
|
# Scale if needed
|
|
423
450
|
# Assume offset = 0 for velocity
|
|
424
451
|
if self.scaled["U"]:
|
|
@@ -435,7 +462,6 @@ class Forcing:
|
|
|
435
462
|
|
|
436
463
|
def _read_field(self, name, n):
|
|
437
464
|
"""Read a 3D field"""
|
|
438
|
-
# print("IBM-forcing:", name)
|
|
439
465
|
frame = self.frame_idx[n]
|
|
440
466
|
F = self._nc.variables[name][frame, :, self._grid.J, self._grid.I]
|
|
441
467
|
if self.scaled[name]:
|
|
@@ -568,7 +594,7 @@ def sdepth(H, Hc, C, stagger="rho", Vtransform=1):
|
|
|
568
594
|
"""
|
|
569
595
|
H = np.asarray(H)
|
|
570
596
|
Hshape = H.shape # Save the shape of H
|
|
571
|
-
H = H.ravel() # and make H 1D for easy shape
|
|
597
|
+
H = H.ravel() # and make H 1D for easy shape maniplation
|
|
572
598
|
C = np.asarray(C)
|
|
573
599
|
N = len(C)
|
|
574
600
|
outshape = (N,) + Hshape # Shape of output
|
|
@@ -584,13 +610,13 @@ def sdepth(H, Hc, C, stagger="rho", Vtransform=1):
|
|
|
584
610
|
B = np.outer(C, H)
|
|
585
611
|
return (A + B).reshape(outshape)
|
|
586
612
|
|
|
587
|
-
|
|
613
|
+
if Vtransform == 2: # New transform by Shchepetkin
|
|
588
614
|
N = Hc * S[:, None] + np.outer(C, H)
|
|
589
615
|
D = 1.0 + Hc / H
|
|
590
616
|
return (N / D).reshape(outshape)
|
|
591
617
|
|
|
592
|
-
else:
|
|
593
|
-
|
|
618
|
+
# else:
|
|
619
|
+
raise ValueError("Unknown Vtransform")
|
|
594
620
|
|
|
595
621
|
|
|
596
622
|
# ------------------------
|
|
@@ -649,13 +675,15 @@ def sample3D(F, X, Y, K, A, method="bilinear"):
|
|
|
649
675
|
|
|
650
676
|
"""
|
|
651
677
|
|
|
652
|
-
# print('sample3D: method =', method)
|
|
653
|
-
|
|
654
|
-
# sjekk om 1-A eller A
|
|
655
678
|
if method == "bilinear":
|
|
656
679
|
# Find rho-point as lower left corner
|
|
657
680
|
I = X.astype("int")
|
|
658
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
|
+
|
|
659
687
|
P = X - I
|
|
660
688
|
Q = Y - J
|
|
661
689
|
W000 = (1 - P) * (1 - Q) * A
|
|
@@ -678,10 +706,15 @@ def sample3D(F, X, Y, K, A, method="bilinear"):
|
|
|
678
706
|
+ W111 * F[K - 1, J + 1, I + 1]
|
|
679
707
|
)
|
|
680
708
|
|
|
681
|
-
else:
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
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]
|
|
685
718
|
|
|
686
719
|
|
|
687
720
|
def sample3DUV(U, V, X, Y, K, A, method="bilinear"):
|
ladim/main.py
CHANGED
|
@@ -47,7 +47,9 @@ def run():
|
|
|
47
47
|
|
|
48
48
|
logging.basicConfig(
|
|
49
49
|
level=logging.INFO,
|
|
50
|
-
format='%(levelname)s:%(module)s - %(message)s'
|
|
50
|
+
format='%(asctime)s %(levelname)s:%(module)s - %(message)s',
|
|
51
|
+
datefmt='%Y-%m-%d %H:%M:%S',
|
|
52
|
+
)
|
|
51
53
|
|
|
52
54
|
# ====================
|
|
53
55
|
# Parse command line
|
ladim/model.py
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import importlib
|
|
2
|
+
import importlib.util
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
2
5
|
|
|
3
6
|
from typing import TYPE_CHECKING
|
|
4
7
|
if TYPE_CHECKING:
|
|
@@ -101,7 +104,26 @@ class Model:
|
|
|
101
104
|
|
|
102
105
|
def load_class(name):
|
|
103
106
|
pkg, cls = name.rsplit(sep='.', maxsplit=1)
|
|
104
|
-
|
|
107
|
+
|
|
108
|
+
# Check if "pkg" is an existing file
|
|
109
|
+
spec = None
|
|
110
|
+
module_name = None
|
|
111
|
+
file_name = pkg + '.py'
|
|
112
|
+
if Path(file_name).exists():
|
|
113
|
+
# This can return None if there were import errors
|
|
114
|
+
module_name = pkg
|
|
115
|
+
spec = importlib.util.spec_from_file_location(module_name, file_name)
|
|
116
|
+
|
|
117
|
+
# If pkg can not be interpreted as a file, use regular import
|
|
118
|
+
if spec is None:
|
|
119
|
+
return getattr(importlib.import_module(pkg), cls)
|
|
120
|
+
|
|
121
|
+
# File import
|
|
122
|
+
else:
|
|
123
|
+
module = importlib.util.module_from_spec(spec)
|
|
124
|
+
sys.modules[module_name] = module
|
|
125
|
+
spec.loader.exec_module(module)
|
|
126
|
+
return getattr(module, cls)
|
|
105
127
|
|
|
106
128
|
|
|
107
129
|
class Module:
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
ladim/__init__.py,sha256=
|
|
1
|
+
ladim/__init__.py,sha256=1pOaNTt2-oJQ33nsnZyDsOulYErImxLErxjlfOb8Oyg,51
|
|
2
2
|
ladim/__main__.py,sha256=8f07EMfxQllDZSgpak5ECyYHnfQFy8LaHl2xdC-aO9c,23
|
|
3
|
-
ladim/config.py,sha256=
|
|
3
|
+
ladim/config.py,sha256=tnTyKDeaDG1M9T5fiUGXG8Ezc_n7Xwm8l6--UpBJhaE,5449
|
|
4
4
|
ladim/forcing.py,sha256=f4PpSwyilSScXeNyorTWLMgVTiat9htSLkCwAkRlJVM,3048
|
|
5
5
|
ladim/grid.py,sha256=m6bQrGJ3cux7rqC8pbRXD86cOI-VQKF-XjP9m1jCIcY,2221
|
|
6
|
-
ladim/main.py,sha256=
|
|
7
|
-
ladim/model.py,sha256=
|
|
6
|
+
ladim/main.py,sha256=6_blu3PYnDXaYdPxfZoukWsjN0o9vh7O8_-W2-aguAI,2894
|
|
7
|
+
ladim/model.py,sha256=iXClvieChhipCSZ-dDrmnjqwS4cuM53VpJv7oaJyQ88,3794
|
|
8
8
|
ladim/output.py,sha256=Rz7iujvS7Z3LoABiJduQqyb3zPswNqhhFsywr3MLsBY,8373
|
|
9
9
|
ladim/release.py,sha256=1j__9Gj0BD0CqVCM2KLZhio1Ia-hz1gbUIhTsa0J3Rg,8451
|
|
10
10
|
ladim/sample.py,sha256=n8wRGd_VsW_qyQe1ZoTpmfZcdcwB929vsM8PoKG6JTs,8292
|
|
@@ -12,10 +12,10 @@ ladim/solver.py,sha256=sZvYgOxzJ-EItI-IB2y8_z8Tf-SJAQSrmydlhDRa7ZQ,755
|
|
|
12
12
|
ladim/state.py,sha256=4XNIIx5sGjlqkZ6bg-dGbqzp8ujFNkHHFL2D9qCQA2w,4119
|
|
13
13
|
ladim/tracker.py,sha256=VVX6T5CqiU6nGSCgLlSCC8w0UYhW273OGFE7ApPjdyI,5091
|
|
14
14
|
ladim/utilities.py,sha256=r7-zShqJhh0cBctDUmtfw-GBOk1eTTYR4S72b0ouiSQ,994
|
|
15
|
-
ladim/gridforce/ROMS.py,sha256=
|
|
15
|
+
ladim/gridforce/ROMS.py,sha256=DF5CSR2iJsPWAmTOrqYavARVtqokQbtAWLSCDWb_9f0,27525
|
|
16
16
|
ladim/gridforce/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
17
|
ladim/gridforce/analytical.py,sha256=qI-1LJdjmnwwanzOdrsDZqwGgo73bT75CB7pMaxbHKM,1094
|
|
18
|
-
ladim/gridforce/zROMS.py,sha256=
|
|
18
|
+
ladim/gridforce/zROMS.py,sha256=4bnrmcXiWpCAUch9uqd_0XmyKRh-Ll6sFvIHiTbTOOg,23996
|
|
19
19
|
ladim/ibms/__init__.py,sha256=GOG75jZDmNEiLr8brxrKqIlqVj-pNR7pnPP8FUKE6hU,565
|
|
20
20
|
ladim/ibms/light.py,sha256=POltHmKkX8-q3t9wXyfcseCKEq9Bq-kX1WEJYsr1lNQ,2737
|
|
21
21
|
ladim/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -24,9 +24,9 @@ postladim/cellcount.py,sha256=nCFu9iJmprubn4YmPB4W0VO02GfEb90Iif7D49w1Kss,2054
|
|
|
24
24
|
postladim/kde_plot.py,sha256=GvMWzT6VxIeXKh1cnqaGzR-4jGG_WIHGMLPpRMXIpo4,1628
|
|
25
25
|
postladim/particlefile.py,sha256=0aif9wYUJ-VrpQKeCef8wB5VCiBB-gWY6sxNCUYviTA,4889
|
|
26
26
|
postladim/variable.py,sha256=-2aihoppYMMmpSpCqaF31XvpinTMaH3Y01-USDIkbBc,6587
|
|
27
|
-
ladim-2.0.
|
|
28
|
-
ladim-2.0.
|
|
29
|
-
ladim-2.0.
|
|
30
|
-
ladim-2.0.
|
|
31
|
-
ladim-2.0.
|
|
32
|
-
ladim-2.0.
|
|
27
|
+
ladim-2.0.4.dist-info/LICENSE,sha256=BgtXyjNr6Ly9nQ7ZLXKpV3r5kWRLnh5MiN0dxp0Bvfc,1085
|
|
28
|
+
ladim-2.0.4.dist-info/METADATA,sha256=9zVlHY1N0c5JKYGu9_ayPTmBVcPnzHaj9i8kJgLRgOU,1841
|
|
29
|
+
ladim-2.0.4.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
30
|
+
ladim-2.0.4.dist-info/entry_points.txt,sha256=JDlNJo87GJaOkH0-BpAzTPLCrZcuPSdSlHNQ4XmnoRg,41
|
|
31
|
+
ladim-2.0.4.dist-info/top_level.txt,sha256=TK8Gl7d6MsrAQvqKG4b6YJCbB4UL46Se3SzsI-sJAuc,16
|
|
32
|
+
ladim-2.0.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|