def test_properties_of_geometric_objects(self): global n, nbar g = '# # # 0 0,'+ \ '# # # 0 0,'+ \ '# # # 0 0,'+ \ '0 0 0 0 2,'+ \ '0 0 0 2 0' c3d = Ga('p1 p2 p3 n nbar',g=g) (p1,p2,p3,n,nbar) = c3d.mv() P1 = F(p1) P2 = F(p2) P3 = F(p3) L = P1^P2^n delta = (L|n)|nbar assert str(delta) == '2*p1 - 2*p2' C = P1^P2^P3 delta = ((C^n)|n)|nbar assert str(delta) == '2*p1^p2 - 2*p1^p3 + 2*p2^p3' assert str((p2-p1)^(p3-p1)) == 'p1^p2 - p1^p3 + p2^p3' return
def test_extracting_vectors_from_conformal_2_blade(self): g = '0 -1 #,'+ \ '-1 0 #,'+ \ '# # #' e2b = Ga('P1 P2 a',g=g) (P1,P2,a) = e2b.mv() B = P1^P2 Bsq = B*B assert str(Bsq) == '1' ap = a-(a^B)*B assert str(ap) == '-(P2.a)*P1 - (P1.a)*P2' Ap = ap+ap*B Am = ap-ap*B assert str(Ap) == '-2*(P2.a)*P1' assert str(Am) == '-2*(P1.a)*P2' assert str(Ap*Ap) == '0' assert str(Am*Am) == '0' aB = a|B assert str(aB) == '-(P2.a)*P1 + (P1.a)*P2' return
def test_conformal_representations_of_circles_lines_spheres_and_planes(self): global n,nbar g = '1 0 0 0 0,0 1 0 0 0,0 0 1 0 0,0 0 0 0 2,0 0 0 2 0' cnfml3d = Ga('e_1 e_2 e_3 n nbar',g=g) (e1,e2,e3,n,nbar) = cnfml3d.mv() e = n+nbar #conformal representation of points A = make_vector(e1,ga=cnfml3d) # point a = (1,0,0) A = F(a) B = make_vector(e2,ga=cnfml3d) # point b = (0,1,0) B = F(b) C = make_vector(-e1,ga=cnfml3d) # point c = (-1,0,0) C = F(c) D = make_vector(e3,ga=cnfml3d) # point d = (0,0,1) D = F(d) X = make_vector('x',3,ga=cnfml3d) assert str(A) == 'e_1 + n/2 - nbar/2' assert str(B) == 'e_2 + n/2 - nbar/2' assert str(C) == '-e_1 + n/2 - nbar/2' assert str(D) == 'e_3 + n/2 - nbar/2' assert str(X) == 'x1*e_1 + x2*e_2 + x3*e_3 + (x1**2/2 + x2**2/2 + x3**2/2)*n - nbar/2' assert str((A^B^C^X)) == '-x3*e_1^e_2^e_3^n + x3*e_1^e_2^e_3^nbar + (x1**2/2 + x2**2/2 + x3**2/2 - 1/2)*e_1^e_2^n^nbar' assert str((A^B^n^X)) == '-x3*e_1^e_2^e_3^n + (x1/2 + x2/2 - 1/2)*e_1^e_2^n^nbar + x3*e_1^e_3^n^nbar/2 - x3*e_2^e_3^n^nbar/2' assert str((((A^B)^C)^D)^X) == '(-x1**2/2 - x2**2/2 - x3**2/2 + 1/2)*e_1^e_2^e_3^n^nbar' assert str((A^B^n^D^X)) == '(-x1/2 - x2/2 - x3/2 + 1/2)*e_1^e_2^e_3^n^nbar' L = (A^B^e)^X assert str(L) == '-x3*e_1^e_2^e_3^n - x3*e_1^e_2^e_3^nbar + (-x1**2/2 + x1 - x2**2/2 + x2 - x3**2/2 - 1/2)*e_1^e_2^n^nbar + x3*e_1^e_3^n^nbar - x3*e_2^e_3^n^nbar' return
def test_derivatives_in_spherical_coordinates(self): X = (r, th, phi) = symbols('r theta phi') s3d = Ga('e_r e_theta e_phi', g=[1, r ** 2, r ** 2 * sin(th) ** 2], coords=X, norm=True) (er, eth, ephi) = s3d.mv() grad = s3d.grad f = s3d.mv('f', 'scalar', f=True) A = s3d.mv('A', 'vector', f=True) B = s3d.mv('B', 'bivector', f=True) assert str(f) == 'f' assert str(A) == 'A__r*e_r + A__theta*e_theta + A__phi*e_phi' assert str(B) == 'B__rtheta*e_r^e_theta + B__rphi*e_r^e_phi + B__thetaphi*e_theta^e_phi' assert str(grad*f) == 'D{r}f*e_r + D{theta}f*e_theta/r + D{phi}f*e_phi/(r*sin(theta))' assert str((grad|A).simplify()) == '(r*D{r}A__r + 2*A__r + A__theta/tan(theta) + D{theta}A__theta + D{phi}A__phi/sin(theta))/r' assert str(-s3d.I()*(grad^A)) == '(A__phi/tan(theta) + D{theta}A__phi - D{phi}A__theta/sin(theta))*e_r/r + (-r*D{r}A__phi - A__phi + D{phi}A__r/sin(theta))*e_theta/r + (r*D{r}A__theta + A__theta - D{theta}A__r)*e_phi/r' assert latex(grad) == r'\boldsymbol{e}_{r} \frac{\partial}{\partial r} + \boldsymbol{e}_{\theta } \frac{1}{r} \frac{\partial}{\partial \theta } + \boldsymbol{e}_{\phi } \frac{1}{r \sin{\left (\theta \right )}} \frac{\partial}{\partial \phi }' assert latex(B|(eth^ephi)) == r'- B^{\theta \phi } {\left (r,\theta ,\phi \right )}' assert str(grad^B) == '(r*D{r}B__thetaphi - B__rphi/tan(theta) + 2*B__thetaphi - D{theta}B__rphi + D{phi}B__rtheta/sin(theta))*e_r^e_theta^e_phi/r' return
def test_derivatives_in_rectangular_coordinates(self): X = (x, y, z) = symbols('x y z') o3d = Ga('e_x e_y e_z', g=[1, 1, 1], coords=X) (ex, ey, ez) = o3d.mv() grad = o3d.grad f = o3d.mv('f', 'scalar', f=True) A = o3d.mv('A', 'vector', f=True) B = o3d.mv('B', 'bivector', f=True) C = o3d.mv('C', 'mv', f=True) assert str(f) == 'f' assert str(A) == 'A__x*e_x + A__y*e_y + A__z*e_z' assert str(B) == 'B__xy*e_x^e_y + B__xz*e_x^e_z + B__yz*e_y^e_z' assert str(C) == 'C + C__x*e_x + C__y*e_y + C__z*e_z + C__xy*e_x^e_y + C__xz*e_x^e_z + C__yz*e_y^e_z + C__xyz*e_x^e_y^e_z' assert str(grad*f) == 'D{x}f*e_x + D{y}f*e_y + D{z}f*e_z' assert str(grad|A) == 'D{x}A__x + D{y}A__y + D{z}A__z' assert str(grad*A) == 'D{x}A__x + D{y}A__y + D{z}A__z + (-D{y}A__x + D{x}A__y)*e_x^e_y + (-D{z}A__x + D{x}A__z)*e_x^e_z + (-D{z}A__y + D{y}A__z)*e_y^e_z' assert str(-o3d.I()*(grad^A)) == '(-D{z}A__y + D{y}A__z)*e_x + (D{z}A__x - D{x}A__z)*e_y + (-D{y}A__x + D{x}A__y)*e_z' assert str(grad*B) == '(-D{y}B__xy - D{z}B__xz)*e_x + (D{x}B__xy - D{z}B__yz)*e_y + (D{x}B__xz + D{y}B__yz)*e_z + (D{z}B__xy - D{y}B__xz + D{x}B__yz)*e_x^e_y^e_z' assert str(grad^B) == '(D{z}B__xy - D{y}B__xz + D{x}B__yz)*e_x^e_y^e_z' assert str(grad|B) == '(-D{y}B__xy - D{z}B__xz)*e_x + (D{x}B__xy - D{z}B__yz)*e_y + (D{x}B__xz + D{y}B__yz)*e_z' assert str(grad<A) == 'D{x}A__x + D{y}A__y + D{z}A__z' assert str(grad>A) == 'D{x}A__x + D{y}A__y + D{z}A__z' assert str(grad<B) == '(-D{y}B__xy - D{z}B__xz)*e_x + (D{x}B__xy - D{z}B__yz)*e_y + (D{x}B__xz + D{y}B__yz)*e_z' assert str(grad>B) == '0' assert str(grad<C) == 'D{x}C__x + D{y}C__y + D{z}C__z + (-D{y}C__xy - D{z}C__xz)*e_x + (D{x}C__xy - D{z}C__yz)*e_y + (D{x}C__xz + D{y}C__yz)*e_z + D{z}C__xyz*e_x^e_y - D{y}C__xyz*e_x^e_z + D{x}C__xyz*e_y^e_z' assert str(grad>C) == 'D{x}C__x + D{y}C__y + D{z}C__z + D{x}C*e_x + D{y}C*e_y + D{z}C*e_z' return
def test_basic_multivector_operations(self): g3d = Ga('e*x|y|z') (ex,ey,ez) = g3d.mv() A = g3d.mv('A','mv') assert str(A) == 'A + A__x*e_x + A__y*e_y + A__z*e_z + A__xy*e_x^e_y + A__xz*e_x^e_z + A__yz*e_y^e_z + A__xyz*e_x^e_y^e_z' assert str(A) == 'A + A__x*e_x + A__y*e_y + A__z*e_z + A__xy*e_x^e_y + A__xz*e_x^e_z + A__yz*e_y^e_z + A__xyz*e_x^e_y^e_z' assert str(A) == 'A + A__x*e_x + A__y*e_y + A__z*e_z + A__xy*e_x^e_y + A__xz*e_x^e_z + A__yz*e_y^e_z + A__xyz*e_x^e_y^e_z' X = g3d.mv('X','vector') Y = g3d.mv('Y','vector') assert str(X) == 'X__x*e_x + X__y*e_y + X__z*e_z' assert str(Y) == 'Y__x*e_x + Y__y*e_y + Y__z*e_z' assert str((X*Y)) == '(e_x.e_x)*X__x*Y__x + (e_x.e_y)*X__x*Y__y + (e_x.e_y)*X__y*Y__x + (e_x.e_z)*X__x*Y__z + (e_x.e_z)*X__z*Y__x + (e_y.e_y)*X__y*Y__y + (e_y.e_z)*X__y*Y__z + (e_y.e_z)*X__z*Y__y + (e_z.e_z)*X__z*Y__z + (X__x*Y__y - X__y*Y__x)*e_x^e_y + (X__x*Y__z - X__z*Y__x)*e_x^e_z + (X__y*Y__z - X__z*Y__y)*e_y^e_z' assert str((X^Y)) == '(X__x*Y__y - X__y*Y__x)*e_x^e_y + (X__x*Y__z - X__z*Y__x)*e_x^e_z + (X__y*Y__z - X__z*Y__y)*e_y^e_z' assert str((X|Y)) == '(e_x.e_x)*X__x*Y__x + (e_x.e_y)*X__x*Y__y + (e_x.e_y)*X__y*Y__x + (e_x.e_z)*X__x*Y__z + (e_x.e_z)*X__z*Y__x + (e_y.e_y)*X__y*Y__y + (e_y.e_z)*X__y*Y__z + (e_y.e_z)*X__z*Y__y + (e_z.e_z)*X__z*Y__z' g2d = Ga('e*x|y') (ex,ey) = g2d.mv() X = g2d.mv('X','vector') A = g2d.mv('A','spinor') assert str(X) == 'X__x*e_x + X__y*e_y' assert str(A) == 'A + A__xy*e_x^e_y' assert str((X|A)) == '-A__xy*((e_x.e_y)*X__x + (e_y.e_y)*X__y)*e_x + A__xy*((e_x.e_x)*X__x + (e_x.e_y)*X__y)*e_y' assert str((X<A)) == '-A__xy*((e_x.e_y)*X__x + (e_y.e_y)*X__y)*e_x + A__xy*((e_x.e_x)*X__x + (e_x.e_y)*X__y)*e_y' assert str((A>X)) == 'A__xy*((e_x.e_y)*X__x + (e_y.e_y)*X__y)*e_x - A__xy*((e_x.e_x)*X__x + (e_x.e_y)*X__y)*e_y' o2d = Ga('e*x|y', g=[1, 1]) (ex, ey) = o2d.mv() X = o2d.mv('X', 'vector') A = o2d.mv('A', 'spinor') assert str(X) == 'X__x*e_x + X__y*e_y' assert str(A) == 'A + A__xy*e_x^e_y' assert str((X*A)) == '(A*X__x - A__xy*X__y)*e_x + (A*X__y + A__xy*X__x)*e_y' assert str((X|A)) == '-A__xy*X__y*e_x + A__xy*X__x*e_y' assert str((X<A)) == '-A__xy*X__y*e_x + A__xy*X__x*e_y' assert str((X>A)) == 'A*X__x*e_x + A*X__y*e_y' assert str((A*X)) == '(A*X__x + A__xy*X__y)*e_x + (A*X__y - A__xy*X__x)*e_y' assert str((A|X)) == 'A__xy*X__y*e_x - A__xy*X__x*e_y' assert str((A<X)) == 'A*X__x*e_x + A*X__y*e_y' assert str((A>X)) == 'A__xy*X__y*e_x - A__xy*X__x*e_y' return
def test_rounding_numerical_components(self): o3d = Ga('e_x e_y e_z', g=[1, 1, 1]) (ex, ey, ez) = o3d.mv() X = 1.2*ex+2.34*ey+0.555*ez Y = 0.333*ex+4*ey+5.3*ez assert str(X) == '1.2*e_x + 2.34*e_y + 0.555*e_z' assert str(Nga(X,2)) == '1.2*e_x + 2.3*e_y + 0.55*e_z' assert str(X*Y) == '12.7011000000000 + 4.02078*e_x^e_y + 6.175185*e_x^e_z + 10.182*e_y^e_z' assert str(Nga(X*Y,2)) == '13. + 4.0*e_x^e_y + 6.2*e_x^e_z + 10.0*e_y^e_z' return
def derivatives_in_elliptic_cylindrical_coordinates(): #Print_Function() a = symbols('a', real=True) coords = (u, v, z) = symbols('u v z', real=True) (elip3d, er, eth, ephi) = Ga.build('e_u e_v e_z', X=[a * cosh(u) * cos(v), a * sinh(u) * sin(v), z], coords=coords, norm=True) grad = elip3d.grad f = elip3d.mv('f', 'scalar', f=True) A = elip3d.mv('A', 'vector', f=True) B = elip3d.mv('B', 'bivector', f=True) print('#Derivatives in Elliptic Cylindrical Coordinates') print('f =', f) print('A =', A) print('B =', B) print('grad*f =', grad * f) print('grad|A =', grad | A) print('-I*(grad^A) =', -elip3d.i * (grad ^ A)) print('grad^B =', grad ^ B) return
def test_misc(self): """ Other miscellaneous tests """ coords = x, y, z = symbols('x y z', real=True) ga, ex, ey, ez = Ga.build('e*x|y|z', g=[1, 1, 1], coords=coords) v = ga.mv('v', 'vector', f=True) laplacian = ga.grad * ga.grad rlaplacian = ga.rgrad * ga.rgrad # laplacian is a scalar operator, so applying it from either side # is the same assert laplacian * v == v * rlaplacian assert laplacian.is_scalar() assert not ga.grad.is_scalar() # test comparison assert ga.grad == ga.grad assert not (ga.grad != ga.grad) assert ga.grad != laplacian assert not (ga.grad == laplacian) assert ga.grad != object() assert not (ga.grad == object()) # inconsistent cmpflg, not clear which side the operator goes on with pytest.raises(ValueError): ga.grad + ga.rgrad with pytest.raises(ValueError): ga.grad * ga.rgrad
def test_blade_coefs(self): """ Various tests on several multivectors. """ (_g3d, e_1, e_2, e_3) = Ga.build('e*1|2|3') m0 = 2 * e_1 + e_2 - e_3 + 3 * (e_1 ^ e_3) + (e_1 ^ e_3) + (e_2 ^ (3 * e_3)) self.assertTrue(m0.blade_coefs([e_1]) == [2]) self.assertTrue(m0.blade_coefs([e_2]) == [1]) self.assertTrue(m0.blade_coefs([e_1, e_2]) == [2, 1]) self.assertTrue(m0.blade_coefs([e_1 ^ e_3]) == [4]) self.assertTrue(m0.blade_coefs([e_1 ^ e_3, e_2 ^ e_3]) == [4, 3]) self.assertTrue(m0.blade_coefs([e_2 ^ e_3, e_1 ^ e_3]) == [3, 4]) self.assertTrue(m0.blade_coefs([e_1, e_2 ^ e_3]) == [2, 3]) a = Symbol('a') b = Symbol('b') m1 = a * e_1 + e_2 - e_3 + b * (e_1 ^ e_2) self.assertTrue(m1.blade_coefs([e_1]) == [a]) self.assertTrue(m1.blade_coefs([e_2]) == [1]) self.assertTrue(m1.blade_coefs([e_3]) == [-1]) self.assertTrue(m1.blade_coefs([e_1 ^ e_2]) == [b]) self.assertTrue(m1.blade_coefs([e_2 ^ e_3]) == [0]) self.assertTrue(m1.blade_coefs([e_1 ^ e_3]) == [0]) self.assertTrue(m1.blade_coefs([e_1 ^ e_2 ^ e_3]) == [0]) # Invalid parameters self.assertRaises(ValueError, lambda: m1.blade_coefs([e_1 + e_2])) self.assertRaises(ValueError, lambda: m1.blade_coefs([e_2 ^ e_1])) self.assertRaises(ValueError, lambda: m1.blade_coefs([e_1, e_2 ^ e_1])) self.assertRaises(ValueError, lambda: m1.blade_coefs([a * e_1])) self.assertRaises(ValueError, lambda: m1.blade_coefs([3 * e_3]))
def derivatives_in_toroidal_coordinates(): Print_Function() a = symbols('a', real=True) coords = (u, v, phi) = symbols('u v phi', real=True) (t3d, eu, ev, ephi) = Ga.build('e_u e_v e_phi', X=[ a * sinh(v) * cos(phi) / (cosh(v) - cos(u)), a * sinh(v) * sin(phi) / (cosh(v) - cos(u)), a * sin(u) / (cosh(v) - cos(u)) ], coords=coords, norm=True) grad = t3d.grad f = t3d.mv('f', 'scalar', f=True) A = t3d.mv('A', 'vector', f=True) B = t3d.mv('B', 'bivector', f=True) print('f =', f) print('A =', A) print('B =', B) print('grad*f =', grad * f) print('grad|A =', grad | A) print('-I*(grad^A) =', -t3d.i * (grad ^ A)) print('grad^B =', grad ^ B) return
def derivatives_in_paraboloidal_coordinates(): #Print_Function() coords = (u, v, phi) = symbols('u v phi', real=True) (par3d, er, eth, ephi) = Ga.build( 'e_u e_v e_phi', X=[u * v * cos(phi), u * v * sin(phi), (u**2 - v**2) / 2], coords=coords, norm=True) grad = par3d.grad f = par3d.mv('f', 'scalar', f=True) A = par3d.mv('A', 'vector', f=True) B = par3d.mv('B', 'bivector', f=True) print('#Derivatives in Paraboloidal Coordinates') print('f =', f) print('A =', A) print('B =', B) print('grad*f =', grad * f) print('grad|A =', grad | A) (-par3d.i * (grad ^ A)).Fmt(3, 'grad\\times A = -I*(grad^A)') print('grad^B =', grad ^ B) return
def test_rep_switching(self): # this ga has a non-diagonal metric _g3d, e_1, e_2, e_3 = Ga.build('e*1|2|3') m0 = 2 * e_1 + e_2 - e_3 + 3 * (e_1 ^ e_3) + (e_1 ^ e_3) + (e_2 ^ (3 * e_3)) m1 = ( -4 * (e_1 | e_3) - 3 * (e_2 | e_3)) + 2 * e_1 + e_2 - e_3 + 4 * e_1 * e_3 + 3 * e_2 * e_3 # m1 was chosen to make this true self.assertEqual(m0, m1) # all objects start off in blade rep self.assertTrue(m0.is_blade_rep) # convert to base rep m0_base = m0.base_rep() self.assertTrue(m0.is_blade_rep) # original should not change self.assertFalse(m0_base.is_blade_rep) self.assertEqual(m0, m0_base) # convert back m0_base_blade = m0_base.blade_rep() self.assertFalse(m0_base.is_blade_rep) # original should not change self.assertTrue(m0_base_blade.is_blade_rep) self.assertEqual(m0, m0_base_blade)
def derivatives_in_bipolar_coordinates(): Print_Function() a = symbols('a', real=True) coords = (u, v, z) = symbols('u v z', real=True) (bp3d, eu, ev, ez) = Ga.build('e_u e_v e_z', X=[ a * sinh(v) / (cosh(v) - cos(u)), a * sin(u) / (cosh(v) - cos(u)), z ], coords=coords, norm=True) grad = bp3d.grad f = bp3d.mv('f', 'scalar', f=True) A = bp3d.mv('A', 'vector', f=True) B = bp3d.mv('B', 'bivector', f=True) print('f =', f) print('A =', A) print('B =', B) print('grad*f =', grad * f) print('grad|A =', grad | A) print('-I*(grad^A) =', -bp3d.i * (grad ^ A)) print('grad^B =', grad ^ B) return
def test_associativity_and_distributivity(self): coords = x, y, z = symbols('x y z', real=True) ga, ex, ey, ez = Ga.build('e*x|y|z', g=[1, 1, 1], coords=coords) v = ga.mv('v', 'vector', f=True) laplacian = ga.grad * ga.grad rlaplacian = ga.rgrad * ga.rgrad # check addition distributes assert (laplacian + ga.grad) * v == laplacian * v + ga.grad * v != 0 assert (laplacian + 1234567) * v == laplacian * v + 1234567 * v != 0 assert (1234 * ex + ga.grad) * v == 1234 * ex * v + ga.grad * v != 0 # check subtraction distributes assert (laplacian - ga.grad) * v == laplacian * v - ga.grad * v != 0 assert (laplacian - 1234567) * v == laplacian * v - 1234567 * v != 0 assert (1234 * ex - ga.grad) * v == 1234 * ex * v - ga.grad * v != 0 # check unary subtraction distributes assert (-ga.grad) * v == -(ga.grad * v) != 0 # check division is associative assert v * (ga.rgrad / 2) == (v * ga.rgrad) / 2 != 0 # check multiplication is associative assert (ex * ga.grad) * v == ex * (ga.grad * v) != 0 assert (20 * ga.grad) * v == 20 * (ga.grad * v) != 0 assert v * (ga.rgrad * ex) == (v * ga.rgrad) * ex != 0 assert v * (ga.rgrad * 20) == (v * ga.rgrad) * 20 != 0 assert (laplacian * ga.grad) * v == laplacian * (ga.grad * v) != 0 # check wedge is associative assert (ex ^ ga.grad) ^ v == ex ^ (ga.grad ^ v) != 0 assert (20 ^ ga.grad) ^ v == 20 ^ (ga.grad ^ v) != 0 assert v ^ (ga.rgrad ^ ex) == (v ^ ga.rgrad) ^ ex != 0 assert v ^ (ga.rgrad ^ 20) == (v ^ ga.rgrad) ^ 20 != 0
def main(): Print_Function() (a, b, c) = abc = symbols('a,b,c', real=True) (o3d, ea, eb, ec) = Ga.build('e_a e_b e_c', g=[1, 1, 1], coords=abc) grad = o3d.grad x = symbols('x', real=True) A = o3d.lt([[x*a*c**2,x**2*a*b*c,x**2*a**3*b**5],\ [x**3*a**2*b*c,x**4*a*b**2*c**5,5*x**4*a*b**2*c],\ [x**4*a*b**2*c**4,4*x**4*a*b**2*c**2,4*x**4*a**5*b**2*c]]) print('A =', A) v = a * ea + b * eb + c * ec print('v =', v) f = v | A(v) print(r'%f = v\cdot \f{A}{v} =', f) (grad * f).Fmt(3, r'%\nabla f') Av = A(v) print(r'%\f{A}{v} =', Av) (grad * Av).Fmt(3, r'%\nabla \f{A}{v}') return
def test_make_grad(self): ga, e_1, e_2, e_3 = Ga.build('e*1|2|3', g=[1, 1, 1], coords=symbols('x y z')) r = ga.mv(ga.coord_vec) assert ga.make_grad(r) == ga.grad assert ga.make_grad(r, cmpflg=True) == ga.rgrad x = ga.mv('x', 'vector') B = ga.mv('B', 'bivector') dx = ga.make_grad(x) dB = ga.make_grad(B) # GA4P, eq. (6.29) for a in [ga.mv(1), e_1, e_1 ^ e_2]: r = a.i_grade assert dx * (x ^ a) == (ga.n - r) * a assert dx * (x * a) == ga.n * a # derivable via the product rule assert dx * (x * x) == 2 * x assert dx * (x * x * x) == (2 * x) * x + (x * x) * ga.n assert dB * (B * B) == 2 * B assert dB * (B * B * B) == (2 * B) * B + (B * B) * ga.n # an arbitrary chained expression to check we do not crash assert dB * dx * (B * x) == -3 assert dx * dB * (x * B) == -3 assert dx * dB * (B * x) == 9 assert dB * dx * (x * B) == 9
def Maxwells_Equations_in_Geom_Calculus(): Print_Function() X = symbols('t x y z', real=True) (st4d, g0, g1, g2, g3) = Ga.build('gamma*t|x|y|z', g=[1, -1, -1, -1], coords=X) I = st4d.i B = st4d.mv('B', 'vector', f=True) E = st4d.mv('E', 'vector', f=True) B.set_coef(1, 0, 0) E.set_coef(1, 0, 0) B *= g0 E *= g0 J = st4d.mv('J', 'vector', f=True) F = E + I * B print(r'\text{Pseudo Scalar\;\;}I =', I) print('\\text{Magnetic Field Bi-Vector\\;\\;} B = \\bm{B\\gamma_{t}} =', B) print('\\text{Electric Field Bi-Vector\\;\\;} E = \\bm{E\\gamma_{t}} =', E) print('\\text{Electromagnetic Field Bi-Vector\\;\\;} F = E+IB =', F) print('%\\text{Four Current Density\\;\\;} J =', J) gradF = st4d.grad * F print('#Geom Derivative of Electomagnetic Field Bi-Vector') gradF.Fmt(3, 'grad*F') print('#Maxwell Equations') print('grad*F = J') print('#Div $E$ and Curl $H$ Equations') print((gradF.get_grade(1) - J).Fmt(3, '%\\grade{\\nabla F}_{1} -J = 0')) print('#Curl $E$ and Div $B$ equations') print((gradF.get_grade(3)).Fmt(3, '%\\grade{\\nabla F}_{3} = 0')) return
def Lorentz_Tranformation_in_Geog_Algebra(): Print_Function() (alpha, beta, gamma) = symbols('alpha beta gamma') (x, t, xp, tp) = symbols("x t x' t'", real=True) (st2d, g0, g1) = Ga.build('gamma*t|x', g=[1, -1]) from sympy import sinh, cosh R = cosh(alpha / 2) + sinh(alpha / 2) * (g0 ^ g1) X = t * g0 + x * g1 Xp = tp * g0 + xp * g1 print('R =', R) print( r"#%t\bm{\gamma_{t}}+x\bm{\gamma_{x}} = t'\bm{\gamma'_{t}}+x'\bm{\gamma'_{x}} = R\lp t'\bm{\gamma_{t}}+x'\bm{\gamma_{x}}\rp R^{\dagger}" ) Xpp = R * Xp * R.rev() Xpp = Xpp.collect() Xpp = Xpp.trigsimp() print(r"%t\bm{\gamma_{t}}+x\bm{\gamma_{x}} =", Xpp) Xpp = Xpp.subs({sinh(alpha): gamma * beta, cosh(alpha): gamma}) print(r'%\f{\sinh}{\alpha} = \gamma\beta') print(r'%\f{\cosh}{\alpha} = \gamma') print(r"%t\bm{\gamma_{t}}+x\bm{\gamma_{x}} =", Xpp.collect()) return
def Dirac_Equation_in_Geom_Calculus(): Print_Function() coords = symbols('t x y z', real=True) (st4d, g0, g1, g2, g3) = Ga.build('gamma*t|x|y|z', g=[1, -1, -1, -1], coords=coords) I = st4d.i (m, e) = symbols('m e') psi = st4d.mv('psi', 'spinor', f=True) A = st4d.mv('A', 'vector', f=True) sig_z = g3 * g0 print('\\text{4-Vector Potential\\;\\;}\\bm{A} =', A) print('\\text{8-component real spinor\\;\\;}\\bm{\\psi} =', psi) dirac_eq = (st4d.grad * psi) * I * sig_z - e * A * psi - m * psi * g0 dirac_eq = dirac_eq.simplify() print( dirac_eq.Fmt( 3, r'%\text{Dirac Equation\;\;}\nabla \bm{\psi} I \sigma_{z}-e\bm{A}\bm{\psi}-m\bm{\psi}\gamma_{t} = 0' )) return
def derivatives_in_prolate_spheroidal_coordinates(): #Print_Function() a = symbols('a', real=True) coords = (xi, eta, phi) = symbols('xi eta phi', real=True) (ps3d, er, eth, ephi) = Ga.build('e_xi e_eta e_phi', X=[ a * sinh(xi) * sin(eta) * cos(phi), a * sinh(xi) * sin(eta) * sin(phi), a * cosh(xi) * cos(eta) ], coords=coords, norm=True) grad = ps3d.grad f = ps3d.mv('f', 'scalar', f=True) A = ps3d.mv('A', 'vector', f=True) B = ps3d.mv('B', 'bivector', f=True) print('#Derivatives in Prolate Spheroidal Coordinates') print('f =', f) print('A =', A) print('B =', B) print('grad*f =', grad * f) print('grad|A =', grad | A) (-ps3d.i * (grad ^ A)).Fmt(3, '-I*(grad^A)') (grad ^ B).Fmt(3, 'grad^B') return
def basic_multivector_operations_3D(): Print_Function() (g3d,ex,ey,ez) = Ga.build('e*x|y|z') print('g_{ij} =',g3d.g) A = g3d.mv('A','mv') print(A.Fmt(1,'A')) print(A.Fmt(2,'A')) print(A.Fmt(3,'A')) print(A.even().Fmt(1,'%A_{+}')) print(A.odd().Fmt(1,'%A_{-}')) X = g3d.mv('X','vector') Y = g3d.mv('Y','vector') print(X.Fmt(1,'X')) print(Y.Fmt(1,'Y')) print((X*Y).Fmt(2,'X*Y')) print((X^Y).Fmt(2,'X^Y')) print((X|Y).Fmt(2,'X|Y')) return
def test_constructor_errors(self): ga, ex, ey, ez = Ga.build('e*x|y|z', g=[1, 1, 1]) # list lengths must match with pytest.raises(ValueError, match='same length'): Dop([ex], [], ga=ga) # the two conventions can't be mixed mixed_args = [ (ex, Pdop({})), (Sdop([]), ex), ] with pytest.raises(TypeError, match='pairs'): Dop(mixed_args, ga=ga) # ga must be non-none with pytest.raises(ValueError, match='must not be None'): Dop([], ga=None) # too few arguments with pytest.raises(TypeError, match='0 were given'): Dop(ga=ga) # too many arguments with pytest.raises(TypeError, match='3 were given'): Dop(1, 2, 3, ga=ga)
def derivatives_in_oblate_spheroidal_coordinates(): Print_Function() a = symbols('a', real=True) coords = (xi, eta, phi) = symbols('xi eta phi', real=True) (os3d, er, eth, ephi) = Ga.build('e_xi e_eta e_phi', X=[ a * cosh(xi) * cos(eta) * cos(phi), a * cosh(xi) * cos(eta) * sin(phi), a * sinh(xi) * sin(eta) ], coords=coords, norm=True) grad = os3d.grad f = os3d.mv('f', 'scalar', f=True) A = os3d.mv('A', 'vector', f=True) B = os3d.mv('B', 'bivector', f=True) print('f =', f) print('A =', A) print('B =', B) print('grad*f =', grad * f) print('grad|A =', grad | A) print('-I*(grad^A) =', -os3d.i * (grad ^ A)) print('grad^B =', grad ^ B) return
def test_from_str(self): coords = symbols('x y', real=True) g, e1, e2 = Ga.build('e*1|2', coords=coords, g=[1, 1]) a1 = g.mv('a1', 'vector') a2 = g.mv('a2', 'vector') a1x, a1y = a1.get_coefs(1) a2x, a2y = a2.get_coefs(1) # one-d T = Mlt('T', g, nargs=1) v = T(a1) # Two new symbols created Tx, Ty = sorted(v.free_symbols - {a1x, a1y}, key=lambda x: x.sort_key()) assert v == ( Tx * a1x + Ty * a1y ) # two-d T = Mlt('T', g, nargs=2) v = T(a1, a2) # four new symbols created Txx, Txy, Tyx, Tyy = sorted(v.free_symbols - {a1x, a1y, a2x, a2y}, key=lambda x: x.sort_key()) assert v == ( Txx * a1x * a2x + Txy * a1x * a2y + Tyx * a1y * a2x + Tyy * a1y * a2y )
def derivatives_in_spherical_coordinates(): #Print_Function() coords = (r, th, phi) = symbols('r theta phi', real=True) (sp3d, er, eth, ephi) = Ga.build('e_r e_theta e_phi', g=[1, r**2, r**2 * sin(th)**2], coords=coords) grad = sp3d.grad f = sp3d.mv('f', 'scalar', f=True) A = sp3d.mv('A', 'vector', f=True) B = sp3d.mv('B', 'bivector', f=True) print('#Derivatives in Spherical Coordinates') print('f =', f) print('A =', A) print('B =', B) print('grad*f =', grad * f) print('grad|A =', grad | A) print('grad\\times A = -I*(grad^A) =', -sp3d.i * (grad ^ A)) print('%\\nabla^{2}f =', grad | (grad * f)) print('grad^B =', grad ^ B) """ print '( \\nabla\\W\\nabla )\\bm{e}_{r} =',((grad^grad)*er).trigsimp() print '( \\nabla\\W\\nabla )\\bm{e}_{\\theta} =',((grad^grad)*eth).trigsimp() print '( \\nabla\\W\\nabla )\\bm{e}_{\\phi} =',((grad^grad)*ephi).trigsimp() """ return
def test_conformal_representations_of_circles_lines_spheres_and_planes( self): global n, nbar g = '1 0 0 0 0,0 1 0 0 0,0 0 1 0 0,0 0 0 0 2,0 0 0 2 0' cnfml3d = Ga('e_1 e_2 e_3 n nbar', g=g) (e1, e2, e3, n, nbar) = cnfml3d.mv() e = n + nbar #conformal representation of points A = make_vector(e1, ga=cnfml3d) # point a = (1,0,0) A = F(a) B = make_vector(e2, ga=cnfml3d) # point b = (0,1,0) B = F(b) C = make_vector(-e1, ga=cnfml3d) # point c = (-1,0,0) C = F(c) D = make_vector(e3, ga=cnfml3d) # point d = (0,0,1) D = F(d) X = make_vector('x', 3, ga=cnfml3d) assert str(A) == 'e_1 + n/2 - nbar/2' assert str(B) == 'e_2 + n/2 - nbar/2' assert str(C) == '-e_1 + n/2 - nbar/2' assert str(D) == 'e_3 + n/2 - nbar/2' assert str( X ) == 'x1*e_1 + x2*e_2 + x3*e_3 + (x1**2/2 + x2**2/2 + x3**2/2)*n - nbar/2' assert str( (A ^ B ^ C ^ X) ) == '-x3*e_1^e_2^e_3^n + x3*e_1^e_2^e_3^nbar + (x1**2/2 + x2**2/2 + x3**2/2 - 1/2)*e_1^e_2^n^nbar' assert str( (A ^ B ^ n ^ X) ) == '-x3*e_1^e_2^e_3^n + (x1/2 + x2/2 - 1/2)*e_1^e_2^n^nbar + x3*e_1^e_3^n^nbar/2 - x3*e_2^e_3^n^nbar/2' assert str( (((A ^ B) ^ C) ^ D) ^ X) == '(-x1**2/2 - x2**2/2 - x3**2/2 + 1/2)*e_1^e_2^e_3^n^nbar' assert str((A ^ B ^ n ^ D ^ X)) == '(-x1/2 - x2/2 - x3/2 + 1/2)*e_1^e_2^e_3^n^nbar' L = (A ^ B ^ e) ^ X assert str( L ) == '-x3*e_1^e_2^e_3^n - x3*e_1^e_2^e_3^nbar + (-x1**2/2 + x1 - x2**2/2 + x2 - x3**2/2 - 1/2)*e_1^e_2^n^nbar + x3*e_1^e_3^n^nbar - x3*e_2^e_3^n^nbar' return
def basic_multivector_operations_2D(): Print_Function() g2d = Ga('e*x|y') (ex, ey) = g2d.mv() print('g_{ij} =', g2d.g) X = g2d.mv('X', 'vector') A = g2d.mv('A', 'spinor') print(X.Fmt(1, 'X')) print(A.Fmt(1, 'A')) print((X | A).Fmt(2, 'X|A')) print((X < A).Fmt(2, 'X<A')) print((A > X).Fmt(2, 'A>X')) return
def test_basis_dict(self): ga = Ga('e*1|2', g=[1, 1]) b = ga.bases_dict() assert b == { 'e1': ga.blades[1][0], 'e2': ga.blades[1][1], 'e12': ga.blades[2][0], }
def test_rounding_numerical_components(self): o3d = Ga('e_x e_y e_z', g=[1, 1, 1]) (ex, ey, ez) = o3d.mv() X = 1.2 * ex + 2.34 * ey + 0.555 * ez Y = 0.333 * ex + 4 * ey + 5.3 * ez assert str(X) == '1.2*e_x + 2.34*e_y + 0.555*e_z' assert str(Nga(X, 2)) == '1.2*e_x + 2.3*e_y + 0.55*e_z' assert str( X * Y ) == '12.7011000000000 + 4.02078*e_x^e_y + 6.175185*e_x^e_z + 10.182*e_y^e_z' assert str(Nga(X * Y, 2)) == '13. + 4.0*e_x^e_y + 6.2*e_x^e_z + 10.0*e_y^e_z' return
def derivatives_in_rectangular_coordinates(): Print_Function() X = (x, y, z) = symbols('x y z') o3d = Ga('e_x e_y e_z', g=[1, 1, 1], coords=X) (ex, ey, ez) = o3d.mv() grad = o3d.grad f = o3d.mv('f', 'scalar', f=True) A = o3d.mv('A', 'vector', f=True) B = o3d.mv('B', 'bivector', f=True) C = o3d.mv('C', 'mv', f=True) print('f =', f) print('A =', A) print('B =', B) print('C =', C) print('grad*f =', grad * f) print('grad|A =', grad | A) print('grad*A =', grad * A) print('-I*(grad^A) =', -o3d.E() * (grad ^ A)) print('grad*B =', grad * B) print('grad^B =', grad ^ B) print('grad|B =', grad | B) print('grad<A =', grad < A) print('grad>A =', grad > A) print('grad<B =', grad < B) print('grad>B =', grad > B) print('grad<C =', grad < C) print('grad>C =', grad > C) return
def test_arithmetic(self): ga, e_1, e_2, e_3 = Ga.build('e*1|2|3', g=[1, 1, 1]) one = ga.mv(sympy.S.One) # test that scalars are promoted to Mvs correctly assert e_1 + 1 == e_1 + one assert 1 + e_1 == one + e_1 assert e_1 - 1 == e_1 - one assert 1 - e_1 == one - e_1
def test_contraction(self, make_one): ga, e_1, e_2 = Ga.build('e*1|2', g=[1, 1]) e12 = e_1 ^ e_2 one = make_one(ga) assert (one < e12) == e12 assert (e12 > one) == e12 assert (e12 < one) == 0 assert (one > e12) == 0
def test_single_basis(self): # dual numbers g, delta = Ga.build('delta,', g=[0]) assert delta * delta == 0 # which work for automatic differentiation x = Symbol('x') xd = x + delta f = lambda x: x**3 + 2 * x * 2 + 1 assert f(xd) == f(x) + f(x).diff(x) * delta
def test_hashable(self): ga, e_1, e_2, e_3 = Ga.build('e*1|2|3') d = {} d[e_1] = 1 d[e_2] = 2 assert d[e_1 + 0] == 1 d[10] = 3 # note: not a multivector key! assert d[e_1 * 0 + 10] == 3
def test_components(self): coords = x, y, z = symbols('x y z', real=True) ga, ex, ey, ez = Ga.build('e*x|y|z', g=[1, 1, 1], coords=coords) components = ga.grad.components() assert components == ( ex * (ex | ga.grad), ey * (ey | ga.grad), ez * (ez | ga.grad), )
def test_check_generalized_BAC_CAB_formulas(self): (a,b,c,d,e) = Ga('a b c d e').mv() assert str(a|(b*c)) == '-(a.c)*b + (a.b)*c' assert str(a|(b^c)) == '-(a.c)*b + (a.b)*c' assert str(a|(b^c^d)) == '(a.d)*b^c - (a.c)*b^d + (a.b)*c^d' expr = (a|(b^c))+(c|(a^b))+(b|(c^a)) # = (a.b)*c - (b.c)*a - ((a.b)*c - (b.c)*a) assert str(expr.simplify()) == '0' assert str(a*(b^c)-b*(a^c)+c*(a^b)) == '3*a^b^c' assert str(a*(b^c^d)-b*(a^c^d)+c*(a^b^d)-d*(a^b^c)) == '4*a^b^c^d' assert str((a^b)|(c^d)) == '-(a.c)*(b.d) + (a.d)*(b.c)' assert str(((a^b)|c)|d) == '-(a.c)*(b.d) + (a.d)*(b.c)' assert str(Ga.com(a^b,c^d)) == '-(b.d)*a^c + (b.c)*a^d + (a.d)*b^c - (a.c)*b^d' assert str((a|(b^c))|(d^e)) == '(-(a.b)*(c.e) + (a.c)*(b.e))*d + ((a.b)*(c.d) - (a.c)*(b.d))*e' return
def test_is_base(self): """ Various tests on several multivectors. """ (_g3d, e_1, e_2, e_3) = Ga.build('e*1|2|3') self.assertTrue((e_1).is_base()) self.assertTrue((e_2).is_base()) self.assertTrue((e_3).is_base()) self.assertTrue((e_1 ^ e_2).is_base()) self.assertTrue((e_2 ^ e_3).is_base()) self.assertTrue((e_1 ^ e_3).is_base()) self.assertTrue((e_1 ^ e_2 ^ e_3).is_base()) self.assertFalse((2*e_1).is_base()) self.assertFalse((e_1 + e_2).is_base()) self.assertFalse((e_3 * 4).is_base()) self.assertFalse(((3 * e_1) ^ e_2).is_base()) self.assertFalse((2 * (e_2 ^ e_3)).is_base()) self.assertFalse((e_3 ^ e_1).is_base()) self.assertFalse((e_2 ^ e_1 ^ e_3).is_base())
def test_reciprocal_frame_test(self): g = '1 # #,'+ \ '# 1 #,'+ \ '# # 1' g3dn = Ga('e1 e2 e3',g=g) (e1,e2,e3) = g3dn.mv() E = e1^e2^e3 Esq = (E*E).scalar() assert str(E) == 'e1^e2^e3' assert str(Esq) == '(e1.e2)**2 - 2*(e1.e2)*(e1.e3)*(e2.e3) + (e1.e3)**2 + (e2.e3)**2 - 1' Esq_inv = 1/Esq E1 = (e2^e3)*E E2 = (-1)*(e1^e3)*E E3 = (e1^e2)*E assert str(E1) == '((e2.e3)**2 - 1)*e1 + ((e1.e2) - (e1.e3)*(e2.e3))*e2 + (-(e1.e2)*(e2.e3) + (e1.e3))*e3' assert str(E2) == '((e1.e2) - (e1.e3)*(e2.e3))*e1 + ((e1.e3)**2 - 1)*e2 + (-(e1.e2)*(e1.e3) + (e2.e3))*e3' assert str(E3) == '(-(e1.e2)*(e2.e3) + (e1.e3))*e1 + (-(e1.e2)*(e1.e3) + (e2.e3))*e2 + ((e1.e2)**2 - 1)*e3' w = (E1|e2) w = w.expand() assert str(w) == '0' w = (E1|e3) w = w.expand() assert str(w) == '0' w = (E2|e1) w = w.expand() assert str(w) == '0' w = (E2|e3) w = w.expand() assert str(w) == '0' w = (E3|e1) w = w.expand() assert str(w) == '0' w = (E3|e2) w = w.expand() assert str(w) == '0' w = (E1|e1) w = (w.expand()).scalar() Esq = expand(Esq) assert str(simplify(w/Esq)) == '1' w = (E2|e2) w = (w.expand()).scalar() assert str(simplify(w/Esq)) == '1' w = (E3|e3) w = (w.expand()).scalar() assert str(simplify(w/Esq)) == '1' return
def test_noneuclidian_distance_calculation(self): from sympy import solve,sqrt g = '0 # #,# 0 #,# # 1' necl = Ga('X Y e',g=g) (X,Y,e) = necl.mv() assert str((X^Y)*(X^Y)) == '(X.Y)**2' L = X^Y^e B = (L*e).expand().blade_rep() # D&L 10.152 assert str(B) == 'X^Y - (Y.e)*X^e + (X.e)*Y^e' Bsq = B*B assert str(Bsq) == '(X.Y)*((X.Y) - 2*(X.e)*(Y.e))' Bsq = Bsq.scalar() assert str(B) == 'X^Y - (Y.e)*X^e + (X.e)*Y^e' BeBr = B*e*B.rev() assert str(BeBr) == '(X.Y)*(-(X.Y) + 2*(X.e)*(Y.e))*e' assert str(B*B) == '(X.Y)*((X.Y) - 2*(X.e)*(Y.e))' assert str(L*L) == '(X.Y)**2 - 2*(X.Y)*(X.e)*(Y.e)' # D&L 10.153 (s,c,Binv,M,S,C,alpha) = symbols('s c (1/B) M S C alpha') XdotY = necl.g[0,1] Xdote = necl.g[0,2] Ydote = necl.g[1,2] Bhat = Binv*B # D&L 10.154 R = c+s*Bhat # Rotor R = exp(alpha*Bhat/2) assert str(R) == 'c + (1/B)*s*X^Y - (1/B)*(Y.e)*s*X^e + (1/B)*(X.e)*s*Y^e' Z = R*X*R.rev() # D&L 10.155 Z.obj = expand(Z.obj) Z.obj = Z.obj.collect([Binv,s,c,XdotY]) assert str(Z) == '((1/B)**2*(X.Y)**2*s**2 - 2*(1/B)**2*(X.Y)*(X.e)*(Y.e)*s**2 + 2*(1/B)*(X.Y)*c*s - 2*(1/B)*(X.e)*(Y.e)*c*s + c**2)*X + 2*(1/B)*(X.e)**2*c*s*Y + 2*(1/B)*(X.Y)*(X.e)*s*(-(1/B)*(X.Y)*s + 2*(1/B)*(X.e)*(Y.e)*s - c)*e' W = Z|Y # From this point forward all calculations are with sympy scalars W = W.scalar() assert str(W) == '(1/B)**2*(X.Y)**3*s**2 - 4*(1/B)**2*(X.Y)**2*(X.e)*(Y.e)*s**2 + 4*(1/B)**2*(X.Y)*(X.e)**2*(Y.e)**2*s**2 + 2*(1/B)*(X.Y)**2*c*s - 4*(1/B)*(X.Y)*(X.e)*(Y.e)*c*s + (X.Y)*c**2' W = expand(W) W = simplify(W) W = W.collect([s*Binv]) M = 1/Bsq W = W.subs(Binv**2,M) W = simplify(W) Bmag = sqrt(XdotY**2-2*XdotY*Xdote*Ydote) W = W.collect([Binv*c*s,XdotY]) #Double angle substitutions W = W.subs(2*XdotY**2-4*XdotY*Xdote*Ydote,2/(Binv**2)) W = W.subs(2*c*s,S) W = W.subs(c**2,(C+1)/2) W = W.subs(s**2,(C-1)/2) W = simplify(W) W = W.subs(Binv,1/Bmag) W = expand(W) assert str(W.simplify()) == '(X.Y)*C - (X.e)*(Y.e)*C + (X.e)*(Y.e) + S*sqrt((X.Y)*((X.Y) - 2*(X.e)*(Y.e)))' Wd = collect(W,[C,S],exact=True,evaluate=False) Wd_1 = Wd[one] Wd_C = Wd[C] Wd_S = Wd[S] assert str(Wd_1) == '(X.e)*(Y.e)' assert str(Wd_C) == '(X.Y) - (X.e)*(Y.e)' assert str(Wd_S) == 'sqrt((X.Y)**2 - 2*(X.Y)*(X.e)*(Y.e))' assert str(Bmag) == 'sqrt((X.Y)**2 - 2*(X.Y)*(X.e)*(Y.e))' Wd_1 = Wd_1.subs(Binv,1/Bmag) Wd_C = Wd_C.subs(Binv,1/Bmag) Wd_S = Wd_S.subs(Binv,1/Bmag) lhs = Wd_1+Wd_C*C rhs = -Wd_S*S lhs = lhs**2 rhs = rhs**2 W = expand(lhs-rhs) W = expand(W.subs(1/Binv**2,Bmag**2)) W = expand(W.subs(S**2,C**2-1)) W = W.collect([C,C**2],evaluate=False) a = simplify(W[C**2]) b = simplify(W[C]) c = simplify(W[one]) assert str(a) == '(X.e)**2*(Y.e)**2' assert str(b) == '2*(X.e)*(Y.e)*((X.Y) - (X.e)*(Y.e))' assert str(c) == '(X.Y)**2 - 2*(X.Y)*(X.e)*(Y.e) + (X.e)**2*(Y.e)**2' x = Symbol('x') C = solve(a*x**2+b*x+c,x)[0] assert str(expand(simplify(expand(C)))) == '-(X.Y)/((X.e)*(Y.e)) + 1' return