Coverage Report

Created: 2024-12-20 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/constants/one.h"   // IWYU pragma: keep
26
#include "num_collect/constants/zero.h"  // IWYU pragma: keep
27
#include "num_collect/integration/gauss_legendre_integrator.h"
28
#include "num_collect/logging/log_tag_view.h"
29
#include "num_collect/logging/logging_macros.h"
30
#include "num_collect/ode/avf/impl/avf_integrand.h"
31
#include "num_collect/ode/concepts/differentiable_problem.h"
32
#include "num_collect/ode/evaluation_type.h"
33
#include "num_collect/ode/non_embedded_formula_wrapper.h"
34
#include "num_collect/ode/simple_solver.h"
35
36
namespace num_collect::ode::avf {
37
38
/*!
39
 * \brief Class of 3rd order average vector field (AVF) method
40
 * \cite Quispel2008.
41
 *
42
 * \tparam Problem Type of problem.
43
 */
44
template <concepts::differentiable_problem Problem>
45
class avf3_formula {
46
public:
47
    //! Type of problem.
48
    using problem_type = Problem;
49
50
    //! Type of variables.
51
    using variable_type = typename problem_type::variable_type;
52
53
    //! Type of scalars.
54
    using scalar_type = typename problem_type::scalar_type;
55
56
    //! Type of Jacobian.
57
    using jacobian_type = typename problem_type::jacobian_type;
58
59
    //! Number of stages of this formula.
60
    static constexpr index_type stages = 1;
61
62
    //! Order of this formula.
63
    static constexpr index_type order = 3;
64
65
    //! Log tag.
66
    static constexpr auto log_tag =
67
        logging::log_tag_view("num_collect::ode::avf::avf3_formula");
68
69
    /*!
70
     * \brief Constructor.
71
     *
72
     * \param[in] problem Problem.
73
     */
74
    explicit avf3_formula(const problem_type& problem = problem_type())
75
4
        : integrand_(problem) {}
76
77
    /*!
78
     * \brief Compute the next variable.
79
     *
80
     * \param[in] time Current time.
81
     * \param[in] step_size Step size.
82
     * \param[in] current Current variable.
83
     * \param[out] estimate Estimate of the next variable.
84
     */
85
    void step(scalar_type time, scalar_type step_size,
86
389
        const variable_type& current, variable_type& estimate) {
87
389
        const auto dim = current.size();
88
89
389
        problem().evaluate_on(time, current,
90
389
            evaluation_type{.diff_coeff = true, .jacobian = true});
91
389
        static constexpr scalar_type coeff_jacobi =
92
389
            -static_cast<scalar_type>(1) / static_cast<scalar_type>(12);
93
389
        const jacobian_type coeff = step_size *
94
389
            (jacobian_type::Identity(dim, dim) +
95
389
                coeff_jacobi * step_size * step_size * problem().jacobian() *
96
389
                    problem().jacobian());
97
98
389
        estimate = current + coeff * problem().diff_coeff();
99
100
389
        integrand_.time(time);
101
389
        integrand_.prev_var(current);
102
389
        variable_type prev_estimate;
103
389
        constexpr index_type max_loops = 10000;
104
1.33k
        for (index_type i = 0; i < max_loops; ++i) {
105
1.33k
            integrand_.next_var(estimate);
106
1.33k
            prev_estimate = estimate;
107
1.33k
            estimate = current +
108
1.33k
                coeff *
109
1.33k
                    integrator_(integrand_, constants::zero<scalar_type>,
110
1.33k
                        constants::one<scalar_type>);
111
1.33k
            if (norm(estimate - prev_estimate) < tol_residual_norm_) {
112
389
                return;
113
389
            }
114
1.33k
        }
115
389
    }
116
117
    /*!
118
     * \brief Get the problem.
119
     *
120
     * \return Problem.
121
     */
122
1.55k
    [[nodiscard]] auto problem() -> problem_type& {
123
1.55k
        return integrand_.problem();
124
1.55k
    }
125
126
    /*!
127
     * \brief Get the problem.
128
     *
129
     * \return Problem.
130
     */
131
    [[nodiscard]] auto problem() const -> const problem_type& {
132
        return integrand_.problem();
133
    }
134
135
    /*!
136
     * \brief Set tolerance of residual norm.
137
     *
138
     * \param[in] val Value.
139
     */
140
    void tol_residual_norm(scalar_type val) {
141
        if (val <= static_cast<scalar_type>(0)) {
142
            NUM_COLLECT_LOG_AND_THROW(invalid_argument,
143
                "Tolerance of residual norm must be a positive value.");
144
        }
145
        tol_residual_norm_ = val;
146
    }
147
148
private:
149
    //! Integrand.
150
    impl::avf_integrand<problem_type> integrand_;
151
152
    //! Degree of integrator.
153
    static constexpr index_type integrator_degree = 5;
154
155
    //! Integrator.
156
    integration::gauss_legendre_integrator<variable_type(scalar_type)>
157
        integrator_{integrator_degree};
158
159
    //! Default tolerance of residual norm.
160
    static constexpr auto default_tol_residual_norm =
161
        static_cast<scalar_type>(1e-8);
162
163
    //! Tolerance of residual norm.
164
    scalar_type tol_residual_norm_{default_tol_residual_norm};
165
};
166
167
/*!
168
 * \brief Class of solver using 3rd order average vector field (AVF) method
169
 * \cite Quispel2008.
170
 *
171
 * \tparam Problem Type of problem.
172
 */
173
template <concepts::differentiable_problem Problem>
174
using avf3_solver = simple_solver<avf3_formula<Problem>>;
175
176
/*!
177
 * \brief Class of solver using 3rd order average vector field (AVF) method
178
 * \cite Quispel2008 with automatic step sizes.
179
 *
180
 * \tparam Problem Type of problem.
181
 */
182
template <concepts::differentiable_problem Problem>
183
using avf3_auto_solver = non_embedded_auto_solver<avf3_formula<Problem>>;
184
185
}  // namespace num_collect::ode::avf