|
| 1 | +import ODEConvergenceTester as OCT |
| 2 | +import OrdinaryDiffEq as ODE |
1 | 3 |
|
2 | 4 | """ |
3 | 5 | DirectSolver |
@@ -47,3 +49,84 @@ function convergence_order(prob, sol, method, dts; kwargs...) |
47 | 49 | _, order_est = hcat(ones(length(dts)), log2.(dts)) \ log2.(errs) |
48 | 50 | return order_est |
49 | 51 | end |
| 52 | + |
| 53 | +function default_expected_order(alg, tab) |
| 54 | + return if tab isa CTS.AbstractIMEXARKTableau |
| 55 | + CTS.theoretical_convergence_order(tab) |
| 56 | + else |
| 57 | + ODE.alg_order(alg) |
| 58 | + end |
| 59 | +end |
| 60 | + |
| 61 | +function test_convergence_order!(test_case, tab, results = Dict(); refinement_range) |
| 62 | + prob, alg = problem_algo(test_case, tab) |
| 63 | + expected_order = default_expected_order(alg, tab()) |
| 64 | + cr = OCT.refinement_study( |
| 65 | + prob, |
| 66 | + alg; |
| 67 | + verbose = false, |
| 68 | + expected_order, |
| 69 | + refinement_range, # ::UnitRange, 2:4 is more fine than 1:3 |
| 70 | + ) |
| 71 | + computed_order = maximum(cr.computed_order) |
| 72 | + results[tab, test_case.test_name] = (; expected_order, computed_order) |
| 73 | + return nothing |
| 74 | +end |
| 75 | + |
| 76 | +distance(theoretic, computed) = abs(computed - theoretic) / theoretic |
| 77 | +pass_conv(theoretic, computed) = distance(theoretic, computed) * 100 < 10 |
| 78 | +fail_conv(theoretic, computed) = !pass_conv(computed, theoretic) && !super_conv(theoretic, computed) |
| 79 | +super_conv(theoretic, computed) = (computed - theoretic) / theoretic * 100 > 10 |
| 80 | + |
| 81 | +#= Calls `test_convergence_order!` for each combination of test case |
| 82 | +and algorithm, returns a `Dict` of the results. =# |
| 83 | +function convergence_order_results(tabs, test_cases) |
| 84 | + results = Dict() |
| 85 | + for test_case in test_cases |
| 86 | + @info "------------------ Test case: $(test_case.test_name)" |
| 87 | + for tab in tabs |
| 88 | + # @info "Running refinement study on $(nameof(tab))" |
| 89 | + test_convergence_order!(test_case, tab, results; refinement_range = 5:9) |
| 90 | + end |
| 91 | + end |
| 92 | + return results |
| 93 | +end |
| 94 | + |
| 95 | +function tabulate_convergence_orders(test_cases, tabs, results) |
| 96 | + columns = map(test_cases) do test_case |
| 97 | + map(tab -> results[tab, test_case.test_name], tabs) |
| 98 | + end |
| 99 | + expected_order = map(tab -> default_expected_order(nothing, tab()), tabs) |
| 100 | + tab_names = map(tab -> "$tab ($(default_expected_order(nothing, tab())))", tabs) |
| 101 | + data = hcat(columns...) |
| 102 | + summary(result) = result.computed_order |
| 103 | + data_summary = map(d -> summary(d), data) |
| 104 | + |
| 105 | + table_data = hcat(tab_names, data_summary) |
| 106 | + precentage_fail = sum(fail_conv.(getindex.(data, 1), getindex.(data, 2))) / length(data) * 100 |
| 107 | + @info "Percentage of failed convergence order tests: $precentage_fail" |
| 108 | + fail_conv_hl = PrettyTables.Highlighter( |
| 109 | + (data, i, j) -> j ≠ 1 && fail_conv(expected_order[i], data[i, j]), |
| 110 | + PrettyTables.crayon"red bold", |
| 111 | + ) |
| 112 | + super_conv_hl = PrettyTables.Highlighter( |
| 113 | + (data, i, j) -> j ≠ 1 && super_conv(expected_order[i], data[i, j]), |
| 114 | + PrettyTables.crayon"yellow bold", |
| 115 | + ) |
| 116 | + tab_column_hl = PrettyTables.Highlighter((data, i, j) -> j == 1, PrettyTables.crayon"green bold") |
| 117 | + test_case_names = map(test_case -> test_case.test_name, test_cases) |
| 118 | + |
| 119 | + header = (["Tableau (theoretic)", test_case_names...], |
| 120 | + # ["", ["" for tc in test_case_names]...], |
| 121 | + ) |
| 122 | + |
| 123 | + PrettyTables.pretty_table( |
| 124 | + table_data; |
| 125 | + header_crayon = PrettyTables.crayon"green bold", |
| 126 | + highlighters = (tab_column_hl, fail_conv_hl, super_conv_hl), |
| 127 | + title = "Computed convergence orders, red=fail, yellow=super-convergence", |
| 128 | + header, |
| 129 | + alignment = :c, |
| 130 | + crop = :none, |
| 131 | + ) |
| 132 | +end |
0 commit comments