diff --git a/docs/src/API/electrical.md b/docs/src/API/electrical.md index 7f6546abd..10249e278 100644 --- a/docs/src/API/electrical.md +++ b/docs/src/API/electrical.md @@ -33,6 +33,7 @@ Inductor IdealOpAmp Diode HeatingDiode +VariableResistor ``` ## Analog Sensors diff --git a/src/Electrical/Analog/ideal_components.jl b/src/Electrical/Analog/ideal_components.jl index a90d9258a..a4896446d 100644 --- a/src/Electrical/Analog/ideal_components.jl +++ b/src/Electrical/Analog/ideal_components.jl @@ -342,4 +342,91 @@ Temperature dependent diode based on the Shockley diode equation. i ~ Is * (exp(v / (n * Vt)) - 1) # Shockley diode equation port.Q_flow ~ -v * i # -LossPower end -end \ No newline at end of file +end + +""" + VariableResistor(; name, R_ref = 1.0, T_ref = 300.15, R_const = 1e-3, T_dep = false) + +Variable resistor with optional temperature dependency. + +The total resistance R ∈ [R_const, R_const + R_ref], where pos is the +position of the wiper and R_ref is the variable resistance between p and n. +The total resistance is then: + +R = R_const + pos * R_ref + +If T_dep is true, then R also depends on the temperature of the heat port with +temperature coefficient alpha. The total resistance is then: + +R = R_const + pos * R_ref * (1 + alpha * (port.T - T_ref)) + +# States + + - See [OnePort](@ref) + - `pos(t)`: Position of the wiper (normally 0-1) + - `R(t)`: Resistance + +# Connectors + + - `p` Positive pin + - `n` Negative pin + - `position` RealInput to set the position of the wiper + - `port` [HeatPort](@ref) Heat port to model the temperature dependency + +# Parameters + + - `R_ref`: [`Ω`] Resistance at temperature T_ref when fully closed (pos=1.0) + - `T_ref`: [K] Reference temperature + - `R_const`: [`Ω`] Constant resistance between p and n + - `T_dep`: Temperature dependency + - `alpha`: [K⁻¹] Temperature coefficient of resistance + - `enforce_bounds`: Enforce bounds for the position of the wiper (0-1) +""" +@mtkmodel VariableResistor begin + @extend v, i = oneport = OnePort() + + @structural_parameters begin + T_dep = false + enforce_bounds = true + end + + @parameters begin + R_ref = 1.0, + [description = "Resistance at temperature T_ref when fully closed (pos=1.0)", + unit = "Ω"] + T_ref = 300.15, [description = "Reference temperature", unit = "K"] + R_const = 1e-3, [description = "Constant resistance between p and n", unit = "Ω"] + end + + @components begin + position = RealInput() + end + + @variables begin + pos(t), [description = "Position of the wiper (normally 0-1)"] + R(t), [description = "Resistance", unit = "Ω"] + end + + if T_dep + @parameters begin + alpha = 1e-3, + [description = "Temperature coefficient of resistance", unit = "K^-1"] + end + @components begin + port = HeatPort() + end + @equations begin + port.Q_flow ~ -v * i # -LossPower + R ~ R_const + pos * R_ref * (1 + alpha * (port.T - T_ref)) + end + else + @equations begin + R ~ R_const + pos * R_ref + end + end + + @equations begin + pos ~ (enforce_bounds ? clamp(position.u, 0, 1) : position.u) + v ~ i * R + end +end diff --git a/src/Electrical/Electrical.jl b/src/Electrical/Electrical.jl index 55c1acd93..68ba19cea 100644 --- a/src/Electrical/Electrical.jl +++ b/src/Electrical/Electrical.jl @@ -15,7 +15,7 @@ include("utils.jl") export Capacitor, Ground, Inductor, Resistor, Conductor, Short, IdealOpAmp, EMF, - HeatingResistor, Diode, HeatingDiode + HeatingResistor, Diode, HeatingDiode, VariableResistor include("Analog/ideal_components.jl") export CurrentSensor, PotentialSensor, VoltageSensor, PowerSensor, MultiSensor diff --git a/test/Electrical/analog.jl b/test/Electrical/analog.jl index befd16381..23119ffaa 100644 --- a/test/Electrical/analog.jl +++ b/test/Electrical/analog.jl @@ -486,3 +486,57 @@ end @test SciMLBase.successful_retcode(sol) @test sol[capacitor.v][end] < capacitor_voltage[end] end + +@testset "VariableResistor with Temperature Dependency" begin + R_ref = 2.0 + R_const = 1.0 + + # Define the RC model as described + @mtkmodel RC begin + @parameters begin + R = R_ref # Variable resistance reference value + C = 1.0 # Capacitance + k = 10.0 # Voltage source scaling factor + f = 0.2 # Frequency of sine input + T = 300.0 # Ambient temperature in Kelvin + end + @components begin + res_input = Sine(frequency = f, amplitude = 1.0, offset = 0.0) + volt_input = Constant(k = 1.0) + resistor = VariableResistor(R_ref = R_ref, R_const = R_const, T_dep = true) + capacitor = Capacitor(C = C, v = 0.0) + source = Voltage() + temp = FixedTemperature(T = T) + ground = Ground() + end + @equations begin + connect(temp.port, resistor.port) + connect(res_input.output, resistor.position) + connect(volt_input.output, source.V) + connect(source.p, resistor.p) + connect(resistor.n, capacitor.p) + connect(capacitor.n, source.n, ground.g) + end + end + + # Build and solve the system + @mtkbuild sys = RC() + prob = ODEProblem(sys, [0.0, 0.0], (0.0, 10.0)) # No state variables initially + sol = solve(prob) + + # Perform Tests + resistor_resistance = sol[sys.resistor.R] + capacitor_voltage = sol[sys.capacitor.v] + + @test SciMLBase.successful_retcode(sol) # Ensure the simulation is successful + @test all(resistor_resistance .>= R_const) # Resistance should be >= constant value + @test maximum(resistor_resistance) ≤ R_const + R_ref # Maximum resistance when pos=1 (R_const + R_ref) + @test all(capacitor_voltage .>= 0.0) # Capacitor voltage should not be negative + + # For visual inspection + # plt = plot(sol; vars = [sys.resistor.R, sys.capacitor.v], + # size = (800, 600), dpi = 300, + # labels = ["Variable Resistor Resistance" "Capacitor Voltage"], + # title = "RC Circuit Test with VariableResistor") + # savefig(plt, "rc_circuit_test_variable_resistor") +end