GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Add support for continuous delay systems by MythasNauveili · Pull Request #1148 · python-control/python-control

Conversation

@MythasNauveili

@MythasNauveili MythasNauveili commented May 11, 2025

Copy link
Copy Markdown

This PR introduces initial support for linear time-invariant (LTI) systems with time delays in python-control, inspired by the capabilities of ControlSystems.jl package, and the InternalDelay.py script from Skogestad-Python.

Key features

  • Creation of delay sytems Systems with delays can now be intuitively constructed by multiplying a TransferFunction object with a delay(tau) object or an exp(-tau*s) expression:
    tf_delay = tf([1], [1,1]) * delay(2)
    # or
    s = tf("s")
    tf_delay = tf([1], [1,1]) * exp(-2*s)
  • Underlying Representation: The approach utilizes Linear Fractional Transformations (LFT) with a PartitionedStateSpace representation (implemented in partitionedssp.py). The DelayLTI class (in delaylti.py) serves as the core object for these systems.
  • Operations: Standard operations such as addition, multiplication, and feedback are supported for DelayLTI objects.
  • Time and Frequency Responses: Time responses for delayed systems are computed by solving the underlying Delay Differential Equations (DDEs). Frequency responses are also supported.
  • Retrocompatibility: Maintain retrocompatibility with existing functionalities.

Implementation Details:

  • DDE Solver: Time responses for delayed systems are computed by solving DDEs. After exploring alternatives, a custom DDE solver based on the Method of Steps, wrapping SciPy's solve_ivp, was implemented in dde.py.
    • Accuracy Note: While this solver performs well for many cases, for stiff problems, it may require smaller step sizes for convergence compared to specialized DDE solvers (like those in ControlSystems.jl). In tested scenarios, the error typically remains at least three orders of magnitude below the system response with reasonable step sizes. (An error estimation algorithm could be a valuable future addition).
  • Validation: A Julia script (control/julia/compute_tests.jl) using ControlSystems.jl has been added to generate reference results, which are exported to a JSON file. These results are used in the test suite (control/tests/delay_lti_test.py and control/tests/dde_test.py) to benchmark the accuracy of the DelayLTI implementation and DDE solver.

Current Scope and Limitations:

  • This implementation currently focuses on continuous-time delay systems. Discrete-time delay systems are not yet covered.
  • The DDE solver may benefit from further enhancements for stiff problems or adaptive step sizing.

Discussion Points:
This PR represents the initial groundwork for this feature. I'm submitting this draft to share these first steps and would appreciate feedback on:

  • The overall approach and class design (DelayLTI, PartitionedStateSpace).
  • The DDE solver implementation and its current performance.
  • Any potential integration issues or API considerations.

I'm aware there is still work to be done for this feature to be ready for a full release, but I believe this provides a solid foundation.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
MythasNauveili and others added 15 commits March 26, 2025 08:56
@MythasNauveili
implement delayLTI and PartionedStateSpace classes and first algorith…
de5a61f
…m to solve delay ODE in timeresp. Add litlle tests about timereps
@MythasNauveili
Merge branch 'python-control:main' into dev-delay
848d763
@MythasNauveili
ongoing tests with julia notebooks, feedback bug solved
2c74ca1
@MythasNauveili
mimo building of delay systems
b6c0cde
@MythasNauveili
implementation of working frequency response for delayed siso system
8dcab54
@MythasNauveili
__call__ implementation to use lti frequency response function
9b7b062
@MythasNauveili
delay systems handle freq plots (julia comparison ok). Time to work o…
e8a197b
…n the DDE integration
@MythasNauveili
add dde.py file to improve readibility in timeresp.py
29081b5
implemented only basic dde solving based on Skogestad python
@MythasNauveili
add tests and docstrings, tests are all passing. ToDo: refine tests, …
8f43ab9
…improve doc and julia notebook. Clean the code
@MythasNauveili
improve tests by scripting julia code to mimic python tests, and seri…
30c05b9
…alize with json file.

Fix scalar multiplication
@MythasNauveili
methods of steps implemented, 1E-5 accuracy against julia for first t…
53641f4
…ests.

TODO: clean dde file, test functions in it, and reloacate dde response tests. More tests also like mimo force response are needed
@MythasNauveili
Merge branch 'python-control:main' into dev-delay
b0f2cd3
@MythasNauveili
cleaning old dde methods
4172ce8
@MythasNauveili
mimo forced response passed. TODO: clean
a460513
@MythasNauveili
docstrings, ruff check and format
f56e5de
murrayrm
murrayrm requested changes May 12, 2025
View reviewed changes

@murrayrm murrayrm left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initial round of comments. I haven't yet checked functionality, but found a few style issues that should be updated.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread control/dde.py Outdated
U = _check_convert_array(
U, legal_shapes, "Parameter `U`: ", squeeze=False, transpose=transpose
)
print("U shape: ", U.shape)

@murrayrm murrayrm May 12, 2025

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a debugging statement that was left in the code?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@MythasNauveili MythasNauveili May 12, 2025

Copy link
Copy Markdown
Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed in last commit

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread control/dde.py Outdated
discontinuity_times.remove(t_stop)
local_t_eval = [t for t in T if current_t < t <= t_stop]

print("Integrate bewtween ", current_t, " and ", t_stop)

@murrayrm murrayrm May 12, 2025

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debugging statement?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@MythasNauveili MythasNauveili May 12, 2025

Copy link
Copy Markdown
Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed in last commit

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread control/__init__.py Outdated

# Allow access to phase_plane functions as ct.phaseplot.fcn or ct.pp.fcn
from . import phaseplot as phaseplot

@murrayrm murrayrm May 12, 2025

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was there a reason to add an extraneous blank line here?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@MythasNauveili MythasNauveili May 12, 2025

Copy link
Copy Markdown
Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, blank line is removed in the last commit

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread control/dde.py Outdated
)


def pchip_interp_u(T, U):

@murrayrm murrayrm May 12, 2025

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like it is probably an internal function. If so, add underscore to start of function name.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@MythasNauveili MythasNauveili May 12, 2025

Copy link
Copy Markdown
Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

underscore added

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread control/dde.py Outdated
return np.array([negative_wrapper(PchipInterpolator(T, ui)) for ui in U])


class DdeHistory:

@murrayrm murrayrm May 12, 2025

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be better named DDEHistory (since DDE is a standard abbreviation, like LTI)?

Also, if this is an internal class, begin the class name with an underscore.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@MythasNauveili MythasNauveili May 12, 2025

Copy link
Copy Markdown
Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed the name and added the underscore

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread control/delaylti.py Outdated

Notes
-----
The way to create a DelayLTI object is by multiplying a transfert

@murrayrm murrayrm May 12, 2025

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: transfert → transfer

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

MythasNauveili reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
Comment thread control/delaylti.py Outdated
Comment on lines +99 to +100
It's possible to MIMO delayed systems from arrays of SISO delayed systems,
see function mimo_delay

@murrayrm murrayrm May 12, 2025

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's possible to MIMO → It is possible to create

Also missing a period at the end.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

MythasNauveili reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
Comment thread control/delaylti.py Outdated

# Poles and zeros functions for DelayLTI are not supported by julia ControlSystems.jl
# might not be accurate for delayLTI, to be discussed
# Here the poles and zeros computed are the ones of the system without taking into accoutn the delay

@murrayrm murrayrm May 12, 2025

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo + may be longer than 78 char (PEP8).

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread control/delaylti.py Outdated
# Check dimensions of K
if K.shape != (self.nu, self.ny):
raise ValueError(
f"Feedback gain K has incompatible shape. Expected ({self.nu}, {self.ny}), got {K.shape}."

@murrayrm murrayrm May 12, 2025

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update to avoid line with more than 78 characters and put closing parenthesis at end of string, for consistency with current code base.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread control/delaylti.py Outdated
Comment on lines +549 to +552
def issiso(self):
"""Check if the system is single-input, single-output."""
# Based on EXTERNAL dimensions
return self.nu == 1 and self.ny == 1

@murrayrm murrayrm May 12, 2025

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be handled by the parent class (InputOutputSystem).

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@MythasNauveili MythasNauveili May 12, 2025

Copy link
Copy Markdown
Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now moved to InputOutputSystem

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@murrayrm

murrayrm commented May 12, 2025

Copy link
Copy Markdown
Member

This would be a great contribution, @MythasNauveili. Thanks for working on it!

There are some issues with unit tests. In addition to the failed ruff and doctest checks, it looks like some of the standard tests might be hanging. Not sure if that is due to the new code or if something is messed up in the GitHub actions. I'll try to come back and take a look at that later.

In the meantime, some style things to adjust in your next pass.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

MythasNauveili added 2 commits May 12, 2025 20:36
@MythasNauveili
improve formatting, underscore internal functions, add file description
36ad284
@MythasNauveili
implement delay case in input_output_response, move and test issiso f…
4870463
…or delayLTI to iosys.py
@MythasNauveili

MythasNauveili commented May 12, 2025

Copy link
Copy Markdown
Author

Thanks for the first feedback @murrayrm. I tried to fix the most in the two last commits. For the tests hanging, it might be because of debug plots that I did not disable in dde_test.py (this should be done now).

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@coveralls

coveralls commented May 12, 2025 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

Coverage Status

coverage: 94.153% (-0.6%) from 94.734%
when pulling e9cf123 on MythasNauveili:dev-delay
into abeb0e4 on python-control:main.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@murrayrm

murrayrm commented May 13, 2025

Copy link
Copy Markdown
Member

@MythasNauveili There are still two unit tests failing. Click on the links above to see the issues. The ruff checks should be straightforward. For the doctest, it looks like something is printing out spurious information; not sure where that is happening.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@MythasNauveili
remove forgotten debug print in timeresp, and unused imports in delay…
1150646
…_test
@MythasNauveili

MythasNauveili commented May 13, 2025

Copy link
Copy Markdown
Author

@murrayrm it might be ok now, I just removed a forgotten debug print in timeresp.py that was the source of the dxdt = [...]

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@MythasNauveili
MythasNauveili marked this pull request as ready for review June 19, 2025 08:20
@MythasNauveili
MythasNauveili requested a review from murrayrm August 25, 2025 12:36
@MythasNauveili
Merge branch 'python-control:main' into dev-delay
e9cf123
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@murrayrm murrayrm Awaiting requested review from murrayrm

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

3 participants

@MythasNauveili @murrayrm @coveralls
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.