from sympy.core.numbers import pi from sympy.core.symbol import symbols from sympy.functions.elementary.trigonometric import (cos, sin) from sympy.matrices.dense import (eye, zeros) from sympy.matrices.immutable import ImmutableDenseMatrix as Matrix from sympy.simplify.simplify import simplify from sympy.physics.vector import (ReferenceFrame, Vector, CoordinateSym, dynamicsymbols, time_derivative, express, dot) from sympy.physics.vector.frame import _check_frame from sympy.physics.vector.vector import VectorTypeError from sympy.testing.pytest import raises import warnings Vector.simp = True def test_dict_list(): A = ReferenceFrame('A') B = ReferenceFrame('B') C = ReferenceFrame('C') D = ReferenceFrame('D') E = ReferenceFrame('E') F = ReferenceFrame('F') B.orient_axis(A, A.x, 1.0) C.orient_axis(B, B.x, 1.0) D.orient_axis(C, C.x, 1.0) assert D._dict_list(A, 0) == [D, C, B, A] E.orient_axis(D, D.x, 1.0) assert C._dict_list(A, 0) == [C, B, A] assert C._dict_list(E, 0) == [C, D, E] # only 0, 1, 2 permitted for second argument raises(ValueError, lambda: C._dict_list(E, 5)) # no connecting path raises(ValueError, lambda: F._dict_list(A, 0)) def test_coordinate_vars(): """Tests the coordinate variables functionality""" A = ReferenceFrame('A') assert CoordinateSym('Ax', A, 0) == A[0] assert CoordinateSym('Ax', A, 1) == A[1] assert CoordinateSym('Ax', A, 2) == A[2] raises(ValueError, lambda: CoordinateSym('Ax', A, 3)) q = dynamicsymbols('q') qd = dynamicsymbols('q', 1) assert isinstance(A[0], CoordinateSym) and \ isinstance(A[0], CoordinateSym) and \ isinstance(A[0], CoordinateSym) assert A.variable_map(A) == {A[0]:A[0], A[1]:A[1], A[2]:A[2]} assert A[0].frame == A B = A.orientnew('B', 'Axis', [q, A.z]) assert B.variable_map(A) == {B[2]: A[2], B[1]: -A[0]*sin(q) + A[1]*cos(q), B[0]: A[0]*cos(q) + A[1]*sin(q)} assert A.variable_map(B) == {A[0]: B[0]*cos(q) - B[1]*sin(q), A[1]: B[0]*sin(q) + B[1]*cos(q), A[2]: B[2]} assert time_derivative(B[0], A) == -A[0]*sin(q)*qd + A[1]*cos(q)*qd assert time_derivative(B[1], A) == -A[0]*cos(q)*qd - A[1]*sin(q)*qd assert time_derivative(B[2], A) == 0 assert express(B[0], A, variables=True) == A[0]*cos(q) + A[1]*sin(q) assert express(B[1], A, variables=True) == -A[0]*sin(q) + A[1]*cos(q) assert express(B[2], A, variables=True) == A[2] assert time_derivative(A[0]*A.x + A[1]*A.y + A[2]*A.z, B) == A[1]*qd*A.x - A[0]*qd*A.y assert time_derivative(B[0]*B.x + B[1]*B.y + B[2]*B.z, A) == - B[1]*qd*B.x + B[0]*qd*B.y assert express(B[0]*B[1]*B[2], A, variables=True) == \ A[2]*(-A[0]*sin(q) + A[1]*cos(q))*(A[0]*cos(q) + A[1]*sin(q)) assert (time_derivative(B[0]*B[1]*B[2], A) - (A[2]*(-A[0]**2*cos(2*q) - 2*A[0]*A[1]*sin(2*q) + A[1]**2*cos(2*q))*qd)).trigsimp() == 0 assert express(B[0]*B.x + B[1]*B.y + B[2]*B.z, A) == \ (B[0]*cos(q) - B[1]*sin(q))*A.x + (B[0]*sin(q) + \ B[1]*cos(q))*A.y + B[2]*A.z assert express(B[0]*B.x + B[1]*B.y + B[2]*B.z, A, variables=True) == \ A[0]*A.x + A[1]*A.y + A[2]*A.z assert express(A[0]*A.x + A[1]*A.y + A[2]*A.z, B) == \ (A[0]*cos(q) + A[1]*sin(q))*B.x + \ (-A[0]*sin(q) + A[1]*cos(q))*B.y + A[2]*B.z assert express(A[0]*A.x + A[1]*A.y + A[2]*A.z, B, variables=True) == \ B[0]*B.x + B[1]*B.y + B[2]*B.z N = B.orientnew('N', 'Axis', [-q, B.z]) assert N.variable_map(A) == {N[0]: A[0], N[2]: A[2], N[1]: A[1]} C = A.orientnew('C', 'Axis', [q, A.x + A.y + A.z]) mapping = A.variable_map(C) assert mapping[A[0]] == 2*C[0]*cos(q)/3 + C[0]/3 - 2*C[1]*sin(q + pi/6)/3 +\ C[1]/3 - 2*C[2]*cos(q + pi/3)/3 + C[2]/3 assert mapping[A[1]] == -2*C[0]*cos(q + pi/3)/3 + \ C[0]/3 + 2*C[1]*cos(q)/3 + C[1]/3 - 2*C[2]*sin(q + pi/6)/3 + C[2]/3 assert mapping[A[2]] == -2*C[0]*sin(q + pi/6)/3 + C[0]/3 - \ 2*C[1]*cos(q + pi/3)/3 + C[1]/3 + 2*C[2]*cos(q)/3 + C[2]/3 def test_ang_vel(): q1, q2, q3, q4 = dynamicsymbols('q1 q2 q3 q4') q1d, q2d, q3d, q4d = dynamicsymbols('q1 q2 q3 q4', 1) N = ReferenceFrame('N') A = N.orientnew('A', 'Axis', [q1, N.z]) B = A.orientnew('B', 'Axis', [q2, A.x]) C = B.orientnew('C', 'Axis', [q3, B.y]) D = N.orientnew('D', 'Axis', [q4, N.y]) u1, u2, u3 = dynamicsymbols('u1 u2 u3') assert A.ang_vel_in(N) == (q1d)*A.z assert B.ang_vel_in(N) == (q2d)*B.x + (q1d)*A.z assert C.ang_vel_in(N) == (q3d)*C.y + (q2d)*B.x + (q1d)*A.z A2 = N.orientnew('A2', 'Axis', [q4, N.y]) assert N.ang_vel_in(N) == 0 assert N.ang_vel_in(A) == -q1d*N.z assert N.ang_vel_in(B) == -q1d*A.z - q2d*B.x assert N.ang_vel_in(C) == -q1d*A.z - q2d*B.x - q3d*B.y assert N.ang_vel_in(A2) == -q4d*N.y assert A.ang_vel_in(N) == q1d*N.z assert A.ang_vel_in(A) == 0 assert A.ang_vel_in(B) == - q2d*B.x assert A.ang_vel_in(C) == - q2d*B.x - q3d*B.y assert A.ang_vel_in(A2) == q1d*N.z - q4d*N.y assert B.ang_vel_in(N) == q1d*A.z + q2d*A.x assert B.ang_vel_in(A) == q2d*A.x assert B.ang_vel_in(B) == 0 assert B.ang_vel_in(C) == -q3d*B.y assert B.ang_vel_in(A2) == q1d*A.z + q2d*A.x - q4d*N.y assert C.ang_vel_in(N) == q1d*A.z + q2d*A.x + q3d*B.y assert C.ang_vel_in(A) == q2d*A.x + q3d*C.y assert C.ang_vel_in(B) == q3d*B.y assert C.ang_vel_in(C) == 0 assert C.ang_vel_in(A2) == q1d*A.z + q2d*A.x + q3d*B.y - q4d*N.y assert A2.ang_vel_in(N) == q4d*A2.y assert A2.ang_vel_in(A) == q4d*A2.y - q1d*N.z assert A2.ang_vel_in(B) == q4d*N.y - q1d*A.z - q2d*A.x assert A2.ang_vel_in(C) == q4d*N.y - q1d*A.z - q2d*A.x - q3d*B.y assert A2.ang_vel_in(A2) == 0 C.set_ang_vel(N, u1*C.x + u2*C.y + u3*C.z) assert C.ang_vel_in(N) == (u1)*C.x + (u2)*C.y + (u3)*C.z assert N.ang_vel_in(C) == (-u1)*C.x + (-u2)*C.y + (-u3)*C.z assert C.ang_vel_in(D) == (u1)*C.x + (u2)*C.y + (u3)*C.z + (-q4d)*D.y assert D.ang_vel_in(C) == (-u1)*C.x + (-u2)*C.y + (-u3)*C.z + (q4d)*D.y q0 = dynamicsymbols('q0') q0d = dynamicsymbols('q0', 1) E = N.orientnew('E', 'Quaternion', (q0, q1, q2, q3)) assert E.ang_vel_in(N) == ( 2 * (q1d * q0 + q2d * q3 - q3d * q2 - q0d * q1) * E.x + 2 * (q2d * q0 + q3d * q1 - q1d * q3 - q0d * q2) * E.y + 2 * (q3d * q0 + q1d * q2 - q2d * q1 - q0d * q3) * E.z) F = N.orientnew('F', 'Body', (q1, q2, q3), 313) assert F.ang_vel_in(N) == ((sin(q2)*sin(q3)*q1d + cos(q3)*q2d)*F.x + (sin(q2)*cos(q3)*q1d - sin(q3)*q2d)*F.y + (cos(q2)*q1d + q3d)*F.z) G = N.orientnew('G', 'Axis', (q1, N.x + N.y)) assert G.ang_vel_in(N) == q1d * (N.x + N.y).normalize() assert N.ang_vel_in(G) == -q1d * (N.x + N.y).normalize() def test_dcm(): q1, q2, q3, q4 = dynamicsymbols('q1 q2 q3 q4') N = ReferenceFrame('N') A = N.orientnew('A', 'Axis', [q1, N.z]) B = A.orientnew('B', 'Axis', [q2, A.x]) C = B.orientnew('C', 'Axis', [q3, B.y]) D = N.orientnew('D', 'Axis', [q4, N.y]) E = N.orientnew('E', 'Space', [q1, q2, q3], '123') assert N.dcm(C) == 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)]]) # This is a little touchy. Is it ok to use simplify in assert? test_mat = D.dcm(C) - Matrix( [[cos(q1) * cos(q3) * cos(q4) - sin(q3) * (- sin(q4) * cos(q2) + sin(q1) * sin(q2) * cos(q4)), - sin(q2) * sin(q4) - sin(q1) * cos(q2) * cos(q4), sin(q3) * cos(q1) * cos(q4) + cos(q3) * (- sin(q4) * cos(q2) + sin(q1) * sin(q2) * cos(q4))], [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(q4) * cos(q1) * cos(q3) - sin(q3) * (cos(q2) * cos(q4) + sin(q1) * sin(q2) * sin(q4)), sin(q2) * cos(q4) - sin(q1) * sin(q4) * cos(q2), sin(q3) * sin(q4) * cos(q1) + cos(q3) * (cos(q2) * cos(q4) + sin(q1) * sin(q2) * sin(q4))]]) assert test_mat.expand() == zeros(3, 3) assert E.dcm(N) == Matrix( [[cos(q2)*cos(q3), sin(q3)*cos(q2), -sin(q2)], [sin(q1)*sin(q2)*cos(q3) - sin(q3)*cos(q1), sin(q1)*sin(q2)*sin(q3) + cos(q1)*cos(q3), sin(q1)*cos(q2)], [sin(q1)*sin(q3) + sin(q2)*cos(q1)*cos(q3), - sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1), cos(q1)*cos(q2)]]) def test_w_diff_dcm1(): # Ref: # Dynamics Theory and Applications, Kane 1985 # Sec. 2.1 ANGULAR VELOCITY A = ReferenceFrame('A') B = ReferenceFrame('B') c11, c12, c13 = dynamicsymbols('C11 C12 C13') c21, c22, c23 = dynamicsymbols('C21 C22 C23') c31, c32, c33 = dynamicsymbols('C31 C32 C33') c11d, c12d, c13d = dynamicsymbols('C11 C12 C13', level=1) c21d, c22d, c23d = dynamicsymbols('C21 C22 C23', level=1) c31d, c32d, c33d = dynamicsymbols('C31 C32 C33', level=1) DCM = Matrix([ [c11, c12, c13], [c21, c22, c23], [c31, c32, c33] ]) B.orient(A, 'DCM', DCM) b1a = (B.x).express(A) b2a = (B.y).express(A) b3a = (B.z).express(A) # Equation (2.1.1) B.set_ang_vel(A, B.x*(dot((b3a).dt(A), B.y)) + B.y*(dot((b1a).dt(A), B.z)) + B.z*(dot((b2a).dt(A), B.x))) # Equation (2.1.21) expr = ( (c12*c13d + c22*c23d + c32*c33d)*B.x + (c13*c11d + c23*c21d + c33*c31d)*B.y + (c11*c12d + c21*c22d + c31*c32d)*B.z) assert B.ang_vel_in(A) - expr == 0 def test_w_diff_dcm2(): q1, q2, q3 = dynamicsymbols('q1:4') N = ReferenceFrame('N') A = N.orientnew('A', 'axis', [q1, N.x]) B = A.orientnew('B', 'axis', [q2, A.y]) C = B.orientnew('C', 'axis', [q3, B.z]) DCM = C.dcm(N).T D = N.orientnew('D', 'DCM', DCM) # Frames D and C are the same ReferenceFrame, # since they have equal DCM respect to frame N. # Therefore, D and C should have same angle velocity in N. assert D.dcm(N) == C.dcm(N) == Matrix([ [cos(q2)*cos(q3), sin(q1)*sin(q2)*cos(q3) + sin(q3)*cos(q1), sin(q1)*sin(q3) - sin(q2)*cos(q1)*cos(q3)], [-sin(q3)*cos(q2), -sin(q1)*sin(q2)*sin(q3) + cos(q1)*cos(q3), sin(q1)*cos(q3) + sin(q2)*sin(q3)*cos(q1)], [sin(q2), -sin(q1)*cos(q2), cos(q1)*cos(q2)]]) assert (D.ang_vel_in(N) - C.ang_vel_in(N)).express(N).simplify() == 0 def test_orientnew_respects_parent_class(): class MyReferenceFrame(ReferenceFrame): pass B = MyReferenceFrame('B') C = B.orientnew('C', 'Axis', [0, B.x]) assert isinstance(C, MyReferenceFrame) def test_orientnew_respects_input_indices(): N = ReferenceFrame('N') q1 = dynamicsymbols('q1') A = N.orientnew('a', 'Axis', [q1, N.z]) #modify default indices: minds = [x+'1' for x in N.indices] B = N.orientnew('b', 'Axis', [q1, N.z], indices=minds) assert N.indices == A.indices assert B.indices == minds def test_orientnew_respects_input_latexs(): N = ReferenceFrame('N') q1 = dynamicsymbols('q1') A = N.orientnew('a', 'Axis', [q1, N.z]) #build default and alternate latex_vecs: def_latex_vecs = [(r"\mathbf{\hat{%s}_%s}" % (A.name.lower(), A.indices[0])), (r"\mathbf{\hat{%s}_%s}" % (A.name.lower(), A.indices[1])), (r"\mathbf{\hat{%s}_%s}" % (A.name.lower(), A.indices[2]))] name = 'b' indices = [x+'1' for x in N.indices] new_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]))] B = N.orientnew(name, 'Axis', [q1, N.z], latexs=new_latex_vecs) assert A.latex_vecs == def_latex_vecs assert B.latex_vecs == new_latex_vecs assert B.indices != indices def test_orientnew_respects_input_variables(): N = ReferenceFrame('N') q1 = dynamicsymbols('q1') A = N.orientnew('a', 'Axis', [q1, N.z]) #build non-standard variable names name = 'b' new_variables = ['notb_'+x+'1' for x in N.indices] B = N.orientnew(name, 'Axis', [q1, N.z], variables=new_variables) for j,var in enumerate(A.varlist): assert var.name == A.name + '_' + A.indices[j] for j,var in enumerate(B.varlist): assert var.name == new_variables[j] def test_issue_10348(): u = dynamicsymbols('u:3') I = ReferenceFrame('I') I.orientnew('A', 'space', u, 'XYZ') def test_issue_11503(): A = ReferenceFrame("A") A.orientnew("B", "Axis", [35, A.y]) C = ReferenceFrame("C") A.orient(C, "Axis", [70, C.z]) def test_partial_velocity(): N = ReferenceFrame('N') A = ReferenceFrame('A') u1, u2 = dynamicsymbols('u1, u2') A.set_ang_vel(N, u1 * A.x + u2 * N.y) assert N.partial_velocity(A, u1) == -A.x assert N.partial_velocity(A, u1, u2) == (-A.x, -N.y) assert A.partial_velocity(N, u1) == A.x assert A.partial_velocity(N, u1, u2) == (A.x, N.y) assert N.partial_velocity(N, u1) == 0 assert A.partial_velocity(A, u1) == 0 def test_issue_11498(): A = ReferenceFrame('A') B = ReferenceFrame('B') # Identity transformation A.orient(B, 'DCM', eye(3)) assert A.dcm(B) == Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) assert B.dcm(A) == Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) # x -> y # y -> -z # z -> -x A.orient(B, 'DCM', Matrix([[0, 1, 0], [0, 0, -1], [-1, 0, 0]])) assert B.dcm(A) == Matrix([[0, 1, 0], [0, 0, -1], [-1, 0, 0]]) assert A.dcm(B) == Matrix([[0, 0, -1], [1, 0, 0], [0, -1, 0]]) assert B.dcm(A).T == A.dcm(B) def test_reference_frame(): raises(TypeError, lambda: ReferenceFrame(0)) raises(TypeError, lambda: ReferenceFrame('N', 0)) raises(ValueError, lambda: ReferenceFrame('N', [0, 1])) raises(TypeError, lambda: ReferenceFrame('N', [0, 1, 2])) raises(TypeError, lambda: ReferenceFrame('N', ['a', 'b', 'c'], 0)) raises(ValueError, lambda: ReferenceFrame('N', ['a', 'b', 'c'], [0, 1])) raises(TypeError, lambda: ReferenceFrame('N', ['a', 'b', 'c'], [0, 1, 2])) raises(TypeError, lambda: ReferenceFrame('N', ['a', 'b', 'c'], ['a', 'b', 'c'], 0)) raises(ValueError, lambda: ReferenceFrame('N', ['a', 'b', 'c'], ['a', 'b', 'c'], [0, 1])) raises(TypeError, lambda: ReferenceFrame('N', ['a', 'b', 'c'], ['a', 'b', 'c'], [0, 1, 2])) N = ReferenceFrame('N') assert N[0] == CoordinateSym('N_x', N, 0) assert N[1] == CoordinateSym('N_y', N, 1) assert N[2] == CoordinateSym('N_z', N, 2) raises(ValueError, lambda: N[3]) N = ReferenceFrame('N', ['a', 'b', 'c']) assert N['a'] == N.x assert N['b'] == N.y assert N['c'] == N.z raises(ValueError, lambda: N['d']) assert str(N) == 'N' A = ReferenceFrame('A') B = ReferenceFrame('B') q0, q1, q2, q3 = symbols('q0 q1 q2 q3') raises(TypeError, lambda: A.orient(B, 'DCM', 0)) raises(TypeError, lambda: B.orient(N, 'Space', [q1, q2, q3], '222')) raises(TypeError, lambda: B.orient(N, 'Axis', [q1, N.x + 2 * N.y], '222')) raises(TypeError, lambda: B.orient(N, 'Axis', q1)) raises(IndexError, lambda: B.orient(N, 'Axis', [q1])) raises(TypeError, lambda: B.orient(N, 'Quaternion', [q0, q1, q2, q3], '222')) raises(TypeError, lambda: B.orient(N, 'Quaternion', q0)) raises(TypeError, lambda: B.orient(N, 'Quaternion', [q0, q1, q2])) raises(NotImplementedError, lambda: B.orient(N, 'Foo', [q0, q1, q2])) raises(TypeError, lambda: B.orient(N, 'Body', [q1, q2], '232')) raises(TypeError, lambda: B.orient(N, 'Space', [q1, q2], '232')) N.set_ang_acc(B, 0) assert N.ang_acc_in(B) == Vector(0) N.set_ang_vel(B, 0) assert N.ang_vel_in(B) == Vector(0) def test_check_frame(): raises(VectorTypeError, lambda: _check_frame(0)) def test_dcm_diff_16824(): # NOTE : This is a regression test for the bug introduced in PR 14758, # identified in 16824, and solved by PR 16828. # This is the solution to Problem 2.2 on page 264 in Kane & Lenvinson's # 1985 book. q1, q2, q3 = dynamicsymbols('q1:4') s1 = sin(q1) c1 = cos(q1) s2 = sin(q2) c2 = cos(q2) s3 = sin(q3) c3 = cos(q3) dcm = Matrix([[c2*c3, s1*s2*c3 - s3*c1, c1*s2*c3 + s3*s1], [c2*s3, s1*s2*s3 + c3*c1, c1*s2*s3 - c3*s1], [-s2, s1*c2, c1*c2]]) A = ReferenceFrame('A') B = ReferenceFrame('B') B.orient(A, 'DCM', dcm) AwB = B.ang_vel_in(A) alpha2 = s3*c2*q1.diff() + c3*q2.diff() beta2 = s1*c2*q3.diff() + c1*q2.diff() assert simplify(AwB.dot(A.y) - alpha2) == 0 assert simplify(AwB.dot(B.y) - beta2) == 0 def test_orient_explicit(): A = ReferenceFrame('A') B = ReferenceFrame('B') A.orient_explicit(B, eye(3)) assert A.dcm(B) == Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) def test_orient_axis(): A = ReferenceFrame('A') B = ReferenceFrame('B') A.orient_axis(B,-B.x, 1) A1 = A.dcm(B) A.orient_axis(B, B.x, -1) A2 = A.dcm(B) A.orient_axis(B, 1, -B.x) A3 = A.dcm(B) assert A1 == A2 assert A2 == A3 raises(TypeError, lambda: A.orient_axis(B, 1, 1)) def test_orient_body(): A = ReferenceFrame('A') B = ReferenceFrame('B') B.orient_body_fixed(A, (1,1,0), 'XYX') assert B.dcm(A) == Matrix([[cos(1), sin(1)**2, -sin(1)*cos(1)], [0, cos(1), sin(1)], [sin(1), -sin(1)*cos(1), cos(1)**2]]) def test_orient_space(): A = ReferenceFrame('A') B = ReferenceFrame('B') B.orient_space_fixed(A, (0,0,0), '123') assert B.dcm(A) == Matrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) def test_orient_quaternion(): A = ReferenceFrame('A') B = ReferenceFrame('B') B.orient_quaternion(A, (0,0,0,0)) assert B.dcm(A) == Matrix([[0, 0, 0], [0, 0, 0], [0, 0, 0]]) def test_looped_frame_warning(): A = ReferenceFrame('A') B = ReferenceFrame('B') C = ReferenceFrame('C') a, b, c = symbols('a b c') B.orient_axis(A, A.x, a) C.orient_axis(B, B.x, b) with warnings.catch_warnings(record = True) as w: warnings.simplefilter("always") A.orient_axis(C, C.x, c) assert issubclass(w[-1].category, UserWarning) assert 'Loops are defined among the orientation of frames. ' + \ 'This is likely not desired and may cause errors in your calculations.' in str(w[-1].message) def test_frame_dict(): A = ReferenceFrame('A') B = ReferenceFrame('B') C = ReferenceFrame('C') a, b, c = symbols('a b c') B.orient_axis(A, A.x, a) assert A._dcm_dict == {B: Matrix([[1, 0, 0],[0, cos(a), -sin(a)],[0, sin(a), cos(a)]])} assert B._dcm_dict == {A: Matrix([[1, 0, 0],[0, cos(a), sin(a)],[0, -sin(a), cos(a)]])} assert C._dcm_dict == {} B.orient_axis(C, C.x, b) # Previous relation is not wiped assert A._dcm_dict == {B: Matrix([[1, 0, 0],[0, cos(a), -sin(a)],[0, sin(a), cos(a)]])} assert B._dcm_dict == {A: Matrix([[1, 0, 0],[0, cos(a), sin(a)],[0, -sin(a), cos(a)]]), \ C: Matrix([[1, 0, 0],[0, cos(b), sin(b)],[0, -sin(b), cos(b)]])} assert C._dcm_dict == {B: Matrix([[1, 0, 0],[0, cos(b), -sin(b)],[0, sin(b), cos(b)]])} A.orient_axis(B, B.x, c) # Previous relation is updated assert B._dcm_dict == {C: Matrix([[1, 0, 0],[0, cos(b), sin(b)],[0, -sin(b), cos(b)]]),\ A: Matrix([[1, 0, 0],[0, cos(c), -sin(c)],[0, sin(c), cos(c)]])} assert A._dcm_dict == {B: Matrix([[1, 0, 0],[0, cos(c), sin(c)],[0, -sin(c), cos(c)]])} assert C._dcm_dict == {B: Matrix([[1, 0, 0],[0, cos(b), -sin(b)],[0, sin(b), cos(b)]])} def test_dcm_cache_dict(): A = ReferenceFrame('A') B = ReferenceFrame('B') C = ReferenceFrame('C') D = ReferenceFrame('D') a, b, c = symbols('a b c') B.orient_axis(A, A.x, a) C.orient_axis(B, B.x, b) D.orient_axis(C, C.x, c) assert D._dcm_dict == {C: Matrix([[1, 0, 0],[0, cos(c), sin(c)],[0, -sin(c), cos(c)]])} assert C._dcm_dict == {B: Matrix([[1, 0, 0],[0, cos(b), sin(b)],[0, -sin(b), cos(b)]]), \ D: Matrix([[1, 0, 0],[0, cos(c), -sin(c)],[0, sin(c), cos(c)]])} assert B._dcm_dict == {A: Matrix([[1, 0, 0],[0, cos(a), sin(a)],[0, -sin(a), cos(a)]]), \ C: Matrix([[1, 0, 0],[0, cos(b), -sin(b)],[0, sin(b), cos(b)]])} assert A._dcm_dict == {B: Matrix([[1, 0, 0],[0, cos(a), -sin(a)],[0, sin(a), cos(a)]])} assert D._dcm_dict == D._dcm_cache D.dcm(A) # Check calculated dcm relation is stored in _dcm_cache and not in _dcm_dict assert list(A._dcm_cache.keys()) == [A, B, D] assert list(D._dcm_cache.keys()) == [C, A] assert list(A._dcm_dict.keys()) == [B] assert list(D._dcm_dict.keys()) == [C] assert A._dcm_dict != A._dcm_cache A.orient_axis(B, B.x, b) # _dcm_cache of A is wiped out and new relation is stored. assert A._dcm_dict == {B: Matrix([[1, 0, 0],[0, cos(b), sin(b)],[0, -sin(b), cos(b)]])} assert A._dcm_dict == A._dcm_cache assert B._dcm_dict == {C: Matrix([[1, 0, 0],[0, cos(b), -sin(b)],[0, sin(b), cos(b)]]), \ A: Matrix([[1, 0, 0],[0, cos(b), -sin(b)],[0, sin(b), cos(b)]])}