wawi 0.0.16__py3-none-any.whl → 0.0.18__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.
wawi/plot.py CHANGED
@@ -8,7 +8,42 @@ from scipy.interpolate import RectBivariateSpline, interp1d
8
8
 
9
9
 
10
10
  def plot_ads(ad_dict, v, terms='stiffness', num=None, test_v=dict(), test_ad=dict(), zasso_type=False, ranges=None):
11
- # v: v or K
11
+ """
12
+ Plot aerodynamic derivative (AD) curves for multiple terms and test data.
13
+
14
+ Parameters
15
+ ----------
16
+ ad_dict : dict
17
+ Dictionary mapping term names (e.g., 'P4', 'H6', etc.) to functions that compute aerodynamic derivative values for a given `v`.
18
+ v : array-like
19
+ Array of reduced velocity (or reduced frequency) at which to evaluate the aerodynamic derivative functions.
20
+ terms : str or list of list of str, optional
21
+ Specifies which terms to plot. If 'stiffness' or 'damping', uses predefined groupings. Otherwise, expects a nested list of term names.
22
+ num : int or None, optional
23
+ Figure number for matplotlib. If None, a new figure is created.
24
+ test_v : dict, optional
25
+ Dictionary mapping term names to arrays of test reduced velocity values for overlaying test data.
26
+ test_ad : dict, optional
27
+ Dictionary mapping term names to arrays of test aerodynamic derivative data corresponding to `test_v`.
28
+ zasso_type : bool, optional
29
+ If True, applies special formatting and scaling for Zasso-type plots.
30
+ ranges : dict or None, optional
31
+ Dictionary mapping term names to (min, max) tuples, specifying valid ranges for plotting. Values outside the range are clipped.
32
+
33
+ Returns
34
+ -------
35
+ fig : matplotlib.figure.Figure
36
+ The matplotlib Figure object containing the plots.
37
+ ax : numpy.ndarray of matplotlib.axes.Axes
38
+ Array of Axes objects for each subplot.
39
+
40
+ Notes
41
+ -----
42
+ - The function arranges subplots in a grid according to the structure of `terms`.
43
+ - Each subplot shows the fitted aerodynamic derivative curve and, if provided, test data points.
44
+ - Axis labels and scaling are adjusted depending on `zasso_type` and the term type.
45
+ """
46
+
12
47
  if terms == 'stiffness':
13
48
  terms = [['P4', 'P6', 'P3'], ['H6', 'H4', 'H3'], ['A6', 'A4', 'A3']]
14
49
  elif terms == 'damping':
@@ -68,6 +103,22 @@ def plot_ads(ad_dict, v, terms='stiffness', num=None, test_v=dict(), test_ad=dic
68
103
  return fig, ax
69
104
 
70
105
  def save_plot(pl, path, w=None, h=None):
106
+ '''
107
+ Saves pyvista plotter screenshot to file.
108
+
109
+ Parameters
110
+ ----------
111
+ pl : pyvista.Plotter
112
+ PyVista plotter object.
113
+ path : str
114
+ Path to save the screenshot.
115
+ w : int, optional
116
+ Width of the screenshot. If None, uses the width of the plotter window.
117
+ h : int, optional
118
+ Height of the screenshot. If None, uses the height of the plotter window.
119
+
120
+
121
+ '''
71
122
  ws = pl.window_size
72
123
  if w is not None and h is None:
73
124
  w = int(np.round(w))
@@ -83,22 +134,74 @@ def save_plot(pl, path, w=None, h=None):
83
134
 
84
135
  pl.screenshot(path, window_size=[w,h], return_img=False)
85
136
 
86
- def plot_dir_and_crests(theta0, Tp, arrow_length=100, origin=np.array([0,0]),
87
- ax=None, n_repeats=2, crest_length=1000,
88
- alpha_crests=0.2, arrow_options={}):
89
- arr_opts = {'head_width': 4, 'width':2, 'edgecolor':'none'}
90
- arr_opts.update(**arrow_options)
137
+ def plot_dir_and_crests(theta0, Tp, U=0.0, thetaU=0.0, arrow_length=100, origin=np.array([0,0]), add_text=True,
138
+ ax=None, n_repeats=2, crest_length=1000, arrow_options={}, crest_options={}):
91
139
 
140
+ """
141
+ Plot wave direction arrow and wave crests on a matplotlib axis.
142
+
143
+ Parameters
144
+ ----------
145
+ theta0 : float
146
+ Wave direction in degrees (0 degrees is along the x-axis).
147
+ Tp : float
148
+ Peak wave period in seconds.
149
+ U : float, optional
150
+ Uniform current velocity [m/s]. Default is 0.0.
151
+ thetaU : float, optional
152
+ Angle of current in degrees. Default is 0.0.
153
+ arrow_length : float, optional
154
+ Length of the direction arrow (default is 100).
155
+ origin : np.ndarray, optional
156
+ 2D coordinates of the arrow origin (default is np.array([0, 0])).
157
+ ax : matplotlib.axes.Axes, optional
158
+ Axis to plot on. If None, uses current axis (default is None).
159
+ n_repeats : int, optional
160
+ Number of wave crests to plot (default is 2).
161
+ crest_length : float, optional
162
+ Length of each wave crest line (default is 1000).
163
+ arrow_options : dict, optional
164
+ Additional keyword arguments for the arrow (default is {}).
165
+ crest_options : dict, optional
166
+ Additional keyword arguments for the crest lines (default is {})
167
+ add_text : bool, optional
168
+ Whether or not to add text annotation, default is True.
169
+
170
+
171
+ Returns
172
+ -------
173
+ ax : matplotlib.axes.Axes
174
+ The axis with the plotted wave direction and crests.
175
+
176
+ Notes
177
+ -----
178
+ - Requires `matplotlib.pyplot` as `plt` and `numpy` as `np` to be imported.
179
+ - The function also requires a `get_kappa` function to compute the wavenumber.
180
+
181
+ Docstring generated by GitHub Copilot.
182
+ """
183
+ arrow_options = {'head_width': 4, 'width':2, 'edgecolor':'none'} | arrow_options
184
+ crest_options = {'alpha': 0.2, 'color': 'black'} | crest_options
185
+
92
186
  if ax is None:
93
187
  ax = plt.gca()
94
188
 
95
189
  # Plot wave angle and crests
96
190
  v = np.array([np.cos(theta0*np.pi/180), np.sin(theta0*np.pi/180)])
97
191
  v_norm = np.array([-np.sin(theta0*np.pi/180), np.cos(theta0*np.pi/180)])
98
- wave_length = 2*np.pi/get_kappa(2*np.pi/Tp, U=0.0)
99
192
 
100
- plt.arrow(origin[0],origin[1], arrow_length*v[0], arrow_length*v[1], **arr_opts)
101
- plt.text(origin[0], origin[1], f'$\\theta_0$ = {theta0}$^o$\n $T_p$={Tp} s\n $\\lambda=${wave_length:.0f} m')
193
+ theta_rel_U = (thetaU - theta0)*180/np.pi
194
+ wave_length = 2*np.pi/get_kappa(2*np.pi/Tp, U=U, theta_rel_U=theta_rel_U)
195
+
196
+ plt.arrow(origin[0],origin[1], arrow_length*v[0], arrow_length*v[1], **arrow_options)
197
+
198
+ if add_text:
199
+ plot_string = f'$\\theta_0$ = {theta0}$^o$\n $T_p$={Tp} s\n $\\lambda=${wave_length:.0f} m'
200
+
201
+ if U != 0.0:
202
+ plot_string = plot_string + f'\nU = {U:.1f}m/s @ {thetaU:.1f} deg'
203
+
204
+ plt.text(origin[0], origin[1], plot_string)
102
205
 
103
206
  dv = v*wave_length
104
207
  for n in range(n_repeats):
@@ -106,12 +209,48 @@ def plot_dir_and_crests(theta0, Tp, arrow_length=100, origin=np.array([0,0]),
106
209
  p2 = origin+v_norm*crest_length/2
107
210
  pts = np.vstack([p1,p2])
108
211
 
109
- ax.plot(pts[:,0], pts[:,1], alpha=alpha_crests, color='black', zorder=0)
212
+ ax.plot(pts[:,0], pts[:,1], zorder=0, **crest_options)
110
213
  origin = origin + dv
111
214
 
215
+ ax.axis('equal')
112
216
  return ax
113
217
 
114
218
  def rotate_image_about_pivot(Z, x, y, angle, x0=0, y0=0):
219
+ """
220
+ Rotate an image array about a specified pivot point.
221
+ This function rotates a 2D image array `Z` by a given angle (in degrees) about a pivot point (`x0`, `y0`),
222
+ where the image axes are defined by the coordinate arrays `x` and `y`. The rotation is performed in the
223
+ coordinate space defined by `x` and `y`, not necessarily pixel indices.
224
+
225
+ Parameters
226
+ ----------
227
+ Z : ndarray
228
+ 2D array representing the image to be rotated.
229
+ x : array_like
230
+ 1D array of x-coordinates corresponding to the columns of `Z`.
231
+ y : array_like
232
+ 1D array of y-coordinates corresponding to the rows of `Z`.
233
+ angle : float
234
+ Rotation angle in degrees. Positive values correspond to counter-clockwise rotation.
235
+ x0 : float, optional
236
+ X-coordinate of the pivot point about which to rotate. Default is 0.
237
+ y0 : float, optional
238
+ Y-coordinate of the pivot point about which to rotate. Default is 0.
239
+
240
+ Returns
241
+ -------
242
+ rotated_Z : ndarray
243
+ The rotated image array, with the same shape as `Z`.
244
+
245
+ Notes
246
+ -----
247
+ - The function uses interpolation to map between coordinate space and pixel indices.
248
+ - The rotation is performed about the specified pivot point (`x0`, `y0`) in the coordinate system defined by `x` and `y`.
249
+ - Requires `numpy`, `scipy.ndimage.shift`, `scipy.ndimage.rotate`, and `scipy.interpolate.interp1d`.
250
+
251
+ Docstring generated by GitHub Copilot.
252
+ """
253
+
115
254
  xc = np.mean(x)
116
255
  yc = np.mean(y)
117
256
 
@@ -130,6 +269,45 @@ def rotate_image_about_pivot(Z, x, y, angle, x0=0, y0=0):
130
269
  return shift(rotate(shift(Z, ds[::-1]), angle), -ds_rot[::-1])
131
270
 
132
271
  def combine_eta(eta_fine, eta_course, x_fine, y_fine, x_course, y_course, x=None, y=None):
272
+ """
273
+ Combine two 2D fields (fine and coarse) into a single field, using the fine field where available.
274
+
275
+ Parameters
276
+ ----------
277
+ eta_fine : ndarray
278
+ 2D array of the fine-resolution field values.
279
+ eta_course : ndarray
280
+ 2D array of the coarse-resolution field values.
281
+ x_fine : ndarray
282
+ 1D array of x-coordinates for the fine field.
283
+ y_fine : ndarray
284
+ 1D array of y-coordinates for the fine field.
285
+ x_course : ndarray
286
+ 1D array of x-coordinates for the coarse field.
287
+ y_course : ndarray
288
+ 1D array of y-coordinates for the coarse field.
289
+ x : ndarray, optional
290
+ 1D array of x-coordinates for the output grid. If None, generated from coarse grid.
291
+ y : ndarray, optional
292
+ 1D array of y-coordinates for the output grid. If None, generated from coarse grid.
293
+
294
+ Returns
295
+ -------
296
+ eta_combined : ndarray
297
+ 2D array of the combined field, using fine field values where available and coarse elsewhere.
298
+ x : ndarray
299
+ 1D array of x-coordinates for the combined field.
300
+ y : ndarray
301
+ 1D array of y-coordinates for the combined field.
302
+
303
+ Notes
304
+ -----
305
+ The function interpolates both input fields onto a common grid. The fine field overwrites the coarse field
306
+ in the region where it is defined.
307
+
308
+ Docstring generated by GitHub Copilot.
309
+ """
310
+
133
311
  dx_fine = x_fine[1]-x_fine[0]
134
312
  dy_fine = y_fine[1]-y_fine[0]
135
313
 
@@ -156,7 +334,47 @@ def combine_eta(eta_fine, eta_course, x_fine, y_fine, x_course, y_course, x=None
156
334
  def animate_surface(eta, x, y, t, filename=None, fps=None,
157
335
  speed_ratio=1.0, figsize=None, writer='ffmpeg',
158
336
  ax=None, surface=None):
159
-
337
+ """
338
+ Animates a time-evolving eta (sea surface).
339
+
340
+ Parameters
341
+ ----------
342
+ eta : ndarray
343
+ 3D array of surface values with shape (nx, ny, nt), where nt is the number of time steps.
344
+ x : ndarray
345
+ 1D or 2D array representing the x-coordinates of the surface grid.
346
+ y : ndarray
347
+ 1D or 2D array representing the y-coordinates of the surface grid.
348
+ t : ndarray
349
+ 1D array of time points corresponding to the third dimension of `eta`.
350
+ filename : str or None, optional
351
+ If provided, the animation is saved to this file. If None, the animation is displayed interactively.
352
+ fps : float or None, optional
353
+ Frames per second for the animation. If None, it is computed from the time step and `speed_ratio`.
354
+ speed_ratio : float, optional
355
+ Ratio to speed up or slow down the animation relative to real time. Default is 1.0.
356
+ figsize : tuple or None, optional
357
+ Size of the figure in inches. Passed to `plt.subplots` if a new figure is created.
358
+ writer : str, optional
359
+ Writer to use for saving the animation (e.g., 'ffmpeg'). Default is 'ffmpeg'.
360
+ ax : matplotlib.axes.Axes or None, optional
361
+ Axes object to plot on. If None, a new figure and axes are created.
362
+ surface : matplotlib.image.AxesImage or None, optional
363
+ Existing surface image to update. If None, a new surface is created.
364
+
365
+ Returns
366
+ -------
367
+ None
368
+ The function either displays the animation or saves it to a file.
369
+
370
+ Notes
371
+ -----
372
+ - Requires matplotlib and numpy.
373
+ - The function uses `matplotlib.animation.FuncAnimation` for animation.
374
+ - If `filename` is provided, the animation is saved and not displayed interactively.
375
+
376
+ Docstring generated by GitHub Copilot.
377
+ """
160
378
 
161
379
  if surface is None:
162
380
  if ax is None:
@@ -195,6 +413,31 @@ def animate_surface(eta, x, y, t, filename=None, fps=None,
195
413
  plt.show()
196
414
 
197
415
  def xy_to_latlon(latlon0, coors, rot=0):
416
+ """
417
+ Convert local Cartesian (x, y) coordinates to latitude and longitude using geodesic calculations.
418
+
419
+ Parameters
420
+ ----------
421
+ latlon0 : array-like, shape (2,)
422
+ The reference latitude and longitude (in degrees) as a 2-element array or list [lat, lon].
423
+ coors : array-like, shape (N, 2)
424
+ Array of local Cartesian coordinates (x, y) to be converted, where each row is a point.
425
+ rot : float, optional
426
+ Rotation angle in degrees to be subtracted from the computed azimuth, default is 0.
427
+
428
+ Returns
429
+ -------
430
+ latlon : ndarray, shape (N, 2)
431
+ Array of latitude and longitude pairs (in degrees) corresponding to the input coordinates.
432
+
433
+ Notes
434
+ -----
435
+ Uses Cartopy's geodesic calculations to convert local (x, y) displacements from a reference point
436
+ (latlon0) to geographic coordinates, accounting for Earth's curvature.
437
+
438
+ Docstring generated by GitHub Copilot.
439
+ """
440
+
198
441
  import cartopy.crs as ccrs, cartopy.geodesic as cgds
199
442
  dist = np.linalg.norm(coors, axis=1)
200
443
  azi = np.arctan2(coors[:,0], coors[:,1])*180/np.pi - rot # atan2(x,y) to get azimuth (relative to N-vector)
@@ -208,6 +451,56 @@ def plot_surface_in_map(eta, x, y, eta_geo0, extent,
208
451
  wms_url='https://openwms.statkart.no/skwms1/wms.terrengmodell?request=GetCapabilities&service=WMS',
209
452
  wms_layers=['relieff'], ax=None,
210
453
  cm='Blues_r', colorbar=True, figsize=None, eta_rot=0, labels=False):
454
+ """
455
+ Plot a 2D surface (e.g., elevation or other field) on a map with optional scatter points and WMS background.
456
+
457
+ Parameters
458
+ ----------
459
+ eta : ndarray or None
460
+ 2D array representing the surface to plot (e.g., elevation values). If None, only scatter and WMS are shown.
461
+ x : ndarray
462
+ 1D or 2D array of x-coordinates corresponding to `eta`.
463
+ y : ndarray
464
+ 1D or 2D array of y-coordinates corresponding to `eta`.
465
+ eta_geo0 : array-like
466
+ Reference geographic coordinates (e.g., origin for coordinate transformation).
467
+ extent : array-like
468
+ Map extent in the format [xmin, xmax, ymin, ymax] (in longitude and latitude).
469
+ eta_scatter : ndarray or None, optional
470
+ Array of points to scatter on the map, shape (N, 2). Default is None.
471
+ wms_url : str, optional
472
+ URL to the WMS service for background map. Default is Statkart's terrain model.
473
+ wms_layers : list of str, optional
474
+ List of WMS layer names to display. Default is ['relieff'].
475
+ ax : matplotlib.axes.Axes or None, optional
476
+ Existing axes to plot on. If None, a new axes with Mercator projection is created.
477
+ cm : str or Colormap, optional
478
+ Colormap for the surface plot. Default is 'Blues_r'.
479
+ colorbar : bool, optional
480
+ If True, display a colorbar for the surface. Default is True.
481
+ figsize : tuple or None, optional
482
+ Figure size. Not used if `ax` is provided. Default is None.
483
+ eta_rot : float, optional
484
+ Rotation angle (degrees) to apply to `eta` and scatter points. Default is 0.
485
+ labels : bool, optional
486
+ If True, draw gridlines with labels. Default is False.
487
+
488
+ Returns
489
+ -------
490
+ ax : matplotlib.axes.Axes
491
+ The axes with the plotted map.
492
+ scatter : matplotlib.collections.PathCollection or None
493
+ The scatter plot object, or None if `eta_scatter` is None.
494
+ surface : matplotlib.image.AxesImage or None
495
+ The surface plot object, or None if `eta` is None.
496
+
497
+ Notes
498
+ -----
499
+ - Requires cartopy and matplotlib.
500
+ - Assumes existence of `xy_to_latlon` and `rotate` helper functions.
501
+
502
+ Docstring generated by GitHub Copilot.
503
+ """
211
504
 
212
505
  import cartopy.crs as ccrs, cartopy.geodesic as cgds
213
506
 
@@ -270,6 +563,42 @@ def plot_surface_in_map(eta, x, y, eta_geo0, extent,
270
563
  def plot_surface(eta, x, y, ax=None,
271
564
  cm='Blues_r', colorbar=True,
272
565
  labels=True, figsize=None, interpolation='none'):
566
+ """
567
+ Plot a 2D surface using imshow.
568
+
569
+ Parameters
570
+ ----------
571
+ eta : ndarray
572
+ 2D array representing the surface to plot (e.g., elevation values).
573
+ x : ndarray
574
+ 1D or 2D array of x-coordinates corresponding to `eta`.
575
+ y : ndarray
576
+ 1D or 2D array of y-coordinates corresponding to `eta`.
577
+ ax : matplotlib.axes.Axes or None, optional
578
+ Existing axes to plot on. If None, a new axes is created.
579
+ cm : str or Colormap, optional
580
+ Colormap for the surface plot. Default is 'Blues_r'.
581
+ colorbar : bool, optional
582
+ If True, display a colorbar for the surface. Default is True.
583
+ labels : bool, optional
584
+ If True, draw gridlines with labels. Default is True.
585
+ figsize : tuple or None, optional
586
+ Figure size. Not used if `ax` is provided. Default is None.
587
+ interpolation : str, optional
588
+ Interpolation method for the surface plot. Default is 'none'.
589
+
590
+ Returns
591
+ -------
592
+ surface : matplotlib.image.AxesImage
593
+ The surface plot object.
594
+ ax : matplotlib.axes.Axes
595
+ The axes with the plotted surface.
596
+
597
+ Notes
598
+ -----
599
+ Docstring generated by GitHub Copilot.
600
+
601
+ """
273
602
 
274
603
  if ax is None:
275
604
  fig, ax = plt.subplots(figsize=figsize)
@@ -295,7 +624,6 @@ def plot_surface(eta, x, y, ax=None,
295
624
 
296
625
 
297
626
  def set_axes_equal(ax: plt.Axes):
298
- import matplotlib.pyplot as plt
299
627
  """Set 3D plot axes to equal scale.
300
628
 
301
629
  Make axes of 3D plot have equal scale so that spheres appear as
@@ -375,103 +703,38 @@ def plot_transformation_mats(x,y,z,T,figno=None, ax=None, scaling='auto'):
375
703
  equal_3d(ax)
376
704
  return ax,h
377
705
 
378
-
379
- def plot_elements(element_matrix, node_matrix, chosen_nodes_ix=[], disp=None, node_labels=False, element_labels=False, plot_nodes=True, plot_elements=True, ax=None, fig=None, element_settings={}, node_settings={}, node_label_settings={}, chosen_node_settings={}, disp_settings={}, element_label_settings={}, three_d=True):
380
- e_dict = {'color': 'LimeGreen', 'alpha': 1}
381
- e_dict.update(**element_settings)
382
-
383
- n_dict = {'color':'Black', 'linestyle':'', 'marker':'.', 'markersize':4, 'alpha':0.8}
384
- n_dict.update(**node_settings)
385
-
386
- n_chosen_dict = {'color':'GreenYellow', 'linestyle':'', 'marker':'o', 'markersize':8, 'alpha':1, 'markeredgecolor':'dimgray'}
387
- n_chosen_dict.update(**chosen_node_settings)
388
-
389
- disp_dict = {'color':'IndianRed', 'alpha':1}
390
- disp_dict.update(**disp_settings)
391
-
392
- l_nodes_dict = {'color':'Black', 'fontsize': 8, 'fontweight':'normal'}
393
- l_nodes_dict.update(**node_label_settings)
394
-
395
- l_elements_dict = {'color':'LimeGreen', 'fontsize': 8, 'fontweight':'bold', 'style':'italic'}
396
- l_elements_dict.update(**element_label_settings)
397
-
398
- if ax is None and fig is None:
399
- fig = plt.figure()
400
-
401
- if ax == None and three_d:
402
- ax = fig.gca(projection='3d')
403
- elif ax == None:
404
- ax = fig.gca()
405
- elif three_d:
406
- 1
407
- # ax.set(projection='3d') #mangler funksjonalitet her...
408
-
409
- element_handles = [None]*len(element_matrix[:,0])
410
-
411
- if plot_elements:
412
- for element_ix, __ in enumerate(element_matrix[:,0]):
413
- node1 = element_matrix[element_ix, 1]
414
- node2 = element_matrix[element_ix, 2]
415
- nodeix1 = np.where(node_matrix[:,0]==node1)[0]
416
- nodeix2 = np.where(node_matrix[:,0]==node2)[0]
417
- x01 = node_matrix[nodeix1,1:4]
418
- x02 = node_matrix[nodeix2,1:4]
419
- x0 = np.vstack([x01,x02])
420
-
421
- if three_d:
422
- element_handles[element_ix] = ax.plot(xs=x0[:,0], ys=x0[:,1], zs=x0[:,2], **e_dict)
423
- else:
424
- element_handles[element_ix] = ax.plot(x0[:,0], x0[:,1], **e_dict)
425
-
426
- if element_labels:
427
- xmean = np.mean(x0, axis=0)
428
- if three_d:
429
- ax.text(xmean[0],xmean[1],xmean[2],'%i' % element_matrix[element_ix,0], **l_elements_dict)
430
- else:
431
- ax.text(xmean[0],xmean[1],s='%i' % element_matrix[element_ix,0], **l_elements_dict)
432
-
433
- if disp is not None:
434
- disp_node1 = disp[nodeix1[0]*6:(nodeix1[0]*6+6)]
435
- disp_node2 = disp[nodeix2[0]*6:(nodeix2[0]*6+6)]
436
- x1 = x01+disp_node1[0:3]
437
- x2 = x02+disp_node2[0:3]
438
- x = np.vstack([x1,x2])
439
-
440
- if three_d:
441
- ax.plot(xs=x[:,0], ys=x[:,1], zs=x[:,2], **disp_dict)
442
- else:
443
- ax.plot(x[:,0], x[:,1], **disp_dict)
444
-
445
- if plot_nodes:
446
- if three_d:
447
- ax.plot(xs=node_matrix[:, 1], ys=node_matrix[:, 2], zs=node_matrix[:, 3], **n_dict)
448
- else:
449
- ax.plot(node_matrix[:, 1], node_matrix[:, 2], **n_dict)
450
-
451
- if chosen_nodes_ix != []:
452
- if three_d:
453
- ax.plot(xs=node_matrix[chosen_nodes_ix, 1], ys=node_matrix[chosen_nodes_ix, 2], zs=node_matrix[chosen_nodes_ix, 3], **n_chosen_dict)
454
- else:
455
- ax.plot(node_matrix[chosen_nodes_ix, 1], node_matrix[chosen_nodes_ix, 2], **n_chosen_dict)
456
-
457
- if node_labels:
458
- if three_d:
459
- for node_ix in range(0, np.shape(node_matrix)[0]):
460
- ax.text(node_matrix[node_ix, 1], node_matrix[node_ix, 2], node_matrix[node_ix, 3], '%i' % node_matrix[node_ix, 0], **l_nodes_dict)
461
- else:
462
- for node_ix in range(0, np.shape(node_matrix)[0]):
463
- ax.text(node_matrix[node_ix, 1], node_matrix[node_ix, 2], '%i' % node_matrix[node_ix, 0], **l_nodes_dict)
706
+ def plot_2d(S2d, x1, x2, ax=None, levels=80, discrete=False, **kwargs):
707
+ """
708
+ Plot a 2D array as either a filled contour plot or a pseudocolor mesh.
709
+
710
+ Parameters
711
+ ----------
712
+ S2d : array_like
713
+ 2D array of values to plot.
714
+ x1 : array_like
715
+ 1D array representing the x-coordinates.
716
+ x2 : array_like
717
+ 1D array representing the y-coordinates.
718
+ ax : matplotlib.axes.Axes, optional
719
+ The axes on which to plot. If None, uses the current axes.
720
+ levels : int, optional
721
+ Number of contour levels to use for filled contour plot. Default is 80.
722
+ discrete : bool, optional
723
+ If True, use `pcolormesh` for a discrete colormap. If False, use `contourf` for a filled contour plot.
724
+ **kwargs
725
+ Additional keyword arguments passed to the plotting function (`contourf` or `pcolormesh`).
726
+
727
+ Returns
728
+ -------
729
+ contour : QuadMesh or QuadContourSet
730
+ The resulting plot object from `pcolormesh` or `contourf`.
731
+
732
+ Notes
733
+ -----
734
+ Docstring generated by GitHub Copilot.
735
+ """
464
736
 
465
- if three_d:
466
- equal_3d(ax)
467
- else:
468
- ax.set_aspect('equal', adjustable='box')
469
-
470
- ax.grid('off')
471
- return ax, element_handles
472
-
473
737
 
474
- def plot_2d(S2d, x1, x2, ax=None, levels=80, discrete=False, **kwargs):
475
738
  if ax is None:
476
739
  ax = plt.gca()
477
740
 
@@ -484,6 +747,39 @@ def plot_2d(S2d, x1, x2, ax=None, levels=80, discrete=False, **kwargs):
484
747
 
485
748
 
486
749
  def plot_S2d(S, omega, theta, D=None, omega_range=None, theta_range=None):
750
+ """
751
+ Plot a 2D spectral density (S2d) as a contour plot, with optional marginal plots (S and D).
752
+
753
+ Parameters
754
+ ----------
755
+ S : array_like
756
+ 1D array of spectral density values as a function of omega.
757
+ omega : array_like
758
+ 1D array of frequency values (rad/s).
759
+ theta : array_like
760
+ 1D array of direction values (rad).
761
+ D : array_like, optional
762
+ 1D array of directional spreading function values as a function of theta.
763
+ If provided, marginal plots of S(omega) and D(theta) are shown.
764
+ omega_range : list or tuple, optional
765
+ Range [min, max] for the omega axis. If None, uses [0, max(omega)].
766
+ theta_range : list or tuple, optional
767
+ Range [min, max] for the theta axis. If None, uses [min(theta), max(theta)].
768
+
769
+ Returns
770
+ -------
771
+ fig : matplotlib.figure.Figure
772
+ The matplotlib Figure object containing the plot.
773
+
774
+ Notes
775
+ -----
776
+ - If `D` is provided, the function plots the 2D spectral density as a contour plot,
777
+ with marginal line plots for S(omega) and D(theta).
778
+ - If `D` is not provided, only the contour plot is shown.
779
+ - The function handles NaN values in the spectral density by setting them to zero.
780
+
781
+ Docstring generated by GitHub Copilot.
782
+ """
487
783
 
488
784
  if theta_range is None:
489
785
  theta_range = [np.min(theta), np.max(theta)]
wawi/prob.py CHANGED
@@ -1,6 +1,34 @@
1
1
  import numpy as np
2
2
 
3
3
  def gumbel_log(umax):
4
+ """
5
+ Compute the Gumbel reduced variate for a given array of maxima.
6
+
7
+ Parameters
8
+ ----------
9
+ umax : array_like
10
+ Array of maxima values.
11
+
12
+ Returns
13
+ -------
14
+ umax_ordered : ndarray
15
+ The input maxima sorted in descending order.
16
+ loglogF : ndarray
17
+ The Gumbel reduced variate corresponding to each sorted maxima.
18
+
19
+ Examples
20
+ --------
21
+ >>> import numpy as np
22
+ >>> umax = np.array([2.3, 3.1, 1.8, 2.9])
23
+ >>> umax_ordered, loglogF = gumbel_log(umax)
24
+
25
+ Notes
26
+ -------
27
+ This function sorts the input array of maxima in descending order and computes the
28
+ Gumbel reduced variate (log-log of the empirical cumulative distribution function)
29
+ for each value.
30
+ """
31
+
4
32
  umax_ordered = np.sort(umax)[::-1]
5
33
  N_stat = len(umax)
6
34
  F = 1-np.arange(1, N_stat+1)/(N_stat+1)