Source code for hyperelastic.models.invariants._torch

[docs] class TorchModel: r"""Isotropic hyperelastic material formulation based on a given strain energy density function ``fun(I1, I2, I3, **kwargs)`` per unit undeformed volume. The gradients are carried out by automatic differentiation using PyTorch. .. math:: \psi = \psi(I_1, I_2, I_3) .. note:: PyTorch uses single-precision by default. This must be considered in numeric simulations, i.e. the error tolerance should not exceed ``np.sqrt(torch.finfo(torch.float).eps)`` (approx. ``tol=5e-4``). For double- precision, enable ``torch.float64`` as default. .. code-block:: python import torch torch.set_default_dtype(torch.float64) Examples -------- >>> import hyperelastic >>> def yeoh(I1, I2, I3, C10, C20, C30): >>> "Yeoh isotropic hyperelastic material formulation." >>> return C10 * (I1 - 3) + C20 * (I1 - 3) ** 2 + C30 * (I1 - 3) ** 3 >>> model = hyperelastic.models.invariants.TorchModel( >>> yeoh, C10=0.5, C20=-0.05, C30=0.02 >>> ) >>> framework = hyperelastic.InvariantsFramework(model) >>> umat = hyperelastic.DistortionalSpace(framework) """ def __init__(self, fun, **kwargs): self.fun = fun self.kwargs = kwargs def _grad(self, fun, x, numpy=False, allow_unused=True, **kwargs): "Return the gradient of a sum of a tensor ``fun`` w.r.t. ``x``." import torch out = None if hasattr(fun, "grad_fn"): out = torch.autograd.grad( fun.sum(), x, allow_unused=allow_unused, **kwargs )[0] if numpy: if out is not None: return out.detach().numpy() return out def _astensors(self, *args): "Convert a list/tuple of arrays to torch-tensors." import torch tensors = [torch.Tensor(arg) for arg in args] [tensor.requires_grad_(True) for tensor in tensors] return tensors
[docs] def gradient( self, I1, I2, I3, statevars, tensor=False, numpy=True, create_graph=False, retain_graph=False, ): """The gradient as the partial derivative of the strain energy function w.r.t. the invariants.""" if not tensor: I1, I2, I3 = self._astensors(I1, I2, I3) f = self.fun(I1, I2, I3, **self.kwargs) kwargs = dict(numpy=numpy, create_graph=create_graph, retain_graph=retain_graph) return (*[self._grad(f, x, **kwargs) for x in [I1, I2, I3]], statevars)
[docs] def hessian(self, I1, I2, I3, statevars, numpy=True): """The hessian as the second partial derivatives of the strain energy function w.r.t. the invariants.""" I1, I2, I3 = self._astensors(I1, I2, I3) dWdI1, dWdI2, dWdI3, statevars = self.gradient( I1, I2, I3, statevars, tensor=True, create_graph=True, numpy=False ) return [ self._grad(f, x, numpy=numpy) for f, x in zip( [dWdI1, dWdI2, dWdI3, dWdI1, dWdI2, dWdI1], [I1, I2, I3, I2, I3, I3] ) ]