Source code for online.loading.macloadincrem

"""Loading path enforcement and incrementation.

This module includes several classes that control the general loading
incrementation flow, namely two classes that allow the enforcement of a
general non-mononotic loading path (composed of mononotic loading subpaths)
and two classes that allow rewinding the solution to a past loading increment.

Classes
-------
LoadingPath
    Loading incrementation flow.
LoadingSubpath
    Loading subpath.
IncrementRewinder
    Rewind analysis to rewind state increment (initial instant).
RewindManager
    Manage analysis rewind operations and evaluate analysis rewind criteria.
"""
#
#                                                                       Modules
# =============================================================================
# Standard
import copy
import time
# Third-party
import numpy as np
import anytree.walker
# Local
import ioput.info as info
import ioput.ioutilities as ioutil
import tensor.matrixoperations as mop
#
#                                                          Authorship & Credits
# =============================================================================
__author__ = 'Bernardo Ferreira (bernardo_ferreira@brown.edu)'
__credits__ = ['Bernardo Ferreira', ]
__status__ = 'Stable'
# =============================================================================
#
# =============================================================================
#
#                                                      Loading path and subpath
# =============================================================================
[docs]class LoadingPath: """Loading incrementation flow. This class contains a collection of loading subpaths, the current loading state and a set of methods to control the loading incrementation flow. Attributes ---------- _n_dim : int Problem number of spatial dimensions. _comp_order_sym : list[str] Strain/Stress components symmetric order. _comp_order_nsym : list[str] Strain/Stress components nonsymmetric order. _n_load_subpaths : int Number of loading subpaths. _load_subpaths : list List of LoadingSubpath. _conv_hom_state : dict Converged homogenized state (item, numpy.ndarray of shape (n_comps,)) for key in {'strain', 'stress'}. _is_last_inc : bool Loading last increment flag. _n_cinc_cuts : int Consecutive loading increment cuts counter. _increm_state : dict Increment state: key `inc` contains the current increment number (int), key `subpath_id` contains the current loading subpath index (int). Methods ------- new_load_increment(self) Setup new loading increment and get associated data. increment_cut(self, n_dim, comp_order) Perform loading increment cut and setup new increment. update_hom_state(self, hom_strain_mf, hom_stress_mf) Update converged homogenized state. get_subpath_state(self) Get current loading subpath state. get_increm_state(self) Get incremental state. _new_subpath(self) Add a new loading subpath to the loading path. _get_load_subpath(self) Get current loading subpath. _update_inc(self) Update loading increment counters. _get_applied_mac_load(self) Compute current applied loading. _get_inc_mac_load(self) Compute current incremental loading. _remove_sym(self, comp_order_sym, comp_order_nsym) Remove the symmetric components of loading related objects. _get_load_mf(n_dim, comp_order, load_vector) Get matricial form of load tensor given in vector form. """
[docs] def __init__(self, strain_formulation, problem_type, mac_load, mac_load_presctype, mac_load_increm, max_subinc_level=5, max_cinc_cuts=5): """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). mac_load : dict For each loading nature type (key, {'strain', 'stress'}), stores the loading constraints for each loading subpath in a numpy.ndarray (2d), where the i-th row is associated with the i-th strain/stress component and the j-th column is associated with the j-th loading subpath. mac_load_presctype : numpy.ndarray (2d) Loading nature type ({'strain', 'stress'}) associated with each loading constraint (numpy.ndarrayndarray of shape (n_comps, n_load_subpaths)), where the i-th row is associated with the i-th strain/stress component and the j-th column is associated with the j-th loading subpath. mac_load_increm : dict For each loading subpath id (key, str), stores a numpy.ndarray of shape (n_load_increments, 2) where each row is associated with a prescribed loading increment, and the columns 0 and 1 contain the corresponding incremental load factor and incremental time, respectively. max_subinc_level : int, default=5 Maximum level of loading subincrementation. max_cinc_cuts : int, default=5 Maximum number of consecutive load increment cuts. """ self._strain_formulation = strain_formulation self._problem_type = problem_type self._mac_load = mac_load self._mac_load_presctype = mac_load_presctype self._mac_load_increm = mac_load_increm self._max_subinc_level = max_subinc_level self._max_cinc_cuts = max_cinc_cuts # Get problem type parameters n_dim, comp_order_sym, comp_order_nsym = \ mop.get_problem_type_parameters(problem_type) self._n_dim = n_dim self._comp_order_sym = comp_order_sym self._comp_order_nsym = comp_order_nsym # Remove symmetric components under an infinitesimal strain formulation if strain_formulation == 'infinitesimal': self._remove_sym(comp_order_sym, comp_order_nsym) # Set total number of loading subpaths self._n_load_subpaths = len(mac_load_increm.keys()) # Initialize list of loading subpaths self._load_subpaths = [] # Initialize increment state self._increm_state = {'inc': 0, 'subpath_id': -1} # Initialize converged homogenized state self._conv_hom_state = {key: None for key in ['strain', 'stress']} # Initialize loading last increment flag self._is_last_inc = False # Initialize consecutive increment cuts counter self._n_cinc_cuts = 0
# -------------------------------------------------------------------------
[docs] def new_load_increment(self): """Setup new loading increment and get associated data. Returns ------- applied_mac_load_mf : dict For each prescribed loading nature type (key, {'strain', 'stress'}), stores the current applied loading constraints in a numpy.ndarray of shape (n_comps,). inc_mac_load_mf : dict For each loading nature type (key, {'strain', 'stress'}), stores the incremental loading constraint matricial form in a numpy.ndarray of shape (n_comps,). n_presc_strain : int Number of prescribed loading strain components. presc_strain_idxs : list[int] Prescribed loading strain components indexes. n_presc_stress : int Number of prescribed loading stress components. presc_stress_idxs : list[int] Prescribed loading stress components indexes. is_last_inc : bool Loading last increment flag. """ # Set strain/stress components order according to problem strain # formulation if self._strain_formulation == 'infinitesimal': comp_order = self._comp_order_sym elif self._strain_formulation == 'finite': comp_order = self._comp_order_nsym else: raise RuntimeError('Unknown problem strain formulation.') # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Reset consecutive loading increment cuts counter self._n_cinc_cuts = 0 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Add a new loading subpath to the loading path if either first load # increment or current loading subpath is completed if self._increm_state['inc'] == 0 \ or self._get_load_subpath()._is_last_subpath_inc: # Add a new loading subpath self._new_subpath() # Get current loading subpath load_subpath = self._get_load_subpath() # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Update loading increment self._update_inc() # Check if last loading increment if load_subpath._id == self._n_load_subpaths - 1 and \ load_subpath._is_last_subpath_inc: self._is_last_inc = True # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Compute current applied loading applied_mac_load = self._get_applied_mac_load() applied_mac_load_mf = {} for ltype in applied_mac_load.keys(): applied_mac_load_mf[ltype] = type(self)._get_load_mf( self._n_dim, comp_order, applied_mac_load[ltype]) # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Compute current incremental loading inc_mac_load = self._get_inc_mac_load() inc_mac_load_mf = {} for ltype in inc_mac_load.keys(): inc_mac_load_mf[ltype] = type(self)._get_load_mf( self._n_dim, comp_order, inc_mac_load[ltype]) # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Return return applied_mac_load_mf, inc_mac_load_mf, \ load_subpath._n_presc_strain, load_subpath._presc_strain_idxs, \ load_subpath._n_presc_stress, load_subpath._presc_stress_idxs, \ self._is_last_inc
# -------------------------------------------------------------------------
[docs] def increment_cut(self, n_dim, comp_order): """Perform loading increment cut and setup new increment. Parameters ---------- n_dim : int Problem dimension. comp_order : list[str] Strain/Stress components (str) order. Returns ------- applied_mac_load_mf : dict For each prescribed loading nature type (key, {'strain', 'stress'}), stores the current applied loading constraints in a numpy.ndarray of shape (n_comps,). inc_mac_load_mf : dict For each loading nature type (key, {'strain', 'stress'}), stores the incremental loading constraint matricial form in a numpy.ndarray of shape (n_comps,). n_presc_strain : int Number of prescribed macroscale loading strain components. presc_strain_idxs : list[int] Prescribed macroscale loading strain components indexes. n_presc_stress : int Number of prescribed macroscale loading stress components. presc_stress_idxs : list[int] Prescribed macroscale loading stress components indexes. is_last_inc : bool Loading last increment flag. """ # Get display features indent = ioutil.setdisplayfeatures()[2] # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Get current loading subpath load_subpath = self._get_load_subpath() # Perform loading increment load_subpath.increment_cut() # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Set last macroscale loading increment flag self._is_last_inc = False # Increment (+1) consecutive loading increment cuts counter self._n_cinc_cuts += 1 # Check if maximum number of consecutive loading increment cuts is # surpassed if self._n_cinc_cuts > self._max_cinc_cuts: summary = 'Maximum number of consecutive loading increment cuts' description = 'Maximum number of macroscale loading consecutive ' \ + 'increment cuts ({}) has been reached' + '\n' \ + indent + 'without solution convergence.' info.displayinfo('4', summary, description, self._max_cinc_cuts) # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Compute current applied loading applied_mac_load = self._get_applied_mac_load() applied_mac_load_mf = {} for ltype in applied_mac_load.keys(): applied_mac_load_mf[ltype] = type(self)._get_load_mf( self._n_dim, comp_order, applied_mac_load[ltype]) # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Compute incremental loading inc_mac_load = self._get_inc_mac_load() inc_mac_load_mf = {} for ltype in inc_mac_load.keys(): inc_mac_load_mf[ltype] = type(self)._get_load_mf( n_dim, comp_order, inc_mac_load[ltype]) # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ return applied_mac_load_mf, inc_mac_load_mf, \ load_subpath._n_presc_strain, load_subpath._presc_strain_idxs, \ load_subpath._n_presc_stress, load_subpath._presc_stress_idxs, \ self._is_last_inc
# -------------------------------------------------------------------------
[docs] def update_hom_state(self, hom_strain_mf, hom_stress_mf): """Update converged homogenized state. Parameters ---------- hom_strain_mf : numpy.ndarray (1d) Homogenized strain tensor stored in matricial form. hom_stress_mf : numpy.ndarray (1d) Homogenized stress tensor stored in matricial form. """ # Set strain/stress components order according to problem strain # formulation if self._strain_formulation == 'infinitesimal': comp_order = self._comp_order_sym elif self._strain_formulation == 'finite': comp_order = self._comp_order_nsym else: raise RuntimeError('Unknown problem strain formulation.') # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Build homogenized strain tensor hom_strain = mop.get_tensor_from_mf(hom_strain_mf, self._n_dim, comp_order) # Build homogenized stress tensor hom_stress = mop.get_tensor_from_mf(hom_stress_mf, self._n_dim, comp_order) # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Initialize converged homogenized strain and stress tensors vector # form self._conv_hom_state['strain'] = np.zeros(len(comp_order)) self._conv_hom_state['stress'] = np.zeros(len(comp_order)) # Loop over strain/stress components for k in range(len(comp_order)): # Get strain/stress component comp = comp_order[k] # Get component indexes i = int(comp[0]) - 1 j = int(comp[1]) - 1 # Build converged homogenized strain and stress tensors vector form self._conv_hom_state['strain'][k] = hom_strain[i, j] self._conv_hom_state['stress'][k] = hom_stress[i, j]
# -------------------------------------------------------------------------
[docs] def get_subpath_state(self): """Get current loading subpath state. Returns ------- id : int Loading subpath id. inc : int Current loading subpath increment counter. total_lfact : float Current loading subpath current total load factor. inc_lfact : float Current loading subpath current incremental load factor. total_time : float Current loading subpath current total time. inc_time : float Current loading subpath current incremental time. sub_inc_level : int Current loading subpath current subincrementation level. """ # Get current loading subpath load_subpath = self._get_load_subpath() # Return loading subpath state return load_subpath.get_state()
# -------------------------------------------------------------------------
[docs] def get_increm_state(self): """Get incremental state. Returns ------- increm_state : dict Increment state: key `inc` contains the current increment number, key `subpath_id` contains the current loading subpath index. """ return copy.deepcopy(self._increm_state)
# -------------------------------------------------------------------------
[docs] def _new_subpath(self): """Add a new loading subpath to the loading path.""" # Increment (+1) loading subpath id self._increm_state['subpath_id'] += 1 subpath_id = self._increm_state['subpath_id'] # Get load and prescription types of the current loading subpath presctype = self._mac_load_presctype[:, subpath_id] load = {key: np.zeros(self._mac_load[key].shape[0]) for key in self._mac_load.keys() if key in presctype} for ltype in load.keys(): load[ltype] = self._mac_load[ltype][:, 1 + subpath_id] # Get loading subpath incremental load factors and incremental times inc_lfacts = list(self._mac_load_increm[str(subpath_id)][:, 0]) inc_times = list(self._mac_load_increm[str(subpath_id)][:, 1]) # Add a new loading subpath self._load_subpaths.append( LoadingSubpath(subpath_id, self._strain_formulation, self._problem_type, self._conv_hom_state, load, presctype, inc_lfacts, inc_times, self._max_subinc_level))
# -------------------------------------------------------------------------
[docs] def _get_load_subpath(self): """Get current loading subpath. Returns ------- load_subpath : LoadingSubpath Current loading subpath. """ return self._load_subpaths[self._increm_state['subpath_id']]
# -------------------------------------------------------------------------
[docs] def _update_inc(self): """Update loading increment counters.""" # Increment (+1) global increment counter self._increm_state['inc'] += 1 # Increment (+1) loading subpath increment counter self._get_load_subpath().update_inc()
# -------------------------------------------------------------------------
[docs] def _get_applied_mac_load(self): """Compute current applied loading. Returns ------- applied_mac_load : dict For each prescribed loading nature type (key, {'strain', 'stress'}), stores the current applied loading constraints in a numpy.ndarray of shape (n_comps,). """ # Get current applied loading applied_mac_load = self._get_load_subpath().get_applied_load() # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ return applied_mac_load
# -------------------------------------------------------------------------
[docs] def _get_inc_mac_load(self): """Compute current incremental loading. Returns ------- inc_mac_load : dict For each loading nature type (key, {'strain', 'stress'}), stores the incremental loading constraint in a numpy.ndarray of shape (n_comps,). """ # Get current incremental loading inc_mac_load = self._get_load_subpath().get_inc_applied_load() # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ return inc_mac_load
# -------------------------------------------------------------------------
[docs] def _remove_sym(self, comp_order_sym, comp_order_nsym): """Remove the symmetric components of loading related objects. Under an infinitesimal strain formulation, remove the symmetric strain/stress components of loading related objects. In addition, the remaining independent components are sorted according to the problem strain/stress symmetric component order. ---- Parameters ---------- comp_order_sym : list[str] Symmetric strain/stress components (str) order. comp_order_nsym : list[str] Nonsymmetric strain/stress components (str) order. """ # Copy loading objects mac_load_cp = copy.deepcopy(self._mac_load) mac_load_presctype_cp = copy.deepcopy(self._mac_load_presctype) # Loop over symmetric components indexes for i in range(len(comp_order_sym)): # Get non-symmetric component index j = comp_order_nsym.index(comp_order_sym[i]) # Assemble symmetric components for ltype in self._mac_load.keys(): if ltype in self._mac_load_presctype: self._mac_load[ltype][i, :] = mac_load_cp[ltype][j, :] self._mac_load_presctype[i, :] = mac_load_presctype_cp[j, :] # Remove (non-symmetric) additional components n_sym = len(comp_order_sym) for ltype in self._mac_load.keys(): if ltype in self._mac_load_presctype: self._mac_load[ltype] = self._mac_load[ltype][0:n_sym, :] self._mac_load_presctype = self._mac_load_presctype[:n_sym, :]
# -------------------------------------------------------------------------
[docs] @staticmethod def _get_load_mf(n_dim, comp_order, load_vector): """Get matricial form of load tensor given in vector form. Parameters ---------- comp_order : list[str] Strain/Stress components (str) order. load_vector : numpy.ndarray (1d) Loading tensor in vector form (numpy.ndarray of shape (n_comps,)). Returns ------- load_mf : numpy.ndarray (1d) Loading tensor matricial form (numpy.ndarray of shape (n_comps,)). """ # Initialize incremental macroscale load tensor load_matrix = np.zeros((n_dim, n_dim)) # Build incremental macroscale load tensor for j in range(n_dim): for i in range(0, j + 1): load_matrix[i, j] = \ load_vector[comp_order.index(str(i + 1) + str(j + 1))] if i != j: if n_dim**2 == len(comp_order): load_matrix[j, i] = load_vector[ comp_order.index(str(j + 1) + str(i + 1))] else: load_matrix[j, i] = load_matrix[i, j] # Set incremental macroscopic load matricial form load_mf = mop.get_tensor_mf(load_matrix, n_dim, comp_order) # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ return load_mf
# ============================================================================= class LoadingSubpath: """Loading subpath. Attributes ---------- _n_dim : int Problem number of spatial dimensions. _comp_order_sym : list[str] Strain/Stress components symmetric order. _comp_order_nsym : list[str] Strain/Stress components nonsymmetric order. _inc : int Loading subpath increment counter. _total_lfact : float Loading subpath total load factor. _total_time : float Loading subpath total time. _n_presc_strain : int Number of prescribed loading strain components. _n_presc_stress : int Number of prescribed loading stress components. _presc_strain_idxs : list[int] Prescribed loading strain components indexes. _presc_stress_idxs : list[int] Prescribed loading stress components indexes. _applied_load : dict For each prescribed loading nature type (key, {'strain', 'stress'}), stores the current applied loading constraints in a numpy.ndarray of shape (n_comps,). _inc_applied_load : dict For each prescribed loading nature type (key, {'strain', 'stress'}), stores the current incremental applied loading constraints in a numpy.ndarray of shape (n_comps,). _is_last_subpath_inc : bool Loading subpath last increment flag. _sub_inc_levels : list History of subincrementation levels. Methods ------- get_state(self) Get loading subpath state data. update_inc(self) Update increment counter, total load factor and applied loading. increment_cut(self) Perform loading increment cut. get_applied_load(self) Get current applied loading. get_inc_applied_load(self) Get current incremental applied loading. _update_inc_applied_load(self) Update current incremental applied loading. """ def __init__(self, id, strain_formulation, problem_type, init_conv_hom_state, load, presctype, inc_lfacts, inc_times, max_subinc_level): """Constructor. Parameters ---------- id : int Loading subpath id. 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). init_conv_hom_state : dict Converged homogenized state (item, numpy.ndarray of shape (n_comps,)) for key in {'strain', 'stress'} at the beginning of loading subpath. load : dict For each prescribed loading nature type (key, {'strain', 'stress'}), stores the loading constraints in a numpy.ndarray of shape (n_comps,). presctype : numpy.ndarray (1d) Loading nature type ({'strain', 'stress'}) associated with each macroscale loading constraint (numpy.ndarray of shape (n_comps,)). inc_lfacts : numpy.ndarray (1d) Loading subpath incremental load factors (numpy.ndarray of shape (n_increments,)). inc_times : numpy.ndarray (1d) Loading subpath incremental times (numpy.ndarray of shape (n_increments,)). max_subinc_level : int Maximum level of loading subincrementation. """ self._id = id self._strain_formulation = strain_formulation self._problem_type = problem_type self._init_conv_hom_state = copy.deepcopy(init_conv_hom_state) self._load = copy.deepcopy(load) self._presctype = copy.deepcopy(presctype) self._inc_lfacts = copy.deepcopy(inc_lfacts) self._inc_times = copy.deepcopy(inc_times) self._max_subinc_level = max_subinc_level # Get problem type parameters n_dim, comp_order_sym, comp_order_nsym = \ mop.get_problem_type_parameters(problem_type) self._n_dim = n_dim self._comp_order_sym = comp_order_sym self._comp_order_nsym = comp_order_nsym # Initialize loading subpath increment counter self._inc = 0 # Initialize loading subpath total load factor self._total_lfact = 0 # Initialize loading subpath total time self._total_time = 0 # Set number of prescribed loading strain and stress components and # associated indexes self._n_presc_strain = sum([x == 'strain' for x in self._presctype]) self._n_presc_stress = sum([x == 'stress' for x in self._presctype]) self._presc_strain_idxs = [] self._presc_stress_idxs = [] for i in range(len(presctype)): if presctype[i] == 'strain': self._presc_strain_idxs.append(i) else: self._presc_stress_idxs.append(i) # Initialize current applied and incremental applied loading self._applied_load = {key: np.zeros(load[key].shape[0]) for key in load.keys()} self._inc_applied_load = {key: np.zeros(load[key].shape[0]) for key in load.keys()} # Initialize loading subpath last increment flag self._is_last_subpath_inc = False # Initialize subincrementation levels self._sub_inc_levels = [0]*len(self._inc_lfacts) # ------------------------------------------------------------------------- def get_state(self): """Get loading subpath state data. Returns ------- id : int Loading subpath id. inc : int Loading subpath increment counter. total_lfact : float Loading subpath current total load factor. inc_lfact : float Loading subpath current incremental load factor. total_time : float Loading subpath current total time. inc_time : float Loading subpath current incremental time. sub_inc_level : int Loading subpath current subincrementation level. """ # Get loading subpath current increment index inc_idx = self._inc - 1 # Return return self._id, self._inc, self._total_lfact, \ self._inc_lfacts[inc_idx], self._total_time, \ self._inc_times[inc_idx], self._sub_inc_levels[inc_idx] # ------------------------------------------------------------------------- def update_inc(self): """Update increment counter, total load factor and applied loading.""" # Increment (+1) loading subpath increment counter self._inc += 1 # Get loading subpath current increment index inc_idx = self._inc - 1 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Procedure related with the loading subincrementation: upon # convergence of a given increment, guarantee that the following # increment magnitude is at most one (subincrementation) level above. # The increment cut procedure is performed the required number of times # in order to ensure this progressive recovery towards the prescribed # incrementation if self._inc > 1: while self._sub_inc_levels[inc_idx - 1] \ - self._sub_inc_levels[inc_idx] >= 2: self.increment_cut() # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Update total load factor self._total_lfact = sum(self._inc_lfacts[0:self._inc]) # Update total time self._total_time = sum(self._inc_times[0:self._inc]) # Update current incremental applied loading self._update_inc_applied_load() # Check if last increment if self._inc == len(self._inc_lfacts): self._is_last_subpath_inc = True # ------------------------------------------------------------------------- def increment_cut(self): """Perform loading increment cut.""" # Get display features indent = ioutil.setdisplayfeatures()[2] # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Get loading subpath current increment index inc_idx = self._inc - 1 # Update subincrementation level self._sub_inc_levels[inc_idx] += 1 self._sub_inc_levels.insert(inc_idx + 1, self._sub_inc_levels[inc_idx]) # Check if maximum subincrementation level is surpassed if self._sub_inc_levels[inc_idx] > self._max_subinc_level: summary = 'Maximum loading subincrementation level' description = 'The maximum macroscale loading subincrementation ' \ + 'level ({}) has been reached without' + '\n' \ + indent + 'solution convergence.' info.displayinfo('4', summary, description, self._max_subinc_level) # Get current incremental load factor and associated incremental time inc_lfact = self._inc_lfacts[inc_idx] inc_time = self._inc_times[inc_idx] # Cut load increment in half self._inc_lfacts[inc_idx] = inc_lfact/2.0 self._inc_lfacts.insert(inc_idx + 1, self._inc_lfacts[inc_idx]) self._inc_times[inc_idx] = inc_time/2.0 self._inc_times.insert(inc_idx + 1, self._inc_times[inc_idx]) # Update total load factor and total time self._total_lfact = sum(self._inc_lfacts[0:self._inc]) self._total_time = sum(self._inc_times[0:self._inc]) # Update current incremental applied loading self._update_inc_applied_load() # Set loading subpath last increment flag self._is_last_subpath_inc = False # ------------------------------------------------------------------------- def get_applied_load(self): """Get current applied loading. Returns ------- applied_load : dict For each prescribed loading nature type (key, {'strain', 'stress'}), stores the current applied loading constraints in a numpy.ndarray of shape (n_comps,). """ return copy.deepcopy(self._applied_load) # ------------------------------------------------------------------------- def get_inc_applied_load(self): """Get current incremental applied loading. Returns ------- inc_applied_load : dict For each prescribed loading nature type (key, {'strain', 'stress'}), stores the current incremental applied loading constraints in a numpy.ndarray of shape (n_comps,). """ return copy.deepcopy(self._inc_applied_load) # ------------------------------------------------------------------------- def _update_inc_applied_load(self): """Update current incremental applied loading. *Infinitesimal strains:* .. math:: \\boldsymbol{\\varepsilon}_{n+1} = \\boldsymbol{\\varepsilon}_{0} + \\lambda_{n+1} (\\boldsymbol{\\varepsilon}^{\\text{total}} - \\boldsymbol{\\varepsilon}_{0}) .. math:: \\Delta \\boldsymbol{\\varepsilon}_{n+1} = \\Delta \\lambda_{n+1} (\\boldsymbol{\\varepsilon}^{ \\text{total}} - \\boldsymbol{\\varepsilon}_{0}) where :math:`\\boldsymbol{\\varepsilon}_{n+1}` is the current applied infinitesimal strain tensor, :math:`\\lambda_{n+1}` is the current load factor, :math:`\\boldsymbol{\\varepsilon}^{\\text{total}}` is the total infinitesimal strain tensor prescribed in the mononotic loading path, :math:`\\boldsymbol{\\varepsilon}_{0}` is the infinitesimal strain tensor at the beginning of the mononotic loading path, :math:`\\Delta \\boldsymbol{\\varepsilon}_{n+1}` is the incremental infinitesimal strain tensor, :math:`\\Delta \\lambda_{n+1}` is the incremental load factor, and :math:`n+1` denotes the current increment. .. math:: \\boldsymbol{\\sigma}_{n+1} = \\boldsymbol{\\sigma}_{0} + \\lambda_{n+1} (\\boldsymbol{\\sigma}^{\\text{total}} - \\boldsymbol{\\sigma}_{0}) .. math:: \\Delta \\boldsymbol{\\sigma}_{n+1} = \\Delta \\lambda_{n+1} (\\boldsymbol{\\sigma}^{\\text{total}} - \\boldsymbol{\\sigma}_{0}) where :math:`\\boldsymbol{\\sigma}_{n+1}` is the current applied Cauchy stress tensor, :math:`\\lambda_{n+1}` is the current load factor, :math:`\\boldsymbol{\\sigma}^{\\text{total}}` is the total Cauchy stress tensor prescribed in the mononotic loading path, :math:`\\boldsymbol{\\sigma}_{0}` is the Cauchy stress tensor at the beginning of the mononotic loading path, :math:`\\Delta \\boldsymbol{\\sigma}_{n+1}` is the incremental Cauchy stress tensor, :math:`\\Delta \\lambda_{n+1}` is the incremental load factor, and :math:`n+1` denotes the current increment. ---- *Finite strains:* .. math:: \\boldsymbol{F}_{n+1} = \\exp (\\lambda_{n+1} \\ln ( \\boldsymbol{F}^{\\text{total}} \\boldsymbol{F}_{0}^{-1})) \\boldsymbol{F}_{0} .. math:: \\boldsymbol{F}_{\\Delta, n+1} = \\exp (\\Delta \\lambda_{n+1} \\ln ( \\boldsymbol{F}^{\\text{total}} \\boldsymbol{F}_{0}^{-1})) where :math:`\\boldsymbol{F}_{n+1}` is the current applied deformation gradient, :math:`\\lambda_{n+1}` is the current load factor, :math:`\\boldsymbol{F}_{\\text{total}}` is the total deformation gradient prescribed in the mononotic loading path, and :math:`\\boldsymbol{F}_{0}` is the deformation gradient at the beginning of the mononotic loading path, :math:`\\boldsymbol{F}_{\\Delta, n+1}` is the incremental deformation gradient, :math:`\\Delta \\lambda_{n+1}` is the incremental load factor, and :math:`n+1` denotes the current increment. .. math:: \\boldsymbol{P}_{n+1} = \\boldsymbol{P}_{0} + \\lambda_{n+1} (\\boldsymbol{P}^{\\text{total}} - \\boldsymbol{P}_{0}) .. math:: \\Delta \\boldsymbol{P}_{n+1} = \\Delta \\lambda_{n+1} (\\boldsymbol{P}^{\\text{total}} - \\boldsymbol{P}_{0}) where :math:`\\boldsymbol{P}_{n+1}` is the current applied first Piola-Kirchhoff stress tensor, :math:`\\lambda_{n+1}` is the current load factor, :math:`\\boldsymbol{P}^{\\text{total}}` is the total first Piola-Kirchhoff stress tensor prescribed in the mononotic loading path, :math:`\\boldsymbol{P}_{0}` is the first Piola-Kirchhoff stress tensor at the beginning of the mononotic loading path, :math:`\\Delta \\boldsymbol{P}_{n+1}` is the incremental first Piola-Kirchhoff stress tensor, :math:`\\Delta \\lambda_{n+1}` is the incremental load factor, and :math:`n+1` denotes the current increment. **Remark**: It is not straightforward how to perform a component-wise multiplicative decomposition of the deformation gradient in the case of a mixed strain-stress loading prescription. """ # Get loading subpath current increment index inc_idx = self._inc - 1 # Get current incremental load factor and associated incremental time inc_lfact = self._inc_lfacts[inc_idx] # Evaluate prescription type is_strain_only = 'stress' not in self._inc_applied_load.keys() # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Update current incremental applied loading if self._strain_formulation == 'finite' and is_strain_only: # Initialize initial and total deformation gradient def_gradient_init = np.zeros((self._n_dim, self._n_dim)) def_gradient_total = np.zeros((self._n_dim, self._n_dim)) # Build initial and total deformation gradient for i in range(len(self._comp_order_nsym)): # Get component second-order index so_idx = tuple([int(x) - 1 for x in list(self._comp_order_nsym[i])]) # Build initial and total deformation gradient def_gradient_init[so_idx] = \ self._init_conv_hom_state['strain'][i] def_gradient_total[so_idx] = self._load['strain'][i] # Compute total incremental deformation gradient (multiplicative # decomposition) inc_def_gradient_total = np.matmul( def_gradient_total, np.linalg.inv(def_gradient_init)) # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Compute incremental deformation gradient relative to initial # deformation gradient (multiplicative decomposition) inc_init_def_gradient = mop.matrix_root(inc_def_gradient_total, self._total_lfact) # Compute current applied deformation gradient applied_def_gradient = np.matmul(inc_init_def_gradient, def_gradient_init) # Store current applied deformation gradient components for i in range(len(self._comp_order_nsym)): # Get component second-order index so_idx = tuple([int(x) - 1 for x in list(self._comp_order_nsym[i])]) # Store current applied deformation gradient component self._applied_load['strain'][i] = applied_def_gradient[so_idx] # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Compute current incremental deformation gradient (multiplicative # decomposition) inc_def_gradient = mop.matrix_root(inc_def_gradient_total, inc_lfact) # Store current incremental deformation gradient components for i in range(len(self._comp_order_nsym)): # Get component second-order index so_idx = tuple([int(x) - 1 for x in list(self._comp_order_nsym[i])]) # Store incremental deformation gradient component self._inc_applied_load['strain'][i] = inc_def_gradient[so_idx] else: # Loop over prescription types for ltype in self._inc_applied_load.keys(): # Loop over loading components for i in range(len(self._inc_applied_load[ltype])): # Compute current applied and incremental loading component # (additive decomposition) if self._presctype[i] == ltype: # Compute current applied loading component self._applied_load[ltype][i] = \ self._init_conv_hom_state[ltype][i] \ + self._total_lfact*( self._load[ltype][i] - self._init_conv_hom_state[ltype][i]) # Compute current incremental loading component self._inc_applied_load[ltype][i] = \ inc_lfact*(self._load[ltype][i] - self._init_conv_hom_state[ltype][i]) # # Loading path rewinder # =============================================================================
[docs]class IncrementRewinder: """Rewind analysis to rewind state increment (initial instant). Attributes ---------- _rewind_inc : int Increment associated with the rewind state. _loading_path : LoadingPath Loading path instance rewind state. _material_state : MaterialState CRVE material constitutive state at rewind state. _clusters_sct_mf : dict Fourth-order strain concentration tensor (matricial form) (item, numpy.ndarray (2d)) associated with each material cluster (key, str). _ref_material : ElasticReferenceMaterial Elastic reference material at rewind state. _global_strain_mf : numpy.ndarray (1d) Global vector of clusters strain tensors (matricial form). _farfield_strain_mf : numpy.ndarray (1d) Far-field strain tensor (matricial form). Methods ------- get_rewind_inc(self) Get increment associated with the rewind state. save_loading_path(self, loading_path) Save loading path rewind state. get_loading_path(self) Get loading path at rewind state. save_material_state(self, material_state) Save material constitutive state at rewind state. save_asca_algorithmic_variables(self, global_strain_mf, \ farfield_strain_mf) Save ASCA algorithmic variables at rewind state. get_asca_algorithmic_variables(self) Get ASCA algorithmic variables at rewind state. rewind_output_files(self, hres_output=None, efftan_output=None, \ ref_mat_output=None, voxels_output=None, \ adapt_output=None, vtk_output=None) """
[docs] def __init__(self, rewind_inc, phase_clusters): """Increment rewinder constructor. Parameters ---------- rewind_inc : int Increment associated with the rewind state. phase_clusters : dict Clusters labels (item, list[int]) associated with each material phase (key, str). """ self._rewind_inc = rewind_inc self._phase_clusters = copy.deepcopy(phase_clusters) # Initialize loading path at rewind state self._loading_path = None # Initialize material constitutive state at rewind state self._material_state = None # Initialize elastic reference material at rewind state self._ref_material = None # Initialize clusters strain concentration tensors at rewind state self._clusters_sct_mf = None # Initialize ASCA algorithmic variables self._global_strain_mf = None self._farfield_strain_mf = None
# -------------------------------------------------------------------------
[docs] def get_rewind_inc(self): """Get increment associated with the rewind state. Returns ------- rewind_inc : int Increment associated with the rewind state. """ return self._rewind_inc
# -------------------------------------------------------------------------
[docs] def save_loading_path(self, loading_path): """Save loading path rewind state. Parameters ---------- loading_path : LoadingPath LoadingPath instance. """ self._loading_path = copy.deepcopy(loading_path)
# -------------------------------------------------------------------------
[docs] def get_loading_path(self): """Get loading path at rewind state. Returns ------- loading_path : LoadingPath Loading path instance rewind state. """ return copy.deepcopy(self._loading_path)
# -------------------------------------------------------------------------
[docs] def save_material_state(self, material_state): """Save material constitutive state at rewind state. Parameters ---------- material_state : MaterialState CRVE material constitutive state at rewind state. """ self._material_state = copy.deepcopy(material_state)
# -------------------------------------------------------------------------
[docs] def get_material_state(self, crve): """Get material constitutive state at rewind state. Parameters ---------- crve : CRVE Cluster-Reduced Representative Volume Element. Returns ------- material_state : MaterialState CRVE material constitutive state at rewind state. """ # If the current CRVE clustering is coincident with the CRVE clustering # at the rewind state, simply return the material constitutive state # stored at rewind state. Otherwise, perform a suitable transfer of # state variables between the rewind state CRVE clustering and the # current CRVE clustering if self._phase_clusters == crve.get_cluster_phases(): # Return material constitutive state stored at rewind state return copy.deepcopy(self._material_state) # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ else: # Get clusters state variables at rewind state clusters_state_rew = self._material_state.get_clusters_state() # Get clusters deformation gradient at rewind state clusters_def_gradient_rew_mf = \ self._material_state.get_clusters_def_gradient_mf() # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Initialize clusters state variables clusters_state = {} # Initialize clusters deformation gradient clusters_def_gradient_mf = {} # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Get material phases material_phases = crve.get_material_phases() # Get cluster-reduced material phases cluster_phases = crve.get_cluster_phases() # Get clusters associated with each material phase phase_clusters = crve.get_phase_clusters() # Get clusters volume fraction clusters_vf = crve.get_clusters_vf() # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Loop over material phases for mat_phase in material_phases: # Get cluster-reduced material phase crmp = cluster_phases[mat_phase] # Get clustering type clustering_type = crmp.get_clustering_type() # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Proceed according to clustering type if clustering_type == 'static': # Loop over material phase clusters for cluster in phase_clusters[mat_phase]: # Set cluster state variables clusters_state[str(cluster)] = \ copy.deepcopy(clusters_state_rew[str(cluster)]) # Set cluster deformation gradient clusters_def_gradient_mf[str(cluster)] = copy.deepcopy( clusters_def_gradient_rew_mf[str(cluster)]) # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ elif clustering_type == 'adaptive': # Get cluster-reduced material phase clustering tree nodes clustering_tree_nodes, root_cluster_node = \ crmp.get_clustering_tree_nodes() # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Get rewind state cluster nodes rewind_clusters_nodes = [] for cluster in self._phase_clusters[mat_phase]: rewind_clusters_nodes.append( clustering_tree_nodes[str(cluster)]) # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Initialize node walker node_walker = anytree.walker.Walker() # Loop over material phase clusters for cluster in phase_clusters[mat_phase]: # Get cluster node cluster_node = clustering_tree_nodes[str(cluster)] # Build walk from cluster node up to the root node node_walk_to_root = node_walker.walk(cluster_node, root_cluster_node) # Loop over walk nodes for node in node_walk_to_root[0]: # Find hierarchicaly closest rewind state cluster # node if node in rewind_clusters_nodes: # Get node cluster parent_cluster = int(node.name) # Set cluster state variables clusters_state[str(cluster)] = copy.deepcopy( clusters_state_rew[str(parent_cluster)]) # Set cluster deformation gradient clusters_def_gradient_mf[str(cluster)] = \ copy.deepcopy(clusters_def_gradient_rew_mf[ str(parent_cluster)]) # Skip to the following cluster break # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ else: raise RuntimeError('Unknown material phase clustering ' 'type.') # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Set material constitutive state at rewind state according to the # current clustering self._material_state.set_rewind_state_updated_clustering( phase_clusters, clusters_vf, clusters_state, clusters_def_gradient_mf) # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Return material constitutive state stored at rewind state # according to the update clustering return copy.deepcopy(self._material_state)
# -------------------------------------------------------------------------
[docs] def save_reference_material(self, ref_material): """Save elastic reference material at rewind state. Parameters ---------- ref_material : ElasticReferenceMaterial Elastic reference material at rewind state. """ # Save elastic reference material self._ref_material = copy.deepcopy(ref_material)
# -------------------------------------------------------------------------
[docs] def get_reference_material(self): """Get elastic reference material at rewind state. Returns ------- ref_material : ElasticReferenceMaterial Elastic reference material at rewind state. """ return copy.deepcopy(self._ref_material)
# -------------------------------------------------------------------------
[docs] def save_clusters_sct(self, clusters_sct_mf): """Save clusters strain concentration tensors at rewind state. Parameters ---------- clusters_sct_mf : dict Fourth-order strain concentration tensor (matricial form) (item, numpy.ndarray (2d)) associated with each material cluster (key, str). """ # Save clusters state variables self._clusters_sct_mf = copy.deepcopy(clusters_sct_mf)
# -------------------------------------------------------------------------
[docs] def get_clusters_sct(self): """Get clusters strain concentration tensors at rewind state. Returns ------- clusters_sct_mf : dict Fourth-order strain concentration tensor (matricial form) (item, numpy.ndarray (2d)) associated with each material cluster (key, str). """ # Save clusters state variables return copy.deepcopy(self._clusters_sct_mf)
# -------------------------------------------------------------------------
[docs] def save_asca_algorithmic_variables(self, global_strain_mf, farfield_strain_mf): """Save ASCA algorithmic variables at rewind state. Parameters ---------- global_strain_mf : numpy.ndarray (1d) Global vector of clusters strain tensors (matricial form). farfield_strain_mf : numpy.ndarray (1d), default=None Far-field strain tensor (matricial form). """ # Save global vector of clusters strain tensors self._global_strain_mf = copy.deepcopy(global_strain_mf) # Save far-field strain tensor self._farfield_strain_mf = farfield_strain_mf
# -------------------------------------------------------------------------
[docs] def get_asca_algorithmic_variables(self): """Get ASCA algorithmic variables at rewind state. Returns ------- global_strain_mf : numpy.ndarray (1d) Global vector of clusters strain tensors (matricial form). farfield_strain_mf : numpy.ndarray (1d), default=None Far-field strain tensor (matricial form). """ return copy.deepcopy(self._global_strain_mf), \ copy.deepcopy(self._farfield_strain_mf)
# -------------------------------------------------------------------------
[docs] def rewind_output_files(self, hres_output=None, efftan_output=None, ref_mat_output=None, voxels_output=None, adapt_output=None, vtk_output=None): """Rewind output files to the rewind state. Parameters ---------- hres_output : HomResOutput Output associated with the homogenized results. efftan_output : EffTanOutput Output associated with the CRVE effective tangent modulus. ref_mat_output : RefMatOutput Output associated with the reference material. voxels_output : VoxelsOutput Output associated with voxels material-related quantities. adapt_output : ClusteringAdaptivityOutput Output associated with the clustering adaptivity procedures. vtk_output : VTKOutput Output associated with the VTK files. """ # Rewind output files if hres_output is not None: hres_output.rewind_file(self._rewind_inc) if efftan_output is not None: efftan_output.rewind_file(self._rewind_inc) if ref_mat_output is not None: ref_mat_output.rewind_file(self._rewind_inc) if voxels_output is not None: voxels_output.rewind_file(self._rewind_inc) if adapt_output is not None: adapt_output.rewind_file(self._rewind_inc) if vtk_output is not None: vtk_output.rewind_files(self._rewind_inc)
# =============================================================================
[docs]class RewindManager: """Manage analysis rewind operations and evaluate analysis rewind criteria. Attributes ---------- _n_rewinds : int Number of rewind operations. _rewind_time : float Total time spent in rewind operations and in deleted analysis increments. _init_time : float Reference time. Methods ------- get_rewind_time(self) Get total time of rewind operations and deleted analysis increments. update_rewind_time(self, mode='init') Update total rewind time. is_rewind_available(self) Evaluate if rewind operations are available. is_save_rewind_state(self, inc) Evaluate conditions to save rewind state. is_rewinding_criteria(self, inc, material_phases, phase_clusters, \ clusters_state) Check analysis rewinding criteria. get_save_rewind_state_criteria() Get available rewind state storage criteria and default parameters. get_rewinding_criteria() Get rewinding criteria and default parameters. """
[docs] def __init__(self, rewind_state_criterion, rewinding_criterion, max_n_rewinds=1): """Analysis rewind manager constructor. Parameters ---------- rewind_state_criterion : tuple Rewind state storage criterion [0] and associated parameter [1]. rewinding_criterion : tuple Rewinding criterion [0] and associated parameter [1]. max_n_rewinds : int, default=1 Maximum number of rewind operations. """ self._rewind_state_criterion = rewind_state_criterion self._rewinding_criterion = rewinding_criterion self._max_n_rewinds = max_n_rewinds # Initialize number of rewind operations self._n_rewinds = 0 # Initialize total rewind time self._rewind_time = 0
# -------------------------------------------------------------------------
[docs] def get_rewind_time(self): """Get total time of rewind operations and deleted analysis increments. Returns ------- rewind_time : float Total time of rewind operations and in deleted analysis increments. """ return self._rewind_time
# -------------------------------------------------------------------------
[docs] def update_rewind_time(self, mode='init'): """Update total rewind time. Parameters ---------- mode : {'init', 'update'}, default='init' """ if mode == 'init': # Set reference initial time self._init_time = time.time() elif mode == 'update': # Update total rewind time self._rewind_time += time.time() - self._init_time # Set reference initial time self._init_time = time.time() else: raise RuntimeError('Unknown mode.')
# -------------------------------------------------------------------------
[docs] def is_rewind_available(self): """Evaluate if rewind operations are available. Returns ------- is_available : bool True if rewind operations are available, False otherwise. """ # Initialize rewind operations availability is_available = True # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Evaluate maximum number of rewind operations if self._n_rewinds >= self._max_n_rewinds: is_available = False # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Return rewind operations availability return is_available
# -------------------------------------------------------------------------
[docs] def is_save_rewind_state(self, inc): """Evaluate conditions to save rewind state. Parameters ---------- inc : int Macroscale loading increment. Returns ------- is_save_state : bool True if conditions to save rewind state are satisfied, False otherwise. """ # Initialize save rewind state flag is_save_state = False # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Get rewind state criterion criterion = self._rewind_state_criterion[0] # Evaluate rewind state criterion if criterion == 'increment_number': # Evaluate increment number if inc == self._rewind_state_criterion[1]: is_save_state = True # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ else: raise RuntimeError('Unknown rewind state criterion.') # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Return save rewind state flag return is_save_state
# -------------------------------------------------------------------------
[docs] def is_rewinding_criteria(self, inc, material_phases, phase_clusters, clusters_state): """Check analysis rewinding criteria. Parameters ---------- inc : int Macroscale loading increment. material_phases : list[str] CRVE material phases labels (str). phase_clusters : dict Clusters labels (item, list[int]) associated with each material phase (key, str). clusters_state : dict Material constitutive model state variables (item, dict) associated with each material cluster (key, str). Returns ------- is_rewind : bool True if analysis rewinding criteria are satisfied, False otherwise. """ # Initialize analysis rewind flag is_rewind = False # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Get rewinding criterion criterion = self._rewinding_criterion[0] # Evaluate analysis rewinding criterion if criterion == 'increment_number': # Evaluate increment number is_rewind = inc == self._rewinding_criterion[1] # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ elif criterion == 'max_acc_p_strain': # Evaluate accumulated plastic strain threshold for mat_phase in material_phases: # Loop over material phase clusters for cluster in phase_clusters[mat_phase]: # Get cluster state variables state_variables = clusters_state[str(cluster)] # Check if accumulated plastic strain is cluster state # variable if 'acc_p_strain' not in state_variables: continue # Evaluate accumulated plastic strain if state_variables['acc_p_strain'] \ > self._rewinding_criterion[1]: is_rewind = True break if is_rewind: break # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ else: raise RuntimeError('Unknown rewinding criterion.') # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Increment number of rewind operations if is_rewind: self._n_rewinds += 1 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Return analysis rewinding flag return is_rewind
# -------------------------------------------------------------------------
[docs] @staticmethod def get_save_rewind_state_criteria(): """Get available rewind state storage criteria and default parameters. Returns ------- available_save_rewind_state_criteria : dict Available rewind state storage criteria (key, str) and associated default parameters (item). """ # Set available rewind state storage criteria available_save_rewind_state_criteria = {'increment_number': 0, } # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Return return available_save_rewind_state_criteria
# -------------------------------------------------------------------------
[docs] @staticmethod def get_rewinding_criteria(): """Get rewinding criteria and default parameters. Returns ------- available_rewinding_criteria : dict Available rewinding criteria (key, str) and associated default parameters (item). """ # Set available rewinding criteria available_rewinding_criteria = {'increment_number': 0, 'max_acc_p_strain': 1.0e-10} # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Return return available_rewinding_criteria