Exemplo n.º 1
0
    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
Exemplo n.º 4
0
  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()
Exemplo n.º 5
0
    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()
Exemplo n.º 6
0
  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()
Exemplo n.º 7
0
    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