-
Notifications
You must be signed in to change notification settings - Fork 77
Description
Hi,
We need our input values to always be consistent. Currently, the conversion factors in quantities change depending on the order that dimensionality picks for the units. It's using dict which seems to return different sequences for the units and thus different conversion steps using inherently different conversion factors. It seems that using an OrderedDict always ensures the same sequence of conversions. I'm not a 100% sure about this because I don't know how the order of units pushed to this dimensionality dict is decided, but using an OrderedDict does seem to work much better.
Here is a float_test.py test you can run to check this issue (it's in quantities 0.12.2 and quantities
0.12.3 both and we're using Python 2.7.15 and Centos 7 :
from quantities import *
feet = UnitQuantity('feet', ft, symbol='feet')
dfl = 1.0/310.000 * degrees/feet/lbf
var = Quantity(dfl) * 1.0
print("var.dimensionality_before_conversion:",var.dimensionality)
var.units = m ** -2 * rad * s ** 2 * kg ** -1
print("var.dimensionality_after",var.dimensionality)
print ("converted magnitude", var.magnitude.tolist())
When you run this several times using bash, it should catch the issue quickly (for some reason it's not so easy catching it when looping it through python, it seems that the order is decided once per python run):
~/float_bash
rm ~/float_output
for i in {0..10}
do
echo "#########################################################" >> ~/float_output
python float_test.py >> ~/float_output
done
cat ~/float_output
Run it using:
~/floatbash | grep -E "dimensionality|converted"
Output:
('var.dimensionality_before_conversion:', Dimensionality({pound_force: -1, feet: -1, arcdegree: 1}))
('var.dimensionality_after', Dimensionality({radian: 1, second: 2, kilogram: -1, meter: -2}))
('converted magnitude', 4.152544497733612e-05)
('var.dimensionality_before_conversion:', Dimensionality({pound_force: -1, arcdegree: 1, feet: -1}))
('var.dimensionality_after', Dimensionality({radian: 1, second: 2, meter: -2, kilogram: -1}))
('converted magnitude', 4.152544497733611e-05)
('var.dimensionality_before_conversion:', Dimensionality({pound_force: -1, feet: -1, arcdegree: 1}))
('var.dimensionality_after', Dimensionality({radian: 1, second: 2, kilogram: -1, meter: -2}))
('converted magnitude', 4.152544497733612e-05)
('var.dimensionality_before_conversion:', Dimensionality({pound_force: -1, feet: -1, arcdegree: 1}))
('var.dimensionality_after', Dimensionality({radian: 1, second: 2, kilogram: -1, meter: -2}))
('converted magnitude', 4.152544497733612e-05)
('var.dimensionality_before_conversion:', Dimensionality({pound_force: -1, arcdegree: 1, feet: -1}))
('var.dimensionality_after', Dimensionality({radian: 1, second: 2, meter: -2, kilogram: -1}))
('converted magnitude', 4.152544497733611e-05)
('var.dimensionality_before_conversion:', Dimensionality({pound_force: -1, arcdegree: 1, feet: -1}))
('var.dimensionality_after', Dimensionality({radian: 1, second: 2, meter: -2, kilogram: -1}))
('converted magnitude', 4.152544497733611e-05)
('var.dimensionality_before_conversion:', Dimensionality({pound_force: -1, arcdegree: 1, feet: -1}))
('var.dimensionality_after', Dimensionality({radian: 1, second: 2, meter: -2, kilogram: -1}))
('converted magnitude', 4.152544497733611e-05)
('var.dimensionality_before_conversion:', Dimensionality({pound_force: -1, arcdegree: 1, feet: -1}))
('var.dimensionality_after', Dimensionality({radian: 1, second: 2, meter: -2, kilogram: -1}))
('converted magnitude', 4.152544497733611e-05)
('var.dimensionality_before_conversion:', Dimensionality({pound_force: -1, arcdegree: 1, feet: -1}))
('var.dimensionality_after', Dimensionality({radian: 1, second: 2, meter: -2, kilogram: -1}))
('converted magnitude', 4.152544497733611e-05)
('var.dimensionality_before_conversion:', Dimensionality({pound_force: -1, arcdegree: 1, feet: -1}))
('var.dimensionality_after', Dimensionality({radian: 1, second: 2, meter: -2, kilogram: -1}))
('converted magnitude', 4.152544497733611e-05)
('var.dimensionality_before_conversion:', Dimensionality({pound_force: -1, arcdegree: 1, feet: -1}))
('var.dimensionality_after', Dimensionality({radian: 1, second: 2, meter: -2, kilogram: -1}))
('converted magnitude', 4.152544497733611e-05)
As you can see, the magnitude can result in either:
4.152544497733611e-05
Dimensionality({pound_force: -1, arcdegree: 1, feet: -1}))
Dimensionality({radian: 1, second: 2, meter: -2, kilogram: -1}))
or
4.152544497733612e-05.
Dimensionality({pound_force: -1, feet: -1, arcdegree: 1})
Dimensionality({radian: 1, second: 2, kilogram: -1, meter: -2}))
This occurs due to different calculation paths taken due to different orderings of the Dimensionality dictionary due to different conversion factors.
I traced the calculations to the following that can be done manually in python by extracting the values of when it loops through the dimensionality array to do the conversion:
CALCULATION ONE:
python>>> 1/.3048
3.280839895013123
python>>> 3.280839895013123 * 0.017453292519943295
0.057261458398764085
python>>> 1/ 0.057261458398764085
17.463753595587495
python>>> 0.057261458398764085 * 4.4482216152605**-1
0.012872887942974195
python>>> 0.012872887942974195 * 1/310
4.152544497733611e-05
CALCULATION TWO:
python>>> 1/4.4482216152605
0.2248089430997105
python>>> 0.2248089430997105/0.3048
0.7375621492772654
python>>> 0.7375621492772654 * 0.017453292519943295
0.012872887942974197
python>>> 0.012872887942974197 * 1/310.0
4.152544497733612e-05
As a very very quick fix, when I change dimensionality.py to include this change:
from collections import OrderedDict class Dimensionality(OrderedDict):
It is always consistent because it goes down the same sequence of unit conversions and the same conversion factors leading to consistent behavior which we need.
Output:
$ ~/floatbash | grep -E "dimensionality|converted"
('var.dimensionality_before_conversion:', Dimensionality({arcdegree: 1, feet: -1, pound_force: -1}))
('var.dimensionality_after', Dimensionality({meter: -2, radian: 1, second: 2, kilogram: -1}))
('converted magnitude', 4.152544497733611e-05)
('var.dimensionality_before_conversion:', Dimensionality({arcdegree: 1, feet: -1, pound_force: -1}))
('var.dimensionality_after', Dimensionality({meter: -2, radian: 1, second: 2, kilogram: -1}))
('converted magnitude', 4.152544497733611e-05)
('var.dimensionality_before_conversion:', Dimensionality({arcdegree: 1, feet: -1, pound_force: -1}))
('var.dimensionality_after', Dimensionality({meter: -2, radian: 1, second: 2, kilogram: -1}))
('converted magnitude', 4.152544497733611e-05)
('var.dimensionality_before_conversion:', Dimensionality({arcdegree: 1, feet: -1, pound_force: -1}))
('var.dimensionality_after', Dimensionality({meter: -2, radian: 1, second: 2, kilogram: -1}))
('converted magnitude', 4.152544497733611e-05)
('var.dimensionality_before_conversion:', Dimensionality({arcdegree: 1, feet: -1, pound_force: -1}))
('var.dimensionality_after', Dimensionality({meter: -2, radian: 1, second: 2, kilogram: -1}))
('converted magnitude', 4.152544497733611e-05)
('var.dimensionality_before_conversion:', Dimensionality({arcdegree: 1, feet: -1, pound_force: -1}))
('var.dimensionality_after', Dimensionality({meter: -2, radian: 1, second: 2, kilogram: -1}))
('converted magnitude', 4.152544497733611e-05)
('var.dimensionality_before_conversion:', Dimensionality({arcdegree: 1, feet: -1, pound_force: -1}))
('var.dimensionality_after', Dimensionality({meter: -2, radian: 1, second: 2, kilogram: -1}))
('converted magnitude', 4.152544497733611e-05)
('var.dimensionality_before_conversion:', Dimensionality({arcdegree: 1, feet: -1, pound_force: -1}))
('var.dimensionality_after', Dimensionality({meter: -2, radian: 1, second: 2, kilogram: -1}))
('converted magnitude', 4.152544497733611e-05)
('var.dimensionality_before_conversion:', Dimensionality({arcdegree: 1, feet: -1, pound_force: -1}))
('var.dimensionality_after', Dimensionality({meter: -2, radian: 1, second: 2, kilogram: -1}))
('converted magnitude', 4.152544497733611e-05)
('var.dimensionality_before_conversion:', Dimensionality({arcdegree: 1, feet: -1, pound_force: -1}))
('var.dimensionality_after', Dimensionality({meter: -2, radian: 1, second: 2, kilogram: -1}))
('converted magnitude', 4.152544497733611e-05)
('var.dimensionality_before_conversion:', Dimensionality({arcdegree: 1, feet: -1, pound_force: -1}))
('var.dimensionality_after', Dimensionality({meter: -2, radian: 1, second: 2, kilogram: -1}))
('converted magnitude', 4.152544497733611e-05)
However, I am unsure whether this change to OrderedDict will work for all the methods you have implemented within dimensionality. Also, I'm not sure why the arcdegree, feet, pound_force is alphabetical but meter, radian, second, kilogram isn't.
What are your thoughts?
Can we add this change in as it might help others as well in consistency with unit conversions and reduce unexpected results? Our stuff is time critical, so it would be good to know if we can atleast put this change in ourselves if it can't be put in an official release
Thanks!