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

scikit-rf / scikit-rf / 25805732079

13 May 2026 02:30PM UTC coverage: 79.567% (-0.01%) from 79.577%
25805732079

Pull #1372

github

web-flow
Merge ec1d18217 into 950534a59
Pull Request #1372: Lazy load most modules

211 of 271 new or added lines in 28 files covered. (77.86%)

4 existing lines in 4 files now uncovered.

18053 of 22689 relevant lines covered (79.57%)

3.98 hits per line

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

62.32
/skrf/plotting.py
1
"""
2
plotting (:mod:`skrf.plotting`)
3
========================================
4

5

6
This module provides general plotting functions.
7

8
Plots and Charts
9
------------------
10

11
.. autosummary::
12
    :toctree: generated/
13

14
    smith
15
    plot_smith
16
    plot_rectangular
17
    plot_polar
18
    plot_complex_rectangular
19
    plot_complex_polar
20
    plot_it_all
21

22
    plot_minmax_bounds_component
23
    plot_minmax_bounds_s_db
24
    plot_minmax_bounds_s_db10
25
    plot_minmax_bounds_s_time_db
26

27
    plot_uncertainty_bounds_component
28
    plot_uncertainty_bounds_s_db
29
    plot_uncertainty_bounds_s_time_db
30

31
    plot_passivity
32
    plot_logsigma
33

34
    plot_contour
35

36
Convenience plotting functions
37
-------------------------------
38
.. autosummary::
39
    :toctree: generated/
40

41
    stylely
42
    subplot_params
43
    shade_bands
44
    save_all_figs
45
    scale_frequency_ticks
46
    add_markers_to_lines
47
    legend_off
48
    func_on_all_figs
49
    scrape_legend
50
    signature
51

52
"""
53
from __future__ import annotations
5✔
54

55
import os
5✔
56
from collections.abc import Callable
5✔
57
from contextlib import suppress
5✔
58
from functools import wraps
5✔
59
from logging import getLogger
5✔
60
from numbers import Number
5✔
61
from typing import TYPE_CHECKING
5✔
62

63
if TYPE_CHECKING:
64
    from matplotlib.axes import Axes
65
    from matplotlib.figure import Figure
66

67
    from .constants import NumberLike, PrimaryPropertiesT
68
    from .frequency import Frequency
69
    from .network import Network
70
    from .networkSet import NetworkSet
71

72
import warnings
5✔
73

74
import numpy as np
5✔
75

76
from . import mathFunctions as mf
5✔
77
from .util import now_string_2_dt
5✔
78

79
logger = getLogger(__name__)
5✔
80

81
SI_PREFIXES_ASCII = 'yzafpnum kMGTPEZY'
5✔
82
SI_CONVERSION = {key: 10**((8-i)*3) for i, key in enumerate(SI_PREFIXES_ASCII)}
5✔
83

84

85
def plotting_available() -> bool:
5✔
NEW
86
    result = False
×
NEW
87
    with suppress(ImportError):
×
NEW
88
        import matplotlib  # noqa: F401
×
NEW
89
        result = True
×
NEW
90
    return result
×
91

92

93
def axes_kwarg(func):
5✔
94
    """
95
    This decorator checks if a :class:`matplotlib.axes.Axes` object is passed,
96
    if not the current axis will be gathered through :func:`plt.gca`.
97

98
    Raises
99
    ------
100
    RuntimeError
101
        When trying to run the decorated function without matplotlib
102
    """
103

104
    @wraps(func)
5✔
105
    def wrapper(*args, **kwargs):
5✔
106
        try:
5✔
107
            import matplotlib.pyplot as plt
5✔
108
            ax = kwargs.pop('ax', None)
5✔
109
            if ax is None:
5✔
110
                ax = plt.gca()
5✔
NEW
111
        except ImportError as err:
×
NEW
112
            raise RuntimeError("Plotting is not available") from err
×
113
        func(*args, ax=ax, **kwargs)
5✔
114

115
    return wrapper
5✔
116

117

118
def figure(*args, **kwargs) -> Figure:
5✔
119
    """
120
    Wraps the matplotlib figure call and raises if not available.
121

122
    Raises
123
    ------
124
    RuntimeError
125
        When trying to get subplots without matplotlib installed.
126
    """
127

NEW
128
    try:
×
NEW
129
        import matplotlib.pyplot as plt
×
NEW
130
        return plt.figure(*args, **kwargs)
×
NEW
131
    except ImportError as err:
×
NEW
132
        raise RuntimeError("Plotting is not available") from err
×
133

134

135
def subplots(*args, **kwargs) -> tuple[Figure, np.ndarray]:
5✔
136
    """
137
    Wraps the matplotlib subplots call and raises if not available.
138

139
    Raises
140
    ------
141
    RuntimeError
142
        When trying to get subplots without matplotlib installed.
143
    """
144

NEW
145
    try:
×
NEW
146
        import matplotlib.pyplot as plt
×
NEW
147
        return plt.subplots(*args, **kwargs)
×
NEW
148
    except ImportError as err:
×
NEW
149
        raise RuntimeError("Plotting is not available") from err
×
150

151

152
def _get_label_str(netw: Network, param: str, m: int, n: int) -> str:
5✔
153
    import matplotlib.pyplot as plt
5✔
154

155
    label_string = ""
5✔
156
    if netw.name is not None:
5✔
157
        label_string += f"{netw.name}, "
5✔
158

159
    if plt.rcParams['text.usetex']:
5✔
160
        label_string += f"${param}_{{{netw._fmt_trace_name(m,n)}}}$"
5✔
161
    else:
162
        label_string += f"{param}{netw._fmt_trace_name(m,n)}"
5✔
163
    return label_string
5✔
164

165

166
def scale_frequency_ticks(ax: Axes, funit: str):
5✔
167
    """
168
    Scale frequency axis ticks.
169

170
    Parameters
171
    ----------
172
    ax : plt.Axes
173
        Matplotlib figure axe
174
    funit : str
175
        frequency unit string as in :data:`~skrf.frequency.Frequency.unit`
176

177
    Raises
178
    ------
179
    ValueError
180
        if invalid unit is passed
181
    """
182
    from matplotlib import ticker
5✔
183

184
    if funit.lower() == "hz":
5✔
185
        prefix = " "
×
186
        scale = 1
×
187
    elif len(funit) == 3:
5✔
188
        prefix = funit[0]
5✔
189
        scale = SI_CONVERSION[prefix]
5✔
190
    else:
191
        raise ValueError(f"invalid funit {funit}")
×
192
    ticks_x = ticker.FuncFormatter(lambda x, pos: f'{x * scale:g}')
5✔
193
    ax.xaxis.set_major_formatter(ticks_x)
5✔
194

195
@axes_kwarg
5✔
196
def smith(smithR: Number = 1, chart_type: str = 'z', draw_labels: bool = False,
5✔
197
          border: bool = False, ax: Axes | None = None, ref_imm: float = 1.0,
198
          draw_vswr: list | bool | None = None):
199
    """
200
    Plot the Smith chart of a given radius.
201

202
    The Smith chart is used to assist in solving problems with transmission lines
203
    and matching circuits. It can be used to simultaneously display multiple
204
    parameters including impedances, admittances, reflection coefficients,
205
    scattering parameters, noise figure circles, etc. [#]_
206

207
    Parameters
208
    ----------
209
    smithR : number, optional
210
        radius of smith chart. Default is 1.
211
    chart_type : str, optional
212
        Contour type. Default is 'z'. Possible values are:
213

214
        * *'z'* : lines of constant impedance
215
        * *'y'* : lines of constant admittance
216
        * *'zy'* : lines of constant impedance stronger than admittance
217
        * *'yz'* : lines of constant admittance stronger than impedance
218
    draw_labels : Boolean, optional
219
        annotate real and imaginary parts of impedance on the
220
        chart (only if smithR=1).
221
        Default is False.
222
    border : Boolean, optional.
223
        draw a rectangular border with axis ticks, around the perimeter
224
        of the figure. Not used if draw_labels = True.
225
        Default is False.
226
    ax : :class:`matplotlib.pyplot.Axes` or None, optional
227
        existing axes to draw smith chart on.
228
        Default is None (creates a new figure)
229
    ref_imm : number, optional
230
        Reference immittance for center of Smith chart. Only changes
231
        labels, if printed.
232
        Default is 1.0.
233
    draw_vswr : list of numbers, Boolean or None, optional
234
        draw VSWR circles. If True, default values are used.
235
        Default is None.
236

237
    References
238
    ----------
239
    .. [#] https://en.wikipedia.org/wiki/Smith_chart
240

241
    """
242
    from matplotlib.patches import Circle
5✔
243

244
    # contour holds matplotlib instances of: pathes.Circle, and lines.Line2D, which
245
    # are the contours on the smith chart
246
    contour = []
5✔
247

248
    # these are hard-coded on purpose,as they should always be present
249
    rHeavyList = [0,1]
5✔
250
    xHeavyList = [1,-1]
5✔
251

252
    #TODO: fix this
253
    # these could be dynamically coded in the future, but work good'nuff for now
254
    if not draw_labels:
5✔
255
        rLightList = np.logspace(3,-5,9,base=.5)
5✔
256
        xLightList = np.hstack([np.logspace(2,-5,8,base=.5), -1*np.logspace(2,-5,8,base=.5)])
5✔
257
    else:
258
        rLightList = np.array( [ 0.2, 0.5, 1.0, 2.0, 5.0 ] )
×
259
        xLightList = np.array( [ 0.2, 0.5, 1.0, 2.0 , 5.0, -0.2, -0.5, -1.0, -2.0, -5.0 ] )
×
260

261
    # vswr lines
262
    if isinstance(draw_vswr, tuple | list):
5✔
263
        vswrVeryLightList = draw_vswr
×
264
    elif draw_vswr is True:
5✔
265
        # use the default I like
266
        vswrVeryLightList = [1.5, 2.0, 3.0, 5.0]
×
267
    else:
268
        vswrVeryLightList = []
5✔
269

270
    # cheap way to make a ok-looking smith chart at larger than 1 radii
271
    if smithR > 1:
5✔
272
        rMax = (1.+smithR)/(1.-smithR)
×
273
        rLightList = np.hstack([ np.linspace(0,rMax,11)  , rLightList ])
×
274

275
    if chart_type.startswith('y'):
5✔
276
        y_flip_sign = -1
×
277
    else:
278
        y_flip_sign = 1
5✔
279

280
    # draw impedance and/or admittance
281
    both_charts = chart_type in ('zy', 'yz')
5✔
282

283

284
    # loops through Verylight, Light and Heavy lists and draws circles using patches
285
    # for analysis of this see R.M. Weikles Microwave II notes (from uva)
286

287
    superLightColor = dict(ec='whitesmoke', fc='none')
5✔
288
    veryLightColor = dict(ec='lightgrey', fc='none')
5✔
289
    lightColor = dict(ec='grey', fc='none')
5✔
290
    heavyColor = dict(ec='black', fc='none')
5✔
291

292
    # vswr circles verylight
293
    for vswr in vswrVeryLightList:
5✔
294
        radius = (vswr-1.0) / (vswr+1.0)
×
295
        contour.append( Circle((0, 0), radius, **veryLightColor))
×
296

297
    # impedance/admittance circles
298
    for r in rLightList:
5✔
299
        center = (r/(1.+r)*y_flip_sign,0 )
5✔
300
        radius = 1./(1+r)
5✔
301
        if both_charts:
5✔
302
            contour.insert(0, Circle((-center[0], center[1]), radius, **superLightColor))
×
303
        contour.append(Circle(center, radius, **lightColor))
5✔
304
    for x in xLightList:
5✔
305
        center = (1*y_flip_sign,1./x)
5✔
306
        radius = 1./x
5✔
307
        if both_charts:
5✔
308
            contour.insert(0, Circle( (-center[0], center[1]), radius, **superLightColor))
×
309
        contour.append(Circle(center, radius, **lightColor))
5✔
310

311
    for r in rHeavyList:
5✔
312
        center = (r/(1.+r)*y_flip_sign,0 )
5✔
313
        radius = 1./(1+r)
5✔
314
        contour.append(Circle(center, radius, **heavyColor))
5✔
315
    for x in xHeavyList:
5✔
316
        center = (1*y_flip_sign,1./x)
5✔
317
        radius = 1./x
5✔
318
        contour.append(Circle(center, radius, **heavyColor))
5✔
319

320
    # clipping circle
321
    clipc = Circle( [0,0], smithR, ec='k',fc='None',visible=True)
5✔
322
    ax.add_patch( clipc)
5✔
323

324
    #draw x and y axis
325
    ax.axhline(0, color='k', lw=.1, clip_path=clipc)
5✔
326
    ax.axvline(1*y_flip_sign, color='k', clip_path=clipc)
5✔
327
    ax.grid(0)
5✔
328
    # Set axis limits by plotting white points so zooming works properly
329
    ax.plot(smithR*np.array([-1.1, 1.1]), smithR*np.array([-1.1, 1.1]), 'w.', markersize = 0)
5✔
330
    ax.axis('image') # Combination of 'equal' and 'tight'
5✔
331

332

333
    if not border:
5✔
334
        ax.yaxis.set_ticks([])
5✔
335
        ax.xaxis.set_ticks([])
5✔
336
        for spine in ax.spines.values():
5✔
337
            spine.set_color('none')
5✔
338

339

340
    if draw_labels:
5✔
341
        #Clear axis
342
        ax.yaxis.set_ticks([])
×
343
        ax.xaxis.set_ticks([])
×
344
        for spine in ax.spines.values():
×
345
            spine.set_color('none')
×
346

347
        # Make annotations only if the radius is 1
348
        if smithR == 1:
×
349
            #Make room for annotation
350
            ax.plot(np.array([-1.25, 1.25]), np.array([-1.1, 1.1]), 'w.', markersize = 0)
×
351
            ax.axis('image')
×
352

353
            #Annotate real part
354
            for value in rLightList:
×
355
                # Set radius of real part's label; offset slightly left (Z
356
                # chart, y_flip_sign == 1) or right (Y chart, y_flip_sign == -1)
357
                # so label doesn't overlap chart's circles
358
                rho = (value - 1)/(value + 1) - y_flip_sign*0.01
×
359
                if y_flip_sign == 1:
×
360
                    halignstyle = "right"
×
361
                else:
362
                    halignstyle = "left"
×
363
                if y_flip_sign == -1:  # 'y' and 'yz' charts
×
364
                    value = 1/value
×
365
                ax.annotate(str(value*ref_imm), xy=(rho*smithR, 0.01),
×
366
                    xytext=(rho*smithR, 0.01), ha = halignstyle, va = "baseline")
367

368
            #Annotate imaginary part
369
            radialScaleFactor = 1.01 # Scale radius of label position by this
×
370
                                     # factor. Making it >1 places the label
371
                                     # outside the Smith chart's circle
372
            for value in xLightList:
×
373
                #Transforms from complex to cartesian
374
                S = (1j*value - 1) / (1j*value + 1)
×
375
                S *= smithR * radialScaleFactor
×
376
                rhox = S.real
×
377
                rhoy = S.imag * y_flip_sign
×
378

379
                # Choose alignment anchor point based on label's value
380
                if ((value == 1.0) or (value == -1.0)):
×
381
                    halignstyle = "center"
×
382
                elif (rhox < 0.0):
×
383
                    halignstyle = "right"
×
384
                else:
385
                    halignstyle = "left"
×
386

387
                if (rhoy < 0):
×
388
                    valignstyle = "top"
×
389
                else:
390
                    valignstyle = "bottom"
×
391
                if y_flip_sign == -1:  # 'y' and 'yz' charts
×
392
                    value = 1/value
×
393
                #Annotate value
394
                ax.annotate(str(value*ref_imm) + 'j', xy=(rhox, rhoy),
×
395
                             xytext=(rhox, rhoy), ha = halignstyle, va = valignstyle)
396

397
            #Annotate 0 and inf
398
            if y_flip_sign == 1:  # z and zy charts
×
399
                label_left, label_right = '0.0', r'$\infty$'
×
400
            else:  # y and yz charts
401
                label_left, label_right = r'$\infty$', '0.0'
×
402
            ax.annotate(label_left, xy=(-1.02, 0), xytext=(-1.02, 0),
×
403
                             ha = "right", va = "center")
404
            ax.annotate(label_right, xy=(radialScaleFactor, 0), xytext=(radialScaleFactor, 0),
×
405
                             ha = "left", va = "center")
406

407
            # annotate vswr circles
408
            for vswr in vswrVeryLightList:
×
409
                rhoy = (vswr-1.0) / (vswr+1.0)
×
410

411
                ax.annotate(str(vswr), xy=(0, rhoy*smithR),
×
412
                    xytext=(0, rhoy*smithR), ha="center", va="bottom",
413
                    color='grey', size='smaller')
414

415
    # loop though contours and draw them on the given axes
416
    for currentContour in contour:
5✔
417
        cc=ax.add_patch(currentContour)
5✔
418
        cc.set_clip_path(clipc)
5✔
419

420

421
def plot_rectangular(x: NumberLike, y: NumberLike,
5✔
422
                     x_label: str | None = None, y_label: str | None = None,
423
                     title: str | None = None, show_legend: bool = True,
424
                     axis: str = 'tight', ax: Axes | None = None,
425
                     *args, **kwargs):
426
    r"""
427
    Plot rectangular data and optionally label axes.
428

429
    Parameters
430
    ----------
431
    x : array-like, of complex data
432
        data to plot
433
    y : array-like, of complex data
434
        data to plot
435
    x_label : string or None, optional.
436
        x-axis label. Default is None.
437
    y_label : string or None, optional.
438
        y-axis label. Default is None.
439
    title : string or None, optional.
440
        plot title. Default is None.
441
    show_legend : Boolean, optional.
442
        controls the drawing of the legend. Default is True.
443
    axis : str, optional
444
        whether or not to autoscale the axis. Default is 'tight'
445
    ax : :class:`matplotlib.axes.AxesSubplot` object or None, optional.
446
        axes to draw on. Default is None (creates a new figure)
447
    \*args, \*\*kwargs : passed to pylab.plot
448

449
    """
450
    import matplotlib.pyplot as plt
5✔
451

452
    if ax is None:
5✔
453
        ax = plt.gca()
5✔
454

455
    my_plot = ax.plot(x, y, *args, **kwargs)
5✔
456

457
    if x_label is not None:
5✔
458
        ax.set_xlabel(x_label)
5✔
459

460
    if y_label is not None:
5✔
461
        ax.set_ylabel(y_label)
5✔
462

463
    if title is not None:
5✔
464
        ax.set_title(title)
5✔
465

466
    if show_legend:
5✔
467
        # only show legend if they provide a label
468
        if 'label' in kwargs:
5✔
469
            ax.legend()
5✔
470

471
    if axis is not None:
5✔
472
        ax.autoscale(True, 'x', True)
5✔
473
        ax.autoscale(True, 'y', False)
5✔
474

475
    if plt.isinteractive():
5✔
476
        plt.draw()
×
477

478
    return my_plot
5✔
479

480

481
def plot_polar(theta: NumberLike, r: NumberLike,
5✔
482
               x_label: str | None = None, y_label: str | None = None,
483
               title: str | None = None, show_legend: bool = True,
484
               axis_equal: bool = False, ax: Axes | None = None,
485
               *args, **kwargs):
486
    r"""
487
    Plot polar data on a polar plot and optionally label axes.
488

489
    Parameters
490
    ----------
491
    theta : array-like
492
        angular data to plot
493
    r : array-like
494
        radial data to plot
495
    x_label : string or None, optional
496
        x-axis label. Default is None.
497
    y_label : string or None, optional.
498
        y-axis label. Default is None
499
    title : string or None, optional.
500
        plot title. Default is None.
501
    show_legend : Boolean, optional.
502
        controls the drawing of the legend. Default is True.
503
    ax : :class:`matplotlib.axes.AxesSubplot` object or None.
504
        axes to draw on. Default is None (creates a new figure).
505
    \*args, \*\*kwargs : passed to pylab.plot
506

507
    See Also
508
    --------
509
    plot_rectangular : plots rectangular data
510
    plot_complex_rectangular : plot complex data on complex plane
511
    plot_polar : plot polar data
512
    plot_complex_polar : plot complex data on polar plane
513
    plot_smith : plot complex data on smith chart
514

515
    """
516
    import matplotlib.pyplot as plt
5✔
517

518
    if ax is None:
5✔
519
        # no Axes passed
520
        # if an existing (polar) plot is already present, grab and use its Axes
521
        # otherwise, create a new polar plot and use that Axes
522
        if not plt.get_fignums() or not plt.gcf().axes or plt.gca().name != 'polar':
5✔
523
            ax = plt.figure().add_subplot(projection='polar')
5✔
524
        else:
525
            ax = plt.gca()
5✔
526
    else:
527
        if ax.name != 'polar':
×
528
            # The projection of an existing axes can't be changed,
529
            # since specifying a projection when creating an axes determines the
530
            # axes class you get, which is different for each projection type.
531
            # So, passing a axe projection not polar is probably undesired
532
            warnings.warn(
×
533
                f"Projection of the Axes passed as `ax` is not 'polar' but is {ax.name}." +
534
                "See Matplotlib documentation to create a polar plot or call this function without the `ax` parameter."
535
                , stacklevel=2
536
            )
537

538
    ax.plot(theta, r, *args, **kwargs)
5✔
539

540
    if x_label is not None:
5✔
541
        ax.set_xlabel(x_label)
×
542

543
    if y_label is not None:
5✔
544
        ax.set_ylabel(y_label)
×
545

546
    if title is not None:
5✔
547
        ax.set_title(title)
×
548

549
    if show_legend:
5✔
550
        # only show legend if they provide a label
551
        if 'label' in kwargs:
5✔
552
            ax.legend()
5✔
553

554
    if axis_equal:
5✔
555
        ax.axis('equal')
×
556

557
    if plt.isinteractive():
5✔
558
        plt.draw()
×
559

560

561
def plot_complex_rectangular(z: NumberLike,
5✔
562
                             x_label: str = 'Real', y_label: str = 'Imag',
563
                             title: str = 'Complex Plane', show_legend: bool = True,
564
                             axis: str = 'equal', ax: Axes | None = None,
565
                             **kwargs):
566
    r"""
567
    Plot complex data on the complex plane.
568

569
    Parameters
570
    ----------
571
    z : array-like, of complex data
572
        data to plot
573
    x_label : string, optional.
574
        x-axis label. Default is 'Real'.
575
    y_label : string, optional.
576
        y-axis label. Default is 'Imag'.
577
    title : string, optional.
578
        plot title. Default is 'Complex Plane'
579
    show_legend : Boolean, optional.
580
        controls the drawing of the legend. Default is True.
581
    ax : :class:`matplotlib.axes.AxesSubplot` object or None.
582
        axes to draw on. Default is None (creates a new figure)
583
    \*\*kwargs : passed to pylab.plot
584

585
    See Also
586
    --------
587
    plot_rectangular : plots rectangular data
588
    plot_complex_rectangular : plot complex data on complex plane
589
    plot_polar : plot polar data
590
    plot_complex_polar : plot complex data on polar plane
591
    plot_smith : plot complex data on smith chart
592

593
    """
594
    x = np.real(z)
5✔
595
    y = np.imag(z)
5✔
596
    plot_rectangular(x=x, y=y, x_label=x_label, y_label=y_label,
5✔
597
        title=title, show_legend=show_legend, axis=axis,
598
        ax=ax, **kwargs)
599

600

601
def plot_complex_polar(z: NumberLike,
5✔
602
                       x_label: str | None = None, y_label: str | None = None,
603
                       title: str | None = None, show_legend: bool = True,
604
                       axis_equal: bool = False, ax: Axes | None = None,
605
                       **kwargs):
606
    r"""
607
    Plot complex data in polar format.
608

609
    Parameters
610
    ----------
611
    z : array-like, of complex data
612
        data to plot
613
    x_label : string or None, optional
614
        x-axis label. Default is None.
615
    y_label : string or None, optional.
616
        y-axis label. Default is None
617
    title : string or None, optional.
618
        plot title. Default is None.
619
    show_legend : Boolean, optional.
620
        controls the drawing of the legend. Default is True.
621
    ax : :class:`matplotlib.axes.AxesSubplot` object or None.
622
        axes to draw on. Default is None (creates a new figure).
623
    \*\*kwargs : passed to pylab.plot
624

625
    See Also
626
    --------
627
    plot_rectangular : plots rectangular data
628
    plot_complex_rectangular : plot complex data on complex plane
629
    plot_polar : plot polar data
630
    plot_complex_polar : plot complex data on polar plane
631
    plot_smith : plot complex data on smith chart
632
    """
633
    theta = np.angle(z)
5✔
634
    r = np.abs(z)
5✔
635
    plot_polar(theta=theta, r=r, x_label=x_label, y_label=y_label,
5✔
636
        title=title, show_legend=show_legend, axis_equal=axis_equal,
637
        ax=ax, **kwargs)
638

639

640
def plot_smith(s: NumberLike, smith_r: float = 1, chart_type: str = 'z',
5✔
641
               x_label: str = 'Real', y_label: str = 'Imaginary', title: str = 'Complex Plane',
642
               show_legend: bool = True, axis: str = 'equal', ax: Axes | None = None,
643
               force_chart: bool = False, draw_vswr: list | bool | None = None, draw_labels: bool = False,
644
               **kwargs):
645
    r"""
646
    Plot complex data on smith chart.
647

648
    Parameters
649
    ------------
650
    s : complex array-like
651
        reflection-coefficient-like data to plot
652
    smith_r : number
653
        radius of smith chart
654
    chart_type : str in ['z','y']
655
        Contour type for chart.
656
        * *'z'* : lines of constant impedance
657
        * *'y'* : lines of constant admittance
658
    x_label : string, optional.
659
        x-axis label. Default is 'Real'.
660
    y_label : string, optional.
661
        y-axis label. Default is 'Imaginary'
662
    title : string, optional.
663
        plot title, Default is 'Complex Plane'.
664
    show_legend : Boolean, optional.
665
        controls the drawing of the legend. Default is True.
666
    axis_equal: Boolean, optional.
667
        sets axis to be equal increments. Default is 'equal'.
668
    ax : :class:`matplotlib.axes.AxesSubplot` object or None.
669
        axes to draw on. Default is None (creates a new figure).
670
    force_chart : Boolean, optional.
671
        forces the re-drawing of smith chart. Default is False.
672
    draw_vswr : list of numbers, Boolean or None, optional
673
        draw VSWR circles. If True, default values are used.
674
        Default is None.
675
    draw_labels : Boolean
676
        annotate chart with impedance values
677
    \*\*kwargs : passed to pylab.plot
678

679
    See Also
680
    ----------
681
    plot_rectangular : plots rectangular data
682
    plot_complex_rectangular : plot complex data on complex plane
683
    plot_polar : plot polar data
684
    plot_complex_polar : plot complex data on polar plane
685
    plot_smith : plot complex data on smith chart
686
    """
NEW
687
    import matplotlib.pyplot as plt
×
688

689
    if ax is None:
×
690
        ax = plt.gca()
×
691

692
    # test if smith chart is already drawn
693
    if not force_chart:
×
694
        if len(ax.patches) == 0:
×
695
            smith(ax=ax, smithR = smith_r, chart_type=chart_type, draw_vswr=draw_vswr, draw_labels=draw_labels)
×
696

697
    plot_complex_rectangular(s, x_label=x_label, y_label=y_label,
×
698
        title=title, show_legend=show_legend, axis=axis,
699
        ax=ax, **kwargs)
700

701
    ax.axis(smith_r*np.array([-1.1, 1.1, -1.1, 1.1]))
×
702
    if plt.isinteractive():
×
703
        plt.draw()
×
704

705

706
def subplot_params(ntwk: Network, param: str = 's', proj: str = 'db',
5✔
707
                   size_per_port: int = 4, newfig: bool = True,
708
                   add_titles: bool = True, keep_it_tight: bool = True,
709
                   subplot_kw: dict = None,
710
                   **kwargs):
711
    """
712
    Plot all networks parameters individually on subplots.
713

714
    Parameters
715
    ----------
716
    ntwk : :class:`~skrf.network.Network`
717
        Network to get data from.
718
    param : str, optional
719
        Parameter to plot, by default 's'
720
    proj : str, optional
721
        Projection type, by default 'db'
722
    size_per_port : int, optional
723
        by default 4
724
    newfig : bool, optional
725
        by default True
726
    add_titles : bool, optional
727
        by default True
728
    keep_it_tight : bool, optional
729
        by default True
730
    subplot_kw : dict, optional
731
        by default {}
732

733
    Returns
734
    -------
735
    f : :class:`matplotlib.pyplot.Figure`
736
        Matplotlib Figure
737
    ax : :class:`matplotlib.pyplot.Axes`
738
        Matplotlib Axes
739

740
    """
NEW
741
    import matplotlib.pyplot as plt
×
742

743
    subplot_kw = subplot_kw if subplot_kw else {}
×
744
    if newfig:
×
745
        f,axs= plt.subplots(ntwk.nports,ntwk.nports,
×
746
                            figsize =(size_per_port*ntwk.nports,
747
                                      size_per_port*ntwk.nports ),
748
                                      **subplot_kw)
749
    else:
750
        f = plt.gcf()
×
751
        axs = np.array(f.get_axes())
×
752

753
    for ports,ax in zip(ntwk.port_tuples, axs.flatten()):
×
754
        plot_func = ntwk.__getattribute__(f'plot_{param}_{proj}')
×
755
        plot_func(m=ports[0], n=ports[1], ax=ax, **kwargs)
×
756
        if add_titles:
×
757
            ax.set_title(f"{param.upper()}{ntwk._fmt_trace_name(ports[0], ports[1])}")
×
758
    if keep_it_tight:
×
759
       plt.tight_layout()
×
760
    return f, axs
×
761

762

763
def shade_bands(edges: NumberLike, y_range: tuple | None = None,
5✔
764
                cmap: str = 'prism', **kwargs):
765
    r"""
766
    Shades frequency bands.
767

768
    When plotting data over a set of frequency bands it is nice to
769
    have each band visually separated from the other. The kwarg `alpha`
770
    is useful.
771

772
    Parameters
773
    ----------
774
    edges : array-like
775
        x-values separating regions of a given shade
776
    y_range : tuple or None, optional.
777
        y-values to shade in. Default is None.
778
    cmap : str, optional.
779
        see matplotlib.cm  or matplotlib.colormaps for acceptable values.
780
        Default is 'prism'.
781
    \*\*kwargs : key word arguments
782
        passed to `matplotlib.fill_between`
783

784
    Examples
785
    --------
786
    >>> rf.shade_bands([325,500,750,1100], alpha=.2)
787
    """
NEW
788
    import matplotlib.pyplot as plt
×
789

790
    cmap = plt.cm.get_cmap(cmap)
×
NEW
791
    if not isinstance(y_range, tuple | list) or (len(y_range) != 2):
×
792
        y_range=plt.gca().get_ylim()
×
793
    axis = plt.axis()
×
794
    for k in range(len(edges)-1):
×
795
        plt.fill_between(
×
796
            [edges[k],edges[k+1]],
797
            y_range[0], y_range[1],
798
            color = cmap(1.0*k/len(edges)),
799
            **kwargs)
800
    plt.axis(axis)
×
801

802

803
def save_all_figs(dir: str = './', format: None | list[str] = None,
5✔
804
                  replace_spaces: bool = True, echo: bool| None = None):
805
    """
806
    Save all open Figures to disk.
807

808
    Parameters
809
    ----------
810
    dir : string, optional.
811
        path to save figures into. Default is './'
812
    format : None or list of strings, optional.
813
        the types of formats to save figures as. The elements of this
814
        list are passed to :func:`matplotlib.pyplot.savefig`. This is a list so that
815
        you can save each figure in multiple formats. Default is None.
816
    replace_spaces : bool, optional
817
        default is True.
818
    echo : bool, optional.
819
        True prints filenames as they are saved. Default is True.
820
    """
NEW
821
    import matplotlib.pyplot as plt
×
822

823
    if echo is not None:
×
824
        warnings.warn("`echo` parameter is deprecated and will be removed in future versions. "
×
825
                      "Use logging instead.", FutureWarning, stacklevel=2)
826

827
    if dir[-1] != '/':
×
828
        dir = dir + '/'
×
829
    for fignum in plt.get_fignums():
×
830
        fileName = plt.figure(fignum).get_axes()[0].get_title()
×
831
        if replace_spaces:
×
832
            fileName = fileName.replace(' ','_')
×
833
        if fileName == '':
×
834
            fileName = 'unnamedPlot'
×
835
        if format is None:
×
836
            plt.savefig(dir+fileName)
×
837
            logger.debug(f"Saved figure {dir+fileName}")
×
838
        else:
839
            for fmt in format:
×
840
                path = dir+fileName+'.'+fmt
×
841
                plt.savefig(path, format=fmt)
×
842
                logger.debug(f"Saved figure {path}")
×
843

844
saf = save_all_figs
5✔
845

846
@axes_kwarg
5✔
847
def add_markers_to_lines(ax: Axes = None,
5✔
848
                         marker_list: list = None,
849
                         markevery: int = 10):
850
    """
851
    Add markers to existing lings on a plot.
852

853
    Convenient if you have already have a plot made, but then
854
    need to add markers afterwards, so that it can be interpreted in
855
    black and white. The markevery argument makes the markers less
856
    frequent than the data, which is generally what you want.
857

858
    Parameters
859
    ----------
860
    ax : matplotlib.Axes or None, optional
861
        axis which to add markers to.
862
        Default is current axe gca()
863
    marker_list : list of string, optional
864
        list of marker characters. Default is ['o', 'D', 's', '+', 'x'].
865
        see matplotlib.plot help for possible marker characters
866
    markevery : int, optional.
867
        markevery number of points with a marker.
868
        Default is 10.
869

870
    """
871
    marker_list = marker_list if marker_list else ['o', 'D', 's', '+', 'x']
×
872

873
    lines = ax.get_lines()
×
874
    if len(lines) > len (marker_list ):
×
875
        marker_list *= 3
×
876
    [k[0].set_marker(k[1]) for k in zip(lines, marker_list)]
×
877
    [line.set_markevery(markevery) for line in lines]
×
878

879
@axes_kwarg
5✔
880
def legend_off(ax: Axes = None):
5✔
881
    """
882
    Turn off the legend for a given axes.
883

884
    If no axes is given then it will use current axes.
885

886
    Parameters
887
    ----------
888
    ax : matplotlib.Axes or None, optional
889
        axis to operate on.
890
        Default is None for current axe gca()
891
    """
892
    ax.legend_.set_visible(0)
×
893

894
@axes_kwarg
5✔
895
def scrape_legend(n: int | None = None,
5✔
896
                  ax: Axes = None):
897
    """
898
    Scrape a legend with redundant labels.
899

900
    Given a legend of m entries of n groups, this will remove all but
901
    every m/nth entry. This is used when you plot many lines representing
902
    the same thing, and only want one label entry in the legend  for the
903
    whole ensemble of lines.
904

905
    Parameters
906
    ----------
907
    n : int or None, optional.
908
        Default is None.
909
    ax : matplotlib.Axes or None, optional
910
        axis to operate on.
911
        Default is None for current axe gca()
912
    """
913

914
    handles, labels = ax.get_legend_handles_labels()
×
915

916
    if n is None:
×
917
        n =len ( set(labels))
×
918

919
    if n>len(handles):
×
920
        raise ValueError('number of entries is too large')
×
921

922
    k_list = [int(k) for k in np.linspace(0,len(handles)-1,n)]
×
923
    ax.legend([handles[k] for k in k_list], [labels[k] for k in k_list])
×
924

925

926
def func_on_all_figs(func: Callable, *args, **kwargs):
5✔
927
    r"""
928
    Run a function after making all open figures current.
929

930
    Useful if you need to change the properties of many open figures
931
    at once, like turn off the grid.
932

933
    Parameters
934
    ----------
935
    func : function
936
        function to call
937
    \*args, \*\*kwargs : passed to func
938

939
    Examples
940
    --------
941
    >>> rf.func_on_all_figs(grid, alpha=.3)
942
    """
NEW
943
    import matplotlib.pyplot as plt
×
944

945
    for fig_n in plt.get_fignums():
×
946
        fig = plt.figure(fig_n)
×
947
        for ax_n in fig.axes:
×
948
            fig.add_axes(ax_n) # trick to make axes current
×
949
            func(*args, **kwargs)
×
950
            plt.draw()
×
951

952
foaf = func_on_all_figs
5✔
953

954

955
def plot_vector(a: complex, off: complex = 0+0j, **kwargs):
5✔
956
    """
957
    Plot a 2d vector.
958

959
    Parameters
960
    ----------
961
    a : complex
962
        complex coordinates (real for X, imag for Y) of the arrow location.
963
    off : complex, optional
964
        complex direction (real for U, imag for V) components
965
        of the arrow vectors, by default 0+0j
966

967
    Returns
968
    -------
969
    quiver : matplotlib.pyplot.quiver
970
    """
NEW
971
    import matplotlib.pyplot as plt
×
972

NEW
973
    return plt.quiver(off.real, off.imag, a.real, a.imag, scale_units='xy',
×
974
           angles='xy', scale=1, **kwargs)
975

976

977
def colors() -> list[str]:
5✔
978
    """
979
    Return the list of colors of the rcParams color cycle.
980

981
    Returns
982
    -------
983
    colors : List[str]
984
    """
NEW
985
    import matplotlib.pyplot as plt
×
986

NEW
987
    return [c['color'] for c in plt.rcParams['axes.prop_cycle']]
×
988

989

990
## specific plotting functions
991
def plot(netw: Network, *args, **kw):
5✔
992
    """
993
    Plot something vs frequency
994
    """
995
    return netw.frequency.plot(*args, **kw)
×
996

997

998
def plot_passivity(netw: Network, port=None, label_prefix=None, **kwargs):
5✔
999
    """
1000
    Plot dB(diag(passivity metric)) vs frequency.
1001

1002
    Note
1003
    ----
1004
    This plot does not completely capture the passivity metric, which
1005
    is a test for `unitary-ness` of the s-matrix. However, it may
1006
    be used to display a measure of power dissipated in a network.
1007

1008
    See Also
1009
    --------
1010
    passivity
1011
    """
1012
    import matplotlib.pyplot as plt
5✔
1013

1014
    name = '' if netw.name is None else netw.name
5✔
1015

1016
    if port is None:
5✔
1017
        ports = range(netw.nports)
5✔
1018
    else:
1019
        ports = [port]
×
1020
    for k in ports:
5✔
1021
        if label_prefix is None:
5✔
1022
            label = name + ', port %i' % (k + 1)
5✔
1023
        else:
1024
            label = label_prefix + ', port %i' % (k + 1)
×
1025
        netw.frequency.plot(mf.complex_2_db(netw.passivity[:, k, k]),
5✔
1026
                            label=label,
1027
                            **kwargs)
1028

1029
    plt.legend()
5✔
1030
    if plt.isinteractive():
5✔
1031
        plt.draw()
×
1032

1033

1034
def plot_reciprocity(netw: Network, db=False, *args, **kwargs):
5✔
1035
    """
1036
    Plot reciprocity metric.
1037

1038
    See Also
1039
    --------
1040
    reciprocity
1041
    """
1042
    import matplotlib.pyplot as plt
5✔
1043

1044
    for m in range(netw.nports):
5✔
1045
        for n in range(netw.nports):
5✔
1046
            if m > n:
5✔
1047
                if 'label' not in kwargs.keys():
5✔
1048
                    kwargs['label'] = f"ports {netw._fmt_trace_name(m, n)}"
5✔
1049
                y = netw.reciprocity[:, m, n].flatten()
5✔
1050
                y = mf.complex_2_db(y) if db else np.abs(y)
5✔
1051
                netw.frequency.plot(y, *args, **kwargs)
5✔
1052

1053
    plt.legend()
5✔
1054
    if plt.isinteractive():
5✔
1055
        plt.draw()
×
1056

1057

1058
def plot_reciprocity2(netw: Network, db=False, *args, **kwargs):
5✔
1059
    """
1060
    Plot reciprocity metric #2.
1061

1062
    This is distance of the determinant of the wave-cascading matrix
1063
    from unity.
1064

1065
    .. math::
1066

1067
            abs(1 - S/S^T )
1068

1069

1070

1071
    See Also
1072
    --------
1073
    reciprocity
1074
    """
1075
    import matplotlib.pyplot as plt
5✔
1076

1077
    for m in range(netw.nports):
5✔
1078
        for n in range(netw.nports):
5✔
1079
            if m > n:
5✔
1080
                if 'label' not in kwargs.keys():
5✔
1081
                    kwargs['label'] = f"ports {netw._fmt_trace_name(m, n)}"
5✔
1082
                y = netw.reciprocity2[:, m, n].flatten()
5✔
1083
                if db:
5✔
1084
                    y = mf.complex_2_db(y)
×
1085
                netw.frequency.plot(y, *args, **kwargs)
5✔
1086

1087
    plt.legend()
5✔
1088
    if plt.isinteractive():
5✔
1089
        plt.draw()
×
1090

1091

1092
def plot_s_db_time(netw: Network, *args, window: str | float | tuple[str, float]=('kaiser', 6),
5✔
1093
        normalize: bool = True, center_to_dc: bool = None, **kwargs):
1094
    return netw.windowed(window, normalize, center_to_dc).plot_s_time_db(*args,**kwargs)
5✔
1095

1096

1097
# plotting
1098
def plot_s_smith(netw: Network, m=None, n=None,r=1, ax=None, show_legend=True,\
5✔
1099
        chart_type='z', draw_labels=False, label_axes=False, draw_vswr=None, *args,**kwargs):
1100
    r"""
1101
    Plots the scattering parameter on a smith chart.
1102

1103
    Plots indices `m`, `n`, where `m` and `n` can be integers or
1104
    lists of integers.
1105

1106

1107
    Parameters
1108
    ----------
1109
    m : int, optional
1110
            first index
1111
    n : int, optional
1112
            second index
1113
    ax : matplotlib.Axes object, optional
1114
            axes to plot on. in case you want to update an existing
1115
            plot.
1116
    show_legend : boolean, optional
1117
            to turn legend show legend of not, optional
1118
    chart_type : ['z','y']
1119
        draw impedance or admittance contours
1120
    draw_labels : Boolean
1121
        annotate chart with impedance values
1122
    label_axes : Boolean
1123
        Label axis with titles `Real` and `Imaginary`
1124
    border : Boolean
1125
        draw rectangular border around image with ticks
1126
    draw_vswr : list of numbers, Boolean or None
1127
        draw VSWR circles. If True, default values are used.
1128

1129
    \*args : arguments, optional
1130
            passed to the matplotlib.plot command
1131
    \*\*kwargs : keyword arguments, optional
1132
            passed to the matplotlib.plot command
1133

1134

1135
    See Also
1136
    --------
1137
    plot_vs_frequency_generic - generic plotting function
1138
    smith -  draws a smith chart
1139

1140
    Examples
1141
    --------
1142
    >>> myntwk.plot_s_smith()
1143
    >>> myntwk.plot_s_smith(m=0,n=1,color='b', marker='x')
1144
    """
1145
    # TODO: prevent this from re-drawing smith chart if one already
1146
    # exists on current set of axes
1147

1148
    # get current axis if user doesn't supply and axis
1149
    import matplotlib.pyplot as plt
5✔
1150

1151
    if ax is None:
5✔
1152
        ax = plt.gca()
5✔
1153

1154

1155
    if m is None:
5✔
1156
        M = range(netw.number_of_ports)
5✔
1157
    else:
1158
        M = [m]
5✔
1159
    if n is None:
5✔
1160
        N = range(netw.number_of_ports)
5✔
1161
    else:
1162
        N = [n]
5✔
1163

1164
    if 'label'  not in kwargs.keys():
5✔
1165
        generate_label=True
5✔
1166
    else:
1167
        generate_label=False
×
1168

1169
    for m in M:
5✔
1170
        for n in N:
5✔
1171
            # set the legend label for this trace to the networks name if it
1172
            # exists, and they didn't pass a name key in the kwargs
1173
            if generate_label:
5✔
1174
                kwargs['label'] = _get_label_str(netw, "S", m, n)
5✔
1175

1176
            # plot the desired attribute vs frequency
1177
            if len (ax.patches) == 0:
5✔
1178
                smith(ax=ax, smithR = r, chart_type=chart_type, draw_labels=draw_labels, draw_vswr=draw_vswr)
5✔
1179
            ax.plot(netw.s[:,m,n].real,  netw.s[:,m,n].imag, *args,**kwargs)
5✔
1180

1181
    #draw legend
1182
    if show_legend:
5✔
1183
        ax.legend()
5✔
1184
    ax.axis(np.array([-1.1,1.1,-1.1,1.1])*r)
5✔
1185

1186
    if label_axes:
5✔
1187
        ax.set_xlabel('Real')
×
1188
        ax.set_ylabel('Imaginary')
×
1189

1190

1191
def plot_it_all(netw: Network, *args, **kwargs):
5✔
1192
    r"""
1193
    Plot dB, deg, smith, and complex in subplots.
1194

1195
    Plots the magnitude in dB in subplot 1, the phase in degrees in
1196
    subplot 2, a smith chart in subplot 3, and a complex plot in
1197
    subplot 4.
1198

1199
    Parameters
1200
    ----------
1201
    \*args : arguments, optional
1202
            passed to the matplotlib.plot command
1203
    \*\*kwargs : keyword arguments, optional
1204
            passed to the matplotlib.plot command
1205

1206
    See Also
1207
    --------
1208
    plot_s_db - plot magnitude (in dB) of s-parameters vs frequency
1209
    plot_s_deg - plot phase of s-parameters (in degrees) vs frequency
1210
    plot_s_smith - plot complex s-parameters on smith chart
1211
    plot_s_complex - plot complex s-parameters in the complex plane
1212

1213
    Examples
1214
    --------
1215
    >>> from skrf.data import ring_slot
1216
    >>> ring_slot.plot_it_all()
1217
    """
1218
    import matplotlib.pyplot as plt
5✔
1219

1220
    plt.clf()
5✔
1221
    plt.subplot(221)
5✔
1222
    netw.plot_s_db(*args, **kwargs)
5✔
1223
    plt.subplot(222)
5✔
1224
    netw.plot_s_deg(*args, **kwargs)
5✔
1225
    plt.subplot(223)
5✔
1226
    netw.plot_s_smith(*args, **kwargs)
5✔
1227
    plt.subplot(224)
5✔
1228
    netw.plot_s_complex(*args, **kwargs)
5✔
1229

1230

1231
def stylely(rc_dict: dict = None, style_file: str = 'skrf.mplstyle'):
5✔
1232
    """
1233
    Loads the rc-params from the specified file (file must be located in skrf/data).
1234

1235
    Parameters
1236
    ----------
1237
    rc_dict : dict, optional
1238
        rc dict passed to :func:`matplotlib.rc`, by default {}
1239
    style_file : str, optional
1240
        style file, by default 'skrf.mplstyle'
1241
    """
1242
    try:
5✔
1243
        import matplotlib.pyplot as plt
5✔
1244

1245
        from .data import pwd  # delayed to solve circular import
5✔
1246

1247
        rc_dict = rc_dict if rc_dict else {}
5✔
1248

1249

1250
        plt.style.use(os.path.join(pwd, style_file))
5✔
1251
        plt.rc(rc_dict)
5✔
NEW
1252
    except ImportError as e:
×
NEW
1253
        warnings.warn(f"Could not import matplotlib: {e}", ImportWarning, stacklevel=2)
×
1254

1255

1256
# Network Set Plotting Commands
1257
def animate(self: NetworkSet, attr: str = 's_deg', ylims: tuple = (-5, 5),
5✔
1258
            xlims: tuple | None = None, show: bool = True,
1259
            savefigs: bool = False, dir_: str = '.', *args, **kwargs):
1260
    r"""
1261
    Animate a property of the networkset.
1262

1263
    This loops through all elements in the NetworkSet and calls
1264
    a plotting attribute (ie Network.plot_`attr`), with given \*args
1265
    and \*\*kwargs.
1266

1267
    Parameters
1268
    ----------
1269
    attr : str, optional
1270
        plotting property of a Network (ie 's_db', 's_deg', etc)
1271
        Default is 's_deg'
1272
    ylims : tuple, optional
1273
        passed to ylim. needed to have consistent y-limits across frames.
1274
        Default is (-5 ,5).
1275
    xlims : tuple or None, optional.
1276
        passed to xlim. Default is None.
1277
    show : bool, optional
1278
        show each frame as its animated. Default is True.
1279
    savefigs : bool, optional
1280
        save each frame as a png. Default is False.
1281

1282
    \*args, \*\*kwargs :
1283
        passed to the Network plotting function
1284

1285
    Note
1286
    ----
1287
    using `label=None` will speed up animation significantly,
1288
    because it prevents the legend from drawing
1289

1290
    to create video paste this:
1291

1292
        !avconv -r 10 -i out_%5d.png  -vcodec huffyuv out.avi
1293

1294
    or (depending on your ffmpeg version)
1295

1296
        !ffmpeg -r 10 -i out_%5d.png  -vcodec huffyuv out.avi
1297

1298
    Examples
1299
    --------
1300
    >>> ns.animate('s_deg', ylims=(-5,5), label=None)
1301

1302
    """
NEW
1303
    import matplotlib.pyplot as plt
×
1304

1305
    was_interactive = plt.isinteractive()
×
1306
    plt.ioff()
×
1307

1308
    for idx, k in enumerate(self):
×
1309
        plt.clf()
×
1310
        if 'time' in attr:
×
1311
            tmp_ntwk = k.windowed()
×
1312
            tmp_ntwk.__getattribute__('plot_' + attr)(*args, **kwargs)
×
1313
        else:
1314
            k.__getattribute__('plot_' + attr)(*args, **kwargs)
×
1315
        if ylims is not None:
×
1316
            plt.ylim(ylims)
×
1317
        if xlims is not None:
×
1318
            plt.xlim(xlims)
×
1319
        # rf.legend_off()
1320
        plt.draw()
×
1321
        if show:
×
1322
            plt.show()
×
1323
        if savefigs:
×
1324
            fname = os.path.join(dir_, 'out_%.5i' % idx + '.png')
×
1325
            plt.savefig(fname)
×
1326

1327
    if was_interactive:
×
1328
        plt.ion()
×
1329

1330

1331
#------------------------------
1332
#
1333
# NetworkSet plotting functions
1334
#
1335
#------------------------------
1336

1337
@axes_kwarg
5✔
1338
def plot_uncertainty_bounds_component(
5✔
1339
        self: NetworkSet, attribute: PrimaryPropertiesT,
1340
        m: int | None = None, n: int | None = None, *,
1341
        type: str = 'shade', n_deviations: int = 3,
1342
        alpha: float = .3, color_error: str | None = None,
1343
        markevery_error: int = 20, ax: Axes = None,
1344
        ppf: bool = None, kwargs_error: dict = None,
1345
        **kwargs):
1346
    r"""
1347
    Plot mean value of a NetworkSet with +/- uncertainty bounds in an Network's attribute.
1348

1349
    This is designed to represent uncertainty in a scalar component of the s-parameter.
1350
    for example plotting the uncertainty in the magnitude would be expressed by,
1351

1352
    .. math::
1353

1354
        mean(|s|) \pm std(|s|)
1355

1356
    The order of mean and abs is important.
1357

1358

1359
    Parameters
1360
    ----------
1361
    attribute : str
1362
        attribute of Network type to analyze
1363
    m : int or None
1364
        first index of attribute matrix. Default is None (all)
1365
    n : int or None
1366
        second index of attribute matrix. Default is None (all)
1367
    type : str
1368
        ['shade' | 'bar'], type of plot to draw
1369
    n_deviations : int
1370
        number of std deviations to plot as bounds
1371
    alpha : float
1372
        passed to matplotlib.fill_between() command. [number, 0-1]
1373
    color_error : str
1374
        color of the +- std dev fill shading. Default is None.
1375
    markevery_error : float
1376
        tbd
1377
    type : str
1378
        if type=='bar', this controls frequency of error bars
1379
    ax : matplotlib axes object
1380
        Axes to plot on. Default is None.
1381
    ppf : function
1382
        post processing function. a function applied to the
1383
        upper and lower bounds. Default is None
1384
     kwargs_error : dict
1385
         dictionary of kwargs to pass to the fill_between or
1386
         errorbar plot command depending on value of type.
1387
    \*\*kwargs :
1388
        passed to Network.plot_s_re command used to plot mean response
1389

1390
    Note
1391
    ----
1392
    For phase uncertainty you probably want s_deg_unwrap, or
1393
    similar. uncertainty for wrapped phase blows up at +-pi.
1394

1395
    """
1396

1397
    kwargs_error = kwargs_error if kwargs_error else {}
5✔
1398

1399
    if m is None:
5✔
1400
        M = range(self[0].number_of_ports)
5✔
1401
    else:
1402
        M = [m]
×
1403
    if n is None:
5✔
1404
        N = range(self[0].number_of_ports)
5✔
1405
    else:
1406
        N = [n]
×
1407

1408
    for m in M:
5✔
1409
        for n in N:
5✔
1410

1411
            plot_attribute = attribute
5✔
1412

1413
            ntwk_mean = self.__getattribute__('mean_'+attribute)
5✔
1414
            ntwk_std = self.__getattribute__('std_'+attribute)
5✔
1415
            ntwk_std.s = n_deviations * ntwk_std.s
5✔
1416

1417
            upper_bound = (ntwk_mean.s[:, m, n] + ntwk_std.s[:, m, n]).squeeze()
5✔
1418
            lower_bound = (ntwk_mean.s[:, m, n] - ntwk_std.s[:, m, n]).squeeze()
5✔
1419

1420
            if ppf is not None:
5✔
1421
                if type == 'bar':
5✔
1422
                    raise NotImplementedError('the \'ppf\' options don\'t work correctly with the bar-type error plots')
×
1423
                ntwk_mean.s = ppf(ntwk_mean.s)
5✔
1424
                upper_bound = ppf(upper_bound)
5✔
1425
                lower_bound = ppf(lower_bound)
5✔
1426
                lower_bound[np.isnan(lower_bound)] = min(lower_bound)
5✔
1427
                if ppf in [mf.magnitude_2_db, mf.mag_2_db]:  # fix of wrong ylabels due to usage of ppf for *_db plots
5✔
1428
                    if attribute == 's_mag':
5✔
1429
                        plot_attribute = 's_db'
5✔
1430
                    elif attribute == 's_time_mag':
5✔
1431
                        plot_attribute = 's_time_db'
5✔
1432

1433
            if type == 'shade':
5✔
1434
                ntwk_mean.plot_s_re(ax=ax, m=m, n=n, **kwargs)
5✔
1435
                if color_error is None:
5✔
1436
                    color_error = ax.get_lines()[-1].get_color()
5✔
1437
                ax.fill_between(ntwk_mean.frequency.f,
5✔
1438
                                lower_bound.real, upper_bound.real, alpha=alpha, color=color_error,
1439
                                **kwargs_error)
1440
                # ax.plot(ntwk_mean.frequency.f_scaled, ntwk_mean.s[:,m,n],*args,**kwargs)
1441

1442
            elif type == 'bar':
×
1443
                ntwk_mean.plot_s_re(ax=ax, m=m, n=n, **kwargs)
×
1444
                if color_error is None:
×
1445
                    color_error = ax.get_lines()[-1].get_color()
×
1446
                ax.errorbar(ntwk_mean.frequency.f[::markevery_error],
×
1447
                            ntwk_mean.s_re[:, m, n].squeeze()[::markevery_error],
1448
                            yerr=ntwk_std.s_mag[:, m, n].squeeze()[::markevery_error],
1449
                            color=color_error, **kwargs_error)
1450

1451
            else:
1452
                raise(ValueError('incorrect plot type'))
×
1453

1454
            ax.set_ylabel(self[0].Y_LABEL_DICT.get(plot_attribute[2:], ''))  # use only the function of the attribute
5✔
1455
            scale_frequency_ticks(ax, ntwk_mean.frequency.unit)
5✔
1456
            ax.axis('tight')
5✔
1457

1458
@axes_kwarg
5✔
1459
def plot_minmax_bounds_component(self: NetworkSet, attribute: PrimaryPropertiesT, m: int = 0, n: int = 0,
5✔
1460
                                 *, type: str = 'shade',
1461
                                 alpha: float = .3, color_error: str | None = None,
1462
                                 markevery_error: int = 20, ax: Axes = None,
1463
                                 ppf: bool = None, kwargs_error: dict = None,
1464
                                 **kwargs):
1465
    r"""
1466
    Plots mean value of the NetworkSet with minimum and maximum bounds in an Network's attribute.
1467

1468
    This is designed to represent min/max in a scalar component of the s-parameter. For example
1469
    plotting the min/max in the magnitude would be expressed by
1470

1471
    .. math::
1472

1473
        min(|s|)
1474

1475
        mean(|s|)
1476

1477
        max(|s|)
1478

1479
    The order of mean and abs is important.
1480

1481
    Parameters
1482
    ----------
1483
    attribute : str
1484
        attribute of Network type to analyze
1485
    m : int
1486
        first index of attribute matrix
1487
    n : int
1488
        second index of attribute matrix
1489
    type : str
1490
        ['shade' | 'bar'], type of plot to draw
1491
    alpha : float
1492
        passed to matplotlib.fill_between() command. [number, 0-1]
1493
    color_error : str
1494
        color of the min/max fill shading. Default is None.
1495
    markevery_error : float
1496
        tbd
1497
    type : str
1498
        if type=='bar', this controls frequency of error bars
1499
    ax : matplotlib axes object
1500
        Axes to plot on. Default is None.
1501
    ppf : function
1502
        post processing function. a function applied to the
1503
        upper and lower bounds. Default is None
1504
     kwargs_error : dict
1505
         dictionary of kwargs to pass to the fill_between or
1506
         errorbar plot command depending on value of type.
1507
    \*\*kwargs :
1508
        passed to Network.plot_s_re command used to plot mean response
1509

1510
    Note
1511
    ----
1512
    For phase uncertainty you probably want s_deg_unwrap, or
1513
    similar.  Uncertainty for wrapped phase blows up at +-pi.
1514

1515
    """
1516

1517
    kwargs_error = kwargs_error if kwargs_error else {}
5✔
1518

1519
    ntwk_mean = self.__getattribute__('mean_'+attribute)
5✔
1520
    ntwk_std = self.__getattribute__('std_'+attribute)
5✔
1521

1522
    lower_bound = self.__getattribute__('min_'+attribute).s_re[:,m,n].squeeze()
5✔
1523
    upper_bound = self.__getattribute__('max_'+attribute).s_re[:,m,n].squeeze()
5✔
1524

1525
    if ppf is not None:
5✔
1526
        if type =='bar':
5✔
1527
            raise NotImplementedError('the \'ppf\' options don\'t work correctly with the bar-type error plots')
×
1528
        ntwk_mean.s = ppf(ntwk_mean.s)
5✔
1529
        upper_bound = ppf(upper_bound)
5✔
1530
        lower_bound = ppf(lower_bound)
5✔
1531
        lower_bound[np.isnan(lower_bound)]=min(lower_bound)
5✔
1532
        if ppf in [mf.magnitude_2_db, mf.mag_2_db]: # quickfix of wrong ylabels due to usage of ppf for *_db plots
5✔
1533
            if attribute == 's_mag':
5✔
1534
                attribute = 's_db'
5✔
1535
            elif attribute == 's_time_mag':
5✔
1536
                attribute = 's_time_db'
5✔
1537

1538
    if type == 'shade':
5✔
1539
        ntwk_mean.plot_s_re(ax=ax,m=m,n=n, **kwargs)
5✔
1540
        if color_error is None:
5✔
1541
            color_error = ax.get_lines()[-1].get_color()
5✔
1542
        ax.fill_between(ntwk_mean.frequency.f,
5✔
1543
                        lower_bound, upper_bound, alpha=alpha, color=color_error,
1544
                        **kwargs_error)
1545
        #ax.plot(ntwk_mean.frequency.f_scaled,ntwk_mean.s[:,m,n],*args,**kwargs)
1546
    elif type =='bar':
×
1547
        raise (NotImplementedError)
×
1548
        ntwk_mean.plot_s_re(ax=ax, m=m, n=n, **kwargs)
1549
        if color_error is None:
1550
            color_error = ax.get_lines()[-1].get_color()
1551
        ax.errorbar(ntwk_mean.frequency.f[::markevery_error],
1552
                    ntwk_mean.s_re[:,m,n].squeeze()[::markevery_error],
1553
                    yerr=ntwk_std.s_mag[:,m,n].squeeze()[::markevery_error],
1554
                    color=color_error,**kwargs_error)
1555

1556
    else:
1557
        raise(ValueError('incorrect plot type'))
×
1558

1559
    ax.set_ylabel(self[0].Y_LABEL_DICT.get(attribute[2:], ''))  # use only the function of the attribute
5✔
1560
    scale_frequency_ticks(ax, ntwk_mean.frequency.unit)
5✔
1561
    ax.axis('tight')
5✔
1562

1563
@axes_kwarg
5✔
1564
def plot_violin(self: NetworkSet, attribute: PrimaryPropertiesT, m: int = 0, n: int = 0,
5✔
1565
                         *, widths: float = None, showmeans: bool = True,
1566
                         showextrema: bool = True, showmedians: bool = False,
1567
                         quantiles = None, points: int = 100, bw_method = None,
1568
                         ax: Axes = None, **kwargs
1569
    ):
1570
    r"""Plots the violin plot of the network set for the desired attribute.
1571

1572
    A violin plot provides the distribution of the attribute at each frequency point, and optionally the
1573
    extrema, mean, and median. The plot becomes cluttered quickly with many frequencies, so reducing the number
1574
    with :meth:`NetworkSet.interpolate_frequency` is recommended.
1575

1576
    Parameters
1577
    ----------
1578
    attribute : str
1579
        attribute of Network type to analyze
1580
    m : int
1581
        first index of attribute matrix
1582
    n : int
1583
        second index of attribute matrix
1584
    widths : float
1585
        The maximum width of each violin in units of the positions axis.
1586
        The default is 0.75 of the distance between the first two frequencies.
1587
    showmeans : bool
1588
        Whether to show the mean with a line.
1589
    showextrema : bool
1590
        Whether to show the extrema with a line.
1591
    showmedians : bool
1592
        Whether to show the median with a line.
1593
    quantiles : ArrayLike
1594
        If not None, set a list of floats in interval [0, 1] for each violin,
1595
        which stands for the quantiles that will be rendered for that violin.
1596
    points : int
1597
        The number of points to evaluate each of the gaussian kernel density estimations at.
1598
    bw_method : {'scott', 'silverman'} or float or callable, default: 'scott'
1599
        _description_. Defaults to None.
1600
    ax : matplotlib axes object
1601
        Axes to plot on. Default is None.
1602
    \*\*kwargs :
1603
        passed to :meth:`matplotlib.pyplot.violinplot`
1604

1605
    Note
1606
    ----
1607
    For phase plots you probably want s_deg_unwrap, or
1608
    similar.  Uncertainty for wrapped phase blows up at +-pi.
1609
    """
1610

1611
    freq = self.ntwk_set[0].f
5✔
1612

1613
    # default widths to 3/4 distance between frequencies
1614
    if not widths and len(freq) > 1:
5✔
1615
        widths = (freq[1]-freq[0])*0.75
5✔
1616
    elif not widths:
×
1617
        widths = 0.5
×
1618

1619
    data = np.array([getattr(p, attribute)[:,m,n] for p in self.ntwk_set])
5✔
1620

1621
    ax.violinplot(data, freq, widths=widths, showmeans=showmeans, showextrema=showextrema,
5✔
1622
                  showmedians=showmedians, quantiles=quantiles, points=points, bw_method=bw_method, **kwargs)
1623

1624
    ax.set_xlabel(f'Frequency ({self.ntwk_set[0].frequency.unit})')
5✔
1625
    ax.set_ylabel(self[0].Y_LABEL_DICT.get(attribute[2:], ''))  # use only the function of the attribute
5✔
1626
    scale_frequency_ticks(ax, self.ntwk_set[0].frequency.unit)
5✔
1627
    ax.axis('tight')
5✔
1628

1629
def plot_uncertainty_bounds_s_db(self: NetworkSet, *args, **kwargs):
5✔
1630
    """
1631
    Call ``plot_uncertainty_bounds(attribute='s_mag','ppf':mf.magnitude_2_db*args,**kwargs)``.
1632

1633
    See plot_uncertainty_bounds for help.
1634

1635
    """
1636
    kwargs.update({'ppf':mf.magnitude_2_db})
5✔
1637
    self.plot_uncertainty_bounds_component("s_mag", *args,**kwargs)
5✔
1638

1639
def plot_minmax_bounds_s_db(self: NetworkSet, *args, **kwargs):
5✔
1640
    """
1641
    Call ``plot_uncertainty_bounds(attribute= 's_mag','ppf':mf.magnitude_2_db*args,**kwargs)``.
1642

1643
    See plot_uncertainty_bounds for help.
1644

1645
    """
1646
    kwargs.update({'ppf':mf.magnitude_2_db})
5✔
1647
    self.plot_minmax_bounds_component("s_mag", *args,**kwargs)
5✔
1648

1649
def plot_minmax_bounds_s_db10(self: NetworkSet, *args, **kwargs):
5✔
1650
    """
1651
    Call ``plot_uncertainty_bounds(attribute= 's_mag','ppf':mf.magnitude_2_db*args,**kwargs)``.
1652

1653
    see plot_uncertainty_bounds for help
1654

1655
    """
1656
    kwargs.update({'ppf':mf.mag_2_db10})
5✔
1657
    self.plot_minmax_bounds_component("s_mag", *args,**kwargs)
5✔
1658

1659
def plot_uncertainty_bounds_s_time_db(self: NetworkSet, *args, **kwargs):
5✔
1660
    """
1661
    Call ``plot_uncertainty_bounds(attribute= 's_mag','ppf':mf.magnitude_2_db*args,**kwargs)``.
1662

1663
    See plot_uncertainty_bounds for help.
1664

1665
    """
1666
    kwargs.update({'ppf':mf.magnitude_2_db})
5✔
1667
    self.plot_uncertainty_bounds_component("s_time_mag", *args,**kwargs)
5✔
1668

1669
def plot_minmax_bounds_s_time_db(self: NetworkSet, *args, **kwargs):
5✔
1670
    """
1671
    Call ``plot_uncertainty_bounds(attribute= 's_mag','ppf':mf.magnitude_2_db*args,**kwargs)``.
1672

1673
    See plot_uncertainty_bounds for help.
1674

1675
    """
1676
    kwargs.update({'ppf':mf.magnitude_2_db})
5✔
1677
    self.plot_minmax_bounds_component("s_time_mag", *args, **kwargs)
5✔
1678

1679
def plot_uncertainty_decomposition(self: NetworkSet, m: int = 0, n: int = 0):
5✔
1680
    """
1681
    Plot the total and component-wise uncertainty.
1682

1683
    Parameters
1684
    ----------
1685
    m : int
1686
        first s-parameters index
1687
    n :
1688
        second s-parameter index
1689

1690
    """
1691
    import matplotlib.pyplot as plt
5✔
1692
    if self.name is not None:
5✔
1693
        plt.title(f"Uncertainty Decomposition: {self.name} $S_{{{self.ntwk_set[0]._fmt.trace_name(m,n)}}}$")
×
1694
    self.std_s.plot_s_mag(label='Distance', m=m,n=n)
5✔
1695
    self.std_s_re.plot_s_mag(label='Real',  m=m,n=n)
5✔
1696
    self.std_s_im.plot_s_mag(label='Imaginary',  m=m,n=n)
5✔
1697
    self.std_s_mag.plot_s_mag(label='Magnitude',  m=m,n=n)
5✔
1698
    self.std_s_arcl.plot_s_mag(label='Arc-length',  m=m,n=n)
5✔
1699

1700
def plot_logsigma(self: NetworkSet, label_axis: bool = True, *args,**kwargs):
5✔
1701
    r"""
1702
    Plot the uncertainty for the set in units of log-sigma.
1703

1704
    Log-sigma is the complex standard deviation, plotted in units
1705
    of dB's.
1706

1707
    Parameters
1708
    ----------
1709
    label_axis : bool, optional
1710
        Default is True.
1711
    \*args, \*\*kwargs : arguments
1712
        passed to self.std_s.plot_s_db()
1713
    """
NEW
1714
    import matplotlib.pyplot as plt
×
1715

1716
    self.std_s.plot_s_db(*args,**kwargs)
×
1717
    if label_axis:
×
1718
        plt.ylabel('Standard Deviation(dB)')
×
1719

1720

1721
def signature(self: NetworkSet, m: int = 0, n: int = 0, component: str = 's_mag',
5✔
1722
              vmax: Number | None = None, vs_time: bool = False,
1723
              cbar_label: str | None = None,
1724
              *args, **kwargs):
1725
    r"""
1726
    Visualization of a NetworkSet.
1727

1728
    Creates a colored image representing the some component
1729
    of each Network in the  NetworkSet, vs frequency.
1730

1731
    Parameters
1732
    ------------
1733
    m : int, optional
1734
        first s-parameters index. Default is 0.
1735
    n : int, optional
1736
        second s-parameter index. Default is 0.
1737
    component : ['s_mag','s_db','s_deg' ..]
1738
        scalar component of Network to visualize. should
1739
        be a property of the Network object.
1740
    vmax : number or None.
1741
        sets upper limit of colorbar, if None, will be set to
1742
        3*mean of the magnitude of the complex difference.
1743
        Default is None.
1744
    vs_time: Boolean, optional.
1745
        if True, then we assume each Network.name was made with
1746
        rf.now_string, and we make the y-axis a datetime axis.
1747
        Default is False.
1748
    cbar_label: String or None, optional
1749
        label for the colorbar. Default is None
1750
    \*args,\*\*kw : arguments, keyword arguments
1751
        passed to :func:`~pylab.imshow`
1752
    """
1753
    import matplotlib.pyplot as plt
5✔
1754
    from matplotlib.dates import date2num
5✔
1755

1756
    mat = np.array([self[k].__getattribute__(component)[:, m, n] \
5✔
1757
                     for k in range(len(self))])
1758

1759
    # if vmax is None:
1760
    #    vmax = 3*mat.mean()
1761

1762
    if vs_time:
5✔
1763
        # create a datetime index
1764
        dt_idx = [now_string_2_dt(k.name) for k in self]
×
1765
        mpl_times = date2num(dt_idx)
×
1766
        y_max = mpl_times[0]
×
1767
        y_min = mpl_times[-1]
×
1768

1769
    else:
1770
        y_min = len(self)
5✔
1771
        y_max = 0
5✔
1772

1773
    # creates x and y scales
1774
    freq = self[0].frequency
5✔
1775
    extent = [freq.f_scaled[0], freq.f_scaled[-1], y_min, y_max]
5✔
1776

1777
    # set default imshow kwargs
1778
    kw = {'extent': extent, 'aspect': 'auto', 'interpolation': 'nearest',
5✔
1779
          'vmax': vmax}
1780
    # update the users kwargs
1781
    kw.update(kwargs)
5✔
1782
    img = plt.imshow(mat, *args, **kw)
5✔
1783

1784
    if vs_time:
5✔
1785
        ax = plt.gca()
×
1786
        ax.yaxis_date()
×
1787
        # date_format = plt.DateFormatter('%M:%S.%f')
1788
        # ax.yaxis.set_major_formatter(date_format)
1789
        # cbar.set_label('Magnitude (dB)')
1790
        plt.ylabel('Time')
×
1791
    else:
1792
        plt.ylabel('Network #')
5✔
1793

1794
    plt.grid(0)
5✔
1795
    freq.labelXAxis()
5✔
1796

1797
    cbar = plt.colorbar()
5✔
1798
    if cbar_label is not None:
5✔
1799
        cbar.set_label(cbar_label)
×
1800

1801
    return img
5✔
1802

1803
def plot_contour(freq: Frequency,
5✔
1804
                 x: NumberLike, y: NumberLike, z: NumberLike,
1805
                 min0max1: int, graph: bool = True,
1806
                 cmap: str = 'plasma_r', title: str = '',
1807
                 **kwargs):
1808
    r"""
1809
    Create a contour plot.
1810

1811
    Parameters
1812
    ----------
1813
    freq : :skrf.Frequency:
1814
        Frequency object.
1815
    x : array
1816
        x points
1817
    y : array
1818
        y points.
1819
    z : array
1820
        z points.
1821
    min0max1 : int
1822
        0 for min, 1 for max.
1823
    graph : bool, optional
1824
        plot graph if True. The default is True.
1825
    cmap : str, optional
1826
        Colormap label. The default is 'plasma_r'.
1827
    title : str, optional
1828
        Figure title. The default is ''.
1829
    \*\*kwargs : dict
1830
        Other parameters passed to `matplotlib.plot()`.
1831

1832
    Returns
1833
    -------
1834
    GAMopt : :skrf.Network:
1835
        Network
1836
    VALopt : float
1837
        min or max.
1838

1839
    """
1840
    import matplotlib.pyplot as plt
5✔
1841
    from matplotlib import tri
5✔
1842

1843
    from . import Network
5✔
1844

1845
    ri =  np.linspace(0,1, 50)
5✔
1846
    ti =  np.linspace(0,2*np.pi, 150)
5✔
1847
    Ri , Ti = np.meshgrid(ri, ti)
5✔
1848
    xi = np.linspace(-1,1, 50)
5✔
1849
    Xi, Yi = np.meshgrid(xi, xi)
5✔
1850
    triang = tri.Triangulation(x, y)
5✔
1851
    interpolator = tri.LinearTriInterpolator(triang, z)
5✔
1852
    Zi = interpolator(Xi, Yi)
5✔
1853
    if min0max1 == 1 :
5✔
1854
        VALopt = np.max(z)
×
1855
    else :
1856
        VALopt = np.min(z)
5✔
1857
    GAMopt = Network(f=[freq], s=x[z==VALopt] +1j*y[z==VALopt])
5✔
1858

1859
    if graph :
5✔
1860
        fig, ax = plt.subplots(**kwargs)
×
1861
        an = np.linspace(0, 2*np.pi, 50)
×
1862
        cs, sn = np.cos(an), np.sin(an)
×
1863
        plt.plot(cs, sn, color='k', lw=0.25)
×
1864
        plt.plot(cs, sn*0, color='g', lw=0.25)
×
1865
        plt.plot((1+cs)/2, sn/2, color='k', lw=0.25)
×
1866
        plt.axis('equal')
×
1867
        ax.set_axis_off()
×
1868
        ax.contour(Xi, Yi, Zi, levels=20, vmin=Zi.min(), vmax= Zi.max(), linewidths=0.5,  colors='k')
×
1869
        cntr1 = ax.contourf(Xi, Yi, Zi, levels=20, vmin=Zi.min(), vmax= Zi.max(),cmap=cmap)
×
1870
        fig.colorbar(cntr1, ax=ax)
×
1871
        ax.plot(x, y, 'o', ms=0.3, color='k')
×
1872
        ax.set(xlim=(-1, 1), ylim=(-1, 1))
×
1873
        plt.title(title)
×
1874
        plt.show()
×
1875
    return GAMopt, VALopt
5✔
1876

1877
def plot_prop_complex(netw: Network, prop_name: str,
5✔
1878
                    m=None, n=None, ax=None,
1879
                    show_legend=True, **kwargs):
1880
    r"""
1881
    plot the Network attribute :attr:`{}` vs frequency.
1882

1883
    Parameters
1884
    ----------
1885
    attribute : string
1886
        Network attribute to plot
1887
    m : int, optional
1888
        first index of s-parameter matrix, if None will use all
1889
    n : int, optional
1890
        second index of the s-parameter matrix, if None will use all
1891
    ax : :class:`matplotlib.Axes` object, optional
1892
        An existing Axes object to plot on
1893
    show_legend : Boolean
1894
        draw legend or not
1895
    y_label : string, optional
1896
        the y-axis label
1897

1898
    \*args,\**kwargs : arguments, keyword arguments
1899
        passed to :func:`matplotlib.plot`
1900

1901
    Note
1902
    ----
1903
    This function is dynamically generated upon Network
1904
    initialization. This is accomplished by calling
1905
    :func:`plot_vs_frequency_generic`
1906

1907
    Examples
1908
    --------
1909
    >>> myntwk.plot_{}(m=1,n=0,color='r')
1910
    """
1911

1912
    # create index lists, if not provided by user
1913
    if m is None:
5✔
1914
        M = range(netw.number_of_ports)
5✔
1915
    else:
1916
        M = [m]
×
1917
    if n is None:
5✔
1918
        N = range(netw.number_of_ports)
5✔
1919
    else:
1920
        N = [n]
×
1921

1922
    if 'label'  not in kwargs.keys():
5✔
1923
        gen_label = True
5✔
1924
    else:
1925
        gen_label = False
×
1926

1927
    for m in M:
5✔
1928
        for n in N:
5✔
1929
            # set the legend label for this trace to the networks
1930
            # name if it exists, and they didn't pass a name key in
1931
            # the kwargs
1932
            if gen_label:
5✔
1933
                kwargs['label'] = _get_label_str(netw, prop_name[0].upper(), m, n)
5✔
1934

1935
            # plot the desired attribute vs frequency
1936
            plot_complex_rectangular(
5✔
1937
                z=getattr(netw, prop_name)[:, m, n],
1938
                show_legend=show_legend, ax=ax,
1939
                **kwargs)
1940

1941
def plot_prop_polar(netw: Network, prop_name: str,
5✔
1942
                    m=None, n=None, ax=None,
1943
                    show_legend=True, **kwargs):
1944

1945
    r"""
1946
    plot the Network attribute :attr:`{}` vs frequency.
1947

1948
    Parameters
1949
    ----------
1950
    attribute : string
1951
        Network attribute to plot
1952
    m : int, optional
1953
        first index of s-parameter matrix, if None will use all
1954
    n : int, optional
1955
        second index of the s-parameter matrix, if None will use all
1956
    ax : :class:`matplotlib.Axes` object, optional
1957
        An existing Axes object to plot on
1958
    show_legend : Boolean
1959
        draw legend or not
1960
    y_label : string, optional
1961
        the y-axis label
1962

1963
    \*args,\**kwargs : arguments, keyword arguments
1964
        passed to :func:`matplotlib.plot`
1965

1966
    Note
1967
    ----
1968
    This function is dynamically generated upon Network
1969
    initialization. This is accomplished by calling
1970
    :func:`plot_vs_frequency_generic`
1971

1972
    Examples
1973
    --------
1974
    >>> myntwk.plot_{}(m=1,n=0,color='r')
1975
    """
1976

1977
    # create index lists, if not provided by user
1978
    if m is None:
5✔
1979
        M = range(netw.number_of_ports)
5✔
1980
    else:
1981
        M = [m]
×
1982
    if n is None:
5✔
1983
        N = range(netw.number_of_ports)
5✔
1984
    else:
1985
        N = [n]
×
1986

1987
    if 'label'  not in kwargs.keys():
5✔
1988
        gen_label = True
5✔
1989
    else:
1990
        gen_label = False
×
1991

1992
    for m in M:
5✔
1993
        for n in N:
5✔
1994
            # set the legend label for this trace to the networks
1995
            # name if it exists, and they didn't pass a name key in
1996
            # the kwargs
1997
            if gen_label:
5✔
1998
                kwargs['label'] = _get_label_str(netw, prop_name[0].upper(), m, n)
5✔
1999

2000
            # plot the desired attribute vs frequency
2001
            plot_complex_polar(
5✔
2002
                z = getattr(netw,prop_name)[:,m,n],
2003
                show_legend = show_legend, ax = ax,
2004
                **kwargs)
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

© 2026 Coveralls, Inc