foxes 0.5.1__py3-none-any.whl → 0.5.2__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.
Potentially problematic release.
This version of foxes might be problematic. Click here for more details.
- foxes/VERSION +1 -1
- foxes/algorithms/downwind/downwind.py +41 -46
- foxes/algorithms/downwind/models/point_wakes_calc.py +4 -9
- foxes/algorithms/downwind/models/set_amb_point_results.py +5 -22
- foxes/core/algorithm.py +1 -1
- foxes/core/data_calc_model.py +26 -2
- foxes/core/partial_wakes_model.py +1 -1
- foxes/core/rotor_model.py +36 -2
- foxes/core/turbine_model.py +36 -0
- foxes/core/turbine_type.py +35 -1
- foxes/core/wake_frame.py +39 -3
- foxes/core/wake_model.py +36 -0
- foxes/models/model_book.py +132 -85
- foxes/models/turbine_models/rotor_centre_calc.py +1 -2
- foxes/models/turbine_types/CpCt_file.py +13 -3
- foxes/models/turbine_types/CpCt_from_two.py +14 -4
- foxes/models/vertical_profiles/abl_log_neutral_ws.py +32 -5
- foxes/models/vertical_profiles/abl_log_stable_ws.py +32 -4
- foxes/models/vertical_profiles/abl_log_unstable_ws.py +32 -4
- foxes/models/vertical_profiles/abl_log_ws.py +50 -18
- foxes/models/wake_frames/yawed_wakes.py +15 -9
- foxes/models/wake_models/induction/__init__.py +1 -1
- foxes/models/wake_models/induction/rankine_half_body.py +33 -7
- foxes/models/wake_models/ti/crespo_hernandez.py +6 -1
- foxes/models/wake_models/ti/iec_ti.py +5 -3
- foxes/models/wake_models/wind/__init__.py +2 -2
- foxes/models/wake_models/wind/{bastankhah.py → bastankhah14.py} +11 -14
- foxes/models/wake_models/wind/{porte_agel.py → bastankhah16.py} +24 -16
- foxes/models/wake_models/wind/turbopark.py +11 -22
- foxes/models/wake_superpositions/__init__.py +9 -5
- foxes/models/wake_superpositions/ti_linear.py +134 -0
- foxes/models/wake_superpositions/ti_max.py +134 -0
- foxes/models/wake_superpositions/{ti_superp.py → ti_pow.py} +15 -57
- foxes/models/wake_superpositions/ti_quadratic.py +134 -0
- foxes/models/wake_superpositions/ws_linear.py +170 -0
- foxes/models/wake_superpositions/ws_max.py +173 -0
- foxes/models/wake_superpositions/ws_pow.py +175 -0
- foxes/models/wake_superpositions/{product.py → ws_product.py} +43 -22
- foxes/models/wake_superpositions/ws_quadratic.py +170 -0
- foxes/output/__init__.py +4 -0
- foxes/output/calc_points.py +143 -0
- foxes/output/flow_plots_2d/__init__.py +1 -0
- foxes/output/flow_plots_2d/common.py +104 -1
- foxes/output/flow_plots_2d/flow_plots.py +237 -569
- foxes/output/flow_plots_2d/get_fig.py +183 -0
- foxes/output/flow_plots_2d/seq_flow_ani_plugin.py +0 -1
- foxes/output/grids.py +705 -0
- foxes/output/output.py +58 -11
- foxes/output/results_writer.py +101 -17
- foxes/output/round.py +10 -0
- foxes/output/slice_data.py +900 -0
- foxes/utils/__init__.py +5 -3
- foxes/utils/exec_python.py +56 -0
- foxes/utils/geopandas_utils.py +294 -0
- foxes/utils/pandas_utils.py +175 -0
- foxes/utils/plotly_utils.py +19 -0
- foxes/utils/xarray_utils.py +38 -0
- {foxes-0.5.1.dist-info → foxes-0.5.2.dist-info}/METADATA +1 -1
- {foxes-0.5.1.dist-info → foxes-0.5.2.dist-info}/RECORD +63 -49
- foxes/models/wake_superpositions/linear.py +0 -242
- foxes/models/wake_superpositions/max.py +0 -258
- foxes/models/wake_superpositions/quadratic.py +0 -252
- {foxes-0.5.1.dist-info → foxes-0.5.2.dist-info}/LICENSE +0 -0
- {foxes-0.5.1.dist-info → foxes-0.5.2.dist-info}/WHEEL +0 -0
- {foxes-0.5.1.dist-info → foxes-0.5.2.dist-info}/top_level.txt +0 -0
- {foxes-0.5.1.dist-info → foxes-0.5.2.dist-info}/zip-safe +0 -0
foxes/output/grids.py
ADDED
|
@@ -0,0 +1,705 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from xarray import Dataset
|
|
4
|
+
|
|
5
|
+
from foxes.utils import wd2uv, write_nc
|
|
6
|
+
import foxes.variables as FV
|
|
7
|
+
import foxes.constants as FC
|
|
8
|
+
|
|
9
|
+
def calc_point_results(
|
|
10
|
+
algo,
|
|
11
|
+
g_pts,
|
|
12
|
+
farm_results=None,
|
|
13
|
+
seq_iter=None,
|
|
14
|
+
runner=None,
|
|
15
|
+
verbosity=0,
|
|
16
|
+
**kwargs,
|
|
17
|
+
):
|
|
18
|
+
"""
|
|
19
|
+
Helper function that calculates results at grid points.
|
|
20
|
+
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
algo: foxes.Algorithm
|
|
24
|
+
The algorithm for point calculation
|
|
25
|
+
g_pts: numpy.ndarray
|
|
26
|
+
The grid points, shape: (n_states, n_x, n_y, 3)
|
|
27
|
+
farm_results: xarray.Dataset, optional
|
|
28
|
+
The farm results
|
|
29
|
+
seq_iter: foxes.algorithms.sequential.SequentialIter, optional
|
|
30
|
+
The sequential itarator
|
|
31
|
+
runner: foxes.utils.runners.Runner, optional
|
|
32
|
+
The runner
|
|
33
|
+
verbosity: int
|
|
34
|
+
The verbosity level, 0 = silent
|
|
35
|
+
kwargs: dict, optional
|
|
36
|
+
Additional parameters for algo.calc_points
|
|
37
|
+
|
|
38
|
+
"""
|
|
39
|
+
averb = None if verbosity == algo.verbosity else algo.verbosity
|
|
40
|
+
if averb is not None:
|
|
41
|
+
algo.verbosity = verbosity
|
|
42
|
+
fres = farm_results if seq_iter is None else seq_iter.farm_results
|
|
43
|
+
if runner is None:
|
|
44
|
+
point_results = algo.calc_points(fres, points=g_pts, **kwargs)
|
|
45
|
+
else:
|
|
46
|
+
kwargs["points"] = g_pts
|
|
47
|
+
point_results = runner.run(algo.calc_points, args=(fres,), kwargs=kwargs)
|
|
48
|
+
if averb is not None:
|
|
49
|
+
algo.verbosity = averb
|
|
50
|
+
|
|
51
|
+
return point_results
|
|
52
|
+
|
|
53
|
+
def get_grid_xy(
|
|
54
|
+
farm_results,
|
|
55
|
+
resolution,
|
|
56
|
+
xmin=None,
|
|
57
|
+
ymin=None,
|
|
58
|
+
xmax=None,
|
|
59
|
+
ymax=None,
|
|
60
|
+
z=None,
|
|
61
|
+
xspace=500.0,
|
|
62
|
+
yspace=500.0,
|
|
63
|
+
verbosity=0,
|
|
64
|
+
):
|
|
65
|
+
"""
|
|
66
|
+
Helper function that generates 2D grid in a horizontal xy-plane.
|
|
67
|
+
|
|
68
|
+
Parameters
|
|
69
|
+
----------
|
|
70
|
+
farm_results: xarray.Dataset
|
|
71
|
+
The farm results. The calculated variables have
|
|
72
|
+
dimensions (state, turbine)
|
|
73
|
+
resolution: float
|
|
74
|
+
The resolution in m
|
|
75
|
+
xmin: float
|
|
76
|
+
The min x coordinate, or None for automatic
|
|
77
|
+
ymin: float
|
|
78
|
+
The min y coordinate, or None for automatic
|
|
79
|
+
xmax: float
|
|
80
|
+
The max x coordinate, or None for automatic
|
|
81
|
+
ymax: float
|
|
82
|
+
The max y coordinate, or None for automatic
|
|
83
|
+
z: float
|
|
84
|
+
The z coordinate of the plane
|
|
85
|
+
xspace: float
|
|
86
|
+
The extra space in x direction, before and after wind farm
|
|
87
|
+
yspace: float
|
|
88
|
+
The extra space in y direction, before and after wind farm
|
|
89
|
+
verbosity: int, optional
|
|
90
|
+
The verbosity level
|
|
91
|
+
|
|
92
|
+
Returns
|
|
93
|
+
-------
|
|
94
|
+
x_pos: numpy.ndarray
|
|
95
|
+
The x grid positions, shape: (n_x,)
|
|
96
|
+
y_pos: numpy.ndarray
|
|
97
|
+
The y grid positions, shape: (n_y,)
|
|
98
|
+
z_pos: float
|
|
99
|
+
The z position of the grid
|
|
100
|
+
g_pts: numpy.ndarray
|
|
101
|
+
The grid points, shape: (n_states, n_pts, 3)
|
|
102
|
+
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
# prepare:
|
|
106
|
+
n_states = farm_results[FV.H].shape[0]
|
|
107
|
+
|
|
108
|
+
# get base rectangle:
|
|
109
|
+
x_min = xmin if xmin is not None else farm_results[FV.X].min() - xspace
|
|
110
|
+
y_min = ymin if ymin is not None else farm_results[FV.Y].min() - yspace
|
|
111
|
+
z_min = z if z is not None else farm_results[FV.H].min()
|
|
112
|
+
x_max = xmax if xmax is not None else farm_results[FV.X].max() + xspace
|
|
113
|
+
y_max = ymax if ymax is not None else farm_results[FV.Y].max() + yspace
|
|
114
|
+
z_max = z if z is not None else farm_results[FV.H].max()
|
|
115
|
+
|
|
116
|
+
x_pos, x_res = np.linspace(
|
|
117
|
+
x_min,
|
|
118
|
+
x_max,
|
|
119
|
+
num=int((x_max - x_min) / resolution) + 1,
|
|
120
|
+
endpoint=True,
|
|
121
|
+
retstep=True,
|
|
122
|
+
dtype=None,
|
|
123
|
+
)
|
|
124
|
+
y_pos, y_res = np.linspace(
|
|
125
|
+
y_min,
|
|
126
|
+
y_max,
|
|
127
|
+
num=int((y_max - y_min) / resolution) + 1,
|
|
128
|
+
endpoint=True,
|
|
129
|
+
retstep=True,
|
|
130
|
+
dtype=None,
|
|
131
|
+
)
|
|
132
|
+
N_x, N_y = len(x_pos), len(y_pos)
|
|
133
|
+
n_pts = len(x_pos) * len(y_pos)
|
|
134
|
+
z_pos = 0.5 * (z_min + z_max)
|
|
135
|
+
g_pts = np.zeros((n_states, N_x, N_y, 3), dtype=FC.DTYPE)
|
|
136
|
+
g_pts[:, :, :, 0] = x_pos[None, :, None]
|
|
137
|
+
g_pts[:, :, :, 1] = y_pos[None, None, :]
|
|
138
|
+
g_pts[:, :, :, 2] = z_pos
|
|
139
|
+
|
|
140
|
+
if verbosity > 1:
|
|
141
|
+
print("\nFlowPlots2D plot grid:")
|
|
142
|
+
print("Min XYZ =", x_min, y_min, z_min)
|
|
143
|
+
print("Max XYZ =", x_max, y_max, z_max)
|
|
144
|
+
print("Pos Z =", z_pos)
|
|
145
|
+
print("Res XY =", x_res, y_res)
|
|
146
|
+
print("Dim XY =", N_x, N_y)
|
|
147
|
+
print("Grid pts =", n_pts)
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
x_pos,
|
|
151
|
+
y_pos,
|
|
152
|
+
z_pos,
|
|
153
|
+
g_pts.reshape(n_states, n_pts, 3),
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
def get_grid_xz(
|
|
157
|
+
farm_results,
|
|
158
|
+
resolution,
|
|
159
|
+
x_direction=270,
|
|
160
|
+
xmin=None,
|
|
161
|
+
zmin=0.0,
|
|
162
|
+
xmax=None,
|
|
163
|
+
zmax=None,
|
|
164
|
+
y=None,
|
|
165
|
+
xspace=500.0,
|
|
166
|
+
zspace=500.0,
|
|
167
|
+
verbosity=0,
|
|
168
|
+
):
|
|
169
|
+
"""
|
|
170
|
+
Helper function that generates 2D grid in a vertical xz-plane.
|
|
171
|
+
|
|
172
|
+
Parameters
|
|
173
|
+
----------
|
|
174
|
+
farm_results: xarray.Dataset
|
|
175
|
+
The farm results. The calculated variables have
|
|
176
|
+
dimensions (state, turbine)
|
|
177
|
+
resolution: float
|
|
178
|
+
The resolution in m
|
|
179
|
+
x_direction: float
|
|
180
|
+
The direction of the x axis, 0 = north
|
|
181
|
+
xmin: float
|
|
182
|
+
The min x coordinate, or None for automatic
|
|
183
|
+
zmin: float
|
|
184
|
+
The min z coordinate
|
|
185
|
+
xmax: float
|
|
186
|
+
The max x coordinate, or None for automatic
|
|
187
|
+
zmax: float
|
|
188
|
+
The max z coordinate, or None for automatic
|
|
189
|
+
y: float
|
|
190
|
+
The y coordinate of the plane
|
|
191
|
+
xspace: float
|
|
192
|
+
The extra space in x direction, before and after wind farm
|
|
193
|
+
zspace: float
|
|
194
|
+
The extra space in z direction, below and above wind farm
|
|
195
|
+
verbosity: int, optional
|
|
196
|
+
The verbosity level
|
|
197
|
+
|
|
198
|
+
Returns
|
|
199
|
+
-------
|
|
200
|
+
x_pos: numpy.ndarray
|
|
201
|
+
The x grid positions, shape: (n_x,)
|
|
202
|
+
y_pos: float
|
|
203
|
+
The y position of the grid
|
|
204
|
+
z_pos: numpy.ndarray
|
|
205
|
+
The z grid positions, shape: (n_z,)
|
|
206
|
+
g_pts: numpy.ndarray
|
|
207
|
+
The grid points, shape: (n_states, n_pts, 3)
|
|
208
|
+
|
|
209
|
+
"""
|
|
210
|
+
|
|
211
|
+
# prepare:
|
|
212
|
+
n_states, n_turbines = farm_results[FV.H].shape
|
|
213
|
+
n_x = np.append(wd2uv(x_direction), [0.0], axis=0)
|
|
214
|
+
n_z = np.array([0.0, 0.0, 1.0])
|
|
215
|
+
n_y = np.cross(n_z, n_x)
|
|
216
|
+
|
|
217
|
+
# project to axes:
|
|
218
|
+
xyz = np.zeros((n_states, n_turbines, 3), dtype=FC.DTYPE)
|
|
219
|
+
xyz[:, :, 0] = farm_results[FV.X]
|
|
220
|
+
xyz[:, :, 1] = farm_results[FV.Y]
|
|
221
|
+
xyz[:, :, 2] = farm_results[FV.H]
|
|
222
|
+
xx = np.einsum("std,d->st", xyz, n_x)
|
|
223
|
+
yy = np.einsum("std,d->st", xyz, n_y)
|
|
224
|
+
zz = np.einsum("std,d->st", xyz, n_z)
|
|
225
|
+
del xyz
|
|
226
|
+
|
|
227
|
+
# get base rectangle:
|
|
228
|
+
x_min = xmin if xmin is not None else np.min(xx) - xspace
|
|
229
|
+
z_min = zmin if zmin is not None else np.minimum(np.min(zz) - zspace, 0.0)
|
|
230
|
+
y_min = y if y is not None else np.min(yy)
|
|
231
|
+
x_max = xmax if xmax is not None else np.max(xx) + xspace
|
|
232
|
+
z_max = zmax if zmax is not None else np.max(zz) + zspace
|
|
233
|
+
y_max = y if y is not None else np.max(yy)
|
|
234
|
+
del xx, yy, zz
|
|
235
|
+
|
|
236
|
+
x_pos, x_res = np.linspace(
|
|
237
|
+
x_min,
|
|
238
|
+
x_max,
|
|
239
|
+
num=int((x_max - x_min) / resolution) + 1,
|
|
240
|
+
endpoint=True,
|
|
241
|
+
retstep=True,
|
|
242
|
+
dtype=None,
|
|
243
|
+
)
|
|
244
|
+
z_pos, z_res = np.linspace(
|
|
245
|
+
z_min,
|
|
246
|
+
z_max,
|
|
247
|
+
num=int((z_max - z_min) / resolution) + 1,
|
|
248
|
+
endpoint=True,
|
|
249
|
+
retstep=True,
|
|
250
|
+
dtype=None,
|
|
251
|
+
)
|
|
252
|
+
N_x, N_z = len(x_pos), len(z_pos)
|
|
253
|
+
n_pts = len(x_pos) * len(z_pos)
|
|
254
|
+
y_pos = 0.5 * (y_min + y_max)
|
|
255
|
+
g_pts = np.zeros((n_states, N_x, N_z, 3), dtype=FC.DTYPE)
|
|
256
|
+
g_pts[:] += x_pos[None, :, None, None] * n_x[None, None, None, :]
|
|
257
|
+
g_pts[:] += y_pos * n_y[None, None, None, :]
|
|
258
|
+
g_pts[:] += z_pos[None, None, :, None] * n_z[None, None, None, :]
|
|
259
|
+
|
|
260
|
+
if verbosity > 1:
|
|
261
|
+
print("\nFlowPlots2D plot grid:")
|
|
262
|
+
print("Min XYZ =", x_min, y_min, z_min)
|
|
263
|
+
print("Max XYZ =", x_max, y_max, z_max)
|
|
264
|
+
print("Pos Y =", y_pos)
|
|
265
|
+
print("Res XZ =", x_res, z_res)
|
|
266
|
+
print("Dim XZ =", N_x, N_z)
|
|
267
|
+
print("Grid pts =", n_pts)
|
|
268
|
+
|
|
269
|
+
return (
|
|
270
|
+
x_pos,
|
|
271
|
+
y_pos,
|
|
272
|
+
z_pos,
|
|
273
|
+
g_pts.reshape(n_states, n_pts, 3),
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
def get_grid_yz(
|
|
277
|
+
farm_results,
|
|
278
|
+
resolution,
|
|
279
|
+
x_direction=270,
|
|
280
|
+
ymin=None,
|
|
281
|
+
zmin=0.0,
|
|
282
|
+
ymax=None,
|
|
283
|
+
zmax=None,
|
|
284
|
+
x=None,
|
|
285
|
+
yspace=500.0,
|
|
286
|
+
zspace=500.0,
|
|
287
|
+
verbosity=0,
|
|
288
|
+
):
|
|
289
|
+
"""
|
|
290
|
+
Helper function that generates 2D grid in a vertical yz-plane.
|
|
291
|
+
|
|
292
|
+
Parameters
|
|
293
|
+
----------
|
|
294
|
+
farm_results: xarray.Dataset
|
|
295
|
+
The farm results. The calculated variables have
|
|
296
|
+
dimensions (state, turbine)
|
|
297
|
+
resolution: float
|
|
298
|
+
The resolution in m
|
|
299
|
+
x_direction: float
|
|
300
|
+
The direction of the x axis, 0 = north
|
|
301
|
+
ymin: float
|
|
302
|
+
The min y coordinate, or None for automatic
|
|
303
|
+
zmin: float
|
|
304
|
+
The min z coordinate
|
|
305
|
+
ymax: float
|
|
306
|
+
The max y coordinate, or None for automatic
|
|
307
|
+
zmax: float
|
|
308
|
+
The max z coordinate, or None for automatic
|
|
309
|
+
x: float
|
|
310
|
+
The x coordinate of the plane
|
|
311
|
+
yspace: float
|
|
312
|
+
The extra space in y direction, before and after wind farm
|
|
313
|
+
zspace: float
|
|
314
|
+
The extra space in z direction, below and above wind farm
|
|
315
|
+
verbosity: int, optional
|
|
316
|
+
The verbosity level
|
|
317
|
+
|
|
318
|
+
Returns
|
|
319
|
+
-------
|
|
320
|
+
x_pos: float
|
|
321
|
+
The x position of the grid
|
|
322
|
+
y_pos: numpy.ndarray
|
|
323
|
+
The y grid positions, shape: (n_y,)
|
|
324
|
+
z_pos: numpy.ndarray
|
|
325
|
+
The z grid positions, shape: (n_z,)
|
|
326
|
+
g_pts: numpy.ndarray
|
|
327
|
+
The grid points, shape: (n_states, n_pts, 3)
|
|
328
|
+
|
|
329
|
+
"""
|
|
330
|
+
|
|
331
|
+
# prepare:
|
|
332
|
+
n_states, n_turbines = farm_results[FV.H].shape
|
|
333
|
+
n_x = np.append(wd2uv(x_direction), [0.0], axis=0)
|
|
334
|
+
n_z = np.array([0.0, 0.0, 1.0])
|
|
335
|
+
n_y = np.cross(n_z, n_x)
|
|
336
|
+
|
|
337
|
+
# project to axes:
|
|
338
|
+
xyz = np.zeros((n_states, n_turbines, 3), dtype=FC.DTYPE)
|
|
339
|
+
xyz[:, :, 0] = farm_results[FV.X]
|
|
340
|
+
xyz[:, :, 1] = farm_results[FV.Y]
|
|
341
|
+
xyz[:, :, 2] = farm_results[FV.H]
|
|
342
|
+
xx = np.einsum("std,d->st", xyz, n_x)
|
|
343
|
+
yy = np.einsum("std,d->st", xyz, n_y)
|
|
344
|
+
zz = np.einsum("std,d->st", xyz, n_z)
|
|
345
|
+
del xyz
|
|
346
|
+
|
|
347
|
+
# get base rectangle:
|
|
348
|
+
y_min = ymin if ymin is not None else np.min(yy) - yspace
|
|
349
|
+
z_min = zmin if zmin is not None else np.minimum(np.min(zz) - zspace, 0.0)
|
|
350
|
+
x_min = x if x is not None else np.min(xx)
|
|
351
|
+
y_max = ymax if ymax is not None else np.max(yy) + yspace
|
|
352
|
+
z_max = zmax if zmax is not None else np.max(zz) + zspace
|
|
353
|
+
x_max = x if x is not None else np.max(xx)
|
|
354
|
+
del xx, yy, zz
|
|
355
|
+
|
|
356
|
+
y_pos, y_res = np.linspace(
|
|
357
|
+
y_min,
|
|
358
|
+
y_max,
|
|
359
|
+
num=int((y_max - y_min) / resolution) + 1,
|
|
360
|
+
endpoint=True,
|
|
361
|
+
retstep=True,
|
|
362
|
+
dtype=None,
|
|
363
|
+
)
|
|
364
|
+
z_pos, z_res = np.linspace(
|
|
365
|
+
z_min,
|
|
366
|
+
z_max,
|
|
367
|
+
num=int((z_max - z_min) / resolution) + 1,
|
|
368
|
+
endpoint=True,
|
|
369
|
+
retstep=True,
|
|
370
|
+
dtype=None,
|
|
371
|
+
)
|
|
372
|
+
N_y, N_z = len(y_pos), len(z_pos)
|
|
373
|
+
n_pts = len(y_pos) * len(z_pos)
|
|
374
|
+
x_pos = 0.5 * (x_min + x_max)
|
|
375
|
+
g_pts = np.zeros((n_states, N_y, N_z, 3), dtype=FC.DTYPE)
|
|
376
|
+
g_pts[:] += x_pos * n_x[None, None, None, :]
|
|
377
|
+
g_pts[:] += y_pos[None, :, None, None] * n_y[None, None, None, :]
|
|
378
|
+
g_pts[:] += z_pos[None, None, :, None] * n_z[None, None, None, :]
|
|
379
|
+
|
|
380
|
+
if verbosity > 1:
|
|
381
|
+
print("\nFlowPlots2D plot grid:")
|
|
382
|
+
print("Min XYZ =", x_min, y_min, z_min)
|
|
383
|
+
print("Max XYZ =", x_max, y_max, z_max)
|
|
384
|
+
print("Pos X =", x_pos)
|
|
385
|
+
print("Res YZ =", y_res, z_res)
|
|
386
|
+
print("Dim YZ =", N_y, N_z)
|
|
387
|
+
print("Grid pts =", n_pts)
|
|
388
|
+
|
|
389
|
+
return (
|
|
390
|
+
x_pos,
|
|
391
|
+
y_pos,
|
|
392
|
+
z_pos,
|
|
393
|
+
g_pts.reshape(n_states, n_pts, 3),
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
def np2np_p(data, a_pos, b_pos):
|
|
397
|
+
"""
|
|
398
|
+
Create numpy data from numpy data
|
|
399
|
+
|
|
400
|
+
Parameters
|
|
401
|
+
----------
|
|
402
|
+
data: dict
|
|
403
|
+
The data on the grid. Key: variable name,
|
|
404
|
+
value: numpy.ndarray with shape (n_gpts,)
|
|
405
|
+
a_pos: numpy.ndarray
|
|
406
|
+
The first axis coordinates, e.g. x_pos,
|
|
407
|
+
shape: (n_a,)
|
|
408
|
+
b_pos: numpy.ndarray
|
|
409
|
+
The second axis coordinates, e.g. y_pos,
|
|
410
|
+
shape: (n_b,)
|
|
411
|
+
|
|
412
|
+
Returns
|
|
413
|
+
-------
|
|
414
|
+
out: dict
|
|
415
|
+
The data on the grid. Key: variable name,
|
|
416
|
+
value: numpy.ndarray with shape (n_a, n_b, n_vars)
|
|
417
|
+
|
|
418
|
+
"""
|
|
419
|
+
n_a = len(a_pos)
|
|
420
|
+
n_b = len(b_pos)
|
|
421
|
+
n_v = len(data)
|
|
422
|
+
out = np.zeros((n_a, n_b, n_v), dtype=FC.DTYPE)
|
|
423
|
+
for vi, (v, d) in enumerate(data.items()):
|
|
424
|
+
out[:, :, vi] = d.reshape(n_a, n_b)
|
|
425
|
+
return out
|
|
426
|
+
|
|
427
|
+
def np2np_sp(data, states, a_pos, b_pos):
|
|
428
|
+
"""
|
|
429
|
+
Create numpy data from numpy data
|
|
430
|
+
|
|
431
|
+
Parameters
|
|
432
|
+
----------
|
|
433
|
+
data: dict
|
|
434
|
+
The data on the grid. Key: variable name,
|
|
435
|
+
value: numpy.ndarray with shape (n_states, n_gpts)
|
|
436
|
+
states: numpy.ndarray
|
|
437
|
+
The states index, shape: (n_states,)
|
|
438
|
+
a_pos: numpy.ndarray
|
|
439
|
+
The first axis coordinates, e.g. x_pos,
|
|
440
|
+
shape: (n_a,)
|
|
441
|
+
b_pos: numpy.ndarray
|
|
442
|
+
The second axis coordinates, e.g. y_pos,
|
|
443
|
+
shape: (n_b,)
|
|
444
|
+
|
|
445
|
+
Returns
|
|
446
|
+
-------
|
|
447
|
+
out: dict
|
|
448
|
+
The data on the grid. Key: variable name,
|
|
449
|
+
value: numpy.ndarray with shape (n_states, n_a, n_b, n_vars)
|
|
450
|
+
|
|
451
|
+
"""
|
|
452
|
+
n_s = len(states)
|
|
453
|
+
n_a = len(a_pos)
|
|
454
|
+
n_b = len(b_pos)
|
|
455
|
+
n_v = len(data)
|
|
456
|
+
out = np.zeros((n_s, n_a, n_b, n_v), dtype=FC.DTYPE)
|
|
457
|
+
for vi, (v, d) in enumerate(data.items()):
|
|
458
|
+
out[:, :, :, vi] = d.reshape(n_s, n_a, n_b)
|
|
459
|
+
return out
|
|
460
|
+
|
|
461
|
+
def np2pd_p(data, a_pos, b_pos, ori, label_map={}):
|
|
462
|
+
"""
|
|
463
|
+
Create pandas DataFrame from numpy data
|
|
464
|
+
|
|
465
|
+
Parameters
|
|
466
|
+
----------
|
|
467
|
+
data: dict
|
|
468
|
+
The data on the grid. Key: variable name,
|
|
469
|
+
value: numpy.ndarray with shape (n_gpts,)
|
|
470
|
+
a_pos: numpy.ndarray
|
|
471
|
+
The first axis coordinates, e.g. x_pos,
|
|
472
|
+
shape: (n_a,)
|
|
473
|
+
b_pos: numpy.ndarray
|
|
474
|
+
The second axis coordinates, e.g. y_pos,
|
|
475
|
+
shape: (n_b,)
|
|
476
|
+
ori: str
|
|
477
|
+
The orientation, 'xy' or 'xz' or 'yz'
|
|
478
|
+
label_map: dict
|
|
479
|
+
The mapping from original to new field names
|
|
480
|
+
|
|
481
|
+
Returns
|
|
482
|
+
-------
|
|
483
|
+
out: pandas.DataFrame
|
|
484
|
+
The multi-indexed DataFrame object
|
|
485
|
+
|
|
486
|
+
"""
|
|
487
|
+
a, b = [label_map.get(o, o) for o in ori]
|
|
488
|
+
n_a = len(a_pos)
|
|
489
|
+
n_b = len(b_pos)
|
|
490
|
+
minds = pd.MultiIndex.from_product([range(n_a), range(n_b)],
|
|
491
|
+
names=[a, b])
|
|
492
|
+
return pd.DataFrame(index=minds, data=data)
|
|
493
|
+
|
|
494
|
+
def np2pd_sp(data, states, a_pos, b_pos, ori, label_map={}):
|
|
495
|
+
"""
|
|
496
|
+
Create pandas DataFrame from numpy data
|
|
497
|
+
|
|
498
|
+
Parameters
|
|
499
|
+
----------
|
|
500
|
+
data: dict
|
|
501
|
+
The data on the grid. Key: variable name,
|
|
502
|
+
value: numpy.ndarray with shape (n_states, n_gpts,)
|
|
503
|
+
states: numpy.ndarray
|
|
504
|
+
The states index, shape: (n_states,)
|
|
505
|
+
a_pos: numpy.ndarray
|
|
506
|
+
The first axis coordinates, e.g. x_pos,
|
|
507
|
+
shape: (n_a,)
|
|
508
|
+
b_pos: numpy.ndarray
|
|
509
|
+
The second axis coordinates, e.g. y_pos,
|
|
510
|
+
shape: (n_b,)
|
|
511
|
+
ori: str
|
|
512
|
+
The orientation, 'xy' or 'xz' or 'yz'
|
|
513
|
+
label_map: dict
|
|
514
|
+
The mapping from original to new field names
|
|
515
|
+
|
|
516
|
+
Returns
|
|
517
|
+
-------
|
|
518
|
+
out: pandas.DataFrame
|
|
519
|
+
The multi-indexed DataFrame object
|
|
520
|
+
|
|
521
|
+
"""
|
|
522
|
+
a, b = [label_map.get(o, o) for o in ori]
|
|
523
|
+
s = label_map.get(FC.STATE, FC.STATE)
|
|
524
|
+
n_a = len(a_pos)
|
|
525
|
+
n_b = len(b_pos)
|
|
526
|
+
minds = pd.MultiIndex.from_product([states, range(n_a), range(n_b)],
|
|
527
|
+
names=[s, a, b])
|
|
528
|
+
return pd.DataFrame(index=minds, data=data)
|
|
529
|
+
|
|
530
|
+
def np2xr_p(data, a_pos, b_pos, c_pos, ori, label_map={}):
|
|
531
|
+
"""
|
|
532
|
+
Create xarray Dataset from numpy data
|
|
533
|
+
|
|
534
|
+
Parameters
|
|
535
|
+
----------
|
|
536
|
+
data: dict
|
|
537
|
+
The data on the grid. Key: variable name,
|
|
538
|
+
value: numpy.ndarray with shape (n_gpts,)
|
|
539
|
+
a_pos: numpy.ndarray
|
|
540
|
+
The first axis coordinates, e.g. x_pos,
|
|
541
|
+
shape: (n_a,)
|
|
542
|
+
b_pos: numpy.ndarray
|
|
543
|
+
The second axis coordinates, e.g. y_pos,
|
|
544
|
+
shape: (n_b,)
|
|
545
|
+
ori: str
|
|
546
|
+
The orientation, 'xy' or 'xz' or 'yz'
|
|
547
|
+
label_map: dict
|
|
548
|
+
The mapping from original to new field names
|
|
549
|
+
|
|
550
|
+
Returns
|
|
551
|
+
-------
|
|
552
|
+
out: xarray.Dataset
|
|
553
|
+
The Dataset object
|
|
554
|
+
|
|
555
|
+
"""
|
|
556
|
+
a, b = [label_map.get(o, o) for o in ori]
|
|
557
|
+
c = list(set("xyz") - set(ori))[0]
|
|
558
|
+
c = label_map.get(c, c)
|
|
559
|
+
n_a = len(a_pos)
|
|
560
|
+
n_b = len(b_pos)
|
|
561
|
+
return Dataset(
|
|
562
|
+
coords={b: b_pos, a: a_pos},
|
|
563
|
+
data_vars={
|
|
564
|
+
v: ((b, a), np.swapaxes(d.reshape(n_a, n_b), 0, 1))
|
|
565
|
+
for v, d in data.items()
|
|
566
|
+
},
|
|
567
|
+
attrs={c: float(c_pos)}
|
|
568
|
+
)
|
|
569
|
+
|
|
570
|
+
def np2xr_sp(data, states, a_pos, b_pos, c_pos, ori, label_map={}):
|
|
571
|
+
"""
|
|
572
|
+
Create xarray Dataset from numpy data
|
|
573
|
+
|
|
574
|
+
Parameters
|
|
575
|
+
----------
|
|
576
|
+
data: dict
|
|
577
|
+
The data on the grid. Key: variable name,
|
|
578
|
+
value: numpy.ndarray with shape (n_states, n_gpts,)
|
|
579
|
+
states: numpy.ndarray
|
|
580
|
+
The states index, shape: (n_states,)
|
|
581
|
+
a_pos: numpy.ndarray
|
|
582
|
+
The first axis coordinates, e.g. x_pos,
|
|
583
|
+
shape: (n_a,)
|
|
584
|
+
b_pos: numpy.ndarray
|
|
585
|
+
The second axis coordinates, e.g. y_pos,
|
|
586
|
+
shape: (n_b,)
|
|
587
|
+
ori: str
|
|
588
|
+
The orientation, 'xy' or 'xz' or 'yz'
|
|
589
|
+
label_map: dict
|
|
590
|
+
The mapping from original to new field names
|
|
591
|
+
|
|
592
|
+
Returns
|
|
593
|
+
-------
|
|
594
|
+
out: xarray.Dataset
|
|
595
|
+
The Dataset object
|
|
596
|
+
|
|
597
|
+
"""
|
|
598
|
+
a, b = [label_map.get(o, o) for o in ori]
|
|
599
|
+
c = list(set("xyz") - set(ori))[0]
|
|
600
|
+
c = label_map.get(c, c)
|
|
601
|
+
s = label_map.get(FC.STATE, FC.STATE)
|
|
602
|
+
n_s = len(states)
|
|
603
|
+
n_a = len(a_pos)
|
|
604
|
+
n_b = len(b_pos)
|
|
605
|
+
return Dataset(
|
|
606
|
+
coords={s: states, b: b_pos, a: a_pos},
|
|
607
|
+
data_vars={
|
|
608
|
+
v: ((s, b, a), np.swapaxes(d.reshape(n_s, n_a, n_b), 1, 2))
|
|
609
|
+
for v, d in data.items()
|
|
610
|
+
},
|
|
611
|
+
attrs={c: float(c_pos)}
|
|
612
|
+
)
|
|
613
|
+
|
|
614
|
+
def data2xr(
|
|
615
|
+
x_pos,
|
|
616
|
+
y_pos,
|
|
617
|
+
z_pos,
|
|
618
|
+
point_results,
|
|
619
|
+
vars=None,
|
|
620
|
+
state_mean=False,
|
|
621
|
+
to_file=None,
|
|
622
|
+
**kwargs,
|
|
623
|
+
):
|
|
624
|
+
"""
|
|
625
|
+
Converts the image data to xarray data
|
|
626
|
+
|
|
627
|
+
Parameter
|
|
628
|
+
---------
|
|
629
|
+
x_pos: numpy.ndarray or float
|
|
630
|
+
The x grid positions, shape: (n_x, 3)
|
|
631
|
+
y_pos: numpy.ndarray or float
|
|
632
|
+
The y grid positions, shape: (n_y, 3)
|
|
633
|
+
z_pos: numpy.ndarray or float
|
|
634
|
+
The z grid positions, shape: (n_z, 3)
|
|
635
|
+
point_results: xarray.Dataset
|
|
636
|
+
Results of calc_points
|
|
637
|
+
vars: list of str, optional
|
|
638
|
+
Variable selection, or None for all
|
|
639
|
+
state_mean: numpy.ndarray or bool
|
|
640
|
+
Computes mean over states, optionally with
|
|
641
|
+
given weights
|
|
642
|
+
round: dict, optional
|
|
643
|
+
Round variables to given digits, or 'auto'
|
|
644
|
+
for default
|
|
645
|
+
to_file: str, optional
|
|
646
|
+
Write to nc file
|
|
647
|
+
kwargs: dict, optional
|
|
648
|
+
Additional parameters for write_nc
|
|
649
|
+
|
|
650
|
+
Returns
|
|
651
|
+
-------
|
|
652
|
+
ds: xarray.Dataset
|
|
653
|
+
The xarray data object
|
|
654
|
+
|
|
655
|
+
"""
|
|
656
|
+
if vars is None:
|
|
657
|
+
vars = list(point_results.data_vars.keys())
|
|
658
|
+
data = {}
|
|
659
|
+
for v in vars:
|
|
660
|
+
if isinstance(state_mean, np.ndarray):
|
|
661
|
+
data[v] = np.einsum('sp,s->p', point_results[v].to_numpy(), state_mean)
|
|
662
|
+
elif state_mean:
|
|
663
|
+
data[v] = np.mean(point_results[v].to_numpy(), axis=0)
|
|
664
|
+
else:
|
|
665
|
+
data[v] = point_results[v].to_numpy()
|
|
666
|
+
|
|
667
|
+
allc = [x_pos, y_pos, z_pos]
|
|
668
|
+
allcn = ["x", "y", "z"]
|
|
669
|
+
ci = [i for i, x in enumerate(allc) if isinstance(x, np.ndarray)]
|
|
670
|
+
cj = [i for i in range(3) if i not in ci][0]
|
|
671
|
+
cl = [len(allc[i]) for i in ci]
|
|
672
|
+
cn = list(reversed([allcn[i] for i in ci]))
|
|
673
|
+
|
|
674
|
+
coords = {}
|
|
675
|
+
attrs = {allcn[cj]: allc[cj].to_numpy()}
|
|
676
|
+
if (
|
|
677
|
+
FC.STATE in point_results.coords and
|
|
678
|
+
isinstance(state_mean, bool)
|
|
679
|
+
and not state_mean
|
|
680
|
+
):
|
|
681
|
+
if point_results.dims[FC.STATE] > 1:
|
|
682
|
+
coords[FC.STATE] = point_results[FC.STATE].to_numpy()
|
|
683
|
+
else:
|
|
684
|
+
attrs[FC.STATE] = str(point_results[FC.STATE][0].to_numpy())
|
|
685
|
+
coords.update({
|
|
686
|
+
allcn[i]: allc[i] for i in reversed(ci)
|
|
687
|
+
})
|
|
688
|
+
|
|
689
|
+
dvars = {}
|
|
690
|
+
for v, d in data.items():
|
|
691
|
+
if len(d.shape) == 1:
|
|
692
|
+
dvars[v] = (cn, np.swapaxes(d.reshape(*cl), 0, 1))
|
|
693
|
+
else:
|
|
694
|
+
dvars[v] = ([FC.STATE] + cn, np.swapaxes(d.reshape(d.shape[0], *cl), 1, 2))
|
|
695
|
+
|
|
696
|
+
ds = Dataset(
|
|
697
|
+
coords=coords,
|
|
698
|
+
data_vars=dvars,
|
|
699
|
+
attrs=attrs
|
|
700
|
+
)
|
|
701
|
+
|
|
702
|
+
if to_file is not None:
|
|
703
|
+
write_nc(ds, to_file, **kwargs)
|
|
704
|
+
|
|
705
|
+
return ds
|