diff --git a/.JuliaFormatter.toml b/.JuliaFormatter.toml index 453925c3f..9c7935911 100644 --- a/.JuliaFormatter.toml +++ b/.JuliaFormatter.toml @@ -1 +1,2 @@ -style = "sciml" \ No newline at end of file +style = "sciml" +format_markdown = true \ No newline at end of file diff --git a/README.md b/README.md index 6dc908cc3..8a4f12801 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![codecov](https://codecov.io/gh/SciML/ModelingToolkitStandardLibrary.jl/branch/main/graph/badge.svg)](https://codecov.io/gh/SciML/ModelingToolkitStandardLibrary.jl) [![Build Status](https://github.com/SciML/ModelingToolkitStandardLibrary.jl/workflows/CI/badge.svg)](https://github.com/SciML/ModelingToolkitStandardLibrary.jl/actions?query=workflow%3ACI) -[![ColPrac: Contributor's Guide on Collaborative Practices for Community Packages](https://img.shields.io/badge/ColPrac-Contributor's%20Guide-blueviolet)](https://github.com/SciML/ColPrac) +[![ColPrac: Contributor's Guide on Collaborative Practices for Community Packages](https://img.shields.io/badge/ColPrac-Contributor%27s%20Guide-blueviolet)](https://github.com/SciML/ColPrac) [![SciML Code Style](https://img.shields.io/static/v1?label=code%20style&message=SciML&color=9558b2&labelColor=389826)](https://github.com/SciML/SciMLStyle) The ModelingToolkit Standard Library is a standard library of components to model the world and beyond. @@ -19,7 +19,8 @@ Assuming that you already have Julia correctly installed, it suffices to import ModelingToolkitStandardLibrary.jl in the standard way: ```julia -import Pkg; Pkg.add("ModelingToolkitStandardLibrary") +import Pkg; +Pkg.add("ModelingToolkitStandardLibrary"); ``` ## Tutorials and Documentation @@ -33,11 +34,11 @@ the documentation, which contains the unreleased features. The following are the constituant libraries of the ModelingToolkit Standard Library. -- [Basic Blocks](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/blocks/) -- [Mechanical Components](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/mechanical/) -- [Electrical Components](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/electrical/) -- [Magnetic Components](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/magnetic/) -- [Thermal Components](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/thermal/) + - [Basic Blocks](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/blocks/) + - [Mechanical Components](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/mechanical/) + - [Electrical Components](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/electrical/) + - [Magnetic Components](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/magnetic/) + - [Thermal Components](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/thermal/) ## Example @@ -52,20 +53,19 @@ R = 1.0 C = 1.0 V = 1.0 @variables t -@named resistor = Resistor(R=R) -@named capacitor = Capacitor(C=C) +@named resistor = Resistor(R = R) +@named capacitor = Capacitor(C = C) @named source = Voltage() -@named constant = Constant(k=V) +@named constant = Constant(k = V) @named ground = Ground() -rc_eqs = [ - connect(constant.output, source.V) - connect(source.p, resistor.p) - connect(resistor.n, capacitor.p) - connect(capacitor.n, source.n, ground.g) - ] +rc_eqs = [connect(constant.output, source.V) + connect(source.p, resistor.p) + connect(resistor.n, capacitor.p) + connect(capacitor.n, source.n, ground.g)] -@named rc_model = ODESystem(rc_eqs, t, systems=[resistor, capacitor, constant, source, ground]) +@named rc_model = ODESystem(rc_eqs, t, + systems = [resistor, capacitor, constant, source, ground]) sys = structural_simplify(rc_model) prob = ODEProblem(sys, Pair[], (0, 10.0)) sol = solve(prob, Tsit5()) diff --git a/docs/make.jl b/docs/make.jl index 14a51b3cf..d61d6c6b0 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -18,7 +18,9 @@ makedocs(sitename = "ModelingToolkitStandardLibrary.jl", authors = "Julia Computing", clean = true, doctest = false, + linkcheck = true, strict = [ + :linkcheck, :doctest, :example_block, ], diff --git a/docs/src/API/blocks.md b/docs/src/API/blocks.md index 4aec59352..0c113fbdc 100644 --- a/docs/src/API/blocks.md +++ b/docs/src/API/blocks.md @@ -1,11 +1,15 @@ # ModelingToolkitStandardLibrary: Blocks + ```@meta CurrentModule = ModelingToolkitStandardLibrary.Blocks ``` + ```@contents Pages = ["blocks.md"] ``` + ## Index + ```@index Pages = ["blocks.md"] ``` @@ -80,4 +84,4 @@ PI LimPI PID LimPID -``` \ No newline at end of file +``` diff --git a/docs/src/API/electrical.md b/docs/src/API/electrical.md index 004b4f5a7..e9e4322f5 100644 --- a/docs/src/API/electrical.md +++ b/docs/src/API/electrical.md @@ -1,13 +1,15 @@ # ModelingToolkitStandardLibrary: Electrical Components + ```@meta CurrentModule = ModelingToolkitStandardLibrary.Electrical ``` - ```@contents Pages = ["electrical.md"] ``` + ## Index + ```@index Pages = ["electrical.md"] ``` @@ -63,6 +65,7 @@ ExpSineCurrent ``` ## Digital Gates + ```@docs Not And @@ -74,6 +77,7 @@ Xnor ``` ## Digital Components + ```@docs HalfAdder FullAdder @@ -84,6 +88,7 @@ Decoder ``` ## Digital Sources + ```@docs PulseDiff Set diff --git a/docs/src/API/linear_analysis.md b/docs/src/API/linear_analysis.md index 7680726cf..d94878911 100644 --- a/docs/src/API/linear_analysis.md +++ b/docs/src/API/linear_analysis.md @@ -1,25 +1,29 @@ # Linear Analysis !!! danger "Experimental" - The interface described here is currently experimental and at any time subject to breaking changes not respecting semantic versioning. + + The interface described here is currently experimental and at any time subject to breaking changes not respecting semantic versioning. Linear analysis refers to the process of linearizing a nonlinear model and analysing the resulting linear dynamical system. To facilitate linear analysis, ModelingToolkitStandardLibrary provides the concept of an [`AnalysisPoint`](@ref), which can be inserted in-between two causal blocks (such as those from the `Blocks` sub module). Once a model containing analysis points is built, several operations are available: -- [`get_sensitivity`](@ref) get the [sensitivity function (wiki)](https://en.wikipedia.org/wiki/Sensitivity_(control_systems)), $S(s)$, as defined in the field of control theory. -- [`get_comp_sensitivity`](@ref) get the complementary sensitivity function $T(s) : S(s)+T(s)=1$. -- [`get_looptransfer`](@ref) get the (open) loop-transfer function where the loop starts and ends in the analysis point. For a typical simple feedback connection with a plant $P(s)$ and a controller $C(s)$, the loop-transfer function at the plant output is $P(s)C(s)$. -- [`linearize`](@ref) can be called with two analysis points denoting the input and output of the linearized system. -- [`open_loop`](@ref) return a new (nonlinear) system where the loop has been broken in the analysis point, i.e., the connection the analysis point usually implies has been removed. + - [`get_sensitivity`](@ref) get the [sensitivity function (wiki)](https://en.wikipedia.org/wiki/Sensitivity_(control_systems)), $S(s)$, as defined in the field of control theory. + - [`get_comp_sensitivity`](@ref) get the complementary sensitivity function $T(s) : S(s)+T(s)=1$. + - [`get_looptransfer`](@ref) get the (open) loop-transfer function where the loop starts and ends in the analysis point. For a typical simple feedback connection with a plant $P(s)$ and a controller $C(s)$, the loop-transfer function at the plant output is $P(s)C(s)$. + - [`linearize`](@ref) can be called with two analysis points denoting the input and output of the linearized system. + - [`open_loop`](@ref) return a new (nonlinear) system where the loop has been broken in the analysis point, i.e., the connection the analysis point usually implies has been removed. An analysis point can be created explicitly using the constructor [`AnalysisPoint`](@ref), or automatically when connecting two causal components using `connect`: + ```julia connect(comp1.output, :analysis_point_name, comp2.input) ``` !!! warning "Causality" - Analysis points are *causal*, i.e., they imply a directionality for the flow of information. The order of the connections in the connect statement is thus important, i.e., `connect(out, :name, in)` is different from `connect(in, :name, out)`. + Analysis points are *causal*, i.e., they imply a directionality for the flow of information. The order of the connections in the connect statement is thus important, i.e., `connect(out, :name, in)` is different from `connect(in, :name, out)`. + The directionality of an analysis point can be thought of as an arrow in a block diagram, where the name of the analysis point applies to the arrow itself. + ``` ┌─────┐ ┌─────┐ │ │ name │ │ @@ -27,14 +31,18 @@ The directionality of an analysis point can be thought of as an arrow in a block │ │ │ │ └─────┘ └─────┘ ``` + This is signified by the name being the middle argument to `connect`. Of the above mentioned functions, all except for [`open_loop`](@ref) return the output of [`ModelingToolkit.linearize`](@ref), which is + ```julia matrices, simplified_sys = linearize(...) # matrices = (; A, B, C, D) ``` + i.e., `matrices` is a named tuple containing the matrices of a linear state-space system on the form + ```math \begin{aligned} \dot x &= Ax + Bu\\ @@ -43,29 +51,31 @@ y &= Cx + Du ``` ## Example + The following example builds a simple closed-loop system with a plant $P$ and a controller $C$. Two analysis points are inserted, one before and one after $P$. We then derive a number of sensitivity functions and show the corresponding code using the package ControlSystemBase.jl ```@example LINEAR_ANALYSIS using ModelingToolkitStandardLibrary.Blocks, ModelingToolkit -@named P = FirstOrder(k=1, T=1) # A first-order system with pole in -1 +@named P = FirstOrder(k = 1, T = 1) # A first-order system with pole in -1 @named C = Gain(-1) # A P controller t = ModelingToolkit.get_iv(P) -eqs = [ - connect(P.output, :plant_output, C.input) # Connect with an automatically created analysis point called :plant_output - connect(C.output, :plant_input, P.input) # Connect with an automatically created analysis point called :plant_input -] -sys = ODESystem(eqs, t, systems=[P,C], name=:feedback_system) +eqs = [connect(P.output, :plant_output, C.input) # Connect with an automatically created analysis point called :plant_output + connect(C.output, :plant_input, P.input)] +sys = ODESystem(eqs, t, systems = [P, C], name = :feedback_system) matrices_S = get_sensitivity(sys, :plant_input)[1] # Compute the matrices of a state-space representation of the (input)sensitivity function. matrices_T = get_comp_sensitivity(sys, :plant_input)[1] ``` + Continued linear analysis and design can be performed using ControlSystemsBase.jl. We create `ControlSystemsBase.StateSpace` objects using + ```@example LINEAR_ANALYSIS using ControlSystemsBase, Plots S = ss(matrices_S...) T = ss(matrices_T...) -bodeplot([S, T], lab=["S" "" "T" ""], plot_title="Bode plot of sensitivity functions", margin=5Plots.mm) +bodeplot([S, T], lab = ["S" "" "T" ""], plot_title = "Bode plot of sensitivity functions", + margin = 5Plots.mm) ``` The sensitivity functions obtained this way should be equivalent to the ones obtained with the code below @@ -84,38 +94,50 @@ We may also derive the loop-transfer function $L(s) = P(s)C(s)$ using matrices_L = get_looptransfer(sys, :plant_output)[1] L = ss(matrices_L...) ``` + which is equivalent to the following with ControlSystems + ```@example LINEAR_ANALYSIS_CS -L = P*(-C) # Add the minus sign to build the negative feedback into the controller +L = P * (-C) # Add the minus sign to build the negative feedback into the controller ``` - To obtain the transfer function between two analysis points, we call `linearize` + ```@example LINEAR_ANALYSIS using ModelingToolkit # hide matrices_PS = linearize(sys, :plant_input, :plant_output)[1] ``` + this particular transfer function should be equivalent to the linear system `P(s)S(s)`, i.e., equivalent to + ```@example LINEAR_ANALYSIS_CS feedback(P, C) ``` ### Obtaining transfer functions + A statespace system from [ControlSystemsBase](https://juliacontrol.github.io/ControlSystems.jl/stable/man/creating_systems/) can be converted to a transfer function using the function `tf`: + ```@example LINEAR_ANALYSIS_CS tf(S) ``` ## Gain and phase margins + Further linear analysis can be performed using the [analysis methods from ControlSystemsBase](https://juliacontrol.github.io/ControlSystems.jl/stable/lib/analysis/). For example, calculating the gain and phase margins of a system can be done using + ```@example LINEAR_ANALYSIS_CS margin(P) ``` -(they are infinite for this system). A Nyquist plot can be produced using + +(they are infinite for this system). A Nyquist plot can be produced using + ```@example LINEAR_ANALYSIS_CS nyquistplot(P) ``` + ## Index + ```@index Pages = ["linear_analysis.md"] ``` diff --git a/docs/src/API/magnetic.md b/docs/src/API/magnetic.md index 15bac70c5..dd099490b 100644 --- a/docs/src/API/magnetic.md +++ b/docs/src/API/magnetic.md @@ -1,14 +1,17 @@ # ModelingToolkitStandardLibrary: Magnetic Components - ```@contents Pages = ["magnetic.md"] ``` + ## Index + ```@index Pages = ["magnetic.md"] ``` + ## Flux Tubes + ```@meta CurrentModule = ModelingToolkitStandardLibrary.Magnetic.FluxTubes ``` @@ -39,4 +42,4 @@ ElectroMagneticConverter ```@docs ConstantMagneticPotentialDifference ConstantMagneticFlux -``` \ No newline at end of file +``` diff --git a/docs/src/API/mechanical.md b/docs/src/API/mechanical.md index 17be25633..312c8b98b 100644 --- a/docs/src/API/mechanical.md +++ b/docs/src/API/mechanical.md @@ -1,16 +1,18 @@ # ModelingToolkit Standard Library: Mechanical Components - ```@contents Pages = ["mechanical.md"] Depth = 3 ``` + ## Index + ```@index Pages = ["mechanical.md"] ``` ## Rotational Components + ```@meta CurrentModule = ModelingToolkitStandardLibrary.Mechanical.Rotational ``` diff --git a/docs/src/API/thermal.md b/docs/src/API/thermal.md index cdc515c3f..d3b984906 100644 --- a/docs/src/API/thermal.md +++ b/docs/src/API/thermal.md @@ -1,13 +1,15 @@ # ModelingToolkitStandardLibrary: Thermal Components + ```@meta CurrentModule = ModelingToolkitStandardLibrary.Thermal ``` - ```@contents Pages = ["thermal.md"] ``` + ## Index + ```@index Pages = ["thermal.md"] ``` @@ -46,4 +48,4 @@ FixedHeatFlow FixedTemperature PrescribedHeatFlow PrescribedTemperature -``` \ No newline at end of file +``` diff --git a/docs/src/connectors/connections.md b/docs/src/connectors/connections.md index 050871d68..1f77f6d41 100644 --- a/docs/src/connectors/connections.md +++ b/docs/src/connectors/connections.md @@ -1,16 +1,19 @@ # Introduction -In Physical Network Acausal modeling each physical domain must define a **connector** to combine model components. Each physical domain **connector** defines a minimum of 2 variables, one which is called a *Through* variable, and one which is called an *Across* variable. Both Modelica and SimScape define these variables in the same way: -- [Modelica Connectors](https://mbe.modelica.university/components/connectors/#acausal-connection) -- [SimScape Connectors](https://www.mathworks.com/help/simscape/ug/basic-principles-of-modeling-physical-networks.html#bq89sba-6) +In Physical Network Acausal modeling, each physical domain must define a **connector** to combine model components. Each physical domain **connector** defines a minimum of 2 variables, one which is called a *Through* variable, and one which is called an *Across* variable. Both Modelica and SimScape define these variables in the same way: -However, the standard libraries differ on the selection of the Across variable for the Mechanical Translation and Rotation libraries, Modelica choosing position and angle and SimScape choosing velocity and angular velocity, respectively for Translation and Rotation. Modelica describes their decision [here](https://mbe.modelica.university/components/connectors/simple_domains/). In summary they would like to provide less integration in the model to avoid lossy numerical behavior, but this decision assumes the lowest order derivative is needed by the model. Numerically it is possible to define the connector either way, but there are some consequences to this decision, and therefore we will study them in detail here as they relate to ModelingToolkit. + - [Modelica Connectors](https://mbe.modelica.university/components/connectors/#acausal-connection) + - [SimScape Connectors](https://www.mathworks.com/help/simscape/ug/basic-principles-of-modeling-physical-networks.html#bq89sba-6) + +However, the standard libraries differ on the selection of the Across variable for the Mechanical Translation and Rotation libraries, Modelica choosing position and angle and SimScape choosing velocity and angular velocity, respectively for Translation and Rotation. Modelica describes their decision [here](https://mbe.modelica.university/components/connectors/simple_domains/). In summary, they would like to provide less integration in the model to avoid lossy numerical behavior, but this decision assumes the lowest order derivative is needed by the model. Numerically it is possible to define the connector either way, but there are some consequences of this decision, and therefore we will study them in detail here as they relate to ModelingToolkit. # Through and Across Variable Theory + ### General -The idea behind the selection of the **through** variable is that it should be a time derivative of some conserved quantity. The conserved quantity should be expressed by the **across** variable. In general terms the physical system is given by -- Energy Dissipation & Flow: +The idea behind the selection of the **through** variable is that it should be a time derivative of some conserved quantity. The conserved quantity should be expressed by the **across** variable. In general terms, the physical system is given by + + - Energy Dissipation & Flow: ```math \begin{aligned} @@ -19,60 +22,69 @@ The idea behind the selection of the **through** variable is that it should be a \end{aligned} ``` +### Electrical +For the Electrical domain, the across variable is *voltage* and the through variable *current*. Therefore -### Electrical -For the Electrical domain the across variable is *voltage* and the through variable *current*. Therefore + - Energy Dissipation: -- Energy Dissipation: ```math \partial {\color{blue}{voltage}} / \partial t \cdot capacitance = {\color{green}{current}} ``` -- Flow: + - Flow: + ```math {\color{green}{current}} \cdot resistance = {\color{blue}{voltage}} ``` ### Translational + For the translation domain, choosing *velocity* for the across variable and *force* for the through gives -- Energy Dissipation: -```math + - Energy Dissipation: + +```math \partial {\color{blue}{velocity}} / \partial t \cdot mass = {\color{green}{force}} ``` -- Flow: -```math + - Flow: + +```math {\color{green}{force}} \cdot (1/damping) = {\color{blue}{velocity}} ``` -The diagram here shows the similarity of problems in different physical domains. +The diagram here shows the similarity of problems in different physical domains. ![Through and Across Variables](through_across.png) ### Translational Connector using *Position* Across Variable -Now, if we choose *position* for the across variable, a similar relationship can be established, but the patern must be broken. -- Energy Dissipation: -```math +Now, if we choose *position* for the across variable, a similar relationship can be established, but the pattern must be broken. + + - Energy Dissipation: + +```math \partial^2 {\color{blue}{position}} / \partial t^2 \cdot mass = {\color{green}{force}} ``` -- Flow: -```math + - Flow: + +```math {\color{green}{force}} \cdot (1/damping) = \partial {\color{blue}{position}} / \partial t ``` As can be seen, we must now establish a higher order derivative to define the Energy Dissipation and Flow equations, requiring an extra equation, as will be shown in the example below. # Examples + ### Electrical Domain + We can generate the above relationship with ModelingToolkit and the ModelingToolkitStandardLibrary using 3 blocks: -- Capacitor: for energy storage with initial voltage = 1V -- Resistor: for energy flow -- Ground: for energy sink + - Capacitor: for energy storage with initial voltage = 1V + - Resistor: for energy flow + - Ground: for energy sink As can be seen, this will give a 1 equation model matching our energy dissipation relationship @@ -86,12 +98,10 @@ using Plots @named capacitor = Capacitor(C = 1) @named ground = Ground() -eqs = [ - connect(capacitor.p, resistor.p) - connect(resistor.n, ground.g, capacitor.n) - ] +eqs = [connect(capacitor.p, resistor.p) + connect(resistor.n, ground.g, capacitor.n)] -@named model = ODESystem(eqs, t; systems=[resistor, capacitor, ground]) +@named model = ODESystem(eqs, t; systems = [resistor, capacitor, ground]) sys = structural_simplify(model) @@ -99,42 +109,39 @@ println.(equations(sys)) nothing # hide ``` -The solution shows what we would expect, a non-linear disipation of voltage and releated decrease in current flow... +The solution shows what we would expect, a non-linear dissipation of voltage and related decrease in current flow… ```@example connections prob = ODEProblem(sys, [1.0], (0, 10.0), []) sol = solve(prob) -p1=plot(sol, idxs=[capacitor.v]) -p2=plot(sol, idxs=[resistor.i]) -plot(p1,p2) -savefig("electrical.png"); nothing # hide +p1 = plot(sol, idxs = [capacitor.v]) +p2 = plot(sol, idxs = [resistor.i]) +plot(p1, p2) ``` -![Plot of Electrical Example](electrical.png) +### Mechanical Translational Domain -### Mechanical Translational Domain #### Across Variable = velocity + Now using the Translational library based on velocity, we can see the same relationship with a system reduced to a single equation, using the components: -- Body (i.e. moving mass): for kinetic energy storage with an initial velocity = 1m/s -- Damper: for energy flow -- Fixed: for energy sink + - Body (i.e. moving mass): for kinetic energy storage with an initial velocity = 1m/s + - Damper: for energy flow + - Fixed: for energy sink ```@example connections using ModelingToolkitStandardLibrary const TV = ModelingToolkitStandardLibrary.Mechanical.Translational -@named damping = TV.Damper(d=1, v_a_0=1) -@named body = TV.Mass(m=1, v_0=1) +@named damping = TV.Damper(d = 1, v_a_0 = 1) +@named body = TV.Mass(m = 1, v_0 = 1) @named ground = TV.Fixed() -eqs = [ - connect(damping.flange_a, body.flange) - connect(ground.flange, damping.flange_b) - ] +eqs = [connect(damping.flange_a, body.flange) + connect(ground.flange, damping.flange_b)] -@named model = ODESystem(eqs, t; systems=[damping, body, ground]) +@named model = ODESystem(eqs, t; systems = [damping, body, ground]) sys = structural_simplify(model) @@ -142,138 +149,136 @@ println.(full_equations(sys)) nothing # hide ``` -As expected we have a similar solution... +As expected, we have a similar solution… + ```@example connections prob = ODEProblem(sys, [], (0, 10.0), []) sol_v = solve(prob) -p1=plot(sol_v, idxs=[body.v]) -p2=plot(sol_v, idxs=[damping.f]) -plot(p1,p2) -savefig("mechanical_velocity.png"); nothing # hide +p1 = plot(sol_v, idxs = [body.v]) +p2 = plot(sol_v, idxs = [damping.f]) +plot(p1, p2) ``` -![Plot of Mechanical (Velocity Based) Example](mechanical_velocity.png) - - #### Across Variable = position -Now, let's consider the position based approach. We can build the same model with the same components. As can be seen, we now end of up with 2 equations, because we need to relate the lower derivative (position) to force (with acceleration). + +Now, let's consider the position-based approach. We can build the same model with the same components. As can be seen, we now end of up with 2 equations, because we need to relate the lower derivative (position) to force (with acceleration). ```@example connections const TP = ModelingToolkitStandardLibrary.Mechanical.TranslationalPosition -@named damping = TP.Damper(d=1, v_a_0=1) -@named body = TP.Mass(m=1, v_0=1) -@named ground = TP.Fixed(s_0=0) +@named damping = TP.Damper(d = 1, v_a_0 = 1) +@named body = TP.Mass(m = 1, v_0 = 1) +@named ground = TP.Fixed(s_0 = 0) - eqs = [ - connect(damping.flange_a, body.flange) - connect(ground.flange, damping.flange_b) - ] +eqs = [connect(damping.flange_a, body.flange) + connect(ground.flange, damping.flange_b)] -@named model = ODESystem(eqs, t; systems=[damping, body, ground]) +@named model = ODESystem(eqs, t; systems = [damping, body, ground]) sys = structural_simplify(model) println.(full_equations(sys)) nothing # hide ``` + As can be seen, we get exactly the same result. The only difference here is that we are solving an extra equation, which allows us to plot the body position as well. ```@example connections prob = ODEProblem(sys, [], (0, 10.0), []) sol_p = solve(prob) -p1=plot(sol_p, idxs=[body.v]) -p2=plot(sol_p, idxs=[damping.f]) -p3=plot(sol_p, idxs=[body.s]) +p1 = plot(sol_p, idxs = [body.v]) +p2 = plot(sol_p, idxs = [damping.f]) +p3 = plot(sol_p, idxs = [body.s]) -plot(p1,p2,p3) -savefig("mechanical_position.png"); nothing # hide +plot(p1, p2, p3) ``` -![Plot of Mechanical (Velocity Based) Example](mechanical_position.png) - The question then arises, can the position be plotted when using the Mechanical Translational Domain based on the Velocity Across variable? Yes, we can! There are 2 solutions: -1. the `Mass` component will add the position variable when the `s_0` parameter is used to set an initial position. Otherwise the position is not tracked by the component. + 1. the `Mass` component will add the position variable when the `s_0` parameter is used to set an initial position. Otherwise, the component does not track the position. ```julia -@named body = TV.Mass(m=1, v_0=1, s_0=0) +@named body = TV.Mass(m = 1, v_0 = 1, s_0 = 0) ``` -2. implement a `PositionSensor` -TODO: Implement Translation Sensors - + 2. implement a `PositionSensor` + TODO: Implement Translation Sensors Either option will produce the same result regardless of which across variable is used. If the same result is given, why are both options included in the Standard Library, what are the differences? These differences will be discussed next so that an informed decision can be made about which domain is best for your model. # Mechanical/Translational Library Differences (Velocity vs. Position Connectors) + ## Initialization -The main difference between `ModelingToolkitStandardLibrary.Mechanical.Translational` and `ModelingToolkitStandardLibrary.Mechanical.TranslationalPosition` is how they are initialized. In the `ModelingToolkitStandardLibrary` initialization parameters are defined at the component level, so we simply need to be careful to set the correct initial conditions for the domain that it used. Let's use the following example problem to explain the differences. + +The main difference between `ModelingToolkitStandardLibrary.Mechanical.Translational` and `ModelingToolkitStandardLibrary.Mechanical.TranslationalPosition` is how they are initialized. In the `ModelingToolkitStandardLibrary` initialization, parameters are defined at the component level, so we simply need to be careful to set the correct initial conditions for the domain that it used. Let's use the following example problem to explain the differences. ![Example Mechanical Model](model.png) -In this problem we have a mass, spring, and damper which are connected to a fixed point. Let's see how each component is defined. +In this problem, we have a mass, spring, and damper which are connected to a fixed point. Let's see how each component is defined. #### Damper -The damper will connect the flange/flange 1 (`flange_a`) to the mass, and flange/flange 2 (`flange_b`) to the fixed point. For both position and velocity based domains, we set the damping constant `d=1` and `v_a_0=1` and leave the default for `v_b_0` at 0. For the position domain we also need to set the initial positions for `flange_a` and `flange_b`. + +The damper will connect the flange/flange 1 (`flange_a`) to the mass, and flange/flange 2 (`flange_b`) to the fixed point. For both position- and velocity-based domains, we set the damping constant `d=1` and `v_a_0=1` and leave the default for `v_b_0` at 0. For the position domain, we also need to set the initial positions for `flange_a` and `flange_b`. ```@example connections -@named dv = TV.Damper(d=1, v_a_0=1) -@named dp = TP.Damper(d=1, v_a_0=1, s_a_0=3, s_b_0=1) +@named dv = TV.Damper(d = 1, v_a_0 = 1) +@named dp = TP.Damper(d = 1, v_a_0 = 1, s_a_0 = 3, s_b_0 = 1) nothing # hide ``` #### Spring -The spring will connect the flange/flange 1 (`flange_a`) to the mass, and flange/flange 2 (`flange_b`) to the fixed point. For both position and velocity based domains, we set the spring constant `k=1`. The velocity domain then requires the initial velocity `v_a_0` and initial spring stretch `delta_s_0`. The position domain instead needs the initial positions for `flange_a` and `flange_b` and the natural spring length `l`. + +The spring will connect the flange/flange 1 (`flange_a`) to the mass, and flange/flange 2 (`flange_b`) to the fixed point. For both position- and velocity-based domains, we set the spring constant `k=1`. The velocity domain then requires the initial velocity `v_a_0` and initial spring stretch `delta_s_0`. The position domain instead needs the initial positions for `flange_a` and `flange_b` and the natural spring length `l`. ```@example connections -@named sv = TV.Spring(k=1, v_a_0=1, delta_s_0=1) -@named sp = TP.Spring(k=1, s_a_0=3, s_b_0=1, l=1) +@named sv = TV.Spring(k = 1, v_a_0 = 1, delta_s_0 = 1) +@named sp = TP.Spring(k = 1, s_a_0 = 3, s_b_0 = 1, l = 1) nothing # hide ``` #### Mass -For both position and velocity based domains, we set the mass `m=1` and initial velocity `v_0=1`. Like the damper, the position domain requires the position initial conditions set as well. + +For both position- and velocity-based domains, we set the mass `m=1` and initial velocity `v_0=1`. Like the damper, the position domain requires the position initial conditions set as well. ```@example connections -@named bv = TV.Mass(m=1, v_0=1) -@named bp = TP.Mass(m=1, v_0=1, s_0=3) +@named bv = TV.Mass(m = 1, v_0 = 1) +@named bp = TP.Mass(m = 1, v_0 = 1, s_0 = 3) nothing # hide ``` #### Fixed -Here the velocity domain requires no initial condition, but for our model to work as defined we must set the position domain component to the correct intital position. + +Here the velocity domain requires no initial condition, but for our model to work as defined we must set the position domain component to the correct initial position. ```@example connections -@named gv = TV.Fixed() -@named gp = TP.Fixed(s_0=1) +@named gv = TV.Fixed() +@named gp = TP.Fixed(s_0 = 1) nothing # hide ``` ### Comparison -As can be seen, the position based domain requires more initial condition information to be properly defined since the absolute position information is required. Thereore based on the model being described, it may be more natural to choose one domain over the other. -Let's define a quick function to simplify and solve the 2 different systems. Note we will solve with a fixed time step and a set tolerance to compare the numerical differences. +As can be seen, the position-based domain requires more initial condition information to be properly defined, since the absolute position information is required. Therefore, based on the model being described, it may be more natural to choose one domain over the other. + +Let's define a quick function to simplify and solve the 2 different systems. Note, we will solve with a fixed time step and a set tolerance to compare the numerical differences. ```@example connections function simplify_and_solve(damping, spring, body, ground) + eqs = [connect(spring.flange_a, body.flange, damping.flange_a) + connect(spring.flange_b, damping.flange_b, ground.flange)] - eqs = [connect(spring.flange_a, body.flange, damping.flange_a) - connect(spring.flange_b, damping.flange_b, ground.flange) - ] + @named model = ODESystem(eqs, t; systems = [ground, body, spring, damping]) - @named model = ODESystem(eqs, t; systems = [ground, body, spring, damping]) + sys = structural_simplify(model) - sys = structural_simplify(model) + println.(full_equations(sys)) - println.(full_equations(sys)) + prob = ODEProblem(sys, [], (0, 10.0), []) + sol = solve(prob; dt = 0.1, adaptive = false, reltol = 1e-9, abstol = 1e-9) - prob = ODEProblem(sys, [], (0, 10.0), []) - sol = solve(prob; dt=0.1, adaptive=false, reltol=1e-9, abstol=1e-9) - - return sol + return sol end nothing # hide ``` @@ -281,44 +286,38 @@ nothing # hide Now let's solve the velocity domain model ```@example connections -solv=simplify_and_solve(dv, sv, bv, gv); +solv = simplify_and_solve(dv, sv, bv, gv); nothing # hide ``` And the position domain model ```@example connections -solp=simplify_and_solve(dp, sp, bp, gp); +solp = simplify_and_solve(dp, sp, bp, gp); nothing # hide ``` -Now we can plot the comparison of the 2 models and see they give the same result. +Now we can plot the comparison of the 2 models and see they give the same result. + ```@example connections -plot(ylabel="mass velocity [m/s]") -plot!(solv, idxs=[bv.v]) -plot!(solp, idxs=[bp.v]) -savefig("mass_velocity.png"); nothing # hide +plot(ylabel = "mass velocity [m/s]") +plot!(solv, idxs = [bv.v]) +plot!(solp, idxs = [bp.v]) ``` -![Mass Velocity Comparison](mass_velocity.png) - - -But, what if we wanted to plot the mass position? This is easy for the position based domain, we have the state `bp₊s(t)`, but for the velocity based domain we have `sv₊delta_s(t)` which is the spring stretch. To get the absolute position we add the spring natrual length (1m) and the fixed position (1m). As can be seen, we then get the same result. +But, what if we wanted to plot the mass position? This is easy for the position-based domain, we have the state `bp₊s(t)`, but for the velocity-based domain we have `sv₊delta_s(t)` which is the spring stretch. To get the absolute position, we add the spring natural length (1m) and the fixed position (1m). As can be seen, we then get the same result. ```@example connections -plot(ylabel="mass position [m]") -plot!(solv, idxs=[sv.delta_s + 1 + 1]) -plot!(solp, idxs=[bp.s]) -savefig("mass_position.png"); nothing # hide +plot(ylabel = "mass position [m]") +plot!(solv, idxs = [sv.delta_s + 1 + 1]) +plot!(solp, idxs = [bp.s]) ``` -![Mass Position Comparison](mass_position.png) - -So in conclusion, the position based domain gives easier access to absolute position information, but requires more initial condition information. +So in conclusion, the position based domain gives easier access to absolute position information, but requires more initial condition information. +## Accuracy -## Acuracy -One may ask then what is the trade off in terms of numerical acuracy? When we look at the simplified equations, we can see that actually both systems solve the same equations. The differential equations of the velocity domain are +One may then ask, what the trade-off in terms of numerical accuracy is. When we look at the simplified equations, we can see that actually both systems solve the same equations. The differential equations of the velocity domain are ```math \begin{aligned} @@ -336,7 +335,7 @@ m \cdot \dot{v} + d \cdot v + k \cdot (s - s_{b_0} - l) = 0 \\ \end{aligned} ``` -By definition the spring stretch is +By definition, the spring stretch is ```math \Delta s = s - s_{b_0} - l @@ -345,12 +344,9 @@ By definition the spring stretch is Which means both systems are actually solving the same exact system. We can plot the numerical difference between the 2 systems and see the result is negligible. ```@example connections -plot(title="numerical difference: vel. vs. pos. domain", xlabel="time [s]", ylabel="solv[bv.v] .- solp[bp.v]") +plot(title = "numerical difference: vel. vs. pos. domain", xlabel = "time [s]", + ylabel = "solv[bv.v] .- solp[bp.v]") time = 0:0.1:10 -plot!(time, (solv(time)[bv.v] .- solp(time)[bp.v]), label="") +plot!(time, (solv(time)[bv.v] .- solp(time)[bp.v]), label = "") Plots.ylims!(-1e-15, 1e-15) -savefig("err.png"); nothing # hide ``` - -![Accuracy Comparison](err.png) - diff --git a/docs/src/index.md b/docs/src/index.md index e955b1f7b..296c41a1b 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -1,85 +1,117 @@ # ModelingToolkitStandardLibrary.jl -ModelingToolkitStandardLibrary.jl is a standard library for the -[ModelingToolkit](https://docs.sciml.ai/ModelingToolkit/stable/) acasual modeling system. +ModelingToolkitStandardLibrary.jl is a standard library for the +[ModelingToolkit](https://docs.sciml.ai/ModelingToolkit/stable/) acausal modeling system. ## Installation -Assuming that you already have Julia correctly installed, it suffices to import -ModelingToolkitStandardLibrary.jl in the standard way: +To install ModelingToolkitStandardLibrary.jl, use the Julia package manager: ```julia -import Pkg; Pkg.add("ModelingToolkitStandardLibrary") +using Pkg +Pkg.add("ModelingToolkitStandardLibrary") ``` -## Tutorials +## Tutorials -- [RC Circuit](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/tutorials/rc_circuit/) -- [Custom Component](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/tutorials/custom_component/) -- [Thermal Model](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/tutorials/thermal_model/) -- [DC Motor with PI-controller](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/tutorials/dc_motor_pi/) + - [RC Circuit](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/tutorials/rc_circuit/) + - [Custom Component](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/tutorials/custom_component/) + - [Thermal Model](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/tutorials/thermal_model/) + - [DC Motor with PI-controller](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/tutorials/dc_motor_pi/) ## Libraries The following are the constituant libraries of the ModelingToolkit Standard Library. -- [Basic Blocks](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/blocks/) -- [Mechanical Components](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/mechanical/) -- [Electrical Components](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/electrical/) -- [Magnetic Components](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/magnetic/) -- [Thermal Components](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/thermal/) + - [Basic Blocks](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/blocks/) + - [Mechanical Components](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/mechanical/) + - [Electrical Components](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/electrical/) + - [Magnetic Components](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/magnetic/) + - [Thermal Components](https://docs.sciml.ai/ModelingToolkitStandardLibrary/stable/API/thermal/) + +## Contributing + + - Please refer to the + [SciML ColPrac: Contributor's Guide on Collaborative Practices for Community Packages](https://github.com/SciML/ColPrac/blob/master/README.md) + for guidance on PRs, issues, and other matters relating to contributing to SciML. + + - See the [SciML Style Guide](https://github.com/SciML/SciMLStyle) for common coding practices and other style decisions. + - There are a few community forums: + + + The #diffeq-bridged and #sciml-bridged channels in the + [Julia Slack](https://julialang.org/slack/) + + The #diffeq-bridged and #sciml-bridged channels in the + [Julia Zulip](https://julialang.zulipchat.com/#narrow/stream/279055-sciml-bridged) + + On the [Julia Discourse forums](https://discourse.julialang.org) + + See also [SciML Community page](https://sciml.ai/community/) ## Reproducibility + ```@raw html
The documentation of this SciML package was built using these direct dependencies, ``` + ```@example using Pkg # hide Pkg.status() # hide ``` + ```@raw html
``` + ```@raw html
and using this machine and Julia version. ``` + ```@example using InteractiveUtils # hide versioninfo() # hide ``` + ```@raw html
``` + ```@raw html
A more complete overview of all dependencies and their versions is also provided. ``` + ```@example using Pkg # hide -Pkg.status(;mode = PKGMODE_MANIFEST) # hide +Pkg.status(; mode = PKGMODE_MANIFEST) # hide ``` + ```@raw html
``` + ```@raw html You can also download the manifest file and the project file. -``` \ No newline at end of file +``` diff --git a/docs/src/tutorials/custom_component.md b/docs/src/tutorials/custom_component.md index a22cd161f..638dc2afd 100644 --- a/docs/src/tutorials/custom_component.md +++ b/docs/src/tutorials/custom_component.md @@ -1,9 +1,11 @@ # Custom Component + In this tutorial, the creation of a custom component is demonstrated via the [Chua's circuit](https://en.wikipedia.org/wiki/Chua%27s_circuit). -The circuit is a simple circuit that shows chaotic behaviour. -Except for a non-linear resistor every other component already is part of `ModelingToolkitStandardLibrary.Electrical`. +The circuit is a simple circuit that shows chaotic behavior. +Except for a non-linear resistor, every other component already is part of `ModelingToolkitStandardLibrary.Electrical`. First, we need to make some imports. + ```@example components using ModelingToolkit using ModelingToolkitStandardLibrary.Electrical @@ -12,9 +14,12 @@ using OrdinaryDiffEq using IfElse: ifelse using Plots ``` + ## Custom Component + Now the custom component can be defined. The [Modelica implementation](https://www.maplesoft.com/documentation_center/online_manuals/modelica/Modelica_Electrical_Analog_Examples_Utilities.html#Modelica.Electrical.Analog.Examples.Utilities.NonlinearResistor) of the `NonlinearResistor` looks as follows: + ```Modelica model NonlinearResistor "Chua's resistor" extends Interfaces.OnePort; @@ -26,101 +31,112 @@ equation i = if (v < -Ve) then Gb*(v + Ve) - Ga*Ve else if (v > Ve) then Gb*(v - Ve) + Ga*Ve else Ga*v; end NonlinearResistor; ``` + this can almost be directly translated to the syntax of `ModelingToolkit`. + ```@example components @parameters t -function NonlinearResistor(;name, Ga, Gb, Ve) +function NonlinearResistor(; name, Ga, Gb, Ve) @named oneport = OnePort() @unpack v, i = oneport pars = @parameters Ga=Ga Gb=Gb Ve=Ve eqs = [ - i ~ ifelse(v < -Ve, - Gb*(v + Ve) - Ga*Ve, - ifelse(v > Ve, - Gb*(v - Ve) + Ga*Ve, - Ga*v, - ), - ) + i ~ ifelse(v < -Ve, + Gb * (v + Ve) - Ga * Ve, + ifelse(v > Ve, + Gb * (v - Ve) + Ga * Ve, + Ga * v)), ] - extend(ODESystem(eqs, t, [], pars; name=name), oneport) + extend(ODESystem(eqs, t, [], pars; name = name), oneport) end nothing # hide ``` ### Explanation + All components in `ModelingToolkit` are created via a function that serves as the constructor and returns some form of system, in this case, an `ODESystem`. Since the non-linear resistor is essentially a standard electrical component with two ports, we can extend from the `OnePort` component of the library. + ```julia @named oneport = OnePort() ``` + This creates a `OnePort` with the `name = :oneport`. For easier notation, we can unpack the states of the component + ```julia @unpack v, i = oneport ``` + It might be a good idea to create parameters for the constants of the `NonlinearResistor`. + ```julia pars = @parameters Ga=Ga Gb=Gb Ve=Ve ``` + The syntax looks funny but it simply creates symbolic parameters with the name `Ga` where its default value is set from the function's argument `Ga`. While this is not strictly necessary it allows the user to `remake` the problem easily with different parameters or allow for auto-tuning or parameter optimization without having to do all the costly steps that may be involved with building and simplifying a model. The non-linear (in this case piece-wise constant) equation for the current can be implemented using `IfElse.ifelse`. Finally, the created `oneport` component is extended with the created equations and parameters. In this case, no extra state variables are added, hence an empty vector is supplied. The independent variable `t` needs to be supplied as the second argument. + ```julia -extend(ODESystem(eqs, t, [], pars; name=name), oneport) +extend(ODESystem(eqs, t, [], pars; name = name), oneport) ``` ## Building the Model + The final model can now be created with the components from the library and the new custom component. + ```@example components -@named L = Inductor(L=18) -@named Ro = Resistor(R=12.5e-3) -@named G = Conductor(G=0.565) -@named C1 = Capacitor(C=10, v_start=4) -@named C2 = Capacitor(C=100) -@named Nr = NonlinearResistor( - Ga = -0.757576, - Gb = -0.409091, - Ve=1) +@named L = Inductor(L = 18) +@named Ro = Resistor(R = 12.5e-3) +@named G = Conductor(G = 0.565) +@named C1 = Capacitor(C = 10, v_start = 4) +@named C2 = Capacitor(C = 100) +@named Nr = NonlinearResistor(Ga = -0.757576, + Gb = -0.409091, + Ve = 1) @named Gnd = Ground() -connections = [ - connect(L.p, G.p) - connect(G.n, Nr.p) - connect(Nr.n, Gnd.g) - connect(C1.p, G.n) - connect(L.n, Ro.p) - connect(G.p, C2.p) - connect(C1.n, Gnd.g) - connect(C2.n, Gnd.g) - connect(Ro.n, Gnd.g) -] - -@named model = ODESystem(connections, t, systems=[L, Ro, G, C1, C2, Nr, Gnd]) +connections = [connect(L.p, G.p) + connect(G.n, Nr.p) + connect(Nr.n, Gnd.g) + connect(C1.p, G.n) + connect(L.n, Ro.p) + connect(G.p, C2.p) + connect(C1.n, Gnd.g) + connect(C2.n, Gnd.g) + connect(Ro.n, Gnd.g)] + +@named model = ODESystem(connections, t, systems = [L, Ro, G, C1, C2, Nr, Gnd]) nothing # hide ``` ## Simulating the Model + Now the model can be simulated. First, `structural_simplify` is called on the model and an `ODEProblem` is built from the result. Since the initial voltage of the first capacitor was already specified via `v_start`, no initial condition is given and an empty pair is supplied. + ```@example components sys = structural_simplify(model) -prob = ODEProblem(sys, Pair[], (0, 5e4), saveat=0.01) +prob = ODEProblem(sys, Pair[], (0, 5e4), saveat = 0.01) sol = solve(prob, Rodas4()) -Plots.plot(sol[C1.v], sol[C2.v], title="Chaotic Attractor", label="", ylabel="C1 Voltage in V", xlabel="C2 Voltage in V") +Plots.plot(sol[C1.v], sol[C2.v], title = "Chaotic Attractor", label = "", + ylabel = "C1 Voltage in V", xlabel = "C2 Voltage in V") Plots.savefig("chua_phase_plane.png") nothing # hide -Plots.plot(sol; vars=[C1.v, C2.v, L.i], labels=["C1 Voltage in V" "C1 Voltage in V" "Inductor Current in A"]) +Plots.plot(sol; vars = [C1.v, C2.v, L.i], + labels = ["C1 Voltage in V" "C1 Voltage in V" "Inductor Current in A"]) Plots.savefig("chua.png") nothing # hide ``` ![Time series plot of C1.v, C2.v and L.i](chua_phase_plane.png) -![Phase plane plot of C1.v and C2.v](chua.png) \ No newline at end of file +![Phase plane plot of C1.v and C2.v](chua.png) diff --git a/docs/src/tutorials/dc_motor_pi.md b/docs/src/tutorials/dc_motor_pi.md index 1b55013c1..0a5eebde4 100644 --- a/docs/src/tutorials/dc_motor_pi.md +++ b/docs/src/tutorials/dc_motor_pi.md @@ -1,15 +1,16 @@ # DC Motor with PI-controller -In this example a PI-controller is setup for speed control of a DC-motor. An equivalent circuit diagram is depicted below. -![DC-motor](https://user-images.githubusercontent.com/50108075/196108356-0e8605e3-61a9-4006-8559-786252e55928.png) +In this example, a PI-controller is set up for speed control of a DC-motor. An equivalent circuit diagram is depicted below. +![DC-motor](https://user-images.githubusercontent.com/50108075/196108356-0e8605e3-61a9-4006-8559-786252e55928.png) ## Modeling and simulation -The electrical part consists of a resistance and inductance. The coupling between the electrical and rotational domain is done via an electro motive force (EMF) component. The voltage across the EMF is proportional to the angular velocity and the current is proportional to the torque. On the mechanical side viscous friction in e.g. a bearing and the inertia of the shaft is modelled. -A PI-controller with anti windup measure should be used as a speed controller. A simulation is performed to verify the tracking performance of the controller and the disturbance rejection capabilities. +The electrical part consists of a resistance and inductance. The coupling between the electrical and rotational domain is done via an electro-motive force (EMF) component. The voltage across the EMF is proportional to the angular velocity and the current is proportional to the torque. On the mechanical side, viscous friction in, e.g., a bearing and the inertia of the shaft is modelled. -First the needed packages are imported and the parameters of the model defined. +A PI-controller with anti-windup measure should be used as a speed controller. A simulation is performed to verify the tracking performance of the controller and the disturbance rejection capabilities. + +First, the needed packages are imported and the parameters of the model defined. ```@example dc_motor_pi using ModelingToolkit @@ -82,8 +83,8 @@ connections = [connect(fixed.flange, emf.support, friction.flange_b) nothing # hide ``` -Now the model can be simulated. Typical rotational mechanical systems are described via `DAE` -(differential algebraic equations), however in this case ModelingToolkit can simplify the model enough +Now the model can be simulated. Typical rotational mechanical systems are described via `DAE` +(differential algebraic equations), however in this case, ModelingToolkit can simplify the model enough so that it can be represented as a system of `ODEs` (ordinary differential equations). ```@example dc_motor_pi @@ -99,12 +100,16 @@ Plots.plot(p1, p2, layout = (2, 1)) ``` ## Closed-loop analysis + When implementing and tuning a control system in simulation, it is a good practice to analyze the closed-loop properties and verify robustness of the closed-loop with respect to, e.g., modeling errors. To facilitate this, we added two analysis points to the set of connections above, more specifically, we added the analysis points named `:y` and `:u` to the connections (for more details on analysis points, see [Linear Analysis](@ref)) + ```julia connect(speed_sensor.w, :y, feedback.input2) connect(pi_controller.ctr_output, :u, source.V) ``` + one at the plant output (`:y`) and one at the plant input (`:u`). We may use these analysis points to calculate, e.g., sensitivity functions, illustrated below. Here, we calculate the sensitivity function $S(s)$ and the complimentary sensitivity function $T(s) = I - S(s)$, defined as + ```math \begin{aligned} S(s) &= \dfrac{1}{I + P(s)C(s)} \\ @@ -118,14 +123,16 @@ matrices_S, simplified_sys = Blocks.get_sensitivity(model, :y) So = ss(matrices_S...) |> minreal # The output-sensitivity function as a StateSpace system matrices_T, simplified_sys = Blocks.get_comp_sensitivity(model, :y) To = ss(matrices_T...)# The output complementary sensitivity function as a StateSpace system -bodeplot([So, To], label=["S" "T"], plot_title="Sensitivity functions", plotphase=false) +bodeplot([So, To], label = ["S" "T"], plot_title = "Sensitivity functions", + plotphase = false) ``` -In a similar fashion, we may compute the loop-transfer function and plot its Nyquist curve +Similarly, we may compute the loop-transfer function and plot its Nyquist curve ```@example dc_motor_pi matrices_L, simplified_sys = Blocks.get_looptransfer(model, :y) L = -ss(matrices_L...) # The loop-transfer function as a StateSpace system. The negative sign is to negate the built-in negative feedback Ms, ωMs = hinfnorm(So) # Compute the peak of the sensitivity function to draw a circle in the Nyquist plot -nyquistplot(L, label="\$L(s)\$", ylims=(-2.5, 0.5), xlims=(-1.2, 0.1), Ms_circles=Ms) -``` \ No newline at end of file +nyquistplot(L, label = "\$L(s)\$", ylims = (-2.5, 0.5), xlims = (-1.2, 0.1), + Ms_circles = Ms) +``` diff --git a/docs/src/tutorials/rc_circuit.md b/docs/src/tutorials/rc_circuit.md index e8760d9f1..312087464 100644 --- a/docs/src/tutorials/rc_circuit.md +++ b/docs/src/tutorials/rc_circuit.md @@ -15,27 +15,27 @@ R = 1.0 C = 1.0 V = 1.0 @variables t -@named resistor = Resistor(R=R) -@named capacitor = Capacitor(C=C) +@named resistor = Resistor(R = R) +@named capacitor = Capacitor(C = C) @named source = Voltage() -@named constant = Constant(k=V) +@named constant = Constant(k = V) @named ground = Ground() -rc_eqs = [ - connect(constant.output, source.V) - connect(source.p, resistor.p) - connect(resistor.n, capacitor.p) - connect(capacitor.n, source.n, ground.g) - ] +rc_eqs = [connect(constant.output, source.V) + connect(source.p, resistor.p) + connect(resistor.n, capacitor.p) + connect(capacitor.n, source.n, ground.g)] -@named rc_model = ODESystem(rc_eqs, t, systems=[resistor, capacitor, constant, source, ground]) +@named rc_model = ODESystem(rc_eqs, t, + systems = [resistor, capacitor, constant, source, ground]) sys = structural_simplify(rc_model) prob = ODAEProblem(sys, Pair[], (0, 10.0)) sol = solve(prob, Tsit5()) plot(sol, vars = [capacitor.v, resistor.i], title = "RC Circuit Demonstration", labels = ["Capacitor Voltage" "Resistor Current"]) -savefig("plot.png"); nothing # hide +savefig("plot.png"); +nothing; # hide ``` -![](plot.png) \ No newline at end of file +![](plot.png) diff --git a/docs/src/tutorials/thermal_model.md b/docs/src/tutorials/thermal_model.md index 50993d6d4..00d4c782f 100644 --- a/docs/src/tutorials/thermal_model.md +++ b/docs/src/tutorials/thermal_model.md @@ -1,9 +1,9 @@ # Heat Conduction Model -This example demonstrates the thermal response of two masses connected by a conducting element. -The two masses have the same heat capacity but different initial temperatures (`T1=100 [°C]`, `T2=0 [°C]`). -The mass with the higher temperature will cool off while the mass with the lower temperature heats up. -They will each asymptotically approach the calculated temperature T_final_K that results +This example demonstrates the thermal response of two masses connected by a conducting element. +The two masses have the same heat capacity but different initial temperatures (`T1=100 [°C]`, `T2=0 [°C]`). +The mass with the higher temperature will cool off, while the mass with the lower temperature heats up. +They will each asymptotically approach the calculated temperature T_final_K that results from dividing the total initial energy in the system by the sum of the heat capacities of each element. ```@example @@ -13,10 +13,10 @@ using ModelingToolkitStandardLibrary.Thermal, ModelingToolkit, OrdinaryDiffEq, P C1 = 15 C2 = 15 -@named mass1 = HeatCapacitor(C=C1, T_start=373.15) -@named mass2 = HeatCapacitor(C=C2, T_start=273.15) -@named conduction = ThermalConductor(G=10) -@named Tsensor1 = TemperatureSensor() +@named mass1 = HeatCapacitor(C = C1, T_start = 373.15) +@named mass2 = HeatCapacitor(C = C2, T_start = 273.15) +@named conduction = ThermalConductor(G = 10) +@named Tsensor1 = TemperatureSensor() @named Tsensor2 = TemperatureSensor() connections = [ @@ -26,7 +26,8 @@ connections = [ connect(mass2.port, Tsensor2.port), ] -@named model = ODESystem(connections, t, systems=[mass1, mass2, conduction, Tsensor1, Tsensor2]) +@named model = ODESystem(connections, t, + systems = [mass1, mass2, conduction, Tsensor1, Tsensor2]) sys = structural_simplify(model) prob = ODEProblem(sys, Pair[], (0, 5.0)) sol = solve(prob, Tsit5()) @@ -36,6 +37,8 @@ T_final_K = sol[(mass1.T * C1 + mass2.T * C2) / (C1 + C2)] plot(title = "Thermal Conduction Demonstration") plot!(sol, vars = [mass1.T, mass2.T], labels = ["Mass 1 Temperature" "Mass 2 Temperature"]) plot!(sol.t, T_final_K, label = "Steady-State Temperature") -savefig("thermal_plot.png"); nothing # hide +savefig("thermal_plot.png"); +nothing; # hide ``` -![Plot of Temperatures](thermal_plot.png) \ No newline at end of file + +![Plot of Temperatures](thermal_plot.png) diff --git a/src/Blocks/analysis_points.jl b/src/Blocks/analysis_points.jl index ee5a39c5f..c7bd063f7 100644 --- a/src/Blocks/analysis_points.jl +++ b/src/Blocks/analysis_points.jl @@ -24,44 +24,50 @@ end AnalysisPoint(name::Symbol) Create an AnalysisPoint for linear analysis. Analysis points can also be created automatically by calling + ``` connect(in, :ap_name, out) ``` !!! danger "Experimental" + The analysis-point interface is currently experimental and at any time subject to breaking changes not respecting semantic versioning. # Arguments: -- `in`: A connector of type [`RealOutput`](@ref). -- `out`: A connector of type [`RealInput`](@ref). -- `name`: The name of the analysis point. + + - `in`: A connector of type [`RealOutput`](@ref). + - `out`: A connector of type [`RealInput`](@ref). + - `name`: The name of the analysis point. See also [`get_sensitivity`](@ref), [`get_comp_sensitivity`](@ref), [`get_looptransfer`](@ref), [`open_loop`](@ref) # Example + ```julia using ModelingToolkitStandardLibrary.Blocks -@named P = FirstOrder(k=1, T=1) +@named P = FirstOrder(k = 1, T = 1) @named C = Gain(-1) t = ModelingToolkit.get_iv(P) -eqs = [ - connect(P.output, C.input) - connect(C.output, :plant_input, P.input) # Connect with an automatically created analysis point -] -sys = ODESystem(eqs, t, systems=[P,C], name=:feedback_system) +eqs = [connect(P.output, C.input) + connect(C.output, :plant_input, P.input)] +sys = ODESystem(eqs, t, systems = [P, C], name = :feedback_system) matrices_S, _ = get_sensitivity(sys, :plant_input) # Compute the matrices of a state-space representation of the (input) sensitivity funciton. matrices_T, _ = get_comp_sensitivity(sys, :plant_input) ``` + Continued linear analysis and design can be performed using ControlSystemsBase.jl. Create `ControlSystemsBase.StateSpace` objects using + ```julia using ControlSystemsBase, Plots S = ss(matrices_S...) T = ss(matrices_T...) -bodeplot([S, T], lab=["S" "T"]) +bodeplot([S, T], lab = ["S" "T"]) ``` + The sensitivity functions obtained this way should be equivalent to the ones obtained with the code below + ``` using ControlSystemsBase P = tf(1.0, [1, 1]) @@ -108,21 +114,26 @@ Connect `output_connector` and `input_connector` with an [`AnalysisPoint`](@ref) The incoming connection `output_connector` is expected to be of type [`RealOutput`](@ref), and vice versa. *PLEASE NOTE*: The connection is assumed to be *causal*, meaning that + ``` connect(C.output, :plant_input, P.input) ``` + is correct, whereas + ``` connect(P.input, :plant_input, C.output) ``` + typically is not (unless the model is an inverse model). # Arguments: -- `output_connector`: A connector of type [`RealOutput`](@ref) -- `input_connector`: A connector of type [`RealInput`](@ref) -- `ap`: An explicitly created [`AnalysisPoint`](@ref) -- `ap_name`: If a name is given, an [`AnalysisPoint`](@ref) with the given name will be created automatically. -- `verbose`: Causes a warning to be displayed if an input is connected to an output (reverse causality). Silence this warning if you are analysing an inverse model. + + - `output_connector`: A connector of type [`RealOutput`](@ref) + - `input_connector`: A connector of type [`RealInput`](@ref) + - `ap`: An explicitly created [`AnalysisPoint`](@ref) + - `ap_name`: If a name is given, an [`AnalysisPoint`](@ref) with the given name will be created automatically. + - `verbose`: Causes a warning to be displayed if an input is connected to an output (reverse causality). Silence this warning if you are analysing an inverse model. """ function ModelingToolkit.connect(in, ap::AnalysisPoint, out; verbose = true) verbose && _isinput(in) && ap_warning(1) @@ -336,10 +347,12 @@ Open the loop at analysis point `ap` by breaking the connection through `ap`. `open_sys` will have `u ~ ap.out` as input and `y ~ ap.in` as output. !!! danger "Experimental" + The analysis-point interface is currently experimental and at any time subject to breaking changes not respecting semantic versioning. # Arguments: -- `kwargs`: Are sent to `ModelingToolkit.linearize` + + - `kwargs`: Are sent to `ModelingToolkit.linearize` See also [`get_sensitivity`](@ref), [`get_comp_sensitivity`](@ref), [`get_looptransfer`](@ref). """ @@ -455,10 +468,12 @@ end Compute the sensitivity function in analysis point `ap`. The sensitivity function is obtained by introducing an infinitesimal perturbation `d` at the input of `ap`, linearizing the system and computing the transfer function between `d` and the output of `ap`. !!! danger "Experimental" + The analysis-point interface is currently experimental and at any time subject to breaking changes not respecting semantic versioning. # Arguments: -- `kwargs`: Are sent to `ModelingToolkit.linearize` + + - `kwargs`: Are sent to `ModelingToolkit.linearize` See also [`get_comp_sensitivity`](@ref), [`get_looptransfer`](@ref). """ @@ -471,10 +486,12 @@ get_sensitivity Compute the complementary sensitivity function in analysis point `ap`. The complementary sensitivity function is obtained by introducing an infinitesimal perturbation `d` at the output of `ap`, linearizing the system and computing the transfer function between `d` and the input of `ap`. !!! danger "Experimental" + The analysis-point interface is currently experimental and at any time subject to breaking changes not respecting semantic versioning. # Arguments: -- `kwargs`: Are sent to `ModelingToolkit.linearize` + + - `kwargs`: Are sent to `ModelingToolkit.linearize` See also [`get_sensitivity`](@ref), [`get_looptransfer`](@ref). """ @@ -487,10 +504,12 @@ get_comp_sensitivity Compute the (linearized) loop-transfer function in analysis point `ap`, from `ap.out` to `ap.in`. !!! danger "Experimental" + The analysis-point interface is currently experimental and at any time subject to breaking changes not respecting semantic versioning. # Arguments: -- `kwargs`: Are sent to `ModelingToolkit.linearize` + + - `kwargs`: Are sent to `ModelingToolkit.linearize` See also [`get_sensitivity`](@ref), [`get_comp_sensitivity`](@ref), [`open_loop`](@ref). """ diff --git a/src/Blocks/continuous.jl b/src/Blocks/continuous.jl index b7320417a..c554a90c4 100644 --- a/src/Blocks/continuous.jl +++ b/src/Blocks/continuous.jl @@ -4,12 +4,14 @@ Outputs `y = ∫k*u dt`, corresponding to the transfer function `1/s`. # Connectors: -- `input` -- `output` + + - `input` + - `output` # Parameters: -- `k`: Gain of integrator -- `x_start`: Initial value of integrator + + - `k`: Gain of integrator + - `x_start`: Initial value of integrator """ function Integrator(; name, k = 1, x_start = 0.0) @named siso = SISO() @@ -25,25 +27,29 @@ end Derivative(; name, k=1, T, x_start=0.0) Outputs an approximate derivative of the input. The transfer function of this block is + ``` -k k +k k ─ - ────────── T 2 ⎛ 1⎞ T ⋅⎜s + ─⎟ ⎝ T⎠ ``` + and a state-space realization is given by `ss(-1/T, 1/T, -k/T, k/T)` where `T` is the time constant of the filter. A smaller `T` leads to a more ideal approximation of the derivative. # Parameters: -- `k`: Gain -- `T`: [s] Time constants (T>0 required; T=0 is ideal derivative block) -- `x_start`: Initial value of state + + - `k`: Gain + - `T`: [s] Time constants (T>0 required; T=0 is ideal derivative block) + - `x_start`: Initial value of state # Connectors: -- `input` -- `output` + + - `input` + - `output` """ function Derivative(; name, k = 1, T, x_start = 0.0) T > 0 || throw(ArgumentError("Time constant `T` has to be strictly positive")) @@ -63,27 +69,31 @@ end A first-order filter with a single real pole in `s = -T` and gain `k`. If `lowpass=true` (default), the transfer function is given by `Y(s)/U(s) = ` + ``` - k + k ─────── sT + 1 ``` + and if `lowpass=false`, by + ``` -sT + 1 - k +sT + 1 - k ────────── sT + 1 ``` - # Parameters: -- `k`: Gain -- `T`: [s] Time constants (T>0 required) -- `x_start`: Initial value of state + + - `k`: Gain + - `T`: [s] Time constants (T>0 required) + - `x_start`: Initial value of state # Connectors: -- `input` -- `output` + + - `input` + - `output` See also [`SecondOrder`](@ref) """ @@ -105,24 +115,28 @@ end A second-order filter with gain `k`, a bandwidth of `w` rad/s and relative damping `d`. The transfer function is given by `Y(s)/U(s) = ` + ``` - k*w^2 + k*w^2 ───────────────── s² + 2d*w*s + w^2 ``` + Critical damping corresponds to `d=1`, which yields the fastest step response without overshoot, `d < 1` results in an underdamped filter while `d > 1` results in an overdamped filter. `d = 1/√2` corresponds to a Butterworth filter of order 2 (maximally flat frequency response). # Parameters: -- `k`: Gain -- `w`: [`rad/s`] Angular frequency -- `d`: Damping -- `x_start`: Initial value of state (output) -- `xd_start`: Initial value of derivative of state (output) + + - `k`: Gain + - `w`: [`rad/s`] Angular frequency + - `d`: Damping + - `x_start`: Initial value of state (output) + - `xd_start`: Initial value of derivative of state (output) # Connectors: -- `input` -- `output` + + - `input` + - `output` """ function SecondOrder(; name, k = 1, w, d, x_start = 0.0, xd_start = 0.0) @named siso = SISO() @@ -144,13 +158,15 @@ end Textbook version of a PI-controller without actuator saturation and anti-windup measure. # Parameters: -- `k`: Gain -- `T`: [s] Integrator time constant (T>0 required) -- `x_start`: Initial value for the integrator + + - `k`: Gain + - `T`: [s] Integrator time constant (T>0 required) + - `x_start`: Initial value for the integrator # Connectors: -- `err_input` -- `ctr_output` + + - `err_input` + - `ctr_output` See also [`LimPI`](@ref) """ @@ -178,16 +194,18 @@ end Text-book version of a PID-controller without actuator saturation and anti-windup measure. # Parameters: -- `k`: Gain -- `Ti`: [s] Integrator time constant (Ti>0 required). If set to false no integral action is used. -- `Td`: [s] Derivative time constant (Td>0 required). If set to false no derivative action is used. -- `Nd`: [s] Time constant for the derivative approximation (Nd>0 required; Nd=0 is ideal derivative). -- `x_start`: Initial value for the integrator. -- `xd_start`: Initial value for the derivative state. + + - `k`: Gain + - `Ti`: [s] Integrator time constant (Ti>0 required). If set to false, no integral action is used. + - `Td`: [s] Derivative time constant (Td>0 required). If set to false, no derivative action is used. + - `Nd`: [s] Time constant for the derivative approximation (Nd>0 required; Nd=0 is ideal derivative). + - `x_start`: Initial value for the integrator. + - `xd_start`: Initial value for the derivative state. # Connectors: -- `err_input` -- `ctr_output` + + - `err_input` + - `ctr_output` See also [`LimPID`](@ref) """ @@ -251,14 +269,16 @@ end Text-book version of a PI-controller with actuator saturation and anti-windup measure. # Parameters: -- `k`: Gain -- `T`: [s] Integrator time constant (T>0 required) -- `Ta`: [s] Tracking time constant (Ta>0 required) -- `x_start`: Initial value for the integrator + + - `k`: Gain + - `T`: [s] Integrator time constant (T>0 required) + - `Ta`: [s] Tracking time constant (Ta>0 required) + - `x_start`: Initial value for the integrator # Connectors: -- `err_input` -- `ctr_output` + + - `err_input` + - `ctr_output` """ function LimPI(; name, k = 1, T, u_max, u_min = -u_max, Ta, x_start = 0.0) Ta > 0 || throw(ArgumentError("Time constant `Ta` has to be strictly positive")) @@ -296,28 +316,32 @@ end Proportional-Integral-Derivative (PID) controller with output saturation, set-point weighting and integrator anti-windup. The equation for the control signal is roughly + ``` k(ep + 1/Ti * ∫e + 1/Td * d/dt(ed)) e = u_r - u_y ep = wp*u_r - u_y ed = wd*u_r - u_y ``` + where the transfer function for the derivative includes additional filtering, see `? Derivative` for more details. # Parameters: -- `k`: Proportional gain -- `Ti`: [s] Integrator time constant. Set to `false` to turn off integral action. -- `Td`: [s] Derivative time constant. Set to `false` to turn off derivative action. -- `wp`: [0,1] Set-point weighting in the proportional part. -- `wd`: [0,1] Set-point weighting in the derivative part. -- `Nd`: [1/s] Derivative limit, limits the derivative gain to Nd/Td. Reasonable values are ∈ [8, 20]. A higher value gives a better approximation of an ideal derivative at the expense of higher noise amplification. -- `Ni`: `Ni*Ti` controls the time constant `Ta` of anti-windup tracking. A common (default) choice is `Ta = √(Ti*Td)` which is realized by `Ni = √(Td / Ti)`. Anti-windup can be effectively turned off by setting `Ni = Inf`. -- `gains`: If `gains = true`, `Ti` and `Td` will be interpreted as gains with a fundamental PID transfer function on parallel form `ki=Ti, kd=Td, k + ki/s + kd*s`. + + - `k`: Proportional gain + - `Ti`: [s] Integrator time constant. Set to `false` to turn off integral action. + - `Td`: [s] Derivative time constant. Set to `false` to turn off derivative action. + - `wp`: [0,1] Set-point weighting in the proportional part. + - `wd`: [0,1] Set-point weighting in the derivative part. + - `Nd`: [1/s] Derivative limit, limits the derivative gain to Nd/Td. Reasonable values are ∈ [8, 20]. A higher value gives a better approximation of an ideal derivative at the expense of higher noise amplification. + - `Ni`: `Ni*Ti` controls the time constant `Ta` of anti-windup tracking. A common (default) choice is `Ta = √(Ti*Td)` which is realized by `Ni = √(Td / Ti)`. Anti-windup can be effectively turned off by setting `Ni = Inf`. + - `gains`: If `gains = true`, `Ti` and `Td` will be interpreted as gains with a fundamental PID transfer function on parallel form `ki=Ti, kd=Td, k + ki/s + kd*s`. # Connectors: -- `reference` -- `measurement` -- `ctr_output` + + - `reference` + - `measurement` + - `ctr_output` """ function LimPID(; name, k = 1, Ti = false, Td = false, wp = 1, wd = 1, Ni = Ti == 0 ? Inf : √(max(Td / Ti, 1e-6)), @@ -424,28 +448,34 @@ end StateSpace(A, B, C, D=0; x_start=zeros(size(A,1)), u0=zeros(size(B,2)), y0=zeros(size(C,1)), name) A linear, time-invariant state-space system on the form. + ```math \\begin{aligned} ẋ &= Ax + Bu \\\\ y &= Cx + Du \\end{aligned} ``` + Transfer functions can also be simulated by converting them to a StateSpace form. -`y0` and `u0` can be used to set an operating point, providing them changes the dynamics from an LTI system to the affine system +`y0` and `u0` can be used to set an operating point, providing them changes the dynamics from an LTI system to the affine system + ```math \\begin{aligned} ẋ &= Ax + B(u - u0) \\\\ y &= Cx + D(u - u0) + y0 \\end{aligned} ``` + For a nonlinear system + ```math \\begin{aligned} ẋ &= f(x, u) \\\\ y &= h(x, u) \\end{aligned} ``` + linearized around the operating point `x₀, u₀`, we have `y0, u0 = h(x₀, u₀), u₀`. """ function StateSpace(; A, B, C, D = nothing, x_start = zeros(size(A, 1)), name, diff --git a/src/Blocks/math.jl b/src/Blocks/math.jl index a0c8fe81f..859510687 100644 --- a/src/Blocks/math.jl +++ b/src/Blocks/math.jl @@ -4,11 +4,13 @@ Output the product of a gain value with the input signal. # Parameters: -- `k`: Scalar gain + + - `k`: Scalar gain # Connectors: -- `input` -- `output` + + - `input` + - `output` """ function Gain(k; name) @named siso = SISO() @@ -26,11 +28,13 @@ end Output the product of a gain matrix with the input signal vector. # Parameters: -- `K`: Matrix gain + + - `K`: Matrix gain # Connectors: -- `input` -- `output` + + - `input` + - `output` """ function MatrixGain(K::AbstractArray; name) nout, nin = size(K, 1), size(K, 2) @@ -46,11 +50,13 @@ end Output the sum of the elements of the input port vector. # Parameters: -- `n`: Input port dimension + + - `n`: Input port dimension # Connectors: -- `input` -- `output` + + - `input` + - `output` """ function Sum(n::Int; name) @named input = RealInput(; nin = n) @@ -67,9 +73,10 @@ end Output difference between reference input (input1) and feedback input (input2). # Connectors: -- `input1` -- `input2` -- `output` + + - `input1` + - `input2` + - `output` """ function Feedback(; name) @named input1 = RealInput() @@ -87,13 +94,15 @@ end Output the sum of the two scalar inputs. # Parameters: -- `k1`: Gain for first input -- `k2`: Gain for second input + + - `k1`: Gain for first input + - `k2`: Gain for second input # Connectors: -- `input1` -- `input2` -- `output` + + - `input1` + - `input2` + - `output` """ function Add(; name, k1 = 1, k2 = 1) @named input1 = RealInput() @@ -113,15 +122,17 @@ end Output the sum of the three scalar inputs. # Parameters: -- `k1`: Gain for first input -- `k2`: Gain for second input -- `k3`: Gain for third input + + - `k1`: Gain for first input + - `k2`: Gain for second input + - `k3`: Gain for third input # Connectors: -- `input1` -- `input2` -- `input3` -- `output` + + - `input1` + - `input2` + - `input3` + - `output` """ function Add3(; name, k1 = 1, k2 = 1, k3 = 1) @named input1 = RealInput() @@ -143,9 +154,10 @@ end Output product of the two inputs. # Connectors: -- `input1` -- `input2` -- `output` + + - `input1` + - `input2` + - `output` """ function Product(; name) @named input1 = RealInput() @@ -163,9 +175,10 @@ end Output first input divided by second input. # Connectors: -- `input1` -- `input2` -- `output` + + - `input1` + - `input2` + - `output` """ function Division(; name) @named input1 = RealInput() @@ -180,13 +193,14 @@ end """ StaticNonLinearity(func ;name) -Applies the given function to the input. +Applies the given function to the input. If the given function is not composed of simple core methods (e.g. sin, abs, ...), it has to be registered via `@register_symbolic func(u)` # Connectors: -- `input` -- `output` + + - `input` + - `output` """ function StaticNonLinearity(func; name) @named siso = SISO() @@ -201,6 +215,7 @@ end Output the absolute value of the input. # Connectors: + See [`StaticNonLinearity`](@ref) """ Abs(; name) = StaticNonLinearity(abs; name) @@ -211,6 +226,7 @@ Abs(; name) = StaticNonLinearity(abs; name) Output the sign of the input # Connectors: + See [`StaticNonLinearity`](@ref) """ Sign(; name) = StaticNonLinearity(sign; name) @@ -221,6 +237,7 @@ Sign(; name) = StaticNonLinearity(sign; name) Output the square root of the input (input >= 0 required). # Connectors: + See [`StaticNonLinearity`](@ref) """ Sqrt(; name) = StaticNonLinearity(sqrt; name) @@ -231,6 +248,7 @@ Sqrt(; name) = StaticNonLinearity(sqrt; name) Output the sine of the input. # Connectors: + See [`StaticNonLinearity`](@ref) """ Sin(; name) = StaticNonLinearity(sin; name) @@ -241,6 +259,7 @@ Sin(; name) = StaticNonLinearity(sin; name) Output the cosine of the input. # Connectors: + See [`StaticNonLinearity`](@ref) """ Cos(; name) = StaticNonLinearity(cos; name) @@ -251,6 +270,7 @@ Cos(; name) = StaticNonLinearity(cos; name) Output the tangent of the input. # Connectors: + See [`StaticNonLinearity`](@ref) """ Tan(; name) = StaticNonLinearity(tan; name) @@ -261,6 +281,7 @@ Tan(; name) = StaticNonLinearity(tan; name) Output the arc sine of the input. # Connectors: + See [`StaticNonLinearity`](@ref) """ Asin(; name) = StaticNonLinearity(asin; name) @@ -271,6 +292,7 @@ Asin(; name) = StaticNonLinearity(asin; name) Output the arc cosine of the input. # Connectors: + See [`StaticNonLinearity`](@ref) """ Acos(; name) = StaticNonLinearity(acos; name) @@ -281,6 +303,7 @@ Acos(; name) = StaticNonLinearity(acos; name) Output the arc tangent of the input. # Connectors: + See [`StaticNonLinearity`](@ref) """ Atan(; name) = StaticNonLinearity(atan; name) @@ -291,9 +314,10 @@ Atan(; name) = StaticNonLinearity(atan; name) Output the arc tangent of the input. # Connectors: -- `input1` -- `input2` -- `output` + + - `input1` + - `input2` + - `output` """ function Atan2(; name) @named input1 = RealInput() @@ -311,6 +335,7 @@ end Output the hyperbolic sine of the input. # Connectors: + See [`StaticNonLinearity`](@ref) """ Sinh(; name) = StaticNonLinearity(sinh; name) @@ -321,6 +346,7 @@ Sinh(; name) = StaticNonLinearity(sinh; name) Output the hyperbolic cosine of the input. # Connectors: + See [`StaticNonLinearity`](@ref) """ Cosh(; name) = StaticNonLinearity(cosh; name) @@ -331,6 +357,7 @@ Cosh(; name) = StaticNonLinearity(cosh; name) Output the hyperbolic tangent of the input. # Connectors: + See [`StaticNonLinearity`](@ref) """ Tanh(; name) = StaticNonLinearity(tanh; name) @@ -341,6 +368,7 @@ Tanh(; name) = StaticNonLinearity(tanh; name) Output the exponential (base e) of the input. # Connectors: + See [`StaticNonLinearity`](@ref) """ Exp(; name) = StaticNonLinearity(exp; name) @@ -351,6 +379,7 @@ Exp(; name) = StaticNonLinearity(exp; name) Output the natural (base e) logarithm of the input. # Connectors: + See [`StaticNonLinearity`](@ref) """ Log(; name) = StaticNonLinearity(log; name) @@ -361,6 +390,7 @@ Log(; name) = StaticNonLinearity(log; name) Output the base 10 logarithm of the input. # Connectors: + See [`StaticNonLinearity`](@ref) """ Log10(; name) = StaticNonLinearity(log10; name) diff --git a/src/Blocks/nonlinear.jl b/src/Blocks/nonlinear.jl index 8bd9f7d80..75db415a0 100644 --- a/src/Blocks/nonlinear.jl +++ b/src/Blocks/nonlinear.jl @@ -7,12 +7,14 @@ _dead_zone(u, u_min, u_max) = ifelse(u > u_max, u - u_max, ifelse(u < u_min, u - Limit the range of a signal. # Parameters: -- `y_max`: Maximum of output signal -- `y_min`: Minimum of output signal + + - `y_max`: Maximum of output signal + - `y_min`: Minimum of output signal # Connectors: -- `input` -- `output` + + - `input` + - `output` """ function Limiter(; name, y_max, y_min = y_max > 0 ? -y_max : -Inf) y_max ≥ y_min || throw(ArgumentError("`y_min` must be smaller than `y_max`")) @@ -32,6 +34,7 @@ end The DeadZone block defines a region of zero output. If the input is within `u_min` ... `u_max`, the output is zero. Outside of this zone, the output is a linear function of the input with a slope of 1. + ``` y▲ │ / @@ -44,12 +47,14 @@ If the input is within `u_min` ... `u_max`, the output is zero. Outside of this ``` # Parameters: -- `u_max`: Upper limit of dead zone -- `u_min`: Lower limit of dead zone + + - `u_max`: Upper limit of dead zone + - `u_min`: Lower limit of dead zone # Connectors: -- `input` -- `output` + + - `input` + - `output` """ function DeadZone(; name, u_max, u_min = -u_max) if !ModelingToolkit.isvariable(u_max) @@ -68,17 +73,19 @@ end """ SlewRateLimiter(;name, rising=1, falling=-rising, Td=0.001, y_start=0.0) - + Limits the slew rate of a signal. # Parameters: -- `rising`: Maximum rising slew rate -- `falling`: Maximum falling slew rate -- `Td`: [s] Derivative time constant + + - `rising`: Maximum rising slew rate + - `falling`: Maximum falling slew rate + - `Td`: [s] Derivative time constant # Connectors: -- `input` -- `output` + + - `input` + - `output` """ function SlewRateLimiter(; name, rising = 1, falling = -rising, Td = 0.001, y_start = 0.0) rising ≥ falling || throw(ArgumentError("`rising` must be smaller than `falling`")) diff --git a/src/Blocks/sources.jl b/src/Blocks/sources.jl index 2498743ac..5f68cf9a0 100644 --- a/src/Blocks/sources.jl +++ b/src/Blocks/sources.jl @@ -62,10 +62,12 @@ end Generate constant signal. # Parameters: -- `k`: Constant output value + + - `k`: Constant output value # Connectors: -- `output` + + - `output` """ function Constant(; name, k = 1) @named output = RealOutput() @@ -80,16 +82,18 @@ end Generate sine signal. # Parameters: -- `frequency`: [Hz] Frequency of sine wave -- `amplitude`: Amplitude of sine wave -- `phase`: [rad] Phase of sine wave -- `offset`: Offset of output signal -- `start_time`: [s] Output `y = offset` for `t < start_time` -- `smooth`: If `true`, returns a smooth wave. Defaults to `false` - It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. + + - `frequency`: [Hz] Frequency of sine wave + - `amplitude`: Amplitude of sine wave + - `phase`: [rad] Phase of sine wave + - `offset`: Offset of output signal + - `start_time`: [s] Output `y = offset` for `t < start_time` + - `smooth`: If `true`, returns a smooth wave. Defaults to `false` + It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. # Connectors: -- `output` + + - `output` """ function Sine(; name, frequency, @@ -158,11 +162,13 @@ end Generate current time signal. # Parameters: -- `offset`: Offset of output signal -- `start_time`: [s] Output `y = offset` for `t < start_time` + + - `offset`: Offset of output signal + - `start_time`: [s] Output `y = offset` for `t < start_time` # Connectors: -- `output` + + - `output` """ function ContinuousClock(; name, offset = 0, start_time = 0) @named output = RealOutput() @@ -178,15 +184,17 @@ end Generate ramp signal. # Parameters: -- `height`: Height of ramp -- `duration`: [s] Duration of ramp (= 0.0 gives a Step) -- `offset`: Offset of output signal -- `start_time`: [s] Output `y = offset` for `t < start_time` -- `smooth`: If `true`, returns a smooth wave. Defaults to `false` - It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. + + - `height`: Height of ramp + - `duration`: [s] Duration of ramp (= 0.0 gives a Step) + - `offset`: Offset of output signal + - `start_time`: [s] Output `y = offset` for `t < start_time` + - `smooth`: If `true`, returns a smooth wave. Defaults to `false` + It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. # Connectors: -- `output` + + - `output` """ function Ramp(; name, height = 1, @@ -216,15 +224,17 @@ end Generate smooth square signal. # Parameters: -- `frequency`: [Hz] Frequency of square wave -- `amplitude`: Amplitude of square wave -- `offset`: Offset of output signal -- `start_time`: [s] Output `y = offset` for `t < start_time` -- `smooth`: If `true`, returns a smooth wave. Defaults to `false` - It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. + + - `frequency`: [Hz] Frequency of square wave + - `amplitude`: Amplitude of square wave + - `offset`: Offset of output signal + - `start_time`: [s] Output `y = offset` for `t < start_time` + - `smooth`: If `true`, returns a smooth wave. Defaults to `false` + It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. # Connectors: -- `output` + + - `output` """ function Square(; name, frequency = 1.0, amplitude = 1.0, offset = 0.0, start_time = 0.0, smooth = false) @@ -256,15 +266,17 @@ end Generate step signal. # Parameters: -- `height`: Height of step -- `offset`: Offset of output signal -- `start_time`: [s] Output `y = offset` for `t < start_time` and thereafter `offset+height`. -- `duration`: [s] If `duration < Inf` is supplied, the output will revert to `offset` after `duration` seconds. -- `smooth`: If `true`, returns a smooth wave. Defaults to `true` - It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. + + - `height`: Height of step + - `offset`: Offset of output signal + - `start_time`: [s] Output `y = offset` for `t < start_time` and thereafter `offset+height`. + - `duration`: [s] If `duration < Inf` is supplied, the output will revert to `offset` after `duration` seconds. + - `smooth`: If `true`, returns a smooth wave. Defaults to `true` + It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. # Connectors: -- `output` + + - `output` """ function Step(; name, height = 1, offset = 0, start_time = 0, duration = Inf, smooth = 1e-5) @named output = RealOutput() @@ -293,17 +305,19 @@ end Generate exponentially damped sine signal. # Parameters: -- `frequency`: [Hz] Frequency of sine wave -- `amplitude`: Amplitude of sine wave -- `damping`: [1/s] Damping coefficient of sine wave -- `phase`: [rad] Phase of sine wave -- `offset`: Offset of output signal -- `start_time`: [s] Output `y = offset` for `t < start_time` -- `smooth`: If `true`, returns a smooth wave. Defaults to `false` - It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. + + - `frequency`: [Hz] Frequency of sine wave + - `amplitude`: Amplitude of sine wave + - `damping`: [1/s] Damping coefficient of sine wave + - `phase`: [rad] Phase of sine wave + - `offset`: Offset of output signal + - `start_time`: [s] Output `y = offset` for `t < start_time` + - `smooth`: If `true`, returns a smooth wave. Defaults to `false` + It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. # Connectors: -- `output` + + - `output` """ function ExpSine(; name, frequency, @@ -337,15 +351,17 @@ end Generate smooth triangular signal for frequencies less than or equal to 25 Hz # Parameters: -- `frequency`: [Hz] Frequency of square wave -- `amplitude`: Amplitude of square wave -- `offset`: Offset of output signal. -- `start_time`: [s] Output `y = offset` for `t < start_time` -- `smooth`: If `true`, returns a smooth wave. Defaults to `false` - It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. + + - `frequency`: [Hz] Frequency of square wave + - `amplitude`: Amplitude of square wave + - `offset`: Offset of output signal. + - `start_time`: [s] Output `y = offset` for `t < start_time` + - `smooth`: If `true`, returns a smooth wave. Defaults to `false` + It uses a default smoothing factor of `δ=1e-5`, but this can be changed by supplying `smooth=δ`. # Connectors: -- `output` + + - `output` """ function Triangular(; name, amplitude = 1.0, frequency = 1.0, offset = 0.0, start_time = 0.0, smooth = false) diff --git a/src/Blocks/utils.jl b/src/Blocks/utils.jl index 86264ff63..d31685d88 100644 --- a/src/Blocks/utils.jl +++ b/src/Blocks/utils.jl @@ -20,10 +20,10 @@ Connector with one input signal of type Real. # Parameters: - `nin=1`: Number of inputs -- `u_start=0`: Initial value for `u` +- `u_start=0`: Initial value for `u` # States: -- `u`: Value of of the connector; if nin=1 this is a scalar +- `u`: Value of the connector; if nin=1 this is a scalar """ RealInput @connector function RealOutput(; name, nout = 1, u_start = nout > 1 ? zeros(nout) : 0.0) @@ -48,10 +48,10 @@ Connector with one output signal of type Real. # Parameters: - `nout=1`: Number of inputs -- `u_start=0`: Initial value for `u` +- `u_start=0`: Initial value for `u` # States: -- `u`: Value of of the connector; if nout=1 this is a scalar +- `u`: Value of the connector; if nout=1 this is a scalar """ RealOutput """ @@ -60,8 +60,9 @@ Connector with one output signal of type Real. Single input single output (SISO) continuous system block. # Parameters: -- `u_start`: Initial value for the input -- `y_start`: Initial value for the output + + - `u_start`: Initial value for the input + - `y_start`: Initial value for the output """ function SISO(; name, u_start = 0.0, y_start = 0.0) @named input = RealInput(u_start = u_start) @@ -79,10 +80,11 @@ end Base class for a multiple input multiple output (MIMO) continuous system block. # Parameters: -- `nin`: Input dimension -- `nout`: Output dimension -- `u_start`: Initial value for the input -- `y_start`: Initial value for the output + + - `nin`: Input dimension + - `nout`: Output dimension + - `u_start`: Initial value for the input + - `y_start`: Initial value for the output """ function MIMO(; name, nin = 1, nout = 1, u_start = zeros(nin), y_start = zeros(nout)) @named input = RealInput(nin = nin, u_start = u_start) diff --git a/src/Electrical/Analog/ideal_components.jl b/src/Electrical/Analog/ideal_components.jl index 959e3d8b7..20b08cd5d 100644 --- a/src/Electrical/Analog/ideal_components.jl +++ b/src/Electrical/Analog/ideal_components.jl @@ -5,7 +5,8 @@ Ground node with the potential of zero and connector `g`. Every circuit must hav node. # Connectors: -- `g` + + - `g` """ function Ground(; name) @named g = Pin() @@ -19,14 +20,17 @@ end Creates an ideal Resistor following Ohm's Law. # States: + See [OnePort](@ref) # Connectors: -- `p` Positive pin -- `n` Negative pin + + - `p` Positive pin + - `n` Negative pin # Parameters: -- `R`: [`Ohm`] Resistance + + - `R`: [`Ohm`] Resistance """ function Resistor(; name, R) @named oneport = OnePort() @@ -44,14 +48,17 @@ end Creates an ideal conductor. # States: + See [OnePort](@ref) # Connectors: -- `p` Positive pin -- `n` Negative pin + + - `p` Positive pin + - `n` Negative pin # Parameters: -- `G`: [`S`] Conductance + + - `G`: [`S`] Conductance """ function Conductor(; name, G) @named oneport = OnePort() @@ -66,19 +73,21 @@ end """ Capacitor(; name, C) - Creates an ideal capacitor. # States: -- `v(t)`: [`V`] The voltage across the capacitor, given by `D(v) ~ p.i / C` + + - `v(t)`: [`V`] The voltage across the capacitor, given by `D(v) ~ p.i / C` # Connectors: -- `p` Positive pin -- `n` Negative pin + + - `p` Positive pin + - `n` Negative pin # Parameters: -- `C`: [`F`] Capacitance -- `v_start`: [`V`] Initial voltage of capacitor + + - `C`: [`F`] Capacitance + - `v_start`: [`V`] Initial voltage of capacitor """ function Capacitor(; name, C, v_start = 0.0) @named oneport = OnePort(; v_start = v_start) @@ -96,15 +105,18 @@ end Creates an ideal Inductor. # States: + See [OnePort](@ref) # Connectors: -- `p` Positive pin -- `n` Negative pin + + - `p` Positive pin + - `n` Negative pin # Parameters: -- `L`: [`H`] Inductance -- `i_start`: [`A`] Initial current through inductor + + - `L`: [`H`] Inductance + - `i_start`: [`A`] Initial current through inductor """ function Inductor(; name, L, i_start = 0.0) @named oneport = OnePort(; i_start = i_start) @@ -124,13 +136,15 @@ The ideal OpAmp is a two-port. The left port is fixed to `v1 = 0` and `i1 = 0` ( At the right port both any voltage `v2` and any current `i2` are possible (norator). # States: + See [TwoPort](@ref) # Connectors: -- `p1` Positive pin (left port) -- `p2` Positive pin (right port) -- `n1` Negative pin (left port) -- `n2` Negative pin (right port) + + - `p1` Positive pin (left port) + - `p2` Positive pin (right port) + - `n1` Negative pin (left port) + - `n2` Negative pin (right port) """ function IdealOpAmp(; name) @named twoport = TwoPort() @@ -147,11 +161,13 @@ end Short is a simple short cut branch. That means the voltage drop between both pins is zero. # States: + See [OnePort](@ref) # Connectors: -- `p` Positive pin -- `n` Negative pin + + - `p` Positive pin + - `n` Negative pin """ function Short(; name) @named oneport = OnePort() @@ -166,16 +182,19 @@ end Temperature dependent electrical resistor # States -- See [OnePort](@ref) -- `R(t)`: [`Ohm`] Temperature dependent resistance `R ~ R_ref*(1 + alpha*(heat_port.T(t) - T_ref))` + + - See [OnePort](@ref) + - `R(t)`: [`Ohm`] Temperature dependent resistance `R ~ R_ref*(1 + alpha*(heat_port.T(t) - T_ref))` # Connectors -- `p` Positive pin -- `n` Negative pin -# Parameters: -- `R_ref`: [`Ω`] Reference resistance -- `T_ref`: [K] Reference temperature + - `p` Positive pin + - `n` Negative pin + +# Parameters: + + - `R_ref`: [`Ω`] Reference resistance + - `T_ref`: [K] Reference temperature """ function HeatingResistor(; name, R_ref = 1.0, T_ref = 300.15, alpha = 0) @named oneport = OnePort() @@ -199,19 +218,22 @@ end Electromotoric force (electric/mechanic transformer) # States -- `v(t)`: [`V`] The voltage across component `p.v - n.v` -- `i(t)`: [`A`] The current passing through positive pin -- `phi`: [`rad`] Rotation angle (=flange.phi - support.phi) -- `w`: [`rad/s`] Angular velocity (= der(phi)) + + - `v(t)`: [`V`] The voltage across component `p.v - n.v` + - `i(t)`: [`A`] The current passing through positive pin + - `phi`: [`rad`] Rotation angle (=flange.phi - support.phi) + - `w`: [`rad/s`] Angular velocity (= der(phi)) # Connectors -- `p` [Pin](@ref) Positive pin -- `n` [Pin](@ref) Negative pin -- `flange` [Flange](@ref) Shaft of EMF shaft -- `support` [Support](@ref) Support/housing of emf shaft -# Parameters: -- `k`: [`N⋅m/A`] Transformation coefficient + - `p` [Pin](@ref) Positive pin + - `n` [Pin](@ref) Negative pin + - `flange` [Flange](@ref) Shaft of EMF shaft + - `support` [Support](@ref) Support/housing of emf shaft + +# Parameters: + + - `k`: [`N⋅m/A`] Transformation coefficient """ function EMF(; name, k) @named p = Pin() diff --git a/src/Electrical/Analog/sensors.jl b/src/Electrical/Analog/sensors.jl index 236629697..9762b0516 100644 --- a/src/Electrical/Analog/sensors.jl +++ b/src/Electrical/Analog/sensors.jl @@ -1,15 +1,17 @@ """ - CurrentSensor(; name) + CurrentSensor(; name) Creates a circuit component that measures the current flowing through it. Analogous to an ideal ammeter. # States: -- `i(t)`: [`A`] Current through the sensor + + - `i(t)`: [`A`] Current through the sensor # Connectors: -- `p` Positive pin -- `n` Negative pin + + - `p` Positive pin + - `n` Negative pin """ function CurrentSensor(; name) @named p = Pin() @@ -22,15 +24,17 @@ function CurrentSensor(; name) end """ - PotentialSensor(; name) +PotentialSensor(; name) Creates a circuit component which measures the potential at a pin. # States: -- `phi(t)`: [`V`] The measured potential at this point + + - `phi(t)`: [`V`] The measured potential at this point # Connectors: -- `p` Pin at which potential is to be measured + + - `p` Pin at which potential is to be measured """ function PotentialSensor(; name) @named p = Pin() @@ -41,16 +45,18 @@ function PotentialSensor(; name) end """ - VoltageSensor(; name) +VoltageSensor(; name) Creates a circuit component that measures the voltage across it. Analogous to an ideal voltmeter. # States: -- `v(t)`: [`V`] The voltage difference form positive to negative pin `p.v - n.v` + + - `v(t)`: [`V`] The voltage difference from positive to negative pin `p.v - n.v` # Connectors: -- `p` Positive pin -- `n` Negative pin + + - `p` Positive pin + - `n` Negative pin """ function VoltageSensor(; name) @named p = Pin() @@ -63,21 +69,23 @@ function VoltageSensor(; name) end """ - PowerSensor(; name) +PowerSensor(; name) Combines a [`VoltageSensor`](@ref) and a [`CurrentSensor`](@ref) to measure the power being consumed by a circuit. # States: -- `power(t)`: [`W`] The power being consumed, given by the product of voltage and current. -- See [VoltageSensor](@ref) -- See [CurrentSensor](@ref) + + - `power(t)`: [`W`] The power being consumed, given by the product of voltage and current. + - See [VoltageSensor](@ref) + - See [CurrentSensor](@ref) # Connectors: -- `pc` Corresponds to the `p` pin of the [`CurrentSensor`](@ref) -- `nc` Corresponds to the `n` pin of the [`CurrentSensor`](@ref) -- `pv` Corresponds to the `p` pin of the [`VoltageSensor`](@ref) -- `nv` Corresponds to the `n` pin of the [`VoltageSensor`](@ref) + + - `pc` Corresponds to the `p` pin of the [`CurrentSensor`](@ref) + - `nc` Corresponds to the `n` pin of the [`CurrentSensor`](@ref) + - `pv` Corresponds to the `p` pin of the [`VoltageSensor`](@ref) + - `nv` Corresponds to the `n` pin of the [`VoltageSensor`](@ref) """ function PowerSensor(; name) @named pc = Pin() @@ -97,19 +105,21 @@ function PowerSensor(; name) end """ - MultiSensor(; name) +MultiSensor(; name) Combines a [`VoltageSensor`](@ref) and a [`CurrentSensor`](@ref). # States: -- `v(t)`: [`V`] The voltage across the [`VoltageSensor`](@ref) -- `i(t)`: [`A`] The current across the [`CurrentSensor`](@ref) + + - `v(t)`: [`V`] The voltage across the [`VoltageSensor`](@ref) + - `i(t)`: [`A`] The current across the [`CurrentSensor`](@ref) # Connectors: -- `pc` Corresponds to the `p` pin of the [`CurrentSensor`](@ref) -- `nc` Corresponds to the `n` pin of the [`CurrentSensor`](@ref) -- `pv` Corresponds to the `p` pin of the [`VoltageSensor`](@ref) -- `nv` Corresponds to the `n` pin of the [`VoltageSensor`](@ref) + + - `pc` Corresponds to the `p` pin of the [`CurrentSensor`](@ref) + - `nc` Corresponds to the `n` pin of the [`CurrentSensor`](@ref) + - `pv` Corresponds to the `p` pin of the [`VoltageSensor`](@ref) + - `nv` Corresponds to the `n` pin of the [`VoltageSensor`](@ref) """ function MultiSensor(; name) @named pc = Pin() diff --git a/src/Electrical/Analog/sources.jl b/src/Electrical/Analog/sources.jl index 2144f8bd7..164781e2d 100644 --- a/src/Electrical/Analog/sources.jl +++ b/src/Electrical/Analog/sources.jl @@ -4,12 +4,14 @@ Acts as an ideal voltage source with no internal resistance. # States: + See [OnePort](@ref) # Connectors: -- `p` Positive pin -- `n` Negative pin -- `V` [RealInput](@ref) Input for the voltage control signal, i.e. `V ~ p.v - n.v` + + - `p` Positive pin + - `n` Negative pin + - `V` [RealInput](@ref) Input for the voltage control signal, i.e. `V ~ p.v - n.v` """ function Voltage(; name) @named oneport = OnePort() @@ -28,12 +30,14 @@ end Acts as an ideal current source with no internal resistance. # States: + See [OnePort](@ref) # Connectors: -- `p` Positive pin -- `n` Negative pin -- `I` [RealInput](@ref) Input for the current control signal, i.e. `I ~ p.i + + - `p` Positive pin + - `n` Negative pin + - `I` [RealInput](@ref) Input for the current control signal, i.e. `I ~ p.i """ function Current(; name) @named oneport = OnePort() diff --git a/src/Electrical/Digital/components.jl b/src/Electrical/Digital/components.jl index 6473e319e..044907e70 100644 --- a/src/Electrical/Digital/components.jl +++ b/src/Electrical/Digital/components.jl @@ -1,24 +1,26 @@ # Adders """ ```julia -function HalfAdder(; name) +HalfAdder(; name) ``` Takes two bits as input, and outputs the sum and the carry # States -- `sum(t)` - The sum of the input bits -- `carry(t)` - The carry generated by the input bits + + - `sum(t)` + The sum of the input bits + - `carry(t)` + The carry generated by the input bits # Connectors -- `x1`, `x2` - The two inputs to add -- `y1` - Output [`DigitalPin`](@ref) corresponding to the sum -- `y2` - Output [`DigitalPin`](@ref) corresponding to the carry + + - `x1`, `x2` + The two inputs to add + - `y1` + Output [`DigitalPin`](@ref) corresponding to the sum + - `y2` + Output [`DigitalPin`](@ref) corresponding to the carry """ function HalfAdder(; name) @named x1 = DigitalPin() @@ -36,24 +38,26 @@ end """ ```julia -function FullAdder(; name) +FullAdder(; name) ``` Takes three bits as input, and outputs the sum and the carry # States -- `sum(t)` - The sum of the input bits -- `carry(t)` - The carry generated by the input bits + + - `sum(t)` + The sum of the input bits + - `carry(t)` + The carry generated by the input bits # Connectors -- `x1`, `x2`, `x3` - The three inputs to add -- `y1` - Output [`DigitalPin`](@ref) corresponding to the sum -- `y2` - Output [`DigitalPin`](@ref) corresponding to the carry + + - `x1`, `x2`, `x3` + The three inputs to add + - `y1` + Output [`DigitalPin`](@ref) corresponding to the sum + - `y2` + Output [`DigitalPin`](@ref) corresponding to the carry """ function FullAdder(; name) @named x1 = DigitalPin() @@ -72,11 +76,11 @@ end # Multiplexers -# This selects data from the `N` input ports (`d₀` to `dₙ₋₁`) -# using values of `n` select lines, where `N = 2^n` +# This selects data from the `N` input ports (`d₀` to `dₙ₋₁`) +# using values of `n` select lines, where `N = 2^n` """ ```julia -function MUX(; name, N=4) +MUX(; name, N = 4) ``` Standard Multiplexer. Selects data from `N` input ports using the values @@ -85,12 +89,13 @@ the values of the select lines should correspond to the binary representation of `i`. # Connectors -- `d1`, `d2`, ... - The `N` input lines -- `s1`, `s2`, ... - The `n` select lines -- `y` - The output, selected from one of the `N` input lines + + - `d1`, `d2`, ... + The `N` input lines + - `s1`, `s2`, ... + The `n` select lines + - `y` + The output, selected from one of the `N` input lines """ function MUX(; name, N = 4) n = log2(N) @@ -124,11 +129,11 @@ function MUX(; name, N = 4) ODESystem(eqs, t, [], [], systems = [d..., s..., y], name = name) end -# This selects one of the `N` output ports (`y₀` to `yₙ₋₁`) -# to transmit data `d` using values of `n` select lines, where `N = 2^n` +# This selects one of the `N` output ports (`y₀` to `yₙ₋₁`) +# to transmit data `d` using values of `n` select lines, where `N = 2^n` """ ```julia -function DEMUX(; name, N=4) +DEMUX(; name, N = 4) ``` Standard Demultiplexer. Performs the reverse operation of a [`MUX`](@ref). @@ -138,12 +143,13 @@ selected, the values of the select lines should correspond to the binary representation of `i`. # Connectors -- `d` - The input to be transmitted to one of the output lines -- `s1`, `s2`, ... - The `n` select lines -- `y1`, `y2`, ... - The `N` output lines + + - `d` + The input to be transmitted to one of the output lines + - `s1`, `s2`, ... + The `n` select lines + - `y1`, `y2`, ... + The `N` output lines """ function DEMUX(; name, N = 4) n = log2(N) @@ -180,17 +186,18 @@ end # Encodes `N` inputs to `n` outputs, where `N = 2^n` """ ```julia -function Encoder(; name, N=4) +Encoder(; name, N = 4) ``` Encodes `N` inputs to `n` outputs, where `N=2ⁿ`. Exactly one of the inputs should be `1`. If the `i`th input is `1`, then the output corresponds to the binary representation of `i`. # Connectors -- `d1`, `d2`, ... - The `N` input lines -- `y1`, `y2`, ... - The `n` output lines + + - `d1`, `d2`, ... + The `N` input lines + - `y1`, `y2`, ... + The `n` output lines """ function Encoder(; name, N = 4) n = log2(N) @@ -235,7 +242,7 @@ end # Decodes `n` inputs to `N` outputs, where `N = 2^n` """ ```julia -function Decoder(; name, n=2) +Decoder(; name, n = 2) ``` Performs the reverse operation of an [`Encoder`](@ref). Decodes `n` inputs @@ -243,10 +250,11 @@ to `N` outputs, where `N=2ⁿ`. The `i`th output is `1` if the values of the select lines correspond to the binary representation of `1`. # Connectors -- `d1`, `d2`, ... - The `n` input lines -- `y1`, `y2`, ... - The `N` output lines + + - `d1`, `d2`, ... + The `n` input lines + - `y1`, `y2`, ... + The `N` output lines """ function Decoder(; name, n = 2) N = 2^n diff --git a/src/Electrical/Digital/gates.jl b/src/Electrical/Digital/gates.jl index bed670ef7..0940e4cfc 100644 --- a/src/Electrical/Digital/gates.jl +++ b/src/Electrical/Digital/gates.jl @@ -1,15 +1,16 @@ """ ```julia -function Not(; name) +Not(; name) ``` NOT gate in 9-level logic. # Connectors -- `x` - Input [`DigitalPin`](@ref) -- `y` - Output [`DigitalPin`](@ref) + + - `x` + Input [`DigitalPin`](@ref) + - `y` + Output [`DigitalPin`](@ref) """ function Not(; name) @named x = DigitalPin() @@ -22,16 +23,17 @@ end """ ```julia -function And(; name, N=2) +And(; name, N = 2) ``` AND gate in 9-level logic, with `N` inputs # Connectors -- `x1`, `x2`, ... - `N` input [`DigitalPin`](@ref)s -- `y` - Output [`DigitalPin`](@ref) + + - `x1`, `x2`, ... + `N` input [`DigitalPin`](@ref)s + - `y` + Output [`DigitalPin`](@ref) """ function And(; name, N = 2) x = map(1:N) do i @@ -47,16 +49,17 @@ end """ ```julia -function Nand(; name, N=2) +Nand(; name, N = 2) ``` NAND gate in 9-level logic, with `N` inputs # Connectors -- `x1`, `x2`, ... - `N` input [`DigitalPin`](@ref)s -- `y` - Output [`DigitalPin`](@ref) + + - `x1`, `x2`, ... + `N` input [`DigitalPin`](@ref)s + - `y` + Output [`DigitalPin`](@ref) """ function Nand(; name, N = 2) x = map(1:N) do i @@ -72,16 +75,17 @@ end """ ```julia -function Or(; name, N=2) +Or(; name, N = 2) ``` OR gate in 9-level logic, with `N` inputs # Connectors -- `x1`, `x2`, ... - `N` input [`DigitalPin`](@ref)s -- `y` - Output [`DigitalPin`](@ref) + + - `x1`, `x2`, ... + `N` input [`DigitalPin`](@ref)s + - `y` + Output [`DigitalPin`](@ref) """ function Or(; name, N = 2) x = map(1:N) do i @@ -97,16 +101,17 @@ end """ ```julia -function Nor(; name, N=2) +Nor(; name, N = 2) ``` NOR gate in 9-level logic, with `N` inputs # Connectors -- `x1`, `x2`, ... - `N` input [`DigitalPin`](@ref)s -- `y` - Output [`DigitalPin`](@ref) + + - `x1`, `x2`, ... + `N` input [`DigitalPin`](@ref)s + - `y` + Output [`DigitalPin`](@ref) """ function Nor(; name, N = 2) x = map(1:N) do i @@ -122,16 +127,17 @@ end """ ```julia -function Xor(; name, N=2) +Xor(; name, N = 2) ``` XOR gate in 9-level logic, with `N` inputs # Connectors -- `x1`, `x2`, ... - `N` input [`DigitalPin`](@ref)s -- `y` - Output [`DigitalPin`](@ref) + + - `x1`, `x2`, ... + `N` input [`DigitalPin`](@ref)s + - `y` + Output [`DigitalPin`](@ref) """ function Xor(; name, N = 2) x = map(1:N) do i @@ -147,16 +153,17 @@ end """ ```julia -function Xnor(; name, N=2) +Xnor(; name, N = 2) ``` XNOR gate in 9-level logic, with `N` inputs # Connectors -- `x1`, `x2`, ... - `N` input [`DigitalPin`](@ref)s -- `y` - Output [`DigitalPin`](@ref) + + - `x1`, `x2`, ... + `N` input [`DigitalPin`](@ref)s + - `y` + Output [`DigitalPin`](@ref) """ function Xnor(; name, N = 2) x = map(1:N) do i diff --git a/src/Electrical/Digital/sources.jl b/src/Electrical/Digital/sources.jl index 76e857c89..9c50afd02 100644 --- a/src/Electrical/Digital/sources.jl +++ b/src/Electrical/Digital/sources.jl @@ -1,15 +1,17 @@ """ ```julia -function PulseDiff(; name, Val=1, dt=0.1) +PulseDiff(; name, Val = 1, dt = 0.1) ``` # States -- `val(t)` - Output value of the source + + - `val(t)` + Output value of the source # Connectors -- `d` - Output [`DigitalPin`](@ref) + + - `d` + Output [`DigitalPin`](@ref) """ function PulseDiff(; name, Val = 1, dt = 0.1) @named d = DigitalPin() @@ -24,14 +26,15 @@ end """ ```julia -function Set(; name) +Set(; name) ``` Source that outputs a constant signal of `1`. # Connectors -- `d` - Output [`DigitalPin`](@ref) + + - `d` + Output [`DigitalPin`](@ref) """ function Set(; name) @named d = DigitalPin() @@ -44,14 +47,15 @@ end """ ```julia -function Reset(; name) +Reset(; name) ``` Source that outputs a constant signal of `1` # Connectors -- `d` - Output [`DigitalPin`](@ref) + + - `d` + Output [`DigitalPin`](@ref) """ function Reset(; name) @named d = DigitalPin() @@ -64,14 +68,15 @@ end """ ```julia -function Pulse(; name, duty_cycle=0.5, T=1.0) +Pulse(; name, duty_cycle = 0.5, T = 1.0) ``` Pulse output with specified `duty_cycle` and time period (`T`) # Connectors -- `d` - Output [`DigitalPin`](@ref) + + - `d` + Output [`DigitalPin`](@ref) """ function Pulse(; name, duty_cycle = 0.5, T = 1.0) @named d = DigitalPin() diff --git a/src/Electrical/utils.jl b/src/Electrical/utils.jl index 33ffa799b..d582d4999 100644 --- a/src/Electrical/utils.jl +++ b/src/Electrical/utils.jl @@ -21,16 +21,19 @@ A pin in an analog circuit. Component with two electrical pins `p` and `n` and current `i` flows from `p` to `n`. # States: -- `v(t)`: [`V`] The voltage across component `p.v - n.v` -- `i(t)`: [`A`] The current passing through positive pin + + - `v(t)`: [`V`] The voltage across component `p.v - n.v` + - `i(t)`: [`A`] The current passing through positive pin # Parameters: -- `v_start`: [`V`] Initial voltage across the component -- `i_start`: [`A`] Initial current through the component + + - `v_start`: [`V`] Initial voltage across the component + - `i_start`: [`A`] Initial current through the component # Connectors: -- `p` Positive pin -- `n` Negative pin + + - `p` Positive pin + - `n` Negative pin """ function OnePort(; name, v_start = 0.0, i_start = 0.0) @named p = Pin() diff --git a/src/Magnetic/FluxTubes/basic.jl b/src/Magnetic/FluxTubes/basic.jl index f4797871e..c4db50f1b 100644 --- a/src/Magnetic/FluxTubes/basic.jl +++ b/src/Magnetic/FluxTubes/basic.jl @@ -62,7 +62,8 @@ end Constant permeance. # Parameters: -- `G_m`: [H] Magnetic permeance + + - `G_m`: [H] Magnetic permeance """ function ConstantPermeance(; name, G_m = 1.0) @named two_port = TwoPort() @@ -80,7 +81,8 @@ end Constant reluctance. # Parameters: -- `R_m`: [H^-1] Magnetic reluctance + + - `R_m`: [H^-1] Magnetic reluctance """ function ConstantReluctance(; name, R_m = 1.0) @named two_port = TwoPort() @@ -102,8 +104,9 @@ V_m = N * i N * dΦ/dt = -v # Parameters: -- `N`: Number of turns -- `Phi_start`: [Wb] Initial magnetic flux flowing into the port_p + + - `N`: Number of turns + - `Phi_start`: [Wb] Initial magnetic flux flowing into the port_p """ function ElectroMagneticConverter(; name, N, Phi_start = 0.0) @named port_p = PositiveMagneticPort() @@ -131,10 +134,11 @@ end For modelling of eddy current in a conductive magnetic flux tube. # Parameters: -- `rho`: [ohm * m] Resistivity of flux tube material (default: Iron at 20degC) -- `l`: [m] Average length of eddy current path -- `A`: [m^2] Cross sectional area of eddy current path -- `Phi_start`: [Wb] Initial magnetic flux flowing into the port_p + + - `rho`: [ohm * m] Resistivity of flux tube material (default: Iron at 20degC) + - `l`: [m] Average length of eddy current path + - `A`: [m^2] Cross sectional area of eddy current path + - `Phi_start`: [Wb] Initial magnetic flux flowing into the port_p """ function EddyCurrent(; name, rho = 0.098e-6, l = 1, A = 1, Phi_start = 0.0) @named two_port = TwoPort(Phi_start = Phi_start) diff --git a/src/Magnetic/FluxTubes/sources.jl b/src/Magnetic/FluxTubes/sources.jl index e8d12356d..6655bf70c 100644 --- a/src/Magnetic/FluxTubes/sources.jl +++ b/src/Magnetic/FluxTubes/sources.jl @@ -2,7 +2,8 @@ Constant magnetomotive force. Parameters: -- `V_m`: [A] Magnetic potential difference + + - `V_m`: [A] Magnetic potential difference """ function ConstantMagneticPotentialDifference(; name, V_m = 1.0) port_p = PositiveMagneticPort() @@ -19,7 +20,8 @@ end Source of constant magnetic flux. Parameters: -- `Phi`: [Wb] Magnetic flux + + - `Phi`: [Wb] Magnetic flux """ function ConstantMagneticFlux(; name, Phi = 1.0) port_p = PositiveMagneticPort() diff --git a/src/Magnetic/FluxTubes/utils.jl b/src/Magnetic/FluxTubes/utils.jl index ffc975b8b..32cc29fb2 100644 --- a/src/Magnetic/FluxTubes/utils.jl +++ b/src/Magnetic/FluxTubes/utils.jl @@ -21,8 +21,9 @@ const NegativeMagneticPort = MagneticPort Partial component with magnetic potential difference between two magnetic ports p and n and magnetic flux Phi from p to n. # Parameters: -- `V_m_start`: Initial magnetic potential difference between both ports -- `Phi_start`: Initial magnetic flux from port_p to port_n + + - `V_m_start`: Initial magnetic potential difference between both ports + - `Phi_start`: Initial magnetic flux from port_p to port_n """ function TwoPort(; name, V_m_start = 0.0, Phi_start = 0.0) @named port_p = PositiveMagneticPort() diff --git a/src/Mechanical/Rotational/components.jl b/src/Mechanical/Rotational/components.jl index ab42bb61a..e29defab5 100644 --- a/src/Mechanical/Rotational/components.jl +++ b/src/Mechanical/Rotational/components.jl @@ -4,10 +4,12 @@ Flange fixed in housing at a given angle. # Connectors: -- `flange` [Flange](@ref) + + - `flange` [Flange](@ref) # Parameters: -- `phi0`: [`rad`] Fixed offset angle of housing + + - `phi0`: [`rad`] Fixed offset angle of housing """ function Fixed(; name, phi0 = 0.0) @named flange = Flange() @@ -21,20 +23,23 @@ end 1D-rotational component with inertia. -# States: -- `phi`: [`rad`] Absolute rotation angle of component -- `w`: [`rad/s`] Absolute angular velocity of component (= D(phi)) -- `a`: [`rad/s²`] Absolute angular acceleration of component (= D(w)) +# States: + + - `phi`: [`rad`] Absolute rotation angle of component + - `w`: [`rad/s`] Absolute angular velocity of component (= D(phi)) + - `a`: [`rad/s²`] Absolute angular acceleration of component (= D(w)) # Connectors: -- `flange_a` [Flange](@ref) Left flange -- `flange_b` [Flange](@ref) Right flange + + - `flange_a` [Flange](@ref) Left flange + - `flange_b` [Flange](@ref) Right flange # Parameters: -- `J`: [`kg·m²`] Moment of inertia -- `phi_start`: [`rad`] Initial value of absolute rotation angle of component -- `w_start`: [`rad/s`] Initial value of absolute angular velocity of component -- `a_start`: [`rad/s²`] Initial value of absolute angular acceleration of component + + - `J`: [`kg·m²`] Moment of inertia + - `phi_start`: [`rad`] Initial value of absolute rotation angle of component + - `w_start`: [`rad/s`] Initial value of absolute angular velocity of component + - `a_start`: [`rad/s²`] Initial value of absolute angular acceleration of component """ function Inertia(; name, J, phi_start = 0.0, w_start = 0.0, a_start = 0.0) @named flange_a = Flange() @@ -59,16 +64,19 @@ end Linear 1D rotational spring # States: -- `phi_rel(t)`: [`rad`] Relative rotation angle (`flange_b.phi - flange_a.phi`) -- `tau(t)`: [`N.m`] Torque between flanges (`flange_b.tau`) + + - `phi_rel(t)`: [`rad`] Relative rotation angle (`flange_b.phi - flange_a.phi`) + - `tau(t)`: [`N.m`] Torque between flanges (`flange_b.tau`) # Connectors: -- `flange_a` [Flange](@ref) -- `flange_b` [Flange](@ref) + + - `flange_a` [Flange](@ref) + - `flange_b` [Flange](@ref) # Parameters: -- `c`: [`N.m/rad`] Spring constant -- `phi_rel0`: [`rad`] Unstretched spring angle + + - `c`: [`N.m/rad`] Spring constant + - `phi_rel0`: [`rad`] Unstretched spring angle """ function Spring(; name, c, phi_rel0 = 0.0) @named partial_comp = PartialCompliant() @@ -87,17 +95,20 @@ end Linear 1D rotational damper # States: -- `phi_rel(t)`: [`rad`] Relative rotation angle (= flange_b.phi - flange_a.phi) -- `w_rel(t)`: [`rad/s`] Relative angular velocity (= D(phi_rel)) -- `a_rel(t)`: [`rad/s²`] Relative angular acceleration (= D(w_rel)) -- `tau(t)`: [`N.m`] Torque between flanges (= flange_b.tau) + + - `phi_rel(t)`: [`rad`] Relative rotation angle (= flange_b.phi - flange_a.phi) + - `w_rel(t)`: [`rad/s`] Relative angular velocity (= D(phi_rel)) + - `a_rel(t)`: [`rad/s²`] Relative angular acceleration (= D(w_rel)) + - `tau(t)`: [`N.m`] Torque between flanges (= flange_b.tau) # Connectors: -- `flange_a` [Flange](@ref) -- `flange_b` [Flange](@ref) + + - `flange_a` [Flange](@ref) + - `flange_b` [Flange](@ref) # Parameters: -- `d`: [`N.m.s/rad`] Damping constant + + - `d`: [`N.m.s/rad`] Damping constant """ function Damper(; name, d) @named partial_comp = PartialCompliantWithRelativeStates() @@ -116,17 +127,20 @@ Ideal gear without inertia. This element characterizes any type of gear box which is fixed in the ground and which has one driving shaft and one driven shaft. # States: -- `phi_a(t)`: [`rad`] Relative angle between shaft a and the support -- `phi_b(t)`: [`rad`] Relative angle between shaft b and the support + + - `phi_a(t)`: [`rad`] Relative angle between shaft a and the support + - `phi_b(t)`: [`rad`] Relative angle between shaft b and the support # Connectors: -- `flange_a` [Flange](@ref) -- `flange_b` [Flange](@ref) -- `support` [Support](@ref) if `use_support == true` + + - `flange_a` [Flange](@ref) + - `flange_b` [Flange](@ref) + - `support` [Support](@ref) if `use_support == true` # Parameters: -- `ratio`: Transmission ratio (flange_a.phi/flange_b.phi) -- `use_support`: If support flange enabled, otherwise implicitly grounded + + - `ratio`: Transmission ratio (flange_a.phi/flange_b.phi) + - `use_support`: If support flange enabled, otherwise implicitly grounded """ function IdealGear(; name, ratio, use_support = false) @named partial_element = PartialElementaryTwoFlangesAndSupport2(use_support = use_support) @@ -152,20 +166,23 @@ The friction torque is a function of the relative angular velocity between flang Friction model: "Armstrong, B. and C.C. de Wit, Friction Modeling and Compensation, The Control Handbook, CRC Press, 1995." # States: -- `phi_rel(t)`: [`rad`] Relative rotation angle (= flange_b.phi - flange_a.phi) -- `w_rel(t)`: [`rad/s`] Relative angular velocity (= D(phi_rel)) -- `a_rel(t)`: [`rad/s²`] Relative angular acceleration (= D(w_rel)) -- `tau(t)`: [`N.m`] Torque between flanges (= flange_b.tau) + + - `phi_rel(t)`: [`rad`] Relative rotation angle (= flange_b.phi - flange_a.phi) + - `w_rel(t)`: [`rad/s`] Relative angular velocity (= D(phi_rel)) + - `a_rel(t)`: [`rad/s²`] Relative angular acceleration (= D(w_rel)) + - `tau(t)`: [`N.m`] Torque between flanges (= flange_b.tau) # Connectors: -- `flange_a` [Flange](@ref) -- `flange_b` [Flange](@ref) + + - `flange_a` [Flange](@ref) + - `flange_b` [Flange](@ref) # Parameters: -- `f`: [`N⋅m/(rad/s)`] Viscous friction coefficient -- `tau_c`: [`N⋅m`] Coulomb friction torque -- `w_brk`: [`rad/s`] Breakaway friction velocity -- `tau_brk`: [`N⋅m`] Breakaway friction torque + + - `f`: [`N⋅m/(rad/s)`] Viscous friction coefficient + - `tau_c`: [`N⋅m`] Coulomb friction torque + - `w_brk`: [`rad/s`] Breakaway friction velocity + - `tau_brk`: [`N⋅m`] Breakaway friction torque """ function RotationalFriction(; name, f, tau_c, w_brk, tau_brk) @named partial_comp = PartialCompliantWithRelativeStates() diff --git a/src/Mechanical/Rotational/sensors.jl b/src/Mechanical/Rotational/sensors.jl index 571143dd1..2fc6cb462 100644 --- a/src/Mechanical/Rotational/sensors.jl +++ b/src/Mechanical/Rotational/sensors.jl @@ -4,8 +4,9 @@ Ideal sensor to measure the absolute flange angle # Connectors: -- `flange`: [Flange](@ref) Flange of shaft from which sensor information shall be measured -- `phi`: [RealOutput](@ref) Absolute angle of flange + + - `flange`: [Flange](@ref) Flange of shaft from which sensor information shall be measured + - `phi`: [RealOutput](@ref) Absolute angle of flange """ function AngleSensor(; name) @named flange = Flange() @@ -21,8 +22,9 @@ end Ideal sensor to measure the absolute flange angular velocity # Connectors: -- `flange`: [Flange](@ref) Flange of shaft from which sensor information shall be measured -- `w`: [RealOutput](@ref) Absolute angular velocity of flange + + - `flange`: [Flange](@ref) Flange of shaft from which sensor information shall be measured + - `w`: [RealOutput](@ref) Absolute angular velocity of flange """ function SpeedSensor(; name) @named flange = Flange() @@ -38,9 +40,10 @@ end Ideal sensor to measure the torque between two flanges (`= flange_a.tau`) # Connectors: -- `flange_a`: [Flange](@ref) Left flange of shaft -- `flange_b`: [Flange](@ref) Left flange of shaft -- `tau`: [RealOutput](@ref) Torque in flange flange_a and flange_b (`tau = flange_a.tau = -flange_b.tau`) + + - `flange_a`: [Flange](@ref) Left flange of shaft + - `flange_b`: [Flange](@ref) Left flange of shaft + - `tau`: [RealOutput](@ref) Torque in flange flange_a and flange_b (`tau = flange_a.tau = -flange_b.tau`) """ function TorqueSensor(; name) @named flange_a = Flange() @@ -57,9 +60,10 @@ end Ideal sensor to measure the relative angular velocity # Connectors: -- `flange_a`: [Flange](@ref) Flange of shaft from which sensor information shall be measured -- `flange_b`: [Flange](@ref) Flange of shaft from which sensor information shall be measured -- `w`: [RealOutput](@ref) Absolute angular velocity of flange + + - `flange_a`: [Flange](@ref) Flange of shaft from which sensor information shall be measured + - `flange_b`: [Flange](@ref) Flange of shaft from which sensor information shall be measured + - `w`: [RealOutput](@ref) Absolute angular velocity of flange """ function RelSpeedSensor(; name) @named flange_a = Flange() diff --git a/src/Mechanical/Rotational/sources.jl b/src/Mechanical/Rotational/sources.jl index 06a375a9b..a6eb57f15 100644 --- a/src/Mechanical/Rotational/sources.jl +++ b/src/Mechanical/Rotational/sources.jl @@ -4,14 +4,17 @@ Input signal acting as external torque on a flange # States: -- `phi_support(t)`: [`rad`] Absolute angle of support flange" + + - `phi_support(t)`: [`rad`] Absolute angle of support flange" # Connectors: -- `flange` [Flange](@ref) -- `tau` [RealInput](@ref) Accelerating torque acting at flange `-flange.tau` + + - `flange` [Flange](@ref) + - `tau` [RealInput](@ref) Accelerating torque acting at flange `-flange.tau` # Parameters: -- `use_support` + + - `use_support` """ function Torque(; name, use_support = false) @named partial_element = PartialElementaryOneFlangeAndSupport2(use_support = use_support) @@ -27,16 +30,19 @@ end Forced movement of a flange according to a reference angular velocity signal # States: -- `phi_support(t)`: [`rad`] Absolute angle of support flange" + + - `phi_support(t)`: [`rad`] Absolute angle of support flange" # Connectors: -- `flange` [Flange](@ref) -- `w_ref` [RealInput](@ref) Reference angular velocity of flange with respect to support as input signal needs to be continuously differential + + - `flange` [Flange](@ref) + - `w_ref` [RealInput](@ref) Reference angular velocity of flange with respect to support as input signal needs to be continuously differential # Parameters: -- `use_support`: If support flange enabled, otherwise implicitly grounded -- `exact`: true/false exact treatment/filtering the input signal -- `tau_filt`: [`rad/s`] if exact=false, Time constant of low-pass filter to filter input signal + + - `use_support`: If support flange enabled, otherwise implicitly grounded + - `exact`: true/false exact treatment/filtering the input signal + - `tau_filt`: [`rad/s`] if exact=false, Time constant of low-pass filter to filter input signal """ function Speed(; name, use_support = false, exact = false, tau_filt = 50) @named partial_element = PartialElementaryOneFlangeAndSupport2(use_support = use_support) diff --git a/src/Mechanical/Rotational/utils.jl b/src/Mechanical/Rotational/utils.jl index 7b07fc7e8..cfc81a6d4 100644 --- a/src/Mechanical/Rotational/utils.jl +++ b/src/Mechanical/Rotational/utils.jl @@ -34,16 +34,19 @@ Support/housing of a 1-dim. rotational shaft Partial model for the compliant connection of two rotational 1-dim. shaft flanges. # States: -- `phi_rel(t)`: [`rad`] Relative rotation angle (`flange_b.phi - flange_a.phi`) -- `tau(t)`: [`N.m`] Torque between flanges (`flange_b.tau`) + + - `phi_rel(t)`: [`rad`] Relative rotation angle (`flange_b.phi - flange_a.phi`) + - `tau(t)`: [`N.m`] Torque between flanges (`flange_b.tau`) # Connectors: -- `flange_a` [Flange](@ref) -- `flange_b` [Flange](@ref) + + - `flange_a` [Flange](@ref) + - `flange_b` [Flange](@ref) # Parameters: -- `phi_rel_start`: [`rad`] Initial relative rotation angle -- `tau_start`: [`N.m`] Initial torque between flanges + + - `phi_rel_start`: [`rad`] Initial relative rotation angle + - `tau_start`: [`N.m`] Initial torque between flanges """ function PartialCompliant(; name, phi_rel_start = 0.0, tau_start = 0.0) @named flange_a = Flange() @@ -63,20 +66,23 @@ end Partial model for the compliant connection of two rotational 1-dim. shaft flanges where the relative angle and speed are used as preferred states # States: -- `phi_rel(t)`: [`rad`] Relative rotation angle (= flange_b.phi - flange_a.phi) -- `w_rel(t)`: [`rad/s`] Relative angular velocity (= D(phi_rel)) -- `a_rel(t)`: [`rad/s²`] Relative angular acceleration (= D(w_rel)) -- `tau(t)`: [`N.m`] Torque between flanges (= flange_b.tau) + + - `phi_rel(t)`: [`rad`] Relative rotation angle (= flange_b.phi - flange_a.phi) + - `w_rel(t)`: [`rad/s`] Relative angular velocity (= D(phi_rel)) + - `a_rel(t)`: [`rad/s²`] Relative angular acceleration (= D(w_rel)) + - `tau(t)`: [`N.m`] Torque between flanges (= flange_b.tau) # Connectors: -- `flange_a` [Flange](@ref) -- `flange_b` [Flange](@ref) + + - `flange_a` [Flange](@ref) + - `flange_b` [Flange](@ref) # Parameters: -- `phi_rel_start`: [`rad`] Initial relative rotation angle -- `w_rel_start`: [`rad/s`] Initial relative angular velocity (= D(phi_rel)) -- `a_rel_start`: [`rad/s²`] Initial relative angular acceleration (= D(w_rel)) -- `tau_start`: [`N.m`] Initial torque between flanges + + - `phi_rel_start`: [`rad`] Initial relative rotation angle + - `w_rel_start`: [`rad/s`] Initial relative angular velocity (= D(phi_rel)) + - `a_rel_start`: [`rad/s²`] Initial relative angular acceleration (= D(w_rel)) + - `tau_start`: [`N.m`] Initial torque between flanges """ function PartialCompliantWithRelativeStates(; name, phi_rel_start = 0.0, w_start = 0.0, a_start = 0.0, tau_start = 0.0) @@ -103,13 +109,16 @@ end Partial model for a component with one rotational 1-dim. shaft flange and a support used for textual modeling, i.e., for elementary models # States: -- `phi_support(t)`: [`rad`] Absolute angle of support flange" + + - `phi_support(t)`: [`rad`] Absolute angle of support flange" # Connectors: -- `flange` [Flange](@ref) + + - `flange` [Flange](@ref) # Parameters: -- `use_support`: If support flange enabled, otherwise implicitly grounded + + - `use_support`: If support flange enabled, otherwise implicitly grounded """ function PartialElementaryOneFlangeAndSupport2(; name, use_support = false) @named flange = Flange() @@ -132,15 +141,18 @@ end Partial model for a component with two rotational 1-dim. shaft flanges and a support used for textual modeling, i.e., for elementary models # States: -- `phi_support(t)`: [`rad`] Absolute angle of support flange + + - `phi_support(t)`: [`rad`] Absolute angle of support flange # Connectors: -- `flange_a` [Flange](@ref) -- `flange_b` [Flange](@ref) -- `support` [Support](@ref) if `use_support == true` + + - `flange_a` [Flange](@ref) + - `flange_b` [Flange](@ref) + - `support` [Support](@ref) if `use_support == true` # Parameters: -- `use_support`: If support flange enabled, otherwise implicitly grounded + + - `use_support`: If support flange enabled, otherwise implicitly grounded """ function PartialElementaryTwoFlangesAndSupport2(; name, use_support = false) @named flange_a = Flange() diff --git a/src/Mechanical/Translational/components.jl b/src/Mechanical/Translational/components.jl index f6235da2b..bf0da565b 100644 --- a/src/Mechanical/Translational/components.jl +++ b/src/Mechanical/Translational/components.jl @@ -4,7 +4,8 @@ Fixes a flange position (velocity = 0) # Connectors: -- `flange: 1-dim. translational flange` + + - `flange: 1-dim. translational flange` """ function Fixed(; name) @named flange = MechanicalPort() @@ -19,19 +20,20 @@ end Sliding mass with inertia # Parameters: -- `m`: [kg] mass of sliding body -- `v_0`: [m/s] Initial value of absolute linear velocity of sliding mass (default 0 m/s) -- `s_0`: [m] (optional) initial value of absolute position of sliding mass -- `g`: [m/s²] (optional) gravity field acting on the mass, positive value acts in the positive direction + - `m`: [kg] mass of sliding body + - `v_0`: [m/s] Initial value of absolute linear velocity of sliding mass (default 0 m/s) + - `s_0`: [m] (optional) initial value of absolute position of sliding mass + - `g`: [m/s²] (optional) gravity field acting on the mass, positive value acts in the positive direction # States: -- `v`: [m/s] absolute linear velocity of sliding mass -- `s`: [m] (optional with parameter s_0) absolute position of sliding mass + - `v`: [m/s] absolute linear velocity of sliding mass + - `s`: [m] (optional with parameter s_0) absolute position of sliding mass # Connectors: -- `flange: 1-dim. translational flange` + + - `flange: 1-dim. translational flange` """ function Mass(; name, v_0 = 0.0, m, s_0 = nothing, g = nothing) pars = @parameters begin @@ -80,14 +82,16 @@ const REL = Val(:relative) Linear 1D translational spring # Parameters: -- `k`: [N/m] Spring constant -- `delta_s_0`: initial spring stretch -- `v_a_0`: [m/s] Initial value of absolute linear velocity at flange_a (default 0 m/s) -- `v_b_0`: [m/s] Initial value of absolute linear velocity at flange_b (default 0 m/s) + + - `k`: [N/m] Spring constant + - `delta_s_0`: initial spring stretch + - `v_a_0`: [m/s] Initial value of absolute linear velocity at flange_a (default 0 m/s) + - `v_b_0`: [m/s] Initial value of absolute linear velocity at flange_b (default 0 m/s) # Connectors: -- `flange_a: 1-dim. translational flange on one side of spring` -- `flange_b: 1-dim. translational flange on opposite side of spring` + + - `flange_a: 1-dim. translational flange on one side of spring` + - `flange_b: 1-dim. translational flange on opposite side of spring` """ function Spring(; name, k, delta_s_0 = 0.0, v_a_0 = 0.0, v_b_0 = 0.0) Spring(REL; name, k, delta_s_0, v_a_0, v_b_0) @@ -155,13 +159,15 @@ end Linear 1D translational damper # Parameters: -- `d`: [N.s/m] Damping constant -- `v_a_0`: [m/s] Initial value of absolute linear velocity at flange_a (default 0 m/s) -- `v_b_0`: [m/s] Initial value of absolute linear velocity at flange_b (default 0 m/s) + + - `d`: [N.s/m] Damping constant + - `v_a_0`: [m/s] Initial value of absolute linear velocity at flange_a (default 0 m/s) + - `v_b_0`: [m/s] Initial value of absolute linear velocity at flange_b (default 0 m/s) # Connectors: -- `flange_a: 1-dim. translational flange on one side of damper` -- `flange_b: 1-dim. translational flange on opposite side of damper` + + - `flange_a: 1-dim. translational flange on one side of damper` + - `flange_b: 1-dim. translational flange on opposite side of damper` """ function Damper(; name, d, v_a_0 = 0.0, v_b_0 = 0.0) pars = @parameters begin diff --git a/src/Mechanical/TranslationalPosition/components.jl b/src/Mechanical/TranslationalPosition/components.jl index 11e9233fb..2a1efa5a2 100644 --- a/src/Mechanical/TranslationalPosition/components.jl +++ b/src/Mechanical/TranslationalPosition/components.jl @@ -4,10 +4,12 @@ Flange fixed in housing at a given position. # Parameters: -- `s_0`: [m] Fixed offset position of housing + + - `s_0`: [m] Fixed offset position of housing # Connectors: -- `flange: 1-dim. translational flange` + + - `flange: 1-dim. translational flange` """ function Fixed(; name, s_0 = 0.0) pars = @parameters s_0 = s_0 @@ -27,16 +29,19 @@ end Sliding mass with inertia # Parameters: -- `m`: [kg] Mass of sliding mass -- `s_0`: [m] Initial value of absolute position of sliding mass -- `v_0`: [m/s] Initial value of absolute linear velocity of sliding mass -# States: -- `s`: [m] Absolute position of sliding mass -- `v`: [m/s] Absolute linear velocity of sliding mass (= der(s)) + - `m`: [kg] Mass of sliding mass + - `s_0`: [m] Initial value of absolute position of sliding mass + - `v_0`: [m/s] Initial value of absolute linear velocity of sliding mass + +# States: + + - `s`: [m] Absolute position of sliding mass + - `v`: [m/s] Absolute linear velocity of sliding mass (= der(s)) # Connectors: -- `flange: 1-dim. translational flange of mass` + + - `flange: 1-dim. translational flange of mass` """ function Mass(; name, m, s_0 = 0.0, v_0 = 0.0) @named flange = Flange() @@ -104,14 +109,16 @@ const ABS = Val(:absolute) Linear 1D translational spring # Parameters: -- `k`: [N/m] Spring constant -- `l`: Unstretched spring length -- `s_a_0`: [m] Initial value of absolute position of flange_a -- `s_b_0`: [m] Initial value of absolute position of flange_b + + - `k`: [N/m] Spring constant + - `l`: Unstretched spring length + - `s_a_0`: [m] Initial value of absolute position of flange_a + - `s_b_0`: [m] Initial value of absolute position of flange_b # Connectors: -- `flange_a: 1-dim. translational flange on one side of spring` -- `flange_b: 1-dim. translational flange on opposite side of spring` + + - `flange_a: 1-dim. translational flange on one side of spring` + - `flange_b: 1-dim. translational flange on opposite side of spring` #default function """ Spring(; name, k, s_a_0 = 0, s_b_0 = 0, l = 0) = Spring(ABS; name, k, s_a_0, s_b_0, l) #default function @@ -149,15 +156,17 @@ end Linear 1D translational damper # Parameters: -- `d`: [N.s/m] Damping constant -- `s_a_0`: [m] Initial value of absolute position of flange_a -- `s_b_0`: [m] Initial value of absolute position of flange_b -- `v_a_0`: [m/s] Initial value of absolute linear velocity of flange_a -- `v_b_0`: [m/s] Initial value of absolute linear velocity of flange_a + + - `d`: [N.s/m] Damping constant + - `s_a_0`: [m] Initial value of absolute position of flange_a + - `s_b_0`: [m] Initial value of absolute position of flange_b + - `v_a_0`: [m/s] Initial value of absolute linear velocity of flange_a + - `v_b_0`: [m/s] Initial value of absolute linear velocity of flange_a # Connectors: -- `flange_a: 1-dim. translational flange on one side of damper` -- `flange_b: 1-dim. translational flange on opposite side of damper` + + - `flange_a: 1-dim. translational flange on one side of damper` + - `flange_b: 1-dim. translational flange on opposite side of damper` """ function Damper(; name, d, v_a_0 = 0.0, v_b_0 = 0.0, s_a_0 = 0, s_b_0 = 0) pars = @parameters begin diff --git a/src/Mechanical/TranslationalPosition/utils.jl b/src/Mechanical/TranslationalPosition/utils.jl index 85187c86d..fdafe14b8 100644 --- a/src/Mechanical/TranslationalPosition/utils.jl +++ b/src/Mechanical/TranslationalPosition/utils.jl @@ -38,12 +38,14 @@ Support/housing 1-dim. translational flange. Partial model for the compliant connection of two translational 1-dim. flanges. # Parameters: -- `s_rel_start`: [m] Initial relative distance between the flanges -- `f_start`: [N] Initial force between flanges + + - `s_rel_start`: [m] Initial relative distance between the flanges + - `f_start`: [N] Initial force between flanges # States: -- `s_rel`: [m] Relative distance (= flange_b.s - flange_a.s) -- `f`: [N] Force between flanges (= flange_b.f) + + - `s_rel`: [m] Relative distance (= flange_b.s - flange_a.s) + - `f`: [N] Force between flanges (= flange_b.f) """ function PartialCompliant(; name, s_rel_start = 0.0, f_start = 0.0) @named flange_a = Flange() @@ -68,16 +70,18 @@ end Partial model for the compliant connection of two translational 1-dim. flanges. # Parameters: -- `s_rel_start`: [m] Initial relative distance -- `v_rel_start`: [m/s] Initial relative linear velocity (= der(s_rel)) -- `a_rel_start`: [m/s²] Initial relative linear acceleration (= der(v_rel)) -- `f_start`: [N] Initial force between flanges + + - `s_rel_start`: [m] Initial relative distance + - `v_rel_start`: [m/s] Initial relative linear velocity (= der(s_rel)) + - `a_rel_start`: [m/s²] Initial relative linear acceleration (= der(v_rel)) + - `f_start`: [N] Initial force between flanges # States: -- `s_rel`: [m] Relative distance (= flange_b.phi - flange_a.phi) -- `v_rel`: [m/s] Relative linear velocity (= der(s_rel)) -- `a_rel`: [m/s²] Relative linear acceleration (= der(v_rel)) -- `f`: [N] Force between flanges (= flange_b.f) + + - `s_rel`: [m] Relative distance (= flange_b.phi - flange_a.phi) + - `v_rel`: [m/s] Relative linear velocity (= der(s_rel)) + - `a_rel`: [m/s²] Relative linear acceleration (= der(v_rel)) + - `f`: [N] Force between flanges (= flange_b.f) """ function PartialCompliantWithRelativeStates(; name, delta_s_0 = 0.0) @named flange_a = Flange() @@ -98,10 +102,12 @@ end Partial model for a component with one translational 1-dim. shaft flange and a support used for textual modeling, i.e., for elementary models # Parameters: -- `use_support`: If support flange enabled, otherwise implicitly grounded + + - `use_support`: If support flange enabled, otherwise implicitly grounded # States: -- `s_support`: [m] Absolute position of support flange" + + - `s_support`: [m] Absolute position of support flange" """ function PartialElementaryOneFlangeAndSupport2(; name, use_support = false) @named flange = Flange() @@ -124,10 +130,12 @@ end Partial model for a component with two translational 1-dim. flanges and a support used for textual modeling, i.e., for elementary models # Parameters: -- `use_support`: If support flange enabled, otherwise implicitly grounded + + - `use_support`: If support flange enabled, otherwise implicitly grounded # States: -- `s_support`: [m] Absolute position of support flange" + + - `s_support`: [m] Absolute position of support flange" """ function PartialElementaryTwoFlangesAndSupport2(; name, use_support = false) @named flange_a = Flange() diff --git a/src/Thermal/HeatTransfer/ideal_components.jl b/src/Thermal/HeatTransfer/ideal_components.jl index feb0dc839..e7b2b2757 100644 --- a/src/Thermal/HeatTransfer/ideal_components.jl +++ b/src/Thermal/HeatTransfer/ideal_components.jl @@ -4,15 +4,18 @@ Lumped thermal element storing heat # States: -- `T`: [`K`] Temperature of element -- `der_T`: [`K/s`] Time derivative of temperature + + - `T`: [`K`] Temperature of element + - `der_T`: [`K/s`] Time derivative of temperature # Connectors: -- `port` + + - `port` # Parameters: -- `C`: [`J/K`] Heat capacity of element (= cp*m) -- `T_start`: [`K`] Initial temperature of element + + - `C`: [`J/K`] Heat capacity of element (= cp*m) + - `T_start`: [`K`] Initial temperature of element """ function HeatCapacitor(; name, C, T_start = 273.15 + 20) @named port = HeatPort() @@ -35,14 +38,17 @@ end Lumped thermal element transporting heat without storing it. # States: + see [`Element1D`](@ref) # Connectors: + `port_a` `port_b` # Parameters: -- `G`: [`W/K`] Constant thermal conductance of material + + - `G`: [`W/K`] Constant thermal conductance of material """ function ThermalConductor(; name, G) @named element1d = Element1D() @@ -60,15 +66,18 @@ end Lumped thermal element transporting heat without storing it. # States: -- `dT`: [`K`] Temperature difference across the component a.T - b.T -- `Q_flow`: [`W`] Heat flow rate from port a -> port b + + - `dT`: [`K`] Temperature difference across the component a.T - b.T + - `Q_flow`: [`W`] Heat flow rate from port a -> port b # Connectors: -- `port_a` -- `port_b` + + - `port_a` + - `port_b` # Parameters: -- `R`: [`K/W`] Constant thermal resistance of material + + - `R`: [`K/W`] Constant thermal resistance of material """ function ThermalResistor(; name, R) @named element1d = Element1D() @@ -87,15 +96,18 @@ end Lumped thermal element for heat convection. # States: -- `dT`: [`K`] Temperature difference across the component solid.T - fluid.T -- `Q_flow`: [`W`] Heat flow rate from `solid` -> `fluid` + + - `dT`: [`K`] Temperature difference across the component solid.T - fluid.T + - `Q_flow`: [`W`] Heat flow rate from `solid` -> `fluid` # Connectors: -- `solid` -- `fluid` + + - `solid` + - `fluid` # Parameters: -- `G`: [W/K] Convective thermal conductance + + - `G`: [W/K] Convective thermal conductance """ function ConvectiveConductor(; name, G) @named solid = HeatPort() @@ -115,15 +127,18 @@ end Lumped thermal element for heat convection. # States: -- `dT`: [`K`] Temperature difference across the component solid.T - fluid.T -- `Q_flow`: [`W`] Heat flow rate from `solid` -> `fluid` + + - `dT`: [`K`] Temperature difference across the component solid.T - fluid.T + - `Q_flow`: [`W`] Heat flow rate from `solid` -> `fluid` # Connectors: -- `solid` -- `fluid` + + - `solid` + - `fluid` # Parameters: -- `R`: [`K/W`] Constant thermal resistance of material + + - `R`: [`K/W`] Constant thermal resistance of material """ function ConvectiveResistor(; name, R) @named solid = HeatPort() @@ -143,15 +158,18 @@ end Lumped thermal element for radiation heat transfer. # States: -- `dT`: [`K`] Temperature difference across the component a.T - b.T -- `Q_flow`: [`W`] Heat flow rate from port a -> port b + + - `dT`: [`K`] Temperature difference across the component a.T - b.T + - `Q_flow`: [`W`] Heat flow rate from port a -> port b # Connectors: -- `port_a` -- `port_b` + + - `port_a` + - `port_b` # Parameters: -- `G`: [m^2] Net radiation conductance between two surfaces + + - `G`: [m^2] Net radiation conductance between two surfaces # Stefan-Boltzmann constant TODO: extract into physical constants module or use existing one """ function BodyRadiation(; name, G) sigma = 5.6703744191844294e-8 # Stefan-Boltzmann constant TODO: extract into physical constants module or use existing one @@ -177,11 +195,13 @@ This is a model to collect the heat flows from `m` heatports to one single heatp # States: # Connectors: -- `port_a1` to `port_am` -- `port_b` + + - `port_a1` to `port_am` + - `port_b` # Parameters: -- `m`: Number of heat ports (e.g. m=2: `port_a1`, `port_a2`) + + - `m`: Number of heat ports (e.g. m=2: `port_a1`, `port_a2`) """ function ThermalCollector(; name, m::Integer = 1) port_a = [HeatPort(name = Symbol(:port_a, i)) for i in 1:m] diff --git a/src/Thermal/HeatTransfer/sensors.jl b/src/Thermal/HeatTransfer/sensors.jl index d95b6c51f..44c0c6bc2 100644 --- a/src/Thermal/HeatTransfer/sensors.jl +++ b/src/Thermal/HeatTransfer/sensors.jl @@ -4,14 +4,16 @@ Absolute temperature sensor in kelvin. This is an ideal absolute temperature sensor which returns the temperature of the connected port in kelvin as an output -signal. The sensor itself has no thermal interaction with whatever it is connected to. Furthermore, no thermocouple-like +signal. The sensor itself has no thermal interaction with whatever it is connected to. Furthermore, no thermocouple-like lags are associated with this sensor model. # States: -- `T(t)`: [`K`] Absolute temperature + + - `T(t)`: [`K`] Absolute temperature # Connectors: -- `port` + + - `port` """ function TemperatureSensor(; name) @named port = HeatPort() @@ -26,15 +28,17 @@ end Relative Temperature sensor. -The relative temperature `port_a.T - port_b.T` is determined between the two ports of this component and is provided as +The relative temperature `port_a.T - port_b.T` is determined between the two ports of this component and is provided as output signal in kelvin. # States: -- `T(t)`: [`K`] Relative temperature `a.T - b.T` + + - `T(t)`: [`K`] Relative temperature `a.T - b.T` # Connectors: -- `port_a` -- `port_b` + + - `port_a` + - `port_b` """ function RelativeTemperatureSensor(; name) @named port_a = HeatPort() @@ -52,16 +56,18 @@ end Heat flow rate sensor. This model is capable of monitoring the heat flow rate flowing through this component. The sensed value of heat flow rate -is the amount that passes through this sensor while keeping the temperature drop across the sensor zero. This is an ideal -model so it does not absorb any energy and it has no direct effect on the thermal response of a system it is included in. +is the amount that passes through this sensor while keeping the temperature drop across the sensor zero. This is an ideal +model, so it does not absorb any energy, and it has no direct effect on the thermal response of a system it is included in. The output signal is positive, if the heat flows from `port_a` to `port_b`. # States: -- `Q_flow(t)`: [`W`] Heat flow from `port_a` to `port_b` + + - `Q_flow(t)`: [`W`] Heat flow from `port_a` to `port_b` # Connectors: -- `port_a` -- `port_b` + + - `port_a` + - `port_b` """ function HeatFlowSensor(; name) @named port_a = HeatPort() diff --git a/src/Thermal/HeatTransfer/sources.jl b/src/Thermal/HeatTransfer/sources.jl index 78636fbc7..e04e7583a 100644 --- a/src/Thermal/HeatTransfer/sources.jl +++ b/src/Thermal/HeatTransfer/sources.jl @@ -8,12 +8,14 @@ The constant amount of heat flow rate `Q_flow` is given as a parameter. The heat the component FixedHeatFlow is connected, if parameter `Q_flow` is positive. # Connectors: -- `port` + + - `port` # Parameters: -- `Q_flow`: [W] Fixed heat flow rate at port -- `T_ref`: [K] Reference temperature -- `alpha`: [1/K] Temperature coefficient of heat flow rate + + - `Q_flow`: [W] Fixed heat flow rate at port + - `T_ref`: [K] Reference temperature + - `alpha`: [1/K] Temperature coefficient of heat flow rate """ function FixedHeatFlow(; name, Q_flow = 1.0, T_ref = 293.15, alpha = 0.0) pars = @parameters begin @@ -37,10 +39,12 @@ Fixed temperature boundary condition in kelvin. This model defines a fixed temperature `T` at its port in kelvin, i.e., it defines a fixed temperature as a boundary condition. # Connectors: -- `port` + + - `port` # Parameters: -- `T`: [K] Fixed temperature boundary condition + + - `T`: [K] Fixed temperature boundary condition """ function FixedTemperature(; name, T) @named port = HeatPort() @@ -56,19 +60,21 @@ end Prescribed heat flow boundary condition. -This model allows a specified amount of heat flow rate to be "injected" into a thermal system at a given port. -The amount of heat is given by the input signal `Q_flow` into the model. The heat flows into the component to which +This model allows a specified amount of heat flow rate to be "injected" into a thermal system at a given port. +The amount of heat is given by the input signal `Q_flow` into the model. The heat flows into the component to which the component `PrescribedHeatFlow` is connected, if the input signal is positive. -If parameter alpha is > 0, the heat flow is multiplied by `1 + alpha*(port.T - T_ref`) in order to simulate temperature -dependent losses (which are given an reference temperature T_ref). +If parameter alpha is > 0, the heat flow is multiplied by `1 + alpha*(port.T - T_ref`) in order to simulate temperature +dependent losses (which are given a reference temperature T_ref). # Connectors: -- `port` -- `Q_flow` Input for the heat flow + + - `port` + - `Q_flow` Input for the heat flow # Parameters: -- `T_ref`: [K] Reference temperature -- `alpha`: [1/K] Temperature coefficient of heat flow rate + + - `T_ref`: [K] Reference temperature + - `alpha`: [1/K] Temperature coefficient of heat flow rate """ function PrescribedHeatFlow(; name, T_ref = 293.15, alpha = 0.0) pars = @parameters begin @@ -87,15 +93,16 @@ end """ PrescribedTemperature(; name, T) -This model represents a variable temperature boundary condition. +This model represents a variable temperature boundary condition. -The temperature in kelvin is given as input signal `T` to the model. The effect is that an instance of -this model acts as an infinite reservoir able to absorb or generate as much energy as required to keep +The temperature in kelvin is given as input signal `T` to the model. The effect is that an instance of +this model acts as an infinite reservoir, able to absorb or generate as much energy as required to keep the temperature at the specified value. # Connectors: -- `port` -- `T` input for the temperature + + - `port` + - `T` input for the temperature """ function PrescribedTemperature(; name) @named port = HeatPort() diff --git a/src/Thermal/utils.jl b/src/Thermal/utils.jl index 8f5fbdc4c..ff7e16c04 100644 --- a/src/Thermal/utils.jl +++ b/src/Thermal/utils.jl @@ -20,21 +20,24 @@ Port for a thermal system. """ Element1D(;name, dT0=0.0, Q_flow0=0.0) -This partial model contains the basic connectors and variables to allow heat transfer models to be created that do not +This partial model contains the basic connectors and variables to allow heat transfer models to be created that do not store energy. This model defines and includes equations for the temperature drop across the element, `dT`, and the heat flow rate through the element from `port_a` to `port_b`, `Q_flow`. # States: -- `dT`: [`K`] Temperature difference across the component a.T - b.T -- `Q_flow`: [`W`] Heat flow rate from port a -> port b + + - `dT`: [`K`] Temperature difference across the component a.T - b.T + - `Q_flow`: [`W`] Heat flow rate from port a -> port b # Connectors: + `port_a` `port_b` # Parameters: -- `dT_start`: [K] Initial temperature difference across the component a.T - b.T -- `Q_flow_start`: [W] Initial heat flow rate from port a -> port b + + - `dT_start`: [K] Initial temperature difference across the component a.T - b.T + - `Q_flow_start`: [W] Initial heat flow rate from port a -> port b """ function Element1D(; name, dT_start = 0.0, Q_flow_start = 0.0) @named port_a = HeatPort() diff --git a/test/Blocks/continuous.jl b/test/Blocks/continuous.jl index 865c307e8..2fb627759 100644 --- a/test/Blocks/continuous.jl +++ b/test/Blocks/continuous.jl @@ -130,7 +130,9 @@ end @test sol[ss.output.u[1]][end]≈y0[] atol=1e-3 # Test that the output equals the operating point end -"""Second order demo plant""" +""" +Second order demo plant +""" function Plant(; name, x_start = zeros(2)) @named input = RealInput() @named output = RealOutput()