Astropy Units equivalencies - 使用类和 SI 前缀的干涉测量基线
Posted
技术标签:
【中文标题】Astropy Units equivalencies - 使用类和 SI 前缀的干涉测量基线【英文标题】:Astropy Units equivalencies - interferometry baselines using classes and SI prefixes 【发布时间】:2021-09-10 18:01:57 【问题描述】:作为Astropy Units equivalencies - interferometry baselines的追随者。我想问一下如何为我的自定义单位使用 SI 前缀。到目前为止,按照附加链接的建议,我从 astropy Quantity 创建了一个子类,然后我重写了 .to 方法。
这是我的代码:
class uWavelength(un.Quantity):
def __new__(cls, value, freq=None, dtype=None, copy=True, **kwargs):
unit = un.Unit(un.def_unit('lambdas', format='format': r'\lambda', prefixes=True))
self = super().__new__(cls, value=value, unit=unit, dtype=dtype, copy=copy, **kwargs)
self.freq = freq
if self.freq is not None:
self.equivalencies = self.lambdas_equivalencies()
return self
@property
def freq(self):
return self._freq
@freq.setter
def freq(self, val):
if val is not None:
self._equivalencies = self.lambdas_equivalencies(restfreq=val)
self._freq = val
@property
def equivalencies(self):
return self._equivalencies
@equivalencies.setter
def equivalencies(self, val):
self._equivalencies = val
def lambdas_equivalencies(self, restfreq=None):
if self.freq is not None:
restfreq_hz = self.freq.to(un.Hz, equivalencies=un.spectral())
elif restfreq is not None:
restfreq_hz = restfreq.to(un.Hz, equivalencies=un.spectral())
else:
sys.exit("Frequency not provided")
eq = [
(self.unit, un.s, lambda x: x / restfreq_hz, lambda x: x * restfreq_hz),
(self.unit, un.m, lambda x: x / restfreq_hz * co.c.to(un.m / un.s).value,
lambda x: x / co.c.to(un.m / un.s).value * restfreq_hz),
(un.m, un.s, lambda x: x / co.c.to(un.m / un.s).value, lambda x: x * co.c.to(un.m / un.s).value),
]
return eq
def to(self, unit, restfreq=None, copy=True):
equiv = []
if restfreq is None:
equiv = self.equivalencies
else:
equiv = self.lambdas_equivalencies(restfreq=restfreq)
unit = un.Unit(unit)
if copy:
# Avoid using to_value to ensure that we make a copy. We also
# don't want to slow down this method (esp. the scalar case).
value = self._to_value(unit, equiv)
else:
# to_value only copies if necessary
value = self.to_value(unit, equiv)
return self._new_view(value, unit)class uWavelength(un.Quantity):
def __new__(cls, value, freq=None, dtype=None, copy=True, **kwargs):
unit = un.Unit(un.def_unit('lambdas', format='format': r'\lambda', prefixes=True))
self = super().__new__(cls, value=value, unit=unit, dtype=dtype, copy=copy, **kwargs)
self.freq = freq
if self.freq is not None:
self.equivalencies = self.lambdas_equivalencies()
return self
@property
def freq(self):
return self._freq
@freq.setter
def freq(self, val):
if val is not None:
self._equivalencies = self.lambdas_equivalencies(restfreq=val)
self._freq = val
@property
def equivalencies(self):
return self._equivalencies
@equivalencies.setter
def equivalencies(self, val):
self._equivalencies = val
def lambdas_equivalencies(self, restfreq=None):
if self.freq is not None:
restfreq_hz = self.freq.to(un.Hz, equivalencies=un.spectral())
elif restfreq is not None:
restfreq_hz = restfreq.to(un.Hz, equivalencies=un.spectral())
else:
sys.exit("Frequency not provided")
eq = [
(self.unit, un.s, lambda x: x / restfreq_hz, lambda x: x * restfreq_hz),
(self.unit, un.m, lambda x: x / restfreq_hz * co.c.to(un.m / un.s).value,
lambda x: x / co.c.to(un.m / un.s).value * restfreq_hz),
(un.m, un.s, lambda x: x / co.c.to(un.m / un.s).value, lambda x: x * co.c.to(un.m / un.s).value),
]
return eq
def to(self, unit, restfreq=None, copy=True):
equiv = []
if restfreq is None:
equiv = self.equivalencies
else:
equiv = self.lambdas_equivalencies(restfreq=restfreq)
unit = un.Unit(unit)
if copy:
# Avoid using to_value to ensure that we make a copy. We also
# don't want to slow down this method (esp. the scalar case).
value = self._to_value(unit, equiv)
else:
# to_value only copies if necessary
value = self.to_value(unit, equiv)
return self._new_view(value, unit)
但是,当我使用类时,我只能使用单位 lambda,但我想使用 klambda 或 mega-lambda 等。根据 astropy 这可以通过使用参数prefixes=True
来完成,但这并不似乎有效。
【问题讨论】:
【参考方案1】:我认为您实际上不应该定义类的 __new__
中的单元,因为它不允许您在实例化 uWavelength
时实际设置单元。
而是把它放在你的课堂之外:
lambdas = u.def_unit('lambdas', format='format': r'\lambda')
我认为文档并不清楚的是,当您使用 prefixes=True
时,它并没有真正做任何事情,除非您还提供 namespace=
参数,即使那样,文档也没有说得非常清楚如何使用命名空间。我认为最好采用 astrofrog 的建议,即明确声明所需的前缀单位,例如:
klambdas = u.def_unit('kilolambdas', represents=u.CompositeUnit(1e3, [lambdas], [1]), format='format' : r'k\lambda')```
unless you *really* need every imaginable SI prefix, you could try:
lambdas = u.def_unit('lambdas', format='format': r'\lambda', prefixes=True, namespace=globals())
and it will inject every prefixed unit into your module namespace.
Then, since you want your default unit for `uWavelength` to be `lambdas`, then both to reduce confusion, and also add some documentation of this fact (through the signature of `__new__`) specify:
```python
class uWavelength(un.Quantity):
def __new__(cls, value, freq=None, unit=lambdas, dtype=None, copy=True, **kwargs):
此外,如果您愿意,可以添加如下检查:
unit = u.Unit(unit)
assert unit.is_equivalent(lambdas), 'unit must be equivalent to lambdas'
【讨论】:
感谢您的回复。我还看到有些人将命名空间设置为:u.__dict__ - 我认为在这种情况下它与 globals() 相同?另一个问题是,这将允许我覆盖方法 .to -> 这是我的目标,这样我就可以像 (10*u.lambda).to(um, restfreq=203.0 * u.GHz) 那样做。也许我应该继承 Unit 而不是 Quantity? 不,globals()
是您运行 def_unit
的模块中的全局变量。你也可以u.__dict__
.
事实上,如果这是你唯一的目标,我认为你不需要首先搞砸Quantity
的子类化,除非你认为有额外的特殊方法是有价值的你的用例。我看到你已经搞砸了等价,所以像(10.u.lambda).to(u.m, equivalencies=lambda_equivalencies(restfreq=203.0 * u.GHz)) where
lambda_equivalencies`这样的函数就足够了,它返回给定restfreq
的等价列表。以上是关于Astropy Units equivalencies - 使用类和 SI 前缀的干涉测量基线的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Astropy 中将 AltAz 坐标转换为赤道坐标