Source code for vtools.functions.unit_conversions
import numpy as np
from scipy.optimize import brentq
# Constants for conversions
J1 = -16.072
J2 = 4.1495
J3 = -0.5345
J4 = 0.0261
K1 = 0.0120
K2 = -0.2174
K3 = 25.3283
K4 = 13.7714
K5 = -6.4788
K6 = 2.5842
k = 0.0162
s_sea = 35.0
ec_sea = 53087.0
M2FT = 3.28084
FT2M = 0.3048
CFS2CMS = 0.028316847
CMS2CFS = 35.31466621
[docs]
def m_to_ft(x):
"""Convert meter to foot
WARNING: The function modifies the input argument if it is TimeSeries
"""
try:
x.props["unit"] = "ft"
x.data *= M2FT
return x
except:
return x * M2FT
[docs]
def ft_to_m(x):
"""Convert foot to meter"""
try:
x.props["unit"] = "m"
x.data *= FT2M
return x
except:
return x * FT2M
[docs]
def cms_to_cfs(x):
"""Convert cms to cfs"""
try:
x.props["unit"] = "cfs"
x.data *= CMS2CFS
return x
except:
return x * CMS2CFS
[docs]
def cfs_to_cms(x):
"""Convert cfs to cms"""
try:
x.props["unit"] = "cms"
x.data *= CFS2CMS
return x
except:
return x * CFS2CMS
[docs]
def fahrenheit_to_celsius(x):
"""Convert cfs to cms"""
try:
x.props["unit"] = "deg C"
x.data -= 32.0
x.data *= 5.0 / 9.0
return x
except:
return (x - 32.0) * 5.0 / 9.0
[docs]
def celsius_to_fahrenheit(x):
"""Convert cfs to cms"""
try:
x.props["unit"] = "deg F"
x.data *= 1.8
x.data += 32.0
return x
except:
return x * 1.8 + 32.0
[docs]
def psu_ec_resid(x, psu, hill_correction):
return ec_psu_25c(x, hill_correction) - psu
[docs]
def ec_psu_25c(ts_ec, hill_correction=True):
"""Convert scalar or vector ec to psu assuming 25C temperature
and no low salinity correction
Note: The input argument, ts_ec, will be altered.
"""
try:
ts_ec.props["unit"] = "PSU"
ec = ts_ec.data
except:
ec = ts_ec
R = np.divide(ec, ec_sea)
R_is_neg = np.less(R, 0.0)
try:
R[R_is_neg] = 0.0001
except:
if R_is_neg:
return np.nan
else:
R_is_neg = None
sqrtR = np.sqrt(R)
Rsq = R * R
s = K1 + K2 * sqrtR + K3 * R + K4 * R * sqrtR + K5 * Rsq + K6 * Rsq * sqrtR
k = 0.0162
f = (25.0 - 15.0) / (1.0 + k * (25.0 - 15.0))
if hill_correction:
y = 100.0 * R
x = 400.0 * R
a_0 = 0.008
b_0_f = 0.0005 * f # = f(T=25)*0.0005
sqrty = np.sqrt(y)
s = s - a_0 / (1.0 + 1.5 * x + x * x) - b_0_f / (1.0 + sqrty + y + y * sqrty)
try:
ts_ec.props["unit"] = "PSU"
ts_ec.data = s
return ts_ec
except:
if R_is_neg is not None:
s[R_is_neg] = np.nan
return s
[docs]
def psu_ec_25c_scalar(psu, refine=True, hill_correction=True):
"""Conversion from psu to ec for a scalar. If used without refinement,
this is the Schemel conversion, otherwise uses root-finding
Note that the round trip ec-psu-ec tends to wander a lot without refine = True.
The refinement usually takes 4-6 iterations
"""
if psu < 0.0:
raise ValueError("Negative psu not allowed: {}".format(psu))
elif np.isnan(psu):
return np.nan
if hill_correction and not refine:
raise ValueError(
"Unrefined (refine=False) psu-to-ec correction cannot have hill_correction"
)
if refine:
if psu > 34.99969:
raise ValueError("psu is over sea salinity: %s" % psu)
ec = brentq(psu_ec_resid, 1.0, ec_sea, args=(psu, hill_correction))
else:
sqrtpsu = np.sqrt(psu)
ec = (psu / s_sea) * ec_sea + psu * (psu - s_sea) * (
J1 + J2 * sqrtpsu + J3 * psu + J4 * sqrtpsu * psu
)
return ec
psu_ec_25c_vec = np.vectorize(
psu_ec_25c_scalar, otypes="d", excluded=["refine", "hill_correction"]
)
[docs]
def psu_ec_25c(psu, refine=True, hill_correction=True):
"""Conversion from psu to ec. If used without refinement,
this is the Schemel conversion, otherwise uses root-finding
Note that the round trip ec-psu-ec tends to wander a lot without refine = True.
The refinement usually takes 4-6 iterations
"""
if type(psu) == float:
return psu_ec_25c_scalar(psu, refine, hill_correction)
else:
try:
out = psu.copy()
out.loc[:, :] = psu_ec_25c_vec(psu.to_numpy(), refine, hill_correction)
return out
except:
return psu_ec_25c_vec(psu, refine, hill_correction)