import warnings from sympy.core.add import Add from sympy.core.function import (Function, diff) from sympy.core.numbers import (Number, Rational) from sympy.core.singleton import S from sympy.core.symbol import (Symbol, symbols) from sympy.functions.elementary.complexes import Abs from sympy.functions.elementary.exponential import (exp, log) from sympy.functions.elementary.miscellaneous import sqrt from sympy.functions.elementary.trigonometric import sin from sympy.integrals.integrals import integrate from sympy.physics.units import (amount_of_substance, area, convert_to, find_unit, volume, kilometer, joule, molar_gas_constant, vacuum_permittivity, elementary_charge, volt, ohm) from sympy.physics.units.definitions import (amu, au, centimeter, coulomb, day, foot, grams, hour, inch, kg, km, m, meter, millimeter, minute, quart, s, second, speed_of_light, bit, byte, kibibyte, mebibyte, gibibyte, tebibyte, pebibyte, exbibyte, kilogram, gravitational_constant, electron_rest_mass) from sympy.physics.units.definitions.dimension_definitions import ( Dimension, charge, length, time, temperature, pressure, energy, mass ) from sympy.physics.units.prefixes import PREFIXES, kilo from sympy.physics.units.quantities import PhysicalConstant, Quantity from sympy.physics.units.systems import SI from sympy.testing.pytest import raises k = PREFIXES["k"] def test_str_repr(): assert str(kg) == "kilogram" def test_eq(): # simple test assert 10*m == 10*m assert 10*m != 10*s def test_convert_to(): q = Quantity("q1") q.set_global_relative_scale_factor(S(5000), meter) assert q.convert_to(m) == 5000*m assert speed_of_light.convert_to(m / s) == 299792458 * m / s # TODO: eventually support this kind of conversion: # assert (2*speed_of_light).convert_to(m / s) == 2 * 299792458 * m / s assert day.convert_to(s) == 86400*s # Wrong dimension to convert: assert q.convert_to(s) == q assert speed_of_light.convert_to(m) == speed_of_light expr = joule*second conv = convert_to(expr, joule) assert conv == joule*second def test_Quantity_definition(): q = Quantity("s10", abbrev="sabbr") q.set_global_relative_scale_factor(10, second) u = Quantity("u", abbrev="dam") u.set_global_relative_scale_factor(10, meter) km = Quantity("km") km.set_global_relative_scale_factor(kilo, meter) v = Quantity("u") v.set_global_relative_scale_factor(5*kilo, meter) assert q.scale_factor == 10 assert q.dimension == time assert q.abbrev == Symbol("sabbr") assert u.dimension == length assert u.scale_factor == 10 assert u.abbrev == Symbol("dam") assert km.scale_factor == 1000 assert km.func(*km.args) == km assert km.func(*km.args).args == km.args assert v.dimension == length assert v.scale_factor == 5000 def test_abbrev(): u = Quantity("u") u.set_global_relative_scale_factor(S.One, meter) assert u.name == Symbol("u") assert u.abbrev == Symbol("u") u = Quantity("u", abbrev="om") u.set_global_relative_scale_factor(S(2), meter) assert u.name == Symbol("u") assert u.abbrev == Symbol("om") assert u.scale_factor == 2 assert isinstance(u.scale_factor, Number) u = Quantity("u", abbrev="ikm") u.set_global_relative_scale_factor(3*kilo, meter) assert u.abbrev == Symbol("ikm") assert u.scale_factor == 3000 def test_print(): u = Quantity("unitname", abbrev="dam") assert repr(u) == "unitname" assert str(u) == "unitname" def test_Quantity_eq(): u = Quantity("u", abbrev="dam") v = Quantity("v1") assert u != v v = Quantity("v2", abbrev="ds") assert u != v v = Quantity("v3", abbrev="dm") assert u != v def test_add_sub(): u = Quantity("u") v = Quantity("v") w = Quantity("w") u.set_global_relative_scale_factor(S(10), meter) v.set_global_relative_scale_factor(S(5), meter) w.set_global_relative_scale_factor(S(2), second) assert isinstance(u + v, Add) assert (u + v.convert_to(u)) == (1 + S.Half)*u # TODO: eventually add this: # assert (u + v).convert_to(u) == (1 + S.Half)*u assert isinstance(u - v, Add) assert (u - v.convert_to(u)) == S.Half*u # TODO: eventually add this: # assert (u - v).convert_to(u) == S.Half*u def test_quantity_abs(): v_w1 = Quantity('v_w1') v_w2 = Quantity('v_w2') v_w3 = Quantity('v_w3') v_w1.set_global_relative_scale_factor(1, meter/second) v_w2.set_global_relative_scale_factor(1, meter/second) v_w3.set_global_relative_scale_factor(1, meter/second) expr = v_w3 - Abs(v_w1 - v_w2) assert SI.get_dimensional_expr(v_w1) == (length/time).name Dq = Dimension(SI.get_dimensional_expr(expr)) assert SI.get_dimension_system().get_dimensional_dependencies(Dq) == { length: 1, time: -1, } assert meter == sqrt(meter**2) def test_check_unit_consistency(): u = Quantity("u") v = Quantity("v") w = Quantity("w") u.set_global_relative_scale_factor(S(10), meter) v.set_global_relative_scale_factor(S(5), meter) w.set_global_relative_scale_factor(S(2), second) def check_unit_consistency(expr): SI._collect_factor_and_dimension(expr) raises(ValueError, lambda: check_unit_consistency(u + w)) raises(ValueError, lambda: check_unit_consistency(u - w)) raises(ValueError, lambda: check_unit_consistency(u + 1)) raises(ValueError, lambda: check_unit_consistency(u - 1)) raises(ValueError, lambda: check_unit_consistency(1 - exp(u / w))) def test_mul_div(): u = Quantity("u") v = Quantity("v") t = Quantity("t") ut = Quantity("ut") v2 = Quantity("v") u.set_global_relative_scale_factor(S(10), meter) v.set_global_relative_scale_factor(S(5), meter) t.set_global_relative_scale_factor(S(2), second) ut.set_global_relative_scale_factor(S(20), meter*second) v2.set_global_relative_scale_factor(S(5), meter/second) assert 1 / u == u**(-1) assert u / 1 == u v1 = u / t v2 = v # Pow only supports structural equality: assert v1 != v2 assert v1 == v2.convert_to(v1) # TODO: decide whether to allow such expression in the future # (requires somehow manipulating the core). # assert u / Quantity('l2', dimension=length, scale_factor=2) == 5 assert u * 1 == u ut1 = u * t ut2 = ut # Mul only supports structural equality: assert ut1 != ut2 assert ut1 == ut2.convert_to(ut1) # Mul only supports structural equality: lp1 = Quantity("lp1") lp1.set_global_relative_scale_factor(S(2), 1/meter) assert u * lp1 != 20 assert u**0 == 1 assert u**1 == u # TODO: Pow only support structural equality: u2 = Quantity("u2") u3 = Quantity("u3") u2.set_global_relative_scale_factor(S(100), meter**2) u3.set_global_relative_scale_factor(Rational(1, 10), 1/meter) assert u ** 2 != u2 assert u ** -1 != u3 assert u ** 2 == u2.convert_to(u) assert u ** -1 == u3.convert_to(u) def test_units(): assert convert_to((5*m/s * day) / km, 1) == 432 assert convert_to(foot / meter, meter) == Rational(3048, 10000) # amu is a pure mass so mass/mass gives a number, not an amount (mol) # TODO: need better simplification routine: assert str(convert_to(grams/amu, grams).n(2)) == '6.0e+23' # Light from the sun needs about 8.3 minutes to reach earth t = (1*au / speed_of_light) / minute # TODO: need a better way to simplify expressions containing units: t = convert_to(convert_to(t, meter / minute), meter) assert t.simplify() == Rational(49865956897, 5995849160) # TODO: fix this, it should give `m` without `Abs` assert sqrt(m**2) == m assert (sqrt(m))**2 == m t = Symbol('t') assert integrate(t*m/s, (t, 1*s, 5*s)) == 12*m*s assert (t * m/s).integrate((t, 1*s, 5*s)) == 12*m*s def test_issue_quart(): assert convert_to(4 * quart / inch ** 3, meter) == 231 assert convert_to(4 * quart / inch ** 3, millimeter) == 231 def test_electron_rest_mass(): assert convert_to(electron_rest_mass, kilogram) == 9.1093837015e-31*kilogram assert convert_to(electron_rest_mass, grams) == 9.1093837015e-28*grams def test_issue_5565(): assert (m < s).is_Relational def test_find_unit(): assert find_unit('coulomb') == ['coulomb', 'coulombs', 'coulomb_constant'] assert find_unit(coulomb) == ['C', 'coulomb', 'coulombs', 'planck_charge', 'elementary_charge'] assert find_unit(charge) == ['C', 'coulomb', 'coulombs', 'planck_charge', 'elementary_charge'] assert find_unit(inch) == [ 'm', 'au', 'cm', 'dm', 'ft', 'km', 'ly', 'mi', 'mm', 'nm', 'pm', 'um', 'yd', 'nmi', 'feet', 'foot', 'inch', 'mile', 'yard', 'meter', 'miles', 'yards', 'inches', 'meters', 'micron', 'microns', 'angstrom', 'angstroms', 'decimeter', 'kilometer', 'lightyear', 'nanometer', 'picometer', 'centimeter', 'decimeters', 'kilometers', 'lightyears', 'micrometer', 'millimeter', 'nanometers', 'picometers', 'centimeters', 'micrometers', 'millimeters', 'nautical_mile', 'planck_length', 'nautical_miles', 'astronomical_unit', 'astronomical_units'] assert find_unit(inch**-1) == ['D', 'dioptre', 'optical_power'] assert find_unit(length**-1) == ['D', 'dioptre', 'optical_power'] assert find_unit(inch ** 2) == ['ha', 'hectare', 'planck_area'] assert find_unit(inch ** 3) == [ 'L', 'l', 'cL', 'cl', 'dL', 'dl', 'mL', 'ml', 'liter', 'quart', 'liters', 'quarts', 'deciliter', 'centiliter', 'deciliters', 'milliliter', 'centiliters', 'milliliters', 'planck_volume'] assert find_unit('voltage') == ['V', 'v', 'volt', 'volts', 'planck_voltage'] assert find_unit(grams) == ['g', 't', 'Da', 'kg', 'me', 'mg', 'ug', 'amu', 'mmu', 'amus', 'gram', 'mmus', 'grams', 'pound', 'tonne', 'dalton', 'pounds', 'kilogram', 'kilograms', 'microgram', 'milligram', 'metric_ton', 'micrograms', 'milligrams', 'planck_mass', 'milli_mass_unit', 'atomic_mass_unit', 'electron_rest_mass', 'atomic_mass_constant'] def test_Quantity_derivative(): x = symbols("x") assert diff(x*meter, x) == meter assert diff(x**3*meter**2, x) == 3*x**2*meter**2 assert diff(meter, meter) == 1 assert diff(meter**2, meter) == 2*meter def test_quantity_postprocessing(): q1 = Quantity('q1') q2 = Quantity('q2') SI.set_quantity_dimension(q1, length*pressure**2*temperature/time) SI.set_quantity_dimension(q2, energy*pressure*temperature/(length**2*time)) assert q1 + q2 q = q1 + q2 Dq = Dimension(SI.get_dimensional_expr(q)) assert SI.get_dimension_system().get_dimensional_dependencies(Dq) == { length: -1, mass: 2, temperature: 1, time: -5, } def test_factor_and_dimension(): assert (3000, Dimension(1)) == SI._collect_factor_and_dimension(3000) assert (1001, length) == SI._collect_factor_and_dimension(meter + km) assert (2, length/time) == SI._collect_factor_and_dimension( meter/second + 36*km/(10*hour)) x, y = symbols('x y') assert (x + y/100, length) == SI._collect_factor_and_dimension( x*m + y*centimeter) cH = Quantity('cH') SI.set_quantity_dimension(cH, amount_of_substance/volume) pH = -log(cH) assert (1, volume/amount_of_substance) == SI._collect_factor_and_dimension( exp(pH)) v_w1 = Quantity('v_w1') v_w2 = Quantity('v_w2') v_w1.set_global_relative_scale_factor(Rational(3, 2), meter/second) v_w2.set_global_relative_scale_factor(2, meter/second) expr = Abs(v_w1/2 - v_w2) assert (Rational(5, 4), length/time) == \ SI._collect_factor_and_dimension(expr) expr = Rational(5, 2)*second/meter*v_w1 - 3000 assert (-(2996 + Rational(1, 4)), Dimension(1)) == \ SI._collect_factor_and_dimension(expr) expr = v_w1**(v_w2/v_w1) assert ((Rational(3, 2))**Rational(4, 3), (length/time)**Rational(4, 3)) == \ SI._collect_factor_and_dimension(expr) def test_dimensional_expr_of_derivative(): l = Quantity('l') t = Quantity('t') t1 = Quantity('t1') l.set_global_relative_scale_factor(36, km) t.set_global_relative_scale_factor(1, hour) t1.set_global_relative_scale_factor(1, second) x = Symbol('x') y = Symbol('y') f = Function('f') dfdx = f(x, y).diff(x, y) dl_dt = dfdx.subs({f(x, y): l, x: t, y: t1}) assert SI.get_dimensional_expr(dl_dt) ==\ SI.get_dimensional_expr(l / t / t1) ==\ Symbol("length")/Symbol("time")**2 assert SI._collect_factor_and_dimension(dl_dt) ==\ SI._collect_factor_and_dimension(l / t / t1) ==\ (10, length/time**2) def test_get_dimensional_expr_with_function(): v_w1 = Quantity('v_w1') v_w2 = Quantity('v_w2') v_w1.set_global_relative_scale_factor(1, meter/second) v_w2.set_global_relative_scale_factor(1, meter/second) assert SI.get_dimensional_expr(sin(v_w1)) == \ sin(SI.get_dimensional_expr(v_w1)) assert SI.get_dimensional_expr(sin(v_w1/v_w2)) == 1 def test_binary_information(): assert convert_to(kibibyte, byte) == 1024*byte assert convert_to(mebibyte, byte) == 1024**2*byte assert convert_to(gibibyte, byte) == 1024**3*byte assert convert_to(tebibyte, byte) == 1024**4*byte assert convert_to(pebibyte, byte) == 1024**5*byte assert convert_to(exbibyte, byte) == 1024**6*byte assert kibibyte.convert_to(bit) == 8*1024*bit assert byte.convert_to(bit) == 8*bit a = 10*kibibyte*hour assert convert_to(a, byte) == 10240*byte*hour assert convert_to(a, minute) == 600*kibibyte*minute assert convert_to(a, [byte, minute]) == 614400*byte*minute def test_conversion_with_2_nonstandard_dimensions(): good_grade = Quantity("good_grade") kilo_good_grade = Quantity("kilo_good_grade") centi_good_grade = Quantity("centi_good_grade") kilo_good_grade.set_global_relative_scale_factor(1000, good_grade) centi_good_grade.set_global_relative_scale_factor(S.One/10**5, kilo_good_grade) charity_points = Quantity("charity_points") milli_charity_points = Quantity("milli_charity_points") missions = Quantity("missions") milli_charity_points.set_global_relative_scale_factor(S.One/1000, charity_points) missions.set_global_relative_scale_factor(251, charity_points) assert convert_to( kilo_good_grade*milli_charity_points*millimeter, [centi_good_grade, missions, centimeter] ) == S.One * 10**5 / (251*1000) / 10 * centi_good_grade*missions*centimeter def test_eval_subs(): energy, mass, force = symbols('energy mass force') expr1 = energy/mass units = {energy: kilogram*meter**2/second**2, mass: kilogram} assert expr1.subs(units) == meter**2/second**2 expr2 = force/mass units = {force:gravitational_constant*kilogram**2/meter**2, mass:kilogram} assert expr2.subs(units) == gravitational_constant*kilogram/meter**2 def test_issue_14932(): assert (log(inch) - log(2)).simplify() == log(inch/2) assert (log(inch) - log(foot)).simplify() == -log(12) p = symbols('p', positive=True) assert (log(inch) - log(p)).simplify() == log(inch/p) def test_issue_14547(): # the root issue is that an argument with dimensions should # not raise an error when the `arg - 1` calculation is # performed in the assumptions system from sympy.physics.units import foot, inch from sympy.core.relational import Eq assert log(foot).is_zero is None assert log(foot).is_positive is None assert log(foot).is_nonnegative is None assert log(foot).is_negative is None assert log(foot).is_algebraic is None assert log(foot).is_rational is None # doesn't raise error assert Eq(log(foot), log(inch)) is not None # might be False or unevaluated x = Symbol('x') e = foot + x assert e.is_Add and set(e.args) == {foot, x} e = foot + 1 assert e.is_Add and set(e.args) == {foot, 1} def test_issue_22164(): warnings.simplefilter("error") dm = Quantity("dm") SI.set_quantity_dimension(dm, length) SI.set_quantity_scale_factor(dm, 1) bad_exp = Quantity("bad_exp") SI.set_quantity_dimension(bad_exp, length) SI.set_quantity_scale_factor(bad_exp, 1) expr = dm ** bad_exp # deprecation warning is not expected here SI._collect_factor_and_dimension(expr) def test_issue_22819(): from sympy.physics.units import tonne, gram, Da from sympy.physics.units.systems.si import dimsys_SI assert tonne.convert_to(gram) == 1000000*gram assert dimsys_SI.get_dimensional_dependencies(area) == {length: 2} assert Da.scale_factor == 1.66053906660000e-24 def test_issue_20288(): from sympy.core.numbers import E from sympy.physics.units import energy u = Quantity('u') v = Quantity('v') SI.set_quantity_dimension(u, energy) SI.set_quantity_dimension(v, energy) u.set_global_relative_scale_factor(1, joule) v.set_global_relative_scale_factor(1, joule) expr = 1 + exp(u**2/v**2) assert SI._collect_factor_and_dimension(expr) == (1 + E, Dimension(1)) def test_issue_24062(): from sympy.core.numbers import E from sympy.physics.units import impedance, capacitance, time, ohm, farad, second R = Quantity('R') C = Quantity('C') T = Quantity('T') SI.set_quantity_dimension(R, impedance) SI.set_quantity_dimension(C, capacitance) SI.set_quantity_dimension(T, time) R.set_global_relative_scale_factor(1, ohm) C.set_global_relative_scale_factor(1, farad) T.set_global_relative_scale_factor(1, second) expr = T / (R * C) dim = SI._collect_factor_and_dimension(expr)[1] assert SI.get_dimension_system().is_dimensionless(dim) exp_expr = 1 + exp(expr) assert SI._collect_factor_and_dimension(exp_expr) == (1 + E, Dimension(1)) def test_issue_24211(): from sympy.physics.units import time, velocity, acceleration, second, meter V1 = Quantity('V1') SI.set_quantity_dimension(V1, velocity) SI.set_quantity_scale_factor(V1, 1 * meter / second) A1 = Quantity('A1') SI.set_quantity_dimension(A1, acceleration) SI.set_quantity_scale_factor(A1, 1 * meter / second**2) T1 = Quantity('T1') SI.set_quantity_dimension(T1, time) SI.set_quantity_scale_factor(T1, 1 * second) expr = A1*T1 + V1 # should not throw ValueError here SI._collect_factor_and_dimension(expr) def test_prefixed_property(): assert not meter.is_prefixed assert not joule.is_prefixed assert not day.is_prefixed assert not second.is_prefixed assert not volt.is_prefixed assert not ohm.is_prefixed assert centimeter.is_prefixed assert kilometer.is_prefixed assert kilogram.is_prefixed assert pebibyte.is_prefixed def test_physics_constant(): from sympy.physics.units import definitions for name in dir(definitions): quantity = getattr(definitions, name) if not isinstance(quantity, Quantity): continue if name.endswith('_constant'): assert isinstance(quantity, PhysicalConstant), f"{quantity} must be PhysicalConstant, but is {type(quantity)}" assert quantity.is_physical_constant, f"{name} is not marked as physics constant when it should be" for const in [gravitational_constant, molar_gas_constant, vacuum_permittivity, speed_of_light, elementary_charge]: assert isinstance(const, PhysicalConstant), f"{const} must be PhysicalConstant, but is {type(const)}" assert const.is_physical_constant, f"{const} is not marked as physics constant when it should be" assert not meter.is_physical_constant assert not joule.is_physical_constant