• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MHKiT-Software / MHKiT-Python / 6712413055

31 Oct 2023 09:02PM UTC coverage: 88.439%. First build
6712413055

Pull #271

github

web-flow
Merge 247f67410 into 007b845e2
Pull Request #271: D3d

25 of 32 new or added lines in 1 file covered. (78.13%)

8430 of 9532 relevant lines covered (88.44%)

7.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.97
/mhkit/river/io/d3d.py
1
from mhkit.utils import unorm
8✔
2
import scipy.interpolate as interp
8✔
3
import numpy as np
8✔
4
import pandas as pd
8✔
5
import xarray as xr
8✔
6
import netCDF4
8✔
7
import warnings
8✔
8

9

10
def get_all_time(data):
8✔
11
    '''
12
    Returns all of the time stamps from a D3D simulation passed to the function 
13
    as a NetCDF object (data)
14
    
15
    Parameters
16
    ----------
17
    data: NetCDF4 object 
18
       A NetCDF4 object that contains spatial data, e.g. velocity or shear
19
       stress generated by running a Delft3D model.  
20

21
    Returns
22
    -------
23
    seconds_run: array
24
        Returns an array of integers representing the number of seconds after
25
        the simulation started and that the data object contains a snapshot of
26
        simulation conditions at that time.
27
    '''
28
    
29
    assert type(data)== netCDF4._netCDF4.Dataset, 'data must be NetCDF4 object' 
8✔
30

31
    seconds_run = np.ma.getdata(data.variables['time'][:], False)
8✔
32

33
    return seconds_run
8✔
34

35

36
def index_to_seconds(data, time_index):
8✔
37
    '''
38
    The function will return 'seconds_run' if passed a 'time_index' 
39

40
    Parameters
41
    ----------
42
    data: NetCDF4 object 
43
       A NetCDF4 object that contains spatial data, e.g. velocity or shear
44
       stress, generated by running a Delft3D model.  
45
    time_index: int 
46
        A positive integer to pull the time index from the dataset. 0 being closest
47
        to time 0. Default is last time index -1.
48

49
    Returns
50
    -------
51
    seconds_run: int, float
52
        The 'seconds_run' is the seconds corresponding to the 'time_index' increments.
53
    '''
54
    return _convert_time(data, time_index=time_index)
8✔
55

56

57
def seconds_to_index(data, seconds_run):
8✔
58
    '''
59
    The function will return the nearest 'time_index' in the data if passed an
60
    integer number of 'seconds_run'
61
    
62
    Parameters
63
    ----------
64
    data: NetCDF4 object 
65
        A NetCDF4 object that contains spatial data, e.g. velocity or shear
66
        stress, generated by running a Delft3D model.  
67
    seconds_run: int, float
68
        A positive integer or float that represents the amount of time in seconds 
69
        passed since starting the simulation.
70

71
    Returns
72
    -------
73
    time_index: int
74
        The 'time_index' is a positive integer starting from 0 
75
        and incrementing until in simulation is complete.
76
    '''
77
    return _convert_time(data, seconds_run=seconds_run)
8✔
78

79

80
def _convert_time(data, time_index=None, seconds_run=None):
8✔
81
    '''
82
    Converts a time index to seconds or seconds to a time index. The user 
83
    must specify 'time_index' or 'seconds_run' (Not both). The function 
84
    will returns 'seconds_run' if passed a 'time_index' or will return the 
85
    closest 'time_index' if passed a number of 'seconds_run'.
86

87
    Parameters
88
    ----------
89
    data: NetCDF4 object 
90
       A NetCDF4 object that contains spatial data, e.g. velocity or shear
91
       stress, generated by running a Delft3D model.  
92
    time_index: int 
93
        An integer to pull the time index from the dataset. 0 being closest
94
        to the start time. 
95
    seconds_run: int, float
96
        An integer or float that represents the amount of time in seconds 
97
        passed since starting the simulation.
98

99
    Returns
100
    -------
101
    QoI: int, float
102
        The quantity of interest is the unknown value either the 'time_index' 
103
        or the 'seconds_run'. The 'time_index' is an integer starting from 0 
104
        and incrementing until in simulation is complete. The 'seconds_run' is
105
        the seconds corresponding to the 'time_index' increments.
106
    '''
107
    
108
    assert type(data)== netCDF4._netCDF4.Dataset, 'data must be NetCDF4 object'
8✔
109
    assert time_index or seconds_run, 'input of time_index or seconds_run needed'
8✔
110
    assert not(time_index and seconds_run), f'only one time_index or seconds_run'
8✔
111
    assert isinstance(time_index, (int, float)) or isinstance(seconds_run, (int,
8✔
112
            float)),'time_index or seconds_run input must be a int or float'
113
   
114
    times = get_all_time(data)
8✔
115
    
116
    if time_index:
8✔
117
        QoI= times[time_index]
8✔
118
    if seconds_run:
8✔
119
        try: 
8✔
120
            idx=np.where(times == seconds_run)
8✔
121
            QoI=idx[0][0]
8✔
122
        except: 
8✔
123
            idx = (np.abs(times - seconds_run)).argmin()
8✔
124
            QoI= idx
8✔
125
            warnings.warn( f'Warning: seconds_run not found. Closest time stamp' 
8✔
126
                          +'found {times[idx]}', stacklevel= 2)
127

128
    return QoI
8✔
129

130

131
def get_layer_data(data, variable, layer_index=-1, time_index=-1):
8✔
132
    '''
133
    Get variable data from the NetCDF4 object at a specified layer and timestep. 
134
    If the data is 2D the layer_index is ignored.
135

136
    Parameters
137
    ----------
138
    data: NetCDF4 object
139
       A NetCDF4 object that contains spatial data, e.g. velocity or shear
140
       stress, generated by running a Delft3D model.
141
    variable: string
142
        Delft3D outputs many vairables. The full list can be
143
        found using "data.variables.keys()" in the console. 
144
    layer_index: int
145
         An integer to pull out a layer from the dataset. 0 being closest 
146
         to the surface. Default is the bottom layer, found with input -1.
147
    time_index: int 
148
        An integer to pull the time index from the dataset. 0 being closest
149
        to the start time. Default is last time index, found with input -1.
150
    Returns
151
    -------
152
    layer_data: DataFrame
153
        DataFrame with columns of "x", "y", "waterdepth", and "waterlevel" location
154
        of the specified layer, variable values "v", and the "time" the 
155
        simulation has run. The waterdepth is measured from the water surface and the
156
        "waterlevel" is the water level diffrencein meters from the zero water level. 
157
    '''
158
    
159
    assert isinstance(time_index, int), 'time_index  must be an int'
8✔
160
    assert isinstance(layer_index, int), 'layer_index  must be an int'
8✔
161
    assert type(data)== netCDF4._netCDF4.Dataset, 'data must be NetCDF4 object'
8✔
162
    assert variable in data.variables.keys(), 'variable not recognized'
8✔
163
    coords = str(data.variables[variable].coordinates).split()
8✔
164
    var=data.variables[variable][:]
8✔
165
    max_time_index= data['time'].shape[0]-1 # to account for zero index
8✔
166
    assert abs(time_index) <= max_time_index, (f'time_index must be less than'
8✔
167
                  +'the absolute value of the max time index {max_time_index}')
168

169
    x=np.ma.getdata(data.variables[coords[0]][:], False) 
8✔
170
    y=np.ma.getdata(data.variables[coords[1]][:], False)
8✔
171
    
172
    
173
    if type(var[0][0]) == np.ma.core.MaskedArray: 
8✔
174
        max_layer= len(var[0][0])
8✔
175
        
176
        assert abs(layer_index) <= max_layer,( f'layer_index must be less than'
8✔
177
                                              +'the max layer {max_layer}')
178
        v= np.ma.getdata(var[time_index,:,layer_index], False)
8✔
179
        dimensions= 3
8✔
180
    
181
    else: 
182
        assert type(var[0][0])== np.float64, 'data not  recognized'
8✔
183
        dimensions= 2
8✔
184
        v= np.ma.getdata(var[time_index,:], False)
8✔
185
        
186
    #waterdepth 
187
    if "mesh2d" in variable:
8✔
188
        cords_to_layers= {'mesh2d_face_x mesh2d_face_y': {'name':'mesh2d_nLayers', 
×
189
                                'coords':data.variables['mesh2d_layer_sigma'][:]},
190
                       'mesh2d_edge_x mesh2d_edge_y': {'name':'mesh2d_nInterfaces', 
191
                                'coords':data.variables['mesh2d_interface_sigma'][:]}}
192
        bottom_depth=np.ma.getdata(data.variables['mesh2d_waterdepth'][time_index, :], False)
×
193
        waterlevel= np.ma.getdata(data.variables['mesh2d_s1'][time_index, :], False)
×
194
        coords = str(data.variables['waterdepth'].coordinates).split()
×
195

196
        
197
        
198
    elif str(data.variables[variable].coordinates)=='FlowElem_xcc FlowElem_ycc':
8✔
199
            cords_to_layers= {'FlowElem_xcc FlowElem_ycc':
8✔
200
                                  {'name':'laydim', 
201
                                    'coords':data.variables['LayCoord_cc'][:]},
202
                               'FlowLink_xu FlowLink_yu': {'name':'wdim', 
203
                                       'coords':data.variables['LayCoord_w'][:]}}
204
            bottom_depth=np.ma.getdata(data.variables['waterdepth'][time_index, :], False)
8✔
205
            waterlevel= np.ma.getdata(data.variables['s1'][time_index, :], False)
8✔
206
            coords = str(data.variables['waterdepth'].coordinates).split()
8✔
207
    else:
208
            cords_to_layers= {'FlowElem_xcc FlowElem_ycc LayCoord_cc LayCoord_cc':
8✔
209
                                      {'name':'laydim', 
210
                                        'coords':data.variables['LayCoord_cc'][:]},
211
                                   'FlowLink_xu FlowLink_yu': {'name':'wdim', 
212
                                           'coords':data.variables['LayCoord_w'][:]}}
213
            bottom_depth=np.ma.getdata(data.variables['waterdepth'][time_index, :], False)
8✔
214
            waterlevel= np.ma.getdata(data.variables['s1'][time_index, :], False)
8✔
215
            coords = str(data.variables['waterdepth'].coordinates).split()
8✔
216
        
217
    layer_dim =  str(data.variables[variable].coordinates)
8✔
218
    
219
    cord_sys= cords_to_layers[layer_dim]['coords']
8✔
220
    layer_percentages= np.ma.getdata(cord_sys, False) #accumulative
8✔
221

222
    if layer_dim == 'FlowLink_xu FlowLink_yu':
8✔
223
        #interpolate 
224
        x_laydim=np.ma.getdata(data.variables[coords[0]][:], False) 
8✔
225
        y_laydim=np.ma.getdata(data.variables[coords[1]][:], False)
8✔
226
        points_laydim = np.array([ [x, y] for x, y in zip(x_laydim, y_laydim)])
8✔
227
        
228
        coords_request = str(data.variables[variable].coordinates).split()
8✔
229
        x_wdim=np.ma.getdata(data.variables[coords_request[0]][:], False) 
8✔
230
        y_wdim=np.ma.getdata(data.variables[coords_request[1]][:], False)
8✔
231
        points_wdim=np.array([ [x, y] for x, y in zip(x_wdim, y_wdim)])
8✔
232
        
233
        bottom_depth_wdim = interp.griddata(points_laydim, bottom_depth,
8✔
234
                                            points_wdim)
235
        water_level_wdim= interp.griddata(points_laydim, waterlevel,
8✔
236
                                            points_wdim)
237
        
238
        idx_bd= np.where(np.isnan(bottom_depth_wdim))
8✔
239
        
240
        for i in idx_bd: 
8✔
241
            bottom_depth_wdim[i]= interp.griddata(points_laydim, bottom_depth,
8✔
242
                                              points_wdim[i], method='nearest')
243
            water_level_wdim[i]= interp.griddata(points_laydim, waterlevel,
8✔
244
                                              points_wdim[i], method='nearest')
245

246
    
247
    waterdepth=[]
8✔
248
    
249
    if dimensions== 2:
8✔
250
        if layer_dim == 'FlowLink_xu FlowLink_yu': 
8✔
251
            z = [bottom_depth_wdim]
×
252
            waterlevel=water_level_wdim
×
253
        else:
254
            z = [bottom_depth]
8✔
255
    else:
256
        if layer_dim == 'FlowLink_xu FlowLink_yu': 
8✔
257
            z = [bottom_depth_wdim*layer_percentages[layer_index]]
8✔
258
            waterlevel=water_level_wdim
8✔
259
        else:
260
            z = [bottom_depth*layer_percentages[layer_index]]
8✔
261
    waterdepth=np.append(waterdepth, z)
8✔
262

263
    time= np.ma.getdata(data.variables['time'][time_index], False)*np.ones(len(x))
8✔
264

265
    layer= np.array([ [x_i, y_i, d_i, w_i, v_i, t_i] for x_i, y_i, d_i, w_i, v_i, t_i in
8✔
266
                     zip(x, y, waterdepth, waterlevel, v, time)]) 
267
    layer_data = pd.DataFrame(layer, columns=['x', 'y', 'waterdepth','waterlevel', 'v', 'time'])
8✔
268

269
    return layer_data
8✔
270

271

272
def create_points(x, y, waterdepth):
8✔
273
    '''
274
    Turns three coordinate inputs into a single output DataFrame of points. 
275
    In any order the three inputs can consist of 3 points, 2 points and 1 array,
276
    or 1 point and 2 arrays or 3 arrays. The final output DataFrame will be the unique
277
    combinations of the 3 inputs. 
278
    
279
    Parameters
280
    ----------
281
    x: float, array or int 
282
        x values to create points.
283
    y: float, array or int
284
        y values to create points.
285
    waterdepth: float, array or int
286
        waterdepth values to create points.
287

288
    Returns
289
    -------
290
    points: DateFrame 
291
        DataFrame with columns x, y and waterdepth points. 
292
        
293
    Example 
294
    -------
295
    If the inputs are 2 arrays:  and [3,4,5] and 1 point [6], the output 
296
    will contain 6 array combinations of the 3 inputs as shown.
297
    
298
    x=np.array([1,2])
299
    y=np.array([3,4,5])
300
    waterdepth= 6
301
    d3d.create_points(x,y,waterdepth)
302
    
303
       x    y    waterdepth
304
    0  1.0  3.0  6.0
305
    1  2.0  3.0  6.0
306
    2  1.0  4.0  6.0
307
    3  2.0  4.0  6.0
308
    4  1.0  5.0  6.0
309
    5  2.0  5.0  6.0        
310
    '''
311
    
312
    assert isinstance(x, (int, float, np.ndarray, pd.Series, xr.DataArray)), ('x' 
8✔
313
                                     +'must be a int, float array or series')
314
    assert isinstance(y, (int, float, np.ndarray,pd.Series, xr.DataArray)), ('y'
8✔
315
                                     +' must be a int, float array or series')
316
    assert isinstance(waterdepth, (int, float, np.ndarray, pd.Series, 
8✔
317
            xr.DataArray)), ('waterdepth must be a int, float array or series')
318
    
319
    directions = {0:{'name':  'x',
8✔
320
                     'values': x},
321
                  1:{'name':  'y',
322
                     'values': y},
323
                  2:{'name':  'waterdepth',
324
                     'values': waterdepth}}
325

326
    for i in directions:
8✔
327
        try:
8✔
328
            N=len(directions[i]['values'])
8✔
329
        except:
8✔
330
            directions[i]['values'] = np.array([directions[i]['values']]) 
8✔
331
            N=len(directions[i]['values'])  
8✔
332
        if N == 1 :
8✔
333
            directions[i]['type']= 'point'      
8✔
334
        elif N > 1 :
8✔
335
            directions[i]['type']= 'array'
8✔
336
        else:
337
            raise Exception(f'length of direction {directions[i]["name"]} was'
×
338
                            +'neagative or zero')
339
    
340
    # Check how many times point is in "types" 
341
    types= [directions[i]['type'] for i in directions]
8✔
342
    N_points = types.count('point')
8✔
343

344
    if N_points >= 2:
8✔
345
        lens = np.array([len(directions[d]['values'])  for d in directions])
8✔
346
        max_len_idx = lens.argmax()
8✔
347
        not_max_idxs= [i for i in directions.keys()]
8✔
348
        
349
        del not_max_idxs[max_len_idx]
8✔
350
        
351
        for not_max in not_max_idxs:     
8✔
352
            N= len(directions[max_len_idx]['values'])
8✔
353
            vals =np.ones(N)*directions[not_max]['values']
8✔
354
            directions[not_max]['values'] = np.array(vals)
8✔
355
                    
356
        x_new = directions[0]['values']
8✔
357
        y_new = directions[1]['values']
8✔
358
        depth_new = directions[2]['values']
8✔
359
            
360
        request= np.array([ [x_i, y_i, depth_i] for x_i, y_i, depth_i in zip(x_new, 
8✔
361
                                                             y_new, depth_new)]) 
362
        points= pd.DataFrame(request, columns=[ 'x', 'y', 'waterdepth'])
8✔
363
        
364
    elif N_points == 1: 
8✔
365
        # treat as plane
366
        #find index of point 
367
        idx_point = types.index('point')
8✔
368
        max_idxs= [i for i in directions.keys()]
8✔
369
        print(max_idxs)
8✔
370
        del max_idxs[idx_point]
8✔
371
        #find vectors 
372
        XX, YY = np.meshgrid(directions[max_idxs[0]]['values'],
8✔
373
                             directions[max_idxs[1]]['values'] )
374
        N_X=np.shape(XX)[1]
8✔
375
        N_Y=np.shape(YY)[0]
8✔
376
        ZZ= np.ones((N_Y,N_X))*directions[idx_point]['values'] 
8✔
377
     
378
        request= np.array([ [x_i, y_i, z_i] for x_i, y_i, z_i in zip(XX.ravel(),
8✔
379
                            YY.ravel() , ZZ.ravel())]) 
380
        columns=[ directions[max_idxs[0]]['name'],  
8✔
381
                 directions[max_idxs[1]]['name'],  directions[idx_point]['name']]
382
        
383
        points= pd.DataFrame(request, columns=columns)
8✔
NEW
384
    elif N_points == 0: 
×
NEW
385
        assert len(directions[0]['values']) == len(directions[1]['values']), ('X'
×
386
             +'and Y must be the same length if you are inputing three arrays')
NEW
387
        x_points = np.tile(directions[0]['values'], len(directions[2]['values']))
×
NEW
388
        y_points = np.tile(directions[1]['values'], len(directions[2]['values']))
×
NEW
389
        depth_points = np.repeat(directions[2]['values'], len(directions[0]['values']))
×
390

NEW
391
        request={
×
392
                directions[0]['name']: x_points, 
393
                directions[1]['name']: y_points, 
394
                directions[2]['name']: depth_points
395
                }
396

NEW
397
        points= pd.DataFrame(request)
×
398
    else: 
399
        raise Exception('Can provide at most two arrays')
×
400

401
    return points 
8✔
402

403

404
def variable_interpolation(data, variables, points='cells', edges= 'none', 
8✔
405
                           x_max_lim= float('inf'), x_min_lim= float('-inf'),
406
                           y_max_lim= float('inf'), y_min_lim= float('-inf')):
407
    '''
408
    Interpolate multiple variables from the Delft3D onto the same points. 
409

410
    Parameters
411
    ----------
412
    data: NetCDF4 object 
413
       A NetCDF4 object that contains spatial data, e.g. velocity or shear
414
       stress generated by running a Delft3D model.  
415
    variables: array of strings
416
        Name of variables to interpolate, e.g. 'turkin1', 'ucx', 'ucy' and 'ucz'.
417
        The full list can be found using "data.variables.keys()" in the console.
418
    points: string, DataFrame  
419
        The points to interpolate data onto.
420
          'cells'- interpolates all data onto the Delft3D cell coordinate system (Default)
421
          'faces'- interpolates all dada onto the Delft3D face coordinate system 
422
          DataFrame of x, y, and waterdepth coordinates - Interpolates data onto user 
423
          povided points. Can be created with `create_points` function.
424
    edges: sting: 'nearest'
425
        If edges is set to 'nearest' the code will fill in nan values with nearest 
426
        interpolation. Otherwise only linear interpolarion will be used. 
427
  
428
    Returns
429
    -------
430
    transformed_data: DataFrame  
431
        Variables on specified grid points saved under the input variable names 
432
        and the x, y, and waterdepth coordinates of those points. 
433
    '''
434
    
435
    assert isinstance(points, (str, pd.DataFrame)),('points must be a string ' 
8✔
436
                    +'or DataFrame')
437
    if isinstance ( points, str):
8✔
438
       assert any([points == 'cells', points=='faces']), ('points must be'
8✔
439
                                                          +' cells or faces')
440
    assert type(data)== netCDF4._netCDF4.Dataset, 'data must be nerCDF4 object'
8✔
441

442
    data_raw = {}
8✔
443
    for var in variables:
8✔
444
        var_data_df = get_all_data_points(data, var,time_index=-1)  
8✔
445
        var_data_df['depth']=var_data_df.waterdepth-var_data_df.waterlevel #added
8✔
446
        var_data_df=var_data_df.loc[:,~var_data_df.T.duplicated(keep='first')]
8✔
447
        var_data_df=var_data_df[var_data_df.x > x_min_lim]
8✔
448
        var_data_df=var_data_df[var_data_df.x < x_max_lim] 
8✔
449
        var_data_df=var_data_df[var_data_df.y > y_min_lim]
8✔
450
        var_data_df=var_data_df[var_data_df.y < y_max_lim]
8✔
451
        data_raw[var] = var_data_df 
8✔
452
    if type(points) == pd.DataFrame:  
8✔
453
        print('points provided')
8✔
454
    elif points=='faces':
8✔
455
        points = data_raw['ucx'][['x','y','waterdepth']]
8✔
456
    elif points=='cells':
8✔
457
        points = data_raw['turkin1'][['x','y','waterdepth']]
8✔
458
    
459
    transformed_data= points.copy(deep=True)
8✔
460
    
461
    for var in variables :    
8✔
462
        transformed_data[var] = interp.griddata(data_raw[var][['x','y','waterdepth']], #waterdepth to depth 
8✔
463
                                        data_raw[var][var], points[['x','y','waterdepth']])
464
        if edges == 'nearest' :
8✔
465
            idx= np.where(np.isnan(transformed_data[var]))
8✔
466
        
467
            if len(idx[0]):
8✔
468
                for i in idx[0]: 
8✔
469
                    transformed_data[var][i]= (interp
8✔
470
                                              .griddata(data_raw[var][['x','y','waterdepth']], 
471
                                                data_raw[var][var],
472
                                                [points['x'][i],points['y'][i],
473
                                                points['waterdepth'][i]], method='nearest'))
474
     
475
    return transformed_data
8✔
476

477

478
def get_all_data_points(data, variable, time_index=-1):  
8✔
479
    '''
480
    Get data points for a passed variable for all layers at a specified time from 
481
    the Delft3D NetCDF4 object by iterating over the `get_layer_data` function. 
482

483
    Parameters
484
    ----------
485
    data: Netcdf4 object 
486
       A NetCDF4 object that contains spatial data, e.g. velocity or shear
487
       stress, generated by running a Delft3D model.  
488
    variable: string
489
        Delft3D variable. The full list can be of variables can be
490
        found using "data.variables.keys()" in the console. 
491
    time_index: int
492
        An integer to pull the time step from the dataset. 
493
        Default is last time step, found with the input -1.
494
        
495
    Returns
496
    -------
497
    all_data: DataFrame 
498
        Dataframe with columns x, y, waterdepth, waterlevel, variable, and time.
499
        The waterdepth is measured from the water surface and the "waterlevel" is 
500
        the water level diffrence in meters from the zero water level.
501
 
502
    '''  
503
    
504
    assert isinstance(time_index, int), 'time_index  must be a int'
8✔
505
    assert type(data)== netCDF4._netCDF4.Dataset, 'data must be NetCDF4 object'
8✔
506
    assert variable in data.variables.keys(), 'variable not recognized'
8✔
507

508
    max_time_index = len(data.variables[variable][:])
8✔
509
    assert abs(time_index) <= max_time_index, (f'time_index must be less than'
8✔
510
                                      +'the max time index {max_time_index}')
511

512
    if "mesh2d" in variable:
8✔
513
        cords_to_layers= {'mesh2d_face_x mesh2d_face_y': {'name':'mesh2d_nLayers', 
×
514
                                'coords':data.variables['mesh2d_layer_sigma'][:]},
515
                       'mesh2d_edge_x mesh2d_edge_y': {'name':'mesh2d_nInterfaces', 
516
                                'coords':data.variables['mesh2d_interface_sigma'][:]}}
517
        
518
    elif str(data.variables[variable].coordinates)=='FlowElem_xcc FlowElem_ycc':
8✔
519
        cords_to_layers= {'FlowElem_xcc FlowElem_ycc':
8✔
520
                              {'name':'laydim', 
521
                                'coords':data.variables['LayCoord_cc'][:]},
522
                           'FlowLink_xu FlowLink_yu': {'name':'wdim', 
523
                                   'coords':data.variables['LayCoord_w'][:]}}
524
    else:
525
        cords_to_layers= {'FlowElem_xcc FlowElem_ycc LayCoord_cc LayCoord_cc':
8✔
526
                                  {'name':'laydim', 
527
                                    'coords':data.variables['LayCoord_cc'][:]},
528
                               'FlowLink_xu FlowLink_yu': {'name':'wdim', 
529
                                       'coords':data.variables['LayCoord_w'][:]}}
530

531
    layer_dim =  str(data.variables[variable].coordinates)
8✔
532
    
533
    try:    
8✔
534
        cord_sys= cords_to_layers[layer_dim]['coords']
8✔
535
    except: 
×
536
        raise Exception('Coordinates not recognized.')
×
537
    else: 
538
        layer_percentages= np.ma.getdata(cord_sys, False) 
8✔
539
        
540
    x_all=[]
8✔
541
    y_all=[]
8✔
542
    depth_all=[]
8✔
543
    water_level_all=[]
8✔
544
    v_all=[]
8✔
545
    time_all=[]
8✔
546
    
547
    layers = range(len(layer_percentages))
8✔
548
    for layer in layers:
8✔
549
        layer_data= get_layer_data(data, variable, layer, time_index)
8✔
550

551
        x_all=np.append(x_all, layer_data.x)
8✔
552
        y_all=np.append(y_all, layer_data.y)
8✔
553
        depth_all=np.append(depth_all, layer_data.waterdepth)
8✔
554
        water_level_all=np.append(water_level_all, layer_data.waterlevel)
8✔
555
        v_all=np.append(v_all, layer_data.v)
8✔
556
        time_all= np.append(time_all, layer_data.time)
8✔
557
    
558
    known_points = np.array([ [x, y, waterdepth, waterlevel, v, time] 
8✔
559
                    for x, y, waterdepth, waterlevel, v, time in zip(x_all, y_all, 
560
                                depth_all, water_level_all, v_all, time_all)])
561
    
562
    all_data= pd.DataFrame(known_points, columns=['x','y','waterdepth', 'waterlevel'
8✔
563
                                                  ,f'{variable}', 'time'])
564

565
    return all_data
8✔
566

567

568

569
def turbulent_intensity(data, points='cells', time_index= -1,
8✔
570
                        intermediate_values = False ):
571
    '''
572
    Calculate the turbulent intensity percentage for a given data set for the 
573
    specified points. Assumes variable names: ucx, ucy, ucz and turkin1.
574

575
    Parameters
576
    ----------
577
    data: NetCDF4 object 
578
       A NetCDF4 object that contains spatial data, e.g. velocity or shear
579
       stress, generated by running a Delft3D model.
580
    points: string, DataFrame  
581
        Points to interpolate data onto. 
582
          'cells': interpolates all data onto velocity coordinate system (Default).
583
          'faces': interpolates all data onto the TKE coordinate system.
584
          DataFrame of x, y, and z coordinates: Interpolates data onto user 
585
          provided points. 
586
    time_index: int 
587
        An integer to pull the time step from the dataset. Default is
588
        late time step -1.  
589
    intermediate_values: boolean (optional)
590
        If false the function will return position and turbulent intensity values. 
591
        If true the function will return position(x,y,z) and values need to calculate
592
        turbulent intensity (ucx, uxy, uxz and turkin1) in a Dataframe.  Default False.
593
   
594
    Returns
595
    -------
596
      TI_data: Dataframe
597
        If intermediate_values is true all values are output. 
598
        If intermediate_values is equal to false only turbulent_intesity and 
599
        x, y, and z variables are output.  
600
            x- position in the x direction 
601
            y- position in the y direction 
602
            waterdepth- position in the vertical direction
603
            turbulet_intesity- turbulent kinetic energy divided by the root
604
                                mean squared velocity
605
            turkin1- turbulent kinetic energy 
606
            ucx- velocity in the x direction 
607
            ucy- velocity in the y direction 
608
            ucz- velocity in the vertical direction 
609
    '''
610
    
611
    assert isinstance(points, (str, pd.DataFrame)),('points must a string or'
8✔
612
                                                    +' DataFrame')
613
    if isinstance ( points, str):
8✔
614
       assert any([points == 'cells', points=='faces']), ('points must be cells'
8✔
615
                                                          +' or faces')
616
    assert isinstance(time_index, int), 'time_index  must be a int'
8✔
617
    max_time_index= data['time'].shape[0]-1 # to account for zero index
8✔
618
    assert abs(time_index) <= max_time_index, (f'time_index must be less than'
8✔
619
                  +'the absolute value of the max time index {max_time_index}')
620
    assert type(data)== netCDF4._netCDF4.Dataset, 'data must be nerCDF4 object'
8✔
621
    assert 'turkin1' in data.variables.keys(), ('Varaiable turkin1 not'
8✔
622
                                                +' present in Data')
623
    assert 'ucx' in data.variables.keys(),'Varaiable ucx 1 not present in Data'
8✔
624
    assert 'ucy' in data.variables.keys(),'Varaiable ucy 1 not present in Data'
8✔
625
    assert 'ucz' in data.variables.keys(),'Varaiable ucz 1 not present in Data'
8✔
626

627
    TI_vars= ['turkin1', 'ucx', 'ucy', 'ucz']
8✔
628
    TI_data_raw = {}
8✔
629
    for var in TI_vars:
8✔
630
        var_data_df = get_all_data_points(data, var ,time_index)           
8✔
631
        TI_data_raw[var] = var_data_df 
8✔
632
    if type(points) == pd.DataFrame:  
8✔
633
        print('points provided')
8✔
634
    elif points=='faces':
8✔
635
        points = TI_data_raw['turkin1'].drop(['waterlevel','turkin1'],axis=1)
8✔
636
    elif points=='cells':
8✔
637
        points = TI_data_raw['ucx'].drop(['waterlevel','ucx'],axis=1)
8✔
638
       
639
    TI_data = points.copy(deep=True)
8✔
640

641
    for var in TI_vars:    
8✔
642
        TI_data[var] = interp.griddata(TI_data_raw[var][['x','y','waterdepth']],
8✔
643
                                TI_data_raw[var][var], points[['x','y','waterdepth']])
644
        idx= np.where(np.isnan(TI_data[var]))
8✔
645
        
646
        if len(idx[0]):
8✔
647
            for i in idx[0]: 
8✔
648
                TI_data[var][i]= interp.griddata(TI_data_raw[var][['x','y','waterdepth']], 
8✔
649
                                TI_data_raw[var][var],
650
                                [points['x'][i],points['y'][i], points['waterdepth'][i]],
651
                                method='nearest')
652

653
    u_mag=unorm(np.array(TI_data['ucx']),np.array(TI_data['ucy']), 
8✔
654
                np.array(TI_data['ucz']))
655
    
656
    neg_index=np.where( TI_data['turkin1']<0)
8✔
657
    zero_bool= np.isclose( TI_data['turkin1'][ TI_data['turkin1']<0].array, 
8✔
658
               np.zeros(len( TI_data['turkin1'][TI_data['turkin1']<0].array)),
659
               atol=1.0e-4)
660
    zero_ind= neg_index[0][zero_bool]
8✔
661
    non_zero_ind= neg_index[0][~zero_bool]
8✔
662
    TI_data.loc[zero_ind,'turkin1']=np.zeros(len(zero_ind))
8✔
663
    TI_data.loc[non_zero_ind,'turkin1']=[np.nan]*len(non_zero_ind)
8✔
664
        
665
    TI_data['turbulent_intensity']= np.sqrt(2/3*TI_data['turkin1'])/u_mag * 100 #%
8✔
666
    
667
    if intermediate_values == False:
8✔
668
        TI_data= TI_data.drop(TI_vars, axis = 1)
8✔
669
        
670
    return TI_data
8✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc