switching to high quality piper tts and added label translations
This commit is contained in:
@@ -0,0 +1,735 @@
|
||||
from sympy.utilities import dict_merge
|
||||
from sympy.utilities.iterables import iterable
|
||||
from sympy.physics.vector import (Dyadic, Vector, ReferenceFrame,
|
||||
Point, dynamicsymbols)
|
||||
from sympy.physics.vector.printing import (vprint, vsprint, vpprint, vlatex,
|
||||
init_vprinting)
|
||||
from sympy.physics.mechanics.particle import Particle
|
||||
from sympy.physics.mechanics.rigidbody import RigidBody
|
||||
from sympy.simplify.simplify import simplify
|
||||
from sympy import Matrix, Mul, Derivative, sin, cos, tan, S
|
||||
from sympy.core.function import AppliedUndef
|
||||
from sympy.physics.mechanics.inertia import (inertia as _inertia,
|
||||
inertia_of_point_mass as _inertia_of_point_mass)
|
||||
from sympy.utilities.exceptions import sympy_deprecation_warning
|
||||
|
||||
__all__ = ['linear_momentum',
|
||||
'angular_momentum',
|
||||
'kinetic_energy',
|
||||
'potential_energy',
|
||||
'Lagrangian',
|
||||
'mechanics_printing',
|
||||
'mprint',
|
||||
'msprint',
|
||||
'mpprint',
|
||||
'mlatex',
|
||||
'msubs',
|
||||
'find_dynamicsymbols']
|
||||
|
||||
# These are functions that we've moved and renamed during extracting the
|
||||
# basic vector calculus code from the mechanics packages.
|
||||
|
||||
mprint = vprint
|
||||
msprint = vsprint
|
||||
mpprint = vpprint
|
||||
mlatex = vlatex
|
||||
|
||||
|
||||
def mechanics_printing(**kwargs):
|
||||
"""
|
||||
Initializes time derivative printing for all SymPy objects in
|
||||
mechanics module.
|
||||
"""
|
||||
|
||||
init_vprinting(**kwargs)
|
||||
|
||||
mechanics_printing.__doc__ = init_vprinting.__doc__
|
||||
|
||||
|
||||
def inertia(frame, ixx, iyy, izz, ixy=0, iyz=0, izx=0):
|
||||
sympy_deprecation_warning(
|
||||
"""
|
||||
The inertia function has been moved.
|
||||
Import it from "sympy.physics.mechanics".
|
||||
""",
|
||||
deprecated_since_version="1.13",
|
||||
active_deprecations_target="moved-mechanics-functions"
|
||||
)
|
||||
return _inertia(frame, ixx, iyy, izz, ixy, iyz, izx)
|
||||
|
||||
|
||||
def inertia_of_point_mass(mass, pos_vec, frame):
|
||||
sympy_deprecation_warning(
|
||||
"""
|
||||
The inertia_of_point_mass function has been moved.
|
||||
Import it from "sympy.physics.mechanics".
|
||||
""",
|
||||
deprecated_since_version="1.13",
|
||||
active_deprecations_target="moved-mechanics-functions"
|
||||
)
|
||||
return _inertia_of_point_mass(mass, pos_vec, frame)
|
||||
|
||||
|
||||
def linear_momentum(frame, *body):
|
||||
"""Linear momentum of the system.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
This function returns the linear momentum of a system of Particle's and/or
|
||||
RigidBody's. The linear momentum of a system is equal to the vector sum of
|
||||
the linear momentum of its constituents. Consider a system, S, comprised of
|
||||
a rigid body, A, and a particle, P. The linear momentum of the system, L,
|
||||
is equal to the vector sum of the linear momentum of the particle, L1, and
|
||||
the linear momentum of the rigid body, L2, i.e.
|
||||
|
||||
L = L1 + L2
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
frame : ReferenceFrame
|
||||
The frame in which linear momentum is desired.
|
||||
body1, body2, body3... : Particle and/or RigidBody
|
||||
The body (or bodies) whose linear momentum is required.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.mechanics import Point, Particle, ReferenceFrame
|
||||
>>> from sympy.physics.mechanics import RigidBody, outer, linear_momentum
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> P = Point('P')
|
||||
>>> P.set_vel(N, 10 * N.x)
|
||||
>>> Pa = Particle('Pa', P, 1)
|
||||
>>> Ac = Point('Ac')
|
||||
>>> Ac.set_vel(N, 25 * N.y)
|
||||
>>> I = outer(N.x, N.x)
|
||||
>>> A = RigidBody('A', Ac, N, 20, (I, Ac))
|
||||
>>> linear_momentum(N, A, Pa)
|
||||
10*N.x + 500*N.y
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(frame, ReferenceFrame):
|
||||
raise TypeError('Please specify a valid ReferenceFrame')
|
||||
else:
|
||||
linear_momentum_sys = Vector(0)
|
||||
for e in body:
|
||||
if isinstance(e, (RigidBody, Particle)):
|
||||
linear_momentum_sys += e.linear_momentum(frame)
|
||||
else:
|
||||
raise TypeError('*body must have only Particle or RigidBody')
|
||||
return linear_momentum_sys
|
||||
|
||||
|
||||
def angular_momentum(point, frame, *body):
|
||||
"""Angular momentum of a system.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
This function returns the angular momentum of a system of Particle's and/or
|
||||
RigidBody's. The angular momentum of such a system is equal to the vector
|
||||
sum of the angular momentum of its constituents. Consider a system, S,
|
||||
comprised of a rigid body, A, and a particle, P. The angular momentum of
|
||||
the system, H, is equal to the vector sum of the angular momentum of the
|
||||
particle, H1, and the angular momentum of the rigid body, H2, i.e.
|
||||
|
||||
H = H1 + H2
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
point : Point
|
||||
The point about which angular momentum of the system is desired.
|
||||
frame : ReferenceFrame
|
||||
The frame in which angular momentum is desired.
|
||||
body1, body2, body3... : Particle and/or RigidBody
|
||||
The body (or bodies) whose angular momentum is required.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.mechanics import Point, Particle, ReferenceFrame
|
||||
>>> from sympy.physics.mechanics import RigidBody, outer, angular_momentum
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> O = Point('O')
|
||||
>>> O.set_vel(N, 0 * N.x)
|
||||
>>> P = O.locatenew('P', 1 * N.x)
|
||||
>>> P.set_vel(N, 10 * N.x)
|
||||
>>> Pa = Particle('Pa', P, 1)
|
||||
>>> Ac = O.locatenew('Ac', 2 * N.y)
|
||||
>>> Ac.set_vel(N, 5 * N.y)
|
||||
>>> a = ReferenceFrame('a')
|
||||
>>> a.set_ang_vel(N, 10 * N.z)
|
||||
>>> I = outer(N.z, N.z)
|
||||
>>> A = RigidBody('A', Ac, a, 20, (I, Ac))
|
||||
>>> angular_momentum(O, N, Pa, A)
|
||||
10*N.z
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(frame, ReferenceFrame):
|
||||
raise TypeError('Please enter a valid ReferenceFrame')
|
||||
if not isinstance(point, Point):
|
||||
raise TypeError('Please specify a valid Point')
|
||||
else:
|
||||
angular_momentum_sys = Vector(0)
|
||||
for e in body:
|
||||
if isinstance(e, (RigidBody, Particle)):
|
||||
angular_momentum_sys += e.angular_momentum(point, frame)
|
||||
else:
|
||||
raise TypeError('*body must have only Particle or RigidBody')
|
||||
return angular_momentum_sys
|
||||
|
||||
|
||||
def kinetic_energy(frame, *body):
|
||||
"""Kinetic energy of a multibody system.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
This function returns the kinetic energy of a system of Particle's and/or
|
||||
RigidBody's. The kinetic energy of such a system is equal to the sum of
|
||||
the kinetic energies of its constituents. Consider a system, S, comprising
|
||||
a rigid body, A, and a particle, P. The kinetic energy of the system, T,
|
||||
is equal to the vector sum of the kinetic energy of the particle, T1, and
|
||||
the kinetic energy of the rigid body, T2, i.e.
|
||||
|
||||
T = T1 + T2
|
||||
|
||||
Kinetic energy is a scalar.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
frame : ReferenceFrame
|
||||
The frame in which the velocity or angular velocity of the body is
|
||||
defined.
|
||||
body1, body2, body3... : Particle and/or RigidBody
|
||||
The body (or bodies) whose kinetic energy is required.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.mechanics import Point, Particle, ReferenceFrame
|
||||
>>> from sympy.physics.mechanics import RigidBody, outer, kinetic_energy
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> O = Point('O')
|
||||
>>> O.set_vel(N, 0 * N.x)
|
||||
>>> P = O.locatenew('P', 1 * N.x)
|
||||
>>> P.set_vel(N, 10 * N.x)
|
||||
>>> Pa = Particle('Pa', P, 1)
|
||||
>>> Ac = O.locatenew('Ac', 2 * N.y)
|
||||
>>> Ac.set_vel(N, 5 * N.y)
|
||||
>>> a = ReferenceFrame('a')
|
||||
>>> a.set_ang_vel(N, 10 * N.z)
|
||||
>>> I = outer(N.z, N.z)
|
||||
>>> A = RigidBody('A', Ac, a, 20, (I, Ac))
|
||||
>>> kinetic_energy(N, Pa, A)
|
||||
350
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(frame, ReferenceFrame):
|
||||
raise TypeError('Please enter a valid ReferenceFrame')
|
||||
ke_sys = S.Zero
|
||||
for e in body:
|
||||
if isinstance(e, (RigidBody, Particle)):
|
||||
ke_sys += e.kinetic_energy(frame)
|
||||
else:
|
||||
raise TypeError('*body must have only Particle or RigidBody')
|
||||
return ke_sys
|
||||
|
||||
|
||||
def potential_energy(*body):
|
||||
"""Potential energy of a multibody system.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
This function returns the potential energy of a system of Particle's and/or
|
||||
RigidBody's. The potential energy of such a system is equal to the sum of
|
||||
the potential energy of its constituents. Consider a system, S, comprising
|
||||
a rigid body, A, and a particle, P. The potential energy of the system, V,
|
||||
is equal to the vector sum of the potential energy of the particle, V1, and
|
||||
the potential energy of the rigid body, V2, i.e.
|
||||
|
||||
V = V1 + V2
|
||||
|
||||
Potential energy is a scalar.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
body1, body2, body3... : Particle and/or RigidBody
|
||||
The body (or bodies) whose potential energy is required.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.mechanics import Point, Particle, ReferenceFrame
|
||||
>>> from sympy.physics.mechanics import RigidBody, outer, potential_energy
|
||||
>>> from sympy import symbols
|
||||
>>> M, m, g, h = symbols('M m g h')
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> O = Point('O')
|
||||
>>> O.set_vel(N, 0 * N.x)
|
||||
>>> P = O.locatenew('P', 1 * N.x)
|
||||
>>> Pa = Particle('Pa', P, m)
|
||||
>>> Ac = O.locatenew('Ac', 2 * N.y)
|
||||
>>> a = ReferenceFrame('a')
|
||||
>>> I = outer(N.z, N.z)
|
||||
>>> A = RigidBody('A', Ac, a, M, (I, Ac))
|
||||
>>> Pa.potential_energy = m * g * h
|
||||
>>> A.potential_energy = M * g * h
|
||||
>>> potential_energy(Pa, A)
|
||||
M*g*h + g*h*m
|
||||
|
||||
"""
|
||||
|
||||
pe_sys = S.Zero
|
||||
for e in body:
|
||||
if isinstance(e, (RigidBody, Particle)):
|
||||
pe_sys += e.potential_energy
|
||||
else:
|
||||
raise TypeError('*body must have only Particle or RigidBody')
|
||||
return pe_sys
|
||||
|
||||
|
||||
def gravity(acceleration, *bodies):
|
||||
from sympy.physics.mechanics.loads import gravity as _gravity
|
||||
sympy_deprecation_warning(
|
||||
"""
|
||||
The gravity function has been moved.
|
||||
Import it from "sympy.physics.mechanics.loads".
|
||||
""",
|
||||
deprecated_since_version="1.13",
|
||||
active_deprecations_target="moved-mechanics-functions"
|
||||
)
|
||||
return _gravity(acceleration, *bodies)
|
||||
|
||||
|
||||
def center_of_mass(point, *bodies):
|
||||
"""
|
||||
Returns the position vector from the given point to the center of mass
|
||||
of the given bodies(particles or rigidbodies).
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
>>> from sympy import symbols, S
|
||||
>>> from sympy.physics.vector import Point
|
||||
>>> from sympy.physics.mechanics import Particle, ReferenceFrame, RigidBody, outer
|
||||
>>> from sympy.physics.mechanics.functions import center_of_mass
|
||||
>>> a = ReferenceFrame('a')
|
||||
>>> m = symbols('m', real=True)
|
||||
>>> p1 = Particle('p1', Point('p1_pt'), S(1))
|
||||
>>> p2 = Particle('p2', Point('p2_pt'), S(2))
|
||||
>>> p3 = Particle('p3', Point('p3_pt'), S(3))
|
||||
>>> p4 = Particle('p4', Point('p4_pt'), m)
|
||||
>>> b_f = ReferenceFrame('b_f')
|
||||
>>> b_cm = Point('b_cm')
|
||||
>>> mb = symbols('mb')
|
||||
>>> b = RigidBody('b', b_cm, b_f, mb, (outer(b_f.x, b_f.x), b_cm))
|
||||
>>> p2.point.set_pos(p1.point, a.x)
|
||||
>>> p3.point.set_pos(p1.point, a.x + a.y)
|
||||
>>> p4.point.set_pos(p1.point, a.y)
|
||||
>>> b.masscenter.set_pos(p1.point, a.y + a.z)
|
||||
>>> point_o=Point('o')
|
||||
>>> point_o.set_pos(p1.point, center_of_mass(p1.point, p1, p2, p3, p4, b))
|
||||
>>> expr = 5/(m + mb + 6)*a.x + (m + mb + 3)/(m + mb + 6)*a.y + mb/(m + mb + 6)*a.z
|
||||
>>> point_o.pos_from(p1.point)
|
||||
5/(m + mb + 6)*a.x + (m + mb + 3)/(m + mb + 6)*a.y + mb/(m + mb + 6)*a.z
|
||||
|
||||
"""
|
||||
if not bodies:
|
||||
raise TypeError("No bodies(instances of Particle or Rigidbody) were passed.")
|
||||
|
||||
total_mass = 0
|
||||
vec = Vector(0)
|
||||
for i in bodies:
|
||||
total_mass += i.mass
|
||||
|
||||
masscenter = getattr(i, 'masscenter', None)
|
||||
if masscenter is None:
|
||||
masscenter = i.point
|
||||
vec += i.mass*masscenter.pos_from(point)
|
||||
|
||||
return vec/total_mass
|
||||
|
||||
|
||||
def Lagrangian(frame, *body):
|
||||
"""Lagrangian of a multibody system.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
This function returns the Lagrangian of a system of Particle's and/or
|
||||
RigidBody's. The Lagrangian of such a system is equal to the difference
|
||||
between the kinetic energies and potential energies of its constituents. If
|
||||
T and V are the kinetic and potential energies of a system then it's
|
||||
Lagrangian, L, is defined as
|
||||
|
||||
L = T - V
|
||||
|
||||
The Lagrangian is a scalar.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
frame : ReferenceFrame
|
||||
The frame in which the velocity or angular velocity of the body is
|
||||
defined to determine the kinetic energy.
|
||||
|
||||
body1, body2, body3... : Particle and/or RigidBody
|
||||
The body (or bodies) whose Lagrangian is required.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.mechanics import Point, Particle, ReferenceFrame
|
||||
>>> from sympy.physics.mechanics import RigidBody, outer, Lagrangian
|
||||
>>> from sympy import symbols
|
||||
>>> M, m, g, h = symbols('M m g h')
|
||||
>>> N = ReferenceFrame('N')
|
||||
>>> O = Point('O')
|
||||
>>> O.set_vel(N, 0 * N.x)
|
||||
>>> P = O.locatenew('P', 1 * N.x)
|
||||
>>> P.set_vel(N, 10 * N.x)
|
||||
>>> Pa = Particle('Pa', P, 1)
|
||||
>>> Ac = O.locatenew('Ac', 2 * N.y)
|
||||
>>> Ac.set_vel(N, 5 * N.y)
|
||||
>>> a = ReferenceFrame('a')
|
||||
>>> a.set_ang_vel(N, 10 * N.z)
|
||||
>>> I = outer(N.z, N.z)
|
||||
>>> A = RigidBody('A', Ac, a, 20, (I, Ac))
|
||||
>>> Pa.potential_energy = m * g * h
|
||||
>>> A.potential_energy = M * g * h
|
||||
>>> Lagrangian(N, Pa, A)
|
||||
-M*g*h - g*h*m + 350
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(frame, ReferenceFrame):
|
||||
raise TypeError('Please supply a valid ReferenceFrame')
|
||||
for e in body:
|
||||
if not isinstance(e, (RigidBody, Particle)):
|
||||
raise TypeError('*body must have only Particle or RigidBody')
|
||||
return kinetic_energy(frame, *body) - potential_energy(*body)
|
||||
|
||||
|
||||
def find_dynamicsymbols(expression, exclude=None, reference_frame=None):
|
||||
"""Find all dynamicsymbols in expression.
|
||||
|
||||
Explanation
|
||||
===========
|
||||
|
||||
If the optional ``exclude`` kwarg is used, only dynamicsymbols
|
||||
not in the iterable ``exclude`` are returned.
|
||||
If we intend to apply this function on a vector, the optional
|
||||
``reference_frame`` is also used to inform about the corresponding frame
|
||||
with respect to which the dynamic symbols of the given vector is to be
|
||||
determined.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
|
||||
expression : SymPy expression
|
||||
|
||||
exclude : iterable of dynamicsymbols, optional
|
||||
|
||||
reference_frame : ReferenceFrame, optional
|
||||
The frame with respect to which the dynamic symbols of the
|
||||
given vector is to be determined.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.mechanics import dynamicsymbols, find_dynamicsymbols
|
||||
>>> from sympy.physics.mechanics import ReferenceFrame
|
||||
>>> x, y = dynamicsymbols('x, y')
|
||||
>>> expr = x + x.diff()*y
|
||||
>>> find_dynamicsymbols(expr)
|
||||
{x(t), y(t), Derivative(x(t), t)}
|
||||
>>> find_dynamicsymbols(expr, exclude=[x, y])
|
||||
{Derivative(x(t), t)}
|
||||
>>> a, b, c = dynamicsymbols('a, b, c')
|
||||
>>> A = ReferenceFrame('A')
|
||||
>>> v = a * A.x + b * A.y + c * A.z
|
||||
>>> find_dynamicsymbols(v, reference_frame=A)
|
||||
{a(t), b(t), c(t)}
|
||||
|
||||
"""
|
||||
t_set = {dynamicsymbols._t}
|
||||
if exclude:
|
||||
if iterable(exclude):
|
||||
exclude_set = set(exclude)
|
||||
else:
|
||||
raise TypeError("exclude kwarg must be iterable")
|
||||
else:
|
||||
exclude_set = set()
|
||||
if isinstance(expression, Vector):
|
||||
if reference_frame is None:
|
||||
raise ValueError("You must provide reference_frame when passing a "
|
||||
"vector expression, got %s." % reference_frame)
|
||||
else:
|
||||
expression = expression.to_matrix(reference_frame)
|
||||
return {i for i in expression.atoms(AppliedUndef, Derivative) if
|
||||
i.free_symbols == t_set} - exclude_set
|
||||
|
||||
|
||||
def msubs(expr, *sub_dicts, smart=False, **kwargs):
|
||||
"""A custom subs for use on expressions derived in physics.mechanics.
|
||||
|
||||
Traverses the expression tree once, performing the subs found in sub_dicts.
|
||||
Terms inside ``Derivative`` expressions are ignored:
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.physics.mechanics import dynamicsymbols, msubs
|
||||
>>> x = dynamicsymbols('x')
|
||||
>>> msubs(x.diff() + x, {x: 1})
|
||||
Derivative(x(t), t) + 1
|
||||
|
||||
Note that sub_dicts can be a single dictionary, or several dictionaries:
|
||||
|
||||
>>> x, y, z = dynamicsymbols('x, y, z')
|
||||
>>> sub1 = {x: 1, y: 2}
|
||||
>>> sub2 = {z: 3, x.diff(): 4}
|
||||
>>> msubs(x.diff() + x + y + z, sub1, sub2)
|
||||
10
|
||||
|
||||
If smart=True (default False), also checks for conditions that may result
|
||||
in ``nan``, but if simplified would yield a valid expression. For example:
|
||||
|
||||
>>> from sympy import sin, tan
|
||||
>>> (sin(x)/tan(x)).subs(x, 0)
|
||||
nan
|
||||
>>> msubs(sin(x)/tan(x), {x: 0}, smart=True)
|
||||
1
|
||||
|
||||
It does this by first replacing all ``tan`` with ``sin/cos``. Then each
|
||||
node is traversed. If the node is a fraction, subs is first evaluated on
|
||||
the denominator. If this results in 0, simplification of the entire
|
||||
fraction is attempted. Using this selective simplification, only
|
||||
subexpressions that result in 1/0 are targeted, resulting in faster
|
||||
performance.
|
||||
|
||||
"""
|
||||
|
||||
sub_dict = dict_merge(*sub_dicts)
|
||||
if smart:
|
||||
func = _smart_subs
|
||||
elif hasattr(expr, 'msubs'):
|
||||
return expr.msubs(sub_dict)
|
||||
else:
|
||||
func = lambda expr, sub_dict: _crawl(expr, _sub_func, sub_dict)
|
||||
if isinstance(expr, (Matrix, Vector, Dyadic)):
|
||||
return expr.applyfunc(lambda x: func(x, sub_dict))
|
||||
else:
|
||||
return func(expr, sub_dict)
|
||||
|
||||
|
||||
def _crawl(expr, func, *args, **kwargs):
|
||||
"""Crawl the expression tree, and apply func to every node."""
|
||||
val = func(expr, *args, **kwargs)
|
||||
if val is not None:
|
||||
return val
|
||||
new_args = (_crawl(arg, func, *args, **kwargs) for arg in expr.args)
|
||||
return expr.func(*new_args)
|
||||
|
||||
|
||||
def _sub_func(expr, sub_dict):
|
||||
"""Perform direct matching substitution, ignoring derivatives."""
|
||||
if expr in sub_dict:
|
||||
return sub_dict[expr]
|
||||
elif not expr.args or expr.is_Derivative:
|
||||
return expr
|
||||
|
||||
|
||||
def _tan_repl_func(expr):
|
||||
"""Replace tan with sin/cos."""
|
||||
if isinstance(expr, tan):
|
||||
return sin(*expr.args) / cos(*expr.args)
|
||||
elif not expr.args or expr.is_Derivative:
|
||||
return expr
|
||||
|
||||
|
||||
def _smart_subs(expr, sub_dict):
|
||||
"""Performs subs, checking for conditions that may result in `nan` or
|
||||
`oo`, and attempts to simplify them out.
|
||||
|
||||
The expression tree is traversed twice, and the following steps are
|
||||
performed on each expression node:
|
||||
- First traverse:
|
||||
Replace all `tan` with `sin/cos`.
|
||||
- Second traverse:
|
||||
If node is a fraction, check if the denominator evaluates to 0.
|
||||
If so, attempt to simplify it out. Then if node is in sub_dict,
|
||||
sub in the corresponding value.
|
||||
|
||||
"""
|
||||
expr = _crawl(expr, _tan_repl_func)
|
||||
|
||||
def _recurser(expr, sub_dict):
|
||||
# Decompose the expression into num, den
|
||||
num, den = _fraction_decomp(expr)
|
||||
if den != 1:
|
||||
# If there is a non trivial denominator, we need to handle it
|
||||
denom_subbed = _recurser(den, sub_dict)
|
||||
if denom_subbed.evalf() == 0:
|
||||
# If denom is 0 after this, attempt to simplify the bad expr
|
||||
expr = simplify(expr)
|
||||
else:
|
||||
# Expression won't result in nan, find numerator
|
||||
num_subbed = _recurser(num, sub_dict)
|
||||
return num_subbed / denom_subbed
|
||||
# We have to crawl the tree manually, because `expr` may have been
|
||||
# modified in the simplify step. First, perform subs as normal:
|
||||
val = _sub_func(expr, sub_dict)
|
||||
if val is not None:
|
||||
return val
|
||||
new_args = (_recurser(arg, sub_dict) for arg in expr.args)
|
||||
return expr.func(*new_args)
|
||||
return _recurser(expr, sub_dict)
|
||||
|
||||
|
||||
def _fraction_decomp(expr):
|
||||
"""Return num, den such that expr = num/den."""
|
||||
if not isinstance(expr, Mul):
|
||||
return expr, 1
|
||||
num = []
|
||||
den = []
|
||||
for a in expr.args:
|
||||
if a.is_Pow and a.args[1] < 0:
|
||||
den.append(1 / a)
|
||||
else:
|
||||
num.append(a)
|
||||
if not den:
|
||||
return expr, 1
|
||||
num = Mul(*num)
|
||||
den = Mul(*den)
|
||||
return num, den
|
||||
|
||||
|
||||
def _f_list_parser(fl, ref_frame):
|
||||
"""Parses the provided forcelist composed of items
|
||||
of the form (obj, force).
|
||||
Returns a tuple containing:
|
||||
vel_list: The velocity (ang_vel for Frames, vel for Points) in
|
||||
the provided reference frame.
|
||||
f_list: The forces.
|
||||
|
||||
Used internally in the KanesMethod and LagrangesMethod classes.
|
||||
|
||||
"""
|
||||
def flist_iter():
|
||||
for pair in fl:
|
||||
obj, force = pair
|
||||
if isinstance(obj, ReferenceFrame):
|
||||
yield obj.ang_vel_in(ref_frame), force
|
||||
elif isinstance(obj, Point):
|
||||
yield obj.vel(ref_frame), force
|
||||
else:
|
||||
raise TypeError('First entry in each forcelist pair must '
|
||||
'be a point or frame.')
|
||||
|
||||
if not fl:
|
||||
vel_list, f_list = (), ()
|
||||
else:
|
||||
unzip = lambda l: list(zip(*l)) if l[0] else [(), ()]
|
||||
vel_list, f_list = unzip(list(flist_iter()))
|
||||
return vel_list, f_list
|
||||
|
||||
|
||||
def _validate_coordinates(coordinates=None, speeds=None, check_duplicates=True,
|
||||
is_dynamicsymbols=True, u_auxiliary=None):
|
||||
"""Validate the generalized coordinates and generalized speeds.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
coordinates : iterable, optional
|
||||
Generalized coordinates to be validated.
|
||||
speeds : iterable, optional
|
||||
Generalized speeds to be validated.
|
||||
check_duplicates : bool, optional
|
||||
Checks if there are duplicates in the generalized coordinates and
|
||||
generalized speeds. If so it will raise a ValueError. The default is
|
||||
True.
|
||||
is_dynamicsymbols : iterable, optional
|
||||
Checks if all the generalized coordinates and generalized speeds are
|
||||
dynamicsymbols. If any is not a dynamicsymbol, a ValueError will be
|
||||
raised. The default is True.
|
||||
u_auxiliary : iterable, optional
|
||||
Auxiliary generalized speeds to be validated.
|
||||
|
||||
"""
|
||||
t_set = {dynamicsymbols._t}
|
||||
# Convert input to iterables
|
||||
if coordinates is None:
|
||||
coordinates = []
|
||||
elif not iterable(coordinates):
|
||||
coordinates = [coordinates]
|
||||
if speeds is None:
|
||||
speeds = []
|
||||
elif not iterable(speeds):
|
||||
speeds = [speeds]
|
||||
if u_auxiliary is None:
|
||||
u_auxiliary = []
|
||||
elif not iterable(u_auxiliary):
|
||||
u_auxiliary = [u_auxiliary]
|
||||
|
||||
msgs = []
|
||||
if check_duplicates: # Check for duplicates
|
||||
seen = set()
|
||||
coord_duplicates = {x for x in coordinates if x in seen or seen.add(x)}
|
||||
seen = set()
|
||||
speed_duplicates = {x for x in speeds if x in seen or seen.add(x)}
|
||||
seen = set()
|
||||
aux_duplicates = {x for x in u_auxiliary if x in seen or seen.add(x)}
|
||||
overlap_coords = set(coordinates).intersection(speeds)
|
||||
overlap_aux = set(coordinates).union(speeds).intersection(u_auxiliary)
|
||||
if coord_duplicates:
|
||||
msgs.append(f'The generalized coordinates {coord_duplicates} are '
|
||||
f'duplicated, all generalized coordinates should be '
|
||||
f'unique.')
|
||||
if speed_duplicates:
|
||||
msgs.append(f'The generalized speeds {speed_duplicates} are '
|
||||
f'duplicated, all generalized speeds should be unique.')
|
||||
if aux_duplicates:
|
||||
msgs.append(f'The auxiliary speeds {aux_duplicates} are duplicated,'
|
||||
f' all auxiliary speeds should be unique.')
|
||||
if overlap_coords:
|
||||
msgs.append(f'{overlap_coords} are defined as both generalized '
|
||||
f'coordinates and generalized speeds.')
|
||||
if overlap_aux:
|
||||
msgs.append(f'The auxiliary speeds {overlap_aux} are also defined '
|
||||
f'as generalized coordinates or generalized speeds.')
|
||||
if is_dynamicsymbols: # Check whether all coordinates are dynamicsymbols
|
||||
for coordinate in coordinates:
|
||||
if not (isinstance(coordinate, (AppliedUndef, Derivative)) and
|
||||
coordinate.free_symbols == t_set):
|
||||
msgs.append(f'Generalized coordinate "{coordinate}" is not a '
|
||||
f'dynamicsymbol.')
|
||||
for speed in speeds:
|
||||
if not (isinstance(speed, (AppliedUndef, Derivative)) and
|
||||
speed.free_symbols == t_set):
|
||||
msgs.append(
|
||||
f'Generalized speed "{speed}" is not a dynamicsymbol.')
|
||||
for aux in u_auxiliary:
|
||||
if not (isinstance(aux, (AppliedUndef, Derivative)) and
|
||||
aux.free_symbols == t_set):
|
||||
msgs.append(
|
||||
f'Auxiliary speed "{aux}" is not a dynamicsymbol.')
|
||||
if msgs:
|
||||
raise ValueError('\n'.join(msgs))
|
||||
|
||||
|
||||
def _parse_linear_solver(linear_solver):
|
||||
"""Helper function to retrieve a specified linear solver."""
|
||||
if callable(linear_solver):
|
||||
return linear_solver
|
||||
return lambda A, b: Matrix.solve(A, b, method=linear_solver)
|
||||
Reference in New Issue
Block a user