MagDipole3Loops3D

An 3D visualization notebook application for learning the conpect of magnetic flux linkage in the 3-loop model for EM induction method

  • Plot loop wire paths and the dipole directions in 3D
  • Plot the primary and secondary magnetic field lines from the transmitter and target loop in 3D
  • Explore how the three loops interact through magnetic flux linkage in 3D
  • "Run All" to get the default result for Loop 1 being the transmitterm, Loop 2 being the target and Loop 3 being the receiver
  • Plot many loops and lines the way you like by changing the plotting codes

Bugs and improvement: yangdikun@gmail.com

Import packages

  • Note the 3D plotting requires plotly

In [ ]:
import numpy as np
from ipywidgets import interact, interactive, fixed, Layout
import ipywidgets as widgets

In [ ]:
import plotly.plotly as py
import plotly.graph_objs as go
from plotly import __version__
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
print(__version__) # requires version >= 1.9.0
init_notebook_mode(connected=True)

Function definitions

  • VerticalMagneticDipoleField: Calculate the magnetic flux intensity (B field) from a vertical magnetic dipole (a horizontal loop) at the origin
  • VerticalMagneticDipoleLine: Calculate the paths of B field lines from a vertical magnetic dipole at the origin
  • MagneticDipoleLine: Calculate the paths of B field lines from a magnetic dipole with an arbitrary orientation and location.
  • MagneticDipoleLoop: Calculate the paths of an arbitrarily oriented and located loop and its normal direction.

In [ ]:
# x, y, z: location of the observing point
# Bx, By, Bz: vector B field at the observing point in vaccum
def VerticalMagneticDipoleField(x, y, z):
    r = (x**2 + y**2 + z**2)**(0.5)
    Bx = 1e-7/r**3 * 3*x*z/r**2
    By = 1e-7/r**3 * 3*y*z/r**2
    Bz = 1e-7/r**3 * (3*z**2/r**2 - 1)
    return Bx, By, Bz

In [ ]:
# radius: a float defining how far the field lines expand
# stepSize: length of the segments that make the field lines
# yloc, zloc: a single field line path in the y-z plane
def VerticalMagneticDipoleLine(radius,stepSize=1.):
    x, y, z = 0., [radius], [-stepSize/4.]
    while (y[-1]>=0.) & (z[-1] <= 0.):
        _, By, Bz = VerticalMagneticDipoleField(x, y[-1], z[-1])
        B = (By**2 + Bz**2)**(0.5)
        By, Bz = By/B*stepSize, Bz/B*stepSize
        y.append(y[-1]+By)
        z.append(z[-1]+Bz)
    y.pop()
    z.pop()
    yloc = np.r_[y[-1:0:-1], y]
    zloc = np.r_[-np.array(z[-1:0:-1]),z]
    return yloc, zloc

In [ ]:
# dipoleLoc: (x,y,z) of a magnetic dipole
# dipoleDec, dipoleInc: declination and inclination angles of the dipole direction in degree
# radii: a float vector defining the radii of the field line shells
# naz: number of azimuths for the plotting of field lines
# stepSize: length of the segments that make the field lines
# xloc, yloc, zloc: a collection of all field line paths 
def MagneticDipoleLine(dipoleLoc,dipoleDec,dipoleInc,radii,naz=10,stepSize=1.):
    x0, y0, z0 = dipoleLoc[0], dipoleLoc[1], dipoleLoc[2]
    
    # rotation matrix
    theta, alpha = -np.pi*(dipoleInc+90.)/180., -np.pi*dipoleDec/180.
    Rx = np.array([[1.,0.,0.],[0.,np.cos(theta),-np.sin(theta)],[0.,np.sin(theta),np.cos(theta)]])
    Rz = np.array([[np.cos(alpha),-np.sin(alpha),0.],[np.sin(alpha),np.cos(alpha),0.],[0.,0.,1.]])
    R = np.dot(Rz,Rx) # Rz @ Rx

    azimuth = np.linspace(0.,2*np.pi,num=naz,endpoint=False)
    xloc, yloc, zloc = [], [], []
    for r in radii:
        hloc, vloc = VerticalMagneticDipoleLine(r,stepSize)
        for a in azimuth:
            x, y, z = np.sin(a)*hloc, np.cos(a)*hloc, vloc
            xyz = np.dot(R, np.vstack((x,y,z)))
            xloc.append(xyz[0]+x0)
            yloc.append(xyz[1]+y0)
            zloc.append(xyz[2]+z0)
    return xloc, yloc, zloc

In [ ]:
# dipoleLoc: (x,y,z) of a magnetic dipole
# dipoleDec, dipoleInc: declination and inclination angles of the dipole direction in degree
# radius: radius of loop surface
# nseg: number of line segments that make a loop
# xloc, yloc, zloc: a collection of line paths for a loop and its normal direction
def MagneticDipoleLoop(dipoleLoc,dipoleDec,dipoleInc,radius,nseg=20):
    x0, y0, z0 = dipoleLoc[0], dipoleLoc[1], dipoleLoc[2]
    
    # rotation matrix
    theta, alpha = -np.pi*(dipoleInc+90.)/180., -np.pi*dipoleDec/180.
    Rx = np.array([[1.,0.,0.],[0.,np.cos(theta),-np.sin(theta)],[0.,np.sin(theta),np.cos(theta)]])
    Rz = np.array([[np.cos(alpha),-np.sin(alpha),0.],[np.sin(alpha),np.cos(alpha),0.],[0.,0.,1.]])
    R = np.dot(Rz,Rx) # Rz @ Rx
    # loop path
    azimuth = np.linspace(0.,2*np.pi,num=nseg+1,endpoint=True)
    x, y, z = np.sin(azimuth)*radius, np.cos(azimuth)*radius, azimuth*0.
    xyz = np.dot(R, np.vstack((x,y,z)))
    xloc1, yloc1, zloc1 = xyz[0]+x0, xyz[1]+y0, xyz[2]+z0
    # normal direction pointer
    x, y, z = [0.,0.],[0.,0.],[0.,radius]
    xyz = np.dot(R, np.vstack((x,y,z)))
    xloc2, yloc2, zloc2 = xyz[0]+x0, xyz[1]+y0, xyz[2]+z0
    return [xloc1,xloc2], [yloc1,yloc2], [zloc1,zloc2]

Plotting code

  • Loop 1 (red) for transmitter, Loop 2 (green) for target, Loop 3 (blue) for recevier
  • Wire paths of Loop 1, 2 & 3 and the normals (dipole direction) defined by inc and dec
  • Magnetic field lines around the dipole from Loop 1 (primary field) and Loop 2 (secondary field)
  • Choose what to plot: disable the loops you do not want to see
  • X for Easting, Y for Northing, Z for Elevation

In [ ]:
# <<< Control Parameters: Change these numbers to visualize different scenarios 
loopRadius = 0.5 # <<< radius of loop wires
stepSize = 0.2 # <<< length of line segments for plotting lines
nazimuth = 4 # <<< number of azimuths for plotting field lines

# define Loop 1 parameters
x1, y1, z1 = -2.0, 0.0, 0.0 # <<< location
dec1, inc1 = 0.0, -90.0 # <<< declination & inclination in degree
line1Radii = [3.0, 3.8] # <<< field lines are everywhere in space but we only plot a few layers specified by radii
enabled1 = True # <<< True or False to turn on or off the display of Loop 1

# define Loop 2 parameters
x2, y2, z2 = 0.0, 0.0, -1.0 # <<< location
dec2, inc2 = 90.0, 0.0 # <<< declination & inclination in degree
line2Radii = [12.0] # <<< field lines are everywhere in space but we only plot a few layers specified by radii
enabled2 = True # <<< True or False to turn on or off the display of Loop 2

# define Loop 3 parameters
x3, y3, z3 = 2.0, 0.0, 0.0 # <<< location
dec3, inc3 = 0.0, -90.0 # <<< declination & inclination in degree
enabled3 = True # <<< True or False to turn on or off the display of Loop 3


# Plotting Commands Below

# get line path for the 3 loops
loopx1, loopy1, loopz1 = MagneticDipoleLoop((x1,y1,z1),dec1,inc1,loopRadius)
loopx2, loopy2, loopz2 = MagneticDipoleLoop((x2,y2,z2),dec2,inc2,loopRadius)
loopx3, loopy3, loopz3 = MagneticDipoleLoop((x3,y3,z3),dec3,inc3,loopRadius)

# get line path for the field lines of loop 1 and 2
linex1, liney1, linez1 = MagneticDipoleLine((x1,y1,z1),dec1,inc1,line1Radii,nazimuth,stepSize)
linex2, liney2, linez2 = MagneticDipoleLine((x2,y2,z2),dec2,inc2,line2Radii,nazimuth,stepSize)

# create plot
data = []
if enabled1:
    for xx,yy,zz in zip(loopx1,loopy1,loopz1):
        data.append(go.Scatter3d(x=xx, y=yy, z=zz, mode='lines', line=dict(color='red',width=3)))
    for xx,yy,zz in zip(linex1,liney1,linez1):
        data.append(go.Scatter3d(x=xx, y=yy, z=zz, mode='lines', line=dict(color='red',width=5)))
if enabled2:
    for xx,yy,zz in zip(loopx2,loopy2,loopz2):
        data.append(go.Scatter3d(x=xx, y=yy, z=zz, mode='lines', line=dict(color='green',width=3)))
    for xx,yy,zz in zip(linex2,liney2,linez2):
        data.append(go.Scatter3d(x=xx, y=yy, z=zz, mode='lines', line=dict(color='green',width=5)))
if enabled3:
    for xx,yy,zz in zip(loopx3,loopy3,loopz3):
        data.append(go.Scatter3d(x=xx, y=yy, z=zz, mode='lines', line=dict(color='blue',width=3)))
layout = dict(width=1000, height=500, autosize=False, showlegend=False, 
              margin=go.Margin(l=5,r=5,b=5,t=5,pad=4),paper_bgcolor='#efefef',
              scene = dict(aspectmode = 'data', aspectratio = dict(x=1, y=1, z=1),
                      camera = dict(up=dict(x=0,y=0,z=1),center=dict(x=0,y=0,z=0),eye=dict(x=0.1,y=-0.5,z=0.2)))
             )
fig = dict(data=data, layout=layout)
iplot(fig)

In [ ]: