From 3f4de0e2444d8777a69ce522c6d99474e8b29888 Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Tue, 28 Aug 2018 09:32:14 +0200 Subject: [PATCH 01/23] Bump Documenter version used on Travis to 0.19.6 --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 80b747c2..543d665e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,9 +29,9 @@ after_success: using Pkg # `using Pkg` and `pkg"..."` must be in separate if blocks end @static if VERSION >= v"0.7.0-DEV.3656" - pkg"add Documenter@0.19.0" + pkg"add Documenter@0.19.6" else - Pkg.add("Documenter", v"0.19.0", v"0.19.0+") + Pkg.add("Documenter", v"0.19.6", v"0.19.6+") cd(Pkg.dir("ACME")) end include(joinpath("docs", "make.jl"))' From 2e40b106ecee8e7a61bd8db36af297323846915b Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Tue, 28 Aug 2018 09:32:55 +0200 Subject: [PATCH 02/23] Depoly docs from Julia 1.0 --- docs/make.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/make.jl b/docs/make.jl index ae29d8db..7b552c4a 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -18,5 +18,5 @@ deploydocs( latest = "develop", deps = nothing, make = nothing, - julia = "0.6", + julia = "1.0", ) From 5eb39c677d631833762092799f4ead01ec4a538a Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Thu, 21 Feb 2019 10:51:20 +0100 Subject: [PATCH 03/23] Add a simple MOSFET model --- docs/src/elements.md | 1 + src/elements.jl | 54 ++++++++++++++++++++++++++++++++++++++++++-- test/runtests.jl | 16 ++++++++++++- 3 files changed, 68 insertions(+), 3 deletions(-) diff --git a/docs/src/elements.md b/docs/src/elements.md index afee49a7..116f45b6 100644 --- a/docs/src/elements.md +++ b/docs/src/elements.md @@ -26,6 +26,7 @@ currentprobe ```@docs diode bjt +mosfet ``` ## Integrated Circuits diff --git a/src/elements.jl b/src/elements.jl index d6deab88..308efa13 100644 --- a/src/elements.jl +++ b/src/elements.jl @@ -1,9 +1,9 @@ -# Copyright 2015, 2016, 2017, 2018 Martin Holters +# Copyright 2015, 2016, 2017, 2018, 2019 Martin Holters # See accompanying license file. export resistor, potentiometer, capacitor, inductor, transformer, voltagesource, currentsource, - voltageprobe, currentprobe, diode, bjt, opamp + voltageprobe, currentprobe, diode, bjt, mosfet, opamp """ @@ -437,6 +437,56 @@ Pins: `base`, `emitter`, `collector` pins = [:base; :emitter; :base; :collector]) end +@doc doc""" + mosfet(typ; vt=0.7, α=2e-5) + +Creates a MOSFET transistor with the simple model + +$i_D=\begin{cases} + 0 & \text{if } v_{GS} \le v_T \\ + \alpha \cdot (v_{GS} - v_T - \tfrac{1}{2}v_{DS})\cdot v_{DS} + & \text{if } v_{DS} \le v_{GS} - v_T \cap v_{GS} > v_T \\ + \frac{\alpha}{2} \cdot (v_{GS} - v_T)^2 & \text{otherwise.} +\end{cases}$ + +The `typ` parameter chooses between NMOS (`:n`) and PMOS (`:p`). The threshold +voltage `vt` is given in Volt, `α` (in A/V²) in a constant depending on the +physics and dimensions of the device. + +Pins: `gate`, `source`, `drain` +""" function mosfet(typ; vt=0.7, α=2e-5) + if typ == :n + polarity = 1 + elseif typ == :p + polarity = -1 + else + throw(ArgumentError("Unknown mosfet type $(typ), must be :n or :p")) + end + return Element(mv=[-1 0; 0 -1; 0 0; 0 0], + mi=[0 0; 0 0; 0 -1; 1 0], + mq=polarity*[1 0 0; 0 1 0; 0 0 1; 0 0 0], + u0=polarity*[-vt; 0; 0; 0], + pins=[:gate, :source, :drain, :source], + nonlinear_eq=quote + let vg=q[1], vds=q[2], id=q[3] # vg = vgs-vt + if vg <= 0 + res[1] = -id + J[1,1] = 0 + J[1,2] = 0 + elseif vds <= vg + res[1] = $α * (vg-0.5*vds)*vds - id + J[1,1] = $α*vds + J[1,2] = $α * (vg-vds) + else # 0 < vg < vds + res[1] = $(α/2) * vg^2 - id + J[1,1] = $α*vg + J[1,2] = 0 + end + J[1,3] = -1 + end + end) +end + """ opamp() diff --git a/test/runtests.jl b/test/runtests.jl index 2d0f5605..d206ee24 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,4 +1,4 @@ -# Copyright 2015, 2016, 2017, 2018 Martin Holters +# Copyright 2015, 2016, 2017, 2018, 2019 Martin Holters # See accompanying license file. include("checklic.jl") @@ -427,6 +427,20 @@ end end end +@testset "MOSFET" begin + for (typ, pol) in ((:n, 1), (:p, -1)) + circ = @circuit begin + vgs = voltagesource(), [-] == gnd + vds = voltagesource(), [-] == gnd + J = mosfet(typ, vt=1, α=1e-4), [gate] == vgs[+], [drain] == vds[+] + out = currentprobe(), [+] == J[source], [-] == gnd + end + model = DiscreteModel(circ, 1); + y = run!(model, pol*[0 1 2 2 2; 5 5 0.5 1 1.5]) + @test y == pol*[0 0 1e-4*(1-0.5/2)*0.5 1e-4*(1-1/2)*1 1e-4/2*1^2] + end +end + function checksteady!(model) x_steady = steadystate!(model) for s in model.solvers From 37128ecdc731a252db2a898062bf9d44d113f4f3 Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Fri, 22 Feb 2019 15:17:59 +0100 Subject: [PATCH 04/23] Simplify defintion of BJT non-linearity Instead of incrementally construction the expression, put the conditionals inside the expression (and let DCE do its work). --- src/elements.jl | 170 ++++++++++++++++++++---------------------------- 1 file changed, 72 insertions(+), 98 deletions(-) diff --git a/src/elements.jl b/src/elements.jl index 308efa13..3f1b8611 100644 --- a/src/elements.jl +++ b/src/elements.jl @@ -314,110 +314,84 @@ Pins: `base`, `emitter`, `collector` throw(ArgumentError(string("Unknown bjt type ", typ, ", must be :npn or :pnp"))) end - kernel = quote - i_f = $(βf/(1+βf)*ise) * (expE - 1) - i_r = $(βr/(1+βr)*isc) * (expC - 1) - di_f1 = $(βf/(1+βf)*ise/(25e-3*ηe)) * expE - di_r2 = $(βr/(1+βr)*isc/(25e-3*ηc)) * expC - end - if var == Inf && vaf == Inf && ikf == Inf && ikr == Inf - append!(kernel.args, (quote - i_cc = i_f-i_r - di_cc1 = di_f1 - di_cc2 = -di_r2 - end).args) - elseif (var ≠ Inf || vaf ≠ Inf) && ikf == Inf && ikr == Inf - append!(kernel.args, (quote - # inverse Early voltage factor - q₁⁻¹ = 1 - vE*$(1/var) - vC*$(1/vaf) - i_cc = q₁⁻¹ * (i_f-i_r) - # partial derivatives without high level injection effect - dq₁⁻¹1 = $(-1/var) - dq₁⁻¹2 = $(-1/vaf) - di_cc1 = dq₁⁻¹1*(i_f-i_r) + q₁⁻¹*di_f1 - di_cc2 = dq₁⁻¹2*(i_f-i_r) - q₁⁻¹*di_r2 - end).args) - elseif var == Inf && vaf == Inf && (ikf ≠ Inf || ikr ≠ Inf) - append!(kernel.args, (quote - # high level injection effect - q₂ = i_f*$(1/ikf) + i_r*$(1/ikr) - qden = 1+sqrt(1+4q₂) - qfact = 2/qden - i_cc = qfact * (i_f-i_r) - # partial derivatives without Early effect - dq₂1 = di_f1*$(1/ikf) - dq₂2 = di_r2*$(1/ikr) - dqfact1 = -4dq₂1/(qden-1) / (qden^2) - dqfact2 = -4dq₂2/(qden-1) / (qden^2) - di_cc1 = dqfact1*(i_f-i_r) + qfact*di_f1 - di_cc2 = dqfact2*(i_f-i_r) - qfact*di_r2 - end).args) - else - append!(kernel.args, (quote - # inverse Early voltage factor - q₁⁻¹ = 1 - vE*$(1/var) - vC*$(1/vaf) - # high level injection effect - q₂ = i_f*$(1/ikf) + i_r*$(1/ikr) - qden = 1+sqrt(1+4q₂) - qfact = 2q₁⁻¹/qden - i_cc = qfact * (i_f-i_r) - # partial derivatives with high level injection effect and Early effect - dq₁⁻¹1 = $(-1/var) - dq₁⁻¹2 = $(-1/vaf) - dq₂1 = di_f1*$(1/ikf) - dq₂2 = di_r2*$(1/ikr) - dqfact1 = (2dq₁⁻¹1*qden - q₁⁻¹*4dq₂1/(qden-1)) / (qden^2) - dqfact2 = (2dq₁⁻¹2*qden - q₁⁻¹*4dq₂2/(qden-1)) / (qden^2) - di_cc1 = dqfact1*(i_f-i_r) + qfact*di_f1 - di_cc2 = dqfact2*(i_f-i_r) - qfact*di_r2 - end).args) - end - - if ile ≠ 0 - if ηel ≠ ηe - append!(kernel.args, (quote - expEl = exp(vE*$(1/(25e-3*ηel))) - iBE = $(1/βf)*i_f + $ile*(expEl - 1) - diBE1 = $(1/βf)*di_f1 + $(ile/(25e-3*ηe))*expEl - end).args) - else - append!(kernel.args, (quote - iBE = $(1/βf)*i_f + $ile*(expE - 1) - diBE1 = $(1/βf)*di_f1 + $(ile/(25e-3*ηe))*expE - end).args) - end - else - append!(kernel.args, (quote - iBE = $(1/βf)*i_f - diBE1 = $(1/βf)*di_f1 - end).args) - end - if ilc ≠ 0 - if ηcl ≠ ηc - append!(kernel.args, (quote - expCl = exp(vC*$(1/(25e-3*ηcl))) - iBC = $(1/βr)*i_r + $ilc*(expCl - 1) - diBC2 = $(1/βr)*di_r2 + $(ilc/(25e-3*ηc))*expCl - end).args) - else - append!(kernel.args, (quote - iBC = $(1/βr)*i_r + $ilc*(expC - 1) - diBC2 = $(1/βr)*di_r2 + $(ilc/(25e-3*ηc))*expC - end).args) - end - else - append!(kernel.args, (quote - iBC = $(1/βr)*i_r - diBC2 = $(1/βr)*di_r2 - end).args) - end nonlinear_eq = quote let vE = q[1], vC = q[2], iE = q[3], iC = q[4], expE=exp(vE*$(1/(25e-3*ηe))), expC=exp(vC*$(1/(25e-3*ηc))) - $kernel + i_f = $(βf/(1+βf)*ise) * (expE - 1) + i_r = $(βr/(1+βr)*isc) * (expC - 1) + di_f1 = $(βf/(1+βf)*ise/(25e-3*ηe)) * expE + di_r2 = $(βr/(1+βr)*isc/(25e-3*ηc)) * expC + + if $(var == Inf && vaf == Inf && ikf == Inf && ikr == Inf) + i_cc = i_f-i_r + di_cc1 = di_f1 + di_cc2 = -di_r2 + elseif $((var ≠ Inf || vaf ≠ Inf) && ikf == Inf && ikr == Inf) + # inverse Early voltage factor + q₁⁻¹ = 1 - vE*$(1/var) - vC*$(1/vaf) + i_cc = q₁⁻¹ * (i_f-i_r) + # partial derivatives without high level injection effect + dq₁⁻¹1 = $(-1/var) + dq₁⁻¹2 = $(-1/vaf) + di_cc1 = dq₁⁻¹1*(i_f-i_r) + q₁⁻¹*di_f1 + di_cc2 = dq₁⁻¹2*(i_f-i_r) - q₁⁻¹*di_r2 + elseif $(var == Inf && vaf == Inf && (ikf ≠ Inf || ikr ≠ Inf)) + # high level injection effect + q₂ = i_f*$(1/ikf) + i_r*$(1/ikr) + qden = 1+sqrt(1+4q₂) + qfact = 2/qden + i_cc = qfact * (i_f-i_r) + # partial derivatives without Early effect + dq₂1 = di_f1*$(1/ikf) + dq₂2 = di_r2*$(1/ikr) + dqfact1 = -4dq₂1/(qden-1) / (qden^2) + dqfact2 = -4dq₂2/(qden-1) / (qden^2) + di_cc1 = dqfact1*(i_f-i_r) + qfact*di_f1 + di_cc2 = dqfact2*(i_f-i_r) - qfact*di_r2 + else + # inverse Early voltage factor + q₁⁻¹ = 1 - vE*$(1/var) - vC*$(1/vaf) + # high level injection effect + q₂ = i_f*$(1/ikf) + i_r*$(1/ikr) + qden = 1+sqrt(1+4q₂) + qfact = 2q₁⁻¹/qden + i_cc = qfact * (i_f-i_r) + # partial derivatives with high level injection effect and Early effect + dq₁⁻¹1 = $(-1/var) + dq₁⁻¹2 = $(-1/vaf) + dq₂1 = di_f1*$(1/ikf) + dq₂2 = di_r2*$(1/ikr) + dqfact1 = (2dq₁⁻¹1*qden - q₁⁻¹*4dq₂1/(qden-1)) / (qden^2) + dqfact2 = (2dq₁⁻¹2*qden - q₁⁻¹*4dq₂2/(qden-1)) / (qden^2) + di_cc1 = dqfact1*(i_f-i_r) + qfact*di_f1 + di_cc2 = dqfact2*(i_f-i_r) - qfact*di_r2 + end + + iBE = $(1/βf)*i_f + diBE1 = $(1/βf)*di_f1 + if $(ile ≠ 0) + if $(ηel ≠ ηe) + expEl = exp(vE*$(1/(25e-3*ηel))) + else + expEl = expE + end + iBE += $ile*(expEl - 1) + diBE1 += $(ile/(25e-3*ηe))*expEl + end + iBC = $(1/βr)*i_r + diBC2 = $(1/βr)*di_r2 + if $(ilc ≠ 0) + if $(ηcl ≠ ηc) + expCl = exp(vC*$(1/(25e-3*ηcl))) + else + expCl = expC + end + iBC += $ilc*(expCl - 1) + diBC2 += $(ilc/(25e-3*ηc))*expCl + end res[1] = i_cc + iBE - iE res[2] = -i_cc + iBC - iC From 2169e4929598179b556f5c094eaff8b608608ffb Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Thu, 4 Jan 2018 10:09:52 +0100 Subject: [PATCH 05/23] Replace a few `eval`s with `Base.invokelatest` --- src/ACME.jl | 55 +++++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/src/ACME.jl b/src/ACME.jl index 7847565c..b99f98e4 100644 --- a/src/ACME.jl +++ b/src/ACME.jl @@ -1,4 +1,4 @@ -# Copyright 2015, 2016, 2017, 2018 Martin Holters +# Copyright 2015, 2016, 2017, 2018, 2019 Martin Holters # See accompanying license file. VERSION < v"0.7.0-beta2.199" && __precompile__() @@ -257,12 +257,13 @@ function DiscreteModel(circ::Circuit, t::Real, ::Type{Solver}=HomotopySolver{Cac return nothing end for pexp in pexps] - solvers = ((eval(:($Solver(ParametricNonLinEq($nonlinear_eq_funcs[$idx], - $nonlinear_eq_set_ps[$idx], - $nonlinear_eq_calc_Jps[$idx], - (zeros($model_nqs[$idx]), zeros($model_nns[$idx], $model_nqs[$idx])), - $model_nns[$idx], $model_nps[$idx]), - zeros($model_nps[$idx]), $init_zs[$idx]))) + solvers = ((Base.invokelatest(Solver, + ParametricNonLinEq(nonlinear_eq_funcs[idx], + nonlinear_eq_set_ps[idx], + nonlinear_eq_calc_Jps[idx], + (zeros(model_nqs[idx]), zeros(model_nns[idx], model_nqs[idx])), + model_nns[idx], model_nps[idx]), + zeros(model_nps[idx]), init_zs[idx]) for idx in eachindex(model_nonlinear_eqs))...,) return DiscreteModel(mats, model_nonlinear_eqs, solvers) end @@ -438,20 +439,21 @@ function initial_solution(nleq, q0, fq) # determine an initial solution with a homotopy solver that may vary q0 # between 0 and the true q0 -> q0 takes the role of p nq, nn = size(fq) - return eval(quote - init_nl_eq_func = (res, J, scratch, z) -> + init_nl_eq_func = eval(quote + (res, J, scratch, z) -> let pfull=scratch[1], Jq=scratch[2], q=$(zeros(nq)), fq=$(fq) $(nleq) return nothing end - init_nleq = ParametricNonLinEq(init_nl_eq_func, $nn, $nq) - init_solver = HomotopySolver{SimpleSolver}(init_nleq, zeros($nq), zeros($nn)) - init_z = solve(init_solver, $q0) - if !hasconverged(init_solver) - error("Failed to find initial solution") - end - return init_z end) + init_nleq = ParametricNonLinEq(init_nl_eq_func, nn, nq) + init_solver = Base.invokelatest(HomotopySolver{SimpleSolver}, + init_nleq, zeros(nq), zeros(nn)) + init_z = Base.invokelatest(solve, init_solver, q0) + if !hasconverged(init_solver) + error("Failed to find initial solution") + end + return init_z end nx(model::DiscreteModel) = length(model.x0) @@ -474,23 +476,22 @@ function steadystate(model::DiscreteModel, u=zeros(nu(model))) zoff_last = zoff+nn(model,idx)-1 steady_q0 = model.q0s[idx] + model.pexps[idx]*((model.dqs[idx]/IA_LU*model.b + model.eqs[idx])*u + (model.dqs[idx]/IA_LU*model.c + model.fqprevs[idx])*steady_z) + model.pexps[idx]*model.dqs[idx]/IA_LU*model.x0 - steady_z[zoff:zoff_last] = eval(quote - steady_nl_eq_func = (res, J, scratch, z) -> + steady_nl_eq_func = eval(quote + (res, J, scratch, z) -> let pfull=scratch[1], Jq=scratch[2], q=$(zeros(nq(model, idx))), fq=$(model.pexps[idx]*model.dqs[idx]/IA_LU*model.c[:,zoff:zoff_last] + model.fqs[idx]) $(model.nonlinear_eqs[idx]) return nothing end - steady_nleq = ParametricNonLinEq(steady_nl_eq_func, nn($model, $idx), nq($model, $idx)) - steady_solver = HomotopySolver{SimpleSolver}(steady_nleq, zeros(nq($model, $idx)), - zeros(nn($model, $idx))) - set_resabstol!(steady_solver, 1e-15) - steady_z = solve(steady_solver, $steady_q0) - if !hasconverged(steady_solver) - error("Failed to find steady state solution") - end - return steady_z end) + steady_nleq = ParametricNonLinEq(steady_nl_eq_func, nn(model, idx), nq(model, idx)) + steady_solver = Base.invokelatest(HomotopySolver{SimpleSolver}, steady_nleq, zeros(nq(model, idx)), + zeros(nn(model, idx))) + set_resabstol!(steady_solver, 1e-15) + steady_z[zoff:zoff_last] = Base.invokelatest(solve, steady_solver, steady_q0) + if !hasconverged(steady_solver) + error("Failed to find steady state solution") + end zoff += nn(model,idx) end return IA_LU\(model.b*u + model.c*steady_z + model.x0) From e415019de965a910e105910450a29435aec41a09 Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Thu, 4 Jan 2018 16:11:39 +0100 Subject: [PATCH 06/23] Don't `eval` new function definition in `initial_solution` Instead, use the function defined for the model anyway. --- src/ACME.jl | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/ACME.jl b/src/ACME.jl index b99f98e4..a5077e78 100644 --- a/src/ACME.jl +++ b/src/ACME.jl @@ -196,10 +196,19 @@ function DiscreteModel(circ::Circuit, t::Real, ::Type{Solver}=HomotopySolver{Cac fqs = Matrix{Float64}.(mats[:fqs]) fqprev_fulls = Matrix{Float64}.(mats[:fqprev_fulls]) + nonlinear_eq_funcs = [eval(quote + (res, J, scratch, z) -> + let pfull=scratch[1], Jq=scratch[2], q=$(zeros(nq)), fq=$fq + $(nonlinear_eq) + return nothing + end + end) for (nonlinear_eq, fq, nq) in zip(model_nonlinear_eqs, fqs, model_nqs)] + init_zs = [zeros(nn) for nn in model_nns] for idx in eachindex(model_nonlinear_eqs) q = q0s[idx] + fqprev_fulls[idx] * vcat(init_zs...) - init_zs[idx] = initial_solution(model_nonlinear_eqs[idx], q, fqs[idx]) + init_zs[idx] = Base.invokelatest(initial_solution, + nonlinear_eq_funcs[idx], q, model_nns[idx]) end while any(np -> np == 0, model_nps) @@ -221,6 +230,7 @@ function DiscreteModel(circ::Circuit, t::Real, ::Type{Solver}=HomotopySolver{Cac deleteat!(model_nns, const_idxs) deleteat!(model_nqs, const_idxs) deleteat!(model_nonlinear_eqs, const_idxs) + deleteat!(nonlinear_eq_funcs, const_idxs) deleteat!(nl_elems, const_idxs) mats[:fy] = mats[:fy][:,varying_zidxs] mats[:c] = mats[:c][:,varying_zidxs] @@ -233,13 +243,6 @@ function DiscreteModel(circ::Circuit, t::Real, ::Type{Solver}=HomotopySolver{Cac fqprev_fulls = Array{Float64}.(mats[:fqprev_fulls]) pexps = Array{Float64}.(mats[:pexps]) - nonlinear_eq_funcs = [eval(quote - (res, J, scratch, z) -> - let pfull=scratch[1], Jq=scratch[2], q=$(zeros(nq)), fq=$fq - $(nonlinear_eq) - return nothing - end - end) for (nonlinear_eq, fq, nq) in zip(model_nonlinear_eqs, fqs, model_nqs)] nonlinear_eq_set_ps = [ function(scratch, p) pfull = scratch[1] @@ -435,21 +438,13 @@ function reduce_pdims!(mats::Dict) end end -function initial_solution(nleq, q0, fq) +function initial_solution(init_nl_eq_func::Function, q0, nn) # determine an initial solution with a homotopy solver that may vary q0 # between 0 and the true q0 -> q0 takes the role of p - nq, nn = size(fq) - init_nl_eq_func = eval(quote - (res, J, scratch, z) -> - let pfull=scratch[1], Jq=scratch[2], q=$(zeros(nq)), fq=$(fq) - $(nleq) - return nothing - end - end) + nq = length(q0) init_nleq = ParametricNonLinEq(init_nl_eq_func, nn, nq) - init_solver = Base.invokelatest(HomotopySolver{SimpleSolver}, - init_nleq, zeros(nq), zeros(nn)) - init_z = Base.invokelatest(solve, init_solver, q0) + init_solver = HomotopySolver{SimpleSolver}(init_nleq, zeros(nq), zeros(nn)) + init_z = solve(init_solver, q0) if !hasconverged(init_solver) error("Failed to find initial solution") end From bc6b8b85a849de4398a869f5c7d9fb68ee885c41 Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Fri, 22 Feb 2019 09:47:16 +0100 Subject: [PATCH 07/23] Don't use `eval` in `DiscreteModel`, only once for `Circuit` Use `eval` to synthesize a function for the circuit non-linearity, but don't work on expressions in the `DiscreteModel` contruction. --- src/ACME.jl | 59 +++++++++++++++++++++++--------------------------- src/circuit.jl | 32 +++++++++++++++++++-------- 2 files changed, 50 insertions(+), 41 deletions(-) diff --git a/src/ACME.jl b/src/ACME.jl index a5077e78..0823d9a7 100644 --- a/src/ACME.jl +++ b/src/ACME.jl @@ -132,12 +132,12 @@ mutable struct DiscreteModel{Solvers} fy::Matrix{Float64} y0::Vector{Float64} - nonlinear_eqs::Vector{Expr} + nonlinear_eq_funcs::Vector solvers::Solvers x::Vector{Float64} - function DiscreteModel(mats::Dict{Symbol}, nonlinear_eqs::Vector{Expr}, + function DiscreteModel(mats::Dict{Symbol}, nonlinear_eq_funcs::Vector, solvers::Solvers) where {Solvers} model = new{Solvers}() @@ -145,7 +145,7 @@ mutable struct DiscreteModel{Solvers} setfield!(model, mat, convert(fieldtype(typeof(model), mat), mats[mat])) end - model.nonlinear_eqs = nonlinear_eqs + model.nonlinear_eq_funcs = nonlinear_eq_funcs model.solvers = solvers model.x = zeros(nx(model)) return model @@ -176,17 +176,6 @@ function DiscreteModel(circ::Circuit, t::Real, ::Type{Solver}=HomotopySolver{Cac reduce_pdims!(mats) - model_nonlinear_eqs = [quote - #copyto!(q, pfull + fq * z) - copyto!(q, pfull) - BLAS.gemv!('N',1.,fq,z,1.,q) - let J=Jq - $(nonlinear_eq(circ, nles)) - end - #copyto!(J, Jq*model.fq) - BLAS.gemm!('N', 'N', 1., Jq, fq, 0., J) - end for nles in nl_elems] - model_nps = size.(mats[:dqs], 1) model_nqs = size.(mats[:pexps], 1) @@ -196,16 +185,26 @@ function DiscreteModel(circ::Circuit, t::Real, ::Type{Solver}=HomotopySolver{Cac fqs = Matrix{Float64}.(mats[:fqs]) fqprev_fulls = Matrix{Float64}.(mats[:fqprev_fulls]) - nonlinear_eq_funcs = [eval(quote - (res, J, scratch, z) -> - let pfull=scratch[1], Jq=scratch[2], q=$(zeros(nq)), fq=$fq - $(nonlinear_eq) + model_nonlinear_eq_funcs = [ + let q = zeros(nq), circ_nl_func = nonlinear_eq_func(circ, nles) + @inline function(res, J, pfull, Jq, fq, z) + #copyto!(q, pfull + fq * z) + copyto!(q, pfull) + BLAS.gemv!('N', 1., fq, z, 1., q) + circ_nl_func(res, Jq, q) + #copyto!(J, Jq*model.fq) + BLAS.gemm!('N', 'N', 1., Jq, fq, 0., J) return nothing end - end) for (nonlinear_eq, fq, nq) in zip(model_nonlinear_eqs, fqs, model_nqs)] + end for (nles, nq) in zip(nl_elems, model_nqs)] + + nonlinear_eq_funcs = [ + @inline function (res, J, scratch, z) + nleq(res, J, scratch[1], scratch[2], fq, z) + end for (nleq, fq) in zip(model_nonlinear_eq_funcs, fqs)] init_zs = [zeros(nn) for nn in model_nns] - for idx in eachindex(model_nonlinear_eqs) + for idx in eachindex(nonlinear_eq_funcs) q = q0s[idx] + fqprev_fulls[idx] * vcat(init_zs...) init_zs[idx] = Base.invokelatest(initial_solution, nonlinear_eq_funcs[idx], q, model_nns[idx]) @@ -229,7 +228,7 @@ function DiscreteModel(circ::Circuit, t::Real, ::Type{Solver}=HomotopySolver{Cac deleteat!(init_zs, const_idxs) deleteat!(model_nns, const_idxs) deleteat!(model_nqs, const_idxs) - deleteat!(model_nonlinear_eqs, const_idxs) + deleteat!(model_nonlinear_eq_funcs, const_idxs) deleteat!(nonlinear_eq_funcs, const_idxs) deleteat!(nl_elems, const_idxs) mats[:fy] = mats[:fy][:,varying_zidxs] @@ -267,8 +266,8 @@ function DiscreteModel(circ::Circuit, t::Real, ::Type{Solver}=HomotopySolver{Cac (zeros(model_nqs[idx]), zeros(model_nns[idx], model_nqs[idx])), model_nns[idx], model_nps[idx]), zeros(model_nps[idx]), init_zs[idx]) - for idx in eachindex(model_nonlinear_eqs))...,) - return DiscreteModel(mats, model_nonlinear_eqs, solvers) + for idx in eachindex(nonlinear_eq_funcs))...,) + return DiscreteModel(mats, model_nonlinear_eq_funcs, solvers) end function model_matrices(circ::Circuit, t::Rational{BigInt}) @@ -471,14 +470,10 @@ function steadystate(model::DiscreteModel, u=zeros(nu(model))) zoff_last = zoff+nn(model,idx)-1 steady_q0 = model.q0s[idx] + model.pexps[idx]*((model.dqs[idx]/IA_LU*model.b + model.eqs[idx])*u + (model.dqs[idx]/IA_LU*model.c + model.fqprevs[idx])*steady_z) + model.pexps[idx]*model.dqs[idx]/IA_LU*model.x0 - steady_nl_eq_func = eval(quote - (res, J, scratch, z) -> - let pfull=scratch[1], Jq=scratch[2], q=$(zeros(nq(model, idx))), - fq=$(model.pexps[idx]*model.dqs[idx]/IA_LU*model.c[:,zoff:zoff_last] + model.fqs[idx]) - $(model.nonlinear_eqs[idx]) - return nothing - end - end) + fq = model.pexps[idx]*model.dqs[idx]/IA_LU*model.c[:,zoff:zoff_last] + model.fqs[idx] + nleq = model.nonlinear_eq_funcs[idx] + steady_nl_eq_func = + (res, J, scratch, z) -> nleq(res, J, scratch[1], scratch[2], fq, z) steady_nleq = ParametricNonLinEq(steady_nl_eq_func, nn(model, idx), nq(model, idx)) steady_solver = Base.invokelatest(HomotopySolver{SimpleSolver}, steady_nleq, zeros(nq(model, idx)), zeros(nn(model, idx))) @@ -543,7 +538,7 @@ function linearize(model::DiscreteModel, usteady::AbstractVector{Float64}=zeros( :eqs => Matrix{Float64}[], :fqprevs => Matrix{Float64}[], :fqs => Matrix{Float64}[], :q0s => Vector{Float64}[], :dy => dy, :ey => ey, :fy => zeros(ny(model), 0), :x0 => x0, :y0 => y0) - return DiscreteModel(mats, Expr[], ()) + return DiscreteModel(mats, [], ()) end """ diff --git a/src/circuit.jl b/src/circuit.jl index 88b5fdc3..3b2b19d7 100644 --- a/src/circuit.jl +++ b/src/circuit.jl @@ -1,10 +1,24 @@ -# Copyright 2015, 2016, 2017, 2018 Martin Holters +# Copyright 2015, 2016, 2017, 2018, 2019 Martin Holters # See accompanying license file. export Circuit, add!, connect!, disconnect!, @circuit import Base: delete! +struct CircuitNLFunc{Fs} + fs::Fs +end + +@inline function (cnlf::CircuitNLFunc{Fs})(res, J, q) where Fs + _apply_all(res, J, q, cnlf.fs...) +end + +_apply_all(res, J, q) = nothing +@inline function _apply_all(res, J, q, f1::F, fs...) where F + f1(res, J, q) + _apply_all(res, J, q, fs...) +end + const Net = Vector{Tuple{Symbol,Symbol}} # pairs of element designator and pin name struct Circuit @@ -52,13 +66,10 @@ function incidence(c::Circuit) dropzeros!(sparse(i, j, v, length(c.nets), nb(c))) end -function nonlinear_eq(c::Circuit, elem_idxs=1:length(elements(c))) - # construct a block expression containing all element's expressions after - # offsetting their indexes into q, J and res - +function nonlinear_eq_func(c::Circuit, elem_idxs=1:length(elements(c))) row_offset = 0 col_offset = 0 - nl_expr = Expr(:block) + funcs = Function[] for elem in collect(elements(c))[elem_idxs] index_offsets = Dict( :q => (col_offset,), :J => (row_offset, col_offset), @@ -88,13 +99,16 @@ function nonlinear_eq(c::Circuit, elem_idxs=1:length(elements(c))) offset_indexes(x::Any) = x - # wrap into a let to keep variables local - push!(nl_expr.args, :( let; $(offset_indexes(elem.nonlinear_eq)) end)) + push!(funcs, eval(quote + @inline function (res, J, q) + $(offset_indexes(elem.nonlinear_eq)) + end + end)) row_offset += nn(elem) col_offset += nq(elem) end - nl_expr + return CircuitNLFunc((funcs...,)) end """ From 96a8139c143b1f8392f6b03be86efec0879d1e12 Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Fri, 22 Feb 2019 13:00:09 +0100 Subject: [PATCH 08/23] Don't use `eval` to include fixed index offsets in the nonlinear eq --- src/ACME.jl | 36 ++++++++++++++++++++++++++++++++++++ src/circuit.jl | 45 ++++++++++++++------------------------------- 2 files changed, 50 insertions(+), 31 deletions(-) diff --git a/src/ACME.jl b/src/ACME.jl index 0823d9a7..a7eb7293 100644 --- a/src/ACME.jl +++ b/src/ACME.jl @@ -112,6 +112,42 @@ function getindex(e::Element, p) return (e, e.pins[Symbol(p)]) end +function nonlinear_eq_func(e::Element) + index_offsets = Dict( :q => (:col_offset,), + :J => (:row_offset, :col_offset), + :res => (:row_offset,) ) + + function offset_indexes(expr::Expr) + ret = Expr(expr.head) + if expr.head == :ref && haskey(index_offsets, expr.args[1]) + push!(ret.args, expr.args[1]) + offsets = index_offsets[expr.args[1]] + length(expr.args) == length(offsets) + 1 || + throw(ArgumentError(string(expr.args[1], " must be indexed with exactly ", length(offsets), " index(es)"))) + for i in 1:length(offsets) + push!(ret.args, + :($(offsets[i]) + $(offset_indexes(expr.args[i+1])))) + end + else + append!(ret.args, offset_indexes.(expr.args)) + end + ret + end + + function offset_indexes(s::Symbol) + haskey(index_offsets, s) && throw(ArgumentError(string(s, " used without indexing expression"))) + s + end + + offset_indexes(x::Any) = x + + return eval(quote + @inline function (res, J, q, row_offset, col_offset) + $(offset_indexes(e.nonlinear_eq)) + end + end) +end + include("elements.jl") include("circuit.jl") diff --git a/src/circuit.jl b/src/circuit.jl index 3b2b19d7..f6939f36 100644 --- a/src/circuit.jl +++ b/src/circuit.jl @@ -71,40 +71,23 @@ function nonlinear_eq_func(c::Circuit, elem_idxs=1:length(elements(c))) col_offset = 0 funcs = Function[] for elem in collect(elements(c))[elem_idxs] - index_offsets = Dict( :q => (col_offset,), - :J => (row_offset, col_offset), - :res => (row_offset,) ) - - function offset_indexes(expr::Expr) - ret = Expr(expr.head) - if expr.head == :ref && haskey(index_offsets, expr.args[1]) - push!(ret.args, expr.args[1]) - offsets = index_offsets[expr.args[1]] - length(expr.args) == length(offsets) + 1 || - throw(ArgumentError(string(expr.args[1], " must be indexed with exactly ", length(offsets), " index(es)"))) - for i in 1:length(offsets) - push!(ret.args, - :($(offsets[i]) + $(offset_indexes(expr.args[i+1])))) + if VERSION >= v"0.7" + push!(funcs, + let row_offset=row_offset, col_offset=col_offset, + nleqfunc=nonlinear_eq_func(elem) + @inline function (res, J, q) + nleqfunc(res, J, q, row_offset, col_offset) + end + end) + else + # needed to avoid allocation (during model execution) on Julia 0.6 + push!(funcs, eval(quote + @inline function (res, J, q) + $(nonlinear_eq_func(elem))(res, J, q, $row_offset, $col_offset) end - else - append!(ret.args, offset_indexes.(expr.args)) - end - ret - end - - function offset_indexes(s::Symbol) - haskey(index_offsets, s) && throw(ArgumentError(string(s, " used without indexing expression"))) - s + end)) end - offset_indexes(x::Any) = x - - push!(funcs, eval(quote - @inline function (res, J, q) - $(offset_indexes(elem.nonlinear_eq)) - end - end)) - row_offset += nn(elem) col_offset += nq(elem) end From 7a47bce0848058dc863b585ee3bf136356a48736 Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Fri, 22 Feb 2019 14:31:35 +0100 Subject: [PATCH 09/23] Specify element nonlinearities using functions (instead of expressions) --- src/ACME.jl | 15 ++- src/elements.jl | 253 +++++++++++++++++++++++------------------------- 2 files changed, 130 insertions(+), 138 deletions(-) diff --git a/src/ACME.jl b/src/ACME.jl index a7eb7293..b7ccdef4 100644 --- a/src/ACME.jl +++ b/src/ACME.jl @@ -37,7 +37,7 @@ mutable struct Element px :: SparseMatrixCSC{Real,Int} pxd :: SparseMatrixCSC{Real,Int} pq :: SparseMatrixCSC{Real,Int} - nonlinear_eq :: Expr + nonlinear_eq pins :: Dict{Symbol, Vector{Tuple{Int, Int}}} function Element(;args...) @@ -78,6 +78,9 @@ mutable struct Element update_sizes(val, mat_dims[key]) elseif key == :pins val = make_pin_dict(val) + elseif key === :nonlinear_eq && val isa Expr + Base.depwarn("nonlinear_eq should be given as a function, not an expression", :Element) + val = @eval @wrap_nleq $val end setfield!(elem, key, val) end @@ -87,7 +90,7 @@ mutable struct Element end end if !isdefined(elem, :nonlinear_eq) - elem.nonlinear_eq = Expr(:block) + elem.nonlinear_eq = (res, J, q, row_offset, col_offset) -> nothing end if !isdefined(elem, :pins) elem.pins = make_pin_dict(1:2nb(elem)) @@ -112,7 +115,9 @@ function getindex(e::Element, p) return (e, e.pins[Symbol(p)]) end -function nonlinear_eq_func(e::Element) +nonlinear_eq_func(e::Element) = e.nonlinear_eq + +macro wrap_nleq(expr) index_offsets = Dict( :q => (:col_offset,), :J => (:row_offset, :col_offset), :res => (:row_offset,) ) @@ -141,9 +146,9 @@ function nonlinear_eq_func(e::Element) offset_indexes(x::Any) = x - return eval(quote + return esc(quote @inline function (res, J, q, row_offset, col_offset) - $(offset_indexes(e.nonlinear_eq)) + $(offset_indexes(expr)) end end) end diff --git a/src/elements.jl b/src/elements.jl index 3f1b8611..ef5df0f3 100644 --- a/src/elements.jl +++ b/src/elements.jl @@ -21,22 +21,21 @@ potentiometer(r) = Element(mv=[Matrix{Int}(I, 2, 2); zeros(3, 2)], mi=[zeros(2, 2); Matrix{Int}(I, 2, 2); zeros(1, 2)], mq=Matrix{Int}(-I, 5, 5), mu=[zeros(4, 1); -1], - nonlinear_eq = quote - let v1=q[1], v2=q[2], i1=q[3], i2=q[4], pos=q[5] - res[1] = v1 - $(r)*pos*i1 - res[2] = v2 - $(r)*(1-pos)*i2 + nonlinear_eq = + @wrap_nleq(let v1=q[1], v2=q[2], i1=q[3], i2=q[4], pos=q[5] + res[1] = v1 - r*pos*i1 + res[2] = v2 - r*(1-pos)*i2 J[1,1] = 1 J[1,2] = 0 - J[1,3] = $(-r)*pos + J[1,3] = -r*pos J[1,4] = 0 - J[1,5] = $(-r)*i1 + J[1,5] = -r*i1 J[2,1] = 0 J[2,2] = 1 J[2,3] = 0 - J[2,4] = $(-r)*(1-pos) - J[2,5] = $(-r)*i2 - end - end, + J[2,4] = -r*(1-pos) + J[2,5] = -r*i2 + end), pins=[1, 2, 2, 3]) """ @@ -108,7 +107,7 @@ on function transformer(::Type{Val{:JA}}; D=2.4e-2, A=4.54e-5, ns=[], a=14.1, α=5e-5, c=0.55, k=17.8, Ms=2.75e5) μ0 = 1.2566370614e-6 - nonlinear_eq = quote + nonlinear_eq = @wrap_nleq begin coth_q1 = coth(q[1]) a_q1 = abs(q[1]) L_q1 = a_q1 < 1e-4 ? q[1]/3 : coth_q1 - 1/q[1] @@ -116,19 +115,19 @@ function transformer(::Type{Val{:JA}}; D=2.4e-2, A=4.54e-5, ns=[], Ld2_q1 = a_q1 < 1e-3 ? -2/15*q[1] : 2*coth_q1*(coth_q1^2 - 1) - 2/q[1]^3 δ = q[3] > 0 ? 1.0 : -1.0 - Man = $(Ms)*L_q1 + Man = Ms*L_q1 δM = sign(q[3]) == sign(Man - q[2]) ? 1.0 : 0.0 - den = δ*$(k*(1-c))-$(α)*(Man-q[2]) + den = δ*(k*(1-c))-α*(Man-q[2]) # at present, the error needs to be scaled to be comparable to those of # the other elements, hence the factor 1e-4/Ms - res[1] = $(1e-4/Ms) * ($(1-c) * δM*(Man-q[2])/den * q[3] - + $(c*Ms/a)*(q[3]+$(α)*q[4])*Ld_q1 - q[4]) - J[1,1] = $(1e-4/Ms) * ($((1-c)^2*k*Ms) * δM*Ld_q1*δ/den^2 * q[3] - + $(c*Ms/a)*(q[3]+$(α)*q[4])*Ld2_q1) - J[1,2] = $(1e-4/Ms) * -$((1-c)^2*k) * δM*δ/den^2 * q[3] - J[1,3] = $(1e-4/Ms) * ($(1-c) * δM*(Man-q[2])/den + $(c*Ms/a)*Ld_q1) - J[1,4] = $(1e-4/Ms) * ($(c * Ms/a * α)*Ld_q1 - 1) + res[1] = (1e-4/Ms) * ((1-c) * δM*(Man-q[2])/den * q[3] + + (c*Ms/a)*(q[3]+α*q[4])*Ld_q1 - q[4]) + J[1,1] = (1e-4/Ms) * (((1-c)^2*k*Ms) * δM*Ld_q1*δ/den^2 * q[3] + + (c*Ms/a)*(q[3]+α*q[4])*Ld2_q1) + J[1,2] = (1e-4/Ms) * -(1-c)^2*k * δM*δ/den^2 * q[3] + J[1,3] = (1e-4/Ms) * ((1-c) * δM*(Man-q[2])/den + (c*Ms/a)*Ld_q1) + J[1,4] = (1e-4/Ms) * ((c * Ms/a * α)*Ld_q1 - 1) end Element(mv=[speye(length(ns)); spzeros(5, length(ns))], mi=[spzeros(length(ns), length(ns)); ns'; spzeros(4, length(ns))], @@ -233,13 +232,11 @@ coefficient `η` is unitless. Pins: `+` (anode) and `-` (cathode) """ diode(;is::Real=1e-12, η::Real = 1) = Element(mv=[1;0], mi=[0;1], mq=[-1 0; 0 -1], pins=[:+; :-], nonlinear_eq = - quote - let v = q[1], i = q[2], ex = exp(v*$(1 / (25e-3 * η))) - res[1] = $(is) * (ex - 1) - i - J[1,1] = $(is/(25e-3 * η)) * ex - J[1,2] = -1 - end - end + @wrap_nleq let v = q[1], i = q[2], ex = exp(v*(1 / (25e-3 * η))) + res[1] = is * (ex - 1) - i + J[1,1] = is/(25e-3 * η) * ex + J[1,2] = -1 + end ) @doc doc""" @@ -316,94 +313,88 @@ Pins: `base`, `emitter`, `collector` end nonlinear_eq = - quote - let vE = q[1], vC = q[2], iE = q[3], iC = q[4], - expE=exp(vE*$(1/(25e-3*ηe))), expC=exp(vC*$(1/(25e-3*ηc))) - - i_f = $(βf/(1+βf)*ise) * (expE - 1) - i_r = $(βr/(1+βr)*isc) * (expC - 1) - di_f1 = $(βf/(1+βf)*ise/(25e-3*ηe)) * expE - di_r2 = $(βr/(1+βr)*isc/(25e-3*ηc)) * expC - - if $(var == Inf && vaf == Inf && ikf == Inf && ikr == Inf) - i_cc = i_f-i_r - di_cc1 = di_f1 - di_cc2 = -di_r2 - elseif $((var ≠ Inf || vaf ≠ Inf) && ikf == Inf && ikr == Inf) - # inverse Early voltage factor - q₁⁻¹ = 1 - vE*$(1/var) - vC*$(1/vaf) - i_cc = q₁⁻¹ * (i_f-i_r) - # partial derivatives without high level injection effect - dq₁⁻¹1 = $(-1/var) - dq₁⁻¹2 = $(-1/vaf) - di_cc1 = dq₁⁻¹1*(i_f-i_r) + q₁⁻¹*di_f1 - di_cc2 = dq₁⁻¹2*(i_f-i_r) - q₁⁻¹*di_r2 - elseif $(var == Inf && vaf == Inf && (ikf ≠ Inf || ikr ≠ Inf)) - # high level injection effect - q₂ = i_f*$(1/ikf) + i_r*$(1/ikr) - qden = 1+sqrt(1+4q₂) - qfact = 2/qden - i_cc = qfact * (i_f-i_r) - # partial derivatives without Early effect - dq₂1 = di_f1*$(1/ikf) - dq₂2 = di_r2*$(1/ikr) - dqfact1 = -4dq₂1/(qden-1) / (qden^2) - dqfact2 = -4dq₂2/(qden-1) / (qden^2) - di_cc1 = dqfact1*(i_f-i_r) + qfact*di_f1 - di_cc2 = dqfact2*(i_f-i_r) - qfact*di_r2 + @wrap_nleq let vE = q[1], vC = q[2], iE = q[3], iC = q[4], + expE=exp(vE*(1/(25e-3*ηe))), expC=exp(vC*(1/(25e-3*ηc))) + i_f = (βf/(1+βf)*ise) * (expE - 1) + i_r = (βr/(1+βr)*isc) * (expC - 1) + di_f1 = (βf/(1+βf)*ise/(25e-3*ηe)) * expE + di_r2 = (βr/(1+βr)*isc/(25e-3*ηc)) * expC + if var == Inf && vaf == Inf && ikf == Inf && ikr == Inf + i_cc = i_f-i_r + di_cc1 = di_f1 + di_cc2 = -di_r2 + elseif (var ≠ Inf || vaf ≠ Inf) && ikf == Inf && ikr == Inf + # inverse Early voltage factor + q₁⁻¹ = 1 - vE*(1/var) - vC*(1/vaf) + i_cc = q₁⁻¹ * (i_f-i_r) + # partial derivatives without high level injection effect + dq₁⁻¹1 = (-1/var) + dq₁⁻¹2 = (-1/vaf) + di_cc1 = dq₁⁻¹1*(i_f-i_r) + q₁⁻¹*di_f1 + di_cc2 = dq₁⁻¹2*(i_f-i_r) - q₁⁻¹*di_r2 + elseif var == Inf && vaf == Inf && (ikf ≠ Inf || ikr ≠ Inf) + # high level injection effect + q₂ = i_f*(1/ikf) + i_r*(1/ikr) + qden = 1+sqrt(1+4q₂) + qfact = 2/qden + i_cc = qfact * (i_f-i_r) + # partial derivatives without Early effect + dq₂1 = di_f1*(1/ikf) + dq₂2 = di_r2*(1/ikr) + dqfact1 = -4dq₂1/(qden-1) / (qden^2) + dqfact2 = -4dq₂2/(qden-1) / (qden^2) + di_cc1 = dqfact1*(i_f-i_r) + qfact*di_f1 + di_cc2 = dqfact2*(i_f-i_r) - qfact*di_r2 + else + # inverse Early voltage factor + q₁⁻¹ = 1 - vE*(1/var) - vC*(1/vaf) + # high level injection effect + q₂ = i_f*(1/ikf) + i_r*(1/ikr) + qden = 1+sqrt(1+4q₂) + qfact = 2q₁⁻¹/qden + i_cc = qfact * (i_f-i_r) + # partial derivatives with high level injection effect and Early effect + dq₁⁻¹1 = -1/var + dq₁⁻¹2 = -1/vaf + dq₂1 = di_f1*(1/ikf) + dq₂2 = di_r2*(1/ikr) + dqfact1 = (2dq₁⁻¹1*qden - q₁⁻¹*4dq₂1/(qden-1)) / (qden^2) + dqfact2 = (2dq₁⁻¹2*qden - q₁⁻¹*4dq₂2/(qden-1)) / (qden^2) + di_cc1 = dqfact1*(i_f-i_r) + qfact*di_f1 + di_cc2 = dqfact2*(i_f-i_r) - qfact*di_r2 + end + iBE = (1/βf)*i_f + diBE1 = (1/βf)*di_f1 + if ile ≠ 0 + if ηel ≠ ηe + expEl = exp(vE*(1/(25e-3*ηel))) else - # inverse Early voltage factor - q₁⁻¹ = 1 - vE*$(1/var) - vC*$(1/vaf) - # high level injection effect - q₂ = i_f*$(1/ikf) + i_r*$(1/ikr) - qden = 1+sqrt(1+4q₂) - qfact = 2q₁⁻¹/qden - i_cc = qfact * (i_f-i_r) - # partial derivatives with high level injection effect and Early effect - dq₁⁻¹1 = $(-1/var) - dq₁⁻¹2 = $(-1/vaf) - dq₂1 = di_f1*$(1/ikf) - dq₂2 = di_r2*$(1/ikr) - dqfact1 = (2dq₁⁻¹1*qden - q₁⁻¹*4dq₂1/(qden-1)) / (qden^2) - dqfact2 = (2dq₁⁻¹2*qden - q₁⁻¹*4dq₂2/(qden-1)) / (qden^2) - di_cc1 = dqfact1*(i_f-i_r) + qfact*di_f1 - di_cc2 = dqfact2*(i_f-i_r) - qfact*di_r2 + expEl = expE end - - iBE = $(1/βf)*i_f - diBE1 = $(1/βf)*di_f1 - if $(ile ≠ 0) - if $(ηel ≠ ηe) - expEl = exp(vE*$(1/(25e-3*ηel))) - else - expEl = expE - end - iBE += $ile*(expEl - 1) - diBE1 += $(ile/(25e-3*ηe))*expEl - end - iBC = $(1/βr)*i_r - diBC2 = $(1/βr)*di_r2 - if $(ilc ≠ 0) - if $(ηcl ≠ ηc) - expCl = exp(vC*$(1/(25e-3*ηcl))) - else - expCl = expC - end - iBC += $ilc*(expCl - 1) - diBC2 += $(ilc/(25e-3*ηc))*expCl + iBE += ile*(expEl - 1) + diBE1 += (ile/(25e-3*ηe))*expEl + end + iBC = (1/βr)*i_r + diBC2 = (1/βr)*di_r2 + if ilc ≠ 0 + if ηcl ≠ ηc + expCl = exp(vC*(1/(25e-3*ηcl))) + else + expCl = expC end - - res[1] = i_cc + iBE - iE - res[2] = -i_cc + iBC - iC - J[1,1] = di_cc1 + diBE1 - J[1,2] = di_cc2 - J[1,3] = -1.0 - J[1,4] = 0.0 - J[2,1] = -di_cc1 - J[2,2] = -di_cc2 + diBC2 - J[2,3] = 0.0 - J[2,4] = -1.0 + iBC += ilc*(expCl - 1) + diBC2 += (ilc/(25e-3*ηc))*expCl end + res[1] = i_cc + iBE - iE + res[2] = -i_cc + iBC - iC + J[1,1] = di_cc1 + diBE1 + J[1,2] = di_cc2 + J[1,3] = -1.0 + J[1,4] = 0.0 + J[2,1] = -di_cc1 + J[2,2] = -di_cc2 + diBC2 + J[2,3] = 0.0 + J[2,4] = -1.0 end return Element(mv=[1 0; 0 1; 0 0; 0 0], mi = [-(re+rb) -rb; -rb -(rc+rb); 1 0; 0 1], @@ -441,23 +432,21 @@ Pins: `gate`, `source`, `drain` mq=polarity*[1 0 0; 0 1 0; 0 0 1; 0 0 0], u0=polarity*[-vt; 0; 0; 0], pins=[:gate, :source, :drain, :source], - nonlinear_eq=quote - let vg=q[1], vds=q[2], id=q[3] # vg = vgs-vt - if vg <= 0 - res[1] = -id - J[1,1] = 0 - J[1,2] = 0 - elseif vds <= vg - res[1] = $α * (vg-0.5*vds)*vds - id - J[1,1] = $α*vds - J[1,2] = $α * (vg-vds) - else # 0 < vg < vds - res[1] = $(α/2) * vg^2 - id - J[1,1] = $α*vg - J[1,2] = 0 - end - J[1,3] = -1 + nonlinear_eq=@wrap_nleq let vg=q[1], vds=q[2], id=q[3] # vg = vgs-vt + if vg <= 0 + res[1] = -id + J[1,1] = 0 + J[1,2] = 0 + elseif vds <= vg + res[1] = α * (vg-0.5*vds)*vds - id + J[1,1] = α*vds + J[1,2] = α * (vg-vds) + else # 0 < vg < vds + res[1] = (α/2) * vg^2 - id + J[1,1] = α*vg + J[1,2] = 0 end + J[1,3] = -1 end) end @@ -498,12 +487,10 @@ Pins: `in+` and `in-` for input, `out+` and `out-` for output offset = 0.5 * (vomin + vomax) scale = 0.5 * (vomax - vomin) nonlinear_eq = - quote - let vi = q[1], vo = q[2], vi_scaled = vi * $(gain/scale) - res[1] = tanh(vi_scaled) * $(scale) - vo - J[1,1] = $(gain) / cosh(vi_scaled)^2 - J[1,2] = -1 - end + @wrap_nleq let vi = q[1], vo = q[2], vi_scaled = vi * (gain/scale) + res[1] = tanh(vi_scaled) * scale - vo + J[1,1] = gain / cosh(vi_scaled)^2 + J[1,2] = -1 end return Element(mv=[0 0; 1 0; 0 1], mi=[1 0; 0 0; 0 0], mq=[0 0; -1 0; 0 -1], u0=[0; 0; offset], From 3d35b67fcc9f01eb8ea2ed118e0dddcf95f02ae0 Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Mon, 25 Feb 2019 10:51:39 +0100 Subject: [PATCH 10/23] Use `SArray`s in the element non-linear function interface --- REQUIRE | 1 + src/ACME.jl | 56 +++++++++++++++--------------- src/circuit.jl | 29 ++++++++++------ src/elements.jl | 92 ++++++++++++++++++++++--------------------------- 4 files changed, 87 insertions(+), 91 deletions(-) diff --git a/REQUIRE b/REQUIRE index be2675ae..33fa0430 100644 --- a/REQUIRE +++ b/REQUIRE @@ -3,3 +3,4 @@ Compat 1.1.0 DataStructures 0.2.9 IterTools 0.1.0 ProgressMeter 0.2.1 +StaticArrays 0.5.0 diff --git a/src/ACME.jl b/src/ACME.jl index b7ccdef4..6c6c91e4 100644 --- a/src/ACME.jl +++ b/src/ACME.jl @@ -15,6 +15,7 @@ import Compat.LinearAlgebra.BLAS using ProgressMeter using IterTools using DataStructures +using StaticArrays import Base.getindex @@ -78,9 +79,6 @@ mutable struct Element update_sizes(val, mat_dims[key]) elseif key == :pins val = make_pin_dict(val) - elseif key === :nonlinear_eq && val isa Expr - Base.depwarn("nonlinear_eq should be given as a function, not an expression", :Element) - val = @eval @wrap_nleq $val end setfield!(elem, key, val) end @@ -90,7 +88,11 @@ mutable struct Element end end if !isdefined(elem, :nonlinear_eq) - elem.nonlinear_eq = (res, J, q, row_offset, col_offset) -> nothing + elem.nonlinear_eq = (q) -> (SVector{0,Float64}(), SMatrix{0,0,Float64}()) + elseif elem.nonlinear_eq isa Expr + Base.depwarn("nonlinear_eq should be given as a function, not an expression", :Element) + nn = get(sizes, :nb, 0) + get(sizes, :nx, 0) + get(sizes, :nq, 0) - get(sizes, :nl, 0) + elem.nonlinear_eq = wrap_nleq_expr(nn, get(sizes, :nq, 0), elem.nonlinear_eq) end if !isdefined(elem, :pins) elem.pins = make_pin_dict(1:2nb(elem)) @@ -117,38 +119,34 @@ end nonlinear_eq_func(e::Element) = e.nonlinear_eq -macro wrap_nleq(expr) - index_offsets = Dict( :q => (:col_offset,), - :J => (:row_offset, :col_offset), - :res => (:row_offset,) ) - - function offset_indexes(expr::Expr) - ret = Expr(expr.head) - if expr.head == :ref && haskey(index_offsets, expr.args[1]) - push!(ret.args, expr.args[1]) - offsets = index_offsets[expr.args[1]] - length(expr.args) == length(offsets) + 1 || - throw(ArgumentError(string(expr.args[1], " must be indexed with exactly ", length(offsets), " index(es)"))) - for i in 1:length(offsets) - push!(ret.args, - :($(offsets[i]) + $(offset_indexes(expr.args[i+1])))) +function wrap_nleq_expr(nn, nq, expr) + res_symbs = [gensym("res_$n") for n in 1:nn] + J_symbs = [gensym("J_$(m)_$n") for m in 1:nn, n in 1:nq] + + function rewrite_refs(expr::Expr) + if expr.head == :ref + if expr.args[1] === :res + return res_symbs[expr.args[2]] + elseif expr.args[1] === :J + return J_symbs[expr.args[2], expr.args[3]] end + return expr else - append!(ret.args, offset_indexes.(expr.args)) + return Expr(expr.head, rewrite_refs.(expr.args)...) end - ret end - function offset_indexes(s::Symbol) - haskey(index_offsets, s) && throw(ArgumentError(string(s, " used without indexing expression"))) - s - end + rewrite_refs(x::Any) = x - offset_indexes(x::Any) = x + expr = rewrite_refs(expr) - return esc(quote - @inline function (res, J, q, row_offset, col_offset) - $(offset_indexes(expr)) + return eval(quote + @inline function (q) + $(nn > 0 || nq > 0 ? Expr(:local, res_symbs..., J_symbs...) : nothing) + $(expr) + res = SVector{$nn}($(res_symbs...)) + J = SMatrix{$nn,$nq}($(J_symbs...)) + return (res, J) end end) end diff --git a/src/circuit.jl b/src/circuit.jl index f6939f36..33ad2eee 100644 --- a/src/circuit.jl +++ b/src/circuit.jl @@ -9,16 +9,22 @@ struct CircuitNLFunc{Fs} fs::Fs end -@inline function (cnlf::CircuitNLFunc{Fs})(res, J, q) where Fs - _apply_all(res, J, q, cnlf.fs...) +@inline function (cnlf::CircuitNLFunc{Fs})(res´, J´, q) where Fs + (res, J) = _apply_all(q, cnlf.fs...) + res´ .= res + J´ .= J end -_apply_all(res, J, q) = nothing -@inline function _apply_all(res, J, q, f1::F, fs...) where F - f1(res, J, q) - _apply_all(res, J, q, fs...) +_apply_all(q) = (SVector{0,Float64}(), SMatrix{0,0,Float64}()) +@inline function _apply_all(q, f1::F, fs...) where F + (res1, J1) = f1(q) + (resrem, Jrem) = _apply_all(q, fs...) + return ([res1; resrem], dcat(J1, Jrem)) end +dcat(a::SMatrix{Ma,Na,Ta}, b::SMatrix{Mb,Nb,Tb}) where {Ma,Na,Mb,Nb,Ta,Tb} = + [[a zeros(SMatrix{Ma,Nb,Ta})]; [zeros(SMatrix{Mb,Na,Tb}) b]] + const Net = Vector{Tuple{Symbol,Symbol}} # pairs of element designator and pin name struct Circuit @@ -73,17 +79,18 @@ function nonlinear_eq_func(c::Circuit, elem_idxs=1:length(elements(c))) for elem in collect(elements(c))[elem_idxs] if VERSION >= v"0.7" push!(funcs, - let row_offset=row_offset, col_offset=col_offset, + let q_indices=SVector{nq(elem)}(col_offset+1:col_offset+nq(elem)), nleqfunc=nonlinear_eq_func(elem) - @inline function (res, J, q) - nleqfunc(res, J, q, row_offset, col_offset) + @inline function (q) + nleqfunc(q[q_indices]) end end) else # needed to avoid allocation (during model execution) on Julia 0.6 + q_indices=SVector{nq(elem)}(col_offset+1:col_offset+nq(elem)) push!(funcs, eval(quote - @inline function (res, J, q) - $(nonlinear_eq_func(elem))(res, J, q, $row_offset, $col_offset) + @inline function (q) + $(nonlinear_eq_func(elem))(q[$q_indices]) end end)) end diff --git a/src/elements.jl b/src/elements.jl index ef5df0f3..ea19e1bd 100644 --- a/src/elements.jl +++ b/src/elements.jl @@ -22,19 +22,11 @@ potentiometer(r) = mi=[zeros(2, 2); Matrix{Int}(I, 2, 2); zeros(1, 2)], mq=Matrix{Int}(-I, 5, 5), mu=[zeros(4, 1); -1], nonlinear_eq = - @wrap_nleq(let v1=q[1], v2=q[2], i1=q[3], i2=q[4], pos=q[5] - res[1] = v1 - r*pos*i1 - res[2] = v2 - r*(1-pos)*i2 - J[1,1] = 1 - J[1,2] = 0 - J[1,3] = -r*pos - J[1,4] = 0 - J[1,5] = -r*i1 - J[2,1] = 0 - J[2,2] = 1 - J[2,3] = 0 - J[2,4] = -r*(1-pos) - J[2,5] = -r*i2 + (@inline function (q) + v1, v2, i1, i2, pos=q + res = @SVector [v1 - r*pos*i1, v2 - r*(1-pos)*i2] + J = @SMatrix [1 0 -r*pos 0 -r*i1; 0 1 0 -r*(1-pos) -r*i2] + return (res, J) end), pins=[1, 2, 2, 3]) @@ -107,7 +99,7 @@ on function transformer(::Type{Val{:JA}}; D=2.4e-2, A=4.54e-5, ns=[], a=14.1, α=5e-5, c=0.55, k=17.8, Ms=2.75e5) μ0 = 1.2566370614e-6 - nonlinear_eq = @wrap_nleq begin + nonlinear_eq = @inline function (q) coth_q1 = coth(q[1]) a_q1 = abs(q[1]) L_q1 = a_q1 < 1e-4 ? q[1]/3 : coth_q1 - 1/q[1] @@ -121,13 +113,14 @@ function transformer(::Type{Val{:JA}}; D=2.4e-2, A=4.54e-5, ns=[], den = δ*(k*(1-c))-α*(Man-q[2]) # at present, the error needs to be scaled to be comparable to those of # the other elements, hence the factor 1e-4/Ms - res[1] = (1e-4/Ms) * ((1-c) * δM*(Man-q[2])/den * q[3] - + (c*Ms/a)*(q[3]+α*q[4])*Ld_q1 - q[4]) - J[1,1] = (1e-4/Ms) * (((1-c)^2*k*Ms) * δM*Ld_q1*δ/den^2 * q[3] + res = @SVector [(1e-4/Ms) * ((1-c) * δM*(Man-q[2])/den * q[3] + + (c*Ms/a)*(q[3]+α*q[4])*Ld_q1 - q[4])] + J_1_1 = (1e-4/Ms) * (((1-c)^2*k*Ms) * δM*Ld_q1*δ/den^2 * q[3] + (c*Ms/a)*(q[3]+α*q[4])*Ld2_q1) - J[1,2] = (1e-4/Ms) * -(1-c)^2*k * δM*δ/den^2 * q[3] - J[1,3] = (1e-4/Ms) * ((1-c) * δM*(Man-q[2])/den + (c*Ms/a)*Ld_q1) - J[1,4] = (1e-4/Ms) * ((c * Ms/a * α)*Ld_q1 - 1) + J_1_2 = (1e-4/Ms) * -(1-c)^2*k * δM*δ/den^2 * q[3] + J_1_3 = (1e-4/Ms) * ((1-c) * δM*(Man-q[2])/den + (c*Ms/a)*Ld_q1) + J_1_4 = (1e-4/Ms) * ((c * Ms/a * α)*Ld_q1 - 1) + return (res, @SMatrix [J_1_1 J_1_2; J_2_1 J_2_2]) end Element(mv=[speye(length(ns)); spzeros(5, length(ns))], mi=[spzeros(length(ns), length(ns)); ns'; spzeros(4, length(ns))], @@ -232,10 +225,12 @@ coefficient `η` is unitless. Pins: `+` (anode) and `-` (cathode) """ diode(;is::Real=1e-12, η::Real = 1) = Element(mv=[1;0], mi=[0;1], mq=[-1 0; 0 -1], pins=[:+; :-], nonlinear_eq = - @wrap_nleq let v = q[1], i = q[2], ex = exp(v*(1 / (25e-3 * η))) - res[1] = is * (ex - 1) - i - J[1,1] = is/(25e-3 * η) * ex - J[1,2] = -1 + @inline function(q) + v, i = q + ex = exp(v*(1 / (25e-3 * η))) + res = @SVector [is * (ex - 1) - i] + J = @SMatrix [is/(25e-3 * η) * ex -1] + return (res, J) end ) @@ -313,8 +308,10 @@ Pins: `base`, `emitter`, `collector` end nonlinear_eq = - @wrap_nleq let vE = q[1], vC = q[2], iE = q[3], iC = q[4], - expE=exp(vE*(1/(25e-3*ηe))), expC=exp(vC*(1/(25e-3*ηc))) + @inline function (q) + vE, vC, iE, iC = q + expE=exp(vE*(1/(25e-3*ηe))) + expC=exp(vC*(1/(25e-3*ηc))) i_f = (βf/(1+βf)*ise) * (expE - 1) i_r = (βr/(1+βr)*isc) * (expC - 1) di_f1 = (βf/(1+βf)*ise/(25e-3*ηe)) * expE @@ -385,16 +382,10 @@ Pins: `base`, `emitter`, `collector` iBC += ilc*(expCl - 1) diBC2 += (ilc/(25e-3*ηc))*expCl end - res[1] = i_cc + iBE - iE - res[2] = -i_cc + iBC - iC - J[1,1] = di_cc1 + diBE1 - J[1,2] = di_cc2 - J[1,3] = -1.0 - J[1,4] = 0.0 - J[2,1] = -di_cc1 - J[2,2] = -di_cc2 + diBC2 - J[2,3] = 0.0 - J[2,4] = -1.0 + res = @SVector [i_cc + iBE - iE, -i_cc + iBC - iC] + J = @SMatrix [di_cc1 + diBE1 di_cc2 -1.0 0.0; + -di_cc1 -di_cc2 + diBC2 0.0 -1.0] + return (res, J) end return Element(mv=[1 0; 0 1; 0 0; 0 0], mi = [-(re+rb) -rb; -rb -(rc+rb); 1 0; 0 1], @@ -432,21 +423,19 @@ Pins: `gate`, `source`, `drain` mq=polarity*[1 0 0; 0 1 0; 0 0 1; 0 0 0], u0=polarity*[-vt; 0; 0; 0], pins=[:gate, :source, :drain, :source], - nonlinear_eq=@wrap_nleq let vg=q[1], vds=q[2], id=q[3] # vg = vgs-vt + nonlinear_eq = @inline function (q) + vg, vds, id=q # vg = vgs-vt if vg <= 0 - res[1] = -id - J[1,1] = 0 - J[1,2] = 0 + res = @SVector [-id] + J = @SMatrix [0.0 0.0 -1.0] elseif vds <= vg - res[1] = α * (vg-0.5*vds)*vds - id - J[1,1] = α*vds - J[1,2] = α * (vg-vds) + res = @SVector [α * (vg-0.5*vds)*vds - id] + J = @SMatrix [α*vds α*(vg-vds) -1.0] else # 0 < vg < vds - res[1] = (α/2) * vg^2 - id - J[1,1] = α*vg - J[1,2] = 0 + res = @SVector [(α/2) * vg^2 - id] + J = @SMatrix [α*vg 0.0 -1.0] end - J[1,3] = -1 + return (res, J) end) end @@ -487,10 +476,11 @@ Pins: `in+` and `in-` for input, `out+` and `out-` for output offset = 0.5 * (vomin + vomax) scale = 0.5 * (vomax - vomin) nonlinear_eq = - @wrap_nleq let vi = q[1], vo = q[2], vi_scaled = vi * (gain/scale) - res[1] = tanh(vi_scaled) * scale - vo - J[1,1] = gain / cosh(vi_scaled)^2 - J[1,2] = -1 + @inline function (q) + vi, vo = q + vi_scaled = vi * (gain/scale) + res = @SVector [tanh(vi_scaled) * scale - vo] + J = @SMatrix [gain / cosh(vi_scaled)^2 -1.0] end return Element(mv=[0 0; 1 0; 0 1], mi=[1 0; 0 0; 0 0], mq=[0 0; -1 0; 0 -1], u0=[0; 0; offset], From 2c65009ac89bb7749e48a8604f05c9e8cf0f78da Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Wed, 27 Feb 2019 10:19:09 +0100 Subject: [PATCH 11/23] Let `Circuit`'s non-linear function return `res` a `J` ...instead of assigning into them given as arguments. --- src/ACME.jl | 4 +++- src/circuit.jl | 6 +----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/ACME.jl b/src/ACME.jl index 6c6c91e4..49f9468d 100644 --- a/src/ACME.jl +++ b/src/ACME.jl @@ -230,7 +230,9 @@ function DiscreteModel(circ::Circuit, t::Real, ::Type{Solver}=HomotopySolver{Cac #copyto!(q, pfull + fq * z) copyto!(q, pfull) BLAS.gemv!('N', 1., fq, z, 1., q) - circ_nl_func(res, Jq, q) + res´, Jq´ = circ_nl_func(q) + res .= res´ + Jq .= Jq´ #copyto!(J, Jq*model.fq) BLAS.gemm!('N', 'N', 1., Jq, fq, 0., J) return nothing diff --git a/src/circuit.jl b/src/circuit.jl index 33ad2eee..ac164887 100644 --- a/src/circuit.jl +++ b/src/circuit.jl @@ -9,11 +9,7 @@ struct CircuitNLFunc{Fs} fs::Fs end -@inline function (cnlf::CircuitNLFunc{Fs})(res´, J´, q) where Fs - (res, J) = _apply_all(q, cnlf.fs...) - res´ .= res - J´ .= J -end +@inline (cnlf::CircuitNLFunc{Fs})(q) where {Fs} = _apply_all(q, cnlf.fs...) _apply_all(q) = (SVector{0,Float64}(), SMatrix{0,0,Float64}()) @inline function _apply_all(q, f1::F, fs...) where F From dd5988b925ccb45814526f2c3daa407734eb96ca Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Wed, 27 Feb 2019 10:22:20 +0100 Subject: [PATCH 12/23] Omit linear elements' functions from `Circuit`s non-linear function Also deal with the pathological case of constant non-linear functions (i.e. `nn>0` but `nq==0`). --- src/circuit.jl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/circuit.jl b/src/circuit.jl index ac164887..29cf221e 100644 --- a/src/circuit.jl +++ b/src/circuit.jl @@ -73,9 +73,12 @@ function nonlinear_eq_func(c::Circuit, elem_idxs=1:length(elements(c))) col_offset = 0 funcs = Function[] for elem in collect(elements(c))[elem_idxs] + if nn(elem) == 0 && nq(elem) == 0 + continue + end if VERSION >= v"0.7" push!(funcs, - let q_indices=SVector{nq(elem)}(col_offset+1:col_offset+nq(elem)), + let q_indices=SVector{nq(elem),Int}(col_offset+1:col_offset+nq(elem)), nleqfunc=nonlinear_eq_func(elem) @inline function (q) nleqfunc(q[q_indices]) @@ -83,7 +86,7 @@ function nonlinear_eq_func(c::Circuit, elem_idxs=1:length(elements(c))) end) else # needed to avoid allocation (during model execution) on Julia 0.6 - q_indices=SVector{nq(elem)}(col_offset+1:col_offset+nq(elem)) + q_indices=SVector{nq(elem),Int}(col_offset+1:col_offset+nq(elem)) push!(funcs, eval(quote @inline function (q) $(nonlinear_eq_func(elem))(q[$q_indices]) From 488478069be4b27b5038ffcfc4dfdd956fad8469 Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Wed, 27 Feb 2019 12:11:26 +0100 Subject: [PATCH 13/23] Add `composite_element` --- src/circuit.jl | 86 +++++++++++++++++++++++++++++++++++++++++++- test/runtests.jl | 92 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 177 insertions(+), 1 deletion(-) diff --git a/src/circuit.jl b/src/circuit.jl index 29cf221e..09e0c80b 100644 --- a/src/circuit.jl +++ b/src/circuit.jl @@ -1,7 +1,7 @@ # Copyright 2015, 2016, 2017, 2018, 2019 Martin Holters # See accompanying license file. -export Circuit, add!, connect!, disconnect!, @circuit +export Circuit, add!, connect!, disconnect!, @circuit, composite_element import Base: delete! @@ -447,3 +447,87 @@ be put in quotes (e.g. `"in+"`, `"9V"`) push!(ccode.args, :(circ)) return ccode end + +@doc doc""" + composite_element(circ, pins) + +Create a circuit element from the (sub-)circuit `circ`. The `pins` are given as +a mapping from pin names of the element to be created to pins (or nets) in +`circ`. + +# Example + +The following will create an element for a 2.5V source, created using a 5V +source and a voltage divider, stabilized with a capacitor. + +```jldoctest; output = false, setup = :(using ACME), filter = r"(ACME\.)?Element\(.*"s +circ = @circuit(begin + r1 = resistor(10e3) + r2 = resistor(10e3), [1] == r1[2] + c = capacitor(1e-6), [1] == r2[1], [2] == r2[2] + src = voltagesource(5), [+] == r1[1], [-] == r2[2] +end + +composite_element(circ, [1 => (:r2, 1), 2 => (:r2, 2)]) + +# output + +Element(...) +``` + +Note that the pins still implicitly specifiy ports, so an even number must be +given, but the same pin may be given multiple times to be part of multiple +ports. +""" function composite_element(circ::Circuit, pins::Vector{<:Pair}) + if ny(circ) > 0 + throw(ArgumentError("creating composite elements from circuits with outputs is not supported")) + end + if isodd(length(pins)) + throw(ArgumentError("`length(pins)`=$(length(pins)), but must be even")) + end + numports = length(pins) ÷ 2 + # construct system matrix with norators for connection ports included + Mᵥ = blockdiag(mv(circ), spzeros(numports, numports)) + Mᵢ = blockdiag(mi(circ), spzeros(numports, numports)) + Mₓ = [mx(circ); spzeros(numports, nx(circ))] + Mₓ´ = [mxd(circ); spzeros(numports, nx(circ))] + Mq = [mq(circ); spzeros(numports, nq(circ))] + Mu = [mu(circ); spzeros(numports, nu(circ))] + u0 = [ACME.u0(circ); spzeros(numports)] + incid = [incidence(circ) spzeros(Int, length(circ.nets), numports)] + b = nb(circ)+1 + p = 1 + for pin_mapping in pins + net = netfor!(circ, pin_mapping[2]) + row = findfirst(==(net), circ.nets) + incid[row, b] = p + if p == 1 + p = -1 + else + p = 1 + b += 1 + end + end + tv, ti = topomat!(incid) + S = SparseMatrixCSC{Rational{BigInt}}([Mᵥ Mᵢ Mₓ Mₓ´ Mq; + blockdiag(tv, ti) spzeros(nb(circ)+numports, 2nx(circ)+nq(circ))]) + ũ, M = gensolve(S, SparseMatrixCSC{Rational{BigInt}}([Mu u0; spzeros(nb(circ)+numports, nu(circ)+1)])) + # [v' i' x' xd' q']' = ũ + My for arbitrary y + # now drop all rows concerning only internal voltages and currents + indices = vcat(consecranges([nb(circ), numports, nb(circ), numports+2nx(circ)+nq(circ)])[[2,4]]...) + ũ = ũ[indices,:] + M = M[indices,:] + S̃ = SparseMatrixCSC(gensolve(M', spzeros(size(M,2), 0))[2]') # output matrices p as RHS? + # S̃ spans nullspace of M', so that + # S̃*[v' i' x' xd' q']' = S̃*ũ + S̃*My = S̃*ũ + # i.e. S̃ acts as a new system matrix + M̃ᵥ, M̃ᵢ, M̃ₓ, M̃ₓ´, M̃q = + matsplit(S̃, [size(S̃,1)], [numports, numports, nx(circ), nx(circ), nq(circ)]) + M̃u = S̃*ũ[:, 1:nu(circ)] + ũ0 = S̃*ũ[:, end] + # note that we need to flip the sign of M̃ᵢ to view the ports from the other side + return Element(mv = M̃ᵥ, mi = -M̃ᵢ, mx = M̃ₓ, mxd = M̃ₓ´, mq = M̃q, + mu = M̃u, u0 = ũ0, + nonlinear_eq=nonlinear_eq_func(circ), + pins=[pin_mapping[1] for pin_mapping in pins]) +end diff --git a/test/runtests.jl b/test/runtests.jl index d206ee24..c3562054 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -274,6 +274,98 @@ end @test y[1] ≈ y[2] ≈ 1e-12*(exp(1/25e-3)-1) end +@testset "composite_element" begin + subcirc1() = composite_element(@circuit(begin + r1 = resistor(100e3) + r2 = resistor(1e3) + r1[2] == r2[1] + src = voltagesource(5), [+] == r1[1], [-] == r2[2] + end), [1 => (:r2, 1), 2 => (:r2, 2)]) + circ = @circuit begin + U = subcirc1() + J = voltageprobe(gp=2), [+] == U[1], [-] == U[2] + end + model = DiscreteModel(circ, 1//44100) + y = run!(model, zeros(0, 100)) + refcirc = @circuit begin + r1 = resistor(100e3) + r2 = resistor(1e3) + r1[2] == r2[1] + src = voltagesource(5), [+] == r1[1], [-] == r2[2] + J = voltageprobe(gp=2), [+] == r2[1], [-] == r2[2] + end + refmodel = DiscreteModel(refcirc, 1//44100) + yref = run!(refmodel, zeros(0, 100)) + @test y ≈ yref + + subcirc2() = composite_element(@circuit(begin + r1 = resistor(100e3) + r2 = resistor(1e3) + r1[2] == r2[1] + src = voltagesource(), [+] == r1[1], [-] == r2[2] + end), [1 => (:r2, 1), 2 => (:r2, 2)]) + circ = @circuit begin + U = subcirc2() + J = voltageprobe(gp=2), [+] == U[1], [-] == U[2] + end + model = DiscreteModel(circ, 1//44100) + y = run!(model, 5*ones(1, 100)) + yref = run!(refmodel, zeros(0, 100)) + @test y ≈ yref + + subcirc3() = composite_element(@circuit(begin + r1 = resistor(100e3) + r2 = resistor(1e3) + c = capacitor(1e-6), [1] == r2[1], [2] == r2[2] + r1[2] == r2[1] + src = voltagesource(5), [+] == r1[1], [-] == r2[2] + end), [1 => (:r2, 1), 2 => (:r2, 2)]) + circ = @circuit begin + U = subcirc3() + J = voltageprobe(gp=2), [+] == U[1], [-] == U[2] + end + model = DiscreteModel(circ, 1//44100) + y = run!(model, zeros(0, 100)) + refcirc = @circuit begin + r1 = resistor(100e3) + r2 = resistor(1e3) + c = capacitor(1e-6), [1] == r2[1], [2] == r2[2] + r1[2] == r2[1] + src = voltagesource(5), [+] == r1[1], [-] == r2[2] + J = voltageprobe(gp=2), [+] == r2[1], [-] == r2[2] + end + refmodel = DiscreteModel(refcirc, 1//44100) + yref = run!(refmodel, zeros(0, 100)) + @test y ≈ yref + + subcirc4() = composite_element(@circuit(begin + r1 = resistor(100e3) + r2 = resistor(1e3) + c = capacitor(1e-6), [1] == r2[1], [2] == r2[2] + d = diode(), [+] == r2[1], [-] == r2[2] + r1[2] == r2[1] + src = voltagesource(5), [+] == r1[1], [-] == r2[2] + end), [1 => (:r2, 1), 2 => (:r2, 2)]) + circ = @circuit begin + U = subcirc4() + J = voltageprobe(gp=2), [+] == U[1], [-] == U[2] + end + model = DiscreteModel(circ, 1//44100) + y = run!(model, zeros(0, 100)) + refcirc = @circuit begin + r1 = resistor(100e3) + r2 = resistor(1e3) + c = capacitor(1e-6), [1] == r2[1], [2] == r2[2] + d = diode(), [+] == r2[1], [-] == r2[2] + r1[2] == r2[1] + src = voltagesource(5), [+] == r1[1], [-] == r2[2] + J = voltageprobe(gp=2), [+] == r2[1], [-] == r2[2] + end + refmodel = DiscreteModel(refcirc, 1//44100) + yref = run!(refmodel, zeros(0, 100)) + @test y ≈ yref +end + @testset "sources/probes" begin # sources and probes with internal resistance/conductance circ = @circuit begin From e67697fac172fe04ab5b7846dc2180a4d1f186d7 Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Mon, 4 Mar 2019 09:02:37 +0100 Subject: [PATCH 14/23] Define `composite_element` with seperated `pinmap` and `ports` --- src/circuit.jl | 51 +++++++++++++++++++++++++++--------------------- test/runtests.jl | 8 ++++---- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/src/circuit.jl b/src/circuit.jl index 09e0c80b..71c73752 100644 --- a/src/circuit.jl +++ b/src/circuit.jl @@ -449,11 +449,13 @@ be put in quotes (e.g. `"in+"`, `"9V"`) end @doc doc""" - composite_element(circ, pins) + composite_element(circ; pinmap=Dict(), ports) -Create a circuit element from the (sub-)circuit `circ`. The `pins` are given as -a mapping from pin names of the element to be created to pins (or nets) in -`circ`. +Create a circuit element from the (sub-)circuit `circ`. The `pinmap` defines +the mapping from pin names of the element to be created to pins (or nets) in +`circ`. Optionally, `ports` can be used to explicitly specify (as a list `Pair`s +of pins) the ports to use. By default, a port will be created between one +arbitrarily chosen pin and every other pin. # Example @@ -468,7 +470,7 @@ circ = @circuit(begin src = voltagesource(5), [+] == r1[1], [-] == r2[2] end -composite_element(circ, [1 => (:r2, 1), 2 => (:r2, 2)]) +composite_element(circ, pinmap=Dict(1 => (:r2, 1), 2 => (:r2, 2))) # output @@ -478,14 +480,11 @@ Element(...) Note that the pins still implicitly specifiy ports, so an even number must be given, but the same pin may be given multiple times to be part of multiple ports. -""" function composite_element(circ::Circuit, pins::Vector{<:Pair}) +""" function composite_element(circ::Circuit; pinmap=Dict(), ports=ports_from_pinmap(pinmap)) if ny(circ) > 0 throw(ArgumentError("creating composite elements from circuits with outputs is not supported")) end - if isodd(length(pins)) - throw(ArgumentError("`length(pins)`=$(length(pins)), but must be even")) - end - numports = length(pins) ÷ 2 + numports = length(ports) # construct system matrix with norators for connection ports included Mᵥ = blockdiag(mv(circ), spzeros(numports, numports)) Mᵢ = blockdiag(mi(circ), spzeros(numports, numports)) @@ -495,19 +494,17 @@ ports. Mu = [mu(circ); spzeros(numports, nu(circ))] u0 = [ACME.u0(circ); spzeros(numports)] incid = [incidence(circ) spzeros(Int, length(circ.nets), numports)] - b = nb(circ)+1 - p = 1 - for pin_mapping in pins - net = netfor!(circ, pin_mapping[2]) + for i in eachindex(ports) + port = ports[i] + net = netfor!(circ, pinmap[port[1]]) row = findfirst(==(net), circ.nets) - incid[row, b] = p - if p == 1 - p = -1 - else - p = 1 - b += 1 - end + b = nb(circ)+i + incid[row, b] = 1 + net = netfor!(circ, pinmap[port[2]]) + row = findfirst(==(net), circ.nets) + incid[row, b] = -1 end + tv, ti = topomat!(incid) S = SparseMatrixCSC{Rational{BigInt}}([Mᵥ Mᵢ Mₓ Mₓ´ Mq; blockdiag(tv, ti) spzeros(nb(circ)+numports, 2nx(circ)+nq(circ))]) @@ -529,5 +526,15 @@ ports. return Element(mv = M̃ᵥ, mi = -M̃ᵢ, mx = M̃ₓ, mxd = M̃ₓ´, mq = M̃q, mu = M̃u, u0 = ũ0, nonlinear_eq=nonlinear_eq_func(circ), - pins=[pin_mapping[1] for pin_mapping in pins]) + pins=vcat(collect.(ports)...)) +end + +function ports_from_pinmap(pinmap) + ks = keys(pinmap) + refpin = first(ks) + ks = Iterators.drop(ks, 1) + return [refpin => pin for pin in ks] end + +Base.@deprecate(composite_element(circ::Circuit, pins::Vector{<:Pair}), + composite_element(circ, pinmap=Dict(pins...))) diff --git a/test/runtests.jl b/test/runtests.jl index c3562054..cef9f023 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -280,7 +280,7 @@ end r2 = resistor(1e3) r1[2] == r2[1] src = voltagesource(5), [+] == r1[1], [-] == r2[2] - end), [1 => (:r2, 1), 2 => (:r2, 2)]) + end), pinmap=Dict(1 => (:r2, 1), 2 => (:r2, 2))) circ = @circuit begin U = subcirc1() J = voltageprobe(gp=2), [+] == U[1], [-] == U[2] @@ -303,7 +303,7 @@ end r2 = resistor(1e3) r1[2] == r2[1] src = voltagesource(), [+] == r1[1], [-] == r2[2] - end), [1 => (:r2, 1), 2 => (:r2, 2)]) + end), pinmap=Dict(1 => (:r2, 1), 2 => (:r2, 2))) circ = @circuit begin U = subcirc2() J = voltageprobe(gp=2), [+] == U[1], [-] == U[2] @@ -319,7 +319,7 @@ end c = capacitor(1e-6), [1] == r2[1], [2] == r2[2] r1[2] == r2[1] src = voltagesource(5), [+] == r1[1], [-] == r2[2] - end), [1 => (:r2, 1), 2 => (:r2, 2)]) + end), pinmap=Dict(1 => (:r2, 1), 2 => (:r2, 2))) circ = @circuit begin U = subcirc3() J = voltageprobe(gp=2), [+] == U[1], [-] == U[2] @@ -345,7 +345,7 @@ end d = diode(), [+] == r2[1], [-] == r2[2] r1[2] == r2[1] src = voltagesource(5), [+] == r1[1], [-] == r2[2] - end), [1 => (:r2, 1), 2 => (:r2, 2)]) + end), pinmap=Dict(1 => (:r2, 1), 2 => (:r2, 2))) circ = @circuit begin U = subcirc4() J = voltageprobe(gp=2), [+] == U[1], [-] == U[2] From a63973bd4351ad67c13529d55924616bd787a346 Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Mon, 4 Mar 2019 09:26:37 +0100 Subject: [PATCH 15/23] Drop old deprecations --- src/ACME.jl | 17 ----------------- src/circuit.jl | 30 +----------------------------- src/elements.jl | 3 --- src/solvers.jl | 9 +-------- 4 files changed, 2 insertions(+), 57 deletions(-) diff --git a/src/ACME.jl b/src/ACME.jl index 49f9468d..828b82ce 100644 --- a/src/ACME.jl +++ b/src/ACME.jl @@ -17,8 +17,6 @@ using IterTools using DataStructures using StaticArrays -import Base.getindex - include("compat.jl") include("kdtree.jl") @@ -108,15 +106,6 @@ nl(e::Element) = size(e.mv, 1) ny(e::Element) = size(e.pv, 1) nn(e::Element) = nb(e) + nx(e) + nq(e) - nl(e) -# a Pin combines an element with a branch/polarity list -const Pin = Tuple{Element, Vector{Tuple{Int,Int}}} - -# allow elem[:pin] notation to get an elements pin -function getindex(e::Element, p) - Base.depwarn("element[$(repr(p))] is deprecated. When connecting pins, use (designator, $(repr(p))) instead", :getindex) - return (e, e.pins[Symbol(p)]) -end - nonlinear_eq_func(e::Element) = e.nonlinear_eq function wrap_nleq_expr(nn, nq, expr) @@ -191,12 +180,6 @@ mutable struct DiscreteModel{Solvers} end end -function DiscreteModel{Solver}(circ::Circuit, t::Float64) where {Solver} - Base.depwarn("DiscreteModel{Solver}(circ, t) is deprecated, use DiscreteModel(circ, t, Solver) instead.", - :DiscreteModel) - DiscreteModel(circ, t, Solver) -end - function DiscreteModel(circ::Circuit, t::Real, ::Type{Solver}=HomotopySolver{CachingSolver{SimpleSolver}}; decompose_nonlinearity=true) where {Solver} mats = model_matrices(circ, t) diff --git a/src/circuit.jl b/src/circuit.jl index 71c73752..1ceaa1da 100644 --- a/src/circuit.jl +++ b/src/circuit.jl @@ -166,20 +166,6 @@ function netfor!(c::Circuit, p::Tuple{Symbol,Symbol}) end netfor!(c::Circuit, p::Tuple{Symbol,Any}) = netfor!(c, (p[1], Symbol(p[2]))) -function netfor!(c::Circuit, p::Pin) - Base.depwarn("pin specification $p is deprecated, use (refdes, pinname) instead", :netfor!) - element = p[1] - designator = add!(c, element) - local pinname - for (pname, pbps) in element.pins - if pbps == p[2] - pinname = pname - break - end - end - return netfor!(c, (designator, pinname)) -end - function netfor!(c::Circuit, name::Symbol) haskey(c.net_names, name) || push!(c.nets, get!(c.net_names, name, [])) c.net_names[name] @@ -206,7 +192,7 @@ connect!(circ, (:src, -), (:r, 2), :gnd) # connect to gnd net ``` """ -function connect!(c::Circuit, pins::Union{Pin,Symbol,Tuple{Symbol,Any}}...) +function connect!(c::Circuit, pins::Union{Symbol,Tuple{Symbol,Any}}...) nets = unique([netfor!(c, pin) for pin in pins]) for net in nets[2:end] append!(nets[1], net) @@ -237,20 +223,6 @@ that if e.g. three pin `p1`, `p2`, and `p3` are connected then """ disconnect!(c::Circuit, p::Tuple{Symbol,Any}) = disconnect!(c, (p[1], Symbol(p[2]))) -function disconnect!(c::Circuit, pin::Pin) - Base.depwarn("disconnect!(::Circuit, $p) is deprecated, use (refdes, pinname) to specify the the pin", :disconnect!) - element = pin[1] - designator = add!(c, element) - local pinname - for (pname, pbps) in element.pins - if pbps == pin[2] - pinname = pname - break - end - end - disconnect!(c, (designator, pinname)) -end - function topomat!(incidence::SparseMatrixCSC{T}) where {T<:Integer} @assert all(x -> abs(x) == 1, nonzeros(incidence)) @static if VERSION ≥ v"0.7.0-DEV.4064" diff --git a/src/elements.jl b/src/elements.jl index ea19e1bd..7e865ff4 100644 --- a/src/elements.jl +++ b/src/elements.jl @@ -487,6 +487,3 @@ Pins: `in+` and `in-` for input, `out+` and `out-` for output nonlinear_eq = nonlinear_eq, pins=["in+", "in-", "out+", "out-"]) end - -@Base.deprecate(opamp_ideal, opamp) -@Base.deprecate(opamp_macak(gain, vomin, vomax), opamp(Val{:macak}, gain, vomin, vomax)) diff --git a/src/solvers.jl b/src/solvers.jl index da327a80..8082908b 100644 --- a/src/solvers.jl +++ b/src/solvers.jl @@ -1,4 +1,4 @@ -# Copyright 2016, 2017, 2018 Martin Holters +# Copyright 2016, 2017, 2018, 2019 Martin Holters # See accompanying license file. export SimpleSolver, HomotopySolver, CachingSolver @@ -403,13 +403,6 @@ set_extrapolation_origin(solver::CachingSolver, p, z) = get_extrapolation_jacobian(solver::CachingSolver) = get_extrapolation_jacobian(solver.basesolver) - -function set_resabs2tol!(solver, tol) - Base.depwarn(string("set_resabs2tol!(solver, tol) is deprecated, use set_resabstol!(solver, sqrt(tol)) instead."), - :set_resabs2tol!) - set_resabstol!(solver, sqrt(tol)) -end - function linearize(solver, p::AbstractVector{Float64}) z = solve(solver, p) set_extrapolation_origin(solver, p, z) From 46aba98e271dcfffcb55ddc5adf9a7aa31c322f9 Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Mon, 4 Mar 2019 09:44:14 +0100 Subject: [PATCH 16/23] Move deprecations to dedicated file --- src/ACME.jl | 35 ++--------------------------------- src/circuit.jl | 3 --- src/deprecated.jl | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 36 deletions(-) create mode 100644 src/deprecated.jl diff --git a/src/ACME.jl b/src/ACME.jl index 828b82ce..c7832c89 100644 --- a/src/ACME.jl +++ b/src/ACME.jl @@ -88,7 +88,6 @@ mutable struct Element if !isdefined(elem, :nonlinear_eq) elem.nonlinear_eq = (q) -> (SVector{0,Float64}(), SMatrix{0,0,Float64}()) elseif elem.nonlinear_eq isa Expr - Base.depwarn("nonlinear_eq should be given as a function, not an expression", :Element) nn = get(sizes, :nb, 0) + get(sizes, :nx, 0) + get(sizes, :nq, 0) - get(sizes, :nl, 0) elem.nonlinear_eq = wrap_nleq_expr(nn, get(sizes, :nq, 0), elem.nonlinear_eq) end @@ -108,38 +107,6 @@ nn(e::Element) = nb(e) + nx(e) + nq(e) - nl(e) nonlinear_eq_func(e::Element) = e.nonlinear_eq -function wrap_nleq_expr(nn, nq, expr) - res_symbs = [gensym("res_$n") for n in 1:nn] - J_symbs = [gensym("J_$(m)_$n") for m in 1:nn, n in 1:nq] - - function rewrite_refs(expr::Expr) - if expr.head == :ref - if expr.args[1] === :res - return res_symbs[expr.args[2]] - elseif expr.args[1] === :J - return J_symbs[expr.args[2], expr.args[3]] - end - return expr - else - return Expr(expr.head, rewrite_refs.(expr.args)...) - end - end - - rewrite_refs(x::Any) = x - - expr = rewrite_refs(expr) - - return eval(quote - @inline function (q) - $(nn > 0 || nq > 0 ? Expr(:local, res_symbs..., J_symbs...) : nothing) - $(expr) - res = SVector{$nn}($(res_symbs...)) - J = SMatrix{$nn,$nq}($(J_symbs...)) - return (res, J) - end - end) -end - include("elements.jl") include("circuit.jl") @@ -779,4 +746,6 @@ matsplit(v::AbstractVector, rowsizes) = [v[rs] for rs in consecranges(rowsizes)] matsplit(m::AbstractMatrix, rowsizes, colsizes=[size(m,2)]) = [m[rs, cs] for rs in consecranges(rowsizes), cs in consecranges(colsizes)] +include("deprecated.jl") + end # module diff --git a/src/circuit.jl b/src/circuit.jl index 1ceaa1da..812fbfc1 100644 --- a/src/circuit.jl +++ b/src/circuit.jl @@ -507,6 +507,3 @@ function ports_from_pinmap(pinmap) ks = Iterators.drop(ks, 1) return [refpin => pin for pin in ks] end - -Base.@deprecate(composite_element(circ::Circuit, pins::Vector{<:Pair}), - composite_element(circ, pinmap=Dict(pins...))) diff --git a/src/deprecated.jl b/src/deprecated.jl new file mode 100644 index 00000000..dce6d8e2 --- /dev/null +++ b/src/deprecated.jl @@ -0,0 +1,39 @@ +# Copyright 2019 Martin Holters +# See accompanying license file. + +function wrap_nleq_expr(nn, nq, expr) + Base.depwarn("nonlinear_eq should be given as a function, not an expression", :Element) + + res_symbs = [gensym("res_$n") for n in 1:nn] + J_symbs = [gensym("J_$(m)_$n") for m in 1:nn, n in 1:nq] + + function rewrite_refs(expr::Expr) + if expr.head == :ref + if expr.args[1] === :res + return res_symbs[expr.args[2]] + elseif expr.args[1] === :J + return J_symbs[expr.args[2], expr.args[3]] + end + return expr + else + return Expr(expr.head, rewrite_refs.(expr.args)...) + end + end + + rewrite_refs(x::Any) = x + + expr = rewrite_refs(expr) + + return eval(quote + @inline function (q) + $(nn > 0 || nq > 0 ? Expr(:local, res_symbs..., J_symbs...) : nothing) + $(expr) + res = SVector{$nn}($(res_symbs...)) + J = SMatrix{$nn,$nq}($(J_symbs...)) + return (res, J) + end + end) +end + +Base.@deprecate(composite_element(circ::Circuit, pins::Vector{<:Pair}), + composite_element(circ, pinmap=Dict(pins...))) From 2f29d01a6f8b1c2452b99f357f778af27787a9f6 Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Mon, 4 Mar 2019 11:05:03 +0100 Subject: [PATCH 17/23] Use ports instead of pins for `Element` definition --- src/ACME.jl | 29 +++++++++++++++-------------- src/circuit.jl | 2 +- src/deprecated.jl | 6 ++++++ src/elements.jl | 28 ++++++++++++++-------------- 4 files changed, 36 insertions(+), 29 deletions(-) diff --git a/src/ACME.jl b/src/ACME.jl index c7832c89..79a3c865 100644 --- a/src/ACME.jl +++ b/src/ACME.jl @@ -52,17 +52,6 @@ mutable struct Element end end - function make_pin_dict(syms) - dict = Dict{Symbol,Vector{Tuple{Int, Int}}}() - for i in 1:length(syms) - branch = (i+1) ÷ 2 - polarity = 2(i % 2) - 1 - push!(get!(dict, Symbol(syms[i]), []), (branch, polarity)) - end - dict - end - make_pin_dict(dict::Dict) = dict - mat_dims = Dict( :mv => (:nl,:nb), :mi => (:nl,:nb), :mx => (:nl,:nx), :mxd => (:nl,:nx), :mq => (:nl,:nq), :mu => (:nl,:nu), @@ -75,8 +64,20 @@ mutable struct Element if haskey(mat_dims, key) val = convert(SparseMatrixCSC{Real,Int}, hcat(val)) # turn val into a sparse matrix whatever it is update_sizes(val, mat_dims[key]) - elseif key == :pins - val = make_pin_dict(val) + else + if key === :pins && !(val isa Dict) + val = ports_from_old_pins(val) + key = :ports + end + if key === :ports + pins = Dict{Symbol,Vector{Tuple{Int, Int}}}() + for branch in 1:length(val) + push!(get!(pins, Symbol(val[branch][1]), []), (branch, 1)) + push!(get!(pins, Symbol(val[branch][2]), []), (branch, -1)) + end + val = pins + key = :pins + end end setfield!(elem, key, val) end @@ -92,7 +93,7 @@ mutable struct Element elem.nonlinear_eq = wrap_nleq_expr(nn, get(sizes, :nq, 0), elem.nonlinear_eq) end if !isdefined(elem, :pins) - elem.pins = make_pin_dict(1:2nb(elem)) + elem.pins = Dict(Symbol(i) => [((i+1) ÷ 2, 2(i % 2) - 1)] for i in 1:2nb(elem)) end elem end diff --git a/src/circuit.jl b/src/circuit.jl index 812fbfc1..a77e60cb 100644 --- a/src/circuit.jl +++ b/src/circuit.jl @@ -498,7 +498,7 @@ ports. return Element(mv = M̃ᵥ, mi = -M̃ᵢ, mx = M̃ₓ, mxd = M̃ₓ´, mq = M̃q, mu = M̃u, u0 = ũ0, nonlinear_eq=nonlinear_eq_func(circ), - pins=vcat(collect.(ports)...)) + ports=ports) end function ports_from_pinmap(pinmap) diff --git a/src/deprecated.jl b/src/deprecated.jl index dce6d8e2..7e22109f 100644 --- a/src/deprecated.jl +++ b/src/deprecated.jl @@ -35,5 +35,11 @@ function wrap_nleq_expr(nn, nq, expr) end) end +function ports_from_old_pins(pins) + ports = [pins[2i-1] => pins[2i] for i in 1:length(pins)÷2] + Base.depwarn("`Element(..., pins=$(repr(pins)))` is depreated, use `Element(..., ports=$(repr(ports, context=:typeinfo=>typeof(ports))))` instead", :Element) + return ports +end + Base.@deprecate(composite_element(circ::Circuit, pins::Vector{<:Pair}), composite_element(circ, pinmap=Dict(pins...))) diff --git a/src/elements.jl b/src/elements.jl index 7e865ff4..d1cab768 100644 --- a/src/elements.jl +++ b/src/elements.jl @@ -16,7 +16,7 @@ Pins: `1`, `2` resistor(r) = Element(mv=-1, mi=r) potentiometer(r, pos) = Element(mv=Matrix{Int}(-I, 2, 2), mi=[r*pos 0; 0 r*(1-pos)], - pins=[1, 2, 2, 3]) + ports=[1 => 2, 2 => 3]) potentiometer(r) = Element(mv=[Matrix{Int}(I, 2, 2); zeros(3, 2)], mi=[zeros(2, 2); Matrix{Int}(I, 2, 2); zeros(1, 2)], @@ -28,7 +28,7 @@ potentiometer(r) = J = @SMatrix [1 0 -r*pos 0 -r*i1; 0 1 0 -r*(1-pos) -r*i2] return (res, J) end), - pins=[1, 2, 2, 3]) + ports=[1 => 2, 2 => 3]) """ capacitor(c) @@ -64,7 +64,7 @@ transformer(l1, l2; coupling_coefficient=1, Element(mv=[1 0; 0 1; 0 0; 0 0], mi=[0 0; 0 0; l1 mutual_coupling; mutual_coupling l2], mx=[0 0; 0 0; -1 0; 0 -1], mxd=[-1 0; 0 -1; 0 0; 0 0], - pins=[:primary1; :primary2; :secondary1; :secondary2]) + ports=[:primary1 => :primary2, :secondary1 => :secondary2]) """ transformer(Val{:JA}; D, A, ns, a, α, c, k, Ms) @@ -173,8 +173,8 @@ internal series resistance `rs` (in Ohm) can be given which defaults to zero. Pins: `+` and `-` with `v` being measured from `+` to `-` """ function voltagesource end -voltagesource(v; rs=0) = Element(mv=1, mi=-rs, u0=v, pins=[:+; :-]) -voltagesource(; rs=0) = Element(mv=1, mi=-rs, mu=1, pins=[:+; :-]) +voltagesource(v; rs=0) = Element(mv=1, mi=-rs, u0=v, ports=[:+ => :-]) +voltagesource(; rs=0) = Element(mv=1, mi=-rs, mu=1, ports=[:+ => :-]) """ currentsource(; gp=0) @@ -188,8 +188,8 @@ zero. Pins: `+` and `-` where `i` measures the current leaving source at the `+` pin """ function currentsource end -currentsource(i; gp=0) = Element(mv=gp, mi=-1, u0=i, pins=[:+; :-]) -currentsource(; gp=0) = Element(mv=gp, mi=-1, mu=1, pins=[:+; :-]) +currentsource(i; gp=0) = Element(mv=gp, mi=-1, u0=i, ports=[:+ => :-]) +currentsource(; gp=0) = Element(mv=gp, mi=-1, mu=1, ports=[:+ => :-]) """ voltageprobe() @@ -200,7 +200,7 @@ defaults to zero. Pins: `+` and `-` with the output voltage being measured from `+` to `-` """ -voltageprobe(;gp=0) = Element(mv=-gp, mi=1, pv=1, pins=[:+; :-]) +voltageprobe(;gp=0) = Element(mv=-gp, mi=1, pv=1, ports=[:+ => :-]) """ currentprobe() @@ -212,7 +212,7 @@ defaults to zero. Pins: `+` and `-` with the output current being the current entering the probe at `+` """ -currentprobe(;rs=0) = Element(mv=1, mi=-rs, pi=1, pins=[:+; :-]) +currentprobe(;rs=0) = Element(mv=1, mi=-rs, pi=1, ports=[:+ => :-]) @doc doc""" diode(;is=1e-12, η = 1) @@ -224,7 +224,7 @@ coefficient `η` is unitless. Pins: `+` (anode) and `-` (cathode) """ diode(;is::Real=1e-12, η::Real = 1) = - Element(mv=[1;0], mi=[0;1], mq=[-1 0; 0 -1], pins=[:+; :-], nonlinear_eq = + Element(mv=[1;0], mi=[0;1], mq=[-1 0; 0 -1], ports=[:+ => :-], nonlinear_eq = @inline function(q) v, i = q ex = exp(v*(1 / (25e-3 * η))) @@ -390,7 +390,7 @@ Pins: `base`, `emitter`, `collector` return Element(mv=[1 0; 0 1; 0 0; 0 0], mi = [-(re+rb) -rb; -rb -(rc+rb); 1 0; 0 1], mq = Matrix{Int}(-polarity*I, 4, 4), nonlinear_eq = nonlinear_eq, - pins = [:base; :emitter; :base; :collector]) + ports = [:base => :emitter, :base => :collector]) end @doc doc""" @@ -422,7 +422,7 @@ Pins: `gate`, `source`, `drain` mi=[0 0; 0 0; 0 -1; 1 0], mq=polarity*[1 0 0; 0 1 0; 0 0 1; 0 0 0], u0=polarity*[-vt; 0; 0; 0], - pins=[:gate, :source, :drain, :source], + ports=[:gate => :source, :drain => :source], nonlinear_eq = @inline function (q) vg, vds, id=q # vg = vgs-vt if vg <= 0 @@ -453,7 +453,7 @@ output pin. Pins: `in+` and `in-` for input, `out+` and `out-` for output """ opamp() = Element(mv=[0 0; 1 0], mi=[1 0; 0 0], - pins=["in+", "in-", "out+", "out-"]) + ports=["in+" => "in-", "out+" => "out-"]) @doc doc""" opamp(Val{:macak}, gain, vomin, vomax) @@ -485,5 +485,5 @@ Pins: `in+` and `in-` for input, `out+` and `out-` for output return Element(mv=[0 0; 1 0; 0 1], mi=[1 0; 0 0; 0 0], mq=[0 0; -1 0; 0 -1], u0=[0; 0; offset], nonlinear_eq = nonlinear_eq, - pins=["in+", "in-", "out+", "out-"]) + ports=["in+" => "in-", "out+" => "out-"]) end From 26d8b7294dde9fd0caccdf199fcdaf809ec0c37b Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Mon, 4 Mar 2019 13:47:11 +0100 Subject: [PATCH 18/23] Run Travis on Julia 1.1, too --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 543d665e..a2dcdacf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ julia: - 0.6 - 0.7 - 1.0 + - 1.1 - nightly notifications: email: false From d9ee535efb35109b4e01f11945c6df6fc2b3e2ff Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Thu, 29 Nov 2018 14:15:35 +0100 Subject: [PATCH 19/23] Travis config: Use `codecov` and `coveralls` options ...instead of including the required julia call in `after_success`. --- .travis.yml | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index a2dcdacf..04b5c853 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,17 +13,9 @@ notifications: on_success: change on_failure: always on_start: never +codecov: true +coveralls: true after_success: - - | - julia -e ' - if VERSION >= v"0.7.0-DEV.3656" - import Pkg - else - cd(Pkg.dir("ACME")) - end - Pkg.add("Coverage") - using Coverage - Codecov.submit(process_folder()); Coveralls.submit(process_folder())' - | julia -e ' if VERSION >= v"0.7.0-DEV.3656" From 1b5f514afe36a35a5073e84a6c7392b9db07c35b Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Fri, 30 Nov 2018 15:03:30 +0100 Subject: [PATCH 20/23] Build documentation in a Travis job --- .travis.yml | 36 ++++++++++------ docs/Manifest.toml | 104 +++++++++++++++++++++++++++++++++++++++++++++ docs/Project.toml | 5 +++ 3 files changed, 132 insertions(+), 13 deletions(-) create mode 100644 docs/Manifest.toml create mode 100644 docs/Project.toml diff --git a/.travis.yml b/.travis.yml index 04b5c853..3e06fb9e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,16 +15,26 @@ notifications: on_start: never codecov: true coveralls: true -after_success: - - | - julia -e ' - if VERSION >= v"0.7.0-DEV.3656" - using Pkg # `using Pkg` and `pkg"..."` must be in separate if blocks - end - @static if VERSION >= v"0.7.0-DEV.3656" - pkg"add Documenter@0.19.6" - else - Pkg.add("Documenter", v"0.19.6", v"0.19.6+") - cd(Pkg.dir("ACME")) - end - include(joinpath("docs", "make.jl"))' +jobs: + include: + - &docs + stage: "Documentation" + julia: 1.0 + script: + - julia --project=docs/ -e 'using Pkg; Pkg.instantiate(); Pkg.develop(PackageSpec(path=pwd()))' + - julia --project=docs/ docs/make.jl + - <<: *docs + julia: 0.7 + script: + - julia --project=docs/ -e 'using Pkg; Pkg.instantiate()' + - julia -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd()))' + - julia --project=docs/ docs/make.jl + - <<: *docs + julia: 1.1 + - <<: *docs + julia: nightly + - <<: *docs + julia: 0.6 + script: + - julia -e 'Pkg.add("Documenter", v"0.19.6", v"0.19.6+"); Pkg.clone(pwd())' + - julia docs/make.jl diff --git a/docs/Manifest.toml b/docs/Manifest.toml new file mode 100644 index 00000000..81106eda --- /dev/null +++ b/docs/Manifest.toml @@ -0,0 +1,104 @@ +[[Base64]] +uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" + +[[Compat]] +deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] +git-tree-sha1 = "ec61a16eed883ad0cfa002d7489b3ce6d039bb9a" +uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" +version = "1.4.0" + +[[Dates]] +deps = ["Printf"] +uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" + +[[DelimitedFiles]] +deps = ["Mmap"] +uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" + +[[Distributed]] +deps = ["LinearAlgebra", "Random", "Serialization", "Sockets"] +uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" + +[[DocStringExtensions]] +deps = ["LibGit2", "Markdown", "Pkg", "Test"] +git-tree-sha1 = "1df01539a1c952cef21f2d2d1c092c2bcf0177d7" +uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" +version = "0.6.0" + +[[Documenter]] +deps = ["Compat", "DocStringExtensions", "Logging", "REPL"] +git-tree-sha1 = "7534ff3c50333a69ae0778d4d869e6f5c9ec022a" +uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +version = "0.19.6" + +[[InteractiveUtils]] +deps = ["LinearAlgebra", "Markdown"] +uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" + +[[LibGit2]] +uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" + +[[Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" + +[[LinearAlgebra]] +deps = ["Libdl"] +uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + +[[Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" + +[[Markdown]] +deps = ["Base64"] +uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" + +[[Mmap]] +uuid = "a63ad114-7e13-5084-954f-fe012c677804" + +[[Pkg]] +deps = ["Dates", "LibGit2", "Markdown", "Printf", "REPL", "Random", "SHA", "UUIDs"] +uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" + +[[Printf]] +deps = ["Unicode"] +uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" + +[[REPL]] +deps = ["InteractiveUtils", "Markdown", "Sockets"] +uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" + +[[Random]] +deps = ["Serialization"] +uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" + +[[SHA]] +uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" + +[[Serialization]] +uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" + +[[SharedArrays]] +deps = ["Distributed", "Mmap", "Random", "Serialization"] +uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" + +[[Sockets]] +uuid = "6462fe0b-24de-5631-8697-dd941f90decc" + +[[SparseArrays]] +deps = ["LinearAlgebra", "Random"] +uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[Statistics]] +deps = ["LinearAlgebra", "SparseArrays"] +uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[Test]] +deps = ["Distributed", "InteractiveUtils", "Logging", "Random"] +uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[[UUIDs]] +deps = ["Random"] +uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" + +[[Unicode]] +uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" diff --git a/docs/Project.toml b/docs/Project.toml new file mode 100644 index 00000000..4c6dfa7d --- /dev/null +++ b/docs/Project.toml @@ -0,0 +1,5 @@ +[deps] +Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" + +[compat] +Documenter = "0.19.6" From 8ddb53821d000f5cd2507ebaaaaf424a0ff5ada2 Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Wed, 6 Mar 2019 11:53:32 +0100 Subject: [PATCH 21/23] Bump used Documenter version to 0.19.7 --- docs/Manifest.toml | 8 ++++---- docs/Project.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/Manifest.toml b/docs/Manifest.toml index 81106eda..7ae19ce8 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -3,9 +3,9 @@ uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" [[Compat]] deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"] -git-tree-sha1 = "ec61a16eed883ad0cfa002d7489b3ce6d039bb9a" +git-tree-sha1 = "49269e311ffe11ac5b334681d212329002a9832a" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "1.4.0" +version = "1.5.1" [[Dates]] deps = ["Printf"] @@ -27,9 +27,9 @@ version = "0.6.0" [[Documenter]] deps = ["Compat", "DocStringExtensions", "Logging", "REPL"] -git-tree-sha1 = "7534ff3c50333a69ae0778d4d869e6f5c9ec022a" +git-tree-sha1 = "d7b6266f1f87a8c85ce4c1f9c0e9ba3b7831ce1d" uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" -version = "0.19.6" +version = "0.19.7" [[InteractiveUtils]] deps = ["LinearAlgebra", "Markdown"] diff --git a/docs/Project.toml b/docs/Project.toml index 4c6dfa7d..b2f82520 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -2,4 +2,4 @@ Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" [compat] -Documenter = "0.19.6" +Documenter = "0.19.7" From 3231166a111137fffa38d7291c4350d8ed263c8c Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Tue, 12 Mar 2019 08:10:06 +0100 Subject: [PATCH 22/23] Remove badges from `pkg.julialang.org` which were outdated anyway --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 1fd438bf..ca5efdd1 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,6 @@ [![Documentation](https://img.shields.io/badge/docs-stable-blue.svg)](https://hsu-ant.github.io/ACME.jl/stable) [![Documentation](https://img.shields.io/badge/docs-latest-blue.svg)](https://hsu-ant.github.io/ACME.jl/latest) -[![ACME](http://pkg.julialang.org/badges/ACME_0.5.svg)](http://pkg.julialang.org/?pkg=ACME) -[![ACME](http://pkg.julialang.org/badges/ACME_0.6.svg)](http://pkg.julialang.org/?pkg=ACME) -[![ACME](http://pkg.julialang.org/badges/ACME_0.7.svg)](http://pkg.julialang.org/?pkg=ACME) - [![Build Status](https://travis-ci.org/HSU-ANT/ACME.jl.svg?branch=develop)](https://travis-ci.org/HSU-ANT/ACME.jl) [![codecov](https://codecov.io/gh/HSU-ANT/ACME.jl/branch/develop/graph/badge.svg)](https://codecov.io/gh/HSU-ANT/ACME.jl) [![Coverage Status](https://coveralls.io/repos/github/HSU-ANT/ACME.jl/badge.svg?branch=develop)](https://coveralls.io/github/HSU-ANT/ACME.jl) From 79ea03f14e43435c0a81864464b8581c0037ecd7 Mon Sep 17 00:00:00 2001 From: Martin Holters Date: Tue, 12 Mar 2019 15:48:22 +0100 Subject: [PATCH 23/23] Bump version to 0.8.0 --- README.md | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index ca5efdd1..4936a507 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,7 @@ # ACME.jl - Analog Circuit Modeling and Emulation for Julia [![Join the chat at https://gitter.im/HSU-ANT/ACME.jl](https://badges.gitter.im/HSU-ANT/ACME.jl.svg)](https://gitter.im/HSU-ANT/ACME.jl?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Documentation](https://img.shields.io/badge/docs-stable-blue.svg)](https://hsu-ant.github.io/ACME.jl/stable) -[![Documentation](https://img.shields.io/badge/docs-latest-blue.svg)](https://hsu-ant.github.io/ACME.jl/latest) - -[![Build Status](https://travis-ci.org/HSU-ANT/ACME.jl.svg?branch=develop)](https://travis-ci.org/HSU-ANT/ACME.jl) -[![codecov](https://codecov.io/gh/HSU-ANT/ACME.jl/branch/develop/graph/badge.svg)](https://codecov.io/gh/HSU-ANT/ACME.jl) -[![Coverage Status](https://coveralls.io/repos/github/HSU-ANT/ACME.jl/badge.svg?branch=develop)](https://coveralls.io/github/HSU-ANT/ACME.jl) +[![Documentation](https://img.shields.io/badge/docs-v0.8.0-blue.svg)](https://hsu-ant.github.io/ACME.jl/v0.8.0/) ACME is a [Julia](http://julialang.org/) package for the simulation of electrical circuits, focusing on audio effect circuits. It allows to @@ -127,7 +122,7 @@ fail to run altogether. ## Moving on -There is some [documentation](https://hsu-ant.github.io/ACME.jl/latest) +There is some [documentation](https://hsu-ant.github.io/ACME.jl/v0.8.0/) available for how to use ACME. Additionally, you can take a look at the examples that can be found in the `examples` directory below `Pkg.dir("ACME")`.