ladim 1.3.4__py3-none-any.whl → 2.0.1__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/__main__.py +2 -0
- ladim/config.py +125 -0
- ladim/forcing.py +95 -0
- ladim/grid.py +79 -0
- ladim/gridforce/ROMS.py +32 -5
- ladim/gridforce/__init__.py +0 -1
- ladim/ibms/__init__.py +19 -4
- ladim/main.py +1 -1
- ladim/model.py +64 -29
- ladim/output.py +246 -0
- ladim/plugins/__init__.py +0 -0
- ladim/release.py +241 -0
- ladim/sample.py +3 -0
- ladim/{timestepper.py → solver.py} +5 -5
- ladim/state.py +142 -0
- ladim/tracker.py +165 -0
- ladim/utilities.py +5 -0
- {ladim-1.3.4.dist-info → ladim-2.0.1.dist-info}/METADATA +1 -1
- ladim-2.0.1.dist-info/RECORD +32 -0
- {ladim-1.3.4.dist-info → ladim-2.0.1.dist-info}/WHEEL +1 -1
- ladim/configuration/__init__.py +0 -1
- ladim/configuration/legacy.py +0 -425
- ladim/configuration/modularized.py +0 -22
- ladim/gridforce/legacy.py +0 -103
- ladim/ibms/legacy.py +0 -34
- ladim/output/__init__.py +0 -1
- ladim/output/legacy.py +0 -247
- ladim/release/__init__.py +0 -1
- ladim/release/legacy.py +0 -316
- ladim/state/__init__.py +0 -1
- ladim/state/legacy.py +0 -126
- ladim/tracker/__init__.py +0 -1
- ladim/tracker/legacy.py +0 -225
- ladim-1.3.4.dist-info/RECORD +0 -36
- {ladim-1.3.4.dist-info → ladim-2.0.1.dist-info}/LICENSE +0 -0
- {ladim-1.3.4.dist-info → ladim-2.0.1.dist-info}/entry_points.txt +0 -0
- {ladim-1.3.4.dist-info → ladim-2.0.1.dist-info}/top_level.txt +0 -0
ladim/gridforce/legacy.py
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Main gridforce controller
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import os
|
|
6
|
-
import sys
|
|
7
|
-
import importlib
|
|
8
|
-
|
|
9
|
-
# from ladim.configuration import config
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class Grid:
|
|
13
|
-
def __init__(self, modules, **conf):
|
|
14
|
-
legacy_conf = dict(
|
|
15
|
-
gridforce=conf,
|
|
16
|
-
start_time=conf.get('start_time', None),
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
# Allow gridforce module in current directory
|
|
20
|
-
sys.path.insert(0, os.getcwd())
|
|
21
|
-
# Import correct gridforce_module
|
|
22
|
-
# gridforce_module = importlib.import_module(config["gridforce_module"])
|
|
23
|
-
gridforce_module = importlib.import_module(conf["legacy_module"])
|
|
24
|
-
self.grid = gridforce_module.Grid(legacy_conf)
|
|
25
|
-
self.xmin = self.grid.xmin
|
|
26
|
-
self.xmax = self.grid.xmax
|
|
27
|
-
self.ymin = self.grid.ymin
|
|
28
|
-
self.ymax = self.grid.ymax
|
|
29
|
-
|
|
30
|
-
def sample_metric(self, X, Y):
|
|
31
|
-
"""Sample the metric coefficients"""
|
|
32
|
-
return self.grid.sample_metric(X, Y)
|
|
33
|
-
|
|
34
|
-
def sample_depth(self, X, Y):
|
|
35
|
-
"""Return the depth of grid cells"""
|
|
36
|
-
return self.grid.sample_depth(X, Y)
|
|
37
|
-
|
|
38
|
-
def lonlat(self, X, Y, method=None):
|
|
39
|
-
"""Return the longitude and latitude from grid coordinates"""
|
|
40
|
-
return self.grid.lonlat(X, Y, method=method)
|
|
41
|
-
|
|
42
|
-
def ingrid(self, X, Y):
|
|
43
|
-
"""Returns True for points inside the subgrid"""
|
|
44
|
-
return self.grid.ingrid(X, Y)
|
|
45
|
-
|
|
46
|
-
def onland(self, X, Y):
|
|
47
|
-
"""Returns True for points on land"""
|
|
48
|
-
return self.grid.onland(X, Y)
|
|
49
|
-
|
|
50
|
-
# Error if point outside
|
|
51
|
-
def atsea(self, X, Y):
|
|
52
|
-
"""Returns True for points at sea"""
|
|
53
|
-
return self.grid.atsea(X, Y)
|
|
54
|
-
|
|
55
|
-
def ll2xy(self, lon, lat):
|
|
56
|
-
return self.grid.ll2xy(lon, lat)
|
|
57
|
-
|
|
58
|
-
def xy2ll(self, X, Y):
|
|
59
|
-
return self.grid.xy2ll(X, Y)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
class Forcing:
|
|
63
|
-
def __init__(self, modules, **conf):
|
|
64
|
-
self.modules = modules
|
|
65
|
-
|
|
66
|
-
grid_ref = GridReference(modules)
|
|
67
|
-
legacy_conf = dict(
|
|
68
|
-
gridforce=conf,
|
|
69
|
-
ibm_forcing=conf.get('ibm_forcing', []),
|
|
70
|
-
start_time=conf.get('start_time', None),
|
|
71
|
-
stop_time=conf.get('stop_time', None),
|
|
72
|
-
dt=conf.get('dt', None),
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
# Allow gridforce module in current directory
|
|
76
|
-
sys.path.insert(0, os.getcwd())
|
|
77
|
-
# Import correct gridforce_module
|
|
78
|
-
gridforce_module = importlib.import_module(conf["legacy_module"])
|
|
79
|
-
self.forcing = gridforce_module.Forcing(legacy_conf, grid_ref)
|
|
80
|
-
# self.steps = self.forcing.steps
|
|
81
|
-
# self.U = self.forcing.U
|
|
82
|
-
# self.V = self.forcing.V
|
|
83
|
-
|
|
84
|
-
def update(self):
|
|
85
|
-
t = self.modules['state'].timestep
|
|
86
|
-
return self.forcing.update(t)
|
|
87
|
-
|
|
88
|
-
def velocity(self, X, Y, Z, tstep=0.0):
|
|
89
|
-
return self.forcing.velocity(X, Y, Z, tstep=tstep)
|
|
90
|
-
|
|
91
|
-
def field(self, X, Y, Z, name):
|
|
92
|
-
return self.forcing.field(X, Y, Z, name)
|
|
93
|
-
|
|
94
|
-
def close(self):
|
|
95
|
-
return self.forcing.close()
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
class GridReference:
|
|
99
|
-
def __init__(self, modules):
|
|
100
|
-
self.modules = modules
|
|
101
|
-
|
|
102
|
-
def __getattr__(self, item):
|
|
103
|
-
return getattr(self.modules['grid'].grid, item)
|
ladim/ibms/legacy.py
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
class Legacy_IBM:
|
|
2
|
-
def __init__(self, modules, **config):
|
|
3
|
-
self.modules = modules
|
|
4
|
-
|
|
5
|
-
legacy_conf = dict(
|
|
6
|
-
ibm=config,
|
|
7
|
-
dt=config['dt'],
|
|
8
|
-
start_time=config['start_time'],
|
|
9
|
-
output_instance=config['output_instance'],
|
|
10
|
-
nc_attributes=config['nc_attributes'],
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
if config["legacy_module"] is not None:
|
|
14
|
-
# Import the module
|
|
15
|
-
import logging
|
|
16
|
-
import sys
|
|
17
|
-
import os
|
|
18
|
-
import importlib
|
|
19
|
-
logging.info("Initializing the IBM")
|
|
20
|
-
sys.path.insert(0, os.getcwd())
|
|
21
|
-
ibm_module = importlib.import_module(config["legacy_module"])
|
|
22
|
-
# Initiate the IBM object
|
|
23
|
-
self.ibm = ibm_module.IBM(legacy_conf)
|
|
24
|
-
|
|
25
|
-
else:
|
|
26
|
-
self.ibm = None
|
|
27
|
-
|
|
28
|
-
def update(self):
|
|
29
|
-
if self.ibm is not None:
|
|
30
|
-
self.ibm.update_ibm(
|
|
31
|
-
self.modules['grid'],
|
|
32
|
-
self.modules['state'],
|
|
33
|
-
self.modules['forcing'],
|
|
34
|
-
)
|
ladim/output/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from .legacy import OutPut as Output
|
ladim/output/legacy.py
DELETED
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
"""Output module for the (py)ladim particle tracking model"""
|
|
2
|
-
|
|
3
|
-
# ------------------------------------
|
|
4
|
-
# Bjørn Ådlandsvik <bjorn@imr.no>
|
|
5
|
-
# Institute of Marine Research
|
|
6
|
-
# 2013-01-04
|
|
7
|
-
# ------------------------------------
|
|
8
|
-
|
|
9
|
-
import os
|
|
10
|
-
import logging
|
|
11
|
-
import datetime
|
|
12
|
-
import re
|
|
13
|
-
|
|
14
|
-
# from pathlib import Path
|
|
15
|
-
import numpy as np
|
|
16
|
-
from netCDF4 import Dataset
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
# Gjør til en iterator
|
|
20
|
-
class OutPut:
|
|
21
|
-
def __init__(self, modules, **config):
|
|
22
|
-
self.modules = modules
|
|
23
|
-
|
|
24
|
-
logging.info("Initializing output")
|
|
25
|
-
|
|
26
|
-
self.filename = config["output_file"]
|
|
27
|
-
self.instance_variables = config["output_instance"]
|
|
28
|
-
self.instance_count = 0
|
|
29
|
-
self.outcount = -1 # No output yet
|
|
30
|
-
self.file_counter = -1 # No file yer
|
|
31
|
-
self.skip_output = config["skip_initial"]
|
|
32
|
-
self.numrec = config["output_numrec"]
|
|
33
|
-
|
|
34
|
-
self.outconf = dict(
|
|
35
|
-
output_period=config['output_period'],
|
|
36
|
-
output_format=config['output_format'],
|
|
37
|
-
reference_time=config['reference_time'],
|
|
38
|
-
output_particle=config['output_particle'],
|
|
39
|
-
nc_attributes=config['nc_attributes'].copy(),
|
|
40
|
-
output_instance=config['output_instance'],
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
if self.numrec == 0:
|
|
44
|
-
self.multi_file = False
|
|
45
|
-
self.numrec = 999999 # A large number
|
|
46
|
-
else:
|
|
47
|
-
self.multi_file = True
|
|
48
|
-
# If multi_file and output_file ends with number "dddd.nc"
|
|
49
|
-
# start output number with dddd, else use zero
|
|
50
|
-
fname0, ext = os.path.splitext(self.filename)
|
|
51
|
-
# fname = f'{fname0}_{self.file_counter:04d}{ext}'
|
|
52
|
-
patt = r"_[0-9]{4}$" # '_dddd' at end of string
|
|
53
|
-
m = re.search(patt, fname0)
|
|
54
|
-
# start with number in output_name
|
|
55
|
-
if m:
|
|
56
|
-
self.filename = fname0[:-5] + ext # Strip the number
|
|
57
|
-
self.file_counter = int(m.group(0)[1:]) - 1
|
|
58
|
-
|
|
59
|
-
self.dt = config["dt"]
|
|
60
|
-
self.num_output = config["num_output"]
|
|
61
|
-
self.nc = None # No open netCD file yet
|
|
62
|
-
# Indicator for lon/lat output
|
|
63
|
-
self.lonlat = (
|
|
64
|
-
"lat" in self.instance_variables or "lon" in self.instance_variables
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
# ----------------------------------------------
|
|
68
|
-
def write(self) -> None:
|
|
69
|
-
config = self.outconf
|
|
70
|
-
state = self.modules['state']
|
|
71
|
-
grid = self.modules['grid']
|
|
72
|
-
|
|
73
|
-
"""Write the model state to NetCDF"""
|
|
74
|
-
|
|
75
|
-
# May skip initial output
|
|
76
|
-
if self.skip_output:
|
|
77
|
-
self.skip_output = False
|
|
78
|
-
return
|
|
79
|
-
|
|
80
|
-
self.outcount += 1
|
|
81
|
-
t = self.outcount % self.numrec # in-file record counter
|
|
82
|
-
|
|
83
|
-
logging.debug(
|
|
84
|
-
"Writing: timestep, timestamp = {} {}".format(
|
|
85
|
-
state.timestep, state.timestamp
|
|
86
|
-
)
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
# Create new file?
|
|
90
|
-
if t == 0:
|
|
91
|
-
# Close old file and open a new
|
|
92
|
-
if self.nc:
|
|
93
|
-
self.nc.close()
|
|
94
|
-
self.file_counter += 1
|
|
95
|
-
self.pstart0 = self.instance_count # Start of data in the file
|
|
96
|
-
self.nc = self._define_netcdf()
|
|
97
|
-
logging.info(f"Opened output file: {self.nc.filepath()}")
|
|
98
|
-
|
|
99
|
-
pcount = len(state) # Present number of particles
|
|
100
|
-
pstart = self.instance_count
|
|
101
|
-
|
|
102
|
-
logging.debug(f"Writing {pcount} particles")
|
|
103
|
-
|
|
104
|
-
tdelta = state.timestamp - config["reference_time"]
|
|
105
|
-
seconds = tdelta.astype("m8[s]").astype("int")
|
|
106
|
-
self.nc.variables["time"][t] = float(seconds)
|
|
107
|
-
|
|
108
|
-
self.nc.variables["particle_count"][t] = pcount
|
|
109
|
-
|
|
110
|
-
# Compute lon, lat if needed
|
|
111
|
-
if self.lonlat:
|
|
112
|
-
lon, lat = grid.xy2ll(state.X, state.Y)
|
|
113
|
-
|
|
114
|
-
start = pstart - self.pstart0
|
|
115
|
-
end = pstart + pcount - self.pstart0
|
|
116
|
-
# print("start, end = ", start, end)
|
|
117
|
-
for name in self.instance_variables:
|
|
118
|
-
if name == "lon":
|
|
119
|
-
self.nc.variables["lon"][start:end] = lon
|
|
120
|
-
elif name == "lat":
|
|
121
|
-
self.nc.variables["lat"][start:end] = lat
|
|
122
|
-
else:
|
|
123
|
-
self.nc.variables[name][start:end] = state[name]
|
|
124
|
-
|
|
125
|
-
# Update counters
|
|
126
|
-
# self.outcount += 1
|
|
127
|
-
self.instance_count += pcount
|
|
128
|
-
|
|
129
|
-
# Flush the data to the file
|
|
130
|
-
self.nc.sync()
|
|
131
|
-
|
|
132
|
-
# Close final file
|
|
133
|
-
if self.outcount == self.num_output - 1:
|
|
134
|
-
self.nc.close()
|
|
135
|
-
|
|
136
|
-
# -----------------------------------------------
|
|
137
|
-
def _define_netcdf(self) -> Dataset:
|
|
138
|
-
"""Define a NetCDF output file"""
|
|
139
|
-
|
|
140
|
-
config = self.outconf
|
|
141
|
-
release = self.modules['release']
|
|
142
|
-
|
|
143
|
-
# Generate file name
|
|
144
|
-
fname = self.filename
|
|
145
|
-
if self.multi_file:
|
|
146
|
-
# fname0 -> fname0_dddd.nc
|
|
147
|
-
fname0, ext = os.path.splitext(self.filename)
|
|
148
|
-
fname = f"{fname0}_{self.file_counter:04d}{ext}"
|
|
149
|
-
|
|
150
|
-
logging.debug(f"Defining output netCDF file: {fname}")
|
|
151
|
-
nc = Dataset(fname, mode="w", format=config["output_format"])
|
|
152
|
-
# --- Dimensions
|
|
153
|
-
nc.createDimension("particle", release.total_particle_count)
|
|
154
|
-
nc.createDimension("particle_instance", None) # unlimited
|
|
155
|
-
# Sett output-period i config (bruk naturlig enhet)
|
|
156
|
-
# regne om til antall tidsteg og få inn under
|
|
157
|
-
outcount0 = self.outcount
|
|
158
|
-
outcount1 = min(outcount0 + self.numrec, self.num_output)
|
|
159
|
-
nc.createDimension("time", outcount1 - outcount0)
|
|
160
|
-
|
|
161
|
-
# ---- Coordinate variable for time
|
|
162
|
-
v = nc.createVariable("time", "f8", ("time",))
|
|
163
|
-
v.long_name = "time"
|
|
164
|
-
v.standard_name = "time"
|
|
165
|
-
timeref = str(config["reference_time"]).replace("T", " ")
|
|
166
|
-
v.units = f"seconds since {timeref}"
|
|
167
|
-
|
|
168
|
-
# instance_offset
|
|
169
|
-
v = nc.createVariable("instance_offset", "i", ())
|
|
170
|
-
v.long_name = "particle instance offset for file"
|
|
171
|
-
|
|
172
|
-
# Particle count
|
|
173
|
-
v = nc.createVariable("particle_count", "i4", ("time",))
|
|
174
|
-
v.long_name = "number of particles in a given timestep"
|
|
175
|
-
v.ragged_row_count = "particle count at nth timestep"
|
|
176
|
-
|
|
177
|
-
# Particle variables
|
|
178
|
-
for name in config["output_particle"]:
|
|
179
|
-
confname = config["nc_attributes"][name]
|
|
180
|
-
if confname["ncformat"][0] == "S": # text
|
|
181
|
-
length = int(confname["ncformat"][1:])
|
|
182
|
-
lendimname = "len_" + name
|
|
183
|
-
nc.createDimension(lendimname, length)
|
|
184
|
-
v = nc.createVariable(
|
|
185
|
-
varname=name,
|
|
186
|
-
datatype="S1",
|
|
187
|
-
dimensions=("particle", lendimname),
|
|
188
|
-
zlib=True,
|
|
189
|
-
)
|
|
190
|
-
else: # Numeric
|
|
191
|
-
v = nc.createVariable(
|
|
192
|
-
varname=name,
|
|
193
|
-
datatype=config["nc_attributes"][name]["ncformat"],
|
|
194
|
-
dimensions=("particle",),
|
|
195
|
-
zlib=True,
|
|
196
|
-
)
|
|
197
|
-
for attr, value in config["nc_attributes"][name].items():
|
|
198
|
-
if attr != "ncformat":
|
|
199
|
-
setattr(v, attr, value)
|
|
200
|
-
|
|
201
|
-
# Instance variables
|
|
202
|
-
for name in config["output_instance"]:
|
|
203
|
-
v = nc.createVariable(
|
|
204
|
-
varname=name,
|
|
205
|
-
datatype=config["nc_attributes"][name]["ncformat"],
|
|
206
|
-
dimensions=("particle_instance",),
|
|
207
|
-
zlib=True,
|
|
208
|
-
)
|
|
209
|
-
|
|
210
|
-
for attr, value in config["nc_attributes"][name].items():
|
|
211
|
-
if attr != "ncformat":
|
|
212
|
-
setattr(v, attr, value)
|
|
213
|
-
|
|
214
|
-
# --- Global attributes
|
|
215
|
-
# Burde ta f.eks. source fra setup
|
|
216
|
-
# hvis andre skulle bruke
|
|
217
|
-
nc.Conventions = "CF-1.5"
|
|
218
|
-
nc.institution = "Institute of Marine Research"
|
|
219
|
-
nc.source = "Lagrangian Advection and Diffusion Model, python version"
|
|
220
|
-
nc.history = "Created by pyladim"
|
|
221
|
-
nc.date = str(datetime.date.today())
|
|
222
|
-
|
|
223
|
-
logging.debug("Netcdf output file defined")
|
|
224
|
-
|
|
225
|
-
# Save particle variables
|
|
226
|
-
for name in config["output_particle"]:
|
|
227
|
-
var = nc.variables[name]
|
|
228
|
-
if var.datatype == np.dtype("S1"): # Text
|
|
229
|
-
n = len(nc.dimensions[var.dimensions[-1]])
|
|
230
|
-
A = [
|
|
231
|
-
list(s[:n].ljust(n))
|
|
232
|
-
for s in release.particle_variables[name][:]
|
|
233
|
-
]
|
|
234
|
-
var[:] = np.array(A)
|
|
235
|
-
else: # Numeric
|
|
236
|
-
nc.variables[name][:] = release.particle_variables[name][:]
|
|
237
|
-
|
|
238
|
-
# Set instance offset
|
|
239
|
-
var = nc.variables["instance_offset"]
|
|
240
|
-
var[:] = self.instance_count
|
|
241
|
-
|
|
242
|
-
return nc
|
|
243
|
-
|
|
244
|
-
def update(self):
|
|
245
|
-
step = self.modules['state'].timestep
|
|
246
|
-
if step % self.outconf['output_period'] == 0:
|
|
247
|
-
self.write()
|
ladim/release/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from .legacy import ParticleReleaser as Releaser
|