Repository: pymoca/pymoca (language: python)

Issue:
rlocus klist->gains 
Replaces #325 as a fix for #324.
control.rlocus kwarg klist->gains
https://github.com/pymoca/pymoca/blob/9d597d76aa2b82bd3d563d4938a098af8baafd49/test/notebooks/Spring.ipynb#L1265

raises 

```txt
TypeError                                 Traceback (most recent call last)
Cell In[29], [line 1](vscode-notebook-cell:?execution_count=29&line=1)
----> [1](vscode-notebook-cell:?execution_count=29&line=1) control.rlocus(ss[0,0], klist=pl.logspace(-2,1,1000));
      [2](vscode-notebook-cell:?execution_count=29&line=2) pl.grid()

File c:\Users\G.McBain\.venv\pymoca\Lib\site-packages\control\rlocus.py:188, in root_locus_plot(sysdata, kvect, grid, plot, **kwargs)
    [185](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/rlocus.py:185)     return responses.loci, responses.gains
    [187](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/rlocus.py:187) # Plot the root loci
--> [188](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/rlocus.py:188) out = responses.plot(grid=grid, **kwargs)
    [190](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/rlocus.py:190) # Legacy processing: return locations of poles and zeros as a tuple
    [191](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/rlocus.py:191) if plot is True:

File c:\Users\G.McBain\.venv\pymoca\Lib\site-packages\control\pzmap.py:127, in PoleZeroData.plot(self, *args, **kwargs)
    [123](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/pzmap.py:123)     kwargs = kwargs.copy()
    [124](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/pzmap.py:124)     kwargs['grid'] = config._get_param(
    [125](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/pzmap.py:125)         'rlocus', 'grid', kwargs.get('grid', None), _rlocus_defaults)
--> [127](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/pzmap.py:127) return pole_zero_plot(self, *args, **kwargs)

File c:\Users\G.McBain\.venv\pymoca\Lib\site-packages\control\pzmap.py:261, in pole_zero_plot(data, plot, grid, title, marker_color, marker_size, marker_width, legend_loc, xlim, ylim, interactive, ax, scaling, initial_gain, **kwargs)
    [259](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/pzmap.py:259) marker_width = config._get_param('pzmap', 'marker_width', marker_width, 1.5)
    [260](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/pzmap.py:260) xlim_user, ylim_user = xlim, ylim
--> [261](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/pzmap.py:261) freqplot_rcParams = config._get_param(
    [262](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/pzmap.py:262)     'freqplot', 'rcParams', kwargs, _freqplot_defaults,
    [263](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/pzmap.py:263)     pop=True, last=True)
    [264](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/pzmap.py:264) user_ax = ax
    [266](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/pzmap.py:266) # If argument was a singleton, turn it into a tuple

File c:\Users\G.McBain\.venv\pymoca\Lib\site-packages\control\config.py:204, in _get_param(module, param, argval, defval, pop, last)
    [202](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/config.py:202)     val = argval.pop(param, None) if pop else argval.get(param, None)
    [203](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/config.py:203)     if last and argval:
--> [204](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/config.py:204)         raise TypeError("unrecognized keywords: " + str(argval))
    [205](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/config.py:205)     argval = val
    [207](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/config.py:207) # If we were passed a dict for the defval, get the param value from there
```

The upstream documentation is inconsistent; see python-control/python-control#998, suggesting that replacing `klist` with `kvect` should do the trick.  It does.

```python
control.rlocus(ss[0,0], kvect=pl.logspace(-2,1,1000));
```

![image](https://github.com/pymoca/pymoca/assets/1588947/705fc946-0989-4ca7-b819-bac2ac13b2f2)



rlocus klist->gains 
Replaces #325 as a fix for #324.
control.rlocus kwarg klist->gains
https://github.com/pymoca/pymoca/blob/9d597d76aa2b82bd3d563d4938a098af8baafd49/test/notebooks/Spring.ipynb#L1265

raises 

```txt
TypeError                                 Traceback (most recent call last)
Cell In[29], [line 1](vscode-notebook-cell:?execution_count=29&line=1)
----> [1](vscode-notebook-cell:?execution_count=29&line=1) control.rlocus(ss[0,0], klist=pl.logspace(-2,1,1000));
      [2](vscode-notebook-cell:?execution_count=29&line=2) pl.grid()

File c:\Users\G.McBain\.venv\pymoca\Lib\site-packages\control\rlocus.py:188, in root_locus_plot(sysdata, kvect, grid, plot, **kwargs)
    [185](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/rlocus.py:185)     return responses.loci, responses.gains
    [187](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/rlocus.py:187) # Plot the root loci
--> [188](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/rlocus.py:188) out = responses.plot(grid=grid, **kwargs)
    [190](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/rlocus.py:190) # Legacy processing: return locations of poles and zeros as a tuple
    [191](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/rlocus.py:191) if plot is True:

File c:\Users\G.McBain\.venv\pymoca\Lib\site-packages\control\pzmap.py:127, in PoleZeroData.plot(self, *args, **kwargs)
    [123](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/pzmap.py:123)     kwargs = kwargs.copy()
    [124](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/pzmap.py:124)     kwargs['grid'] = config._get_param(
    [125](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/pzmap.py:125)         'rlocus', 'grid', kwargs.get('grid', None), _rlocus_defaults)
--> [127](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/pzmap.py:127) return pole_zero_plot(self, *args, **kwargs)

File c:\Users\G.McBain\.venv\pymoca\Lib\site-packages\control\pzmap.py:261, in pole_zero_plot(data, plot, grid, title, marker_color, marker_size, marker_width, legend_loc, xlim, ylim, interactive, ax, scaling, initial_gain, **kwargs)
    [259](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/pzmap.py:259) marker_width = config._get_param('pzmap', 'marker_width', marker_width, 1.5)
    [260](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/pzmap.py:260) xlim_user, ylim_user = xlim, ylim
--> [261](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/pzmap.py:261) freqplot_rcParams = config._get_param(
    [262](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/pzmap.py:262)     'freqplot', 'rcParams', kwargs, _freqplot_defaults,
    [263](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/pzmap.py:263)     pop=True, last=True)
    [264](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/pzmap.py:264) user_ax = ax
    [266](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/pzmap.py:266) # If argument was a singleton, turn it into a tuple

File c:\Users\G.McBain\.venv\pymoca\Lib\site-packages\control\config.py:204, in _get_param(module, param, argval, defval, pop, last)
    [202](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/config.py:202)     val = argval.pop(param, None) if pop else argval.get(param, None)
    [203](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/config.py:203)     if last and argval:
--> [204](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/config.py:204)         raise TypeError("unrecognized keywords: " + str(argval))
    [205](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/config.py:205)     argval = val
    [207](file:///C:/Users/G.McBain/.venv/pymoca/Lib/site-packages/control/config.py:207) # If we were passed a dict for the defval, get the param value from there
```

The upstream documentation is inconsistent; see python-control/python-control#998, suggesting that replacing `klist` with `kvect` should do the trick.  It does.

```python
control.rlocus(ss[0,0], kvect=pl.logspace(-2,1,1000));
```

![image](https://github.com/pymoca/pymoca/assets/1588947/705fc946-0989-4ca7-b819-bac2ac13b2f2)





Discussion / hints:
Does this affect the minimum version of `control` required for running the examples? If so, could you add it to pyproject.toml (under the examples section).

If I understand correctly, it's `klist` -> `kvect` -> `gains`? And we're jumping over the `kvect` change straight to `gains`? 
> Does this affect the minimum version of `control` required for running the examples? If so, could you add it to pyproject.toml (under the examples section).

`control` doesn't appear in `pyproject.toml`.  It does in https://github.com/pymoca/pymoca/blob/9d597d76aa2b82bd3d563d4938a098af8baafd49/tools/create_conda_env.sh#L26

When I installed `pymoca` and ran `Spring.ipynb`, I didn't have `control` at all but on getting

> ModuleNotFoundError: No module named 'control'

I issued

```python
uv pip install control
```

and then hit #324; hence worked on #325 and this.

But  you're right, `gains` seems to have been introduced in [c638362](https://github.com/python-control/python-control/blame/c6383624b390c617ef8a2f0818cfb6994961bb30/control/rlocus.py) on 2024-04-04 but not yet released.  [PyPI](https://pypi.org/project/control) currently has [0.10.0](https://github.com/python-control/python-control/releases/tag/0.10.0), in which [`control.rlocus`](https://github.com/python-control/python-control/blob/0.10.0/control/rlocus.py) has

[`rlocus = root_locus_plot`](https://github.com/python-control/python-control/blob/1b2abe39ea8fa2ac152e4a53fdcb7c3d32e718b9/control/rlocus.py#L450)

and 

[`def root_locus_plot(
        sysdata, kvect=None, grid=None, plot=None, **kwargs):`](https://github.com/python-control/python-control/blob/1b2abe39ea8fa2ac152e4a53fdcb7c3d32e718b9/control/rlocus.py#L100-L101) 

and 

[`        responses = root_locus_map(sysdata, gains=kvect)`](https://github.com/python-control/python-control/blob/1b2abe39ea8fa2ac152e4a53fdcb7c3d32e718b9/control/rlocus.py#L169)

So I guess it would be best to use `kvect` here and pin `control==0.10.0`.  I'll close this and prepare a new PR.
As your own title says, this `kvect` will be deprecated in favor of `gains` for the next release.
Right, yes, sorry, (as noted in python-control/python-control#998) I thought `control.rlocus` was `control.matlab.wrappers.rlocus` rather than `control.rlocus.rlocus = control.rlocus.root_locus_plot`.
Does this affect the minimum version of `control` required for running the examples? If so, could you add it to pyproject.toml (under the examples section).

If I understand correctly, it's `klist` -> `kvect` -> `gains`? And we're jumping over the `kvect` change straight to `gains`? 
> Does this affect the minimum version of `control` required for running the examples? If so, could you add it to pyproject.toml (under the examples section).

`control` doesn't appear in `pyproject.toml`.  It does in https://github.com/pymoca/pymoca/blob/9d597d76aa2b82bd3d563d4938a098af8baafd49/tools/create_conda_env.sh#L26

When I installed `pymoca` and ran `Spring.ipynb`, I didn't have `control` at all but on getting

> ModuleNotFoundError: No module named 'control'

I issued

```python
uv pip install control
```

and then hit #324; hence worked on #325 and this.

But  you're right, `gains` seems to have been introduced in [c638362](https://github.com/python-control/python-control/blame/c6383624b390c617ef8a2f0818cfb6994961bb30/control/rlocus.py) on 2024-04-04 but not yet released.  [PyPI](https://pypi.org/project/control) currently has [0.10.0](https://github.com/python-control/python-control/releases/tag/0.10.0), in which [`control.rlocus`](https://github.com/python-control/python-control/blob/0.10.0/control/rlocus.py) has

[`rlocus = root_locus_plot`](https://github.com/python-control/python-control/blob/1b2abe39ea8fa2ac152e4a53fdcb7c3d32e718b9/control/rlocus.py#L450)

and 

[`def root_locus_plot(
        sysdata, kvect=None, grid=None, plot=None, **kwargs):`](https://github.com/python-control/python-control/blob/1b2abe39ea8fa2ac152e4a53fdcb7c3d32e718b9/control/rlocus.py#L100-L101) 

and 

[`        responses = root_locus_map(sysdata, gains=kvect)`](https://github.com/python-control/python-control/blob/1b2abe39ea8fa2ac152e4a53fdcb7c3d32e718b9/control/rlocus.py#L169)

So I guess it would be best to use `kvect` here and pin `control==0.10.0`.  I'll close this and prepare a new PR.
As your own title says, this `kvect` will be deprecated in favor of `gains` for the next release.
Right, yes, sorry, (as noted in python-control/python-control#998) I thought `control.rlocus` was `control.matlab.wrappers.rlocus` rather than `control.rlocus.rlocus = control.rlocus.root_locus_plot`.

Fix patch:
diff --git a/pyproject.toml b/pyproject.toml
index df1ac05d..cadd20e5 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -52,7 +52,7 @@ homepage = "https://github.com/pymoca/pymoca"
 casadi = ["casadi>=3.4.0", "setuptools>=60.0.0"]
 lxml = ["lxml>=3.5.0", "scipy>=0.13.3"]
 sympy = ["sympy>=0.7.6.1", "scipy>=0.13.3", "jinja2>=2.10.1"]
-examples = ["jupyterlab", "matplotlib"]
+examples = ["jupyterlab", "matplotlib", "control==0.10.0"]
 all = ["pymoca[casadi,lxml,sympy,examples]"]
 
 # See the docstring in versioneer.py for instructions. Note that after changing


Test patch:
diff --git a/test/notebooks/Spring.ipynb b/test/notebooks/Spring.ipynb
index c41210af..90546448 100644
--- a/test/notebooks/Spring.ipynb
+++ b/test/notebooks/Spring.ipynb
@@ -1262,7 +1262,7 @@
     }
    ],
    "source": [
-    "control.rlocus(ss[0,0], klist=pl.logspace(-2,1,1000));\n",
+    "control.rlocus(ss[0,0], kvect=pl.logspace(-2,1,1000));\n",
     "pl.grid()"
    ]
   },