def get_lhs_block_form_1(block_V): block_u = BlockTrialFunction(block_V) block_v = BlockTestFunction(block_V) (u, ) = block_split(block_u) (v, ) = block_split(block_v) shape_1 = block_V[0].ufl_element().value_shape() if len(shape_1) is 0: f = Expression("2*x[0] + 4*x[1]*x[1]", degree=2) block_form = [[f * u * v * dx]] elif len(shape_1) is 1 and shape_1[0] is 2: f = Expression(("2*x[0] + 4*x[1]*x[1]", "3*x[0] + 5*x[1]*x[1]"), degree=2) block_form = [[(f[0] * u[0] * v[0] + f[1] * u[1].dx(1) * v[1]) * dx]] elif len(shape_1) is 1 and shape_1[0] is 3: f = Expression(("2*x[0] + 4*x[1]*x[1]", "3*x[0] + 5*x[1]*x[1]", "7*x[0] + 11*x[1]*x[1]"), degree=2) block_form = [[(f[0] * u[0] * v[0] + f[1] * u[1].dx(1) * v[1] + f[2] * u[2].dx(0) * v[2].dx(1)) * dx]] elif len(shape_1) is 2: f = Expression((("2*x[0] + 4*x[1]*x[1]", "3*x[0] + 5*x[1]*x[1]"), ("7*x[0] + 11*x[1]*x[1]", "13*x[0] + 17*x[1]*x[1]")), degree=2) block_form = [[ (f[0, 0] * u[0, 0] * v[0, 0] + f[0, 1] * u[0, 1].dx(1) * v[0, 1] + f[1, 0] * u[1, 0].dx(0) * v[1, 0].dx(1) + f[1, 1] * u[1, 1].dx(0) * v[1, 1]) * dx ]] return block_form
def get_inner_products(W, for_): # Test and trial functions block_v = BlockTestFunction(W) v, q = block_split(block_v) block_u = BlockTrialFunction(W) u, p = block_split(block_u) # Inner products assert for_ in ("POD", "L2 projection") if for_ == "POD": # x = { # "u": [[inner(grad(u), grad(v)) * dx]] # } x = { "u": [[inner(grad(u), grad(v)) * dx, 0], [0, 0]], "p": [[0, 0], [0, inner(p, q) * dx]] } elif for_ == "L2 projection": # x = { # "u": [[inner(u, v) * dx]] # } x = { "u": [[inner(u, v) * dx, 0], [0, 0]], "p": [[0, 0], [0, inner(p, q) * dx]] } return {c: block_assemble(x[c]) for c in components}
def get_rhs_block_form_2(block_V): block_v = BlockTestFunction(block_V) (v1, v2) = block_split(block_v) block_form = [None, None] shape_1 = block_V[0].ufl_element().value_shape() if len(shape_1) is 0: f1 = Expression("2*x[0] + 4*x[1]*x[1]", degree=2) block_form[0] = f1*v1*dx elif len(shape_1) is 1 and shape_1[0] is 2: f1 = Expression(("2*x[0] + 4*x[1]*x[1]", "3*x[0] + 5*x[1]*x[1]"), degree=2) block_form[0] = inner(f1, v1)*dx elif len(shape_1) is 1 and shape_1[0] is 3: f1 = Expression(("2*x[0] + 4*x[1]*x[1]", "3*x[0] + 5*x[1]*x[1]", "7*x[0] + 11*x[1]*x[1]"), degree=2) block_form[0] = inner(f1, v1)*dx elif len(shape_1) is 2: f1 = Expression((("2*x[0] + 4*x[1]*x[1]", "3*x[0] + 5*x[1]*x[1]"), ("7*x[0] + 11*x[1]*x[1]", "13*x[0] + 17*x[1]*x[1]")), degree=2) block_form[0] = inner(f1, v1)*dx shape_2 = block_V[1].ufl_element().value_shape() if len(shape_2) is 0: f2 = Expression("2*x[1] + 4*x[0]*x[0]", degree=2) block_form[1] = f2*v2*dx elif len(shape_2) is 1 and shape_2[0] is 2: f2 = Expression(("2*x[1] + 4*x[0]*x[0]", "3*x[1] + 5*x[0]*x[0]"), degree=2) block_form[1] = inner(f2, v2)*dx elif len(shape_2) is 1 and shape_2[0] is 3: f2 = Expression(("2*x[1] + 4*x[0]*x[0]", "3*x[1] + 5*x[0]*x[0]", "7*x[1] + 11*x[0]*x[0]"), degree=2) block_form[1] = inner(f2, v2)*dx elif len(shape_2) is 2: f2 = Expression((("2*x[1] + 4*x[0]*x[0]", "3*x[1] + 5*x[0]*x[0]"), ("7*x[1] + 11*x[0]*x[0]", "13*x[1] + 17*x[0]*x[0]")), degree=2) block_form[1] = inner(f2, v2)*dx return block_form
def __init__(self, mesh, sigma, Eps, others): self.mesh = mesh self.others = others self.sigma = sigma self.Eps = Eps V = self.fluctuation_space() R = self.zero_average_space() restrictions = [None, None] + self.other_restrictions() W = [V, R] + self.other_spaces() self.W = mp.BlockFunctionSpace(W, restrict=restrictions) self.uu = mp.BlockTrialFunction(self.W) self.vv = mp.BlockTestFunction(self.W) self.uu_ = mp.block_split(self.uu) self.vv_ = mp.block_split(self.vv)
def get_list_of_functions_1(block_V): block_v = BlockTestFunction(block_V) (v, ) = block_split(block_v) shape_1 = block_V[0].ufl_element().value_shape() if len(shape_1) is 0: f = Expression("2*x[0] + 4*x[1]*x[1]", degree=2) elif len(shape_1) is 1 and shape_1[0] is 2: f = Expression(("2*x[0] + 4*x[1]*x[1]", "3*x[0] + 5*x[1]*x[1]"), degree=2) elif len(shape_1) is 1 and shape_1[0] is 3: f = Expression(("2*x[0] + 4*x[1]*x[1]", "3*x[0] + 5*x[1]*x[1]", "7*x[0] + 11*x[1]*x[1]"), degree=2) elif len(shape_1) is 2: f = Expression((("2*x[0] + 4*x[1]*x[1]", "3*x[0] + 5*x[1]*x[1]"), ("7*x[0] + 11*x[1]*x[1]", "13*x[0] + 17*x[1]*x[1]")), degree=2) return [project(f, block_V[0])]
def get_rhs_block_form_1(block_V): block_v = BlockTestFunction(block_V) (v, ) = block_split(block_v) shape_1 = block_V[0].ufl_element().value_shape() if len(shape_1) is 0: f = Expression("2*x[0] + 4*x[1]*x[1]", degree=2) block_form = [f*v*dx] elif len(shape_1) is 1 and shape_1[0] is 2: f = Expression(("2*x[0] + 4*x[1]*x[1]", "3*x[0] + 5*x[1]*x[1]"), degree=2) block_form = [inner(f, v)*dx] elif len(shape_1) is 1 and shape_1[0] is 3: f = Expression(("2*x[0] + 4*x[1]*x[1]", "3*x[0] + 5*x[1]*x[1]", "7*x[0] + 11*x[1]*x[1]"), degree=2) block_form = [inner(f, v)*dx] elif len(shape_1) is 2: f = Expression((("2*x[0] + 4*x[1]*x[1]", "3*x[0] + 5*x[1]*x[1]"), ("7*x[0] + 11*x[1]*x[1]", "13*x[0] + 17*x[1]*x[1]")), degree=2) block_form = [inner(f, v)*dx] return block_form
def transport_linear(integrator_type, mesh, subdomains, boundaries, t_start, dt, T, solution0, \ alpha_0, K_0, mu_l_0, lmbda_l_0, Ks_0, \ alpha_1, K_1, mu_l_1, lmbda_l_1, Ks_1, \ alpha, K, mu_l, lmbda_l, Ks, \ cf_0, phi_0, rho_0, mu_0, k_0,\ cf_1, phi_1, rho_1, mu_1, k_1,\ cf, phi, rho, mu, k, \ d_0, d_1, d_t, vel_c, p_con, A_0, Temp, c_extrapolate): # Create mesh and define function space parameters["ghost_mode"] = "shared_facet" # required by dS dx = Measure('dx', domain=mesh, subdomain_data=subdomains) ds = Measure('ds', domain=mesh, subdomain_data=boundaries) dS = Measure('dS', domain=mesh, subdomain_data=boundaries) C_cg = FiniteElement("CG", mesh.ufl_cell(), 1) C_dg = FiniteElement("DG", mesh.ufl_cell(), 0) mini = C_cg + C_dg C = FunctionSpace(mesh, mini) C = BlockFunctionSpace([C]) TM = TensorFunctionSpace(mesh, 'DG', 0) PM = FunctionSpace(mesh, 'DG', 0) n = FacetNormal(mesh) vc = CellVolume(mesh) fc = FacetArea(mesh) h = vc / fc h_avg = (vc('+') + vc('-')) / (2 * avg(fc)) penalty1 = Constant(1.0) tau = Function(PM) tau = tau_cal(tau, phi, -0.5) tuning_para = 0.25 vel_norm = (dot(vel_c, n) + abs(dot(vel_c, n))) / 2.0 cell_size = CellDiameter(mesh) vnorm = sqrt(dot(vel_c, vel_c)) I = Identity(mesh.topology().dim()) d_eff = Function(TM) d_eff = diff_coeff_cal_rev(d_eff, d_0, tau, phi) + tuning_para * cell_size * vnorm * I monitor_dt = dt # Define variational problem dc, = BlockTrialFunction(C) dc_dot, = BlockTrialFunction(C) psic, = BlockTestFunction(C) block_c = BlockFunction(C) c, = block_split(block_c) block_c_dot = BlockFunction(C) c_dot, = block_split(block_c_dot) theta = -1.0 a_time = phi * rho * inner(c_dot, psic) * dx a_dif = dot(rho*d_eff*grad(c),grad(psic))*dx \ - dot(avg_w(rho*d_eff*grad(c),weight_e(rho*d_eff,n)), jump(psic, n))*dS \ + theta*dot(avg_w(rho*d_eff*grad(psic),weight_e(rho*d_eff,n)), jump(c, n))*dS \ + penalty1/h_avg*k_e(rho*d_eff,n)*dot(jump(c, n), jump(psic, n))*dS a_adv = -dot(rho*vel_c*c,grad(psic))*dx \ + dot(jump(psic), rho('+')*vel_norm('+')*c('+') - rho('-')*vel_norm('-')*c('-') )*dS \ + dot(psic, rho*vel_norm*c)*ds(3) R_c = R_c_cal(c_extrapolate, p_con, Temp) c_D1 = Constant(0.5) rhs_c = R_c * A_s_cal(phi, phi_0, A_0) * psic * dx - dot( rho * phi * vel_c, n) * c_D1 * psic * ds(1) r_u = [a_dif + a_adv] j_u = block_derivative(r_u, [c], [dc]) r_u_dot = [a_time] j_u_dot = block_derivative(r_u_dot, [c_dot], [dc_dot]) r = [r_u_dot[0] + r_u[0] - rhs_c] # this part is not applied. exact_solution_expression1 = Expression("1.0", t=0, element=C[0].ufl_element()) def bc(t): p5 = DirichletBC(C.sub(0), exact_solution_expression1, boundaries, 1, method="geometric") return BlockDirichletBC([p5]) # Define problem wrapper class ProblemWrapper(object): def set_time(self, t): pass # Residual and jacobian functions def residual_eval(self, t, solution, solution_dot): return r def jacobian_eval(self, t, solution, solution_dot, solution_dot_coefficient): return [[ Constant(solution_dot_coefficient) * j_u_dot[0, 0] + j_u[0, 0] ]] # Define boundary condition def bc_eval(self, t): pass # Define initial condition def ic_eval(self): return solution0 # Define custom monitor to plot the solution def monitor(self, t, solution, solution_dot): pass problem_wrapper = ProblemWrapper() (solution, solution_dot) = (block_c, block_c_dot) solver = TimeStepping(problem_wrapper, solution, solution_dot) solver.set_parameters({ "initial_time": t_start, "time_step_size": dt, "monitor": { "time_step_size": monitor_dt, }, "final_time": T, "exact_final_time": "stepover", "integrator_type": integrator_type, "problem_type": "linear", "linear_solver": "mumps", "report": True }) export_solution = solver.solve() return export_solution, T
def solve_initial_problem(self): """ Solve a simple intial problem for a first guess on the iteration process of the main problem. This simple problem is defined as: div(r*grad(phiv)) = 0, in vacuum subdomain phil = 0, in liquid subdomain sigma = 0, at interface Returns ------- phiv : dolfin.function.function.Function Solution of the potential at vacuum. phil : dolfin.function.function.Function Solution of potential at liquid. sigma : dolfin.function.function.Function Solution of the surface charge density. """ V = fn.FunctionSpace(self.mesh, 'Lagrange', 2) # Define the restrictions. restrictions_init = [] for key in self.subdomains_ids.keys(): key = key.lower() + '_rtc' restrictions_init.append(self.restrictions_dict[key]) restrictions_init.append(self.restrictions_dict['interface_rtc']) # Define the block Function Space. W = mp.BlockFunctionSpace([V, V, V], restrict=restrictions_init) # Define the trial and test functions. test = mp.BlockTestFunction(W) (v1, v2, l) = mp.block_split(test) trial = mp.BlockTrialFunction(W) (phiv, phil, sigma) = mp.block_split(trial) # Define auxiliary terms. r = fn.SpatialCoordinate(self.mesh)[0] # phiv phil sigma # aa = [ [ r * fn.inner(fn.grad(phiv), fn.grad(v1)) * self.dx(self.subdomains_ids['Vacuum']), 0, 0 ], # Test Function v1 [0, phil * v2 * self.dx(self.subdomains_ids['Liquid']), 0], # Test function v2 [0, 0, sigma("+") * l("+") * self.dS] ] # Test function l bb = [ fn.Constant(0.) * v1 * self.dx(self.subdomains_ids['Vacuum']), fn.Constant(0.) * v2 * self.dx(self.subdomains_ids['Liquid']), fn.Constant(0.) * l("+") * self.dS ] # Assemble the previous expressions. AA = mp.block_assemble(aa) BB = mp.block_assemble(bb) # Define the boundary conditions. bcs_v = [] bcs_l = [] bcs_i = [] for i in self.boundary_conditions: if 'Dirichlet' in self.boundary_conditions[i]: sub_id = self.boundary_conditions[i]['Dirichlet'][1] if sub_id.lower() == list( self.subdomains_ids.keys())[0].lower(): sub_id = 0 elif sub_id.lower() == list( self.subdomains_ids.keys())[1].lower(): sub_id = 1 else: raise ValueError( f'Subdomain {sub_id} is not defined on the .geo file.') bc_val = self.boundary_conditions[i]['Dirichlet'][0] bc = mp.DirichletBC(W.sub(sub_id), bc_val, self.boundaries, self.boundaries_ids[i]) # Check the created boundary condition. assert len(bc.get_boundary_values() ) > 0., f'Wrongly defined boundary {i}' if sub_id == 0: bcs_v.append(bc) elif sub_id == 1: bcs_l.append(bc) else: bcs_i.append(bc) bcs = mp.BlockDirichletBC([bcs_v, bcs_l, bcs_i]) # Apply the boundary conditions. bcs.apply(AA) bcs.apply(BB) # Define the solution function and solve. sol = mp.BlockFunction(W) mp.block_solve(AA, sol.block_vector(), BB) # Split the solution. (phiv, phil, sigma) = sol.block_split() return phiv, phil, sigma
def trialFunction(self): if self.split: return (TrialFunction(self.W), TrialFunction(self.Q)) else: return block_split(BlockTrialFunction(self.mixedSpace))
def solve(self): """ Solve the Stokes_sim problem based on the mathematical procedure presented by Ximo in this thesis. Returns: """ # -------------------------------------------------------------------- # DEFINE THE INPUTS # # -------------------------------------------------------------------- self.get_mesh() self.get_boundaries() self.get_subdomains() self.get_restrictions() # Create a block of restrictions. """ This variable will be used by multiphenics when creating function spaces. It will create function spaces on the introduced restrictions. """ block_restrictions = [ self.restrictions_dict['liquid_rtc'], self.restrictions_dict['liquid_rtc'], self.restrictions_dict['interface_rtc'] ] # -------------------------------------------------------------------- # -------------------------------------------------------------------- # FUNCTION SPACES # # -------------------------------------------------------------------- V = fn.VectorFunctionSpace(self.mesh, "CG", 2) Q = fn.FunctionSpace(self.mesh, "CG", 1) L = fn.FunctionSpace(self.mesh, "DGT", 0) # DGT 0. # Create a block function space. """ Block Function Spaces are similar to FEniCS function spaces. However, since we are creating function spaces based on the block of restrictions, we need to create a 'block of function spaces' for each of the restrictions. That block of functions is the list [V, Q, L] from the line of code below this comment. They are assigned in the same order in which the block of restrictions has been created, that is: - V -> liquid_rtc - Q -> liquid_rtc - L -> interface_rtc """ W = mp.BlockFunctionSpace([V, Q, L], restrict=block_restrictions) # -------------------------------------------------------------------- # -------------------------------------------------------------------- # TRIAL/TEST FUNCTIONS # # -------------------------------------------------------------------- """ Trial and test functions are created the multiphenics commands for creating these functions. However, the difference wrt the FEniCS functions for this purpose, a trial/test function will be created for each of the restrictions (for each function space of the BlockFunctionSpace). """ test = mp.BlockTestFunction(W) (v, q, l) = mp.block_split(test) trial = mp.BlockTrialFunction(W) (u, p, theta) = mp.block_split(trial) # Use a value of previous velocity to make the system linear, as explained by Ximo. u_prev = fn.Function(V) u_prev.assign(fn.Constant((0.1, 0.1))) # -------------------------------------------------------------------- # -------------------------------------------------------------------- # MEASURES # # -------------------------------------------------------------------- self.get_measures() self.dS = self.dS( self.boundaries_ids['Interface']) # Restrict to the interface. # Check proper marking of the interface. assert fn.assemble( 1 * self.dS(domain=self.mesh) ) > 0., "The length of the interface is zero, wrong marking." # -------------------------------------------------------------------- # DEFINE THE VARIATIONAL PROBLEM # # -------------------------------------------------------------------- r = fn.SpatialCoordinate(self.mesh)[0] n = fn.FacetNormal(self.mesh) tan_vector = fn.as_vector((n[1], -n[0])) e_r = fn.Constant((1., 0.)) # Define unit radial vector e_z = fn.Constant((0., 1.)) # Define unit axial vector aux_term = (self.eps_r * self.Ca * np.sqrt(self.B)) / (1 + self.Lambda * (self.T_h - 1)) # Define the term a. a = r * aux_term * fn.inner((fn.grad(u) + fn.grad(u).T), (fn.grad(v) + fn.grad(v).T)) * self.dx( self.subdomains_ids['Liquid']) a += 2 / r * aux_term * fn.dot(u, e_r) * fn.dot(v, e_r) * self.dx( self.subdomains_ids['Liquid']) # Define the term d. del_operation = fn.dot(fn.grad(u), u_prev) d = r * self.eps_r**2 * self.We * fn.dot(del_operation, v) * self.dx( self.subdomains_ids['Liquid']) # Define the term l1. def evaporated_charge(): return (self.sigma * self.T_h) / (self.eps_r * self.Chi) * fn.exp( -self.Phi / self.T_h * (1 - self.B**0.25 * fn.sqrt(self.E_v_n))) l1 = -r * evaporated_charge() * l("+") * self.dS # Define the term l2. l2 = r * self.sigma * fn.dot(self.E_v, tan_vector("-")) * fn.dot( v("+"), tan_vector("-")) * self.dS # Define the term b. def b(vector, scalar): radial_term = r * fn.dot(vector, e_r) axial_term = r * fn.dot(vector, e_z) return -(radial_term.dx(0) + axial_term.dx(1)) * scalar * self.dx( self.subdomains_ids['Liquid']) # Define the term c. c1 = -r * fn.dot(v("+"), n("-")) * theta("+") * self.dS c2 = -r * fn.dot(u("+"), n("-")) * l("+") * self.dS # Define the tensors to be solved. # The following order is used. # u p theta # aa = [ [a + d, b(v, p), c1], # Test function v [b(u, q), 0, 0], # Test function q [c2, 0, 0] ] # Test function l bb = [l2, fn.Constant(0.) * q("+") * self.dS, l1] # -------------------------------------------------------------------- # DEFINE THE BOUNDARY CONDITIONS # # -------------------------------------------------------------------- """ When creating Dirichlet boundary conditions with the multiphenics code, a function space from the Block must be selected, depending on which subdomain/boundary should it be applied. To do so, the .sub method is used. The input is an integer, which depends on the function space in which you want the BC to be applied. For this case, inputs of 0, 1 and 2 are accepted, because we have 3 restrictions. The assignments of these ids to the function space is the one done in the block of restrictions. """ bcs_u = [] bcs_p = [] for i in self.boundary_conditions: if 'Dirichlet' in self.boundary_conditions[i]: bc_val = self.boundary_conditions[i]['Dirichlet'][1] if self.boundary_conditions[i]['Dirichlet'][0] == 'v': bc = mp.DirichletBC(W.sub(0), bc_val, self.boundaries, self.boundaries_ids[i]) # Check the created boundary condition. assert len(bc.get_boundary_values() ) > 0., f'Wrongly defined boundary {i}' bcs_u.append(bc) elif self.boundary_conditions[i]['Dirichlet'][0] == 'p': bc = mp.DirichletBC(W.sub(1), bc_val, self.boundaries, self.boundaries_ids[i]) # Check the created boundary condition. assert len(bc.get_boundary_values() ) > 0., f'Wrongly defined boundary {i}' bcs_p.append(bc) bcs_block = mp.BlockDirichletBC([bcs_u, bcs_p]) # -------------------------------------------------------------------- # -------------------------------------------------------------------- # SOLVE # # -------------------------------------------------------------------- # Assemble the system. AA = mp.block_assemble(aa) BB = mp.block_assemble(bb) # Apply the boundary conditions. bcs_block.apply(AA) bcs_block.apply(BB) # Solve. uptheta = mp.BlockFunction(W) mp.block_solve(AA, uptheta.block_vector(), BB) (u, p, theta) = uptheta.block_split() self.u = u self.p_star = p self.theta = theta # Compute normal and tangential velocity components. u_n = fn.dot(u, n) self.u_n = Stokes.block_project( u_n, self.mesh, self.restrictions_dict['interface_rtc'], self.boundaries, self.boundaries_ids['Interface'], space_type='scalar', boundary_type='internal', sign='-') u_t = fn.dot(u, tan_vector) self.u_t = Stokes.block_project( u_t, self.mesh, self.restrictions_dict['interface_rtc'], self.boundaries, self.boundaries_ids['Interface'], space_type='scalar', boundary_type='internal', sign='+') # Compute the convection charge transport. special = (fn.Identity(self.mesh.topology().dim()) - fn.outer(n, n)) * fn.grad(self.sigma) self.j_conv = self.Kc * self.B**( 3 / 2) * (fn.dot(self.sigma * n, fn.dot(fn.grad(self.u), n)) - fn.dot(self.u, special)) self.j_conv = Stokes.block_project( self.j_conv, self.mesh, self.restrictions_dict['interface_rtc'], self.boundaries, self.boundaries_ids['Interface'], space_type='scalar', boundary_type='internal', sign='-')
def m_linear(integrator_type, mesh, subdomains, boundaries, t_start, dt, T, solution0, \ alpha_0, K_0, mu_l_0, lmbda_l_0, Ks_0, \ alpha_1, K_1, mu_l_1, lmbda_l_1, Ks_1, \ alpha, K, mu_l, lmbda_l, Ks, \ cf_0, phi_0, rho_0, mu_0, k_0,\ cf_1, phi_1, rho_1, mu_1, k_1,\ cf, phi, rho, mu, k, \ pressure_freeze): # Create mesh and define function space parameters["ghost_mode"] = "shared_facet" # required by dS dx = Measure('dx', domain=mesh, subdomain_data=subdomains) ds = Measure('ds', domain=mesh, subdomain_data=boundaries) dS = Measure('dS', domain=mesh, subdomain_data=boundaries) C = VectorFunctionSpace(mesh, "CG", 2) C = BlockFunctionSpace([C]) TM = TensorFunctionSpace(mesh, 'DG', 0) PM = FunctionSpace(mesh, 'DG', 0) n = FacetNormal(mesh) vc = CellVolume(mesh) fc = FacetArea(mesh) h = vc/fc h_avg = (vc('+') + vc('-'))/(2*avg(fc)) monitor_dt = dt f_stress_x = Constant(-1.e3) f_stress_y = Constant(-20.0e6) f = Constant((0.0, 0.0)) #sink/source for displacement I = Identity(mesh.topology().dim()) # Define variational problem psiu, = BlockTestFunction(C) block_u = BlockTrialFunction(C) u, = block_split(block_u) w = BlockFunction(C) theta = -1.0 a_time = inner(-alpha*pressure_freeze*I,sym(grad(psiu)))*dx #quasi static a = inner(2*mu_l*strain(u)+lmbda_l*div(u)*I, sym(grad(psiu)))*dx rhs_a = inner(f,psiu)*dx \ + dot(f_stress_y*n,psiu)*ds(2) r_u = [a] #DirichletBC bcd1 = DirichletBC(C.sub(0).sub(0), 0.0, boundaries, 1) # No normal displacement for solid on left side bcd3 = DirichletBC(C.sub(0).sub(0), 0.0, boundaries, 3) # No normal displacement for solid on right side bcd4 = DirichletBC(C.sub(0).sub(1), 0.0, boundaries, 4) # No normal displacement for solid on bottom side bcs = BlockDirichletBC([bcd1,bcd3,bcd4]) AA = block_assemble([r_u]) FF = block_assemble([rhs_a - a_time]) bcs.apply(AA) bcs.apply(FF) block_solve(AA, w.block_vector(), FF, "mumps") export_solution = w return export_solution, T
def truth_solve(mu_unkown): print("Performing truth solve at mu =", mu_unkown) (mesh, subdomains, boundaries, restrictions) = read_mesh() # (mesh, subdomains, boundaries, restrictions) = create_mesh() dx = Measure('dx', subdomain_data=subdomains) ds = Measure('ds', subdomain_data=boundaries) W = generate_block_function_space(mesh, restrictions) # Test and trial functions block_v = BlockTestFunction(W) v, q = block_split(block_v) block_du = BlockTrialFunction(W) du, dp = block_split(block_du) block_u = BlockFunction(W) u, p = block_split(block_u) # gap # V2 = FunctionSpace(mesh, "CG", 1) # gap = Function(V2, name="Gap") # obstacle R = 0.25 d = 0.15 x_0 = mu_unkown[0] y_0 = mu_unkown[1] obstacle = Expression("-d+(pow(x[0]-x_0,2)+pow(x[1]-y_0, 2))/2/R", d=d, R=R , x_0 = x_0, y_0 = y_0, degree=0) # Constitutive parameters E = Constant(10.0) nu = Constant(0.3) mu, lmbda = Constant(E/(2*(1 + nu))), Constant(E*nu/((1 + nu)*(1 - 2*nu))) B = Constant((0.0, 0.0, 0.0)) # Body force per unit volume T = Constant((0.0, 0.0, 0.0)) # Traction force on the boundary # Kinematics # ----------------------------------------------------------------------------- mesh_dim = mesh.topology().dim() # Spatial dimension I = Identity(mesh_dim) # Identity tensor F = I + grad(u) # Deformation gradient C = F.T*F # Right Cauchy-Green tensor J = det(F) # 3rd invariant of the deformation tensor # Strain function def P(u): # P = dW/dF: return mu*(F - inv(F.T)) + lmbda*ln(J)*inv(F.T) def eps(v): return sym(grad(v)) def sigma(v): return lmbda*tr(eps(v))*Identity(3) + 2.0*mu*eps(v) # Definition of The Mackauley bracket <x>+ def ppos(x): return (x+abs(x))/2. # Define the augmented lagrangian def aug_l(x): return x + pen*(obstacle-u[2]) pen = Constant(1e4) # Boundary conditions # bottom_bc = DirichletBC(W.sub(0), Constant((0., 0., 0.)), boundaries, 2) # left_bc = DirichletBC(W.sub(0), Constant((0., 0., 0.)), boundaries, 3) # right_bc = DirichletBC(W.sub(0), Constant((0., 0., 0.)), boundaries, 4) # front_bc = DirichletBC(W.sub(0), Constant((0., 0., 0.)), boundaries, 5) # back_bc = DirichletBC(W.sub(0), Constant((0., 0., 0.)), boundaries, 6) # # sym_x_bc = DirichletBC(W.sub(0).sub(0), Constant(0.), boundaries, 2) # # sym_y_bc = DirichletBC(W.sub(0).sub(1), Constant(0.), boundaries, 3) # # bc = BlockDirichletBC([bottom_bc, sym_x_bc, sym_y_bc]) # bc = BlockDirichletBC([bottom_bc, left_bc, right_bc, front_bc, back_bc]) bottom_bc = DirichletBC(W.sub(0), Constant((0., 0., 0.)), boundaries, 2) left_bc_x = DirichletBC(W.sub(0).sub(0), Constant(0.), boundaries, 3) left_bc_y = DirichletBC(W.sub(0).sub(1), Constant(0.), boundaries, 3) right_bc_x = DirichletBC(W.sub(0).sub(0), Constant(0.), boundaries, 4) right_bc_y = DirichletBC(W.sub(0).sub(1), Constant(0.), boundaries, 4) front_bc_x = DirichletBC(W.sub(0).sub(0), Constant(0.), boundaries, 5) front_bc_y = DirichletBC(W.sub(0).sub(1), Constant(0.), boundaries, 5) back_bc_x = DirichletBC(W.sub(0).sub(0), Constant(0.), boundaries, 6) back_bc_y = DirichletBC(W.sub(0).sub(1), Constant(0.), boundaries, 6) # sym_x_bc = DirichletBC(W.sub(0).sub(0), Constant(0.), boundaries, 2) # sym_y_bc = DirichletBC(W.sub(0).sub(1), Constant(0.), boundaries, 3) # bc = BlockDirichletBC([bottom_bc, sym_x_bc, sym_y_bc]) bc = BlockDirichletBC([bottom_bc, left_bc_x, left_bc_y, \ right_bc_x, right_bc_y, front_bc_x, front_bc_y, \ back_bc_x, back_bc_y]) # Variational forms # F = inner(sigma(u), eps(v))*dx + pen*dot(v[2], ppos(u[2]-obstacle))*ds(1) # F = [inner(sigma(u), eps(v))*dx - aug_l(l)*v[2]*ds(1) + ppos(aug_l(l))*v[2]*ds(1), # (obstacle-u[2])*v*ds(1) - (1/pen)*ppos(aug_l(l))*v*ds(1)] # F_a = inner(sigma(u), eps(v))*dx # F_b = - aug_l(p)*v[2]*ds(1) + ppos(aug_l(p))*v[2]*ds(1) # F_c = (obstacle-u[2])*q*ds(1) # F_d = - (1/pen)*ppos(aug_l(p))*q*ds(1) # # block_F = [[F_a, F_b], # [F_c, F_d]] F_a = inner(P(u), grad(v))*dx - dot(B, v)*dx - dot(T, v)*ds \ - aug_l(p)*v[2]*ds(1) + ppos(aug_l(p))*v[2]*ds(1) F_b = (obstacle-u[2])*q*ds(1) - (1/pen)*ppos(aug_l(p))*q*ds(1) block_F = [F_a, F_b] J = block_derivative(block_F, block_u, block_du) # Setup solver problem = BlockNonlinearProblem(block_F, block_u, bc, J) solver = BlockPETScSNESSolver(problem) solver.parameters.update({ "linear_solver": "mumps", "absolute_tolerance": 1E-4, "relative_tolerance": 1E-4, "maximum_iterations": 50, "report": True, "error_on_nonconvergence": True }) # solver.parameters.update({ # "linear_solver": "cg", # "absolute_tolerance": 1E-4, # "relative_tolerance": 1E-4, # "maximum_iterations": 50, # "report": True, # "error_on_nonconvergence": True # }) # Perform a fake loop over time. Note how up will store the solution at the last time. # Q. for? # A. You can remove it, since your problem is stationary. The template was targeting # a final application which was transient, but in which the ROM should have only # described the final solution (when reaching the steady state). # for _ in range(2): # solver.solve() a1 = solver.solve() print(a1) # save all the solution here as a function of time # Return the solution at the last time # Q. block_u or block # A. I think block_u, it will split split among the components elsewhere return block_u
else: raise RuntimeError("Invalid discretization") W = BlockFunctionSpace(mesh, W_element) PM = FunctionSpace(mesh, "DG", 0) TM = TensorFunctionSpace(mesh, "DG", 0) I = Identity(mesh.topology().dim()) dx = Measure("dx", domain=mesh, subdomain_data=subdomains) ds = Measure("ds", domain=mesh, subdomain_data=boundaries) dS = Measure("dS", domain=mesh, subdomain_data=boundaries) # Test and trial functions vq = BlockTestFunction(W) (v, q) = block_split(vq) up = BlockTrialFunction(W) (u, p) = block_split(up) w = BlockFunction(W) w0 = BlockFunction(W) (u0, p0) = block_split(w0) n = FacetNormal(mesh) vc = CellVolume(mesh) fc = FacetArea(mesh) h = vc / fc h_avg = (vc("+") + vc("-")) / (2 * avg(fc)) penalty1 = 1.0
def testFunction(self): if self.split: return (TestFunction(self.W), TestFunction(self.Q)) else: return block_split(BlockTestFunction(self.mixedSpace))
def get_lhs_block_form_2(block_V): block_u = BlockTrialFunction(block_V) block_v = BlockTestFunction(block_V) (u1, u2) = block_split(block_u) (v1, v2) = block_split(block_v) block_form = [[None, None], [None, None]] # (1, 1) block shape_1 = block_V[0].ufl_element().value_shape() if len(shape_1) is 0: f1 = Expression("2*x[0] + 4*x[1]*x[1]", degree=2) block_form[0][0] = f1*u1*v1.dx(0)*dx elif len(shape_1) is 1 and shape_1[0] is 2: f1 = Expression(("2*x[0] + 4*x[1]*x[1]", "3*x[0] + 5*x[1]*x[1]"), degree=2) block_form[0][0] = (f1[0]*u1[0]*v1[0].dx(0) + f1[1]*u1[1].dx(1)*v1[1])*dx elif len(shape_1) is 1 and shape_1[0] is 3: f1 = Expression(("2*x[0] + 4*x[1]*x[1]", "3*x[0] + 5*x[1]*x[1]", "7*x[0] + 11*x[1]*x[1]"), degree=2) block_form[0][0] = (f1[0]*u1[0]*v1[0].dx(0) + f1[1]*u1[1].dx(1)*v1[1] + f1[2]*u1[2].dx(0)*v1[2].dx(1))*dx elif len(shape_1) is 2: f1 = Expression((("2*x[0] + 4*x[1]*x[1]", "3*x[0] + 5*x[1]*x[1]"), ("7*x[0] + 11*x[1]*x[1]", "13*x[0] + 17*x[1]*x[1]")), degree=2) block_form[0][0] = (f1[0, 0]*u1[0, 0]*v1[0, 0].dx(0) + f1[0, 1]*u1[0, 1].dx(1)*v1[0, 1] + f1[1, 0]*u1[1, 0].dx(0)*v1[1, 0].dx(1) + f1[1, 1]*u1[1, 1].dx(0)*v1[1, 1])*dx # (2, 2) block shape_2 = block_V[1].ufl_element().value_shape() if len(shape_2) is 0: f2 = Expression("2*x[1] + 4*x[0]*x[0]", degree=2) block_form[1][1] = f2*u2*v2.dx(0)*dx elif len(shape_2) is 1 and shape_2[0] is 2: f2 = Expression(("2*x[1] + 4*x[0]*x[0]", "3*x[1] + 5*x[0]*x[0]"), degree=2) block_form[1][1] = (f2[0]*u2[0]*v2[0].dx(0) + f2[1]*u2[1].dx(1)*v2[1])*dx elif len(shape_2) is 1 and shape_2[0] is 3: f2 = Expression(("2*x[1] + 4*x[0]*x[0]", "3*x[1] + 5*x[0]*x[0]", "7*x[1] + 11*x[0]*x[0]"), degree=2) block_form[1][1] = (f2[0]*u2[0]*v2[0].dx(0) + f2[1]*u2[1].dx(1)*v2[1] + f2[2]*u2[2].dx(0)*v2[2].dx(1))*dx elif len(shape_2) is 2: f2 = Expression((("2*x[1] + 4*x[0]*x[0]", "3*x[1] + 5*x[0]*x[0]"), ("7*x[1] + 11*x[0]*x[0]", "13*x[1] + 17*x[0]*x[0]")), degree=2) block_form[1][1] = (f2[0, 0]*u2[0, 0]*v2[0, 0].dx(0) + f2[0, 1]*u2[0, 1].dx(1)*v2[0, 1] + f2[1, 0]*u2[1, 0].dx(0)*v2[1, 0].dx(1) + f2[1, 1]*u2[1, 1].dx(0)*v2[1, 1])*dx # (1, 2) and (2, 1) blocks if len(shape_1) is 0: if len(shape_2) is 0: block_form[0][1] = f1*u2*v1.dx(0)*dx block_form[1][0] = f2*u1*v2.dx(0)*dx elif len(shape_2) is 1 and shape_2[0] is 2: block_form[0][1] = f1*u2[0]*v1.dx(0)*dx + f1*u2[1]*v1.dx(1)*dx block_form[1][0] = (f2[0]*u1*v2[0].dx(0) + f2[1]*u1.dx(1)*v2[1])*dx elif len(shape_2) is 1 and shape_2[0] is 3: block_form[0][1] = f1*u2[0]*v1.dx(0)*dx + f1*u2[1]*v1.dx(1)*dx + f1*u2[2]*v1*dx block_form[1][0] = (f2[0]*u1*v2[0].dx(0) + f2[1]*u1.dx(1)*v2[1] + f2[2]*u1.dx(0)*v2[2].dx(1))*dx elif len(shape_2) is 2: block_form[0][1] = f1*u2[0, 0]*v1.dx(0)*dx + f1*u2[1, 1]*v1.dx(0)*dx block_form[1][0] = (f2[0, 0]*u1*v2[0, 0].dx(0) + f2[0, 1]*u1.dx(1)*v2[0, 1] + f2[1, 0]*u1.dx(0)*v2[1, 0].dx(1) + f2[1, 1]*u1.dx(0)*v2[1, 1])*dx elif len(shape_1) is 1 and shape_1[0] is 2: if len(shape_2) is 0: block_form[0][1] = (f1[0]*u2*v1[0].dx(0) + f1[1]*u2.dx(1)*v1[1])*dx block_form[1][0] = f2*u1[0]*v2.dx(0)*dx + f2*u1[1]*v2.dx(0)*dx elif len(shape_2) is 1 and shape_2[0] is 2: block_form[0][1] = (f1[0]*u2[0]*v1[0].dx(0) + f1[1]*u2[1].dx(1)*v1[1])*dx block_form[1][0] = (f2[0]*u1[0]*v2[0].dx(0) + f2[1]*u1[1].dx(1)*v2[1])*dx elif len(shape_2) is 1 and shape_2[0] is 3: block_form[0][1] = (f1[0]*u2[0]*v1[0].dx(0) + f1[1]*u2[1].dx(1)*v1[1] + f1[0]*u2[2]*v1[0])*dx block_form[1][0] = (f2[0]*u1[0]*v2[0].dx(0) + f2[1]*u1[1].dx(1)*v2[1] + f2[2]*u1[0].dx(0)*v2[2].dx(1))*dx elif len(shape_2) is 2: block_form[0][1] = (f1[0]*u2[0, 0]*v1[0].dx(0) + f1[1]*u2[1, 1].dx(1)*v1[1])*dx block_form[1][0] = (f2[0, 0]*u1[0]*v2[0, 0].dx(0) + f2[0, 1]*u1[0].dx(1)*v2[0, 1] + f2[1, 0]*u1[1].dx(0)*v2[1, 0].dx(1) + f2[1, 1]*u1[0].dx(0)*v2[1, 1])*dx elif len(shape_1) is 1 and shape_1[0] is 3: if len(shape_2) is 0: block_form[0][1] = (f1[0]*u2*v1[0].dx(0) + f1[1]*u2.dx(1)*v1[1] + f1[2]*u2.dx(0)*v1[2].dx(1))*dx block_form[1][0] = f2*u1[0]*v2.dx(0)*dx + f2*u1[1]*v2.dx(1)*dx + f2*u1[2]*v2*dx elif len(shape_2) is 1 and shape_2[0] is 2: block_form[0][1] = (f1[0]*u2[0]*v1[0].dx(0) + f1[1]*u2[1].dx(1)*v1[1] + f1[2]*u2[0].dx(0)*v1[2].dx(1))*dx block_form[1][0] = (f2[0]*u1[0]*v2[0].dx(0) + f2[1]*u1[1].dx(1)*v2[1] + f2[1]*u1[2].dx(1)*v2[1])*dx elif len(shape_2) is 1 and shape_2[0] is 3: block_form[0][1] = (f1[0]*u2[0]*v1[0].dx(0) + f1[1]*u2[1].dx(1)*v1[1] + f1[2]*u2[2].dx(0)*v1[2].dx(1))*dx block_form[1][0] = (f2[0]*u1[0]*v2[0].dx(0) + f2[1]*u1[1].dx(1)*v2[1] + f2[2]*u1[2].dx(0)*v2[2].dx(1))*dx elif len(shape_2) is 2: block_form[0][1] = (f1[0]*u2[0, 0]*v1[0].dx(0) + f1[1]*u2[1, 0].dx(1)*v1[1] + f1[2]*u2[0, 1].dx(0)*v1[2].dx(1) + f1[0]*u2[1, 1]*v1[0].dx(1))*dx block_form[1][0] = (f2[0, 0]*u1[0]*v2[0, 0].dx(0) + f2[0, 1]*u1[1].dx(1)*v2[0, 1] + f2[1, 0]*u1[2].dx(0)*v2[1, 0].dx(1) + f2[1, 1]*u1[0].dx(0)*v2[1, 1])*dx elif len(shape_1) is 2: if len(shape_2) is 0: block_form[0][1] = (f1[0, 0]*u2*v1[0, 0].dx(0) + f1[0, 1]*u2.dx(1)*v1[0, 1] + f1[1, 0]*u2.dx(0)*v1[1, 0].dx(1) + f1[1, 1]*u2.dx(0)*v1[1, 1])*dx block_form[1][0] = f2*u1[0, 0]*v2.dx(0)*dx + f2*u1[1, 1]*v2.dx(1)*dx elif len(shape_2) is 1 and shape_2[0] is 2: block_form[0][1] = (f1[0, 0]*u2[0]*v1[0, 0].dx(0) + f1[0, 1]*u2[0].dx(1)*v1[0, 1] + f1[1, 0]*u2[1].dx(0)*v1[1, 0].dx(1) + f1[1, 1]*u2[1].dx(0)*v1[1, 1])*dx block_form[1][0] = (f2[0]*u1[0, 0]*v2[0].dx(0) + f2[1]*u1[1, 1].dx(1)*v2[1])*dx elif len(shape_2) is 1 and shape_2[0] is 3: block_form[0][1] = (f1[0, 0]*u2[0]*v1[0, 0].dx(0) + f1[0, 1]*u2[1].dx(1)*v1[0, 1] + f1[1, 0]*u2[2].dx(0)*v1[1, 0].dx(1) + f1[1, 1]*u2[0].dx(0)*v1[1, 1])*dx block_form[1][0] = (f2[0]*u1[0, 0]*v2[0].dx(0) + f2[1]*u1[1, 0].dx(1)*v2[1] + f2[2]*u1[0, 1].dx(0)*v2[2].dx(1) + f2[0]*u1[1, 1]*v2[0].dx(1))*dx elif len(shape_2) is 2: block_form[0][1] = (f1[0, 0]*u2[0, 0]*v1[0, 0].dx(0) + f1[0, 1]*u2[0, 1].dx(1)*v1[0, 1] + f1[1, 0]*u2[1, 0].dx(0)*v1[1, 0].dx(1) + f1[1, 1]*u2[1, 1].dx(0)*v1[1, 1])*dx block_form[1][0] = (f2[0, 0]*u1[0, 0]*v2[0, 0].dx(0) + f2[0, 1]*u1[0, 1].dx(1)*v2[0, 1] + f2[1, 0]*u1[1, 0].dx(0)*v2[1, 0].dx(1) + f2[1, 1]*u1[1, 1].dx(0)*v2[1, 1])*dx return block_form
def solve(self, **kwargs): """ Solves the variational form of the electrostatics as defined in the End of Master thesis from Ximo Gallud Cidoncha: A comprehensive numerical procedure for solving the Taylor-Melcher leaky dielectric model with charge evaporation. Parameters ---------- **kwargs : dict Accepted kwargs are: - electrostatics_solver_settings: The user may define its own solver parameters. They must be defined as follows: solver_parameters = {"snes_solver": {"linear_solver": "mumps", "maximum_iterations": 50, "report": True, "error_on_nonconvergence": True, 'line_search': 'bt', 'relative_tolerance': 1e-4}} where: - snes_solver is the type of solver to be used. In this case, it is compulsory to use snes, since it's the solver accepted by multiphenics. However, one may try other options if only FEniCS is used. These are: krylov_solver and lu_solver. - linear_solver is the type of linear solver to be used. - maximum_iterations is the maximum number of iterations the solver will try to solve the problem. In case no convergence is achieved, the variable error_on_nonconvergence will raise an error in case this is True. If the user preferes not to raise an error when no convergence, the script will continue with the last results obtained in the iteration process. - line_search is the type of line search technique to be used for solving the problem. It is stronly recommended to use the backtracking (bt) method, since it has been proven to be the most robust one, specially in cases where sqrt are defined, where NaNs may appear due to a bad initial guess or a bad step in the iteration process. - relative_tolerance will tell the solver the parameter to consider convergence on the solution. All this options, as well as all the other options available can be consulted by calling the method Poisson.check_solver_options(). - initial_potential: Dolfin/FEniCS function which will be used as an initial guess on the iterative process. This must be introduced along with kwarg initial_surface_charge_density. Optional. - initial_surface_charge_density: Dolfin/FEniCS function which will be used as an initial guess on the iterative process. This must be introduced along with kwarg initial_potential. Optional. Raises ------ TypeError This error will raise when the convection charge has not one of the following types: - Dolfin Function. - FEniCS UserExpression. - FEniCS Constant. - Integer or float number, which will be converted to a FEniCS Constant. Returns ------- phi : dolfin.function.function.Function Dolfin function containing the potential solution. surface_charge_density : dolfin.function.function.Function Dolfin function conataining the surface charge density solution. """ # -------------------------------------------------------------------- # EXTRACT THE INPUTS # # -------------------------------------------------------------------- # Check if the type of j_conv is the proper one. if not isinstance(self.j_conv, (int, float)) \ and not Poisson.isDolfinFunction(self.j_conv) \ and not Poisson.isfenicsexpression(self.j_conv) \ and not Poisson.isfenicsconstant(self.j_conv): conv_type = type(self.j_conv) raise TypeError( f'Convection charge must be an integer, float, Dolfin function, FEniCS UserExpression or FEniCS constant, not {conv_type}.' ) else: if isinstance(self.j_conv, (int, float)): self.j_conv = fn.Constant(float(self.j_conv)) # Extract the solver parameters. solver_parameters = kwargs.get('electrostatics_solver_settings') # -------------------------------------------------------------------- # FUNCTION SPACES # # -------------------------------------------------------------------- # Extract the restrictions to create the function spaces. """ This variable will be used by multiphenics when creating function spaces. It will create function spaces on the introduced restrictions. """ restrictions_block = [ self.restrictions_dict['domain_rtc'], self.restrictions_dict['interface_rtc'] ] # Base Function Space. V = fn.FunctionSpace(self.mesh, 'Lagrange', 2) # Block Function Space. """ Block Function Spaces are similar to FEniCS function spaces. However, since we are creating function spaces based on the block of restrictions, we need to create a 'block of function spaces' for each of the restrictions. That block of functions is the list [V, V] from the line of code below this comment. They are assigned in the same order in which the block of restrictions has been created, that is: - V -> domain_rtc - V -> interface_rtc """ W = mp.BlockFunctionSpace([V, V], restrict=restrictions_block) # Check the dimensions of the created block function spaces. for ix, _ in enumerate(restrictions_block): assert W.extract_block_sub_space( (ix, )).dim() > 0., f'Subdomain {ix} has dimension 0.' # -------------------------------------------------------------------- # TRIAL/TEST FUNCTIONS # # -------------------------------------------------------------------- # Trial Functions. dphisigma = mp.BlockTrialFunction(W) # Test functions. vl = mp.BlockTestFunction(W) (v, l) = mp.block_split(vl) phisigma = mp.BlockFunction(W) (phi, sigma) = mp.block_split(phisigma) # -------------------------------------------------------------------- # MEASURES # # -------------------------------------------------------------------- self.get_measures() self.dS = self.dS( self.boundaries_ids['Interface']) # Restrict to the interface. # Check proper marking of the interface. assert fn.assemble( 1 * self.dS(domain=self.mesh) ) > 0., "The length of the interface is zero, wrong marking. Check the files in Paraview." # -------------------------------------------------------------------- # DEFINE THE F TERM # # -------------------------------------------------------------------- n = fn.FacetNormal(self.mesh) t = fn.as_vector((n[1], -n[0])) # Define auxiliary terms. r = fn.SpatialCoordinate(self.mesh)[0] K = 1 + self.Lambda * (self.T_h - 1) E_v_n_aux = fn.dot(-fn.grad(phi("-")), n("-")) def expFun(): sqrterm = E_v_n_aux expterm = (self.Phi / self.T_h) * (1 - pow(self.B, 0.25) * fn.sqrt(sqrterm)) return fn.exp(expterm) def sigma_fun(): num = K * E_v_n_aux + self.eps_r * self.j_conv den = K + (self.T_h / self.Chi) * expFun() return r * num / den # Define the relative permittivity. class relative_perm(fn.UserExpression): def __init__(self, markers, subdomain_ids, relative, **kwargs): super().__init__(**kwargs) self.markers = markers self.subdomain_ids = subdomain_ids self.relative = relative def eval_cell(self, values, x, cell): if self.markers[cell.index] == self.subdomain_ids['Vacuum']: values[0] = 1. else: values[0] = self.relative rel_perm = relative_perm(self.subdomains, self.subdomains_ids, relative=self.eps_r, degree=0) # Define the variational form. # vacuum_int = r*fn.inner(fn.grad(phi), fn.grad(v))*self.dx(self.subdomains_ids['Vacuum']) # liquid_int = self.eps_r*r*fn.inner(fn.grad(phi), fn.grad(v))*self.dx(self.subdomains_ids['Liquid']) F = [ r * rel_perm * fn.inner(fn.grad(phi), fn.grad(v)) * self.dx - r * sigma("-") * v("-") * self.dS, r * sigma_fun() * l("-") * self.dS - r * sigma("-") * l("-") * self.dS ] J = mp.block_derivative(F, phisigma, dphisigma) # -------------------------------------------------------------------- # BOUNDARY CONDITIONS # # -------------------------------------------------------------------- bcs_block = [] for i in self.boundary_conditions: if 'Dirichlet' in self.boundary_conditions[i]: bc_val = self.boundary_conditions[i]['Dirichlet'][0] bc = mp.DirichletBC(W.sub(0), bc_val, self.boundaries, self.boundaries_ids[i]) # Check the created boundary condition. assert len(bc.get_boundary_values() ) > 0., f'Wrongly defined boundary {i}' bcs_block.append(bc) bcs_block = mp.BlockDirichletBC([bcs_block]) # -------------------------------------------------------------------- # SOLVE # # -------------------------------------------------------------------- # Define and assign the initial guesses. if kwargs.get('initial_potential') is None: """ Check if the user is introducing a potential from a previous iteration. """ phiv, phil, sigma_init = self.solve_initial_problem() # phi_init = self.solve_initial_problem_v2() phi.assign(phiv) sigma.assign(sigma_init) else: phi.assign(kwargs.get('initial_potential')) sigma.assign(kwargs.get('initial_surface_charge_density')) # Apply the initial guesses to the main function. phisigma.apply('from subfunctions') # Solve the problem with the solver options (either default or user). problem = mp.BlockNonlinearProblem(F, phisigma, bcs_block, J) solver = mp.BlockPETScSNESSolver(problem) solver_type = [i for i in solver_parameters.keys()][0] solver.parameters.update(solver_parameters[solver_type]) solver.solve() # Extract the solutions. (phi, _) = phisigma.block_split() self.phi = phi # -------------------------------------------------------------------- # Compute the electric field at vacuum and correct the surface charge density. self.E_v = self.get_electric_field('Vacuum') self.E_v_n = self.get_normal_field(n("-"), self.E_v) self.E_t = self.get_tangential_component(t("+"), self.E_v) C = self.Phi / self.T_h * (1 - self.B**0.25 * fn.sqrt(self.E_v_n)) self.sigma = (K * self.E_v_n) / (K + self.T_h / self.Chi * fn.exp(-C))
def h_linear(integrator_type, mesh, subdomains, boundaries, t_start, dt, T, solution0, \ alpha_0, K_0, mu_l_0, lmbda_l_0, Ks_0, \ alpha_1, K_1, mu_l_1, lmbda_l_1, Ks_1, \ alpha, K, mu_l, lmbda_l, Ks, \ cf_0, phi_0, rho_0, mu_0, k_0,\ cf_1, phi_1, rho_1, mu_1, k_1,\ cf, phi, rho, mu, k, \ sigma_v_freeze, dphi_c_dt): # Create mesh and define function space parameters["ghost_mode"] = "shared_facet" # required by dS dx = Measure('dx', domain=mesh, subdomain_data=subdomains) ds = Measure('ds', domain=mesh, subdomain_data=boundaries) dS = Measure('dS', domain=mesh, subdomain_data=boundaries) BDM = FiniteElement("BDM", mesh.ufl_cell(), 1) PDG = FiniteElement("DG", mesh.ufl_cell(), 0) BDM_F = FunctionSpace(mesh, BDM) PDG_F = FunctionSpace(mesh, PDG) W = BlockFunctionSpace([BDM_F, PDG_F], restrict=[None, None]) TM = TensorFunctionSpace(mesh, 'DG', 0) PM = FunctionSpace(mesh, 'DG', 0) n = FacetNormal(mesh) vc = CellVolume(mesh) fc = FacetArea(mesh) h = vc / fc h_avg = (vc('+') + vc('-')) / (2 * avg(fc)) I = Identity(mesh.topology().dim()) monitor_dt = dt p_outlet = 0.1e6 p_inlet = 1000.0 M_inv = phi_0 * cf + (alpha - phi_0) / Ks # Define variational problem trial = BlockTrialFunction(W) dv, dp = block_split(trial) trial_dot = BlockTrialFunction(W) dv_dot, dp_dot = block_split(trial_dot) test = BlockTestFunction(W) psiv, psip = block_split(test) block_w = BlockFunction(W) v, p = block_split(block_w) block_w_dot = BlockFunction(W) v_dot, p_dot = block_split(block_w_dot) a_time = Constant(0.0) * inner(v_dot, psiv) * dx #quasi static # k is a function of phi #k = perm_update_rutqvist_newton(p,p0,phi0,phi,coeff) lhs_a = inner(dot(v, mu * inv(k)), psiv) * dx - p * div( psiv ) * dx #+ 6.0*inner(psiv,n)*ds(2) # - inner(gravity*(rho-rho0), psiv)*dx b_time = (M_inv + pow(alpha, 2.) / K) * p_dot * psip * dx lhs_b = div(v) * psip * dx #div(rho*v)*psip*dx #TODO rho rhs_v = -p_outlet * inner(psiv, n) * ds(3) rhs_p = -alpha / K * sigma_v_freeze * psip * dx - dphi_c_dt * psip * dx r_u = [lhs_a, lhs_b] j_u = block_derivative(r_u, block_w, trial) r_u_dot = [a_time, b_time] j_u_dot = block_derivative(r_u_dot, block_w_dot, trial_dot) r = [r_u_dot[0] + r_u[0] - rhs_v, \ r_u_dot[1] + r_u[1] - rhs_p] def bc(t): #bc_v = [DirichletBC(W.sub(0), (.0, .0), boundaries, 4)] v1 = DirichletBC(W.sub(0), (1.e-4 * 2.0, 0.0), boundaries, 1) v2 = DirichletBC(W.sub(0), (0.0, 0.0), boundaries, 2) v4 = DirichletBC(W.sub(0), (0.0, 0.0), boundaries, 4) bc_v = [v1, v2, v4] return BlockDirichletBC([bc_v, None]) # Define problem wrapper class ProblemWrapper(object): def set_time(self, t): pass #g.t = t # Residual and jacobian functions def residual_eval(self, t, solution, solution_dot): #print(as_backend_type(assemble(p_time - p_time_error)).vec().norm()) #print("gravity effect", as_backend_type(assemble(inner(gravity*(rho-rho0), psiv)*dx)).vec().norm()) return r def jacobian_eval(self, t, solution, solution_dot, solution_dot_coefficient): return [[Constant(solution_dot_coefficient)*j_u_dot[0, 0] + j_u[0, 0], \ Constant(solution_dot_coefficient)*j_u_dot[0, 1] + j_u[0, 1]], \ [Constant(solution_dot_coefficient)*j_u_dot[1, 0] + j_u[1, 0], \ Constant(solution_dot_coefficient)*j_u_dot[1, 1] + j_u[1, 1]]] # Define boundary condition def bc_eval(self, t): return bc(t) # Define initial condition def ic_eval(self): return solution0 # Define custom monitor to plot the solution def monitor(self, t, solution, solution_dot): pass # Solve the time dependent problem problem_wrapper = ProblemWrapper() (solution, solution_dot) = (block_w, block_w_dot) solver = TimeStepping(problem_wrapper, solution, solution_dot) solver.set_parameters({ "initial_time": t_start, "time_step_size": dt, "monitor": { "time_step_size": monitor_dt, }, "final_time": T, "exact_final_time": "stepover", "integrator_type": integrator_type, "problem_type": "linear", "linear_solver": "mumps", "report": True }) export_solution = solver.solve() return export_solution, T