michellab/BioSimSpace

Unit conversion

xiki-tempula opened this issue · 9 comments

Is your feature request related to a problem? Please describe.
I wonder if BSS allows the https://pypi.org/project/quantities/ style unit conversion?

Describe the solution you'd like
Say I have a force constant, which is
fc = 10 * _Units.Energy.kcal_per_mol / _Units.Length.angstrom / _Units.Length.angstrom
I could do
fc.rescale(_Units.Energy.kJ_per_mol / _Units.Length.nanometer / _Units.Length.nanometer)
To rescale the unit.

This is already supported using our internal units engine, see e.g:

# Get the force constant in units of kJ_per_mol/nanometer**2
force_constant = self._protocol.getForceConstant()._sire_unit
force_constant = force_constant.to(_SireUnits.kJ_per_mol/_SireUnits.nanometer2)

(This returns the value in the rescaled unit.)

General unit-based quantities are stored using default units, then converted to the appropriate engine-specific format internally.

@lohedges Thanks. I wonder if there is any plan of having a BSS level function to do the conversion? Instead of going through Sire each time.

You already can, e.g. by doing algebra directly with the BioSimSpace objects, i.e.:

from BioSimSpace import Units

fc = 10 * Units.Energy.kcal_per_mol / Units.Area.angstrom2

# Work out conversion factor.
print(fc / (Units.Energy.kj_per_mol / Units.Area.nanometer2))
4184.000

(With Sire.Units there is more flexibility in what things can be converted to. With BioSimSpace we support a limited set of unit based types, which allows us to validate things robustly when evaluating inputs, e.g. from the command-line.)

@lohedges Thanks. So BSS.Units supports Units.Area.angstrom2?

Yes. See here for details.

@lohedges I see. Thanks, I was trying to find angstrom2 in Units.Length.

These are the core units that we support. (We can add more if needed.) These are all typed handy convenience functions for direct conversion between units of the same type. Internally we can also use Sire.Unit for a wider range of options. Our GeneralUnit type holds anything that falls outside of the core types, but results from some combination of the core types, e.g. the force constant that you created.

@lohedges I see. Thanks, I was trying to find angstrom2 in Units.Length.

No problem, we created convenience types for Area and Volume to save typing.

@lohedges Thanks for the help. I wonder if there is a way of checking for complex type?
I know that one could check if a unit is a type by isinstance(1*BioSimSpace.Units.angstrom, BioSimSpace.Types.Length).
I wonder how do one check for complex unit like kcal/mol/nm^2? it seems that I cannot do BioSimSpace.Types.Energy / BioSimSpace.Types.Length.

There is an example here:

elif isinstance(force_constant, _Types._GeneralUnit):
# Validate the dimensions.
if force_constant.dimensions() != (0, 0, 0, 1, -1, 0, -2):
raise ValueError("'force_constant' has invalid dimensions! "
f"Expected dimensions are 'M Q-1 T-2', found '{force_constant.unit()}'")

Essentially you check the dimensions of the general unit are what you expect. (The dimensions() method returns a tuple containing the powers in terms of (angle, charge, length, mass, quantity, temperature, time).)