def make_unit_vector(V, VV, dofs_x, fill_coordinates, foc=None): e_c_x = Function(V) e_c_y = Function(V) e_c_z = Function(V) for i, coord in enumerate(dofs_x): fill_coordinates(i, e_c_x, e_c_y, e_c_z, coord, foc) e = Function(VV) fa = [FunctionAssigner(VV.sub(i), V) for i in range(3)] for i, e_c_comp in enumerate([e_c_x, e_c_y, e_c_z]): fa[i].assign(e.split()[i], e_c_comp) return e
class MechanicsProblem(object): """ Base class for mechanics problem """ def __init__( self, geometry: Geometry, material: Material, bcs=None, bcs_parameters=None, solver_parameters=None, ): logger.debug("Initialize mechanics problem") self.geometry = geometry self.material = material self._handle_bcs(bcs=bcs, bcs_parameters=bcs_parameters) # Make sure that the material has microstructure information for attr in ("f0", "s0", "n0"): setattr(self.material, attr, getattr(self.geometry, attr)) self.solver_parameters = NonlinearSolver.default_solver_parameters() if solver_parameters is not None: self.solver_parameters.update(**solver_parameters) self._init_spaces() self._init_forms() def _handle_bcs(self, bcs, bcs_parameters): if bcs is None: if isinstance(self.geometry, HeartGeometry): self.bcs_parameters = MechanicsProblem.default_bcs_parameters() if bcs_parameters is not None: self.bcs_parameters.update(**bcs_parameters) else: raise ValueError(("Please provive boundary conditions " "to MechanicsProblem"), ) self.bcs = cardiac_boundary_conditions(self.geometry, **self.bcs_parameters) else: self.bcs = bcs # TODO: FIX THIS or require this # Just store this as well in case both is provided self.bcs_parameters = MechanicsProblem.default_bcs_parameters() if bcs_parameters is not None: self.bcs_parameters.update(**bcs_parameters) @staticmethod def default_bcs_parameters(): return dict(pericardium_spring=0.0, base_spring=0.0, base_bc="fixed") def _init_spaces(self): logger.debug("Initialize spaces for mechanics problem") mesh = self.geometry.mesh P2 = dolfin.VectorElement("Lagrange", mesh.ufl_cell(), 2) P1 = dolfin.FiniteElement("Lagrange", mesh.ufl_cell(), 1) # P2_space = FunctionSpace(mesh, P2) # P1_space = FunctionSpace(mesh, P1) self.state_space = dolfin.FunctionSpace(mesh, P2 * P1) self.state = Function(self.state_space, name="state") self.state_test = dolfin.TestFunction(self.state_space) def _init_forms(self): logger.debug("Initialize forms mechanics problem") # Displacement and hydrostatic_pressure u, p = dolfin.split(self.state) v, q = dolfin.split(self.state_test) # Some mechanical quantities F = dolfin.variable(kinematics.DeformationGradient(u)) J = kinematics.Jacobian(F) dx = self.geometry.dx internal_energy = (self.material.strain_energy(F, ) + self.material.compressibility(p, J)) self._virtual_work = dolfin.derivative( internal_energy * dx, self.state, self.state_test, ) external_work = self._external_work(u, v) if external_work is not None: self._virtual_work += external_work self._set_dirichlet_bc() self._jacobian = dolfin.derivative( self._virtual_work, self.state, dolfin.TrialFunction(self.state_space), ) self._init_solver() def _init_solver(self): if has_dolfin_adjoint: self._problem = NonlinearVariationalProblem( J=self._jacobian, F=self._virtual_work, u=self.state, bcs=self._dirichlet_bc, ) self.solver = NonlinearVariationalSolver(self._problem) else: self._problem = NonlinearProblem( J=self._jacobian, F=self._virtual_work, bcs=self._dirichlet_bc, ) self.solver = NonlinearSolver( self._problem, self.state, parameters=self.solver_parameters, ) def _set_dirichlet_bc(self): # DirichletBC for dirichlet_bc in self.bcs.dirichlet: msg = ("DirichletBC only implemented for as " "callable. Please provide DirichletBC " "as a callable with argument being the " "state space only") if hasattr(dirichlet_bc, "__call__"): try: self._dirichlet_bc = dirichlet_bc(self.state_space) except Exception as ex: logger.error(msg) raise ex else: raise NotImplementedError(msg) def _external_work(self, u, v): F = dolfin.variable(kinematics.DeformationGradient(u)) N = self.geometry.facet_normal ds = self.geometry.ds dx = self.geometry.dx external_work = [] for neumann in self.bcs.neumann: n = neumann.traction * ufl.cofac(F) * N external_work.append(dolfin.inner(v, n) * ds(neumann.marker)) for robin in self.bcs.robin: external_work.append( dolfin.inner(robin.value * u, v) * ds(robin.marker)) for body_force in self.bcs.body_force: external_work.append( -dolfin.derivative(dolfin.inner(body_force, u) * dx, u, v), ) if len(external_work) > 0: return list_sum(external_work) return None def reinit(self, state, annotate=False): """Reinitialze state""" if has_dolfin_adjoint: try: self.state.assign(state, annotate=annotate) except Exception as ex: print(ex) self.state.assign(state) else: self.state.assign(state) self._init_forms() @staticmethod def default_solver_parameters(): return NonlinearSolver.default_solver_parameters() def solve(self): r""" Solve the variational problem .. math:: \delta W = 0 """ logger.debug("Solving variational problem") # Get old state in case of non-convergence if has_dolfin_adjoint: try: old_state = self.state.copy(deepcopy=True, name="Old state (pulse)") except TypeError: # In case this is a dolfin fuction and not a # dolfin-adjoint function old_state = self.state.copy(deepcopy=True) else: old_state = self.state.copy(deepcopy=True) try: logger.debug("Try to solve") nliter, nlconv = self.solver.solve() if not nlconv: logger.debug("Failed") raise SolverDidNotConverge("Solver did not converge...") except RuntimeError as ex: logger.debug("Failed") logger.debug("Reintialize old state and raise exception") self.reinit(old_state) raise SolverDidNotConverge(ex) from ex else: logger.debug("Sucess") return nliter, nlconv def get_displacement(self, annotate=False): D = self.state_space.sub(0) V = D.collapse() fa = FunctionAssigner(V, D) u = Function(V, name="displacement") if has_dolfin_adjoint: fa.assign(u, self.state.split()[0], annotate=annotate) else: fa.assign(u, self.state.split()[0]) return u def SecondPiolaStress(self): u, p = self.state.split(deepcopy=True) F = kinematics.DeformationGradient(u) return self.material.SecondPiolaStress(F, p) def FirstPiolaStress(self): u, p = self.state.split(deepcopy=True) F = kinematics.DeformationGradient(u) return self.material.FirstPiolaStress(F, p) def ChachyStress(self): u, p = self.state.split(deepcopy=True) F = kinematics.DeformationGradient(u) return self.material.CauchyStress(F, p) def GreenLagrangeStrain(self): u, p = self.state.split(deepcopy=True) F = kinematics.DeformationGradient(u) return kinematics.GreenLagrangeStrain(F)