def SecondPiolaStress(self, F, p=None, deviatoric=False): import dolfin from pulse import kinematics material = self.material I = kinematics.SecondOrderIdentity(F) f0 = material.f0 f0f0 = dolfin.outer(f0, f0) I1 = dolfin.variable(material.active.I1(F)) I4f = dolfin.variable(material.active.I4(F)) Fe = material.active.Fe(F) Fa = material.active.Fa Ce = Fe.T * Fe # fe = Fe*f0 # fefe = dolfin.outer(fe, fe) # Elastic volume ratio J = dolfin.variable(dolfin.det(Fe)) # Active volume ration Ja = dolfin.det(Fa) dim = self.geometry.dim() Ce_bar = pow(J, -2.0 / float(dim)) * Ce w1 = material.W_1(I1, diff=1, dim=dim) w4f = material.W_4(I4f, diff=1) # Total Stress S_bar = Ja * (2 * w1 * I + 2 * w4f * f0f0) * dolfin.inv(Fa).T if material.is_isochoric: # Deviatoric Dev_S_bar = S_bar - (1.0 / 3.0) * dolfin.inner( S_bar, Ce_bar) * dolfin.inv(Ce_bar) S_mat = J**(-2.0 / 3.0) * Dev_S_bar else: S_mat = S_bar # Volumetric if p is None or deviatoric: S_vol = dolfin.zero((dim, dim)) else: psi_vol = material.compressibility(p, J) S_vol = J * dolfin.diff(psi_vol, J) * dolfin.inv(Ce) # Active stress wactive = material.active.Wactive(F, diff=1) eta = material.active.eta S_active = wactive * (f0f0 + eta * (I - f0f0)) S = S_mat + S_vol + S_active return S
def _build_residuals(V, dx, phi, omega, Mu, Sigma, convections, voltages): #class OuterBoundary(SubDomain): # def inside(self, x, on_boundary): # return on_boundary and abs(x[0]) > DOLFIN_EPS #boundaries = FacetFunction('size_t', mesh) #boundaries.set_all(0) #outer_boundary = OuterBoundary() #outer_boundary.mark(boundaries, 1) #ds = Measure('ds')[boundaries] r = Expression('x[0]', degree=1, domain=V.mesh()) subdomain_indices = Mu.keys() #u = TrialFunction(V) v = TestFunction(V) r_r = zero() * dx(0) for i in subdomain_indices: r_r += 1.0 / (Mu[i] * r) * dot(grad(r * phi[0]), grad(r * v)) * 2 * pi * dx(i) \ - omega * Sigma[i] * phi[1] * v * 2 * pi * r * dx(i) # convections for i, conv in convections.items(): r_r += dot(conv, grad(r * phi[0])) * v * 2 * pi * dx(i) # rhs for i, voltage in voltages.items(): r_r -= Sigma[i] * voltage.real * v * dx(i) ## boundaries #r_r += 1.0/Mu[i] * phi[0] * v * 2*pi*ds(1) # imaginary part r_i = zero() * dx(0) for i in subdomain_indices: r_i += 1.0 / (Mu[i] * r) * dot(grad(r * phi[1]), grad(r * v)) * 2 * pi * dx(i) \ + omega * Sigma[i] * phi[0] * v * 2 * pi * r * dx(i) # convections for i, conv in convections.items(): r_i += dot(conv, grad(r * phi[1])) * v * 2 * pi * dx(i) # rhs for i, voltage in voltages.items(): r_r -= Sigma[i] * voltage.imag * v * dx(i) ## boundaries #r_i += 1.0/Mu[i] * phi[1] * v * 2*pi*ds(1) return r_r, r_i
def _residual_strong(dx, v, phi, mu, sigma, omega, conv, voltages): '''Get the residual in strong form, projected onto V. ''' r = Expression('x[0]', degree=1, cell=triangle) R = [zero() * dx(0), zero() * dx(0)] subdomain_indices = mu.keys() for i in subdomain_indices: # diffusion, reaction R_r = - div(1 / (mu[i] * r) * grad(r * phi[0])) \ - sigma[i] * omega * phi[1] R_i = - div(1 / (mu[i] * r) * grad(r * phi[1])) \ + sigma[i] * omega * phi[0] # convection if i in conv: R_r += dot(conv[i], 1 / r * grad(r * phi[0])) R_i += dot(conv[i], 1 / r * grad(r * phi[1])) # right-hand side if i in voltages: R_r -= sigma[i] * voltages[i].real / (2 * pi * r) R_i -= sigma[i] * voltages[i].imag / (2 * pi * r) R[0] += R_r * v * dx(i) R[1] += R_i * v * dx(i) return R
def form_rhs(self, state, w): """ Returns the right-hand-side contribution of the effective field for Alouges-type integration schemes. *Arguments* state (:class:`State`) The simulation state. w (:class:`dolfin.TestFunction`) The test function used in the Alouges integrator. *Returns* :class:`dolfin.Form` the form contribution for the RHS """ return zero()
def form_lhs(self, state, w, dt_v): """ Returns the left-hand-side contribution of the effective field for Alouges-type integration schemes. *Arguments* state (:class:`State`) The simulation state. w (:class:`dolfin.TestFunction`) The test function used in the Alouges integrator. dt_v (:class:`dolfin.TrialFunction`) The trial function multiplied with the timestep and theta. *Returns* :class:`dolfin.Form` the form contribution for the LHS """ return zero()
def _error_estimator(dx, phi, mu, sigma, omega, conv, voltages): '''Simple error estimator from A posteriori error estimation and adaptive mesh-refinement techniques; R. Verfürth; Journal of Computational and Applied Mathematics; Volume 50, Issues 1-3, 20 May 1994, Pages 67-83; <https://www.sciencedirect.com/science/article/pii/0377042794902909>. The strong PDE is - div(1/(mu r) grad(rphi)) + <u, 1/r grad(rphi)> + i sigma omega phi = sigma v_k / (2 pi r). ''' from dolfin import cells mesh = phi.function_space().mesh() # Assemble the cell-wise residual in DG space DG = FunctionSpace(mesh, 'DG', 0) # get residual in DG v = TestFunction(DG) R = _residual_strong(dx, v, phi, mu, sigma, omega, conv, voltages) r_r = assemble(R[0]) r_i = assemble(R[1]) r = r_r * r_r + r_i * r_i visualize = True if visualize: # Plot the cell-wise residual u = TrialFunction(DG) a = zero() * dx(0) subdomain_indices = mu.keys() for i in subdomain_indices: a += u * v * dx(i) A = assemble(a) R2 = Function(DG) solve(A, R2.vector(), r) plot(R2, title='||R||^2') interactive() K = r.array() info('%r' % K) h = numpy.array([c.diameter() for c in cells(mesh)]) eta = h * numpy.sqrt(K) return eta
def solve_maxwell(V, dx, Mu, Sigma, # dictionaries omega, f_list, # list of dictionaries convections, # dictionary bcs=None, tol=1.0e-12, compute_residuals=True, verbose=False ): '''Solve the complex-valued time-harmonic Maxwell system in 2D cylindrical coordinates. :param V: function space for potentials :param dx: measure :param omega: current frequency :type omega: float :param f_list: list of right-hand sides :param convections: convection terms by subdomains :type convections: dictionary :param bcs: Dirichlet boundary conditions :param tol: solver tolerance :type tol: float :param verbose: solver verbosity :type verbose: boolean :rtype: list of functions ''' # For the exact solution of the magnetic scalar potential, see # <http://www.physics.udel.edu/~jim/PHYS809_10F/Class_Notes/Class_26.pdf>. # Here, the value of \phi along the rotational axis is specified as # # phi(z) = 2 pi I / c * (z/|z| - z/sqrt(z^2 + a^2)) # # where 'a' is the radius of the coil. This expression contradicts what is # specified by [Chaboudez97]_ who claim that phi=0 is the natural value # at the symmetry axis. # # For more analytic expressions, see # # Simple Analytic Expressions for the Magnetic Field of a Circular # Current Loop; # James Simpson, John Lane, Christopher Immer, and Robert Youngquist; # <http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/20010038494_2001057024.pdf>. # # Check if boundary conditions on phi are explicitly provided. if not bcs: # Create Dirichlet boundary conditions. # In the cylindrically symmetric formulation, the magnetic vector # potential is given by # # A = e^{i omega t} phi(r,z) e_{theta}. # # It is natural to demand phi=0 along the symmetry axis r=0 to avoid # discontinuities there. # Also, this makes sure that the system is well-defined (see comment # below). # def xzero(x, on_boundary): return on_boundary and abs(x[0]) < DOLFIN_EPS bcs = DirichletBC(V * V, (0.0, 0.0), xzero) # # Concerning the boundary conditions for the rest of the system: # At the other boundaries, it is not uncommon (?) to set so-called # impedance boundary conditions; see, e.g., # # Chaboudez et al., # Numerical Modeling in Induction Heating for Axisymmetric # Geometries, # IEEE Transactions on Magnetics, vol. 33, no. 1, Jan 1997, # <http://www.esi-group.com/products/casting/publications/Articles_PDF/InductionaxiIEEE97.pdf>. # # or # # <ftp://ftp.math.ethz.ch/pub/sam-reports/reports/reports2010/2010-39.pdf>. # # TODO review those, references don't seem to be too accurate # Those translate into Robin-type boundary conditions (and are in fact # sometimes called that, cf. # https://en.wikipedia.org/wiki/Robin_boundary_condition). # The classical reference is # # Impedance boundary conditions for imperfectly conducting # surfaces, # T.B.A. Senior, # <http://link.springer.com/content/pdf/10.1007/BF02920074>. # #class OuterBoundary(SubDomain): # def inside(self, x, on_boundary): # return on_boundary and abs(x[0]) > DOLFIN_EPS #boundaries = FacetFunction('size_t', mesh) #boundaries.set_all(0) #outer_boundary = OuterBoundary() #outer_boundary.mark(boundaries, 1) #ds = Measure('ds')[boundaries] ##n = FacetNormal(mesh) ##a += - 1.0/Mu[i] * dot(grad(r*ur), n) * vr * ds(1) \ ## - 1.0/Mu[i] * dot(grad(r*ui), n) * vi * ds(1) ##L += - 1.0/Mu[i] * 1.0 * vr * ds(1) \ ## - 1.0/Mu[i] * 1.0 * vi * ds(1) ## This is -n.grad(r u) = u: #a += 1.0/Mu[i] * ur * vr * ds(1) \ # + 1.0/Mu[i] * ui * vi * ds(1) # Create the system matrix, preconditioner, and the right-hand sides. # For preconditioners, there are two approaches. The first one, described # in # # Algebraic Multigrid for Complex Symmetric Systems; # D. Lahaye, H. De Gersem, S. Vandewalle, and K. Hameyer; # <https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=877730> # # doesn't work too well here. # The matrix P, created in _build_system(), provides a better alternative. # For more details, see documentation in _build_system(). # A, P, b_list, M, W = _build_system(V, dx, Mu, Sigma, # dictionaries omega, f_list, # list of dicts convections, # dict bcs ) #from matplotlib import pyplot as pp #rows, cols, values = M.data() #from scipy.sparse import csr_matrix #M_matrix = csr_matrix((values, cols, rows)) ##from matplotlib import pyplot as pp ###pp.spy(M_matrix, precision=1e-3, marker='.', markersize=5) ##pp.spy(M_matrix) ##pp.show() ## colormap #cmap = pp.cm.gray_r #M_dense = M_matrix.todense() #from matplotlib.colors import LogNorm #im = pp.imshow(abs(M_dense), cmap=cmap, interpolation='nearest', norm=LogNorm()) ##im = pp.imshow(abs(M_dense), cmap=cmap, interpolation='nearest') ##im = pp.imshow(abs(A_r), cmap=cmap, interpolation='nearest') ##im = pp.imshow(abs(A_i), cmap=cmap, interpolation='nearest') #pp.colorbar() #pp.show() #exit() #print A #rows, cols, values = A.data() #from scipy.sparse import csr_matrix #A_matrix = csr_matrix((values, cols, rows)) ###pp.spy(A_matrix, precision=1e-3, marker='.', markersize=5) ##pp.spy(A_matrix) ##pp.show() ## colormap #cmap = pp.cm.gray_r #A_dense = A_matrix.todense() ##A_r = A_dense[0::2][0::2] ##A_i = A_dense[1::2][0::2] #cmap.set_bad('r') ##im = pp.imshow(abs(A_dense), cmap=cmap, interpolation='nearest', norm=LogNorm()) #im = pp.imshow(abs(A_dense), cmap=cmap, interpolation='nearest') ##im = pp.imshow(abs(A_r), cmap=cmap, interpolation='nearest') ##im = pp.imshow(abs(A_i), cmap=cmap, interpolation='nearest') #pp.colorbar() #pp.show() # prepare solver solver = KrylovSolver('gmres', 'amg') solver.set_operators(A, P) # The PDE for A has huge coefficients (order 10^8) all over. Hence, if # relative residual is set to 10^-6, the actual residual will still be of # the order 10^2. While this isn't too bad (after all the equations are # upscaled by a large factor), one can choose a very small relative # tolerance here to get a visually pleasing residual norm. solver.parameters['relative_tolerance'] = 1.0e-12 solver.parameters['absolute_tolerance'] = 0.0 solver.parameters['maximum_iterations'] = 100 solver.parameters['report'] = verbose solver.parameters['monitor_convergence'] = verbose phi_list = [] for k, b in enumerate(b_list): with Message('Computing coil ring %d/%d...' % (k + 1, len(b_list))): # Define goal functional for adaptivity. # Adaptivity not working for subdomains, cf. # https://bugs.launchpad.net/dolfin/+bug/872105. #(phi_r, phi_i) = split(phi) #M = (phi_r*phi_r + phi_i*phi_i) * dx(2) phi_list.append(Function(W)) phi_list[-1].rename('phi%d' % k, 'phi%d' % k) solver.solve(phi_list[-1].vector(), b) ## Adaptive mesh refinement. #_adaptive_mesh_refinement(dx, # phi_list[-1], # Mu, Sigma, omega, # convections, # f_list[k] # ) #exit() if compute_residuals: # Sanity check: Compute residuals. # This is quite the good test that we haven't messed up # real/imaginary in the above formulation. r_r, r_i = _build_residuals(V, dx, phi_list[-1], omega, Mu, Sigma, convections, voltages ) def xzero(x, on_boundary): return on_boundary and abs(x[0]) < DOLFIN_EPS subdomain_indices = Mu.keys() # Solve an FEM problem to get the corresponding residual function # out. # This is exactly what we need here! :) u = TrialFunction(V) v = TestFunction(V) a = zero() * dx(0) for i in subdomain_indices: a += u * v * dx(i) # TODO don't hard code the boundary conditions like this R_r = Function(V) solve(a == r_r, R_r, bcs=DirichletBC(V, 0.0, xzero) ) # TODO don't hard code the boundary conditions like this R_i = Function(V) solve(a == r_i, R_i, bcs=DirichletBC(V, 0.0, xzero) ) nrm_r = norm(R_r) info('||r_r|| = %e' % nrm_r) nrm_i = norm(R_i) info('||r_i|| = %e' % nrm_i) res_norm = sqrt(nrm_r * nrm_r + nrm_i * nrm_i) info('||r|| = %e' % res_norm) plot(R_r, title='R_r') plot(R_i, title='R_i') interactive() #exit() return phi_list