Generateurv2/backend/env/lib/python3.10/site-packages/sympy/physics/units/unitsystem.py
2022-06-24 17:14:37 +02:00

214 lines
7.3 KiB
Python

"""
Unit system for physical quantities; include definition of constants.
"""
from typing import Dict
from sympy import S, Mul, Pow, Add, Function, Derivative
from sympy.physics.units.dimensions import _QuantityMapper
from sympy.utilities.exceptions import SymPyDeprecationWarning
from .dimensions import Dimension
class UnitSystem(_QuantityMapper):
"""
UnitSystem represents a coherent set of units.
A unit system is basically a dimension system with notions of scales. Many
of the methods are defined in the same way.
It is much better if all base units have a symbol.
"""
_unit_systems = {} # type: Dict[str, UnitSystem]
def __init__(self, base_units, units=(), name="", descr="", dimension_system=None):
UnitSystem._unit_systems[name] = self
self.name = name
self.descr = descr
self._base_units = base_units
self._dimension_system = dimension_system
self._units = tuple(set(base_units) | set(units))
self._base_units = tuple(base_units)
super().__init__()
def __str__(self):
"""
Return the name of the system.
If it does not exist, then it makes a list of symbols (or names) of
the base dimensions.
"""
if self.name != "":
return self.name
else:
return "UnitSystem((%s))" % ", ".join(
str(d) for d in self._base_units)
def __repr__(self):
return '<UnitSystem: %s>' % repr(self._base_units)
def extend(self, base, units=(), name="", description="", dimension_system=None):
"""Extend the current system into a new one.
Take the base and normal units of the current system to merge
them to the base and normal units given in argument.
If not provided, name and description are overridden by empty strings.
"""
base = self._base_units + tuple(base)
units = self._units + tuple(units)
return UnitSystem(base, units, name, description, dimension_system)
def print_unit_base(self, unit):
"""
Useless method.
DO NOT USE, use instead ``convert_to``.
Give the string expression of a unit in term of the basis.
Units are displayed by decreasing power.
"""
SymPyDeprecationWarning(
deprecated_since_version="1.2",
issue=13336,
feature="print_unit_base",
useinstead="convert_to",
).warn()
from sympy.physics.units import convert_to
return convert_to(unit, self._base_units)
def get_dimension_system(self):
return self._dimension_system
def get_quantity_dimension(self, unit):
qdm = self.get_dimension_system()._quantity_dimension_map
if unit in qdm:
return qdm[unit]
return super().get_quantity_dimension(unit)
def get_quantity_scale_factor(self, unit):
qsfm = self.get_dimension_system()._quantity_scale_factors
if unit in qsfm:
return qsfm[unit]
return super().get_quantity_scale_factor(unit)
@staticmethod
def get_unit_system(unit_system):
if isinstance(unit_system, UnitSystem):
return unit_system
if unit_system not in UnitSystem._unit_systems:
raise ValueError(
"Unit system is not supported. Currently"
"supported unit systems are {}".format(
", ".join(sorted(UnitSystem._unit_systems))
)
)
return UnitSystem._unit_systems[unit_system]
@staticmethod
def get_default_unit_system():
return UnitSystem._unit_systems["SI"]
@property
def dim(self):
"""
Give the dimension of the system.
That is return the number of units forming the basis.
"""
return len(self._base_units)
@property
def is_consistent(self):
"""
Check if the underlying dimension system is consistent.
"""
# test is performed in DimensionSystem
return self.get_dimension_system().is_consistent
def get_dimensional_expr(self, expr):
from sympy import Mul, Add, Pow, Derivative
from sympy import Function
from sympy.physics.units import Quantity
if isinstance(expr, Mul):
return Mul(*[self.get_dimensional_expr(i) for i in expr.args])
elif isinstance(expr, Pow):
return self.get_dimensional_expr(expr.base) ** expr.exp
elif isinstance(expr, Add):
return self.get_dimensional_expr(expr.args[0])
elif isinstance(expr, Derivative):
dim = self.get_dimensional_expr(expr.expr)
for independent, count in expr.variable_count:
dim /= self.get_dimensional_expr(independent)**count
return dim
elif isinstance(expr, Function):
args = [self.get_dimensional_expr(arg) for arg in expr.args]
if all(i == 1 for i in args):
return S.One
return expr.func(*args)
elif isinstance(expr, Quantity):
return self.get_quantity_dimension(expr).name
return S.One
def _collect_factor_and_dimension(self, expr):
"""
Return tuple with scale factor expression and dimension expression.
"""
from sympy.physics.units import Quantity
if isinstance(expr, Quantity):
return expr.scale_factor, expr.dimension
elif isinstance(expr, Mul):
factor = 1
dimension = Dimension(1)
for arg in expr.args:
arg_factor, arg_dim = self._collect_factor_and_dimension(arg)
factor *= arg_factor
dimension *= arg_dim
return factor, dimension
elif isinstance(expr, Pow):
factor, dim = self._collect_factor_and_dimension(expr.base)
exp_factor, exp_dim = self._collect_factor_and_dimension(expr.exp)
if exp_dim.is_dimensionless:
exp_dim = 1
return factor ** exp_factor, dim ** (exp_factor * exp_dim)
elif isinstance(expr, Add):
factor, dim = self._collect_factor_and_dimension(expr.args[0])
for addend in expr.args[1:]:
addend_factor, addend_dim = \
self._collect_factor_and_dimension(addend)
if dim != addend_dim:
raise ValueError(
'Dimension of "{}" is {}, '
'but it should be {}'.format(
addend, addend_dim, dim))
factor += addend_factor
return factor, dim
elif isinstance(expr, Derivative):
factor, dim = self._collect_factor_and_dimension(expr.args[0])
for independent, count in expr.variable_count:
ifactor, idim = self._collect_factor_and_dimension(independent)
factor /= ifactor**count
dim /= idim**count
return factor, dim
elif isinstance(expr, Function):
fds = [self._collect_factor_and_dimension(
arg) for arg in expr.args]
return (expr.func(*(f[0] for f in fds)),
expr.func(*(d[1] for d in fds)))
elif isinstance(expr, Dimension):
return 1, expr
else:
return expr, Dimension(1)