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.
Quantity::compat
Message. But either of your suggestions work too; probably that latter one. $\endgroup$