7
$\begingroup$

I am trying to convert arbitrary units to CGS. I know this can get a bit tricky with ESU, but for this question that does not need to be addressed.

What I would like to do is to give a quantity-to-be-converted and a list of quantities to convert the quantity. I want to convert one quantity into a linear combination of other quantities. This would make the CGS conversion easy, so I could do.

UnitConvert[Quantity[1,"Watts"]/Quantity[2,"meters"], {cm, g, K, s, etc.}]
½ g cm/s^3

tl;dr How do I express one quantity as a linear combination of other quantities?

*This is not what UnitConvert actually does, it will Thread over that last.

Relevant sources:

https://mathematica.stackexchange.com/a/101483/45153

Implementing CGS unit system in Mathematica 9

$\endgroup$
2
  • 1
    $\begingroup$ What do you want done with the other unit dimensions (the ones not specified in your second list?) Do you want to keep them in their existing units, or convert to a default unit system, presumably SI? $\endgroup$
    – Daniel W
    Commented Feb 13, 2019 at 20:00
  • $\begingroup$ Probably it should throw a Quantity::compat Message. But either of your suggestions work too; probably that latter one. $\endgroup$
    – Max Coplan
    Commented Feb 13, 2019 at 20:04

3 Answers 3

6
$\begingroup$

Based on one of the solutions in the second relevant source cited, the following function will convert all units to SI, except the base units you specify in the second argument. The base units can be given as Quantity or strings that Quantity can convert

units::notbase = "The argument `1` is not a base unit."; 
baserule[base_Quantity] := 
Module[{ud}, 
  If[
    MatchQ[ud = UnitDimensions[base], {{_, 1}}], 
    ud[[1,1]] -> QuantityUnit[base], 
    Message[units::notbase, base]; Nothing
  ]
]; 
baserule[base_String] := baserule[Quantity[base]]; 

unitConvert[q_Quantity, bases_List] := 
  UnitConvert[q, 
    Quantity[Times @@ Apply[Power, UnitDimensions[q] /. baserule /@ bases /. 
      {"LengthUnit" -> "Meters", "MassUnit" -> "Kilograms", "TimeUnit" -> "Seconds", 
      "ElectricCurrentUnit" -> "Amperes", "TemperatureUnit" -> "Kelvins", 
      "TemperatureDifferenceUnit" -> "KelvinsDifference", "AmountUnit" -> "Moles", 
      "LuminousIntensityUnit" -> "Candelas"}, {1}]
    ]
  ]

unitConvert[
  Quantity[1, "Watts"]/Quantity[2, "meters"], 
  {"cm", "g", "K difference", Quantity[1, "Seconds"]}
]

(* Quantity[50000, ("Centimeters"*"Grams")/"Seconds"^3] *)
$\endgroup$
6
  • $\begingroup$ Wow! Excellent answer. I'm blown away by how sophisticated this code is. Thank you so much! It is very well done. $\endgroup$
    – Max Coplan
    Commented Feb 14, 2019 at 20:17
  • $\begingroup$ Your answer has inspired me to pursue a generalization of this. Instead of only going between systems with 1→1 equivalences, what about a more general system of any quantity? For example, expressing energy in h/s, or lengths in c*t. In a theoretical application, the ability to use your unitConvert with unitConvert[q, {c,G,h, Kb, etc.}] Thanks again for your excellent response! $\endgroup$
    – Max Coplan
    Commented Feb 14, 2019 at 20:28
  • 1
    $\begingroup$ Yeah, I thought about that. Tough to do, or even define the problem, in general. It’s easy to work in fundamental units like this, but compound units doesn’t seem to have a unique solution. $\endgroup$
    – Daniel W
    Commented Feb 15, 2019 at 1:38
  • $\begingroup$ Well, I know of maybe not the most elegant solution. But what we're trying to do is express one quantity as a linear combination of other quantities. Which means we WILL have a unique solution when the basis we choose is linearly independent. $\endgroup$
    – Max Coplan
    Commented Feb 15, 2019 at 16:11
  • $\begingroup$ So imagine we express each basis quantity as a column vector with entries {Length, Time, Mass, Temperature, etc.}, (e.g. c is {1,-1,0,..}) and construct a matrix with each basis quantity as a column vector. Then multiply that matrix by the column vector {?,?,?,...}, and set it equal to the user's input quantity (e.g. distance {1,0,...}), then solve for the {?,?,?,..}. This will return the user's quantity in terms of ANY quantity they choose (given they provide a linearly independent basis). $\endgroup$
    – Max Coplan
    Commented Feb 15, 2019 at 16:15
4
$\begingroup$

Based on @DanielW's answer, here's a simplified conversion that assumes you use a fixed unit system:

toCGS[x_Quantity] := UnitConvert[x, Times @@ Power @@@ (UnitDimensions[x] /.
    {"LengthUnit" -> "Centimeters",
     "MassUnit" -> "Grams",
     "TimeUnit" -> "Seconds",
     "ElectricCurrentUnit" -> "Amperes",
     "TemperatureUnit" -> "Kelvins",
     "TemperatureDifferenceUnit" -> "KelvinsDifference",
     "AmountUnit" -> "Moles",
     "LuminousIntensityUnit" -> "Candelas"})]
$\endgroup$
1
  • $\begingroup$ Good answer. Does precisely what I need. Wish I could select this as a runner-up (even though there's only two responses 😝) $\endgroup$
    – Max Coplan
    Commented Feb 14, 2019 at 20:18
3
$\begingroup$

I'm going to extend this question to the case where we want to use units that are not simple multiples of base units: for example, energy or force may be desirable as units, and even physical constants like $\hbar$ and $k_{\text{B}}$ may be considered as units.

Below is a procedure makeUnitSystem that takes a set of desired units and generates a list of base unit replacements from it. As a first example, it generates the CGS unit system with

makeUnitSystem[{"Centimeters", "Grams", "Seconds"}]

{"TimeUnit" -> "Seconds", "LengthUnit" -> "Centimeters", "MassUnit" -> "Grams", "TemperatureUnit" -> "Kelvins", "TemperatureDifferenceUnit" -> "KelvinsDifference", "ElectricCurrentUnit" -> "Amperes", "LuminousIntensityUnit" -> "Candelas", "AmountUnit" -> "Moles", "AngleUnit" -> "Radians"}

A bit more complex is a unit system that uses energy, force, and Planck's constant:

makeUnitSystem[{"Rydbergs", "Zeptonewtons", "PlanckConstant"}]

{"TimeUnit" -> ("PlanckConstant")/("Rydbergs"), "LengthUnit" -> ("Rydbergs")/("Zeptonewtons"), "MassUnit" -> (("PlanckConstant")^2 ("Zeptonewtons")^2)/( "Rydbergs")^3, "TemperatureUnit" -> "Kelvins", "TemperatureDifferenceUnit" -> "KelvinsDifference", "ElectricCurrentUnit" -> "Amperes", "LuminousIntensityUnit" -> "Candelas", "AmountUnit" -> "Moles", "AngleUnit" -> "Radians"}

As a last example, it fails on overcomplete unit systems:

makeUnitSystem[{"Meters", "Centimeters"}]

makeUnitSystem: The unit system {Meters, Centimeters} is overcomplete. Please remove some unit.

$Failed

The generated unit system can then be used in a conversion procedure:

unitConvert[Quantity[1, "Mole/Liter"], makeUnitSystem[{"Millimoles", "Nanometers"}]]

Quantity[1/1000000000000000000000, ("Millimoles")/("Nanometers")^3]

unitConvert[Quantity["SpeedOfLight"], makeUnitSystem[{"Rydbergs", "Zeptonewtons", "PlanckConstant"}]]

Quantity[4.180369*10^-11, ("Rydbergs")^2/("PlanckConstant" "Zeptonewtons")]

Implementation

(* a set of standard units that are used when not specified *)
standardUnits = {"Seconds", "Meters", "Kilograms", "Kelvins", "KelvinsDifference",
  "Amperes", "Candelas", "Moles", "Radians"};
standardUnitDimensions = UnitDimensions[#][[1, 1]] & /@ standardUnits;

makeUnitSystem::overcomplete = "The unit system `1` is overcomplete. Please remove some unit.";
makeUnitSystem[] = Thread[standardUnitDimensions -> standardUnits];
makeUnitSystem[L_List] := Module[{M, n, u},
  (* convert the desired unit system to base units *)
  M = Lookup[#, standardUnitDimensions, 0] & /@ Apply[Rule, UnitDimensions /@ L, {2}];
  If[MatrixRank[M] < Length[L],
    Message[makeUnitSystem::overcomplete, L];
    Return[$Failed]];
  (* check which base units cannot be expressed in this system *)
  n = Position[Diagonal[PseudoInverse[M].M], Except[1], {1}, Heads -> False];
  (* extend the unit system if necessary *)
  If[Length[n] > 0,
    Return[makeUnitSystem[Append[L, standardUnits[[n[[1,1]]]]]]]];
  (* find the compound units that represent the base units *)
  u = Times @@@ Transpose[L^Transpose[PseudoInverse[M]]];
  (* return replacement list *)
  Thread[standardUnitDimensions -> u]]

unitConvert[x_Quantity, unitSystem_ /; VectorQ[unitSystem, Head[#] === Rule &]] :=
  UnitConvert[x, Times @@ Power @@@ (UnitDimensions[x] /. unitSystem)]

Update 18/04/2021

  • removed Steradians from standardUnits because Mathematica 12 treats Steradians as squared radians.
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.