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:将 FITS 表拆分为训练和测试集

在 Astropy 中将 cgs 转换为“自定义”单位

如何在 Astropy 中将 AltAz 坐标转换为赤道坐标

如何将 astropy 表的标题和单位保存到 ascii 文件中

具有 .every() 等效项的 Mongoose 聚合

Base包equivalent