def main(): args = docopt(__doc__) vol_file = args['<vol>'] fem_file = args['<FEM>'] out = args['<out>'] # First load in the image file img = nib.load(vol_file) tet_entity = guess_entity(fem_file, 3, 2) gm_entity = guess_entity(fem_file, 2, 2) wm_entity = guess_entity(fem_file, 2, 1) tn_tag, tn_coord, _ = gl.load_gmsh_nodes(fem_file, tet_entity) te_tag, _, te_param = gl.load_gmsh_elems(fem_file, tet_entity) gmn_tag, gmn_coord, _ = gl.load_gmsh_nodes(fem_file, gm_entity) wmn_tag, wmn_coord, _ = gl.load_gmsh_nodes(fem_file, wm_entity) #Pull ribbon data ribbon = img.get_data() affine = img.affine x, y, z = np.where(ribbon > 0) #Formulate lists as arrays tn_tag = np.array(tn_tag) gmn_tag = np.array(gmn_tag) wmn_tag = np.array(wmn_tag) #Set up inputs tn_list = te_param[0].reshape((-1, 4)) #First concatenate nodes min_t, max_t, len_t = np.min(tn_tag), np.max(tn_tag), np.size(tn_tag) min_g, max_g, len_g = np.min(gmn_tag), np.max(gmn_tag), np.size(gmn_tag) min_w, max_w, len_w = np.min(wmn_tag), np.max(wmn_tag), np.size(wmn_tag) #Wrap up features prop_arr = np.array([[min_t, max_t, len_t], [min_g, max_g, len_g], [min_w, max_w, len_w]]) #Map nodes to contiguous indexing node_list = tp.map_nodes(tn_list, prop_arr) #Coordinate array matching indexing coord_arr = np.concatenate((tn_coord, gmn_coord, wmn_coord)) coords = coord_arr.reshape((-1, 3)) #Determine whether the file is parcellation-based (integer encoded) or weight-based float_encoding = np.any(np.mod(ribbon, 1)) #Project if not float_encoding: n_out_arr = tp.tetrahedral_parcel_projection(node_list, coord_arr, ribbon, affine) else: n_out_arr = tp.tetrahedral_weight_projection(node_list, coord_arr, ribbon, affine) np.save(out, n_out_arr)
def load_surf_trigs(f_msh, entities): ''' surf: Path to gmsh MSH file entities: List of entities to attempt of form (dim, tag) ''' for dim, tag in entities: try: _, _, trigs = gl.load_gmsh_elems(f_msh, (dim, tag)) except ValueError: continue else: return trigs.reshape(-1, 3) logging.error("Could not properly load Mesh! Check entity tags!") raise ValueError
def main(): arguments = docopt(__doc__) msh_file = arguments['<msh>'] centroid_file = arguments['<centroid>'] out = arguments['<out_prefix>'] radius = arguments['--radius'] or 25 distance = arguments['--distance'] or 1 #Try every alternative of SURF_HEAD n_tag, n_coord, tri = None, None, None for i, s in enumerate(SURF_HEAD): try: n_tag, n_coord, _ = gl.load_gmsh_nodes(msh_file, s) _, _, tri = gl.load_gmsh_elems(msh_file, s) except ValueError: if i == len(SURF_HEAD): raise else: continue else: break tri = tri[0] tri = tri.reshape((len(tri) // 3, -1)) #Load in centroid voxel centroid = np.genfromtxt(centroid_file) #Get minimum euclidean distance eudist = np.linalg.norm(n_coord - centroid, axis=1) min_ind = np.argmin(eudist) #Capture nodes within spherical ROI head_centroid_to_all = np.linalg.norm(n_coord - n_coord[min_ind], axis=1) search_inds = np.where(head_centroid_to_all < radius) #Get relevant triangles to vertices vert_list = n_tag[search_inds] vert_coords = n_coord[search_inds] t_arr = gl.get_relevant_triangles(vert_list, tri) rel_ind = np.where(t_arr > 0) t_rel = tri[rel_ind[0], :] #Get triangle to index mapping u_val = np.unique(t_rel) u_ind = np.arange(0, u_val.shape[0]) #Map each triangle to its index sort_map = {v: i for v, i in zip(u_val, u_ind)} map_func = np.vectorize(lambda x: sort_map[x]) mapped_trigs = map_func(t_rel) rel_verts = np.where(np.isin(n_tag, u_val)) rel_verts_coords = n_coord[rel_verts, :][0] #Compute vertex normals norm_arr = gl.get_vert_norms(mapped_trigs, rel_verts_coords) #Dilate v_norm = np.mean(norm_arr, axis=0) dil_coords = vert_coords + distance * v_norm #Write dilated vertices and mean normal to file np.save(out + "_dilated_coords.npy", dil_coords) np.save(out + "_mean_norm.npy", v_norm) #Generate param surf dil_faces_ind = gl.get_subset_triangles(vert_list, t_rel) dil_faces = t_rel[np.where(dil_faces_ind)].flatten( order='C') + vert_list.max() dil_faces = list(dil_faces) dil_verts = vert_list + vert_list.max() dil_coords = dil_coords.flatten() gmsh.initialize() gmsh.model.add('param_surf') tag = gmsh.model.addDiscreteEntity(2, 2001) gmsh.model.mesh.setNodes(2, tag, nodeTags=dil_verts, coord=dil_coords) gmsh.model.mesh.setElements( 2, tag, [2], elementTags=[range(1, len(dil_faces) // 3 + 1)], nodeTags=[dil_faces]) gmsh.write(out + "_param_surf.msh") gmsh.finalize()
def __init__(self, mesh_file, initial_centroid, tet_weights, field_dir, coil, span=35, local_span=8, distance=1, didt=1e6, cpus=1, solver_options=None): ''' Standard constructor Arguments: mesh_file Path to FEM model initial_centroid Initial point to grow sampling region tet_weights Weighting scores for each tetrahedron (1D array ordered by node ID) field_dir Directory to perform simulation experiments in coil TMS coil file (either dA/dt volume or coil geometry) span Radius of points to include in sampling surface local_span Radius of points to include in construction of local geometry for normal and curvature estimation distance Distance from coil to head surface didt Intensity of stimulation cpus Number of cpus to use for simulation ''' self.mesh = mesh_file self.tw = tet_weights self.field_dir = field_dir self.coil = coil self.didt = didt logger.info(f"Configured to use {cpus} cpus...") self.cpus = cpus self.geo_radius = local_span self.distance = distance self.solver_opt = solver_options logger.info('Loading in coordinate data from mesh file...') self.nodes, self.coords, _ = geolib.load_gmsh_nodes(self.mesh, (2, 5)) _, _, trigs = geolib.load_gmsh_elems(self.mesh, (2, 5)) self.trigs = np.array(trigs).reshape(-1, 3) logger.info('Successfully pulled in node and element data!') # Construct basis of sampling space using centroid logger.info('Constructing initial sampling surface...') C, iR, bounds = self._initialize(initial_centroid, span) self.C = C self.iR = iR self.bounds = bounds logger.info('Successfully constructed initial sampling surface') # Store single read in memory, this will prevent GC issues logger.info('Caching mesh file on instance construction...') self.cached_mesh = mesh_io.read_msh(mesh_file) self.cached_mesh.fix_surface_labels() logger.info('Successfully cached mesh file') logger.info('Storing standard conductivity values...') condlist = [c.value for c in cond.standard_cond()] self.cond = cond.cond2elmdata(self.cached_mesh, condlist) logger.info('Successfully stored conductivity values...') # Control for coil file-type and SimNIBS changing convention if self.coil.endswith('.ccd'): self.normflip = 1 else: self.normflip = -1