Skip to content

Commit

Permalink
Merge branch 'release-0.7.4'
Browse files Browse the repository at this point in the history
  • Loading branch information
martinholters committed Jul 24, 2018
2 parents 1d3cde8 + 2f9f993 commit 3b32f8f
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 42 deletions.
5 changes: 3 additions & 2 deletions .travis.yml
Expand Up @@ -2,6 +2,7 @@ language: julia
julia:
- 0.6
- 0.7
- nightly
notifications:
email: false
webhooks:
Expand All @@ -27,9 +28,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.13.2"
pkg"add Documenter@0.19.0"
else
Pkg.add("Documenter", v"0.13.2", v"0.13.2+")
Pkg.add("Documenter", v"0.19.0", v"0.19.0+")
cd(Pkg.dir("ACME"))
end
include(joinpath("docs", "make.jl"))'
4 changes: 2 additions & 2 deletions README.md
@@ -1,7 +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-v0.7.3-blue.svg)](https://hsu-ant.github.io/ACME.jl/v0.7.3)
[![Documentation](https://img.shields.io/badge/docs-v0.7.4-blue.svg)](https://hsu-ant.github.io/ACME.jl/v0.7.4)

ACME is a [Julia](http://julialang.org/) package for the simulation of
electrical circuits, focusing on audio effect circuits. It allows to
Expand Down Expand Up @@ -122,7 +122,7 @@ fail to run altogether.

## Moving on

There is some [documentation](https://hsu-ant.github.io/ACME.jl/v0.7.3)
There is some [documentation](https://hsu-ant.github.io/ACME.jl/v0.7.4)
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")`.
Expand Down
3 changes: 1 addition & 2 deletions REQUIRE
@@ -1,6 +1,5 @@
julia 0.6
Compat 0.65.0
Compat 1.0.0
DataStructures 0.2.9
IterTools 0.1.0
ProgressMeter 0.2.1
StatsBase 0.23.0
37 changes: 29 additions & 8 deletions docs/src/gettingstarted.md
Expand Up @@ -19,13 +19,16 @@ This will download ACME and all of its dependencies.
We will demonstrate ACME by modeling a simple diode clipper. The first step is
to load ACME:

```Julia
```jldoctest firststeps; output = false
using ACME
# output
```

Now we create the circuit description:

```Julia
```jldoctest firststeps; output = false, filter = r"(ACME\.)?Circuit\(.*"s
circ = @circuit begin
j_in = voltagesource()
r1 = resistor(1e3)
Expand All @@ -38,6 +41,10 @@ circ = @circuit begin
r1[2] ⟷ c1[1] ⟷ d1[+] ⟷ d2[-] ⟷ j_out[+]
gnd ⟷ c1[2] ⟷ d1[-] ⟷ d2[+] ⟷ j_out[-]
end
# output
Circuit(...)
```

The first six lines inside the `begin`/`end` block instantiate circuit elements.
Expand All @@ -63,7 +70,7 @@ It is also possible to specify connections following the element definition
one can only connect to elements defined before. Thus, above circuit could also
be entered as:

```Julia
```jldoctest firststeps; output = false, filter = r"(ACME\.)?Circuit\(.*"s
circ = @circuit begin
j_in = voltagesource(), [-] ⟷ gnd
r1 = resistor(1e3), [1] ⟷ j_in[+]
Expand All @@ -72,24 +79,38 @@ circ = @circuit begin
d2 = diode(is=1.8e-15), [+] ⟷ gnd, [-] ⟷ r1[2]
j_out = voltageprobe(), [+] ⟷ r1[2], [-] ⟷ gnd
end
# output
Circuit(...)
```

Now that the circuit has been set up, we need to turn it into a model. This
could hardly be any easier:

```Julia
model = DiscreteModel(circ, 1./44100)
```jldoctest firststeps; output = false, filter = r"(ACME\.)?DiscreteModel{.*"s
model = DiscreteModel(circ, 1/44100)
# output
DiscreteModel{...}(...)
```

The second argument specifies the sampling interval, the reciprocal of the
sampling rate, here assumed to be the typical 44100 Hz.

Now we can process some input data. It has to be provided as a matrix with one
row per input (just one in the example) and one column per sample. So for a
sinusoid at 1 kHz lasting one second, we do::
sinusoid at 1 kHz lasting one second, we do:

```Julia
y = run!(model, sin(2π*1000/44100*(0:44099)'))
```jldoctest firststeps; filter = r"\r?Running model:.*"
y = run!(model, sin.(2π*1000/44100*(0:44099)'))
# output
Running model: 100%|████████████████████████████████████| Time: 0:00:01
1×44100 Array{Float64,2}:
0.0 0.0275964 0.0990996 0.195777 … -0.537508 -0.462978 -0.36521
```

The output `y` now likewise is a matrix with one row for the one probe we have
Expand Down
76 changes: 67 additions & 9 deletions docs/src/ug.md
Expand Up @@ -24,7 +24,7 @@ disconnect!
```

For example, a cascade of 20 RC-lowpasses could be generated by:
```julia
```jldoctest ug; output = false, setup = :(using ACME)
circ = @circuit begin
src = voltagesource(), [-] ⟷ gnd
output = voltageprobe(), [-] ⟷ gnd
Expand All @@ -36,9 +36,12 @@ for i in 1:20
connect!(circ, (resrefdes, 1), pin)
connect!(circ, (resrefdes, 2), (caprefdes, 1))
connect!(circ, (caprefdes, 2), :gnd)
pin = (resrefdes, 2)
global pin = (resrefdes, 2)
end
connect!(circ, pin, (:output, +))
# output
```

## Model Creation and Use
Expand All @@ -47,35 +50,63 @@ A `Circuit` only stores elements and information about their connections. To
simulate a circuit, a model has to be derived from it. This can be as simple
as:

```Julia
```jldoctest ug; output = false, filter = r"(ACME\.)?DiscreteModel{.*"s
model = DiscreteModel(circ, 1/44100)
# output
DiscreteModel{...}(...)
```

Here, `1/44100` denotes the sampling interval, i.e. the reciprocal of the
sampling rate at which the model should run. Optionally, one can specify the
solver to use for solving the model's non-linear equation:

```Julia
```jldoctest ug; output = false, filter = r"(ACME\.)?DiscreteModel{.*"s
model = DiscreteModel(circ, 1/44100, HomotopySolver{SimpleSolver})
# output
DiscreteModel{...}(...)
```

See [Solvers](@ref) for more information about the available solvers.

Once a model is created, it can be run:

```Julia
```jldoctest; output = false, setup = :(using ACME; model=DiscreteModel(@circuit(begin end), 1); u=zeros(0,10))
y = run!(model, u)
# output
0×10 Array{Float64,2}
```

The input `u` is matrix with one row for each of the circuit's inputs and one
column for each time step to simulate. Likewise, the output `y` will be a
matrix with one row for each of the circuit's outputs and one column for each
simulated time step. The order of the rows will correspond to the order in which
the respective input and output elements were added to the `Circuit`. To
the respective input and output elements were added to the `Circuit`.
So for above circuit, we may obtain the first 100 samples of the impulse
response with

```jldoctest ug
run!(model, [1 zeros(1,99)])
# output
1×100 Array{Float64,2}:
1.83357e-8 3.1622e-7 2.59861e-6 … 0.00465423 0.00459275 0.00453208
```
To
simulate a circuit without inputs, a matrix with zero rows may be passed:

```Julia
```jldoctest; output = false, setup = :(using ACME; model=DiscreteModel(@circuit(begin end), 1))
y = run!(model, zeros(0, 100))
# output
0×100 Array{Float64,2}
```

The internal state of the model (e.g. capacitor charges) is preserved accross
Expand All @@ -85,9 +116,12 @@ Each invocation of `run!` in this way has to allocate some memory as temporary
storage. To avoid this overhead when running the same model for many small input
blocks, a `ModelRunner` instance can be created explicitly:

```Julia
```jldoctest ug; output = false, setup = :(u=zeros(1,10); y=zeros(1,10))
runner = ModelRunner(model, false)
run!(runner, y, u)
# output
```

By using a pre-allocated output `y` as in the example, allocations in `run!` are
Expand All @@ -97,8 +131,32 @@ Upon creation of a `DiscreteModel`, its internal states (e.g. capacitor charges)
are set to zero. It is also possible to set the states to a steady state (if
one can be found) with:

```Julia
```jldoctest ug; output = false
steadystate!(model)
# output
20-element Array{Float64,1}:
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
0.0
```

This is often desirable for circuits where bias voltages are only slowly
Expand Down
14 changes: 7 additions & 7 deletions src/ACME.jl
Expand Up @@ -460,7 +460,7 @@ np(model::DiscreteModel, subidx) = size(model.dqs[subidx], 1)
nu(model::DiscreteModel) = size(model.b, 2)
ny(model::DiscreteModel) = length(model.y0)
nn(model::DiscreteModel, subidx) = size(model.fqs[subidx], 2)
nn(model::DiscreteModel) = reduce(+, 0, size(fq, 2) for fq in model.fqs)
nn(model::DiscreteModel) = Compat.reduce(+, init=0, size(fq, 2) for fq in model.fqs)

function steadystate(model::DiscreteModel, u=zeros(nu(model)))
@static if VERSION < v"0.7.0-DEV.5211"
Expand Down Expand Up @@ -528,8 +528,8 @@ function linearize(model::DiscreteModel, usteady::AbstractVector{Float64}=zeros(

zranges[idx] = zoff:zoff+length(zsub)-1
fqdzdps = [model.fqprevs[idx][:,zranges[n]] * dzdps[n] for n in 1:idx-1]
dqlins[idx] = reduce(+, model.dqs[idx], fqdzdps .* dqlins[1:idx-1])
eqlins[idx] = reduce(+, model.eqs[idx], fqdzdps .* eqlins[1:idx-1])
dqlins[idx] = Compat.reduce(+, init=model.dqs[idx], fqdzdps .* dqlins[1:idx-1])
eqlins[idx] = Compat.reduce(+, init=model.eqs[idx], fqdzdps .* eqlins[1:idx-1])

x0 += model.c[:,zranges[idx]] * (zsub - dzdps[idx]*psteady)
a += model.c[:,zranges[idx]] * dzdps[idx] * dqlins[idx]
Expand Down Expand Up @@ -716,19 +716,19 @@ function gensolve(a, b, x, h, thresh=0.1)
if m == 0
return x, h
end
t = sortperm(vec(mapslices(ait -> count(!iszero, ait), a, 2))) # row indexes in ascending order of nnz
t = sortperm(vec(mapslices(ait -> count(!iszero, ait), a, dims=2))) # row indexes in ascending order of nnz
tol = 3 * max(eps(float(eltype(a))), eps(float(eltype(h)))) * size(a, 2)
for i in 1:m
ait = a[t[i],:]' # ait is a row of the a matrix
s = ait * h;
inz, jnz, nz_vals = findnz(s)
jnz, nz_vals = findnz(s')
nz_abs_vals = abs.(nz_vals)
max_abs_val = reduce(max, zero(eltype(s)), nz_abs_vals)
max_abs_val = Compat.reduce(max, init=zero(eltype(s)), nz_abs_vals)
if max_abs_val tol # cosidered numerical zero
continue
end
jat = jnz[nz_abs_vals .≥ thresh*max_abs_val] # cols above threshold
j = jat[argmin(vec(mapslices(hj -> count(!iszero, hj), h[:,jat], 1)))]
j = jat[argmin(vec(mapslices(hj -> count(!iszero, hj), h[:,jat], dims=1)))]
q = h[:,j]
x = x + convert(typeof(x), q * ((b[t[i],:]' - ait*x) * (1 / (ait*q))))
if size(h)[2] > 1
Expand Down

0 comments on commit 3b32f8f

Please sign in to comment.