| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575 |
- from sympy import (diff, expand, sin, cos, sympify, eye, zeros,
- ImmutableMatrix as Matrix, MatrixBase)
- from sympy.core.symbol import Symbol
- from sympy.simplify.trigsimp import trigsimp
- from sympy.physics.vector.vector import Vector, _check_vector
- from sympy.utilities.misc import translate
- from warnings import warn
- __all__ = ['CoordinateSym', 'ReferenceFrame']
- class CoordinateSym(Symbol):
- """
- A coordinate symbol/base scalar associated wrt a Reference Frame.
- Ideally, users should not instantiate this class. Instances of
- this class must only be accessed through the corresponding frame
- as 'frame[index]'.
- CoordinateSyms having the same frame and index parameters are equal
- (even though they may be instantiated separately).
- Parameters
- ==========
- name : string
- The display name of the CoordinateSym
- frame : ReferenceFrame
- The reference frame this base scalar belongs to
- index : 0, 1 or 2
- The index of the dimension denoted by this coordinate variable
- Examples
- ========
- >>> from sympy.physics.vector import ReferenceFrame, CoordinateSym
- >>> A = ReferenceFrame('A')
- >>> A[1]
- A_y
- >>> type(A[0])
- <class 'sympy.physics.vector.frame.CoordinateSym'>
- >>> a_y = CoordinateSym('a_y', A, 1)
- >>> a_y == A[1]
- True
- """
- def __new__(cls, name, frame, index):
- # We can't use the cached Symbol.__new__ because this class depends on
- # frame and index, which are not passed to Symbol.__xnew__.
- assumptions = {}
- super()._sanitize(assumptions, cls)
- obj = super().__xnew__(cls, name, **assumptions)
- _check_frame(frame)
- if index not in range(0, 3):
- raise ValueError("Invalid index specified")
- obj._id = (frame, index)
- return obj
- def __getnewargs_ex__(self):
- return (self.name, *self._id), {}
- @property
- def frame(self):
- return self._id[0]
- def __eq__(self, other):
- # Check if the other object is a CoordinateSym of the same frame and
- # same index
- if isinstance(other, CoordinateSym):
- if other._id == self._id:
- return True
- return False
- def __ne__(self, other):
- return not self == other
- def __hash__(self):
- return (self._id[0].__hash__(), self._id[1]).__hash__()
- class ReferenceFrame:
- """A reference frame in classical mechanics.
- ReferenceFrame is a class used to represent a reference frame in classical
- mechanics. It has a standard basis of three unit vectors in the frame's
- x, y, and z directions.
- It also can have a rotation relative to a parent frame; this rotation is
- defined by a direction cosine matrix relating this frame's basis vectors to
- the parent frame's basis vectors. It can also have an angular velocity
- vector, defined in another frame.
- """
- _count = 0
- def __init__(self, name, indices=None, latexs=None, variables=None):
- """ReferenceFrame initialization method.
- A ReferenceFrame has a set of orthonormal basis vectors, along with
- orientations relative to other ReferenceFrames and angular velocities
- relative to other ReferenceFrames.
- Parameters
- ==========
- indices : tuple of str
- Enables the reference frame's basis unit vectors to be accessed by
- Python's square bracket indexing notation using the provided three
- indice strings and alters the printing of the unit vectors to
- reflect this choice.
- latexs : tuple of str
- Alters the LaTeX printing of the reference frame's basis unit
- vectors to the provided three valid LaTeX strings.
- Examples
- ========
- >>> from sympy.physics.vector import ReferenceFrame, vlatex
- >>> N = ReferenceFrame('N')
- >>> N.x
- N.x
- >>> O = ReferenceFrame('O', indices=('1', '2', '3'))
- >>> O.x
- O['1']
- >>> O['1']
- O['1']
- >>> P = ReferenceFrame('P', latexs=('A1', 'A2', 'A3'))
- >>> vlatex(P.x)
- 'A1'
- ``symbols()`` can be used to create multiple Reference Frames in one
- step, for example:
- >>> from sympy.physics.vector import ReferenceFrame
- >>> from sympy import symbols
- >>> A, B, C = symbols('A B C', cls=ReferenceFrame)
- >>> D, E = symbols('D E', cls=ReferenceFrame, indices=('1', '2', '3'))
- >>> A[0]
- A_x
- >>> D.x
- D['1']
- >>> E.y
- E['2']
- >>> type(A) == type(D)
- True
- Unit dyads for the ReferenceFrame can be accessed through the attributes ``xx``, ``xy``, etc. For example:
- >>> from sympy.physics.vector import ReferenceFrame
- >>> N = ReferenceFrame('N')
- >>> N.yz
- (N.y|N.z)
- >>> N.zx
- (N.z|N.x)
- >>> P = ReferenceFrame('P', indices=['1', '2', '3'])
- >>> P.xx
- (P['1']|P['1'])
- >>> P.zy
- (P['3']|P['2'])
- Unit dyadic is also accessible via the ``u`` attribute:
- >>> from sympy.physics.vector import ReferenceFrame
- >>> N = ReferenceFrame('N')
- >>> N.u
- (N.x|N.x) + (N.y|N.y) + (N.z|N.z)
- >>> P = ReferenceFrame('P', indices=['1', '2', '3'])
- >>> P.u
- (P['1']|P['1']) + (P['2']|P['2']) + (P['3']|P['3'])
- """
- if not isinstance(name, str):
- raise TypeError('Need to supply a valid name')
- # The if statements below are for custom printing of basis-vectors for
- # each frame.
- # First case, when custom indices are supplied
- if indices is not None:
- if not isinstance(indices, (tuple, list)):
- raise TypeError('Supply the indices as a list')
- if len(indices) != 3:
- raise ValueError('Supply 3 indices')
- for i in indices:
- if not isinstance(i, str):
- raise TypeError('Indices must be strings')
- self.str_vecs = [(name + '[\'' + indices[0] + '\']'),
- (name + '[\'' + indices[1] + '\']'),
- (name + '[\'' + indices[2] + '\']')]
- self.pretty_vecs = [(name.lower() + "_" + indices[0]),
- (name.lower() + "_" + indices[1]),
- (name.lower() + "_" + indices[2])]
- self.latex_vecs = [(r"\mathbf{\hat{%s}_{%s}}" % (name.lower(),
- indices[0])),
- (r"\mathbf{\hat{%s}_{%s}}" % (name.lower(),
- indices[1])),
- (r"\mathbf{\hat{%s}_{%s}}" % (name.lower(),
- indices[2]))]
- self.indices = indices
- # Second case, when no custom indices are supplied
- else:
- self.str_vecs = [(name + '.x'), (name + '.y'), (name + '.z')]
- self.pretty_vecs = [name.lower() + "_x",
- name.lower() + "_y",
- name.lower() + "_z"]
- self.latex_vecs = [(r"\mathbf{\hat{%s}_x}" % name.lower()),
- (r"\mathbf{\hat{%s}_y}" % name.lower()),
- (r"\mathbf{\hat{%s}_z}" % name.lower())]
- self.indices = ['x', 'y', 'z']
- # Different step, for custom latex basis vectors
- if latexs is not None:
- if not isinstance(latexs, (tuple, list)):
- raise TypeError('Supply the indices as a list')
- if len(latexs) != 3:
- raise ValueError('Supply 3 indices')
- for i in latexs:
- if not isinstance(i, str):
- raise TypeError('Latex entries must be strings')
- self.latex_vecs = latexs
- self.name = name
- self._var_dict = {}
- # The _dcm_dict dictionary will only store the dcms of adjacent
- # parent-child relationships. The _dcm_cache dictionary will store
- # calculated dcm along with all content of _dcm_dict for faster
- # retrieval of dcms.
- self._dcm_dict = {}
- self._dcm_cache = {}
- self._ang_vel_dict = {}
- self._ang_acc_dict = {}
- self._dlist = [self._dcm_dict, self._ang_vel_dict, self._ang_acc_dict]
- self._cur = 0
- self._x = Vector([(Matrix([1, 0, 0]), self)])
- self._y = Vector([(Matrix([0, 1, 0]), self)])
- self._z = Vector([(Matrix([0, 0, 1]), self)])
- # Associate coordinate symbols wrt this frame
- if variables is not None:
- if not isinstance(variables, (tuple, list)):
- raise TypeError('Supply the variable names as a list/tuple')
- if len(variables) != 3:
- raise ValueError('Supply 3 variable names')
- for i in variables:
- if not isinstance(i, str):
- raise TypeError('Variable names must be strings')
- else:
- variables = [name + '_x', name + '_y', name + '_z']
- self.varlist = (CoordinateSym(variables[0], self, 0),
- CoordinateSym(variables[1], self, 1),
- CoordinateSym(variables[2], self, 2))
- ReferenceFrame._count += 1
- self.index = ReferenceFrame._count
- def __getitem__(self, ind):
- """
- Returns basis vector for the provided index, if the index is a string.
- If the index is a number, returns the coordinate variable correspon-
- -ding to that index.
- """
- if not isinstance(ind, str):
- if ind < 3:
- return self.varlist[ind]
- else:
- raise ValueError("Invalid index provided")
- if self.indices[0] == ind:
- return self.x
- if self.indices[1] == ind:
- return self.y
- if self.indices[2] == ind:
- return self.z
- else:
- raise ValueError('Not a defined index')
- def __iter__(self):
- return iter([self.x, self.y, self.z])
- def __str__(self):
- """Returns the name of the frame. """
- return self.name
- __repr__ = __str__
- def _dict_list(self, other, num):
- """Returns an inclusive list of reference frames that connect this
- reference frame to the provided reference frame.
- Parameters
- ==========
- other : ReferenceFrame
- The other reference frame to look for a connecting relationship to.
- num : integer
- ``0``, ``1``, and ``2`` will look for orientation, angular
- velocity, and angular acceleration relationships between the two
- frames, respectively.
- Returns
- =======
- list
- Inclusive list of reference frames that connect this reference
- frame to the other reference frame.
- Examples
- ========
- >>> from sympy.physics.vector import ReferenceFrame
- >>> A = ReferenceFrame('A')
- >>> B = ReferenceFrame('B')
- >>> C = ReferenceFrame('C')
- >>> D = ReferenceFrame('D')
- >>> B.orient_axis(A, A.x, 1.0)
- >>> C.orient_axis(B, B.x, 1.0)
- >>> D.orient_axis(C, C.x, 1.0)
- >>> D._dict_list(A, 0)
- [D, C, B, A]
- Raises
- ======
- ValueError
- When no path is found between the two reference frames or ``num``
- is an incorrect value.
- """
- connect_type = {0: 'orientation',
- 1: 'angular velocity',
- 2: 'angular acceleration'}
- if num not in connect_type.keys():
- raise ValueError('Valid values for num are 0, 1, or 2.')
- possible_connecting_paths = [[self]]
- oldlist = [[]]
- while possible_connecting_paths != oldlist:
- oldlist = possible_connecting_paths.copy()
- for frame_list in possible_connecting_paths:
- frames_adjacent_to_last = frame_list[-1]._dlist[num].keys()
- for adjacent_frame in frames_adjacent_to_last:
- if adjacent_frame not in frame_list:
- connecting_path = frame_list + [adjacent_frame]
- if connecting_path not in possible_connecting_paths:
- possible_connecting_paths.append(connecting_path)
- for connecting_path in oldlist:
- if connecting_path[-1] != other:
- possible_connecting_paths.remove(connecting_path)
- possible_connecting_paths.sort(key=len)
- if len(possible_connecting_paths) != 0:
- return possible_connecting_paths[0] # selects the shortest path
- msg = 'No connecting {} path found between {} and {}.'
- raise ValueError(msg.format(connect_type[num], self.name, other.name))
- def _w_diff_dcm(self, otherframe):
- """Angular velocity from time differentiating the DCM. """
- from sympy.physics.vector.functions import dynamicsymbols
- dcm2diff = otherframe.dcm(self)
- diffed = dcm2diff.diff(dynamicsymbols._t)
- angvelmat = diffed * dcm2diff.T
- w1 = trigsimp(expand(angvelmat[7]), recursive=True)
- w2 = trigsimp(expand(angvelmat[2]), recursive=True)
- w3 = trigsimp(expand(angvelmat[3]), recursive=True)
- return Vector([(Matrix([w1, w2, w3]), otherframe)])
- def variable_map(self, otherframe):
- """
- Returns a dictionary which expresses the coordinate variables
- of this frame in terms of the variables of otherframe.
- If Vector.simp is True, returns a simplified version of the mapped
- values. Else, returns them without simplification.
- Simplification of the expressions may take time.
- Parameters
- ==========
- otherframe : ReferenceFrame
- The other frame to map the variables to
- Examples
- ========
- >>> from sympy.physics.vector import ReferenceFrame, dynamicsymbols
- >>> A = ReferenceFrame('A')
- >>> q = dynamicsymbols('q')
- >>> B = A.orientnew('B', 'Axis', [q, A.z])
- >>> A.variable_map(B)
- {A_x: B_x*cos(q(t)) - B_y*sin(q(t)), A_y: B_x*sin(q(t)) + B_y*cos(q(t)), A_z: B_z}
- """
- _check_frame(otherframe)
- if (otherframe, Vector.simp) in self._var_dict:
- return self._var_dict[(otherframe, Vector.simp)]
- else:
- vars_matrix = self.dcm(otherframe) * Matrix(otherframe.varlist)
- mapping = {}
- for i, x in enumerate(self):
- if Vector.simp:
- mapping[self.varlist[i]] = trigsimp(vars_matrix[i],
- method='fu')
- else:
- mapping[self.varlist[i]] = vars_matrix[i]
- self._var_dict[(otherframe, Vector.simp)] = mapping
- return mapping
- def ang_acc_in(self, otherframe):
- """Returns the angular acceleration Vector of the ReferenceFrame.
- Effectively returns the Vector:
- ``N_alpha_B``
- which represent the angular acceleration of B in N, where B is self,
- and N is otherframe.
- Parameters
- ==========
- otherframe : ReferenceFrame
- The ReferenceFrame which the angular acceleration is returned in.
- Examples
- ========
- >>> from sympy.physics.vector import ReferenceFrame
- >>> N = ReferenceFrame('N')
- >>> A = ReferenceFrame('A')
- >>> V = 10 * N.x
- >>> A.set_ang_acc(N, V)
- >>> A.ang_acc_in(N)
- 10*N.x
- """
- _check_frame(otherframe)
- if otherframe in self._ang_acc_dict:
- return self._ang_acc_dict[otherframe]
- else:
- return self.ang_vel_in(otherframe).dt(otherframe)
- def ang_vel_in(self, otherframe):
- """Returns the angular velocity Vector of the ReferenceFrame.
- Effectively returns the Vector:
- ^N omega ^B
- which represent the angular velocity of B in N, where B is self, and
- N is otherframe.
- Parameters
- ==========
- otherframe : ReferenceFrame
- The ReferenceFrame which the angular velocity is returned in.
- Examples
- ========
- >>> from sympy.physics.vector import ReferenceFrame
- >>> N = ReferenceFrame('N')
- >>> A = ReferenceFrame('A')
- >>> V = 10 * N.x
- >>> A.set_ang_vel(N, V)
- >>> A.ang_vel_in(N)
- 10*N.x
- """
- _check_frame(otherframe)
- flist = self._dict_list(otherframe, 1)
- outvec = Vector(0)
- for i in range(len(flist) - 1):
- outvec += flist[i]._ang_vel_dict[flist[i + 1]]
- return outvec
- def dcm(self, otherframe):
- r"""Returns the direction cosine matrix of this reference frame
- relative to the provided reference frame.
- The returned matrix can be used to express the orthogonal unit vectors
- of this frame in terms of the orthogonal unit vectors of
- ``otherframe``.
- Parameters
- ==========
- otherframe : ReferenceFrame
- The reference frame which the direction cosine matrix of this frame
- is formed relative to.
- Examples
- ========
- The following example rotates the reference frame A relative to N by a
- simple rotation and then calculates the direction cosine matrix of N
- relative to A.
- >>> from sympy import symbols, sin, cos
- >>> from sympy.physics.vector import ReferenceFrame
- >>> q1 = symbols('q1')
- >>> N = ReferenceFrame('N')
- >>> A = ReferenceFrame('A')
- >>> A.orient_axis(N, q1, N.x)
- >>> N.dcm(A)
- Matrix([
- [1, 0, 0],
- [0, cos(q1), -sin(q1)],
- [0, sin(q1), cos(q1)]])
- The second row of the above direction cosine matrix represents the
- ``N.y`` unit vector in N expressed in A. Like so:
- >>> Ny = 0*A.x + cos(q1)*A.y - sin(q1)*A.z
- Thus, expressing ``N.y`` in A should return the same result:
- >>> N.y.express(A)
- cos(q1)*A.y - sin(q1)*A.z
- Notes
- =====
- It is important to know what form of the direction cosine matrix is
- returned. If ``B.dcm(A)`` is called, it means the "direction cosine
- matrix of B rotated relative to A". This is the matrix
- :math:`{}^B\mathbf{C}^A` shown in the following relationship:
- .. math::
- \begin{bmatrix}
- \hat{\mathbf{b}}_1 \\
- \hat{\mathbf{b}}_2 \\
- \hat{\mathbf{b}}_3
- \end{bmatrix}
- =
- {}^B\mathbf{C}^A
- \begin{bmatrix}
- \hat{\mathbf{a}}_1 \\
- \hat{\mathbf{a}}_2 \\
- \hat{\mathbf{a}}_3
- \end{bmatrix}.
- :math:`{}^B\mathbf{C}^A` is the matrix that expresses the B unit
- vectors in terms of the A unit vectors.
- """
- _check_frame(otherframe)
- # Check if the dcm wrt that frame has already been calculated
- if otherframe in self._dcm_cache:
- return self._dcm_cache[otherframe]
- flist = self._dict_list(otherframe, 0)
- outdcm = eye(3)
- for i in range(len(flist) - 1):
- outdcm = outdcm * flist[i]._dcm_dict[flist[i + 1]]
- # After calculation, store the dcm in dcm cache for faster future
- # retrieval
- self._dcm_cache[otherframe] = outdcm
- otherframe._dcm_cache[self] = outdcm.T
- return outdcm
- def _dcm(self, parent, parent_orient):
- # If parent.oreint(self) is already defined,then
- # update the _dcm_dict of parent while over write
- # all content of self._dcm_dict and self._dcm_cache
- # with new dcm relation.
- # Else update _dcm_cache and _dcm_dict of both
- # self and parent.
- frames = self._dcm_cache.keys()
- dcm_dict_del = []
- dcm_cache_del = []
- if parent in frames:
- for frame in frames:
- if frame in self._dcm_dict:
- dcm_dict_del += [frame]
- dcm_cache_del += [frame]
- # Reset the _dcm_cache of this frame, and remove it from the
- # _dcm_caches of the frames it is linked to. Also remove it from
- # the _dcm_dict of its parent
- for frame in dcm_dict_del:
- del frame._dcm_dict[self]
- for frame in dcm_cache_del:
- del frame._dcm_cache[self]
- # Reset the _dcm_dict
- self._dcm_dict = self._dlist[0] = {}
- # Reset the _dcm_cache
- self._dcm_cache = {}
- else:
- # Check for loops and raise warning accordingly.
- visited = []
- queue = list(frames)
- cont = True # Flag to control queue loop.
- while queue and cont:
- node = queue.pop(0)
- if node not in visited:
- visited.append(node)
- neighbors = node._dcm_dict.keys()
- for neighbor in neighbors:
- if neighbor == parent:
- warn('Loops are defined among the orientation of '
- 'frames. This is likely not desired and may '
- 'cause errors in your calculations.')
- cont = False
- break
- queue.append(neighbor)
- # Add the dcm relationship to _dcm_dict
- self._dcm_dict.update({parent: parent_orient.T})
- parent._dcm_dict.update({self: parent_orient})
- # Update the dcm cache
- self._dcm_cache.update({parent: parent_orient.T})
- parent._dcm_cache.update({self: parent_orient})
- def orient_axis(self, parent, axis, angle):
- """Sets the orientation of this reference frame with respect to a
- parent reference frame by rotating through an angle about an axis fixed
- in the parent reference frame.
- Parameters
- ==========
- parent : ReferenceFrame
- Reference frame that this reference frame will be rotated relative
- to.
- axis : Vector
- Vector fixed in the parent frame about about which this frame is
- rotated. It need not be a unit vector and the rotation follows the
- right hand rule.
- angle : sympifiable
- Angle in radians by which it the frame is to be rotated.
- Warns
- ======
- UserWarning
- If the orientation creates a kinematic loop.
- Examples
- ========
- Setup variables for the examples:
- >>> from sympy import symbols
- >>> from sympy.physics.vector import ReferenceFrame
- >>> q1 = symbols('q1')
- >>> N = ReferenceFrame('N')
- >>> B = ReferenceFrame('B')
- >>> B.orient_axis(N, N.x, q1)
- The ``orient_axis()`` method generates a direction cosine matrix and
- its transpose which defines the orientation of B relative to N and vice
- versa. Once orient is called, ``dcm()`` outputs the appropriate
- direction cosine matrix:
- >>> B.dcm(N)
- Matrix([
- [1, 0, 0],
- [0, cos(q1), sin(q1)],
- [0, -sin(q1), cos(q1)]])
- >>> N.dcm(B)
- Matrix([
- [1, 0, 0],
- [0, cos(q1), -sin(q1)],
- [0, sin(q1), cos(q1)]])
- The following two lines show that the sense of the rotation can be
- defined by negating the vector direction or the angle. Both lines
- produce the same result.
- >>> B.orient_axis(N, -N.x, q1)
- >>> B.orient_axis(N, N.x, -q1)
- """
- from sympy.physics.vector.functions import dynamicsymbols
- _check_frame(parent)
- if not isinstance(axis, Vector) and isinstance(angle, Vector):
- axis, angle = angle, axis
- axis = _check_vector(axis)
- theta = sympify(angle)
- if not axis.dt(parent) == 0:
- raise ValueError('Axis cannot be time-varying.')
- unit_axis = axis.express(parent).normalize()
- unit_col = unit_axis.args[0][0]
- parent_orient_axis = (
- (eye(3) - unit_col * unit_col.T) * cos(theta) +
- Matrix([[0, -unit_col[2], unit_col[1]],
- [unit_col[2], 0, -unit_col[0]],
- [-unit_col[1], unit_col[0], 0]]) *
- sin(theta) + unit_col * unit_col.T)
- self._dcm(parent, parent_orient_axis)
- thetad = (theta).diff(dynamicsymbols._t)
- wvec = thetad*axis.express(parent).normalize()
- self._ang_vel_dict.update({parent: wvec})
- parent._ang_vel_dict.update({self: -wvec})
- self._var_dict = {}
- def orient_explicit(self, parent, dcm):
- """Sets the orientation of this reference frame relative to another (parent) reference frame
- using a direction cosine matrix that describes the rotation from the parent to the child.
- Parameters
- ==========
- parent : ReferenceFrame
- Reference frame that this reference frame will be rotated relative
- to.
- dcm : Matrix, shape(3, 3)
- Direction cosine matrix that specifies the relative rotation
- between the two reference frames.
- Warns
- ======
- UserWarning
- If the orientation creates a kinematic loop.
- Examples
- ========
- Setup variables for the examples:
- >>> from sympy import symbols, Matrix, sin, cos
- >>> from sympy.physics.vector import ReferenceFrame
- >>> q1 = symbols('q1')
- >>> A = ReferenceFrame('A')
- >>> B = ReferenceFrame('B')
- >>> N = ReferenceFrame('N')
- A simple rotation of ``A`` relative to ``N`` about ``N.x`` is defined
- by the following direction cosine matrix:
- >>> dcm = Matrix([[1, 0, 0],
- ... [0, cos(q1), -sin(q1)],
- ... [0, sin(q1), cos(q1)]])
- >>> A.orient_explicit(N, dcm)
- >>> A.dcm(N)
- Matrix([
- [1, 0, 0],
- [0, cos(q1), sin(q1)],
- [0, -sin(q1), cos(q1)]])
- This is equivalent to using ``orient_axis()``:
- >>> B.orient_axis(N, N.x, q1)
- >>> B.dcm(N)
- Matrix([
- [1, 0, 0],
- [0, cos(q1), sin(q1)],
- [0, -sin(q1), cos(q1)]])
- **Note carefully that** ``N.dcm(B)`` **(the transpose) would be passed
- into** ``orient_explicit()`` **for** ``A.dcm(N)`` **to match**
- ``B.dcm(N)``:
- >>> A.orient_explicit(N, N.dcm(B))
- >>> A.dcm(N)
- Matrix([
- [1, 0, 0],
- [0, cos(q1), sin(q1)],
- [0, -sin(q1), cos(q1)]])
- """
- _check_frame(parent)
- # amounts must be a Matrix type object
- # (e.g. sympy.matrices.dense.MutableDenseMatrix).
- if not isinstance(dcm, MatrixBase):
- raise TypeError("Amounts must be a SymPy Matrix type object.")
- self.orient_dcm(parent, dcm.T)
- def orient_dcm(self, parent, dcm):
- """Sets the orientation of this reference frame relative to another (parent) reference frame
- using a direction cosine matrix that describes the rotation from the child to the parent.
- Parameters
- ==========
- parent : ReferenceFrame
- Reference frame that this reference frame will be rotated relative
- to.
- dcm : Matrix, shape(3, 3)
- Direction cosine matrix that specifies the relative rotation
- between the two reference frames.
- Warns
- ======
- UserWarning
- If the orientation creates a kinematic loop.
- Examples
- ========
- Setup variables for the examples:
- >>> from sympy import symbols, Matrix, sin, cos
- >>> from sympy.physics.vector import ReferenceFrame
- >>> q1 = symbols('q1')
- >>> A = ReferenceFrame('A')
- >>> B = ReferenceFrame('B')
- >>> N = ReferenceFrame('N')
- A simple rotation of ``A`` relative to ``N`` about ``N.x`` is defined
- by the following direction cosine matrix:
- >>> dcm = Matrix([[1, 0, 0],
- ... [0, cos(q1), sin(q1)],
- ... [0, -sin(q1), cos(q1)]])
- >>> A.orient_dcm(N, dcm)
- >>> A.dcm(N)
- Matrix([
- [1, 0, 0],
- [0, cos(q1), sin(q1)],
- [0, -sin(q1), cos(q1)]])
- This is equivalent to using ``orient_axis()``:
- >>> B.orient_axis(N, N.x, q1)
- >>> B.dcm(N)
- Matrix([
- [1, 0, 0],
- [0, cos(q1), sin(q1)],
- [0, -sin(q1), cos(q1)]])
- """
- _check_frame(parent)
- # amounts must be a Matrix type object
- # (e.g. sympy.matrices.dense.MutableDenseMatrix).
- if not isinstance(dcm, MatrixBase):
- raise TypeError("Amounts must be a SymPy Matrix type object.")
- self._dcm(parent, dcm.T)
- wvec = self._w_diff_dcm(parent)
- self._ang_vel_dict.update({parent: wvec})
- parent._ang_vel_dict.update({self: -wvec})
- self._var_dict = {}
- def _rot(self, axis, angle):
- """DCM for simple axis 1,2,or 3 rotations."""
- if axis == 1:
- return Matrix([[1, 0, 0],
- [0, cos(angle), -sin(angle)],
- [0, sin(angle), cos(angle)]])
- elif axis == 2:
- return Matrix([[cos(angle), 0, sin(angle)],
- [0, 1, 0],
- [-sin(angle), 0, cos(angle)]])
- elif axis == 3:
- return Matrix([[cos(angle), -sin(angle), 0],
- [sin(angle), cos(angle), 0],
- [0, 0, 1]])
- def _parse_consecutive_rotations(self, angles, rotation_order):
- """Helper for orient_body_fixed and orient_space_fixed.
- Parameters
- ==========
- angles : 3-tuple of sympifiable
- Three angles in radians used for the successive rotations.
- rotation_order : 3 character string or 3 digit integer
- Order of the rotations. The order can be specified by the strings
- ``'XZX'``, ``'131'``, or the integer ``131``. There are 12 unique
- valid rotation orders.
- Returns
- =======
- amounts : list
- List of sympifiables corresponding to the rotation angles.
- rot_order : list
- List of integers corresponding to the axis of rotation.
- rot_matrices : list
- List of DCM around the given axis with corresponding magnitude.
- """
- amounts = list(angles)
- for i, v in enumerate(amounts):
- if not isinstance(v, Vector):
- amounts[i] = sympify(v)
- approved_orders = ('123', '231', '312', '132', '213', '321', '121',
- '131', '212', '232', '313', '323', '')
- # make sure XYZ => 123
- rot_order = translate(str(rotation_order), 'XYZxyz', '123123')
- if rot_order not in approved_orders:
- raise TypeError('The rotation order is not a valid order.')
- rot_order = [int(r) for r in rot_order]
- if not (len(amounts) == 3 & len(rot_order) == 3):
- raise TypeError('Body orientation takes 3 values & 3 orders')
- rot_matrices = [self._rot(order, amount)
- for (order, amount) in zip(rot_order, amounts)]
- return amounts, rot_order, rot_matrices
- def orient_body_fixed(self, parent, angles, rotation_order):
- """Rotates this reference frame relative to the parent reference frame
- by right hand rotating through three successive body fixed simple axis
- rotations. Each subsequent axis of rotation is about the "body fixed"
- unit vectors of a new intermediate reference frame. This type of
- rotation is also referred to rotating through the `Euler and Tait-Bryan
- Angles`_.
- .. _Euler and Tait-Bryan Angles: https://en.wikipedia.org/wiki/Euler_angles
- The computed angular velocity in this method is by default expressed in
- the child's frame, so it is most preferable to use ``u1 * child.x + u2 *
- child.y + u3 * child.z`` as generalized speeds.
- Parameters
- ==========
- parent : ReferenceFrame
- Reference frame that this reference frame will be rotated relative
- to.
- angles : 3-tuple of sympifiable
- Three angles in radians used for the successive rotations.
- rotation_order : 3 character string or 3 digit integer
- Order of the rotations about each intermediate reference frames'
- unit vectors. The Euler rotation about the X, Z', X'' axes can be
- specified by the strings ``'XZX'``, ``'131'``, or the integer
- ``131``. There are 12 unique valid rotation orders (6 Euler and 6
- Tait-Bryan): zxz, xyx, yzy, zyz, xzx, yxy, xyz, yzx, zxy, xzy, zyx,
- and yxz.
- Warns
- ======
- UserWarning
- If the orientation creates a kinematic loop.
- Examples
- ========
- Setup variables for the examples:
- >>> from sympy import symbols
- >>> from sympy.physics.vector import ReferenceFrame
- >>> q1, q2, q3 = symbols('q1, q2, q3')
- >>> N = ReferenceFrame('N')
- >>> B = ReferenceFrame('B')
- >>> B1 = ReferenceFrame('B1')
- >>> B2 = ReferenceFrame('B2')
- >>> B3 = ReferenceFrame('B3')
- For example, a classic Euler Angle rotation can be done by:
- >>> B.orient_body_fixed(N, (q1, q2, q3), 'XYX')
- >>> B.dcm(N)
- Matrix([
- [ cos(q2), sin(q1)*sin(q2), -sin(q2)*cos(q1)],
- [sin(q2)*sin(q3), -sin(q1)*sin(q3)*cos(q2) + cos(q1)*cos(q3), sin(q1)*cos(q3) + sin(q3)*cos(q1)*cos(q2)],
- [sin(q2)*cos(q3), -sin(q1)*cos(q2)*cos(q3) - sin(q3)*cos(q1), -sin(q1)*sin(q3) + cos(q1)*cos(q2)*cos(q3)]])
- This rotates reference frame B relative to reference frame N through
- ``q1`` about ``N.x``, then rotates B again through ``q2`` about
- ``B.y``, and finally through ``q3`` about ``B.x``. It is equivalent to
- three successive ``orient_axis()`` calls:
- >>> B1.orient_axis(N, N.x, q1)
- >>> B2.orient_axis(B1, B1.y, q2)
- >>> B3.orient_axis(B2, B2.x, q3)
- >>> B3.dcm(N)
- Matrix([
- [ cos(q2), sin(q1)*sin(q2), -sin(q2)*cos(q1)],
- [sin(q2)*sin(q3), -sin(q1)*sin(q3)*cos(q2) + cos(q1)*cos(q3), sin(q1)*cos(q3) + sin(q3)*cos(q1)*cos(q2)],
- [sin(q2)*cos(q3), -sin(q1)*cos(q2)*cos(q3) - sin(q3)*cos(q1), -sin(q1)*sin(q3) + cos(q1)*cos(q2)*cos(q3)]])
- Acceptable rotation orders are of length 3, expressed in as a string
- ``'XYZ'`` or ``'123'`` or integer ``123``. Rotations about an axis
- twice in a row are prohibited.
- >>> B.orient_body_fixed(N, (q1, q2, 0), 'ZXZ')
- >>> B.orient_body_fixed(N, (q1, q2, 0), '121')
- >>> B.orient_body_fixed(N, (q1, q2, q3), 123)
- """
- from sympy.physics.vector.functions import dynamicsymbols
- _check_frame(parent)
- amounts, rot_order, rot_matrices = self._parse_consecutive_rotations(
- angles, rotation_order)
- self._dcm(parent, rot_matrices[0] * rot_matrices[1] * rot_matrices[2])
- rot_vecs = [zeros(3, 1) for _ in range(3)]
- for i, order in enumerate(rot_order):
- rot_vecs[i][order - 1] = amounts[i].diff(dynamicsymbols._t)
- u1, u2, u3 = rot_vecs[2] + rot_matrices[2].T * (
- rot_vecs[1] + rot_matrices[1].T * rot_vecs[0])
- wvec = u1 * self.x + u2 * self.y + u3 * self.z # There is a double -
- self._ang_vel_dict.update({parent: wvec})
- parent._ang_vel_dict.update({self: -wvec})
- self._var_dict = {}
- def orient_space_fixed(self, parent, angles, rotation_order):
- """Rotates this reference frame relative to the parent reference frame
- by right hand rotating through three successive space fixed simple axis
- rotations. Each subsequent axis of rotation is about the "space fixed"
- unit vectors of the parent reference frame.
- The computed angular velocity in this method is by default expressed in
- the child's frame, so it is most preferable to use ``u1 * child.x + u2 *
- child.y + u3 * child.z`` as generalized speeds.
- Parameters
- ==========
- parent : ReferenceFrame
- Reference frame that this reference frame will be rotated relative
- to.
- angles : 3-tuple of sympifiable
- Three angles in radians used for the successive rotations.
- rotation_order : 3 character string or 3 digit integer
- Order of the rotations about the parent reference frame's unit
- vectors. The order can be specified by the strings ``'XZX'``,
- ``'131'``, or the integer ``131``. There are 12 unique valid
- rotation orders.
- Warns
- ======
- UserWarning
- If the orientation creates a kinematic loop.
- Examples
- ========
- Setup variables for the examples:
- >>> from sympy import symbols
- >>> from sympy.physics.vector import ReferenceFrame
- >>> q1, q2, q3 = symbols('q1, q2, q3')
- >>> N = ReferenceFrame('N')
- >>> B = ReferenceFrame('B')
- >>> B1 = ReferenceFrame('B1')
- >>> B2 = ReferenceFrame('B2')
- >>> B3 = ReferenceFrame('B3')
- >>> B.orient_space_fixed(N, (q1, q2, q3), '312')
- >>> B.dcm(N)
- Matrix([
- [ sin(q1)*sin(q2)*sin(q3) + cos(q1)*cos(q3), sin(q1)*cos(q2), sin(q1)*sin(q2)*cos(q3) - sin(q3)*cos(q1)],
- [-sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1), cos(q1)*cos(q2), sin(q1)*sin(q3) + sin(q2)*cos(q1)*cos(q3)],
- [ sin(q3)*cos(q2), -sin(q2), cos(q2)*cos(q3)]])
- is equivalent to:
- >>> B1.orient_axis(N, N.z, q1)
- >>> B2.orient_axis(B1, N.x, q2)
- >>> B3.orient_axis(B2, N.y, q3)
- >>> B3.dcm(N).simplify()
- Matrix([
- [ sin(q1)*sin(q2)*sin(q3) + cos(q1)*cos(q3), sin(q1)*cos(q2), sin(q1)*sin(q2)*cos(q3) - sin(q3)*cos(q1)],
- [-sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1), cos(q1)*cos(q2), sin(q1)*sin(q3) + sin(q2)*cos(q1)*cos(q3)],
- [ sin(q3)*cos(q2), -sin(q2), cos(q2)*cos(q3)]])
- It is worth noting that space-fixed and body-fixed rotations are
- related by the order of the rotations, i.e. the reverse order of body
- fixed will give space fixed and vice versa.
- >>> B.orient_space_fixed(N, (q1, q2, q3), '231')
- >>> B.dcm(N)
- Matrix([
- [cos(q1)*cos(q2), sin(q1)*sin(q3) + sin(q2)*cos(q1)*cos(q3), -sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1)],
- [ -sin(q2), cos(q2)*cos(q3), sin(q3)*cos(q2)],
- [sin(q1)*cos(q2), sin(q1)*sin(q2)*cos(q3) - sin(q3)*cos(q1), sin(q1)*sin(q2)*sin(q3) + cos(q1)*cos(q3)]])
- >>> B.orient_body_fixed(N, (q3, q2, q1), '132')
- >>> B.dcm(N)
- Matrix([
- [cos(q1)*cos(q2), sin(q1)*sin(q3) + sin(q2)*cos(q1)*cos(q3), -sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1)],
- [ -sin(q2), cos(q2)*cos(q3), sin(q3)*cos(q2)],
- [sin(q1)*cos(q2), sin(q1)*sin(q2)*cos(q3) - sin(q3)*cos(q1), sin(q1)*sin(q2)*sin(q3) + cos(q1)*cos(q3)]])
- """
- from sympy.physics.vector.functions import dynamicsymbols
- _check_frame(parent)
- amounts, rot_order, rot_matrices = self._parse_consecutive_rotations(
- angles, rotation_order)
- self._dcm(parent, rot_matrices[2] * rot_matrices[1] * rot_matrices[0])
- rot_vecs = [zeros(3, 1) for _ in range(3)]
- for i, order in enumerate(rot_order):
- rot_vecs[i][order - 1] = amounts[i].diff(dynamicsymbols._t)
- u1, u2, u3 = rot_vecs[0] + rot_matrices[0].T * (
- rot_vecs[1] + rot_matrices[1].T * rot_vecs[2])
- wvec = u1 * self.x + u2 * self.y + u3 * self.z # There is a double -
- self._ang_vel_dict.update({parent: wvec})
- parent._ang_vel_dict.update({self: -wvec})
- self._var_dict = {}
- def orient_quaternion(self, parent, numbers):
- """Sets the orientation of this reference frame relative to a parent
- reference frame via an orientation quaternion. An orientation
- quaternion is defined as a finite rotation a unit vector, ``(lambda_x,
- lambda_y, lambda_z)``, by an angle ``theta``. The orientation
- quaternion is described by four parameters:
- - ``q0 = cos(theta/2)``
- - ``q1 = lambda_x*sin(theta/2)``
- - ``q2 = lambda_y*sin(theta/2)``
- - ``q3 = lambda_z*sin(theta/2)``
- See `Quaternions and Spatial Rotation
- <https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation>`_ on
- Wikipedia for more information.
- Parameters
- ==========
- parent : ReferenceFrame
- Reference frame that this reference frame will be rotated relative
- to.
- numbers : 4-tuple of sympifiable
- The four quaternion scalar numbers as defined above: ``q0``,
- ``q1``, ``q2``, ``q3``.
- Warns
- ======
- UserWarning
- If the orientation creates a kinematic loop.
- Examples
- ========
- Setup variables for the examples:
- >>> from sympy import symbols
- >>> from sympy.physics.vector import ReferenceFrame
- >>> q0, q1, q2, q3 = symbols('q0 q1 q2 q3')
- >>> N = ReferenceFrame('N')
- >>> B = ReferenceFrame('B')
- Set the orientation:
- >>> B.orient_quaternion(N, (q0, q1, q2, q3))
- >>> B.dcm(N)
- Matrix([
- [q0**2 + q1**2 - q2**2 - q3**2, 2*q0*q3 + 2*q1*q2, -2*q0*q2 + 2*q1*q3],
- [ -2*q0*q3 + 2*q1*q2, q0**2 - q1**2 + q2**2 - q3**2, 2*q0*q1 + 2*q2*q3],
- [ 2*q0*q2 + 2*q1*q3, -2*q0*q1 + 2*q2*q3, q0**2 - q1**2 - q2**2 + q3**2]])
- """
- from sympy.physics.vector.functions import dynamicsymbols
- _check_frame(parent)
- numbers = list(numbers)
- for i, v in enumerate(numbers):
- if not isinstance(v, Vector):
- numbers[i] = sympify(v)
- if not (isinstance(numbers, (list, tuple)) & (len(numbers) == 4)):
- raise TypeError('Amounts are a list or tuple of length 4')
- q0, q1, q2, q3 = numbers
- parent_orient_quaternion = (
- Matrix([[q0**2 + q1**2 - q2**2 - q3**2,
- 2 * (q1 * q2 - q0 * q3),
- 2 * (q0 * q2 + q1 * q3)],
- [2 * (q1 * q2 + q0 * q3),
- q0**2 - q1**2 + q2**2 - q3**2,
- 2 * (q2 * q3 - q0 * q1)],
- [2 * (q1 * q3 - q0 * q2),
- 2 * (q0 * q1 + q2 * q3),
- q0**2 - q1**2 - q2**2 + q3**2]]))
- self._dcm(parent, parent_orient_quaternion)
- t = dynamicsymbols._t
- q0, q1, q2, q3 = numbers
- q0d = diff(q0, t)
- q1d = diff(q1, t)
- q2d = diff(q2, t)
- q3d = diff(q3, t)
- w1 = 2 * (q1d * q0 + q2d * q3 - q3d * q2 - q0d * q1)
- w2 = 2 * (q2d * q0 + q3d * q1 - q1d * q3 - q0d * q2)
- w3 = 2 * (q3d * q0 + q1d * q2 - q2d * q1 - q0d * q3)
- wvec = Vector([(Matrix([w1, w2, w3]), self)])
- self._ang_vel_dict.update({parent: wvec})
- parent._ang_vel_dict.update({self: -wvec})
- self._var_dict = {}
- def orient(self, parent, rot_type, amounts, rot_order=''):
- """Sets the orientation of this reference frame relative to another
- (parent) reference frame.
- .. note:: It is now recommended to use the ``.orient_axis,
- .orient_body_fixed, .orient_space_fixed, .orient_quaternion``
- methods for the different rotation types.
- Parameters
- ==========
- parent : ReferenceFrame
- Reference frame that this reference frame will be rotated relative
- to.
- rot_type : str
- The method used to generate the direction cosine matrix. Supported
- methods are:
- - ``'Axis'``: simple rotations about a single common axis
- - ``'DCM'``: for setting the direction cosine matrix directly
- - ``'Body'``: three successive rotations about new intermediate
- axes, also called "Euler and Tait-Bryan angles"
- - ``'Space'``: three successive rotations about the parent
- frames' unit vectors
- - ``'Quaternion'``: rotations defined by four parameters which
- result in a singularity free direction cosine matrix
- amounts :
- Expressions defining the rotation angles or direction cosine
- matrix. These must match the ``rot_type``. See examples below for
- details. The input types are:
- - ``'Axis'``: 2-tuple (expr/sym/func, Vector)
- - ``'DCM'``: Matrix, shape(3,3)
- - ``'Body'``: 3-tuple of expressions, symbols, or functions
- - ``'Space'``: 3-tuple of expressions, symbols, or functions
- - ``'Quaternion'``: 4-tuple of expressions, symbols, or
- functions
- rot_order : str or int, optional
- If applicable, the order of the successive of rotations. The string
- ``'123'`` and integer ``123`` are equivalent, for example. Required
- for ``'Body'`` and ``'Space'``.
- Warns
- ======
- UserWarning
- If the orientation creates a kinematic loop.
- """
- _check_frame(parent)
- approved_orders = ('123', '231', '312', '132', '213', '321', '121',
- '131', '212', '232', '313', '323', '')
- rot_order = translate(str(rot_order), 'XYZxyz', '123123')
- rot_type = rot_type.upper()
- if rot_order not in approved_orders:
- raise TypeError('The supplied order is not an approved type')
- if rot_type == 'AXIS':
- self.orient_axis(parent, amounts[1], amounts[0])
- elif rot_type == 'DCM':
- self.orient_explicit(parent, amounts)
- elif rot_type == 'BODY':
- self.orient_body_fixed(parent, amounts, rot_order)
- elif rot_type == 'SPACE':
- self.orient_space_fixed(parent, amounts, rot_order)
- elif rot_type == 'QUATERNION':
- self.orient_quaternion(parent, amounts)
- else:
- raise NotImplementedError('That is not an implemented rotation')
- def orientnew(self, newname, rot_type, amounts, rot_order='',
- variables=None, indices=None, latexs=None):
- r"""Returns a new reference frame oriented with respect to this
- reference frame.
- See ``ReferenceFrame.orient()`` for detailed examples of how to orient
- reference frames.
- Parameters
- ==========
- newname : str
- Name for the new reference frame.
- rot_type : str
- The method used to generate the direction cosine matrix. Supported
- methods are:
- - ``'Axis'``: simple rotations about a single common axis
- - ``'DCM'``: for setting the direction cosine matrix directly
- - ``'Body'``: three successive rotations about new intermediate
- axes, also called "Euler and Tait-Bryan angles"
- - ``'Space'``: three successive rotations about the parent
- frames' unit vectors
- - ``'Quaternion'``: rotations defined by four parameters which
- result in a singularity free direction cosine matrix
- amounts :
- Expressions defining the rotation angles or direction cosine
- matrix. These must match the ``rot_type``. See examples below for
- details. The input types are:
- - ``'Axis'``: 2-tuple (expr/sym/func, Vector)
- - ``'DCM'``: Matrix, shape(3,3)
- - ``'Body'``: 3-tuple of expressions, symbols, or functions
- - ``'Space'``: 3-tuple of expressions, symbols, or functions
- - ``'Quaternion'``: 4-tuple of expressions, symbols, or
- functions
- rot_order : str or int, optional
- If applicable, the order of the successive of rotations. The string
- ``'123'`` and integer ``123`` are equivalent, for example. Required
- for ``'Body'`` and ``'Space'``.
- indices : tuple of str
- Enables the reference frame's basis unit vectors to be accessed by
- Python's square bracket indexing notation using the provided three
- indice strings and alters the printing of the unit vectors to
- reflect this choice.
- latexs : tuple of str
- Alters the LaTeX printing of the reference frame's basis unit
- vectors to the provided three valid LaTeX strings.
- Examples
- ========
- >>> from sympy import symbols
- >>> from sympy.physics.vector import ReferenceFrame, vlatex
- >>> q0, q1, q2, q3 = symbols('q0 q1 q2 q3')
- >>> N = ReferenceFrame('N')
- Create a new reference frame A rotated relative to N through a simple
- rotation.
- >>> A = N.orientnew('A', 'Axis', (q0, N.x))
- Create a new reference frame B rotated relative to N through body-fixed
- rotations.
- >>> B = N.orientnew('B', 'Body', (q1, q2, q3), '123')
- Create a new reference frame C rotated relative to N through a simple
- rotation with unique indices and LaTeX printing.
- >>> C = N.orientnew('C', 'Axis', (q0, N.x), indices=('1', '2', '3'),
- ... latexs=(r'\hat{\mathbf{c}}_1',r'\hat{\mathbf{c}}_2',
- ... r'\hat{\mathbf{c}}_3'))
- >>> C['1']
- C['1']
- >>> print(vlatex(C['1']))
- \hat{\mathbf{c}}_1
- """
- newframe = self.__class__(newname, variables=variables,
- indices=indices, latexs=latexs)
- approved_orders = ('123', '231', '312', '132', '213', '321', '121',
- '131', '212', '232', '313', '323', '')
- rot_order = translate(str(rot_order), 'XYZxyz', '123123')
- rot_type = rot_type.upper()
- if rot_order not in approved_orders:
- raise TypeError('The supplied order is not an approved type')
- if rot_type == 'AXIS':
- newframe.orient_axis(self, amounts[1], amounts[0])
- elif rot_type == 'DCM':
- newframe.orient_explicit(self, amounts)
- elif rot_type == 'BODY':
- newframe.orient_body_fixed(self, amounts, rot_order)
- elif rot_type == 'SPACE':
- newframe.orient_space_fixed(self, amounts, rot_order)
- elif rot_type == 'QUATERNION':
- newframe.orient_quaternion(self, amounts)
- else:
- raise NotImplementedError('That is not an implemented rotation')
- return newframe
- def set_ang_acc(self, otherframe, value):
- """Define the angular acceleration Vector in a ReferenceFrame.
- Defines the angular acceleration of this ReferenceFrame, in another.
- Angular acceleration can be defined with respect to multiple different
- ReferenceFrames. Care must be taken to not create loops which are
- inconsistent.
- Parameters
- ==========
- otherframe : ReferenceFrame
- A ReferenceFrame to define the angular acceleration in
- value : Vector
- The Vector representing angular acceleration
- Examples
- ========
- >>> from sympy.physics.vector import ReferenceFrame
- >>> N = ReferenceFrame('N')
- >>> A = ReferenceFrame('A')
- >>> V = 10 * N.x
- >>> A.set_ang_acc(N, V)
- >>> A.ang_acc_in(N)
- 10*N.x
- """
- if value == 0:
- value = Vector(0)
- value = _check_vector(value)
- _check_frame(otherframe)
- self._ang_acc_dict.update({otherframe: value})
- otherframe._ang_acc_dict.update({self: -value})
- def set_ang_vel(self, otherframe, value):
- """Define the angular velocity vector in a ReferenceFrame.
- Defines the angular velocity of this ReferenceFrame, in another.
- Angular velocity can be defined with respect to multiple different
- ReferenceFrames. Care must be taken to not create loops which are
- inconsistent.
- Parameters
- ==========
- otherframe : ReferenceFrame
- A ReferenceFrame to define the angular velocity in
- value : Vector
- The Vector representing angular velocity
- Examples
- ========
- >>> from sympy.physics.vector import ReferenceFrame
- >>> N = ReferenceFrame('N')
- >>> A = ReferenceFrame('A')
- >>> V = 10 * N.x
- >>> A.set_ang_vel(N, V)
- >>> A.ang_vel_in(N)
- 10*N.x
- """
- if value == 0:
- value = Vector(0)
- value = _check_vector(value)
- _check_frame(otherframe)
- self._ang_vel_dict.update({otherframe: value})
- otherframe._ang_vel_dict.update({self: -value})
- @property
- def x(self):
- """The basis Vector for the ReferenceFrame, in the x direction. """
- return self._x
- @property
- def y(self):
- """The basis Vector for the ReferenceFrame, in the y direction. """
- return self._y
- @property
- def z(self):
- """The basis Vector for the ReferenceFrame, in the z direction. """
- return self._z
- @property
- def xx(self):
- """Unit dyad of basis Vectors x and x for the ReferenceFrame."""
- return Vector.outer(self.x, self.x)
- @property
- def xy(self):
- """Unit dyad of basis Vectors x and y for the ReferenceFrame."""
- return Vector.outer(self.x, self.y)
- @property
- def xz(self):
- """Unit dyad of basis Vectors x and z for the ReferenceFrame."""
- return Vector.outer(self.x, self.z)
- @property
- def yx(self):
- """Unit dyad of basis Vectors y and x for the ReferenceFrame."""
- return Vector.outer(self.y, self.x)
- @property
- def yy(self):
- """Unit dyad of basis Vectors y and y for the ReferenceFrame."""
- return Vector.outer(self.y, self.y)
- @property
- def yz(self):
- """Unit dyad of basis Vectors y and z for the ReferenceFrame."""
- return Vector.outer(self.y, self.z)
- @property
- def zx(self):
- """Unit dyad of basis Vectors z and x for the ReferenceFrame."""
- return Vector.outer(self.z, self.x)
- @property
- def zy(self):
- """Unit dyad of basis Vectors z and y for the ReferenceFrame."""
- return Vector.outer(self.z, self.y)
- @property
- def zz(self):
- """Unit dyad of basis Vectors z and z for the ReferenceFrame."""
- return Vector.outer(self.z, self.z)
- @property
- def u(self):
- """Unit dyadic for the ReferenceFrame."""
- return self.xx + self.yy + self.zz
- def partial_velocity(self, frame, *gen_speeds):
- """Returns the partial angular velocities of this frame in the given
- frame with respect to one or more provided generalized speeds.
- Parameters
- ==========
- frame : ReferenceFrame
- The frame with which the angular velocity is defined in.
- gen_speeds : functions of time
- The generalized speeds.
- Returns
- =======
- partial_velocities : tuple of Vector
- The partial angular velocity vectors corresponding to the provided
- generalized speeds.
- Examples
- ========
- >>> from sympy.physics.vector import ReferenceFrame, dynamicsymbols
- >>> N = ReferenceFrame('N')
- >>> A = ReferenceFrame('A')
- >>> u1, u2 = dynamicsymbols('u1, u2')
- >>> A.set_ang_vel(N, u1 * A.x + u2 * N.y)
- >>> A.partial_velocity(N, u1)
- A.x
- >>> A.partial_velocity(N, u1, u2)
- (A.x, N.y)
- """
- from sympy.physics.vector.functions import partial_velocity
- vel = self.ang_vel_in(frame)
- partials = partial_velocity([vel], gen_speeds, frame)[0]
- if len(partials) == 1:
- return partials[0]
- else:
- return tuple(partials)
- def _check_frame(other):
- from .vector import VectorTypeError
- if not isinstance(other, ReferenceFrame):
- raise VectorTypeError(other, ReferenceFrame('A'))
|