# -*- coding: utf-8 -*-
import typing
import pydantic
from snowprofile._base_classes import BaseProfile, BaseProfile2
from snowprofile._constants import QUALITY_FLAGS, GRAIN_SHAPES, MANUAL_WETNESS, MANUAL_HARDNESS, \
manual_wetness_attribution, manual_hardness_attribution
__all__ = ['Stratigraphy',
'TemperatureProfile', 'DensityProfile', 'LWCProfile',
'SSAProfile', 'SSAPointProfile',
'HardnessProfile', 'HardnessPointProfile',
'StrengthProfile', 'ImpurityProfile',
'ScalarProfile', 'VectorialProfile']
[docs]
class Stratigraphy(BaseProfile):
"""
Measurements of the snowpack stratigraphy, according to the international snowpack classification,
Fierz et al. 2009.
The ``data`` key contains:
- ``top_height``: Top height of the snow layer (m)
- ``bottom_height``: Bottom height of the snow layer (m)
- ``thickness``: Thickness of the snow layer (m)
- ``grain_1``: Primary snow grain shape
- ``grain_2``: Secondary snow grain shape
- ``grain_size``: Snow grain size (m)
- ``grain_size_max``: Maximum snow grain size (m)
- ``hardness``: Manual hand hardness (intermediates are allowed, details in Fierz et al., 2009):
- F: Fist (1)
- 4F : 4 Fingers (2)
- 1F : 1 Finger (3)
- P : Pencil (4)
- K : Knife (5)
- ``wetness``: Manual snow wetness (intermediates are allowed, details in Fierz et al., 2009):
- D : Dry (1)
- M : Moist (2)
- W : Wet (3)
- V : Very wet (4)
- S : Soaked (5)
- ``formation_time``: Date of the layer formation
- ``formation_period_begin``: Date of the begining of the layer formation
- ``formation_period_end``: Date of the end of the layer formation
- ``loc``: Layer of concern, possible values are 'no', 'top', 'bottom', 'all', 'true', 'false'
- ``comment``: Comment on the layer
- ``uncertainty``: Quantitative uncertainty on the layer (see :ref:`uncertainty`)
- ``quality``: Quality flag on the layer (see :ref:`uncertainty`)
"""
_data_config = dict(
grain_1=dict(type='O',
values=GRAIN_SHAPES + [None]),
grain_2=dict(type='O',
values=GRAIN_SHAPES + [None]),
grain_size=dict(min=0,
nan_allowed=True),
grain_size_max=dict(min=0,
optional=True,
nan_allowed=True),
hardness=dict(type='O',
values=MANUAL_HARDNESS + [None],
translate=manual_hardness_attribution,
),
wetness=dict(type='O',
values=MANUAL_WETNESS + [None],
translate=manual_wetness_attribution,
),
loc=dict(type='O',
optional=True,
values=['no', 'top', 'bottom', 'all', 'true', 'false', None]),
uncertainty=dict(optional=True,
nan_allowed=True),
quality=dict(optional=True,
type='O',
values=QUALITY_FLAGS + [None]),
comment=dict(optional=True,
type='O', ),
additional_data=dict(optional=True,
type='O', ),
formation_time=dict(optional=True,
type='O'),
formation_period_begin=dict(optional=True,
type='O'),
formation_period_end=dict(optional=True,
type='O'),
)
[docs]
class TemperatureProfile(BaseProfile2):
"""
Vertical profile of temperature profile.
The ``data`` key contains:
- ``height``: Height of the temperature measurement (m)
- ``temperature``: Temperature value (°C)
- ``uncertainty``: Quantitative uncertainty of the point measurement (same unit as data).
See :ref:`uncertainty` for details.
- ``quality``: Quality flag of the point measurement. See :ref:`uncertainty` for details.
"""
method_of_measurement: typing.Optional[typing.Literal[
"Thermometer", "IR thermometer", "IR photography", "other"]] = None
_data_config = dict(
_mode='Point',
temperature=dict(max=0),
uncertainty=dict(optional=True,
nan_allowed=True),
quality=dict(optional=True,
type='O',
values=QUALITY_FLAGS + [None]),
)
[docs]
class DensityProfile(BaseProfile2):
"""
Vertical profile of density (kg/m3).
The ``data`` key contains:
- ``top_height``: Top height of the measurement (m)
- ``bottom_height``: Bottom height of the measurement (m)
- ``thickness``: Thickness (m)
- ``density``: Density value (kg/m3)
- ``uncertainty``: Quantitative uncertainty of the point measurement (same unit as data).
See :ref:`uncertainty` for details.
- ``quality``: Quality flag of the point measurement. See :ref:`uncertainty` for details.
"""
method_of_measurement: typing.Optional[typing.Literal[
"Snow Tube", "Snow Cylinder", "Snow Cutter", "Snow wedge Cutter", "other gravimetric measurement method",
"Denoth Probe", "SnowPro Probe", "Snow Fork", "other dielectric permittivity method",
"Tomography", "SMP", "Neutron scattering probe",
"other"]] = pydantic.Field(
None,
description="Measurement method")
probed_volume: typing.Optional[float] = pydantic.Field(
None,
description="Probe volume (m3)")
probed_diameter: typing.Optional[float] = pydantic.Field(
None,
description="Probe diameter (m)")
probed_length: typing.Optional[float] = pydantic.Field(
None,
description="Probe length (m)")
probed_thickness: typing.Optional[float] = pydantic.Field(
None,
description="Probe thickness (vertical dimension, m)")
_data_config = dict(
density=dict(min=0, max=917),
uncertainty=dict(optional=True,
nan_allowed=True),
quality=dict(optional=True,
type='O',
values=QUALITY_FLAGS + [None]),
)
[docs]
class LWCProfile(BaseProfile2):
"""
Vertical profile of LWC (Liquid water content, % by volume).
The ``data`` key contains:
- ``top_height``: Top height of the measurement (m)
- ``bottom_height``: Bottom height of the measurement (m)
- ``thickness``: Thickness (m)
- ``lwc``: Liqud water content (% vol.)
- ``uncertainty``: Quantitative uncertainty of the measurement (same unit as data).
See :ref:`uncertainty` for details.
- ``quality``: Quality flag of the measurement. See :ref:`uncertainty` for details.
"""
method_of_measurement: typing.Optional[typing.Literal[
"Denoth Probe", "Snow Fork", "SnowPro Probe", "WISe", "other dielectric permittivity method",
"MRI", "other"]] = pydantic.Field(
None,
description="Measurement method")
probed_thickness: typing.Optional[float] = pydantic.Field(
None,
description="Probe thickness (vertical dimension of measurement, m)")
_data_config = dict(
lwc=dict(min=0, max=100),
uncertainty=dict(optional=True,
nan_allowed=True),
quality=dict(optional=True,
type='O',
values=QUALITY_FLAGS + [None]),
)
class _SSAProfile(BaseProfile2):
"""
Vertical profile of SSA (Specific surface area, m2/kg).
Base class for SSA, whether the profile is a point profile or a layer profile.
"""
method_of_measurement: typing.Optional[typing.Literal[
"Ice Cube", "IRIS", "InfraSnow", "DUFFISSS-1310", "DUFFISSS-1550",
"HISSGraS", "ASSSAP", "other IR integrating sphere",
"SWIRcam", "SnowImager", "other NIR method",
"Tomography", "SMP",
"other"]] = pydantic.Field(
None,
description="Measurement method")
probed_thickness: typing.Optional[float] = pydantic.Field(
None,
description="Probe thickness (vertical dimension of measurement, m)")
[docs]
class SSAProfile(_SSAProfile):
"""
Vertical profile of SSA (Specific surface area, m2/kg), measured on layers (volume).
The ``data`` key contains:
- ``top_height``: Top height of the measurement (m)
- ``bottom_height``: Bottom height of the measurement (m)
- ``thickness``: Thickness (m)
- ``ssa``: SSA value (m2/kg)
- ``uncertainty``: Quantitative uncertainty of the measurement (same unit as data).
See :ref:`uncertainty` for details.
- ``quality``: Quality flag of the measurement. See :ref:`uncertainty` for details.
"""
_data_config = dict(
ssa=dict(min=0),
uncertainty=dict(optional=True,
nan_allowed=True),
quality=dict(optional=True,
type='O',
values=QUALITY_FLAGS + [None]),
)
type: typing.Literal['SSAProfile'] = 'SSAProfile'
[docs]
class SSAPointProfile(_SSAProfile):
"""
Vertical profile of SSA (Specific Surface Area), measured at points (heights).
The ``data`` key contains:
- ``height``: Height of the measurement (m)
- ``ssa``: SSA value (m2/kg)
- ``uncertainty``: Quantitative uncertainty of the measurement (same unit as data).
See :ref:`uncertainty` for details.
- ``quality``: Quality flag of the measurement. See :ref:`uncertainty` for details.
"""
_data_config = dict(
_mode='Point',
ssa=dict(min=0),
uncertainty=dict(optional=True,
nan_allowed=True),
quality=dict(optional=True,
type='O',
values=QUALITY_FLAGS + [None]),
)
type: typing.Literal['SSAPointProfile'] = 'SSAPointProfile'
class _HardnessProfile(BaseProfile2):
"""
Vertical profile of hardness (N).
Base class for both RAMSONDE profile or simple hardness profile from other source.
"""
method_of_measurement: typing.Optional[typing.Literal[
"SnowMicroPen", "Ram Sonde", "Push-Pull Gauge",
"Avatech SP1", "Avatech SP2", "Scope propagation labs",
"other automatic penetrometer",
"other"]] = pydantic.Field(
None,
description="Measurement method")
surface_of_indentation: typing.Optional[float] = pydantic.Field(
None,
description='Indentation surface of the measurement tool (m2)')
penetration_speed: typing.Optional[float] = pydantic.Field(
None,
description='Penetration speed (m/s)')
[docs]
class HardnessPointProfile(_HardnessProfile):
"""
Vertical profile of hardness.
The ``data`` key contains:
- ``height``: Height of the measurement (m)
- ``hardness``: Hardness value (N)
- ``uncertainty``: Quantitative uncertainty of the measurement (same unit as data).
See :ref:`uncertainty` for details.
- ``quality``: Quality flag of the measurement. See :ref:`uncertainty` for details.
"""
_data_config = dict(
_mode='Point',
hardness=dict(min=0),
uncertainty=dict(optional=True,
nan_allowed=True),
quality=dict(optional=True,
type='O',
values=QUALITY_FLAGS + [None]),
)
type: typing.Literal['HardnessPointProfile'] = 'HardnessPointProfile'
[docs]
class HardnessProfile(_HardnessProfile):
"""
Vertical profile of hardness profile, measured on snow layers (e.g. for RamSonde measurements).
The ``data`` key contains:
- ``top_height``: Top height of the measurement (m)
- ``bottom_height``: Bottom height of the measurement (m)
- ``thickness``: Thickness (m)
- ``hardness`` (N): Hardness value (N)
- ``weight_hammer`` (kg)
- ``weight_tube`` (kg)
- ``n_drops`` (-)
- ``drop_height`` (m)
- ``uncertainty``: Quantitative uncertainty of the measurement (same unit as data).
See :ref:`uncertainty` for details.
- ``quality``: Quality flag of the measurement. See :ref:`uncertainty` for details.
"""
_data_config = dict(
hardness=dict(min=0),
weight_hammer=dict(min=0, optional=True),
weight_tube=dict(min=0, optional=True),
n_drops=dict(type=int, min=0, optional=True),
drop_height=dict(min=0, optional=True),
uncertainty=dict(optional=True,
nan_allowed=True),
quality=dict(optional=True,
type='O',
values=QUALITY_FLAGS + [None]),
)
# TODO: Computation of hardness from other data ?
type: typing.Literal['HardnessProfile'] = 'HardnessProfile'
[docs]
class StrengthProfile(BaseProfile2):
"""
Vertical profile of strength (N).
The ``data`` key contains:
- ``top_height``: Top height of the measurement (m)
- ``bottom_height``: Bottom height of the measurement (m)
- ``thickness``: Thickness (m)
- ``strength``: Strength value (N)
- ``fracture_character`` (optional):
- SP: Sudden planar
- SC: Sudden collapse
- SDN: Sudden (both)
- RP: Resistant planar
- PC: Progressive compression
- RES: Resistant (both)
- BRK or B: Break
- X: Other/Unknown
- ``uncertainty``: Quantitative uncertainty of the measurement (same unit as data).
See :ref:`uncertainty` for details.
- ``quality``: Quality flag of the measurement. See :ref:`uncertainty` for details.
"""
method_of_measurement: typing.Optional[typing.Literal[
"Shear Frame",
"other"]] = pydantic.Field(
None,
description="Measurement method")
strength_type: typing.Optional[typing.Literal[
"compressive", "tensile", "shear", "mixed", "other"]] = pydantic.Field(
None,
description="Shear direction: compressive, tensile, shear, other or mixed")
probed_area: typing.Optional[float] = pydantic.Field(
None,
description="Probed area (m2)")
_data_config = dict(
strength=dict(min=0),
fracture_character=dict(optional=True,
nan_allowed=True,
values=["SDN", "SP",
"SC", "RES",
"PC", "RP",
"BRK", "B",
"X", None],
type='O'),
uncertainty=dict(optional=True,
nan_allowed=True),
quality=dict(optional=True,
type='O',
values=QUALITY_FLAGS + [None]),
)
[docs]
class ImpurityProfile(BaseProfile2):
"""
Vertical profile of impurities (mass or volume fraction).
The ``data`` key contains:
- ``top_height``: Top height of the measurement (m)
- ``bottom_height``: Bottom height of the measurement (m)
- ``thickness``: Thickness (m)
- ``mass_fraction``: Mass fraction of impurity (%)
- ``volume_fraction``: Volume fration of impurity (%)
- ``uncertainty``: Quantitative uncertainty of the measurement (same unit as data).
See :ref:`uncertainty` for details.
- ``quality``: Quality flag of the measurement. See :ref:`uncertainty` for details.
"""
method_of_measurement: typing.Optional[typing.Literal[
"other"]] = pydantic.Field(
None,
description="Measurement method")
impurity_type: typing.Literal[
"Black Carbon", "Dust", "Isotopes", "Other"] = pydantic.Field(
description="Impurity type for the profile")
probed_volume: typing.Optional[float] = pydantic.Field(
None,
description="Probe volume (m3)")
probed_diameter: typing.Optional[float] = pydantic.Field(
None,
description="Probe volume (m)")
probed_length: typing.Optional[float] = pydantic.Field(
None,
description="Probe length (m)")
probed_thickness: typing.Optional[float] = pydantic.Field(
None,
description="Probe thickness (vertical dimension, m)")
_data_config = dict(
mass_fraction=dict(min=0, max=100,
optional=True,
nan_allowed=True),
volume_fraction=dict(min=0, max=100,
optional=True,
nan_allowed=True),
uncertainty=dict(optional=True,
nan_allowed=True),
quality=dict(optional=True,
type='O',
values=QUALITY_FLAGS + [None]),
)
[docs]
class ScalarProfile(BaseProfile2):
"""
Other profile of scalar data (not covered by other Profile class).
The ``data`` key contains:
- ``top_height``: Top height of the measurement (m)
- ``bottom_height``: Bottom height of the measurement (m)
- ``thickness``: Thickness (m)
- ``data``: Data value
- ``uncertainty``: Quantitative uncertainty of the measurement (same unit as data).
See :ref:`uncertainty` for details.
- ``quality``: Quality flag of the measurement. See :ref:`uncertainty` for details.
"""
method_of_measurement: str = pydantic.Field(
description="Measurement method description")
unit: str = pydantic.Field(
description="Unit (SI unit please)")
parameter: str = pydantic.Field(
description="Measured parameter")
_data_config = dict(
data=dict(),
uncertainty=dict(optional=True,
nan_allowed=True),
quality=dict(optional=True,
type='O',
values=QUALITY_FLAGS + [None]),
)
[docs]
class VectorialProfile(ScalarProfile):
"""
Other profile of vectorial data (data inherently multi-dimensional, with same unit and type).
The ``data`` key is a pandas.DataFrame with key data containing a python list of values.
"""
rank: int = pydantic.Field(
gt = 1,
description="Length of the vector (>1, otherwise use OtherScalarProfile)")
_data_config = dict(
data=dict(type='O'),
uncertainty=dict(optional=True,
nan_allowed=True),
quality=dict(optional=True,
type='O',
values=QUALITY_FLAGS + [None]),
)