【问题标题】:JScience: Create a unit that is defined as a combination of multiple variablesJScience:创建一个定义为多个变量组合的单元
【发布时间】:2019-12-21 10:23:22
【问题描述】:

几个月前我发现了 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

1 cmH2O可以直接换算成1 cm。

假设我知道以 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 和/或基于 DerivedUnit 的类之一。

我的一部分想要放弃,只是走 util 方法的快速简单(和丑陋)的路线,这样我就可以继续做更重要的事情,另一部分我想找到一个让我爱上 JScience 的解决方案更多。

【问题讨论】:

  • 我对 JScience(及其应用程序)并不像我想的那样熟悉,但尝试了一下,现在我不确定生成的代码是否有意义以及它是否有意义是你要找的……你能描述一下(在代码层面上)这个单元的预期用途应该是什么样子吗?
  • 编辑了一些理想的使用示例。
  • 我还没有与 JScience 合作太多,对这里的某些“最佳实践”或规则了解不多。但是如果我对更新的理解正确,那么您想引入一个名为PressureHead 的新Quantity,并带有对应的Unit。向Amount 等添加新方法可能并不比实用方法好或差。但我想知道你想如何进行转换,因为看起来尺寸根本不匹配......
  • 现在,我正在使用一种实用方法将Pressure 转换为PressureHead,它完成了工作。我进行了一系列测试,以确保计算结果与可靠的来源相匹配,并且它们都通过了。我真的不需要转换另一个方向,所以摆脱了这样做的方法。一些数学运算需要PressureHeadLength 之间的转换,我通过在所有内容上使用doubleValue()、进行数学运算并使用valueOf() 重新创建Amount 来做到这一点。
  • 有一点是:pressureHead 单元的getDimension 应该是Length。但是在 Pascal(维度为 [M]/([L]·[T]²))和长度 [L] 之间转换是不可能的。我认为 JScience 应该/可以为此提供解决方案,但不确定。或者更直接地说:您打算将单位设为Unit&lt;Pressure&gt;Unit&lt;Length&gt; 还是Unit&lt;PressureHead&gt;? (从语法上讲,后者。但它实际上是一个压力单位,并且测量长度 - 这就是我无法绕开我的头:-/)

标签: java units-of-measurement fluid-dynamics jscience


【解决方案1】:

虽然我现在不再需要进行这种转换,但在将大部分项目(除了 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

【讨论】:

    猜你喜欢
    • 2011-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多