Source code for compute_wind_direction

"""Module for computing wind direction from sonic anemometer measurements.

This module provides functions to convert wind measurements from sonic anemometer
coordinate systems to meteorological wind direction conventions. It handles
different sonic anemometer types (Gill, Campbell, etc.) and accounts for their
specific mounting orientations.

See Also
--------
UCAR EOL Wind Direction Quick Reference:
    https://www.eol.ucar.edu/content/wind-direction-quick-reference
    (but there seems to be an error, with arguments of arctg2 eronously signed,
     and the routine is here vectorized for performance improvement)
Author
------
B. Heinesch
University of Liege, Gembloux Agro-Bio Tech

Created
-------
2020
"""

import numpy as np

[docs] def compute_wind_direction(Usonic, Vsonic, SonicType, SonicNorthAzimuth): """Convert sonic anemometer wind components to meteorological wind direction. This function converts wind velocity components measured in sonic anemometer coordinates to meteorological wind direction (direction FROM which the wind is blowing). It handles different sonic types and mounting orientations. The function is vectorized for improved performance. Coordinate Systems ================== Meteorological (target): - Umet: Positive = wind blowing to East - Vmet: Positive = wind blowing to North - Forms right-handed system with upward Wmet - Wind direction: 0° = North, 90° = East (direction wind is FROM) Sonic (source): - Coordinate system varies by manufacturer - Vsonic axis angle (Vaz) depends on type: * CSAT3: Vaz = array direction - 90° * Gill R2: Vaz = N arrow + 240° (U flipped) * Gill R3/HS50 AXIS: Vaz = N arrow + 240° * Gill R3/HS50 SPAR: Vaz = N arrow + 270° Coordinate Diagram:: v>0 ^ | | +----> U>0 Converting between Sonic and Meteorological Coordinates Determine the angle with respect to true north, (0=N,90=E) of the +Vsonic direction. Call this angle Vaz. Looking from above, the sonic coordinate system is therefore rotated Vaz degrees clockwise from meteorological coordinates. For ATI and Campbell CSAT3 sonics, Vaz is the direction relative to true north, straight into the array from the un-obstructed direction, minus 90 degrees. For Gill R2 sonics, if the sign of V is flipped, then Vaz is the angle of the N arrow + 60. If the sign of U is flipped, then Vaz is the N arrow direction + 240. For Gill R3s in the AXIS configuration, Vaz is the N arrow direction + 240 degrees. From this Meteorological Coordinates, the direction the wind is blowing FROM is finaly computed Parameters ---------- Usonic : array_like U wind component in sonic coordinates [m/s] Vsonic : array_like V wind component in sonic coordinates [m/s] SonicType : str Sonic anemometer model/configuration: - 'R2': Gill R2 - 'R3Spar': Gill R3 SPAR configuration - 'R3Axis': Gill R3 AXIS configuration - 'HS50Spar': Gill HS50 SPAR configuration - 'HS50Axis': Gill HS50 AXIS configuration - 'CSAT3': Campbell CSAT3 SonicNorthAzimuth : float Azimuth angle of sonic anemometer reference direction relative to true North [degrees] (=0 if pointing to North) Returns ------- Dirmet : ndarray Wind direction in meteorological coordinates [degrees] (0-360°, direction wind is coming FROM) Notes ----- The conversion process: 1. Convert U,V components to direction in sonic coordinates 2. Apply sonic-specific rotation (Vaz) 3. Correct for sonic mounting orientation (SonicNorthAzimuth) 4. Convert from 'wind blowing TO' to 'wind blowing FROM' """ # Ensure Usonic and Vsonic are arrays for vectorized operations Usonic = np.asarray(Usonic) Vsonic = np.asarray(Vsonic) if SonicType == 'R2': Usonic = -Usonic Vaz = 240 if SonicType == 'R3Spar' or SonicType == 'HS50Spar': Vaz = 270 if SonicType == 'R3Axis' or SonicType == 'HS50Axis': Vaz = 240 if SonicType == 'CSAT3': Vaz = 180-90 # compute wind direction in the sonic coordinate system Dirsonic = np.arctan2(Usonic, Vsonic) * 180./np.pi Dirsonic = np.where(Dirsonic < 0, Dirsonic + 360, Dirsonic) # adjust negative angles # switch to the met coordinate system Dirmet = Dirsonic + Vaz Dirmet = np.where(Dirmet > 360, Dirmet - 360, Dirmet) # adjust angles > 360 # correct for SonicNorthAzimuth Dirmet = (Dirmet + SonicNorthAzimuth) % 360 # switch from the "to" the "from" wind direction convention Dirmet = np.where(Dirmet < 180, Dirmet + 180, Dirmet - 180) return Dirmet