sensor_coupling = np.einsum("ijk,ij->ik", c.B_coupling(p), n) # a = np.linalg.pinv(sensor_coupling, rcond=1e-15) @ field ss = np.linalg.svd(sensor_coupling @ sensor_coupling.T, False, False) # reg_exps = [0.5, 1, 2, 3, 4, 5, 6, 7, 8] reg_exps = [1] rel_errors = [] for reg_exp in reg_exps: _lambda = np.max(ss) * (10 ** (-reg_exp)) # Laplacian in the suh basis is diagonal BB = sensor_coupling.T @ sensor_coupling + _lambda * (-c.laplacian) / np.max( abs(c.laplacian) ) a = np.linalg.solve(BB, sensor_coupling.T @ field) s = StreamFunction(a, c) b_filt = sensor_coupling @ s rel_error = np.linalg.norm(b_filt - field) / np.linalg.norm(field) print("Relative error:", rel_error * 100, "%") rel_errors.append(rel_error) if PLOT: fig = mlab.figure(bgcolor=(1, 1, 1)) surf = s.plot(False, figure=fig) surf.actor.mapper.interpolate_scalars_before_mapping = True surf.module_manager.scalar_lut_manager.number_of_colors = 16 if SAVE_FIGURES: mlab.savefig( SAVE_DIR + "SUH_scalp_streamfunction.png", magnification=4, figure=fig
#%% # Specify spherical harmonic and calculate corresponding shielded field beta = np.zeros(Beta1.shape[0]) # beta[7] = 1 # Gradient beta[2] = 1 # Homogeneous # Minimum residual _lambda = 1e3 # Minimum energy # _lambda=1e-3 I1inner = np.linalg.solve(C.T @ C + M * ssmax / _lambda, C.T @ beta) I2inner = P @ I1inner coil.s = StreamFunction(I1inner, coil) shieldcoil.s = StreamFunction(I2inner, shieldcoil) #%% # Do a quick 3D plot f = mlab.figure(None, bgcolor=(1, 1, 1), fgcolor=(0.5, 0.5, 0.5), size=(800, 800)) coil.s.plot(figure=f, contours=20) shieldcoil.s.plot(figure=f, contours=20) #%% # Compute the field and scalar potential on an XY-plane
[0, 0, 1, 0], [0, 0, 0, 1], ]) @ np.array([ [1, 0, 0, 0], [0, np.cos(T_x), -np.sin(T_x), 0], [0, np.sin(T_x), np.cos(T_x), 0], [0, 0, 0, 1], ]) c.mesh.apply_transform(rotmat) s = np.zeros((c.basis.shape[1], )) s[2] += 1 # s[63] += 2 s = StreamFunction(s, c) from mayavi import mlab from mayavi.api import Engine engine = Engine() engine.start() f = mlab.figure(None, bgcolor=(1, 1, 1), fgcolor=(0.5, 0.5, 0.5), size=(800, 700)) s.plot(figure=f, ncolors=256) c.plot_mesh(representation="wireframe", figure=f) j = gradient(s.vert, c.mesh, rotated=True)
ssmax = eigvalsh(C.T @ C, M, eigvals=[M.shape[1] - 1, M.shape[1] - 1]) #%% Specify spherical harmonic and calculate corresponding shielded field beta = np.zeros(Beta1.shape[0]) beta[7] = 1 # Gradient # beta[2] = 1 # Homogeneous # Minimum residual _lambda = 1e3 # Minimum energy # _lambda=1e-3 I1inner = np.linalg.solve(C.T @ C + M * ssmax / _lambda, C.T @ beta) I2inner = P @ I1inner s1 = StreamFunction(I1inner, coil1) s2 = StreamFunction(I2inner, coil2) # s = mlab.triangular_mesh(*mesh1.vertices.T, mesh1.faces, scalars=I1) # s.enable_contours=True # s = mlab.triangular_mesh(*mesh2.vertices.T, mesh2.faces, scalars=I2) # s.enable_contours=True B1 = CB1 @ s1 B2 = CB2 @ s2 U1 = CU1 @ s1 U2 = CU2 @ s2 #%% Plot cc1 = scalar_contour(mesh1, mesh1.vertices[:, 2], contours=[-0.001]) cc2 = scalar_contour(mesh2, mesh2.vertices[:, 2], contours=[-0.001])
from bfieldtools.utils import load_example_mesh import numpy as np from mayavi import mlab #%% # We create a set of wire loops by picking a single (arbitrary) surface-harmonic mode # from a plane mesh. Finally, we discretize the mode into a set of wire loops, which we plot. mesh = load_example_mesh("10x10_plane") mesh.apply_scale(0.1) # Downsize from 10 meters to 1 meter N_contours = 20 sb = SuhBasis(mesh, 10) # Construct surface-harmonics basis sf = StreamFunction( sb.basis[:, 1], sb.mesh_conductor) # Turn single mode into a stream function c = LineConductor( mesh=mesh, scalars=sf, N_contours=N_contours) # Discretize the stream function into wire loops # Plot loops for testing c.plot_loops(origin=np.array([0, -100, 0])) #%% # Now, we create a shifted copy of the wire loops, and the calculate the # mutual_inductance between two sets of line conductors mesh2 = mesh.copy() mesh2.vertices[:, 1] += 1 c2 = LineConductor(mesh=mesh2, scalars=sf, N_contours=N_contours)
import mosek coil.s, prob = optimize_streamfunctions( coil, [target_spec] + induction_spec, objective="minimum_inductive_energy", solver="MOSEK", solver_opts={"mosek_params": { mosek.iparam.num_threads: 8 }}, ) from bfieldtools.mesh_conductor import StreamFunction shield.induced_s = StreamFunction(shield.M_coupling @ coil.s, shield) #%% # Plot coil windings and target points loops = scalar_contour(coil.mesh, coil.s.vert, N_contours=6) # loops = [simplify_contour(loop, min_edge=1e-2, angle_threshold=2e-2, smooth=True) for loop in loops] # loops = [loop for loop in loops if loop is not None] if PLOT: f = mlab.figure(None, bgcolor=(1, 1, 1), fgcolor=(0.5, 0.5, 0.5), size=(600, 500)) mlab.clf()
f.scene.isometric_view() f.scene.camera.zoom(0.95) #%% # Now, let's compute the effect of the shield on the field produced by the coil # Points slightly inside the shield d = (np.mean(np.diff(shield.mesh.vertices[shield.mesh.faces[:, 0:2]], axis=1), axis=0) / 10) points = shield.mesh.vertices - d * shield.mesh.vertex_normals # Solve equivalent stream function for the perfect linear mu-metal layer. # This is the equivalent surface current in the shield that would cause its # scalar magnetic potential to be constant shield.s = StreamFunction( np.linalg.solve(shield.U_coupling(points), coil.U_coupling(points) @ coil.s), shield) #%% # Plot the difference in field when taking the shield into account f = mlab.figure(None, bgcolor=(1, 1, 1), fgcolor=(0.5, 0.5, 0.5), size=(800, 800)) mlab.clf() B_target = coil.B_coupling(target_points) @ coil.s B_target_w_shield = (coil.B_coupling(target_points) @ coil.s + shield.B_coupling(target_points) @ shield.s)
opacity=0.6, ) #%% fig = mlab.figure(bgcolor=(1, 1, 1)) s0 = mlab.triangular_mesh(*shieldmesh.vertices.T, shieldmesh.faces, color=(0.5, 0.5, 0.5), opacity=0.3) s0.actor.property.backface_culling = False s0.actor.property.ambient = 0.5 I_prim = np.load( "../publication_software/Shielded coil/biplanar_streamfunction.npy") sprim = StreamFunction(coil.vert2inner @ I_prim, coil) m = max(abs(sprim)) s1 = sprim.plot(False, 16, vmin=-m, vmax=m) s2 = sprim.plot(True, 20) s2.actor.mapper.scalar_visibility = False s2.actor.property.line_width = 1.2 # s2 = mlab.triangular_mesh(*planemesh.vertices.T, planemesh.faces, scalars=I_prim, # colormap='RdBu') # s2.enable_contours = True # s2.contour.filled_contours = True # s2.contour.number_of_contours = 20 s2.actor.property.render_lines_as_tubes = True plot_plane() scene = s0.module_manager scene.scene.camera.position = [ 39.154871143623325,
from bfieldtools.line_conductor import LineConductor from bfieldtools.mesh_conductor import StreamFunction from bfieldtools.suhtools import SuhBasis from bfieldtools.utils import load_example_mesh import numpy as np ############################################### # We create a set of wire loops by picking a single (arbitrary) surface-harmonic mode # from a plane mesh. Finally, we discretize mesh = load_example_mesh("10x10_plane") sb = SuhBasis(mesh, 10) # Construct surface-harmonics basis sf = StreamFunction( sb.basis[:, 1], sb.mesh_conductor) #Turn single mode into a stream function c = LineConductor(mesh=mesh, scalars=sf) # Discretize the stream function into wire loops # Plot loops for testing fig = c.plot_loops(origin=np.array([0, -100, 0])) ############################################### # Now, we create a shifted copy of the wire loops, and the calculate the # mutual_inductance between two sets of line conductors mesh2 = mesh.copy() mesh2.vertices[:, 1] += 1 c2 = LineConductor(mesh=mesh2, scalars=sf) c2.plot_loops(figure=fig, origin=np.array([0, -100, 0]))