#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
V1DGeometry:
Contains the classe for Vertical 1D geometry of fields.
"""
from footprints import FootprintBase, FPDict
from epygram import profiles, epygramError, config
from epygram.util import RecursiveObject
[docs]class V1DGeometry(RecursiveObject, FootprintBase):
"""
Handles the geometry for a Vertical 1-Dimension Field (column).
Here, the grid defines the vertical position of each level
between a bottom and a top positions.
The position of points w/r to the vertical grid (mass or flux points),
is interpreted as:\n
- mass: points are located on same levels as the grid points.
- flux: points are located on half-levels, hence are N+1.
"""
_collector = ('geometry',)
_footprint = dict(
attr=dict(
structure=dict(
values=set(['V1D'])),
coordinate=dict(
values=set(['hybrid_pressure', 'hybrid_height', 'pressure',
'altitude', 'height', 'potential_vortex'])),
grid=dict(
type=FPDict,
info="Handles description of the vertical grid."),
hlocation=dict(
type=FPDict,
optional=True,
info="Handles horizontal location of the column."),
position_on_grid=dict(
optional=True,
info="Position of points w/r to the vertical grid.",
values=set(['mass', 'flux']),
default='mass')
)
)
[docs] def hybridP2pressure(self, Psurf, gridposition=None):
"""
Converts a hybrid_pressure coordinate grid into pressure.
*Psurf* is the surface pressure, needed for integration of Ai and Bi.
If *gridposition* is given ('mass' or 'flux'), the target grid is
computed accordingly. If not, the pressures are computed at the
hybrid-pressure gridposition (i.e. flux generally).
"""
if self.grid['gridposition'] != 'flux':
raise NotImplementedError('A and B must define flux levels')
if gridposition == None:
gridposition = self.grid['gridposition']
# compute pressures
A = self.grid['levels']['Ai']
B = self.grid['levels']['Bi']
if gridposition == 'mass':
P = profiles.hybridpressureAB2masspressure(A, B, Psurf)
elif gridposition == 'flux':
P = profiles.hybridpressureAB2fluxpressure(A, B, Psurf)
else:
raise epygramError("gridposition != 'mass' or 'flux'.")
# and update info
self._attributes['coordinate'] = 'pressure'
self._attributes['grid']['gridposition'] = gridposition
self._attributes['grid']['levels'] = tuple(P)
[docs] def hybridP2altitude(self, R, T, Psurf, Pdep=0., Phi_surf=0.):
"""
Converts a hybrid_pressure coordinate grid into altitude of mass levels.
- *R* is the profile of specific gas constant (J/kg/K).
- *T* is the profile of temperature (K).
- *Psurf* is the surface pressure, needed for integration of Ai and Bi.
- *Pdep* is the optional profile of NH pressure departures.
- *Phi_surf* is the optional surface geopotential.
If given, the final coordinate is altitude above sea level,
else height above ground surface.
"""
A = self.grid['levels']['Ai']
B = self.grid['levels']['Bi']
if self.grid['gridposition'] == 'flux':
# compute alt
alt = profiles.hybridpressureAB2altitude(A, B, R, T,
Psurf=Psurf, Pdep=Pdep,
Phi_surf=Phi_surf,
Ptop=config.default_Ptop)
# and update info
if abs(Phi_surf) < config.epsilon:
self._attributes['coordinate'] = 'height'
self._attributes['grid']['levels'] = tuple(alt)
else:
self._attributes['coordinate'] = 'altitude'
self._attributes['grid']['levels'] = tuple(alt)
self._attributes['grid']['gridposition'] = 'mass'
elif self.grid['gridposition'] == 'mass':
raise NotImplementedError("hybrid-pressure grid at mass-levels.")
[docs] def hybridH2altitude(self, Zsurf, gridposition=None, conv2height=False):
"""
Converts a hybrid_height coordinate grid into altitude.
*Zsurf* is the surface pressure, needed for integration of Ai and Bi.
If *gridposition* is given ('mass' or 'flux'), the target grid is
computed accordingly. If not, the altitudes are computed at the
hybrid-height gridposition (i.e. flux generally).
If conv2height is True, conversion into height is performed instead of
altitude.
"""
if self.grid['gridposition'] != 'flux':
raise NotImplementedError('A and B must define flux levels')
if gridposition == None:
gridposition = self.grid['gridposition']
# compute altitudes
A = self.grid['levels']['Ai']
B = self.grid['levels']['Bi']
if gridposition == 'mass':
Z = profiles.hybridheightAB2massheight(A, B, Zsurf,
conv2height=conv2height)
elif gridposition == 'flux':
Z = profiles.hybridheightAB2fluxheight(A, B, Zsurf,
conv2height=conv2height)
else:
raise epygramError("gridposition != 'mass' or 'flux'.")
# and update info
self._attributes['coordinate'] = 'height' if conv2height else 'altitude'
self._attributes['grid']['gridposition'] = gridposition
self._attributes['grid']['levels'] = tuple(Z)
[docs] def pressure2altitude(self, R, T, Pdep=0., Phi_surf=0.):
"""
Converts a pressure coordinate grid (on mass or flux levels) to
altitude on mass levels).
- *R* is the profile of specific gas constant (J/kg/K).
- *T* is the profile of temperature (K).
- *Pdep* is the optional profile of NH pressure departures.
- *Phi_surf* is the optional surface geopotential.
If given, the final coordinate is altitude above sea level,
else height above ground surface.
"""
if self.grid['gridposition'] == 'flux':
# compute alt
alt = profiles.pressure2altitude(R, T, pi_tilde=self.grid['levels'],
Pdep=Pdep, Phi_surf=Phi_surf)
elif self.grid['gridposition'] == 'mass':
# compute alt
alt = profiles.pressure2altitude(R, T, pi=self.grid['levels'],
Pdep=Pdep, Phi_surf=Phi_surf)
# and update info
if abs(Phi_surf) < config.epsilon:
self._attributes['coordinate'] = 'height'
self._attributes['grid']['levels'] = tuple(alt)
else:
self._attributes['coordinate'] = 'altitude'
self._attributes['grid']['levels'] = tuple(alt)
self.grid['gridposition'] = 'mass'
del self._attributes['grid']['levels']