|
from .vector import Vector, _check_vector |
|
from .frame import _check_frame |
|
from warnings import warn |
|
from sympy.utilities.misc import filldedent |
|
|
|
__all__ = ['Point'] |
|
|
|
|
|
class Point: |
|
"""This object represents a point in a dynamic system. |
|
|
|
It stores the: position, velocity, and acceleration of a point. |
|
The position is a vector defined as the vector distance from a parent |
|
point to this point. |
|
|
|
Parameters |
|
========== |
|
|
|
name : string |
|
The display name of the Point |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.physics.vector import Point, ReferenceFrame, dynamicsymbols |
|
>>> from sympy.physics.vector import init_vprinting |
|
>>> init_vprinting(pretty_print=False) |
|
>>> N = ReferenceFrame('N') |
|
>>> O = Point('O') |
|
>>> P = Point('P') |
|
>>> u1, u2, u3 = dynamicsymbols('u1 u2 u3') |
|
>>> O.set_vel(N, u1 * N.x + u2 * N.y + u3 * N.z) |
|
>>> O.acc(N) |
|
u1'*N.x + u2'*N.y + u3'*N.z |
|
|
|
``symbols()`` can be used to create multiple Points in a single step, for |
|
example: |
|
|
|
>>> from sympy.physics.vector import Point, ReferenceFrame, dynamicsymbols |
|
>>> from sympy.physics.vector import init_vprinting |
|
>>> init_vprinting(pretty_print=False) |
|
>>> from sympy import symbols |
|
>>> N = ReferenceFrame('N') |
|
>>> u1, u2 = dynamicsymbols('u1 u2') |
|
>>> A, B = symbols('A B', cls=Point) |
|
>>> type(A) |
|
<class 'sympy.physics.vector.point.Point'> |
|
>>> A.set_vel(N, u1 * N.x + u2 * N.y) |
|
>>> B.set_vel(N, u2 * N.x + u1 * N.y) |
|
>>> A.acc(N) - B.acc(N) |
|
(u1' - u2')*N.x + (-u1' + u2')*N.y |
|
|
|
""" |
|
|
|
def __init__(self, name): |
|
"""Initialization of a Point object. """ |
|
self.name = name |
|
self._pos_dict = {} |
|
self._vel_dict = {} |
|
self._acc_dict = {} |
|
self._pdlist = [self._pos_dict, self._vel_dict, self._acc_dict] |
|
|
|
def __str__(self): |
|
return self.name |
|
|
|
__repr__ = __str__ |
|
|
|
def _check_point(self, other): |
|
if not isinstance(other, Point): |
|
raise TypeError('A Point must be supplied') |
|
|
|
def _pdict_list(self, other, num): |
|
"""Returns a list of points that gives the shortest path with respect |
|
to position, velocity, or acceleration from this point to the provided |
|
point. |
|
|
|
Parameters |
|
========== |
|
other : Point |
|
A point that may be related to this point by position, velocity, or |
|
acceleration. |
|
num : integer |
|
0 for searching the position tree, 1 for searching the velocity |
|
tree, and 2 for searching the acceleration tree. |
|
|
|
Returns |
|
======= |
|
list of Points |
|
A sequence of points from self to other. |
|
|
|
Notes |
|
===== |
|
|
|
It is not clear if num = 1 or num = 2 actually works because the keys |
|
to ``_vel_dict`` and ``_acc_dict`` are :class:`ReferenceFrame` objects |
|
which do not have the ``_pdlist`` attribute. |
|
|
|
""" |
|
outlist = [[self]] |
|
oldlist = [[]] |
|
while outlist != oldlist: |
|
oldlist = outlist.copy() |
|
for v in outlist: |
|
templist = v[-1]._pdlist[num].keys() |
|
for v2 in templist: |
|
if not v.__contains__(v2): |
|
littletemplist = v + [v2] |
|
if not outlist.__contains__(littletemplist): |
|
outlist.append(littletemplist) |
|
for v in oldlist: |
|
if v[-1] != other: |
|
outlist.remove(v) |
|
outlist.sort(key=len) |
|
if len(outlist) != 0: |
|
return outlist[0] |
|
raise ValueError('No Connecting Path found between ' + other.name + |
|
' and ' + self.name) |
|
|
|
def a1pt_theory(self, otherpoint, outframe, interframe): |
|
"""Sets the acceleration of this point with the 1-point theory. |
|
|
|
The 1-point theory for point acceleration looks like this: |
|
|
|
^N a^P = ^B a^P + ^N a^O + ^N alpha^B x r^OP + ^N omega^B x (^N omega^B |
|
x r^OP) + 2 ^N omega^B x ^B v^P |
|
|
|
where O is a point fixed in B, P is a point moving in B, and B is |
|
rotating in frame N. |
|
|
|
Parameters |
|
========== |
|
|
|
otherpoint : Point |
|
The first point of the 1-point theory (O) |
|
outframe : ReferenceFrame |
|
The frame we want this point's acceleration defined in (N) |
|
fixedframe : ReferenceFrame |
|
The intermediate frame in this calculation (B) |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.physics.vector import Point, ReferenceFrame |
|
>>> from sympy.physics.vector import dynamicsymbols |
|
>>> from sympy.physics.vector import init_vprinting |
|
>>> init_vprinting(pretty_print=False) |
|
>>> q = dynamicsymbols('q') |
|
>>> q2 = dynamicsymbols('q2') |
|
>>> qd = dynamicsymbols('q', 1) |
|
>>> q2d = dynamicsymbols('q2', 1) |
|
>>> N = ReferenceFrame('N') |
|
>>> B = ReferenceFrame('B') |
|
>>> B.set_ang_vel(N, 5 * B.y) |
|
>>> O = Point('O') |
|
>>> P = O.locatenew('P', q * B.x + q2 * B.y) |
|
>>> P.set_vel(B, qd * B.x + q2d * B.y) |
|
>>> O.set_vel(N, 0) |
|
>>> P.a1pt_theory(O, N, B) |
|
(-25*q + q'')*B.x + q2''*B.y - 10*q'*B.z |
|
|
|
""" |
|
|
|
_check_frame(outframe) |
|
_check_frame(interframe) |
|
self._check_point(otherpoint) |
|
dist = self.pos_from(otherpoint) |
|
v = self.vel(interframe) |
|
a1 = otherpoint.acc(outframe) |
|
a2 = self.acc(interframe) |
|
omega = interframe.ang_vel_in(outframe) |
|
alpha = interframe.ang_acc_in(outframe) |
|
self.set_acc(outframe, a2 + 2 * (omega.cross(v)) + a1 + |
|
(alpha.cross(dist)) + (omega.cross(omega.cross(dist)))) |
|
return self.acc(outframe) |
|
|
|
def a2pt_theory(self, otherpoint, outframe, fixedframe): |
|
"""Sets the acceleration of this point with the 2-point theory. |
|
|
|
The 2-point theory for point acceleration looks like this: |
|
|
|
^N a^P = ^N a^O + ^N alpha^B x r^OP + ^N omega^B x (^N omega^B x r^OP) |
|
|
|
where O and P are both points fixed in frame B, which is rotating in |
|
frame N. |
|
|
|
Parameters |
|
========== |
|
|
|
otherpoint : Point |
|
The first point of the 2-point theory (O) |
|
outframe : ReferenceFrame |
|
The frame we want this point's acceleration defined in (N) |
|
fixedframe : ReferenceFrame |
|
The frame in which both points are fixed (B) |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.physics.vector import Point, ReferenceFrame, dynamicsymbols |
|
>>> from sympy.physics.vector import init_vprinting |
|
>>> init_vprinting(pretty_print=False) |
|
>>> q = dynamicsymbols('q') |
|
>>> qd = dynamicsymbols('q', 1) |
|
>>> N = ReferenceFrame('N') |
|
>>> B = N.orientnew('B', 'Axis', [q, N.z]) |
|
>>> O = Point('O') |
|
>>> P = O.locatenew('P', 10 * B.x) |
|
>>> O.set_vel(N, 5 * N.x) |
|
>>> P.a2pt_theory(O, N, B) |
|
- 10*q'**2*B.x + 10*q''*B.y |
|
|
|
""" |
|
|
|
_check_frame(outframe) |
|
_check_frame(fixedframe) |
|
self._check_point(otherpoint) |
|
dist = self.pos_from(otherpoint) |
|
a = otherpoint.acc(outframe) |
|
omega = fixedframe.ang_vel_in(outframe) |
|
alpha = fixedframe.ang_acc_in(outframe) |
|
self.set_acc(outframe, a + (alpha.cross(dist)) + |
|
(omega.cross(omega.cross(dist)))) |
|
return self.acc(outframe) |
|
|
|
def acc(self, frame): |
|
"""The acceleration Vector of this Point in a ReferenceFrame. |
|
|
|
Parameters |
|
========== |
|
|
|
frame : ReferenceFrame |
|
The frame in which the returned acceleration vector will be defined |
|
in. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.physics.vector import Point, ReferenceFrame |
|
>>> N = ReferenceFrame('N') |
|
>>> p1 = Point('p1') |
|
>>> p1.set_acc(N, 10 * N.x) |
|
>>> p1.acc(N) |
|
10*N.x |
|
|
|
""" |
|
|
|
_check_frame(frame) |
|
if not (frame in self._acc_dict): |
|
if self.vel(frame) != 0: |
|
return (self._vel_dict[frame]).dt(frame) |
|
else: |
|
return Vector(0) |
|
return self._acc_dict[frame] |
|
|
|
def locatenew(self, name, value): |
|
"""Creates a new point with a position defined from this point. |
|
|
|
Parameters |
|
========== |
|
|
|
name : str |
|
The name for the new point |
|
value : Vector |
|
The position of the new point relative to this point |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.physics.vector import ReferenceFrame, Point |
|
>>> N = ReferenceFrame('N') |
|
>>> P1 = Point('P1') |
|
>>> P2 = P1.locatenew('P2', 10 * N.x) |
|
|
|
""" |
|
|
|
if not isinstance(name, str): |
|
raise TypeError('Must supply a valid name') |
|
if value == 0: |
|
value = Vector(0) |
|
value = _check_vector(value) |
|
p = Point(name) |
|
p.set_pos(self, value) |
|
self.set_pos(p, -value) |
|
return p |
|
|
|
def pos_from(self, otherpoint): |
|
"""Returns a Vector distance between this Point and the other Point. |
|
|
|
Parameters |
|
========== |
|
|
|
otherpoint : Point |
|
The otherpoint we are locating this one relative to |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.physics.vector import Point, ReferenceFrame |
|
>>> N = ReferenceFrame('N') |
|
>>> p1 = Point('p1') |
|
>>> p2 = Point('p2') |
|
>>> p1.set_pos(p2, 10 * N.x) |
|
>>> p1.pos_from(p2) |
|
10*N.x |
|
|
|
""" |
|
|
|
outvec = Vector(0) |
|
plist = self._pdict_list(otherpoint, 0) |
|
for i in range(len(plist) - 1): |
|
outvec += plist[i]._pos_dict[plist[i + 1]] |
|
return outvec |
|
|
|
def set_acc(self, frame, value): |
|
"""Used to set the acceleration of this Point in a ReferenceFrame. |
|
|
|
Parameters |
|
========== |
|
|
|
frame : ReferenceFrame |
|
The frame in which this point's acceleration is defined |
|
value : Vector |
|
The vector value of this point's acceleration in the frame |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.physics.vector import Point, ReferenceFrame |
|
>>> N = ReferenceFrame('N') |
|
>>> p1 = Point('p1') |
|
>>> p1.set_acc(N, 10 * N.x) |
|
>>> p1.acc(N) |
|
10*N.x |
|
|
|
""" |
|
|
|
if value == 0: |
|
value = Vector(0) |
|
value = _check_vector(value) |
|
_check_frame(frame) |
|
self._acc_dict.update({frame: value}) |
|
|
|
def set_pos(self, otherpoint, value): |
|
"""Used to set the position of this point w.r.t. another point. |
|
|
|
Parameters |
|
========== |
|
|
|
otherpoint : Point |
|
The other point which this point's location is defined relative to |
|
value : Vector |
|
The vector which defines the location of this point |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.physics.vector import Point, ReferenceFrame |
|
>>> N = ReferenceFrame('N') |
|
>>> p1 = Point('p1') |
|
>>> p2 = Point('p2') |
|
>>> p1.set_pos(p2, 10 * N.x) |
|
>>> p1.pos_from(p2) |
|
10*N.x |
|
|
|
""" |
|
|
|
if value == 0: |
|
value = Vector(0) |
|
value = _check_vector(value) |
|
self._check_point(otherpoint) |
|
self._pos_dict.update({otherpoint: value}) |
|
otherpoint._pos_dict.update({self: -value}) |
|
|
|
def set_vel(self, frame, value): |
|
"""Sets the velocity Vector of this Point in a ReferenceFrame. |
|
|
|
Parameters |
|
========== |
|
|
|
frame : ReferenceFrame |
|
The frame in which this point's velocity is defined |
|
value : Vector |
|
The vector value of this point's velocity in the frame |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.physics.vector import Point, ReferenceFrame |
|
>>> N = ReferenceFrame('N') |
|
>>> p1 = Point('p1') |
|
>>> p1.set_vel(N, 10 * N.x) |
|
>>> p1.vel(N) |
|
10*N.x |
|
|
|
""" |
|
|
|
if value == 0: |
|
value = Vector(0) |
|
value = _check_vector(value) |
|
_check_frame(frame) |
|
self._vel_dict.update({frame: value}) |
|
|
|
def v1pt_theory(self, otherpoint, outframe, interframe): |
|
"""Sets the velocity of this point with the 1-point theory. |
|
|
|
The 1-point theory for point velocity looks like this: |
|
|
|
^N v^P = ^B v^P + ^N v^O + ^N omega^B x r^OP |
|
|
|
where O is a point fixed in B, P is a point moving in B, and B is |
|
rotating in frame N. |
|
|
|
Parameters |
|
========== |
|
|
|
otherpoint : Point |
|
The first point of the 1-point theory (O) |
|
outframe : ReferenceFrame |
|
The frame we want this point's velocity defined in (N) |
|
interframe : ReferenceFrame |
|
The intermediate frame in this calculation (B) |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.physics.vector import Point, ReferenceFrame |
|
>>> from sympy.physics.vector import dynamicsymbols |
|
>>> from sympy.physics.vector import init_vprinting |
|
>>> init_vprinting(pretty_print=False) |
|
>>> q = dynamicsymbols('q') |
|
>>> q2 = dynamicsymbols('q2') |
|
>>> qd = dynamicsymbols('q', 1) |
|
>>> q2d = dynamicsymbols('q2', 1) |
|
>>> N = ReferenceFrame('N') |
|
>>> B = ReferenceFrame('B') |
|
>>> B.set_ang_vel(N, 5 * B.y) |
|
>>> O = Point('O') |
|
>>> P = O.locatenew('P', q * B.x + q2 * B.y) |
|
>>> P.set_vel(B, qd * B.x + q2d * B.y) |
|
>>> O.set_vel(N, 0) |
|
>>> P.v1pt_theory(O, N, B) |
|
q'*B.x + q2'*B.y - 5*q*B.z |
|
|
|
""" |
|
|
|
_check_frame(outframe) |
|
_check_frame(interframe) |
|
self._check_point(otherpoint) |
|
dist = self.pos_from(otherpoint) |
|
v1 = self.vel(interframe) |
|
v2 = otherpoint.vel(outframe) |
|
omega = interframe.ang_vel_in(outframe) |
|
self.set_vel(outframe, v1 + v2 + (omega.cross(dist))) |
|
return self.vel(outframe) |
|
|
|
def v2pt_theory(self, otherpoint, outframe, fixedframe): |
|
"""Sets the velocity of this point with the 2-point theory. |
|
|
|
The 2-point theory for point velocity looks like this: |
|
|
|
^N v^P = ^N v^O + ^N omega^B x r^OP |
|
|
|
where O and P are both points fixed in frame B, which is rotating in |
|
frame N. |
|
|
|
Parameters |
|
========== |
|
|
|
otherpoint : Point |
|
The first point of the 2-point theory (O) |
|
outframe : ReferenceFrame |
|
The frame we want this point's velocity defined in (N) |
|
fixedframe : ReferenceFrame |
|
The frame in which both points are fixed (B) |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.physics.vector import Point, ReferenceFrame, dynamicsymbols |
|
>>> from sympy.physics.vector import init_vprinting |
|
>>> init_vprinting(pretty_print=False) |
|
>>> q = dynamicsymbols('q') |
|
>>> qd = dynamicsymbols('q', 1) |
|
>>> N = ReferenceFrame('N') |
|
>>> B = N.orientnew('B', 'Axis', [q, N.z]) |
|
>>> O = Point('O') |
|
>>> P = O.locatenew('P', 10 * B.x) |
|
>>> O.set_vel(N, 5 * N.x) |
|
>>> P.v2pt_theory(O, N, B) |
|
5*N.x + 10*q'*B.y |
|
|
|
""" |
|
|
|
_check_frame(outframe) |
|
_check_frame(fixedframe) |
|
self._check_point(otherpoint) |
|
dist = self.pos_from(otherpoint) |
|
v = otherpoint.vel(outframe) |
|
omega = fixedframe.ang_vel_in(outframe) |
|
self.set_vel(outframe, v + (omega.cross(dist))) |
|
return self.vel(outframe) |
|
|
|
def vel(self, frame): |
|
"""The velocity Vector of this Point in the ReferenceFrame. |
|
|
|
Parameters |
|
========== |
|
|
|
frame : ReferenceFrame |
|
The frame in which the returned velocity vector will be defined in |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.physics.vector import Point, ReferenceFrame, dynamicsymbols |
|
>>> N = ReferenceFrame('N') |
|
>>> p1 = Point('p1') |
|
>>> p1.set_vel(N, 10 * N.x) |
|
>>> p1.vel(N) |
|
10*N.x |
|
|
|
Velocities will be automatically calculated if possible, otherwise a |
|
``ValueError`` will be returned. If it is possible to calculate |
|
multiple different velocities from the relative points, the points |
|
defined most directly relative to this point will be used. In the case |
|
of inconsistent relative positions of points, incorrect velocities may |
|
be returned. It is up to the user to define prior relative positions |
|
and velocities of points in a self-consistent way. |
|
|
|
>>> p = Point('p') |
|
>>> q = dynamicsymbols('q') |
|
>>> p.set_vel(N, 10 * N.x) |
|
>>> p2 = Point('p2') |
|
>>> p2.set_pos(p, q*N.x) |
|
>>> p2.vel(N) |
|
(Derivative(q(t), t) + 10)*N.x |
|
|
|
""" |
|
|
|
_check_frame(frame) |
|
if not (frame in self._vel_dict): |
|
valid_neighbor_found = False |
|
is_cyclic = False |
|
visited = [] |
|
queue = [self] |
|
candidate_neighbor = [] |
|
while queue: |
|
node = queue.pop(0) |
|
if node not in visited: |
|
visited.append(node) |
|
for neighbor, neighbor_pos in node._pos_dict.items(): |
|
if neighbor in visited: |
|
continue |
|
try: |
|
|
|
neighbor_pos.express(frame) |
|
except ValueError: |
|
continue |
|
if neighbor in queue: |
|
is_cyclic = True |
|
try: |
|
|
|
neighbor_velocity = neighbor._vel_dict[frame] |
|
except KeyError: |
|
queue.append(neighbor) |
|
continue |
|
candidate_neighbor.append(neighbor) |
|
if not valid_neighbor_found: |
|
self.set_vel(frame, self.pos_from(neighbor).dt(frame) + neighbor_velocity) |
|
valid_neighbor_found = True |
|
if is_cyclic: |
|
warn(filldedent(""" |
|
Kinematic loops are defined among the positions of points. This |
|
is likely not desired and may cause errors in your calculations. |
|
""")) |
|
if len(candidate_neighbor) > 1: |
|
warn(filldedent(f""" |
|
Velocity of {self.name} automatically calculated based on point |
|
{candidate_neighbor[0].name} but it is also possible from |
|
points(s): {str(candidate_neighbor[1:])}. Velocities from these |
|
points are not necessarily the same. This may cause errors in |
|
your calculations.""")) |
|
if valid_neighbor_found: |
|
return self._vel_dict[frame] |
|
else: |
|
raise ValueError(filldedent(f""" |
|
Velocity of point {self.name} has not been defined in |
|
ReferenceFrame {frame.name}.""")) |
|
|
|
return self._vel_dict[frame] |
|
|
|
def partial_velocity(self, frame, *gen_speeds): |
|
"""Returns the partial velocities of the linear velocity vector of this |
|
point in the given frame with respect to one or more provided |
|
generalized speeds. |
|
|
|
Parameters |
|
========== |
|
frame : ReferenceFrame |
|
The frame with which the velocity is defined in. |
|
gen_speeds : functions of time |
|
The generalized speeds. |
|
|
|
Returns |
|
======= |
|
partial_velocities : tuple of Vector |
|
The partial velocity vectors corresponding to the provided |
|
generalized speeds. |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy.physics.vector import ReferenceFrame, Point |
|
>>> from sympy.physics.vector import dynamicsymbols |
|
>>> N = ReferenceFrame('N') |
|
>>> A = ReferenceFrame('A') |
|
>>> p = Point('p') |
|
>>> u1, u2 = dynamicsymbols('u1, u2') |
|
>>> p.set_vel(N, u1 * N.x + u2 * A.y) |
|
>>> p.partial_velocity(N, u1) |
|
N.x |
|
>>> p.partial_velocity(N, u1, u2) |
|
(N.x, A.y) |
|
|
|
""" |
|
|
|
from sympy.physics.vector.functions import partial_velocity |
|
|
|
vel = self.vel(frame) |
|
partials = partial_velocity([vel], gen_speeds, frame)[0] |
|
|
|
if len(partials) == 1: |
|
return partials[0] |
|
else: |
|
return tuple(partials) |
|
|