diff --git a/docs/_static/custom.css b/docs/_static/custom.css index 76574a6..2909810 100644 --- a/docs/_static/custom.css +++ b/docs/_static/custom.css @@ -1,3 +1,11 @@ .wy-nav-content { max-width: 100% !important; } + + +/* To change the left bar of executable cells in markdown and executable cells in jupyter notebooks */ +/* See https://github.com/executablebooks/meta/discussions/667 */ +div.cell div.cell_input { + border-left-color: #343131; +} + diff --git a/docs/contents/about/showcase/Quick_Guide.ipynb b/docs/contents/about/showcase/Quick_Guide.ipynb index 30b8e5a..1cfcaf9 100644 --- a/docs/contents/about/showcase/Quick_Guide.ipynb +++ b/docs/contents/about/showcase/Quick_Guide.ipynb @@ -68,6 +68,26 @@ "puw.configure.get_default_form()" ] }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'pint'" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "puw.configure.get_default_parser()" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -79,7 +99,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -88,7 +108,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -103,7 +123,7 @@ "2.5 " ] }, - "execution_count": 6, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -121,7 +141,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -130,7 +150,7 @@ "True" ] }, - "execution_count": 7, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -141,7 +161,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -150,7 +170,7 @@ "'pint'" ] }, - "execution_count": 8, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -168,7 +188,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -177,7 +197,7 @@ "2.5" ] }, - "execution_count": 9, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -188,7 +208,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -203,7 +223,7 @@ "" ] }, - "execution_count": 10, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } @@ -221,7 +241,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -230,7 +250,7 @@ "{'[L]': 1, '[M]': 0, '[T]': -1, '[K]': 0, '[mol]': 0, '[A]': 0, '[Cd]': 0}" ] }, - "execution_count": 11, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } @@ -248,7 +268,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -257,7 +277,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -266,7 +286,7 @@ "'openmm.unit'" ] }, - "execution_count": 13, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } @@ -277,7 +297,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -286,7 +306,7 @@ "Quantity(value=2.5, unit=nanometer/picosecond)" ] }, - "execution_count": 14, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -304,7 +324,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -329,7 +349,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -338,7 +358,7 @@ "{'[L]': 1.0, '[M]': 0, '[T]': -1.0, '[K]': 0, '[mol]': 0, '[A]': 0, '[Cd]': 0}" ] }, - "execution_count": 16, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } @@ -356,7 +376,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -365,7 +385,7 @@ "True" ] }, - "execution_count": 17, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } @@ -383,7 +403,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -392,7 +412,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -401,7 +421,7 @@ "Unit({BaseUnit(base_dim=BaseDimension(\"amount\"), name=\"mole\", symbol=\"mol\"): -1.0, ScaledUnit(factor=1000.0, master=meter*newton, name='kilojoule', symbol='kJ'): 1.0})" ] }, - "execution_count": 19, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } @@ -419,7 +439,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -428,7 +448,7 @@ "'openmm.unit'" ] }, - "execution_count": 20, + "execution_count": 21, "metadata": {}, "output_type": "execute_result" } @@ -439,7 +459,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 22, "metadata": {}, "outputs": [ { @@ -448,7 +468,7 @@ "True" ] }, - "execution_count": 21, + "execution_count": 22, "metadata": {}, "output_type": "execute_result" } @@ -466,7 +486,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 23, "metadata": {}, "outputs": [ { @@ -481,7 +501,7 @@ " '[Cd]': 0}" ] }, - "execution_count": 22, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" } @@ -499,7 +519,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 24, "metadata": {}, "outputs": [ { @@ -508,7 +528,7 @@ "'kilojoule/mole'" ] }, - "execution_count": 23, + "execution_count": 24, "metadata": {}, "output_type": "execute_result" } @@ -526,7 +546,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 25, "metadata": {}, "outputs": [], "source": [ @@ -535,7 +555,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 26, "metadata": {}, "outputs": [ { @@ -544,7 +564,7 @@ "Quantity(value=1.75, unit=newton/(nanometer**2))" ] }, - "execution_count": 25, + "execution_count": 26, "metadata": {}, "output_type": "execute_result" } @@ -562,7 +582,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -571,7 +591,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 28, "metadata": {}, "outputs": [ { @@ -586,7 +606,7 @@ "array([0., 0., 0.]) " ] }, - "execution_count": 27, + "execution_count": 28, "metadata": {}, "output_type": "execute_result" } @@ -604,7 +624,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 29, "metadata": {}, "outputs": [ { @@ -613,7 +633,7 @@ "'[0.0 0.0 0.0] nanometer'" ] }, - "execution_count": 28, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } @@ -631,7 +651,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 30, "metadata": {}, "outputs": [], "source": [ @@ -640,7 +660,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 31, "metadata": {}, "outputs": [ { @@ -655,7 +675,7 @@ "" ] }, - "execution_count": 30, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" } @@ -666,7 +686,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 32, "metadata": {}, "outputs": [ { @@ -675,7 +695,7 @@ "'kelvin'" ] }, - "execution_count": 31, + "execution_count": 32, "metadata": {}, "output_type": "execute_result" } @@ -700,7 +720,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 33, "metadata": {}, "outputs": [ { @@ -709,7 +729,7 @@ "['pint', 'openmm.unit']" ] }, - "execution_count": 32, + "execution_count": 33, "metadata": {}, "output_type": "execute_result" } @@ -720,7 +740,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 34, "metadata": {}, "outputs": [ { @@ -729,7 +749,7 @@ "'pint'" ] }, - "execution_count": 33, + "execution_count": 34, "metadata": {}, "output_type": "execute_result" } @@ -747,7 +767,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 35, "metadata": {}, "outputs": [ { @@ -776,7 +796,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 36, "metadata": {}, "outputs": [], "source": [ @@ -785,7 +805,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 37, "metadata": {}, "outputs": [ { @@ -817,7 +837,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 38, "metadata": {}, "outputs": [], "source": [ @@ -834,7 +854,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 39, "metadata": {}, "outputs": [ { @@ -870,7 +890,7 @@ " '[Cd]': 0}}" ] }, - "execution_count": 38, + "execution_count": 39, "metadata": {}, "output_type": "execute_result" } @@ -881,7 +901,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 40, "metadata": {}, "outputs": [ { @@ -890,7 +910,7 @@ "'pint'" ] }, - "execution_count": 39, + "execution_count": 40, "metadata": {}, "output_type": "execute_result" } @@ -908,7 +928,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 41, "metadata": {}, "outputs": [], "source": [ @@ -917,7 +937,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 42, "metadata": {}, "outputs": [ { @@ -942,7 +962,7 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 43, "metadata": {}, "outputs": [ { @@ -967,7 +987,7 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 44, "metadata": {}, "outputs": [ { @@ -990,15 +1010,6 @@ "As you noticed, we have mention that `pyunitwizard.get_standard` and `pyunitwizard.standardize` results with the compatible default standard units. This is combination of standard units are also considered:" ] }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": {}, - "outputs": [], - "source": [ - "q = puw.quantity('100 angstroms**3')" - ] - }, { "cell_type": "code", "execution_count": 45, @@ -1105,7 +1116,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.10" + "version": "3.7.12" } }, "nbformat": 4, diff --git a/pyunitwizard/_private_tools/exceptions.py b/pyunitwizard/_private_tools/exceptions.py index c40ed24..db1d617 100644 --- a/pyunitwizard/_private_tools/exceptions.py +++ b/pyunitwizard/_private_tools/exceptions.py @@ -18,7 +18,7 @@ class NotImplementedMethodError(NotImplementedError): Examples -------- - >>> from pyuniwizard._private_tools.exceptions import NotImplementedMethodError + >>> from pyunitwizard._private_tools.exceptions import NotImplementedMethodError >>> def method_name(a, b=True): ... raise NotImplementedMethodError ... pass @@ -32,7 +32,7 @@ class NotImplementedMethodError(NotImplementedError): def __init__(self): - from pyuniwizard import __github_issues_web__ + from pyunitwizard import __github_issues_web__ from inspect import stack all_stack_frames = stack() @@ -70,7 +70,7 @@ class NotImplementedClassError(NotImplementedError): Examples -------- - >>> from pyuniwizard._private_tools.exceptions import NotImplementedClassError + >>> from pyunitwizard._private_tools.exceptions import NotImplementedClassError >>> class ClassName(): ... def __init__(self): ... raise NotImplementedClassError @@ -85,7 +85,7 @@ class NotImplementedClassError(NotImplementedError): def __init__(self): - from pyuniwizard import __github_issues_web__ + from pyunitwizard import __github_issues_web__ from inspect import stack all_stack_frames = stack() @@ -102,6 +102,57 @@ def __init__(self): super().__init__(message) + +class NotImplementedParsingError(NotImplementedError): + """Exception raised when parsing a string with the parser and to the form choosen has not been + implemented yet. + + This exception is raised when parsing a strint with the parser and to the form choosen by the + user, has not been implemented yet. + + Parameters + ---------- + parser : str + The name of the parser choosen by the user. + to_form : str + The name of target form choosen by the user. + + Raises + ------ + NotImplementedMethodError + A message is printed out with the link to the documentation section about parsing strings, + and the link to the issues board of PyUnitWizard's GitHub repository. + + Examples + -------- + >>> from pyunitwizard._private_tools.exceptions import NotImplementedParsingError + >>> def method_name(string, parser='pint', to_form='new_form'): + ... if parser=='pint' and to_form='new_form': + ... raise NotImplementedParsingError('pint', 'new_form') + ... pass + + .. admonition:: See Also + :class: attention + + :ref:`Developer Guide \> Exceptions \> NotImplementedParsingError ` + + """ + + def __init__(self, parser, to_form): + + from pyunitwizard import __github_issues_web__ + + api_doc = '' + + message = ( + f"Parsing a string with the \"{parser}\" parser to the form \"{to_form}\" has not been implemented yet. " + f"Check {api_doc} for more information. " + f"If you still want to suggest its implementation, open a new issue in {__github_issues_web__}" + ) + + super().__init__(message) + + class BadCallError(ValueError): """Exception raised when a method, or a class, was not properly called or instantiated. @@ -121,7 +172,7 @@ class BadCallError(ValueError): Examples -------- - >>> from pyuniwizard._private_tools.exceptions import BadCallError + >>> from pyunitwizard._private_tools.exceptions import BadCallError >>> def method_name(item, a=True): ... if type(a) not in [int, float]: ... raise BadCallError('a') @@ -136,7 +187,7 @@ class BadCallError(ValueError): def __init__(self, argument=None): - from pyuniwizard import __github_issues_web__ + from pyunitwizard import __github_issues_web__ from inspect import stack all_stack_frames = stack() @@ -186,7 +237,7 @@ class NoStandardError(ValueError): def __init__(self): - from pyuniwizard import __github_issues_web__ + from pyunitwizard import __github_issues_web__ from inspect import stack all_stack_frames = stack() @@ -313,5 +364,62 @@ def __init__(self, library): super().__init__(message) +class LibraryWithoutParserError(NotImplementedError): + """Exception raised when a library can not parse strings. + + Some libraries cannot convert strings to quantities. This error will be raised by the API methods + `string_to_quantity` and `string_to_unit` of those libraries without strings' parser. + + Parameters + ---------- + argument : str + The name of the library without parser. + + Raises + ------ + LibraryWithoutParserError + A message is printed out with the name of the library wihtout parser, the link to the + section in the documentation regarding parser regarding parsers, and the link to the + issues board of PytUnitWizard's GitHub repository. + + Examples + -------- + >>> import pyunitwizard as puw + >>> from pyunitwizard._private_tools.exceptions import LibraryWithoutParserError + >>> from pyunitwizard._private_tools.exceptions import LibraryWithoutParserError + >>> def method_name(item, argument='pint'): + ... if argument == 'pint': + ... try: + ... import pint + ... except: + ... raise LibraryNotFoundError('pint') + ... pass + + .. admonition:: See Also + :class: attention + + :ref:`Developer Guide \> Exceptions \> LibraryWithoutStringsParserError ` + + """ + + def __init__(self, library): + + from pyunitwizard import __github_issues_web__ + from inspect import stack + + all_stack_frames = stack() + caller_stack_frame = all_stack_frames[1] + caller_name = caller_stack_frame[3] + + api_doc = '' + + message = ( + f"The python library {library} was not found. " + f"Although {library} is not considered a dependency, it needs " + f"to be installed to execute the {caller_name} method the way you require. " + f"If you still need help, open a new issue in {__github_issues_web__}." + ) + + super().__init__(message) diff --git a/pyunitwizard/_private_tools/forms.py b/pyunitwizard/_private_tools/forms.py index 09ef6c2..bbc9ef0 100644 --- a/pyunitwizard/_private_tools/forms.py +++ b/pyunitwizard/_private_tools/forms.py @@ -17,12 +17,16 @@ def digest_form(form): return output -def digest_to_form(to_form): +def digest_to_form(to_form, from_form=None): output = None if to_form is not None: output = digest_form(to_form) + else: + if from_form == 'string': + from_form = None + output = digest_form(from_form) return output diff --git a/pyunitwizard/_private_tools/parsers.py b/pyunitwizard/_private_tools/parsers.py index 1f74d81..8a48c74 100644 --- a/pyunitwizard/_private_tools/parsers.py +++ b/pyunitwizard/_private_tools/parsers.py @@ -9,6 +9,9 @@ def digest_parser(parser): output = parser.lower() else: raise ValueError + else: + from pyunitwizard.kernel import default_parser + output = default_parser return output diff --git a/pyunitwizard/forms/__init__.py b/pyunitwizard/forms/__init__.py index 6652af2..b6cff57 100644 --- a/pyunitwizard/forms/__init__.py +++ b/pyunitwizard/forms/__init__.py @@ -9,7 +9,6 @@ dict_translate={} dict_convert={} dict_string_to_quantity={} -dict_string_to_unit={} dict_to_string={} dict_dimensionality={} dict_compatibility={} @@ -32,7 +31,7 @@ def load_library(library): dict_convert[library] = api.convert dict_translate[library]={} dict_string_to_quantity[library] = api.string_to_quantity - dict_string_to_unit[library] = api.string_to_unit + dict_to_string[library] = api.to_string dict_dimensionality[library] = api.dimensionality dict_compatibility[library] = api.compatibility @@ -76,7 +75,7 @@ def load_library(library): dict_convert['string'] = api.convert dict_translate['string']={} dict_string_to_quantity['string'] = api.string_to_quantity -dict_string_to_unit['string'] = api.string_to_unit +dict_to_string['string'] = api.to_string dict_dimensionality['string'] = api.dimensionality dict_compatibility['string'] = api.compatibility diff --git a/pyunitwizard/forms/api_openmm_unit.py b/pyunitwizard/forms/api_openmm_unit.py index 2bddac1..5c60bb5 100644 --- a/pyunitwizard/forms/api_openmm_unit.py +++ b/pyunitwizard/forms/api_openmm_unit.py @@ -13,12 +13,6 @@ openmm_unit.Unit:form_name, } -try: - from pint.util import ParserHelper as PintParserHelper - from .api_pint import string_to_quantity as string_to_pint_quantity -except: - PintParserHelper=None - def is_quantity(quantity_or_unit): output = (type(quantity_or_unit)==openmm_unit.Quantity) @@ -77,36 +71,20 @@ def compatibility(quantity_or_unit_1, quantity_or_unit_2): return tmp_unit_1.is_compatible(tmp_unit_2) -def make_quantity(value, unit_name): +def make_quantity(value, unit): - unit=string_to_unit(unit_name) return openmm_unit.Quantity(value, unit) def string_to_quantity(string): - if PintParserHelper is None: - raise PintNotFoundError() - - pint_quantity = string_to_pint_quantity(string) - parser = PintParserHelper.from_string(pint_quantity.__str__()) - tmp_quantity = parser.scale - for unit_name, exponent in parser.items(): - tmp_quantity *= getattr(openmm_unit, unit_name)**exponent - return tmp_quantity - -def string_to_unit(string): - - tmp_quantity = string_to_quantity(string) - tmp_unit = get_unit(tmp_quantity) - return tmp_unit + raise LibraryWithoutStringsParserError('openmm.unit') def to_string(quantity_or_unit): return quantity_or_unit.__str__() -def convert(quantity, unit_name): +def convert(quantity, unit): - unit = string_to_unit(unit_name) return quantity.in_units_of(unit) def get_value(quantity): diff --git a/pyunitwizard/forms/api_pint.py b/pyunitwizard/forms/api_pint.py index c427e47..8b289c6 100644 --- a/pyunitwizard/forms/api_pint.py +++ b/pyunitwizard/forms/api_pint.py @@ -84,26 +84,7 @@ def get_unit(quantity): def string_to_quantity(string): - if string.startswith('[') or string.startswith('('): - - import ast - - end_list = max(string.rfind(')')+1, string.rfind(']')+1) - value_string = string[:end_list] - unit_string = string[end_list:] - - tmp_quantity=ast.literal_eval(value_string)*Q_(unit_string) - - else: - - tmp_quantity=Q_(string) - - return tmp_quantity - -def string_to_unit(string): - - tmp_quantity = string_to_quantity(string) - return get_unit(tmp_quantity) + return Q_(string) def to_string(quantity_or_item): @@ -115,12 +96,20 @@ def convert(quantity, unit_name): def to_openmm_unit(quantity): - from .api_openmm_unit import make_quantity as make_openmm_unit_quantity + from pint.util import ParserHelper as PintParserHelper + try: + import openmm.unit as openmm_unit + except: + raise LibraryNotFoundError('openmm') - value = get_value(quantity) - unit_name = to_string(get_unit(quantity)) + pint_parser = PintParserHelper.from_string(quantity.__str__()) + tmp_quantity = pint_parser.scale + for unit_name, exponent in pint_parser.items(): + if unit_name in ['unified_atomic_mass_unit']: + unit_name = 'amu' + tmp_quantity *= getattr(openmm_unit, unit_name)**exponent - return make_openmm_unit_quantity(value, unit_name) + return tmp_quantity def to_unyt(quantity): diff --git a/pyunitwizard/forms/api_string.py b/pyunitwizard/forms/api_string.py index e8b270b..764da09 100644 --- a/pyunitwizard/forms/api_string.py +++ b/pyunitwizard/forms/api_string.py @@ -29,10 +29,10 @@ def is_unit(quantity_or_unit): def dimensionality(quantity_or_unit): from pyunitwizard.kernel import default_form, default_parser - from pyunitwizard import convert as _convert, dimensionality as _dimensionality + from pyunitwizard import convert as _convert, get_dimensionality as _get_dimensionality tmp_quantity_or_unit = _convert(quantity_or_unit, to_form=default_form, parser=default_parser) - output = _dimensionality(tmp_quantity_or_unit) + output = _get_dimensionality(tmp_quantity_or_unit) return output @@ -70,11 +70,12 @@ def get_value(quantity): def get_unit(quantity): - from pyunitwizard.kernel import default_form, default_parser + from pyunitwizard.kernel import default_parser from pyunitwizard import convert as _convert, get_unit as _get_unit - tmp_quantity_or_unit = _convert(quantity, to_form=default_form, parser=default_parser) - tmp_unit = _get_unit(tmp_quantity_or_unit, to_form='string', parser=default_parser) + tmp_quantity_or_unit = _convert(quantity, to_form=default_parser) + tmp_unit = _get_unit(tmp_quantity_or_unit) + tmp_unit = _convert(tmp_unit, to_form='string') return tmp_unit @@ -82,11 +83,6 @@ def string_to_quantity(string): return string -def string_to_unit(string): - - tmp_quantity = string_to_quantity(string) - return get_unit(tmp_quantity) - def to_string(quantity_or_item): return quantity_or_item @@ -97,7 +93,7 @@ def convert(quantity, unit_name): from pyunitwizard import convert as _convert, get_unit as _get_unit tmp_quantity_or_unit = _convert(quantity, to_form=default_form, parser=default_parser) - tmp_quantity_or_unit = _convert(tmp_quantity_or_unit, to_units=unit_name, parser=default_parser) + tmp_quantity_or_unit = _convert(tmp_quantity_or_unit, to_unit=unit_name, parser=default_parser) tmp_quantity_or_unit = _convert(tmp_quantity_or_unit, to_form='string') return tmp_quantity_or_unit diff --git a/pyunitwizard/forms/api_unyt.py b/pyunitwizard/forms/api_unyt.py index ec7140b..c4a25ed 100644 --- a/pyunitwizard/forms/api_unyt.py +++ b/pyunitwizard/forms/api_unyt.py @@ -44,10 +44,6 @@ def string_to_quantity(string): raise NotImplementedMethodError() -def string_to_unit(string): - - raise NotImplementedMethodError() - def to_string(unit): raise NotImplementedMethodError() diff --git a/pyunitwizard/forms/template_api_form.py b/pyunitwizard/forms/template_api_form.py index 54973cf..5880229 100644 --- a/pyunitwizard/forms/template_api_form.py +++ b/pyunitwizard/forms/template_api_form.py @@ -30,10 +30,6 @@ def string_to_quantity(string): raise NotImplementedError -def string_to_unit(string): - - raise NotImplementedError - def to_string(unit): raise NotImplementedError diff --git a/pyunitwizard/main.py b/pyunitwizard/main.py index 266dbab..2f4a83e 100644 --- a/pyunitwizard/main.py +++ b/pyunitwizard/main.py @@ -3,8 +3,9 @@ from ._private_tools.parsers import digest_parser from .forms import dict_is_form, dict_is_unit, dict_is_quantity, dict_dimensionality, dict_compatibility from .forms import dict_get_unit, dict_get_value, dict_make_quantity -from .forms import dict_convert, dict_translate, dict_string_to_quantity, dict_string_to_unit, dict_to_string +from .forms import dict_convert, dict_translate, dict_to_string from .import kernel +from .parse import parse as _parse import numpy as np def get_form(quantity_or_unit): @@ -184,179 +185,95 @@ def quantity(value, unit=None, form=None, parser=None): return output -def unit(unit_name, form=None, parser=None): +def unit(unit, form=None, parser=None): - return convert(unit_name, to_form=form, parser=parser, to_type='unit') + return convert(unit, to_form=form, parser=parser, to_type='unit') def convert(quantity_or_unit, to_unit=None, to_form=None, parser=None, to_type='quantity'): output = None form_in = get_form(quantity_or_unit) - to_form = digest_to_form(to_form) + to_form = digest_to_form(to_form, form_in) parser = digest_parser(parser) - if to_form is None: - if form_in == 'string': - to_form=kernel.default_form - else: - to_form=form_in - - if parser is None: - if form_in == 'string': - if to_form == 'string': - parser = kernel.default_parser - else: - parser = to_form - else: - if to_form is None: - parser = kernel.default_parser - else: - parser = form_in + if to_type not in ['unit', 'value', 'quantity']: + raise BadCallError("to_type") - output = quantity_or_unit + if type(to_unit) is str: + to_unit = _parse(to_unit, parser=parser, to_form=to_form) + to_unit = dict_get_unit[to_form](to_unit) - if to_type=='quantity': + if form_in=='string': - if form_in=='string': - if parser==to_form: - output = dict_translate[form_in][to_form](output) - if to_unit is not None: - output = dict_convert[to_form](output, to_unit) - else: - output = dict_translate[form_in][parser](output) - if to_unit is not None: - output = dict_convert[parser](output, to_unit) - output = dict_translate[parser][to_form](output) - else: - if form_in==to_form: - if to_unit is not None: - if parser==to_form: - output = dict_convert[form_in](output, to_unit) - else: - output = dict_translate[form_in][parser](output) - output = dict_convert[parser](output, to_unit) - output = dict_translate[parser][to_form](output) - elif form_in!=to_form: - if to_unit is not None: - if parser==form_in: - output = dict_convert[form_in](output, to_unit) - output = dict_translate[form_in][to_form](output) - elif parser==to_form: - output = dict_translate[form_in][to_form](output) - output = dict_convert[to_form](output, to_unit) - else: - output = dict_translate[form_in][parser](output) - output = dict_convert[parser](output, to_unit) - output = dict_translate[parser][to_form](output) - else: - output = dict_translate[form_in][to_form](output) + if to_form=='string': - elif to_type=='unit': + output = _parse(quantity_or_unit, parser=parser, to_form=parser) - if form_in=='string': - if parser==to_form: - if to_unit is None: - output = dict_string_to_unit[to_form](output) + if to_unit is not None: + output = dict_convert[parser](output, to_unit) + if to_type == 'unit': + if is_unit(output): + output=output else: - output = dict_string_to_quantity[to_form](output) - output = dict_convert[to_form](output, to_unit) - output = dict_get_unit[to_form](output) - else: - if to_unit is None: - output = dict_translate[form_in][parser](output) output = dict_get_unit[parser](output) - output = dict_translate[parser][to_form](output) - else: - output = dict_translate[form_in][parser](output) - output = dict_convert[parser](output, to_unit) - output = dict_get_unit[parser](output) - output = dict_translate[parser][to_form](output) + output = dict_to_string[parser](output) + elif to_type == 'value': + output = dict_get_value[parser](output) + output = str(output) + else: + output = dict_to_string[parser](output) + else: - if form_in==to_form: - if to_unit is not None: - if parser==to_form: - output = dict_convert[form_in](output, to_unit) - else: - output = dict_translate[form_in][parser](output) - output = dict_convert[parser](output, to_unit) - output = dict_translate[parser][to_form](output) - output = dict_get_unit[to_form](output) - elif form_in!=to_form: - if to_form == 'string': - if to_unit is not None: - if parser==form_in: - output = dict_convert[form_in](output, to_unit) - output = dict_get_unit[form_in](output) - output = dict_translate[form_in][to_form](output) - else: - output = dict_translate[form_in][parser](output) - output = dict_convert[parser](output, to_unit) - output = dict_get_unit[parser](output) - output = dict_translate[parser][to_form](output) - else: - output = dict_get_unit[form_in](output) - output = dict_translate[form_in][to_form](output) + + output = _parse(quantity_or_unit, parser=parser, to_form=to_form) + + if to_unit is not None: + output = dict_convert[to_form](output, to_unit) + if to_type == 'unit': + if is_unit(output): + output=output else: - if to_unit is not None: - if parser==form_in: - output = dict_convert[form_in](output, to_unit) - output = dict_translate[form_in][to_form](output) - elif parser==to_form: - output = dict_translate[form_in][to_form](output) - output = dict_convert[to_form](output, to_unit) - else: - output = dict_translate[form_in][parser](output) - output = dict_convert[parser](output, to_unit) - output = dict_translate[parser][to_form](output) - else: - output = dict_translate[form_in][to_form](output) output = dict_get_unit[to_form](output) + elif to_type == 'value': + output = dict_get_value[to_form](output) - elif to_type=='value': + else: - if form_in=='string': - if parser==to_form: - output = dict_string_to_quantity[to_form](output) - if to_unit is not None: - output = dict_convert[to_form](output, to_unit) - output = dict_get_value[to_form](output) + if to_form=='string': + + output = quantity_or_unit + + if to_unit is not None: + output = dict_convert[form_in](output, to_unit) + if to_type == 'unit': + if is_unit(output): + output=output + else: + output = dict_get_unit[form_in](output) + output = dict_to_string[form_in](output) + elif to_type == 'value': + output = dict_get_value[form_in](output) + output = str(output) else: - output = dict_translate[form_in][parser](output) - if to_unit is not None: - output = dict_convert[parser](output, to_unit) - output = dict_get_value[parser](output) + output = dict_to_string[form_in](output) + else: - if form_in==to_form: - if to_unit is not None: - if parser==to_form: - output = dict_convert[form_in](output, to_unit) - output = dict_get_value[form_in](output) - else: - output = dict_translate[form_in][parser](output) - output = dict_convert[parser](output, to_unit) - output = dict_get_value[parser](output) - else: - output = dict_get_value[form_in](output) - elif form_in!=to_form: - if to_unit is not None: - if parser==form_in: - output = dict_convert[form_in](output, to_unit) - output = dict_get_value[form_in](output) - elif parser==to_form: - output = dict_translate[form_in][to_form](output) - output = dict_convert[to_form](output, to_unit) - output = dict_get_value[to_form](output) - else: - output = dict_translate[form_in][parser](output) - output = dict_convert[parser](output, to_unit) - output = dict_get_value[parser](output) - else: - output = dict_get_value[form_in](output) - else: + if form_in==to_form: + output = quantity_or_unit + else: + output = dict_translate[form_in][to_form](quantity_or_unit) - raise BadCallError("to_type") + if to_unit is not None: + output = dict_convert[to_form](output, to_unit) + if to_type == 'unit': + if is_unit(output): + output=output + else: + output = dict_get_unit[to_form](output) + elif to_type == 'value': + output = dict_get_value[to_form](output) return output diff --git a/pyunitwizard/parse.py b/pyunitwizard/parse.py new file mode 100644 index 0000000..1e1102d --- /dev/null +++ b/pyunitwizard/parse.py @@ -0,0 +1,65 @@ +from ._private_tools.exceptions import * +from ._private_tools.forms import digest_to_form +from ._private_tools.parsers import digest_parser +from .forms import dict_string_to_quantity +from .forms import dict_to_string +from .forms import dict_translate + +def _parse_with_pint(string): + + if string.startswith('[') or string.startswith('('): + + import ast + + end_list = max(string.rfind(')')+1, string.rfind(']')+1) + value_string = string[:end_list] + unit_string = string[end_list:] + + output=ast.literal_eval(value_string)*dict_string_to_quantity['pint'](unit_string) + + else: + + output=dict_string_to_quantity['pint'](string) + + return output + +def parse(string, parser=None, to_form=None): + + if type(string) is not str: + raise BadCallError('string') + + parser = digest_parser(parser) + to_form = digest_to_form(to_form) + + output = None + + if parser == 'pint': + + if to_form == 'pint': + + output = _parse_with_pint(string) + + elif to_form == 'openmm.unit': + + pint_quantity = _parse_with_pint(string) + output = dict_translate['pint']['openmm.unit'](pint_quantity) + + elif to_form == 'string': + + pint_quantity = _parse_with_pint(string) + output = dict_to_string['pint'](pint_quantity) + + else: + + raise NotImplementedParsingError(parser, to_form) + + elif parser is 'openmm.unit': + + raise LibraryWithoutStringParserError('openmm.unit') + + else: + + raise NotImplementedParsingError(parser, to_form) + + return output + diff --git a/pyunitwizard/tests/openmm_unit/test_convert_with_openmm_unit.py b/pyunitwizard/tests/openmm_unit/test_convert_with_openmm_unit.py index 81c169f..a223b89 100644 --- a/pyunitwizard/tests/openmm_unit/test_convert_with_openmm_unit.py +++ b/pyunitwizard/tests/openmm_unit/test_convert_with_openmm_unit.py @@ -2,59 +2,60 @@ import pyunitwizard as puw import openmm.unit as openmm_unit -def test_from_string_1_a(): +def test_from_string_1(): puw.configure.reset() - puw.configure.load_library(['openmm.unit']) - q = puw.convert('1 meter') - q_true = 1*openmm_unit.meter - assert q == q_true + puw.configure.load_library(['openmm.unit', 'pint']) + q = puw.convert(1*openmm_unit.meter, to_form='pint') + q_form = puw.get_form(q) + assert q_form == 'pint' def test_from_string_2(): puw.configure.reset() - puw.configure.load_library(['openmm.unit']) - q = puw.convert('1 meter', to_unit='cm') + puw.configure.load_library(['openmm.unit', 'pint']) + puw.configure.set_default_parser('pint') + q = puw.convert(1*openmm_unit.meter, to_unit='cm') q_true = 100.0*openmm_unit.centimeter assert q == q_true def test_from_string_3(): puw.configure.reset() puw.configure.load_library(['openmm.unit']) - q = puw.convert('1 meter', to_type='unit') + q = puw.convert(1*openmm_unit.meter, to_type='unit') q_true = openmm_unit.meter assert q == q_true def test_from_string_4(): puw.configure.reset() puw.configure.load_library(['openmm.unit']) - q = puw.convert('1 meter', to_unit='cm', to_type='unit') + q = puw.convert(1*openmm_unit.meter, to_unit=openmm_unit.centimeter, to_type='unit') q_true = openmm_unit.centimeter assert q == q_true def test_from_string_5(): puw.configure.reset() puw.configure.load_library(['openmm.unit']) - q = puw.convert('1 meter', to_unit='cm', to_form='string') + q = puw.convert(1*openmm_unit.meter, to_unit=openmm_unit.centimeter, to_form='string') q_true = '100.0 cm' assert q == q_true def test_from_string_6(): puw.configure.reset() puw.configure.load_library(['openmm.unit']) - q = puw.convert('1 meter', to_unit='cm', to_form='string', to_type='unit') + q = puw.convert(1*openmm_unit.meter, to_unit=openmm_unit.centimeter, to_form='string', to_type='unit') q_true = 'centimeter' assert q == q_true def test_from_string_7(): puw.configure.reset() puw.configure.load_library(['openmm.unit']) - q = puw.convert('1 meter', to_type='value') + q = puw.convert(1*openmm_unit.meter, to_type='value') q_true = 1 assert q == q_true def test_from_string_8(): puw.configure.reset() puw.configure.load_library(['openmm.unit']) - q = puw.convert('1 meter', to_unit='cm', to_type='value') + q = puw.convert(1*openmm_unit.meter, to_unit=openmm_unit.centimeter, to_type='value') q_true = 100.0 assert q == q_true diff --git a/pyunitwizard/tests/openmm_unit/test_quantity_with_openmm_unit.py b/pyunitwizard/tests/openmm_unit/test_quantity_with_openmm_unit.py index 7226162..2a36aee 100644 --- a/pyunitwizard/tests/openmm_unit/test_quantity_with_openmm_unit.py +++ b/pyunitwizard/tests/openmm_unit/test_quantity_with_openmm_unit.py @@ -5,7 +5,7 @@ def test_1(): puw.configure.reset() puw.configure.load_library(['openmm.unit']) openmm_unit = puw.forms.api_openmm_unit.openmm_unit - q = puw.quantity(10, 'kJ/mol') + q = puw.quantity(10, openmm_unit.kilojoule/openmm_unit.mole) q_true = 10*openmm_unit.kilojoule/openmm_unit.mole assert puw.similarity(q, q_true) diff --git a/pyunitwizard/tests/openmm_unit/test_unit_with_openmm_unit.py b/pyunitwizard/tests/openmm_unit/test_unit_with_openmm_unit.py index 31dff0e..f45c2a4 100644 --- a/pyunitwizard/tests/openmm_unit/test_unit_with_openmm_unit.py +++ b/pyunitwizard/tests/openmm_unit/test_unit_with_openmm_unit.py @@ -5,7 +5,7 @@ def test_1(): puw.configure.reset() puw.configure.load_library(['openmm.unit']) openmm_unit = puw.forms.api_openmm_unit.openmm_unit - q = puw.unit('kJ/mol') + q = puw.unit(openmm_unit.kilojoule/openmm_unit.mole) q_true = openmm_unit.kilojoule/openmm_unit.mole assert q == q_true diff --git a/pyunitwizard/tests/test_configure.py b/pyunitwizard/tests/test_configure.py index 549837e..e666afb 100644 --- a/pyunitwizard/tests/test_configure.py +++ b/pyunitwizard/tests/test_configure.py @@ -23,8 +23,9 @@ def test_default_parser(): assert puw.configure.get_default_parser()=='pint' def test_init_openmolecularsystems(): - puw.configure.load_library(['openmm.unit']) + puw.configure.load_library(['pint','openmm.unit']) puw.configure.set_default_form('openmm.unit') + puw.configure.set_default_parser('pint') puw.configure.set_standard_units(['nm', 'ps', 'K', 'mole', 'amu', 'e', 'kJ/mol', 'kJ/(mol*nm**2)', 'N', 'degrees']) diff --git a/pyunitwizard/tests/test_similarity.py b/pyunitwizard/tests/test_similarity.py index 4f208f7..b155846 100644 --- a/pyunitwizard/tests/test_similarity.py +++ b/pyunitwizard/tests/test_similarity.py @@ -2,14 +2,15 @@ import pyunitwizard as puw def test_1(): - puw.configure.reset() - puw.configure.load_library(['openmm.unit']) - output = puw.similarity('1 meter', '1 meter') - assert output == True + from pyunitwizard._private_tools.exceptions import NotImplementedParsingError + with pytest.raises(NotImplementedParsingError): + puw.configure.reset() + puw.configure.load_library(['openmm.unit']) + output = puw.similarity('1 meter', '1 meter') def test_2(): puw.configure.reset() - puw.configure.load_library(['openmm.unit']) + puw.configure.load_library(['pint', 'openmm.unit']) output = puw.similarity('1 meter', '1 kilometer') assert output == False @@ -21,7 +22,7 @@ def test_3(): def test_4(): puw.configure.reset() - puw.configure.load_library(['openmm.unit']) + puw.configure.load_library(['pint', 'openmm.unit']) output = puw.similarity('1 meter', '100 cm') assert output == True @@ -49,5 +50,19 @@ def test_6(): output = puw.similarity(aa, bb) assert output == False +def test_7(): + puw.configure.reset() + puw.configure.load_library(['pint','openmm.unit']) + aa = puw.quantity(4,'mm') + bb = puw.quantity(0.4,'cm') + output = puw.similarity(aa, bb) + assert output == True + + + + + + +