Python 实现物理单位转化 - physics.py

平时处理数据时有一个需求是要转化物理单位,比如标准压强单位是帕斯卡(Pa),但其实用的比较多的是 bar(相当于0.1 mPa),atm(标准大气压),psi(外国用的比较多,磅每平方英寸)。要想实现这些单位的标准化,最简单的方法就是编写些公式,比如我想让他们全都转换成标准大气压,根据下面公式可以开始写函数:

1 bar = 0.1mPa = 0.9869 atm

1
2
3
4
5
6
def to_atm(value, unit):
if unit == 'bar':
return value / 1.01325
elif unit == 'pa':
return value / 100000 / 1.01325
...

这样写临时用自然没有问题,但一来觉得有大量冗余代码(比如上述案例中 1.01325 是个标准大气压的常数,二来如果下次我想要用 kPa 或者 mPa 作为标准单位,则又要写一个函数,显然非常笨拙。

在网上找了一番后发现有个非常精简(仅一个 Python 文件)且好用的 Python 库 ipython_physics,从名字可以看出该库最初的目的是作为 IPython(或 Jupyter)的插件。

具体使用时这样就行:

1
2
3
4
5
6
from ipython_physics import Q
a = Q('1 bar')
print(a.to('atm'))
# 0.98692327 atm
print(a.to('atm').value)
# 0.98692327

该库的类主要借鉴了 PhysicalQuantities 中的 quantity.pyunit.py ,并往里面加了大量的物理单位,其中就包含了我需要使用到的压力相关的物理单位:

1
2
3
4
5
6
7
# Pressure units
_addUnit("bar", "1.e5*Pa", "bar (cgs unit)")
_addUnit("mbar", "1.e2*Pa", "millibar")
_addUnit("kbar", "1.e8*Pa", "kilobar")
_addUnit("atm", "101325.*Pa", "standard atmosphere")
_addUnit("torr", "atm/760", "torr = mm of mercury")
_addUnit("psi", "6894.75729317*Pa", "pounds per square inch")

咱们来看看它实现物理量转换的核心函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def _convertValue(value, src_unit, target_unit):
(factor, offset) = src_unit.conversion_tuple_to(target_unit)
return (value + offset) * factor

class PhysicalQuantity(object):
...
def to(self, *units):
units = [_findUnit(x) for x in units]
if len(units) == 1:
unit = units[0]
value = _convertValue(self.value, self.unit, unit)
return self.__class__(value, unit)
...

class PhysicalUnit(object):
...
def conversion_tuple_to(self, other):
if self.powers != other.powers:
raise UnitError("Incompatible units")
factor = self.factor / other.factor
offset = self.offset - (other.offset * other.factor / self.factor)
return (factor, offset)

假设是这个过程: Q(’1 pa’).to(’atm’)

首先就是得到系数, factor = (pa).factor / (atm).factor

pa 的系数就是标准物理量 1 Pa,而 atm 则是 101325.*Pa,因此算出 factor = 1/101325,就很容易得到结果。

最后是一个我在这个过程中踩的一个坑,有一个通俗的压力单位是 kg/cm2,甚至更简单点用“公斤”描述压强,但其实这个不是一个标准的压力单位,kg 是质量单位,而压力单位是牛顿,因此需要乘以一个重力才行,否则是不能识别的。

1
2
3
4
5
6
7

a = Q('10 kg/cm^2')
a.to('atm')
# UnitError: Incompatible units
g0=Q("9.80665 m/s**2")
(a * g0).to('atm')
# 9.6784111 atm

这样就完成了所有通俗压强单位到标准大气压的单位转换。