Introduction

Most functions need an instance of the struct AtmosphericModel as first parameter, which can be created using the following code:

using AtmosphericModels, KiteUtils

set_data_path("data")
set = load_settings("system.yaml"; relax=true)
am::AtmosphericModel = AtmosphericModel(set)

This requires that the files system.yaml and settings.yaml exist in the folder data. See also Settings. The parameter relax=true allows loading a yaml file that does not contain all sections needed to run a kite power system simulation. This is useful if you want to use this package for other purposes than simulating kite power systems.

Types

Exported types

AtmosphericModels.ProfileLawType
@enum ProfileLaw CONSTANT=0 EXP=1 LOG=2 EXPLOG=3 CUSTOM_LOG=4 CUSTOM_EXP=5 CUSTOM_JET=6

Enumeration to describe the wind profile law that is used.

source
AtmosphericModels.AtmosphericModelType
mutable struct AtmosphericModel

Struct that is storing the settings and the state of the atmosphere.

Fields

  • set::Settings: The Settings struct
  • rho_zero_temp
  • wf::Union{WindField, Nothing}: The 3D WindField or nothing
  • jet_cache: cached (heights, speeds, coeffs) of the last custom_jet fit, or nothing
source
AtmosphericModels.AtmosphericModelMethod
AtmosphericModel(set::Settings; nowindfield::Bool=false)

Constructs an AtmosphericModel using the provided Settings.

Arguments

  • set::Settings: The settings object containing configuration parameters for the atmospheric model.
  • nowindfield::Bool=false: Optional keyword argument. If true, the wind field will not be loaded.

Returns

  • An instance of AtmosphericModel configured according to the provided settings.
source

Private types

AtmosphericModels.WindFieldType
struct WindField

Struct that is storing a 3D model of wind vectors of the atmosphere. The fields u, v and w store the wind turbulence vectors; x, y and z are the grid axes, rebuilt from the settings by grid_axes rather than read from the .npz file.

Fields

  • x_max::Float64 = NaN
  • x_min::Float64 = NaN
  • y_max::Float64 = NaN
  • y_min::Float64 = NaN
  • z_max::Float64 = NaN
  • z_min::Float64 = NaN
  • last_speed::Float64 = 0.0
  • valid::Bool = false
  • x::Union{SRL, Array{Float64, 3}}
  • y::Union{SRL, Array{Float64, 3}}
  • z::Union{SRL, Array{Float64, 3}}
  • u::Array{Float64, 3}
  • v::Array{Float64, 3}
  • w::Array{Float64, 3}
  • param::Vector{Float64} = [0, 0] # [alpha, v_wind_gnd]
  • v_wind_gnd::Float64: the set.v_wind_gnds entry this field was generated for, which selects the matching set.rel_turbs correction in get_wind
source

Functions

Wind shear and air density calculation

AtmosphericModels.calc_rhoFunction
calc_rho(s::AM, height)

Calculates the air density at a given height above ground level.

Arguments

  • s::AM: An instance of the AM (Atmospheric Model) struct containing atmospheric parameters.
  • height: The height above ground level (in meters) at which to calculate the air density.

Returns

  • The air density at the specified height (in kg/m³).

Notes

  • The calculation assumes an exponential decrease of air density with altitude.
  • s.rho_zero_temp is the reference air density at ground level.
  • s.set.height_gnd is the ground height offset.
  • The scale height used is 8550.0 meters.
source
AtmosphericModels.calc_wind_factorFunction
calc_wind_factor(am::AM, height; profile_law::Int64=am.set.profile_law)

Calculates the wind factor at a given height using the specified wind profile law.

Arguments

  • am::AM: An instance of the AM type containing atmospheric model parameters.
  • height: The height (in meters) at which to calculate the wind factor.
  • profile_law::Int64: (Optional) The wind profile law to use for the calculation. Defaults to am.set.profile_law.

Returns

  • The wind factor at the specified height as determined by the chosen profile law.
source

Custom profile laws

AtmosphericModels.custom_logFunction
custom_log(heights, speeds, height)

Evaluate a logarithmic wind profile u(z) = a * log(z) + b at height, with a and b fitted to heights/speeds by ordinary least squares.

source
AtmosphericModels.custom_expFunction
custom_exp(heights, speeds, height)

Evaluate a power-law wind profile u(z) = c * z^a at height, with a and c fitted to heights/speeds by ordinary least squares in log-log space.

source
AtmosphericModels.custom_jetFunction
custom_jet(heights, speeds, height)

Evaluate a power-law background profile with a superimposed Gaussian jet, u(z) = c * z^a + U_J * exp(-(z-z_c)^2/(2*sigma^2)), at height. All five coefficients are fitted jointly to heights/speeds by nonlinear least squares (Levenberg-Marquardt).

source

Wind turbulence calculation

AtmosphericModels.get_windFunction
get_wind(am::AtmosphericModel, x, y, z, t; upwind_dir=-π/4, interpolate=false)

Returns the wind vector at the specified position (x, y, z) and time t using the given AtmosphericModel (am).

Uses Taylor's frozen-turbulence hypothesis: the field is advected along the mean wind direction. The position is first rotated into the wind-aligned frame so that:

  • the along-wind component (+ time advection) maps to whichever of the field's first two dimensions is long (the larger of am.set.grid[1], am.set.grid[2]), avoiding short-period repetition during long simulations.
  • the cross-wind component maps to the short dimension, so the kite stays within the spatial range of the field.

The long/short axis is detected from the actual array size at each call, since which of x/y is longer depends on am.set.grid and differs between configurations (e.g. [4050, 100, ...] vs. the [100, 4050, ...] default).

The stored field is scaled at lookup by am.set.use_turbulence * rel_turbo(am, wf.v_wind_gnd), so one file per ground wind speed serves every turbulence intensity and changing use_turbulence needs no regeneration. The rel_turbs correction is taken for the speed the loaded field was generated for, not for am.set.v_wind, so a field loaded at any other speed keeps its own intensity.

Arguments

  • am::AtmosphericModel: The atmospheric model providing environmental parameters.
  • x, y, z: Position in the simulation (ENU) frame where the wind is evaluated. [m]
  • t: Current simulation time. [s]
  • upwind_dir (optional, default = -π/4): Direction the wind is coming FROM [rad]. Zero is north, clockwise positive (same convention as in calc_turbulent_wind).
  • interpolate (optional, default = false): If true, interpolate the turbulence trilinearly between the eight surrounding grid points; otherwise, use nearest-grid-point values. Interpolation removes the steps a kite flying through the field sees, at about 1.8x the cost of the lookup itself (29 ns → 51 ns per position for the vector method).

Returns

  • A tuple (v_x, v_y, v_z) representing the wind velocity in the wind-aligned frame [m/s], where v_x is the along-wind component (includes mean wind), v_y is cross-wind, v_z is vertical.
source
get_wind(am::AtmosphericModel, positions::AbstractVector, t; upwind_dir=-π/4, interpolate=false)

Return the wind vectors at all positions at time t as a Vector{SVec3}.

Faster than calling the scalar get_wind in a loop: the turbulence scaling (rel_turbo, which allocates), the sine/cosine of the wind direction and the settings lookups are computed once per call instead of once per position.

Arguments

  • am::AtmosphericModel: The atmospheric model providing environmental parameters.
  • positions: Vector of 3D positions in the simulation (ENU) frame, e.g. Vector{SVec3}; any vector of indexable, 3-element positions works. The height positions[i][3] must be >= 5 m.
  • t: Current simulation time. [s]
  • upwind_dir (optional, default = -π/4): Direction the wind is coming FROM [rad].
  • interpolate (optional, default = false): Interpolate between grid points, see the scalar method of get_wind.

Returns

  • A Vector{SVec3} of wind velocities in the wind-aligned frame [m/s], one per position; the components are (v_x, v_y, v_z) as returned by the scalar method.

See also get_wind!, which writes into a pre-allocated result vector.

source
AtmosphericModels.get_wind!Function
get_wind!(res::AbstractVector{SVec3}, am::AtmosphericModel, positions::AbstractVector, t;
          upwind_dir=-π/4, interpolate=false)

In-place version of the vector method of get_wind: write the wind vectors at positions into res and return res. res must have the same length as positions.

source
AtmosphericModels.calc_turbulent_windFunction
calc_turbulent_wind(am::AtmosphericModel, pos, t; upwind_dir=-π/4, interpolate=false)

Calculate the wind velocity vectors at the kite and at the mid-tether point, in the ENU simulation frame.

When am.set.use_turbulence == 0, the mean wind for the configured am.set.profile_law is returned. Otherwise the turbulent wind vectors are looked up from the pre-computed wind field via get_wind and rotated from the wind-aligned frame into the simulation frame.

Arguments

  • am::AtmosphericModel: atmospheric model; the settings are read from am.set.
  • pos: 3D position of the kite [m]; pos[3] is the height, clamped to 6.0 m minimum.
  • t: current simulation time [s].
  • upwind_dir (optional, default = -π/4): direction the wind is coming FROM [rad]. Zero is north, clockwise positive (same convention as in get_wind).
  • interpolate (optional, default = false): interpolate between grid points, see get_wind.

Returns

A tuple (v_wind, v_wind_tether) of SVec3 in the ENU frame [m/s]:

  • v_wind: wind velocity at the kite position.
  • v_wind_tether: wind velocity at half the kite position (0.5x, 0.5y, 0.5z), with the height clamped to 5.0 m minimum.
source
AtmosphericModels.rel_turboFunction
rel_turbo(am::AtmosphericModel, v_wind = am.set.v_wind)

Find the closest relative turbulence value for a given ground wind speed.

Arguments

  • am::AtmosphericModel: The atmospheric model instance containing relevant parameters.
  • v_wind: (Optional) The wind velocity to use for the calculation. Defaults to am.set.v_wind.

Returns

  • The computed relative turbulence value.
source
AtmosphericModels.new_windfieldFunction
new_windfield(am::AtmosphericModel, v_wind_gnd; prn=true)

Create a new wind field file using the given, scalar ground wind velocity v_wind_gnd.

The field is stored at the reference intensity (sigma1 = calc_sigma1(am, v_wind_gnd)); am.set.use_turbulence and rel_turbo(am) are applied when it is read by get_wind.

Parameters

  • am::AtmosphericModel: The atmospheric model for which the wind field is created.
  • v_wind_gnd: A scalar representing the wind velocity at ground level.
  • prn: Optional boolean flag to control printing of progress messages (default is true).

Returns

  • nothing
source
AtmosphericModels.new_windfieldsFunction
new_windfields(am::AtmosphericModel; prn=true)

Create and initialize new wind fields for all ground wind speeds, defined in am.set.v_wind_gnds and save them for the given AtmosphericModel instance am.

Arguments

  • am::AtmosphericModel: The atmospheric model for which wind fields are to be generated.
  • prn: Optional boolean flag to control printing of progress messages (default is true).

Returns

  • nothing
source
AtmosphericModels.windfield_pathFunction
windfield_path()

Directory the generated .npz wind fields are read from and written to.

By default a Scratch.jl scratchspace, created on first use: the files are derived artifacts of ~1.2 GB apiece, so they belong in a cache shared by every package using this one, not in the data/ directory of each of them. Redirect it with set_windfield_path!.

source
AtmosphericModels.set_windfield_path!Function
set_windfield_path!(path)

Store the wind fields in path instead of the scratchspace, e.g. on a disk with room for them. Pass "" to go back to the default. The directory is created if it does not exist.

source

Private functions

AtmosphericModels.WindFieldMethod
WindField(am, speed; prn=true)

Load (or generate) the wind field for the ground wind speed and wrap it in a WindField.

Throws if the field cannot be built: the settings are checked by check_windfield_settings first, and any later failure propagates. Returning nothing here, as versions before v0.3.8 did, moved the failure to the wf !== nothing assertion in get_wind, a stack trace away from its cause.

source
AtmosphericModels.wind_contextFunction
wind_context(am::AtmosphericModel, wf::WindField, upwind_dir)

Compute everything wind_at needs that does not depend on the position: the sine/cosine of the wind direction, the turbulence scaling and the settings read out of am.set.

Hoisted out of the lookup so that the vector method of get_wind pays for it once per call instead of once per position — rel_turbo in particular allocates.

source
AtmosphericModels.wind_atFunction
wind_at(am, wf, x, y, z, t, cos_dir, sin_dir, rel_turb, profile_law, v_wind, grid_step,
        height_step; interpolate=false)

Look up the wind vector at one position; the trailing arguments come from wind_context.

With interpolate=false the turbulence is read at the nearest grid point, with interpolate=true it is interpolated trilinearly between the eight surrounding ones. The mean wind is a smooth function of the height either way, so only the turbulence is affected.

Returns the tuple (v_x, v_y, v_z) in the wind-aligned frame, see get_wind.

source
AtmosphericModels.check_windfield_settingsFunction
check_windfield_settings(set::Settings)

Throw an ArgumentError naming the offending key if set cannot describe a wind field.

Runs before a field is loaded or generated, so a missing or inconsistent setting is reported against the settings file rather than surfacing later and elsewhere. set.grid defaulting to Int64[] when the YAML does not declare it is the case that motivated this.

source
AtmosphericModels.loadFunction
load(am::AtmosphericModel; v_wind_gnd=8.0)

Read the stored wind field for the ground wind speed v_wind_gnd and return (u, v, w, param).

The file is located with find_windfield, which also accepts the two names used before v0.3.8. Which one was found is logged, because a name without the param_digest cannot be checked against am.set. If there is no file at all, new_windfield generates one under the current name, which takes ~30 s or more.

The x, y and z meshgrids are not read back even when an old file still stores them (622 MB of 1.24 GB for the default grid); the axes are rebuilt from the settings by grid_axes.

Callers normally want load_windfield, which picks v_wind_gnd from am.set.v_wind_gnds and reports back which entry it used.

source
AtmosphericModels.load_windfieldFunction
load_windfield(am::AtmosphericModel, speed)

Load the wind field generated for the am.set.v_wind_gnds entry closest to speed.

Returns (u, v, w, param, v_wind_gnd); the trailing v_wind_gnd is the grid speed that was actually chosen, which is what get_wind needs to pick the matching rel_turbs.

source
AtmosphericModels.find_windfieldFunction
find_windfield(set::Settings, v_wind_gnd)

Path of the stored wind field for v_wind_gnd, without the .npz suffix, or nothing.

Prefers the current name — the one carrying the param_digest, which proves the file matches set — over the two older ones, and windfield_path over get_data_path(), where versions before v0.3.8 kept the files. A file found under an older name cannot be checked against set; load says so.

source
AtmosphericModels.param_digestFunction
param_digest(set::Settings)

Eight hex digits of a SHA-256 over the settings that change the generated field but are not in its file name: grid_step and height_step, which resolve the grid, and i_ref, alpha, avg_height and h_ref, which enter calc_sigma1. grid and the ground wind speed are in the name already.

profile_law and z0 are deliberately not in it: neither reaches the generator, since calc_sigma1 evaluates the wind profile as EXP whatever the setting says. use_turbulence is not either — it scales the field at lookup, see get_wind.

source
AtmosphericModels.grid_axesFunction
grid_axes(am::AtmosphericModel)

The (x, y, z) coordinate axes of the wind field grid as ranges [m], from am.set.grid, am.set.grid_step and am.set.height_step.

x runs downwind from zero, y is centered on zero and z starts at am.set.grid[4]. The stored .npz holds only u, v, w, so these axes are rebuilt here instead of being read back.

source
AtmosphericModels.create_gridFunction
create_grid(am::AtmosphericModel)

Creates a 3D grid for the wind field model.

Parameters

  • am: An instance of AtmosphericModel containing the settings.

Returns Y, X and Z

Three arrays representing the generated 3D grid.

source