API
Table of contents
System objects and types
HarmonicBalance.DifferentialEquation Type
mutable struct DifferentialEquation
Holds differential equation(s) of motion and a set of harmonics to expand each variable. This is the primary input for HarmonicBalance.jl
. After inputting the equations, the harmonics ansatz needs to be specified using add_harmonic!
.
Fields
equations::OrderedCollections.OrderedDict{Num, Equation}
: Assigns to each variable an equation of motion.harmonics::OrderedCollections.OrderedDict{Num, OrderedCollections.OrderedSet{Num}}
: Assigns to each variable a set of harmonics.
Example
julia> @variables t, x(t), y(t), ω0, ω, F, k;
# equivalent ways to enter the simple harmonic oscillator
julia> DifferentialEquation(d(x,t,2) + ω0^2 * x - F * cos(ω*t), x);
julia> DifferentialEquation(d(x,t,2) + ω0^2 * x ~ F * cos(ω*t), x);
# two coupled oscillators, one of them driven
julia> DifferentialEquation(
[d(x,t,2) + ω0^2 * x - k*y, d(y,t,2) + ω0^2 * y - k*x] .~ [F * cos(ω*t), 0], [x,y]
);
HarmonicBalance.HarmonicVariable Type
mutable struct HarmonicVariable
Holds a variable stored under symbol
describing the harmonic ω
of natural_variable
.
Fields
symbol::Num
: Symbol of the variable in the HarmonicBalance namespace.name::String
: Human-readable labels of the variable, used for plotting.type::String
: Type of the variable (u or v for quadratures, a for a constant, Hopf for Hopf etc.)ω::Num
: The harmonic being described.natural_variable::Num
: The natural variable whose harmonic is being described.
HarmonicBalance.HarmonicEquation Type
mutable struct HarmonicEquation
Holds a set of algebraic equations governing the harmonics of a DifferentialEquation
.
Fields
equations::Vector{Equation}
: A set of equations governing the harmonics.variables::Vector{HarmonicVariable}
: A set of variables describing the harmonics.parameters::Vector{Num}
: The parameters of the equation set.natural_equation::DifferentialEquation
: The natural equation (before the harmonic ansatz was used).jacobian::Matrix{Num}
: The Jacobian of the natural equation.
HarmonicBalance.rearrange_standard Function
rearrange_standard(
eom::HarmonicEquation
) -> HarmonicEquation
Rearrange eom
to the standard form, such that the derivatives of the variables are on one side.
HarmonicBalance.rearrange_standard! Function
rearrange_standard!(eom::DifferentialEquation)
rearrange_standard!(eom::DifferentialEquation, degree)
Rearranges the differential equations in eom
to standard form, where the highest derivative of each variable (specified by degree
, default 2) appears isolated on the left-hand side. Modifies the equations in place.
rearrange_standard!(
eom::HarmonicEquation
) -> HarmonicEquation
Rearrange eom
to the standard form, such that the derivatives of the variables are on one side.
HarmonicBalance.first_order_transform! Function
first_order_transform!(diff_eom::DifferentialEquation, time)
Transforms a higher-order differential equation system into an equivalent first-order system by introducing additional variables. Modifies the system in place. The time
parameter specifies the independent variable used for differentiation.
HarmonicBalance.is_rearranged_standard Function
is_rearranged_standard(eom::DifferentialEquation) -> Any
is_rearranged_standard(
eom::DifferentialEquation,
degree
) -> Any
Checks if the differential equations in eom
are arranged in standard form, where the highest derivative of each variable appears isolated on the left-hand side. The default degree is 2, corresponding to second-order differential equations.
HarmonicBalance.get_equations Function
get_equations(eom::DifferentialEquation) -> Vector{Equation}
Return the equations of eom
.
HarmonicBalance.get_harmonic_equations Function
get_harmonic_equations(
diff_eom::DifferentialEquation;
fast_time,
slow_time,
degree,
jacobian
) -> HarmonicEquation
Apply the harmonic ansatz, followed by the slow-flow, Fourier transform and dropping higher-order derivatives to obtain a set of ODEs (the harmonic equations) governing the harmonics of diff_eom
.
The harmonics evolve in slow_time
, the oscillating terms themselves in fast_time
. If no input is used, a variable T is defined for slow_time
and fast_time
is taken as the independent variable of diff_eom
.
By default, all products of order > 1 of slow_time
-derivatives are dropped, which means the equations are linear in the time-derivatives.
Example
julia> @variables t, x(t), ω0, ω, F;
# enter the simple harmonic oscillator
julia> diff_eom = DifferentialEquation( d(x,t,2) + ω0^2 * x ~ F *cos(ω*t), x);
# expand x in the harmonic ω
julia> add_harmonic!(diff_eom, x, ω);
# get equations for the harmonics evolving in the slow time T
julia> harmonic_eom = get_harmonic_equations(diff_eom)
A set of 2 harmonic equations
Variables: u1(T), v1(T)
Parameters: ω0, ω, F
Harmonic ansatz:
x(t) = u1*cos(ωt) + v1*sin(ωt)
Harmonic equations:
(ω0^2)*u1(T) + (2//1)*ω*Differential(T)(v1(T)) - (ω^2)*u1(T) ~ F
(ω0^2)*v1(T) - (ω^2)*v1(T) - (2//1)*ω*Differential(T)(u1(T)) ~ 0
HarmonicBalance.KrylovBogoliubov.get_krylov_equations Function
get_krylov_equations(
diff_eom::DifferentialEquation;
order,
fast_time,
slow_time
)
Apply the Krylov-Bogoliubov averaging method to a specific order
to obtain a set of ODEs (the slow-flow equations) governing the harmonics of diff_eom
.
The harmonics evolve in slow_time
, the oscillating terms themselves in fast_time
. If no input is used, a variable T is defined for slow_time
and fast_time
is taken as the independent variable of diff_eom
.
Krylov-Bogoliubov averaging method can be applied up to order = 2
.
Example
julia> @variables t, x(t), ω0, ω, F;
# enter the simple harmonic oscillator
julia> diff_eom = DifferentialEquation( d(x,t,2) + ω0^2 * x ~ F *cos(ω*t), x);
# expand x in the harmonic ω
julia> add_harmonic!(diff_eom, x, ω);
# get equations for the harmonics evolving in the slow time T to first order
julia> harmonic_eom = get_krylov_equations(diff_eom, order = 1)
A set of 2 harmonic equations
Variables: u1(T), v1(T)
Parameters: ω, F, ω0
Harmonic ansatz:
xˍt(t) =
x(t) = u1(T)*cos(ωt) + v1(T)*sin(ωt)
Harmonic equations:
((1//2)*(ω^2)*v1(T) - (1//2)*(ω0^2)*v1(T)) / ω ~ Differential(T)(u1(T))
((1//2)*(ω0^2)*u1(T) - (1//2)*F - (1//2)*(ω^2)*u1(T)) / ω ~ Differential(T)(v1(T))
HarmonicBalance.add_harmonic! Function
add_harmonic!(diff_eom::DifferentialEquation, var::Num, ω)
Add the harmonic ω
to the harmonic ansatz used to expand the variable var
in diff_eom
.
Example
define the simple harmonic oscillator and specify that x(t) oscillates with frequency ω
julia> @variables t, x(t), y(t), ω0, ω, F, k;
julia> diff_eq = DifferentialEquation(d(x,t,2) + ω0^2 * x ~ F * cos(ω*t), x);
julia> add_harmonic!(diff_eq, x, ��) # expand x using ω
System of 1 differential equations
Variables: x(t)
Harmonic ansatz: x(t) => ω;
(ω0^2)*x(t) + Differential(t)(Differential(t)(x(t))) ~ F*cos(t*ω)
HarmonicBalance.get_independent_variables Function
get_independent_variables(
diff_eom::DifferentialEquation
) -> Any
Return the independent dependent variables of diff_eom
.
get_independent_variables(
eom::HarmonicEquation
) -> Vector{Num}
Return the independent variables (typically time) of eom
.
Symbolics.get_variables Function
get_variables(diff_eom::DifferentialEquation) -> Vector{Num}
Return the dependent variables of diff_eom
.
get_variables(eom::HarmonicEquation) -> Vector{Num}
Get the internal symbols of the independent variables of eom
.
Solving and transforming solutions
HarmonicBalance.get_steady_states Function
get_steady_states(problem::HarmonicEquation,
method::HarmonicBalanceMethod,
swept_parameters,
fixed_parameters;
show_progress=true,
sorting="nearest",
classify_default=true)
Solves problem
with the method
over the ranges specified by swept_parameters
, keeping fixed_parameters
constant. swept_parameters
accepts pairs mapping symbolic variables to arrays or ranges. fixed_parameters
accepts pairs mapping symbolic variables to numbers.
Keyword arguments
show_progress
: Indicate whether a progress bar should be displayed.sorting
: the method used bysort_solutions
to get continuous solutions branches. The current options are"hilbert"
(1D sorting along a Hilbert curve),"nearest"
(nearest-neighbor sorting) and"none"
.classify_default
: Iftrue
, the solutions will be classified using the default classification method.
Example
solving a simple harmonic oscillator
# having obtained a Problem object, let's find steady states
julia> range = (ω => range(0.8, 1.2, 100) ) # 100 parameter sets to solve
julia> fixed = ParameterList(m => 1, γ => 0.01, F => 0.5, ω_0 => 1)
julia> get_steady_states(problem, range, fixed)
A steady state result for 100 parameter points
Solution branches: 1
of which real: 1
of which stable: 1
Classes: stable, physical, Hopf, binary_labels
It is also possible to perform 2-dimensional sweeps.
# The swept parameters take precedence over fixed -> use the same fixed
julia> range = (ω => range(0.8,1.2,100), F => range(0.1,1.0,10) )
# The swept parameters take precedence over fixed -> the F in fixed is now ignored
julia> get_steady_states(problem, range, fixed)
A steady state result for 1000 parameter points
Solution branches: 1
of which real: 1
of which stable: 1
Classes: stable, physical, Hopf, binary_labels
Methods
HarmonicBalance.WarmUp Type
WarmUp
The Warm Up method prepares a warmup system with the Total Degree method using the parameter at index
perturbed by perturbation_size
. The warmup system is used to perform a homotopy using all other systems in the parameter sweep. It is very efficient for systems with minimal bifurcation in the parameter sweep. The Warm Up method should in theory guarantee to find all solutions, however, if the start_parameters
is not proper (to close to the real line) it could miss some solutions.
SeeHomotopyContinuation.jl for more information.
Fields
warm_up_method::Union{Polyhedral{T}, TotalDegree{T}} where T
: Method used for the warmup system.start_parameters::Vector
: Start parameters.thread::Bool
: Boolean indicating if threading is enabled.tracker_options::HomotopyContinuation.TrackerOptions
: Options for the tracker.endgame_options::HomotopyContinuation.EndgameOptions
: Options for the endgame.compile::Union{Bool, Symbol}
: Compilation options.seed::UInt32
: Seed for random number generation.
HarmonicBalance.TotalDegree Type
TotalDegree
The Total Degree homotopy method performs a homotopy
Fields
gamma::Complex
: Complex multiplying factor of the start system G(x) for the homotopythread::Bool
: Boolean indicating if threading is enabled.tracker_options::HomotopyContinuation.TrackerOptions
: Options for the tracker.endgame_options::HomotopyContinuation.EndgameOptions
: Options for the endgame.compile::Union{Bool, Symbol}
: Compilation options.seed::UInt32
: Seed for random number generation.
HarmonicBalance.Polyhedral Type
Polyhedral
The Polyhedral homotopy method constructs a homotopy based on the polyhedral structure of the polynomial system. It is more efficient than the Total Degree method for sparse systems, meaning most of the coefficients are zero. It can be especially useful if you don't need to find the zero solutions (only_non_zero = true
), resulting in a speed up. See HomotopyContinuation.jl for more information.
Fields
only_non_zero::Bool
: Boolean indicating if only non-zero solutions are considered.thread::Bool
: Boolean indicating if threading is enabled.tracker_options::HomotopyContinuation.TrackerOptions
: Options for the tracker.endgame_options::HomotopyContinuation.EndgameOptions
: Options for the endgame.compile::Union{Bool, Symbol}
: Compilation options.seed::UInt32
: Seed for random number generation.
Access solutions
HarmonicBalance.get_solutions Function
get_solutions(
res::Result, x::String;
branches=1:branch_count(res), realify=false, class=["stable"], not_class=[]
)
get_solutions(res::Result; branches=1:branch_count(res), class=["stable"], not_class=[])
Extract solution vectors from a Result
object based on specified filtering criteria given by the class
keywords. The first method allows extracting a specific solution component by name x
. The second method returns complete solution vectors.
Keyword arguments
branches=1:branch_count(res)
: Range of branches to include in the outputrealify=false
: Whether to convert complex solutions to real formclass=["physical", "stable"]
: Array of classification labels to includenot_class=[]
: Array of classification labels to exclude
Returns
Filtered solution vectors matching the specified criteria
HarmonicBalance.attractors Function
attractors(res::Result{D}; class="stable", not_class=[]) where D
Extract attractors from a Result
object. Returns an array of dictionaries, where each dictionary maps branch identifier to the attractor. The attractors are filtered by their corresponding class.
Keyword arguments
Class selection done by passing String
or Vector{String}
as kwarg:
class::String : only count solutions in this class ("all" --> plot everything)
not_class::String : do not count solutions in this class
Returns
Array{Dict,D}
: Vector of dictionaries mapping branch indices to points satisfying the stability criteria at each parameter value
HarmonicBalance.phase_diagram Function
phase_diagram(res::Result{D}; class="physical", not_class=[]) where {D}
Calculate the phase diagram from a Result
object by summing over the number of states at each swept parameters.
Keyword arguments
Class selection done by passing String
or Vector{String}
as kwarg:
class::String : only count solutions in this class ("all" --> plot everything)
not_class::String : do not count solutions in this class
Returns
- Array{Int64,D}: Sum of states after applying the specified class masks
HarmonicBalance.get_single_solution Function
get_single_solution(
res::HarmonicBalance.Result{D, S, P, F} where F<:FunctionWrappers.FunctionWrapper{Array{S, 2}, Tuple{Array{S, 1}}};
branch,
index
)
Return an ordered dictionary specifying all variables and parameters of the solution in result
on branch
at the position index
.
HarmonicBalance.transform_solutions Function
transform_solutions(
res::HarmonicBalance.Result{D, S, ParType, F} where {ParType<:Number, F<:FunctionWrappers.FunctionWrapper{Array{S, 2}, Tuple{Array{S, 1}}}},
func;
branches,
realify
) -> Vector
Takes a Result
object and a string f
representing a Symbolics.jl expression. Returns an array with the values of f
evaluated for the respective solutions. Additional substitution rules can be specified in rules
in the format ("a" => val)
or (a => val)
Classify
HarmonicBalance.classify_solutions! Function
classify_solutions!(
res::HarmonicBalance.Result,
func::Union{Function, String},
name::String;
physical
) -> Any
Creates a solution class in res
using the function func
(parsed into Symbolics.jl input). The new class is labeled with name
and stored under res.classes[name]
. By default, only physical (real) solutions are classified, and false
is returned for the rest. To also classify complex solutions, set physical=false
.
Example
# solve a previously-defined problem
res = get_steady_states(problem, swept_parameters, fixed_parameters)
# classify, store in result.classes["large_amplitude"]
classify_solutions!(res, "sqrt(u1^2 + v1^2) > 1.0" , "large_amplitude")
HarmonicBalance.get_class Function
get_class(
res::HarmonicBalance.Result,
branch::Int64,
class::String
) -> Any
Returns an array of booleans classifying branch
in the solutions in res
according to class
.
get_class(
soln::HarmonicBalance.Result,
class::String
) -> Vector
Returns an array of booleans classifying each branch in the solutions in res
according to class
.
Plotting
RecipesBase.plot Function
plot(
res::HarmonicBalance.Result{D, S, P, F} where F<:FunctionWrappers.FunctionWrapper{Array{S, 2}, Tuple{Array{S, 1}}},
varargs...;
cut,
kwargs...
) -> Plots.Plot
Plot a Result
object.
Class selection done by passing String
or Vector{String}
as kwarg:
class : only plot solutions in this class(es) ("all" --> plot everything)
not_class : do not plot solutions in this class(es)
Other kwargs are passed onto Plots.gr().
See also plot!
The x,y,z arguments are Strings compatible with Symbolics.jl, e.g., y=2*sqrt(u1^2+v1^2)
plots the amplitude of the first quadratures multiplied by 2.
1D plots
plot(res::Result; x::String, y::String, class="default", not_class=[], kwargs...)
plot(res::Result, y::String; kwargs...) # take x automatically from Result
Default behaviour is to plot stable solutions as full lines, unstable as dashed.
If a sweep in two parameters were done, i.e., dimension(res)==2
, a one dimensional cut can be plotted by using the keyword cut
were it takes a Pair{Num, Float}
type entry. For example, plot(res, y="sqrt(u1^2+v1^2), cut=(λ => 0.2))
plots a cut at λ = 0.2
.
2D plots
plot(res::Result; z::String, branch::Int64, class="physical", not_class=[], kwargs...)
To make the 2d plot less chaotic it is required to specify the specific branch
to plot, labeled by a Int64
.
The x and y axes are taken automatically from res
plot(soln::ODESolution, f::String, harm_eq::HarmonicEquation; kwargs...)
Plot a function f
of a time-dependent solution soln
of harm_eq
.
As a function of time
plot(soln::ODESolution, f::String, harm_eq::HarmonicEquation; kwargs...)
f
is parsed by Symbolics.jl
parametric plots
plot(soln::ODESolution, f::Vector{String}, harm_eq::HarmonicEquation; kwargs...)
Parametric plot of f[1] against f[2]
Also callable as plot!
RecipesBase.plot! Function
plot!(
res::HarmonicBalance.Result,
varargs...;
kwargs...
) -> Plots.Plot
Similar to plot
but adds a plot onto an existing plot.
HarmonicBalance.plot_phase_diagram Function
plot_phase_diagram(
res::HarmonicBalance.Result{D, SolType} where SolType<:Number;
kwargs...
) -> Plots.Plot
Plot the number of solutions in a Result
object as a function of the parameters. Works with 1D and 2D datasets.
Class selection done by passing String
or Vector{String}
as kwarg:
class::String : only count solutions in this class ("all" --> plot everything)
not_class::String : do not count solutions in this class
Other kwargs are passed onto Plots.gr()
HarmonicBalance.plot_spaghetti Function
plot_spaghetti(
res::HarmonicBalance.Result{D, SolType} where SolType<:Number;
x,
y,
z,
class,
not_class,
add,
kwargs...
)
Plot a three dimension line plot of a Result
object as a function of the parameters. Works with 1D and 2D datasets.
Class selection done by passing String
or Vector{String}
as kwarg:
class::String : only count solutions in this class ("all" --> plot everything)
not_class::String : do not count solutions in this class
Other kwargs are passed onto Plots.gr()
Limit cycles
HarmonicBalance.LimitCycles.get_limit_cycles Function
get_limit_cycles(
eom::HarmonicEquation, method::HarmonicBalanceMethod, swept, fixed, ω_lc; kwargs...)
Variant of get_steady_states
for a limit cycle problem characterised by a Hopf frequency (usually called ω_lc)
Solutions with ω_lc = 0 are labelled unphysical since this contradicts the assumption of distinct harmonic variables corresponding to distinct harmonics.
HarmonicBalance.LimitCycles.get_cycle_variables Function
get_cycle_variables(
eom::HarmonicEquation,
ω_lc::Num
) -> Vector{HarmonicVariable}
Return the harmonic variables which participate in the limit cycle labelled by ω_lc
.
HarmonicBalance.LimitCycles.add_pairs! Function
add_pairs!(eom::DifferentialEquation; ω_lc::Num, n=1)
Add a limit cycle harmonic ω_lc
to the system Equivalent to adding n
pairs of harmonics ω +- ω_lc for each existing ω.
Linear Response
HarmonicBalance.LinearResponse.eigenvalues Method
eigenvalues(res::Result, branch; class=["physical"])
Calculate the eigenvalues of the Jacobian matrix of the harmonic equations of a branch
for a one dimensional sweep in the Result struct.
Arguments
res::Result
: Result object containing solutions and jacobian informationbranch
: Index of the solution branch to analyzeclass=["physical"]
: Filter for solution classes to include, defaults to physical solutions
Returns
- Vector of filtered eigenvalues along the solution branch
Notes
Currently only supports 1-dimensional parameter sweeps (D=1)
Will throw an error if branch contains NaN values
Eigenvalues are filtered based on the specified solution classes
HarmonicBalance.LinearResponse.eigenvectors Method
eigenvectors(res::Result, branch; class=["physical"])
Calculate the eigenvectors of the Jacobian matrix of the harmonic equations of a branch
for a one dimensional sweep in the Result struct.
Arguments
res::Result
: Result object containing solutions and jacobian informationbranch
: Index of the solution branch to analyzeclass=["physical"]
: Filter for solution classes to include, defaults to physical solutions
Returns
- Vector of filtered eigenvectors along the solution branch
Notes
Currently only supports 1-dimensional parameter sweeps (D=1)
Will throw an error if branch contains NaN values
Eigenvectors are filtered based on the specified solution classes
HarmonicBalance.LinearResponse.get_jacobian_response Method
get_jacobian_response(
res::HarmonicBalance.Result{D, S, P, F} where F<:FunctionWrappers.FunctionWrapper{Array{S, 2}, Tuple{Array{S, 1}}},
nat_var::Num,
Ω_range,
branch::Int64;
show_progress
) -> Matrix
Calculate the Jacobian response spectrum for a given system. Computes the magnitude of the Jacobian response for stable solutions across specified frequency ranges.
Arguments
res::Result
: Result object containing the system's solutionsnat_var::Num
: Natural variable to evaluate in the responseΩ_range
: Range of frequencies to evaluatebranch::Int
orfollowed_branches::Vector{Int}
: Branch number(s) to analyzeshow_progress=true
: Whether to show a progress barforce=false
: Force recalculation of spectrum even if already exists
Returns
- Array{P,2}: Complex response matrix where rows correspond to frequencies and columns to solutions
HarmonicBalance.LinearResponse.get_linear_response Method
get_linear_response(
res::HarmonicBalance.Result{D, S, P, F} where F<:FunctionWrappers.FunctionWrapper{Array{S, 2}, Tuple{Array{S, 1}}},
nat_var::Num,
Ω_range,
branch::Int64;
order,
show_progress
)
Calculate the linear response of the system for a given branch. Evaluates the linear response by solving the linear response ODE for each stable solution and input frequency in the given range.
Arguments
res
: Result object containing the system's solutionsnat_var::Num
: Natural variable to evaluate in the responseΩ_range
: Range of frequencies to evaluatebranch::Int
: Branch number to analyzeorder
: Order of the response to calculateshow_progress=true
: Whether to show a progress bar
Returns
- Array{P,2}: Response matrix where rows correspond to frequencies and columns to stable solutions
HarmonicBalance.LinearResponse.get_rotframe_jacobian_response Method
get_rotframe_jacobian_response(
res::HarmonicBalance.Result{D, S, P, F} where F<:FunctionWrappers.FunctionWrapper{Array{S, 2}, Tuple{Array{S, 1}}},
Ω_range,
branch::Int64;
show_progress,
damping_mod
)
Calculate the rotating frame Jacobian response for a given branch. Computes the rotating frame Jacobian response by evaluating eigenvalues of the numerical Jacobian and calculating the response magnitude for each frequency in the range.
Arguments
res::Result
: Result object containing the system's solutionsΩ_range
: Range of frequencies to evaluatebranch::Int
: Branch number to analyzeshow_progress=true
: Whether to show a progress bardamping_mod
: Damping modification parameter
Returns
- Array{P,2}: Response matrix in the rotating frame
HarmonicBalance.get_Jacobian Function
get_Jacobian(eom)
Obtain the symbolic Jacobian matrix of eom
. This is the linearised left-hand side of F(u) = du/dT.
Obtain a Jacobian from a DifferentialEquation
by first converting it into a HarmonicEquation
.
Get the Jacobian of a set of equations eqs
with respect to the variables vars
.
Extensions
OrdinaryDiffEq
HarmonicBalance.AdiabaticSweep Type
Represents a sweep of one or more parameters of a HarmonicEquation
. During a sweep, the selected parameters vary linearly over some timespan and are constant elsewhere.
Sweeps of different variables can be combined using +
.
Fields
functions::Dict{Num, Function}
: Maps each swept parameter to a function.
Examples
# create a sweep of parameter a from 0 to 1 over time 0 -> 100
julia> @variables a,b;
julia> sweep = AdiabaticSweep(a => [0., 1.], (0, 100));
julia> sweep[a](50)
0.5
julia> sweep[a](200)
1.0
# do the same, varying two parameters simultaneously
julia> sweep = AdiabaticSweep([a => [0.,1.], b => [0., 1.]], (0,100))
Successive sweeps can be combined,
sweep1 = AdiabaticSweep(ω => [0.95, 1.0], (0, 2e4))
sweep2 = AdiabaticSweep(λ => [0.05, 0.01], (2e4, 4e4))
sweep = sweep1 + sweep2
multiple parameters can be swept simultaneously,
sweep = AdiabaticSweep([ω => [0.95;1.0], λ => [5e-2;1e-2]], (0, 2e4))
and custom sweep functions may be used.
ωfunc(t) = cos(t)
sweep = AdiabaticSweep(ω => ωfunc)
HarmonicBalance.follow_branch Function
follow_branch(
starting_branch::Int64,
res::HarmonicBalance.Result;
y,
sweep,
tf,
ϵ
) -> Tuple{Any, Any}
Return the indexes and values following stable branches along a 1D sweep. When a no stable solutions are found (e.g. in a bifurcation), the next stable solution is calculated by time evolving the previous solution (quench).
Keyword arguments
y
: Dependent variable expression (parsed into Symbolics.jl) to evaluate the followed solution branches on .sweep
: Direction for the sweeping of solutions. Aright
(left
) sweep proceeds from the first (last) solution, ordered as the sweeping parameter.tf
: time to reach steadyϵ
: small random perturbation applied to quenched solution, in a bifurcation in order to favour convergence in cases where multiple solutions are identically accessible (e.g. symmetry breaking into two equal amplitude states)
HarmonicBalance.plot_1D_solutions_branch Function
plot_1D_solutions_branch(
starting_branch::Int64,
res::HarmonicBalance.Result;
x,
y,
sweep,
tf,
ϵ,
class,
not_class,
kwargs...
)
Plot a bifurcation diagram from a continuation sweep starting from starting_branch
using the Result struct res
. Time integration is used to determine what follow up branch in the continuation is.
Keyword arguments
x::String
: Expression for the x-axis variabley::String
: Expression for the y-axis variablesweep::String="right"
: Direction to follow the branch ("right" or "left")tf::Real=10000
: Final time for time integrationϵ::Real=1e-4
: Tolerance for branch followingkwargs...
: Additional plotting arguments passed to Plots.jlClass selection done by passing
String
orVector{String}
as kwarg: class::String : only count solutions in this class ("all" –> plot everything) not_class::String : do not count solutions in this class
Returns
- A Plots.jl plot object containing the bifurcation diagram with the followed branch
Description
This function creates a bifurcation diagram using follow_branch
. The followed branch is plotted as a dashed gray line.
HarmonicBalance.follow_branch Method
follow_branch(
starting_branch::Int64,
res::HarmonicBalance.Result;
y,
sweep,
tf,
ϵ
) -> Tuple{Any, Any}
Return the indexes and values following stable branches along a 1D sweep. When a no stable solutions are found (e.g. in a bifurcation), the next stable solution is calculated by time evolving the previous solution (quench).
Keyword arguments
y
: Dependent variable expression (parsed into Symbolics.jl) to evaluate the followed solution branches on .sweep
: Direction for the sweeping of solutions. Aright
(left
) sweep proceeds from the first (last) solution, ordered as the sweeping parameter.tf
: time to reach steadyϵ
: small random perturbation applied to quenched solution, in a bifurcation in order to favour convergence in cases where multiple solutions are identically accessible (e.g. symmetry breaking into two equal amplitude states)
SteadyStateSweep
HarmonicBalance.steady_state_sweep Function
steady_state_sweep(prob::SteadyStateProblem, alg::DynamicSS; varied::Pair, kwargs...)
Sweeps through a range of parameter values using a dynamic steady state solver DynamicSS
of the SteadyStateDiffEq.jl
package. Given a steady state problem and a parameter to vary, computes the steady state solution for each value in the sweep range. The solutions are returned as a vector where each element corresponds to the steady state found at that parameter value.
steady_state_sweep(prob_np::NonlinearProblem, prob_ss::SteadyStateProblem,
alg_np, alg_ss::DynamicSS; varied::Pair, kwargs...)
Performs a parameter sweep by combining nonlinear root alg_np
and steady state solvers alg_ss
. For each parameter value, it first attempts a direct nonlinear root solver and checks its stability. If the solution is unstable or not found, it switches to a dynamic steady state solver. This hybrid approach is much faster then only using a steady state solver.
ModelingToolkit
SciMLBase.ODEProblem Type
ODEProblem(
eom::HarmonicEquation,
fixed_parameters;
sweep,
u0,
timespan,
perturb_initial,
kwargs...
)
Creates an ODEProblem object used by OrdinaryDiffEqTsit5.jl from the equations in eom
to simulate time-evolution within timespan
. fixed_parameters
must be a dictionary mapping parameters+variables to numbers (possible to use a solution index, e.g. solutions[x][y] for branch y of solution x). If u0
is specified, it is used as an initial condition; otherwise the values from fixed_parameters
are used.
ODEProblem(
eom::Union{DifferentialEquation, HarmonicEquation},
u0,
tspan::Tuple,
p::AbstractDict;
in_place,
kwargs...
) -> Any
Creates and ModelingToolkit.ODEProblem from a DifferentialEquation or HarmonicEquation.
Example
using ModelingToolkit, StaticArrays
@variables α ω ω0 F γ t x(t)
diff_eq = DifferentialEquation(
d(x, t, 2) + ω0^2 * x + α * x^3 + γ * d(x, t) ~ F * cos(ω * t), x
)
add_harmonic!(diff_eq, x, ω) #
harmonic_eq = get_harmonic_equations(diff_eq)
# in place (most performant for large systems)
ODEProblem(harmonic_eq, [1.0, 0.0], (0, 100), param)
# out of place (most performant for small systems with StaticArrays)
ODEProblem(
harmonic_eq, [1.0, 0.0], (0, 100), param;
in_place=false, u0_constructor=x -> SVector(x...)
)
ModelingToolkit.ODESystem Type
ODESystem(eom::HarmonicEquation) -> Any
Creates and ModelingToolkit.ODESystem from a HarmonicEquation.
Example
using ModelingToolkit
@variables α ω ω0 F γ t x(t)
diff_eq = DifferentialEquation(
d(x, t, 2) + ω0^2 * x + α * x^3 + γ * d(x, t) ~ F * cos(ω * t), x
)
add_harmonic!(diff_eq, x, ω) #
harmonic_eq = get_harmonic_equations(diff_eq)
sys = ODESystem(harmonic_eq)
param = (α => 1.0, ω0 => 1.1, F => 0.01, γ => 0.01, ω => 1.1)
ODEProblem(sys, [1.0, 0.0], (0, 100), param)
ODESystem(diff_eq::DifferentialEquation) -> Any
Creates and ModelingToolkit.ODESystem from a DifferentialEquation.
Example
using ModelingToolkit
@variables α ω ω0 F γ t x(t)
diff_eq = DifferentialEquation(
d(x, t, 2) + ω0^2 * x + α * x^3 + γ * d(x, t) ~ F * cos(ω * t), x
)
sys = ODESystem(diff_eq)
param = (α => 1.0, ω0 => 1.1, F => 0.01, γ => 0.01, ω => 1.1)
ODEProblem(sys, [1.0, 0.0], (0, 100), param)
SciMLBase.SteadyStateProblem Type
SteadyStateProblem(
eom::HarmonicEquation,
u0,
p::AbstractDict;
in_place,
kwargs...
) -> Any
Creates and ModelingToolkit.SteadyStateProblem from a HarmonicEquation.
Example
using ModelingToolkit, StaticArrays
@variables α ω ω0 F γ t x(t)
diff_eq = DifferentialEquation(
d(x, t, 2) + ω0^2 * x + α * x^3 + γ * d(x, t) ~ F * cos(ω * t), x
)
add_harmonic!(diff_eq, x, ω) #
harmonic_eq = get_harmonic_equations(diff_eq)
SteadyStateProblem(harmonic_eq, [1.0, 0.0], param)
SciMLBase.NonlinearProblem Type
NonlinearProblem(
eom::HarmonicEquation,
u0,
p::AbstractDict;
in_place,
kwargs...
) -> Any
Creates and ModelingToolkit.NonlinearProblem from a HarmonicEquation.
Example
using ModelingToolkit, StaticArrays
@variables α ω ω0 F γ t x(t)
diff_eq = DifferentialEquation(
d(x, t, 2) + ω0^2 * x + α * x^3 + γ * d(x, t) ~ F * cos(ω * t), x
)
add_harmonic!(diff_eq, x, ω) #
harmonic_eq = get_harmonic_equations(diff_eq)
NonlinearProblem(harmonic_eq, [1.0, 0.0], param)