Skip to content

Commit fb39896

Browse files
authored
Merge pull request #150 from ModiaSim/dae_mode
Fix issues with IDA() (DAE mode)
2 parents c060428 + d6c92a1 commit fb39896

File tree

4 files changed

+120
-50
lines changed

4 files changed

+120
-50
lines changed

docs/src/index.md

+6
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ functionalities of these packages.
4242

4343
## Release Notes
4444

45+
### Version 0.9.2
46+
47+
- Bug fix: integrator IDA() can be used (especially to avoid solving large linear equation systems in the model).\
48+
Extend some test models to use IDA().
49+
50+
4551
### Version 0.9.1
4652

4753
- Requires SignalTables 0.3.5.

src/SimulateAndPlot.jl

+97-42
Original file line numberDiff line numberDiff line change
@@ -372,36 +372,38 @@ end
372372

373373
function simulateSegment!(m::SimulationModel{FloatType,TimeType}, algorithm=missing; kwargs...) where {FloatType,TimeType}
374374
solution = nothing
375+
options = m.options
375376

376377
sizesOfLinearEquationSystems = Int[length(leq.b) for leq in m.linearEquations]
377378

378379
# Define problem and callbacks based on algorithm and model type
379-
interval = m.options.interval
380-
if abs(m.options.stopTime - m.options.startTime) <= 0
380+
interval = options.interval
381+
if abs(options.stopTime - options.startTime) <= 0
381382
interval = 1.0
382-
tspan2 = [m.options.startTime]
383-
elseif abs(m.options.interval) < abs(m.options.stopTime-m.options.startTime)
383+
tspan2 = [options.startTime]
384+
elseif abs(options.interval) < abs(options.stopTime-options.startTime)
384385
if m.nsegments == 1
385-
tspan2 = m.options.startTime:interval:m.options.stopTime
386+
tspan2 = options.startTime:interval:options.stopTime
386387
else
387-
i = ceil( (m.options.startTime - m.options.startTimeFirstSegment)/interval )
388-
tnext = m.options.startTimeFirstSegment + i*interval
389-
tspan2 = tnext:interval:m.options.stopTime
390-
if tspan2[1] > m.options.startTime
391-
tspan2 = [m.options.startTime, tspan2...]
388+
i = ceil( (options.startTime - options.startTimeFirstSegment)/interval )
389+
tnext = options.startTimeFirstSegment + i*interval
390+
tspan2 = tnext:interval:options.stopTime
391+
if tspan2[1] > options.startTime
392+
tspan2 = [options.startTime, tspan2...]
392393
end
393394
end
394-
if tspan2[end] < m.options.stopTime
395-
tspan2 = [tspan2..., m.options.stopTime]
395+
if tspan2[end] < options.stopTime
396+
tspan2 = [tspan2..., options.stopTime]
396397
end
397398
else
398-
tspan2 = [m.options.startTime, m.options.stopTime]
399+
tspan2 = [options.startTime, options.stopTime]
399400
end
400-
tspan = (m.options.startTime, m.options.stopTime)
401+
tspan = (options.startTime, options.stopTime)
401402

402403
eh = m.eventHandler
403-
m.odeMode = true
404-
m.solve_leq = true
404+
for leq in m.linearEquations
405+
leq.odeMode = true
406+
end
405407
if typeof(algorithm) <: DifferentialEquations.DiffEqBase.AbstractDAEAlgorithm
406408
# DAE integrator
407409
m.odeIntegrator = false
@@ -411,27 +413,65 @@ function simulateSegment!(m::SimulationModel{FloatType,TimeType}, algorithm=miss
411413
TimerOutputs.@timeit m.timer "DifferentialEquations.DAEProblem" problem = DifferentialEquations.DAEProblem{true}(DAEresidualsForODE!, m.der_x, m.x_init, tspan, m, differential_vars = differential_vars)
412414
empty!(m.daeCopyInfo)
413415
if length(sizesOfLinearEquationSystems) > 0 && maximum(sizesOfLinearEquationSystems) >= options.nlinearMinForDAE
414-
# Prepare data structure to efficiently perform copy operations for DAE integrator
416+
# Prepare data structure to efficiently perform copy operations for DAE integrator
415417
x_info = m.equationInfo.x_info
416418
der_x_dict = m.equationInfo.der_x_dict
417419
der_x_names = keys(der_x_dict)
420+
daeMode = false
418421
for (ileq,leq) in enumerate(m.linearEquations)
419-
if sizesOfLinearEquationSystems[ileq] >= options.nlinearMinForDAE &&
420-
length(intersect(leq.x_names,der_x_names)) == length(leq.x_names)
421-
# Linear equation shall be solved by DAE and all unknowns of the linear equation system are DAE derivatives
422-
leq.odeMode = false
423-
m.odeMode = false
424-
leq_copy = LinearEquationsCopyInfoForDAEMode(ileq)
425-
for ix in 1:length(leq.x_names)
426-
x_name = leq.x_names[ix]
427-
x_length = leq.x_lengths[ix]
428-
x_info_i = x_info[ der_x_dict[x_name] ]
429-
@assert(x_length == x_info_i.length)
430-
startIndex = x_info_i.startIndex
431-
endIndex = startIndex + x_length - 1
432-
append!(leq_copy.index, startIndex:endIndex)
422+
if sizesOfLinearEquationSystems[ileq] >= options.nlinearMinForDAE
423+
answer = leq.x_names .∈ Ref(der_x_names)
424+
daeMode = true
425+
for (index, val) in enumerate(answer)
426+
if !val && leq.x_lengths[index] > 0
427+
daeMode = false
428+
break
429+
end
430+
end
431+
432+
if daeMode
433+
# Linear equation shall be solved by DAE and all unknowns of the linear equation system are DAE derivatives
434+
if eh.nz > 0
435+
leq.odeMode = true
436+
daeMode = false
437+
if options.log
438+
println(" No DAE mode for equation system $ileq because $(eh.nz) crossing function(s) defined (see issue #686 of DifferentialEquations.jl)")
439+
end
440+
441+
else
442+
leq.odeMode = false
443+
leq_copy = LinearEquationsCopyInfoForDAEMode(ileq)
444+
for ix in 1:length(leq.x_names)
445+
x_length = leq.x_lengths[ix]
446+
if x_length > 0
447+
x_name = leq.x_names[ix]
448+
x_info_i = x_info[ der_x_dict[x_name] ]
449+
@assert(x_length == x_info_i.length)
450+
startIndex = x_info_i.startIndex
451+
endIndex = startIndex + x_length - 1
452+
append!(leq_copy.index, startIndex:endIndex)
453+
end
454+
end
455+
push!(m.daeCopyInfo, leq_copy)
456+
end
457+
else
458+
if options.log
459+
unknownsThatAreNoStateDerivatives = ""
460+
first = true
461+
for (index, val) in enumerate(answer)
462+
if !val && leq.x_lengths[index] > 0
463+
if first
464+
first = false
465+
unknownsThatAreNoStateDerivatives = "\"" * leq.x_names[index] * "\""
466+
else
467+
unknownsThatAreNoStateDerivatives *= ",\"" * leq.x_names[index] * "\""
468+
end
469+
end
470+
end
471+
println(" No DAE mode for equation system $ileq because the unknowns $unknownsThatAreNoStateDerivatives are no state derivatives!")
472+
end
473+
leq.odeMode = true
433474
end
434-
push!(m.daeCopyInfo, leq_copy)
435475
else
436476
leq.odeMode = true
437477
end
@@ -442,6 +482,21 @@ function simulateSegment!(m::SimulationModel{FloatType,TimeType}, algorithm=miss
442482
m.odeIntegrator = true
443483
TimerOutputs.@timeit m.timer "DifferentialEquations.ODEProblem" problem = DifferentialEquations.ODEProblem{true}(derivatives!, m.x_init, tspan, m)
444484
end
485+
486+
if length(m.linearEquations) == 0
487+
m.odeMode = true
488+
m.solve_leq = true
489+
else
490+
m.odeMode = false
491+
m.solve_leq = false
492+
for leq in m.linearEquations
493+
if leq.odeMode
494+
m.odeMode = true
495+
m.solve_leq = true
496+
break
497+
end
498+
end
499+
end
445500

446501
callback2 = DifferentialEquations.DiscreteCallback(timeEventCondition!, affectTimeEvent!)
447502
if eh.nz > 0
@@ -450,9 +505,9 @@ function simulateSegment!(m::SimulationModel{FloatType,TimeType}, algorithm=miss
450505
# FunctionalCallingCallback(outputs!, ...) is not correctly called when zero crossings are present.
451506
# The fix is to call outputs!(..) from the previous to the current event, when an event occurs.
452507
# (alternativey: callback4 = DifferentialEquations.PresetTimeCallback(tspan2, affect_outputs!) )
453-
callback1 = DifferentialEquations.FunctionCallingCallback(outputs!, funcat=[m.options.startTime]) # call outputs!(..) at startTime
508+
callback1 = DifferentialEquations.FunctionCallingCallback(outputs!, funcat=[options.startTime]) # call outputs!(..) at startTime
454509
callback3 = DifferentialEquations.VectorContinuousCallback(zeroCrossings!,
455-
affectStateEvent!, eh.nz, interp_points=m.options.interp_points, rootfind=DifferentialEquations.SciMLBase.RightRootFind)
510+
affectStateEvent!, eh.nz, interp_points=options.interp_points, rootfind=DifferentialEquations.SciMLBase.RightRootFind)
456511
#callback4 = DifferentialEquations.PresetTimeCallback(tspan2, affect_outputs!)
457512
callbacks = DifferentialEquations.CallbackSet(callback1, callback2, callback3) #, callback4)
458513
else
@@ -463,24 +518,24 @@ function simulateSegment!(m::SimulationModel{FloatType,TimeType}, algorithm=miss
463518

464519
# Initial step size (the default of DifferentialEquations integrators is too large) + step-size of fixed-step algorithm
465520
if !m.sundials
466-
dt = m.options.adaptive ? m.options.interval/10 : m.options.interval # initial step-size
521+
dt = options.adaptive ? options.interval/10 : options.interval # initial step-size
467522
end
468523

469524
# Compute solution
470-
abstol = 0.1*m.options.tolerance
525+
abstol = 0.1*options.tolerance
471526
tstops = (m.eventHandler.nextEventTime,)
472527
maxiters = Int(typemax(Int32)) # switch off maximum number of iterations (typemax(Int) gives an inexact error for Sundials)
473528
if ismissing(algorithm)
474-
TimerOutputs.@timeit m.timer "DifferentialEquations.solve" solution = DifferentialEquations.solve(problem, reltol=m.options.tolerance, abstol=abstol, save_everystep=false,
475-
callback=callbacks, adaptive=m.options.adaptive, saveat=tspan2, dt=dt, dtmax=m.options.dtmax, maxiters=maxiters, tstops = tstops,
529+
TimerOutputs.@timeit m.timer "DifferentialEquations.solve" solution = DifferentialEquations.solve(problem, reltol=options.tolerance, abstol=abstol, save_everystep=false,
530+
callback=callbacks, adaptive=options.adaptive, saveat=tspan2, dt=dt, dtmax=options.dtmax, maxiters=maxiters, tstops = tstops,
476531
initializealg = DifferentialEquations.NoInit())
477532
elseif m.sundials
478-
TimerOutputs.@timeit m.timer "DifferentialEquations.solve" solution = DifferentialEquations.solve(problem, algorithm, reltol=m.options.tolerance, abstol=abstol, save_everystep=false,
479-
callback=callbacks, adaptive=m.options.adaptive, saveat=tspan2, dtmax=m.options.dtmax, maxiters=maxiters, tstops = tstops,
533+
TimerOutputs.@timeit m.timer "DifferentialEquations.solve" solution = DifferentialEquations.solve(problem, algorithm, reltol=options.tolerance, abstol=abstol, save_everystep=false,
534+
callback=callbacks, adaptive=options.adaptive, saveat=tspan2, dtmax=options.dtmax, maxiters=maxiters, tstops = tstops,
480535
initializealg = DifferentialEquations.NoInit())
481536
else
482-
TimerOutputs.@timeit m.timer "DifferentialEquations.solve" solution = DifferentialEquations.solve(problem, algorithm, reltol=m.options.tolerance, abstol=abstol, save_everystep=false,
483-
callback=callbacks, adaptive=m.options.adaptive, saveat=tspan2, dt=dt, dtmax=m.options.dtmax, maxiters=maxiters, tstops = tstops,
537+
TimerOutputs.@timeit m.timer "DifferentialEquations.solve" solution = DifferentialEquations.solve(problem, algorithm, reltol=options.tolerance, abstol=abstol, save_everystep=false,
538+
callback=callbacks, adaptive=options.adaptive, saveat=tspan2, dt=dt, dtmax=options.dtmax, maxiters=maxiters, tstops = tstops,
484539
initializealg = DifferentialEquations.NoInit())
485540
end
486541

test/TestSimpleStateEvents.jl

+3
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,7 @@ simulate!(model, Tsit5(), stopTime = 10, log=false, logEvents=false, requiredFin
2525

2626
plot(model, ["s", "v", "sPos", "f"])
2727

28+
# Test IDA
29+
simulate!(model, IDA(), nlinearMinForDAE=1, stopTime = 10, log=false, logEvents=false, requiredFinalStates = [1.1756992201144405, -0.5351307571761175])
30+
2831
end

test/TestTwoInertiasAndIdealGear.jl

+14-8
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ TwoInertiasAndIdealGearTooManyInits = Model(
99
J2 = 170,
1010
r = 105,
1111
tau_max = 1,
12-
phi1 = Var(init = 0.0),
13-
w1 = Var(init = 1.0),
14-
phi2 = Var(init = 0.5),
12+
phi1 = Var(init = 0.0),
13+
w1 = Var(init = 1.0),
14+
phi2 = Var(init = 0.5),
1515
w2 = Var(init = 0.0),
1616
equations = :[
1717
tau = if time < 1u"s"; tau_max elseif time < 2u"s"; 0 elseif time < 3u"s"; -tau_max else 0 end,
@@ -41,18 +41,24 @@ simulate!(twoInertiasAndIdealGearTooManyInits, Tsit5(), stopTime = 4.0, log=true
4141
simulate!(twoInertiasAndIdealGear, Tsit5(), stopTime = 4.0, log=true,
4242
logParameters=true, logStates=true,
4343
requiredFinalStates=[1.5628074713622309, -6.878080753044174e-5])
44-
44+
4545
plot(twoInertiasAndIdealGear, ["phi2", "w2"])
4646

47+
simulate!(twoInertiasAndIdealGear, IDA(), stopTime = 4.0, log=true,
48+
logParameters=false, logStates=false, nlinearMinForDAE=1,
49+
requiredFinalStates=[1.5628074713622309, -6.878080753044174e-5])
50+
4751
simulate!(twoInertiasAndIdealGear, stopTime = 4.0,
4852
useRecursiveFactorizationUptoSize = 500,
49-
log=true, logParameters=true, logStates=true,
50-
requiredFinalStates=missing)
53+
log=true, logParameters=false, logStates=false,
54+
requiredFinalStates=[1.5628074713622309, -6.878080753044174e-5])
55+
56+
5157
# Linearize
5258
println("\n... Linearize at stopTime = 0 and 4")
5359
(A_0, x_0) = linearize!(twoInertiasAndIdealGear, stopTime=0, analytic = true)
54-
(A_4, x_4) = linearize!(twoInertiasAndIdealGear, stopTime=4, analytic = true)
55-
(A_4_numeric, x_4_numeric) = linearize!(twoInertiasAndIdealGear, stopTime=4, analytic=false)
60+
(A_4, x_4) = linearize!(twoInertiasAndIdealGear, stopTime=4, analytic = true)
61+
(A_4_numeric, x_4_numeric) = linearize!(twoInertiasAndIdealGear, stopTime=4, analytic=false)
5662

5763
xNames = get_xNames(twoInertiasAndIdealGear)
5864
@show xNames

0 commit comments

Comments
 (0)