Skip to content
This repository has been archived by the owner on Jun 25, 2022. It is now read-only.

Commit

Permalink
Add RadialVelocity
Browse files Browse the repository at this point in the history
Signed-off-by: Carlos Quiroz <carlos.m.quiroz@gmail.com>
  • Loading branch information
cquiroz committed Aug 4, 2020
1 parent c25ace8 commit 692a41d
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 25 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) 2016-2020 Association of Universities for Research in Astronomy, Inc. (AURA)
// For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause

package gem

import cats.tests.CatsSuite
import cats.kernel.laws.discipline._

import coulomb._
import coulomb.si._
import coulomb.siprefix._
import gem.arb._

final class RadialVelocitySpec extends CatsSuite {
import ArbRadialVelocity._

// Laws
checkAll("RadialVelocity", EqTests[RadialVelocity].eqv)
checkAll("RadialVelocityOrder", OrderTests[RadialVelocity].order)

test("toRedshift") {
assert(
// Note the speed is given in Meter per second but coulomb will convert
RadialVelocity(0.withUnit[Meter %/ Second])
.flatMap(_.toRedshift)
.exists(_ === Redshift.Zero)
)
assert(RadialVelocity.CRadialVelocity.toRedshift.isEmpty)
assert(
RadialVelocity(1000.withUnit[(Kilo %* Meter) %/ Second])
.flatMap(_.toRedshift)
.exists(_ === Redshift(0.003341222805847144))
)
}
}
15 changes: 11 additions & 4 deletions modules/model-tests/shared/src/test/scala/gem/RedshiftSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import cats.kernel.laws.discipline._

import coulomb._
import coulomb.si._
import coulomb.siprefix._
import java.math.MathContext
import gem.arb._

final class RedshiftSpec extends CatsSuite {
Expand All @@ -17,11 +19,16 @@ final class RedshiftSpec extends CatsSuite {
checkAll("Redshift", EqTests[Redshift].eqv)
checkAll("RedshiftOrder", OrderTests[Redshift].order)

test("fromVelocity") {
assert(Redshift.Zero.some === Redshift.fromVelocity(0.withUnit[Meter %/ Second]))
assert(Redshift.fromVelocity(Redshift.C).isEmpty)
test("toRadialVelocity") {
assert(Redshift.Zero.toRadialVelocity === RadialVelocity(0.withUnit[Meter %/ Second]))
assert(
Redshift(3.335641007851109e-8).some === Redshift.fromVelocity(10.withUnit[Meter %/ Second])
// Example from http://spiff.rit.edu/classes/phys240/lectures/expand/expand.html
// We need to specify the Math context to properly compale
Redshift(BigDecimal.decimal(5.82, MathContext.DECIMAL32)).toRadialVelocity === RadialVelocity(
BigDecimal
.decimal(287172.912028, MathContext.DECIMAL32)
.withUnit[(Kilo %* Meter) %/ Second]
)
)
}
}
59 changes: 59 additions & 0 deletions modules/model/shared/src/main/scala/gem/RadialVelocity.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) 2016-2020 Association of Universities for Research in Astronomy, Inc. (AURA)
// For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause

package gem

import cats._
import coulomb._
import coulomb.si._
import coulomb.siprefix._
import spire.std.bigDecimal._

/**
* Representation of a radial velocity in kilomoters per second
* Valid range is ]-C, C[ where C is the speed of light
* This is often represented as RV
*/
final case class RadialVelocity private (rv: RadialVelocity.RVQuantity) {

/**
* Converts the radial velocity to a Redshift, approximate
* a return value of None should be understood as an infinity Redshift
*/
def toRedshift: Option[Redshift] =
// Though we forbid constructing an RV with value C there is at least one instance with that value
if (rv.value.abs < RadialVelocity.CValue) {
val i = (rv / RadialVelocity.C).value
val t = (1 + i) / (1 - i)
Some(Redshift(BigDecimal.decimal(scala.math.sqrt(t.toDouble) - 1).round(rv.value.mc)))
} else None
}

object RadialVelocity {
type RVUnit = (Kilo %* Meter) %/ Second
type RVQuantity = Quantity[BigDecimal, RVUnit]

val CValue: BigDecimal = BigDecimal.decimal(299792.458) // Use the default math context

val C: RVQuantity =
CValue.withUnit[RVUnit] // Speed of light in km/s

val CRadialVelocity: RadialVelocity = new RadialVelocity(C)

def apply(rv: RadialVelocity.RVQuantity): Option[RadialVelocity] =
if (rv.value.abs < CValue) Some(new RadialVelocity(rv)) else None

def unsafeFromRVQuantity(rv: RadialVelocity.RVQuantity): RadialVelocity =
apply(rv).getOrElse(sys.error(s"Value of rv $rv not allowed"))

/**
* `No RadialVelocity`
* @group Constructors
*/
val Zero: RadialVelocity = new RadialVelocity(0.withUnit[RVUnit])

/** @group Typeclass Instances */
implicit val order: Order[RadialVelocity] =
Order.by(_.rv.value)

}
42 changes: 23 additions & 19 deletions modules/model/shared/src/main/scala/gem/Redshift.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,41 @@
package gem

import cats._
import cats.implicits._
import coulomb._
import coulomb.si._
import coulomb.siprefix._
import spire.std.bigDecimal._

case class Redshift(z: BigDecimal)
/**
* Represents a redshift of an object if it move away or towards the observing point
* Redshift can be higher than 1 or negative
* Redshift can be converted to RadialVelocity which takes into account relativistic effects and cannot be more than C
* For nearer objects we can convert to ApparentRadialVelocity which doesn't consider relativistic effects
* Offten Redshift is referred as z
*/
final case class Redshift(z: BigDecimal) {

object Redshift {
type RadialVelocity = (Kilo %* Meter) %/ Second
type RadialVelocityQuantity = Quantity[BigDecimal, RadialVelocity]

val C: RadialVelocityQuantity =
299792.458.withUnit[RadialVelocity] // Speed of light in km/s
/**
* Converts to RadialVelocity, approximate
*/
def toRadialVelocity: Option[RadialVelocity] = {
val rv = RadialVelocity.CValue * (((z + 1) * (z + 1) - 1) / ((z + 1) * (z + 1) + 1))
RadialVelocity(rv.round(z.mc).withUnit[RadialVelocity.RVUnit])
}

// // We need a Functor[Quantity]
// def toApparentRadialVelocity: RadialVelocity =
// RadialVelocity((RadialVelocity.CValue * z).withUnit[RadialVelocity.RVUnit])
}

/** @group Typeclass Instances */
implicit val eqRedshift: Eq[Redshift] = Eq.by(_.z)
object Redshift {

/**
* The `No redshift`
* @group Constructors
*/
val Zero: Redshift = Redshift(0)
val Zero: Redshift = new Redshift(0)

/** @group Typeclass Instances */
implicit val order: Order[Redshift] =
Order.by(_.z)

def fromVelocity(v: RadialVelocityQuantity): Option[Redshift] =
if (v < C) {
val i = (v / C).value
val t = (1 + i) / (1 - i)
Some(Redshift(scala.math.sqrt(t.toDouble) - 1))
} else None
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) 2016-2020 Association of Universities for Research in Astronomy, Inc. (AURA)
// For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause

package gem
package arb

import coulomb._
import org.scalacheck.Arbitrary
import org.scalacheck.Gen
import org.scalacheck.Cogen

trait ArbRadialVelocity {

implicit val arbRedshift: Arbitrary[RadialVelocity] =
Arbitrary {
for {
rv <-
Gen.chooseNum(-1 * RadialVelocity.CValue.toDouble + 1, RadialVelocity.CValue.toDouble - 1)
} yield RadialVelocity.unsafeFromRVQuantity(rv.withUnit[RadialVelocity.RVUnit])
}

implicit val cogRedshift: Cogen[RadialVelocity] =
Cogen[BigDecimal].contramap(_.rv.value)
}

object ArbRadialVelocity extends ArbRadialVelocity
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ package arb
import gem.Redshift
import org.scalacheck.Arbitrary
import org.scalacheck.Cogen
import org.scalacheck.Arbitrary._
import org.scalacheck.Gen

trait ArbRedshift {

implicit val arbRedshift: Arbitrary[Redshift] =
Arbitrary {
for {
rs <- arbitrary[BigDecimal]
rs <- Gen.chooseNum(-10.0, 10.0)
} yield Redshift(rs)
}

Expand Down

0 comments on commit 692a41d

Please sign in to comment.