Simulation 104: Electromagnetic Mapping with Vector Fields

Author:Murphy  |  View: 20746  |  Time: 2025-03-23 13:11:28

Water, fire, air, and dirt, magnets how do they work? It's not miracles, it's science! We've all played with magnets before be it on the fridge or in science class, but we might not understand what magnets really are or what they do. In this article we will learn the basic theory behind electric and magnetic fields and learn how to make a computational framework to model them.

Figure 1: Example of electric field

In this article we will:

  • Learn basic electromagnetic (EM) theory
  • Create vector fields
  • Map out EM fields using vector fields

Electric and Magnetic Fields

Electromagnetism is one of the 4 fundamental forces of the universe. It is the force that governs the behavior of charged particles and electric and magnetic fields are how this force manifests itself. In this section, we will break down the theory behind each of these fields

Electric Fields

Electric fields are inherent to charged particles. It is the reason why charged particles can repel and attract each other. By convention, we say that positively charged particles have an electric field that points outward and negatively charged particles have an electric field that points inward seen in figure 2. In the attractive case when a negatively and a positively charged particle are near each other, the field lines start at the positive charge and terminate at the negative charge as seen in figure 1.

Figure 2: Electric field lines for a positive, negative, and neutrally charged particle.

The force charged particles exert on each other is described by Coulomb's law. Coulomb's law states that the force between charges is proportional to the charge of each particle divided by the distance between then. The exact equation is stated in equation 1 below.

Equation 1: Coulomb's law

We now know the basics of electric fields and can move on to magnetic.

Magnetic Fields

Magnetic fields are a bit more complicated than electric fields because they are generated from moving charges. This can be due to electric currents in the case of electromagnets or inherent magnetic dipoles due to electron spin in ferromagnets. In either case, the magnetic field is what exerts force between magnets (and moving charges). Magnetic field is more complicated because, unlike charged particles, there are no magnetic monopoles. That is, there are no positive and negative magnets, each one has a north and a south end that are attracted to the opposite end of other magnets. This means that orientation and geometry of the magnets need to be taken into account when determining magnet field.

Figure 3: Magnetic field lines of a cylindrical magnet

Because of the complexity of magnetic fields, we are going to be looking at the simplest case of magnets that are so small or so far apart their geometry does not matter. In this case, magnets can be treated like charged particles or rather a positive and negative particle next to each other like in figure 1. This simplified set up is know as magnet dipole-dipole interaction and it further simplifies if we assume the magnets are aligned in the same direction.

With all these simplifying assumptions we get the magnetic field described in equation 2 with the force applied by the magnetic field described in equation 3.

Equation 2: Magnet dipole-dipole field
Equation 3: Magnet dipole-dipole force

With an understanding of electric and magnetic fields, we will move on to Vector fields; a computational scheme which will help us model our fields.

Note: many simplifications were taken in covering the theory behind EM fields for the sake of brevity and simplicity for this introductory article. More advanced explanations will be given in future articles.

Vector Fields

Vectors are mathematical representations of quantities that also have an associated direction. For instance, I could tell you the windspeed outside is 25 mph which is the quantity, or scalar, associated with windspeed, but if I told you that the windspeed is 25 mph northeast you now have the windspeed vector since you know the strength or magnitude of the wind as well as what direction it is blowing.

Keeping with this windspeed analogy, a vector could only represent the wind at one point in space, but let's say we want to know how the wind is behaving over an entire area. For this, we need a vector field which is similar to a mesh grid as discussed in our pervious article, except instead of looking at points in a grid we are now looking at vectors in a grid. An example of a vector field is shown below in figure 4. As we can see, there are many uniformly spaced vectors with their own direction and size. Visually, the magnitude or strength of the vector is represented by size so bigger vectors represent a higher windspeed in their given direction.

Figure 4: Example of a vector field

Vector fields are perfect computational tools to model electromagnetic fields because, as we saw in the previous section, they have a strength and direction. We will now go into how to model electric and magnetic fields using vector fields.

EM Field Mapping

To create our vector fields we are going to be using matplotlib's arrow function. This function will plot a vector at a given x,y positions along with an x,y magnitude. Combine this with with numpy's mesh grid function and we can create a vector field by making a mesh grid of vectors. Now, let's get started.

Electric Field

Let's first create a class for charged particle we can call to make however many particles we want. The class will take in a position and a given charge amount to initialize a particle.

class charge():

    def init(self, position, charge):
        self.position = np.array(position)
        self.charge = charge

Now let's write a function for Coulomb's law that will take in coulomb's constant, the charge of a particle, and the distance to some observation point. Note that at this point we are using Coulomb's law to look at the field strength at some observation point due to one particle so the charge of a second particle is not needed (yet). Also note we are representing our direction unit vector with r divided by the magnitude of r.

def coulombs_law(k,q,r):
    F = k*q*r/(np.linalg.norm(r))**3
    return F

Let's map the electric field around a single charged particle. We will make a ring of observation points around our particle and see what the field strength (magnitude) and direction is. Our results will be seen in figure 5.

#Define coulomb's constant and charge of our particle
k = 8.9e9                           
q = 0.1e-6    

#Make our charged particle
particle = charge()
particle.init([0,0],q)

#Define our source and observation points
source = np.array(particle.position)
observations = []
r = []
field = []
#Need to scale our vectors to always have a visible magnitude
visual_scale = []

#Loop over points in a circle around our charge
for i in np.linspace(0,2*np.pi,20):
    observations.append(np.array([0.1*np.cos(i),0.1*np.sin(i)]))          
    r.append(observations[-1] - source)                
    field.append(coulombs_law(k,q,r[-1]))  
    visual_scale.append(np.linalg.norm(r[-1])/np.linalg.norm(field[-1])/2)  

#Plot our particle and the vector field around it
fig = plt.figure(figsize=(5,5))
plt.plot(particle.position[0],particle.position[1],'ro')

for j in range(0, len(observations)):
    plt.arrow(observations[j][0],observations[j][1],visual_scale[j]*field[j][0],visual_scale[j]*field[j][1])  

plt.title("Positively Charged Particle");
Figure 5: Simulated positively charged particle with electric field

We have successfully simulated the electric field around a single charged particle which is cool, but let's simulate something more interesting. How about a negative charge next to a positive one like in figure 1? Now we will have 2 charged particles with equal and opposite charges so we have to keep track of the electric field from both particles and see how they add up to the overall field. This will require us to find 2 distance _r_s from each particle to a given observation point and then find the vector contribution of field from each particle to sum them up and get the overall field at that observation point.

Here we will be using a mesh grid of observation points to create a vector field. See results for our electric charge dipole in figure 6.

#Define coulomb's constant and charge of our particle
k = 8.9e9                           
q = 0.1e-6  

#Make our charges
q1 = charge()
q2 = charge()
q1.init([0.4,0],q)
q2.init([-0.4,0],-q)

#Make a meshgrid of observation points
x = np.linspace(-.8,.8,20)
X,Y = np.meshgrid(x,x)
grid =[]
for i in range(0, len(X)):
    for j in range(0, len(Y)):
        grid.append([X[i][j],Y[i][j]])

#Define our needed quantities
observations = np.array(grid)  
r1 = []
r2 = []
field1 = []
field2 = []
field_total = []

#Loop through observation points
for n in range(0,len(observations)):

    r1.append(observations[n] - q1.position)               
    r2.append(observations[n] - q2.position)

    field1.append(coulombs_law(k,q1.charge,r1[-1]))  
    field2.append(coulombs_law(k,q2.charge,r2[-1]))
    field_total.append(field1[-1]+field2[-1])

#Scale x and y
field_total = np.array(field_total)
scale_x = .3/max(field_total[:,0])
scale_y = .3/max(field_total[:,1])

#Plot vector field
fig = plt.figure(figsize=(5,5))

plt.plot(q1.position[0],q1.position[1],'ro', label = "Positive")
plt.plot(q2.position[0],q2.position[1],'bo', label = "Negitive")

for j in range(0, len(observations)):
    plt.arrow(observations[j][0],observations[j][1],scale_x*field_total[j][0],scale_y*field_total[j][1], head_width = .025)  

plt.title("Electric Field of Positive and Negative Charge")
plt.show()
Figure 6: Simulated electric field for charge dipole

We've have now simulated the electric field for a positive and negative charge like in figure 1. As we can see all of the vector heads are pointing in the correct direction; from the positive charge (red) to the negative charge (blue). There is some visual noise added by looking at observation points near our charged particles. Due to how Coulomb's law is formulated, as the distance from the particle goes to 0, the field strength will go to infinity; thus we will get large vectors looking at points near the charges. We can choose to normalize them or remove them or just keep them in as a sanity check.

Now that we have simulated electric field, it's time to move onto magnetic.

Magnetic Field

As we simplified our magnetic field scenario to look like electric field, the code will look very similar. We will define a magnet class and a dipole-dipole interaction function. Note that different from equation 2, I use x and y coordinates instead of a distance r. This just makes the computation cleaner since unlike particle charge, dipole (m) is a vector in itself with x,y components. Also note that here we will be mapping magnetic field not magnetic force. The magnetic force applied by a magnetic field is dependent on how a charged particle/other magnetic moves through it which is outside the scope of this article.

class magnet():

    def init(self, position, dipole):
        self.position = np.array(position)
        self.dipole = dipole

def dipole_dipole(u_0,m,x,y):

    r = np.sqrt(x**2 + y**2)
    r_hat_x = x / r
    r_hat_y = y / r

    factor = (u_0 / (4 * np.pi)) * (3 * (m[0] * r_hat_x + m[1] * r_hat_y))

    Bx = factor * r_hat_x - m[0]
    By = factor * r_hat_y - m[1]

    Bx /= r**3
    By /= r**3

    return Bx, By

Now let us simulate a magnetic field. Here we will simulate the magnetic field of an electron and use its dipole.

#Define magnetic constant and dipole of our magnet
u_0 = 4*np.pi*10**-7
#dipole on an electron
m = (-9.28*10**-24,0)   

#Make our magnets
m1 = magnet()
m1.init([.1,0],m)

#Make a meshgrid of observation points
x = np.linspace(-.5,.5,20)
X,Y = np.meshgrid(x,x)
grid =[]
for i in range(0, len(X)):
    for j in range(0, len(Y)):
        grid.append([X[i][j],Y[i][j]])

#Define our needed quantities
observations = np.array(grid)  
field1 = []

#Loop through observation points
for n in range(0,len(observations)):

    x = observations[n][0] - m1.position[0]               
    y = observations[n][0] - m1.position[1]

    field1.append(dipole_dipole(u_0,m1.dipole,x,y))  

#Scale x and y
field_total = np.array(field1)
scale_x = .1/max(field_total[:,0])
scale_y = .1/max(field_total[:,1])

#Plot vector field
fig = plt.figure(figsize=(5,5))

plt.plot(m1.position[0],m1.position[1],'rs')

for j in range(0, len(observations)):
    plt.arrow(observations[j][0],observations[j][1],scale_x*field1[j][0],scale_y*field1[j][1], head_width = .02)  

plt.title("Magnetic Field of an Electron")
Figure 7: Simulated magnetic field of an electron

Here we see our simulated magnetic field of an electron. It looks a bit odd because we are only working in 2 dimensions so really we are only seeing a vertical slice of the magnetic field. In 3 dimensions we would see the vectors curl towards us and coil around the electron.

We will see the same effect with 2 electrons as well.

#Define coulomb's constant and charge of our particle
u_0 = 4*np.pi*10**-7                          
m = (-9.28*10**-24,0)   

#Make our charges
m1 = magnet()
m2 = magnet()
m1.init([0,.2],m)
m2.init([0,0],m)

#Make a meshgrid of observation points
x = np.linspace(-1,1,30)
X,Y = np.meshgrid(x,x)
grid =[]
for i in range(0, len(X)):
    for j in range(0, len(Y)):
        grid.append([X[i][j],Y[i][j]])

#Define our needed quantities
observations = np.array(grid)  
field1 = []
field2 = []
field_total = []

#Loop through observation points
for n in range(0,len(observations)):

    x1 = observations[n][0] - m1.position[0]               
    y1 = observations[n][0] - m1.position[1]

    x2 = observations[n][0] - m2.position[0]               
    y2 = observations[n][0] - m2.position[1]

    field1.append(dipole_dipole(u_0,m1.dipole,x1,y1))  
    field2.append(dipole_dipole(u_0,m2.dipole,x2,y2))
    field_total.append(field1[-1]+field2[-1])

#Scale x and y
field_total = np.array(field_total)
scale_x = .1/max(field_total[:,0])
scale_y = .1/max(field_total[:,1])

#Plot vector field
fig = plt.figure(figsize=(5,5))

plt.plot(m1.position[0],m1.position[1],'ro', label = "Positive")
plt.plot(m2.position[0],m2.position[1],'bo', label = "Negitive")

for j in range(0, len(observations)):
    plt.arrow(observations[j][0],observations[j][1],scale_x*field_total[j][0],scale_y*field_total[j][1], head_width = .025)  

plt.title("Magnetic Field of 2 Electrons")
Figure 8: Magnetic field of 2 electrons (they are represented by different color but have the same dipole)

This may not be the most intuitive result, but in a future article we will do EM field mapping in 3 dimensions and include geometry to see better defined fields.

Full Code

Electric Field

import numpy as np
import matplotlib.pyplot as plt

class charge():

    def init(self, position, charge):
        self.position = np.array(position)
        self.charge = charge

def coulombs_law(k,q,r):
    F = k*q*r/(np.linalg.norm(r))**3
    return F

#Define coulomb's constant and charge of our particle
k = 8.9e9                           
q = 0.1e-6  

#Make our charged particle
particle = charge()
particle.init([0,0],q)

#Define our source and observation points
source = np.array(particle.position)
observations = []
r = []
field = []
#Need to scale our vectors to always have a visible magnitude
visual_scale = []

#Loop over points in a circle around our charge
for i in np.linspace(0,2*np.pi,20):
    observations.append(np.array([0.1*np.cos(i),0.1*np.sin(i)]))          
    r.append(observations[-1] - source)                
    field.append(coulombs_law(k,q,r[-1]))  
    visual_scale.append(np.linalg.norm(r[-1])/np.linalg.norm(field[-1])/2)  

#Plot our particle and the vector field around it
fig = plt.figure(figsize=(5,5))
plt.plot(particle.position[0],particle.position[1],'ro')

for j in range(0, len(observations)):
    plt.arrow(observations[j][0],observations[j][1],visual_scale[j]*field[j][0],visual_scale[j]*field[j][1])  

plt.title("Positively Charged Particle");
#Make our charges
q1 = charge()
q2 = charge()
q1.init([0.4,0],q)
q2.init([-0.4,0],-q)

#Make a meshgrid of observation points
x = np.linspace(-.8,.8,20)
X,Y = np.meshgrid(x,x)
grid =[]
for i in range(0, len(X)):
    for j in range(0, len(Y)):
        grid.append([X[i][j],Y[i][j]])

#Define our needed quantities
observations = np.array(grid)  
r1 = []
r2 = []
field1 = []
field2 = []
field_total = []

#Loop through observation points
for n in range(0,len(observations)):

    r1.append(observations[n] - q1.position)               
    r2.append(observations[n] - q2.position)

    field1.append(coulombs_law(k,q1.charge,r1[-1]))  
    field2.append(coulombs_law(k,q2.charge,r2[-1]))
    field_total.append(field1[-1]+field2[-1])

#Scale x and y
field_total = np.array(field_total)
scale_x = .3/max(field_total[:,0])
scale_y = .3/max(field_total[:,1])

#Plot vector field
fig = plt.figure(figsize=(5,5))

plt.plot(q1.position[0],q1.position[1],'ro', label = "Positive")
plt.plot(q2.position[0],q2.position[1],'bo', label = "Negitive")

for j in range(0, len(observations)):
    plt.arrow(observations[j][0],observations[j][1],scale_x*field_total[j][0],scale_y*field_total[j][1], head_width = .025)  

plt.title("Electric Field of Positive and Negative Charge")

Magnetic Field

import numpy as np
import matplotlib.pyplot as plt
class magnet():

    def init(self, position, dipole):
        self.position = np.array(position)
        self.dipole = dipole

def dipole_dipole(u_0,m,x,y):

    r = np.sqrt(x**2 + y**2)
    r_hat_x = x / r
    r_hat_y = y / r

    factor = (u_0 / (4 * np.pi)) * (3 * (m[0] * r_hat_x + m[1] * r_hat_y))

    Bx = factor * r_hat_x - m[0]
    By = factor * r_hat_y - m[1]

    Bx /= r**3
    By /= r**3

    return Bx, By

#Define magnetic constant and dipole of our magnet
u_0 = 4*np.pi*10**-7
#dipole on an electron
m = (-9.28*10**-24,0)   

#Make our magnets
m1 = magnet()
m1.init([.1,0],m)

#Make a meshgrid of observation points
x = np.linspace(-.5,.5,20)
X,Y = np.meshgrid(x,x)
grid =[]
for i in range(0, len(X)):
    for j in range(0, len(Y)):
        grid.append([X[i][j],Y[i][j]])

#Define our needed quantities
observations = np.array(grid)  
field1 = []

#Loop through observation points
for n in range(0,len(observations)):

    x = observations[n][0] - m1.position[0]               
    y = observations[n][0] - m1.position[1]

    field1.append(dipole_dipole(u_0,m1.dipole,x,y))  

#Scale x and y
field_total = np.array(field1)
scale_x = .1/max(field_total[:,0])
scale_y = .1/max(field_total[:,1])

#Plot vector field
fig = plt.figure(figsize=(5,5))

plt.plot(m1.position[0],m1.position[1],'rs')

for j in range(0, len(observations)):
    plt.arrow(observations[j][0],observations[j][1],scale_x*field1[j][0],scale_y*field1[j][1], head_width = .02)  

plt.title("Magnetic Field of an Electron")
#Make our charges
m1 = magnet()
m2 = magnet()
m1.init([0,.2],m)
m2.init([0,0],m)

#Make a meshgrid of observation points
x = np.linspace(-1,1,30)
X,Y = np.meshgrid(x,x)
grid =[]
for i in range(0, len(X)):
    for j in range(0, len(Y)):
        grid.append([X[i][j],Y[i][j]])

#Define our needed quantities
observations = np.array(grid)  
field1 = []
field2 = []
field_total = []

#Loop through observation points
for n in range(0,len(observations)):

    x1 = observations[n][0] - m1.position[0]               
    y1 = observations[n][0] - m1.position[1]

    x2 = observations[n][0] - m2.position[0]               
    y2 = observations[n][0] - m2.position[1]

    field1.append(dipole_dipole(u_0,m1.dipole,x1,y1))  
    field2.append(dipole_dipole(u_0,m2.dipole,x2,y2))
    field_total.append(field1[-1]+field2[-1])

#Scale x and y
field_total = np.array(field_total)
scale_x = .1/max(field_total[:,0])
scale_y = .1/max(field_total[:,1])

#Plot vector field
fig = plt.figure(figsize=(5,5))

plt.plot(m1.position[0],m1.position[1],'ro', label = "Positive")
plt.plot(m2.position[0],m2.position[1],'bo', label = "Negitive")

for j in range(0, len(observations)):
    plt.arrow(observations[j][0],observations[j][1],scale_x*field_total[j][0],scale_y*field_total[j][1], head_width = .025)  

plt.title("Magnetic Field of 2 Electrons")

References

  1. All figures used in this article were either generated by the author or fall under the creative common license CC BY-SA as stated by the original image creators
  2. Introduction to Electrodynamics, 4th edition https://hansandcassady.org/David%20J.%20Griffiths-Introduction%20to%20Electrodynamics-Addison-Wesley%20(2012).pdf

Tags: Electricity Magnetism Physics Simulation Vector

Comment