def set_quantity(quantity, dimension, scale_factor): """ Set value and unit to physical quantity. Example: >>> import sympy.physics.units as un >>> from sympy.functions import * >>> from sympy.physics.units import Quantity >>> from utils import * >>> from sympy.physics.units import convert_to >>> >>> E = Quantity("E") >>> c = Quantity("c") >>> set_quantity(E, un.energy, 2.15e12 * un.kilo * un.watts * un.hour) >>> set_quantity(c, un.speed, 3.00e8 * un.m / un.s) >>> E = convert_to(E, un.joule).n() >>> m = E/c**2 7.74e+18*joule/c**2 convert_to(m, un.kg).n() 86.0*kilogram :param quantity: physical quantity, for example length, mass or energy :param dimension: time, length, mass ... based on sympy.physics.units :param scale_factor: value to be set to `quantity` parameter """ SI.set_quantity_dimension(quantity, dimension) SI.set_quantity_scale_factor(quantity, scale_factor)
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_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_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_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_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_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) with warns_deprecated_sympy(): assert (3000, Dimension(1)) == Quantity._collect_factor_and_dimension(3000)
def test_prefix_operations(): m = PREFIXES["m"] k = PREFIXES["k"] M = PREFIXES["M"] dodeca = Prefix("dodeca", "dd", 1, base=12) assert m * k == 1 assert k * k == M assert 1 / m == k assert k / m == M assert dodeca * dodeca == 144 assert 1 / dodeca == S.One / 12 assert k / dodeca == S(1000) / 12 assert dodeca / dodeca == 1 m = Quantity("fake_meter") SI.set_quantity_dimension(m, S.One) SI.set_quantity_scale_factor(m, S.One) assert dodeca * m == 12 * m assert dodeca / m == 12 / m expr1 = kilo * 3 assert isinstance(expr1, Mul) assert expr1.args == (3, kilo) expr2 = kilo * x assert isinstance(expr2, Mul) assert expr2.args == (x, kilo) expr3 = kilo / 3 assert isinstance(expr3, Mul) assert expr3.args == (Rational(1, 3), kilo) assert expr3.args == (S.One / 3, kilo) expr4 = kilo / x assert isinstance(expr4, Mul) assert expr4.args == (1 / x, kilo)
def v_unit(t): # Note: # At the moment t has to be a scalar cond_1 = simplify(t) == 0 cond_2 = dimsys_SI.equivalent_dims(SI.get_dimensional_expr(t), time) assert cond_1 | cond_2 omega = 2 * pi / second # phi = 0 phi = pi / 8 V_0 = 20 * meter / second V_range = 5 * meter / second return V_0 + V_range * sin(omega * t + phi)
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)) with warns_deprecated_sympy(): Dq1 = Dimension(Quantity.get_dimensional_expr(expr)) assert Dq == Dq1 assert SI.get_dimension_system().get_dimensional_dependencies(Dq) == { 'length': 1, 'time': -1, } assert meter == sqrt(meter**2)
def test_prefix_unit(): m = Quantity("fake_meter", abbrev="m") m.set_global_relative_scale_factor(1, meter) pref = {"m": PREFIXES["m"], "c": PREFIXES["c"], "d": PREFIXES["d"]} q1 = Quantity("millifake_meter", abbrev="mm") q2 = Quantity("centifake_meter", abbrev="cm") q3 = Quantity("decifake_meter", abbrev="dm") SI.set_quantity_dimension(q1, length) SI.set_quantity_scale_factor(q1, PREFIXES["m"]) SI.set_quantity_scale_factor(q1, PREFIXES["c"]) SI.set_quantity_scale_factor(q1, PREFIXES["d"]) res = [q1, q2, q3] prefs = prefix_unit(m, pref) assert set(prefs) == set(res) assert set(map(lambda v: v.abbrev, prefs)) == set(symbols("mm,cm,dm"))
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_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_dimensions_of_expressions(self): a = Quantity("a") SI.set_quantity_dimension(a, length) t = Quantity("t") SI.set_quantity_dimension(t, time) res = a**2 / t # we can now determine the physical dimension of res res_dim = SI.get_dimensional_expr(res) self.assertTrue(dimsys_SI.equivalent_dims(res_dim, length**2 / time)) # In parameter dicts we can describe values along with units a_val = Quantity("a_val") SI.set_quantity_dimension(a_val, length) SI.set_quantity_scale_factor(a_val, 5 * meter) parameter_dict = {a: 5 * meter, t: 4 * second} res_val = res.subs(parameter_dict) # we can now determine the physical dimension of res_val # and check it against the expected print(SI.get_dimensional_expr(res_val)) self.assertTrue( dimsys_SI.equivalent_dims(SI.get_dimensional_expr(res), SI.get_dimensional_expr(res_val)))
def test_numerification(self): # apply a function defined with units def v_unit(t): # Note: # At the moment t has to be a scalar cond_1 = simplify(t) == 0 cond_2 = dimsys_SI.equivalent_dims(SI.get_dimensional_expr(t), time) assert cond_1 | cond_2 omega = 2 * pi / second # phi = 0 phi = pi / 8 V_0 = 20 * meter / second V_range = 5 * meter / second return V_0 + V_range * sin(omega * t + phi) # Note ts = [second * t for t in np.linspace(0, float(2 * pi), 100)] ysf = [v_unit(t) for t in ts] fig = plt.figure() ax = fig.add_subplot(1, 1, 1) auto_plot_with_units(ax, ts, ysf) fig.savefig("example3_auto.pdf") t = Quantity("t") SI.set_quantity_dimension(t, time) # We can transform Expressions to numerical functions # This also works for Expressions of Quanteties and functions that # contain units v = Function("v") m = Quantity("m") # create an expression containing a function E = m / 2 * v(t)**2 # print(v_unit(3*day)) # substitute paramters E_parUnit = E.subs({m: 5 * kilogram}) # lambify the expression to a function tup = (t, ) E_funcUnit = lambdify(tup, E_parUnit, {"v": v_unit}) # ysE = [E_funcUnit(t) for t in ts] # # fig = plt.figure() # ax = fig.add_subplot(1, 1, 1) # auto_plot_with_units(ax, ts, ysE) # fig.savefig("example4_auto.pdf") # #################################################################################################### # # Exampe 5 # Here we assume that parameters, arguments and return values of functions # have units attached. # This is natural from sympys perspective and allows computations involving # quanteties ( numbers with units ) to be completely transparent. # E.g. a function or expression receiving a # length argument will always compute the correct result since the argument # carries its unit with it. Also the result can be expressed in any unit # compatible with its dimension. # # However numerical computations in scipy like solving an ode require numbers # or functions that consume and return numbers (as opposed to quanteties) # To perform those computations we temporarily have to remove units from the # arguments before the computation and attach units to the results. # We can automatically convert 'numerify' a function working on quanteties # (f_quant) to a function on number (f_num) if we choose the units for # arguments and return values. # The resulting purely numerical function f_num represents # f_quant faithfully for those units and for those units only. # This has several consequences: # - Along with f_num the untis of the arguments and the return value have to # be remembered externally since they are no intrinsic part of f_num. # - The numerical representations of different functions and paramters # should be consistent. E.g in f_quant(g_quant(x_qunat)) x_qant,f_quant # and g_quant should be "numerified" making sure that x_num represents # the original quantity with respect to the unit that f_num expects and # that f_num returns its result w.r.t. the unit g_num expects... # This is possible with the help of the unit system. # - Unfortunately the "numerification" of functions is computationally # expensive as the numeric function f_num everytime it is called it will # attach units call f_quant and remove units from the result. def numerify(f_quant, *units): def f_num(*num_args): target_unit = units[-1] u_args = tuple(num_arg * units[ind] for ind, num_arg in enumerate(num_args)) res_quant = f_quant(*u_args) # res_quant_target_unit = convert_to(res_quant, target_unit) # res_num = factor(res_quant_target_unit, target_unit)/target_unit res_num = simplify(res_quant / target_unit) return float(res_num) return f_num C_0 = describedQuantity("C_0", mass, "") C_1 = describedQuantity("C_1", mass, "") t = describedQuantity("t", time, "") k_01 = describedQuantity("k_01", 1 / time, "") k_10 = describedQuantity("k_10", 1 / time, "") k_0o = describedQuantity("k_0o", 1 / time, "") k_1o = Function("k_1o") state_variables = [C_0, C_1] # order is important inputs = { 0: sin(t) + 2, 1: cos(t) + 2 } # input to pool 0 # input to pool 1 outputs = { 0: k_0o * C_0**3, # output from pool 0 1: k_1o(t) * C_1**3, # output from pool 0 } internal_fluxes = { (0, 1): k_01 * C_0 * C_1**2, # flux from pool 0 to pool 1 (1, 0): k_10 * C_0 * C_1, # flux from pool 1 to pool 0 } time_symbol = t srm = SmoothReservoirModel(state_variables, time_symbol, inputs, outputs, internal_fluxes) par_dict_quant = { k_01: 1 / 100 * 1 / day, k_10: 1 / 100 * 1 / day, k_0o: 1 / 2 * 1 / day, } def k_1o_func_quant(t): omega = 2 * pi / day phi = pi / 8 V_0 = 20 * kilogram / day V_range = 5 * kilogram / day u_res = V_0 + V_range * sin(omega * t + phi) return u_res times_quant = np.linspace(0, 20, 16) * year start_values_quant = np.array([1 * gram, 2 * kilogram]) # Note that the time units of the parameters the function and the time array # are different. Also note that the components of the startvalue tuple are # given with respect to two different units (gigagram and kilogram) and the # k_1o_func_quant uses kilogram/second as the unit for the return value. # # The different units are handled correctly by sympy, becuase the same quantety # can be represented with respect to differnt units (1*day=24*hour) # # If we convert ot purely numerical values and functions, we have to consider # the consistency of the whole ensemble The easiest way to achieve this is to # normalize all times to an arbitrary but common unit of time (e.g. year), and # all masses to an arbitrary unit of mass (e.g. kilogram) # create a numerical function expecting its argument to be a number measuring # years and returning a number to be interpreted as kilogram/year k_1o_func_num = numerify(k_1o_func_quant, year, kilogram / year) # create a par_dict of numbers (since all parameter in the dictionary are rates # we can deal with the whole dict at once par_dict_num = { k: to_number(v, 1 / year) for k, v in par_dict_quant.items() } # extract the times in years times_num = np.array([to_number(t, year) for t in times_quant]) # crete numerical start values start_values_num = np.array( [to_number(v, kilogram) for v in start_values_quant]) n_smr = SmoothModelRun( srm, par_dict_num, start_values_num, times_num, func_set={k_1o: k_1o_func_num}, ) before = tm.time() sol_num = n_smr.solve() # extremely slow after = tm.time() print(before - after) def k_1o_func_num_manual(t): omega = 2 * pi phi = pi / 8 V_0 = 20 V_range = 5 u_res = V_0 + V_range * sin(omega * t + phi) return u_res n_smr = SmoothModelRun( srm, par_dict_num, start_values_num, times_num, func_set={k_1o: k_1o_func_num_manual}, ) before = tm.time() sol_num = n_smr.solve() after = tm.time() print(before - after)
def check_unit_consistency(expr): SI._collect_factor_and_dimension(expr)
from sympy import symbols, solve, Eq, pretty_print from sympy.physics.units import Quantity, mass, acceleration, power, velocity from sympy.physics.units import convert_to from sympy.physics.units import kilo from sympy.physics.units import km, hour, m, s, kg, W from sympy.physics.units.systems import SI a = symbols("a") m_ = Quantity("m") v = Quantity("v") W_ = Quantity("W") SI.set_quantity_dimension(a, acceleration) SI.set_quantity_dimension(m_, mass) SI.set_quantity_scale_factor(m_, 1000*kg) SI.set_quantity_dimension(v, velocity) SI.set_quantity_scale_factor(v, 60*km/hour) SI.set_quantity_dimension(W_, power) SI.set_quantity_scale_factor(W_, 120*kilo*W) eq = Eq(W_, m_*a*v) result = solve(eq, a)[0] pretty_print(result) pretty_print(convert_to(result, m / s ** 2).n())
# real=True, positive=True) th_0p95: Symbol = Symbol(r"t^{\rightarrow{0.95}}", real=True, negative=False) h_0p95: Symbol = Symbol(r"h_{0.95}", real=True, negative=False) th_0p9: Symbol = Symbol(r"t^{\rightarrow{0.9}}", real=True, negative=False) # h_0p9: Symbol = Symbol(r"h_{0.9}", real=True, negative=False) t_oneyear: Symbol = Symbol(r"t_{\mathrm{1y}}", real=True, positive=True) t_My: Symbol = Symbol(r"t_{\mathrm{My}}", real=True, positive=True) that: Symbol = Symbol(r"\hat{t}", real=True, negative=False) tv_0: Symbol = Symbol(r"t^{\downarrow_{0}}", real=True, positive=True) th_0: Symbol = Symbol(r"t^{\rightarrow_{0}}", real=True, positive=True) beta: Symbol = Symbol(r"\beta", real=True) beta_: Symbol = Symbol(r"\beta_x", real=True) beta_crit: Symbol = Symbol(r"\beta_c", real=True) beta_0: Symbol = Symbol(r"\beta_0", real=True, positive=True) SI.set_quantity_dimension(beta_0, 1) betaplus: Symbol = Symbol(r"\beta^+", real=True, positive=True) alpha: Symbol = Symbol(r"\alpha", real=True) alpha_ext: Symbol = Symbol(r"\alpha_\mathrm{ext}", real=True) alphaplus: Symbol = Symbol(r"\alpha^+", real=True, positive=True) alpha_extremum: Symbol = Symbol(r"\alpha_\text{extremum}") beta_at_alpha_extremum: Symbol = Symbol(r"\beta_{\text{extremum}\{\alpha\}}") phi: Symbol = Symbol(r"\phi", real=True) psi: Symbol = Symbol(r"\psi", real=True) rvec: Symbol = Symbol(r"\mathbf{r}", real=True) r: Symbol = Symbol(r"r", real=True, negative=False) rx: Symbol = Symbol(r"{r}^x", real=True, negative=False) rz: Symbol = Symbol(r"{r}^z", real=True) rvechat: Symbol = Symbol(r"\mathbf{\hat{r}}", real=True) rhat: Symbol = Symbol(r"\hat{r}", real=True, negative=False)
from sympy.interactive import init_printing init_printing() Wo = symbols("Wo") v = symbols("v") rho = Quantity("rho") s = Quantity("s") g = Quantity("g") h1 = Quantity("h1") h2 = Quantity("h2") etha = Quantity("etha") theta = Quantity("theta") SI.set_quantity_dimension(rho, mass / length**3) SI.set_quantity_scale_factor(rho, 1.0 * un.grams / un.cm**3) SI.set_quantity_dimension(s, length**2) SI.set_quantity_scale_factor(s, 35.0 * un.cm**2) SI.set_quantity_dimension(h1, length) SI.set_quantity_scale_factor(h1, 2.4 * un.meters) SI.set_quantity_dimension(h2, length) SI.set_quantity_scale_factor(h2, 4.8 * un.meters) SI.set_quantity_dimension(g, acceleration) SI.set_quantity_scale_factor(g, 9.81 * un.m / un.s**2) SI.set_quantity_dimension(etha, One)
def describedQuantity(name, dimension, description): obj = Quantity(name=name) SI.set_quantity_dimension(obj, dimension) obj.description = description # obj = Symbol(name) return obj