"""Clustering adaptivity.
This module includes the class that has major control over the clustering
adaptivity procedures. It also includes a class that handles the clustering
adaptivity output file.
The so-called adaptive clustering-based reduced-order models were introduced
by Ferreira et. al (2022) [#]_.
.. [#] Ferreira, B.P., Andrade Pires, F.M. and Bessa, M.A. (2022).
*Adaptivity for clustering-based reduced-order modeling of
localized history-dependent phenomena.* Comp Methods Appl M, 393
(see `here <https://www.sciencedirect.com/science/article/pii/
S0045782522000895?via%3Dihub>`_)
Classes
-------
AdaptivityManager
CRVE clustering adaptivity manager.
ClusteringAdaptivityOutput
Clustering adaptivity output.
"""
#
# Modules
# =============================================================================
# Standard
import copy
import time
import re
# Third-party
import numpy as np
# Local
import ioput.info as info
import tensor.matrixoperations as mop
import ioput.ioutilities as ioutil
from material.materialoperations import compute_spatial_log_strain, \
cauchy_from_first_piola, \
MaterialQuantitiesComputer
from clustering.adaptivity.adaptivity_criterion import \
AdaptiveClusterGrouping, SpatialDiscontinuities
#
# Authorship & Credits
# =============================================================================
__author__ = 'Bernardo Ferreira (bernardo_ferreira@brown.edu)'
__credits__ = ['Bernardo Ferreira', ]
__status__ = 'Stable'
# =============================================================================
#
# =============================================================================
[docs]class AdaptivityManager:
"""CRVE clustering adaptivity manager.
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.
_adapt_phase_criterions : dict
Clustering adaptivity criterion instance (item, AdaptivityCriterion)
associated with each material phase (key, str).
_inc_adaptive_steps : dict
For each macroscale loading increment (key, str), store the performed
number of clustering adaptive steps (item, int).
_adapt_ref_init_inc : dict
Clustering adaptivity reference initial increment (item, int)
associated with each material phase (key, str).
_adapt_feature_min_trigger : dict
Clustering adaptivity feature minimum significant value surpassed
status (item, bool) associated with each material phase (key, str).
max_inc_adaptive_steps : int
Maximum number of clustering adaptive steps per macroscale loading
increment.
adaptive_evaluation_time : float
Total amount of time (s) spent in selecting target clusters for
clustering adaptivity.
adaptive_time : float
Total amount of time (s) spent in clustering adaptivity procedures.
Methods
-------
get_adaptivity_criterions()
Get available clustering adaptivity criterions.
get_target_clusters(self, phase_clusters, voxels_clusters, clusters_state,\
clusters_def_gradient_mf, \
clusters_def_gradient_old_mf, clusters_state_old, \
clusters_sct_mf, clusters_sct_old_mf, \
clusters_residuals_mf, inc=None, verbose=False)
Get adaptive clustering target clusters.
_get_adaptivity_data_matrix(self, target_phase, adapt_control_feature, \
phase_clusters, clusters_def_gradient_mf, \
clusters_def_gradient_old_mf, \
clusters_state, clusters_state_old, \
clusters_sct_mf, clusters_sct_old_mf, \
clusters_residuals_mf)
Build adaptivity feature data matrix for target material phase.
adaptive_refinement(self, crve, material_state, target_clusters, \
target_clusters_data, inc, improved_init_guess=None, \
verbose=False)
Perform CRVE adaptive clustering refinement.
check_inc_adaptive_steps(self, inc)
Check number of adaptive steps performed in loading increment.
clear_inc_adaptive_steps(self, inc_threshold)
Reset number of adaptive steps performed after threshold increment.
is_adapt_phase_activated(self, mat_phase, inc)
Check if material phase adaptivity procedures are activated.
reset_adapt_activation_parameters(self)
Reset parameters associated with clustering adaptivity activation.
get_adapt_vtk_array(self, voxels_clusters)
Get regular grid array with the adaptive level of each cluster.
"""
[docs] def __init__(self, strain_formulation, problem_type, adapt_material_phases,
phase_clusters, adaptivity_control_feature,
adapt_criterion_data, clust_adapt_freq):
"""Constructor.
Parameters
----------
strain_formulation: {'infinitesimal', 'finite'}
Problem strain formulation.
problem_type : int
Problem type identifier (1 - Plain strain (2D), 4- Tridimensional)
adapt_material_phases : list[str]
RVE adaptive material phases labels (str).
phase_clusters : dict
Clusters labels (item, list[int]) associated with each material
phase (key, str).
adaptivity_control_feature : dict
Clustering adaptivity control feature (item, str) associated with
each material phase (key, str).
adapt_criterion_data : dict
Clustering adaptivity criterion (item, dict) associated with each
material phase (key, str). This dictionary contains the adaptivity
criterion to be used and the required parameters.
clust_adapt_freq : dict
Clustering adaptivity frequency (relative to the macroscale
loading) (item, int, default=1) associated with each adaptive
cluster-reduced material phase (key, str).
"""
self._strain_formulation = strain_formulation
self._problem_type = problem_type
self._adapt_material_phases = copy.deepcopy(adapt_material_phases)
self._adaptivity_control_feature = \
copy.deepcopy(adaptivity_control_feature)
self._adapt_criterion_data = copy.deepcopy(adapt_criterion_data)
self._clust_adapt_freq = copy.deepcopy(clust_adapt_freq)
self._inc_adaptive_steps = {}
self._adapt_ref_init_inc = {mat_phase: 0
for mat_phase in adapt_material_phases}
self._adapt_feature_min_trigger = {mat_phase: False for mat_phase
in adapt_material_phases}
self.max_inc_adaptive_steps = 1
self.adaptive_evaluation_time = 0
self.adaptive_time = 0
# 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
# Loop over adaptive material phases
self._adapt_phase_criterions = {}
for mat_phase in adapt_material_phases:
# Get adaptive material phase clustering adaptivity criterion
adapt_criterion = adapt_criterion_data[mat_phase]['criterion']
# Initialize adaptive material phase clustering adaptivity
# criterion
if adapt_criterion is AdaptiveClusterGrouping:
# Get clustering adaptivity criterion parameters
adapt_trigger_ratio = \
adapt_criterion_data[mat_phase]['adapt_trigger_ratio']
adapt_max_level = \
adapt_criterion_data[mat_phase]['adapt_max_level']
adapt_min_voxels = \
adapt_criterion_data[mat_phase]['adapt_min_voxels']
adapt_split_threshold = \
adapt_criterion_data[mat_phase]['adapt_split_threshold']
is_merge_adapt_groups = bool(adapt_criterion_data[mat_phase][
'is_merge_adapt_groups'])
min_adapt_feature_val = \
adapt_criterion_data[mat_phase]['min_adapt_feature_val']
# Initialize clustering adaptivity criterion
self._adapt_phase_criterions[mat_phase] = \
AdaptiveClusterGrouping(
mat_phase, phase_clusters,
adapt_trigger_ratio=adapt_trigger_ratio,
adapt_split_threshold=adapt_split_threshold,
adapt_max_level=adapt_max_level,
adapt_min_voxels=adapt_min_voxels,
is_merge_adapt_groups=is_merge_adapt_groups,
min_adapt_feature_val=min_adapt_feature_val)
elif adapt_criterion == SpatialDiscontinuities:
# Get clustering adaptivity criterion parameters
adapt_trigger_ratio = \
adapt_criterion_data[mat_phase]['adapt_trigger_ratio']
adapt_max_level = \
adapt_criterion_data[mat_phase]['adapt_max_level']
adapt_min_voxels = \
adapt_criterion_data[mat_phase]['adapt_min_voxels']
adapt_level_max_diff = \
adapt_criterion_data[mat_phase]['adapt_level_max_diff']
swipe_dim_1_every = \
adapt_criterion_data[mat_phase]['swipe_dim_1_every']
swipe_dim_2_every = \
adapt_criterion_data[mat_phase]['swipe_dim_2_every']
swipe_dim_3_every = \
adapt_criterion_data[mat_phase]['swipe_dim_3_every']
min_adapt_feature_val = \
adapt_criterion_data[mat_phase]['min_adapt_feature_val']
magnitude_lower_factor = \
adapt_criterion_data[mat_phase]['magnitude_lower_factor']
# Initialize clustering adaptivity criterion
self._adapt_phase_criterions[mat_phase] = \
SpatialDiscontinuities(
mat_phase, phase_clusters,
adapt_trigger_ratio=adapt_trigger_ratio,
adapt_max_level=adapt_max_level,
adapt_min_voxels=adapt_min_voxels,
adapt_level_max_diff=adapt_level_max_diff,
swipe_dim_1_every=swipe_dim_1_every,
swipe_dim_2_every=swipe_dim_2_every,
swipe_dim_3_every=swipe_dim_3_every,
min_adapt_feature_val=min_adapt_feature_val,
magnitude_lower_factor=magnitude_lower_factor)
else:
raise RuntimeError('Unknown clustering adaptivity criterion.')
# -------------------------------------------------------------------------
[docs] @staticmethod
def get_adaptivity_criterions():
"""Get available clustering adaptivity criterions.
Returns
-------
available_adapt_criterions : dict
Available clustering adaptivity criterions
(item, AdaptivityCriterion) and associated identifiers (key, str).
"""
# Set available clustering adaptivity criterions
available_adapt_criterions = {'1': AdaptiveClusterGrouping,
'2': SpatialDiscontinuities}
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Return
return available_adapt_criterions
# -------------------------------------------------------------------------
[docs] def get_target_clusters(self, phase_clusters, voxels_clusters,
clusters_state, clusters_def_gradient_mf,
clusters_def_gradient_old_mf, clusters_state_old,
clusters_sct_mf, clusters_sct_old_mf,
clusters_residuals_mf, inc=None, verbose=False):
"""Get adaptive clustering target clusters.
Parameters
----------
phase_clusters : dict
Clusters labels (item, list[int]) associated with each material
phase (key, str).
clusters_def_gradient_mf : dict
Deformation gradient (item, numpy.ndarray (1d)) associated with
each material cluster (key, str), stored in matricial form.
clusters_def_gradient_old_mf : dict
Last converged deformation gradient (item, numpy.ndarray (1d))
associated with each material cluster (key, str), stored in
matricial form.
clusters_state : dict
Material constitutive model state variables (item, dict) associated
with each material cluster (key, str).
clusters_state_old : dict
Last increment converged material constitutive model state
variables (item, dict) associated with each material cluster
(key, str).
clusters_sct_mf : dict
Fourth-order strain concentration tensor (matricial form)
(item, numpy.ndarray) associated with each material cluster
(key, str).
clusters_sct_old_mf : dict
Last increment converged fourth-order strain concentration tensor
(matricial form) (item, numpy.ndarray) associated with each
material cluster (key, str).
clusters_residuals_mf : dict
Equilibrium residual second-order tensor (matricial form)
(item, numpy.ndarray) associated with each material cluster
(key, str).
inc : int, default=None
Incremental counter serving as a reference basis for the clustering
adaptivity frequency control.
verbose : bool, default=False
Enable verbose output.
Returns
-------
is_trigger : bool
True if clustering adaptivity is triggered, False otherwise.
target_clusters : list[int]
List containing the labels (int) of clusters to be adapted.
"""
init_time = time.time()
# Output execution data
if verbose:
info.displayinfo('5', 'Clustering adaptivity criterion:')
info.displayinfo('5', 'Selecting target clusters...', 2)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Initialize target clusters list
target_clusters = []
# Initialize target clusters data
target_clusters_data = {}
# Loop over adaptive material phases
for mat_phase in self._adapt_material_phases:
# Check material phase adaptivity activation if clustering
# adaptivity feature minimum significant value has already been
# surpassed
if self._adapt_feature_min_trigger[mat_phase]:
if not self.is_adapt_phase_activated(mat_phase, inc):
continue
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Get adaptivity feature
adapt_control_feature = self._adaptivity_control_feature[mat_phase]
# Build adaptivity feature data matrix
adapt_data_matrix = self._get_adaptivity_data_matrix(
mat_phase, adapt_control_feature, phase_clusters,
clusters_def_gradient_mf, clusters_def_gradient_old_mf,
clusters_state, clusters_state_old, clusters_sct_mf,
clusters_sct_old_mf, clusters_residuals_mf)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Get material phase clustering adaptivity criterion
adapt_criterion = self._adapt_phase_criterions[mat_phase]
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Check clustering adaptivity feature minimum significant value
# surpassed status if hasn't been previously triggered
if not self._adapt_feature_min_trigger[mat_phase]:
# Get clustering adaptivity feature minimum significant value
min_sign_value = adapt_criterion.get_min_adapt_feature_val()
# Check clustering adaptivity feature minimum significant value
if np.max(adapt_data_matrix[:, 1]) >= min_sign_value:
# Set clustering adaptivity reference initial increment
self._adapt_ref_init_inc[mat_phase] = inc
# Update clustering adaptivity feature minimum significant
# value surpassed status
self._adapt_feature_min_trigger[mat_phase] = True
else:
continue
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Get material phase clustering adaptivity target clusters
if isinstance(adapt_criterion, AdaptiveClusterGrouping):
phase_target_clusters, phase_target_clusters_data = \
adapt_criterion.get_target_clusters(adapt_data_matrix,
voxels_clusters)
elif isinstance(adapt_criterion, SpatialDiscontinuities):
phase_target_clusters, phase_target_clusters_data = \
adapt_criterion.get_target_clusters(adapt_data_matrix,
voxels_clusters)
# Update list of target clusters and associated data
target_clusters += phase_target_clusters
target_clusters_data.update(phase_target_clusters_data)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Set adaptivity trigger flag according to the list of target clusters
if target_clusters:
is_trigger = True
else:
is_trigger = False
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Update total amount of time spent in selecting target clusters for
# clustering adaptivity
self.adaptive_evaluation_time += time.time() - init_time
# Update total amount of time spent in clustering adaptivity procedures
self.adaptive_time += time.time() - init_time
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Output execution data
if verbose:
# Build output data
output_list = []
output_total = 0
for mat_phase in self._adapt_material_phases:
phase_target_clusters = list(set(target_clusters).intersection(
phase_clusters[mat_phase]))
output_list += [mat_phase, len(phase_target_clusters)]
output_total += len(phase_target_clusters)
# Output adaptive phases target clusters summary table
indent = 10*' '
info.displayinfo(
'5', 'Summary:' + '\n\n'
+ indent + 'Phase Target Clusters' + '\n'
+ indent + 23*'-' + '\n'
+ ((indent + '{:^5s}{:>11d}\n')
* (len(self._adapt_material_phases))).format(*output_list)
+ indent + 23*'-' + '\n'
+ indent + '{:^5s}'.format('Total')
+ '{:>11d}'.format(output_total) + '\n',
2)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
return is_trigger, target_clusters, target_clusters_data
# -------------------------------------------------------------------------
[docs] def _get_adaptivity_data_matrix(self, target_phase, adapt_control_feature,
phase_clusters, clusters_def_gradient_mf,
clusters_def_gradient_old_mf,
clusters_state, clusters_state_old,
clusters_sct_mf, clusters_sct_old_mf,
clusters_residuals_mf):
"""Build adaptivity feature data matrix for target material phase.
Parameters
----------
target_phase : str
Target adaptive material phase whose clusters adaptive feature data
is collected or computed.
adapt_control_feature : str
Scalar adaptivity feature available directly or indirectly from
clusters state variables.
phase_clusters : dict
Clusters labels (item, list[int]) associated with each material
phase (key, str).
clusters_def_gradient_mf : dict
Deformation gradient (item, numpy.ndarray (1d)) associated with
each material cluster (key, str), stored in matricial form.
clusters_def_gradient_old_mf : dict
Last converged deformation gradient (item, numpy.ndarray (1d))
associated with each material cluster (key, str), stored in
matricial form.
clusters_state : dict
Material constitutive model state variables (item, dict) associated
with each material cluster (key, str).
clusters_state_old : dict
Last increment converged material constitutive model state
variables (item, dict) associated with each material cluster
(key, str).
clusters_sct_mf : dict
Fourth-order strain concentration tensor (matricial form)
(item, numpy.ndarray (2d)) associated with each material cluster
(key, str).
clusters_sct_old_mf : dict
Last increment converged fourth-order strain concentration tensor
(matricial form) (item, numpy.ndarray (2d)) associated with each
material cluster (key, str).
clusters_residuals_mf : dict
Equilibrium residual second-order tensor (matricial form)
(item, numpy.ndarray (1d)) associated with each material cluster
(key, str).
Returns
-------
adapt_data_matrix : numpy.ndarray (2d)
Adaptivity feature data matrix (numpy.ndarray of shape
(adapt_phase_n_clusters, 2)) that, for the i-th cluster of the
adaptive material phase, contains the cluster label in
adapt_data_matrix[i, 0] and the associated adaptive feature value
in adapt_data_matrix[i, 1].
"""
# Get target adaptive material phase number of clusters
n_clusters = len(phase_clusters[target_phase])
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Initialize adaptivity feature data matrix
adapt_data_matrix = np.zeros((n_clusters, 2))
adapt_data_matrix[:, 0] = phase_clusters[target_phase]
# Get target material phase state variables
state_variables = \
clusters_state[str(int(adapt_data_matrix[0, 0]))].keys()
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Evaluate nature of adaptivity feature
is_state_variable = False
for state_variable in state_variables:
if state_variable in adapt_control_feature:
is_state_variable = True
break
is_component = False
is_norm = False
is_incremental = False
if re.search('_[1-3][1-3]$', adapt_control_feature):
# If adaptivity feature is component of second order tensor
is_component = True
# Get second order tensor component and matricial form index
feature_comp = adapt_control_feature[-2:]
# Trim adaptivity feature
adapt_control_feature = adapt_control_feature[:-3]
if re.search('_norm$', adapt_control_feature):
# If considering the norm of the tensorial adaptivity feature
is_norm = True
# Trim adaptivity feature
adapt_control_feature = adapt_control_feature[:-5]
if re.search('^inc_', adapt_control_feature):
# If considering the incremental value of the adaptivity feature
is_incremental = True
# Trim adaptivity feature
adapt_control_feature = adapt_control_feature[4:]
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Clustering adaptivity feature is scalar state variable
if is_state_variable and not is_component:
# Check if adaptivity feature is scalar
if not ioutil.checknumber(
clusters_state[str(int(adapt_data_matrix[0, 0]))][
adapt_control_feature]) and not is_norm:
raise RuntimeError('The clustering adaptivity feature ('
+ adapt_control_feature + ') prescribed '
+ 'for material phase ' + target_phase
+ ' must be a scalar.')
else:
# Build adaptivity feature data matrix
for i in range(n_clusters):
# Get cluster label
cluster = int(adapt_data_matrix[i, 0])
# Collect adaptivity feature data
if is_norm:
if is_incremental:
adapt_data_matrix[i, 1] = \
np.linalg.norm(clusters_state[str(cluster)]
[adapt_control_feature]) - \
np.linalg.norm(clusters_state_old[str(cluster)]
[adapt_control_feature])
else:
adapt_data_matrix[i, 1] = \
np.linalg.norm(clusters_state[str(cluster)]
[adapt_control_feature])
else:
if is_incremental:
adapt_data_matrix[i, 1] = \
clusters_state[str(cluster)][
adapt_control_feature] - clusters_state_old[
str(cluster)][adapt_control_feature]
else:
adapt_data_matrix[i, 1] = clusters_state[
str(cluster)][adapt_control_feature]
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Clustering adaptivity feature is component of strain/stress related
# second order tensor (stored in matricial form) state variable
elif is_state_variable and is_component:
# Check if adaptivity feature is scalar
if ioutil.checknumber(clusters_state[
str(int(adapt_data_matrix[0, 0]))][adapt_control_feature]):
raise RuntimeError('The clustering adaptivity feature ('
+ adapt_control_feature + ') prescribed '
+ 'for material material phase '
+ target_phase + ' is a scalar.')
# Build adaptivity feature data matrix
for i in range(n_clusters):
# Get cluster label
cluster = int(adapt_data_matrix[i, 0])
# Get second order tensor matricial form index
if len(clusters_state[str(cluster)][
adapt_control_feature]) == self._comp_order_nsym:
index = self._comp_order_nsym.index(feature_comp)
elif len(clusters_state[str(cluster)][
adapt_control_feature]) == self._comp_order_sym:
if feature_comp in self._comp_order_sym:
index = self._comp_order_sym.index(feature_comp)
elif feature_comp[::-1] in self._comp_order_sym:
index = self._comp_order_sym.index(feature_comp)
else:
raise RuntimeError('Invalid component specified for '
'the clustering adaptivity '
'feature '
+ adapt_control_feature + '.')
else:
raise RuntimeError('Clustering adaptivity feature '
'is not a second-order tensor stored '
'in matricial form as expected.')
# Collect adaptivity feature data
if is_incremental:
adapt_data_matrix[i, 1] = \
clusters_state[str(cluster)][
adapt_control_feature][index] \
- clusters_state_old[str(cluster)][
adapt_control_feature][index]
else:
adapt_data_matrix[i, 1] = clusters_state[
str(cluster)][adapt_control_feature][index]
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Clustering adaptivity feature is Von Mises equivalent stress
elif adapt_control_feature == 'vm_stress':
# Instantiate material state computations
csbvar_computer = MaterialQuantitiesComputer()
# Build adaptivity feature data matrix
for i in range(n_clusters):
# Get cluster label
cluster = int(adapt_data_matrix[i, 0])
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Get Cauchy stress tensor
if self._strain_formulation == 'infinitesimal':
# Get cluster stress tensor (matricial form)
stress_mf = clusters_state[str(cluster)]['stress_mf']
else:
# Get cluster deformation gradient (matricial form)
def_gradient_mf = clusters_def_gradient_mf[str(cluster)]
# Build deformation gradient
def_gradient = mop.get_tensor_from_mf(
def_gradient_mf, self._n_dim, self._comp_order_nsym)
# Get first Piola-Kirchhoff stress tensor (matricial form)
first_piola_stress_mf = \
clusters_state[str(cluster)]['stress_mf']
# Build first Piola-Kirchhoff stress tensor
first_piola_stress = mop.get_tensor_from_mf(
first_piola_stress_mf, self._n_dim,
self._comp_order_sym)
# Compute Cauchy stress tensor
cauchy_stress = cauchy_from_first_piola(def_gradient,
first_piola_stress)
# Get Cauchy stress tensor (matricial form)
stress_mf = mop.get_tensor_mf(cauchy_stress, self._n_dim,
self._comp_order_sym)
# Build 3D stress tensor (matricial form)
if self._problem_type == 1:
# Get Cauchy stress tensor out-of-plain component
if self._strain_formulation == 'infinitesimal':
stress_33 = clusters_state[str(cluster)]['stress_33']
else:
# Get Cauchy stress tensor out-of-plain component from
# first Piola-Kirchhoff counterpart
stress_33 = (1.0/np.linalg.det(def_gradient))\
* clusters_state[str(cluster)]['stress_33']
# Build 3D stress tensor (matricial form)
stress_mf = mop.getstate3Dmffrom2Dmf(self._problem_type,
stress_mf, stress_33)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Compute von Mises equivalent stress
vm_stress = csbvar_computer.get_vm_stress(stress_mf)
# Assemble adaptivity feature data matrix
if is_incremental:
# Get cluster previously converged stress tensor (matricial
# form)
if self._strain_formulation == 'infinitesimal':
# Get cluster stress tensor (matricial form)
stress_mf = \
clusters_state_old[str(cluster)]['stress_mf']
else:
# Get cluster deformation gradient (matricial form)
def_gradient_mf = \
clusters_def_gradient_old_mf[str(cluster)]
# Build deformation gradient
def_gradient = mop.get_tensor_from_mf(
def_gradient_mf, self._n_dim,
self._comp_order_nsym)
# Get first Piola-Kirchhoff stress tensor (matricial
# form)
first_piola_stress_mf = \
clusters_state_old[str(cluster)]['stress_mf']
# Build first Piola-Kirchhoff stress tensor
first_piola_stress = mop.get_tensor_from_mf(
first_piola_stress_mf, self._n_dim,
self._comp_order_sym)
# Compute Cauchy stress tensor
cauchy_stress = cauchy_from_first_piola(
def_gradient, first_piola_stress)
# Get Cauchy stress tensor (matricial form)
stress_mf = mop.get_tensor_mf(cauchy_stress,
self._n_dim,
self._comp_order_sym)
# Build 3D stress tensor (matricial form)
if self._problem_type == 1:
# Get Cauchy stress tensor out-of-plain component
if self._strain_formulation == 'infinitesimal':
stress_33 = \
clusters_state_old[str(cluster)]['stress_33']
else:
# Get Cauchy stress tensor out-of-plain component
# from first Piola-Kirchhoff counterpart
stress_33 = (1.0/np.linalg.det(def_gradient))\
* clusters_state_old[str(cluster)]['stress_33']
# Build 3D stress tensor (matricial form)
stress_mf = mop.getstate3Dmffrom2Dmf(
self._problem_type, stress_mf, stress_33)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Compute previously converged von Mises equivalent stress
vm_stress_old = csbvar_computer.get_vm_stress(stress_mf)
# Assemble incremental adaptivity feature data matrix
adapt_data_matrix[i, 1] = vm_stress - vm_stress_old
else:
adapt_data_matrix[i, 1] = vm_stress
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Clustering adaptivity feature is Von Mises equivalent strain
elif adapt_control_feature == 'vm_strain':
# Instantiate material state computations
csbvar_computer = MaterialQuantitiesComputer()
# Build adaptivity feature data matrix
for i in range(n_clusters):
# Get cluster label
cluster = int(adapt_data_matrix[i, 0])
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Get infinitesimal strain tensor (infinitesimal strains) or
# spatial logarithmic strain tensor (finite strains)
if self._strain_formulation == 'infinitesimal':
# Get cluster strain tensor (matricial form)
strain_mf = clusters_state[str(cluster)]['strain_mf']
else:
# Get cluster deformation gradient (matricial form)
def_gradient_mf = clusters_def_gradient_mf[str(cluster)]
# Build deformation gradient
def_gradient = mop.get_tensor_from_mf(
def_gradient_mf, self._n_dim, self._comp_order_nsym)
# Compute spatial logarithmic strain tensor
log_strain = compute_spatial_log_strain(def_gradient)
# Get spatial logarithmic strain tensor (matricial form)
strain_mf = mop.get_tensor_mf(log_strain, self._n_dim,
self._comp_order_sym)
# Build 3D strain tensor (matricial form)
if self._problem_type == 1:
# Get out-of-plain strain component
strain_33 = 0.0
# Build 3D strain tensor (matricial form)
strain_mf = mop.getstate3Dmffrom2Dmf(self._problem_type,
strain_mf, strain_33)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Compute von Mises equivalent strain
vm_strain = csbvar_computer.get_vm_strain(strain_mf)
# Assemble adaptivity feature data matrix
if is_incremental:
# Get previously converged infinitesimal strain tensor
# (infinitesimal strains) or spatial logarithmic strain
# tensor (finite strains)
if self._strain_formulation == 'infinitesimal':
# Get cluster strain tensor (matricial form)
strain_mf = \
clusters_state_old[str(cluster)]['strain_mf']
else:
# Get cluster deformation gradient (matricial form)
def_gradient_mf = \
clusters_def_gradient_old_mf[str(cluster)]
# Build deformation gradient
def_gradient = mop.get_tensor_from_mf(
def_gradient_mf, self._n_dim,
self._comp_order_nsym)
# Compute spatial logarithmic strain tensor
log_strain = compute_spatial_log_strain(def_gradient)
# Get spatial logarithmic strain tensor (matricial
# form)
strain_mf = mop.get_tensor_mf(log_strain, self._n_dim,
self._comp_order_sym)
# Build 3D strain tensor (matricial form)
if self._problem_type == 1:
# Get out-of-plain strain component
strain_33 = 0.0
# Build 3D strain tensor (matricial form)
strain_mf = mop.getstate3Dmffrom2Dmf(
self._problem_type, strain_mf, strain_33)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Compute previously converged von Mises equivalent strain
vm_strain_old = csbvar_computer.get_vm_strain(strain_mf)
# Assemble incremental adaptivity feature data matrix
adapt_data_matrix[i, 1] = vm_strain - vm_strain_old
else:
adapt_data_matrix[i, 1] = vm_strain
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Clustering adaptivity feature is norm of strain concentration tensor
elif adapt_control_feature == 'strain_concentration_tensor' \
and is_norm:
# Build adaptivity feature data matrix
for i in range(n_clusters):
# Get cluster label
cluster = int(adapt_data_matrix[i, 0])
# Collect adaptivity feature data
if is_incremental:
adapt_data_matrix[i, 1] = \
np.linalg.norm(clusters_sct_mf[str(cluster)]) - \
np.linalg.norm(clusters_sct_old_mf[str(cluster)])
else:
adapt_data_matrix[i, 1] = \
np.linalg.norm(clusters_sct_mf[str(cluster)])
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Clustering adaptivity feature is norm of Lippmann-Schwinger
# equilibrium residual
elif adapt_control_feature == 'equilibrium_residual' and is_norm:
# Build adaptivity feature data matrix
for i in range(n_clusters):
# Get cluster label
cluster = int(adapt_data_matrix[i, 0])
# Collect adaptivity feature data
adapt_data_matrix[i, 1] = \
np.linalg.norm(clusters_residuals_mf[str(cluster)])
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
else:
raise RuntimeError('Unknown or unavailable clustering adaptivity '
'feature.')
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
return adapt_data_matrix
# -------------------------------------------------------------------------
[docs] def adaptive_refinement(self, crve, material_state, target_clusters,
target_clusters_data, inc,
improved_init_guess=None, verbose=False):
"""Perform CRVE adaptive clustering refinement.
Parameters
----------
crve : CRVE
Cluster-Reduced Representative Volume Element.
material_state : MaterialState
CRVE material constitutive state at rewind state.
target_clusters : list[int]
List containing the labels (int) of clusters to be refined.
target_clusters_data : dict
For each target cluster (key, str), store dictionary (item, dict)
containing cluster associated parameters required for the adaptive
procedures.
inc : int
Macroscale loading increment.
improved_init_guess : list, default=None
List that allows an improved initial iterative guess for the
clusters incremental strain global vector (matricial form) after
the clustering adaptivity is carried out. Index 0 contains a flag
which is True if an improved initial iterative guess is to be
computed, False otherwise. Index 1 contains the improved
incremental strain global vector (matricial form) if computation
flag is True, otherwise is None.
verbose : bool, default=False
Enable verbose output.
"""
# Set initial time
init_time = time.time()
# Output execution data
if verbose:
info.displayinfo('5', 'Clustering adaptivity refinement:')
info.displayinfo('5', 'A - Performing clustering adaptivity...', 2)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Perform CRVE adaptive clustering refinement
if not target_clusters:
return
else:
# Store previous clustering cluster labels and total number of
# clusters
phase_clusters_old = copy.deepcopy(crve.get_phase_clusters())
n_total_clusters_old = crve.get_n_total_clusters()
# Perform CRVE adaptive clustering refinement
adaptive_clustering_map = crve.perform_crve_adaptivity(
target_clusters, target_clusters_data)
# Increment current increment adaptive step
if str(inc) in self._inc_adaptive_steps.keys():
self._inc_adaptive_steps[str(inc)] += 1
else:
self._inc_adaptive_steps[str(inc)] = 1
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Output execution data
if verbose:
a_time = time.time() - init_time
info.displayinfo('5',
'B - Computing cluster interaction tensors...', 2)
ref_time = time.time()
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Compute CRVE cluster interaction tensors
crve.compute_cit(mode='adaptive',
adaptive_clustering_map=adaptive_clustering_map)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Output execution data
if verbose:
b_time = time.time() - ref_time
info.displayinfo('5',
'C - Updating cluster-related quantities...', 2)
ref_time = time.time()
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Update cluster-related variables according to clustering adaptivity
# step
material_state.clustering_adaptivity_update(crve.get_phase_clusters(),
crve.get_clusters_vf(),
adaptive_clustering_map)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Loop over adaptive material phases
for mat_phase in self._adapt_material_phases:
# Check material phase adaptivity lock status. If material phase
# adaptivity is deactivated, then set the associated clustering
# adaptivity frequency to zero
if crve.get_cluster_phases()[mat_phase].adaptivity_lock:
self._clust_adapt_freq[mat_phase] = 0
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Get improved initial iterative guess flag
if improved_init_guess is None:
is_improved_init_guess = False
else:
is_improved_init_guess = improved_init_guess[0]
if not is_improved_init_guess:
improved_init_guess[1] = None
# Compute improved initial iterative guess for the global vector of
# clusters strain tensors (matricial form)
if is_improved_init_guess:
# Get CRVE material phases and total number of clusters
material_phases = crve.get_material_phases()
n_total_clusters = crve.get_n_total_clusters()
# Set cluster strain component order
if self._strain_formulation == 'infinitesimal':
comp_order = self._comp_order_sym
else:
comp_order = self._comp_order_nsym
# Get previous clustering global vector of clusters strain tensors
if len(improved_init_guess[1]) \
!= n_total_clusters_old*len(comp_order):
raise RuntimeError('Unexpected size of previous clustering '
'global vector of clusters strain tensors.')
else:
global_strain_mf_old = copy.deepcopy(improved_init_guess[1])
# Initialize new clustering global vector of clusters strain
# tensors
global_strain_mf_new = np.zeros((n_total_clusters*len(comp_order)))
# Initialize material phase initial index in global vector
mat_phase_init_idx_old = 0
mat_phase_init_idx_new = 0
# Loop over material phases
for mat_phase in material_phases:
# Loop over previous clustering cluster labels
for cluster in phase_clusters_old[mat_phase]:
# Get previous clustering cluster initial index
init_idx_old = mat_phase_init_idx_old \
+ phase_clusters_old[mat_phase].index(cluster) \
* len(comp_order)
# Build new clustering global vector of clusters strain
# tensors. If cluster remained unchanged after the
# clustering adaptive step, then simply transfer the
# associated values to the proper position in the new
# global vector. If cluster has been refined, then copy the
# associated values to its child clusters positions in the
# new global vector.
if cluster in crve.get_phase_clusters()[mat_phase]:
# Get new clustering cluster initial index
init_idx_new = mat_phase_init_idx_new \
+ crve.get_phase_clusters()[mat_phase].index(
cluster)*len(comp_order)
# Transfer cluster strain
global_strain_mf_new[
init_idx_new:init_idx_new
+ len(comp_order)] = global_strain_mf_old[
init_idx_old:init_idx_old+len(comp_order)]
else:
# Get list of target's child clusters
child_clusters = adaptive_clustering_map[mat_phase][
str(cluster)]
# Loop over child clusters
for child_cluster in child_clusters:
# Get new clustering child cluster initial index
init_idx_new = mat_phase_init_idx_new \
+ crve.get_phase_clusters()[mat_phase].index(
child_cluster)*len(comp_order)
# Copy parent cluster strain
global_strain_mf_new[init_idx_new:init_idx_new
+ len(comp_order)] = \
global_strain_mf_old[init_idx_old:
init_idx_old
+ len(comp_order)]
# Update material phase initial index in global vector
mat_phase_init_idx_old += \
len(phase_clusters_old[mat_phase])*len(comp_order)
mat_phase_init_idx_new += \
len(crve.get_phase_clusters()[mat_phase])*len(comp_order)
# Store improved initial iterative guess for the global vector of
# clusters strain tensors (matricial form)
improved_init_guess[1] = global_strain_mf_new
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Output execution data
if verbose:
c_time = time.time() - ref_time
info.displayinfo('5',
'D - Perfoming clustering adaptivity criterion '
'post-processing computations...', 2)
ref_time = time.time()
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Loop over adaptive material phases
for mat_phase in self._adapt_material_phases:
# Get adaptive material phase clustering adaptivity criterion
adapt_criterion = self._adapt_phase_criterions[mat_phase]
# Perform clustering adaptivity criterion post-processing
# computations
if isinstance(adapt_criterion, AdaptiveClusterGrouping):
# Update adaptive cluster groups
adapt_criterion.update_group_clusters(
adaptive_clustering_map[mat_phase])
elif isinstance(adapt_criterion, SpatialDiscontinuities):
# Update clusters adaptive level
adapt_criterion.update_clusters_adapt_level(
adaptive_clustering_map[mat_phase])
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Output execution data
if verbose:
d_time = time.time() - ref_time
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Update total amount of time spent in the ACRVE adaptive procedures
dtime = time.time() - init_time
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Update total amount of time spent in clustering adaptivity procedures
self.adaptive_time += time.time() - init_time
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Output execution data
if verbose:
# Build adaptive material phase's target clusters list
output_list = []
output_total = [0, 0]
# Loop over adaptive material phases
for mat_phase in self._adapt_material_phases:
# Loop over material phase target clusters
n_new_clusters = 0
for target_cluster in \
adaptive_clustering_map[mat_phase].keys():
n_new_clusters += \
len(adaptive_clustering_map[mat_phase][target_cluster])
n_total_clusters = len(crve.get_phase_clusters()[mat_phase])
output_list += [mat_phase, n_new_clusters, n_total_clusters]
output_total[0] += n_new_clusters
output_total[1] += n_total_clusters
# Output adaptive phases new clusters summary table
indent = 10*' '
info.displayinfo(
'5',
'Summary:' + '\n\n'
+ indent + 'Phase New Clusters Total Clusters' + '\n'
+ indent + 37*'-' + '\n'
+ ((indent + '{:^5s}{:>11d}{:>15d}\n')
* (len(self._adapt_material_phases))).format(*output_list)
+ indent + 37*'-' + '\n'
+ indent + '{:^5s}'.format('Total')
+ '{:>11d}{:>15d}'.format(*output_total),
2)
# Output adaptive phases execution time table
indent = 10*' '
info.displayinfo(
'5', 'Execution times (s):' + '\n\n'
+ indent + ' Time(s) %' + '\n'
+ indent + 28*'-' + '\n'
+ indent + '{:^5s}'.format('A') + '{:^18.4e}'.format(a_time)
+ '{:>5.2f}'.format((a_time/dtime)*100) + '\n'
+ indent + '{:^5s}'.format('B') + '{:^18.4e}'.format(b_time)
+ '{:>5.2f}'.format((b_time/dtime)*100) + '\n'
+ indent + '{:^5s}'.format('C') + '{:^18.4e}'.format(c_time)
+ '{:>5.2f}'.format((c_time/dtime)*100) + '\n'
+ indent + '{:^5s}'.format('D') + '{:^18.4e}'.format(c_time)
+ '{:>5.2f}'.format((d_time/dtime)*100) + '\n'
+ indent + 28*'-' + '\n'
+ indent + '{:^5s}'.format('Total')
+ '{:>14.4e}'.format(dtime),
2)
# -------------------------------------------------------------------------
[docs] def check_inc_adaptive_steps(self, inc):
"""Check number of adaptive steps performed in loading increment.
Parameters
----------
inc : int
Macroscale loading increment.
Returns
-------
bool
True if maximum number of clustering adaptive steps has not been
reached, False otherwise.
"""
if str(inc) in self._inc_adaptive_steps.keys():
if self._inc_adaptive_steps[str(inc)] \
>= self.max_inc_adaptive_steps:
return False
else:
return True
else:
self._inc_adaptive_steps[str(inc)] = 0
return True
# -------------------------------------------------------------------------
[docs] def clear_inc_adaptive_steps(self, inc_threshold):
"""Reset number of adaptive steps performed after threshold increment.
Parameters
----------
inc_threshold : int
Macroscale loading increment.
"""
# Get clustering adaptive steps associated increments
adaptive_steps_incs = [int(x) for x in self._inc_adaptive_steps.keys()]
# Loop over clustering adaptive steps associated increments
for inc in adaptive_steps_incs:
# Reset number of clustering adaptive steps if increment is greater
# than provided threshold
if inc > inc_threshold:
self._inc_adaptive_steps[str(inc)] = 0
# -------------------------------------------------------------------------
[docs] def is_adapt_phase_activated(self, mat_phase, inc):
"""Check if material phase adaptivity procedures are activated.
Parameters
----------
mat_phase : str
Material phase label.
inc : int
Incremental counter serving as a reference basis for the clustering
adaptivity frequency control.
Returns
-------
is_activated : bool
True if material phase adaptivity procedures are activated, False
otherwise.
"""
# Get clustering adaptivity reference initial increment
ref_inc = self._adapt_ref_init_inc[mat_phase]
# Get clustering adaptivity frequency
adapt_freq = self._clust_adapt_freq[mat_phase]
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Initialize adaptivity activation flag
is_activated = False
# Evaluate activation conditions
if adapt_freq != 0:
# Clustering adaptivity incremental frequency
if inc > ref_inc and (inc - ref_inc) % adapt_freq == 0:
is_activated = True
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Return adaptivity activation flag
return is_activated
# -------------------------------------------------------------------------
[docs] def reset_adapt_activation_parameters(self):
"""Reset parameters associated with clustering adaptivity activation.
"""
self._adapt_ref_init_inc = \
{mat_phase: 0 for mat_phase in self._adapt_material_phases}
self._adapt_feature_min_trigger = \
{mat_phase: False for mat_phase in self._adapt_material_phases}
# -------------------------------------------------------------------------
[docs] def get_adapt_vtk_array(self, voxels_clusters):
"""Get regular grid array with the adaptive level of each cluster.
A cluster adaptive level of 0 is associated with the base clustering
and is increased by one whenever the cluster is refined.
----
Parameters
----------
voxels_clusters : numpy.ndarray (2d or 3d)
Regular grid of voxels (spatial discretization of the RVE), where
each entry contains the cluster label (int) assigned to the
corresponding pixel/voxel.
"""
# Initialize flattened regular grid array
rg_array = np.zeros(voxels_clusters.shape).flatten('F')
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Loop over adaptive material phases
for mat_phase in self._adapt_material_phases:
# Get adaptive material phase clustering adaptivity criterion
adapt_criterion = self._adapt_phase_criterions[mat_phase]
# Assemble flattened regular grid array
if isinstance(adapt_criterion, AdaptiveClusterGrouping):
# Get adaptive cluster groups and associated adaptive level
adapt_groups = adapt_criterion.get_adapt_groups()
groups_adapt_level = adapt_criterion.get_groups_adapt_level()
# Get adaptive material phase cluster groups
adapt_groups_ids = list(adapt_groups.keys())
# Loop over adaptive cluster groups
for group_id in adapt_groups_ids:
# Get adaptive cluster group adaptive level
adapt_level = groups_adapt_level[group_id]
# Get adaptive cluster group clusters
adapt_group = adapt_groups[group_id]
# Get flat indexes associated with the adaptive cluster
# group clusters
flat_idxs = np.in1d(voxels_clusters.flatten('F'),
adapt_group)
# Store adaptive cluster group adaptive level
rg_array[flat_idxs] = adapt_level
elif isinstance(adapt_criterion, SpatialDiscontinuities):
# Get cluster adaptive level
clusters_adapt_level = \
adapt_criterion.get_clusters_adapt_level()
# Loop over clusters
for cluster in clusters_adapt_level.keys():
# Get cluster flat indexes
flat_idxs = np.in1d(voxels_clusters.flatten('F'),
int(cluster))
# Store cluster adaptive level
rg_array[flat_idxs] = clusters_adapt_level[str(cluster)]
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Build regular grid array
rg_array = rg_array.reshape(voxels_clusters.shape, order='F')
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Return
return rg_array
#
# Clustering adaptivity output file
# =============================================================================
[docs]class ClusteringAdaptivityOutput:
"""Clustering adaptivity output.
Attributes
----------
_header : list[str]
List containing the header of each column (str).
_col_width : int
Output file column width.
Methods
-------
init_adapt_file(self, crve)
Open clustering adaptivity output file and write file header.
write_adapt_file(self, inc, adaptivity_manager, crve, mode='increment')
Write clustering adaptivity output file.
rewind_file(self, rewind_inc)
Rewind clustering adaptivity output file.
"""
# -------------------------------------------------------------------------
[docs] def __init__(self, adapt_file_path, adapt_material_phases):
"""Constructor.
Parameters
----------
adapt_file_path : str
Path of clustering adaptivity output file.
adapt_material_phases : list[str]
RVE adaptive material phases labels (str).
"""
self._adapt_file_path = adapt_file_path
self._adapt_material_phases = adapt_material_phases
# Set clustering adaptivity output file header
self._header = ['Increment', 'total_adapt_time', 'eval_adapt_time',
'clust_adapt_time', 'cit_adapt_time']
for mat_phase in self._adapt_material_phases:
self._header += ['n_clusters_' + mat_phase,
'adapt_step_' + mat_phase,
'adapt_time_' + mat_phase]
# Set column width
self._col_width = max(16, max([len(x) for x in self._header]) + 2)
# -------------------------------------------------------------------------
[docs] def init_adapt_file(self, crve):
"""Open clustering adaptivity output file and write file header.
Parameters
----------
crve : CRVE
Cluster-Reduced Representative Volume Element.
"""
# Build adaptive material phases output list
adaptivity_output = crve.get_adaptivity_output()
output_list = []
for mat_phase in self._adapt_material_phases:
output_list += adaptivity_output[mat_phase]
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Open clustering adaptivity output file (write mode)
adapt_file = open(self._adapt_file_path, 'w')
# Set clustering adaptivity output file header format structure
write_list = ['{:>9s}'.format(self._header[0])
+ ''.join([('{:>' + str(self._col_width)
+ 's}').format(x)
for x in self._header[1:]]), ]
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Set clustering adaptivity output file increment format structure
write_list += [
'\n' + '{:>9d}'.format(0)
+ ('{:>' + str(self._col_width) + '.8e}').format(0)
+ ('{:>' + str(self._col_width) + '.8e}').format(0)
+ ('{:>' + str(self._col_width) + '.8e}').format(0)
+ ('{:>' + str(self._col_width) + '.8e}').format(0)
+ ''.join([''.join([('{:>' + str(self._col_width) + 'd}').format(x)
for x in output_list[3*i:3*i+2]]
+ [('{:>' + str(self._col_width)
+ '.8e}').format(output_list[3*i+2])])
for i in range(len(self._adapt_material_phases))])]
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Write clustering adaptivity output file
adapt_file.writelines(write_list)
# Close clustering adaptivity output file
adapt_file.close()
# -------------------------------------------------------------------------
[docs] def write_adapt_file(self, inc, adaptivity_manager, crve,
mode='increment'):
"""Write clustering adaptivity output file.
Parameters
----------
inc : int
Macroscale loading increment.
adaptivity_manager : AdaptivityManager
CRVE clustering adaptivity manager.
crve : CRVE
Cluster-Reduced Representative Volume Element.
mode : {'init', 'increment'}, default='increment'
Clustering adaptivity output mode. Mode `init` writes the file
header and the increment 0 (base clustering), while `increment`
appends the clustering adaptivity data associated with macroscale
loading increment.
"""
write_list = []
if mode == 'init':
# Open clustering adaptivity output file (write mode)
adapt_file = open(self._adapt_file_path, 'w')
# Set clustering adaptivity output file header format structure
write_list += ['{:>9s}'.format(self._header[0])
+ ''.join([('{:>' + str(self._col_width)
+ 's}').format(x)
for x in self._header[1:]]), ]
elif mode == 'increment':
# Open clustering adaptivity output file (append mode)
adapt_file = open(self._adapt_file_path, 'a')
else:
raise RuntimeError('Unknown clustering adaptivity output mode.')
# Build adaptive material phases output list
adaptivity_output = crve.get_adaptivity_output()
output_list = []
for mat_phase in self._adapt_material_phases:
output_list += adaptivity_output[mat_phase]
# Set clustering adaptivity output file increment format structure
write_list += [
'\n' + '{:>9d}'.format(inc)
+ ('{:>' + str(self._col_width) + '.8e}').format(
adaptivity_manager.adaptive_time)
+ ('{:>' + str(self._col_width) + '.8e}').format(
adaptivity_manager.adaptive_evaluation_time)
+ ('{:>' + str(self._col_width) + '.8e}').format(
crve.get_adaptive_clustering_time())
+ ('{:>' + str(self._col_width) + '.8e}').format(
crve.get_adaptive_cit_time())
+ ''.join([''.join([('{:>' + str(self._col_width) + 'd}').format(x)
for x in output_list[3*i:3*i+2]]
+ [('{:>' + str(self._col_width)
+ '.8e}').format(output_list[3*i+2])])
for i in range(len(self._adapt_material_phases))])]
# Write clustering adaptivity output file
adapt_file.writelines(write_list)
# Close clustering adaptivity output file
adapt_file.close()
# -------------------------------------------------------------------------
[docs] def rewind_file(self, rewind_inc):
"""Rewind clustering adaptivity output file.
Parameters
----------
rewind_inc : int
Increment associated with the rewind state.
"""
# Open clustering adaptivity output file and read lines (read)
file_lines = open(self._adapt_file_path, 'r').readlines()
# Set output file last line
last_line = 1 + rewind_inc
file_lines[last_line] = file_lines[last_line][:-1]
# Open clustering adaptivity output file (write mode)
open(self._adapt_file_path, 'w').writelines(
file_lines[: last_line + 1])