Skip to content

Commit

Permalink
Move to Python 3 only and include a setup.py script
Browse files Browse the repository at this point in the history
  • Loading branch information
xnx committed Jul 12, 2016
1 parent 82dca5b commit c058786
Show file tree
Hide file tree
Showing 17 changed files with 130 additions and 174 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@
*.swp
*.swo
.DS_Store
build/
dist/
pyqn.egg-info/
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# pyvalem
A Python package for handling physical units and quantities.

A very quick overview:

In [1]: from pyqn.units import Units

In [2]: u1 = Units('km')

In [3]: u2 = Units('hr')

In [4]: u3 = u1/u2

In [5]: print(u3)
km.hr-1

In [6]: u4 = Units('m/s')

In [7]: u3.conversion(u4) # OK: can convert from km/hr to m/s
Out[7]: 0.2777777777777778

In [8]: u3.conversion(u2) # Oops: can't convert from km/hr to m!
...
UnitsError: Failure in units conversion: units km.hr-1[L.T-1] and hr[T] have
different dimensions

For more information and examples, see http://christianhill.co.uk/projects/pyqn
File renamed without changes.
37 changes: 17 additions & 20 deletions atom_unit.py → pyqn/atom_unit.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# atom_unit.py
# A class for, and attributes used in the parsing of a single "atom unit"
# identified by a base unit, SI-prefix and exponent, such as 'kg', 's-1',
Expand Down Expand Up @@ -26,20 +25,20 @@
import sys
from pyparsing import Word, Group, Literal, Suppress, ParseException, oneOf,\
Optional
from si import si_prefixes
from base_unit import BaseUnit, base_unit_stems
from dimensions import Dimensions
from .si import si_prefixes
from .base_unit import BaseUnit, base_unit_stems
from .dimensions import Dimensions

# pyparsing stuff for parsing unit strings:
caps = u'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
caps = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
lowers = caps.lower()
letters = u'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0'
digits = u'123456789'
letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0'
digits = '123456789'
exponent = Word(digits + '-')
prefix = oneOf(si_prefixes.keys())
ustem = Word(letters + u'Å' + u'Ω')
uatom = (Group( u'1' | (Optional(prefix) + ustem)) + Optional(exponent))\
| (Group( u'1' | ustem) + Optional(exponent))
prefix = oneOf(list(si_prefixes.keys()))
ustem = Word(letters + 'Å' + 'Ω')
uatom = (Group( '1' | (Optional(prefix) + ustem)) + Optional(exponent))\
| (Group( '1' | ustem) + Optional(exponent))

# floating point equality and its negation, to some suitable tolerance
def feq(f1, f2, tol=1.e-10):
Expand Down Expand Up @@ -79,7 +78,7 @@ def __init__(self, prefix, base_unit, exponent=1):
try:
self.si_prefix = si_prefixes[prefix]
except KeyError:
raise UnitsError(u'Invalid or unsupported SI prefix: %s'
raise UnitsError('Invalid or unsupported SI prefix: %s'
% prefix)
self.si_fac = self.si_prefix.fac
# now calculate the factor relating this AtomUnit to its
Expand All @@ -98,7 +97,7 @@ def parse(self, s_unit_atom):
uatom_data = uatom.parseString(s_unit_atom)
except ParseException:
raise
raise UnitsError(u'Invalid unit atom syntax: %s' % s_unit_atom)
raise UnitsError('Invalid unit atom syntax: %s' % s_unit_atom)

# uatom_data comes back as (([prefix], <stem>), [exponent])
if len(uatom_data[0]) == 1:
Expand Down Expand Up @@ -128,17 +127,15 @@ def __pow__(self, power):
""" Return the current AtomUnit raised to a specified power. """
return AtomUnit(self.prefix, self.base_unit, self.exponent * power)

def __unicode__(self):
def __str__(self):
""" String representation of this AtomUnit. """
s = u''
s = ''
if self.prefix:
s = self.prefix
s_exponent = u''
s_exponent = ''
if self.exponent != 1:
s_exponent = unicode(self.exponent)
return ''.join([s, unicode(self.base_unit), s_exponent])
def __str__(self):
return unicode(self).encode('utf-8')
s_exponent = str(self.exponent)
return ''.join([s, str(self.base_unit), s_exponent])

def prefix_base_eq(self, other):
"""
Expand Down
17 changes: 9 additions & 8 deletions base_unit.py → pyqn/base_unit.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
# -*- coding: utf-8 -*-
# base_unit.py
# A class representing a "base unit", identified as a single unit with no
# prefix or exponent, such as 'g', 'hr', 'bar', 's'.
#
# Copyright (C) 2012-2015 Christian Hill
# Copyright (C) 2012-2016 Christian Hill
# Department of Physics and Astronomy, University College London
# [email protected]
#
Expand All @@ -22,7 +21,7 @@
# You should have received a copy of the GNU General Public License
# along with PyQn. If not, see <http://www.gnu.org/licenses/>

from dimensions import *
from .dimensions import *

class BaseUnit(object):
"""
Expand Down Expand Up @@ -83,7 +82,7 @@ def __str__(self):
BaseUnit('Pa', 'pascal', 'pressure', 1., '', 'Pa', d_pressure),
BaseUnit('C', 'coulomb', 'charge', 1., '', 'C', d_charge),
BaseUnit('V', 'volt', 'voltage', 1., '', 'V', d_voltage),
BaseUnit(u'Ω', 'ohm', 'electric resistance', 1, '', r'\Omega',
BaseUnit('Ω', 'ohm', 'electric resistance', 1, '', r'\Omega',
d_voltage / d_current),
BaseUnit('F', 'farad', 'capacitance', 1., '', 'F', d_charge / d_voltage),
BaseUnit('Wb', 'weber', 'magnetic flux', 1, '', 'Wb', d_magnetic_flux),
Expand Down Expand Up @@ -137,15 +136,15 @@ def __str__(self):
]),

('Non-SI mass units', [
BaseUnit('u', 'atomic mass unit', 'mass', 1.660538921e-27, '', 'u', d_mass),
BaseUnit('amu', 'atomic mass unit', 'mass', 1.660538921e-27, '', 'amu', d_mass),
BaseUnit('u', 'atomic mass unit', 'mass', 1.660538921e-27, '', '', d_mass),
BaseUnit('amu', 'atomic mass unit', 'mass', 1.660538921e-27, '', 'am', d_mass),
BaseUnit('Da', 'dalton', 'mass', 1.660538921e-27, '', 'Da', d_mass),
BaseUnit('m_e', 'electron mass', 'mass', 9.10938291e-31, '', 'm_e', d_mass),
]),

('Non-SI units of length, area and volume', [
# Non-SI length units
BaseUnit(u'Å', 'angstrom', 'length', 1.e-10, '', '\AA', d_length),
BaseUnit('Å', 'angstrom', 'length', 1.e-10, '', '\AA', d_length),
BaseUnit('a0', 'bohr', 'length', 5.2917721092e-11, '', 'a_0', d_length),
# Non-SI area units
BaseUnit('b', 'barn', 'area', 1.e-28, '', 'b', d_area),
Expand Down Expand Up @@ -200,7 +199,9 @@ def __str__(self):

('Miscellaneous units', [
BaseUnit('Td', 'townsend', 'reduced electric field', 1.e-21, '', 'Td',
d_voltage * d_area)
d_voltage * d_area),
BaseUnit('Jy', 'jansky', 'spectral flux density', 1.e-26, '', 'Jy',
d_energy / d_area) # W.m-2.s-1
]),
)

Expand Down
7 changes: 3 additions & 4 deletions dimensions.py → pyqn/dimensions.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
# -*- coding: utf-8 -*-
# dimensions.py
# A class representing the dimensions of a physical quantity's units, in
# terms of powers of length (L), mass (M), time (T), temperature (Theta),
# amount of substance (Q), current (C) and luminous intensity (I).
#
# Copyright (C) 2012 Christian Hill
# Copyright (C) 2012-2016 Christian Hill
# Department of Physics and Astronomy, University College London
# [email protected]
#
Expand Down Expand Up @@ -40,7 +39,7 @@ def __init__(self, dims=None, **kwargs):
if not kwargs:
self.dims = dims
else:
print 'bad initialisation of Dimensions object'
print('bad initialisation of Dimensions object')
sys.exit(1)
else:
# initialize by keyword arguments
Expand All @@ -53,7 +52,7 @@ def __mul__(self, other):
new_dims.append(dim + other.dims[i])
return Dimensions(tuple(new_dims))

def __div__(self, other):
def __truediv__(self, other):
new_dims = []
for i, dim in enumerate(self.dims):
new_dims.append(dim - other.dims[i])
Expand Down
14 changes: 6 additions & 8 deletions list_base_units.py → pyqn/list_base_units.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
# list_base_units.py
from __future__ import print_function
import sys
from base_unit import base_units

Expand All @@ -12,21 +10,21 @@ def list_units_text():
print('{0:6s} {1:30s} {2:20s}'.format('Unit', 'Name', 'Dimensions'))
print('{0:6s} {1:30s} {2:20s}'.format('----', '----', '----------'))
for base_unit in base_unit_group:
print(u'{0:6s} {1:30s} {2:20s}'.format(unicode(base_unit.stem),
print('{0:6s} {1:30s} {2:20s}'.format(unicode(base_unit.stem),
base_unit.name, base_unit.dims))

def list_units_html():
for group_name, base_unit_group in base_units:
print(u'<h4>{0}</h4>'.format(group_name))
print(u'<table class="my-table">')
print(u'<tr><th>Unit</th><th>Name</th><th>Dimensions</th></tr>')
print('<h4>{0}</h4>'.format(group_name))
print('<table class="my-table">')
print('<tr><th>Unit</th><th>Name</th><th>Dimensions</th></tr>')
for base_unit in base_unit_group:
# NB we need to encode('utf-8') to pipe in that encoding to the
# shell, e.g. to create a file
print(u'<tr><td>{0:s}</td><td>{1:s}</td><td>{2:s}</td><td></tr>'
print('<tr><td>{0:s}</td><td>{1:s}</td><td>{2:s}</td><td></tr>'
.format(unicode(base_unit.stem), base_unit.name,
base_unit.dims).encode('utf-8'))
print(u'</table>')
print('</table>')


try:
Expand Down
9 changes: 4 additions & 5 deletions quantity.py → pyqn/quantity.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
# quantity.py
# A class representing physical quantity, with name, units and uncertainty.
#
# Copyright (C) 2012 Christian Hill
# Copyright (C) 2012-2016 Christian Hill
# Department of Physics and Astronomy, University College London
# [email protected]
#
Expand All @@ -23,8 +22,8 @@

import re
import math
from symbol import Symbol
from units import Units, UnitsError
from .symbol import Symbol
from .units import Units, UnitsError

class QuantityError(Exception):
"""
Expand Down Expand Up @@ -231,7 +230,7 @@ def __mul__(self, other):
units = self.units * other.units
return Quantity(value=value, units=units, sd=sd)

def __div__(self, other):
def __truediv__(self, other):
"""
Divide this quantity by a number or another quantity. Errors are
propagated, but assumed to be uncorrelated.
Expand Down
41 changes: 20 additions & 21 deletions si.py → pyqn/si.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
# -*- coding: utf-8 -*-
# si.py
# A class representing the SI prefixes (SIPrefix) and a list of the SI
# base units (si_unit_stems): length (L), mass (M), time (T), temperature
# (Theta), amount of substance (Q), current (C) and luminous intensity (I).
#
# Copyright (C) 2012 Christian Hill
# Copyright (C) 2012-2016 Christian Hill
# Department of Physics and Astronomy, University College London
# [email protected]
#
Expand Down Expand Up @@ -32,27 +31,27 @@ def __init__(self, prefix, name, power):
self.fac = 10**power

# Here are the SI prefixes that we recognise.
si_prefixes = { u'y': SIPrefix(u'y', 'yocto', -24),
u'z': SIPrefix(u'z', 'zepto', -21),
u'a': SIPrefix(u'a', 'atto', -18),
u'f': SIPrefix(u'f', 'femto', -15),
u'p': SIPrefix(u'p', 'pico', -12),
u'n': SIPrefix(u'n', 'nano', -9),
u'μ': SIPrefix(u'μ', 'micro', -6),
u'm': SIPrefix(u'm', 'milli', -3),
u'c': SIPrefix(u'c', 'centi', -2),
u'd': SIPrefix(u'd', 'deci', -1),
u'k': SIPrefix(u'k', 'kilo', 3),
u'M': SIPrefix(u'M', 'mega', 6),
u'G': SIPrefix(u'G', 'giga', 9),
u'T': SIPrefix(u'T', 'tera', 12),
u'P': SIPrefix(u'P', 'peta', 15),
u'E': SIPrefix(u'E', 'exa', 18),
u'Z': SIPrefix(u'Z', 'zetta', 21),
u'Y': SIPrefix(u'Y', 'yotta', 24),
si_prefixes = { 'y': SIPrefix('y', 'yocto', -24),
'z': SIPrefix('z', 'zepto', -21),
'a': SIPrefix('a', 'atto', -18),
'f': SIPrefix('f', 'femto', -15),
'p': SIPrefix('p', 'pico', -12),
'n': SIPrefix('n', 'nano', -9),
'μ': SIPrefix('μ', 'micro', -6),
'm': SIPrefix('m', 'milli', -3),
'c': SIPrefix('c', 'centi', -2),
'd': SIPrefix('d', 'deci', -1),
'k': SIPrefix('k', 'kilo', 3),
'M': SIPrefix('M', 'mega', 6),
'G': SIPrefix('G', 'giga', 9),
'T': SIPrefix('T', 'tera', 12),
'P': SIPrefix('P', 'peta', 15),
'E': SIPrefix('E', 'exa', 18),
'Z': SIPrefix('Z', 'zetta', 21),
'Y': SIPrefix('Y', 'yotta', 24),
}

# The base SI unit stems for length, time, mass, amount of substance,
# thermodynamic temperature, luminous intenstiy and current respectively:
si_unit_stems = (u'm', u's', u'g', u'mol', u'K', u'cd', u'A')
si_unit_stems = ('m', 's', 'g', 'mol', 'K', 'cd', 'A')

3 changes: 1 addition & 2 deletions symbol.py → pyqn/symbol.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
# -*- coding: utf-8 -*-
# symbol.py
# A class representing a symbol (perhaps the label for a physical quantity
# represented as a Quantity object) with a name in text, LaTeX and HTML.
#
# Copyright (C) 2012 Christian Hill
# Copyright (C) 2012-2016 Christian Hill
# Department of Physics and Astronomy, University College London
# [email protected]
#
Expand Down
12 changes: 7 additions & 5 deletions test_units.py → pyqn/tests/test_units.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# -*- coding: utf-8 -*-
# test_units.py
# Version 0.1a
#
# Copyright (C) 2012-2016 Christian Hill
#
# Version 1.0
# Unit tests for the Units class.

import unittest
from units import Units
from ..units import Units

class UnitsCheck(unittest.TestCase):
"""Unit tests for the Units class."""
Expand All @@ -27,8 +29,8 @@ def test_units_algebra(self):
self.assertFalse(u3_over_u4.has_units())

def test_unicode_units(self):
u1 = Units(u'kΩ')
self.assertEqual(unicode(u1), u'kΩ')
u1 = Units('kΩ')
self.assertEqual(str(u1), 'kΩ')

if __name__ == '__main__':
unittest.main()
14 changes: 8 additions & 6 deletions test_units_collisions.py → pyqn/tests/test_units_collisions.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
#! -*- coding: utf-8 -*-
# test_units_collisions.py
# Version 0.1a
#
# Copyright (C) 2012-2016 Christian Hill
#
# Version 1.0
# Check for conflicts amongst derived and base units. For example, inches
# can't be called "in" because the milli-inch, "min" conflicts with the use
# of "min" for minutes.

import unittest
from units import Units
from base_unit import base_units
from si import si_prefixes
from ..units import Units
from ..base_unit import base_units
from ..si import si_prefixes

class ConflictsCheck(unittest.TestCase):
"""
Expand All @@ -18,10 +20,10 @@ class ConflictsCheck(unittest.TestCase):
"""

def test_units_conflicts(self):
prefixes = ['']; prefixes.extend(si_prefixes)
seen_units = []
for base_unit_group in base_units:
for base_unit in base_unit_group[1]:
prefixes = ['']; prefixes.extend(si_prefixes)
for si_prefix in prefixes:
this_unit = '%s%s' % (si_prefix, base_unit.stem)
self.assertEqual(this_unit in seen_units, False,
Expand Down
Loading

0 comments on commit c058786

Please sign in to comment.