-
Notifications
You must be signed in to change notification settings - Fork 122
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
Rotational dynamics units #227
Changes from all commits
b9fe5f4
4468209
4f277d3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package squants.mass | ||
|
||
import squants.motion.{AngularAcceleration, NewtonMeters, Torque} | ||
import squants.space.{Feet, Meters} | ||
import squants.{AbstractQuantityNumeric, Dimension, Length, PrimaryUnit, Quantity, SiBaseUnit, UnitConverter, UnitOfMeasure} | ||
|
||
/** | ||
* | ||
* @author paxelord | ||
* @since 1.2 | ||
* | ||
* @param value Double | ||
*/ | ||
final class MomentOfInertia private (val value: Double, val unit: MomentOfInertiaUnit) | ||
extends Quantity[MomentOfInertia]{ | ||
|
||
def dimension = MomentOfInertia | ||
|
||
def toKilogramsMetersSquared = to(KilogramsMetersSquared) | ||
def toPoundsSquareFeet = to(PoundsSquareFeet) | ||
|
||
def *(angularAcceleration: AngularAcceleration): Torque = { | ||
val radiansPerSecondSquared = angularAcceleration.toRadiansPerSecondSquared | ||
|
||
NewtonMeters(toKilogramsMetersSquared * radiansPerSecondSquared) | ||
} | ||
|
||
/** | ||
* For a point mass with the given MomentOfInertia rotating with a center of | ||
* rotation at the given radius, return the mass of the point mass | ||
* @param radius distance to axis of rotation | ||
* @return mass of point mass with given radius and MomentOfInertia | ||
*/ | ||
def atCenter(radius: Length): Mass = { | ||
Kilograms(toKilogramsMetersSquared / radius.squared.toSquareMeters) | ||
} | ||
} | ||
|
||
object MomentOfInertia extends Dimension[MomentOfInertia] { | ||
private[mass] def apply[A](n: A, unit: MomentOfInertiaUnit)(implicit num: Numeric[A]) = new MomentOfInertia(num.toDouble(n), unit) | ||
def apply = parse _ | ||
def name = "MomentOfInertia" | ||
def primaryUnit = KilogramsMetersSquared | ||
def siUnit = KilogramsMetersSquared | ||
def units = Set(KilogramsMetersSquared, PoundsSquareFeet) | ||
} | ||
|
||
trait MomentOfInertiaUnit extends UnitOfMeasure[MomentOfInertia] with UnitConverter { | ||
def apply[A](n: A)(implicit num: Numeric[A]) = { | ||
MomentOfInertia(num.toDouble(n), this) | ||
} | ||
} | ||
|
||
object KilogramsMetersSquared extends MomentOfInertiaUnit with PrimaryUnit with SiBaseUnit { | ||
val symbol = Kilograms.symbol + "‧" + Meters.symbol + "²" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we using exponentials in unicode like this in other places? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In Acceleration and Area, unicode exponentials are used. |
||
} | ||
|
||
object PoundsSquareFeet extends MomentOfInertiaUnit { | ||
val symbol = Pounds.symbol + "‧" + Feet.symbol + "²" | ||
val conversionFactor = Pounds.conversionFactor * math.pow(Feet.conversionFactor, 2D) | ||
} | ||
|
||
object MomentOfInertiaConversions { | ||
lazy val kilogramMetersSquared = KilogramsMetersSquared(1) | ||
lazy val poundSquareFeet = PoundsSquareFeet(1) | ||
|
||
implicit class MomentOfInertiaConversions[A](val n: A) extends AnyVal { | ||
def kilogramMetersSquared(implicit num: Numeric[A]) = KilogramsMetersSquared(n) | ||
def poundSquareFeet(implicit num: Numeric[A]) = PoundsSquareFeet(n) | ||
} | ||
|
||
implicit object MomentOfInertiaNumeric extends AbstractQuantityNumeric[MomentOfInertia](MomentOfInertia.primaryUnit) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
package squants.motion | ||
|
||
import squants.mass.MomentOfInertia | ||
import squants.space._ | ||
import squants.time.{Seconds, Time, TimeDerivative} | ||
import squants.{AbstractQuantityNumeric, Dimension, Length, PrimaryUnit, Quantity, SiUnit, UnitConverter, UnitOfMeasure} | ||
|
||
/** | ||
* | ||
* @author paxelord | ||
* @since 1.2 | ||
* | ||
* @param value Double | ||
*/ | ||
final class AngularAcceleration private (val value: Double, val unit: AngularAccelerationUnit) | ||
extends Quantity[AngularAcceleration] with TimeDerivative[AngularVelocity] { | ||
|
||
def dimension = AngularAcceleration | ||
|
||
def toRadiansPerSecondSquared = to(RadiansPerSecondSquared) | ||
def toDegreesPerSecondSquared = to(DegreesPerSecondSquared) | ||
def toGradsPerSecondSquared = to(GradiansPerSecondSquared) | ||
def toTurnsPerSecondSquared = to(TurnsPerSecondSquared) | ||
def toArcminutesPerSecondSquared = to(ArcminutesPerSecondSquared) | ||
def toArcsecondsPerSecondSquared = to(ArcsecondsPerSecondSquared) | ||
|
||
/** | ||
* linear acceleration of an object rotating with this angular acceleration | ||
* and the given radius from the center of rotation | ||
* @param radius the distance from the center of rotation | ||
* @return linear acceleration with given angular acceleration and radius | ||
*/ | ||
def onRadius(radius: Length): Acceleration = toRadiansPerSecondSquared * radius / Seconds(1).squared | ||
|
||
|
||
def *(that: MomentOfInertia): Torque = { | ||
NewtonMeters(toRadiansPerSecondSquared * that.toKilogramsMetersSquared) | ||
} | ||
|
||
override protected[squants] def timeIntegrated: AngularVelocity = RadiansPerSecond(toRadiansPerSecondSquared) | ||
|
||
override protected[squants] def time: Time = Seconds(1) | ||
} | ||
|
||
object AngularAcceleration extends Dimension[AngularAcceleration] { | ||
private[motion] def apply[A](n: A, unit: AngularAccelerationUnit)(implicit num: Numeric[A]) = new AngularAcceleration(num.toDouble(n), unit) | ||
def apply = parse _ | ||
def name = "AngularAcceleration" | ||
def primaryUnit = RadiansPerSecondSquared | ||
def siUnit = RadiansPerSecondSquared | ||
def units = Set( | ||
RadiansPerSecondSquared, | ||
DegreesPerSecondSquared, | ||
GradiansPerSecondSquared, | ||
TurnsPerSecondSquared, | ||
ArcminutesPerSecondSquared, | ||
ArcsecondsPerSecondSquared) | ||
} | ||
|
||
trait AngularAccelerationUnit extends UnitOfMeasure[AngularAcceleration] with | ||
UnitConverter { | ||
def apply[A](n: A)(implicit num: Numeric[A]) = { | ||
AngularAcceleration(num.toDouble(n), this) | ||
} | ||
|
||
val conversionFactor: Double | ||
} | ||
|
||
object RadiansPerSecondSquared extends AngularAccelerationUnit with PrimaryUnit with SiUnit{ | ||
val symbol = Radians.symbol + "/s²" | ||
} | ||
|
||
object DegreesPerSecondSquared extends AngularAccelerationUnit { | ||
val symbol = Degrees.symbol + "/s²" | ||
val conversionFactor = Degrees.conversionFactor | ||
} | ||
|
||
object GradiansPerSecondSquared extends AngularAccelerationUnit { | ||
val symbol = Gradians.symbol + "/s²" | ||
val conversionFactor = Gradians.conversionFactor | ||
} | ||
|
||
object TurnsPerSecondSquared extends AngularAccelerationUnit { | ||
val symbol = Turns.symbol + "/s²" | ||
val conversionFactor = Turns.conversionFactor | ||
} | ||
|
||
object ArcminutesPerSecondSquared extends AngularAccelerationUnit { | ||
val symbol = Arcminutes.symbol + "/s²" | ||
val conversionFactor = Arcminutes.conversionFactor | ||
} | ||
|
||
object ArcsecondsPerSecondSquared extends AngularAccelerationUnit{ | ||
val symbol = Arcseconds.symbol + "/s²" | ||
val conversionFactor = Arcseconds.conversionFactor | ||
} | ||
|
||
object AngularAccelerationConversions { | ||
lazy val radianPerSecondSquared = RadiansPerSecondSquared(1) | ||
lazy val degreePerSecondSquared = DegreesPerSecondSquared(1) | ||
lazy val gradPerSecondSquared = GradiansPerSecondSquared(1) | ||
lazy val turnPerSecondSquared = TurnsPerSecondSquared(1) | ||
|
||
implicit class AngularAccelerationConversions[A](val n: A) extends AnyVal { | ||
def radiansPerSecondSquared(implicit num: Numeric[A]) = RadiansPerSecondSquared(n) | ||
def degreesPerSecondSquared(implicit num: Numeric[A]) = DegreesPerSecondSquared(n) | ||
def gradsPerSecondSquared(implicit num: Numeric[A]) = GradiansPerSecondSquared(n) | ||
def turnsPerSecondSquared(implicit num: Numeric[A]) = TurnsPerSecondSquared(n) | ||
} | ||
|
||
implicit object AngularAccelerationNumeric extends AbstractQuantityNumeric[AngularAcceleration](AngularAcceleration.primaryUnit) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package squants.motion | ||
|
||
import squants.mass.{Kilograms, MomentOfInertia, Pounds} | ||
import squants.space.{Feet, Meters} | ||
import squants.{AbstractQuantityNumeric, Dimension, Energy, PrimaryUnit, Quantity, SiBaseUnit, UnitConverter, UnitOfMeasure} | ||
|
||
/** | ||
* | ||
* @author paxelord | ||
* @since 1.2 | ||
* | ||
* @param value Double | ||
*/ | ||
final class Torque private (val value: Double, val unit: TorqueUnit) | ||
extends Quantity[Torque] { | ||
|
||
def dimension = Torque | ||
|
||
def toNewtonMeters = to(NewtonMeters) | ||
def toPoundFeet = to(PoundFeet) | ||
|
||
def / (that: MomentOfInertia): AngularAcceleration = { | ||
RadiansPerSecondSquared(toNewtonMeters / that.toKilogramsMetersSquared) | ||
} | ||
} | ||
|
||
object Torque extends Dimension[Torque] { | ||
private[motion] def apply[A](n: A, unit: TorqueUnit)(implicit num: Numeric[A]) = new Torque(num.toDouble(n), unit) | ||
def apply = parse _ | ||
def name = "Torque" | ||
def primaryUnit = NewtonMeters | ||
def siUnit = NewtonMeters | ||
def units = Set(NewtonMeters, PoundFeet) | ||
} | ||
|
||
trait TorqueUnit extends UnitOfMeasure[Torque] with UnitConverter { | ||
def apply[A](n: A)(implicit num: Numeric[A]) = { | ||
Torque(num.toDouble(n), this) | ||
} | ||
} | ||
|
||
object NewtonMeters extends TorqueUnit with PrimaryUnit with SiBaseUnit { | ||
val symbol = Newtons.symbol + "‧" + Meters.symbol | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is meant to be the times symbol. I looked though squants, and I think this is the first time we have a times symbol in a unit. It might be clearer to replace the current bullet In a future PR, it might be useful to have have a |
||
} | ||
|
||
object PoundFeet extends TorqueUnit { | ||
val symbol = Pounds.symbol + "‧" + Feet.symbol | ||
val conversionFactor = PoundForce.conversionFactor * Feet.conversionFactor | ||
} | ||
|
||
object TorqueConversions { | ||
lazy val newtonMeters = NewtonMeters(1) | ||
lazy val poundFeet = PoundFeet(1) | ||
|
||
implicit class TorqueConversions[A](val n: A) extends AnyVal { | ||
def newtonMeters(implicit num: Numeric[A]) = NewtonMeters(n) | ||
def poundFeet(implicit num: Numeric[A]) = PoundFeet(n) | ||
} | ||
|
||
implicit object TorqueNumeric extends AbstractQuantityNumeric[Torque](Torque.primaryUnit) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could the constructors here return an
Option
to account for negative values?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not 100% sure that's a good idea. People might be confused why they are getting a
None.get
Exception, whereas as throwing an IlleagalArgumentException would be more descriptive.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Throwing exceptions isn't very functional. If
Option
isn't suitable, we can look atEither
which has a place to place an error data structure.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will try experimenting with
Option
andEither
, and try to figure out what is most naturalThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On a side note, I am starting to second guess whether this class should even exist. Maybe Squants should only provide compile time safety of units, and not tell people how to represent quantities, even if they are doing unusual things like having negative
Mass
orMomentOfInertia
.This might hinder people trying to explain legitimate scientific phenomena: https://en.wikipedia.org/wiki/Negative_mass
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One compromise would be to have the strictly positive feature optional, enabled by default. Someone who needs to use negative mass would have to explicitly disable the strictly positive feature. I'm not completely sure how that would work though.