metrolopy 0.6.5__py3-none-any.whl → 1.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- metrolopy/__init__.py +5 -4
- metrolopy/budget.py +61 -50
- metrolopy/builtin_constants.py +903 -0
- metrolopy/constant.py +108 -104
- metrolopy/constcom.py +84 -83
- metrolopy/distributions.py +120 -39
- metrolopy/exceptions.py +7 -9
- metrolopy/fit.py +3 -3
- metrolopy/functions.py +4 -4
- metrolopy/gummy.py +571 -530
- metrolopy/indexed.py +69 -20
- metrolopy/logunit.py +1 -1
- metrolopy/mean.py +8 -9
- metrolopy/miscunits.py +21 -6
- metrolopy/nummy.py +224 -180
- metrolopy/offsetunit.py +2 -3
- metrolopy/prefixedunit.py +24 -23
- metrolopy/relunits.py +1 -2
- metrolopy/siunits.py +7 -5
- metrolopy/tests/__init__.py +6 -6
- metrolopy/tests/test_complex.py +22 -0
- metrolopy/tests/test_create.py +10 -6
- metrolopy/tests/test_gummy.py +93 -43
- metrolopy/tests/test_misc.py +4 -1
- metrolopy/tests/test_operations.py +58 -2
- metrolopy/tests/test_ubreakdown.py +6 -2
- metrolopy/ummy.py +890 -898
- metrolopy/unit.py +287 -182
- metrolopy/unitparser.py +40 -42
- metrolopy/unitutils.py +183 -159
- metrolopy/usunits.py +14 -13
- metrolopy/version.py +1 -1
- {metrolopy-0.6.5.dist-info → metrolopy-1.0.1.dist-info}/METADATA +23 -5
- metrolopy-1.0.1.dist-info/RECORD +45 -0
- metrolopy-0.6.5.dist-info/RECORD +0 -44
- {metrolopy-0.6.5.dist-info → metrolopy-1.0.1.dist-info}/WHEEL +0 -0
- {metrolopy-0.6.5.dist-info → metrolopy-1.0.1.dist-info}/licenses/LICENSE +0 -0
- {metrolopy-0.6.5.dist-info → metrolopy-1.0.1.dist-info}/top_level.txt +0 -0
- {metrolopy-0.6.5.dist-info → metrolopy-1.0.1.dist-info}/zip-safe +0 -0
metrolopy/unit.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# module unit
|
|
4
4
|
|
|
5
|
-
# Copyright (C)
|
|
5
|
+
# Copyright (C) 2025 National Research Council Canada
|
|
6
6
|
# Author: Harold Parks
|
|
7
7
|
|
|
8
8
|
# This file is part of MetroloPy.
|
|
@@ -29,12 +29,20 @@ import numpy as np
|
|
|
29
29
|
import weakref
|
|
30
30
|
import warnings
|
|
31
31
|
from .exceptions import (UnitLibError,CircularUnitConversionError,
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
from .printing import PrettyPrinter
|
|
32
|
+
UnitNotFoundError,IncompatibleUnitsError,
|
|
33
|
+
UnitLibNotFoundError,UnitWarning)
|
|
34
|
+
from .printing import PrettyPrinter,MetaPrettyPrinter
|
|
36
35
|
from .indexed import Indexed
|
|
37
36
|
from .unitparser import _UnitParser
|
|
37
|
+
from numbers import Rational,Integral,Number
|
|
38
|
+
from abc import ABCMeta
|
|
39
|
+
from fractions import Fraction
|
|
40
|
+
from decimal import Decimal
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
from mpmath import mp,mpf,rational
|
|
44
|
+
except:
|
|
45
|
+
mp = mpf = rational = None
|
|
38
46
|
|
|
39
47
|
def unit(name,exception=True):
|
|
40
48
|
"""
|
|
@@ -106,6 +114,8 @@ class Conversion:
|
|
|
106
114
|
|
|
107
115
|
def __init__(self,unit,factor=1):
|
|
108
116
|
self._unit = unit
|
|
117
|
+
if isinstance(factor,Fraction):
|
|
118
|
+
factor = MFraction(factor)
|
|
109
119
|
self.factor = factor
|
|
110
120
|
|
|
111
121
|
def to(self,g):
|
|
@@ -455,24 +465,31 @@ class Unit(PrettyPrinter,Indexed):
|
|
|
455
465
|
self._conversion = c
|
|
456
466
|
if c is not None:
|
|
457
467
|
c.parent = self
|
|
458
|
-
|
|
459
|
-
def _convs(self,d,c,h):
|
|
460
|
-
if c is None:
|
|
461
|
-
c = self._conversion.copy()
|
|
462
|
-
else:
|
|
463
|
-
if self in h:
|
|
464
|
-
if self is d:
|
|
465
|
-
raise CircularUnitConversionError('a circular conversion chain was found starting with unit ' + d.name + ' in library ' + d.library)
|
|
466
|
-
else:
|
|
467
|
-
raise CircularUnitConversionError('a circular conversion chain was found;\nstarted search with unit ' + d.name + ' in library ' + d.library + ' and the loop started with unit ' + self.name + ' in library ' + self.library)
|
|
468
|
-
h.add(self)
|
|
469
|
-
d._gconv = c
|
|
470
|
-
if self._conversion is None:
|
|
471
|
-
return
|
|
472
|
-
c = c.chain(self._conversion)
|
|
473
468
|
|
|
469
|
+
def _convs(self,h):
|
|
470
|
+
if self in h:
|
|
471
|
+
raise CircularUnitConversionError('a circular conversion chain was found nwith unit ' + self.name)
|
|
472
|
+
h.add(self)
|
|
473
|
+
|
|
474
474
|
try:
|
|
475
|
-
|
|
475
|
+
c = self._conversion
|
|
476
|
+
u = c.unit
|
|
477
|
+
if isinstance(u,_CompositeUnit):
|
|
478
|
+
una = u._units
|
|
479
|
+
unb = {}
|
|
480
|
+
for k,v in una:
|
|
481
|
+
if k in unb:
|
|
482
|
+
unb[k] += v
|
|
483
|
+
else:
|
|
484
|
+
unb[k] = v
|
|
485
|
+
unb = {k:v for k,v in unb.items() if v != 0 and k is not one}
|
|
486
|
+
if len(unb) == 0:
|
|
487
|
+
u = one
|
|
488
|
+
else:
|
|
489
|
+
u = _CompositeUnit([(k,v) for k,v in unb.items()])
|
|
490
|
+
u._find_conv()
|
|
491
|
+
c = c.chain(Conversion(u,1))
|
|
492
|
+
|
|
476
493
|
except UnitNotFoundError:
|
|
477
494
|
warnings.warn('while searching conversions: unit ' + self.name + ' should have a conversion to ' + self._conversion.unit_name + ' in library ' + str(self._conversion.unit_library) + ' but the second unit was not found',UnitWarning,stacklevel=3)
|
|
478
495
|
return
|
|
@@ -480,7 +497,13 @@ class Unit(PrettyPrinter,Indexed):
|
|
|
480
497
|
warnings.warn('while searching conversions: unit ' + self.name + ' should have a conversion to ' + self._conversion.unit_name + ' in library ' + str(self._conversion.unit_library) + ' but the library containing the second unit has not been loaded',UnitWarning,stacklevel=3)
|
|
481
498
|
return
|
|
482
499
|
|
|
483
|
-
u.
|
|
500
|
+
if u._conversion is None:
|
|
501
|
+
self._gconv = c
|
|
502
|
+
else:
|
|
503
|
+
r = u._convs(h)
|
|
504
|
+
self._gconv = c.chain(r)
|
|
505
|
+
|
|
506
|
+
return self._gconv
|
|
484
507
|
|
|
485
508
|
@property
|
|
486
509
|
def linear(self):
|
|
@@ -496,18 +519,18 @@ class Unit(PrettyPrinter,Indexed):
|
|
|
496
519
|
return self._linear
|
|
497
520
|
|
|
498
521
|
def _convtree(self):
|
|
499
|
-
if self.
|
|
500
|
-
self._convs(
|
|
522
|
+
if self.conversion is not None and self._gconv is None:
|
|
523
|
+
self._convs(set())
|
|
501
524
|
|
|
502
525
|
def _convert(self,g,un):
|
|
503
|
-
if self.
|
|
504
|
-
return self.
|
|
526
|
+
if self.conversion is not None and self.conversion.unit is un:
|
|
527
|
+
return self.conversion.to(g)
|
|
505
528
|
|
|
506
529
|
if self._gconv is not None and self._gconv._unit is un:
|
|
507
530
|
return self._gconv.to(g)
|
|
508
531
|
|
|
509
|
-
if un.
|
|
510
|
-
return un.
|
|
532
|
+
if un.conversion is not None and un.conversion.unit is self:
|
|
533
|
+
return un.conversion.frm(g)
|
|
511
534
|
|
|
512
535
|
if un._gconv is not None and un._gconv._unit is self:
|
|
513
536
|
return un._gconv.frm(g)
|
|
@@ -536,7 +559,7 @@ class Unit(PrettyPrinter,Indexed):
|
|
|
536
559
|
|
|
537
560
|
unm = unit.short_name
|
|
538
561
|
if ret is None:
|
|
539
|
-
raise
|
|
562
|
+
raise IncompatibleUnitsError('no conversion found from unit ' + self.short_name + ' to unit ' + unm)
|
|
540
563
|
return ret
|
|
541
564
|
|
|
542
565
|
@property
|
|
@@ -562,6 +585,20 @@ class Unit(PrettyPrinter,Indexed):
|
|
|
562
585
|
"""
|
|
563
586
|
return [(self,1)]
|
|
564
587
|
|
|
588
|
+
@property
|
|
589
|
+
def base(self):
|
|
590
|
+
"""read-only
|
|
591
|
+
|
|
592
|
+
Returns the base unit. That is we follow the chain of conversions
|
|
593
|
+
starting with this units `Conversion` instance until we get to a unit
|
|
594
|
+
that has no `Conversion` instance. If this unit has no `Conversion`
|
|
595
|
+
instance `self` is returned.
|
|
596
|
+
"""
|
|
597
|
+
if self.conversion is None:
|
|
598
|
+
return self
|
|
599
|
+
self._convtree()
|
|
600
|
+
return self._gconv._unit
|
|
601
|
+
|
|
565
602
|
@staticmethod
|
|
566
603
|
def _cancel(a,b=[],asign=1,bsign=1):
|
|
567
604
|
# Takes .units lists a**asign and b**bsign and attempts to convert
|
|
@@ -586,7 +623,7 @@ class Unit(PrettyPrinter,Indexed):
|
|
|
586
623
|
b[f] += v
|
|
587
624
|
converted = True
|
|
588
625
|
break
|
|
589
|
-
except
|
|
626
|
+
except IncompatibleUnitsError:
|
|
590
627
|
pass
|
|
591
628
|
if not converted:
|
|
592
629
|
b[k] = v
|
|
@@ -770,7 +807,7 @@ class Unit(PrettyPrinter,Indexed):
|
|
|
770
807
|
if aconv:
|
|
771
808
|
return (self.convert(a,bunit) % b,bunit)
|
|
772
809
|
return (a % bunit.convert(b,self),self)
|
|
773
|
-
except
|
|
810
|
+
except IncompatibleUnitsError:
|
|
774
811
|
raise IncompatibleUnitsError('a quantity with unit ' + self.tostring() + ' may not be added to a quantity with unit ' + bunit.tostring())
|
|
775
812
|
|
|
776
813
|
def _rmod(self,a,bunit,b,aconv):
|
|
@@ -784,7 +821,7 @@ class Unit(PrettyPrinter,Indexed):
|
|
|
784
821
|
if aconv:
|
|
785
822
|
return (b % self.convert(a,bunit),bunit)
|
|
786
823
|
return (bunit.convert(b,self) % a,self)
|
|
787
|
-
except
|
|
824
|
+
except IncompatibleUnitsError:
|
|
788
825
|
raise IncompatibleUnitsError('a quantity with unit ' + self.tostring() + ' may not be added to a quantity with unit ' + bunit.tostring())
|
|
789
826
|
|
|
790
827
|
def _cpow(self,v):
|
|
@@ -801,30 +838,12 @@ class Unit(PrettyPrinter,Indexed):
|
|
|
801
838
|
if bunit is not one:
|
|
802
839
|
try:
|
|
803
840
|
b = bunit.convert(b,one)
|
|
804
|
-
except
|
|
841
|
+
except IncompatibleUnitsError:
|
|
805
842
|
raise IncompatibleUnitsError('the exponent is not dimensionless')
|
|
806
843
|
|
|
807
844
|
un,c = self._cpow(b)
|
|
808
845
|
un = _CompositeUnit(un)
|
|
809
846
|
return ((a**b)*c,un)
|
|
810
|
-
#bb = float(b)
|
|
811
|
-
#if bb != b:
|
|
812
|
-
#try:
|
|
813
|
-
#a = self.convert(a,one)
|
|
814
|
-
#except NoUnitConversionFoundError:
|
|
815
|
-
#raise IncompatibleUnitsError('a gummy that is not dimensonless may not be raised to a power with an uncertainty')
|
|
816
|
-
#return (a**b,one)
|
|
817
|
-
#b = bb
|
|
818
|
-
#if int(b) == b:
|
|
819
|
-
#b = int(b)
|
|
820
|
-
|
|
821
|
-
#if b == 0:
|
|
822
|
-
#return (a**0,one)
|
|
823
|
-
|
|
824
|
-
#un,c = self._cpow(b)
|
|
825
|
-
#un = _CompositeUnit(un)
|
|
826
|
-
|
|
827
|
-
#return ((a**b)*c,un)
|
|
828
847
|
|
|
829
848
|
def _rpow(self,a,bunit,b,aconv):
|
|
830
849
|
if isinstance(b,Unit):
|
|
@@ -834,28 +853,11 @@ class Unit(PrettyPrinter,Indexed):
|
|
|
834
853
|
if self is not one:
|
|
835
854
|
try:
|
|
836
855
|
a = self.convert(a,one)
|
|
837
|
-
except
|
|
856
|
+
except IncompatibleUnitsError:
|
|
838
857
|
raise IncompatibleUnitsError('the exponent is not dimensionless')
|
|
839
858
|
un,c = bunit._cpow(a)
|
|
840
859
|
un = _CompositeUnit(un)
|
|
841
860
|
return ((b**a)*c,un)
|
|
842
|
-
#aa = float(a)
|
|
843
|
-
#if aa != a:
|
|
844
|
-
#try:
|
|
845
|
-
#b = self.convert(b,one)
|
|
846
|
-
#except NoUnitConversionFoundError:
|
|
847
|
-
#raise IncompatibleUnitsError('a gummy that is not dimensonless may not be raised to a power with an uncertainty')
|
|
848
|
-
#return (b**a,one)
|
|
849
|
-
#a = float(a)
|
|
850
|
-
#if int(a) == a:
|
|
851
|
-
#va = int(a)
|
|
852
|
-
|
|
853
|
-
#if a == 0:
|
|
854
|
-
#return (b**0,one)
|
|
855
|
-
|
|
856
|
-
#un,c = _CompositeUnit(bunit._cpow(a))
|
|
857
|
-
|
|
858
|
-
#return ((b**a)*c,un)
|
|
859
861
|
|
|
860
862
|
def _ufunc(self,func,*args,**kwds):
|
|
861
863
|
if self.linear:
|
|
@@ -919,11 +921,11 @@ class Unit(PrettyPrinter,Indexed):
|
|
|
919
921
|
return _CompositeUnit(self.units + vi)
|
|
920
922
|
|
|
921
923
|
def __rtruediv__(self,v):
|
|
922
|
-
return Quantity._make(1/v,unit=self)
|
|
923
|
-
|
|
924
924
|
if v is one or v == 1:
|
|
925
925
|
return _CompositeUnit([(e[0],-e[1]) for e in self.units])
|
|
926
926
|
|
|
927
|
+
return Quantity._make(v,unit=self**-1)
|
|
928
|
+
|
|
927
929
|
def __pow__(self,v):
|
|
928
930
|
if v == -1:
|
|
929
931
|
return _CompositeUnit([(e[0],-e[1]) for e in self.units])
|
|
@@ -939,42 +941,6 @@ class Unit(PrettyPrinter,Indexed):
|
|
|
939
941
|
|
|
940
942
|
return _CompositeUnit([(e[0],v*e[1]) for e in self.units])
|
|
941
943
|
|
|
942
|
-
# Define comparison operators and a hash function so, for the
|
|
943
|
-
# _CompositeUnit subclass we can sort the Unit order and store them as keys
|
|
944
|
-
# in a unique OrderedDict.
|
|
945
|
-
def __lt__(self, b):
|
|
946
|
-
if self.order == b.order:
|
|
947
|
-
return self.name < b.name
|
|
948
|
-
return self.order < b.order
|
|
949
|
-
|
|
950
|
-
def __le__(self, b):
|
|
951
|
-
if self.__eq__(b):
|
|
952
|
-
return True
|
|
953
|
-
if self.order == b.order:
|
|
954
|
-
return self.name < b.name
|
|
955
|
-
return self.order < b.order
|
|
956
|
-
|
|
957
|
-
def __eq__(self, b):
|
|
958
|
-
return (self is b)
|
|
959
|
-
|
|
960
|
-
def __ge__(self, b):
|
|
961
|
-
if self.__eq__(b):
|
|
962
|
-
return True
|
|
963
|
-
if self.order == b.order:
|
|
964
|
-
return self.name > b.name
|
|
965
|
-
return self.order > b.order
|
|
966
|
-
|
|
967
|
-
def __gt__(self, b):
|
|
968
|
-
if self.order == b.order:
|
|
969
|
-
return self.name > b.name
|
|
970
|
-
return self.order > b.order
|
|
971
|
-
|
|
972
|
-
def __ne__(self, b):
|
|
973
|
-
return not (self is b)
|
|
974
|
-
|
|
975
|
-
def __hash__(self):
|
|
976
|
-
return id(self)
|
|
977
|
-
|
|
978
944
|
|
|
979
945
|
class _CompositeUnit(Unit):
|
|
980
946
|
# Instances of this class represent composite derived unit.
|
|
@@ -985,7 +951,7 @@ class _CompositeUnit(Unit):
|
|
|
985
951
|
|
|
986
952
|
@staticmethod
|
|
987
953
|
def _unicode_super(e):
|
|
988
|
-
if
|
|
954
|
+
if int(e) == e:
|
|
989
955
|
se = str(int(e))
|
|
990
956
|
so = ''
|
|
991
957
|
for s in se:
|
|
@@ -1012,10 +978,15 @@ class _CompositeUnit(Unit):
|
|
|
1012
978
|
if s == '9':
|
|
1013
979
|
so += '\u2079'
|
|
1014
980
|
return so
|
|
1015
|
-
return '**' +
|
|
981
|
+
return '**' + _CompositeUnit._format_sscript(e,parenth=True)
|
|
1016
982
|
|
|
1017
983
|
@staticmethod
|
|
1018
|
-
def _format_sscript(e):
|
|
984
|
+
def _format_sscript(e,parenth=False):
|
|
985
|
+
if isinstance(e,Rational) and not isinstance(e,Integral):
|
|
986
|
+
ret = str(e.numerator) + '/' + str(e.denominator)
|
|
987
|
+
if parenth:
|
|
988
|
+
ret = '(' + ret + ')'
|
|
989
|
+
return ret
|
|
1019
990
|
mf = np.modf(e)
|
|
1020
991
|
if mf[0] == 0:
|
|
1021
992
|
return '{:.0f}'.format(e)
|
|
@@ -1023,9 +994,7 @@ class _CompositeUnit(Unit):
|
|
|
1023
994
|
return '{:.0f}'.format(2*e) + '/2'
|
|
1024
995
|
if mf[0] == 0.25:
|
|
1025
996
|
return '{:.0f}'.format(4*e) + '/4'
|
|
1026
|
-
|
|
1027
|
-
return '{:.0f}'.format(3*e) + '/3'
|
|
1028
|
-
return '{:.2f}'.format(e).rstrip('0')
|
|
997
|
+
return str(e).rstrip('0.')
|
|
1029
998
|
|
|
1030
999
|
@staticmethod
|
|
1031
1000
|
def _iaddsym(txt,s,e,fmt,f):
|
|
@@ -1057,7 +1026,7 @@ class _CompositeUnit(Unit):
|
|
|
1057
1026
|
txt += '*'
|
|
1058
1027
|
txt += s
|
|
1059
1028
|
if e != 1:
|
|
1060
|
-
txt += '**' +
|
|
1029
|
+
txt += '**' + _CompositeUnit._format_sscript(e,parenth=True)
|
|
1061
1030
|
else:
|
|
1062
1031
|
if txt != '':
|
|
1063
1032
|
txt += ' '
|
|
@@ -1071,7 +1040,10 @@ class _CompositeUnit(Unit):
|
|
|
1071
1040
|
return _CompositeUnit._iaddsym(txt,u[0].tostring(fmt=fmt),u[1],fmt,f)
|
|
1072
1041
|
|
|
1073
1042
|
def __new__(cls,ul):
|
|
1074
|
-
|
|
1043
|
+
|
|
1044
|
+
# ul is a list of tupels in each tuple a (non _CompositeUnit) Unit
|
|
1045
|
+
# instance followed by an exponent.
|
|
1046
|
+
ul = [u for u in ul if u[0] is not one and u[1] != 0]
|
|
1075
1047
|
|
|
1076
1048
|
if len(ul) == 0:
|
|
1077
1049
|
return one
|
|
@@ -1079,7 +1051,7 @@ class _CompositeUnit(Unit):
|
|
|
1079
1051
|
if len(ul) == 1 and ul[0][1] == 1:
|
|
1080
1052
|
return ul[0][0]
|
|
1081
1053
|
|
|
1082
|
-
ul = sorted(ul,key = lambda x: (x[0],-x[1]))
|
|
1054
|
+
ul = sorted(ul,key = lambda x: (x[0].order,-x[1]))
|
|
1083
1055
|
|
|
1084
1056
|
uid = tuple([(id(u[0]),u[1]) for u in ul])
|
|
1085
1057
|
ret = _CompositeUnit.living_units.get(uid)
|
|
@@ -1262,28 +1234,9 @@ class _CompositeUnit(Unit):
|
|
|
1262
1234
|
return ret
|
|
1263
1235
|
|
|
1264
1236
|
def _find_conv(self):
|
|
1265
|
-
c = None
|
|
1266
|
-
cun = {}
|
|
1267
1237
|
cg = None
|
|
1268
1238
|
cgun = {}
|
|
1269
1239
|
for un,e in self._units:
|
|
1270
|
-
if un.conversion is None:
|
|
1271
|
-
if un in cun:
|
|
1272
|
-
cun[un] += e
|
|
1273
|
-
else:
|
|
1274
|
-
cun[un] = e
|
|
1275
|
-
else:
|
|
1276
|
-
p = un.conversion.pow(e)
|
|
1277
|
-
if c is None:
|
|
1278
|
-
c = p
|
|
1279
|
-
else:
|
|
1280
|
-
c = c.chain(p)
|
|
1281
|
-
for t in p.unit.units:
|
|
1282
|
-
if t[0] in cun:
|
|
1283
|
-
cun[t[0]] += t[1]
|
|
1284
|
-
else:
|
|
1285
|
-
cun[t[0]] = t[1]
|
|
1286
|
-
|
|
1287
1240
|
un._convtree()
|
|
1288
1241
|
|
|
1289
1242
|
if un._gconv is None:
|
|
@@ -1296,27 +1249,15 @@ class _CompositeUnit(Unit):
|
|
|
1296
1249
|
if cg is None:
|
|
1297
1250
|
cg = p
|
|
1298
1251
|
else:
|
|
1299
|
-
cg =
|
|
1252
|
+
cg = cg.chain(p)
|
|
1300
1253
|
for t in p.unit.units:
|
|
1301
1254
|
if t[0] in cgun:
|
|
1302
1255
|
cgun[t[0]] += t[1]
|
|
1303
1256
|
else:
|
|
1304
1257
|
cgun[t[0]] = t[1]
|
|
1305
1258
|
|
|
1306
|
-
cun = [(k,v) for k,v in cun.items() if v != 0]
|
|
1307
1259
|
cgun = [(k,v) for k,v in cgun.items() if v != 0]
|
|
1308
1260
|
|
|
1309
|
-
cun = _CompositeUnit(cun)
|
|
1310
|
-
if cun is not self:
|
|
1311
|
-
if c is None:
|
|
1312
|
-
c = Conversion(cun,1)
|
|
1313
|
-
else:
|
|
1314
|
-
c._unit = cun
|
|
1315
|
-
c.parent = self
|
|
1316
|
-
self._conversion = c
|
|
1317
|
-
else:
|
|
1318
|
-
self._conversion = None
|
|
1319
|
-
|
|
1320
1261
|
cgun = _CompositeUnit(cgun)
|
|
1321
1262
|
if cgun is not self:
|
|
1322
1263
|
if cg is None:
|
|
@@ -1324,9 +1265,9 @@ class _CompositeUnit(Unit):
|
|
|
1324
1265
|
else:
|
|
1325
1266
|
cg._unit = cgun
|
|
1326
1267
|
cg.parent = self
|
|
1327
|
-
self._gconv = cg
|
|
1268
|
+
self._gconv = self._conversion = cg
|
|
1328
1269
|
else:
|
|
1329
|
-
self._gconv = None
|
|
1270
|
+
self._gconv = self._conversion = None
|
|
1330
1271
|
|
|
1331
1272
|
self._have_conversion = True
|
|
1332
1273
|
|
|
@@ -1368,17 +1309,6 @@ class _CompositeUnit(Unit):
|
|
|
1368
1309
|
"""
|
|
1369
1310
|
sha = {a for a in self._aliases if Unit.unit('[' + a + ']',exception=False) is not self}
|
|
1370
1311
|
return sha
|
|
1371
|
-
|
|
1372
|
-
def _convtree(self):
|
|
1373
|
-
if not self._have_conversion:
|
|
1374
|
-
self._find_conv()
|
|
1375
|
-
Unit._convtree(self)
|
|
1376
|
-
|
|
1377
|
-
def _convs(self,d,c,h):
|
|
1378
|
-
if not self._have_conversion:
|
|
1379
|
-
self._find_conv()
|
|
1380
|
-
Unit._convs(self,d,c,h)
|
|
1381
|
-
|
|
1382
1312
|
|
|
1383
1313
|
class _One(Unit):
|
|
1384
1314
|
"""
|
|
@@ -1423,9 +1353,11 @@ class _One(Unit):
|
|
|
1423
1353
|
|
|
1424
1354
|
with Unit._builtin():
|
|
1425
1355
|
one = _One('1','',add_symbol=False)
|
|
1426
|
-
|
|
1427
1356
|
|
|
1428
|
-
class
|
|
1357
|
+
class MetaQuantity(MetaPrettyPrinter,ABCMeta):
|
|
1358
|
+
pass
|
|
1359
|
+
|
|
1360
|
+
class Quantity(PrettyPrinter,Number,metaclass=MetaQuantity):
|
|
1429
1361
|
"""
|
|
1430
1362
|
Instances of this class represent a quantity with a value and a unit.
|
|
1431
1363
|
The behavior of Quantity instances under mathematical operations with
|
|
@@ -1445,7 +1377,7 @@ class Quantity(PrettyPrinter):
|
|
|
1445
1377
|
Parameters
|
|
1446
1378
|
----------
|
|
1447
1379
|
|
|
1448
|
-
value:
|
|
1380
|
+
value: numeric (including `ummy`)
|
|
1449
1381
|
the value of the Quantity
|
|
1450
1382
|
|
|
1451
1383
|
unit: `str` or `Unit`
|
|
@@ -1486,6 +1418,10 @@ class Quantity(PrettyPrinter):
|
|
|
1486
1418
|
return unit
|
|
1487
1419
|
|
|
1488
1420
|
def __init__(self,value,unit=one):
|
|
1421
|
+
try:
|
|
1422
|
+
hash(value)
|
|
1423
|
+
except TypeError:
|
|
1424
|
+
raise TypeError('value is unhashable')
|
|
1489
1425
|
self._value = value
|
|
1490
1426
|
if isinstance(unit,Unit):
|
|
1491
1427
|
self._unit = unit
|
|
@@ -1606,6 +1542,12 @@ class Quantity(PrettyPrinter):
|
|
|
1606
1542
|
return c
|
|
1607
1543
|
|
|
1608
1544
|
def tostring(self,fmt=None,**kwds):
|
|
1545
|
+
"""
|
|
1546
|
+
tostring(fmt='unicode')
|
|
1547
|
+
|
|
1548
|
+
returns a string representation of the Quantity. fmt may be "unicode",
|
|
1549
|
+
"html","latex" or "ascii". The default is "unicode".
|
|
1550
|
+
"""
|
|
1609
1551
|
unit = self._unit.tostring(fmt=fmt,**kwds)
|
|
1610
1552
|
unit = self._add_unit_sp(fmt,unit)
|
|
1611
1553
|
if isinstance(self.value,PrettyPrinter):
|
|
@@ -1615,6 +1557,12 @@ class Quantity(PrettyPrinter):
|
|
|
1615
1557
|
return value + unit
|
|
1616
1558
|
|
|
1617
1559
|
def copy(self,tofloat=False):
|
|
1560
|
+
"""
|
|
1561
|
+
copy(tofloat=False)
|
|
1562
|
+
|
|
1563
|
+
returns a copy of self. If tofloat is True, the self.value will be
|
|
1564
|
+
converted to float. The default is False.
|
|
1565
|
+
"""
|
|
1618
1566
|
if tofloat:
|
|
1619
1567
|
try:
|
|
1620
1568
|
return type(self)(self.value.tofloat(),unit=self.unit)
|
|
@@ -1627,11 +1575,24 @@ class Quantity(PrettyPrinter):
|
|
|
1627
1575
|
return type(self)(self.value,self.unit)
|
|
1628
1576
|
|
|
1629
1577
|
def tofloat(self):
|
|
1578
|
+
"""
|
|
1579
|
+
returns a copy of self with value float(self.value) equivalent to
|
|
1580
|
+
copy(tofloat=True)
|
|
1581
|
+
"""
|
|
1630
1582
|
return self.copy(tofloat=True)
|
|
1631
1583
|
|
|
1632
1584
|
def totuple(self):
|
|
1585
|
+
"""
|
|
1586
|
+
returns the tuple (self.value,self.unit)
|
|
1587
|
+
"""
|
|
1633
1588
|
return (self.value,self.unit)
|
|
1634
1589
|
|
|
1590
|
+
def tobaseunit(self):
|
|
1591
|
+
"""
|
|
1592
|
+
Returns a Quantity equal to self converted to unit self.unit.base
|
|
1593
|
+
"""
|
|
1594
|
+
return self.convert(self.unit.base)
|
|
1595
|
+
|
|
1635
1596
|
def splonk(self):
|
|
1636
1597
|
"""
|
|
1637
1598
|
returns self.value if self.unit is one else returns self
|
|
@@ -1720,21 +1681,27 @@ class Quantity(PrettyPrinter):
|
|
|
1720
1681
|
if self._unit is not v.unit:
|
|
1721
1682
|
try:
|
|
1722
1683
|
v = v.convert(self.unit)
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1684
|
+
except IncompatibleUnitsError:
|
|
1685
|
+
raise IncompatibleUnitsError('Quantities with incompatible units cannot be ordered')
|
|
1686
|
+
return f(self.value,v.value)
|
|
1726
1687
|
else:
|
|
1727
1688
|
try:
|
|
1728
1689
|
s = self.convert(one)
|
|
1729
1690
|
return f(s.value,v)
|
|
1730
|
-
except:
|
|
1731
|
-
raise IncompatibleUnitsError('Quantities with incompatible units cannot be
|
|
1691
|
+
except IncompatibleUnitsError:
|
|
1692
|
+
raise IncompatibleUnitsError('Quantities with incompatible units cannot be ordered')
|
|
1732
1693
|
|
|
1733
1694
|
def __eq__(self, v):
|
|
1734
|
-
|
|
1695
|
+
try:
|
|
1696
|
+
return self._cmp(v,lambda x,y: x == y)
|
|
1697
|
+
except IncompatibleUnitsError:
|
|
1698
|
+
return False
|
|
1735
1699
|
|
|
1736
1700
|
def __ne__(self, v):
|
|
1737
|
-
|
|
1701
|
+
try:
|
|
1702
|
+
return self._cmp(v,lambda x,y: x != y)
|
|
1703
|
+
except IncompatibleUnitsError:
|
|
1704
|
+
return True
|
|
1738
1705
|
|
|
1739
1706
|
def __lt__(self, v):
|
|
1740
1707
|
return self._cmp(v,lambda x,y: x < y)
|
|
@@ -1767,7 +1734,7 @@ class Quantity(PrettyPrinter):
|
|
|
1767
1734
|
if self.splonk_func_ret:
|
|
1768
1735
|
return func(*x,**kwds)
|
|
1769
1736
|
return make(func(*x,**kwds))
|
|
1770
|
-
except
|
|
1737
|
+
except IncompatibleUnitsError:
|
|
1771
1738
|
raise NotImplementedError()
|
|
1772
1739
|
|
|
1773
1740
|
def __array_ufunc__(self,ufunc,method,*args,**kwds):
|
|
@@ -1780,22 +1747,55 @@ class Quantity(PrettyPrinter):
|
|
|
1780
1747
|
def __array_function__(self,func,method,*args,**kwds):
|
|
1781
1748
|
return self._ufunc(func,*args,**kwds)
|
|
1782
1749
|
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
#def __int__(self):
|
|
1787
|
-
#return int(self.value)
|
|
1750
|
+
def __float__(self):
|
|
1751
|
+
s = self.convert(1)
|
|
1752
|
+
return float(s.value)
|
|
1788
1753
|
|
|
1789
|
-
|
|
1790
|
-
|
|
1754
|
+
def __complex__(self):
|
|
1755
|
+
s = self.convert(1)
|
|
1756
|
+
return complex(s.value)
|
|
1757
|
+
|
|
1758
|
+
def __bool__(self):
|
|
1759
|
+
return self != 0
|
|
1791
1760
|
|
|
1792
1761
|
@property
|
|
1793
1762
|
def real(self):
|
|
1794
|
-
|
|
1795
|
-
|
|
1763
|
+
if self.unit.linear:
|
|
1764
|
+
s = self
|
|
1765
|
+
else:
|
|
1766
|
+
s = self.tobaseunit()
|
|
1767
|
+
|
|
1768
|
+
try:
|
|
1769
|
+
v = s.value.real
|
|
1770
|
+
except:
|
|
1771
|
+
v = float(s.value)
|
|
1772
|
+
return type(self)(v,unit=s.unit)
|
|
1773
|
+
|
|
1796
1774
|
@property
|
|
1797
1775
|
def imag(self):
|
|
1798
|
-
|
|
1776
|
+
if self.unit.linear:
|
|
1777
|
+
s = self
|
|
1778
|
+
else:
|
|
1779
|
+
s = self.tobaseunit()
|
|
1780
|
+
|
|
1781
|
+
try:
|
|
1782
|
+
v = s.value.imag
|
|
1783
|
+
except:
|
|
1784
|
+
v = 0
|
|
1785
|
+
return type(self)(v,unit=s.unit)
|
|
1786
|
+
|
|
1787
|
+
def __hash__(self):
|
|
1788
|
+
r = self.tobaseunit().totuple()
|
|
1789
|
+
if r[1] is one:
|
|
1790
|
+
return hash(r[0])
|
|
1791
|
+
return hash(r)
|
|
1792
|
+
|
|
1793
|
+
def conjugate(self):
|
|
1794
|
+
try:
|
|
1795
|
+
v = self.value.conjugate()
|
|
1796
|
+
except:
|
|
1797
|
+
v = complex(float(self.value))
|
|
1798
|
+
return type(self)(v,unit=self.unit)
|
|
1799
1799
|
|
|
1800
1800
|
|
|
1801
1801
|
class QuantityArray(Quantity):
|
|
@@ -1849,5 +1849,110 @@ class QuantityArray(Quantity):
|
|
|
1849
1849
|
|
|
1850
1850
|
|
|
1851
1851
|
Quantity._arraytype = QuantityArray
|
|
1852
|
+
|
|
1852
1853
|
|
|
1854
|
+
class MFraction(Fraction):
|
|
1855
|
+
"""
|
|
1856
|
+
A fraction.Fraction sub-class that works with Decimal and mpmath.mpf objects
|
|
1857
|
+
"""
|
|
1858
|
+
|
|
1859
|
+
@classmethod
|
|
1860
|
+
def fromnum(cls,x):
|
|
1861
|
+
return MFraction(Fraction(x))
|
|
1862
|
+
|
|
1863
|
+
def __new__(cls, *args, **kwargs):
|
|
1864
|
+
if len(args) == 1 and not (isinstance(args[0],str)
|
|
1865
|
+
or isinstance(args[0],Fraction)):
|
|
1866
|
+
return args[0]
|
|
1867
|
+
ret = super(MFraction, cls).__new__(cls, *args, **kwargs)
|
|
1868
|
+
if ret.denominator == 1:
|
|
1869
|
+
return ret.numerator
|
|
1870
|
+
return ret
|
|
1871
|
+
|
|
1872
|
+
def _mpmath_(self,p,r):
|
|
1873
|
+
return rational.mpq(self.numerator,self.denominator)
|
|
1874
|
+
|
|
1875
|
+
def todecimal(self):
|
|
1876
|
+
return Decimal(self.numerator)/Decimal(self.denominator)
|
|
1877
|
+
|
|
1878
|
+
def __add__(self,v):
|
|
1879
|
+
if isinstance(v,Decimal):
|
|
1880
|
+
return self.todecimal().__add__(v)
|
|
1881
|
+
return MFraction(super().__add__(v))
|
|
1882
|
+
|
|
1883
|
+
def __radd__(self,v):
|
|
1884
|
+
if isinstance(v,Decimal):
|
|
1885
|
+
return self.todecimal().__radd__(v)
|
|
1886
|
+
return MFraction(super().__radd__(v))
|
|
1887
|
+
|
|
1888
|
+
def __sub__(self,v):
|
|
1889
|
+
if isinstance(v,Decimal):
|
|
1890
|
+
return self.todecimal().__sub__(v)
|
|
1891
|
+
return MFraction(super().__sub__(v))
|
|
1892
|
+
|
|
1893
|
+
def __rsub__(self,v):
|
|
1894
|
+
if isinstance(v,Decimal):
|
|
1895
|
+
return self.todecimal().__rsub__(v)
|
|
1896
|
+
return MFraction(super().__rsub__(v))
|
|
1897
|
+
|
|
1898
|
+
def __mul__(self,v):
|
|
1899
|
+
if isinstance(v,Decimal):
|
|
1900
|
+
return self.todecimal().__mul__(v)
|
|
1901
|
+
return MFraction(super().__mul__(v))
|
|
1902
|
+
|
|
1903
|
+
def __rmul__(self,v):
|
|
1904
|
+
if isinstance(v,Decimal):
|
|
1905
|
+
return self.todecimal().__rmul__(v)
|
|
1906
|
+
return MFraction(super().__rmul__(v))
|
|
1907
|
+
|
|
1908
|
+
def __truediv__(self,v):
|
|
1909
|
+
if isinstance(v,Decimal):
|
|
1910
|
+
return self.todecimal().__truediv__(v)
|
|
1911
|
+
return MFraction(super().__truediv__(v))
|
|
1912
|
+
|
|
1913
|
+
def __rtruediv__(self,v):
|
|
1914
|
+
if isinstance(v,Decimal):
|
|
1915
|
+
return self.todecimal().__rtruediv__(v)
|
|
1916
|
+
return MFraction(super().__rtruediv__(v))
|
|
1917
|
+
|
|
1918
|
+
def __pow__(self,v):
|
|
1919
|
+
if isinstance(v,Decimal):
|
|
1920
|
+
return self.todecimal().__pow__(v)
|
|
1921
|
+
return MFraction(super().__pow__(v))
|
|
1922
|
+
|
|
1923
|
+
def __rpow__(self,v):
|
|
1924
|
+
if isinstance(v,Decimal):
|
|
1925
|
+
return self.todecimal().__rpow__(v)
|
|
1926
|
+
if isinstance(v,Fraction):
|
|
1927
|
+
return MFraction(v).__pow__(self)
|
|
1928
|
+
return MFraction(super().__rpow__(v))
|
|
1929
|
+
|
|
1930
|
+
def __floordiv__(self,v):
|
|
1931
|
+
if isinstance(v,Decimal):
|
|
1932
|
+
return self.todecimal().__floordiv__(v)
|
|
1933
|
+
return MFraction(super().__floordiv__(v))
|
|
1934
|
+
|
|
1935
|
+
def __rfloordiv__(self,v):
|
|
1936
|
+
if isinstance(v,Decimal):
|
|
1937
|
+
return self.todecimal().__rfloordiv__(v)
|
|
1938
|
+
return MFraction(super().__rfloordiv__(v))
|
|
1939
|
+
|
|
1940
|
+
def __mod__(self,v):
|
|
1941
|
+
if isinstance(v,Decimal):
|
|
1942
|
+
return self.todecimal().__mod__(v)
|
|
1943
|
+
return MFraction(super().__mod__(v))
|
|
1944
|
+
|
|
1945
|
+
def __rmod__(self,v):
|
|
1946
|
+
if isinstance(v,Decimal):
|
|
1947
|
+
return self.todecimal().__rmod__(v)
|
|
1948
|
+
return MFraction(super().__rmod__(v))
|
|
1949
|
+
|
|
1950
|
+
def __abs__(self):
|
|
1951
|
+
return MFraction(super().__abs__())
|
|
1952
|
+
|
|
1953
|
+
def __neg__(self):
|
|
1954
|
+
return MFraction(super().__neg__())
|
|
1955
|
+
|
|
1956
|
+
def __pos__(self):
|
|
1957
|
+
return MFraction(super().__pos__())
|
|
1853
1958
|
|