JScience:创建一个定义为多个变量组合的单元

JScience: Create a unit that is defined as a combination of multiple variables

几个月前我发现了 JScience,它对我的​​项目提供了巨大的帮助,尽管我正在努力解决一件事。

我正在尝试创建一个 PressureHead(又名水柱)单位,它可以直接用 Length 转换并间接用 Pressure 给定 VolumetricDensity 值.

求压力:Density × Gravity × Head = Pressure

下面是 Wikipedia:

的转换示例

1 cmH2O (4°C) = 999.9720 kg/m3 × 9.80665 m/s2 × 1 cm = 98.063754138 Pa

1cmH2O可以直接换算成1cm

假设我知道以 Pa 为单位的压力并想找到以 mH2O 为单位的压头,这是我在项目中最常进行的转换。我还需要知道流体的密度。压力和密度是可变输入。公式也必须知道重力,但为了我的目的,它可以固定为标准重力。

求压头:Pressure / (Density × Gravity) = Head

为了简单起见,我只是重新调整了上面示例中的值,将压力乘以 100 得到 1 mH2O 而不是 1 cmH2O.

9806.3754138 Pa / (999.9720 kg/m3 × 9.80665 m/s2) = 1 mH2O

看起来 JScience 可能足够灵活以允许这样的单元,但我还没有看到任何示例来帮助我创建它。最坏的情况下,我可能只会满足于使用 util 方法在它们之间进行转换。

编辑

我希望看到的一些理想用法示例:

// Pressure to PressureHead
Amount<Pressure> pressure = Amount.valueOf(9806.3754138, PASCAL);
Amount<VolumetricDensity> density = Amount.valueOf(999.9720, KG_M3);
Amount<PressureHead> head = pressure.to(M_H2O, density);
System.out.println(pressure.doubleValue(M_H2O, density)); // 1.0

// PressureHead <-> Length
Amount<Length> length = head.to(METER);
System.out.println(head.doubleValue(METER)); // 1.0
head = length.to(M_H2O);
System.out.println(length.doubleValue(M_H2O)); // 1.0

// PressureHead to Pressure
pressure = head.to(PASCAL, density);
System.out.println(head.doubleValue(PASCAL, density)); // 9806.3754138

PressureHead 单位之间的转换很容易。我可以像这样定义额外的单位:

Unit<PressureHead> FT_H2O = M_H2O.transform(FOOT.getConverterTo(METER));

为了上面的理想用法,我需要继承 Amount 并重载 to()doubleValue()。我怀疑是否有更合适的转换方法(尽管不是很好的用法),它涉及子类化 UnitConverter and/or 基于 DerivedUnit 的 类 之一。

一方面我想放弃,只是走快速简单(而且丑陋)的 util 方法路线,这样我就可以继续做更重要的事情,另一方面我想找到一个解决方案让我更喜欢JScience

虽然我现在不再需要进行这种转换,但在将大部分项目(UI 代码除外)转换为 Kotlin 并找到喜欢 Kotlin 的理由后,我正在重新审视这个问题.我做出转换是因为我认为这是在项目仍处于早期阶段时学习该语言的好机会。因此,这并不能完全回答我过去想要 Java 答案的自己,但我现在的自己很乐意接受这个答案。

此解决方案利用了 Kotlin 的 extension functions

为防止这两个方向冲突,它们必须使用不同的函数名称或在单独的对象或包中定义。

object PressureToHead {
    fun Amount<Pressure>.to(unit: Unit<PressureHead>,
            fluidDensity: Amount<VolumetricDensity>): Amount<PressureHead> {
        return Amount.valueOf(doubleValue(PASCAL)
                / (fluidDensity.doubleValue(KG_M3)
                * SensorManager.GRAVITY_EARTH), M_H2O).to(unit)
    }

    fun Amount<Pressure>.doubleValue(unit: Unit<PressureHead>,
            fluidDensity: Amount<VolumetricDensity>): Double {
        return to(unit, fluidDensity).estimatedValue
    }
}

object HeadToPressure {
    fun Amount<PressureHead>.to(unit: Unit<Pressure>,
            fluidDensity: Amount<VolumetricDensity>): Amount<Pressure> {
        return Amount.valueOf(fluidDensity.doubleValue(KG_M3)
                * SensorManager.GRAVITY_EARTH
                * doubleValue(M_H2O), PASCAL).to(unit)
    }

    fun Amount<PressureHead>.doubleValue(unit: Unit<Pressure>,
            fluidDensity: Amount<VolumetricDensity>): Double {
        return to(unit, fluidDensity).estimatedValue
    }
}

为了在 PressureHeadLength 之间进行转换,我不能使用相同的函数名称,因为它们掩盖了现有的函数名称。最好的解决方案似乎是只给函数一个不同的名称,我可以接受。

object HeadToLength {
    fun Amount<PressureHead>.toLength(unit: Unit<Length>): Amount<Length> {
        return Amount.valueOf(doubleValue(M_H2O), METER).to(unit)
    }

    fun Amount<PressureHead>.doubleValueLength(unit: Unit<Length>): Double {
        return toLength(unit).estimatedValue
    }
}

object LengthToHead {
    fun Amount<Length>.toHead(unit: Unit<PressureHead>): Amount<PressureHead> {
        return Amount.valueOf(doubleValue(METER), M_H2O).to(unit)
    }

    fun Amount<Length>.doubleValueHead(unit: Unit<PressureHead>): Double {
        return toHead(unit).estimatedValue
    }
}

除了与 Length 转换的细微差别外,用法完全符合我的要求。这次是用 Kotlin 写的。

// Pressure to PressureHead
val density = Amount.valueOf(999.9720, KG_M3)
var pressure = Amount.valueOf(9806.3754138, PASCAL)
var head = pressure.to(M_H2O, density)
println(pressure.doubleValue(M_H2O, density)) // 1.0

// PressureHead to Pressure
pressure = head.to(PASCAL, density)
println(head.doubleValue(PASCAL, density)) // 9806.3754138

// PressureHead <-> Length
Amount<Length> length = head.toLength(METER)
println(head.doubleValueLength(METER)) // 1.0
head = length.toHead(M_H2O)
println(length.doubleValueHead(M_H2O)) // 1.0