def test_mesh_utils(): mesh1 = utils.load_example_mesh("unit_disc") mesh2 = utils.load_example_mesh("10x10_plane") mesh2.vertices += np.array([[0, 0, 20]]) mesh3 = utils.combine_meshes((mesh1, mesh2)) assert len(mesh3.vertices) == len(mesh1.vertices) + len(mesh2.vertices)
def test_line_conductor_functionality(): """ Test LineConductor creation and functionality """ lp = _fake_line_conductor() lp2 = _fake_line_conductor_closed() lp.plot_loops() mesh = load_example_mesh("unit_disc") scalars = np.ones( (len(mesh.vertices), )) - np.linalg.norm(mesh.vertices, axis=1) lp = line_conductor.LineConductor(mesh=mesh, scalars=scalars, N_contours=6) slp = lp.simplify() B = slp.magnetic_field(mesh.vertices + np.array([0, 0, 1])) A = slp.vector_potential(mesh.vertices + np.array([0, 0, 1])) U = slp.scalar_potential(mesh.vertices + np.array([0, 0, 1])) mesh.vertices += np.array([[0, 0, 1]]) M = slp.mesh_mutual_inductance(mesh)
def test_magnetic_vector_potential_coupling(): mesh = load_example_mesh("unit_disc") points = mesh.vertices + np.array([0, 0, 20]) A_1_chunk = mesh_magnetics.vector_potential_coupling(mesh, points, Nchunks=1) A_2_chunk = mesh_magnetics.vector_potential_coupling(mesh, points, Nchunks=2) assert_allclose(A_1_chunk, A_2_chunk, atol=1e-16) A_exact = mesh_magnetics.vector_potential_coupling(mesh, points, approx_far=False) A_approx_far = mesh_magnetics.vector_potential_coupling(mesh, points, approx_far=True, margin=0.5) A_approx_far_clusters = mesh_magnetics.vector_potential_coupling( mesh, points, approx_far=True, margin=0.5, chunk_clusters=True) assert_allclose(A_exact, A_approx_far, atol=2e-3 * np.mean(np.abs(A_exact))) assert_allclose(A_exact, A_approx_far_clusters, atol=2e-3 * np.mean(np.abs(A_exact)))
def test_triangle_potential_approximation(): """ test whether 1/r triangle potential approximations give close enough to exact (with/without planar assumption) """ mesh = load_example_mesh("unit_disc") points = np.array([[0, 0, 1], [-0.1, 0.1, -5]]) R = points[:, None, None, :] - mesh.vertices[mesh.faces][None, :, :, :] Rcenters = points[:, None, :] - mesh.triangles_center[None, :, :] exact = integrals.triangle_potential_uniform(R, mesh.face_normals) approx = integrals.triangle_potential_approx(Rcenters, mesh.area_faces) assert_allclose(approx, exact, rtol=5e-3) points = np.array([[0, 0, 0], [-0.1, 0.1, 0]]) R = points[:, None, None, :] - mesh.vertices[mesh.faces][None, :, :, :] exact = integrals.triangle_potential_uniform(R, mesh.face_normals) exact_planar = integrals.triangle_potential_uniform(R, mesh.face_normals, planar=True) assert_allclose(exact_planar, exact)
def test_mesh_plotting(): mesh = load_example_mesh("unit_disc") v_scalars = np.random.rand(len(mesh.vertices)) f_scalars = np.random.rand(len(mesh.faces)) viz.plot_mesh(mesh, figure=None) f = mlab.figure() viz.plot_mesh(mesh, figure=f) viz.plot_data_on_faces(mesh, f_scalars) f = mlab.figure() viz.plot_data_on_faces(mesh, f_scalars, figure=f, colormap="jet") f_scalars = np.random.rand(len(mesh.faces)) - 0.5 viz.plot_data_on_faces(mesh, f_scalars, figure=f, colorbar=True) viz.plot_data_on_vertices(mesh, v_scalars, colorbar=True) viz.plot_data_on_vertices(mesh, v_scalars, autoscale=True) v_scalars = np.random.rand(len(mesh.vertices)) - 0.5 viz.plot_data_on_vertices(mesh, v_scalars, colormap="jet")
def test_magnetic_scalar_potential_coupling(): mesh = load_example_mesh("unit_disc") points = mesh.vertices + np.array([0, 0, 20]) U_1_chunk = mesh_magnetics.scalar_potential_coupling(mesh, points, Nchunks=1) U_2_chunk = mesh_magnetics.scalar_potential_coupling(mesh, points, Nchunks=2) assert_allclose(U_1_chunk, U_2_chunk, atol=1e-16) U_exact = mesh_magnetics.scalar_potential_coupling(mesh, points, approx_far=False) U_approx_far = mesh_magnetics.scalar_potential_coupling(mesh, points, approx_far=True, margin=0.5) assert_allclose(U_exact, U_approx_far, atol=2e-3 * np.mean(np.abs(U_exact)))
def test_quad_points(): mesh = utils.load_example_mesh("unit_disc") w, qp = utils.get_quad_points(mesh.vertices, mesh.faces) w, qp = utils.get_quad_points(mesh.vertices, mesh.faces, "dunavant_02") w, qp = utils.get_quad_points(mesh.vertices, mesh.faces, method="lether", index=3)
def test_d_distance(): mesh = load_example_mesh("unit_disc") for z_offset in [1, 0, -1]: points = mesh.vertices + np.array([[0, 0, z_offset]]) R = points[:, None, None, :] - mesh.vertices[mesh.faces][None, :, :, :] assert_allclose(integrals.d_distance(R, mesh.face_normals), z_offset)
def setup_contour_input(): """ Load example mesh and create scalars data """ from bfieldtools.utils import load_example_mesh mesh = load_example_mesh("unit_disc") r = np.linalg.norm(mesh.vertices, axis=1) scalars = r**2 return mesh, scalars
def test_others(): mesh = load_example_mesh("unit_disc") vals = np.ones((len(mesh.vertices),)) - np.linalg.norm(mesh.vertices, axis=1) G = mesh_calculus.gradient(vals, mesh, rotated=False) Gr = mesh_calculus.gradient(vals, mesh, rotated=True) D = mesh_calculus.divergence(Gr.T, mesh) C = mesh_calculus.curl(Gr.T, mesh)
def test_suhbasis(): mesh = load_example_mesh("unit_disc") for obj in [mesh, _fake_mesh_conductor(mesh_name="unit_disc")]: for mag in [False]: for bc in ["neumann", "dirichlet"]: suh = suhtools.SuhBasis(obj, Nc=10, boundary_condition=bc, magnetic=mag) assert suh.basis.shape[1] == 10 suh.field(suh.basis[1], np.array([[0, 0, 1]])) suh.plot(Nfuncs=3) suh = suhtools.SuhBasis(mesh, Nc=10, boundary_condition="dirichlet", magnetic="DC") assert suh.basis.shape[1] == 10 suh = suhtools.SuhBasis(mesh, Nc=10, boundary_condition="dirichlet", magnetic="AC") assert suh.basis.shape[1] == 10 suh = suhtools.SuhBasis(mesh, boundary_condition="dirichlet", magnetic="DC") suh = suhtools.SuhBasis(mesh, boundary_condition="dirichlet", magnetic="AC") suh = suhtools.SuhBasis(mesh, boundary_condition="dirichlet", magnetic=False) suh.calculate_basis(shiftinvert=False, v0=np.ones(57)) try: suh = suhtools.SuhBasis(10, Nc=10) assert suh.basis.shape[1] == 10 except: print("Caught test exception") f = mlab.figure() suh.plot([0, 2], figure=f) suh.plot(Nfuncs=4, Ncols=2, figure=f)
def test_omega(): """ Tests solid angle (whether sphere sums up to 4*pi) """ mesh = load_example_mesh("unit_sphere") points = np.array([[0, 0, 0], [0.1, 0, 0.2], [-0.1, 0.1, -0.2]]) R = points[:, None, None, :] - mesh.vertices[mesh.faces][None, :, :, :] solid_angle = integrals.omega(R) assert_allclose(np.sum(solid_angle, axis=1), 4 * np.pi)
def _fake_streamfunction(vals=None, mesh_name="unit_disc", **kwargs): """ Creates an example 'fake' StreamFunction object """ if vals: return mesh_conductor.StreamFunction( vals, _fake_mesh_conductor(mesh_name, **kwargs) ) else: mesh = load_example_mesh(mesh_name) vals = 1 - np.linalg.norm(mesh.vertices, axis=1) return mesh_conductor.StreamFunction( vals, _fake_mesh_conductor(mesh_name, basis_name="vertex") )
def test_shiftinvert(): mesh = load_example_mesh("unit_disc") suh = suhtools.SuhBasis(mesh, Nc=10, boundary_condition="dirichlet", magnetic=False) suh.calculate_basis(shiftinvert=True) shiftinvert_eigenvals = suh.eigenvals shiftinvert_basis = suh.basis suh.calculate_basis(shiftinvert=False) nonshiftinvert_eigenvals = suh.eigenvals nonshiftinvert_basis = suh.basis assert_allclose(shiftinvert_eigenvals, nonshiftinvert_eigenvals)
def test_magnetic_field_coupling(): mesh = load_example_mesh("unit_disc") points = mesh.vertices + np.array([0, 0, 20]) B_1_chunk = mesh_magnetics.magnetic_field_coupling(mesh, points, Nchunks=1) B_2_chunk = mesh_magnetics.magnetic_field_coupling(mesh, points, Nchunks=2) assert_allclose(B_1_chunk, B_2_chunk, atol=1e-16) B_q_1 = mesh_magnetics.magnetic_field_coupling(mesh, points, quad_degree=1) B_q_2 = mesh_magnetics.magnetic_field_coupling(mesh, points, quad_degree=2) B_q_3 = mesh_magnetics.magnetic_field_coupling(mesh, points, quad_degree=3) assert_allclose(B_q_1, B_q_2, atol=1e-3 * np.mean(np.abs(B_q_2))) assert_allclose(B_q_2, B_q_3, atol=1e-3 * np.mean(np.abs(B_q_2))) B_a = mesh_magnetics.magnetic_field_coupling(mesh, points, analytic=True) assert_allclose(B_a, B_q_1, atol=1e-3 * np.mean(np.abs(B_a))) assert_allclose(B_a, B_q_2, atol=1e-3 * np.mean(np.abs(B_a))) assert_allclose(B_a, B_q_3, atol=1e-3 * np.mean(np.abs(B_a)))
def test_dipole_potential_approximation(): """ Test whether approximations and exact solution are close enough """ mesh = load_example_mesh("unit_disc") points = np.array([[0.1, 0, 0.2], [-0.1, 0.1, -0.2]]) * 20 R = points[:, None, None, :] - mesh.vertices[mesh.faces][None, :, :, :] R_v = points[:, None, :] - mesh.vertices[None, :, :] vertex_areas = mass_matrix(mesh, lumped=True).diagonal() approx_pot_v = integrals.potential_vertex_dipoles(R_v, mesh.vertex_normals, vertex_areas) approx_pot_f = integrals.potential_dipoles(R, mesh.face_normals, mesh.area_faces) exact_pot_f = integrals.triangle_potential_dipole_linear( R, mesh.face_normals, mesh.area_faces) # Map faces -> vertices Nf = len(mesh.faces) Nv = len(mesh.vertices) M0 = csc_matrix((np.ones(Nf), (np.arange(Nf), mesh.faces[:, 0])), (Nf, Nv)) M1 = csc_matrix((np.ones(Nf), (np.arange(Nf), mesh.faces[:, 1])), (Nf, Nv)) M2 = csc_matrix((np.ones(Nf), (np.arange(Nf), mesh.faces[:, 2])), (Nf, Nv)) exact_pot_v = (exact_pot_f[:, :, 0] @ M0 + exact_pot_f[:, :, 1] @ M1 + exact_pot_f[:, :, 2] @ M2) approx_pot_fv = (approx_pot_f[:, :, 0] @ M0 + approx_pot_f[:, :, 1] @ M1 + approx_pot_f[:, :, 2] @ M2) assert_allclose(approx_pot_v, exact_pot_v, rtol=5e-2) assert_allclose(approx_pot_fv, exact_pot_v, rtol=1e-3)
def field_disc(z, a): """Bfield z-component of streamfunction psi=r**2 z-axis for a disk with radius "a" on the xy plane """ coeff = 1e-7 field = -2 * (a**2 + 2 * z**2) / np.sqrt((a**2 + z**2)) + 4 * abs(z) field *= coeff * 2 * np.pi return field # Load disc and subdivide disc = load_example_mesh("unit_disc") for i in range(4): disc = disc.subdivide() disc.vertices -= disc.vertices.mean(axis=0) # Stream functions s = disc.vertices[:, 0]**2 + disc.vertices[:, 1]**2 #%% Create evaluation points and plot them Np = 50 z = np.linspace(-1, 10, Np) + 0.001 points = np.zeros((Np, 3)) points[:, 2] = z mlab.triangular_mesh(*disc.vertices.T, disc.faces, scalars=s,
def test_suhbasis_fail3(): mesh = load_example_mesh("unit_disc") suh = suhtools.SuhBasis(mesh, Nc=1000)
def test_suhbasis_fail2(): mesh = load_example_mesh("unit_disc") suh = suhtools.SuhBasis(mesh, Nc=10, magnetic="foo")
def test_suhbasis_fail(): mesh = load_example_mesh("unit_disc") suh = suhtools.SuhBasis(mesh, Nc=10, boundary_condition="foo")
Head gradient coil ================== Example showing a gradient coil designed on the surface of a MEG system helmet """ import numpy as np from mayavi import mlab from bfieldtools.mesh_conductor import MeshConductor from bfieldtools.coil_optimize import optimize_streamfunctions from bfieldtools.utils import load_example_mesh from bfieldtools import sphtools # Load simple plane mesh that is centered on the origin helmetmesh = load_example_mesh("meg_helmet") coil = MeshConductor(mesh_obj=helmetmesh, fix_normals=True) #%% # Set up target and stray field points. # Here, the target points are on a volumetric grid within a sphere offset = np.array([0, 0, 0.04]) center = offset sidelength = 0.05 n = 12 xx = np.linspace(-sidelength / 2, sidelength / 2, n) yy = np.linspace(-sidelength / 2, sidelength / 2, n) zz = np.linspace(-sidelength / 2, sidelength / 2, n) X, Y, Z = np.meshgrid(xx, yy, zz, indexing="ij")
import matplotlib.pyplot as plt from mayavi import mlab import trimesh import pkg_resources from pyface.api import GUI _gui = GUI() from bfieldtools.mesh_magnetics import magnetic_field_coupling from bfieldtools.mesh_magnetics import magnetic_field_coupling_analytic from bfieldtools.mesh_magnetics import scalar_potential_coupling from bfieldtools.sphtools import compute_sphcoeffs_mesh, basis_fields from bfieldtools.suhtools import SuhBasis from bfieldtools.utils import load_example_mesh mesh = load_example_mesh("bunny_repaired") mesh.vertices -= mesh.vertices.mean(axis=0) mesh_field = mesh.copy() mesh_field.vertices += 0.005 * mesh_field.vertex_normals mesh_field = trimesh.smoothing.filter_laplacian(mesh_field, iterations=1) Ca, Cb = basis_fields(mesh_field.vertices, 4) bsuh = SuhBasis(mesh, 25) Csuh = magnetic_field_coupling_analytic(mesh, mesh_field.vertices) @ bsuh.basis #%% def plot_basis_fields(C, comps):
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 ) #%% # Interpolate MEG data to the sensor surface from bfieldtools.utils import load_example_mesh helmet = load_example_mesh("meg_helmet", process=False) # Bring the surface roughly to the correct place helmet.vertices[:, 2] -= 0.05 # Reset coupling by hand c.B_coupling.reset() mlab.figure(bgcolor=(1, 1, 1)) B_surf = np.sum( c.B_coupling(helmet.vertices) * helmet.vertex_normals[:, :, None], axis=1 ) if PLOT: fig = mlab.quiver3d(*p.T, *n.T, mode="arrow") scalars = B_surf @ s surf = mlab.triangular_mesh( *helmet.vertices.T, helmet.faces, scalars=scalars, colormap="seismic"
Example showing a basic biplanar coil producing homogeneous field in a target region between the two coil planes. The coil planes have holes in them, """ import numpy as np import trimesh from bfieldtools.mesh_conductor import MeshConductor from bfieldtools.coil_optimize import optimize_streamfunctions from bfieldtools.utils import combine_meshes, load_example_mesh # Load simple plane mesh that is centered on the origin planemesh = load_example_mesh("plane_w_holes") angle = np.pi / 2 rotation_matrix = np.array( [ [1, 0, 0, 0], [0, np.cos(angle), -np.sin(angle), 0], [0, np.sin(angle), np.cos(angle), 0], [0, 0, 0, 1], ] ) planemesh.apply_transform(rotation_matrix) # Specify coil plane geometry center_offset = np.array([0, 0, 0])
from mayavi import mlab import trimesh from bfieldtools.suhtools import SuhBasis from bfieldtools.utils import find_mesh_boundaries from trimesh.creation import icosphere from bfieldtools.utils import load_example_mesh # Import meshes # Sphere sphere = icosphere(4, 0.1) # Plane mesh centered on the origin plane = load_example_mesh("10x10_plane_hires") scaling_factor = 0.02 plane.apply_scale(scaling_factor) # Rotate to x-plane t = np.eye(4) t[1:3, 1:3] = np.array([[0, 1], [-1, 0]]) plane.apply_transform(t) # Bunny bunny = load_example_mesh("bunny_repaired") bunny.vertices -= bunny.vertices.mean(axis=0) mlab.figure(bgcolor=(1, 1, 1)) for mesh in (sphere, plane, bunny): s = mlab.triangular_mesh(*mesh.vertices.T, mesh.faces,
""" 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 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])) #%%
def _fake_mesh_conductor(mesh_name="10x10_plane", **kwargs): """ Creates an example 'fake' MeshConductor object """ return mesh_conductor.MeshConductor(mesh_obj=load_example_mesh(mesh_name), **kwargs)
import matplotlib.pyplot as plt from mayavi import mlab import trimesh import pkg_resources from pyface.api import GUI _gui = GUI() from bfieldtools.mesh_magnetics import magnetic_field_coupling from bfieldtools.mesh_magnetics import magnetic_field_coupling_analytic from bfieldtools.mesh_magnetics import scalar_potential_coupling from bfieldtools.sphtools import compute_sphcoeffs_mesh, basis_fields from bfieldtools.suhtools import SuhBasis from bfieldtools.utils import load_example_mesh mesh = load_example_mesh("bunny_repaired", process=True) mesh.vertices -= mesh.vertices.mean(axis=0) mesh_field = mesh.copy() mesh_field.vertices += 0.001 * mesh_field.vertex_normals # mesh_field = trimesh.smoothing.filter_laplacian(mesh_field, iterations=1) # Ca, Cb = basis_fields(mesh_field.vertices, 4) # bsuh = SuhBasis(mesh, 25) # Csuh = magnetic_field_coupling_analytic(mesh, mesh_field.vertices) @ bsuh.basis #%% # def plot_basis_fields(C, comps): # d = 0.17
Example showing a basic biplanar coil producing a high-order spherical harmonic field in a specific target region between the two coil planes. """ import numpy as np from mayavi import mlab import trimesh from bfieldtools.mesh_conductor import MeshConductor from bfieldtools.coil_optimize import optimize_streamfunctions from bfieldtools.utils import combine_meshes, load_example_mesh # Load simple plane mesh that is centered on the origin planemesh = load_example_mesh("10x10_plane_hires") # Specify coil plane geometry center_offset = np.array([0, 0, 0]) standoff = np.array([0, 3, 0]) # Create coil plane pairs coil_plus = trimesh.Trimesh(planemesh.vertices + center_offset + standoff, planemesh.faces, process=False) coil_minus = trimesh.Trimesh(planemesh.vertices + center_offset - standoff, planemesh.faces, process=False) joined_planes = combine_meshes((coil_plus, coil_minus))
for i in range(len(alpha)): xticknames[i] = str(M[i]) m_l = np.arange(-L[i], L[i] + 1, step=1) if i == int(np.floor(len(m_l))): xticknames[i] += "\n" + str(L[i]) plt.figure() plt.plot(a**2) #%% Compute potential on the helmet mesh from bfieldtools.utils import load_example_mesh from bfieldtools.flatten_mesh import flatten_mesh, mesh2plane helmet = load_example_mesh("meg_helmet", process=False) # Bring the surface roughly to the correct place helmet.vertices[:, 2] -= 0.045 # The helmet is slightly tilted, correct for this # (probably the right coordinate transformation could be found from MNE) rotmat = np.eye(3) tt = 0.015 * np.pi rotmat[:2, :2] = np.array([[np.cos(tt), np.sin(tt)], [-np.sin(tt), np.cos(tt)]]) helmet.vertices = helmet.vertices @ rotmat tt = -0.02 * np.pi rotmat[1:, 1:] = np.array([[np.cos(tt), np.sin(tt)], [-np.sin(tt), np.cos(tt)]]) helmet.vertices = helmet.vertices @ rotmat helmet.vertices[:, 1] += 0.005