Package pyl4c

This is a collection of Python tools for managing, analyzing, and visualizing data from the NASA Soil Moisture Active Passive (SMAP) Level 4 Carbon (L4C) mission. In particular:

Key things to note:

  • File paths for your system may need to be updated in order to access essential ancillary datasets; see pyl4c.data.fixtures.

Sub-modules

pyl4c.apps

Applications of the SMAP Level 4 Carbon (L4C) model, including sub-modules related to L4C model calibration (pyl4c.apps.calibration) and forward …

pyl4c.data
pyl4c.ease2

Functions for affine transformations between geographic coordinates (WGS84) and EASE-Grid 2.0 row-column coordinates …

pyl4c.epsg

Contains spatial reference system (SRS) definitions.

pyl4c.lib
pyl4c.science

Specialized scientific functions for biogeophysical variables and L4C model processes.

pyl4c.spatial

Helper functions and data (EASE-Grid 2.0 parameters) for converting SMAP L4C data from its native HDF5 format into or out of various GIS-friendly …

pyl4c.stats

Various statistical functions. Note that many have an add_intercept argument and that this is True by default, which means the X matrix will have a …

pyl4c.towers

Tools for representing flux tower sites and working with flux tower data …

pyl4c.utils

Convenience functions for working with SMAP L4C data in HDF5 arrays. NOTE: All of the functions beginning with get_ require access to ancillary data …

Functions

def equal_or_nan(x, y)
Expand source code
def equal_or_nan(x, y):
    '''
    A variation on numpy.equal() that also returns True where respective
    inputs are NaN.

    Parameters
    ----------
    x : numpy.ndarray
    y : numpy.ndarray

    Returns
    -------
    numpy.ndarray
    '''
    return np.where(
        np.logical_or(np.isnan(x), np.isnan(y)), True, np.equal(x, y))

A variation on numpy.equal() that also returns True where respective inputs are NaN.

Parameters

x : numpy.ndarray
 
y : numpy.ndarray
 

Returns

numpy.ndarray
 
def haversine(p1, p2, radius=6371000.0)
Expand source code
def haversine(p1, p2, radius = 6371e3):
    '''
    Haversine formula for great circle distance, in meters. Accurate for
    points separated near and far but for small distances the accuracy is
    improved by providing a different radius of the sphere, say 6356.7523 km
    for polar regions or 6378.1370 km for equatorial regions. Default is the
    mean earth radius.

    NOTE: Distance returned is in the same units as radius.

    Parameters
    ----------
    p1 : tuple or list
        Sequence of two floats, longitude and latitude, respectively
    p2 : tuple or list
        Same as p1 but for the second point
    radius : int or float
        Radius of the sphere to use in distance calculation
        (Default: 6,371,000 meters)

    Returns
    -------
    float
    '''
    x1, y1 = map(np.deg2rad, p1)
    x2, y2 = map(np.deg2rad, p2)
    dphi = np.abs(y2 - y1) # Difference in latitude
    dlambda = np.abs(x2 - x1) # Difference in longitude
    angle = 2 * np.arcsin(np.sqrt(np.add(
        np.power(np.sin(dphi / 2), 2),
        np.cos(y1) * np.cos(y2) * np.power(np.sin(dlambda / 2), 2)
    )))
    return float(angle * radius)

Haversine formula for great circle distance, in meters. Accurate for points separated near and far but for small distances the accuracy is improved by providing a different radius of the sphere, say 6356.7523 km for polar regions or 6378.1370 km for equatorial regions. Default is the mean earth radius.

NOTE: Distance returned is in the same units as radius.

Parameters

p1 : tuple or list
Sequence of two floats, longitude and latitude, respectively
p2 : tuple or list
Same as p1 but for the second point
radius : int or float
Radius of the sphere to use in distance calculation (Default: 6,371,000 meters)

Returns

float
 
def pft_dominant(pft_map_array: numpy.ndarray, site_list: Sequence = None) ‑> numpy.ndarray
Expand source code
def pft_dominant(
        pft_map_array: np.ndarray, site_list: Sequence = None) -> np.ndarray:
    '''
    Returns the PFT class dominant among a (1-km) subarray; for example,
    for each 9-km EASE-Grid 2.0 pixel, returns the PFT class that is dominant
    among the 1-km sub-array pixels.

    Parameters
    ----------
    pft_map_array : numpy.ndarray
        An M x N array specifying the PFT code for every pixel

    Returns
    -------
    numpy.ndarray
        An M-element 1D array of the dominant PFT among N pixels
    '''
    most_common = [ # Count how many instances of each PFT code
        a.most_common() for a in
        np.apply_along_axis(lambda x: Counter(x.tolist()), 1, pft_map_array)
    ]
    candidates = [ # Filter out results for PFT codes not in [1,9)
        list(filter(lambda x: x in range(1, 9), options)) for options in
        [[code for code, _ in result] for result in most_common]
    ]
    # Some of the candidates have no valid PFT codes, hence len(x) == 0
    cleaned = [np.nan if len(x) == 0 else x[0] for x in candidates]
    if site_list is None:
        return cleaned
    # Prescribe DNF sites based on an analysis of sites with at least
    #   5% DNF pixels
    for name in ('CN-Moh', 'CA-SCB', 'CA-SCC', 'CA-ARB', 'CA-ARF'):
        if name in site_list:
            idx = site_list.index(name)
            cleaned[idx] = 3
    return cleaned

Returns the PFT class dominant among a (1-km) subarray; for example, for each 9-km EASE-Grid 2.0 pixel, returns the PFT class that is dominant among the 1-km sub-array pixels.

Parameters

pft_map_array : numpy.ndarray
An M x N array specifying the PFT code for every pixel

Returns

numpy.ndarray
An M-element 1D array of the dominant PFT among N pixels
def pft_selector(pft_map, pft)
Expand source code
def pft_selector(pft_map, pft):
    '''
    For a given PFT class, returns the tower sites, as rank indices, that
    represent that PFT. Exceptions are made according to the L4C calibration
    protocol, e.g., sites with any amount of Deciduous Needleleaf (DNF) in
    their 1-km subgrid are considered to represent the DNF PFT class.

    Parameters
    ----------
    pft_map : numpy.ndarray
        An (N x M) array where N is the number of sites and M is the number
        of replicates within each site; e.g., for SMAP L4C, M=81 corresponding
        with the 81 cells of the 1-km subgrid for each eddy covariance flux
        tower site.
    pft : int
        The integer number of the PFT to select

    Returns
    -------
    numpy.ndarray
    '''
    idx = pft_map.shape[0]
    if pft == 3:
        return np.apply_along_axis(lambda x: x == 3, 1, pft_map).any(axis = 1)
    return np.equal(pft, [ # Skip invalid PFT codes
        count[1][0] if count[0][0] not in range(1, 9) else count[0][0]
        for count in [ # Count 1-km cells by PFT
            a.most_common() for a in np.apply_along_axis(
                lambda x: Counter(x.tolist()), 1, pft_map)
        ]
    ])

For a given PFT class, returns the tower sites, as rank indices, that represent that PFT. Exceptions are made according to the L4C calibration protocol, e.g., sites with any amount of Deciduous Needleleaf (DNF) in their 1-km subgrid are considered to represent the DNF PFT class.

Parameters

pft_map : numpy.ndarray
An (N x M) array where N is the number of sites and M is the number of replicates within each site; e.g., for SMAP L4C, M=81 corresponding with the 81 cells of the 1-km subgrid for each eddy covariance flux tower site.
pft : int
The integer number of the PFT to select

Returns

numpy.ndarray
 
def suppress_warnings(func)
Expand source code
def suppress_warnings(func):
    'Decorator to suppress NumPy warnings'
    def inner(*args, **kwargs):
        with warnings.catch_warnings():
            warnings.simplefilter('ignore')
            return func(*args, **kwargs)
    return inner

Decorator to suppress NumPy warnings

Classes

class Namespace
Expand source code
class Namespace(object):
    '''
    Dummy class for holding attributes.
    '''
    def __init__(self):
        pass

    def add(self, label, value):
        '''
        Adds a new attribute to the Namespace instance.

        Parameters
        ----------
        label : str
            The name of the attribute
        value : None
            Any kind of value to be stored
        '''
        setattr(self, label, value)

Dummy class for holding attributes.

Methods

def add(self, label, value)
Expand source code
def add(self, label, value):
    '''
    Adds a new attribute to the Namespace instance.

    Parameters
    ----------
    label : str
        The name of the attribute
    value : None
        Any kind of value to be stored
    '''
    setattr(self, label, value)

Adds a new attribute to the Namespace instance.

Parameters

label : str
The name of the attribute
value : None
Any kind of value to be stored