mirror of
				https://github.com/saitohirga/WSJT-X.git
				synced 2025-10-27 11:00:32 -04:00 
			
		
		
		
	
		
			
	
	
		
			135 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			135 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
|  | /*
 | ||
|  |  * find_crossing.cpp | ||
|  |  * | ||
|  |  * Finds the energy threshold crossing for a damped oscillator. | ||
|  |  * The algorithm uses a dense out stepper with find_if to first find an | ||
|  |  * interval containing the threshold crossing and the utilizes the dense out | ||
|  |  * functionality with a bisection to further refine the interval until some | ||
|  |  * desired precision is reached. | ||
|  |  * | ||
|  |  * Copyright 2015 Mario Mulansky | ||
|  |  * | ||
|  |  * Distributed under the Boost Software License, Version 1.0. | ||
|  |  * (See accompanying file LICENSE_1_0.txt or | ||
|  |  * copy at http://www.boost.org/LICENSE_1_0.txt)
 | ||
|  |  */ | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | #include <iostream>
 | ||
|  | #include <utility>
 | ||
|  | #include <algorithm>
 | ||
|  | #include <array>
 | ||
|  | 
 | ||
|  | #include <boost/numeric/odeint/stepper/runge_kutta_dopri5.hpp>
 | ||
|  | #include <boost/numeric/odeint/stepper/generation.hpp>
 | ||
|  | #include <boost/numeric/odeint/iterator/adaptive_iterator.hpp>
 | ||
|  | 
 | ||
|  | namespace odeint = boost::numeric::odeint; | ||
|  | 
 | ||
|  | typedef std::array<double, 2> state_type; | ||
|  | 
 | ||
|  | const double gam = 1.0;  // damping strength
 | ||
|  | 
 | ||
|  | void damped_osc(const state_type &x, state_type &dxdt, const double /*t*/) | ||
|  | { | ||
|  |     dxdt[0] = x[1]; | ||
|  |     dxdt[1] = -x[0] - gam * x[1]; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | struct energy_condition { | ||
|  | 
 | ||
|  |     // defines the threshold crossing in terms of a boolean functor
 | ||
|  | 
 | ||
|  |     double m_min_energy; | ||
|  | 
 | ||
|  |     energy_condition(const double min_energy) | ||
|  |         : m_min_energy(min_energy) { } | ||
|  | 
 | ||
|  |     double energy(const state_type &x) { | ||
|  |         return 0.5 * x[1] * x[1] + 0.5 * x[0] * x[0]; | ||
|  |     } | ||
|  | 
 | ||
|  |     bool operator()(const state_type &x) { | ||
|  |         // becomes true if the energy becomes smaller than the threshold
 | ||
|  |         return energy(x) <= m_min_energy; | ||
|  |     } | ||
|  | }; | ||
|  | 
 | ||
|  | 
 | ||
|  | template<class System, class Condition> | ||
|  | std::pair<double, state_type> | ||
|  | find_condition(state_type &x0, System sys, Condition cond, | ||
|  |                const double t_start, const double t_end, const double dt, | ||
|  |                const double precision = 1E-6) { | ||
|  | 
 | ||
|  |     // integrates an ODE until some threshold is crossed
 | ||
|  |     // returns time and state at the point of the threshold crossing
 | ||
|  |     // if no threshold crossing is found, some time > t_end is returned
 | ||
|  | 
 | ||
|  |     auto stepper = odeint::make_dense_output(1.0e-6, 1.0e-6, | ||
|  |                                              odeint::runge_kutta_dopri5<state_type>()); | ||
|  | 
 | ||
|  |     auto ode_range = odeint::make_adaptive_range(std::ref(stepper), sys, x0, | ||
|  |                                                  t_start, t_end, dt); | ||
|  | 
 | ||
|  |     // find the step where the condition changes
 | ||
|  |     auto found_iter = std::find_if(ode_range.first, ode_range.second, cond); | ||
|  | 
 | ||
|  |     if(found_iter == ode_range.second) | ||
|  |     { | ||
|  |         // no threshold crossing -> return time after t_end and ic
 | ||
|  |         return std::make_pair(t_end + dt, x0); | ||
|  |     } | ||
|  | 
 | ||
|  |     // the dense out stepper now covers the interval where the condition changes
 | ||
|  |     // improve the solution by bisection
 | ||
|  |     double t0 = stepper.previous_time(); | ||
|  |     double t1 = stepper.current_time(); | ||
|  |     double t_m; | ||
|  |     state_type x_m; | ||
|  |     // use odeint's resizing functionality to allocate memory for x_m
 | ||
|  |     odeint::adjust_size_by_resizeability(x_m, x0, | ||
|  |                                          typename odeint::is_resizeable<state_type>::type()); | ||
|  |     while(std::abs(t1 - t0) > precision) { | ||
|  |         t_m = 0.5 * (t0 + t1);  // get the mid point time
 | ||
|  |         stepper.calc_state(t_m, x_m); // obtain the corresponding state
 | ||
|  |         if (cond(x_m)) | ||
|  |             t1 = t_m;  // condition changer lies before midpoint
 | ||
|  |         else | ||
|  |             t0 = t_m;  // condition changer lies after midpoint
 | ||
|  |     } | ||
|  |     // we found the interval of size eps, take it's midpoint as final guess
 | ||
|  |     t_m = 0.5 * (t0 + t1); | ||
|  |     stepper.calc_state(t_m, x_m); | ||
|  |     return std::make_pair(t_m, x_m); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | int main(int argc, char **argv) | ||
|  | { | ||
|  |     state_type x0 = {{10.0, 0.0}}; | ||
|  |     const double t_start = 0.0; | ||
|  |     const double t_end = 10.0; | ||
|  |     const double dt = 0.1; | ||
|  |     const double threshold = 0.1; | ||
|  | 
 | ||
|  |     energy_condition cond(threshold); | ||
|  |     state_type x_cond; | ||
|  |     double t_cond; | ||
|  |     std::tie(t_cond, x_cond) = find_condition(x0, damped_osc, cond, | ||
|  |                                               t_start, t_end, dt, 1E-6); | ||
|  |     if(t_cond > t_end) | ||
|  |     { | ||
|  |         // time after t_end -> no threshold crossing within [t_start, t_end]
 | ||
|  |         std::cout << "No threshold crossing found." << std::endl; | ||
|  |     } else | ||
|  |     { | ||
|  |         std::cout.precision(16); | ||
|  |         std::cout << "Time of energy threshold crossing: " << t_cond << std::endl; | ||
|  |         std::cout << "State: [" << x_cond[0] << " , " << x_cond[1] << "]" << std::endl; | ||
|  |         std::cout << "Energy: " << cond.energy(x_cond) << std::endl; | ||
|  |     } | ||
|  | } |