Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add dimension and units of measure for VolumeAcceleration (#288) #289

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions shared/src/main/scala/squants/motion/VolumeAcceleration.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/* *\
** Squants **
** **
** Scala Quantities and Units of Measure Library and DSL **
** (c) 2017, Transcriptic, Inc. **
garyKeorkunian marked this conversation as resolved.
Show resolved Hide resolved
** **
\* */

package squants.motion

import squants.space._
import squants.{ AbstractQuantityNumeric, Dimension, PrimaryUnit, Quantity, SiUnit, UnitConverter, UnitOfMeasure }
import squants.time.{ SecondTimeDerivative, Seconds, TimeDerivative, TimeSquared }

/**
* Represents the time rate of change of VolumeFlow i.e. volume per time per time.
*
* @author Phil Sung
* @since 1.4
*
* @param value Double
*/
final class VolumeAcceleration private (val value: Double, val unit: VolumeAccelerationUnit)
extends Quantity[VolumeAcceleration]
with TimeDerivative[VolumeFlow]
with SecondTimeDerivative[Volume] {

def dimension = VolumeAcceleration

protected[squants] def timeIntegrated = CubicMetersPerSecond(toCubicMetersPerSecondSquared)
protected[squants] def time = Seconds(1)

def *(that: TimeSquared): Volume = this * that.time1 * that.time2

def toCubicMetersPerSecondSquared = to(CubicMetersPerSecondSquared)
def toLitresPerSecondSquared = to(LitresPerSecondSquared)
def toMillilitresPerSecondSquared = to(MillilitresPerSecondSquared)
def toMicrolitresPerSecondSquared = to(MicrolitresPerSecondSquared)
}

object VolumeAcceleration extends Dimension[VolumeAcceleration] {
private[motion] def apply[A](n: A, unit: VolumeAccelerationUnit)(implicit num: Numeric[A]) = {
new VolumeAcceleration(num.toDouble(n), unit)
}
def apply = parse _
def name = "VolumeAcceleration"
def primaryUnit = CubicMetersPerSecondSquared
def siUnit = CubicMetersPerSecondSquared
def units = Set(
CubicMetersPerSecondSquared,
LitresPerSecondSquared,
MillilitresPerSecondSquared,
MicrolitresPerSecondSquared
)
}

/**
* Base trait for units of [[squants.motion.VolumeAcceleration]]
*
* @author Phil Sung
* @since 1.4
*/
trait VolumeAccelerationUnit extends UnitOfMeasure[VolumeAcceleration] with UnitConverter {
def apply[A](n: A)(implicit num: Numeric[A]) = VolumeAcceleration(n, this)
}

object CubicMetersPerSecondSquared extends VolumeAccelerationUnit with PrimaryUnit with SiUnit {
val symbol = "m³/s²"
}

object LitresPerSecondSquared extends VolumeAccelerationUnit {
val symbol = "L/s²"
val conversionFactor = Litres.conversionFactor / CubicMeters.conversionFactor
}

object MillilitresPerSecondSquared extends VolumeAccelerationUnit {
val symbol = "mL/s²"
val conversionFactor = Millilitres.conversionFactor / CubicMeters.conversionFactor
}

object MicrolitresPerSecondSquared extends VolumeAccelerationUnit {
val symbol = "µL/s²"
val conversionFactor = Microlitres.conversionFactor / CubicMeters.conversionFactor
}

object VolumeAccelerationConversions {
lazy val cubicMetersPerSecondSquared = CubicMetersPerSecondSquared(1)
lazy val litresPerSecondSquared = LitresPerSecondSquared(1)
lazy val millilitresPerSecondSquared = MillilitresPerSecondSquared(1)
lazy val microlitresPerSecondSquared = MicrolitresPerSecondSquared(1)

implicit class VolumeFlowConversions[A](n: A)(implicit num: Numeric[A]) {
def cubicMetersPerSecondSquared = CubicMetersPerSecondSquared(n)
def litresPerSecondSquared = LitresPerSecondSquared(n)
def millilitresPerSecondSquared = MillilitresPerSecondSquared(n)
def microlitresPerSecondSquared = MicrolitresPerSecondSquared(n)
}

implicit object VolumeAccelerationNumeric extends AbstractQuantityNumeric[VolumeAcceleration](
VolumeAcceleration.primaryUnit
)
}
2 changes: 2 additions & 0 deletions shared/src/main/scala/squants/motion/VolumeFlow.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ import squants.time._
*/
final class VolumeFlow private (val value: Double, val unit: VolumeFlowRateUnit)
extends Quantity[VolumeFlow]
with TimeIntegral[VolumeAcceleration]
with TimeDerivative[Volume] {

def dimension = VolumeFlow

protected[squants] def timeDerived = CubicMetersPerSecondSquared(toCubicMetersPerSecond)
protected[squants] def timeIntegrated = CubicMeters(toCubicMetersPerSecond)
protected[squants] def time = Seconds(1)

Expand Down
10 changes: 7 additions & 3 deletions shared/src/main/scala/squants/space/Volume.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ package squants.space
import squants._
import squants.energy.{ EnergyDensity, Joules }
import squants.mass.{ ChemicalAmount, Kilograms }
import squants.motion.{ CubicMetersPerSecond, VolumeFlow }
import squants.time.TimeIntegral
import squants.motion.{ CubicMetersPerSecond, VolumeAcceleration, VolumeFlow }
import squants.time.{ SecondTimeIntegral, TimeIntegral, TimeSquared }

/**
* Represents a quantity of Volume (three-dimensional space)
Expand All @@ -24,7 +24,8 @@ import squants.time.TimeIntegral
*/
final class Volume private (val value: Double, val unit: VolumeUnit)
extends Quantity[Volume]
with TimeIntegral[VolumeFlow] {
with TimeIntegral[VolumeFlow]
with SecondTimeIntegral[VolumeAcceleration] {

def dimension = Volume

Expand All @@ -50,6 +51,9 @@ final class Volume private (val value: Double, val unit: VolumeUnit)
case _ ⇒ SquareMeters(this.toCubicMeters / that.toMeters)
}

def /(that: TimeSquared): VolumeAcceleration = this / that.time1 / that.time2
def /(that: VolumeAcceleration): TimeSquared = (this / that.timeIntegrated) * time

def /(that: Mass) = ??? // returns SpecificVolume (inverse of Density)
def /(that: ChemicalAmount) = ??? // return MolarVolume

Expand Down
85 changes: 85 additions & 0 deletions shared/src/test/scala/squants/motion/VolumeAccelerationSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/* *\
** Squants **
** **
** Scala Quantities and Units of Measure Library and DSL **
** (c) 2017, Transcriptic, Inc. **
** **
\* */

package squants.motion

import org.scalatest.{ FlatSpec, Matchers }
import squants.space.CubicMeters
import squants.time.Seconds
import squants.{ CustomMatchers, QuantityParseException }

class VolumeAccelerationSpec extends FlatSpec with Matchers with CustomMatchers {
behavior of "VolumeAcceleration and its Units of Measure"

it should "create values using UOM factories" in {
CubicMetersPerSecondSquared(1).toCubicMetersPerSecondSquared should be(1)
LitresPerSecondSquared(1).toLitresPerSecondSquared should be(1)
MillilitresPerSecondSquared(1).toMillilitresPerSecondSquared should be(1)
MicrolitresPerSecondSquared(1).toMicrolitresPerSecondSquared should be(1)
}

it should "create values from properly formatted Strings" in {
VolumeAcceleration("12.34 m³/s²").get should be(CubicMetersPerSecondSquared(12.34))
VolumeAcceleration("12.34 L/s²").get should be(LitresPerSecondSquared(12.34))
VolumeAcceleration("12.34 mL/s²").get should be(MillilitresPerSecondSquared(12.34))
VolumeAcceleration("12.34 µL/s²").get should be(MicrolitresPerSecondSquared(12.34))
VolumeAcceleration("12.34 zz").failed.get should be(QuantityParseException("Unable to parse VolumeAcceleration", "12.34 zz"))
VolumeAcceleration("zz m³/s²").failed.get should be(QuantityParseException("Unable to parse VolumeAcceleration", "zz m³/s²"))
}

it should "properly convert to all supported units of Measure" in {
val x = CubicMetersPerSecondSquared(12.34)
x.toCubicMetersPerSecondSquared should be(12.34)
x.toLitresPerSecondSquared should be(12340.0 +- 0.00000001)
x.toMillilitresPerSecondSquared should be(12340000.0 +- 0.00000001)
x.toMicrolitresPerSecondSquared should be(12340000000.0 +- 0.00000001)
}

it should "return properly formatted strings for all supported Units of Measure" in {
CubicMetersPerSecondSquared(1).toString(CubicMetersPerSecondSquared) should be("1.0 m³/s²")
LitresPerSecondSquared(1).toString(LitresPerSecondSquared) should be("1.0 L/s²")
MillilitresPerSecondSquared(1).toString(MillilitresPerSecondSquared) should be("1.0 mL/s²")
MicrolitresPerSecondSquared(1).toString(MicrolitresPerSecondSquared) should be("1.0 µL/s²")
}

it should "return VolumeFlow when multiplied by Time" in {
CubicMetersPerSecondSquared(2) * Seconds(3) should be(CubicMetersPerSecond(6))
}

it should "return Volume when multiplied by TimeSquared" in {
CubicMetersPerSecondSquared(2) * Seconds(2).squared should be(CubicMeters(8))
}

behavior of "VolumeAccelerationConversions"

it should "provide aliases for single unit values" in {
import VolumeAccelerationConversions._

cubicMetersPerSecondSquared should be(CubicMetersPerSecondSquared(1))
litresPerSecondSquared should be(LitresPerSecondSquared(1))
millilitresPerSecondSquared should be(MillilitresPerSecondSquared(1))
microlitresPerSecondSquared should be(MicrolitresPerSecondSquared(1))
}

it should "provide implicit conversion from Double" in {
import VolumeAccelerationConversions._

val d = 12.34d
d.cubicMetersPerSecondSquared should be(CubicMetersPerSecondSquared(d))
d.litresPerSecondSquared should be(LitresPerSecondSquared(d))
d.millilitresPerSecondSquared should be(MillilitresPerSecondSquared(d))
d.microlitresPerSecondSquared should be(MicrolitresPerSecondSquared(d))
}

it should "provide Numeric support" in {
import VolumeAccelerationConversions.VolumeAccelerationNumeric
implicit val tolerance = CubicMetersPerSecondSquared(0.00000001)
val vaList = List(CubicMetersPerSecondSquared(1), LitresPerSecondSquared(2))
vaList.sum should beApproximately(LitresPerSecondSquared(1002))
}
}
8 changes: 8 additions & 0 deletions shared/src/test/scala/squants/motion/VolumeFlowSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@ class VolumeFlowSpec extends AnyFlatSpec with Matchers with CustomMatchers {
CubicMetersPerSecond(1) * Seconds(1) should be(CubicMeters(1))
}

it should "return VolumeAcceleration when divided by Time" in {
CubicMetersPerSecond(8) / Seconds(2) should be(CubicMetersPerSecondSquared(4))
}

it should "return Time when divided by VolumeAcceleration" in {
CubicMetersPerSecond(8) / CubicMetersPerSecondSquared(2) should be(Seconds(4))
}

behavior of "VolumeFlowConversions"

it should "provide aliases for single unit values" in {
Expand Down
10 changes: 9 additions & 1 deletion shared/src/test/scala/squants/space/VolumeSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ package squants.space
import squants.QuantityParseException
import squants.energy.{ Joules, JoulesPerCubicMeter }
import squants.mass.{ Kilograms, KilogramsPerCubicMeter }
import squants.motion.CubicMetersPerSecond
import squants.motion.{ CubicMetersPerSecond, CubicMetersPerSecondSquared }
import squants.time.Seconds
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
Expand Down Expand Up @@ -162,10 +162,18 @@ class VolumeSpec extends AnyFlatSpec with Matchers {
CubicMeters(1) / Seconds(1) should be(CubicMetersPerSecond(1))
}

it should "return VolumeAcceleration when divided by TimeSquared" in {
CubicMeters(8) / Seconds(2).squared should be(CubicMetersPerSecondSquared(2))
}

it should "return Time when divided by VolumeFlowRate" in {
CubicMeters(1) / CubicMetersPerSecond(1) should be(Seconds(1))
}

it should "return TimeSquared when divided by VolumeAcceleration" in {
CubicMeters(8) / CubicMetersPerSecondSquared(4) should be(Seconds(2) * Seconds(1))
}

it should "return Length when cube rooted" in {
CubicMeters(27).cubeRoot should be(Meters(3))
}
Expand Down