From f334c5fdb9c18c1691e4db855fcb49a0310a2c74 Mon Sep 17 00:00:00 2001 From: srjoglekar246 Date: Mon, 17 Dec 2012 22:41:27 +0530 Subject: [PATCH 1/3] First push, added EField and ParticleCharge --- sympy/physics/electrical/__init__.py | 5 + sympy/physics/electrical/electrical.py | 178 +++++++++++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 sympy/physics/electrical/__init__.py create mode 100644 sympy/physics/electrical/electrical.py diff --git a/sympy/physics/electrical/__init__.py b/sympy/physics/electrical/__init__.py new file mode 100644 index 000000000000..ef0606bfe5f3 --- /dev/null +++ b/sympy/physics/electrical/__init__.py @@ -0,0 +1,5 @@ +__all__ = [] + +import electrical +from electrical import * +__all__.extend(electrical.__all__) diff --git a/sympy/physics/electrical/electrical.py b/sympy/physics/electrical/electrical.py new file mode 100644 index 000000000000..1d7b4d64d00b --- /dev/null +++ b/sympy/physics/electrical/electrical.py @@ -0,0 +1,178 @@ +__all__ = ['EField', 'ParticleCharge'] + +from sympy.physics.mechanics import Vector, Particle +from sympy.physics.mechanics.essential import _check_vector +from sympy import integrate +from math import pi + +e0 = 8.854187817620 * 10 ** -12 + + +class EField: + """ + Represents a purely electric vector field in space. It must be defined in + such a way that it can be expressed entirely in one frame. + """ + + def __init__(self, vector): + """ + Constructor for EField class. + + Parameters + ========== + + vector : Vector + + A Vector defining the EField. The specified Vector must be a function + of x ,y and z only. 'vector' must be conservative in nature. + + Examples + ======== + + >>> from sympy.physics.electrical import EField + >>> from sympy.physics.mechanics import Vector, ReferenceFrame + >>> from sympy import symbols + >>> N = ReferenceFrame('N') + >>> x, y, v = symbols('x y v') + >>> v = - y /(x ** 2 + y ** 2) * N.x + x /(x ** 2 + y ** 2) * N.y + >>> field = EField(v) + >>> print field + - y/(x**2 + y**2)*N.x + x/(x**2 + y**2)*N.y + + """ + self.set_value(vector) + + def get_vector(self): + """Returns the Vector corresponding to this EField.""" + return self.vector + + def set_value(self, vector): + """Set the vector value of an EField.""" + _check_vector(vector) + #Check whether the field can be expressed entirely in one of the frames + #it is defined in. + vector.express(vector.args[0][1]) + #Check whether field is conservative. + if (_check_conservative(vector)): + self.vector = vector + #Store a reference frame to do all the relevant calculations in. + self.frame = vector.args[0][1] + else: + raise ValueError("Given vector must define conservative field.") + + def __add__(self, other_field): + """Addition operator for EField.""" + return EField(self.vector + other_field.get_vector()) + + def __sub__(self, other_field): + """Subtraction operator for EField.""" + return EField(self.vector - other_field.get_vector()) + + def __mul__(self, expr): + """ + Returns the EField whose Vector is the product of this field's + Vector and expr. + 'expr' must be a Sympifiable expression. + """ + return EField(expr * self.vector) + + def __div__(self, expr): + """Division operator for EField.""" + return EField(self.vector / expr) + + def __eq__(self, other): + """To test the equality of two EFields.""" + return self.vector == other.get_vector() + + def __str__(self): + """Printing methid.""" + return (self.vector).__str__() + + def potential_difference(self, origin, point1, point2): + """ + Calculates the electrostatic potential difference between two points for the field + and the specified origin. The position vectors of the points with respect to the + origin must be such that they can be expressed entirely in the reference frames + contained in the definition of the electric field. + + The potential difference is irrespective of the reference frame used for calculations. + + Examples + ======== + >>> from sympy.physics.electrical import EField + >>> from sympy.physics.mechanics import Vector, ReferenceFrame, Point + >>> from sympy import Symbol + >>> N = ReferenceFrame('N') + >>> x = Symbol('x') + >>> field = EField(3*N.x) + >>> o = Point('o') + >>> p = Point('p') + >>> p.set_pos(o,3*N.x+4*N.y) + >>> q = Point('q') + >>> q.set_pos(o,2*N.x+7*N.y) + >>> field.potential_difference(o,p,q) + 3 + + """ + from sympy.abc import x, y, z + variables = [x, y, z] + #Express every vector in one frame. + pos_vector1 = (point1.pos_from(origin)).express(self.frame) + pos_vector2 = (point2.pos_from(origin)).express(self.frame) + temp = (self.vector).express(self.frame) + initial_values = [] + final_values = [] + #Store limits of integration in initial_values and final_values + for i in range(3): + initial_values.append(pos_vector1.args[0][0][i]) + final_values.append(pos_vector2.args[0][0][i]) + pot_difference = 0 + #Integrate electric field to get potential difference. + for i in range(3): + function = integrate(temp.args[0][0][i], variables[i]) + pot_difference = pot_difference - function.subs(zip(variables, final_values)) + function.subs(zip(variables, initial_values)) + return pot_difference + + +class ParticleCharge(Particle): + """ + Represents a charged particle of zero volume. May have non-zero mass and + electric charge. + """ + + def __init__(self, name, point, charge = 0, mass = 0): + Particle.__init__(self, name, point, mass) + self.set_charge(charge) + + def set_charge(self, newcharge): + self.charge = newcharge + + def get_charge(self): + return self.charge + + def field_at(self, point): + pos_vector = point.pos_from(self.get_point()) + magnitude = (self.charge)/(4 * pi * e0 * (pos_vector.magnitude()) ** 2) + return EField(magnitude * (pos_vector.normalize())) + + def potential_at(self,vpoint): + pos_vector = point.pos_from(self.get_point()) + return (self.charge)/(4 * pi * e0 * abs(pos_vector.magnitude())) + + +def _check_conservative(vector): + """ + Checks if 'vector' defines a field that is conservative. + If yes, return True. + Else, return False. + """ + + from sympy import symbols + x, y, z = symbols('x y z') + dfdx = ((vector.args[0][0][0]).diff(y)).diff(z) + dfdy = ((vector.args[0][0][1]).diff(x)).diff(z) + dfdz = ((vector.args[0][0][2]).diff(x)).diff(y) + if (dfdx == dfdy == dfdz): + return True + else: + return False From e64e26674bbfaa46361c5280392e7a39c46275e0 Mon Sep 17 00:00:00 2001 From: srjoglekar246 Date: Tue, 18 Dec 2012 23:16:31 +0530 Subject: [PATCH 2/3] Added get_field, refactored potential_difference, added docs --- sympy/physics/electrical/electrical.py | 65 +++++++++++++++++++------- 1 file changed, 49 insertions(+), 16 deletions(-) diff --git a/sympy/physics/electrical/electrical.py b/sympy/physics/electrical/electrical.py index 1d7b4d64d00b..1ea87dac34fd 100644 --- a/sympy/physics/electrical/electrical.py +++ b/sympy/physics/electrical/electrical.py @@ -1,14 +1,14 @@ -__all__ = ['EField', 'ParticleCharge'] +__all__ = ['EField', 'ParticleCharge', 'check_conservative'] from sympy.physics.mechanics import Vector, Particle from sympy.physics.mechanics.essential import _check_vector -from sympy import integrate +from sympy import integrate, simplify, diff, Basic from math import pi e0 = 8.854187817620 * 10 ** -12 -class EField: +class EField(Basic): """ Represents a purely electric vector field in space. It must be defined in such a way that it can be expressed entirely in one frame. @@ -53,13 +53,17 @@ def set_value(self, vector): #it is defined in. vector.express(vector.args[0][1]) #Check whether field is conservative. - if (_check_conservative(vector)): + if (check_conservative(vector)): self.vector = vector #Store a reference frame to do all the relevant calculations in. self.frame = vector.args[0][1] else: raise ValueError("Given vector must define conservative field.") + def magnitude(self): + """Magnitude of the EField Vector.""" + return self.vector.magnitude() + def __add__(self, other_field): """Addition operator for EField.""" return EField(self.vector + other_field.get_vector()) @@ -88,12 +92,12 @@ def __str__(self): """Printing methid.""" return (self.vector).__str__() - def potential_difference(self, origin, point1, point2): + def potential_difference(self, point1, point2): """ - Calculates the electrostatic potential difference between two points for the field - and the specified origin. The position vectors of the points with respect to the - origin must be such that they can be expressed entirely in the reference frames - contained in the definition of the electric field. + Calculates the electrostatic potential difference between two points for the field. + The two points must be defined in such a way that the vectorial distance between them + can be expressed entirely in the one of the reference frames used in the definition + of the EField. The potential difference is irrespective of the reference frame used for calculations. @@ -110,22 +114,21 @@ def potential_difference(self, origin, point1, point2): >>> p.set_pos(o,3*N.x+4*N.y) >>> q = Point('q') >>> q.set_pos(o,2*N.x+7*N.y) - >>> field.potential_difference(o,p,q) + >>> field.potential_difference(p,q) 3 """ from sympy.abc import x, y, z variables = [x, y, z] #Express every vector in one frame. - pos_vector1 = (point1.pos_from(origin)).express(self.frame) - pos_vector2 = (point2.pos_from(origin)).express(self.frame) + pos_vector = ((point2).pos_from(point1)).express(self.frame) temp = (self.vector).express(self.frame) initial_values = [] final_values = [] #Store limits of integration in initial_values and final_values for i in range(3): - initial_values.append(pos_vector1.args[0][0][i]) - final_values.append(pos_vector2.args[0][0][i]) + initial_values.append(0) + final_values.append(pos_vector.args[0][0][i]) pot_difference = 0 #Integrate electric field to get potential difference. for i in range(3): @@ -141,26 +144,44 @@ class ParticleCharge(Particle): """ def __init__(self, name, point, charge = 0, mass = 0): + """Constructor for ParticleCharge class.""" Particle.__init__(self, name, point, mass) self.set_charge(charge) def set_charge(self, newcharge): + """ + Set the electrostatic charge of the ParticleCharge to the specified + value. + """ self.charge = newcharge def get_charge(self): + """ + Returns the charge possesed by the ParticleCharge. + """ return self.charge def field_at(self, point): + """ + Returns the EField due to the ParticleCharge at the specified Point. + The vectorial distance between the ParticleCharge and the Point must + be entirely expressable in one ReferenceFrame. + """ pos_vector = point.pos_from(self.get_point()) magnitude = (self.charge)/(4 * pi * e0 * (pos_vector.magnitude()) ** 2) return EField(magnitude * (pos_vector.normalize())) def potential_at(self,vpoint): + """ + Returns the potential due to the ParticleCharge at the specified Point. + The vectorial distance between the ParticleCharge and the Point must + be entirely expressable in one ReferenceFrame. + """ pos_vector = point.pos_from(self.get_point()) return (self.charge)/(4 * pi * e0 * abs(pos_vector.magnitude())) -def _check_conservative(vector): +def check_conservative(vector): """ Checks if 'vector' defines a field that is conservative. If yes, return True. @@ -172,7 +193,19 @@ def _check_conservative(vector): dfdx = ((vector.args[0][0][0]).diff(y)).diff(z) dfdy = ((vector.args[0][0][1]).diff(x)).diff(z) dfdz = ((vector.args[0][0][2]).diff(x)).diff(y) - if (dfdx == dfdy == dfdz): + if simplify(dfdx - dfdy) == 0 and simplify(dfdy - dfdz) == 0: return True else: return False + +def get_field(expr, N): + """ + Returns the EField associated with the potential function 'expr' + 'expr' must be a valid potential function. If not, gives ValueError. + """ + from sympy.abc import x, y, z + v = - diff(expr,x) *N.x - diff(expr,y)*N.y - diff(expr,z)*N.z + if (check_conservative(v)): + return EField(v) + else: + raise ValueError("Given function is not a valid potential function.") From a9d051f03d37ee3f08bb6fa197f6d5bc68577206 Mon Sep 17 00:00:00 2001 From: srjoglekar246 Date: Thu, 20 Dec 2012 23:29:25 +0530 Subject: [PATCH 3/3] Added tests for EField,ParticleCharge and check_conservative --- sympy/physics/electrical/electrical.py | 26 +++++-- sympy/physics/electrical/tests/__init__.py | 0 .../electrical/tests/test_electrical.py | 68 +++++++++++++++++++ 3 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 sympy/physics/electrical/tests/__init__.py create mode 100644 sympy/physics/electrical/tests/test_electrical.py diff --git a/sympy/physics/electrical/electrical.py b/sympy/physics/electrical/electrical.py index 1ea87dac34fd..7653677dd2b5 100644 --- a/sympy/physics/electrical/electrical.py +++ b/sympy/physics/electrical/electrical.py @@ -1,4 +1,4 @@ -__all__ = ['EField', 'ParticleCharge', 'check_conservative'] +__all__ = ['EField', 'ParticleCharge', 'check_conservative', 'get_field'] from sympy.physics.mechanics import Vector, Particle from sympy.physics.mechanics.essential import _check_vector @@ -12,8 +12,8 @@ class EField(Basic): """ Represents a purely electric vector field in space. It must be defined in such a way that it can be expressed entirely in one frame. - """ - + """ + def __init__(self, vector): """ Constructor for EField class. @@ -30,7 +30,7 @@ def __init__(self, vector): ======== >>> from sympy.physics.electrical import EField - >>> from sympy.physics.mechanics import Vector, ReferenceFrame + >>> from sympy.physics.mechanics import ReferenceFrame >>> from sympy import symbols >>> N = ReferenceFrame('N') >>> x, y, v = symbols('x y v') @@ -88,6 +88,10 @@ def __eq__(self, other): """To test the equality of two EFields.""" return self.vector == other.get_vector() + def __neg__(self): + """Negation function""" + return EField(-1 * self.vector) + def __str__(self): """Printing methid.""" return (self.vector).__str__() @@ -121,7 +125,11 @@ def potential_difference(self, point1, point2): from sympy.abc import x, y, z variables = [x, y, z] #Express every vector in one frame. - pos_vector = ((point2).pos_from(point1)).express(self.frame) + pos_vector = ((point2).pos_from(point1)) + #Handle special case where point1 and point2 to are same. + if pos_vector == 0: + return 0 + pos_vector = pos_vector.express(self.frame) temp = (self.vector).express(self.frame) initial_values = [] final_values = [] @@ -168,16 +176,22 @@ def field_at(self, point): be entirely expressable in one ReferenceFrame. """ pos_vector = point.pos_from(self.get_point()) + if pos_vector == 0: + from sympy import oo + return oo magnitude = (self.charge)/(4 * pi * e0 * (pos_vector.magnitude()) ** 2) return EField(magnitude * (pos_vector.normalize())) - def potential_at(self,vpoint): + def potential_at(self,point): """ Returns the potential due to the ParticleCharge at the specified Point. The vectorial distance between the ParticleCharge and the Point must be entirely expressable in one ReferenceFrame. """ pos_vector = point.pos_from(self.get_point()) + if pos_vector == 0: + from sympy import oo + return oo return (self.charge)/(4 * pi * e0 * abs(pos_vector.magnitude())) diff --git a/sympy/physics/electrical/tests/__init__.py b/sympy/physics/electrical/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/sympy/physics/electrical/tests/test_electrical.py b/sympy/physics/electrical/tests/test_electrical.py new file mode 100644 index 000000000000..9d7b9736f808 --- /dev/null +++ b/sympy/physics/electrical/tests/test_electrical.py @@ -0,0 +1,68 @@ +from sympy import symbols, simplify, oo, sqrt +from sympy.physics.electrical import EField, ParticleCharge, check_conservative +from sympy.physics.mechanics import ReferenceFrame, Point +from sympy.physics.mechanics.essential import _check_vector + +N = ReferenceFrame('N') + +def test_EField(): + """ + Test working of EField. + """ + + x, y, z = symbols('x y z') + field = EField(x * N.x + y * N.y + z * N.z) + assert check_conservative(field.get_vector()) + assert simplify(field.magnitude() - sqrt(x ** 2 + y ** 2 + z ** 2)) == 0 + assert (field + EField(N.x + N.y + N.z)).get_vector() == (x + 1) * N.x + (y + 1) * N.y + (z + 1) * N.z + assert (field - EField(N.x + N.y + N.z)).get_vector() == (x - 1) * N.x + (y - 1) * N.y + (z - 1) * N.z + assert (field * 3).__class__ == EField + assert (field / 2.0).__class__ == EField + field1 = EField(- x * N.x - y * N.y - z * N.z) + assert field == -field1 + field = EField(3*N.x) + o = Point('o') + p = Point('p') + q = Point('q') + p.set_pos(o,3*N.x + 4*N.y) + q.set_pos(o,2*N.x + 7*N.y) + assert field.potential_difference(p, q) == 3 + assert field.potential_difference(p, p) == 0 + +def test_ParticleCharge(): + """ + Test working of ParticleCharge. + """ + + o = Point('o') + p = Point('p') + q = Point('q') + r = Point('r') + p.set_pos(o, N.x) + q.set_pos(o, -N.x) + r.set_pos(o, N.y) + P1 = ParticleCharge('P1', p, 1) + Q1 = ParticleCharge('Q1', q, 1) + assert P1.get_charge() == 1 + #Test whether the vectorial sum of fields at r due to p and q + #is parallel to N.y + assert (P1.field_at(r) + Q1.field_at(r)).get_vector().dot(N.x) == 0 + assert P1.field_at(p) == oo + assert P1.potential_at(p) == oo + P1.set_charge(-1) + assert P1.potential_at(o) == -Q1.potential_at(o) + +def test_check_conservative(): + """ + Test check_conservative + """ + + assert check_conservative(N.x) + x, y, z = symbols('x y z') + v = - y /(x ** 2 + y ** 2) * N.x + x /(x ** 2 + y ** 2) * N.y + assert check_conservative(v) + assert check_conservative(y * z * N.x) == False + + + +