123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725 |
- 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.core.backend import (Matrix, sympify, Mul, Derivative, sin, cos,
- tan, AppliedUndef, S)
- __all__ = ['inertia',
- 'inertia_of_point_mass',
- '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):
- """Simple way to create inertia Dyadic object.
- Explanation
- ===========
- If you don't know what a Dyadic is, just treat this like the inertia
- tensor. Then, do the easy thing and define it in a body-fixed frame.
- Parameters
- ==========
- frame : ReferenceFrame
- The frame the inertia is defined in
- ixx : Sympifyable
- the xx element in the inertia dyadic
- iyy : Sympifyable
- the yy element in the inertia dyadic
- izz : Sympifyable
- the zz element in the inertia dyadic
- ixy : Sympifyable
- the xy element in the inertia dyadic
- iyz : Sympifyable
- the yz element in the inertia dyadic
- izx : Sympifyable
- the zx element in the inertia dyadic
- Examples
- ========
- >>> from sympy.physics.mechanics import ReferenceFrame, inertia
- >>> N = ReferenceFrame('N')
- >>> inertia(N, 1, 2, 3)
- (N.x|N.x) + 2*(N.y|N.y) + 3*(N.z|N.z)
- """
- if not isinstance(frame, ReferenceFrame):
- raise TypeError('Need to define the inertia in a frame')
- ol = sympify(ixx) * (frame.x | frame.x)
- ol += sympify(ixy) * (frame.x | frame.y)
- ol += sympify(izx) * (frame.x | frame.z)
- ol += sympify(ixy) * (frame.y | frame.x)
- ol += sympify(iyy) * (frame.y | frame.y)
- ol += sympify(iyz) * (frame.y | frame.z)
- ol += sympify(izx) * (frame.z | frame.x)
- ol += sympify(iyz) * (frame.z | frame.y)
- ol += sympify(izz) * (frame.z | frame.z)
- return ol
- def inertia_of_point_mass(mass, pos_vec, frame):
- """Inertia dyadic of a point mass relative to point O.
- Parameters
- ==========
- mass : Sympifyable
- Mass of the point mass
- pos_vec : Vector
- Position from point O to point mass
- frame : ReferenceFrame
- Reference frame to express the dyadic in
- Examples
- ========
- >>> from sympy import symbols
- >>> from sympy.physics.mechanics import ReferenceFrame, inertia_of_point_mass
- >>> N = ReferenceFrame('N')
- >>> r, m = symbols('r m')
- >>> px = r * N.x
- >>> inertia_of_point_mass(m, px, N)
- m*r**2*(N.y|N.y) + m*r**2*(N.z|N.z)
- """
- return mass * (((frame.x | frame.x) + (frame.y | frame.y) +
- (frame.z | frame.z)) * (pos_vec & pos_vec) -
- (pos_vec | pos_vec))
- 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):
- """
- Returns a list of gravity forces given the acceleration
- due to gravity and any number of particles or rigidbodies.
- Example
- =======
- >>> from sympy.physics.mechanics import ReferenceFrame, Point, Particle, outer, RigidBody
- >>> from sympy.physics.mechanics.functions import gravity
- >>> from sympy import symbols
- >>> N = ReferenceFrame('N')
- >>> m, M, g = symbols('m M g')
- >>> F1, F2 = symbols('F1 F2')
- >>> po = Point('po')
- >>> pa = Particle('pa', po, m)
- >>> A = ReferenceFrame('A')
- >>> P = Point('P')
- >>> I = outer(A.x, A.x)
- >>> B = RigidBody('B', P, A, M, (I, P))
- >>> forceList = [(po, F1), (P, F2)]
- >>> forceList.extend(gravity(g*N.y, pa, B))
- >>> forceList
- [(po, F1), (P, F2), (po, g*m*N.y), (P, M*g*N.y)]
- """
- gravity_force = []
- if not bodies:
- raise TypeError("No bodies(instances of Particle or Rigidbody) were passed.")
- for e in bodies:
- point = getattr(e, 'masscenter', None)
- if point is None:
- point = e.point
- gravity_force.append((point, e.mass*acceleration))
- return gravity_force
- 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
|