def test_calc_dadt(self, sphere3_msh): phi = sphere3_msh.nodes.node_coord[:, 0] + \ 2 * sphere3_msh.nodes.node_coord[:, 1] + \ -3 * sphere3_msh.nodes.node_coord[:, 2] potential = mesh_io.NodeData(phi, mesh=sphere3_msh) dadt = .2 * sphere3_msh.nodes.node_coord dadt = mesh_io.NodeData(dadt, mesh=sphere3_msh) E = np.zeros((sphere3_msh.elm.nr, 3)) E = [-1, -2, 3] - dadt.node_data2elm_data().value E = mesh_io.ElementData(E, mesh=sphere3_msh) E.assign_triangle_values() cond = sphere3_msh.elm.tag1 cond = mesh_io.ElementData(cond, mesh=sphere3_msh) m = fem.calc_fields(potential, 'vDJEgsej', cond, dadt=dadt, units='m') assert np.allclose(m.field['v'].value, potential.value) assert np.allclose(m.field['D'].value, dadt.value) assert np.allclose(m.field['g'].value, [1, 2, -3]) assert np.allclose(m.field['E'].value, E.value) assert np.allclose(m.field['J'].value, cond.value[:, None] * E.value) assert np.allclose(m.field['conductivity'].value, cond.value) assert np.allclose(m.field['normE'].value, np.linalg.norm(E.value, axis=1)) assert np.allclose(m.field['normJ'].value, np.linalg.norm(cond.value[:, None] * E.value, axis=1))
def test_calc_vEJgs(self, sphere3_msh): phi = sphere3_msh.nodes.node_coord[:, 0] + \ 2 * sphere3_msh.nodes.node_coord[:, 1] + \ -3 * sphere3_msh.nodes.node_coord[:, 2] potential = mesh_io.NodeData(phi, mesh=sphere3_msh) E = np.zeros((sphere3_msh.elm.nr, 3)) E[:] = [-1., -2., 3.] E = mesh_io.ElementData(E, mesh=sphere3_msh) cond = sphere3_msh.elm.tag1 cond = mesh_io.ElementData(cond, mesh=sphere3_msh) m = fem.calc_fields(potential, 'vJEgsej', cond) assert np.allclose(m.field['v'].value, potential.value) assert np.allclose(m.field['E'].value, E.value * 1e3) assert np.allclose(m.field['J'].value, cond.value[:, None] * E.value * 1e3) assert np.allclose(m.field['g'].value, -E.value * 1e3) assert np.allclose(m.field['conductivity'].value, cond.value) assert np.allclose(m.field['normE'].value, np.linalg.norm(E.value, axis=1) * 1e3) assert np.allclose( m.field['normJ'].value, np.linalg.norm(cond.value[:, None] * E.value, axis=1) * 1e3)
def test_tms_dadt(self, tms_sphere): m, cond, dAdt, E_analytical = tms_sphere v = fem.tms_dadt(m, cond, dAdt) E = -v.gradient().value * 1e3 - dAdt.node_data2elm_data().value m.elmdata = [mesh_io.ElementData(E_analytical, 'analytical'), mesh_io.ElementData(E, 'E_FEM'), mesh_io.ElementData(E_analytical + dAdt.node_data2elm_data().value, 'grad_analytical'), dAdt] #mesh_io.write_msh(m, '~/Tests/fem.msh') assert rdm(E, E_analytical) < .2 assert np.abs(mag(E, E_analytical)) < np.log(1.1)
def test_single_dipole(self, dipole_direction, pos_target, sphere3_msh): bar = sphere3_msh.elements_baricenters()[sphere3_msh.elm.tetrahedra] vol = sphere3_msh.elements_volumes_and_areas() dipole_th = np.argmin(np.linalg.norm(pos_target - bar, axis=1)) dipole_pos = bar[dipole_th] dipole_th = sphere3_msh.elm.tetrahedra[dipole_th] surface_nodes = np.unique( sphere3_msh.elm[sphere3_msh.elm.tag1 == 1005, :3]) surface_nodes_pos = sphere3_msh.nodes[surface_nodes] analytical_v = analytical_solutions.potential_dipole_3layers( [85, 90, 95], 2, 0.1, dipole_pos, dipole_direction, surface_nodes_pos) # Relationship between primary current J and dipole vector p # p = \int J dV # p = J*V_i # J = p/V_i primary_j = mesh_io.ElementData(np.zeros((sphere3_msh.elm.nr, 3))) primary_j[dipole_th] = dipole_direction / (vol[dipole_th] * 1e-9) cond = 2 * np.ones(sphere3_msh.elm.nr) cond[sphere3_msh.elm.tag1 == 4] = 0.1 S = fem.FEMSystem.electric_dipole(sphere3_msh, cond) b = S.assemble_electric_dipole_rhs(primary_j) numerical_v = S.solve(b)[surface_nodes - 1] analytical_v -= np.average(analytical_v) numerical_v -= np.average(numerical_v) assert rdm(analytical_v, numerical_v) < 0.2 assert mag(analytical_v, numerical_v) < 0.15
def test_solve_petsc(self, tms_sphere): m, cond, dAdt, E_analytical = tms_sphere S = fem.FEMSystem.tms(m, cond) b = S.assemble_tms_rhs(dAdt) x = S.solve(b) v = mesh_io.NodeData(x, 'FEM', mesh=m) E = -v.gradient().value * 1e3 - dAdt.node_data2elm_data().value m.elmdata = [mesh_io.ElementData(E_analytical, 'analytical'), mesh_io.ElementData(E, 'E_FEM'), mesh_io.ElementData(E_analytical + dAdt.node_data2elm_data().value, 'grad_analytical'), dAdt] m.nodedata = [mesh_io.NodeData(x, 'FEM')] #mesh_io.write_msh(m, '~/Tests/fem.msh') assert rdm(E, E_analytical) < .2 assert np.abs(mag(E, E_analytical)) < np.log(1.1)
def test_avoid_mean_field_norm_elm(self, sphere_surf): a = opt_struct.TDCSavoid(tissues=[1003], weight=1e4, lf_type='element', mesh=sphere_surf) field = mesh_io.ElementData(np.ones((sphere_surf.elm.nr, 3))) field[sphere_surf.elm.tag1 == 1003] = [2, 0, 0] assert np.isclose(a.mean_field_norm_in_region(field), 2)
def calc_B(J, n_voxels, affine, calc_res=2., mask=False): ''' Calculates the magnetic field caused by a current density distribution J Parameters ---------- J: simnibs.msh.ElementData ElementData field with the current density field n_voxels: list or tuple number of voxels in x, y, and z directions for the output B affine: ndarray A 4x4 matrix specifying the transformation from voxels to xyz for the output B calc_res: float (optional) Resolution of the space used for the FFT calculations, in mm. Default: 2 mask: bool (optional) Wether to mask the resul using the mesh. Default: False Returns -------- B: ndarray numpy array of size n_voxesx3 with the magnetic field Reference ---------- Yazdanian, H., Saturnino, G. B., Thielscher, A., & Knudsen, K. (2020). Fast evaluation of the Biot-Savart integral using FFT for electrical conductivity imaging. Journal of Computational Physics, 109408. https://doi.org/10.1016/j.jcp.2020.109408 Example --------- Load a simulation result with a 'J' field can calculate B in an ROI Defined by a nifti file >>> import simnibs >>> import nibabel >>> simulation_J = simnibs.read_msh('simulation_result_with_J.msh') >>> reference_nifti = nibabel.load('reference.nii) >>> B = simnibs.simulation.calc_B(simulation_J.field['J'], reference_nifti.shape, reference_nifti.affine) ''' mesh = J.mesh # Computational Domain (before zero-padding) domain = _comp_domain(mesh, n_voxels, affine) # Voxelize J J_vol, affine_vol = _voxelize(J, domain, calc_res) # Calculate B in the whole domain B_vol = _bs_ft(J_vol, calc_res) # Interpolate B to the domain of interest B = transformations.volumetric_affine((B_vol, affine_vol), np.eye(4), affine, n_voxels, intorder=1, keep_vector_length=False) if mask: msk = mesh_io.ElementData(np.ones(mesh.elm.nr), mesh=mesh) msk = msk.interpolate_to_grid(n_voxels, affine, method='assign') B *= msk[..., None] return B
def test_mean_angle(self, sphere_vol): t = opt_struct.TDCStarget(indexes=[1, 2], directions=[[1, 0, 0], [0, 1, 0]], mesh=sphere_vol, lf_type='element', radius=0) f = mesh_io.ElementData(np.zeros((sphere_vol.elm.nr, 3))) f[1] = [1, 1, 0] f[2] = [2, 2, 0] assert np.isclose(t.mean_angle(f), 45)
def test_leadfield(self, input_type, n_workers, field, post_pro, cube_msh): if sys.platform == 'win32' and n_workers > 1: ''' Same as above, does not work on windows ''' return m = cube_msh cond = np.ones(m.elm.nr) cond[m.elm.tag1 > 5] = 1e3 cond = mesh_io.ElementData(cond, mesh=m) if input_type == 'tag': el = [1100, 1101, 1101] elif input_type == 'node': el = [ np.unique(m.elm[m.elm.tag1 == 1100, :3]), np.unique(m.elm[m.elm.tag1 == 1101, :3]), np.unique(m.elm[m.elm.tag1 == 1101, :3]) ] fn_hdf5 = tempfile.NamedTemporaryFile(delete=False).name dataset = 'leadfield' if post_pro: def post(E): return E[:10] * 2 else: post = None fem.tdcs_leadfield(m, cond, el, fn_hdf5, dataset, roi=[5], field=field, post_pro=post, n_workers=n_workers, input_type=input_type) if not post_pro: n_roi = np.sum(m.elm.tag1 == 5) with h5py.File(fn_hdf5) as f: assert f[dataset].shape == (2, n_roi, 3) assert rdm(f[dataset][0, ...], np.tile([0., 100, 0.], (n_roi, 1))) < .2 assert mag(f[dataset][0, ...], np.tile([0., 100, 0.], (n_roi, 1))) < np.log(1.1) if post_pro: with h5py.File(fn_hdf5) as f: assert f[dataset].shape == (2, 10, 3) assert rdm(f[dataset][0, ...], np.tile([0., 200, 0.], (10, 1))) < .2 assert mag(f[dataset][0, ...], np.tile([0., 200, 0.], (10, 1))) < np.log(1.1) os.remove(fn_hdf5)
def test_mean_intensity_none(self, sphere_vol): t = opt_struct.TDCStarget(indexes=[1, 2], directions=None, mesh=sphere_vol, lf_type='element', radius=0) f = mesh_io.ElementData(np.zeros((sphere_vol.elm.nr, 3))) f[1] = [np.sqrt(2), np.sqrt(2), 0] f[2] = [np.sqrt(3), np.sqrt(3), np.sqrt(3)] vols = sphere_vol.elements_volumes_and_areas()[:] m = np.average([2, 3], weights=vols[:2]) assert np.isclose(t.mean_intensity(f), m)
def test_tdcs_neumann_3_el(self, cube_msh): m = cube_msh cond = np.ones(m.elm.nr) cond[m.elm.tag1 > 5] = 1e3 cond = mesh_io.ElementData(cond) el_tags = [1100, 1101, 1101] currents = [.5, -1.5, 1.] x = fem.tdcs_neumann(m, cond, currents, el_tags) sol = (m.nodes.node_coord[:, 1] - 50) / 20 m.nodedata = [x, mesh_io.NodeData(sol, 'Analytical')] #mesh_io.write_msh(m, '~/Tests/fem.msh') assert rdm(sol, x.value) < .1 assert np.abs(mag(x.value, sol)) < np.log(1.1)
def test_solve_dirichlet_petsc(self, cube_msh): m = cube_msh cond = np.ones(m.elm.nr) cond[m.elm.tag1 > 5] = 1e3 cond = mesh_io.ElementData(cond) el_tags = [1100, 1101] potentials = [1, -1] S = fem.FEMSystem.tdcs(m, cond, el_tags, potentials) x = S.solve() sol = m.nodes.node_coord[:, 1]/50. m.nodedata = [mesh_io.NodeData(x, 'FEM'), mesh_io.NodeData(sol, 'Analytical')] #mesh_io.write_msh(m, '~/Tests/fem.msh') assert rdm(sol, x.T) < .1 assert np.abs(mag(x, sol)) < np.log(1.1)
def test_visualization(self, tensor): eig_val, eig_vec = np.linalg.eig(tensor) max_eig = eig_val.argmax() min_eig = eig_val.argmin() t = np.tile(tensor, [10, 1, 1]) factor = np.arange(1, 11) t *= factor[:, None, None] t = mesh_io.ElementData(t.reshape(-1, 9)) fields = cond.TensorVisualization(t, None, all_compoents=True) assert np.allclose(fields[0].value / factor[:, None], eig_val[max_eig] * eig_vec[:, max_eig]) assert np.allclose(fields[2].value / factor[:, None], eig_val[min_eig] * eig_vec[:, min_eig]) assert np.allclose(fields[3].value / factor, np.prod(eig_val) ** (1.0/3.0))
def test_solve_assemble_neumann_petsc(self, cube_msh): m = cube_msh cond = np.ones(m.elm.nr) cond[m.elm.tag1 > 5] = 25 cond = mesh_io.ElementData(cond) el_tags = [1100, 1101] currents = [1, -1] S = fem.FEMSystem.tdcs_neumann(m, cond, el_tags[0]) b = S.assemble_tdcs_neumann_rhs(el_tags[1:], currents[1:]) x = S.solve(b) sol = (m.nodes.node_coord[:, 1] - 50) / 10 m.nodedata = [mesh_io.NodeData(x, 'FEM'), mesh_io.NodeData(sol, 'Analytical')] #mesh_io.write_msh(m, '~/Tests/fem.msh') assert rdm(sol, x.T) < .1 assert np.abs(mag(x, sol)) < np.log(1.5)
def tms_sphere(sphere3_msh): m = sphere3_msh.crop_mesh(elm_type=4) dipole_pos = np.array([0., 0., 300]) dipole_moment = np.array([1., 0., 0.]) didt = 1e6 r = (m.nodes.node_coord - dipole_pos) * 1e-3 dAdt = 1e-7 * didt * np.cross(dipole_moment, r) / (np.linalg.norm(r, axis=1)[:, None] ** 3) dAdt = mesh_io.NodeData(dAdt, mesh=m) dAdt.field_name = 'dAdt' dAdt.mesh = m pos = m.elements_baricenters().value E_analytical = analytical_solutions.tms_E_field(dipole_pos * 1e-3, dipole_moment, didt, pos * 1e-3) cond = mesh_io.ElementData(np.ones(m.elm.nr)) cond.mesh = m return m, cond, dAdt, E_analytical
def test_solve_assemble_aniso(self, cube_msh): m = cube_msh cond = np.tile(np.eye(3), (m.elm.nr, 1, 1)) cond[m.elm.tag1 > 5] *= 100 cond[m.elm.tag1 < 5, 0, :] = 0.01 cond[m.elm.tag1 < 5, 1, :] = 0.1 cond = mesh_io.ElementData(cond.reshape(-1, 9)) el_tags = [1100, 1101] currents = [1, -1] S = fem.FEMSystem.tdcs_neumann(m, cond, el_tags[0]) b = S.assemble_tdcs_neumann_rhs(el_tags[1:], currents[1:]) x = S.solve(b) sol = (m.nodes.node_coord[:, 1] - 50) / 10 m.nodedata = [mesh_io.NodeData(x, 'FEM'), mesh_io.NodeData(sol, 'Analytical')] #mesh_io.write_msh(m, '~/Tests/fem.msh') assert rdm(sol, x.T) < .1 assert np.abs(mag(x, sol)) < np.log(1.5)
def test_calc_tensor(self, sphere3_msh): phi = sphere3_msh.nodes.node_coord[:, 0] + \ 2 * sphere3_msh.nodes.node_coord[:, 1] + \ -3 * sphere3_msh.nodes.node_coord[:, 2] potential = mesh_io.NodeData(phi, mesh=sphere3_msh) o = np.ones(sphere3_msh.elm.nr) z = np.zeros(sphere3_msh.elm.nr) cond = np.reshape(np.eye(3) * np.array([1, 2, 3]), -1) cond = np.tile(cond, [sphere3_msh.elm.nr, 1]) cond = mesh_io.ElementData(cond, mesh=sphere3_msh) m = fem.calc_fields(potential, 'vJEgsej', cond, units='m') assert np.allclose(m.field['v'].value, potential.value) assert np.allclose(m.field['g'].value, [1, 2, -3]) assert np.allclose(m.field['E'].value, [-1, -2, 3]) assert np.allclose(m.field['J'].value, [-1, -4, 9])
def test_calc_gradient(self, cube_msh): m = cube_msh cond = np.ones(m.elm.nr) cond = mesh_io.ElementData(cond) S = fem.FEMSystem.tdcs_neumann(m, cond, 1100) z = m.nodes.node_coord[:, 0] assert np.allclose(S.calc_gradient(z), [1, 0, 0]) z = m.nodes.node_coord[:, 1] assert np.allclose(S.calc_gradient(z), [0, 1, 0]) z = m.nodes.node_coord[:, 2] assert np.allclose(S.calc_gradient(z), [0, 0, 1]) z = m.nodes.node_coord grad = S.calc_gradient(z) * [2, 3, 1] assert np.allclose(grad[:, :, 0], [2, 0, 0]) assert np.allclose(grad[:, :, 1], [0, 3, 0]) assert np.allclose(grad[:, :, 2], [0, 0, 1])
def test_solve_assemble_neumann_nodes(self, cube_msh): m = cube_msh cond = np.ones(m.elm.nr) cond[m.elm.tag1 > 5] = 25 cond = mesh_io.ElementData(cond) currents = [1, -1] nodes_top = np.unique(m.elm[m.elm.tag1 == 1100, :3]) nodes_bottom = np.unique(m.elm[m.elm.tag1 == 1101, :3]) S = fem.FEMSystem.tdcs_neumann(m, cond, nodes_top, input_type='node') b = S.assemble_tdcs_neumann_rhs(nodes_bottom, currents[1:], input_type='node') x = S.solve(b) sol = (m.nodes.node_coord[:, 1] - 50) / 10 m.nodedata = [ mesh_io.NodeData(x, 'FEM'), mesh_io.NodeData(sol, 'Analytical') ] #mesh_io.write_msh(m, '~/Tests/fem.msh') assert rdm(sol, x.T) < .1 assert np.abs(mag(x, sol)) < np.log(1.5)