Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Nichols plot feature #26437

Open
wants to merge 29 commits into
base: master
Choose a base branch
from
Open

Conversation

Soham-xT
Copy link

@Soham-xT Soham-xT commented Apr 1, 2024

Description:

The Nichols plot functionality implemented here provides a way to generate and display Nichols plots for a given system using SymPy. The nichols_numerical_data function calculates the numerical data required for the plot, including magnitude and phase, while the nichols_plot function generates the plot itself.

Here's how the process works:

  1. Magnitude and Phase Calculation: The nichols_numerical_data function calculates the magnitude (in decibels) and phase (in degrees) of the system's transfer function at different frequencies. It does this by first substituting the frequency variable (symbolized by w) into the transfer function expression. Then, it computes the magnitude and phase using SymPy functions such as log and arg.

2,Expression Generation: After computing the magnitude and phase, the function returns these expressions along with the frequency variable w.

3.Plot Generation: The nichols_plot function takes the magnitude and phase expressions along with the frequency variable as input. It then uses SymPy's plot_parametric function to plot the magnitude against the phase for the specified frequency range. This generates the Nichols plot, where the x-axis represents the phase and the y-axis represents the magnitude.

References to other Issues or PRs

#25251 I have used this a reference and inspiration

Tests:

Tested both functions with various systems to ensure correct computation and plotting of Nichols plots.

  1. system = TransferFunction(-(0.1)*s**3 - (2.4)*s**2 - 181*s - 1950, s**3 + (3.3)*s**2 + 990*s + 2600, s) nichols_plot(system,0.01,100)
    image
  2. system = TransferFunction(s**2 + (0.1)*s + 7.5,s**4 + (0.12)*s**3 + 9*s**2,s) nichols_plot(system,0.01,100)
    image

Brief description of what is fixed or changed

The changes made to the codebase have reduced dependencies on NumPy and Matplotlib by utilizing SymPy's built-in symbolic computation capabilities. This approach eliminates the need for external libraries for certain functionalities, enhancing the efficiency and portability of the codebase.

Release Notes

NO ENTRY

@sympy-bot
Copy link

sympy-bot commented Apr 1, 2024

Hi, I am the SymPy bot. I'm here to help you write a release notes entry. Please read the guide on how to write release notes.

  • No release notes entry will be added for this pull request.
Click here to see the pull request description that was parsed.
<!-- Your title above should be a short description of what
was changed. Do not include the issue number in the title. -->
## Description:
The Nichols plot functionality implemented here provides a way to generate and display Nichols plots for a given system using SymPy. The `nichols_numerical_data` function calculates the numerical data required for the plot, including magnitude and phase, while the `nichols_plot` function generates the plot itself.

Here's how the process works:

1. Magnitude and Phase Calculation: The `nichols_numerical_data` function calculates the magnitude (in decibels) and phase (in degrees) of the system's transfer function at different frequencies. It does this by first substituting the frequency variable (symbolized by w) into the transfer function expression. Then, it computes the magnitude and phase using SymPy functions such as log and arg.

2,Expression Generation: After computing the magnitude and phase, the function returns these expressions along with the frequency variable w.

3.Plot Generation: The `nichols_plot` function takes the magnitude and phase expressions along with the frequency variable as input. It then uses SymPy's `plot_parametric` function to plot the magnitude against the phase for the specified frequency range. This generates the Nichols plot, where the x-axis represents the phase and the y-axis represents the magnitude.

#### References to other Issues or PRs
https://github.com/sympy/sympy/pull/25251  I have used this a reference and inspiration 

## Tests:
Tested both functions with various systems to ensure correct computation and plotting of Nichols plots.
1. `system = TransferFunction(-(0.1)*s**3 - (2.4)*s**2 - 181*s - 1950, s**3 + (3.3)*s**2 + 990*s + 2600, s)
nichols_plot(system,0.01,100)`
![image](https://github.com/sympy/sympy/assets/136966898/babd53b0-853d-4814-b75b-d2f8c13ecf8c)
2. `system = TransferFunction(s**2 + (0.1)*s + 7.5,s**4 + (0.12)*s**3 + 9*s**2,s)
nichols_plot(system,0.01,100)
`
![image](https://github.com/sympy/sympy/assets/136966898/76a93fdc-8324-4bb1-b01f-962f008cdbe5)

#### Brief description of what is fixed or changed
The changes made to the codebase have reduced dependencies on NumPy and Matplotlib by utilizing SymPy's built-in symbolic computation capabilities. This approach eliminates the need for external libraries for certain functionalities, enhancing the efficiency and portability of the codebase.


#### Release Notes

<!-- BEGIN RELEASE NOTES -->
NO ENTRY
<!-- END RELEASE NOTES -->

@Soham-xT Soham-xT changed the title Nichols plot feature Add Nichols plot feature Apr 1, 2024
Copy link

github-actions bot commented Apr 5, 2024

Benchmark results from GitHub Actions

Lower numbers are good, higher numbers are bad. A ratio less than 1
means a speed up and greater than 1 means a slowdown. Green lines
beginning with + are slowdowns (the PR is slower then master or
master is slower than the previous release). Red lines beginning
with - are speedups.

Significantly changed benchmark results (PR vs master)

Significantly changed benchmark results (master vs previous release)

| Change   | Before [2487dbb5]    | After [8e00716a]    |   Ratio | Benchmark (Parameter)                                                |
|----------|----------------------|---------------------|---------|----------------------------------------------------------------------|
| -        | 67.7±0.3ms           | 43.5±0.2ms          |    0.64 | integrate.TimeIntegrationRisch02.time_doit(10)                       |
| -        | 67.7±1ms             | 43.4±0.4ms          |    0.64 | integrate.TimeIntegrationRisch02.time_doit_risch(10)                 |
| +        | 18.6±0.1μs           | 29.7±0.09μs         |    1.6  | integrate.TimeIntegrationRisch03.time_doit(1)                        |
| -        | 5.24±0.03ms          | 2.82±0.01ms         |    0.54 | logic.LogicSuite.time_load_file                                      |
| -        | 73.6±0.5ms           | 28.6±0.1ms          |    0.39 | polys.TimeGCD_GaussInt.time_op(1, 'dense')                           |
| -        | 25.7±0.2ms           | 16.9±0.04ms         |    0.66 | polys.TimeGCD_GaussInt.time_op(1, 'expr')                            |
| -        | 72.5±0.5ms           | 28.8±0.1ms          |    0.4  | polys.TimeGCD_GaussInt.time_op(1, 'sparse')                          |
| -        | 255±1ms              | 127±6ms             |    0.5  | polys.TimeGCD_GaussInt.time_op(2, 'dense')                           |
| -        | 256±2ms              | 125±0.8ms           |    0.49 | polys.TimeGCD_GaussInt.time_op(2, 'sparse')                          |
| -        | 648±4ms              | 372±0.8ms           |    0.57 | polys.TimeGCD_GaussInt.time_op(3, 'dense')                           |
| -        | 654±4ms              | 372±1ms             |    0.57 | polys.TimeGCD_GaussInt.time_op(3, 'sparse')                          |
| -        | 494±2μs              | 284±0.6μs           |    0.58 | polys.TimeGCD_LinearDenseQuadraticGCD.time_op(1, 'dense')            |
| -        | 1.79±0ms             | 1.06±0.01ms         |    0.59 | polys.TimeGCD_LinearDenseQuadraticGCD.time_op(2, 'dense')            |
| -        | 5.79±0.05ms          | 3.07±0.01ms         |    0.53 | polys.TimeGCD_LinearDenseQuadraticGCD.time_op(3, 'dense')            |
| -        | 452±3μs              | 229±2μs             |    0.51 | polys.TimeGCD_QuadraticNonMonicGCD.time_op(1, 'dense')               |
| -        | 1.48±0.02ms          | 680±3μs             |    0.46 | polys.TimeGCD_QuadraticNonMonicGCD.time_op(2, 'dense')               |
| -        | 4.84±0.05ms          | 1.63±0.02ms         |    0.34 | polys.TimeGCD_QuadraticNonMonicGCD.time_op(3, 'dense')               |
| -        | 378±1μs              | 204±0.8μs           |    0.54 | polys.TimeGCD_SparseGCDHighDegree.time_op(1, 'dense')                |
| -        | 2.46±0.03ms          | 1.23±0ms            |    0.5  | polys.TimeGCD_SparseGCDHighDegree.time_op(3, 'dense')                |
| -        | 10.1±0.09ms          | 4.30±0.01ms         |    0.43 | polys.TimeGCD_SparseGCDHighDegree.time_op(5, 'dense')                |
| -        | 363±3μs              | 168±1μs             |    0.46 | polys.TimeGCD_SparseNonMonicQuadratic.time_op(1, 'dense')            |
| -        | 2.48±0.02ms          | 894±5μs             |    0.36 | polys.TimeGCD_SparseNonMonicQuadratic.time_op(3, 'dense')            |
| -        | 9.64±0.06ms          | 2.63±0.01ms         |    0.27 | polys.TimeGCD_SparseNonMonicQuadratic.time_op(5, 'dense')            |
| -        | 1.02±0.01ms          | 429±2μs             |    0.42 | polys.TimePREM_LinearDenseQuadraticGCD.time_op(3, 'dense')           |
| -        | 1.71±0ms             | 507±1μs             |    0.3  | polys.TimePREM_LinearDenseQuadraticGCD.time_op(3, 'sparse')          |
| -        | 5.79±0.1ms           | 1.80±0.01ms         |    0.31 | polys.TimePREM_LinearDenseQuadraticGCD.time_op(5, 'dense')           |
| -        | 8.24±0.05ms          | 1.51±0ms            |    0.18 | polys.TimePREM_LinearDenseQuadraticGCD.time_op(5, 'sparse')          |
| -        | 282±1μs              | 65.2±0.7μs          |    0.23 | polys.TimePREM_QuadraticNonMonicGCD.time_op(1, 'sparse')             |
| -        | 3.39±0.03ms          | 392±3μs             |    0.12 | polys.TimePREM_QuadraticNonMonicGCD.time_op(3, 'dense')              |
| -        | 3.96±0.03ms          | 279±1μs             |    0.07 | polys.TimePREM_QuadraticNonMonicGCD.time_op(3, 'sparse')             |
| -        | 6.98±0.05ms          | 1.26±0.01ms         |    0.18 | polys.TimePREM_QuadraticNonMonicGCD.time_op(5, 'dense')              |
| -        | 8.75±0.09ms          | 839±2μs             |    0.1  | polys.TimePREM_QuadraticNonMonicGCD.time_op(5, 'sparse')             |
| -        | 5.01±0.05ms          | 2.97±0ms            |    0.59 | polys.TimeSUBRESULTANTS_LinearDenseQuadraticGCD.time_op(2, 'sparse') |
| -        | 12.1±0.07ms          | 6.60±0.03ms         |    0.55 | polys.TimeSUBRESULTANTS_LinearDenseQuadraticGCD.time_op(3, 'dense')  |
| -        | 22.1±0.2ms           | 9.07±0.08ms         |    0.41 | polys.TimeSUBRESULTANTS_LinearDenseQuadraticGCD.time_op(3, 'sparse') |
| -        | 5.20±0.02ms          | 873±3μs             |    0.17 | polys.TimeSUBRESULTANTS_QuadraticNonMonicGCD.time_op(1, 'sparse')    |
| -        | 12.4±0.02ms          | 7.05±0.01ms         |    0.57 | polys.TimeSUBRESULTANTS_QuadraticNonMonicGCD.time_op(2, 'sparse')    |
| -        | 101±0.4ms            | 25.6±0.1ms          |    0.25 | polys.TimeSUBRESULTANTS_QuadraticNonMonicGCD.time_op(3, 'dense')     |
| -        | 165±0.9ms            | 54.0±0.4ms          |    0.33 | polys.TimeSUBRESULTANTS_QuadraticNonMonicGCD.time_op(3, 'sparse')    |
| -        | 172±0.9μs            | 110±0.7μs           |    0.64 | polys.TimeSUBRESULTANTS_SparseGCDHighDegree.time_op(1, 'dense')      |
| -        | 356±2μs              | 220±2μs             |    0.62 | polys.TimeSUBRESULTANTS_SparseGCDHighDegree.time_op(1, 'sparse')     |
| -        | 4.23±0.02ms          | 839±0.6μs           |    0.2  | polys.TimeSUBRESULTANTS_SparseGCDHighDegree.time_op(3, 'dense')      |
| -        | 5.19±0.05ms          | 382±2μs             |    0.07 | polys.TimeSUBRESULTANTS_SparseGCDHighDegree.time_op(3, 'sparse')     |
| -        | 20.2±0.1ms           | 2.84±0.01ms         |    0.14 | polys.TimeSUBRESULTANTS_SparseGCDHighDegree.time_op(5, 'dense')      |
| -        | 22.7±0.08ms          | 626±1μs             |    0.03 | polys.TimeSUBRESULTANTS_SparseGCDHighDegree.time_op(5, 'sparse')     |
| -        | 483±2μs              | 136±0.8μs           |    0.28 | polys.TimeSUBRESULTANTS_SparseNonMonicQuadratic.time_op(1, 'sparse') |
| -        | 4.69±0.01ms          | 626±6μs             |    0.13 | polys.TimeSUBRESULTANTS_SparseNonMonicQuadratic.time_op(3, 'dense')  |
| -        | 5.30±0.01ms          | 140±1μs             |    0.03 | polys.TimeSUBRESULTANTS_SparseNonMonicQuadratic.time_op(3, 'sparse') |
| -        | 13.0±0.06ms          | 1.28±0ms            |    0.1  | polys.TimeSUBRESULTANTS_SparseNonMonicQuadratic.time_op(5, 'dense')  |
| -        | 14.1±0.09ms          | 140±1μs             |    0.01 | polys.TimeSUBRESULTANTS_SparseNonMonicQuadratic.time_op(5, 'sparse') |
| -        | 133±0.6μs            | 75.3±1μs            |    0.56 | solve.TimeMatrixOperations.time_rref(3, 0)                           |
| -        | 255±3μs              | 88.1±0.3μs          |    0.35 | solve.TimeMatrixOperations.time_rref(4, 0)                           |
| -        | 24.5±0.2ms           | 10.3±0.09ms         |    0.42 | solve.TimeSolveLinSys189x49.time_solve_lin_sys                       |
| -        | 28.6±0.06ms          | 15.5±0.08ms         |    0.54 | solve.TimeSparseSystem.time_linsolve_Aaug(20)                        |
| -        | 55.2±0.3ms           | 25.1±0.3ms          |    0.46 | solve.TimeSparseSystem.time_linsolve_Aaug(30)                        |
| -        | 28.2±0.3ms           | 15.2±0.09ms         |    0.54 | solve.TimeSparseSystem.time_linsolve_Ab(20)                          |
| -        | 54.9±0.2ms           | 24.6±0.07ms         |    0.45 | solve.TimeSparseSystem.time_linsolve_Ab(30)                          |

Full benchmark results can be found as artifacts in GitHub Actions
(click on checks at the top of the PR).

@Soham-xT
Copy link
Author

@moorepants I was wondering if you could review the code

1 similar comment
@Soham-xT
Copy link
Author

@moorepants I was wondering if you could review the code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants