Source code for simulators.fetorch.material.models.standard.elastic

"""Linear elastic constitutive model.

This module includes the implementation of the linear elastic constitutive
model under general anisotropic elasticity. The corresponding class also
includes several methods to handle elastic anisotropy, namely to process the
specification of the elastic moduli and the computation of the elasticity
tensor.

Apart from a conversion to a PyTorch framework, the code is taken from the
package cratepy [#]_.

.. [#] Ferreira, B.P., Andrade Pires, F.M., and Bessa, M.A. (2023). CRATE: A
       Python package to perform fast material simulations. The Journal of Open
       Source Software, 8(87)
       (see `here <https://joss.theoj.org/papers/10.21105/joss.05594>`_)

Classes
-------
Elastic
    Elastic constitutive model.
"""
#
#                                                                       Modules
# =============================================================================
# Third-party
import torch
# Local
from simulators.fetorch.material.models.interface import ConstitutiveModel
from simulators.fetorch.math.tensorops import get_id_operators
from simulators.fetorch.math.matrixops import get_problem_type_parameters, \
    vget_tensor_mf, vget_state_3Dmf_from_2Dmf, vget_state_2Dmf_from_3Dmf, \
    kelvin_factor
#
#                                                          Authorship & Credits
# =============================================================================
__author__ = 'Bernardo Ferreira (bernardo_ferreira@brown.edu)'
__credits__ = ['Bernardo Ferreira', ]
__status__ = 'Stable'
# =============================================================================
#
# =============================================================================
[docs]class Elastic(ConstitutiveModel): """Linear elastic constitutive model. Attributes ---------- _name : str Material constitutive model name. _strain_type : {'infinitesimal', 'finite', 'finite-kinext'} Material constitutive model strain formulation: infinitesimal strain formulation ('infinitesimal'), finite strain formulation ('finite') or finite strain formulation through kinematic extension ('finite-kinext'). _model_parameters : dict Material constitutive model parameters. _n_dim : int Problem number of spatial dimensions. _comp_order_sym : tuple[str] Strain/Stress components symmetric order. _comp_order_nsym : tuple[str] Strain/Stress components nonsymmetric order. _device_type : {'cpu', 'cuda'} Type of device on which torch.Tensor is allocated. _device : torch.device Device on which torch.Tensor is allocated. Methods ------- get_required_model_parameters() Get required material constitutive model parameters. state_init(self) Get initialized material constitutive model state variables. state_update(self, inc_strain, state_variables_old) Perform material constitutive model state update. get_available_elastic_symmetries() Get available elastic symmetries under general anisotropy. elastic_tangent_modulus(elastic_properties, elastic_symmetry='isotropic') Compute 3D elasticity tensor under general anisotropic elasticity. get_technical_from_elastic_moduli(elastic_symmetry, elastic_properties) Get technical constants of elasticity from elastic moduli. """
[docs] def __init__(self, strain_formulation, problem_type, model_parameters, device_type='cpu'): """Constructor. Parameters ---------- strain_formulation: {'infinitesimal', 'finite'} Problem strain formulation. problem_type : int Problem type: 2D plane strain (1), 2D plane stress (2), 2D axisymmetric (3) and 3D (4). model_parameters : dict Material constitutive model parameters. device_type : {'cpu', 'cuda'}, default='cpu' Type of device on which torch.Tensor is allocated. """ # Set material constitutive model name self._name = 'elastic' # Set constitutive model strain formulation self._strain_type = 'finite-kinext' # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Set initialization parameters self._strain_formulation = strain_formulation self._problem_type = problem_type self._model_parameters = model_parameters # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Set device self.set_device(device_type) # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Get problem type parameters self._n_dim, self._comp_order_sym, self._comp_order_nsym = \ get_problem_type_parameters(problem_type) # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Get elastic symmetry elastic_symmetry = self._model_parameters['elastic_symmetry'] # Check finite strains formulation if (self._strain_formulation == 'finite' and elastic_symmetry != 'isotropic'): raise RuntimeError('The elastic constitutive model is only ' 'available under finite strains for the ' 'elastic isotropic case.') # Compute technical constants of elasticity if elastic_symmetry == 'isotropic': # Compute technical constants of elasticity technical_constants = Elastic.get_technical_from_elastic_moduli( elastic_symmetry, self._model_parameters) # Assemble technical constants of elasticity self._model_parameters.update(technical_constants) else: raise RuntimeError(f'Technical constants are not implemented for ' f'{elastic_symmetry} material.')
# -------------------------------------------------------------------------
[docs] @staticmethod def get_required_model_parameters(): """Get required material constitutive model parameters. Model parameters: - 'elastic_symmetry' : Elastic symmetry (str, {'isotropic', 'transverse_isotropic', 'orthotropic', 'monoclinic', 'triclinic'}); - 'elastic_moduli' : Elastic moduli (dict, {'Eijkl': float}); - 'euler_angles' : Euler angles (degrees) sorted according with Bunge convention (tuple[float]). Returns ------- model_parameters_names : tuple[str] Material constitutive model parameters names (str). """ # Set material properties names model_parameters_names = ('elastic_symmetry', 'elastic_moduli', 'euler_angles') # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ return model_parameters_names
# -------------------------------------------------------------------------
[docs] def state_init(self): """Get initialized material constitutive model state variables. Constitutive model state variables: * ``e_strain_mf`` * *Infinitesimal strains*: Elastic infinitesimal strain tensor (matricial form). * *Finite strains*: Elastic spatial logarithmic strain tensor (matricial form). * *Symbol*: :math:`\\boldsymbol{\\varepsilon^{e}}` / :math:`\\boldsymbol{\\varepsilon^{e}}` * ``strain_mf`` * *Infinitesimal strains*: Infinitesimal strain tensor (matricial form). * *Finite strains*: Spatial logarithmic strain tensor (matricial form). * *Symbol*: :math:`\\boldsymbol{\\varepsilon}` / :math:`\\boldsymbol{\\varepsilon}` * ``stress_mf`` * *Infinitesimal strains*: Cauchy stress tensor (matricial form). * *Finite strains*: Kirchhoff stress tensor (matricial form) within :py:meth:`state_update`, first Piola-Kirchhoff stress tensor (matricial form) otherwise. * *Symbol*: :math:`\\boldsymbol{\\sigma}` / (:math:`\\boldsymbol{\\tau}`, :math:`\\boldsymbol{P}`) * ``is_su_fail`` * State update failure flag. ---- Returns ------- state_variables_init : dict Initialized material constitutive model state variables. """ # Initialize constitutive model state variables state_variables_init = dict() # Initialize strain tensors state_variables_init['e_strain_mf'] = vget_tensor_mf( torch.zeros((self._n_dim, self._n_dim), device=self._device), self._n_dim, self._comp_order_sym) state_variables_init['strain_mf'] = \ state_variables_init['e_strain_mf'].clone() # Initialize stress tensors if self._strain_formulation == 'infinitesimal': # Cauchy stress tensor (symmetric) state_variables_init['stress_mf'] = vget_tensor_mf( torch.zeros((self._n_dim, self._n_dim), device=self._device), self._n_dim, self._comp_order_sym) else: # First Piola-Kirchhoff stress tensor (nonsymmetric) state_variables_init['stress_mf'] = vget_tensor_mf( torch.zeros((self._n_dim, self._n_dim), device=self._device), self._n_dim, self._comp_order_nsym) # Initialize state flags state_variables_init['is_plast'] = False state_variables_init['is_su_fail'] = False # Set additional out-of-plane strain and stress components if self._problem_type == 1: state_variables_init['e_strain_33'] = 0.0 state_variables_init['stress_33'] = 0.0 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ return state_variables_init
# -------------------------------------------------------------------------
[docs] def state_update(self, inc_strain, state_variables_old): """Perform material constitutive model state update. Parameters ---------- inc_strain : torch.Tensor(2d) Incremental strain second-order tensor. state_variables_old : dict Last converged constitutive model material state variables. Returns ------- state_variables : dict Material constitutive model state variables. consistent_tangent_mf : torch.Tensor(2d) Material constitutive model consistent tangent modulus stored in matricial form. """ # Get last increment converged state variables e_strain_old_mf = state_variables_old['e_strain_mf'] if self._problem_type == 1: e_strain_33_old = state_variables_old['e_strain_33'] # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Set state update failure flag is_su_fail = False # # State update & Consistent tangent modulus # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Build incremental strain tensor matricial form inc_strain_mf = vget_tensor_mf(inc_strain, self._n_dim, self._comp_order_sym, device=self._device) # # 2D > 3D conversion # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # When the problem type corresponds to a 2D analysis, perform the state # update and consistent tangent computation as in the 3D case, # considering the appropriate out-of-plain strain and stress components if self._problem_type == 4: comp_order_sym = self._comp_order_sym else: # Set 3D problem parameters _, comp_order_sym, _ = get_problem_type_parameters(4) # Build strain tensors (matricial form) by including the # appropriate out-of-plain components inc_strain_mf = vget_state_3Dmf_from_2Dmf( inc_strain_mf, comp_33=0.0, device=self._device) e_strain_old_mf = vget_state_3Dmf_from_2Dmf( e_strain_old_mf, e_strain_33_old, self._device) # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Update elastic strain e_strain_mf = e_strain_old_mf + inc_strain_mf # Compute 3D elasticity tensor (matricial form) consistent_tangent_mf = Elastic.elastic_tangent_modulus( self._model_parameters, elastic_symmetry=self._model_parameters['elastic_symmetry'], device=self._device) # Update stress stress_mf = torch.matmul(consistent_tangent_mf, e_strain_mf) # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Get the out-of-plane strain and stress components if self._problem_type == 1: e_strain_33 = e_strain_mf[comp_order_sym.index('33')] stress_33 = stress_mf[comp_order_sym.index('33')] # # 3D > 2D Conversion # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # When the problem type corresponds to a 2D analysis, build the # 2D strain and stress tensors (matricial form) once the state update # has been performed if self._problem_type == 1: # Builds 2D strain and stress tensors (matricial form) from the # associated 3D counterparts e_strain_mf = vget_state_2Dmf_from_3Dmf( e_strain_mf, device=self._device) stress_mf = vget_state_2Dmf_from_3Dmf( stress_mf, device=self._device) # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # When the problem type corresponds to a 2D analysis, build the # 2D consistent tangent modulus (matricial form) from the # 3D counterpart if self._problem_type == 1: consistent_tangent_mf = vget_state_2Dmf_from_3Dmf( consistent_tangent_mf, device=self._device) # # Update state variables # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Initialize state variables dictionary state_variables = self.state_init() # Store updated state variables state_variables['e_strain_mf'] = e_strain_mf state_variables['strain_mf'] = e_strain_mf state_variables['stress_mf'] = stress_mf state_variables['is_su_fail'] = is_su_fail if self._problem_type == 1: state_variables['e_strain_33'] = e_strain_33 state_variables['stress_33'] = stress_33 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ return state_variables, consistent_tangent_mf
# -------------------------------------------------------------------------
[docs] @staticmethod def get_available_elastic_symmetries(): """Get available elastic symmetries under general anisotropy. Returns ------- elastic_symmetries : dict Elastic moduli (item, tuple[str]) required for each available elastic symmetry (key, str). """ # Set available elastic symmetries and required elastic moduli elastic_symmetries = { 'isotropic': ('E1111', 'E1122'), 'transverse_isotropic': ('E1111', 'E3333', 'E1122', 'E1133', 'E2323'), 'orthotropic': ('E1111', 'E2222', 'E3333', 'E1122', 'E1133', 'E2233', 'E1212', 'E2323', 'E1313'), 'monoclinic': ('E1111', 'E2222', 'E3333', 'E1122', 'E1133', 'E2233', 'E1112', 'E2212', 'E3312', 'E1212', 'E2323', 'E1313', 'E2313'), 'triclinic': ('E1111', 'E1122', 'E1133', 'E1112', 'E1123', 'E1113', 'E2222', 'E2233', 'E2212', 'E2223', 'E2213', 'E3333', 'E3312', 'E3323', 'E3313', 'E1212', 'E1223', 'E1213', 'E2323', 'E2313', 'E1313')} # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ return elastic_symmetries
# -------------------------------------------------------------------------
[docs] @staticmethod def elastic_tangent_modulus(elastic_properties, elastic_symmetry='isotropic', device=None): """Compute 3D elasticity tensor under general anisotropic elasticity. Parameters ---------- elastic_properties : dict Elastic material properties (key, str) values (item, float). Expecting independent elastic moduli ('Eijkl') according to elastic symmetries. Young modulus ('E') and Poisson's ratio ('v') may alternatively be provided under elastic isotropy. elastic_symmetry : {'isotropic', 'transverse_isotropic', \ 'orthotropic', 'monoclinic', 'triclinic'}, \ default='isotropic' Elastic symmetries: * 'triclinic': assumes no elastic symmetries. * 'monoclinic': assumes plane of symmetry 12. * 'orthotropic': assumes planes of symmetry 12 and 13. * 'transverse_isotropic': assumes axis of symmetry 3 * 'isotropic': assumes complete symmetry. device : torch.device, default=None Device on which torch.Tensor is allocated. Returns ------- elastic_tangent_mf : torch.Tensor(2d) 3D elasticity tensor in matricial form. """ # Get 3D problem parameters n_dim, comp_order_sym, _ = get_problem_type_parameters(problem_type=4) # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Get available elastic symmetries and required elastic moduli elastic_symmetries = Elastic.get_available_elastic_symmetries() # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Compute 3D elasticity tensor matricial form if elastic_symmetry not in elastic_symmetries.keys(): raise RuntimeError('Unavailable elastic symmetry.') # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ elif elastic_symmetry == 'isotropic' and \ {'E', 'v'}.issubset(set(elastic_properties.keys())): # Set required fourth-order tensors _, _, _, fosym, fodiagtrace, _, _ = \ get_id_operators(n_dim, device=device) # Get Young modulus and Poisson coefficient E = elastic_properties['E'] v = elastic_properties['v'] # Compute Lamé parameters lam = (E*v)/((1.0 + v)*(1.0 - 2.0*v)) miu = E/(2.0*(1.0 + v)) # Compute elasticity tensor elastic_tangent = lam*fodiagtrace + 2.0*miu*fosym # Build elasticity tensor matricial form elastic_tangent_mf = vget_tensor_mf(elastic_tangent, n_dim, comp_order_sym, device=device) # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ else: # Get required elastic moduli required_moduli = elastic_symmetries[elastic_symmetry] # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Initialize elastic moduli elastic_moduli = {} # Extract elastic moduli from elastic properties for param in elastic_properties.keys(): if param in elastic_symmetries['triclinic']: elastic_moduli[param] = elastic_properties[param] # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Initialize independent elastic moduli ind_moduli = {str(modulus): 0.0 for modulus in elastic_symmetries['triclinic']} # Loop over required elastic moduli for modulus in required_moduli: # Set symmetric modulus sym_modulus = modulus[3:5] + modulus[1:3] # Check if requires elastic modulus has been provided if modulus in elastic_moduli.keys(): ind_moduli[modulus] = elastic_moduli[modulus] elif sym_modulus in elastic_moduli.keys(): ind_moduli[modulus] = elastic_moduli[sym_modulus] else: raise RuntimeError('Missing elastic moduli for ' + elastic_symmetry + ' material.') # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Set all (non-symmetric) elastic moduli all_moduli = {str(modulus): 0.0 for modulus in ind_moduli.keys()} if elastic_symmetry == 'isotropic': all_moduli['E1111'] = ind_moduli['E1111'] all_moduli['E2222'] = all_moduli['E1111'] all_moduli['E3333'] = all_moduli['E1111'] all_moduli['E1122'] = ind_moduli['E1122'] all_moduli['E1133'] = all_moduli['E1122'] all_moduli['E2233'] = all_moduli['E1122'] all_moduli['E1212'] = \ 0.5*(all_moduli['E1111'] - all_moduli['E1122']) all_moduli['E2323'] = all_moduli['E1212'] all_moduli['E1313'] = all_moduli['E1212'] elif elastic_symmetry == 'transverse_isotropic': all_moduli['E1111'] = ind_moduli['E1111'] all_moduli['E2222'] = all_moduli['E1111'] all_moduli['E3333'] = ind_moduli['E3333'] all_moduli['E1122'] = ind_moduli['E1122'] all_moduli['E1133'] = ind_moduli['E1133'] all_moduli['E2233'] = all_moduli['E1133'] all_moduli['E1212'] = \ 0.5*(all_moduli['E1111'] - all_moduli['E1122']) all_moduli['E2323'] = ind_moduli['E2323'] all_moduli['E1313'] = all_moduli['E2323'] elif elastic_symmetry == 'orthotropic': all_moduli['E1111'] = ind_moduli['E1111'] all_moduli['E2222'] = ind_moduli['E2222'] all_moduli['E3333'] = ind_moduli['E3333'] all_moduli['E1122'] = ind_moduli['E1122'] all_moduli['E1133'] = ind_moduli['E1133'] all_moduli['E2233'] = ind_moduli['E2233'] all_moduli['E1212'] = ind_moduli['E1212'] all_moduli['E2323'] = ind_moduli['E2323'] all_moduli['E1313'] = ind_moduli['E1313'] elif elastic_symmetry == 'monoclinic': all_moduli['E1111'] = ind_moduli['E1111'] all_moduli['E2222'] = ind_moduli['E2222'] all_moduli['E3333'] = ind_moduli['E3333'] all_moduli['E1122'] = ind_moduli['E1122'] all_moduli['E1133'] = ind_moduli['E1133'] all_moduli['E2233'] = ind_moduli['E2233'] all_moduli['E1212'] = ind_moduli['E1212'] all_moduli['E2323'] = ind_moduli['E2323'] all_moduli['E1313'] = ind_moduli['E1313'] all_moduli['E1112'] = ind_moduli['E1112'] all_moduli['E2212'] = ind_moduli['E2212'] all_moduli['E3312'] = ind_moduli['E3312'] all_moduli['E2313'] = ind_moduli['E2313'] elif elastic_symmetry == 'triclinic': all_moduli = ind_moduli # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Initialize elasticity tensor elastic_tangent_mf = torch.zeros(2*(len(comp_order_sym),), device=device) # Build elasticity tensor according with elastic symmetry for modulus in all_moduli.keys(): # Get elastic modulus second-order indexes and associated # Kelvin factors idx_1 = comp_order_sym.index(modulus[1:3]) kf_1 = kelvin_factor(idx_1, comp_order_sym) idx_2 = comp_order_sym.index(modulus[3:5]) kf_2 = kelvin_factor(idx_2, comp_order_sym) # Assemble elastic modulus in elasticity tensor matricial form elastic_tangent_mf[idx_1, idx_2] = \ kf_1*kf_2*all_moduli[modulus] # Set symmetric component of elasticity tensor if idx_1 != idx_2: elastic_tangent_mf[idx_2, idx_1] = \ elastic_tangent_mf[idx_1, idx_2] # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ return elastic_tangent_mf
# -------------------------------------------------------------------------
[docs] def get_technical_from_elastic_moduli(elastic_symmetry, elastic_properties): """Get technical constants of elasticity from elastic moduli. Parameters ---------- elastic_symmetry : {'isotropic', 'transverse_isotropic', \ 'orthotropic', 'monoclinic', 'triclinic'}, \ default='isotropic' Elastic symmetries: * 'triclinic': assumes no elastic symmetries. * 'monoclinic': assumes plane of symmetry 12. * 'orthotropic': assumes planes of symmetry 12 and 13. * 'transverse_isotropic': assumes axis of symmetry 3 * 'isotropic': assumes complete symmetry. elastic_properties : dict Elastic material properties (key, str) values (item, float). Expecting independent elastic moduli ('Eijkl') according to elastic symmetries. Returns ------- technical_constants : dict Technical constants of elasticity according with elastic symmetries. """ # Get available elastic symmetries and required elastic moduli elastic_symmetries = Elastic.get_available_elastic_symmetries() # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Check elastic symmetry and required elastic moduli if elastic_symmetry not in elastic_symmetries.keys(): raise RuntimeError('Unavailable elastic symmetry.') else: # Get required elastic moduli required_moduli = elastic_symmetries[elastic_symmetry] # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Initialize independent elastic moduli ind_moduli = {str(modulus): 0.0 for modulus in elastic_symmetries['triclinic']} # Check elastic moduli if not (elastic_symmetry == 'isotropic' and {'E', 'v'}.issubset(set(elastic_properties.keys()))): # Loop over required elastic moduli for modulus in required_moduli: # Set symmetric modulus sym_modulus = modulus[3:5] + modulus[1:3] # Check if requires elastic modulus has been provided if modulus in elastic_properties.keys(): ind_moduli[modulus] = elastic_properties[modulus] elif sym_modulus in elastic_properties.keys(): ind_moduli[modulus] = elastic_properties[sym_modulus] else: raise RuntimeError('Missing elastic moduli for ' + elastic_symmetry + ' material.') # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Initialize technical constants of elasticity technical_constants = {} # Compute technical constants of elasticity according with elastic # symmetries if elastic_symmetry == 'isotropic': if {'E', 'v'}.issubset(set(elastic_properties.keys())): # Assemble technical constants of elasticity technical_constants['E'] = elastic_properties['E'] technical_constants['v'] = elastic_properties['v'] else: # Get required elastic moduli E1111 = ind_moduli['E1111'] E1122 = ind_moduli['E1122'] # Compute Young's modulus E = (1.0/(E1111 + E1122))*(E1111**2 + E1111*E1122 - 2.0*E1122**2) # Compute Poisson's coefficient v = E1122/(E1111 + E1122) # Assemble technical constants of elasticity technical_constants['E'] = E technical_constants['v'] = v else: raise RuntimeError('Technical constants are not implemented for ' + elastic_symmetry + ' material.') # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ return technical_constants