def compute_field(mesh_sol, Domains, subdomains, boundaries_sol, kappa_r, Field_calc_param, kappa_i=False): set_log_active(False) #turns off debugging info if MPI.comm_world.rank == 1: print("_________________________") parameters['linear_algebra_backend'] = 'PETSc' if Field_calc_param.EQS_mode == 'EQS': kappa = [kappa_r, kappa_i] else: kappa = [kappa_r] if Field_calc_param.anisotropy == 1: Cond_tensor = get_cond_tensor( mesh_sol ) # unlike get_scaled_cond_tensor, this function does not scale tensor with conductivity (as it was already scaled) else: Cond_tensor = False #just to initialize from Math_module_hybrid import choose_solver_for_me if Field_calc_param.Solver_type == 'Default': Solver_type = choose_solver_for_me( Field_calc_param.EQS_mode, Domains.Float_contacts ) #choses solver basing on the Laplace formulation and whether the floating conductors are used else: Solver_type = Field_calc_param.Solver_type # just get the solver directly #In case of current-controlled stimulation, Dirichlet_bc or the whole potential distribution will be scaled afterwards (due to the system's linearity) from FEM_in_spectrum import get_solution_space_and_Dirichlet_BC V_space, Dirichlet_bc, ground_index, facets = get_solution_space_and_Dirichlet_BC( Field_calc_param.external_grounding, Field_calc_param.c_c, mesh_sol, subdomains, boundaries_sol, Field_calc_param.element_order, Field_calc_param.EQS_mode, Domains.Contacts, Domains.fi) #ground index refers to the ground in .med/.msh file #facets = MeshFunction('size_t',mesh_sol,2) #facets.set_all(0) if Field_calc_param.external_grounding == False: facets.array()[boundaries_sol.array() == Domains.Contacts[ground_index]] = 1 dsS = Measure("ds", domain=mesh_sol, subdomain_data=facets) Ground_surface_size = assemble(1.0 * dsS(1)) dx = Measure("dx", domain=mesh_sol) # to solve the Laplace equation div(kappa*grad(phi))=0 (variational form: a(u,v)=L(v)) start_math = tm.time() from FEM_in_spectrum import define_variational_form_and_solve phi_sol = define_variational_form_and_solve(V_space, Dirichlet_bc, kappa, Field_calc_param.EQS_mode, Cond_tensor, Solver_type) if MPI.comm_world.rank == 1: minutes = int((tm.time() - start_math) / 60) secnds = int(tm.time() - start_math) - minutes * 60 print("--- assembled and solved in ", minutes, " min ", secnds, " s ---") if Field_calc_param.EQS_mode == 'EQS': (phi_r_sol, phi_i_sol) = phi_sol.split(deepcopy=True) else: phi_r_sol = phi_sol phi_i_sol = Function(V_space) phi_i_sol.vector()[:] = 0.0 if MPI.comm_world.rank == 1: print("dofs on 2nd process: ", (max(V_space.dofmap().dofs()) + 1)) # get current flowing through the grounded contact and the electric field in the whole domain from FEM_in_spectrum import get_current J_ground, E_field, E_field_im = get_current(mesh_sol, facets, boundaries_sol, Field_calc_param.element_order, Field_calc_param.EQS_mode, Domains.Contacts, kappa, Cond_tensor, phi_r_sol, phi_i_sol, ground_index, get_E_field=True) # If EQS, J_ground is a complex number. If QS, E_field_im is a null function # to get current density function which is required for mesh refinement when checking current convergence from Math_module_hybrid import get_current_density j_dens_real, j_dens_im = get_current_density( mesh_sol, Field_calc_param.element_order, Field_calc_param.EQS_mode, kappa, Cond_tensor, E_field, E_field_im) # If QS, j_dens_im is null function # will be used for mesh refinement j_dens_real_unscaled = j_dens_real.copy(deepcopy=True) j_dens_im_unscaled = j_dens_im.copy(deepcopy=True) # null function if QS import copy J_real_unscaled = copy.deepcopy(np.real(J_ground)) J_im_unscaled = copy.deepcopy(np.imag(J_ground)) # 0 if QS # to project the E-field magnitude if Field_calc_param.element_order > 1: V_normE = FunctionSpace(mesh_sol, "CG", Field_calc_param.element_order - 1) else: V_normE = FunctionSpace(mesh_sol, "CG", Field_calc_param.element_order) if Field_calc_param.external_grounding == True and ( Field_calc_param.c_c == 1 or len(Domains.fi) == 1): V_max = max(Domains.fi[:], key=abs) V_min = 0.0 elif -1 * Domains.fi[0] == Domains.fi[ 1]: # V_across is needed only for 2 active contact systems V_min = -1 * abs(Domains.fi[0]) V_max = abs(Domains.fi[0]) else: V_min = min(Domains.fi[:], key=abs) V_max = max(Domains.fi[:], key=abs) V_across = V_max - V_min # this can be negative ### Do not probe anything when inside MPI process! # Vertices_get=read_csv('Neuron_model_arrays/Vert_of_Neural_model_NEURON.csv', delimiter=' ', header=None) # Vertices_array=Vertices_get.values # Phi_ROI=np.zeros((Vertices_array.shape[0],4),float) # # for inx in range(Vertices_array.shape[0]): # pnt=Point(Vertices_array[inx,0],Vertices_array[inx,1],Vertices_array[inx,2]) # # Phi_ROI[inx,0]=Vertices_array[inx,0] # Phi_ROI[inx,1]=Vertices_array[inx,1] # Phi_ROI[inx,2]=Vertices_array[inx,2] # if Field_calc_param.c_c==1: # phi_r_sol_scaled_on_point=V_across*np.real((phi_r_sol(pnt)+1j*phi_i_sol(pnt))/(J_real+1j*J_im)) # phi_i_sol_scaled_on_point=V_across*np.imag((phi_r_sol(pnt)+1j*phi_i_sol(pnt))/(J_real+1j*J_im)) # Phi_ROI[inx,3]=np.sqrt(phi_r_sol_scaled_on_point*phi_r_sol_scaled_on_point+phi_i_sol_scaled_on_point*phi_i_sol_scaled_on_point) # else: # Phi_ROI[inx,3]=np.sqrt(phi_r_sol(pnt)*phi_r_sol(pnt)+phi_i_sol(pnt)*phi_i_sol(pnt)) # # np.savetxt('Results_adaptive/Phi_'+str(Field_calc_param.frequenc)+'.csv', Phi_ROI, delimiter=" ") #save the results if Field_calc_param.c_c != 1 and Field_calc_param.CPE != 1: #Just to save total currunt through the ground in FEniCS hdf5 J_Vector = Vector(MPI.comm_self, 2) J_Vector.set_local( np.array([J_real_unscaled, J_im_unscaled], dtype=np.float64)) Hdf = HDF5File( mesh_sol.mpi_comm(), "/opt/Patient/Results_adaptive/Solution_" + str(np.round(Field_calc_param.frequenc, 6)) + ".h5", "w") Hdf.write(mesh_sol, "mesh_sol") Hdf.write(phi_sol, "solution_phi_full") Hdf.write(E_field, "solution_E_field") Hdf.write(E_field_im, "solution_E_field_im") Hdf.write(j_dens_real_unscaled, "solution_j_real") Hdf.write(j_dens_im_unscaled, "solution_j_im") Hdf.write(J_Vector, "/J_Vector") Hdf.close() return True ### Do not probe anything when inside MPI process! # #Probe_of_potential # probe_z=np.zeros((100,4),float) # for inx in range(100): # pnt=Point(75.5,78.5,27.865+inx/10.0) # probe_z[inx,0]=75.5 # probe_z[inx,1]=78.5 # probe_z[inx,2]=27.865+inx/10.0 # if Field_calc_param.c_c==1: # phi_r_sol_scaled_on_point=V_across*np.real((phi_r_sol(pnt)+1j*phi_i_sol(pnt))/(J_real+1j*J_im)) # phi_i_sol_scaled_on_point=V_across*np.imag((phi_r_sol(pnt)+1j*phi_i_sol(pnt))/(J_real+1j*J_im)) # probe_z[inx,3]=np.sqrt(phi_r_sol_scaled_on_point*phi_r_sol_scaled_on_point+phi_i_sol_scaled_on_point*phi_i_sol_scaled_on_point) # else: # probe_z[inx,3]=np.sqrt(phi_r_sol(pnt)*phi_r_sol(pnt)+phi_i_sol(pnt)*phi_i_sol(pnt)) # np.savetxt('Results_adaptive/Phi_Zprobe'+str(Field_calc_param.frequenc)+'.csv', probe_z, delimiter=" ") #print("Tissue impedance: ", Z_tis) #=============================================================================# if Field_calc_param.c_c == 1 or Field_calc_param.CPE == 1: Z_tissue = V_across / J_ground # Tissue impedance if MPI.comm_world.rank == 1: print("Tissue impedance: ", Z_tissue) if Field_calc_param.CPE == 1: if len(Domains.fi) > 2: print( "Currently, CPE can be used only for simulations with two contacts. Please, assign the rest to 'None'" ) raise SystemExit from GUI_inp_dict import d as d_cpe CPE_param = [ d_cpe["K_A"], d_cpe["beta"], d_cpe["K_A_ground"], d_cpe["beta_ground"] ] from FEM_in_spectrum import get_CPE_corrected_Dirichlet_BC #-1.0 to avoid printing Dirichlet_bc_with_CPE, total_impedance = get_CPE_corrected_Dirichlet_BC( Field_calc_param.external_grounding, facets, boundaries_sol, CPE_param, Field_calc_param.EQS_mode, Field_calc_param.frequenc, -1.0, Domains.Contacts, Domains.fi, V_across, Z_tissue, V_space) if MPI.comm_world.rank == 1: print( "Solving for an adjusted potential on contacts to account for CPE" ) start_math = tm.time() # to solve the Laplace equation for the adjusted Dirichlet phi_sol_CPE = define_variational_form_and_solve( V_space, Dirichlet_bc_with_CPE, kappa, Field_calc_param.EQS_mode, Cond_tensor, Solver_type) if MPI.comm_world.rank == 1: minutes = int((tm.time() - start_math) / 60) secnds = int(tm.time() - start_math) - minutes * 60 print("--- assembled and solved in ", minutes, " min ", secnds, " s ") if Field_calc_param.EQS_mode == 'EQS': (phi_r_CPE, phi_i_CPE) = phi_sol_CPE.split(deepcopy=True) else: phi_r_CPE = phi_sol_CPE phi_i_CPE = Function(V_space) phi_i_CPE.vector()[:] = 0.0 # get current flowing through the grounded contact and the electric field in the whole domain J_ground_CPE, E_field_CPE, E_field_im_CPE = get_current( mesh_sol, facets, boundaries_sol, Field_calc_param.element_order, Field_calc_param.EQS_mode, Domains.Contacts, kappa, Cond_tensor, phi_r_CPE, phi_i_CPE, ground_index, get_E_field=True) # If EQS, J_ground is a complex number. If QS, E_field_CPE is a null function # to get current density function which is required for mesh refinement when checking current convergence j_dens_real_CPE, j_dens_im_CPE = get_current_density( mesh_sol, Field_calc_param.element_order, Field_calc_param.EQS_mode, kappa, Cond_tensor, E_field_CPE, E_field_im_CPE) # If QS, j_dens_im is null function # will be used for mesh refinement j_dens_real_unscaled = j_dens_real_CPE.copy(deepcopy=True) j_dens_im_unscaled = j_dens_im_CPE.copy(deepcopy=True) J_real_unscaled = copy.deepcopy(np.real(J_ground)) J_im_unscaled = copy.deepcopy(np.imag(J_ground)) #Just to save total currunt through the ground in FEniCS hdf5 J_Vector = Vector(MPI.comm_self, 2) J_Vector.set_local( np.array([J_real_unscaled, J_im_unscaled], dtype=np.float64)) Hdf = HDF5File( mesh_sol.mpi_comm(), "/opt/Patient/Results_adaptive/Solution_" + str(np.round(Field_calc_param.frequenc, 6)) + ".h5", "w") Hdf.write(mesh_sol, "mesh_sol") Hdf.write(phi_sol_CPE, "solution_phi_full") Hdf.write(E_field_CPE, "solution_E_field") Hdf.write(E_field_im_CPE, "solution_E_field_im") Hdf.write(j_dens_real_unscaled, "solution_j_real") Hdf.write(j_dens_im_unscaled, "solution_j_im") Hdf.write(J_Vector, "/J_Vector") Hdf.close() return True if Field_calc_param.c_c == 1: if Field_calc_param.EQS_mode == 'EQS': # For EQS, we need to scale the potential on boundaries (because the error is absolute) and recompute field, etc. Maybe we can scale them also directly? Dirichlet_bc_scaled = [] for bc_i in range( len(Domains.Contacts) ): #CPE estimation is valid only for one activa and one ground contact configuration if Field_calc_param.EQS_mode == 'EQS': if Domains.fi[bc_i] != 0.0: Active_with_CC = V_across * V_across / J_ground #(impedance * current through the contact (V_across coincides with the assigned current magnitude)) Dirichlet_bc_scaled.append( DirichletBC(V_space.sub(0), np.real(Active_with_CC), boundaries_sol, Domains.Contacts[bc_i])) Dirichlet_bc_scaled.append( DirichletBC(V_space.sub(1), np.imag(Active_with_CC), boundaries_sol, Domains.Contacts[bc_i])) else: Dirichlet_bc_scaled.append( DirichletBC(V_space.sub(0), Constant(0.0), boundaries_sol, Domains.Contacts[bc_i])) Dirichlet_bc_scaled.append( DirichletBC(V_space.sub(1), Constant(0.0), boundaries_sol, Domains.Contacts[bc_i])) if Field_calc_param.external_grounding == True: if Sim_setup.Laplace_eq == 'EQS': Dirichlet_bc_scaled.append( DirichletBC(V_space.sub(0), 0.0, facets, 1)) Dirichlet_bc_scaled.append( DirichletBC(V_space.sub(1), 0.0, facets, 1)) else: Dirichlet_bc_scaled.append( DirichletBC(V_space, 0.0, facets, 1)) print( "Solving for a scaled potential on contacts (to match the desired current)" ) start_math = tm.time() # to solve the Laplace equation for the adjusted Dirichlet phi_sol_scaled = define_variational_form_and_solve( V_space, Dirichlet_bc_scaled, kappa, Field_calc_param.EQS_mode, Cond_tensor, Solver_type) minutes = int((tm.time() - start_math) / 60) secnds = int(tm.time() - start_math) - minutes * 60 print("--- assembled and solved in ", minutes, " min ", secnds, " s ---") (phi_r_sol_scaled, phi_i_sol_scaled) = phi_sol_scaled.split(deepcopy=True) # get current flowing through the grounded contact and the electric field in the whole domain J_ground_scaled, E_field_scaled, E_field_im_scaled = get_current( mesh_sol, facets, boundaries_sol, Field_calc_param.element_order, Field_calc_param.EQS_mode, Domains.Contacts, kappa, Cond_tensor, phi_r_CPE, phi_i_CPE, ground_index, get_E_field=True) # If EQS, J_ground is a complex number. If QS, E_field_im is 0 else: # here we can simply scale the potential in the domain and recompute the E-field phi_r_sol_scaled = Function(V_space) phi_i_sol_scaled = Function(V_space) phi_i_sol_scaled.vector()[:] = 0.0 phi_r_sol_scaled.vector( )[:] = V_across * phi_r_sol.vector()[:] / J_ground phi_sol_scaled = phi_r_sol_scaled J_ground_scaled, E_field_scaled, E_field_im_scaled = get_current( mesh_sol, facets, boundaries_sol, Field_calc_param.element_order, Field_calc_param.EQS_mode, Domains.Contacts, kappa, Cond_tensor, phi_r_sol_scaled, phi_i_sol_scaled, ground_index, get_E_field=True) #E_field_im_scale is a null function #Just to save total currunt through the ground in FEniCS hdf5 (we save unscaled currents!) J_Vector = Vector(MPI.comm_self, 2) J_Vector.set_local( np.array([J_real_unscaled, J_im_unscaled], dtype=np.float64)) Hdf = HDF5File( mesh_sol.mpi_comm(), "/opt/Patient/Results_adaptive/Solution_" + str(np.round(Field_calc_param.frequenc, 6)) + ".h5", "w") Hdf.write(mesh_sol, "mesh_sol") Hdf.write(phi_sol_scaled, "solution_phi_full") Hdf.write(E_field_scaled, "solution_E_field") Hdf.write(E_field_im_scaled, "solution_E_field_im") Hdf.write(j_dens_real_unscaled, "solution_j_real") Hdf.write(j_dens_im_unscaled, "solution_j_im") Hdf.write(J_Vector, "/J_Vector") Hdf.close() return True return True
def solve_Laplace_multicontact(Sim_setup, Solver_type, Vertices_array, Domains, core, VTA_IFFT, output): Sim_setup.external_grounding = True set_log_active(False) #turns off debugging info tol = 1e-14 #tolerance Sim_setup.mesh.coordinates() Sim_setup.mesh.init() #get scaled potentials on the contacts that provide the desired currents through the contacts Phi_scaled = scale_bc_potentials_with_superposition( Sim_setup, Domains, Solver_type) #Dirichlet_bc was scaled to match the desired current (using system's linearity). from FEM_in_spectrum import get_solution_space_and_Dirichlet_BC V_space, facets = get_solution_space_and_Dirichlet_BC( Sim_setup.external_grounding, Sim_setup.c_c, Sim_setup.mesh, Sim_setup.subdomains, Sim_setup.boundaries, Sim_setup.element_order, Sim_setup.Laplace_eq, Domains.Contacts, Phi_scaled, only_space=True) Dirichlet_bc_scaled = [] for bc_i in range(len(Domains.Contacts)): if Sim_setup.Laplace_eq == 'EQS': Dirichlet_bc_scaled.append( DirichletBC(V_space.sub(0), np.real(Phi_scaled[bc_i]), Sim_setup.boundaries, Domains.Contacts[bc_i])) Dirichlet_bc_scaled.append( DirichletBC(V_space.sub(1), np.imag(Phi_scaled[bc_i]), Sim_setup.boundaries, Domains.Contacts[bc_i])) else: Dirichlet_bc_scaled.append( DirichletBC(V_space, Phi_scaled[bc_i], Sim_setup.boundaries, Domains.Contacts[bc_i])) if Sim_setup.external_grounding == True: if Sim_setup.Laplace_eq == 'EQS': Dirichlet_bc_scaled.append( DirichletBC(V_space.sub(0), 0.0, facets, 1)) Dirichlet_bc_scaled.append( DirichletBC(V_space.sub(1), 0.0, facets, 1)) else: Dirichlet_bc_scaled.append(DirichletBC(V_space, 0.0, facets, 1)) # to get conductivity (and permittivity if EQS formulation) mapped accrodingly to the subdomains. k_val_r is just a list of conductivities (S/mm!) in a specific order to scale the cond. tensor from FEM_in_spectrum import get_dielectric_properties_from_subdomains kappa, k_val_r = get_dielectric_properties_from_subdomains( Sim_setup.mesh, Sim_setup.subdomains, Sim_setup.Laplace_eq, Domains.Float_contacts, Sim_setup.conductivities, Sim_setup.rel_permittivities, Sim_setup.sine_freq) if int(Sim_setup.sine_freq) == int(Sim_setup.signal_freq): file = File('/opt/Patient/Field_solutions/Conductivity_map_' + str(Sim_setup.signal_freq) + 'Hz.pvd') file << kappa[0] if Sim_setup.Laplace_eq == 'EQS': file = File('/opt/Patient/Field_solutions/Permittivity_map_' + str(Sim_setup.signal_freq) + 'Hz.pvd') file << kappa[1] # to get tensor scaled by the conductivity map if Sim_setup.anisotropy == 1: from FEM_in_spectrum import get_scaled_cond_tensor Cond_tensor = get_scaled_cond_tensor( Sim_setup.mesh, Sim_setup.subdomains, Sim_setup.sine_freq, Sim_setup.signal_freq, Sim_setup.unscaled_tensor, k_val_r) else: Cond_tensor = False #just to initialize # to solve the Laplace equation div(kappa*grad(phi))=0 (variational form: a(u,v)=L(v)) from FEM_in_spectrum import define_variational_form_and_solve phi_sol = define_variational_form_and_solve(V_space, Dirichlet_bc_scaled, kappa, Sim_setup.Laplace_eq, Cond_tensor, Solver_type) if Sim_setup.Laplace_eq == 'EQS': (phi_r, phi_i) = phi_sol.split(deepcopy=True) else: phi_r = phi_sol phi_i = Function(V_space) phi_i.vector()[:] = 0.0 # the system was solved for already scaled potential, but to check the scaling accuracy, we will assess the currents through the contacts if int(Sim_setup.sine_freq) == int(Sim_setup.signal_freq): file = File('/opt/Patient/Field_solutions/Phi_real_scaled_' + str(Sim_setup.signal_freq) + 'Hz.pvd') file << phi_r, Sim_setup.mesh print("DoFs on the mesh for " + Sim_setup.Laplace_eq + " : ", (max(V_space.dofmap().dofs()) + 1)) # to get function space and manual projections for the E-field E_field, E_field_im = get_E_field(Sim_setup.mesh, Sim_setup.element_order, Sim_setup.Laplace_eq, phi_r, phi_i) #if QS, E_field_im is 0 # to get current on the active contacts (inlcuding the ground) J_currents_real, J_currents_imag = get_current_on_multiple_contacts( Sim_setup.external_grounding, facets, Sim_setup.mesh, Sim_setup.boundaries, Sim_setup.Laplace_eq, Domains.Contacts, Phi_scaled, E_field, E_field_im, kappa, Cond_tensor) # J_currents_imag is a zero array if 'QS' mode print( "Complex currents on contacts at the base frequency (signal repetition rate): ", J_currents_real, J_currents_imag) file = File('/opt/Patient/Field_solutions/E_real_scaled_' + str(Sim_setup.sine_freq) + 'Hz.pvd') file << E_field, Sim_setup.mesh #if Full_IFFT==1: # Hdf=HDF5File(Sim_setup.mesh.mpi_comm(), "/opt/Patient/Field_solutions_functions/solution"+str(np.round(Sim_setup.sine_freq,6))+".h5", "w") # Hdf.write(Sim_setup.mesh, "mesh") # Hdf.write(phi_sol, "solution_full") # Hdf.close() if VTA_IFFT == 1: Sim_type = 'Astrom' # fixed for now E_field_real, E_field_im = get_E_field(Sim_setup.mesh, Sim_setup.element_order, Sim_setup.Laplace_eq, phi_r, phi_i) if Sim_type == 'Astrom': W_amp = FunctionSpace(Sim_setup.mesh, 'DG', Sim_setup.element_order - 1) w_amp = TestFunction(W_amp) Pv_amp = TrialFunction(W_amp) E_amp_real = Function(W_amp) a_local = inner(w_amp, Pv_amp) * dx L_local = inner(w_amp, sqrt(dot(E_field_r, E_field_r))) * dx A_local, b_local = assemble_system(a_local, L_local, bcs=[]) local_solver = PETScKrylovSolver('bicgstab') local_solver.solve(A_local, E_amp_real.vector(), b_local) #E_amp_real.vector()[:]=E_amp_real.vector() E_amp_imag = Function(W_amp) a_local = inner(w_amp, Pv_amp) * dx L_local = inner(w_amp, sqrt(dot(E_field_im, E_field_im))) * dx A_local, b_local = assemble_system(a_local, L_local, bcs=[]) local_solver = PETScKrylovSolver('bicgstab') local_solver.solve(A_local, E_amp_imag.vector(), b_local) Phi_ROI = np.zeros((Vertices_array.shape[0], 5), float) #VTA=0.0 for inx in range(Vertices_array.shape[0]): pnt = Point(Vertices_array[inx, 0], Vertices_array[inx, 1], Vertices_array[inx, 2]) if Sim_setup.mesh.bounding_box_tree( ).compute_first_entity_collision( pnt) < Sim_setup.mesh.num_cells() * 100: Phi_ROI[inx, 0] = Vertices_array[inx, 0] Phi_ROI[inx, 1] = Vertices_array[inx, 1] Phi_ROI[inx, 2] = Vertices_array[inx, 2] #if Sim_setup.c_c==1: if Sim_type == 'Butson': Phi_ROI[inx, 3] = Second_deriv(pnt) # already scaled Phi_ROI[inx, 4] = Second_deriv_imag(pnt) # already scaled elif Sim_type == 'Astrom': Phi_ROI[inx, 3] = E_amp_real(pnt) # already scaled Phi_ROI[inx, 4] = E_amp_imag(pnt) # already scaled #if Sim_setup.sine_freq==Sim_setup.signal_freq and abs(Phi_ROI[inx,3])>=0.3: # VTA+=0.1**3 else: # we assign 0.0 here Phi_ROI[inx, 3] = 0.0 Phi_ROI[inx, 4] = 0.0 #print("Couldn't probe the potential at the point ",Vertices_array[inx,0],Vertices_array[inx,1],Vertices_array[inx,2]) #print("check the neuron array, exiting....") #raise SystemExit fre_vector = [Sim_setup.sine_freq] * Phi_ROI.shape[0] comb = np.vstack((Phi_ROI[:, 0], Phi_ROI[:, 1], Phi_ROI[:, 2], Phi_ROI[:, 3], Phi_ROI[:, 4], fre_vector)).T f = h5py.File( '/opt/Patient/Field_solutions/sol_cor' + str(core) + '.h5', 'a') f.create_dataset(str(Sim_setup.sine_freq), data=comb) f.close() else: Phi_ROI = np.zeros((Vertices_array.shape[0], 5), float) for inx in range(Vertices_array.shape[0]): pnt = Point(Vertices_array[inx, 0], Vertices_array[inx, 1], Vertices_array[inx, 2]) if Sim_setup.mesh.bounding_box_tree( ).compute_first_entity_collision( pnt) < Sim_setup.mesh.num_cells() * 100: Phi_ROI[inx, 0] = Vertices_array[inx, 0] Phi_ROI[inx, 1] = Vertices_array[inx, 1] Phi_ROI[inx, 2] = Vertices_array[inx, 2] Phi_ROI[inx, 3] = phi_r(pnt) Phi_ROI[inx, 4] = phi_i(pnt) else: print("Couldn't probe the potential at the point ", Vertices_array[inx, 0], Vertices_array[inx, 1], Vertices_array[inx, 2]) print("check the neuron array, exitting....") raise SystemExit fre_vector = [Sim_setup.sine_freq] * Phi_ROI.shape[0] ### freq_vector=[frequenc]*coordinates.shape[0] ##### com=np.vstack((coordinat[0:10,0],coordinat[0:10,1],coordinat[0:10,2],real_par[0:10],image_par[0:10],fre_vector[0:10])).T comb = np.vstack((Phi_ROI[:, 0], Phi_ROI[:, 1], Phi_ROI[:, 2], Phi_ROI[:, 3], Phi_ROI[:, 4], fre_vector)).T f = h5py.File( '/opt/Patient/Field_solutions/sol_cor' + str(core) + '.h5', 'a') f.create_dataset(str(Sim_setup.sine_freq), data=comb) f.close() output.put(1)
def get_field_with_floats(Sim_setup, active_index, Domains, Solver_type): set_log_active(False) #turns off debugging info parameters['linear_algebra_backend'] = 'PETSc' # to get conductivity (and permittivity if EQS formulation) mapped accrodingly to the subdomains. k_val_r is just a list of conductivities (S/mm!) in a specific order to scale the cond. tensor from FEM_in_spectrum import get_dielectric_properties_from_subdomains kappa, k_val_r = get_dielectric_properties_from_subdomains( Sim_setup.mesh, Sim_setup.subdomains, Sim_setup.Laplace_eq, Domains.Float_contacts, Sim_setup.conductivities, Sim_setup.rel_permittivities, Sim_setup.sine_freq) # to get tensor scaled by the conductivity map if Sim_setup.anisotropy == 1: from FEM_in_spectrum import get_scaled_cond_tensor Cond_tensor = get_scaled_cond_tensor( Sim_setup.mesh, Sim_setup.subdomains, Sim_setup.sine_freq, Sim_setup.signal_freq, Sim_setup.unscaled_tensor, k_val_r) else: Cond_tensor = False #just to initialize from FEM_in_spectrum import get_solution_space_and_Dirichlet_BC V_space, facets = get_solution_space_and_Dirichlet_BC( Sim_setup.external_grounding, 1, Sim_setup.mesh, Sim_setup.subdomains, Sim_setup.boundaries, Sim_setup.element_order, Sim_setup.Laplace_eq, Domains.Contacts, Domains.fi, only_space=True) facets_active = MeshFunction('size_t', Sim_setup.mesh, 2) facets_active.set_all(0) # here we have a custom way to assign Dirichlet BC dirichlet_bc = [] float_surface = 2 active_floats = 0 # assign only the chosen active contact and the ground, other should be just marked on the mesh (starting from 2) for bc_i in range(len(Domains.Contacts)): if bc_i == active_index or Domains.fi[bc_i] == 0.0: if Sim_setup.Laplace_eq == 'EQS': dirichlet_bc.append( DirichletBC(V_space.sub(0), Domains.fi[bc_i], Sim_setup.boundaries, Domains.Contacts[bc_i])) dirichlet_bc.append( DirichletBC(V_space.sub(1), Constant(0.0), Sim_setup.boundaries, Domains.Contacts[bc_i])) else: dirichlet_bc.append( DirichletBC(V_space, Domains.fi[bc_i], Sim_setup.boundaries, Domains.Contacts[bc_i])) if bc_i == active_index: facets_active.array()[Sim_setup.boundaries.array() == Domains.Contacts[bc_i]] = 1 else: facets_active.array()[Sim_setup.boundaries.array( ) == Domains.Contacts[ bc_i]] = float_surface #it will not be assigned to always floating contacts float_surface += 1 active_floats += 1 if Sim_setup.external_grounding == True: if Sim_setup.Laplace_eq == 'EQS': dirichlet_bc.append(DirichletBC(V_space.sub(0), 0.0, facets, 1)) dirichlet_bc.append(DirichletBC(V_space.sub(1), 0.0, facets, 1)) else: dirichlet_bc.append(DirichletBC(V_space, 0.0, facets, 1)) #definitions for integrators dx = Measure("dx", domain=Sim_setup.mesh) dsS = Measure("ds", domain=Sim_setup.mesh, subdomain_data=facets_active) dsS_int = Measure("dS", domain=Sim_setup.mesh, subdomain_data=facets_active) # to solve the Laplace equation div(kappa*grad(phi))=0 (variational form: a(u,v)=L(v)) from FEM_in_spectrum import define_variational_form_and_solve if Sim_setup.Laplace_eq == 'EQS': phi_sol = define_variational_form_and_solve( V_space, dirichlet_bc, kappa, Sim_setup.Laplace_eq, Cond_tensor, 'MUMPS') #fixed to MUMPS here else: phi_sol = define_variational_form_and_solve(V_space, dirichlet_bc, kappa, Sim_setup.Laplace_eq, Cond_tensor, Solver_type) if Sim_setup.Laplace_eq == 'EQS': (phi_r, phi_i) = phi_sol.split(deepcopy=True) else: phi_r = phi_sol phi_i = Function(V_space) phi_i.vector()[:] = 0.0 Float_potentials_real = np.zeros(active_floats, float) Float_potentials_imag = np.zeros(active_floats, float) float_surface = 2 #float indicies start from 2 # assess the floating potential by integrating over the contact's surface for fl_in in range(active_floats): Float_surface_size = assemble(1.0 * dsS_int(2)) Float_potentials_real[float_surface - 2] = assemble( phi_r * dsS_int(float_surface)) / Float_surface_size if Sim_setup.Laplace_eq == 'EQS': Float_potentials_imag[float_surface - 2] = assemble( phi_i * dsS_int(float_surface)) / Float_surface_size float_surface += 1 # to get manual projections for the E-field E_field, E_field_im = get_E_field(Sim_setup.mesh, Sim_setup.element_order, Sim_setup.Laplace_eq, phi_r, phi_i) #if QS, E_field_im is a null function n = FacetNormal(Sim_setup.mesh) if Sim_setup.Laplace_eq == 'EQS': if Cond_tensor != False: j_dens_real_contact = dot( Cond_tensor * E_field, -1 * n)('-') * dsS_int(1) - dot( kappa[1] * E_field_im, -1 * n)('-') * dsS_int(1) j_dens_im_contact = dot( Cond_tensor * E_field_im, -1 * n)('-') * dsS_int(1) + dot( kappa[1] * E_field, -1 * n)('-') * dsS_int(1) else: j_dens_real_contact = dot( kappa[0] * E_field, -1 * n)('-') * dsS_int(1) - dot( kappa[1] * E_field_im, -1 * n)('-') * dsS_int(1) j_dens_im_contact = dot( kappa[0] * E_field_im, -1 * n)('-') * dsS_int(1) + dot( kappa[1] * E_field, -1 * n)('-') * dsS_int(1) J_real = assemble(j_dens_real_contact) J_im = assemble(j_dens_im_contact) return Float_potentials_real, Float_potentials_imag, J_real, J_im else: if Cond_tensor != False: j_dens_real_contact = dot(Cond_tensor * E_field, -1 * n)('-') * dsS_int(1) else: j_dens_real_contact = dot(kappa[0] * E_field, -1 * n)('-') * dsS_int(1) J_real = assemble(j_dens_real_contact) return Float_potentials_real, 0, J_real, 0
def get_field(mesh_sol, Domains, subdomains, boundaries_sol, Field_calc_param): set_log_active(False) #turns off debugging info print("_________________________") parameters['linear_algebra_backend'] = 'PETSc' [cond_GM, perm_GM] = DielectricProperties(3).get_dielectrics( Field_calc_param.frequenc ) #3 for grey matter and so on (numeration as in voxel_data) [cond_WM, perm_WM ] = DielectricProperties(2).get_dielectrics(Field_calc_param.frequenc) [cond_CSF, perm_CSF ] = DielectricProperties(1).get_dielectrics(Field_calc_param.frequenc) [cond_default, perm_default] = DielectricProperties( Field_calc_param.default_material).get_dielectrics( Field_calc_param.frequenc) from GUI_inp_dict import d as d_encap [cond_encap, perm_encap ] = DielectricProperties(d_encap['encap_tissue_type']).get_dielectrics( Field_calc_param.frequenc) cond_encap = cond_encap * d_encap['encap_scaling_cond'] perm_encap = perm_encap * d_encap['encap_scaling_perm'] if Field_calc_param.Solver_type == 'Default': Solver_type = choose_solver_for_me( Field_calc_param.EQS_mode, Domains.Float_contacts ) #choses solver basing on the Laplace formulation and whether the floating conductors are used else: Solver_type = Field_calc_param.Solver_type # just get the solver directly conductivities = [cond_default, cond_GM, cond_WM, cond_CSF, cond_encap] rel_permittivities = [perm_default, perm_GM, perm_WM, perm_CSF, perm_encap] # to get conductivity (and permittivity if EQS formulation) mapped accrodingly to the subdomains. k_val_r is just a list of conductivities (S/mm!) in a specific order to scale the cond. tensor from FEM_in_spectrum import get_dielectric_properties_from_subdomains kappa, k_val_r = get_dielectric_properties_from_subdomains( mesh_sol, subdomains, Field_calc_param.EQS_mode, Domains.Float_contacts, conductivities, rel_permittivities, Field_calc_param.frequenc) file = File('/opt/Patient/Results_adaptive/Last_subdomains_map.pvd') file << subdomains file = File('/opt/Patient/Results_adaptive/Last_conductivity_map.pvd') file << kappa[0] if Field_calc_param.EQS_mode == 'EQS': file = File('/opt/Patient/Results_adaptive/Last_permittivity_map.pvd') file << kappa[1] if Field_calc_param.anisotropy == 1: # order xx,xy,xz,yy,yz,zz c00 = MeshFunction("double", mesh_sol, 3, 0.0) c01 = MeshFunction("double", mesh_sol, 3, 0.0) c02 = MeshFunction("double", mesh_sol, 3, 0.0) c11 = MeshFunction("double", mesh_sol, 3, 0.0) c12 = MeshFunction("double", mesh_sol, 3, 0.0) c22 = MeshFunction("double", mesh_sol, 3, 0.0) # load the diffusion tensor (should be normalized beforehand) hdf = HDF5File( mesh_sol.mpi_comm(), "/opt/Patient/Results_adaptive/Tensors_to_solve_num_el_" + str(mesh_sol.num_cells()) + ".h5", "r") hdf.read(c00, "/c00") hdf.read(c01, "/c01") hdf.read(c02, "/c02") hdf.read(c11, "/c11") hdf.read(c12, "/c12") hdf.read(c22, "/c22") hdf.close() unscaled_tensor = [c00, c01, c02, c11, c12, c22] # to get tensor scaled by the conductivity map (twice send Field_calc_param.frequenc to always get unscaled ellipsoid tensor for visualization) from FEM_in_spectrum import get_scaled_cond_tensor Cond_tensor = get_scaled_cond_tensor(mesh_sol, subdomains, Field_calc_param.frequenc, Field_calc_param.frequenc, unscaled_tensor, k_val_r, plot_tensors=True) else: Cond_tensor = False #just to initialize #In case of current-controlled stimulation, Dirichlet_bc or the whole potential distribution will be scaled afterwards (due to the system's linearity) from FEM_in_spectrum import get_solution_space_and_Dirichlet_BC V_space, Dirichlet_bc, ground_index, facets = get_solution_space_and_Dirichlet_BC( Field_calc_param.external_grounding, Field_calc_param.c_c, mesh_sol, subdomains, boundaries_sol, Field_calc_param.element_order, Field_calc_param.EQS_mode, Domains.Contacts, Domains.fi) #ground index refers to the ground in .med/.msh file print("dofs: ", (max(V_space.dofmap().dofs()) + 1)) print("N of elements: ", mesh_sol.num_cells()) #facets = MeshFunction('size_t',mesh_sol,2) #facets.set_all(0) if Field_calc_param.external_grounding == False: # otherwise we have it already from get_solution_space_and_Dirichlet_BC() facets.array()[boundaries_sol.array() == Domains.Contacts[ground_index]] = 1 dsS = Measure("ds", domain=mesh_sol, subdomain_data=facets) Ground_surface_size = assemble(1.0 * dsS(1)) dx = Measure("dx", domain=mesh_sol) # to solve the Laplace equation div(kappa*grad(phi))=0 (variational form: a(u,v)=L(v)) start_math = tm.time() from FEM_in_spectrum import define_variational_form_and_solve phi_sol = define_variational_form_and_solve(V_space, Dirichlet_bc, kappa, Field_calc_param.EQS_mode, Cond_tensor, Solver_type) minutes = int((tm.time() - start_math) / 60) secnds = int(tm.time() - start_math) - minutes * 60 print("--- assembled and solved in ", minutes, " min ", secnds, " s ---") if Field_calc_param.EQS_mode == 'EQS': (phi_r_sol, phi_i_sol) = phi_sol.split(deepcopy=True) else: phi_r_sol = phi_sol phi_i_sol = Function(V_space) phi_i_sol.vector()[:] = 0.0 # get current flowing through the grounded contact and the electric field in the whole domain from FEM_in_spectrum import get_current J_ground, E_field, E_field_im = get_current(mesh_sol, facets, boundaries_sol, Field_calc_param.element_order, Field_calc_param.EQS_mode, Domains.Contacts, kappa, Cond_tensor, phi_r_sol, phi_i_sol, ground_index, get_E_field=True) #print("J_ground_unscaled: ",J_ground) # If EQS, J_ground is a complex number. If QS, E_field_im is a null function # to get current density function which is required for mesh refinement when checking current convergence j_dens_real, j_dens_im = get_current_density( mesh_sol, Field_calc_param.element_order, Field_calc_param.EQS_mode, kappa, Cond_tensor, E_field, E_field_im) # If QS, j_dens_im is null function # will be used for mesh refinement j_dens_real_unscaled = j_dens_real.copy(deepcopy=True) j_dens_im_unscaled = j_dens_im.copy(deepcopy=True) # null function if QS import copy J_real_unscaled = copy.deepcopy(np.real(J_ground)) J_im_unscaled = copy.deepcopy(np.imag(J_ground)) # 0 if QS # to project the E-field magnitude if Field_calc_param.element_order > 1: V_normE = FunctionSpace(mesh_sol, "CG", Field_calc_param.element_order - 1) else: V_normE = FunctionSpace(mesh_sol, "CG", Field_calc_param.element_order) #V_across=max(Domains.fi[:], key=abs) #actually, not across, but against ground!!! if Field_calc_param.external_grounding == True and ( Field_calc_param.c_c == 1 or len(Domains.fi) == 1): V_max = max(Domains.fi[:], key=abs) V_min = 0.0 elif -1 * Domains.fi[0] == Domains.fi[ 1]: # V_across is needed only for 2 active contact systems V_min = -1 * abs(Domains.fi[0]) V_max = abs(Domains.fi[0]) else: V_min = min(Domains.fi[:], key=abs) V_max = max(Domains.fi[:], key=abs) V_across = V_max - V_min # this can be negative Vertices_get = read_csv( '/opt/Patient/Neuron_model_arrays/Vert_of_Neural_model_NEURON.csv', delimiter=' ', header=None) Vertices_array = Vertices_get.values Phi_ROI = np.zeros((Vertices_array.shape[0], 4), float) for inx in range(Vertices_array.shape[0]): pnt = Point(Vertices_array[inx, 0], Vertices_array[inx, 1], Vertices_array[inx, 2]) Phi_ROI[inx, 0] = Vertices_array[inx, 0] Phi_ROI[inx, 1] = Vertices_array[inx, 1] Phi_ROI[inx, 2] = Vertices_array[inx, 2] if Field_calc_param.c_c == 1: phi_r_sol_scaled_on_point = V_across * np.real( (phi_r_sol(pnt) + 1j * phi_i_sol(pnt)) / J_ground) phi_i_sol_scaled_on_point = V_across * np.imag( (phi_r_sol(pnt) + 1j * phi_i_sol(pnt)) / J_ground) Phi_ROI[inx, 3] = np.sqrt( phi_r_sol_scaled_on_point * phi_r_sol_scaled_on_point + phi_i_sol_scaled_on_point * phi_i_sol_scaled_on_point) else: Phi_ROI[inx, 3] = np.sqrt( phi_r_sol(pnt) * phi_r_sol(pnt) + phi_i_sol(pnt) * phi_i_sol(pnt)) np.savetxt('/opt/Patient/Results_adaptive/Phi_' + str(Field_calc_param.frequenc) + '.csv', Phi_ROI, delimiter=" ") # this is amplitude, actually # #Probe_of_potential # probe_z=np.zeros((100,4),float) # for inx in range(100): # pnt=Point(75.5,78.5,27.865+inx/10.0) # probe_z[inx,0]=75.5 # probe_z[inx,1]=78.5 # probe_z[inx,2]=27.865+inx/10.0 # if Field_calc_param.c_c==1: # phi_r_sol_scaled_on_point=V_across*np.real((phi_r_sol(pnt)+1j*phi_i_sol(pnt))/(J_real_unscaled+1j*J_im_unscaled)) # phi_i_sol_scaled_on_point=V_across*np.imag((phi_r_sol(pnt)+1j*phi_i_sol(pnt))/(J_real_unscaled+1j*J_im_unscaled)) # probe_z[inx,3]=np.sqrt(phi_r_sol_scaled_on_point*phi_r_sol_scaled_on_point+phi_i_sol_scaled_on_point*phi_i_sol_scaled_on_point) # else: # probe_z[inx,3]=np.sqrt(phi_r_sol(pnt)*phi_r_sol(pnt)+phi_i_sol(pnt)*phi_i_sol(pnt)) # np.savetxt('Results_adaptive/Phi_Zprobe'+str(Field_calc_param.frequenc)+'.csv', probe_z, delimiter=" ") #print("Tissue impedance: ", Z_tis) #=============================================================================# if Field_calc_param.c_c == 1 or Field_calc_param.CPE == 1: Z_tissue = V_across / J_ground # Tissue impedance print("Tissue impedance: ", Z_tissue) if Field_calc_param.CPE == 1: if len(Domains.fi) > 2: print( "Currently, CPE can be used only for simulations with two contacts. Please, assign the rest to 'None'" ) raise SystemExit from GUI_inp_dict import d as d_cpe CPE_param = [ d_cpe["K_A"], d_cpe["beta"], d_cpe["K_A_ground"], d_cpe["beta_ground"] ] from FEM_in_spectrum import get_CPE_corrected_Dirichlet_BC Dirichlet_bc_with_CPE, total_impedance = get_CPE_corrected_Dirichlet_BC( Field_calc_param.external_grounding, facets, boundaries_sol, CPE_param, Field_calc_param.EQS_mode, Field_calc_param.frequenc, Field_calc_param.frequenc, Domains.Contacts, Domains.fi, V_across, Z_tissue, V_space) print( "Solving for an adjusted potential on contacts to account for CPE" ) start_math = tm.time() # to solve the Laplace equation for the adjusted Dirichlet phi_sol_CPE = define_variational_form_and_solve( V_space, Dirichlet_bc_with_CPE, kappa, Field_calc_param.EQS_mode, Cond_tensor, Solver_type) minutes = int((tm.time() - start_math) / 60) secnds = int(tm.time() - start_math) - minutes * 60 print("--- assembled and solved in ", minutes, " min ", secnds, " s ") if Field_calc_param.EQS_mode == 'EQS': (phi_r_CPE, phi_i_CPE) = phi_sol_CPE.split(deepcopy=True) else: phi_r_CPE = phi_sol_CPE phi_i_CPE = Function(V_space) phi_i_CPE.vector()[:] = 0.0 # get current flowing through the grounded contact and the electric field in the whole domain J_ground_CPE, E_field_CPE, E_field_im_CPE = get_current( mesh_sol, facets, boundaries_sol, Field_calc_param.element_order, Field_calc_param.EQS_mode, Domains.Contacts, kappa, Cond_tensor, phi_r_CPE, phi_i_CPE, ground_index, get_E_field=True) # If EQS, J_ground is a complex number. If QS, E_field_CPE is a null function # to get current density function which is required for mesh refinement when checking current convergence j_dens_real_CPE, j_dens_im_CPE = get_current_density( mesh_sol, Field_calc_param.element_order, Field_calc_param.EQS_mode, kappa, Cond_tensor, E_field_CPE, E_field_im_CPE) # If QS, j_dens_im is null function # will be used for mesh refinement j_dens_real_unscaled = j_dens_real_CPE.copy(deepcopy=True) j_dens_im_unscaled = j_dens_im_CPE.copy(deepcopy=True) J_real_unscaled = copy.deepcopy(np.real(J_ground)) J_im_unscaled = copy.deepcopy(np.imag(J_ground)) E_norm = project(sqrt( inner(E_field_CPE, E_field_CPE) + inner(E_field_im_CPE, E_field_im_CPE)), V_normE, solver_type="cg", preconditioner_type="amg") max_E = E_norm.vector().max() file = File('/opt/Patient/Results_adaptive/E_ampl_' + str(Field_calc_param.EQS_mode) + '.pvd') file << E_norm, mesh_sol file = File('/opt/Patient/Results_adaptive/Last_Phi_r_field_' + str(Field_calc_param.EQS_mode) + '.pvd') file << phi_r_CPE, mesh_sol if Field_calc_param.EQS_mode == 'EQS': file = File( '/opt/Patient/Results_adaptive/Last_Phi_im_field_' + str(Field_calc_param.EQS_mode) + '.pvd') file << phi_i_CPE, mesh_sol return phi_r_CPE, phi_i_CPE, E_field_CPE, E_field_im_CPE, max_E, J_real_unscaled, J_im_unscaled, j_dens_real_unscaled, j_dens_im_unscaled if Field_calc_param.c_c == 1: if Field_calc_param.EQS_mode == 'EQS': # For EQS, we need to scale the potential on boundaries (because the error is absolute) and recompute field, etc. Maybe we can scale them also directly? Dirichlet_bc_scaled = [] for bc_i in range( len(Domains.Contacts) ): #CPE estimation is valid only for one activa and one ground contact configuration if Field_calc_param.EQS_mode == 'EQS': if Domains.fi[bc_i] != 0.0: Active_with_CC = V_across * V_across / J_ground #(impedance * current through the contact (V_across coincides with the assigned current magnitude)) Dirichlet_bc_scaled.append( DirichletBC(V_space.sub(0), np.real(Active_with_CC), boundaries_sol, Domains.Contacts[bc_i])) Dirichlet_bc_scaled.append( DirichletBC(V_space.sub(1), np.imag(Active_with_CC), boundaries_sol, Domains.Contacts[bc_i])) else: Dirichlet_bc_scaled.append( DirichletBC(V_space.sub(0), Constant(0.0), boundaries_sol, Domains.Contacts[bc_i])) Dirichlet_bc_scaled.append( DirichletBC(V_space.sub(1), Constant(0.0), boundaries_sol, Domains.Contacts[bc_i])) if Field_calc_param.external_grounding == True: if Field_calc_param.EQS_mode == 'EQS': Dirichlet_bc_scaled.append( DirichletBC(V_space.sub(0), 0.0, facets, 1)) Dirichlet_bc_scaled.append( DirichletBC(V_space.sub(1), 0.0, facets, 1)) else: Dirichlet_bc_scaled.append( DirichletBC(V_space, 0.0, facets, 1)) print( "Solving for a scaled potential on contacts (to match the desired current)" ) start_math = tm.time() # to solve the Laplace equation for the adjusted Dirichlet phi_sol_scaled = define_variational_form_and_solve( V_space, Dirichlet_bc_scaled, kappa, Field_calc_param.EQS_mode, Cond_tensor, Solver_type) minutes = int((tm.time() - start_math) / 60) secnds = int(tm.time() - start_math) - minutes * 60 print("--- assembled and solved in ", minutes, " min ", secnds, " s ---") (phi_r_sol_scaled, phi_i_sol_scaled) = phi_sol_scaled.split(deepcopy=True) # get current flowing through the grounded contact and the electric field in the whole domain J_ground_scaled, E_field_scaled, E_field_im_scaled = get_current( mesh_sol, facets, boundaries_sol, Field_calc_param.element_order, Field_calc_param.EQS_mode, Domains.Contacts, kappa, Cond_tensor, phi_r_sol_scaled, phi_i_sol_scaled, ground_index, get_E_field=True) # If EQS, J_ground is a complex number. If QS, E_field_im is 0 else: # here we can simply scale the potential in the domain and recompute the E-field phi_i_sol_scaled = Function(V_space) phi_i_sol_scaled.vector()[:] = 0.0 phi_r_sol_scaled = Function(V_space) phi_r_sol_scaled.vector( )[:] = V_across * phi_r_sol.vector()[:] / J_ground J_ground_scaled, E_field_scaled, E_field_im_scaled = get_current( mesh_sol, facets, boundaries_sol, Field_calc_param.element_order, Field_calc_param.EQS_mode, Domains.Contacts, kappa, Cond_tensor, phi_r_sol_scaled, phi_i_sol_scaled, ground_index, get_E_field=True) #E_field_im_scale is a null function E_norm = project(sqrt( inner(E_field_scaled, E_field_scaled) + inner(E_field_im_scaled, E_field_im_scaled)), V_normE, solver_type="cg", preconditioner_type="amg") max_E = E_norm.vector().max() file = File('/opt/Patient/Results_adaptive/E_ampl_' + str(Field_calc_param.EQS_mode) + '.pvd') file << E_norm, mesh_sol file = File('/opt/Patient/Results_adaptive/Last_Phi_r_field_' + str(Field_calc_param.EQS_mode) + '.pvd') file << phi_r_sol_scaled, mesh_sol if Field_calc_param.EQS_mode == 'EQS': file = File( '/opt/Patient/Results_adaptive/Last_Phi_im_field_' + str(Field_calc_param.EQS_mode) + '.pvd') file << phi_i_sol_scaled, mesh_sol return phi_r_sol_scaled, phi_i_sol_scaled, E_field_scaled, E_field_im_scaled, max_E, J_real_unscaled, J_im_unscaled, j_dens_real_unscaled, j_dens_im_unscaled else: E_norm = project( sqrt(inner(E_field, E_field) + inner(E_field_im, E_field_im)), V_normE, solver_type="cg", preconditioner_type="amg") max_E = E_norm.vector().max() file = File('/opt/Patient/Results_adaptive/E_ampl_' + str(Field_calc_param.EQS_mode) + '.pvd') file << E_norm, mesh_sol file = File('/opt/Patient/Results_adaptive/Last_Phi_r_field_' + str(Field_calc_param.EQS_mode) + '.pvd') file << phi_r_sol, mesh_sol if Field_calc_param.EQS_mode == 'EQS': file = File('/opt/Patient/Results_adaptive/Last_Phi_im_field_' + str(Field_calc_param.EQS_mode) + '.pvd') file << phi_i_sol, mesh_sol return phi_r_sol, phi_i_sol, E_field, E_field_im, max_E, J_real_unscaled, J_im_unscaled, j_dens_real_unscaled, j_dens_im_unscaled
def get_field_with_scaled_BC(mesh_sol, Domains, Phi_scaled, subdomains, boundaries_sol, default_material, element_order, Laplace_mode, anisotropy, frequenc, Solver_type, calc_with_MPI=False, kappa=False): set_log_active(False) #turns off debugging info parameters['linear_algebra_backend'] = 'PETSc' if calc_with_MPI == False or MPI.comm_world.rank == 1: print("Calculating field with scaled voltage on contacts") if calc_with_MPI == False: [cond_GM, perm_GM] = DielectricProperties(3).get_dielectrics( frequenc ) #3 for grey matter and so on (numeration as in voxel_data) [cond_WM, perm_WM] = DielectricProperties(2).get_dielectrics(frequenc) [cond_CSF, perm_CSF] = DielectricProperties(1).get_dielectrics(frequenc) [cond_default, perm_default ] = DielectricProperties(default_material).get_dielectrics(frequenc) from GUI_inp_dict import d as d_encap [cond_encap, perm_encap] = DielectricProperties( d_encap['encap_tissue_type']).get_dielectrics(frequenc) cond_encap = cond_encap * d_encap['encap_scaling_cond'] perm_encap = perm_encap * d_encap['encap_scaling_perm'] conductivities = [cond_default, cond_GM, cond_WM, cond_CSF, cond_encap] rel_permittivities = [ perm_default, perm_GM, perm_WM, perm_CSF, perm_encap ] # to get conductivity (and permittivity if EQS formulation) mapped accrodingly to the subdomains. k_val_r is just a list of conductivities (S/mm!) in a specific order to scale the cond. tensor from FEM_in_spectrum import get_dielectric_properties_from_subdomains kappa, k_val_r = get_dielectric_properties_from_subdomains( mesh_sol, subdomains, Laplace_mode, Domains.Float_contacts, conductivities, rel_permittivities, frequenc) file = File('Results_adaptive/Last_subdomains_map.pvd') file << subdomains file = File('Results_adaptive/Last_conductivity_map.pvd') file << kappa[0] if Laplace_mode == 'EQS': file = File('Results_adaptive/Last_permittivity_map.pvd') file << kappa[1] if anisotropy == 1: # order xx,xy,xz,yy,yz,zz c00 = MeshFunction("double", mesh_sol, 3, 0.0) c01 = MeshFunction("double", mesh_sol, 3, 0.0) c02 = MeshFunction("double", mesh_sol, 3, 0.0) c11 = MeshFunction("double", mesh_sol, 3, 0.0) c12 = MeshFunction("double", mesh_sol, 3, 0.0) c22 = MeshFunction("double", mesh_sol, 3, 0.0) if calc_with_MPI == True: #load predefined Tensor Cond_tensor = load_scaled_cond_tensor(c00, c01, c02, c11, c12, c22, mesh_sol) else: # load the unscaled diffusion tensor (should be normalized beforehand) hdf = HDF5File( mesh_sol.mpi_comm(), "Results_adaptive/Tensors_to_solve_num_el_" + str(mesh_sol.num_cells()) + ".h5", "r") hdf.read(c00, "/c00") hdf.read(c01, "/c01") hdf.read(c02, "/c02") hdf.read(c11, "/c11") hdf.read(c12, "/c12") hdf.read(c22, "/c22") hdf.close() unscaled_tensor = [c00, c01, c02, c11, c12, c22] # to get tensor scaled by the conductivity map (twice send frequenc to always get unscaled ellipsoid tensor for visualization) from FEM_in_spectrum import get_scaled_cond_tensor Cond_tensor = get_scaled_cond_tensor(mesh_sol, subdomains, frequenc, frequenc, unscaled_tensor, k_val_r, plot_tensors=True) else: Cond_tensor = False #just to initialize from FEM_in_spectrum import get_solution_space_and_Dirichlet_BC V_space = get_solution_space_and_Dirichlet_BC(1, mesh_sol, boundaries_sol, element_order, Laplace_mode, Domains.Contacts, Phi_scaled, only_space=True) Dirichlet_bc_scaled = [] if calc_with_MPI == False or MPI.comm_world.rank == 1: print("Scaled complex potential on contacts: ", Phi_scaled[:]) for bc_i in range(len(Domains.Contacts)): if Laplace_mode == 'EQS': Dirichlet_bc_scaled.append( DirichletBC(V_space.sub(0), np.real(Phi_scaled[bc_i]), boundaries_sol, Domains.Contacts[bc_i])) Dirichlet_bc_scaled.append( DirichletBC(V_space.sub(1), np.imag(Phi_scaled[bc_i]), boundaries_sol, Domains.Contacts[bc_i])) else: Dirichlet_bc_scaled.append( DirichletBC(V_space, Phi_scaled[bc_i], boundaries_sol, Domains.Contacts[bc_i])) #facets.array()[boundaries_sol.array()==Domains.Contacts[bc_i]]=bc_i+1 # to solve the Laplace equation div(kappa*grad(phi))=0 (variational form: a(u,v)=L(v)) from FEM_in_spectrum import define_variational_form_and_solve phi_sol = define_variational_form_and_solve(V_space, Dirichlet_bc_scaled, kappa, Laplace_mode, Cond_tensor, Solver_type) if Laplace_mode == 'EQS': (phi_r_sol, phi_i_sol) = phi_sol.split(deepcopy=True) else: phi_r_sol = phi_sol phi_i_sol = Function(V_space) phi_i_sol.vector()[:] = 0.0 # to get manual projections for the E-field from FEM_in_spectrum_multicontact import get_E_field E_field, E_field_im = get_E_field(mesh_sol, element_order, Laplace_mode, phi_r_sol, phi_i_sol) #if QS, E_field_im is a null function # to get current density function which is required for mesh refinement when checking current convergence from Math_module_hybrid import get_current_density j_dens_real, j_dens_im = get_current_density(mesh_sol, element_order, Laplace_mode, kappa, Cond_tensor, E_field, E_field_im) # If QS, j_dens_im is null function # to project the E-field magnitude if element_order > 1: V_normE = FunctionSpace(mesh_sol, "CG", element_order - 1) else: V_normE = FunctionSpace(mesh_sol, "CG", element_order) # to get current on the active contacts (inlcuding the ground) from FEM_in_spectrum_multicontact import get_current_on_multiple_contacts J_r_contacts, J_im_contacts = get_current_on_multiple_contacts( mesh_sol, boundaries_sol, Laplace_mode, Domains.Contacts, Phi_scaled, E_field, E_field_im, kappa, Cond_tensor) # J_currents_imag is a zero array if 'QS' mode if calc_with_MPI == False: Vertices_get = read_csv( 'Neuron_model_arrays/Vert_of_Neural_model_NEURON.csv', delimiter=' ', header=None) Vertices_array = Vertices_get.values Phi_ROI = np.zeros((Vertices_array.shape[0], 4), float) for inx in range(Vertices_array.shape[0]): pnt = Point(Vertices_array[inx, 0], Vertices_array[inx, 1], Vertices_array[inx, 2]) Phi_ROI[inx, 0] = Vertices_array[inx, 0] Phi_ROI[inx, 1] = Vertices_array[inx, 1] Phi_ROI[inx, 2] = Vertices_array[inx, 2] Phi_ROI[inx, 3] = np.sqrt( phi_r_sol(pnt) * phi_r_sol(pnt) + phi_i_sol(pnt) * phi_i_sol(pnt)) np.savetxt('Results_adaptive/Phi_' + str(frequenc) + '.csv', Phi_ROI, delimiter=" ") # this is amplitude, actually Quasi_imp_real = np.zeros(len(Domains.Contacts), float) #not really, but gives an idea Quasi_imp_im = np.zeros(len(Domains.Contacts), float) #not really, but gives an idea for bc_i in range(len(Domains.Contacts)): if calc_with_MPI == False or MPI.comm_world.rank == 1: print("J on contact ", Domains.Active_on_lead[bc_i], ": ", J_r_contacts[bc_i] + 1j * J_im_contacts[bc_i], "A") Quasi_imp_real[bc_i] = np.real( Phi_scaled[bc_i] / (J_r_contacts[bc_i] + 1j * J_im_contacts[bc_i])) if Laplace_mode == 'EQS': Quasi_imp_im[bc_i] = np.imag( Phi_scaled[bc_i] / (J_r_contacts[bc_i] + 1j * J_im_contacts[bc_i])) J_r_max = J_r_contacts.max( ) # we need it to filter out contacts with very small currents (they have very high quasi-impedance) ind_high_current = [] for bc_i in range(len(Domains.Contacts)): if J_r_contacts[bc_i] >= 0.1 * J_r_max: ind_high_current.append(bc_i) Quasi_imp_real_total = np.sum(abs(Quasi_imp_real[ind_high_current])) Quasi_imp_im_total = np.sum(abs(Quasi_imp_im[ind_high_current])) if calc_with_MPI == True: J_Vector = Vector(MPI.comm_self, 2) J_Vector.set_local( np.array([Quasi_imp_real_total, Quasi_imp_im_total], dtype=np.float64)) Hdf = HDF5File( mesh_sol.mpi_comm(), "Results_adaptive/Solution_" + str(np.round(frequenc, 6)) + ".h5", "w") Hdf.write(mesh_sol, "mesh_sol") Hdf.write(phi_sol, "solution_phi_full") Hdf.write(E_field, "solution_E_field") Hdf.write(E_field_im, "solution_E_field_im") Hdf.write(j_dens_real, "solution_j_real") Hdf.write(j_dens_im, "solution_j_im") Hdf.write(J_Vector, "/J_Vector") Hdf.close() return True else: print("Quasi_impedance (for current check):", sqrt(Quasi_imp_real_total**2 + Quasi_imp_im_total**2)) E_norm = project( sqrt(inner(E_field, E_field) + inner(E_field_im, E_field_im)), V_normE, solver_type="cg", preconditioner_type="amg") max_E = E_norm.vector().max() if calc_with_MPI == False or MPI.comm_world.rank == 1: file = File('Results_adaptive/E_ampl_' + str(Laplace_mode) + '.pvd') file << E_norm, mesh_sol file = File('Results_adaptive/Last_Phi_r_field_' + str(Laplace_mode) + '.pvd') file << phi_r_sol, mesh_sol if Laplace_mode == 'EQS': file = File('Results_adaptive/Last_Phi_im_field_' + str(Laplace_mode) + '.pvd') file << phi_i_sol, mesh_sol return (phi_r_sol, phi_i_sol, E_field, E_field_im, max_E, Quasi_imp_real_total, Quasi_imp_im_total, j_dens_real, j_dens_im)
def get_field_with_floats(mesh_sol, active_index, Domains, subdomains, boundaries_sol, default_material, element_order, anisotropy, frequenc, Laplace_mode, Solver_type, calc_with_MPI=False, kappa=False): set_log_active(False) #turns off debugging info parameters['linear_algebra_backend'] = 'PETSc' if Laplace_mode == 'EQS': Solver_type = 'MUMPS' # always direct solver for EQS when multiple floats if calc_with_MPI == False: [cond_GM, perm_GM] = DielectricProperties(3).get_dielectrics( frequenc ) #3 for grey matter and so on (numeration as in voxel_data) [cond_WM, perm_WM] = DielectricProperties(2).get_dielectrics(frequenc) [cond_CSF, perm_CSF] = DielectricProperties(1).get_dielectrics(frequenc) [cond_default, perm_default ] = DielectricProperties(default_material).get_dielectrics(frequenc) from GUI_inp_dict import d as d_encap [cond_encap, perm_encap] = DielectricProperties( d_encap['encap_tissue_type']).get_dielectrics(frequenc) cond_encap = cond_encap * d_encap['encap_scaling_cond'] perm_encap = perm_encap * d_encap['encap_scaling_perm'] conductivities = [cond_default, cond_GM, cond_WM, cond_CSF, cond_encap] rel_permittivities = [ perm_default, perm_GM, perm_WM, perm_CSF, perm_encap ] # to get conductivity (and permittivity if EQS formulation) mapped accrodingly to the subdomains. k_val_r is just a list of conductivities (S/mm!) in a specific order to scale the cond. tensor from FEM_in_spectrum import get_dielectric_properties_from_subdomains kappa, k_val_r = get_dielectric_properties_from_subdomains( mesh_sol, subdomains, Laplace_mode, Domains.Float_contacts, conductivities, rel_permittivities, frequenc) if anisotropy == 1: # order xx,xy,xz,yy,yz,zz c00 = MeshFunction("double", mesh_sol, 3, 0.0) c01 = MeshFunction("double", mesh_sol, 3, 0.0) c02 = MeshFunction("double", mesh_sol, 3, 0.0) c11 = MeshFunction("double", mesh_sol, 3, 0.0) c12 = MeshFunction("double", mesh_sol, 3, 0.0) c22 = MeshFunction("double", mesh_sol, 3, 0.0) if calc_with_MPI == True: #load predefined Tensor Cond_tensor = load_scaled_cond_tensor(c00, c01, c02, c11, c12, c22, mesh_sol) else: # load the unscaled diffusion tensor (should be normalized beforehand) hdf = HDF5File( mesh_sol.mpi_comm(), "Results_adaptive/Tensors_to_solve_num_el_" + str(mesh_sol.num_cells()) + ".h5", "r") hdf.read(c00, "/c00") hdf.read(c01, "/c01") hdf.read(c02, "/c02") hdf.read(c11, "/c11") hdf.read(c12, "/c12") hdf.read(c22, "/c22") hdf.close() unscaled_tensor = [c00, c01, c02, c11, c12, c22] # to get tensor scaled by the conductivity map (twice send frequenc to always get unscaled ellipsoid tensor for visualization) from FEM_in_spectrum import get_scaled_cond_tensor Cond_tensor = get_scaled_cond_tensor(mesh_sol, subdomains, frequenc, frequenc, unscaled_tensor, k_val_r) else: Cond_tensor = False #just to initialize from FEM_in_spectrum import get_solution_space_and_Dirichlet_BC V_space = get_solution_space_and_Dirichlet_BC(1, mesh_sol, boundaries_sol, element_order, Laplace_mode, Domains.Contacts, Domains.fi, only_space=True) facets = MeshFunction('size_t', mesh_sol, 2) facets.set_all(0) # here we have a custom way to assign Dirichlet BC dirichlet_bc = [] float_surface = 2 active_floats = 0 # assign only the chosen active contact and the ground, other should be just marked on the mesh (starting from 2) #print("Contacts: ",Domains.Contacts) #print("fi: ",Domains.fi) #print("active_index: ",active_index) #print("ponteial: ", Domains.fi[active_index]) for bc_i in range(len(Domains.Contacts)): if bc_i == active_index or Domains.fi[bc_i] == 0.0: #print("one") if Laplace_mode == 'EQS': dirichlet_bc.append( DirichletBC(V_space.sub(0), Domains.fi[bc_i], boundaries_sol, Domains.Contacts[bc_i])) dirichlet_bc.append( DirichletBC(V_space.sub(1), Constant(0.0), boundaries_sol, Domains.Contacts[bc_i])) else: dirichlet_bc.append( DirichletBC(V_space, Domains.fi[bc_i], boundaries_sol, Domains.Contacts[bc_i])) if bc_i == active_index: facets.array()[boundaries_sol.array() == Domains.Contacts[bc_i]] = 1 else: facets.array()[boundaries_sol.array() == Domains.Contacts[ bc_i]] = float_surface #it will not be assigned to always floating contacts float_surface = float_surface + 1 active_floats = active_floats + 1 #definitions for integrators dx = Measure("dx", domain=mesh_sol) dsS = Measure("ds", domain=mesh_sol, subdomain_data=facets) dsS_int = Measure("dS", domain=mesh_sol, subdomain_data=facets) #An_surface_size=assemble(1.0*dsS_int(1)) #Cat_surface_size=assemble(1.0*dsS_int(2)) #print "Anode size: " #print An_surface_size # to solve the Laplace equation div(kappa*grad(phi))=0 (variational form: a(u,v)=L(v)) from FEM_in_spectrum import define_variational_form_and_solve phi_sol = define_variational_form_and_solve( V_space, dirichlet_bc, kappa, Laplace_mode, Cond_tensor, Solver_type ) # with multiple floats MUMPS is the most stable though slow if Laplace_mode == 'EQS': (phi_r_sol, phi_i_sol) = phi_sol.split(deepcopy=True) else: phi_r_sol = phi_sol phi_i_sol = Function(V_space) phi_i_sol.vector()[:] = 0.0 Float_potentials_real = np.zeros(active_floats, float) Float_potentials_imag = np.zeros(active_floats, float) float_surface = 2 #float indicies start from 2 # assess the floating potential by integrating over the contact's surface for fl_in in range(active_floats): Float_surface_size = assemble(1.0 * dsS_int(float_surface)) Float_potentials_real[float_surface - 2] = assemble( phi_r_sol * dsS_int(float_surface)) / Float_surface_size if Laplace_mode == 'EQS': Float_potentials_imag[float_surface - 2] = assemble( phi_i_sol * dsS_int(float_surface)) / Float_surface_size float_surface = float_surface + 1 # to get manual projections for the E-field from FEM_in_spectrum_multicontact import get_E_field E_field, E_field_im = get_E_field(mesh_sol, element_order, Laplace_mode, phi_r_sol, phi_i_sol) #if QS, E_field_im is a null function n = FacetNormal(mesh_sol) if Laplace_mode == 'EQS': if Cond_tensor != False: j_dens_real_contact = dot( Cond_tensor * E_field, -1 * n)('-') * dsS_int(1) - dot( kappa[1] * E_field_im, -1 * n)('-') * dsS_int(1) j_dens_im_contact = dot( Cond_tensor * E_field_im, -1 * n)('-') * dsS_int(1) + dot( kappa[1] * E_field, -1 * n)('-') * dsS_int(1) else: j_dens_real_contact = dot( kappa[0] * E_field, -1 * n)('-') * dsS_int(1) - dot( kappa[1] * E_field_im, -1 * n)('-') * dsS_int(1) j_dens_im_contact = dot( kappa[0] * E_field_im, -1 * n)('-') * dsS_int(1) + dot( kappa[1] * E_field, -1 * n)('-') * dsS_int(1) J_real = assemble(j_dens_real_contact) J_im = assemble(j_dens_im_contact) return Float_potentials_real, Float_potentials_imag, J_real, J_im else: if Cond_tensor != False: j_dens_real_contact = dot(Cond_tensor * E_field, -1 * n)('-') * dsS_int(1) else: j_dens_real_contact = dot(kappa[0] * E_field, -1 * n)('-') * dsS_int(1) J_real = assemble(j_dens_real_contact) #print("Shape float potentials: ",Float_potentials_real.shape[0]) return Float_potentials_real, 0.0, J_real, 0.0