Custom SystemStructure and SymbolicAWESystem

A custom SystemStructure can be used to create models of kite power systems of almost any configuration.

  • custom amount of tethers
  • custom bridle configurations
  • quasi-static or dynamic point masses
  • different amounts of stiffness, damping and diameter on different tether segments

Precondition

First, following the Home section up to the installation of the examples. To start Julia, either use julia --project=., or ./bin/run_julia. Make sure that at least SymbolicAWEModels version 0.2 is installed by typing ]status in the Julia REPL. If necessary, update SymbolicAWEModels by typing ]up.

Creating a simple tether

We start by loading the necessary packages and defining settings and parameters.

using SymbolicAWEModels, VortexStepMethod, Makie

set = Settings("system.yaml")
set.segments = 20
set.l_tether = 50.0
dynamics_type = DYNAMIC

Then, we define vectors of the system structure types we are going to use. For this simple example we only need points and segments.

points = Point[]
segments = Segment[]

points = push!(points, Point(1, zeros(3), STATIC; wing_idx=0))

The first point we add is a static point. There are four different DynamicsTypes to choose from: STATIC, QUASI_STATIC, DYNAMIC and WING. STATIC just means that the point doesn't move. DYNAMIC is a point modeled with acceleration, while QUASI_STATIC constrains this acceleration to be zero at all times. A WING point is connected to a wing body.

Now we can add DYNAMIC points and connect them to each other with segments. BRIDLE segments don't need to have a tether, because they have a constant unstretched length.

segment_idxs = Int[]
for i in 1:set.segments
    global points, segments
    point_idx = i+1
    pos = [0.0, 0.0, i * set.l_tether / set.segments]
    if i < set.segments
        push!(points, Point(point_idx, pos, dynamics_type; wing_idx=0))
    else
        push!(points, Point(point_idx, pos, dynamics_type; mass=1.0, wing_idx=0))
    end
    segment_idx = i
    push!(segments, Segment(segment_idx, set, (point_idx-1, point_idx), BRIDLE))
    push!(segment_idxs, segment_idx)
end

In order to describe the initial orientation of the structure, we define a Transform(idx, elevation, azimuth, heading) with an elevation (-80 degrees), azimuth and heading, and a base position [0.0, 0.0, 50.0].

transforms = [Transform(1, deg2rad(-80), 0.0, 0.0; 
              base_pos = [0.0, 0.0, 50.0], base_point_idx=points[1].idx,
              rot_point_idx=points[end].idx)]

From the points, segments and transform we create a SystemStructure(name, set), which can be plotted in 3D to quickly investigate if the model is correct.

sys_struct = SystemStructure("tether", set; points, segments, transforms)
plot(sys_struct, 0.0)

SystemStructure visualization

If the system looks good, we can easily model it, by first creating a SymbolicAWEModel, initializing it and stepping through time.

sam = SymbolicAWEModel(set, sys_struct)

init!(sam; remake=false)
plot(sys_struct, 0.0)  # Create initial plot
for i in 1:80
    next_step!(sam)
    plot(sys_struct, i/set.sample_freq)  # Update plot
end

Tether during simulation

Using a winch and a tether

Let's try to adjust the length of the tether in the last example. To do this we first need to create a set of segments with a common changing l0, called a Tether.

set.v_wind = 0.0
tethers = [Tether(1,[segment.idx for segment in segments])]

As you can see, we just add all of the segments from the simple tether to our Tether struct. The next step is to create a winch. Each winch can be connected to one or more tethers, so it is possible to connect multiple tethers to the same winch. The winch is a torque controlled winch.

using WinchModels
winches = [Winch(1, set, [1])]

The plot of the SystemStructure should still look the same, so we don't have to plot that. We can just create the system, and simulate it. We call plot(sys_struct, 0.0) to create a new plot.

sys_struct = SystemStructure("winch", set; points, segments, tethers, winches, transforms)
sam = SymbolicAWEModel(set, sys_struct)
init!(sam; remake=false)
ss = SysState(sam)

plot(sys_struct, 0.0)  # Create initial plot
for i in 1:80
    next_step!(sam; set_values=[-20.0])
    update_sys_state!(ss, sam)
    plot(sys_struct, i/set.sample_freq)  # Update plot
end

Using a pulley

First, we need to update some settings. l_tether is specified such that the plot window is zoomed in correctly.

using SymbolicAWEModels, VortexStepMethod, Makie

set = se("system.yaml")
set.v_wind = 10.0
set.l_tether = 5.0
set.abs_tol = 1e-4
set.rel_tol = 1e-4
dynamics_type = DYNAMIC

Now we create points and segments in a similar manner as in the last example. The mass keyword can be used to specify the mass of the point itself. When mass=0.0 the mass of the point just consists of the tether/segment mass.

points = Point[]
segments = Segment[]
pulleys = Pulley[]

push!(points, Point(1, [0.0, 0.0, 2.0], STATIC))
push!(points, Point(2, [2.0, 0.0, 2.0], STATIC))
push!(points, Point(3, [0.1, 0.0, 1.0], DYNAMIC))
push!(points, Point(4, [0.1, 0.0, 0.0], DYNAMIC; mass=0.1))

push!(segments, Segment(1, set, (3,1), BRIDLE))
push!(segments, Segment(2, set, (3,2), BRIDLE))
push!(segments, Segment(3, set, (3,4), BRIDLE))

Pulleys can be modeled when three or more Segments are connected to a common Point. When creating a pulley, only two segments are specified: these are the segments of the tether moving through the pulley.

push!(pulleys, Pulley(1, (1,2), DYNAMIC))

We can then use a Transform to describe the orientation of the initial system.

transforms = [Transform(1, -deg2rad(0.0), 0.0, 0.0; base_pos=[1.0, 0.0, 4.0], base_point_idx=1, rot_point_idx=2)]
sys_struct = SymbolicAWEModels.SystemStructure("pulley", set; points, segments, pulleys, transforms)
plot(sys_struct, 0.0)

If the plot of the SystemStructure looks good, we can continue by creating a SymbolicAWEModel and simulating through time.

sam = SymbolicAWEModel(set, sys_struct)
init!(sam; remake=false)
plot(sys_struct, 0.0)  # Create initial plot
for i in 1:100
    next_step!(sam)
    plot(sys_struct, i/set.sample_freq)  # Update plot
end