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)) joined_planes = joined_planes.subdivide() # Create mesh class object coil = MeshConductor( verts=joined_planes.vertices, tris=joined_planes.faces, fix_normals=True, basis_name="suh", N_suh=100, ) #%% # Set up target and stray field points # Here, the target points are on a volumetric grid within a sphere center = np.array([0, 0, 0]) * scaling_factor sidelength = 2 * scaling_factor n = 8 xx = np.linspace(-sidelength / 2, sidelength / 2, n) yy = np.linspace(-sidelength / 2, sidelength / 2, n)
standoff = np.array([0, 4, 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) mesh1 = combine_meshes((coil_plus, coil_minus)) mesh2 = mesh1.copy() mesh2.apply_scale(1.4) coil = MeshConductor(mesh_obj=mesh1, basis_name="inner", N_sph=4) shieldcoil = MeshConductor(mesh_obj=mesh2, basis_name="inner", N_sph=4) #%% # Plot geometry f = mlab.figure(None, bgcolor=(1, 1, 1), fgcolor=(0.5, 0.5, 0.5), size=(800, 800)) coil.plot_mesh(opacity=0.2, figure=f) shieldcoil.plot_mesh(opacity=0.2, figure=f) #%% # Compute inductances and coupling M11 = coil.inductance
================================== First 15 surface harmonics of a triangle mesh. The index (and spatial frequency) increases from left to right, row by row. The hole and outer boundary have zero tangential derivative. The mesh discretization is shown in the magnified inset to the right. """ from bfieldtools.mesh_conductor import MeshConductor import numpy as np SAVE = False SAVE_DIR = "./Surface harmonics/" c = MeshConductor( mesh_file=SAVE_DIR + "arbitrary_surface.stl", process=True, basis_name="suh", N_suh=15, fix_normals=True, ) T_x = 1.5 * np.pi / 2 T_z = -1.02 * np.pi rotmat = np.array([ [np.cos(T_z), -np.sin(T_z), 0, 0], [np.sin(T_z), np.cos(T_z), 0, 0], [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],
else: with np.load(SAVE_DIR + "mne_data.npz", allow_pickle=True) as data: mesh = data["mesh"] p = data["p"] n = data["n"] mesh = trimesh.Trimesh(vertices=data["vertices"], faces=data["faces"]) evoked = mne.Evoked(SAVE_DIR + "left_auditory-ave.fif") #%% # Fit the surface current for the auditory evoked response c = MeshConductor(mesh_obj=mesh, basis_name="suh", N_suh=150) M = c.mass 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)
standoff = np.array([0, 4, 0]) * scaling_factor # 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) mesh1 = combine_meshes((coil_minus, coil_plus)) mesh2 = mesh1.copy() mesh2.apply_scale(1.4) coil1 = MeshConductor(mesh_obj=mesh1, basis_name="inner", N_sph=4) coil2 = MeshConductor(mesh_obj=mesh2, basis_name="inner", N_sph=4) M11 = coil1.inductance M22 = coil2.inductance M21 = coil2.mutual_inductance(coil1) # Mapping from I1 to I2, constraining flux through mesh2 to zero P = -np.linalg.solve(M22, M21) A1, Beta1 = coil1.sph_couplings A2, Beta2 = coil2.sph_couplings # Use lines below to get coulings with different normalization # from bfieldtools.sphtools import compute_sphcoeffs_mesh # A1, Beta1 = compute_sphcoeffs_mesh(mesh1, 5, normalization='energy', R=1) # A2, Beta2 = compute_sphcoeffs_mesh(mesh2, 5, normalization='energy', R=1)
# To spice things up, let's distort the planes a bit joined_planes.vertices = ( joined_planes.vertices - 0.5 * np.linalg.norm(joined_planes.vertices, axis=1)[:, None] * np.sign( joined_planes.vertices[:, 1])[:, None] * joined_planes.vertex_normals) joined_planes.vertices = ( joined_planes.vertices - 0.5 * np.linalg.norm(joined_planes.vertices, axis=1)[:, None] * np.sign( joined_planes.vertices[:, 1])[:, None] * joined_planes.vertex_normals) # Create mesh class object coil = MeshConductor( mesh_obj=joined_planes, fix_normals=True, basis_name="suh", N_suh=100, sph_radius=0.2, sph_normalization="energy", ) #%% # Set up target spherical harmonics components target_alms = np.zeros((coil.opts["N_sph"] * (coil.opts["N_sph"] + 2), )) target_blms = np.zeros((coil.opts["N_sph"] * (coil.opts["N_sph"] + 2), )) target_blms[4] += 1 #%% # Create bfield specifications used when optimizing the coil geometry
================== 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")
mesh1 = filter_laplacian(mesh1) mesh2 = filter_laplacian(mesh2, 0.9) elif domain == "combined": from trimesh.creation import icosphere mesh1 = icosphere(4, 0.65) mesh2 = load_example_mesh("cube_fillet") mesh2.vertices -= mesh2.vertices.mean(axis=0) mesh2.vertices *= 0.15 # mesh2 = mesh2.subdivide() coil1 = MeshConductor( mesh_obj=mesh1, N_sph=3, inductance_nchunks=100, fix_normals=False, inductance_quad_degree=2, ) coil2 = MeshConductor( mesh_obj=mesh2, N_sph=3, inductance_nchunks=100, fix_normals=False, inductance_quad_degree=2, ) M11 = coil1.inductance M22 = coil2.inductance # Add rank-one matrix, so that M22 can be inverted M22 += np.ones_like(M22) / M22.shape[0] * np.mean(np.diag(M22))
coilmesh.apply_transform(rotation_matrix) coilmesh1 = coilmesh.copy() # coilmesh1.apply_scale(1.3) coilmesh2 = coilmesh.copy() # coilmesh1 = coilmesh.union(coilmesh1) # coilmesh1 = coilmesh1.subdivide().subdivide() # coilmesh2 = coilmesh.subdivide() # Create mesh class object coil = MeshConductor( verts=coilmesh1.vertices * 0.75, tris=coilmesh1.faces, fix_normals=True, basis_name="suh", N_suh=400, ) def alu_sigma(T): ref_T = 293 # K ref_rho = 2.82e-8 # ohm*meter alpha = 0.0039 # 1/K rho = alpha * (T - ref_T) * ref_rho + ref_rho return 1 / rho
#%% ##%% Fit the surface current for the auditory evoked response using pinv # c = MeshConductor(mesh_obj=mesh, basis_name="suh", N_suh=35) # M = c.mass # B_sensors = np.einsum("ijk,ij->ik", c.B_coupling(p), n) # # # asuh = np.linalg.pinv(B_sensors, rcond=1e-15) @ field # # s = StreamFunction(asuh, c) # b_filt = B_sensors @ s #%% Suh fit c = MeshConductor(mesh_obj=mesh, basis_name="suh", N_suh=150) M = c.mass B_sensors = np.einsum("ijk,ij->ik", c.B_coupling(p), n) ss = np.linalg.svd(B_sensors @ B_sensors.T, False, False) reg_exp = 1 plot_this = True rel_errors = [] _lambda = np.max(ss) * (10**(-reg_exp)) # Laplacian in the suh basis is diagonal BB = B_sensors.T @ B_sensors + _lambda * (-c.laplacian) / np.max( abs(c.laplacian)) a = np.linalg.solve(BB, B_sensors.T @ field) s = StreamFunction(a, c)
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)) # Create mesh class object coil = MeshConductor( mesh_obj=joined_planes, fix_normals=True, basis_name="suh", N_suh=100 ) #%% # Set up target and stray field points # Here, the target points are on a volumetric grid within a sphere center = np.array([0, 0, 0]) sidelength = 1.5 n = 8 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")
from bfieldtools.mesh_calculus import gradient from bfieldtools.mesh_magnetics import ( magnetic_field_coupling, magnetic_field_coupling_analytic, ) from bfieldtools.mesh_conductor import MeshConductor import pkg_resources # Load simple plane mesh that is centered on the origin file_obj = pkg_resources.resource_filename( "bfieldtools", "example_meshes/10x10_plane.obj" ) coilmesh = trimesh.load(file_obj, process=False) coil = MeshConductor(mesh_obj=coilmesh) weights = np.zeros(coilmesh.vertices.shape[0]) weights[coil.inner_vertices] = 1 test_points = coilmesh.vertices + np.array([0, 1, 0]) B0 = magnetic_field_coupling(coilmesh, test_points) @ weights B1 = magnetic_field_coupling_analytic(coilmesh, test_points) @ weights f = mlab.figure(None, bgcolor=(1, 1, 1), fgcolor=(0.5, 0.5, 0.5), size=(800, 800)) s = mlab.triangular_mesh( *coilmesh.vertices.T, coilmesh.faces, scalars=weights, colormap="viridis" ) s.enable_contours = True
standoff = np.array([0, 4, 0]) * scaling_factor # 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)) # Create mesh class object coil = MeshConductor(mesh_obj=joined_planes, fix_normals=True, basis_name="inner") # Separate object for shield geometry shieldmesh = trimesh.load( file_obj=pkg_resources.resource_filename( "bfieldtools", "example_meshes/closed_cylinder_remeshed.stl"), process=True, ) shieldmesh.apply_scale(15) shield = MeshConductor(mesh_obj=shieldmesh, process=True, fix_normals=True, basis_name="vertex")
center_offset = np.array([0, 0, 0]) standoff = np.array([0, 20, 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)) # Create MeshConductor object, which finds the holes and sets the boundary condition coil = MeshConductor(mesh_obj=joined_planes, fix_normals=True) #%% # Set up target and stray field points # Here, the target points are on a volumetric grid within a sphere center = np.array([0, 0, 0]) sidelength = 10 n = 8 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")
An example stream function (red--blue colormap) and its rotated gradient, i.e. the surface current density (arrows; green colormap) on a surface mesh with a hole in it. The surface normal is oriented up towards the reader. """ from bfieldtools.mesh_conductor import MeshConductor, StreamFunction import pkg_resources from bfieldtools.mesh_calculus import gradient import numpy as np SAVE = True SAVE_DIR = "./Streamfunction gradient/" c = MeshConductor( mesh_file=pkg_resources.resource_filename( "bfieldtools", "example_meshes/curved_surf_w_hole.stl"), process=True, basis_name="suh", N_suh=10, fix_normals=True, ) T_x = 1.5 * np.pi / 2 T_z = -1.02 * np.pi rotmat = np.array([ [np.cos(T_z), -np.sin(T_z), 0, 0], [np.sin(T_z), np.cos(T_z), 0, 0], [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],
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)) # Create mesh class object coil = MeshConductor(mesh_obj=joined_planes, fix_normals=True, basis_name="suh", N_suh=100) #%% # Set up target and stray field points # Here, the target points are on a volumetric grid within a sphere center = np.array([0, 0, 0]) sidelength = 1.5 n = 8 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")
standoff = np.array([0, 4, 0]) * scaling_factor # 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)) planemesh = joined_planes # Create mesh class object coil = MeshConductor(mesh_obj=joined_planes, fix_normals=True) # Separate object for shield geometry shieldmesh = load_example_mesh("closed_cylinder_remeshed") shieldmesh.apply_scale(15) shield = MeshConductor(mesh_obj=shieldmesh, process=True, fix_normals=True) #%% N = 80 points = np.zeros((N * N, 3)) w = 12 X, Y = np.meshgrid(np.linspace(-w, w, N), np.linspace(-w, w, N), indexing="ij") X += planemesh.vertices.mean(axis=0)[0] Y += planemesh.vertices.mean(axis=0)[1] points[:, 0] = X.flatten()