@@ -248,7 +248,7 @@ called dummy derivatives.
248
248
State selection is done. All non-differentiated variables are algebraic
249
249
variables, and all variables that appear differentiated are differential variables.
250
250
"""
251
- function substitute_derivatives_algevars! (ts:: TearingState , neweqs, dummy_sub, var_eq_matching )
251
+ function substitute_derivatives_algevars! (ts:: TearingState , neweqs, var_eq_matching, dummy_sub )
252
252
@unpack fullvars, sys, structure = ts
253
253
@unpack solvable_graph, var_to_diff, eq_to_diff, graph = structure
254
254
diff_to_var = invview (var_to_diff)
@@ -353,30 +353,32 @@ Effects on the system structure:
353
353
- solvable_graph:
354
354
- var_eq_matching: match D(x) to the added identity equation
355
355
"""
356
- function generate_derivative_variables! (ts:: TearingState , neweqs, var_eq_matching;
357
- is_discrete = false , mm = nothing )
356
+ function generate_derivative_variables! (ts:: TearingState , neweqs, var_eq_matching; mm = nothing )
358
357
@unpack fullvars, sys, structure = ts
359
358
@unpack solvable_graph, var_to_diff, eq_to_diff, graph = structure
360
359
eq_var_matching = invview (var_eq_matching)
361
360
diff_to_var = invview (var_to_diff)
362
361
iv = ModelingToolkit. has_iv (sys) ? ModelingToolkit. get_iv (sys) : nothing
363
- lower_name = is_discrete ? lower_varname_withshift : lower_varname_with_unit
364
-
362
+ is_discrete = is_only_discrete (structure)
363
+ lower_varname = is_discrete ? lower_shift_varname : lower_varname_with_unit
365
364
linear_eqs = mm === nothing ? Dict {Int, Int} () :
366
365
Dict (reverse (en) for en in enumerate (mm. nzrows))
367
366
368
- # Generate new derivative variables for all unsolved variables that have a derivative in the system
367
+ # For variable x, make dummy derivative x_t if the
368
+ # derivative is in the system
369
369
for v in 1 : length (var_to_diff)
370
- # Check if a derivative 1) exists and 2) is unsolved for
371
370
dv = var_to_diff[v]
371
+ # For discrete systems, directly substitute lowest-order variable
372
+ if is_discrete && diff_to_var[v] == nothing
373
+ fullvars[v] = lower_varname (fullvars[v], iv)
374
+ end
372
375
dv isa Int || continue
373
376
solved = var_eq_matching[dv] isa Int
374
377
solved && continue
375
378
376
379
# If there's `D(x) = x_t` already, update mappings and continue without
377
380
# adding new equations/variables
378
- dd = find_duplicate_dd (dv, lineareqs, mm)
379
-
381
+ dd = find_duplicate_dd (dv, solvable_graph, linear_eqs, mm)
380
382
if ! isnothing (dd)
381
383
dummy_eq, v_t = dd
382
384
var_to_diff[v_t] = var_to_diff[dv]
@@ -386,26 +388,25 @@ function generate_derivative_variables!(ts::TearingState, neweqs, var_eq_matchin
386
388
end
387
389
388
390
dx = fullvars[dv]
389
- order, lv = var_order (diff_to_var, dv)
390
- x_t = is_discrete ? lower_name (fullvars[lv], iv)
391
- : lower_name (fullvars[lv], iv, order)
392
-
391
+ order, lv = var_order (dv, diff_to_var)
392
+ x_t = is_discrete ? lower_varname (fullvars[dv], iv) : lower_varname (fullvars[lv], iv, order)
393
+
393
394
# Add `x_t` to the graph
394
- add_dd_variable! (structure, x_t, dv)
395
+ v_t = add_dd_variable! (structure, fullvars , x_t, dv)
395
396
# Add `D(x) - x_t ~ 0` to the graph
396
- add_dd_equation! (structure, neweqs, 0 ~ dx - x_t, dv)
397
+ dummy_eq = add_dd_equation! (structure, neweqs, 0 ~ dx - x_t, dv, v_t )
397
398
398
399
# Update matching
399
400
push! (var_eq_matching, unassigned)
400
401
var_eq_matching[dv] = unassigned
401
- eq_var_matching[dummy_eq] = dv
402
+ eq_var_matching[dummy_eq] = dv
402
403
end
403
404
end
404
405
405
406
"""
406
- Check if there's `D(x) = x_t` already.
407
+ Check if there's `D(x) = x_t` already.
407
408
"""
408
- function find_duplicate_dd (dv, lineareqs , mm)
409
+ function find_duplicate_dd (dv, solvable_graph, linear_eqs , mm)
409
410
for eq in 𝑑neighbors (solvable_graph, dv)
410
411
mi = get (linear_eqs, eq, 0 )
411
412
iszero (mi) && continue
@@ -424,28 +425,28 @@ function find_duplicate_dd(dv, lineareqs, mm)
424
425
return nothing
425
426
end
426
427
427
- function add_dd_variable! (s:: SystemStructure , x_t, dv)
428
- push! (s. fullvars, simplify_shifts (x_t))
428
+ function add_dd_variable! (s:: SystemStructure , fullvars, x_t, dv)
429
+ push! (fullvars, simplify_shifts (x_t))
430
+ v_t = length (fullvars)
429
431
v_t_idx = add_vertex! (s. var_to_diff)
430
- @assert v_t_idx == ndsts (graph) == ndsts (solvable_graph) == length (fullvars) ==
431
- length (var_eq_matching)
432
432
add_vertex! (s. graph, DST)
433
433
# TODO : do we care about solvable_graph? We don't use them after
434
434
# `dummy_derivative_graph`.
435
435
add_vertex! (s. solvable_graph, DST)
436
- var_to_diff[v_t] = var_to_diff[dv]
436
+ s. var_to_diff[v_t] = s. var_to_diff[dv]
437
+ v_t
437
438
end
438
439
439
440
# dv = index of D(x), v_t = index of x_t
440
- function add_dd_equation! (s:: SystemStructure , neweqs, eq, dv)
441
+ function add_dd_equation! (s:: SystemStructure , neweqs, eq, dv, v_t )
441
442
push! (neweqs, eq)
442
443
add_vertex! (s. graph, SRC)
443
- v_t = length (s. fullvars)
444
444
dummy_eq = length (neweqs)
445
445
add_edge! (s. graph, dummy_eq, dv)
446
446
add_edge! (s. graph, dummy_eq, v_t)
447
447
add_vertex! (s. solvable_graph, SRC)
448
448
add_edge! (s. solvable_graph, dummy_eq, dv)
449
+ dummy_eq
449
450
end
450
451
451
452
"""
@@ -463,6 +464,10 @@ function generate_system_equations!(state::TearingState, neweqs, var_eq_matching
463
464
iv = get_iv (sys)
464
465
if is_only_discrete (structure)
465
466
D = Shift (iv, 1 )
467
+ for v in fullvars
468
+ op = operation (v)
469
+ op isa Shift && (op. steps < 0 ) && (total_sub[v] = lower_shift_varname (v, iv))
470
+ end
466
471
else
467
472
D = Differential (iv)
468
473
end
@@ -493,24 +498,40 @@ function generate_system_equations!(state::TearingState, neweqs, var_eq_matching
493
498
eqs = Iterators. reverse (toporder)
494
499
idep = iv
495
500
496
- # Generate differential equations.
497
- # fullvars[iv] is a differential variable of the form D^n(x), and neweqs[ieq]
498
- # is solved to give the RHS.
501
+ # Generate equations.
502
+ # Solvable equations of differential variables D(x) become differential equations
503
+ # Solvable equations of non-differential variables become observable equations
504
+ # Non-solvable equations become algebraic equations.
499
505
for ieq in eqs
500
506
iv = eq_var_matching[ieq]
501
- if is_solvable (ieq, iv)
502
- if isdervar (iv)
503
- isnothing (D) &&
504
- error (" Differential found in a non-differential system. Likely this is a bug in the construction of an initialization system. Please report this issue with a reproducible example. Offending equation: $(equations (sys)[ieq]) " )
505
- add_differential_equation! (structure, iv, neweqs, ieq,
506
- diff_vars, diff_eqs, diffeq_idxs, total_sub)
507
- else
508
- add_solved_equation! (structure, iv, neweqs, ieq,
509
- solved_vars, solved_eqs, solvedeq_idxs, total_sub)
507
+ var = fullvars[iv]
508
+ eq = neweqs[ieq]
509
+
510
+ if is_solvable (ieq, iv) && isdervar (iv)
511
+ isnothing (D) && throw (UnexpectedDifferentialError (equations (sys)[ieq]))
512
+ order, lv = var_order (iv, diff_to_var)
513
+ dx = D (simplify_shifts (fullvars[lv]))
514
+
515
+ neweq = make_differential_equation (var, dx, eq, total_sub)
516
+ for e in 𝑑neighbors (graph, iv)
517
+ e == ieq && continue
518
+ rem_edge! (graph, e, iv)
519
+ end
520
+
521
+ push! (diff_eqs, neweq)
522
+ push! (diffeq_idxs, ieq)
523
+ push! (diff_vars, diff_to_var[iv])
524
+ elseif is_solvable (ieq, iv)
525
+ neweq = make_solved_equation (var, eq, total_sub; simplify)
526
+ ! isnothing (neweq) && begin
527
+ push! (solved_eqs, neweq)
528
+ push! (solvedeq_idxs, ieq)
529
+ push! (solved_vars, iv)
510
530
end
511
531
else
512
- add_algebraic_equation! (structure, neweqs, ieq,
513
- alge_eqs, algeeq_idxs, total_sub)
532
+ neweq = make_algebraic_equation (var, eq, total_sub)
533
+ push! (alge_eqs, neweq)
534
+ push! (algeeq_idxs, ieq)
514
535
end
515
536
end
516
537
@@ -529,55 +550,43 @@ function generate_system_equations!(state::TearingState, neweqs, var_eq_matching
529
550
return neweqs, solved_eqs, eq_ordering, var_ordering, length (solved_vars), length (solved_vars_set)
530
551
end
531
552
532
- function add_differential_equation! (s:: SystemStructure , iv, neweqs, ieq, diff_vars, diff_eqs, diffeqs_idxs, total_sub)
533
- diff_to_var = invview (s. var_to_diff)
553
+ struct UnexpectedDifferentialError
554
+ eq:: Equation
555
+ end
534
556
535
- order, lv = var_order (diff_to_var, iv)
536
- dx = D (simplify_shifts (fullvars[lv]))
537
- eq = dx ~ simplify_shifts (Symbolics. fixpoint_sub (
538
- Symbolics. symbolic_linear_solve (neweqs[ieq],
539
- fullvars[iv]),
540
- total_sub; operator = ModelingToolkit. Shift))
541
- for e in 𝑑neighbors (s. graph, iv)
542
- e == ieq && continue
543
- rem_edge! (s. graph, e, iv)
544
- end
557
+ function Base. showerror (io:: IO , err:: UnexpectedDifferentialError )
558
+ error (" Differential found in a non-differential system. Likely this is a bug in the construction of an initialization system. Please report this issue with a reproducible example. Offending equation: $(err. eq) " )
559
+ end
545
560
546
- push! (diff_eqs, eq )
547
- total_sub[ simplify_shifts (eq . lhs)] = eq . rhs
548
- push! (diffeq_idxs, ieq)
549
- push! (diff_vars, diff_to_var[iv] )
561
+ function make_differential_equation (var, dx, eq, total_sub )
562
+ dx ~ simplify_shifts (Symbolics . fixpoint_sub (
563
+ Symbolics . symbolic_linear_solve (eq, var),
564
+ total_sub; operator = ModelingToolkit . Shift) )
550
565
end
551
566
552
- function add_algebraic_equation! (s:: SystemStructure , neweqs, ieq, alge_eqs, algeeq_idxs, total_sub)
553
- eq = neweqs[ieq]
567
+ function make_algebraic_equation (var, eq, total_sub)
554
568
rhs = eq. rhs
555
569
if ! (eq. lhs isa Number && eq. lhs == 0 )
556
570
rhs = eq. rhs - eq. lhs
557
571
end
558
- push! (alge_eqs, 0 ~ simplify_shifts (Symbolics. fixpoint_sub (rhs, total_sub)))
559
- push! (algeeq_idxs, ieq)
572
+ 0 ~ simplify_shifts (Symbolics. fixpoint_sub (rhs, total_sub))
560
573
end
561
574
562
- function add_solved_equation! (s:: SystemStructure , iv, neweqs, ieq, solved_vars, solved_eqs, solvedeq_idxs, total_sub)
563
- eq = neweqs[ieq]
564
- var = fullvars[iv]
575
+ function make_solved_equation (var, eq, total_sub; simplify = false )
565
576
residual = eq. lhs - eq. rhs
566
577
a, b, islinear = linear_expansion (residual, var)
567
578
@assert islinear
568
579
# 0 ~ a * var + b
569
580
# var ~ -b/a
570
581
if ModelingToolkit. _iszero (a)
571
582
@warn " Tearing: solving $eq for $var is singular!"
583
+ return nothing
572
584
else
573
585
rhs = - b / a
574
- neweq = var ~ simplify_shifts (Symbolics. fixpoint_sub (
586
+ return var ~ simplify_shifts (Symbolics. fixpoint_sub (
575
587
simplify ?
576
588
Symbolics. simplify (rhs) : rhs,
577
589
total_sub; operator = ModelingToolkit. Shift))
578
- push! (solved_eqs, neweq)
579
- push! (solvedeq_idxs, ieq)
580
- push! (solved_vars, iv)
581
590
end
582
591
end
583
592
@@ -592,8 +601,8 @@ such that the mass matrix is:
592
601
Update the state to account for the new ordering and equations.
593
602
"""
594
603
# TODO : BLT sorting
595
- function reorder_vars! (s :: SystemStructure , var_eq_matching, eq_ordering, var_ordering, nelim_eq, nelim_var)
596
- @unpack solvable_graph, var_to_diff, eq_to_diff, graph = structure
604
+ function reorder_vars! (state :: TearingState , var_eq_matching, eq_ordering, var_ordering, nelim_eq, nelim_var)
605
+ @unpack solvable_graph, var_to_diff, eq_to_diff, graph = state . structure
597
606
598
607
eqsperm = zeros (Int, nsrcs (graph))
599
608
for (i, v) in enumerate (eq_ordering)
@@ -623,20 +632,26 @@ function reorder_vars!(s::SystemStructure, var_eq_matching, eq_ordering, var_ord
623
632
d′ = eqsperm[d]
624
633
new_eq_to_diff[v′] = d′ > 0 ? d′ : nothing
625
634
end
626
- new_fullvars = fullvars[invvarsperm ]
635
+ new_fullvars = state . fullvars[var_ordering ]
627
636
637
+ @show new_graph
638
+ @show new_var_to_diff
628
639
# Update system structure
629
640
@set! state. structure. graph = complete (new_graph)
630
641
@set! state. structure. var_to_diff = new_var_to_diff
631
642
@set! state. structure. eq_to_diff = new_eq_to_diff
632
643
@set! state. fullvars = new_fullvars
644
+ state
633
645
end
634
646
635
647
"""
636
648
Set the system equations, unknowns, observables post-tearing.
637
649
"""
638
- function update_simplified_system! (state:: TearingState , neweqs, solved_eqs, dummy_sub, var_eq_matching, extra_unknowns)
650
+ function update_simplified_system! (state:: TearingState , neweqs, solved_eqs, dummy_sub, var_eq_matching, extra_unknowns;
651
+ cse_hack = true , array_hack = true )
639
652
@unpack solvable_graph, var_to_diff, eq_to_diff, graph = state. structure
653
+ @show graph
654
+ @show var_to_diff
640
655
diff_to_var = invview (var_to_diff)
641
656
642
657
ispresent = let var_to_diff = var_to_diff, graph = graph
@@ -656,7 +671,12 @@ function update_simplified_system!(state::TearingState, neweqs, solved_eqs, dumm
656
671
unknowns = Any[v
657
672
for (i, v) in enumerate (state. fullvars)
658
673
if diff_to_var[i] === nothing && ispresent (i)]
674
+ @show unknowns
675
+ @show state. fullvars
676
+ @show 𝑑neighbors (graph, 5 )
677
+ @show neweqs
659
678
unknowns = [unknowns; extra_unknowns]
679
+ @show unknowns
660
680
@set! sys. unknowns = unknowns
661
681
662
682
obs, subeqs, deps = cse_and_array_hacks (
684
704
# differential variables.
685
705
686
706
# Give the order of the variable indexed by dv
687
- function var_order (diff_to_var, dv )
707
+ function var_order (dv, diff_to_var )
688
708
order = 0
689
709
while (dv′ = diff_to_var[dv]) != = nothing
690
710
order += 1
695
715
696
716
function tearing_reassemble (state:: TearingState , var_eq_matching,
697
717
full_var_eq_matching = nothing ; simplify = false , mm = nothing , cse_hack = true , array_hack = true )
698
-
699
718
extra_vars = Int[]
700
719
if full_var_eq_matching != = nothing
701
720
for v in 𝑑vertices (state. structure. graph)
@@ -704,21 +723,22 @@ function tearing_reassemble(state::TearingState, var_eq_matching,
704
723
push! (extra_vars, v)
705
724
end
706
725
end
707
- extra_unknowns = fullvars[extra_vars]
726
+ extra_unknowns = state . fullvars[extra_vars]
708
727
neweqs = collect (equations (state))
728
+ dummy_sub = Dict ()
709
729
710
730
# Structural simplification
711
- dummy_sub = Dict ()
712
- substitute_derivatives_algevars! (state, neweqs, dummy_sub, var_eq_matching)
731
+ substitute_derivatives_algevars! (state, neweqs, var_eq_matching, dummy_sub)
713
732
714
733
generate_derivative_variables! (state, neweqs, var_eq_matching; mm)
715
734
716
735
neweqs, solved_eqs, eq_ordering, var_ordering, nelim_eq, nelim_var =
717
736
generate_system_equations! (state, neweqs, var_eq_matching; simplify)
718
737
719
- reorder_vars! (state. structure, var_eq_matching, eq_ordering, var_ordering, nelim_eq, nelim_var)
738
+ state = reorder_vars! (state, var_eq_matching, eq_ordering, var_ordering, nelim_eq, nelim_var)
739
+
740
+ sys = update_simplified_system! (state, neweqs, solved_eqs, dummy_sub, var_eq_matching, extra_unknowns; cse_hack, array_hack)
720
741
721
- sys = update_simplified_system! (state, neweqs, solved_eqs, dummy_sub, var_eq_matching, extra_unknowns)
722
742
@set! state. sys = sys
723
743
@set! sys. tearing_state = state
724
744
return invalidate_cache! (sys)
0 commit comments