quantities.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. """
  2. Physical quantities.
  3. """
  4. from sympy.core.expr import AtomicExpr
  5. from sympy.core.symbol import Symbol
  6. from sympy.core.sympify import sympify
  7. from sympy.physics.units.dimensions import _QuantityMapper
  8. from sympy.physics.units.prefixes import Prefix
  9. from sympy.utilities.exceptions import (sympy_deprecation_warning,
  10. SymPyDeprecationWarning,
  11. ignore_warnings)
  12. class Quantity(AtomicExpr):
  13. """
  14. Physical quantity: can be a unit of measure, a constant or a generic quantity.
  15. """
  16. is_commutative = True
  17. is_real = True
  18. is_number = False
  19. is_nonzero = True
  20. _diff_wrt = True
  21. def __new__(cls, name, abbrev=None, dimension=None, scale_factor=None,
  22. latex_repr=None, pretty_unicode_repr=None,
  23. pretty_ascii_repr=None, mathml_presentation_repr=None,
  24. **assumptions):
  25. if not isinstance(name, Symbol):
  26. name = Symbol(name)
  27. # For Quantity(name, dim, scale, abbrev) to work like in the
  28. # old version of SymPy:
  29. if not isinstance(abbrev, str) and not \
  30. isinstance(abbrev, Symbol):
  31. dimension, scale_factor, abbrev = abbrev, dimension, scale_factor
  32. if dimension is not None:
  33. sympy_deprecation_warning(
  34. """
  35. The 'dimension' argument to to Quantity() is deprecated.
  36. Instead use the unit_system.set_quantity_dimension() method.
  37. """,
  38. deprecated_since_version="1.3",
  39. active_deprecations_target="deprecated-quantity-dimension-scale-factor"
  40. )
  41. if scale_factor is not None:
  42. sympy_deprecation_warning(
  43. """
  44. The 'scale_factor' argument to to Quantity() is deprecated.
  45. Instead use the unit_system.set_quantity_scale_factors()
  46. method.
  47. """,
  48. deprecated_since_version="1.3",
  49. active_deprecations_target="deprecated-quantity-dimension-scale-factor"
  50. )
  51. if abbrev is None:
  52. abbrev = name
  53. elif isinstance(abbrev, str):
  54. abbrev = Symbol(abbrev)
  55. obj = AtomicExpr.__new__(cls, name, abbrev)
  56. obj._name = name
  57. obj._abbrev = abbrev
  58. obj._latex_repr = latex_repr
  59. obj._unicode_repr = pretty_unicode_repr
  60. obj._ascii_repr = pretty_ascii_repr
  61. obj._mathml_repr = mathml_presentation_repr
  62. if dimension is not None:
  63. # TODO: remove after deprecation:
  64. with ignore_warnings(SymPyDeprecationWarning):
  65. obj.set_dimension(dimension)
  66. if scale_factor is not None:
  67. # TODO: remove after deprecation:
  68. with ignore_warnings(SymPyDeprecationWarning):
  69. obj.set_scale_factor(scale_factor)
  70. return obj
  71. def set_dimension(self, dimension, unit_system="SI"):
  72. sympy_deprecation_warning(
  73. f"""
  74. Quantity.set_dimension() is deprecated. Use either
  75. unit_system.set_quantity_dimension() or
  76. {self}.set_global_dimension() instead.
  77. """,
  78. deprecated_since_version="1.5",
  79. active_deprecations_target="deprecated-quantity-methods",
  80. )
  81. from sympy.physics.units import UnitSystem
  82. unit_system = UnitSystem.get_unit_system(unit_system)
  83. unit_system.set_quantity_dimension(self, dimension)
  84. def set_scale_factor(self, scale_factor, unit_system="SI"):
  85. sympy_deprecation_warning(
  86. f"""
  87. Quantity.set_scale_factor() is deprecated. Use either
  88. unit_system.set_quantity_scale_factors() or
  89. {self}.set_global_relative_scale_factor() instead.
  90. """,
  91. deprecated_since_version="1.5",
  92. active_deprecations_target="deprecated-quantity-methods",
  93. )
  94. from sympy.physics.units import UnitSystem
  95. unit_system = UnitSystem.get_unit_system(unit_system)
  96. unit_system.set_quantity_scale_factor(self, scale_factor)
  97. def set_global_dimension(self, dimension):
  98. _QuantityMapper._quantity_dimension_global[self] = dimension
  99. def set_global_relative_scale_factor(self, scale_factor, reference_quantity):
  100. """
  101. Setting a scale factor that is valid across all unit system.
  102. """
  103. from sympy.physics.units import UnitSystem
  104. scale_factor = sympify(scale_factor)
  105. # replace all prefixes by their ratio to canonical units:
  106. scale_factor = scale_factor.replace(
  107. lambda x: isinstance(x, Prefix),
  108. lambda x: x.scale_factor
  109. )
  110. scale_factor = sympify(scale_factor)
  111. UnitSystem._quantity_scale_factors_global[self] = (scale_factor, reference_quantity)
  112. UnitSystem._quantity_dimensional_equivalence_map_global[self] = reference_quantity
  113. @property
  114. def name(self):
  115. return self._name
  116. @property
  117. def dimension(self):
  118. from sympy.physics.units import UnitSystem
  119. unit_system = UnitSystem.get_default_unit_system()
  120. return unit_system.get_quantity_dimension(self)
  121. @property
  122. def abbrev(self):
  123. """
  124. Symbol representing the unit name.
  125. Prepend the abbreviation with the prefix symbol if it is defines.
  126. """
  127. return self._abbrev
  128. @property
  129. def scale_factor(self):
  130. """
  131. Overall magnitude of the quantity as compared to the canonical units.
  132. """
  133. from sympy.physics.units import UnitSystem
  134. unit_system = UnitSystem.get_default_unit_system()
  135. return unit_system.get_quantity_scale_factor(self)
  136. def _eval_is_positive(self):
  137. return True
  138. def _eval_is_constant(self):
  139. return True
  140. def _eval_Abs(self):
  141. return self
  142. def _eval_subs(self, old, new):
  143. if isinstance(new, Quantity) and self != old:
  144. return self
  145. @staticmethod
  146. def get_dimensional_expr(expr, unit_system="SI"):
  147. sympy_deprecation_warning(
  148. """
  149. Quantity.get_dimensional_expr() is deprecated. It is now
  150. associated with UnitSystem objects. The dimensional relations
  151. depend on the unit system used. Use
  152. unit_system.get_dimensional_expr() instead.
  153. """,
  154. deprecated_since_version="1.5",
  155. active_deprecations_target="deprecated-quantity-methods",
  156. )
  157. from sympy.physics.units import UnitSystem
  158. unit_system = UnitSystem.get_unit_system(unit_system)
  159. return unit_system.get_dimensional_expr(expr)
  160. @staticmethod
  161. def _collect_factor_and_dimension(expr, unit_system="SI"):
  162. """Return tuple with scale factor expression and dimension expression."""
  163. sympy_deprecation_warning(
  164. """
  165. Quantity._collect_factor_and_dimension() is deprecated. This
  166. method has been moved to the UnitSystem class. Use
  167. unit_system._collect_factor_and_dimension(expr) instead.
  168. """,
  169. deprecated_since_version="1.5",
  170. active_deprecations_target="deprecated-quantity-methods",
  171. )
  172. from sympy.physics.units import UnitSystem
  173. unit_system = UnitSystem.get_unit_system(unit_system)
  174. return unit_system._collect_factor_and_dimension(expr)
  175. def _latex(self, printer):
  176. if self._latex_repr:
  177. return self._latex_repr
  178. else:
  179. return r'\text{{{}}}'.format(self.args[1] \
  180. if len(self.args) >= 2 else self.args[0])
  181. def convert_to(self, other, unit_system="SI"):
  182. """
  183. Convert the quantity to another quantity of same dimensions.
  184. Examples
  185. ========
  186. >>> from sympy.physics.units import speed_of_light, meter, second
  187. >>> speed_of_light
  188. speed_of_light
  189. >>> speed_of_light.convert_to(meter/second)
  190. 299792458*meter/second
  191. >>> from sympy.physics.units import liter
  192. >>> liter.convert_to(meter**3)
  193. meter**3/1000
  194. """
  195. from .util import convert_to
  196. return convert_to(self, other, unit_system)
  197. @property
  198. def free_symbols(self):
  199. """Return free symbols from quantity."""
  200. return set()