def assemble_and_block_assemble_vector(block_form): N = len(block_form) assert N in (1, 2) if N == 1: return assemble(block_form[0]), block_assemble(block_form) else: return (assemble(block_form[0]), assemble(block_form[1])), block_assemble(block_form)
def assemble_and_block_assemble_matrix(block_form): N = len(block_form) assert N in (1, 2) M = len(block_form[0]) assert M == N if N == 1: return assemble(block_form[0][0]), block_assemble(block_form) else: return ((assemble(block_form[0][0]), assemble(block_form[0][1])), (assemble(block_form[1][0]), assemble(block_form[1][1]))), block_assemble(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 assembly(self, entity, bcs=[]): if self.split: entity = assemble(entity) else: entity = block_assemble(entity) if bcs: self.apply(entity, bcs) return entity
def computeTangent(self): dy = df.Measure('dx', self.mesh) vol = df.assemble(df.Constant(1.0) * dy) y = df.SpatialCoordinate(self.mesh) Eps = df.Constant(((0., 0.), (0., 0.))) # just placeholder form = self.multiscaleModel(self.mesh, self.sigmaLaw, Eps, self.others) a, f, bcs, W = form() start = timer() A = mp.block_assemble(a) if (len(bcs) > 0): bcs.apply(A) # decompose just once (the faster for single process) solver = df.PETScLUSolver('superlu') sol = mp.BlockFunction(W) end = timer() print('time assembling system', end - start) for i in range(self.nvoigt): start = timer() Eps.assign(df.Constant(macro_strain(i))) F = mp.block_assemble(f) if (len(bcs) > 0): bcs.apply(F) solver.solve(A, sol.block_vector(), F) sol.block_vector().block_function().apply("to subfunctions") sig_mu = self.sigmaLaw(df.dot(Eps, y) + sol[0]) sigma_hom = Integral(sig_mu, dy, (2, 2)) / vol self.Chom_[:, i] = sigma_hom.flatten()[[0, 3, 1]] end = timer() print('time in solving system', end - start) print(self.Chom_) # from the second run onwards, just returns self.getTangent = self.getTangent_ return self.Chom_
def get_tangent(self): if self.Chom_ is None: self.Chom_ = np.zeros((self.nvoigt, self.nvoigt)) dy = ufl.Measure("dx", self.mesh) vol = df.assemble(df.Constant(1.0) * dy) y = ufl.SpatialCoordinate(self.mesh) Eps = df.Constant(((0., 0.), (0., 0.))) # just placeholder form = self.multiscale_model(self.mesh, self.sigma_law, Eps, self.others) a, f, bcs, W = form() start = timer() A = mp.block_assemble(a) if len(bcs) > 0: bcs.apply(A) # decompose just once, since the matrix A is the same in every solve solver = df.PETScLUSolver("superlu") sol = mp.BlockFunction(W) end = timer() print("time assembling system", end - start) for i in range(self.nvoigt): start = timer() Eps.assign(df.Constant(self.macro_strain(i))) F = mp.block_assemble(f) if len(bcs) > 0: bcs.apply(F) solver.solve(A, sol.block_vector(), F) sol.block_vector().block_function().apply("to subfunctions") sig_mu = self.sigma_law(df.dot(Eps, y) + sol[0]) sigma_hom = self.integrate(sig_mu, dy, (2, 2)) / vol self.Chom_[:, i] = sigma_hom.flatten()[[0, 3, 1]] end = timer() print("time in solving system", end - start) return self.Chom_
def block_project(u, mesh, restriction, subdomain_data, project_id, **kwargs): """ Implement the FEniCS' project function for multiphenics. This method allows to extract and project the solution of implicit FEniCS functions into user-friendly functions, and then extract the solution of a specific subdomain (up to know subdomains of dimensions 1 or 2 are accepted). Applications of this method may be to extract the gradient of a function (when using FEniCS grad function) and get the solution in/on a desired subdomain, or do the same with the dot product when using FEniCS dot (or inner) function. The implemented method is the same one used when obtaining the weak form of a PDE. That is, we impose: f = u, where u is the function containing the desired data and f is the trial function. Then, we multiply by a test function and integrate. In that way, we are projecting the solution into a function that can be easily used for post-processing. Parameters ---------- u: ufl.tensor... Tensor containing the desired solution. mesh: cpp.mesh.Mesh Mesh object. restriction: mesh.mesh_restriction.MeshRestriction Subdomain restriction. subdomain_data: cpp.mesh.MeshFunctionSizet Contains the all information of the subdomain. project_id: int Indentifier of the place (in the subdomain_data) where the solution is desired. **kwargs: Accepted kwargs are space_type, boundary_type sign and restricted. The space_type identifies the type of Function Space in which we want to project the solution into. It can be scalar (for scalar fields) or vectorial (for vectorial fields). The boundary_type is only required when subdomains of dimension 1 are introduced, and it is used to specify the type of facets (they can be internal or external). The sign is only required when subdomains of dimension 1 (boundaries) are introduced. It is used to specify the sign to evaluate the line integral For example, let us consider two subdomains whose ids are 0 and 1, respectively. To indicate that we are integrating quantities on subdomain 0, we should introduce '-' as sign, because it is the the subdomain with the lower id. Thus, for subdomain 1, we should introduce '+' as sign, which is the default one. The restricted kwarg indicates if the function to be projected is already restricted. This should be taken into account because UFL cannot restrict an expression twice. Raises ------ TypeError This error will rise when the specified type of function space is unknown. NotImplementedError This error will raise when the dimension of the introduced subdomain is greater than 2. Returns ------- sol: dolfin.function.function.Function Dolfin Function which can be easily used for post-processing tasks. """ if 'space_type' not in kwargs: print( "** WARNING: No Function Space type was specified. Assuming scalar space.", flush=True) if subdomain_data.dim() == 1 and 'boundary_type' not in kwargs: print( "** WARNING: No boundary type specified. Assuming internal type.", flush=True) kwargs.setdefault('space_type', 'scalar') kwargs.setdefault('boundary_type', 'internal') kwargs.setdefault('sign', "+") kwargs.setdefault('restricted', False) kwargs.setdefault('function_space_degree', 2) # Create a Block Function Space. function_space_degree = kwargs.get('function_space_degree') if kwargs['space_type'] == 'scalar': aux_space = mp.BlockFunctionSpace( [fn.FunctionSpace(mesh, 'CG', function_space_degree)], restrict=[restriction]) elif kwargs['space_type'] == 'vectorial': aux_space = mp.BlockFunctionSpace( [fn.VectorFunctionSpace(mesh, 'CG', function_space_degree)], restrict=[restriction]) else: raise TypeError( f"Unknown type of Function Space: {kwargs['space_type']}") # Define the trial and test functions. trial, = mp.BlockTrialFunction(aux_space) test, = mp.BlockTestFunction(aux_space) # Define the measure of the subdomain and the variational problem. if subdomain_data.dim() == 2: dom_measure = fn.Measure('dx')(subdomain_data=subdomain_data) lhs = [[fn.inner(trial, test) * dom_measure(project_id)]] rhs = [fn.inner(u, test) * dom_measure(project_id)] elif subdomain_data.dim() == 1: if kwargs['boundary_type'] == 'internal': dom_measure = fn.Measure('dS')(subdomain_data=subdomain_data) elif kwargs['boundary_type'] == 'external': dom_measure = fn.Measure('ds')(subdomain_data=subdomain_data) # Check if the interface exists. assert fn.assemble( 1 * dom_measure(domain=mesh) ) > 0., "The length of the interface is zero, wrong marking." lhs = [[ fn.inner(trial, test)(kwargs['sign']) * dom_measure(project_id) ]] if not kwargs.get('restricted'): rhs = [ fn.inner(u, test)(kwargs['sign']) * dom_measure(project_id) ] else: rhs = [ fn.inner(u, test(kwargs['sign'])) * dom_measure(project_id) ] else: raise NotImplementedError( f"Domains of dimension {subdomain_data.dim()} are not supported." ) # Define the variational form and solve. LHS = mp.block_assemble(lhs) RHS = mp.block_assemble(rhs) sol = mp.BlockFunction(aux_space) mp.block_solve(LHS, sol.block_vector(), RHS) return sol[0]
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 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
dot(avg_w(rho * k / vis * grad(p_con), weight_e(k, n)), jump(con, n)) * dS + penalty1 / h_avg * avg(rho) * k_e(k, n) / avg(vis) * dot(jump(p_con, n), jump(con, n)) * dS - dot(rho * k / vis * grad(p_con), n) * con * ds(2) + penalty2 / h * rho / vis * dot(dot(n, k), n) * (p_con - p_D1) * con * ds(2)) if MPI.size(MPI.comm_world) == 1: xdmu = XDMFFile("biot_" + discretization + "_u.xdmf") xdmp = XDMFFile("biot_" + discretization + "_p.xdmf") print("Discretization:", discretization) while t < T: t += dt print("\tsolving at time", t) AA = block_assemble(lhs) FF = block_assemble(rhs) bcs.apply(AA) bcs.apply(FF) block_solve(AA, w.block_vector(), FF, "mumps") block_assign(w0, w) xdmu.write(w[0], t) xdmp.write(w[1], t) u_con.assign(w[0]) p_con.assign(w[1]) mass = assemble(mass_con) print("\taverage mass residual: ", np.average(np.abs(mass[:]))) if discretization == "DG": assert np.isclose(w[0].vector().norm("l2"), 0.019389822)
def computeTangent(self): self.readMesh() sigmaLaw = lambda u: self.lame[0] * nabla_div(u) * df.Identity( 2) + 2 * self.lame[1] * symgrad(u) dy = self.mesh.dx # specially for the case of enriched mesh, otherwise it does not work vol = df.assemble(df.Constant(1.0) * dy(0)) + df.assemble( df.Constant(1.0) * dy(1)) y = df.SpatialCoordinate(self.mesh) Eps = df.Constant(((0., 0.), (0., 0.))) # just placeholder form = self.multiscaleModel(self.mesh, sigmaLaw, Eps, self.others) a, f, bcs, W = form() start = timer() A = mp.block_assemble(a) if (len(bcs) > 0): bcs.apply(A) solver = df.PETScLUSolver('superlu') sol = mp.BlockFunction(W) if (self.model == 'lin' or self.model == 'dnn'): Vref = self.others['uD'].function_space() Mref = Vref.mesh() normal = df.FacetNormal(Mref) volMref = 4.0 B = np.zeros((2, 2)) for i in range(self.nvoigt): start = timer() if (self.model == 'lin' or self.model == 'dnn'): self.others['uD'].vector().set_local( self.others['uD{0}_'.format(i)]) B = -feut.Integral(df.outer(self.others['uD'], normal), Mref.ds, (2, 2)) / volMref T = feut.affineTransformationExpression( np.zeros(2), B, Mref) # ignore a, since the basis is already translated self.others['uD'].vector().set_local( self.others['uD'].vector().get_local()[:] + df.interpolate(T, Vref).vector().get_local()[:]) Eps.assign(df.Constant(mscm.macro_strain(i) - B)) F = mp.block_assemble(f) if (len(bcs) > 0): bcs.apply(F) solver.solve(A, sol.block_vector(), F) sol.block_vector().block_function().apply("to subfunctions") sig_mu = sigmaLaw(df.dot(Eps, y) + sol[0]) sigma_hom = sum([Integral(sig_mu, dy(i), (2, 2)) for i in [0, 1]]) / vol self.Chom_[:, i] = sigma_hom.flatten()[[0, 3, 1]] end = timer() print('time in solving system', end - start) # Time in seconds self.getTangent = self.getTangent_ # from the second run onwards, just returns return self.Chom_