numerical-collection-cpp 0.10.0
A collection of algorithms in numerical analysis implemented in C++
Loading...
Searching...
No Matches
initial_step_size_calculator.h
Go to the documentation of this file.
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 */
20#pragma once
21
22#include <algorithm>
23#include <cmath> // IWYU pragma: keep
24#include <tuple>
25
34
35namespace num_collect::ode {
36
39 logging::log_tag_view("num_collect::ode::initial_step_size_calculator");
40
46template <concepts::formula Formula>
48public:
50 using formula_type = Formula;
51
53 using problem_type = typename formula_type::problem_type;
54
56 using variable_type = typename problem_type::variable_type;
57
59 using scalar_type = typename problem_type::scalar_type;
60
66
77 [[nodiscard]] auto calculate(problem_type& problem,
78 const scalar_type& initial_time, const variable_type& initial_variable,
80 const error_tolerances<variable_type>& tolerances) const
81 -> scalar_type {
82 problem.evaluate_on(initial_time, initial_variable,
83 evaluation_type{.diff_coeff = true});
84 const variable_type initial_diff = problem.diff_coeff();
85
86 const auto [step_size_from_diff, initial_diff_norm] =
88 initial_variable, initial_diff, tolerances);
89
90 const scalar_type step_size_from_second_diff =
91 calculate_step_size_from_second_diff(problem, initial_time,
92 initial_variable, initial_diff, step_size_from_diff,
93 initial_diff_norm, tolerances);
94
95 const scalar_type step_size_without_limit =
96 std::min(1e+2 * step_size_from_diff, step_size_from_second_diff);
98 "Selection of step size without limits: {}",
99 step_size_without_limit);
100
101 const scalar_type final_step_size =
102 limits.apply(step_size_without_limit);
104 "Final selection of step size: {}", step_size_without_limit);
105
106 return final_step_size;
107 }
108
109private:
120 const variable_type& initial_variable,
121 const variable_type& initial_diff,
122 const error_tolerances<variable_type>& tolerances) const
123 -> std::tuple<scalar_type, scalar_type> {
124 // d0 in Hairer1993
125 const scalar_type initial_variable_norm =
126 tolerances.calc_norm(initial_variable, initial_variable);
128 this->logger(), "Norm of variable: {}", initial_variable_norm);
129
130 // d1 in Hairer1993
131 const scalar_type initial_diff_norm =
132 tolerances.calc_norm(initial_variable, initial_diff);
134 this->logger(), "Norm of first derivative: {}", initial_diff_norm);
135
136 // h0 in Hairer1993
137 const scalar_type step_size_from_diff =
138 (initial_variable_norm >= static_cast<scalar_type>(1e-5) &&
139 initial_diff_norm >= static_cast<scalar_type>(1e-5))
140 ? static_cast<scalar_type>(1e-2) * initial_variable_norm /
141 initial_diff_norm
142 : static_cast<scalar_type>(1e-6);
144 "First estimate of step size using differential coefficient: {}",
145 step_size_from_diff);
146
147 return {step_size_from_diff, initial_diff_norm};
148 }
149
165 problem_type& problem, const scalar_type& initial_time,
166 const variable_type& initial_variable,
167 const variable_type& initial_diff,
168 const scalar_type& step_size_from_diff,
169 const scalar_type& initial_diff_norm,
170 const error_tolerances<variable_type>& tolerances) const {
171 // y1 in Hairer1993 (Explicit Euler method)
172 const variable_type euler_updated_variable =
173 initial_variable + step_size_from_diff * initial_diff;
174
175 problem.evaluate_on(initial_time + step_size_from_diff,
176 euler_updated_variable, evaluation_type{.diff_coeff = true});
177 const variable_type& euler_updated_diff = problem.diff_coeff();
178
179 // d2 in Hairer1993 (Evaluation of second derivative)
180 const scalar_type second_diff_norm =
181 tolerances.calc_norm(
182 initial_variable, euler_updated_diff - initial_diff) /
183 step_size_from_diff;
185 this->logger(), "Norm of second derivative: {}", second_diff_norm);
186
187 const scalar_type larger_norm =
188 std::max(initial_diff_norm, second_diff_norm);
189 constexpr scalar_type exponent_of_order = static_cast<scalar_type>(1) /
190 static_cast<scalar_type>(
192 // h1 in Hairer1993
193 const scalar_type step_size_from_second_diff =
194 (larger_norm > static_cast<scalar_type>(1e-15))
195 ? std::pow(static_cast<scalar_type>(1e-2) / larger_norm,
196 exponent_of_order)
197 : std::max(static_cast<scalar_type>(1e-6),
198 static_cast<scalar_type>(1e-3) * step_size_from_diff);
200 "Second estimate of step size using second derivative: {}",
201 step_size_from_second_diff);
202
203 return step_size_from_second_diff;
204 }
205};
206
207} // namespace num_collect::ode
Class of tags of logs without memory management.
Class to incorporate logging in algorithms.
logging_mixin(log_tag_view tag)
Constructor.
auto logger() const noexcept -> const num_collect::logging::logger &
Access to the logger.
Class of error tolerances hairer1993.
Class to calculate initial step sizes hairer1993.
auto calculate_step_size_from_diff(const variable_type &initial_variable, const variable_type &initial_diff, const error_tolerances< variable_type > &tolerances) const -> std::tuple< scalar_type, scalar_type >
Calculate a step size from differential coefficients at the initial time.
typename formula_type::problem_type problem_type
Type of problem.
auto calculate_step_size_from_second_diff(problem_type &problem, const scalar_type &initial_time, const variable_type &initial_variable, const variable_type &initial_diff, const scalar_type &step_size_from_diff, const scalar_type &initial_diff_norm, const error_tolerances< variable_type > &tolerances) const
Calculate a step size using a estimate of the second derivative.
typename problem_type::scalar_type scalar_type
Type of scalars.
auto calculate(problem_type &problem, const scalar_type &initial_time, const variable_type &initial_variable, const step_size_limits< scalar_type > &limits, const error_tolerances< variable_type > &tolerances) const -> scalar_type
Calculate the initial step size.
typename problem_type::variable_type variable_type
Type of variables.
Class of limits of step sizes.
Definition of error_tolerances class.
Definition of evaluation_type enumeration.
Definition of formula concept.
Definition of get_least_known_order function.
Definition of log_tag_view class.
Definition of macros for logging.
#define NUM_COLLECT_LOG_TRACE(LOGGER,...)
Write a trace log.
Definition of logging_mixin class.
constexpr auto get_least_known_order() -> index_type
Get the least known order of a formula.
Namespace of solvers of ordinary differential equations (ODE).
constexpr auto initial_step_size_calculator_log_tag
Log tag.
Definition of step_size_limits class.
Struct to specify types of evaluations.