Esempio n. 1
0
 def solve_problem_variational_form(self):
     u = fa.Function(self.V)
     du = fa.TrialFunction(self.V)
     v = fa.TestFunction(self.V)
     E = self._energy_density(u) * fa.dx
     dE = fa.derivative(E, u, v)
     jacE = fa.derivative(dE, u, du)
     fa.solve(dE == 0, u, self.bcs, J=jacE)
     return u
Esempio n. 2
0
def vjp_solve_eval_impl(
    g: np.array,
    fenics_solution: fenics.Function,
    fenics_residual: ufl.Form,
    fenics_inputs: List[FenicsVariable],
    bcs: List[fenics.DirichletBC],
) -> Tuple[np.array]:
    """Computes the gradients of the output with respect to the inputs."""
    # Convert tangent covector (adjoint) to a FEniCS variable
    adj_value = numpy_to_fenics(g, fenics_solution)
    adj_value = adj_value.vector()

    F = fenics_residual
    u = fenics_solution
    V = u.function_space()
    dFdu = fenics.derivative(F, u)
    adFdu = ufl.adjoint(
        dFdu, reordered_arguments=ufl.algorithms.extract_arguments(dFdu)
    )

    u_adj = fenics.Function(V)
    adj_F = ufl.action(adFdu, u_adj)
    adj_F = ufl.replace(adj_F, {u_adj: fenics.TrialFunction(V)})
    adj_F_assembled = fenics.assemble(adj_F)

    if len(bcs) != 0:
        for bc in bcs:
            bc.homogenize()
        hbcs = bcs

        for bc in hbcs:
            bc.apply(adj_F_assembled)
            bc.apply(adj_value)

    fenics.solve(adj_F_assembled, u_adj.vector(), adj_value)

    fenics_grads = []
    for fenics_input in fenics_inputs:
        if isinstance(fenics_input, fenics.Function):
            V = fenics_input.function_space()
        dFdm = fenics.derivative(F, fenics_input, fenics.TrialFunction(V))
        adFdm = fenics.adjoint(dFdm)
        result = fenics.assemble(-adFdm * u_adj)
        if isinstance(fenics_input, fenics.Constant):
            fenics_grad = fenics.Constant(result.sum())
        else:  # fenics.Function
            fenics_grad = fenics.Function(V, result)
        fenics_grads.append(fenics_grad)

    # Convert FEniCS gradients to jax array representation
    jax_grads = (
        None if fg is None else np.asarray(fenics_to_numpy(fg)) for fg in fenics_grads
    )

    jax_grad_tuple = tuple(jax_grads)

    return jax_grad_tuple
Esempio n. 3
0
    def __compute_shape_derivative(self):
        """Computes the shape derivative.

		Returns
		-------
		None

		Notes
		-----
		This only works properly if differential operators only
		act on state and adjoint variables, else the results are incorrect.
		A corresponding warning whenever this could be the case is issued.
		"""

        # Shape derivative of Lagrangian w/o regularization and pull-backs
        self.shape_derivative = fenics.derivative(
            self.lagrangian.lagrangian_form,
            fenics.SpatialCoordinate(self.mesh), self.test_vector_field)

        # Add pull-backs
        if self.use_pull_back:
            self.state_adjoint_ids = [coeff.id() for coeff in self.states] + [
                coeff.id() for coeff in self.adjoints
            ]

            self.material_derivative_coeffs = []
            for coeff in self.lagrangian.lagrangian_form.coefficients():
                if coeff.id() in self.state_adjoint_ids:
                    pass
                else:
                    if not (coeff.ufl_element().family() == 'Real'):
                        self.material_derivative_coeffs.append(coeff)

            if len(self.material_derivative_coeffs) > 0:
                warning(
                    'Shape derivative might be wrong, if differential operators act on variables other than states and adjoints. \n'
                    'You can check for correctness of the shape derivative with cashocs.verification.shape_gradient_test\n'
                )

            for coeff in self.material_derivative_coeffs:
                # temp_space = fenics.FunctionSpace(self.mesh, coeff.ufl_element())
                # placeholder = fenics.Function(temp_space)
                # temp_form = fenics.derivative(self.lagrangian.lagrangian_form, coeff, placeholder)
                # material_derivative = replace(temp_form, {placeholder : fenics.dot(fenics.grad(coeff), self.test_vector_field)})

                material_derivative = fenics.derivative(
                    self.lagrangian.lagrangian_form, coeff,
                    fenics.dot(fenics.grad(coeff), self.test_vector_field))
                material_derivative = expand_derivatives(material_derivative)

                self.shape_derivative += material_derivative

        # Add regularization
        self.shape_derivative += self.regularization.compute_shape_derivative()
Esempio n. 4
0
    def __compute_state_equations(self):
        """Calculates the weak form of the state equation for the use with fenics.

		Returns
		-------
		None
		"""

        if self.state_is_linear:
            self.state_eq_forms = [
                replace(
                    self.state_forms[i], {
                        self.states[i]: self.trial_functions_state[i],
                        self.adjoints[i]: self.test_functions_state[i]
                    }) for i in range(self.state_dim)
            ]

        else:
            self.state_eq_forms = [
                fenics.derivative(self.state_forms[i], self.adjoints[i],
                                  self.test_functions_state[i])
                for i in range(self.state_dim)
            ]

        if self.state_is_picard:
            self.state_picard_forms = [
                fenics.derivative(self.state_forms[i], self.adjoints[i],
                                  self.test_functions_state[i])
                for i in range(self.state_dim)
            ]

        if self.state_is_linear:
            self.state_eq_forms_lhs = []
            self.state_eq_forms_rhs = []
            for i in range(self.state_dim):
                try:
                    a, L = fenics.system(self.state_eq_forms[i])
                except UFLException:
                    raise CashocsException(
                        'The state system could not be transferred to a linear system.\n'
                        'Perhaps you specified that the system is linear, allthough it is not.\n'
                        'In your config, in the StateEquation section, try using is_linear = False.'
                    )
                self.state_eq_forms_lhs.append(a)
                if L.empty():
                    zero_form = fenics.inner(
                        fenics.Constant(
                            np.zeros(self.test_functions_state[i].ufl_shape)),
                        self.test_functions_state[i]) * self.dx
                    self.state_eq_forms_rhs.append(zero_form)
                else:
                    self.state_eq_forms_rhs.append(L)
    def _solve(self, z, x=None):
        # problem variables
        du = TrialFunction(self.V)                          # incremental displacement
        v = TestFunction(self.V)                            # test function
        u = Function(self.V)                                # displacement from previous iteration

        # kinematics
        ii = Identity(3)                                    # identity tensor dimension 3
        f = ii + grad(u)                                    # deformation gradient
        c = f.T * f                                         # right Cauchy-Green tensor

        # invariants of deformation tensors
        ic = tr(c)
        j = det(f)

        # elasticity parameters
        if type(z) in [list, np.ndarray]:
            param = self.param_remapper(z[0]) if self.param_remapper is not None else z[0]
        else:
            param = self.param_remapper(z) if self.param_remapper is not None else z

        e_var = variable(Constant(param))                   # Young's modulus
        nu = Constant(.3)                                   # Shear modulus (Lamè's second parameter)
        mu, lmbda = e_var / (2 * (1 + nu)), e_var * nu / ((1 + nu) * (1 - 2 * nu))

        # strain energy density, total potential energy
        psi = (mu / 2) * (ic - 3) - mu * ln(j) + (lmbda / 2) * (ln(j)) ** 2
        pi = psi * dx - self.time * dot(self.f, u) * self.ds(3)

        ff = derivative(pi, u, v)                           # compute first variation of pi
        jj = derivative(ff, u, du)                          # compute jacobian of f

        # solving
        if x is not None:
            numeric_evals = np.zeros(shape=(x.shape[1], len(self.times)))
            evals = np.zeros(shape=(x.shape[1], len(self.eval_times)))
        else:
            numeric_evals = None
            evals = None
        for it, t in enumerate(self.times):
            self.time.t = t
            self.solver(ff == 0, u, self.bcs, J=jj, bcs=self.bcs, solver_parameters=self.solver_parameters)
            if x is not None:
                numeric_evals[:, it] = np.log(np.array([-u(x_)[2] for x_ in x.T]).T)

        # time-interpolation
        if x is not None:
            for i in range(evals.shape[0]):
                evals[i, :] = np.interp(self.eval_times, self.times, numeric_evals[i, :])
        return (evals, u) if x is not None else u
 def effective_field(m, volume=None):
     w_Zeeman = - mu0 * Ms * fe.dot(m, H)
     w_exchange = A  * fe.inner(fe.grad(m), fe.grad(m))
     w_DMI = D * fe.inner(m, fe.curl(m))
     w_ani = - K * fe.inner(m, ea)**2
     w = w_Zeeman + w_exchange + w_DMI + w_ani
     return -1/(mu0*Ms) * fe.derivative(w*fe.dx, m)
Esempio n. 7
0
def jvp_assemble_eval(
    fenics_function: Callable,
    fenics_templates: Iterable[FenicsVariable],
    primals: Tuple[np.array],
    tangents: Tuple[np.array],
) -> Tuple[np.array]:
    """Computes the jacobian-vector product for fenics.assemble
    """

    numpy_output_primal, output_primal_form, fenics_primals = assemble_eval(
        fenics_function, fenics_templates, *primals)

    # Now tangent evaluation!
    fenics_tangents = convert_all_to_fenics(fenics_primals, *tangents)
    output_tangent_form = 0.0
    for fp, ft in zip(fenics_primals, fenics_tangents):
        output_tangent_form += fenics.derivative(output_primal_form, fp, ft)

    if not isinstance(output_tangent_form, float):
        output_tangent_form = ufl.algorithms.expand_derivatives(
            output_tangent_form)
        output_tangent = fenics.assemble(output_tangent_form)

    jax_output_tangent = output_tangent

    return numpy_output_primal, jax_output_tangent
Esempio n. 8
0
def vjp_assemble_impl(
    g: np.array,
    fenics_output_form: ufl.Form,
    fenics_inputs: List[FenicsVariable],
) -> Tuple[np.array]:
    """Computes the gradients of the output with respect to the inputs."""

    # Compute derivative form for the output with respect to each input
    fenics_grads_forms = []
    for fenics_input in fenics_inputs:
        # Need to construct direction (test function) first
        if isinstance(fenics_input, fenics.Function):
            V = fenics_input.function_space()
        elif isinstance(fenics_input, fenics.Constant):
            mesh = fenics_output_form.ufl_domain().ufl_cargo()
            V = fenics.FunctionSpace(mesh, "Real", 0)
        else:
            raise NotImplementedError

        dv = fenics.TestFunction(V)
        fenics_grad_form = fenics.derivative(fenics_output_form, fenics_input,
                                             dv)
        fenics_grads_forms.append(fenics_grad_form)

    # Assemble the derivative forms
    fenics_grads = [fenics.assemble(form) for form in fenics_grads_forms]

    # Convert FEniCS gradients to jax array representation
    jax_grads = (None if fg is None else np.asarray(g * fenics_to_numpy(fg))
                 for fg in fenics_grads)

    jax_grad_tuple = tuple(jax_grads)

    return jax_grad_tuple
def Estimate(Ic, J, u, v, du, BoundaryConditions, InitialState, u_1):
    
    # Use Neo-Hookean constitutive model
    Nu_H   = 0.49                         # (-)
    Mu_NH  = 1.15                         # (kPa)
    Lambda = 2*Mu_NH*Nu_H/(1-2*Nu_H)      # (kPa)
    Psi    = CompressibleNeoHookean(Mu_NH, Lambda, Ic, J)
    Pi = Psi * fe.dx
    
    # First directional derivative of the potential energy
    Fpi = fe.derivative(Pi,u,v)

    # Jacobian of Fpi
    Jac = fe.derivative(Fpi,u,du)
    
    # Define option for the compiler (optional)
    ffc_options = {"optimize": True, \
                   "eliminate_zeros": True, \
                   "precompute_basis_const": True, \
                   "precompute_ip_const": True, \
                   "quadrature_degree": 2, \
                   "representation" : "uflacs" }
    
    # Define the problem
    Problem = fe.NonlinearVariationalProblem(Fpi, u, BoundaryConditions, Jac, form_compiler_parameters=ffc_options)

    # Define the solver
    Solver = fe.NonlinearVariationalSolver(Problem)
    
    # Set solver parameters (optional)
    Prm = Solver.parameters
    Prm['nonlinear_solver'] = 'newton'
    Prm['newton_solver']['linear_solver'] = 'cg'             # Conjugate gradient
    Prm['newton_solver']['preconditioner'] = 'icc'           # Incomplete Choleski   
    Prm['newton_solver']['krylov_solver']['nonzero_initial_guess'] = True
    
    # Set initial displacement
    u_1.s = InitialState
    
    # Compute solution and save displacement
    Solver.solve()
    
    return u
Esempio n. 10
0
def jvp_solve_eval(
    fenics_function: Callable,
    fenics_templates: Iterable[FenicsVariable],
    primals: Tuple[np.array],
    tangents: Tuple[np.array],
) -> Tuple[np.array]:
    """Computes the tangent linear model
    """

    (
        numpy_output_primal,
        fenics_solution_primal,
        residual_form,
        fenics_primals,
        bcs,
    ) = solve_eval(fenics_function, fenics_templates, *primals)

    # Now tangent evaluation!
    F = residual_form
    u = fenics_solution_primal
    V = u.function_space()

    if len(bcs) != 0:
        for bc in bcs:
            bc.homogenize()
    hbcs = bcs

    fenics_tangents = convert_all_to_fenics(fenics_primals, *tangents)
    fenics_output_tangents = []
    for fp, ft in zip(fenics_primals, fenics_tangents):
        dFdu = fenics.derivative(F, u)
        dFdm = fenics.derivative(F, fp, ft)
        u_tlm = fenics.Function(V)
        tlm_F = ufl.action(dFdu, u_tlm) + dFdm
        tlm_F = ufl.replace(tlm_F, {u_tlm: fenics.TrialFunction(V)})
        fenics.solve(ufl.lhs(tlm_F) == ufl.rhs(tlm_F), u_tlm, bcs=hbcs)
        fenics_output_tangents.append(u_tlm)

    jax_output_tangents = (fenics_to_numpy(ft)
                           for ft in fenics_output_tangents)
    jax_output_tangent = sum(jax_output_tangents)

    return numpy_output_primal, jax_output_tangent
Esempio n. 11
0
    def setup_solver(self):
        """ Sometimes it is necessary to set up the solver again after breaking
        important references, e.g. after re-meshing.
        """
        self._governing_form = self.governing_form()

        self._boundary_conditions = self.boundary_conditions()

        self._problem = fenics.NonlinearVariationalProblem(
            F=self._governing_form,
            u=self.solution,
            bcs=self._boundary_conditions,
            J=fenics.derivative(form=self._governing_form, u=self.solution))

        save_parameters = False

        if hasattr(self, "solver"):

            save_parameters = True

        if save_parameters:

            solver_parameters = self.solver.parameters.copy()

        self.solver = fenics.NonlinearVariationalSolver(problem=self._problem)

        if save_parameters:

            self.solver.parameters = solver_parameters.copy()

        self._adaptive_goal = self.adaptive_goal()

        if self._adaptive_goal is not None:

            save_parameters = False

            if self.adaptive_solver is not None:

                save_parameters = True

            if save_parameters:

                adaptive_solver_parameters = self.adaptive_solver.parameters.copy(
                )

            self.adaptive_solver = fenics.AdaptiveNonlinearVariationalSolver(
                problem=self._problem, goal=self._adaptive_goal)

            if save_parameters:

                self.adaptive_solver.parameters = adaptive_solver_parameters.copy(
                )

        self.solver_needs_setup = False
Esempio n. 12
0
    def setup_solver(self):
        F = self.d_LHS - self.d_RHS
        J = fe.derivative(F, self._sp.w)

        # Initialize solver
        problem = fe.NonlinearVariationalProblem(F,
                                                 self._sp.w,
                                                 bcs=self.bcs,
                                                 J=J)
        self.solver = fe.NonlinearVariationalSolver(problem)
        self.solver.parameters['newton_solver']['relative_tolerance'] = 1e-6
Esempio n. 13
0
    def solve_problem_weak_form(self):
        u = fa.Function(self.V)
        du = fa.TrialFunction(self.V)
        v = fa.TestFunction(self.V)
        F = fa.inner(fa.grad(u), fa.grad(v)) * fa.dx - self.source * v * fa.dx
        J = fa.derivative(F, u, du)

        # The problem in this case is indeed linear, but using a nonlinear
        # solver doesn't hurt
        problem = fa.NonlinearVariationalProblem(F, u, self.bcs, J)
        solver = fa.NonlinearVariationalSolver(problem)
        solver.solve()
        return u
Esempio n. 14
0
    def __compute_gradient_equations(self):
        """Calculates the variational form of the gradient equation, for the Riesz projection.

		Returns
		-------
		None
		"""

        self.gradient_forms_rhs = [
            fenics.derivative(self.lagrangian.lagrangian_form,
                              self.controls[i], self.test_functions_control[i])
            for i in range(self.control_dim)
        ]
def solve_fenics(kappa0, kappa1):

    f = fenics.Expression(
        "10*exp(-(pow(x[0] - 0.5, 2) + pow(x[1] - 0.5, 2)) / 0.02)", degree=2)

    u = fenics.Function(V)
    bcs = [fenics.DirichletBC(V, fenics.Constant(0.0), "on_boundary")]

    inner, grad, dx = ufl.inner, ufl.grad, ufl.dx
    JJ = 0.5 * inner(kappa0 * grad(u), grad(u)) * dx - kappa1 * f * u * dx
    v = fenics.TestFunction(V)
    F = fenics.derivative(JJ, u, v)
    fenics.solve(F == 0, u, bcs=bcs)
    return u, F, bcs
Esempio n. 16
0
def solve_steady_state_heiser_weissinger(kappa):

    w = fe.Function(V_up)
    (u, p) = fe.split(w)
    p = fe.variable(p)
    (eta, q) = fe.TestFunctions(V_up)
    dw = fe.TrialFunction(V_up)

    kappa = fe.Constant(kappa)

    bcs_u, bcs_p, bcs_v = load_2d_muscle_bc(V_up.sub(0), V_up.sub(1), None,
                                            boundaries)

    F = deformation_grad(u)
    I_1, I_2, J = invariants(F)
    F_iso = isochronic_deformation_grad(F, J)
    I_1_iso, I_2_iso = invariants(F)[0:2]

    W = material_mooney_rivlin(I_1_iso, I_2_iso, c_10,
                               c_01)  #+ incompr_relaxation(p, kappa)
    g = incompr_constr(J)

    # Lagrange function (without constraint)
    L = -W

    # Modified Lagrange function (with constraints)
    L_mod = L - p * g
    P = first_piola_stress(L, F)
    G = incompr_stress(g, F)  # = J*fe.inv(F.T)

    Lp = const_eq(L_mod, p)

    a_static = weak_div_term(P + p * G,
                             eta) + inner(B, eta) * dx + inner(Lp, q) * dx

    J_static = fe.derivative(a_static, w, dw)
    ffc_options = {"optimize": True}
    problem = fe.NonlinearVariationalProblem(
        a_static,
        w,
        bcs_u + bcs_p,
        J=J_static,
        form_compiler_parameters=ffc_options)
    solver = fe.NonlinearVariationalSolver(problem)

    solver.solve()

    return w
Esempio n. 17
0
    def setup_solver(self):
        F = self.d_LHS - self.d_RHS
        J = fe.derivative(F, self._domain.w)

        # Initialize solver
        problem = fe.NonlinearVariationalProblem(F,
                                                 self._domain.w,
                                                 bcs=self._dbcs,
                                                 J=J)

        self.solver = fe.NonlinearVariationalSolver(problem)
        for key, val in self._solver_parameters.items():

            if isinstance(val, dict):
                for sub_key, sub_val in val.items():
                    self.solver.parameters[key][sub_key] = sub_val

            else:
                self.solver.parameters[key] = val
Esempio n. 18
0
def _create_variational_problem(m0, u0, W, dt):
    """We set up the variational problem."""
    
    p, q = fc.TestFunctions(W)
    w = fc.Function(W)  # Function to solve for
    m, u = fc.split(w)
    # Relabel i.e. initialise m_prev, u_prev as m0, u0.
    m_prev, u_prev = m0, u0
    m_mid = 0.5 * (m + m_prev)
    u_mid = 0.5 * (u + u_prev)
    F = (
        (q * u + q.dx(0) * u.dx(0) - q * m) * fc.dx +                                          # q part
        (p * (m - m_prev) + dt * (p * m_mid * u_mid.dx(0) - p.dx(0) * m_mid * u_mid)) * fc.dx  # p part
        )
    J = fc.derivative(F, w)
    problem = fc.NonlinearVariationalProblem(F, w, J=J)
    solver = fc.NonlinearVariationalSolver(problem)
    solver.parameters["newton_solver"]["maximum_iterations"] = 100  # Default is 50
    solver.parameters["newton_solver"]["error_on_nonconvergence"] = False
    
    return solver, w, m_prev, u_prev
Esempio n. 19
0
    def FromPhysics(cls,
                    physics,
                    dtype=torch.double,
                    device=torch.device("cpu")):

        V = physics.V
        Vc = physics.Vc

        if V.mesh().num_cells() > 290:
            raise Exception('ROM exceeds intended maximum size')

        M = np.zeros((V.dim(), V.dim(), Vc.dim()))

        m = df.Function(Vc)
        for i in range(Vc.dim()):
            m.vector()[:] = 0
            m.vector()[i] = 1
            a2 = df.derivative(physics.a, physics.alpha, m)
            M[:, :, i] = df.assemble(a2).array().copy()

        M = torch.tensor(M, dtype=dtype, device=device)

        return cls(physics, M, dtype=dtype, device=device)
Esempio n. 20
0
fe.plot(h_n)
plt.title('Initial boundary condition of enthalpy')
plt.xlabel('x')
plt.ylabel('Enthalpy')
plt.show()
# Here is where we solve the newton's linearized problem. The variable JF stores the jacobian of the function F with respect to the enthalpy h.

h_t = (
    h - h_n
) / Delta_t  #{d\T}/{d\h} Simple gateaux derivative of the c*h^3 function
diffth = (3 * (h)**2
          )  #{d\h}/{d\t} but as piecewise as we know the intermediate values
F = (diffth * (fe.dot(fe.grad(v), fe.grad(h))) + (rho / K) *
     (h_t * v)) * fe.dx  #The nonlinear function to be solved is assembled
JF = fe.derivative(
    F, h, fe.TrialFunction(V)
)  #Taking the derivative of the non linear function to solve by newton's method
#Invoking the Newton's method
problem = fe.NonlinearVariationalProblem(F, h, bc, JF)
solver = fe.NonlinearVariationalSolver(problem)
solver.solve()
fe.plot(h)
plt.xlabel('x')
plt.ylabel('Enthalpy')
plt.title('Solution of Enthalpy distribution initially against x ')
plt.show()
fe.plot(phis(h))
plt.xlabel('x')
plt.ylabel('$\phi$')
plt.title('Solid volume fraction before melting')
plt.show()
Esempio n. 21
0
P = fe.diff(W_iso, F)
S = 2 * fe.diff(W_iso, C)
F_static = inner(S, DE(v)) * dx - rho * inner(b, v) * dx - inner(
    t_bar, v) * ds + (p / kappa + J - 1) * q * dx
#F_static = inner(P, grad(v))*dx - rho*inner(b, v)*dx - inner(t_bar, v)*ds + (p/kappa + J - 1)*q*dx

file_u = fe.File("../results/displacement.pvd")
file_p = fe.File("../results/pressure.pvd")

bcul = fe.DirichletBC(V.sub(0), u_left, left)
bcur = fe.DirichletBC(V.sub(0), u_right, right)
#bcpl = fe.DirichletBC(V.sub(1), p_left, left)
bcs = [bcul, bcur]  # bcpl]

J_static = fe.derivative(F_static, w, dw)

#fe.solve(F_static==0, w, bcs)

ffc_options = {"optimize": True}
problem = fe.NonlinearVariationalProblem(F_static,
                                         w,
                                         bcs,
                                         J=J_static,
                                         form_compiler_parameters=ffc_options)
solver = fe.NonlinearVariationalSolver(problem)

# set parameters
prm = solver.parameters
if True:
    iterative_solver = True
Esempio n. 22
0
    def staggered_solve(self):
        self.U = fe.VectorFunctionSpace(self.mesh, 'CG', 1)
        self.W = fe.FunctionSpace(self.mesh, 'CG', 1)

        self.WW = fe.FunctionSpace(self.mesh, 'DG', 0)
        self.EE = fe.TensorFunctionSpace(self.mesh, 'DG', 0)
        self.MM = fe.VectorFunctionSpace(self.mesh, 'CG', 1)

        self.eta = fe.TestFunction(self.U)
        self.zeta = fe.TestFunction(self.W)
        q = fe.TestFunction(self.WW)

        del_x = fe.TrialFunction(self.U)
        del_d = fe.TrialFunction(self.W)
        p = fe.TrialFunction(self.WW)

        self.x_new = fe.Function(self.U, name="u")
        self.d_new = fe.Function(self.W, name="d")
        self.d_pre = fe.Function(self.W)
        self.x_pre = fe.Function(self.U)

        x_old = fe.Function(self.U)
        d_old = fe.Function(self.W)

        self.H_old = fe.Function(self.WW)

        self.map_plot = fe.Function(self.MM, name="m")
        e = fe.Function(self.EE, name="e")

        self.create_custom_xdmf_files()

        self.file_results = fe.XDMFFile('data/xdmf/{}/u.xdmf'.format(
            self.case_name))
        self.file_results.parameters["functions_share_mesh"] = True

        vtkfile_e = fe.File('data/pvd/simulation/{}/e.pvd'.format(
            self.case_name))
        vtkfile_u = fe.File('data/pvd/simulation/{}/u.pvd'.format(
            self.case_name))
        vtkfile_d = fe.File('data/pvd/simulation/{}/d.pvd'.format(
            self.case_name))

        for i, (disp, rp) in enumerate(
                zip(self.displacements, self.relaxation_parameters)):
            print('\n')
            print(
                '================================================================================='
            )
            print('>> Step {}, disp boundary condition = {} [mm]'.format(
                i, disp))
            print(
                '================================================================================='
            )
            self.i = i
            self.update_weak_form_due_to_Model_C_bug()

            if self.update_weak_form:
                self.set_bcs_staggered()
                print("Update weak form...")
                self.build_weak_form_staggered()

                print("Taking derivatives of weak form...")
                J_u = fe.derivative(self.G_u, self.x_new, del_x)
                J_d = fe.derivative(self.G_d, self.d_new, del_d)
                print("Define nonlinear problems...")
                p_u = fe.NonlinearVariationalProblem(self.G_u, self.x_new,
                                                     self.BC_u, J_u)
                p_d = fe.NonlinearVariationalProblem(self.G_d, self.d_new,
                                                     self.BC_d, J_d)
                print("Define solvers...")
                solver_u = fe.NonlinearVariationalSolver(p_u)
                solver_d = fe.NonlinearVariationalSolver(p_d)
                self.update_weak_form = False

                print("Update history weak form")
                a = p * q * fe.dx
                L = history(self.H_old, self.update_history(),
                            self.psi_cr) * q * fe.dx

                if self.map_flag:
                    self.interpolate_map()
                    # delta_x = self.x - self.x_hat
                    # self.map_plot.assign(fe.project(delta_x, self.MM))

            self.presLoad.t = disp

            newton_prm = solver_u.parameters['newton_solver']
            newton_prm['maximum_iterations'] = 100
            # newton_prm['absolute_tolerance'] = 1e-8
            newton_prm['relaxation_parameter'] = rp

            newton_prm = solver_d.parameters['newton_solver']
            newton_prm['maximum_iterations'] = 100
            # newton_prm['absolute_tolerance'] = 1e-8
            newton_prm['relaxation_parameter'] = rp

            vtkfile_e_staggered = fe.File(
                'data/pvd/simulation/{}/step{}/e.pvd'.format(
                    self.case_name, i))
            vtkfile_u_staggered = fe.File(
                'data/pvd/simulation/{}/step{}/u.pvd'.format(
                    self.case_name, i))
            vtkfile_d_staggered = fe.File(
                'data/pvd/simulation/{}/step{}/d.pvd'.format(
                    self.case_name, i))
            iteration = 0
            err = 1.
            while err > self.staggered_tol:
                iteration += 1

                solver_d.solve()

                solver_u.solve()

                if self.solution_scheme == 'explicit':
                    break

                # # Remarks(Tianju): self.x_new.vector() does not behave as expected: producing nan values
                # The following lines of codes cause issues
                # We use an error measure similar in https://doi.org/10.1007/s10704-019-00372-y
                # np_x_new = np.asarray(self.x_new.vector())
                # np_d_new = np.asarray(self.d_new.vector())
                # np_x_old = np.asarray(x_old.vector())
                # np_d_old = np.asarray(d_old.vector())
                # err_x = np.linalg.norm(np_x_new - np_x_old) / np.sqrt(len(np_x_new))
                # err_d = np.linalg.norm(np_d_new - np_d_old) / np.sqrt(len(np_d_new))
                # err = max(err_x, err_d)

                # # Remarks(Tianju): dolfin (2019.1.0) errornorm function has severe bugs not behave as expected
                # The bug seems to be fixed in later versions
                # The following sometimes produces nonzero results in dolfin (2019.1.0)
                # print(fe.errornorm(self.d_new, self.d_new, norm_type='l2'))

                err_x = fe.errornorm(self.x_new, x_old, norm_type='l2')
                err_d = fe.errornorm(self.d_new, d_old, norm_type='l2')
                err = max(err_x, err_d)

                x_old.assign(self.x_new)
                d_old.assign(self.d_new)
                e.assign(
                    fe.project(strain(self.mfem_grad(self.x_new)), self.EE))

                print(
                    '---------------------------------------------------------------------------------'
                )
                print(
                    '>> iteration. {}, err_u = {:.5}, err_d = {:.5}, error = {:.5}'
                    .format(iteration, err_x, err_d, err))
                print(
                    '---------------------------------------------------------------------------------'
                )

                # vtkfile_e_staggered << e
                # vtkfile_u_staggered << self.x_new
                # vtkfile_d_staggered << self.d_new

                if err < self.staggered_tol or iteration >= self.staggered_maxiter:
                    print(
                        '================================================================================='
                    )
                    print('\n')
                    break

            print("L2 projection to update the history function...")
            fe.solve(a == L, self.H_old, [])

            # self.d_pre.assign(self.d_new)
            # self.H_old.assign(fe.project(history(self.H_old, self.update_history(), self.psi_cr), self.WW))

            if self.map_flag and not self.finish_flag:
                self.update_map()

            if self.compute_and_save_intermediate_results:
                print("Save files...")
                self.file_results.write(e, i)
                self.file_results.write(self.x_new, i)
                self.file_results.write(self.d_new, i)
                self.file_results.write(self.map_plot, i)

                vtkfile_e << e
                vtkfile_u << self.x_new
                vtkfile_d << self.d_new

                # Assume boundary is not affected by the map.
                # There's no need to use the mfem_grad wrapper so that fe.grad is used for speed-up
                print("Define forces...")
                sigma = cauchy_stress_plus(strain(fe.grad(self.x_new)),
                                           self.psi)
                sigma_minus = cauchy_stress_minus(strain(fe.grad(self.x_new)),
                                                  self.psi_minus)
                sigma_plus = cauchy_stress_plus(strain(fe.grad(self.x_new)),
                                                self.psi_plus)
                sigma_degraded = g_d(self.d_new) * sigma_plus + sigma_minus

                print("Compute forces...")
                if self.case_name == 'pure_shear':
                    f_full = float(fe.assemble(sigma[0, 1] * self.ds(1)))
                    f_degraded = float(
                        fe.assemble(sigma_degraded[0, 1] * self.ds(1)))
                else:
                    f_full = float(fe.assemble(sigma[1, 1] * self.ds(1)))
                    f_degraded = float(
                        fe.assemble(sigma_degraded[1, 1] * self.ds(1)))

                print("Force full is {}".format(f_full))
                print("Force degraded is {}".format(f_degraded))
                self.delta_u_recorded.append(disp)
                self.force_full.append(f_full)
                self.force_degraded.append(f_degraded)

                # if force_upper < 0.5 and i > 10:
                #     break

                if self.display_intermediate_results and i % 10 == 0:
                    self.show_force_displacement()

                self.save_data_in_loop()

        if self.display_intermediate_results:
            plt.ioff()
            plt.show()
Esempio n. 23
0
    mu = Ey / (2. * (1 + nu))
    lambda_ = Ey * nu / ((1 + nu) * (1 - 2 * nu))
    return lambda_ / 2. * (fe.tr(E))**2. + mu * fe.tr(E * E)


# Collect the strain density functions
integrals_E = []
for i in materials:
    psi = strainDensityFunction(E, materials[i][0], materials[i][1])
    integrals_E.append(psi * dxp(i))

# Total potential energy
Pi = sum(integrals_E) - sum(integrals_V) - sum(integrals_S)

# Compute 1st variation of Pi (directional derivative about u in dir. of v)
F = fe.derivative(Pi, u, v)

# Compute Jacobian of F
J = fe.derivative(F, u, du)

############################## SOLVER PARAMS ##################################
# Define the solver params
problem = fe.NonlinearVariationalProblem(F, u, bcs, J)
solver = fe.NonlinearVariationalSolver(problem)
prm = solver.parameters
prm['nonlinear_solver'] = 'newton'
prm['newton_solver']['linear_solver'] = 'petsc'

prm['newton_solver']['error_on_nonconvergence'] = True
prm['newton_solver']['absolute_tolerance'] = 1E-9
prm['newton_solver']['relative_tolerance'] = 1E-8
Esempio n. 24
0
res = -tau * fe.inner(fe.dot(u, fe.grad(v)), fe.grad(u) * u) * fe.dx
res += -tau * fe.inner(fe.dot(u, fe.grad(v)), fe.grad(p)) * fe.dx
#res += - tau * fe.inner(fe.dot(u, fe.grad(v)),  -1 * fv1 * nu_trial * fe.div(fe.grad(u))) * fe.dx #TODO - update residual term for new turbulence model
res += -tau * fe.inner(fe.dot(u, fe.grad(v)),
                       -1 * nu * fe.div(fe.grad(u))) * fe.dx
res += -tau * fe.inner(fe.dot(u, fe.grad(q)), fe.div(u)) * fe.dx
res += -tau * fe.inner(fe.dot(u, fe.grad(v)), -1 * b) * fe.dx

if STAB:
    weakForm += res
stab = -tau * fe.inner(fe.grad(q), fe.grad(p)) * fe.dx
weakForm = weakForm + stab

dW = fe.TrialFunction(W)
dFdW = fe.derivative(weakForm, W0, dW)

if MODEL:
    bcSet = [
        bc_1, bc_2, bc_3, bc_inflow, bc_p, bc_v_x0, bc_v_x1, bc_v_y1, bc_v_in,
        bc_v_top
    ]
else:
    bcSet = [bc_1, bc_2, bc_3, bc_inflow, bc_p]
problem = fe.NonlinearVariationalProblem(weakForm, W0, bcSet, J=dFdW)

solver = fe.NonlinearVariationalSolver(problem)

prm = solver.parameters

t = 0.0
Esempio n. 25
0
def SolveProblem(LoadCase, ConstitutiveModel, BCsType, FinalRelativeStretch, RelativeStepSize, Dimensions, NumberElements, Mesh, V, u, du, v, Ic, J, F, Psi, Plot = False, Paraview = False):
    
    if LoadCase == 'Compression':
                
        # Load case
        [u_0, u_1, InitialState, Direction, Normal, NumberSteps, DeltaStretch] = LoadCaseDefinition(LoadCase, FinalRelativeStretch, RelativeStepSize, Dimensions, BCsType)
        
        
    elif LoadCase == 'Tension':
                
        # Load case
        [u_0, u_1, InitialState, Direction, Normal, NumberSteps, DeltaStretch] = LoadCaseDefinition(LoadCase, FinalRelativeStretch, RelativeStepSize, Dimensions, BCsType)

        
    elif LoadCase == 'SimpleShear':
                
        # Load case
        [u_0, u_1, InitialState, Direction, Normal, NumberSteps, DeltaStretch] = LoadCaseDefinition(LoadCase, FinalRelativeStretch*2, RelativeStepSize*2, Dimensions, BCsType)

    # Boundary conditions
    [BoundaryConditions, ds] = BCsDefinition(Dimensions, Mesh, V, u_0, u_1, LoadCase, BCsType)
    
    # Estimation of the displacement field using Neo-Hookean model (necessary for Ogden)
    u = Estimate(Ic, J, u, v, du, BoundaryConditions, InitialState, u_1)
    
    # Reformulate the problem with the correct constitutive model
    Pi = Psi * fe.dx

    # First directional derivative of the potential energy
    Fpi = fe.derivative(Pi,u,v)

    # Jacobian of Fpi
    Jac = fe.derivative(Fpi,u,du)
    
    # Define option for the compiler (optional)
    ffc_options = {"optimize": True, \
                   "eliminate_zeros": True, \
                   "precompute_basis_const": True, \
                   "precompute_ip_const": True }

    # Define the problem
    Problem = fe.NonlinearVariationalProblem(Fpi, u, BoundaryConditions, Jac, form_compiler_parameters=ffc_options)

    # Define the solver
    Solver = fe.NonlinearVariationalSolver(Problem)

    # Set solver parameters (optional)
    Prm = Solver.parameters
    Prm['nonlinear_solver'] = 'newton'
    Prm['newton_solver']['linear_solver'] = 'cg'             # Conjugate gradient
    Prm['newton_solver']['preconditioner'] = 'icc'           # Incomplete Choleski
    Prm['newton_solver']['krylov_solver']['nonzero_initial_guess'] = True
    
    # Data frame to store values
    cols = ['Stretches','P']
    df = pd.DataFrame(columns=cols, index=range(int(NumberSteps)+1), dtype='float64')
    
    if Paraview == True:
        # Results File
        Output_Path = os.path.join('OptimizationResults', BCsType, ConstitutiveModel)
        ResultsFile = xdmffile = fe.XDMFFile(os.path.join(Output_Path, str(NumberElements) + 'Elements_' + LoadCase + '.xdmf'))
        ResultsFile.parameters["flush_output"] = True
        ResultsFile.parameters["functions_share_mesh"] = True
    
    if Plot == True:
        plt.rc('figure', figsize=[12,7])
        fig = plt.figure()
        ax = fig.add_subplot(1, 1, 1)
    
    # Set the stretch state to initial state
    StretchState = InitialState
    
    # Loop to solve for each step
    for Step in range(int(NumberSteps+1)):

        # Update current state
        u_1.s = StretchState

        # Compute solution and save displacement
        Solver.solve()

        # First Piola Kirchoff (nominal) stress
        P = fe.diff(Psi, F)

        # Nominal stress vectors normal to upper surface
        p = fe.dot(P,Normal)

        # Reaction force on the upper surface
        f = fe.assemble(fe.inner(p,Direction)*ds(2))

        # Mean nominal stress on the upper surface
        Pm = f/fe.assemble(1*ds(2))

        # Save values to table
        df.loc[Step].Stretches = StretchState
        df.loc[Step].P = Pm

        # Plot
        if Plot == True:
            ax.cla()
            ax.plot(df.Stretches, df.P,  color = 'r', linestyle = '--', label = 'P', marker = 'o', markersize = 8, fillstyle='none')
            ax.set_xlabel('Stretch ratio (-)')
            ax.set_ylabel('Stresses (kPa)')
            ax.xaxis.set_major_locator(plt.MultipleLocator(0.02))
            ax.legend(loc='upper left', frameon=True, framealpha=1)
            display(fig)
            clear_output(wait=True)
            
        if Paraview == True:
            # Project the displacement onto the vector function space
            u_project = fe.project(u, V, solver_type='cg')
            u_project.rename('Displacement (mm)', '')
            ResultsFile.write(u_project,Step)
            
            # Compute nominal stress vector
            p_project = fe.project(p, V)
            p_project.rename("Nominal stress vector (kPa)","")
            ResultsFile.write(p_project,Step)



        # Update the stretch state
        StretchState += DeltaStretch

    return df
Esempio n. 26
0
def damped_newton_solve(F, u, bcs, rtol=1e-10, atol=1e-10, max_iter=50, convergence_type='combined', norm_type='l2',
						damped=True, verbose=True, ksp=None, ksp_options=None):
	r"""A damped Newton method for solving nonlinear equations.

	The damped Newton method is based on the natural monotonicity test from
	`Deuflhard, Newton methods for nonlinear problems <https://doi.org/10.1007/978-3-642-23899-4>`_.
	It also allows fine tuning via a direct interface, and absolute, relative,
	and combined stopping criteria. Can also be used to specify the solver for
	the inner (linear) subproblems via petsc ksps.

	The method terminates after ``max_iter`` iterations, or if a termination criterion is
	satisfied. These criteria are given by

	- a relative one in case ``convergence_type = 'rel'``, i.e.,

	.. math:: \lvert\lvert F_{k} \rvert\rvert \leq \texttt{rtol} \lvert\lvert F_0 \rvert\rvert.

	- an absolute one in case ``convergence_type = 'abs'``, i.e.,

	.. math:: \lvert\lvert F_{k} \rvert\rvert \leq \texttt{atol}.

	- a combination of both in case ``convergence_type = 'combined'``, i.e.,

	.. math:: \lvert\lvert F_{k} \rvert\rvert \leq \texttt{atol} + \texttt{rtol} \lvert\lvert F_0 \rvert\rvert.

	The norm chosen for the termination criterion is specified via ``norm_type``.

	Parameters
	----------
	F : ufl.form.Form
		The variational form of the nonlinear problem to be solved by Newton's method.
	u : dolfin.function.function.Function
		The sought solution / initial guess. It is not assumed that the initial guess
		satisfies the Dirichlet boundary conditions, they are applied automatically.
		The method overwrites / updates this Function.
	bcs : list[dolfin.fem.dirichletbc.DirichletBC]
		A list of DirichletBCs for the nonlinear variational problem.
	rtol : float, optional
		Relative tolerance of the solver if convergence_type is either ``'combined'`` or ``'rel'``
		(default is ``rtol = 1e-10``).
	atol : float, optional
		Absolute tolerance of the solver if convergence_type is either ``'combined'`` or ``'abs'``
		(default is ``atol = 1e-10``).
	max_iter : int, optional
		Maximum number of iterations carried out by the method
		(default is ``max_iter = 50``).
	convergence_type : {'combined', 'rel', 'abs'}
		Determines the type of stopping criterion that is used.
	norm_type : {'l2', 'linf'}
		Determines which norm is used in the stopping criterion.
	damped : bool, optional
		If ``True``, then a damping strategy is used. If ``False``, the classical
		Newton-Raphson iteration (without damping) is used (default is ``True``).
	verbose : bool, optional
		If ``True``, prints status of the iteration to the console (default
		is ``True``).
	ksp : petsc4py.PETSc.KSP, optional
		The PETSc ksp object used to solve the inner (linear) problem
		if this is ``None`` it uses the direct solver MUMPS (default is
		``None``).
	ksp_options : list[list[str]]
		The list of options for the linear solver.


	Returns
	-------
	dolfin.function.function.Function
		The solution of the nonlinear variational problem, if converged.
		This overrides the input function u.


	Examples
	--------
	Consider the problem

	.. math::
		\begin{alignedat}{2}
		- \Delta u + u^3 &= 1 \quad &&\text{ in } \Omega=(0,1)^2 \\
		u &= 0 \quad &&\text{ on } \Gamma.
		\end{alignedat}

	This is solved with the code ::

		from fenics import *
		import cashocs

		mesh, _, boundaries, dx, _, _ = cashocs.regular_mesh(25)
		V = FunctionSpace(mesh, 'CG', 1)

		u = Function(V)
		v = TestFunction(V)
		F = inner(grad(u), grad(v))*dx + pow(u,3)*v*dx - Constant(1)*v*dx
		bcs = cashocs.create_bcs_list(V, Constant(0.0), boundaries, [1,2,3,4])
		cashocs.damped_newton_solve(F, u, bcs)
	"""

	if not convergence_type in ['rel', 'abs', 'combined']:
		raise InputError('cashocs.nonlinear_solvers.damped_newton_solve', 'convergence_type', 'Input convergence_type has to be one of \'rel\', \'abs\', or \'combined\'.')

	if not norm_type in ['l2', 'linf']:
		raise InputError('cashocs.nonlinear_solvers.damped_newton_solve', 'norm_type', 'Input norm_type has to be one of \'l2\' or \'linf\'.')

	# create the PETSc ksp
	if ksp is None:
		if ksp_options is None:
			ksp_options = [
				['ksp_type', 'preonly'],
				['pc_type', 'lu'],
				['pc_factor_mat_solver_type', 'mumps'],
				['mat_mumps_icntl_24', 1]
			]

		ksp = PETSc.KSP().create()
		_setup_petsc_options([ksp], [ksp_options])
		ksp.setFromOptions()

	# Calculate the Jacobian.
	dF = fenics.derivative(F, u)

	# Setup increment and function for monotonicity test
	V = u.function_space()
	du = fenics.Function(V)
	ddu = fenics.Function(V)
	u_save = fenics.Function(V)

	iterations = 0

	[bc.apply(u.vector()) for bc in bcs]
	# copy the boundary conditions and homogenize them for the increment
	bcs_hom = [fenics.DirichletBC(bc) for bc in bcs]
	[bc.homogenize() for bc in bcs_hom]

	assembler = fenics.SystemAssembler(dF, -F, bcs_hom)
	assembler.keep_diagonal = True
	A_fenics = fenics.PETScMatrix()
	residual = fenics.PETScVector()

	# Compute the initial residual
	assembler.assemble(A_fenics, residual)
	A_fenics.ident_zeros()
	A = fenics.as_backend_type(A_fenics).mat()
	b = fenics.as_backend_type(residual).vec()

	res_0 = residual.norm(norm_type)
	if res_0 == 0.0:
		if verbose:
			print('Residual vanishes, input is already a solution.')
		return u

	res = res_0
	if verbose:
		print('Newton Iteration ' + format(iterations, '2d') + ' - residual (abs):  '
			  + format(res, '.3e') + ' (tol = ' + format(atol, '.3e') + ')    residual (rel): '
			  + format(res/res_0, '.3e') + ' (tol = ' + format(rtol, '.3e') + ')')

	if convergence_type == 'abs':
		tol = atol
	elif convergence_type == 'rel':
		tol = rtol*res_0
	else:
		tol = rtol*res_0 + atol

	# While loop until termination
	while res > tol and iterations < max_iter:
		iterations += 1
		lmbd = 1.0
		breakdown = False
		u_save.vector()[:] = u.vector()[:]

		# Solve the inner problem
		_solve_linear_problem(ksp, A, b, du.vector().vec(), ksp_options)
		du.vector().apply('')

		# perform backtracking in case damping is used
		if damped:
			while True:
				u.vector()[:] += lmbd*du.vector()[:]
				assembler.assemble(residual)
				b = fenics.as_backend_type(residual).vec()
				_solve_linear_problem(ksp=ksp, b=b, x=ddu.vector().vec(), ksp_options=ksp_options)
				ddu.vector().apply('')

				if ddu.vector().norm(norm_type)/du.vector().norm(norm_type) <= 1:
					break
				else:
					u.vector()[:] = u_save.vector()[:]
					lmbd /= 2

				if lmbd < 1e-6:
					breakdown = True
					break

		else:
			u.vector()[:] += du.vector()[:]

		if breakdown:
			raise NotConvergedError('Newton solver (state system)', 'Stepsize for increment too low.')

		if iterations == max_iter:
			raise NotConvergedError('Newton solver (state system)', 'Maximum number of iterations were exceeded.')

		# compute the new residual
		assembler.assemble(A_fenics, residual)
		A_fenics.ident_zeros()
		A = fenics.as_backend_type(A_fenics).mat()
		b = fenics.as_backend_type(residual).vec()

		[bc.apply(residual) for bc in bcs_hom]

		res = residual.norm(norm_type)
		if verbose:
			print('Newton Iteration ' + format(iterations, '2d') + ' - residual (abs):  '
				  + format(res, '.3e') + ' (tol = ' + format(atol, '.3e') + ')    residual (rel): '
				  + format(res/res_0, '.3e') + ' (tol = ' + format(rtol, '.3e') + ')')

		if res < tol:
			if verbose:
				print('\nNewton Solver converged after ' + str(iterations) + ' iterations.\n')
			break

	return u
    def compute_static_deformation(self):

        assert self.mesh is not None

        # now we define subdomains on the mesh

        bottom = fe.CompiledSubDomain('near(x[2], 0) && on_boundary')
        top = fe.CompiledSubDomain('near(x[2], 1) && on_boundary')
        # middle = fe.CompiledSubDomain('x[2] > 0.3 && x[2] < 0.7')

        # Initialize mesh function for interior domains
        self.domains = fe.MeshFunction('size_t', self.mesh, 3)
        self.domains.set_all(0)
        # middle.mark(self.domains, 1)

        # Initialize mesh function for boundary domains
        self.boundaries = fe.MeshFunction('size_t', self.mesh, 2)
        self.boundaries.set_all(0)
        bottom.mark(self.boundaries, 1)
        top.mark(self.boundaries, 2)

        # Define new measures associated with the interior domains and
        # exterior boundaries

        self.dx = fe.Measure('dx',
                             domain=self.mesh,
                             subdomain_data=self.domains)
        self.ds = fe.Measure('ds',
                             domain=self.mesh,
                             subdomain_data=self.boundaries)

        # define function spaces
        V = fe.VectorFunctionSpace(self.mesh, "Lagrange", 1)
        # now we define subdomains on the mesh

        bottom = fe.CompiledSubDomain('near(x[2], 0) && on_boundary')
        top = fe.CompiledSubDomain('near(x[2], 1) && on_boundary')
        # middle = fe.CompiledSubDomain('x[2] > 0.3 && x[2] < 0.7')

        d = self.mesh.geometry().dim()

        # Initialize mesh function for interior domains
        self.domains = fe.MeshFunction('size_t', self.mesh, d)
        self.domains.set_all(0)
        # middle.mark(self.domains, 1)

        # Initialize mesh function for boundary domains
        self.boundaries = fe.MeshFunction('size_t', self.mesh, d - 1)
        self.boundaries.set_all(0)
        bottom.mark(self.boundaries, 1)
        top.mark(self.boundaries, 2)

        # Define new measures associated with the interior domains and
        # exterior boundaries

        self.dx = fe.Measure('dx',
                             domain=self.mesh,
                             subdomain_data=self.domains)
        self.ds = fe.Measure('ds',
                             domain=self.mesh,
                             subdomain_data=self.boundaries)

        c_zero = fe.Constant((0, 0, 0))

        # define boundary conditions
        bc_bottom = fe.DirichletBC(V, c_zero, bottom)
        bc_top = fe.DirichletBC(V, c_zero, top)

        bcs = [bc_bottom]  # , bc_top]

        # define functions
        du = TrialFunction(V)
        v = TestFunction(V)
        u = Function(V)
        B = fe.Constant((0., 2.0, 0.))
        T = fe.Constant((0.0, 0.0, 0.0))

        d = u.geometric_dimension()
        I = fe.Identity(d)
        F = I + grad(u)
        C = F.T * F

        I_1 = tr(C)
        J = det(F)

        E, mu = 10., 0.3
        mu, lmbda = fe.Constant(E / (2 * (1 + mu))), fe.Constant(
            E * mu / ((1 + mu) * (1 - 2 * mu)))

        # stored energy (comp. neo-hookean model)
        psi = (mu / 2.) * (I_1 - 3) - mu * fe.ln(J) + (lmbda /
                                                       2.) * (fe.ln(J))**2

        dx = self.dx
        ds = self.ds

        Pi = psi * fe.dx - dot(B, u) * fe.dx - dot(T, u) * fe.ds

        F = fe.derivative(Pi, u, v)
        J = fe.derivative(F, u, du)

        fe.solve(F == 0, u, bcs, J=J)

        # save results
        self.u = u

        # write to disk
        output = fe.File("/tmp/static.pvd")
        output << u
Esempio n. 28
0
 def setup_derivative(self):
     """ Set the derivative of the governing form, needed for the nonlinear solver. """
     self.derivative_of_governing_form = fenics.derivative(self.governing_form, 
         self.state.solution, 
         fenics.TrialFunction(self.function_space))
Esempio n. 29
0
    def __compute_newton_forms(self):
        """Calculates the needed forms for the truncated Newton method.

		Returns
		-------
		None
		"""

        # Use replace -> derivative to speed up the computations
        self.sensitivity_eqs_temp = [
            replace(self.state_forms[i],
                    {self.adjoints[i]: self.test_functions_state[i]})
            for i in range(self.state_dim)
        ]

        self.sensitivity_eqs_lhs = [
            fenics.derivative(self.sensitivity_eqs_temp[i], self.states[i],
                              self.trial_functions_state[i])
            for i in range(self.state_dim)
        ]
        if self.state_is_picard:
            self.sensitivity_eqs_picard = [
                fenics.derivative(self.sensitivity_eqs_temp[i], self.states[i],
                                  self.states_prime[i])
                for i in range(self.state_dim)
            ]

        # Need to distinguish cases due to empty sum in case state_dim = 1
        if self.state_dim > 1:
            self.sensitivity_eqs_rhs = [
                -summation([
                    fenics.derivative(self.sensitivity_eqs_temp[i],
                                      self.states[j], self.states_prime[j])
                    for j in range(self.state_dim) if j != i
                ]) - summation([
                    fenics.derivative(self.sensitivity_eqs_temp[i],
                                      self.controls[j],
                                      self.test_directions[j])
                    for j in range(self.control_dim)
                ]) for i in range(self.state_dim)
            ]
        else:
            self.sensitivity_eqs_rhs = [
                -summation([
                    fenics.derivative(self.sensitivity_eqs_temp[i],
                                      self.controls[j],
                                      self.test_directions[j])
                    for j in range(self.control_dim)
                ]) for i in range(self.state_dim)
            ]

        # Add the right-hand-side for the picard iteration
        if self.state_is_picard:
            for i in range(self.state_dim):
                self.sensitivity_eqs_picard[i] -= self.sensitivity_eqs_rhs[i]

        # Compute forms for the truncated Newton method
        self.L_y = [
            fenics.derivative(self.lagrangian.lagrangian_form, self.states[i],
                              self.test_functions_state[i])
            for i in range(self.state_dim)
        ]
        self.L_u = [
            fenics.derivative(self.lagrangian.lagrangian_form,
                              self.controls[i], self.test_functions_control[i])
            for i in range(self.control_dim)
        ]

        self.L_yy = [[
            fenics.derivative(self.L_y[i], self.states[j],
                              self.states_prime[j])
            for j in range(self.state_dim)
        ] for i in range(self.state_dim)]
        self.L_yu = [[
            fenics.derivative(self.L_u[i], self.states[j],
                              self.states_prime[j])
            for j in range(self.state_dim)
        ] for i in range(self.control_dim)]
        self.L_uy = [[
            fenics.derivative(self.L_y[i], self.controls[j],
                              self.test_directions[j])
            for j in range(self.control_dim)
        ] for i in range(self.state_dim)]
        self.L_uu = [[
            fenics.derivative(self.L_u[i], self.controls[j],
                              self.test_directions[j])
            for j in range(self.control_dim)
        ] for i in range(self.control_dim)]

        self.w_1 = [
            summation([self.L_yy[i][j] for j in range(self.state_dim)]) +
            summation([self.L_uy[i][j] for j in range(self.control_dim)])
            for i in range(self.state_dim)
        ]
        self.w_2 = [
            summation([self.L_yu[i][j] for j in range(self.state_dim)]) +
            summation([self.L_uu[i][j] for j in range(self.control_dim)])
            for i in range(self.control_dim)
        ]

        # Use replace -> derivative for faster computations
        self.adjoint_sensitivity_eqs_diag_temp = [
            replace(self.state_forms[i],
                    {self.adjoints[i]: self.trial_functions_adjoint[i]})
            for i in range(self.state_dim)
        ]

        mapping_dict = {
            self.adjoints[j]: self.adjoints_prime[j]
            for j in range(self.state_dim)
        }
        self.adjoint_sensitivity_eqs_all_temp = [
            replace(self.state_forms[i], mapping_dict)
            for i in range(self.state_dim)
        ]

        self.adjoint_sensitivity_eqs_lhs = [
            fenics.derivative(self.adjoint_sensitivity_eqs_diag_temp[i],
                              self.states[i], self.test_functions_adjoint[i])
            for i in range(self.state_dim)
        ]
        if self.state_is_picard:
            self.adjoint_sensitivity_eqs_picard = [
                fenics.derivative(self.adjoint_sensitivity_eqs_all_temp[i],
                                  self.states[i],
                                  self.test_functions_adjoint[i])
                for i in range(self.state_dim)
            ]

        # Need cases distinction due to empty sum for state_dim == 1
        if self.state_dim > 1:
            for i in range(self.state_dim):
                self.w_1[i] -= summation([
                    fenics.derivative(self.adjoint_sensitivity_eqs_all_temp[j],
                                      self.states[i],
                                      self.test_functions_adjoint[i])
                    for j in range(self.state_dim) if j != i
                ])
        else:
            pass

        # Add right-hand-side for picard iteration
        if self.state_is_picard:
            for i in range(self.state_dim):
                self.adjoint_sensitivity_eqs_picard[i] -= self.w_1[i]

        self.adjoint_sensitivity_eqs_rhs = [
            summation([
                fenics.derivative(self.adjoint_sensitivity_eqs_all_temp[j],
                                  self.controls[i],
                                  self.test_functions_control[i])
                for j in range(self.state_dim)
            ]) for i in range(self.control_dim)
        ]

        self.w_3 = [
            -self.adjoint_sensitivity_eqs_rhs[i]
            for i in range(self.control_dim)
        ]

        self.hessian_rhs = [
            self.w_2[i] + self.w_3[i] for i in range(self.control_dim)
        ]
Esempio n. 30
0
    def __compute_adjoint_equations(self):
        """Calculates the weak form of the adjoint equation for use with fenics.

		Returns
		-------
		None
		"""

        # Use replace -> derivative to speed up computations
        self.lagrangian_temp_forms = [
            replace(self.lagrangian.lagrangian_form,
                    {self.adjoints[i]: self.trial_functions_adjoint[i]})
            for i in range(self.state_dim)
        ]

        if self.state_is_picard:
            self.adjoint_picard_forms = [
                fenics.derivative(self.lagrangian.lagrangian_form,
                                  self.states[i],
                                  self.test_functions_adjoint[i])
                for i in range(self.state_dim)
            ]

        self.adjoint_eq_forms = [
            fenics.derivative(self.lagrangian_temp_forms[i], self.states[i],
                              self.test_functions_adjoint[i])
            for i in range(self.state_dim)
        ]
        self.adjoint_eq_lhs = []
        self.adjoint_eq_rhs = []

        for i in range(self.state_dim):
            a, L = fenics.system(self.adjoint_eq_forms[i])
            self.adjoint_eq_lhs.append(a)
            if L.empty():
                zero_form = fenics.inner(
                    fenics.Constant(
                        np.zeros(self.test_functions_adjoint[i].ufl_shape)),
                    self.test_functions_adjoint[i]) * self.dx
                self.adjoint_eq_rhs.append(zero_form)
            else:
                self.adjoint_eq_rhs.append(L)

        # Compute the  adjoint boundary conditions
        if self.state_adjoint_equal_spaces:
            self.bcs_list_ad = [[
                fenics.DirichletBC(bc) for bc in self.bcs_list[i]
            ] for i in range(self.state_dim)]
            [[bc.homogenize() for bc in self.bcs_list_ad[i]]
             for i in range(self.state_dim)]
        else:

            def get_subdx(V, idx, ls):
                if V.id() == idx:
                    return ls
                if V.num_sub_spaces() > 1:
                    for i in range(V.num_sub_spaces()):
                        ans = get_subdx(V.sub(i), idx, ls + [i])
                        if ans is not None:
                            return ans
                else:
                    return None

            self.bcs_list_ad = [[1 for bc in range(len(self.bcs_list[i]))]
                                for i in range(self.state_dim)]

            for i in range(self.state_dim):
                for j, bc in enumerate(self.bcs_list[i]):
                    idx = bc.function_space().id()
                    subdx = get_subdx(self.state_spaces[i], idx, ls=[])
                    W = self.adjoint_spaces[i]
                    for num in subdx:
                        W = W.sub(num)
                    shape = W.ufl_element().value_shape()
                    try:
                        if shape == ():
                            self.bcs_list_ad[i][j] = fenics.DirichletBC(
                                W, fenics.Constant(0), bc.domain_args[0],
                                bc.domain_args[1])
                        else:
                            self.bcs_list_ad[i][j] = fenics.DirichletBC(
                                W,
                                fenics.Constant([0] *
                                                W.ufl_element().value_size()),
                                bc.domain_args[0], bc.domain_args[1])
                    except AttributeError:
                        if shape == ():
                            self.bcs_list_ad[i][j] = fenics.DirichletBC(
                                W, fenics.Constant(0), bc.sub_domain)
                        else:
                            self.bcs_list_ad[i][j] = fenics.DirichletBC(
                                W,
                                fenics.Constant([0] *
                                                W.ufl_element().value_size()),
                                bc.sub_domain)