Skip to content

Commit 820514d

Browse files
committed
fix(nyquist): honor explicit indent direction
Preserve automatic near-axis indentation when no direction is passed, but let explicit left/right override pole-side selection for poles within the indent radius. Fixes #1194
1 parent 146ccee commit 820514d

2 files changed

Lines changed: 40 additions & 2 deletions

File tree

control/freqplot.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1312,6 +1312,8 @@ def nyquist_response(
13121312
# Use existing dictionary, to keep track of processed keywords
13131313
_kwargs |= kwargs
13141314

1315+
indent_direction_arg = 'indent_direction' in _kwargs
1316+
13151317
# Get values for params
13161318
omega_num_given = omega_num is not None
13171319
omega_num = config._get_param('freqplot', 'number_of_samples', omega_num)
@@ -1469,8 +1471,21 @@ def nyquist_response(
14691471
- (s - p).real
14701472

14711473
# Figure out which way to offset the contour point
1472-
if p.real < 0 or (p.real == 0 and
1473-
indent_direction == 'right'):
1474+
if indent_direction_arg:
1475+
if indent_direction == 'right':
1476+
# Indent to the right
1477+
splane_contour[i] += offset
1478+
1479+
elif indent_direction == 'left':
1480+
# Indent to the left
1481+
splane_contour[i] -= offset
1482+
1483+
else:
1484+
raise ValueError(
1485+
"unknown value for indent_direction")
1486+
1487+
elif p.real < 0 or (p.real == 0 and
1488+
indent_direction == 'right'):
14741489
# Indent to the right
14751490
splane_contour[i] += offset
14761491

control/tests/nyquist_test.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,29 @@ def test_nyquist_indent_im():
372372
assert _Z(sys) == response.count + _P(sys)
373373

374374

375+
def test_nyquist_indent_near_imaginary_axis():
376+
"""Test indent direction for poles near the imaginary axis."""
377+
sys = ct.tf([1, 11, 10], [0.01, 1, 0.01, 1])
378+
omega = np.linspace(0, 2, 21)
379+
380+
_, contour_default = ct.nyquist_response(
381+
sys, omega, indent_radius=0.1, return_contour=True,
382+
warn_encirclements=False)
383+
_, contour_right = ct.nyquist_response(
384+
sys, omega, indent_radius=0.1, indent_direction='right',
385+
return_contour=True, warn_encirclements=False)
386+
_, contour_left = ct.nyquist_response(
387+
sys, omega, indent_radius=0.1, indent_direction='left',
388+
return_contour=True, warn_encirclements=False)
389+
390+
# The pole near +1j has a small positive real part, so the default
391+
# behavior indents to the left. Explicit directions override this.
392+
pole_index = np.argmin(np.abs(contour_default.imag - 1))
393+
assert contour_default[pole_index].real < 0
394+
assert contour_right[pole_index].real > 0
395+
assert contour_left[pole_index].real < 0
396+
397+
375398
def test_nyquist_exceptions():
376399
# MIMO not implemented
377400
sys = ct.rss(2, 2, 2)

0 commit comments

Comments
 (0)