Skip to content

Commit 1bfed7a

Browse files
committed
Also store dq, eq, fqprev, and solver in ModelNonlinearEquation
1 parent d1bc087 commit 1bfed7a

File tree

2 files changed

+77
-74
lines changed

2 files changed

+77
-74
lines changed

src/ACME.jl

Lines changed: 73 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -109,56 +109,70 @@ include("elements.jl")
109109

110110
include("circuit.jl")
111111

112-
struct ModelNonlinearEquation{F,Tpexp<:SMatrix,Tq0<:SVector,Tfq<:SMatrix}
112+
struct ModelNonlinearEquation{F,Solver,Tpexp<:SMatrix,Tq0<:SVector,Tfq<:SMatrix}
113113
func::F
114114
pexp::Tpexp
115115
q0::Tq0
116+
dq::Matrix{Float64}
117+
eq::Matrix{Float64}
118+
fqprev::Matrix{Float64}
116119
fq::Tfq
120+
solver::Solver
117121
end
118-
function ModelNonlinearEquation(func, pexp, q0, fq)
122+
function ModelNonlinearEquation(func, pexp′, q0′, dq, eq, fqprev, fq′, ::Type{Solver}, init_z) where {Solver}
123+
nn = size(fq′, 2)
124+
nq = size(pexp′, 1)
125+
np = size(pexp′, 2)
126+
pexp = SMatrix{nq, np, Float64}(pexp′)
127+
q0 = SVector{nq, Float64}(q0′)
128+
fq = SMatrix{nq, nn, Float64}(fq′)
129+
solver = Solver(
130+
ParametricNonLinEq{nn, np, nq}(
131+
(pfull, z) -> func(pfull, fq, z),
132+
(p) -> q0 + pexp * p,
133+
(Jq) -> Jq * pexp,
134+
),
135+
zeros(np),
136+
init_z,
137+
)
119138
return ModelNonlinearEquation(
120139
func,
121-
SMatrix{size(pexp,1),size(pexp,2),Float64}(pexp),
122-
SVector{length(q0),Float64}(q0),
123-
SMatrix{size(fq,1),size(fq,2),Float64}(fq)
140+
pexp,
141+
q0,
142+
Matrix{Float64}(dq),
143+
Matrix{Float64}(eq),
144+
Matrix{Float64}(fqprev),
145+
fq,
146+
solver,
124147
)
125148
end
126149

127150
nn(nleq::ModelNonlinearEquation) = size(nleq.fq, 2)
128151
np(nleq::ModelNonlinearEquation) = size(nleq.pexp, 2)
129152
nq(nleq::ModelNonlinearEquation) = size(nleq.pexp, 1)
130153

131-
mutable struct DiscreteModel{Solvers}
154+
mutable struct DiscreteModel{NLEQs}
132155
a::Matrix{Float64}
133156
b::Matrix{Float64}
134157
c::Matrix{Float64}
135158
x0::Vector{Float64}
136-
dqs::Vector{Matrix{Float64}}
137-
eqs::Vector{Matrix{Float64}}
138-
fqprevs::Vector{Matrix{Float64}}
139159
dy::Matrix{Float64}
140160
ey::Matrix{Float64}
141161
fy::Matrix{Float64}
142162
y0::Vector{Float64}
143163

144-
nonlinear_eqs::Vector{<:ModelNonlinearEquation}
164+
nonlinear_eqs::NLEQs
145165

146-
solvers::Solvers
147166
x::Vector{Float64}
148167

149-
function DiscreteModel(
150-
mats::Dict{Symbol},
151-
nonlinear_eqs::Vector{<:ModelNonlinearEquation},
152-
solvers::Solvers,
153-
) where {Solvers}
154-
model = new{Solvers}()
168+
function DiscreteModel(mats::Dict{Symbol}, nonlinear_eqs::NLEQs) where {NLEQs}
169+
model = new{NLEQs}()
155170

156-
for mat in (:a, :b, :c, :dqs, :eqs, :fqprevs, :dy, :ey, :fy, :x0, :y0)
171+
for mat in (:a, :b, :c, :dy, :ey, :fy, :x0, :y0)
157172
setfield!(model, mat, convert(fieldtype(typeof(model), mat), mats[mat]))
158173
end
159174

160175
model.nonlinear_eqs = nonlinear_eqs
161-
model.solvers = solvers
162176
model.x = zeros(nx(model))
163177
return model
164178
end
@@ -235,26 +249,16 @@ function DiscreteModel(circ::Circuit, t::Real, ::Type{Solver}=HomotopySolver{Cac
235249
model_nps = size.(mats[:dqs], 1)
236250
end
237251

238-
nonlinear_eqs = ModelNonlinearEquation[
239-
ModelNonlinearEquation(func, pexp, q0, fq)
240-
for (func, pexp, q0, fq) in zip(
241-
nonlinear_eq_funcs, mats[:pexps], mats[:q0s], mats[:fqs]
242-
)
243-
]
244-
245-
solvers = Tuple(
246-
Solver(
247-
ParametricNonLinEq{nn(nleq), np(nleq), nq(nleq)}(
248-
(pfull, z) -> nleq.func(pfull, nleq.fq, z),
249-
(p) -> nleq.q0 + nleq.pexp * p,
250-
(Jq) -> Jq * nleq.pexp,
251-
),
252-
zeros(np(nleq)),
253-
init_z,
252+
nonlinear_eqs = Tuple(
253+
ModelNonlinearEquation(func, pexp, q0, dq, eq, fqprev, fq, Solver, init_z)
254+
for (func, pexp, q0, dq, eq, fqprev, fq, init_z) in zip(
255+
nonlinear_eq_funcs,
256+
getindex.((mats,), (:pexps, :q0s, :dqs, :eqs, :fqprevs, :fqs))...,
257+
init_zs,
254258
)
255-
for (nleq, init_z) in zip(nonlinear_eqs, init_zs)
256259
)
257-
return DiscreteModel(mats, nonlinear_eqs, solvers)
260+
261+
return DiscreteModel(mats, nonlinear_eqs)
258262
end
259263

260264
function model_matrices(circ::Circuit, t::Rational{BigInt})
@@ -439,7 +443,7 @@ end
439443

440444
nx(model::DiscreteModel) = length(model.x0)
441445
nq(model::DiscreteModel, subidx) = length(model.nonlinear_eqs[subidx].q0)
442-
np(model::DiscreteModel, subidx) = size(model.dqs[subidx], 1)
446+
np(model::DiscreteModel, subidx) = np(model.nonlinear_eqs[subidx])
443447
nu(model::DiscreteModel) = size(model.b, 2)
444448
ny(model::DiscreteModel) = length(model.y0)
445449
nn(model::DiscreteModel, subidx) = size(model.nonlinear_eqs[subidx].fq, 2)
@@ -449,22 +453,22 @@ function steadystate(model::DiscreteModel, u=zeros(nu(model)))
449453
IA_LU = lu(I-model.a)
450454
steady_z = zeros(nn(model))
451455
zoff = 1
452-
for idx in 1:length(model.solvers)
453-
zoff_last = zoff+nn(model,idx)-1
454-
steady_q0 = model.nonlinear_eqs[idx].q0 + model.nonlinear_eqs[idx].pexp*((model.dqs[idx]/IA_LU*model.b + model.eqs[idx])*u + (model.dqs[idx]/IA_LU*model.c + model.fqprevs[idx])*steady_z) +
455-
model.nonlinear_eqs[idx].pexp*model.dqs[idx]/IA_LU*model.x0
456-
fq = model.nonlinear_eqs[idx].pexp*model.dqs[idx]/IA_LU*model.c[:,zoff:zoff_last] + model.nonlinear_eqs[idx].fq
457-
nleq = model.nonlinear_eqs[idx].func
458-
steady_nl_eq_func = (pfull, z) -> nleq(pfull, fq, z)
459-
steady_nleq = ParametricNonLinEq{nn(model, idx), nq(model, idx)}(steady_nl_eq_func)
460-
steady_solver = Base.invokelatest(HomotopySolver{SimpleSolver}, steady_nleq, zeros(nq(model, idx)),
461-
zeros(nn(model, idx)))
456+
for nleq in model.nonlinear_eqs
457+
zoff_last = zoff+nn(nleq)-1
458+
steady_q0 = nleq.q0 +
459+
nleq.pexp*((nleq.dq/IA_LU*model.b + nleq.eq)*u + (nleq.dq/IA_LU*model.c + nleq.fqprev)*steady_z) +
460+
nleq.pexp*nleq.dq/IA_LU*model.x0
461+
fq = nleq.pexp*nleq.dq/IA_LU*model.c[:,zoff:zoff_last] + nleq.fq
462+
steady_nl_eq_func = (pfull, z) -> nleq.func(pfull, fq, z)
463+
steady_nleq = ParametricNonLinEq{nn(nleq), nq(nleq)}(steady_nl_eq_func)
464+
steady_solver =
465+
HomotopySolver{SimpleSolver}(steady_nleq, zeros(nq(nleq)), zeros(nn(nleq)))
462466
set_resabstol!(steady_solver, 1e-15)
463467
steady_z[zoff:zoff_last] = Base.invokelatest(solve, steady_solver, steady_q0)
464468
if !hasconverged(steady_solver)
465469
error("Failed to find steady state solution")
466470
end
467-
zoff += nn(model,idx)
471+
zoff += nn(nleq)
468472
end
469473
return IA_LU\(model.b*u + model.c*steady_z + model.x0)
470474
end
@@ -477,10 +481,10 @@ end
477481

478482
function linearize(model::DiscreteModel, usteady::AbstractVector{Float64}=zeros(nu(model)))
479483
xsteady = steadystate(model, usteady)
480-
zranges = Vector{UnitRange{Int64}}(undef, length(model.solvers))
481-
dzdps = Vector{Matrix{Float64}}(undef, length(model.solvers))
482-
dqlins = Vector{Matrix{Float64}}(undef, length(model.solvers))
483-
eqlins = Vector{Matrix{Float64}}(undef, length(model.solvers))
484+
zranges = Vector{UnitRange{Int64}}(undef, length(model.nonlinear_eqs))
485+
dzdps = Vector{Matrix{Float64}}(undef, length(model.nonlinear_eqs))
486+
dqlins = Vector{Matrix{Float64}}(undef, length(model.nonlinear_eqs))
487+
eqlins = Vector{Matrix{Float64}}(undef, length(model.nonlinear_eqs))
484488
zsteady = zeros(nn(model))
485489
zoff = 1
486490
x0 = copy(model.x0)
@@ -492,17 +496,16 @@ function linearize(model::DiscreteModel, usteady::AbstractVector{Float64}=zeros(
492496
ey = copy(model.ey)
493497
fy = copy(model.fy)
494498

495-
for idx in 1:length(model.solvers)
496-
psteady = model.dqs[idx] * xsteady + model.eqs[idx] * usteady +
497-
model.fqprevs[idx] * zsteady
498-
zsub, dzdps[idx] =
499-
Base.invokelatest(linearize, model.solvers[idx], psteady)
499+
for idx in 1:length(model.nonlinear_eqs)
500+
psteady = model.nonlinear_eqs[idx].dq * xsteady + model.nonlinear_eqs[idx].eq * usteady +
501+
model.nonlinear_eqs[idx].fqprev * zsteady
502+
zsub, dzdps[idx] = linearize(model.nonlinear_eqs[idx].solver, psteady)
500503
copyto!(zsteady, zoff, zsub, 1, length(zsub))
501504

502505
zranges[idx] = zoff:zoff+length(zsub)-1
503-
fqdzdps = [model.fqprevs[idx][:,zranges[n]] * dzdps[n] for n in 1:idx-1]
504-
dqlins[idx] = reduce(+, init=model.dqs[idx], fqdzdps .* dqlins[1:idx-1])
505-
eqlins[idx] = reduce(+, init=model.eqs[idx], fqdzdps .* eqlins[1:idx-1])
506+
fqdzdps = [model.nonlinear_eqs[idx].fqprev[:,zranges[n]] * dzdps[n] for n in 1:idx-1]
507+
dqlins[idx] = reduce(+, init=model.nonlinear_eqs[idx].dq, fqdzdps .* dqlins[1:idx-1])
508+
eqlins[idx] = reduce(+, init=model.nonlinear_eqs[idx].eq, fqdzdps .* eqlins[1:idx-1])
506509

507510
x0 += model.c[:,zranges[idx]] * (zsub - dzdps[idx]*psteady)
508511
a += model.c[:,zranges[idx]] * dzdps[idx] * dqlins[idx]
@@ -520,7 +523,7 @@ function linearize(model::DiscreteModel, usteady::AbstractVector{Float64}=zeros(
520523
:eqs => Matrix{Float64}[], :fqprevs => Matrix{Float64}[],
521524
:fqs => Matrix{Float64}[], :q0s => Vector{Float64}[],
522525
:dy => dy, :ey => ey, :fy => zeros(ny(model), 0), :x0 => x0, :y0 => y0)
523-
return DiscreteModel(mats, ModelNonlinearEquation[], ())
526+
return DiscreteModel(mats, ())
524527
end
525528

526529
"""
@@ -550,7 +553,7 @@ struct ModelRunner{Model<:DiscreteModel,ShowProgress}
550553
z::Vector{Float64}
551554
function ModelRunner{Model,ShowProgress}(model::Model) where {Model<:DiscreteModel,ShowProgress}
552555
ucur = Vector{Float64}(undef, nu(model))
553-
ps = Vector{Float64}[Vector{Float64}(undef, np(model, idx)) for idx in 1:length(model.solvers)]
556+
ps = Vector{Float64}[Vector{Float64}(undef, np(model, idx)) for idx in 1:length(model.nonlinear_eqs)]
554557
ycur = Vector{Float64}(undef, ny(model))
555558
xnew = Vector{Float64}(undef, nx(model))
556559
z = Vector{Float64}(undef, nn(model))
@@ -646,20 +649,20 @@ function step!(runner::ModelRunner, y::AbstractMatrix{Float64}, u::AbstractMatri
646649
copyto!(ucur, 1, u, (n-1)*nu(model)+1, nu(model))
647650
zoff = 1
648651
fill!(z, 0.0)
649-
for idx in 1:length(model.solvers)
652+
for idx in 1:length(model.nonlinear_eqs)
650653
p = runner.ps[idx]
651654
# copyto!(p, model.dqs[idx] * model.x + model.eqs[idx] * u[:,n]) + model.fqprevs[idx] * z
652-
if size(model.dqs[idx], 2) == 0
655+
if size(model.nonlinear_eqs[idx].dq, 2) == 0
653656
fill!(p, 0.0)
654657
else
655-
BLAS.gemv!('N', 1., model.dqs[idx], model.x, 0., p)
658+
BLAS.gemv!('N', 1., model.nonlinear_eqs[idx].dq, model.x, 0., p)
656659
end
657-
BLAS.gemv!('N', 1., model.eqs[idx], ucur, 1., p)
660+
BLAS.gemv!('N', 1., model.nonlinear_eqs[idx].eq, ucur, 1., p)
658661
if idx > 1
659-
BLAS.gemv!('N', 1., model.fqprevs[idx], z, 1., p)
662+
BLAS.gemv!('N', 1., model.nonlinear_eqs[idx].fqprev, z, 1., p)
660663
end
661-
zsub = solve(model.solvers[idx], p)
662-
if !hasconverged(model.solvers[idx])
664+
zsub = solve(model.nonlinear_eqs[idx].solver, p)
665+
if !hasconverged(model.nonlinear_eqs[idx].solver)
663666
if all(isfinite, zsub)
664667
@warn "Failed to converge while solving non-linear equation."
665668
else

test/runtests.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -573,8 +573,8 @@ end
573573

574574
function checksteady!(model)
575575
x_steady = steadystate!(model)
576-
for s in model.solvers
577-
ACME.set_resabstol!(s, 1e-13)
576+
for nleq in model.nonlinear_eqs
577+
ACME.set_resabstol!(nleq.solver, 1e-13)
578578
end
579579
run!(model, zeros(1, 1))
580580
return model.x x_steady
@@ -628,8 +628,8 @@ end
628628
@testset "birdie" begin
629629
include(joinpath(dirname(@__FILE__), "..", "examples", "birdie.jl"))
630630
model=birdie(vol=0.8)
631-
ACME.solve(model.solvers[1], [0.003, -0.0002])
632-
@assert all(ACME.hasconverged, model.solvers)
631+
ACME.solve(model.nonlinear_eqs[1].solver, [0.003, -0.0002])
632+
@assert all((nleq) -> ACME.hasconverged(nleq.solver), model.nonlinear_eqs)
633633
println("Running birdie with fixed vol")
634634
@test ACME.np(model, 1) == 2
635635
y = run!(model, sin.(2π*1000/44100*(0:44099)'); showprogress=false)

0 commit comments

Comments
 (0)