Coverage Report

Created: 2025-01-24 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/builds/MusicScience37Projects/numerical-analysis/numerical-collection-cpp/include/num_collect/ode/avf/avf3_formula.h
Line
Count
Source
1
/*
2
 * Copyright 2021 MusicScience37 (Kenta Kabashima)
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
/*!
17
 * \file
18
 * \brief Definition of avf3_formula class.
19
 */
20
#pragma once
21
22
#include "num_collect/base/exception.h"
23
#include "num_collect/base/index_type.h"
24
#include "num_collect/base/norm.h"
25
#include "num_collect/base/precondition.h"
26
#include "num_collect/constants/one.h"   // IWYU pragma: keep
27
#include "num_collect/constants/zero.h"  // IWYU pragma: keep
28
#include "num_collect/integration/gauss_legendre_integrator.h"
29
#include "num_collect/logging/log_tag_view.h"
30
#include "num_collect/logging/logging_macros.h"
31
#include "num_collect/ode/avf/impl/avf_integrand.h"
32
#include "num_collect/ode/concepts/differentiable_problem.h"
33
#include "num_collect/ode/evaluation_type.h"
34
#include "num_collect/ode/non_embedded_formula_wrapper.h"
35
#include "num_collect/ode/simple_solver.h"
36
37
namespace num_collect::ode::avf {
38
39
/*!
40
 * \brief Class of 3rd order average vector field (AVF) method
41
 * \cite Quispel2008.
42
 *
43
 * \tparam Problem Type of problem.
44
 */
45
template <concepts::differentiable_problem Problem>
46
class avf3_formula {
47
public:
48
    //! Type of problem.
49
    using problem_type = Problem;
50
51
    //! Type of variables.
52
    using variable_type = typename problem_type::variable_type;
53
54
    //! Type of scalars.
55
    using scalar_type = typename problem_type::scalar_type;
56
57
    //! Type of Jacobian.
58
    using jacobian_type = typename problem_type::jacobian_type;
59
60
    //! Number of stages of this formula.
61
    static constexpr index_type stages = 1;
62
63
    //! Order of this formula.
64
    static constexpr index_type order = 3;
65
66
    //! Log tag.
67
    static constexpr auto log_tag =
68
        logging::log_tag_view("num_collect::ode::avf::avf3_formula");
69
70
    /*!
71
     * \brief Constructor.
72
     *
73
     * \param[in] problem Problem.
74
     */
75
    explicit avf3_formula(const problem_type& problem = problem_type())
76
4
        : integrand_(problem) {}
77
78
    /*!
79
     * \brief Compute the next variable.
80
     *
81
     * \param[in] time Current time.
82
     * \param[in] step_size Step size.
83
     * \param[in] current Current variable.
84
     * \param[out] estimate Estimate of the next variable.
85
     */
86
    void step(scalar_type time, scalar_type step_size,
87
389
        const variable_type& current, variable_type& estimate) {
88
389
        const auto dim = current.size();
89
90
389
        problem().evaluate_on(time, current,
91
389
            evaluation_type{.diff_coeff = true, .jacobian = true});
92
389
        static constexpr scalar_type coeff_jacobi =
93
389
            -static_cast<scalar_type>(1) / static_cast<scalar_type>(12);
94
389
        const jacobian_type coeff = step_size *
95
389
            (jacobian_type::Identity(dim, dim) +
96
389
                coeff_jacobi * step_size * step_size * problem().jacobian() *
97
389
                    problem().jacobian());
98
99
389
        estimate = current + coeff * problem().diff_coeff();
100
101
389
        integrand_.time(time);
102
389
        integrand_.prev_var(current);
103
389
        variable_type prev_estimate;
104
389
        constexpr index_type max_loops = 10000;
105
1.33k
        for (index_type i = 0; i < max_loops; ++i) {
106
1.33k
            integrand_.next_var(estimate);
107
1.33k
            prev_estimate = estimate;
108
1.33k
            estimate = current +
109
1.33k
                coeff *
110
1.33k
                    integrator_(integrand_, constants::zero<scalar_type>,
111
1.33k
                        constants::one<scalar_type>);
112
1.33k
            if (norm(estimate - prev_estimate) < tol_residual_norm_) {
113
389
                return;
114
389
            }
115
1.33k
        }
116
389
    }
117
118
    /*!
119
     * \brief Get the problem.
120
     *
121
     * \return Problem.
122
     */
123
1.55k
    [[nodiscard]] auto problem() -> problem_type& {
124
1.55k
        return integrand_.problem();
125
1.55k
    }
126
127
    /*!
128
     * \brief Get the problem.
129
     *
130
     * \return Problem.
131
     */
132
    [[nodiscard]] auto problem() const -> const problem_type& {
133
        return integrand_.problem();
134
    }
135
136
    /*!
137
     * \brief Set tolerance of residual norm.
138
     *
139
     * \param[in] val Value.
140
     */
141
    void tol_residual_norm(scalar_type val) {
142
        NUM_COLLECT_PRECONDITION(val > static_cast<scalar_type>(0),
143
            "Tolerance of residual norm must be a positive value.");
144
        tol_residual_norm_ = val;
145
    }
146
147
private:
148
    //! Integrand.
149
    impl::avf_integrand<problem_type> integrand_;
150
151
    //! Degree of integrator.
152
    static constexpr index_type integrator_degree = 5;
153
154
    //! Integrator.
155
    integration::gauss_legendre_integrator<variable_type(scalar_type)>
156
        integrator_{integrator_degree};
157
158
    //! Default tolerance of residual norm.
159
    static constexpr auto default_tol_residual_norm =
160
        static_cast<scalar_type>(1e-8);
161
162
    //! Tolerance of residual norm.
163
    scalar_type tol_residual_norm_{default_tol_residual_norm};
164
};
165
166
/*!
167
 * \brief Class of solver using 3rd order average vector field (AVF) method
168
 * \cite Quispel2008.
169
 *
170
 * \tparam Problem Type of problem.
171
 */
172
template <concepts::differentiable_problem Problem>
173
using avf3_solver = simple_solver<avf3_formula<Problem>>;
174
175
/*!
176
 * \brief Class of solver using 3rd order average vector field (AVF) method
177
 * \cite Quispel2008 with automatic step sizes.
178
 *
179
 * \tparam Problem Type of problem.
180
 */
181
template <concepts::differentiable_problem Problem>
182
using avf3_auto_solver = non_embedded_auto_solver<avf3_formula<Problem>>;
183
184
}  // namespace num_collect::ode::avf