Examples: Analysis of Real Jumps

This page analyses several jumps in which people have been injured on. The jumps have been measured by the package authors over the years. The intention is to show the utility of the software for analyzing arbitrary jump shapes and to highlight the large equivalent fall heights of these jump constructions. The jumps are idenified by location and the year it was measured.

Import packages needed on this page:

import numpy as np
import matplotlib.pyplot as plt
from skijumpdesign import Skier, Surface
from skijumpdesign.functions import (
    make_jump, plot_efh, cartesian_from_measurements)

(Source code)

Selection of an Equivalent Fall Height

Beginner ski slopes range from 6% to 25% grade (3 to 14 degrees) and intermediate ski slopes range from 25% to 40% (14 to 22 degrees) [1]. Terrain parks are typically built on steeper beginner slopes or shallow intermediate slopes, thus a parent slope grade of 25% (14 degrees) is a reasonable choice to compare the snow budgets of different jumps designed with equivalent fall heights. The following plot shows how the snow budget increases as EFH decreases.

parent_slope_grade = 0.25  # percent grade
parent_slope_angle = -np.rad2deg(np.arctan(parent_slope_grade))  # degrees
approach_length = 100.0  # meters
takeoff_angle = 20.0  # degrees

fig, ax = plt.subplots(1, 1)

for efh, color in zip((0.5, 1.0, 1.5), ('C0', 'C1', 'C2')):
   jump = make_jump(parent_slope_angle, 0.0, approach_length,
                    takeoff_angle, efh)
   snow_budget = jump[-1]['Snow Budget']
   for i, surf in enumerate(jump[3:-2]):
      if i % 2 == 1:
         lab = 'EFH: {:1.1f} m, Snow Budget: {:1.0f} m²'.format(efh, snow_budget)
      else:
         lab = None
      surf.plot(ax=ax, color=color, label=lab)

ax.set_xlabel('Horizontal Position [m]')
ax.set_ylabel('Vertical Position [m]')
ax.set_aspect('equal')
ax.legend()
ax.grid()

(Source code, png, hires.png, pdf)

_images/real-jumps-2.png

An equivalent fall height of 1.5 m will, on average, cause knee collapse in an adult [2]. This is a sensible absolute maximal boundary for equivalent fall height in constructed jumps. An equivalent fall height of 0.5 m is a fairly benign height, similar to falling from two or three stair steps. A height of 1 m is a good compromise between these two numbers that has a reasonable snow budget with moderate height. The following jumps will compared to jumps designed with a 1 m equivalent fall height for this reason.

[1]https://en.wikipedia.org/wiki/Piste
[2]A. E. Minetti, “Using leg muscles as shock absorbers: theoretical predictions and experimental results of drop landing performance,” Ergonomics, vol. 41, no. 12, pp. 1771–1791, Dec. 1998, doi: 10.1080/001401398185965.

Design Speed

The “design speed” for a constant equivalent fall height jump is defined in [3] as:

The maximum takeoff velocity (resulting from the highest start point and minimum snow friction \(\mu\) and air drag \(\eta\)) is called the design speed.

It is the maximum expected takeoff speed of a skier or snowboarder, which is a function of the inrun length, slope, friction coefficient, and air drag. The designed jumps ensure a constant equivalent fall height up to this design speed.

[3]Levy, Dean, Mont Hubbard, James A. McNeil, and Andrew Swedberg. “A Design Rationale for Safer Terrain Park Jumps That Limit Equivalent Fall Height.” Sports Engineering 18, no. 4 (December 2015): 227–39. https://doi.org/10.1007/s12283-015-0182-6.

California 2002

The california-2002-surface.csv file contains the horizontal (x) and vertical (y) coordinates of a jump measured at a ski resort in California, USA in 2002. The comma separated value file can be loaded with numpy.loadtxt() and used to create a Surface. The plot() method is used to quickly visualize the measured landing surface. The takeoff location is situated at (x=0 m, y=0 m).

landing_surface_data = np.loadtxt('california-2002-surface.csv',
                                  delimiter=',',  # comma separated
                                  skiprows=1)  # skip the header row

landing_surface = Surface(landing_surface_data[:, 0],  # x values in meters
                          landing_surface_data[:, 1])  # y values in meters

ax = landing_surface.plot()

(Source code, png, hires.png, pdf)

_images/real-jumps-3.png

The takeoff angle of this jump was measured as 13 degrees. Using this angle the equivalent fall height can be visualized across the landing surface.

takeoff_angle = 30.0  # degrees
takeoff_point = (0.0, 0.0)  # meters

skier = Skier()

plot_efh(landing_surface, takeoff_angle, takeoff_point,
         skier=skier, increment=1.0)

(Source code, png, hires.png, pdf)

_images/real-jumps-4.png

The actual jump can be compared to a jump designed with a constant equivalent fall height. The figure below shows such a comparison.

def compare_measured_to_designed(measured_surface, equiv_fall_height,
                                 parent_slope_angle, approach_length,
                                 takeoff_angle, skier):

    # NOTE : A different Skier() object is used internally in make_jump()
    slope, approach, takeoff, landing, landing_trans, flight, outputs = \
        make_jump(parent_slope_angle, 0.0, approach_length, takeoff_angle,
                  equiv_fall_height)

    measured_surface.shift_coordinates(takeoff.end[0], takeoff.end[1])

    design_speed = flight.speed[0]
    low_speed = 1/2*design_speed
    med_speed = 3/4*design_speed

    vel_vec = np.array([np.cos(np.deg2rad(takeoff_angle)),
                        np.sin(np.deg2rad(takeoff_angle))])

    flight_low = skier.fly_to(measured_surface, init_pos=takeoff.end,
                              init_vel=tuple(low_speed*vel_vec))
    flight_med = skier.fly_to(measured_surface, init_pos=takeoff.end,
                              init_vel=tuple(med_speed*vel_vec))

    fig, (prof_ax, efh_ax) = plt.subplots(2, 1, sharex=True,
                                          constrained_layout=True)

    increment = 1.0

    dist, efh, _ = measured_surface.calculate_efh(np.deg2rad(takeoff_angle),
                                                  takeoff.end, skier, increment)

    efh_ax.bar(dist, efh, color='black', align='center', width=increment/2,
               label="Measured Landing Surface")

    dist, efh, _ = landing.calculate_efh(np.deg2rad(takeoff_angle),
                                         takeoff.end, skier, increment)

    efh_ax.bar(dist, efh, color='C2', align='edge', width=increment/2,
               label="Designed Landing Surface")

    dist, efh, _ = landing_trans.calculate_efh(np.deg2rad(takeoff_angle),
                                               takeoff.end, skier, increment)

    efh_ax.bar(dist, efh, color='C2', align='edge', width=increment/2,
               label=None)

    efh_ax.axhline(5.1, color='C1', label='Avg. 2 Story Fall Height')
    efh_ax.axhline(2.6, color='C1', linestyle='dashed',
                   label='Avg. 1 Story Fall Height')
    efh_ax.axhline(1.5, color='C1', linestyle='dashdot',
                   label='Knee Collapse Height')

    prof_ax = takeoff.plot(ax=prof_ax, linewidth=2, color='C2', label=None)

    prof_ax = flight_low.plot(ax=prof_ax, color='black', linestyle='dashdot',
                              label='Flight @ {:1.0f} m/s'.format(low_speed))
    prof_ax = flight_med.plot(ax=prof_ax, color='black', linestyle='dashed',
                              label='Flight @ {:1.0f} m/s'.format(med_speed))
    prof_ax = flight.plot(ax=prof_ax, color='black', linestyle='dotted',
                          label='Flight @ {:1.0f} m/s'.format(design_speed))

    prof_ax = landing.plot(ax=prof_ax, color='C2', linewidth=2, label=None)
    prof_ax = landing_trans.plot(ax=prof_ax, color='C2', linewidth=2,
                                 label='Designed Landing Surface')

    prof_ax = measured_surface.plot(ax=prof_ax, color='black',
                                    label="Measured Landing Surface")

    prof_ax.set_title('Design Speed: {:1.0f} m/s'.format(design_speed))

    prof_ax.set_ylabel('Vertical Position [m]')
    efh_ax.set_ylabel('Equivalent Fall Height [m]')
    efh_ax.set_xlabel('Horizontal Position [m]')

    efh_ax.grid()
    prof_ax.grid()
    efh_ax.legend(loc='upper left')
    prof_ax.legend(loc='lower left')

    return prof_ax, efh_ax

(Source code)

fall_height = 1.0  # meters
slope_angle = -8.0  # degrees
approach_length = 180.0  # meters

compare_measured_to_designed(landing_surface, fall_height, slope_angle,
                             approach_length, takeoff_angle, skier)

(Source code, png, hires.png, pdf)

_images/real-jumps-6.png

The average story heights are estimated from [4].

[4]N. L. Vish, “Pediatric window falls: not just a problem for children in high rises,” Injury Prevention, vol. 11, no. 5, pp. 300–303, Oct. 2005, doi: 10.1136/ip.2005.008664.

Washington 2004

The washington-2004-surface.csv file contains the horizontal (x) and vertical (y) coordinates of a jump measured at a Washington, USA ski resort in 2004. The comma separated value file can be loaded with numpy.loadtxt() and used to create a Surface. The plot() method is used to quickly visualize the measured landing surface. The takeoff location is situated at (x=0 m, y=0 m).

landing_surface_data = np.loadtxt('washington-2004-surface.csv',
                                  delimiter=',',  # comma separated
                                  skiprows=1)  # skip the header row

landing_surface = Surface(landing_surface_data[:, 0],  # x values in meters
                          landing_surface_data[:, 1])  # y values in meters

ax = landing_surface.plot()

(Source code, png, hires.png, pdf)

_images/real-jumps-7.png

The takeoff angle of this jump was measured as 25 degrees. Using this angle the equivalent fall height can be visualized across the landing surface.

takeoff_angle = 25.0  # degrees
takeoff_point = (0.0, 0.0)  # meters

skier = Skier()

plot_efh(landing_surface, takeoff_angle, takeoff_point,
         skier=skier, increment=1.0)

(Source code, png, hires.png, pdf)

_images/real-jumps-8.png

For high takeoff speeds, this jump has very large equivalent fall heights (3 m to 13 m).

The actual jump can be compared to a jump designed with a constant equivalent fall height. The figure below shows such a comparison. Note that the first 15 meters or so of the surface is reasonable, but if a jumper lands beyond 15 m they will be subjected to dangerous impact velocities.

fall_height = 1.0  # meters
slope_angle = -10.0  # degrees
approach_length = 220.0  # meters

compare_measured_to_designed(landing_surface, fall_height, slope_angle,
                             approach_length, takeoff_angle, skier)

(Source code, png, hires.png, pdf)

_images/real-jumps-9.png

Utah 2010

The utah-2010-surface.csv file contains the horizontal (x) and vertical (y) coordinates of a jump measured at a Utah, USA ski resort in February 2010. The comma separated value file can be loaded with numpy.loadtxt() and used to create a Surface. The plot() method is used to quickly visualize the measured landing surface. The takeoff location is situated at (x=0 m, y=0 m).

landing_surface_data = np.loadtxt('utah-2010-surface.csv',
                                  delimiter=',',  # comma separated
                                  skiprows=1)  # skip the header row

landing_surface = Surface(landing_surface_data[:, 0],  # x values in meters
                          landing_surface_data[:, 1])  # y values in meters

ax = landing_surface.plot()

(Source code, png, hires.png, pdf)

_images/real-jumps-10.png

The takeoff angle of this jump was measured as 23 degrees. Using this angle the equivalent fall height can be visualized across the landing surface.

takeoff_angle = 23.0  # degrees
takeoff_point = (0.0, 0.0)  # meters

skier = Skier()

plot_efh(landing_surface, takeoff_angle, takeoff_point,
         skier=skier, increment=1.0)

(Source code, png, hires.png, pdf)

_images/real-jumps-11.png

For high takeoff speeds, this jump has very large equivalent fall heights (5 m to 10 m). And no mater the takeoff speed, the equivalent fall height is greater than or equal to the 1.5 m threshold for knee collapse.

The measured jump can be compared to a jump designed to ensure a constant equivalent fall height of 1.5 m at any takeoff speed. The figure below shows such a comparison. Note that the first 15 meters or so of the surface is reasonable, but if a jumper lands beyond 15 m they will be subjected to dangerous impact speeds.

fall_height = 1.0  # meters
slope_angle = -12.0  # degrees
approach_length = 220.0  # meters

compare_measured_to_designed(landing_surface, fall_height, slope_angle,
                             approach_length, takeoff_angle, skier)

(Source code, png, hires.png, pdf)

_images/real-jumps-12.png

Colorado 2009

The colorado-2009-surface.csv file contains the horizontal (x) and vertical (y) coordinates of a jump measured by professional surveyors at a Colorado, USA ski resort in March 2009. The comma separated value file can be loaded with numpy.loadtxt() and used to create a Surface. The plot() method is used to quickly visualize the measured landing surface. The takeoff location is situated at (x=0 m, y=0 m).

landing_surface_data = np.loadtxt('colorado-2009-surface.csv',
                                  delimiter=',',  # comma separated
                                  skiprows=1)  # skip the header row

landing_surface = Surface(landing_surface_data[:, 0],  # x values in meters
                          landing_surface_data[:, 1])  # y values in meters

ax = landing_surface.plot()

(Source code, png, hires.png, pdf)

_images/real-jumps-13.png

The takeoff angle of this jump was measured as 16 degrees. Using this angle the equivalent fall height can be visualized across the landing surface.

takeoff_angle = 16.0  # degrees
takeoff_point = (0.0, 0.0)  # meters

skier = Skier()

plot_efh(landing_surface, takeoff_angle, takeoff_point,
         skier=skier, increment=1.0)

(Source code, png, hires.png, pdf)

_images/real-jumps-14.png

The actual jump can be compared to a jump designed with a constant equivalent fall height. The figure below shows such a comparison.

fall_height = 1.0  # meters
slope_angle = -15.0  # degrees
approach_length = 70.0  # meters

compare_measured_to_designed(landing_surface, fall_height, slope_angle,
                             approach_length, takeoff_angle, skier)

(Source code, png, hires.png, pdf)

_images/real-jumps-15.png

Wisconsin 2015

The wisconsin-2015-surface.csv file contains the horizontal (x) and vertical (y) coordinates of a jump measured at a Wisconsin, USA ski resort in 2015. The comma separated value file can be loaded with numpy.loadtxt() and used to create a Surface. The plot() method is used to quickly visualize the measured landing surface. The takeoff location is situated at (x=0 m, y=0 m).

landing_surface_data = np.loadtxt('wisconsin-2015-surface.csv',
                                  delimiter=',',  # comma separated
                                  skiprows=1)  # skip the header row

landing_surface = Surface(landing_surface_data[:, 0],  # x values in meters
                          landing_surface_data[:, 1])  # y values in meters

ax = landing_surface.plot()

(Source code, png, hires.png, pdf)

_images/real-jumps-16.png

The takeoff angle of this jump was measured as 13 degrees. Using this angle the equivalent fall height can be visualized across the landing surface.

takeoff_angle = 13.0  # degrees
takeoff_point = (0.0, 0.0)  # meters

skier = Skier()

plot_efh(landing_surface, takeoff_angle, takeoff_point,
         skier=skier, increment=1.0)

(Source code, png, hires.png, pdf)

_images/real-jumps-17.png

The actual jump can be compared to a jump designed with a constant equivalent fall height. The figure below shows such a comparison.

fall_height = 1.0  # meters
slope_angle = -10.0  # degrees
approach_length = 100.0  # meters

compare_measured_to_designed(landing_surface, fall_height, slope_angle,
                             approach_length, takeoff_angle, skier)

(Source code, png, hires.png, pdf)

_images/real-jumps-18.png

Sydney 2020

The sydney-measurements-2020.csv file contains the distance along the jump surface and absolute angle measurements (different measures than all above files) of a single-track dirt mountain bike jump measured near Sydney, Australia in 2020. The comma separated value file can be loaded with numpy.loadtxt(). These measurements require conversion to the Cartesian coordinates for constructing the surface using cartesian_from_measurements(). After conversion the data can be used to create a Surface. The plot() method is used to quickly visualize the measured landing surface. The takeoff location is situated at the first measurement point.

surface_measurement_data = np.loadtxt('sydney-measurements-2020.csv',
                                      delimiter=',',  # comma separated
                                      skiprows=1)  # skip the header row

x, y, takeoff_point, takeoff_angle = cartesian_from_measurements(
    surface_measurement_data[:, 0],  # distance along surface in meters
    np.deg2rad(surface_measurement_data[:, 1]))  # absolute angle deg -> rad

landing_surface = Surface(x,  # x values in meters
                          y)  # y values in meters

ax = landing_surface.plot()

(Source code, png, hires.png, pdf)

_images/real-jumps-19.png

The takeoff angle is taken from the angle measurements. Using this angle the equivalent fall height can be visualized across the landing surface.

skier = Skier()

plot_efh(landing_surface, np.rad2deg(takeoff_angle), takeoff_point,
         skier=skier, increment=1.0)

(Source code, png, hires.png, pdf)

_images/real-jumps-20.png

The actual jump can be compared to a jump designed with a constant equivalent fall height. The figure below shows such a comparison.

fall_height = 1.0  # meters
slope_angle = -7.0  # degrees
approach_length = 140.0  # meters

compare_measured_to_designed(landing_surface, fall_height, slope_angle,
                             approach_length, np.rad2deg(takeoff_angle), skier)

(Source code, png, hires.png, pdf)

_images/real-jumps-21.png