def __init__( self, activation=None, f0=None, s0=None, n0=None, model: ActiveModels = ActiveModels.active_strain, active_isotropy: ActiveStressModels = ActiveStressModels.transversally, eta: Optional[float] = None, T_ref: Optional[float] = None, isochoric=True, ): # Fiber system self.f0 = f0 self.s0 = s0 self.n0 = n0 self._activation = (Constant(0, name="activation") if activation is None else activation) self.T_ref = (Constant(T_ref, name="T_ref") if T_ref else Constant(1.0, name="T_ref")) self.eta = Constant(eta, name="eta") if eta else Constant(0.0, name="eta") self.isochoric = isochoric self.active_isotropy = active_isotropy self.model = model
def __init__( self, activation=None, f0=None, s0=None, n0=None, T_ref=None, isochoric=True, *args, **kwargs ): # Fiber system self.f0 = f0 self.s0 = s0 self.n0 = n0 self._activation = ( Constant(0, name="activation") if activation is None else activation ) self.T_ref = ( Constant(T_ref, name="T_ref") if T_ref else Constant(1.0, name="T_ref") ) kinematics.Invariants.__init__(self, isochoric, *args)
def dirichlet_bc(W): V = W if W.sub(0).num_sub_spaces() == 0 else W.sub(0) return [ DirichletBC(V.sub(0), Constant(-x_strain), xlow), DirichletBC(V.sub(0), Constant(x_strain), xhigh), DirichletBC(V.sub(1), Constant(-y_strain), ylow), DirichletBC(V.sub(1), Constant(y_strain), yhigh), ]
def strain_energy(self, F_): """ UFL form of the strain energy. """ params = self.parameters # Elastic part of deformation gradient F = self.Fe(F_) E = kinematics.GreenLagrangeStrain(F, isochoric=self.isochoric) CC = Constant(params["C"], name="C") e1 = self.f0 e2 = self.s0 e3 = self.n0 if any(e is None for e in (e1, e2, e3)): msg = ("Need to provide the full orthotropic basis " "for the Guccione model got \ne1 = {e1}\n" "e2 = {e2}\ne3 = {e3}").format(e1=e1, e2=e2, e3=e3) raise ValueError(msg) if self.is_isotropic(): # isotropic case Q = dolfin.inner(E, E) else: # fully anisotropic bt = Constant(params["bt"], name="bt") bf = Constant(params["bf"], name="bf") bfs = Constant(params["bfs"], name="bfs") E11, E12, E13 = ( dolfin.inner(E * e1, e1), dolfin.inner(E * e1, e2), dolfin.inner(E * e1, e3), ) _, E22, E23 = ( dolfin.inner(E * e2, e1), dolfin.inner(E * e2, e2), dolfin.inner(E * e2, e3), ) _, _, E33 = ( dolfin.inner(E * e3, e1), dolfin.inner(E * e3, e2), dolfin.inner(E * e3, e3), ) Q = (bf * E11**2 + bt * (E22**2 + E33**2 + 2 * E23**2) + bfs * (2 * E12**2 + 2 * E13**2)) # passive strain energy Wpassive = CC / 2.0 * (dolfin.exp(Q) - 1) Wactive = self.Wactive(F, diff=0) return Wpassive + Wactive
def dirichlet_bc(W): V = W if W.sub(0).num_sub_spaces() == 0 else W.sub(0) return [ DirichletBC(V.sub(0), Constant(-x_strain), xlow), DirichletBC(V.sub(0), Constant(x_strain), xhigh), DirichletBC(V.sub(1), Constant(-y_strain), ylow), DirichletBC(V.sub(1), Constant(y_strain), yhigh), DirichletBC(V, np.zeros(3), center, method="pointwise"), ]
def __init__(self, w=None, eta=None, dim=None): self.w = Constant(w, name="width") self.eta = Constant(eta, name="eta") W, eta, x1, x2 = sp.symbols('w, eta, x[0], x[1]') self.dependencies = [W, eta] X, Z = ligaro(W, eta) # R = L/eta if dim == 2: self.gamma_sp = gamma_sp = [X, Z] self.Gsub2 = None if dim == 3: self.gamma_sp = gamma_sp = [X, x2, Z] # for dim==2 or dim ==3: self.gamma = Expression(tuple([ccode(i) for i in gamma_sp]), eta=self.eta, w=self.w, degree=DEGREE, name="gamma") self.Gsub1 = self.build_derivative([x1], name='Gsub1') if ADJOINT: # adjoint gamma self.gamma.dependencies = [self.w, self.eta] self.gamma.user_defined_derivatives = {} self.gamma.user_defined_derivatives[ self.w] = self.build_derivative([W], name='d_gamma_dw') self.gamma.user_defined_derivatives[ self.eta] = self.build_derivative([eta], name='d_gamma_deta') # adjoint Gsub1 self.Gsub1.dependencies = [self.w, self.eta] self.Gsub1.user_defined_derivatives = {} self.Gsub1.user_defined_derivatives[ self.w] = self.build_derivative([x1, W], name='d_Gsub1_dw') self.Gsub1.user_defined_derivatives[ self.eta] = self.build_derivative([x1, eta], name='d_Gsub1_deta') if dim == 3: self.Gsub2 = self.build_derivative([x2], name='Gsub2') if ADJOINT: self.Gsub2.dependencies = [self.w, self.eta] self.Gsub2.user_defined_derivatives = {} self.Gsub2.user_defined_derivatives[ self.w] = self.build_derivative([x2, W], name='d_Gsub2_dw') self.Gsub2.user_defined_derivatives[ self.eta] = self.build_derivative([x2, eta], name='d_Gsub2_deta')
def I5(F, a0, isochoric=False): if a0 is not None: C = RightCauchyGreen(F, isochoric) I5 = inner(C * a0, C * a0) else: I5 = Constant(0.0) return I5
def _set_parameter_attrs(self, geometry=None): for k, v in self.parameters.items(): if isinstance(v, (float, int)): setattr(self, k, Constant(v, name=k)) elif isinstance(v, RegionalParameter): if geometry is not None: v_new = RegionalParameter(geometry.sfun) numpy_mpi.assign_to_vector( v_new.vector(), numpy_mpi.gather_vector(v.vector()), ) v = v_new ind_space = v.proj_space setattr(self, k, Function(ind_space, name=k)) mat = getattr(self, k) matfun = v.function mat.assign(project(matfun, ind_space)) else: if geometry is not None and v.ufl_element().cell() is not None: v_new = update_function(geometry.mesh, v) v = v_new setattr(self, k, v)
def copy(f, deepcopy=True, name="copied_function"): """ Copy a function. This is to ease the integration with dolfin adjoint where copied fuctions are annotated. """ if isinstance(f, (dolfin.Function, Function)): if has_dolfin_adjoint: try: return f.copy(deepcopy=deepcopy, name=name) except TypeError: return f.copy(deepcopy=deepcopy) else: return f.copy(deepcopy=deepcopy) elif isinstance(f, dolfin.Constant): return dolfin.Constant(f, name=name) elif isinstance(f, Constant): return Constant(f, name=name) elif isinstance(f, (float, int)): return f elif isinstance(f, Enlisted): lst = [] for fi in f: lst.append(copy(fi)) return enlist(lst) elif isinstance(f, (list, tuple)): lst = [] for fi in f: lst.append(copy(fi)) return tuple(lst) else: return f
def Wactive_orthotropic(Ta, C, f0, s0, n0): """Return active strain energy for an orthotropic active stress Arguments --------- Ta : dolfin.Function or dolfin.Constant A vector function representng the mangnitude of the active stress in the reference configuration (firt Pioala). Ta = (Ta_f0, Ta_s0, Ta_n0) C : ufl.Form The right Cauchy-Green deformation tensor f0 : dolfin.Function A vector function representng the direction of the first component s0 : dolfin.Function A vector function representng the direction of the second component n0 : dolfin.Function A vector function representng the direction of the third component """ I4f = dolfin.inner(C * f0, f0) I4s = dolfin.inner(C * s0, s0) I4n = dolfin.inner(C * n0, n0) I4 = dolfin.as_vector([I4f - 1, I4s - 1, I4n - 1]) return Constant(0.5) * dolfin.inner(Ta, I4)
def dirichlet_bc(W): V = W if W.sub(0).num_sub_spaces() == 0 else W.sub(0) return DirichletBC( V, Constant((0.0, 0.0, 0.0)), geometry.ffun, geometry.markers["BASE"][0], )
def I4(F, a0=None, isochoric=False): if a0 is not None: C = RightCauchyGreen(F, isochoric) I4 = inner(C * a0, a0) else: I4 = Constant(0.0) return I4
def assign_control(self, new_control): """ Assign a new value to the control """ for c, n in zip(self.control, new_control): try: c.assign(n) except TypeError: c.assign(Constant(n))
def fix_basal_plane(W): V = W if W.sub(0).num_sub_spaces() == 0 else W.sub(0) bc = DirichletBC( V.sub(0), Constant(0.0), geometry.ffun, geometry.markers["BASE"][0], ) return bc
def increment_control(self): for c, s in zip(self.control, self.step): if isinstance(c, (dolfin.Function, Function)): c_arr = numpy_mpi.gather_vector(c.vector()) c_tmp = Function(c.function_space()) c_tmp.vector()[:] = c_arr + s c.assign(c_tmp) else: c_arr = c c.assign(Constant(constant2float(c) + s))
def _init_spaces(self): mesh = self.geometry.mesh element = dolfin.VectorElement("P", mesh.ufl_cell(), 1) self.state_space = dolfin.FunctionSpace(mesh, element) self.state = Function(self.state_space) self.state_test = dolfin.TestFunction(self.state_space) # Add penalty factor self.kappa = Constant(1e3)
def increment_control(self): for c, s in zip(self.control, self.step): # if isinstance(s, (dolfin.Function, Function)) if isinstance(c, (dolfin.Function, Function)): c_arr = numpy_mpi.gather_vector(c.vector(), c.function_space().dim()) c_tmp = Function(c.function_space()) c_tmp.vector().set_local(np.array(c_arr + s)) c_tmp.vector().apply("") c.assign(c_tmp) else: c_arr = c c.assign(Constant(constant2float(c) + s))
def get_cavity_volume_form(mesh, u=None, xshift=0.0): from . import kinematics shift = Constant((xshift, 0.0, 0.0)) X = dolfin.SpatialCoordinate(mesh) - shift N = dolfin.FacetNormal(mesh) if u is None: vol_form = (-1.0 / 3.0) * dolfin.dot(X, N) else: F = kinematics.DeformationGradient(u) J = kinematics.Jacobian(F) vol_form = (-1.0 / 3.0) * dolfin.dot(X + u, J * dolfin.inv(F).T * N) return vol_form
def rigid_motion_term(mesh, u, r): position = dolfin.SpatialCoordinate(mesh) RM = [ Constant((1, 0, 0)), Constant((0, 1, 0)), Constant((0, 0, 1)), dolfin.cross(position, Constant((1, 0, 0))), dolfin.cross(position, Constant((0, 1, 0))), dolfin.cross(position, Constant((0, 0, 1))), ] return sum(dolfin.dot(u, zi) * r[i] * dolfin.dx for i, zi in enumerate(RM))
class DummyProblemParameters(FrozenClass): """ A set of parameters for a :class:`DummyProblem`. """ domain = None dt = None rho = 1000. # Finite element settings finite_element = staticmethod(finite_elements.p2p1) # Initial condition initial_condition = Constant((1e-16, 0, 0)) # Tidal farm tidal_farm = None functional_final_time_only = False
def test_pass_active_model_as_object(unitcube_geometry): activation = Constant(0.001) def dirichlet_bc(W): V = W if W.sub(0).num_sub_spaces() == 0 else W.sub(0) return DirichletBC(V, Constant((0.0, 0.0, 0.0)), fixed) bcs = BoundaryConditions(dirichlet=(dirichlet_bc,)) matparams = NeoHookean.default_parameters() material = NeoHookean( parameters=matparams, active_model="active_strain", activation=activation, ) problem = MechanicsProblem(unitcube_geometry, material, bcs) problem.solve() u, p = problem.state.split(deepcopy=True) assert np.linalg.norm(u.vector().get_local()) > 0
def test_active_contraction_yield_displacement(unitcube_geometry, active_model): activation = Constant(0.001) def dirichlet_bc(W): V = W if W.sub(0).num_sub_spaces() == 0 else W.sub(0) return DirichletBC(V, Constant((0.0, 0.0, 0.0)), fixed) bcs = BoundaryConditions(dirichlet=(dirichlet_bc,)) matparams = HolzapfelOgden.default_parameters() material = HolzapfelOgden( activation=activation, parameters=matparams, active_model=active_model, ) problem = MechanicsProblem(unitcube_geometry, material, bcs) problem.solve() u, p = problem.state.split(deepcopy=True) assert np.linalg.norm(u.vector().get_local()) > 0
def Wactive_transversally(Ta, C, f0, eta=0.0): """ Return active strain energy when activation is only working along the fibers, with a possible transverse component defined by eta Arguments --------- Ta : dolfin.Function or dolfin.Constant A scalar function representng the mangnitude of the active stress in the reference configuration (firt Pioala) C : ufl.Form The right Cauchy-Green deformation tensor f0 : dolfin.Function A vector function representng the direction of the active stress eta : float Amount of active stress in the transverse direction (relative to f0) """ I4f = dolfin.inner(C * f0, f0) I1 = dolfin.tr(C) return Constant(0.5) * Ta * ((I4f - 1) + eta * ((I1 - 3) - (I4f - 1)))
) fiber_space = dolfin.FunctionSpace(mesh, fiber_element) fiber = Function(fiber_space, "data/fiber.xml") microstructure = pulse.Microstructure(f0=fiber) # Create the geometry geometry = pulse.HeartGeometry( mesh=mesh, markers=markers, marker_functions=marker_functions, microstructure=microstructure, ) activation = Function(dolfin.FunctionSpace(geometry.mesh, "R", 0)) activation.assign(Constant(0.0)) matparams = pulse.HolzapfelOgden.default_parameters() material = pulse.HolzapfelOgden( activation=activation, parameters=matparams, f0=geometry.f0, ) # LV Pressure lvp = Constant(0.0) lv_marker = markers["ENDO"][0] lv_pressure = pulse.NeumannBC(traction=lvp, marker=lv_marker, name="lv") neumann_bc = [lv_pressure] # Add spring term at the base with stiffness 1.0 kPa/cm^2 base_spring = 1.0
def dirichlet_bc(W): V = W if W.sub(0).num_sub_spaces() == 0 else W.sub(0) return DirichletBC(V, Constant((0.0, 0.0, 0.0)), fixed)
# Create the geometry geometry = pulse.Geometry( mesh=mesh, marker_functions=marker_functions, microstructure=microstructure, ) # Use the default material parameters material_parameters = pulse.HolzapfelOgden.default_parameters() # Select model for active contraction active_model = pulse.ActiveModels.active_strain # active_model = "active_stress" # Set the activation activation = Constant(0.0) # Create material material = pulse.HolzapfelOgden( active_model=active_model, parameters=material_parameters, activation=activation, ) # Make Dirichlet boundary conditions def dirichlet_bc(W): V = W if W.sub(0).num_sub_spaces() == 0 else W.sub(0) return DirichletBC(V, Constant((0.0, 0.0, 0.0)), fixed)
def __init__(self, w=None, eta=None, dim=None, length=1): self.w = Constant(w, name="width") self.eta = Constant(eta, name="eta") self.t = Constant(length, name="length") W, eta, x1, x2, t = sp.symbols('w, eta, x[0], x[1], t') X, Z = ligaro(W, eta) if dim == 2: self.Gsub2 = None self.gamma_sp = gamma_sp = [X, Z] self.gamma = Expression(tuple([ccode(i) for i in gamma_sp]), eta=self.eta, w=self.w, degree=DEGREE, name="gamma") if dim == 3: self.gamma_sp = gamma_sp = [X, t * x2, Z] # fixed-end open surface volume correction # ONLY VALID FOR FIXED EDGES R = self.w / sqrt(2 * (1 - cos(self.eta))) Area = -0.5 * R**2 * (self.eta - sin(self.eta) ) # negative because upsidedown #TODO fix self.vol_correct = (Area * self.t / 3) self.gamma = Expression(tuple([ccode(i) for i in gamma_sp]), eta=self.eta, w=self.w, t=self.t, degree=DEGREE, name="gamma") self.Gsub1 = self.build_derivative([x1], name='Gsub1') if dim == 3: self.Gsub2 = self.build_derivative([x2], name='Gsub2') if ADJOINT: # adjoint gamma self.gamma.dependencies = [self.w, self.eta, self.t] self.gamma.user_defined_derivatives = {} self.gamma.user_defined_derivatives[ self.w] = self.build_derivative([W], name='d_gamma_dw') self.gamma.user_defined_derivatives[ self.eta] = self.build_derivative([eta], name='d_gamma_deta') self.gamma.user_defined_derivatives[ self.t] = self.build_derivative([t], name='d_gamma_dt') # adjoint Gsub1 self.Gsub1.dependencies = [self.w, self.eta, self.t] self.Gsub1.user_defined_derivatives = {} self.Gsub1.user_defined_derivatives[ self.w] = self.build_derivative([x1, W], name='d_Gsub1_dw') self.Gsub1.user_defined_derivatives[ self.eta] = self.build_derivative([x1, eta], name='d_Gsub1_deta') self.Gsub1.user_defined_derivatives[ self.t] = self.build_derivative([x1, t], name='d_Gsub1_dt') if dim == 3: # adjoint Gsub2 self.Gsub2.dependencies = [self.w, self.eta, self.t] self.Gsub2.user_defined_derivatives = {} self.Gsub2.user_defined_derivatives[ self.w] = self.build_derivative([x2, W], name='d_Gsub2_dw') self.Gsub2.user_defined_derivatives[ self.eta] = self.build_derivative([x2, eta], name='d_Gsub2_deta') self.Gsub2.user_defined_derivatives[ self.t] = self.build_derivative([x2, t], name='d_Gsub2_dt')
def test_material(unitcube_geometry, Material, active_model, isochoric): compressible_model = "incompressible" if active_model == "active_stress": active_value = 20.0 activation = Constant(1.0) T_ref = active_value def dirichlet_bc(W): V = W if W.sub(0).num_sub_spaces() == 0 else W.sub(0) return DirichletBC(V, Constant((0.0, 0.0, 0.0)), fixed) else: activation = Constant(0.0) active_value = 0.0 T_ref = 1.0 def dirichlet_bc(W): V = W if W.sub(0).num_sub_spaces() == 0 else W.sub(0) return DirichletBC(V.sub(0), Constant(0.0), fixed, "pointwise") neumann_bc = NeumannBC(traction=Constant(-active_value), marker=free_marker) bcs = BoundaryConditions(dirichlet=(dirichlet_bc,), neumann=(neumann_bc,)) matparams = Material.default_parameters() material = Material( activation=activation, parameters=matparams, T_ref=T_ref, isochoric=isochoric, compressible_model=compressible_model, active_model=active_model, ) assert material.isochoric == isochoric problem = MechanicsProblem(unitcube_geometry, material, bcs) problem.solve() u, p = problem.state.split(deepcopy=True) print(material.name) if active_model == "active_strain": tol = 1e-4 if not isochoric: if material.name in [ "guccione", "linear_elastic", "saint_venant_kirchhoff", ]: assert all(abs(p.vector().get_local()) < tol) elif material.name == "holzapfel_ogden": assert all(abs(p.vector().get_local() - material.parameters["a"]) < tol) elif material.name == "neo_hookean": assert all( abs(p.vector().get_local() - material.parameters["mu"]) < tol, ) else: raise TypeError(f"Unkown material {material.name}") else: assert all(abs(p.vector().get_local()) < tol) else: F = kinematics.DeformationGradient(u) T = material.CauchyStress(F, p) V_dg = dolfin.FunctionSpace(unitcube_geometry.mesh, "DG", 1) # Fiber on current geometry f = F * unitcube_geometry.f0 # Fiber stress Tf = dolfin.inner(T * f / f ** 2, f) Tf_dg = project(Tf, V_dg) tol = 1e-10 assert all(abs(Tf_dg.vector().get_local() - active_value) < tol) assert all(abs(u.vector().get_local()) < tol) if not isochoric: if material.name in [ "guccione", "linear_elastic", "saint_venant_kirchhoff", ]: assert all(abs(p.vector().get_local()) < tol) elif material.name == "holzapfel_ogden": assert all(abs(p.vector().get_local() - material.parameters["a"]) < tol) elif material.name == "neo_hookean": assert all( abs(p.vector().get_local() - material.parameters["mu"]) < tol, ) else: raise TypeError(f"Unkown material {material.name}") else: assert all(abs(p.vector().get_local()) < tol)
def dirichlet_bc(W): V = W if W.sub(0).num_sub_spaces() == 0 else W.sub(0) return DirichletBC(V.sub(0), Constant(0.0), fixed, "pointwise")
def compute_meshvolume(domain=None, dx=dolfin.dx, subdomain_id=None): return Constant( assemble( Constant(1.0) * dx(domain=domain, subdomain_id=subdomain_id), ), )