diff --git a/.gitattributes b/.gitattributes index 7a5c5bde5..7ed90d7ba 100644 --- a/.gitattributes +++ b/.gitattributes @@ -8,3 +8,10 @@ *.jpg binary *.hdf5 binary *.pdf binary + +# ignore files from MSC.Marc in language statistics +installation/mods_MarcMentat/* linguist-vendored +src/MarcInclude/* linguist-vendored + +# ignore reference files for tests in language statistics +python/tests/reference/* linguist-vendored diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ce645dcf5..ed16394ed 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -104,7 +104,7 @@ checkout: - release ################################################################################################### -Pytest: +Pytest_python: stage: python script: - cd $DAMASKROOT/python @@ -142,13 +142,6 @@ Pre_General: - master - release -grid_geometryPacking: - stage: preprocessing - script: grid_geometryPacking/test.py - except: - - master - - release - ################################################################################################### Post_AverageDown: stage: postprocessing @@ -385,6 +378,15 @@ Phenopowerlaw_singleSlip: - master - release +Pytest_grid: + stage: grid + script: + - cd pytest + - pytest + except: + - master + - release + ################################################################################################### Marc_compileIfort: stage: compileMarc diff --git a/PRIVATE b/PRIVATE index ec615d249..9573ce7bd 160000 --- a/PRIVATE +++ b/PRIVATE @@ -1 +1 @@ -Subproject commit ec615d249d39e5d01446b01ab9a5b7e7601340ad +Subproject commit 9573ce7bd2c1a7188c1aac5b83aa76d480c2bdb0 diff --git a/VERSION b/VERSION index 59cceb119..433cf0451 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.0.3-1726-gef4b7437 +v2.0.3-1808-g13c3ce00 diff --git a/examples/ConfigFiles/Phase_Phenopowerlaw_multiField.config b/examples/ConfigFiles/Phase_Phenopowerlaw_multiField.config deleted file mode 100644 index adad3710e..000000000 --- a/examples/ConfigFiles/Phase_Phenopowerlaw_multiField.config +++ /dev/null @@ -1,49 +0,0 @@ -[Aluminum] -elasticity hooke -plasticity phenopowerlaw - -(output) resistance_slip -(output) shearrate_slip -(output) resolvedstress_slip -(output) accumulated_shear_slip -(output) resistance_twin -(output) shearrate_twin -(output) resolvedstress_twin -(output) accumulated_shear_twin - -lattice_structure fcc -Nslip 12 # per family -Ntwin 0 # per family - -c11 106.75e9 -c12 60.41e9 -c44 28.34e9 - -gdot0_slip 0.001 -n_slip 20 -tau0_slip 31e6 # per family -tausat_slip 63e6 # per family -a_slip 2.25 -gdot0_twin 0.001 -n_twin 20 -tau0_twin 31e6 # per family -h0_slipslip 75e6 -interaction_slipslip 1 1 1.4 1.4 1.4 1.4 -atol_resistance 1 - -(stiffness_degradation) damage -(stiffness_degradation) porosity -{./Phase_Damage.config} -{./Phase_Thermal.config} -{./Phase_Vacancy.config} -{./Phase_Porosity.config} -{./Phase_Hydrogen.config} -{./Source_Damage_IsoBrittle.config} -{./Source_Thermal_Dissipation.config} -{./Source_Vacancy_PhenoPlasticity.config} -{./Source_Vacancy_Irradiation.config} -{./Kinematics_Thermal_Expansion.config} -{./Kinematics_Vacancy_Strain.config} -{./Kinematics_Hydrogen_Strain.config} - - diff --git a/processing/post/DADF5_postResults.py b/processing/post/DADF5_postResults.py index a6dc0b34a..4fa237df8 100755 --- a/processing/post/DADF5_postResults.py +++ b/processing/post/DADF5_postResults.py @@ -52,15 +52,15 @@ for filename in options.filenames: table = damask.Table(np.ones(np.product(results.grid),dtype=int)*int(inc[3:]),{'inc':(1,)}) table.add('pos',coords.reshape((-1,3))) - results.set_visible('materialpoints',False) - results.set_visible('constituents', True) + results.pick('materialpoints',False) + results.pick('constituents', True) for label in options.con: x = results.get_dataset_location(label) if len(x) != 0: table.add(label,results.read_dataset(x,0,plain=True).reshape((results.grid.prod(),-1))) - results.set_visible('constituents', False) - results.set_visible('materialpoints',True) + results.pick('constituents', False) + results.pick('materialpoints',True) for label in options.mat: x = results.get_dataset_location(label) if len(x) != 0: diff --git a/processing/post/addCauchy.py b/processing/post/addCauchy.py index afc5a57be..8bbc7fb0e 100755 --- a/processing/post/addCauchy.py +++ b/processing/post/addCauchy.py @@ -42,8 +42,8 @@ for name in filenames: table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) table.add('Cauchy', - damask.mechanics.Cauchy(table.get(options.defgrad).reshape(-1,3,3), - table.get(options.stress ).reshape(-1,3,3)).reshape(-1,9), + damask.mechanics.Cauchy(table.get(options.stress ).reshape(-1,3,3), + table.get(options.defgrad).reshape(-1,3,3)).reshape(-1,9), scriptID+' '+' '.join(sys.argv[1:])) table.to_ASCII(sys.stdout if name is None else name) diff --git a/processing/post/addPK2.py b/processing/post/addPK2.py index 185160d79..2894a5a90 100755 --- a/processing/post/addPK2.py +++ b/processing/post/addPK2.py @@ -43,8 +43,8 @@ for name in filenames: table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) table.add('S', - damask.mechanics.PK2(table.get(options.defgrad).reshape(-1,3,3), - table.get(options.stress ).reshape(-1,3,3)).reshape(-1,9), + damask.mechanics.PK2(table.get(options.stress ).reshape(-1,3,3), + table.get(options.defgrad).reshape(-1,3,3)).reshape(-1,9), scriptID+' '+' '.join(sys.argv[1:])) table.to_ASCII(sys.stdout if name is None else name) diff --git a/processing/post/addSpectralDecomposition.py b/processing/post/addSpectralDecomposition.py index c711c6a45..5909ca147 100755 --- a/processing/post/addSpectralDecomposition.py +++ b/processing/post/addSpectralDecomposition.py @@ -2,6 +2,7 @@ import os import sys +from io import StringIO from optparse import OptionParser import numpy as np @@ -33,69 +34,27 @@ parser.add_option('--no-check', parser.set_defaults(rh = True, ) + (options,filenames) = parser.parse_args() - -if options.tensor is None: - parser.error('no data column specified.') - -# --- loop over input files ------------------------------------------------------------------------- - if filenames == []: filenames = [None] for name in filenames: - try: - table = damask.ASCIItable(name = name, - buffered = False) - except: continue - damask.util.report(scriptName,name) + damask.util.report(scriptName,name) -# ------------------------------------------ read header ------------------------------------------ + table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) - table.head_read() + for tensor in options.tensor: + t = table.get(tensor).reshape(-1,3,3) + (u,v) = np.linalg.eigh(damask.mechanics.symmetric(t)) + if options.rh: v[np.linalg.det(v) < 0.0,:,2] *= -1.0 -# ------------------------------------------ assemble header 1 ------------------------------------ + for i,o in enumerate(['Min','Mid','Max']): + table.add('eigval{}({})'.format(o,tensor),u[:,i], + scriptID+' '+' '.join(sys.argv[1:])) - items = { - 'tensor': {'dim': 9, 'shape': [3,3], 'labels':options.tensor, 'column': []}, - } - errors = [] - remarks = [] - - for type, data in items.items(): - for what in data['labels']: - dim = table.label_dimension(what) - if dim != data['dim']: remarks.append('column {} is not a {}...'.format(what,type)) - else: - items[type]['column'].append(table.label_index(what)) - for order in ['Min','Mid','Max']: - table.labels_append(['eigval{}({})'.format(order,what)]) # extend ASCII header with new labels - for order in ['Min','Mid','Max']: - table.labels_append(['{}_eigvec{}({})'.format(i+1,order,what) for i in range(3)]) # extend ASCII header with new labels - - if remarks != []: damask.util.croak(remarks) - if errors != []: - damask.util.croak(errors) - table.close(dismiss = True) - continue - -# ------------------------------------------ assemble header 2 ------------------------------------ - - table.info_append(scriptID + '\t' + ' '.join(sys.argv[1:])) - table.head_write() - -# ------------------------------------------ process data ----------------------------------------- - - outputAlive = True - while outputAlive and table.data_read(): # read next data line of ASCII table - for type, data in items.items(): - for column in data['column']: - (u,v) = np.linalg.eigh(np.array(list(map(float,table.data[column:column+data['dim']]))).reshape(data['shape'])) - if options.rh and np.dot(np.cross(v[:,0], v[:,1]), v[:,2]) < 0.0 : v[:, 2] *= -1.0 # ensure right-handed eigenvector basis - table.data_append(list(u)) # vector of max,mid,min eigval - table.data_append(list(v.transpose().reshape(data['dim']))) # 3x3=9 combo vector of max,mid,min eigvec coordinates - outputAlive = table.data_write() # output processed line in accordance with column labeling - -# ------------------------------------------ output finalization ----------------------------------- - - table.close() # close input ASCII table (works for stdin) + for i,o in enumerate(['Min','Mid','Max']): + table.add('eigvec{}({})'.format(o,tensor),v[:,:,i], + scriptID+' '+' '.join(sys.argv[1:])) + + table.to_ASCII(sys.stdout if name is None else name) diff --git a/processing/post/permuteData.py b/processing/post/permuteData.py index a589cb6da..81af71adb 100755 --- a/processing/post/permuteData.py +++ b/processing/post/permuteData.py @@ -83,7 +83,7 @@ for name in filenames: # ------------------------------------------ assemble header --------------------------------------- - randomSeed = int(os.urandom(4).encode('hex'), 16) if options.randomSeed is None else options.randomSeed # random seed per file + randomSeed = int(os.urandom(4).hex(), 16) if options.randomSeed is None else options.randomSeed # random seed per file np.random.seed(randomSeed) table.info_append([scriptID + '\t' + ' '.join(sys.argv[1:]), diff --git a/processing/post/postResults.py b/processing/post/postResults.py deleted file mode 100755 index 31a78440b..000000000 --- a/processing/post/postResults.py +++ /dev/null @@ -1,1139 +0,0 @@ -#!/usr/bin/env python3 - -import os -import sys -from optparse import OptionParser, OptionGroup -import math -import re -import time -import struct - -import damask - - -scriptName = os.path.splitext(os.path.basename(__file__))[0] -scriptID = ' '.join([scriptName,damask.version]) - - -fileExtensions = { \ - 'marc': ['.t16',], - 'spectral': ['.spectralOut',], - } - - -# ----------------------------- -class vector: # mimic py_post node object - x,y,z = [None,None,None] - - def __init__(self,coords): - self.x = coords[0] - self.y = coords[1] - self.z = coords[2] - -# ----------------------------- -class element: # mimic py_post element object - items = [] - type = None - - def __init__(self,nodes,type): - self.items = nodes - self.type = type - -# ----------------------------- -class elemental_scalar: # mimic py_post element_scalar object - id = None - value = None - - def __init__(self,node,value): - self.id = node - self.value = value - - -# ----------------------------- -class MPIEspectral_result: # mimic py_post result object - - file = None - dataOffset = 0 - N_elemental_scalars = 0 - grid = [0,0,0] - size = [0.0,0.0,0.0] - theTitle = '' - wd = '' - geometry = '' - extrapolate = '' - N_loadcases = 0 - N_increments = 0 - N_positions = 0 - _frequencies = [] - _increments = [] - _times = [] - increment = 0 - startingIncrement = 0 - position = 0 -# this is a dummy at the moment, we need to parse the load file and figure out what time a particular increment corresponds to - time = 0.0 - N_nodes = 0 - N_node_scalars = 0 - N_elements = 0 - N_element_scalars = 0 - N_element_tensors = 0 - - def __init__(self,filename): - - self.file = open(filename, 'rb') - self.filesize = os.path.getsize(filename) - self.dataOffset = 0 - while self.dataOffset < self.filesize: - self.file.seek(self.dataOffset) - if self.file.read(3) == b'eoh': break - self.dataOffset += 1 - self.dataOffset += 7 -#search first for the new keywords with ':', if not found try to find the old ones - self.theTitle = self._keyedString('load:') - if self.theTitle is None: - self.theTitle = self._keyedString('load') - - self.wd = self._keyedString('workingdir:') - if self.wd is None: - self.wd = self._keyedString('workingdir') - - self.geometry = self._keyedString('geometry:') - if self.geometry is None: - self.geometry = self._keyedString('geometry') - - self.N_loadcases = self._keyedPackedArray('loadcases:',count=1,type='i')[0] - if self.N_loadcases is None: - self.N_loadcases = self._keyedPackedArray('loadcases',count=1,type='i')[0] - - self._frequencies = self._keyedPackedArray('frequencies:',count=self.N_loadcases,type='i') - if all ( i is None for i in self._frequencies): - self._frequencies = self._keyedPackedArray('frequencies',count=self.N_loadcases,type='i') - - self._increments = self._keyedPackedArray('increments:',count=self.N_loadcases,type='i') - if all (i is None for i in self._increments): - self._increments = self._keyedPackedArray('increments',count=self.N_loadcases,type='i') - - self.startingIncrement = self._keyedPackedArray('startingIncrement:',count=1,type='i')[0] - if self.startingIncrement is None: - self.startingIncrement = self._keyedPackedArray('startingIncrement',count=1,type='i')[0] - - - self._times = self._keyedPackedArray('times:',count=self.N_loadcases,type='d') - if all (i is None for i in self._times): - self._times = self._keyedPackedArray('times',count=self.N_loadcases,type='d') - - self._logscales = self._keyedPackedArray('logscales:',count=self.N_loadcases,type='i') - if all (i is None for i in self._logscales): - self._logscales = self._keyedPackedArray('logscales',count=self.N_loadcases,type='i') - - self.size = self._keyedPackedArray('size:',count=3,type='d') - - self.grid = self._keyedPackedArray('grid:',count=3,type='i') - - self.N_nodes = (self.grid[0]+1)*(self.grid[1]+1)*(self.grid[2]+1) - self.N_elements = self.grid[0] * self.grid[1] * self.grid[2] - - self.N_element_scalars = self._keyedPackedArray('materialpoint_sizeResults:',count=1,type='i')[0] - if self.N_element_scalars is None: - self.N_element_scalars = self._keyedPackedArray('materialpoint_sizeResults',count=1,type='i')[0] - - self.N_positions = (self.filesize-self.dataOffset)//(self.N_elements*self.N_element_scalars*8) - self.N_increments = 1 # add zero'th entry - for i in range(self.N_loadcases): - self.N_increments += self._increments[i]//self._frequencies[i] - -# parameters for file handling depending on output format - - self.tagLen=0 - self.expectedFileSize = self.dataOffset+self.N_increments*(self.tagLen+self.N_elements*self.N_element_scalars*8) - if self.expectedFileSize != self.filesize: - print('\n**\n* Unexpected file size. Incomplete simulation or file corrupted!\n**') - - def __str__(self): - """Summary of results file""" - return '\n'.join([ - 'workdir: %s'%self.wd, - 'geometry: %s'%self.geometry, - 'loadcases: %i'%self.N_loadcases, - 'grid: %s'%(','.join(map(str,self.grid))), - 'size: %s'%(','.join(map(str,self.size))), - 'header size: %i'%self.dataOffset, - 'actual file size: %i'%self.filesize, - 'expected file size: %i'%self.expectedFileSize, - 'positions in file : %i'%self.N_positions, - 'starting increment: %i'%self.startingIncrement, - ] - ) - - - def locateKeyValue(self,identifier): - - key = {'name':None,'pos':None} - - name = '' - filepos=0 # start at the beginning - while name != identifier and filepos < self.dataOffset: # stop searching when found or when reached end of header - self.file.seek(filepos) -# read the starting tag in front of the keyword (Fortran indicates start and end of writing by a 4 byte tag indicating the length of the following data) - dataLen=struct.unpack('i',self.file.read(4))[0] - name = self.file.read(len(identifier)).decode(errors="ignore") # anticipate identifier - start=filepos+(4+len(identifier)) # position of the values for the found key - filepos=filepos+(4+dataLen+4) # forward to next keyword - - if name==identifier: # found the correct name - key['pos'] = start # save position - key['name'] = name - return key - - def _keyedPackedArray(self,identifier,count = 3,type = 'd',default = None): - bytecount = {'d': 8,'i': 4} - values = [default]*count - key = self.locateKeyValue(identifier) - if key['name'] == identifier and key['pos'] is not None: - self.file.seek(key['pos']) - for i in range(count): - values[i] = struct.unpack(type,self.file.read(bytecount[type]))[0] - return values - - - def _keyedString(self,identifier,default=None): - value = default - self.file.seek(0) - m = re.search(r'(.{4})%s(.*?)\1'%identifier,self.file.read(self.dataOffset).decode(errors="ignore"),re.DOTALL) - if m: - value = m.group(2) - return value - - def title(self): - return self.theTitle - - def moveto(self,pos): - self.position = pos - self.increment = 0 - self.time = 0.0 - p = pos - for l in range(self.N_loadcases): - if p <= self._increments[l]//self._frequencies[l]: - break - else: - self.increment += self._increments[l] - self.time += self._times[l] - p -= self._increments[l]//self._frequencies[l] - - self.increment += self._frequencies[l] * p - - if self._logscales[l] > 0: # logarithmic time scale - if l == 0: self.time = 2**(self._increments[l] - (1+self._frequencies[l]*p)) * self._times[l] # first loadcase - else: self.time *= ((self.time + self._times[l])/self.time)**((1+self._frequencies[l]*p)/self._increments[l]) # any subsequent loadcase - else: # linear time scale - self.time += self._times[l]/self._increments[l] * self._frequencies[l] * p - - def extrapolation(self,value): - self.extrapolate = value - - def node_sequence(self,n): - return n-1 - - def node_id(self,n): - return n+1 - - def node(self,n): - a = self.grid[0]+1 - b = self.grid[1]+1 - c = self.grid[2]+1 - return vector([self.size[0] * (n%a) / self.grid[0], - self.size[1] * ((n//a)%b) / self.grid[1], - self.size[2] * ((n//a//b)%c) / self.grid[2], - ]) - - def element_sequence(self,e): - return e-1 - - def element_id(self,e): - return e+1 - - def element(self,e): - a = self.grid[0]+1 - b = self.grid[1]+1 - basenode = 1 + e+e//self.grid[0] + e//self.grid[0]//self.grid[1]*a - basenode2 = basenode+a*b - return (element([basenode ,basenode +1,basenode +a+1,basenode +a, - basenode2 ,basenode2+1,basenode2+a+1,basenode2+a, - ],117)) - - def increments(self): - return self.N_positions - - def nodes(self): - return self.N_nodes - - def node_scalars(self): - return self.N_node_scalars - - def elements(self): - return self.N_elements - - def element_scalars(self): - return self.N_element_scalars - - def element_scalar(self,e,idx): - incStart = self.dataOffset \ - + self.position*8*self.N_elements*self.N_element_scalars - where = (e*self.N_element_scalars + idx)*8 - try: - self.file.seek(incStart+where) - value = struct.unpack('d',self.file.read(8))[0] - except: - print('seeking {}'.format(incStart+where)) - print('e {} idx {}'.format(e,idx)) - sys.exit(1) - - return [elemental_scalar(node,value) for node in self.element(e).items] - - def element_scalar_label(elem,idx): - return 'User Defined Variable %i'%(idx+1) - - def element_tensors(self): - return self.N_element_tensors - -# ----------------------------- -def ipCoords(elemType, nodalCoordinates): - """Returns IP coordinates for a given element""" - nodeWeightsPerNode = { - 7: [ [27.0, 9.0, 3.0, 9.0, 9.0, 3.0, 1.0, 3.0], - [ 9.0, 27.0, 9.0, 3.0, 3.0, 9.0, 3.0, 1.0], - [ 3.0, 9.0, 27.0, 9.0, 1.0, 3.0, 9.0, 3.0], - [ 9.0, 3.0, 9.0, 27.0, 3.0, 1.0, 3.0, 9.0], - [ 9.0, 3.0, 1.0, 3.0, 27.0, 9.0, 3.0, 9.0], - [ 3.0, 9.0, 3.0, 1.0, 9.0, 27.0, 9.0, 3.0], - [ 1.0, 3.0, 9.0, 3.0, 3.0, 9.0, 27.0, 9.0], - [ 3.0, 1.0, 3.0, 9.0, 9.0, 3.0, 9.0, 27.0] ], - 57: [ [27.0, 9.0, 3.0, 9.0, 9.0, 3.0, 1.0, 3.0], - [ 9.0, 27.0, 9.0, 3.0, 3.0, 9.0, 3.0, 1.0], - [ 3.0, 9.0, 27.0, 9.0, 1.0, 3.0, 9.0, 3.0], - [ 9.0, 3.0, 9.0, 27.0, 3.0, 1.0, 3.0, 9.0], - [ 9.0, 3.0, 1.0, 3.0, 27.0, 9.0, 3.0, 9.0], - [ 3.0, 9.0, 3.0, 1.0, 9.0, 27.0, 9.0, 3.0], - [ 1.0, 3.0, 9.0, 3.0, 3.0, 9.0, 27.0, 9.0], - [ 3.0, 1.0, 3.0, 9.0, 9.0, 3.0, 9.0, 27.0] ], - 117: [ [ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] ], - 125: [ [ 3.0, 0.0, 0.0, 4.0, 1.0, 4.0], - [ 0.0, 3.0, 0.0, 4.0, 4.0, 1.0], - [ 0.0, 0.0, 3.0, 1.0, 4.0, 4.0],], - 127: [ [ 45.0, 17.0, 17.0, 17.0], - [ 17.0, 45.0, 17.0, 17.0], - [ 17.0, 17.0, 45.0, 17.0], - [ 17.0, 17.0, 17.0, 45.0],], - 136: [ [42.0, 15.0, 15.0, 14.0, 5.0, 5.0], - [15.0, 42.0, 15.0, 5.0, 14.0, 5.0], - [15.0, 15.0, 42.0, 5.0, 5.0, 14.0], - [14.0, 5.0, 5.0, 42.0, 15.0, 15.0], - [ 5.0, 14.0, 5.0, 15.0, 42.0, 15.0], - [ 5.0, 5.0, 14.0, 15.0, 15.0, 42.0] ], - } - - Nips = len(nodeWeightsPerNode[elemType]) - ipCoordinates = [[0.0,0.0,0.0] for i in range(Nips)] - for ip in range(Nips): - for node in range(len(nodeWeightsPerNode[elemType][ip])): - for i in range(3): - ipCoordinates[ip][i] += nodeWeightsPerNode[elemType][ip][node] * nodalCoordinates[node][i] - for i in range(3): - ipCoordinates[ip][i] /= sum(nodeWeightsPerNode[elemType][ip]) - - return ipCoordinates - - - -# ----------------------------- -def ipIDs(elemType): - """Returns IP numbers for given element type""" - ipPerNode = { - 7: [ 1, 2, 4, 3, 5, 6, 8, 7 ], - 57: [ 1, 2, 4, 3, 5, 6, 8, 7 ], - 117: [ 1 ], - 125: [ 1, 2, 3 ], - 127: [ 1, 2, 3, 4 ], - 136: [ 1, 2, 3, 4, 5, 6 ], - } - - return ipPerNode[elemType] - - - -# ----------------------------- -def substituteLocation(string, mesh, coords): - """Do variable interpolation in group and filter strings""" - substitute = string - substitute = substitute.replace('elem', str(mesh[0])) - substitute = substitute.replace('node', str(mesh[1])) - substitute = substitute.replace('ip', str(mesh[2])) - substitute = substitute.replace('grain', str(mesh[3])) - substitute = substitute.replace('x', '%.6g'%coords[0]) - substitute = substitute.replace('y', '%.6g'%coords[1]) - substitute = substitute.replace('z', '%.6g'%coords[2]) - return substitute - - - -# ----------------------------- -def heading(glue,parts): - """Joins pieces from parts by glue. second to last entry in pieces tells multiplicity""" - header = [] - for pieces in parts: - if pieces[-2] == 0: - del pieces[-2] - header.append(glue.join(map(str,pieces))) - - return header - - -# ----------------------------- -def mapIncremental(label, mapping, N, base, new): - """ - Applies the function defined by "mapping" - - (can be either 'min','max','avg', 'sum', or user specified) - to a list of data - """ - theMap = { 'min': lambda n,b,a: a if n==0 else min(b,a), - 'max': lambda n,b,a: a if n==0 else max(b,a), - 'avg': lambda n,b,a: (n*b+a)/(n+1), - 'avgabs': lambda n,b,a: (n*b+abs(a))/(n+1), - 'sum': lambda n,b,a: a if n==0 else b+a, - 'sumabs': lambda n,b,a: abs(a) if n==0 else b+abs(a), - 'unique': lambda n,b,a: a if n==0 or b==a else 'nan' - } - if mapping in theMap: - mapped = list(map(theMap[mapping],[N for i in range(len(base))],base,new)) # map one of the standard functions to data - if label.lower() == 'orientation': # orientation is special case:... - orientationNorm = math.sqrt(sum([q*q for q in mapped])) # ...calc norm of average quaternion - mapped = list(map(lambda x: x/orientationNorm, mapped)) # ...renormalize quaternion - else: - try: - mapped = list(eval('map(%s,[N for i in range(len(base))],base,new)'%mapping)) # map user defined function to colums in chunks - except: - mapped = ['nan' for i in range(len(base))] - - return list(mapped) - - - -# ----------------------------- -def OpenPostfile(name,type,nodal = False): - """Open postfile with extrapolation mode 'translate'""" - p = { - 'spectral': MPIEspectral_result, - 'marc': post_open, - }[type](name) - p.extrapolation({True:'linear',False:'translate'}[nodal]) - p.moveto(1) - - return p - - -# ----------------------------- -def ParseOutputFormat(filename,what,me): - """Parse .output* files in order to get a list of outputs""" - content = [] - format = {'outputs':{},'specials':{'brothers':[]}} - for prefix in ['']+list(map(str,range(1,17))): - if os.path.exists(prefix+filename+'.output'+what): - try: - file = open(prefix+filename+'.output'+what) - content = file.readlines() - file.close() - break - except: - pass - - if content == []: return format # nothing found... - - tag = '' - tagID = 0 - for line in content: - if re.match("\s*$",line) or re.match("#",line): # skip blank lines and comments - continue - m = re.match("\[(.+)\]",line) # look for block indicator - if m: # next section - tag = m.group(1) - tagID += 1 - format['specials']['brothers'].append(tag) - if tag == me or (me.isdigit() and tagID == int(me)): - format['specials']['_id'] = tagID - format['outputs'] = [] - tag = me - else: # data from section - if tag == me: - (output,length) = line.split() - output.lower() - if length.isdigit(): - length = int(length) - if re.match("\((.+)\)",output): # special data, e.g. (Ngrains) - format['specials'][output] = length - elif length > 0: - format['outputs'].append([output,length]) - return format - - -# ----------------------------- -def ParsePostfile(p,filename, outputFormat): - """ - Parse postfile in order to get position and labels of outputs - - needs "outputFormat" for mapping of output names to postfile output indices - """ - stat = { - 'IndexOfLabel': {}, - 'Title': p.title(), - 'Extrapolation': p.extrapolate, - 'NumberOfIncrements': p.increments(), - 'NumberOfNodes': p.nodes(), - 'NumberOfNodalScalars': p.node_scalars(), - 'LabelOfNodalScalar': [None]*p.node_scalars(), - 'NumberOfElements': p.elements(), - 'NumberOfElementalScalars': p.element_scalars(), - 'LabelOfElementalScalar': [None]*p.element_scalars(), - 'NumberOfElementalTensors': p.element_tensors(), - 'LabelOfElementalTensor': [None]*p.element_tensors(), - } - -# --- find labels - - for labelIndex in range(stat['NumberOfNodalScalars']): - label = p.node_scalar_label(labelIndex) - stat['IndexOfLabel'][label] = labelIndex - stat['LabelOfNodalScalar'][labelIndex] = label - - for labelIndex in range(stat['NumberOfElementalScalars']): - label = p.element_scalar_label(labelIndex) - stat['IndexOfLabel'][label] = labelIndex - stat['LabelOfElementalScalar'][labelIndex] = label - - for labelIndex in range(stat['NumberOfElementalTensors']): - label = p.element_tensor_label(labelIndex) - stat['IndexOfLabel'][label] = labelIndex - stat['LabelOfElementalTensor'][labelIndex] = label - - if 'User Defined Variable 1' in stat['IndexOfLabel']: # output format without dedicated names? - stat['IndexOfLabel']['HomogenizationCount'] = stat['IndexOfLabel']['User Defined Variable 1'] # adjust first named entry - - if 'HomogenizationCount' in stat['IndexOfLabel']: # does the result file contain relevant user defined output at all? - startIndex = stat['IndexOfLabel']['HomogenizationCount'] - stat['LabelOfElementalScalar'][startIndex] = 'HomogenizationCount' - -# We now have to find a mapping for each output label as defined in the .output* files to the output position in the post file -# Since we know where the user defined outputs start ("startIndex"), we can simply assign increasing indices to the labels -# given in the .output* file - - offset = 1 - for (name,N) in outputFormat['Homogenization']['outputs']: - for i in range(N): - label = {False: '%s'%( name), - True:'%i_%s'%(i+1,name)}[N > 1] - stat['IndexOfLabel'][label] = startIndex + offset - stat['LabelOfElementalScalar'][startIndex + offset] = label - offset += 1 - stat['IndexOfLabel']['GrainCount'] = startIndex + offset - stat['LabelOfElementalScalar'][startIndex + offset] = 'GrainCount' # add GrainCount - offset += 1 - - if '(ngrains)' in outputFormat['Homogenization']['specials']: - for grain in range(outputFormat['Homogenization']['specials']['(ngrains)']): - - stat['IndexOfLabel']['%i_CrystalliteCount'%(grain+1)] = startIndex + offset # report crystallite count - stat['LabelOfElementalScalar'][startIndex + offset] = '%i_CrystalliteCount'%(grain+1) # add GrainCount - offset += 1 - - for (name,N) in outputFormat['Crystallite']['outputs']: # add crystallite outputs - for i in range(N): - label = '%i_'%(grain+1) + ('%i_'%(i+1) if N>1 else '') + name - stat['IndexOfLabel'][label] = startIndex + offset - stat['LabelOfElementalScalar'][startIndex + offset] = label - offset += 1 - - stat['IndexOfLabel']['%i_ConstitutiveCount'%(grain+1)] = startIndex + offset # report constitutive count - stat['LabelOfElementalScalar'][startIndex + offset] = '%i_ConstitutiveCount'%(grain+1) # add GrainCount - offset += 1 - - for (name,N) in outputFormat['Constitutive']['outputs']: # add constitutive outputs - for i in range(N): - label = '%i_'%(grain+1) + ('%i_'%(i+1) if N>1 else '') + name - stat['IndexOfLabel'][label] = startIndex + offset - try: - stat['LabelOfElementalScalar'][startIndex + offset] = label - except IndexError: - print('trying to assign {} at position {}+{}'.format(label,startIndex,offset)) - sys.exit(1) - offset += 1 - - return stat - - -# ----------------------------- -def SummarizePostfile(stat,where=sys.stdout,format='marc'): - - where.write('\n\n') - where.write('title:\t%s'%stat['Title'] + '\n\n') - where.write('extraplation:\t%s'%stat['Extrapolation'] + '\n\n') - where.write('increments:\t%i'%(stat['NumberOfIncrements']) + '\n\n') - where.write('nodes:\t%i'%stat['NumberOfNodes'] + '\n\n') - where.write('elements:\t%i'%stat['NumberOfElements'] + '\n\n') - where.write('nodal scalars:\t%i'%stat['NumberOfNodalScalars'] + '\n\n '\ - +'\n '.join(stat['LabelOfNodalScalar']) + '\n\n') - where.write('elemental scalars:\t%i'%stat['NumberOfElementalScalars'] + '\n\n '\ - + '\n '.join(stat['LabelOfElementalScalar']) + '\n\n') - where.write('elemental tensors:\t%i'%stat['NumberOfElementalTensors'] + '\n\n '\ - + '\n '.join(stat['LabelOfElementalTensor']) + '\n\n') - - return True - - -# ----------------------------- -# MAIN FUNCTION STARTS HERE -# ----------------------------- - -# --- input parsing - -parser = OptionParser(option_class=damask.extendableOption, usage='%prog options [file[s]]', description = """ -Extract data from a .t16 (MSC.Marc) or .spectralOut results file. - -List of output variables is given by options '--ns','--es','--et','--ho','--cr','--co'. - -Filters and separations use 'elem','node','ip','grain', and 'x','y','z' as key words. -Example: -1) get averaged results in slices perpendicular to x for all negative y coordinates ---filter 'y < 0.0' --separation x --map 'avg' -2) global sum of squared data falling into first quadrant arc between R1 and R2 ---filter 'x >= 0.0 and y >= 0.0 and x*x + y*y >= R1*R1 and x*x + y*y <= R2*R2' ---map 'lambda n,b,a: n*b+a*a' - -User mappings need to be formulated in an incremental fashion for each new data point, a(dd), -and may use the current (incremental) result, b(ase), as well as the number, n(umber), -of already processed data points for evaluation. - -""", version = scriptID) - -parser.add_option('-i','--info', action='store_true', dest='info', - help='list contents of resultfile') -parser.add_option('-n','--nodal', action='store_true', dest='nodal', - help='data is extrapolated to nodal value') -parser.add_option( '--prefix', dest='prefix', - metavar='string', - help='prefix to result file name') -parser.add_option( '--suffix', dest='suffix', - metavar='string', - help='suffix to result file name') -parser.add_option('-d','--dir', dest='dir', - metavar='string', - help='name of subdirectory to hold output [%default]') -parser.add_option('-s','--split', action='store_true', dest='separateFiles', - help='split output per increment') -parser.add_option('-r','--range', dest='range', type='int', nargs=3, - metavar='int int int', - help='range of positions (or increments) to output (start, end, step) [all]') -parser.add_option('--increments', action='store_true', dest='getIncrements', - help='switch to increment range') -parser.add_option('-m','--map', dest='func', - metavar='string', - help='data reduction mapping [%default] out of min, max, avg, avgabs, sum, sumabs or user-lambda') -parser.add_option('-p','--type', dest='filetype', - metavar = 'string', - help = 'type of result file [auto]') -parser.add_option('-q','--quiet', dest='verbose', - action = 'store_false', - help = 'hide status bar (useful when piping to file)') - -group_material = OptionGroup(parser,'Material identifier') - -group_material.add_option('--homogenization', dest='homog', - help='homogenization identifier (as string or integer [%default])', metavar='string') -group_material.add_option('--crystallite', dest='cryst', - help='crystallite identifier (as string or integer [%default])', metavar='string') -group_material.add_option('--phase', dest='phase', - help='phase identifier (as string or integer [%default])', metavar='string') - -group_special = OptionGroup(parser,'Special outputs') - -group_special.add_option('-t','--time', action='store_true', dest='time', - help='output time of increment [%default]') -group_special.add_option('-f','--filter', dest='filter', - help='condition(s) to filter results [%default]', metavar='string') -group_special.add_option('--separation', action='extend', dest='sep', - help='properties to separate results [%default]', metavar='') -group_special.add_option('--sort', action='extend', dest='sort', - help='properties to sort results [%default]', metavar='') - -group_general = OptionGroup(parser,'General outputs') - -group_general.add_option('--ns', action='extend', dest='nodalScalar', - help='nodal scalars to extract', metavar='') -group_general.add_option('--es', action='extend', dest='elemScalar', - help='elemental scalars to extract', metavar='') -group_general.add_option('--et', action='extend', dest='elemTensor', - help='elemental tensors to extract', metavar='') -group_general.add_option('--ho', action='extend', dest='homogenizationResult', - help='homogenization results to extract', metavar='') -group_general.add_option('--cr', action='extend', dest='crystalliteResult', - help='crystallite results to extract', metavar='') -group_general.add_option('--co', action='extend', dest='constitutiveResult', - help='constitutive results to extract', metavar='') - -parser.add_option_group(group_material) -parser.add_option_group(group_general) -parser.add_option_group(group_special) - -parser.set_defaults(info = False, - nodal = False, - verbose = True, - prefix = '', - suffix = '', - dir = 'postProc', - filetype = None, - func = 'avg', - homog = '1', - cryst = '1', - phase = '1', - filter = '', - sep = [], - sort = [], - inc = False, - time = False, - separateFiles = False, - getIncrements= False, - ) - -(options, files) = parser.parse_args() - -# --- basic sanity checks - - -if files == []: - parser.print_help() - parser.error('no file specified...') - -damask.util.report(scriptName,files[0]) - -if not os.path.exists(files[0]): - parser.print_help() - parser.error('invalid file "%s" specified...'%files[0]) - -# --- figure out filetype - -if options.filetype is None: - ext = os.path.splitext(files[0])[1] - for theType in fileExtensions.keys(): - if ext in fileExtensions[theType]: - options.filetype = theType - break - -if options.filetype is not None: options.filetype = options.filetype.lower() - -if options.filetype == 'marc': offset_pos = 1 -else: offset_pos = 0 - - -# --- more sanity checks - -if options.filetype not in ['marc','spectral']: - parser.print_help() - parser.error('file type "%s" not supported...'%options.filetype) - -if options.filetype == 'marc': - sys.path.append(damask.solver.Marc().libraryPath()) - - try: - from py_post import post_open - except: - print('error: no valid Mentat release found') - sys.exit(-1) -else: - def post_open(): - return - -if options.constitutiveResult and not options.phase: - parser.print_help() - parser.error('constitutive results require phase...') - -if options.nodalScalar and ( options.elemScalar or options.elemTensor\ - or options.homogenizationResult or options.crystalliteResult or options.constitutiveResult ): - parser.print_help() - parser.error('not allowed to mix nodal with elemental results...') - -if not options.nodalScalar: options.nodalScalar = [] -if not options.elemScalar: options.elemScalar = [] -if not options.elemTensor: options.elemTensor = [] -if not options.homogenizationResult: options.homogenizationResult = [] -if not options.crystalliteResult: options.crystalliteResult = [] -if not options.constitutiveResult: options.constitutiveResult = [] - -options.sort.reverse() -options.sep.reverse() - -# --- parse .output and .t16 files - -if os.path.splitext(files[0])[1] == '': - filename = files[0] - extension = fileExtensions[options.filetype] -else: - filename = os.path.splitext(files[0])[0] - extension = os.path.splitext(files[0])[1] - -outputFormat = {} -me = { - 'Homogenization': options.homog, - 'Crystallite': options.cryst, - 'Constitutive': options.phase, - } - -for what in me: - outputFormat[what] = ParseOutputFormat(filename, what, me[what]) - if '_id' not in outputFormat[what]['specials']: - print("\nsection '{}' not found in <{}>".format(me[what], what)) - print('\n'.join(map(lambda x:' [%s]'%x, outputFormat[what]['specials']['brothers']))) - -p = OpenPostfile(filename+extension,options.filetype,options.nodal) -stat = ParsePostfile(p, filename, outputFormat) -if options.filetype == 'marc': - stat['NumberOfIncrements'] -= 1 # t16 contains one "virtual" increment (at 0) - -# --- sanity check for output variables -# for mentat variables (nodalScalar,elemScalar,elemTensor) we simply have to check whether the label -# is found in the stat[indexOfLabel] dictionary for user defined variables (homogenizationResult, -# crystalliteResult,constitutiveResult) we have to check the corresponding outputFormat, since the -# namescheme in stat['IndexOfLabel'] is different - -for opt in ['nodalScalar','elemScalar','elemTensor','homogenizationResult','crystalliteResult','constitutiveResult']: - if eval('options.%s'%opt): - for label in eval('options.%s'%opt): - if (opt in ['nodalScalar','elemScalar','elemTensor'] and label not in stat['IndexOfLabel'] and label not in ['elements',]) \ - or (opt in ['homogenizationResult','crystalliteResult','constitutiveResult'] \ - and (not outputFormat[opt[:-6].capitalize()]['outputs'] \ - or label not in list(zip(*outputFormat[opt[:-6].capitalize()]['outputs']))[0])): - parser.error('%s "%s" unknown...'%(opt,label)) - - -# --- output info - -if options.info: - if options.filetype == 'marc': - print('\n\nMentat release {}'.format(damask.solver.Marc().version())) - if options.filetype == 'spectral': - print('\n\n{}'.format(p)) - - SummarizePostfile(stat) - - print('\nUser Defined Outputs') - for what in me: - print('\n {}:'.format(what)) - for output in outputFormat[what]['outputs']: - print(' {}'.format(output)) - - sys.exit(0) - - -# --- build connectivity maps - -elementsOfNode = {} -Nelems = stat['NumberOfElements'] -for e in range(Nelems): - if options.verbose and Nelems >= 50 and e%(Nelems//50) == 0: # report in 2% steps if possible and avoid modulo by zero - damask.util.progressBar(iteration=e,total=Nelems,prefix='1/3: connecting elements') - for n in map(p.node_sequence,p.element(e).items): - if n not in elementsOfNode: - elementsOfNode[n] = [p.element_id(e)] - else: - elementsOfNode[n] += [p.element_id(e)] - -maxCountElementsOfNode = 0 -for l in elementsOfNode.values(): - maxCountElementsOfNode = max(maxCountElementsOfNode,len(l)) - - -# --------------------------- build group membership -------------------------------- - -p.moveto(offset_pos) -index = {} -groups = [] -groupCount = 0 -memberCount = 0 -if options.verbose: damask.util.progressBar(iteration=1,total=1,prefix='1/3: connecting elements') - -if options.nodalScalar: - Npoints = stat['NumberOfNodes'] - for n in range(Npoints): - if options.verbose and Npoints >= 50 and e%(Npoints//50) == 0: # report in 2% steps if possible and avoid modulo by zero - damask.util.progressBar(iteration=n,total=Npoints,prefix='2/3: scanning nodes ') - myNodeID = p.node_id(n) - myNodeCoordinates = [p.node(n).x, p.node(n).y, p.node(n).z] - myElemID = 0 - myIpID = 0 - myGrainID = 0 - - # generate an expression that is only true for the locations specified by options.filter - filter = substituteLocation(options.filter, [myElemID,myNodeID,myIpID,myGrainID], myNodeCoordinates) - if filter != '' and not eval(filter): # for all filter expressions that are not true:... - continue # ... ignore this data point and continue with next - - # --- group data locations - # generate a unique key for a group of separated data based on the separation criterium for the location - grp = substituteLocation('#'.join(options.sep), [myElemID,myNodeID,myIpID,myGrainID], myNodeCoordinates) - - if grp not in index: # create a new group if not yet present - index[grp] = groupCount - groups.append([[0,0,0,0,0.0,0.0,0.0]]) # initialize with avg location - groupCount += 1 - - groups[index[grp]][0][:4] = mapIncremental('','unique', - len(groups[index[grp]])-1, - groups[index[grp]][0][:4], - [myElemID,myNodeID,myIpID,myGrainID]) # keep only if unique average location - groups[index[grp]][0][4:] = mapIncremental('','avg', - len(groups[index[grp]])-1, - groups[index[grp]][0][4:], - myNodeCoordinates) # incrementally update average location - groups[index[grp]].append([myElemID,myNodeID,myIpID,myGrainID,0]) # append a new list defining each group member - memberCount += 1 - if options.verbose: damask.util.progressBar(iteration=1,total=1,prefix='2/3: scanning nodes ') - -else: - Nelems = stat['NumberOfElements'] - for e in range(Nelems): - if options.verbose and Nelems >= 50 and e%(Nelems//50) == 0: # report in 2% steps if possible and avoid modulo by zero - damask.util.progressBar(iteration=e,total=Nelems,prefix='2/3: scanning elements ') - myElemID = p.element_id(e) - myIpCoordinates = ipCoords(p.element(e).type, list(map(lambda node: [node.x, node.y, node.z], - list(map(p.node, map(p.node_sequence, p.element(e).items)))))) - myIpIDs = ipIDs(p.element(e).type) - Nips = len(myIpIDs) - myNodeIDs = p.element(e).items[:Nips] - for n in range(Nips): - myIpID = myIpIDs[n] - myNodeID = myNodeIDs[n] - for g in range(('GrainCount' in stat['IndexOfLabel']\ - and int(p.element_scalar(e, stat['IndexOfLabel']['GrainCount'])[0].value))\ - or 1): - myGrainID = g + 1 - - # --- filter valid locations - # generates an expression that is only true for the locations specified by options.filter - filter = substituteLocation(options.filter, [myElemID,myNodeID,myIpID,myGrainID], myIpCoordinates[n]) - if filter != '' and not eval(filter): # for all filter expressions that are not true:... - continue # ... ignore this data point and continue with next - - # --- group data locations - # generates a unique key for a group of separated data based on the separation criterium for the location - grp = substituteLocation('#'.join(options.sep), [myElemID,myNodeID,myIpID,myGrainID], myIpCoordinates[n]) - - if grp not in index: # create a new group if not yet present - index[grp] = groupCount - groups.append([[0,0,0,0,0.0,0.0,0.0]]) # initialize with avg location - groupCount += 1 - - groups[index[grp]][0][:4] = mapIncremental('','unique', - len(groups[index[grp]])-1, - groups[index[grp]][0][:4], - [myElemID,myNodeID,myIpID,myGrainID]) # keep only if unique average location - groups[index[grp]][0][4:] = mapIncremental('','avg', - len(groups[index[grp]])-1, - groups[index[grp]][0][4:], - myIpCoordinates[n]) # incrementally update average location - groups[index[grp]].append([myElemID,myNodeID,myIpID,myGrainID,n]) # append a new list defining each group member - memberCount += 1 - if options.verbose: damask.util.progressBar(iteration=1,total=1,prefix='2/3: scanning elements ') - - -# --------------------------- sort groups -------------------------------- - -where = { - 'elem': 0, - 'node': 1, - 'ip': 2, - 'grain': 3, - 'x': 4, - 'y': 5, - 'z': 6, - } - -sortProperties = [] -for item in options.sep: - if item not in options.sort: - sortProperties.append(item) - -theKeys = [] -if 'none' not in map(str.lower, options.sort): - for criterium in options.sort + sortProperties: - if criterium in where: - theKeys.append('x[0][%i]'%where[criterium]) - -sortKeys = eval('lambda x:(%s)'%(','.join(theKeys))) -groups.sort(key = sortKeys) # in-place sorting to save mem - - -# --------------------------- create output dir -------------------------------- - -dirname = os.path.abspath(os.path.join(os.path.dirname(filename),options.dir)) -if not os.path.isdir(dirname): - os.mkdir(dirname,0o755) - -fileOpen = False -assembleHeader = True -header = [] -standard = ['inc'] + \ - (['time'] if options.time else []) + \ - ['elem','node','ip','grain','1_pos','2_pos','3_pos'] - -# --------------------------- loop over positions -------------------------------- - -incAtPosition = {} -positionOfInc = {} - -for position in range(int(stat['NumberOfIncrements'])): - p.moveto(position+offset_pos) - incAtPosition[position] = p.increment # remember "real" increment at this position - positionOfInc[p.increment] = position # remember position of "real" increment - -if not options.range: - options.getIncrements = False - locations = range(stat['NumberOfIncrements']) # process all positions -else: - options.range = list(options.range) # convert to list - if options.getIncrements: - locations = [positionOfInc[x] for x in range(options.range[0],options.range[1]+1,options.range[2]) - if x in positionOfInc] - else: - locations = range( max(0,options.range[0]), - min(stat['NumberOfIncrements'],options.range[1]+1), - options.range[2] ) - -increments = [incAtPosition[x] for x in locations] # build list of increments to process - -time_start = time.time() - -Nincs = len([i for i in locations]) -for incCount,position in enumerate(locations): # walk through locations - p.moveto(position+offset_pos) # wind to correct position - -# --------------------------- file management -------------------------------- - - if options.separateFiles: - if fileOpen: - file.close() # noqa - fileOpen = False - outFilename = eval('"'+eval("'%%s_inc%%0%ii%%s.txt'%(math.log10(max(increments+[1]))+1)")\ - +'"%(dirname + os.sep + options.prefix + os.path.split(filename)[1],increments[incCount],options.suffix)') - else: - outFilename = '%s.txt'%(dirname + os.sep + options.prefix + os.path.split(filename)[1] + options.suffix) - - if not fileOpen: - file = open(outFilename,'w') - fileOpen = True - file.write('2\theader\n') - file.write(scriptID + '\t' + ' '.join(sys.argv[1:]) + '\n') - headerWritten = False - - file.flush() - -# --------------------------- read and map data per group -------------------------------- - - member = 0 - Ngroups = len(groups) - for j,group in enumerate(groups): - f = incCount*Ngroups + j - if options.verbose and (Ngroups*Nincs) >= 50 and f%((Ngroups*Nincs)//50) == 0: # report in 2% steps if possible and avoid modulo by zero - damask.util.progressBar(iteration=f,total=Ngroups*Nincs,prefix='3/3: processing points ') - N = 0 # group member counter - for (e,n,i,g,n_local) in group[1:]: # loop over group members - member += 1 - newby = [] # current member's data - - if options.nodalScalar: - for label in options.nodalScalar: - if label == 'elements': - length = maxCountElementsOfNode - content = elementsOfNode[p.node_sequence(n)]+[0]*(length-len(elementsOfNode[p.node_sequence(n)])) - else: - length = 1 - content = [ p.node_scalar(p.node_sequence(n),stat['IndexOfLabel'][label]) ] - if assembleHeader: - header += heading('_',[[component,''.join( label.split() )] - for component in range(int(length>1),length+int(length>1))]) - newby.append({'label':label, - 'len':length, - 'content':content }) - - if options.elemScalar: - for label in options.elemScalar: - if assembleHeader: - header += [''.join( label.split() )] - newby.append({'label':label, - 'len':1, - 'content':[ p.element_scalar(p.element_sequence(e),stat['IndexOfLabel'][label])[n_local].value ]}) - - if options.elemTensor: - for label in options.elemTensor: - if assembleHeader: - header += heading('.',[[''.join( label.split() ),component] - for component in ['intensity','t11','t22','t33','t12','t23','t13']]) - myTensor = p.element_tensor(p.element_sequence(e),stat['IndexOfLabel'][label])[n_local] - newby.append({'label':label, - 'len':7, - 'content':[ myTensor.intensity, - myTensor.t11, myTensor.t22, myTensor.t33, - myTensor.t12, myTensor.t23, myTensor.t13, - ]}) - - if options.homogenizationResult or \ - options.crystalliteResult or \ - options.constitutiveResult: - for (label,resultType) in zip(options.homogenizationResult + - options.crystalliteResult + - options.constitutiveResult, - ['Homogenization']*len(options.homogenizationResult) + - ['Crystallite']*len(options.crystalliteResult) + - ['Constitutive']*len(options.constitutiveResult) - ): - outputIndex = (list(zip(*outputFormat[resultType]['outputs']))[0]).index(label) # find the position of this output in the outputFormat - length = int(outputFormat[resultType]['outputs'][outputIndex][1]) - thisHead = heading('_',[[component,''.join( label.split() )] for component in range(int(length>1),length+int(length>1))]) - if assembleHeader: header += thisHead - if resultType != 'Homogenization': - thisHead = heading('_',[[g,component,label] for component in range(int(length>1),length+int(length>1))]) - try: - newby.append({'label':label, - 'len':length, - 'content':[ p.element_scalar(p.element_sequence(e),stat['IndexOfLabel'][head])[n_local].value - for head in thisHead ]}) - except KeyError: - print('\nDAMASK outputs seem missing from "post" section of the *.dat file!') - sys.exit() - - assembleHeader = False - - if N == 0: - mappedResult = [float(x) for x in range(len(header))] # init with debug data (should get deleted by *N at N=0) - - pos = 0 - for chunk in newby: - mappedResult[pos:pos+chunk['len']] = mapIncremental(chunk['label'],options.func, - N,mappedResult[pos:pos+chunk['len']],chunk['content']) - pos += chunk['len'] - - N += 1 - - # --- write data row to file --- - - if not headerWritten: - file.write('\t'.join(standard + header) + '\n') - headerWritten = True - - file.write('\t'.join(list(map(str,[p.increment] + \ - ([p.time] if options.time else []) + \ - group[0] + \ - mappedResult) - )) + '\n') -if options.verbose: damask.util.progressBar(iteration=1,total=1,prefix='3/3: processing points ') - -if fileOpen: - file.close() - diff --git a/processing/pre/seeds_fromDistribution.py b/processing/pre/seeds_fromDistribution.py index 2e8936f27..318598ba0 100755 --- a/processing/pre/seeds_fromDistribution.py +++ b/processing/pre/seeds_fromDistribution.py @@ -1,11 +1,9 @@ #!/usr/bin/env python3 -# -*- coding: UTF-8 no BOM -*- import threading,time,os,sys,random import numpy as np from optparse import OptionParser from io import StringIO -import binascii import damask scriptName = os.path.splitext(os.path.basename(__file__))[0] @@ -16,9 +14,10 @@ currentSeedsName = None #--------------------------------------------------------------------------------------------------- class myThread (threading.Thread): - """perturbes seed in seed file, performes Voronoi tessellation, evaluates, and updates best match""" + """Perturb seed in seed file, performes Voronoi tessellation, evaluates, and updates best match.""" def __init__(self, threadID): + """Threading class with thread ID.""" threading.Thread.__init__(self) self.threadID = threadID @@ -215,20 +214,16 @@ options = parser.parse_args()[0] damask.util.report(scriptName,options.seedFile) if options.randomSeed is None: - options.randomSeed = int(binascii.hexlify(os.urandom(4)),16) + options.randomSeed = int(os.urandom(4).hex(),16) damask.util.croak(options.randomSeed) delta = (options.scale/options.grid[0],options.scale/options.grid[1],options.scale/options.grid[2]) baseFile=os.path.splitext(os.path.basename(options.seedFile))[0] points = np.array(options.grid).prod().astype('float') # ----------- calculate target distribution and bin edges -targetGeomFile = os.path.splitext(os.path.basename(options.target))[0]+'.geom' -targetGeomTable = damask.ASCIItable(targetGeomFile,None,labeled=False,readonly=True) -targetGeomTable.head_read() -info,devNull = targetGeomTable.head_getGeom() -nMicrostructures = info['microstructures'] -targetVolFrac = np.bincount(targetGeomTable.microstructure_read(info['grid']))[1:nMicrostructures+1]/\ - float(info['grid'].prod()) +targetGeom = damask.Geom.from_file(os.path.splitext(os.path.basename(options.target))[0]+'.geom') +nMicrostructures = len(np.unique(targetGeom.microstructure)) +targetVolFrac = np.bincount(targetGeom.microstructure.flatten())/targetGeom.grid.prod().astype(np.float) target=[] for i in range(1,nMicrostructures+1): targetHist,targetBins = np.histogram(targetVolFrac,bins=i) #bin boundaries @@ -252,13 +247,12 @@ initialGeomVFile = StringIO() initialGeomVFile.write(damask.util.execute('geom_fromVoronoiTessellation '+ ' -g '+' '.join(list(map(str, options.grid))),bestSeedsVFile)[0]) initialGeomVFile.seek(0) -initialGeomTable = damask.ASCIItable(initialGeomVFile,None,labeled=False,readonly=True) -initialGeomTable.head_read() -info,devNull = initialGeomTable.head_getGeom() +initialGeom = damask.Geom.from_file(initialGeomVFile) -if info['microstructures'] != nMicrostructures: damask.util.croak('error. Microstructure count mismatch') +if len(np.unique(targetGeom.microstructure)) != nMicrostructures: + damask.util.croak('error. Microstructure count mismatch') -initialData = np.bincount(initialGeomTable.microstructure_read(info['grid']))/points +initialData = np.bincount(initialGeom.microstructure.flatten())/points for i in range(nMicrostructures): initialHist = np.histogram(initialData,bins=target[i]['bins'])[0] target[i]['error']=np.sqrt(np.square(np.array(target[i]['histogram']-initialHist)).sum()) @@ -274,7 +268,7 @@ for i in range(nMicrostructures): if options.maxseeds < 1: - maxSeeds = info['microstructures'] + maxSeeds = len(np.unique(initialGeom.microstructure)) else: maxSeeds = options.maxseeds diff --git a/python/README b/python/README deleted file mode 100644 index 7fc372881..000000000 --- a/python/README +++ /dev/null @@ -1,13 +0,0 @@ -DAMASK - The Düsseldorf Advanced Material Simulation Kit -Visit damask.mpie.de for installation and usage instructions - -CONTACT INFORMATION - -Max-Planck-Institut für Eisenforschung GmbH -Max-Planck-Str. 1 -40237 Düsseldorf -Germany - -Email: DAMASK@mpie.de -https://damask.mpie.de -https://magit1.mpie.de diff --git a/python/README b/python/README new file mode 120000 index 000000000..59a23c461 --- /dev/null +++ b/python/README @@ -0,0 +1 @@ +../README \ No newline at end of file diff --git a/python/damask/__init__.py b/python/damask/__init__.py index fa0677d7b..4aa28853e 100644 --- a/python/damask/__init__.py +++ b/python/damask/__init__.py @@ -14,9 +14,10 @@ from .asciitable import ASCIItable # noqa from .config import Material # noqa from .colormaps import Colormap, Color # noqa from .rotation import Rotation # noqa -from .lattice import Symmetry, Lattice # noqa +from .lattice import Symmetry, Lattice# noqa from .orientation import Orientation # noqa -from .dadf5 import DADF5 # noqa +from .result import Result # noqa +from .result import Result as DADF5 # noqa from .geom import Geom # noqa from .solver import Solver # noqa diff --git a/python/damask/asciitable.py b/python/damask/asciitable.py index c3d1bd55e..d81c22530 100644 --- a/python/damask/asciitable.py +++ b/python/damask/asciitable.py @@ -11,12 +11,12 @@ class ASCIItable(): """Read and write to ASCII tables.""" tmpext = '_tmp' # filename extension for in-place access - + # ------------------------------------------------------------------ def __init__(self, name = None, outname = None, - buffered = False, # flush writes + buffered = False, # is ignored, only exists for compatibility reasons labeled = True, # assume table has labels readonly = False, # no reading from file ): @@ -52,7 +52,7 @@ class ASCIItable(): if self.__IO__['in'] is None \ or self.__IO__['out'] is None: raise IOError # complain if any required file access not possible - + # ------------------------------------------------------------------ def _removeCRLF(self, @@ -63,7 +63,6 @@ class ASCIItable(): except AttributeError: return str(string) - # ------------------------------------------------------------------ def _quote(self, what): @@ -71,6 +70,7 @@ class ASCIItable(): return '{quote}{content}{quote}'.format( quote = ('"' if str(what)=='' or re.search(r"\s",str(what)) else ''), content = what) + # ------------------------------------------------------------------ def close(self, dismiss = False): @@ -110,7 +110,7 @@ class ASCIItable(): def head_read(self): """ Get column labels. - + by either reading the first row or, if keyword "head[*]" is present, the last line of the header """ @@ -121,7 +121,7 @@ class ASCIItable(): firstline = self.__IO__['in'].readline().strip() m = re.search(r'(\d+)\s+head', firstline.lower()) # search for "head" keyword - + if m: # proper ASCIItable format if self.__IO__['labeled']: # table features labels @@ -145,7 +145,7 @@ class ASCIItable(): if self.__IO__['labeled']: # table features labels self.tags = self.data # get tags from last line in "header"... self.data_read() # ...and remove from buffer - + if self.__IO__['labeled']: # table features tags self.__IO__['tags'] = list(self.tags) # backup tags (make COPY, not link) @@ -163,7 +163,7 @@ class ASCIItable(): if self.__IO__['labeled']: head.append('\t'.join(map(self._quote,self.tags))) if len(self.tags) == 0: raise ValueError('no labels present.') - + return self.output_write(head) # ------------------------------------------------------------------ @@ -178,15 +178,11 @@ class ASCIItable(): 'grid': lambda x: int(x), 'size': lambda x: float(x), 'origin': lambda x: float(x), - 'homogenization': lambda x: int(x), - 'microstructures': lambda x: int(x), } info = { 'grid': np.zeros(3,'i'), 'size': np.zeros(3,'d'), 'origin': np.zeros(3,'d'), - 'homogenization': 0, - 'microstructures': 0, } extra_header = [] @@ -234,7 +230,7 @@ class ASCIItable(): raw = False): """ Tell abstract labels. - + "x" for "1_x","2_x",... unless raw output is requested. operates on object tags or given list. """ @@ -347,7 +343,7 @@ class ASCIItable(): """ start = self.label_index(labels) dim = self.label_dimension(labels) - + return np.hstack([range(s,s+d) for s,d in zip(start,dim)]).astype(int) \ if isinstance(labels, Iterable) and not isinstance(labels, str) \ else range(start,start+dim) @@ -375,15 +371,6 @@ class ASCIItable(): self.tags = list(self.__IO__['tags']) # restore label info found in header (as COPY, not link) self.__IO__['labeled'] = len(self.tags) > 0 -# ------------------------------------------------------------------ - def data_skipLines(self, - count): - """Wind forward by count number of lines.""" - for i in range(count): - alive = self.data_read() - - return alive - # ------------------------------------------------------------------ def data_read(self, advance = True, @@ -425,8 +412,8 @@ class ASCIItable(): columns = [] for i,(c,d) in enumerate(zip(indices[present],dimensions[present])): # for all valid labels ... # ... transparently add all components unless column referenced by number or with explicit dimension - columns += list(range(c,c + - (d if str(c) != str(labels[present[i]]) else + columns += list(range(c,c + + (d if str(c) != str(labels[present[i]]) else 1))) use = np.array(columns) if len(columns) > 0 else None @@ -457,7 +444,7 @@ class ASCIItable(): output = [fmt % value for value in row] if fmt else list(map(repr,row)) except Exception: output = [fmt % row] if fmt else [repr(row)] - + try: self.__IO__['out'].write(delimiter.join(output) + '\n') except Exception: @@ -473,33 +460,3 @@ class ASCIItable(): for item in what: self.data_append(item) except TypeError: self.data += [str(what)] - -# ------------------------------------------------------------------ - def microstructure_read(self, - grid, - type = 'i', - strict = False): - """Read microstructure data (from .geom format).""" - def datatype(item): - return int(item) if type.lower() == 'i' else float(item) - - N = grid.prod() # expected number of microstructure indices in data - microstructure = np.zeros(N,type) # initialize as flat array - - i = 0 - while i < N and self.data_read(): - items = self.data - if len(items) > 2: - if items[1].lower() == 'of': - items = np.ones(datatype(items[0]))*datatype(items[2]) - elif items[1].lower() == 'to': - items = np.linspace(datatype(items[0]),datatype(items[2]), - abs(datatype(items[2])-datatype(items[0]))+1,dtype=int) - else: items = list(map(datatype,items)) - else: items = list(map(datatype,items)) - - s = min(len(items), N-i) # prevent overflow of microstructure array - microstructure[i:i+s] = items[:s] - i += len(items) - - return (microstructure, i == N and not self.data_read()) if strict else microstructure # check for proper point count and end of file diff --git a/python/damask/dadf5.py b/python/damask/dadf5.py deleted file mode 100644 index 819b5603e..000000000 --- a/python/damask/dadf5.py +++ /dev/null @@ -1,1008 +0,0 @@ -from queue import Queue -import re -import glob -import os - -import vtk -from vtk.util import numpy_support -import h5py -import numpy as np - -from . import util -from . import version -from . import mechanics - -# ------------------------------------------------------------------ -class DADF5(): - """ - Read and write to DADF5 files. - - DADF5 files contain DAMASK results. - """ - -# ------------------------------------------------------------------ - def __init__(self,fname): - """ - Opens an existing DADF5 file. - - Parameters - ---------- - fname : str - name of the DADF5 file to be openend. - - """ - with h5py.File(fname,'r') as f: - - try: - self.version_major = f.attrs['DADF5_version_major'] - self.version_minor = f.attrs['DADF5_version_minor'] - except KeyError: - self.version_major = f.attrs['DADF5-major'] - self.version_minor = f.attrs['DADF5-minor'] - - if self.version_major != 0 or not 2 <= self.version_minor <= 6: - raise TypeError('Unsupported DADF5 version {}.{} '.format(f.attrs['DADF5_version_major'], - f.attrs['DADF5_version_minor'])) - - self.structured = 'grid' in f['geometry'].attrs.keys() - - if self.structured: - self.grid = f['geometry'].attrs['grid'] - self.size = f['geometry'].attrs['size'] - if self.version_major == 0 and self.version_minor >= 5: - self.origin = f['geometry'].attrs['origin'] - - - r=re.compile('inc[0-9]+') - increments_unsorted = {int(i[3:]):i for i in f.keys() if r.match(i)} - self.increments = [increments_unsorted[i] for i in sorted(increments_unsorted)] - self.times = [round(f[i].attrs['time/s'],12) for i in self.increments] - - self.Nmaterialpoints, self.Nconstituents = np.shape(f['mapping/cellResults/constituent']) - self.materialpoints = [m.decode() for m in np.unique(f['mapping/cellResults/materialpoint']['Name'])] - self.constituents = [c.decode() for c in np.unique(f['mapping/cellResults/constituent'] ['Name'])] - - self.con_physics = [] - for c in self.constituents: - self.con_physics += f['/'.join([self.increments[0],'constituent',c])].keys() - self.con_physics = list(set(self.con_physics)) # make unique - - self.mat_physics = [] - for m in self.materialpoints: - self.mat_physics += f['/'.join([self.increments[0],'materialpoint',m])].keys() - self.mat_physics = list(set(self.mat_physics)) # make unique - - self.visible= {'increments': self.increments, - 'constituents': self.constituents, - 'materialpoints': self.materialpoints, - 'constituent': range(self.Nconstituents), # ToDo: stupid naming - 'con_physics': self.con_physics, - 'mat_physics': self.mat_physics} - - self.fname = fname - - - def __manage_visible(self,datasets,what,action): - """ - Manages the visibility of the groups. - - Parameters - ---------- - datasets : list of str or Boolean - name of datasets as list, supports ? and * wildcards. - True is equivalent to [*], False is equivalent to [] - what : str - attribute to change (must be in self.visible) - action : str - select from 'set', 'add', and 'del' - - """ - # allow True/False and string arguments - if datasets is True: - datasets = ['*'] - elif datasets is False: - datasets = [] - choice = [datasets] if isinstance(datasets,str) else datasets - - valid = [e for e_ in [glob.fnmatch.filter(getattr(self,what),s) for s in choice] for e in e_] - existing = set(self.visible[what]) - - if action == 'set': - self.visible[what] = valid - elif action == 'add': - self.visible[what] = list(existing.union(valid)) - elif action == 'del': - self.visible[what] = list(existing.difference_update(valid)) - - - def __time_to_inc(self,start,end): - selected = [] - for i,time in enumerate(self.times): - if start <= time <= end: - selected.append(self.increments[i]) - return selected - - - def set_by_time(self,start,end): - """ - Set active increments based on start and end time. - - Parameters - ---------- - start : float - start time (included) - end : float - end time (included) - - """ - self.__manage_visible(self.__time_to_inc(start,end),'increments','set') - - - def add_by_time(self,start,end): - """ - Add to active increments based on start and end time. - - Parameters - ---------- - start : float - start time (included) - end : float - end time (included) - - """ - self.__manage_visible(self.__time_to_inc(start,end),'increments','add') - - - def del_by_time(self,start,end): - """ - Delete from active increments based on start and end time. - - Parameters - ---------- - start : float - start time (included) - end : float - end time (included) - - """ - self.__manage_visible(self.__time_to_inc(start,end),'increments','del') - - - def set_by_increment(self,start,end): - """ - Set active time increments based on start and end increment. - - Parameters - ---------- - start : int - start increment (included) - end : int - end increment (included) - - """ - if self.version_minor >= 4: - self.__manage_visible([ 'inc{}'.format(i) for i in range(start,end+1)],'increments','set') - else: - self.__manage_visible(['inc{:05d}'.format(i) for i in range(start,end+1)],'increments','set') - - - def add_by_increment(self,start,end): - """ - Add to active time increments based on start and end increment. - - Parameters - ---------- - start : int - start increment (included) - end : int - end increment (included) - - """ - if self.version_minor >= 4: - self.__manage_visible([ 'inc{}'.format(i) for i in range(start,end+1)],'increments','add') - else: - self.__manage_visible(['inc{:05d}'.format(i) for i in range(start,end+1)],'increments','add') - - - def del_by_increment(self,start,end): - """ - Delet from active time increments based on start and end increment. - - Parameters - ---------- - start : int - start increment (included) - end : int - end increment (included) - - """ - if self.version_minor >= 4: - self.__manage_visible([ 'inc{}'.format(i) for i in range(start,end+1)],'increments','del') - else: - self.__manage_visible(['inc{:05d}'.format(i) for i in range(start,end+1)],'increments','del') - - - def iter_visible(self,what): - """ - Iterate over visible items by setting each one visible. - - Parameters - ---------- - what : str - attribute to change (must be in self.visible) - - """ - datasets = self.visible[what] - last_datasets = datasets.copy() - for dataset in datasets: - if last_datasets != self.visible[what]: - self.__manage_visible(datasets,what,'set') - raise Exception - self.__manage_visible(dataset,what,'set') - last_datasets = self.visible[what] - yield dataset - self.__manage_visible(datasets,what,'set') - - - def set_visible(self,what,datasets): - """ - Set active groups. - - Parameters - ---------- - datasets : list of str or Boolean - name of datasets as list, supports ? and * wildcards. - True is equivalent to [*], False is equivalent to [] - what : str - attribute to change (must be in self.visible) - - """ - self.__manage_visible(datasets,what,'set') - - - def add_visible(self,what,datasets): - """ - Add to active groups. - - Parameters - ---------- - datasets : list of str or Boolean - name of datasets as list, supports ? and * wildcards. - True is equivalent to [*], False is equivalent to [] - what : str - attribute to change (must be in self.visible) - - """ - self.__manage_visible(datasets,what,'add') - - - def del_visible(self,what,datasets): - """ - Delete from active groupse. - - Parameters - ---------- - datasets : list of str or Boolean - name of datasets as list, supports ? and * wildcards. - True is equivalent to [*], False is equivalent to [] - what : str - attribute to change (must be in self.visible) - - """ - self.__manage_visible(datasets,what,'del') - - - def groups_with_datasets(self,datasets): - """ - Get groups that contain all requested datasets. - - Only groups within inc?????/constituent/*_*/* inc?????/materialpoint/*_*/* - are considered as they contain the data. - Single strings will be treated as list with one entry. - - Wild card matching is allowed, but the number of arguments need to fit. - - Parameters - ---------- - datasets : iterable or str or boolean - - Examples - -------- - datasets = False matches no group - datasets = True matches all groups - datasets = ['F','P'] matches a group with ['F','P','sigma'] - datasets = ['*','P'] matches a group with ['F','P'] - datasets = ['*'] does not match a group with ['F','P','sigma'] - datasets = ['*','*'] does not match a group with ['F','P','sigma'] - datasets = ['*','*','*'] matches a group with ['F','P','sigma'] - - """ - if datasets is False: return [] - sets = [datasets] if isinstance(datasets,str) else datasets - - groups = [] - - with h5py.File(self.fname,'r') as f: - for i in self.iter_visible('increments'): - for o,p in zip(['constituents','materialpoints'],['con_physics','mat_physics']): - for oo in self.iter_visible(o): - for pp in self.iter_visible(p): - group = '/'.join([i,o[:-1],oo,pp]) # o[:-1]: plural/singular issue - if sets is True: - groups.append(group) - else: - match = [e for e_ in [glob.fnmatch.filter(f[group].keys(),s) for s in sets] for e in e_] - if len(set(match)) == len(sets) : groups.append(group) - return groups - - - def list_data(self): - """Return information on all active datasets in the file.""" - message = '' - with h5py.File(self.fname,'r') as f: - for i in self.iter_visible('increments'): - message+='\n{} ({}s)\n'.format(i,self.times[self.increments.index(i)]) - for o,p in zip(['constituents','materialpoints'],['con_physics','mat_physics']): - for oo in self.iter_visible(o): - message+=' {}\n'.format(oo) - for pp in self.iter_visible(p): - message+=' {}\n'.format(pp) - group = '/'.join([i,o[:-1],oo,pp]) # o[:-1]: plural/singular issue - for d in f[group].keys(): - try: - dataset = f['/'.join([group,d])] - message+=' {} / ({}): {}\n'.format(d,dataset.attrs['Unit'].decode(),dataset.attrs['Description'].decode()) - except KeyError: - pass - return message - - - def get_dataset_location(self,label): - """Return the location of all active datasets with given label.""" - path = [] - with h5py.File(self.fname,'r') as f: - for i in self.iter_visible('increments'): - k = '/'.join([i,'geometry',label]) - try: - f[k] - path.append(k) - except KeyError as e: - pass - for o,p in zip(['constituents','materialpoints'],['con_physics','mat_physics']): - for oo in self.iter_visible(o): - for pp in self.iter_visible(p): - k = '/'.join([i,o[:-1],oo,pp,label]) - try: - f[k] - path.append(k) - except KeyError as e: - pass - return path - - - def get_constituent_ID(self,c=0): - """Pointwise constituent ID.""" - with h5py.File(self.fname,'r') as f: - names = f['/mapping/cellResults/constituent']['Name'][:,c].astype('str') - return np.array([int(n.split('_')[0]) for n in names.tolist()],dtype=np.int32) - - - def get_crystal_structure(self): # ToDo: extension to multi constituents/phase - """Info about the crystal structure.""" - with h5py.File(self.fname,'r') as f: - return f[self.get_dataset_location('orientation')[0]].attrs['Lattice'].astype('str') # np.bytes_ to string - - - def read_dataset(self,path,c=0,plain=False): - """ - Dataset for all points/cells. - - If more than one path is given, the dataset is composed of the individual contributions. - """ - with h5py.File(self.fname,'r') as f: - shape = (self.Nmaterialpoints,) + np.shape(f[path[0]])[1:] - if len(shape) == 1: shape = shape +(1,) - dataset = np.full(shape,np.nan,dtype=np.dtype(f[path[0]])) - for pa in path: - label = pa.split('/')[2] - - if (pa.split('/')[1] == 'geometry'): - dataset = np.array(f[pa]) - continue - - p = np.where(f['mapping/cellResults/constituent'][:,c]['Name'] == str.encode(label))[0] - if len(p)>0: - u = (f['mapping/cellResults/constituent']['Position'][p,c]) - a = np.array(f[pa]) - if len(a.shape) == 1: - a=a.reshape([a.shape[0],1]) - dataset[p,:] = a[u,:] - - p = np.where(f['mapping/cellResults/materialpoint']['Name'] == str.encode(label))[0] - if len(p)>0: - u = (f['mapping/cellResults/materialpoint']['Position'][p.tolist()]) - a = np.array(f[pa]) - if len(a.shape) == 1: - a=a.reshape([a.shape[0],1]) - dataset[p,:] = a[u,:] - - if plain and dataset.dtype.names is not None: - return dataset.view(('float64',len(dataset.dtype.names))) - else: - return dataset - - - def cell_coordinates(self): - """Return initial coordinates of the cell centers.""" - if self.structured: - delta = self.size/self.grid*0.5 - z, y, x = np.meshgrid(np.linspace(delta[2],self.size[2]-delta[2],self.grid[2]), - np.linspace(delta[1],self.size[1]-delta[1],self.grid[1]), - np.linspace(delta[0],self.size[0]-delta[0],self.grid[0]), - ) - return np.concatenate((x[:,:,:,None],y[:,:,:,None],z[:,:,:,None]),axis = 3).reshape([np.product(self.grid),3]) - else: - with h5py.File(self.fname,'r') as f: - return f['geometry/x_c'][()] - - - def add_absolute(self,x): - """ - Add absolute value. - - Parameters - ---------- - x : str - Label of the dataset containing a scalar, vector, or tensor. - - """ - def __add_absolute(x): - - return { - 'data': np.abs(x['data']), - 'label': '|{}|'.format(x['label']), - 'meta': { - 'Unit': x['meta']['Unit'], - 'Description': 'Absolute value of {} ({})'.format(x['label'],x['meta']['Description']), - 'Creator': 'dadf5.py:add_abs v{}'.format(version) - } - } - - requested = [{'label':x,'arg':'x'}] - - self.__add_generic_pointwise(__add_absolute,requested) - - - def add_calculation(self,formula,label,unit='n/a',description=None,vectorized=True): - """ - Add result of a general formula. - - Parameters - ---------- - formula : str - Formula, refer to datasets by ‘#Label#‘. - label : str - Label of the dataset containing the result of the calculation. - unit : str, optional - Physical unit of the result. - description : str, optional - Human readable description of the result. - vectorized : bool, optional - Indicate whether the formula is written in vectorized form. Default is ‘True’. - - """ - if vectorized is False: - raise NotImplementedError - - def __add_calculation(**kwargs): - - formula = kwargs['formula'] - for d in re.findall(r'#(.*?)#',formula): - formula = formula.replace('#{}#'.format(d),"kwargs['{}']['data']".format(d)) - - return { - 'data': eval(formula), - 'label': kwargs['label'], - 'meta': { - 'Unit': kwargs['unit'], - 'Description': '{} (formula: {})'.format(kwargs['description'],kwargs['formula']), - 'Creator': 'dadf5.py:add_calculation v{}'.format(version) - } - } - - requested = [{'label':d,'arg':d} for d in set(re.findall(r'#(.*?)#',formula))] # datasets used in the formula - pass_through = {'formula':formula,'label':label,'unit':unit,'description':description} - - self.__add_generic_pointwise(__add_calculation,requested,pass_through) - - - def add_Cauchy(self,P='P',F='F'): - """ - Add Cauchy stress calculated from 1. Piola-Kirchhoff stress and deformation gradient. - - Parameters - ---------- - P : str, optional - Label of the dataset containing the 1. Piola-Kirchhoff stress. Default value is ‘P’. - F : str, optional - Label of the dataset containing the deformation gradient. Default value is ‘F’. - - """ - def __add_Cauchy(F,P): - - return { - 'data': mechanics.Cauchy(F['data'],P['data']), - 'label': 'sigma', - 'meta': { - 'Unit': P['meta']['Unit'], - 'Description': 'Cauchy stress calculated from {} ({}) '.format(P['label'],P['meta']['Description'])+\ - 'and deformation gradient {} ({})'.format(F['label'],F['meta']['Description']), - 'Creator': 'dadf5.py:add_Cauchy v{}'.format(version) - } - } - - requested = [{'label':F,'arg':'F'}, - {'label':P,'arg':'P'} ] - - self.__add_generic_pointwise(__add_Cauchy,requested) - - - def add_determinant(self,x): - """ - Add the determinant of a tensor. - - Parameters - ---------- - x : str - Label of the dataset containing a tensor. - - """ - def __add_determinant(x): - - return { - 'data': np.linalg.det(x['data']), - 'label': 'det({})'.format(x['label']), - 'meta': { - 'Unit': x['meta']['Unit'], - 'Description': 'Determinant of tensor {} ({})'.format(x['label'],x['meta']['Description']), - 'Creator': 'dadf5.py:add_determinant v{}'.format(version) - } - } - - requested = [{'label':x,'arg':'x'}] - - self.__add_generic_pointwise(__add_determinant,requested) - - - def add_deviator(self,x): - """ - Add the deviatoric part of a tensor. - - Parameters - ---------- - x : str - Label of the dataset containing a tensor. - - """ - def __add_deviator(x): - - if not np.all(np.array(x['data'].shape[1:]) == np.array([3,3])): - raise ValueError - - return { - 'data': mechanics.deviatoric_part(x['data']), - 'label': 's_{}'.format(x['label']), - 'meta': { - 'Unit': x['meta']['Unit'], - 'Description': 'Deviator of tensor {} ({})'.format(x['label'],x['meta']['Description']), - 'Creator': 'dadf5.py:add_deviator v{}'.format(version) - } - } - - requested = [{'label':x,'arg':'x'}] - - self.__add_generic_pointwise(__add_deviator,requested) - - - def add_maximum_shear(self,x): - """ - Add maximum shear components of symmetric tensor. - - Parameters - ---------- - x : str - Label of the dataset containing a symmetric tensor. - - """ - def __add_maximum_shear(x): - - return { - 'data': mechanics.maximum_shear(x['data']), - 'label': 'max_shear({})'.format(x['label']), - 'meta': { - 'Unit': x['meta']['Unit'], - 'Description': 'Maximum shear component of of {} ({})'.format(x['label'],x['meta']['Description']), - 'Creator': 'dadf5.py:add_maximum_shear v{}'.format(version) - } - } - - requested = [{'label':x,'arg':'x'}] - - self.__add_generic_pointwise(__add_maximum_shear,requested) - - - def add_Mises(self,x): - """ - Add the equivalent Mises stress or strain of a symmetric tensor. - - Parameters - ---------- - x : str - Label of the dataset containing a symmetric stress or strain tensor. - - """ - def __add_Mises(x): - - t = 'strain' if x['meta']['Unit'] == '1' else \ - 'stress' - return { - 'data': mechanics.Mises_strain(x['data']) if t=='strain' else mechanics.Mises_stress(x['data']), - 'label': '{}_vM'.format(x['label']), - 'meta': { - 'Unit': x['meta']['Unit'], - 'Description': 'Mises equivalent {} of {} ({})'.format(t,x['label'],x['meta']['Description']), - 'Creator': 'dadf5.py:add_Mises v{}'.format(version) - } - } - - requested = [{'label':x,'arg':'x'}] - - self.__add_generic_pointwise(__add_Mises,requested) - - - def add_norm(self,x,ord=None): - """ - Add the norm of vector or tensor. - - Parameters - ---------- - x : str - Label of the dataset containing a vector or tensor. - ord : {non-zero int, inf, -inf, ‘fro’, ‘nuc’}, optional - Order of the norm. inf means numpy’s inf object. For details refer to numpy.linalg.norm. - - """ - def __add_norm(x,ord): - - o = ord - if len(x['data'].shape) == 2: - axis = 1 - t = 'vector' - if o is None: o = 2 - elif len(x['data'].shape) == 3: - axis = (1,2) - t = 'tensor' - if o is None: o = 'fro' - else: - raise ValueError - - return { - 'data': np.linalg.norm(x['data'],ord=o,axis=axis,keepdims=True), - 'label': '|{}|_{}'.format(x['label'],o), - 'meta': { - 'Unit': x['meta']['Unit'], - 'Description': '{}-Norm of {} {} ({})'.format(ord,t,x['label'],x['meta']['Description']), - 'Creator': 'dadf5.py:add_norm v{}'.format(version) - } - } - - requested = [{'label':x,'arg':'x'}] - - self.__add_generic_pointwise(__add_norm,requested,{'ord':ord}) - - - def add_principal_components(self,x): - """ - Add principal components of symmetric tensor. - - The principal components are sorted in descending order, each repeated according to its multiplicity. - - Parameters - ---------- - x : str - Label of the dataset containing a symmetric tensor. - - """ - def __add_principal_components(x): - - return { - 'data': mechanics.principal_components(x['data']), - 'label': 'lambda_{}'.format(x['label']), - 'meta': { - 'Unit': x['meta']['Unit'], - 'Description': 'Pricipal components of {} ({})'.format(x['label'],x['meta']['Description']), - 'Creator': 'dadf5.py:add_principal_components v{}'.format(version) - } - } - - requested = [{'label':x,'arg':'x'}] - - self.__add_generic_pointwise(__add_principal_components,requested) - - - def add_spherical(self,x): - """ - Add the spherical (hydrostatic) part of a tensor. - - Parameters - ---------- - x : str - Label of the dataset containing a tensor. - - """ - def __add_spherical(x): - - if not np.all(np.array(x['data'].shape[1:]) == np.array([3,3])): - raise ValueError - - return { - 'data': mechanics.spherical_part(x['data']), - 'label': 'p_{}'.format(x['label']), - 'meta': { - 'Unit': x['meta']['Unit'], - 'Description': 'Spherical component of tensor {} ({})'.format(x['label'],x['meta']['Description']), - 'Creator': 'dadf5.py:add_spherical v{}'.format(version) - } - } - - requested = [{'label':x,'arg':'x'}] - - self.__add_generic_pointwise(__add_spherical,requested) - - - def add_strain_tensor(self,F='F',t='U',m=0): - """ - Add strain tensor calculated from a deformation gradient. - - For details refer to damask.mechanics.strain_tensor - - Parameters - ---------- - F : str, optional - Label of the dataset containing the deformation gradient. Default value is ‘F’. - t : {‘V’, ‘U’}, optional - Type of the polar decomposition, ‘V’ for right stretch tensor and ‘U’ for left stretch tensor. - Defaults value is ‘U’. - m : float, optional - Order of the strain calculation. Default value is ‘0.0’. - - """ - def __add_strain_tensor(F,t,m): - - return { - 'data': mechanics.strain_tensor(F['data'],t,m), - 'label': 'epsilon_{}^{}({})'.format(t,m,F['label']), - 'meta': { - 'Unit': F['meta']['Unit'], - 'Description': 'Strain tensor of {} ({})'.format(F['label'],F['meta']['Description']), - 'Creator': 'dadf5.py:add_strain_tensor v{}'.format(version) - } - } - - requested = [{'label':F,'arg':'F'}] - - self.__add_generic_pointwise(__add_strain_tensor,requested,{'t':t,'m':m}) - - - def __add_generic_pointwise(self,func,datasets_requested,extra_args={}): - """ - General function to add pointwise data. - - Parameters - ---------- - func : function - Function that calculates a new dataset from one or more datasets per HDF5 group. - datasets_requested : list of dictionaries - Details of the datasets to be used: label (in HDF5 file) and arg (argument to which the data is parsed in func). - extra_args : dictionary, optional - Any extra arguments parsed to func. - - """ - def job(args): - """Call function with input data + extra arguments, returns results + group.""" - args['results'].put({**args['func'](**args['in']),'group':args['group']}) - - - N_threads = 1 # ToDo: should be a parameter - - results = Queue(N_threads) - pool = util.ThreadPool(N_threads) - N_added = N_threads + 1 - - todo = [] - # ToDo: It would be more memory efficient to read only from file when required, i.e. do to it in pool.add_task - for group in self.groups_with_datasets([d['label'] for d in datasets_requested]): - with h5py.File(self.fname,'r') as f: - datasets_in = {} - for d in datasets_requested: - loc = f[group+'/'+d['label']] - data = loc[()] - meta = {k:loc.attrs[k].decode() for k in loc.attrs.keys()} - datasets_in[d['arg']] = {'data': data, 'meta' : meta, 'label' : d['label']} - - todo.append({'in':{**datasets_in,**extra_args},'func':func,'group':group,'results':results}) - - pool.map(job, todo[:N_added]) # initialize - - N_not_calculated = len(todo) - while N_not_calculated > 0: - result = results.get() - with h5py.File(self.fname,'a') as f: # write to file - dataset_out = f[result['group']].create_dataset(result['label'],data=result['data']) - for k in result['meta'].keys(): - dataset_out.attrs[k] = result['meta'][k].encode() - N_not_calculated-=1 - - if N_added < len(todo): # add more jobs - pool.add_task(job,todo[N_added]) - N_added +=1 - - pool.wait_completion() - - - def to_vtk(self,labels,mode='Cell'): - """ - Export to vtk cell/point data. - - Parameters - ---------- - labels : str or list of - Labels of the datasets to be exported. - mode : str, either 'Cell' or 'Point' - Export in cell format or point format. - Default value is 'Cell'. - - """ - if mode=='Cell': - - if self.structured: - - coordArray = [vtk.vtkDoubleArray(),vtk.vtkDoubleArray(),vtk.vtkDoubleArray()] - for dim in [0,1,2]: - for c in np.linspace(0,self.size[dim],1+self.grid[dim]): - coordArray[dim].InsertNextValue(c) - - vtk_geom = vtk.vtkRectilinearGrid() - vtk_geom.SetDimensions(*(self.grid+1)) - vtk_geom.SetXCoordinates(coordArray[0]) - vtk_geom.SetYCoordinates(coordArray[1]) - vtk_geom.SetZCoordinates(coordArray[2]) - - else: - - nodes = vtk.vtkPoints() - with h5py.File(self.fname,'r') as f: - nodes.SetData(numpy_support.numpy_to_vtk(f['/geometry/x_n'][()],deep=True)) - - vtk_geom = vtk.vtkUnstructuredGrid() - vtk_geom.SetPoints(nodes) - vtk_geom.Allocate(f['/geometry/T_c'].shape[0]) - - if self.version_major == 0 and self.version_minor <= 5: - vtk_type = vtk.VTK_HEXAHEDRON - n_nodes = 8 - else: - if f['/geometry/T_c'].attrs['VTK_TYPE'] == b'TRIANGLE': - vtk_type = vtk.VTK_TRIANGLE - n_nodes = 3 - elif f['/geometry/T_c'].attrs['VTK_TYPE'] == b'QUAD': - vtk_type = vtk.VTK_QUAD - n_nodes = 4 - elif f['/geometry/T_c'].attrs['VTK_TYPE'] == b'TETRA': # not tested - vtk_type = vtk.VTK_TETRA - n_nodes = 4 - elif f['/geometry/T_c'].attrs['VTK_TYPE'] == b'HEXAHEDRON': - vtk_type = vtk.VTK_HEXAHEDRON - n_nodes = 8 - - for i in f['/geometry/T_c']: - vtk_geom.InsertNextCell(vtk_type,n_nodes,i-1) - - elif mode == 'Point': - Points = vtk.vtkPoints() - Vertices = vtk.vtkCellArray() - for c in self.cell_coordinates(): - pointID = Points.InsertNextPoint(c) - Vertices.InsertNextCell(1) - Vertices.InsertCellPoint(pointID) - - vtk_geom = vtk.vtkPolyData() - vtk_geom.SetPoints(Points) - vtk_geom.SetVerts(Vertices) - vtk_geom.Modified() - - N_digits = int(np.floor(np.log10(int(self.increments[-1][3:]))))+1 - - for i,inc in enumerate(self.iter_visible('increments')): - vtk_data = [] - - materialpoints_backup = self.visible['materialpoints'].copy() - self.set_visible('materialpoints',False) - for label in (labels if isinstance(labels,list) else [labels]): - for p in self.iter_visible('con_physics'): - if p != 'generic': - for c in self.iter_visible('constituents'): - x = self.get_dataset_location(label) - if len(x) == 0: - continue - array = self.read_dataset(x,0) - shape = [array.shape[0],np.product(array.shape[1:])] - vtk_data.append(numpy_support.numpy_to_vtk(num_array=array.reshape(shape), - deep=True,array_type= vtk.VTK_DOUBLE)) - vtk_data[-1].SetName('1_'+x[0].split('/',1)[1]) #ToDo: hard coded 1! - vtk_geom.GetCellData().AddArray(vtk_data[-1]) - - else: - x = self.get_dataset_location(label) - if len(x) == 0: - continue - array = self.read_dataset(x,0) - shape = [array.shape[0],np.product(array.shape[1:])] - vtk_data.append(numpy_support.numpy_to_vtk(num_array=array.reshape(shape), - deep=True,array_type= vtk.VTK_DOUBLE)) - ph_name = re.compile(r'(?<=(constituent\/))(.*?)(?=(generic))') # identify phase name - dset_name = '1_' + re.sub(ph_name,r'',x[0].split('/',1)[1]) # removing phase name - vtk_data[-1].SetName(dset_name) - vtk_geom.GetCellData().AddArray(vtk_data[-1]) - - self.set_visible('materialpoints',materialpoints_backup) - - constituents_backup = self.visible['constituents'].copy() - self.set_visible('constituents',False) - for label in (labels if isinstance(labels,list) else [labels]): - for p in self.iter_visible('mat_physics'): - if p != 'generic': - for m in self.iter_visible('materialpoints'): - x = self.get_dataset_location(label) - if len(x) == 0: - continue - array = self.read_dataset(x,0) - shape = [array.shape[0],np.product(array.shape[1:])] - vtk_data.append(numpy_support.numpy_to_vtk(num_array=array.reshape(shape), - deep=True,array_type= vtk.VTK_DOUBLE)) - vtk_data[-1].SetName('1_'+x[0].split('/',1)[1]) #ToDo: why 1_? - vtk_geom.GetCellData().AddArray(vtk_data[-1]) - else: - x = self.get_dataset_location(label) - if len(x) == 0: - continue - array = self.read_dataset(x,0) - shape = [array.shape[0],np.product(array.shape[1:])] - vtk_data.append(numpy_support.numpy_to_vtk(num_array=array.reshape(shape), - deep=True,array_type= vtk.VTK_DOUBLE)) - vtk_data[-1].SetName('1_'+x[0].split('/',1)[1]) - vtk_geom.GetCellData().AddArray(vtk_data[-1]) - self.set_visible('constituents',constituents_backup) - - if mode=='Cell': - writer = vtk.vtkXMLRectilinearGridWriter() if self.structured else \ - vtk.vtkXMLUnstructuredGridWriter() - x = self.get_dataset_location('u_n') - vtk_data.append(numpy_support.numpy_to_vtk(num_array=self.read_dataset(x,0), - deep=True,array_type=vtk.VTK_DOUBLE)) - vtk_data[-1].SetName('u') - vtk_geom.GetPointData().AddArray(vtk_data[-1]) - elif mode == 'Point': - writer = vtk.vtkXMLPolyDataWriter() - - - file_out = '{}_inc{}.{}'.format(os.path.splitext(os.path.basename(self.fname))[0], - inc[3:].zfill(N_digits), - writer.GetDefaultFileExtension()) - - writer.SetCompressorTypeToZLib() - writer.SetDataModeToBinary() - writer.SetFileName(file_out) - writer.SetInputData(vtk_geom) - - writer.Write() diff --git a/python/damask/environment.py b/python/damask/environment.py index 2fde2c329..4c9ab762c 100644 --- a/python/damask/environment.py +++ b/python/damask/environment.py @@ -8,7 +8,7 @@ class Environment(): def __init__(self): """Read and provide values of DAMASK configuration.""" self.options = {} - self.get_options() + self.__get_options() def relPath(self,relative = '.'): return os.path.join(self.rootDir(),relative) @@ -16,7 +16,7 @@ class Environment(): def rootDir(self): return os.path.normpath(os.path.join(os.path.realpath(__file__),'../../../')) - def get_options(self): + def __get_options(self): for item in ['DAMASK_NUM_THREADS', 'MSC_ROOT', 'MARC_VERSION', diff --git a/python/damask/mechanics.py b/python/damask/mechanics.py index 307f1d83d..29c405b7d 100644 --- a/python/damask/mechanics.py +++ b/python/damask/mechanics.py @@ -1,11 +1,11 @@ import numpy as np -def Cauchy(F,P): +def Cauchy(P,F): """ - Return Cauchy stress calculated from 1. Piola-Kirchhoff stress and deformation gradient. - + Return Cauchy stress calculated from first Piola-Kirchhoff stress and deformation gradient. + Resulting tensor is symmetrized as the Cauchy stress needs to be symmetric. - + Parameters ---------- F : numpy.array of shape (:,3,3) or (3,3) @@ -21,32 +21,189 @@ def Cauchy(F,P): return symmetric(sigma) -def PK2(F,P): +def deviatoric_part(T): """ - Return 2. Piola-Kirchhoff stress calculated from 1. Piola-Kirchhoff stress and deformation gradient. - + Return deviatoric part of a tensor. + + Parameters + ---------- + T : numpy.array of shape (:,3,3) or (3,3) + Tensor of which the deviatoric part is computed. + + """ + return T - np.eye(3)*spherical_part(T) if np.shape(T) == (3,3) else \ + T - np.einsum('ijk,i->ijk',np.broadcast_to(np.eye(3),[T.shape[0],3,3]),spherical_part(T)) + + +def eigenvalues(T_sym): + """ + Return the eigenvalues, i.e. principal components, of a symmetric tensor. + + The eigenvalues are sorted in ascending order, each repeated according to + its multiplicity. + + Parameters + ---------- + T_sym : numpy.array of shape (:,3,3) or (3,3) + Symmetric tensor of which the eigenvalues are computed. + + """ + return np.linalg.eigvalsh(symmetric(T_sym)) + + +def eigenvectors(T_sym,RHS=False): + """ + Return eigenvectors of a symmetric tensor. + + The eigenvalues are sorted in ascending order of their associated eigenvalues. + + Parameters + ---------- + T_sym : numpy.array of shape (:,3,3) or (3,3) + Symmetric tensor of which the eigenvectors are computed. + RHS: bool, optional + Enforce right-handed coordinate system. Default is False. + + """ + (u,v) = np.linalg.eigh(symmetric(T_sym)) + + if RHS: + if np.shape(T_sym) == (3,3): + if np.linalg.det(v) < 0.0: v[:,2] *= -1.0 + else: + v[np.linalg.det(v) < 0.0,:,2] *= -1.0 + return v + + +def left_stretch(T): + """ + Return the left stretch of a tensor. + + Parameters + ---------- + T : numpy.array of shape (:,3,3) or (3,3) + Tensor of which the left stretch is computed. + + """ + return __polar_decomposition(T,'V')[0] + + +def maximum_shear(T_sym): + """ + Return the maximum shear component of a symmetric tensor. + + Parameters + ---------- + T_sym : numpy.array of shape (:,3,3) or (3,3) + Symmetric tensor of which the maximum shear is computed. + + """ + w = eigenvalues(T_sym) + return (w[0] - w[2])*0.5 if np.shape(T_sym) == (3,3) else \ + (w[:,0] - w[:,2])*0.5 + + +def Mises_strain(epsilon): + """ + Return the Mises equivalent of a strain tensor. + + Parameters + ---------- + epsilon : numpy.array of shape (:,3,3) or (3,3) + Symmetric strain tensor of which the von Mises equivalent is computed. + + """ + return __Mises(epsilon,2.0/3.0) + + +def Mises_stress(sigma): + """ + Return the Mises equivalent of a stress tensor. + + Parameters + ---------- + sigma : numpy.array of shape (:,3,3) or (3,3) + Symmetric stress tensor of which the von Mises equivalent is computed. + + """ + return __Mises(sigma,3.0/2.0) + + +def PK2(P,F): + """ + Calculate second Piola-Kirchhoff stress from first Piola-Kirchhoff stress and deformation gradient. + Parameters ---------- - F : numpy.array of shape (:,3,3) or (3,3) - Deformation gradient. P : numpy.array of shape (:,3,3) or (3,3) 1. Piola-Kirchhoff stress. + F : numpy.array of shape (:,3,3) or (3,3) + Deformation gradient. """ if np.shape(F) == np.shape(P) == (3,3): S = np.dot(np.linalg.inv(F),P) else: S = np.einsum('ijk,ikl->ijl',np.linalg.inv(F),P) - return S + return symmetric(S) + + +def right_stretch(T): + """ + Return the right stretch of a tensor. + + Parameters + ---------- + T : numpy.array of shape (:,3,3) or (3,3) + Tensor of which the right stretch is computed. + + """ + return __polar_decomposition(T,'U')[0] + + +def rotational_part(T): + """ + Return the rotational part of a tensor. + + Parameters + ---------- + T : numpy.array of shape (:,3,3) or (3,3) + Tensor of which the rotational part is computed. + + """ + return __polar_decomposition(T,'R')[0] + + +def spherical_part(T,tensor=False): + """ + Return spherical (hydrostatic) part of a tensor. + + Parameters + ---------- + T : numpy.array of shape (:,3,3) or (3,3) + Tensor of which the hydrostatic part is computed. + tensor : bool, optional + Map spherical part onto identity tensor. Default is false + + """ + if T.shape == (3,3): + sph = np.trace(T)/3.0 + return sph if not tensor else np.eye(3)*sph + else: + sph = np.trace(T,axis1=1,axis2=2)/3.0 + if not tensor: + return sph + else: + return np.einsum('ijk,i->ijk',np.broadcast_to(np.eye(3),(T.shape[0],3,3)),sph) def strain_tensor(F,t,m): """ Return strain tensor calculated from deformation gradient. - + For details refer to https://en.wikipedia.org/wiki/Finite_strain_theory and https://de.wikipedia.org/wiki/Verzerrungstensor - + Parameters ---------- F : numpy.array of shape (:,3,3) or (3,3) @@ -64,209 +221,87 @@ def strain_tensor(F,t,m): elif t == 'U': C = np.matmul(transpose(F_),F_) w,n = np.linalg.eigh(C) - + if m > 0.0: - eps = 1.0/(2.0*abs(m)) * (+ np.matmul(n,np.einsum('ij,ikj->ijk',w**m,n)) + eps = 1.0/(2.0*abs(m)) * (+ np.matmul(n,np.einsum('ij,ikj->ijk',w**m,n)) - np.broadcast_to(np.eye(3),[F_.shape[0],3,3])) elif m < 0.0: eps = 1.0/(2.0*abs(m)) * (- np.matmul(n,np.einsum('ij,ikj->ijk',w**m,n)) + np.broadcast_to(np.eye(3),[F_.shape[0],3,3])) else: eps = np.matmul(n,np.einsum('ij,ikj->ijk',0.5*np.log(w),n)) - + return eps.reshape((3,3)) if np.shape(F) == (3,3) else \ eps -def deviatoric_part(x): - """ - Return deviatoric part of a tensor. - - Parameters - ---------- - x : numpy.array of shape (:,3,3) or (3,3) - Tensor of which the deviatoric part is computed. - - """ - return x - np.eye(3)*spherical_part(x) if np.shape(x) == (3,3) else \ - x - np.einsum('ijk,i->ijk',np.broadcast_to(np.eye(3),[x.shape[0],3,3]),spherical_part(x)) - - -def spherical_part(x,tensor=False): - """ - Return spherical (hydrostatic) part of a tensor. - - Parameters - ---------- - x : numpy.array of shape (:,3,3) or (3,3) - Tensor of which the hydrostatic part is computed. - tensor : bool, optional - Map spherical part onto identity tensor. Default is false - - """ - if x.shape == (3,3): - sph = np.trace(x)/3.0 - return sph if not tensor else np.eye(3)*sph - else: - sph = np.trace(x,axis1=1,axis2=2)/3.0 - if not tensor: - return sph - else: - return np.einsum('ijk,i->ijk',np.broadcast_to(np.eye(3),(x.shape[0],3,3)),sph) - - -def Mises_stress(sigma): - """ - Return the Mises equivalent of a stress tensor. - - Parameters - ---------- - sigma : numpy.array of shape (:,3,3) or (3,3) - Symmetric stress tensor of which the von Mises equivalent is computed. - - """ - s = deviatoric_part(sigma) - return np.sqrt(3.0/2.0*(np.sum(s**2.0))) if np.shape(sigma) == (3,3) else \ - np.sqrt(3.0/2.0*np.einsum('ijk->i',s**2.0)) - - -def Mises_strain(epsilon): - """ - Return the Mises equivalent of a strain tensor. - - Parameters - ---------- - epsilon : numpy.array of shape (:,3,3) or (3,3) - Symmetric strain tensor of which the von Mises equivalent is computed. - - """ - s = deviatoric_part(epsilon) - return np.sqrt(2.0/3.0*(np.sum(s**2.0))) if np.shape(epsilon) == (3,3) else \ - np.sqrt(2.0/3.0*np.einsum('ijk->i',s**2.0)) - - -def symmetric(x): +def symmetric(T): """ Return the symmetrized tensor. - + Parameters ---------- - x : numpy.array of shape (:,3,3) or (3,3) + T : numpy.array of shape (:,3,3) or (3,3) Tensor of which the symmetrized values are computed. """ - return (x+transpose(x))*0.5 + return (T+transpose(T))*0.5 -def maximum_shear(x): - """ - Return the maximum shear component of a symmetric tensor. - - Parameters - ---------- - x : numpy.array of shape (:,3,3) or (3,3) - Symmetric tensor of which the maximum shear is computed. - - """ - w = np.linalg.eigvalsh(symmetric(x)) # eigenvalues in ascending order - return (w[2] - w[0])*0.5 if np.shape(x) == (3,3) else \ - (w[:,2] - w[:,0])*0.5 - - -def principal_components(x): - """ - Return the principal components of a symmetric tensor. - - The principal components (eigenvalues) are sorted in descending order, each repeated according to - its multiplicity. - - Parameters - ---------- - x : numpy.array of shape (:,3,3) or (3,3) - Symmetric tensor of which the principal compontents are computed. - - """ - w = np.linalg.eigvalsh(symmetric(x)) # eigenvalues in ascending order - return w[::-1] if np.shape(x) == (3,3) else \ - w[:,::-1] - - -def transpose(x): +def transpose(T): """ Return the transpose of a tensor. - + Parameters ---------- - x : numpy.array of shape (:,3,3) or (3,3) + T : numpy.array of shape (:,3,3) or (3,3) Tensor of which the transpose is computed. """ - return x.T if np.shape(x) == (3,3) else \ - np.transpose(x,(0,2,1)) + return T.T if np.shape(T) == (3,3) else \ + np.transpose(T,(0,2,1)) -def rotational_part(x): - """ - Return the rotational part of a tensor. - - Parameters - ---------- - x : numpy.array of shape (:,3,3) or (3,3) - Tensor of which the rotational part is computed. - - """ - return __polar_decomposition(x,'R')[0] - - -def left_stretch(x): - """ - Return the left stretch of a tensor. - - Parameters - ---------- - x : numpy.array of shape (:,3,3) or (3,3) - Tensor of which the left stretch is computed. - - """ - return __polar_decomposition(x,'V')[0] - - -def right_stretch(x): - """ - Return the right stretch of a tensor. - - Parameters - ---------- - x : numpy.array of shape (:,3,3) or (3,3) - Tensor of which the right stretch is computed. - - """ - return __polar_decomposition(x,'U')[0] - - -def __polar_decomposition(x,requested): +def __polar_decomposition(T,requested): """ Singular value decomposition. - + Parameters ---------- - x : numpy.array of shape (:,3,3) or (3,3) + T : numpy.array of shape (:,3,3) or (3,3) Tensor of which the singular values are computed. requested : iterable of str - Requested outputs: ‘R’ for the rotation tensor, + Requested outputs: ‘R’ for the rotation tensor, ‘V’ for left stretch tensor and ‘U’ for right stretch tensor. """ - u, s, vh = np.linalg.svd(x) - R = np.dot(u,vh) if np.shape(x) == (3,3) else \ + u, s, vh = np.linalg.svd(T) + R = np.dot(u,vh) if np.shape(T) == (3,3) else \ np.einsum('ijk,ikl->ijl',u,vh) - + output = [] if 'R' in requested: output.append(R) if 'V' in requested: - output.append(np.dot(x,R.T) if np.shape(x) == (3,3) else np.einsum('ijk,ilk->ijl',x,R)) + output.append(np.dot(T,R.T) if np.shape(T) == (3,3) else np.einsum('ijk,ilk->ijl',T,R)) if 'U' in requested: - output.append(np.dot(R.T,x) if np.shape(x) == (3,3) else np.einsum('ikj,ikl->ijl',R,x)) - + output.append(np.dot(R.T,T) if np.shape(T) == (3,3) else np.einsum('ikj,ikl->ijl',R,T)) + return tuple(output) + + +def __Mises(T_sym,s): + """ + Base equation for Mises equivalent of a stres or strain tensor. + + Parameters + ---------- + T_sym : numpy.array of shape (:,3,3) or (3,3) + Symmetric tensor of which the von Mises equivalent is computed. + s : float + Scaling factor (2/3 for strain, 3/2 for stress). + + """ + d = deviatoric_part(T_sym) + return np.sqrt(s*(np.sum(d**2.0))) if np.shape(T_sym) == (3,3) else \ + np.sqrt(s*np.einsum('ijk->i',d**2.0)) diff --git a/python/damask/result.py b/python/damask/result.py new file mode 100644 index 000000000..f58b57f23 --- /dev/null +++ b/python/damask/result.py @@ -0,0 +1,1139 @@ +import multiprocessing +import re +import glob +import os +from functools import partial + +import vtk +from vtk.util import numpy_support +import h5py +import numpy as np + +from . import util +from . import version +from . import mechanics +from . import Rotation +from . import Orientation +from . import Environment +from . import grid_filters + +class Result(): + """ + Read and write to DADF5 files. + + DADF5 (DAKMASK HDF5) files contain DAMASK results. + """ + + def __init__(self,fname): + """ + Opens an existing DADF5 file. + + Parameters + ---------- + fname : str + name of the DADF5 file to be openend. + + """ + with h5py.File(fname,'r') as f: + + try: + self.version_major = f.attrs['DADF5_version_major'] + self.version_minor = f.attrs['DADF5_version_minor'] + except KeyError: + self.version_major = f.attrs['DADF5-major'] + self.version_minor = f.attrs['DADF5-minor'] + + if self.version_major != 0 or not 2 <= self.version_minor <= 6: + raise TypeError('Unsupported DADF5 version {}.{} '.format(self.version_major, + self.version_minor)) + + self.structured = 'grid' in f['geometry'].attrs.keys() + + if self.structured: + self.grid = f['geometry'].attrs['grid'] + self.size = f['geometry'].attrs['size'] + self.origin = f['geometry'].attrs['origin'] if self.version_major == 0 and self.version_minor >= 5 else \ + np.zeros(3) + + r=re.compile('inc[0-9]+') + increments_unsorted = {int(i[3:]):i for i in f.keys() if r.match(i)} + self.increments = [increments_unsorted[i] for i in sorted(increments_unsorted)] + self.times = [round(f[i].attrs['time/s'],12) for i in self.increments] + + self.Nmaterialpoints, self.Nconstituents = np.shape(f['mapping/cellResults/constituent']) + self.materialpoints = [m.decode() for m in np.unique(f['mapping/cellResults/materialpoint']['Name'])] + self.constituents = [c.decode() for c in np.unique(f['mapping/cellResults/constituent'] ['Name'])] + + self.con_physics = [] + for c in self.constituents: + self.con_physics += f['/'.join([self.increments[0],'constituent',c])].keys() + self.con_physics = list(set(self.con_physics)) # make unique + + self.mat_physics = [] + for m in self.materialpoints: + self.mat_physics += f['/'.join([self.increments[0],'materialpoint',m])].keys() + self.mat_physics = list(set(self.mat_physics)) # make unique + + self.selection= {'increments': self.increments, + 'constituents': self.constituents, + 'materialpoints': self.materialpoints, + 'con_physics': self.con_physics, + 'mat_physics': self.mat_physics} + + self.fname = fname + + + def _manage_selection(self,action,what,datasets): + """ + Manages the visibility of the groups. + + Parameters + ---------- + action : str + select from 'set', 'add', and 'del' + what : str + attribute to change (must be from self.selection) + datasets : list of str or Boolean + name of datasets as list, supports ? and * wildcards. + True is equivalent to [*], False is equivalent to [] + + """ + # allow True/False and string arguments + if datasets is True: + datasets = ['*'] + elif datasets is False: + datasets = [] + choice = datasets if hasattr(datasets,'__iter__') and not isinstance(datasets,str) else \ + [datasets] + + if what == 'increments': + choice = [c if isinstance(c,str) and c.startswith('inc') else + 'inc{}'.format(c) for c in choice] + elif what == 'times': + what = 'increments' + if choice == ['*']: + choice = self.increments + else: + iterator = map(float,choice) + choice = [] + for c in iterator: + idx=np.searchsorted(self.times,c) + if np.isclose(c,self.times[idx]): + choice.append(self.increments[idx]) + elif np.isclose(c,self.times[idx+1]): + choice.append(self.increments[idx+1]) + + valid = [e for e_ in [glob.fnmatch.filter(getattr(self,what),s) for s in choice] for e in e_] + existing = set(self.selection[what]) + + if action == 'set': + self.selection[what] = valid + elif action == 'add': + add=existing.union(valid) + add_sorted=sorted(add, key=lambda x: int("".join([i for i in x if i.isdigit()]))) + self.selection[what] = add_sorted + elif action == 'del': + diff=existing.difference(valid) + diff_sorted=sorted(diff, key=lambda x: int("".join([i for i in x if i.isdigit()]))) + self.selection[what] = diff_sorted + + + def incs_in_range(self,start,end): + selected = [] + for i,inc in enumerate([int(i[3:]) for i in self.increments]): + s,e = map(lambda x: int(x[3:] if isinstance(x,str) and x.startswith('inc') else x), (start,end)) + if s <= inc <= e: + selected.append(self.increments[i]) + return selected + + + def times_in_range(self,start,end): + selected = [] + for i,time in enumerate(self.times): + if start <= time <= end: + selected.append(self.increments[i]) + return selected + + + + def iter_selection(self,what): + """ + Iterate over selection items by setting each one selected. + + Parameters + ---------- + what : str + attribute to change (must be from self.selection) + + """ + datasets = self.selection[what] + last_datasets = datasets.copy() + for dataset in datasets: + if last_datasets != self.selection[what]: + self._manage_selection('set',what,datasets) + raise Exception + self._manage_selection('set',what,dataset) + last_datasets = self.selection[what] + yield dataset + self._manage_selection('set',what,datasets) + + + def pick(self,what,datasets): + """ + Set selection. + + Parameters + ---------- + what : str + attribute to change (must be from self.selection) + datasets : list of str or Boolean + name of datasets as list, supports ? and * wildcards. + True is equivalent to [*], False is equivalent to [] + + """ + self._manage_selection('set',what,datasets) + + + def pick_more(self,what,datasets): + """ + Add to selection. + + Parameters + ---------- + what : str + attribute to change (must be from self.selection) + datasets : list of str or Boolean + name of datasets as list, supports ? and * wildcards. + True is equivalent to [*], False is equivalent to [] + + """ + self._manage_selection('add',what,datasets) + + + def pick_less(self,what,datasets): + """ + Delete from selection. + + Parameters + ---------- + what : str + attribute to change (must be from self.selection) + datasets : list of str or Boolean + name of datasets as list, supports ? and * wildcards. + True is equivalent to [*], False is equivalent to [] + + """ + self._manage_selection('del',what,datasets) + + + def groups_with_datasets(self,datasets): + """ + Return groups that contain all requested datasets. + + Only groups within + - inc?????/constituent/*_*/* + - inc?????/materialpoint/*_*/* + - inc?????/geometry/* + are considered as they contain user-relevant data. + Single strings will be treated as list with one entry. + + Wild card matching is allowed, but the number of arguments need to fit. + + Parameters + ---------- + datasets : iterable or str or Boolean + + Examples + -------- + datasets = False matches no group + datasets = True matches all groups + datasets = ['F','P'] matches a group with ['F','P','sigma'] + datasets = ['*','P'] matches a group with ['F','P'] + datasets = ['*'] does not match a group with ['F','P','sigma'] + datasets = ['*','*'] does not match a group with ['F','P','sigma'] + datasets = ['*','*','*'] matches a group with ['F','P','sigma'] + + """ + if datasets is False: return [] + + sets = datasets if isinstance(datasets,bool) or (hasattr(datasets,'__iter__') and not isinstance(datasets,str)) else \ + [datasets] + + groups = [] + + with h5py.File(self.fname,'r') as f: + for i in self.iter_selection('increments'): + for o,p in zip(['constituents','materialpoints'],['con_physics','mat_physics']): + for oo in self.iter_selection(o): + for pp in self.iter_selection(p): + group = '/'.join([i,o[:-1],oo,pp]) # o[:-1]: plural/singular issue + if sets is True: + groups.append(group) + else: + match = [e for e_ in [glob.fnmatch.filter(f[group].keys(),s) for s in sets] for e in e_] + if len(set(match)) == len(sets) : groups.append(group) + return groups + + + def list_data(self): + """Return information on all active datasets in the file.""" + message = '' + with h5py.File(self.fname,'r') as f: + for i in self.iter_selection('increments'): + message+='\n{} ({}s)\n'.format(i,self.times[self.increments.index(i)]) + for o,p in zip(['constituents','materialpoints'],['con_physics','mat_physics']): + for oo in self.iter_selection(o): + message+=' {}\n'.format(oo) + for pp in self.iter_selection(p): + message+=' {}\n'.format(pp) + group = '/'.join([i,o[:-1],oo,pp]) # o[:-1]: plural/singular issue + for d in f[group].keys(): + try: + dataset = f['/'.join([group,d])] + message+=' {} / ({}): {}\n'.\ + format(d,dataset.attrs['Unit'].decode(),dataset.attrs['Description'].decode()) + except KeyError: + pass + return message + + + def get_dataset_location(self,label): + """Return the location of all active datasets with given label.""" + path = [] + with h5py.File(self.fname,'r') as f: + for i in self.iter_selection('increments'): + k = '/'.join([i,'geometry',label]) + try: + f[k] + path.append(k) + except KeyError as e: + pass + for o,p in zip(['constituents','materialpoints'],['con_physics','mat_physics']): + for oo in self.iter_selection(o): + for pp in self.iter_selection(p): + k = '/'.join([i,o[:-1],oo,pp,label]) + try: + f[k] + path.append(k) + except KeyError as e: + pass + return path + + + def get_constituent_ID(self,c=0): + """Pointwise constituent ID.""" + with h5py.File(self.fname,'r') as f: + names = f['/mapping/cellResults/constituent']['Name'][:,c].astype('str') + return np.array([int(n.split('_')[0]) for n in names.tolist()],dtype=np.int32) + + + def get_crystal_structure(self): # ToDo: extension to multi constituents/phase + """Info about the crystal structure.""" + with h5py.File(self.fname,'r') as f: + return f[self.get_dataset_location('orientation')[0]].attrs['Lattice'].astype('str') # np.bytes_ to string + + + def read_dataset(self,path,c=0,plain=False): + """ + Dataset for all points/cells. + + If more than one path is given, the dataset is composed of the individual contributions. + """ + with h5py.File(self.fname,'r') as f: + shape = (self.Nmaterialpoints,) + np.shape(f[path[0]])[1:] + if len(shape) == 1: shape = shape +(1,) + dataset = np.full(shape,np.nan,dtype=np.dtype(f[path[0]])) + for pa in path: + label = pa.split('/')[2] + + if (pa.split('/')[1] == 'geometry'): + dataset = np.array(f[pa]) + continue + + p = np.where(f['mapping/cellResults/constituent'][:,c]['Name'] == str.encode(label))[0] + if len(p)>0: + u = (f['mapping/cellResults/constituent']['Position'][p,c]) + a = np.array(f[pa]) + if len(a.shape) == 1: + a=a.reshape([a.shape[0],1]) + dataset[p,:] = a[u,:] + + p = np.where(f['mapping/cellResults/materialpoint']['Name'] == str.encode(label))[0] + if len(p)>0: + u = (f['mapping/cellResults/materialpoint']['Position'][p.tolist()]) + a = np.array(f[pa]) + if len(a.shape) == 1: + a=a.reshape([a.shape[0],1]) + dataset[p,:] = a[u,:] + + if plain and dataset.dtype.names is not None: + return dataset.view(('float64',len(dataset.dtype.names))) + else: + return dataset + + + def cell_coordinates(self): + """Return initial coordinates of the cell centers.""" + if self.structured: + return grid_filters.cell_coord0(self.grid,self.size,self.origin) + else: + with h5py.File(self.fname,'r') as f: + return f['geometry/x_c'][()] + + + @staticmethod + def _add_absolute(x): + return { + 'data': np.abs(x['data']), + 'label': '|{}|'.format(x['label']), + 'meta': { + 'Unit': x['meta']['Unit'], + 'Description': 'Absolute value of {} ({})'.format(x['label'],x['meta']['Description']), + 'Creator': 'result.py:add_abs v{}'.format(version) + } + } + def add_absolute(self,x): + """ + Add absolute value. + + Parameters + ---------- + x : str + Label of scalar, vector, or tensor dataset to take absolute value of. + + """ + self._add_generic_pointwise(self._add_absolute,{'x':x}) + + + @staticmethod + def _add_calculation(**kwargs): + formula = kwargs['formula'] + for d in re.findall(r'#(.*?)#',formula): + formula = formula.replace('#{}#'.format(d),"kwargs['{}']['data']".format(d)) + + return { + 'data': eval(formula), + 'label': kwargs['label'], + 'meta': { + 'Unit': kwargs['unit'], + 'Description': '{} (formula: {})'.format(kwargs['description'],kwargs['formula']), + 'Creator': 'result.py:add_calculation v{}'.format(version) + } + } + def add_calculation(self,label,formula,unit='n/a',description=None,vectorized=True): + """ + Add result of a general formula. + + Parameters + ---------- + label : str + Label of resulting dataset. + formula : str + Formula to calculate resulting dataset. Existing datasets are referenced by ‘#TheirLabel#‘. + unit : str, optional + Physical unit of the result. + description : str, optional + Human-readable description of the result. + vectorized : bool, optional + Indicate whether the formula can be used in vectorized form. Defaults to ‘True’. + + """ + if not vectorized: + raise NotImplementedError + + dataset_mapping = {d:d for d in set(re.findall(r'#(.*?)#',formula))} # datasets used in the formula + args = {'formula':formula,'label':label,'unit':unit,'description':description} + self._add_generic_pointwise(self._add_calculation,dataset_mapping,args) + + + @staticmethod + def _add_Cauchy(P,F): + return { + 'data': mechanics.Cauchy(P['data'],F['data']), + 'label': 'sigma', + 'meta': { + 'Unit': P['meta']['Unit'], + 'Description': 'Cauchy stress calculated from {} ({}) '.format(P['label'], + P['meta']['Description'])+\ + 'and {} ({})'.format(F['label'],F['meta']['Description']), + 'Creator': 'result.py:add_Cauchy v{}'.format(version) + } + } + def add_Cauchy(self,P='P',F='F'): + """ + Add Cauchy stress calculated from first Piola-Kirchhoff stress and deformation gradient. + + Parameters + ---------- + P : str, optional + Label of the dataset containing the first Piola-Kirchhoff stress. Defaults to ‘P’. + F : str, optional + Label of the dataset containing the deformation gradient. Defaults to ‘F’. + + """ + self._add_generic_pointwise(self._add_Cauchy,{'P':P,'F':F}) + + + @staticmethod + def _add_determinant(T): + return { + 'data': np.linalg.det(T['data']), + 'label': 'det({})'.format(T['label']), + 'meta': { + 'Unit': T['meta']['Unit'], + 'Description': 'Determinant of tensor {} ({})'.format(T['label'],T['meta']['Description']), + 'Creator': 'result.py:add_determinant v{}'.format(version) + } + } + def add_determinant(self,T): + """ + Add the determinant of a tensor. + + Parameters + ---------- + T : str + Label of tensor dataset. + + """ + self._add_generic_pointwise(self._add_determinant,{'T':T}) + + + @staticmethod + def _add_deviator(T): + if not T['data'].shape[1:] == (3,3): + raise ValueError + + return { + 'data': mechanics.deviatoric_part(T['data']), + 'label': 's_{}'.format(T['label']), + 'meta': { + 'Unit': T['meta']['Unit'], + 'Description': 'Deviator of tensor {} ({})'.format(T['label'],T['meta']['Description']), + 'Creator': 'result.py:add_deviator v{}'.format(version) + } + } + def add_deviator(self,T): + """ + Add the deviatoric part of a tensor. + + Parameters + ---------- + T : str + Label of tensor dataset. + + """ + self._add_generic_pointwise(self._add_deviator,{'T':T}) + + + @staticmethod + def _add_eigenvalue(T_sym): + return { + 'data': mechanics.eigenvalues(T_sym['data']), + 'label': 'lambda({})'.format(T_sym['label']), + 'meta' : { + 'Unit': T_sym['meta']['Unit'], + 'Description': 'Eigenvalues of {} ({})'.format(T_sym['label'],T_sym['meta']['Description']), + 'Creator': 'result.py:add_eigenvalues v{}'.format(version) + } + } + def add_eigenvalues(self,T_sym): + """ + Add eigenvalues of symmetric tensor. + + Parameters + ---------- + T_sym : str + Label of symmetric tensor dataset. + + """ + self._add_generic_pointwise(self._add_eigenvalue,{'T_sym':T_sym}) + + + @staticmethod + def _add_eigenvector(T_sym): + return { + 'data': mechanics.eigenvectors(T_sym['data']), + 'label': 'v({})'.format(T_sym['label']), + 'meta' : { + 'Unit': '1', + 'Description': 'Eigenvectors of {} ({})'.format(T_sym['label'],T_sym['meta']['Description']), + 'Creator': 'result.py:add_eigenvectors v{}'.format(version) + } + } + def add_eigenvectors(self,T_sym): + """ + Add eigenvectors of symmetric tensor. + + Parameters + ---------- + T_sym : str + Label of symmetric tensor dataset. + + """ + self._add_generic_pointwise(self._add_eigenvector,{'T_sym':T_sym}) + + + @staticmethod + def _add_IPFcolor(q,l): + d = np.array(l) + d_unit = d/np.linalg.norm(d) + m = util.scale_to_coprime(d) + colors = np.empty((len(q['data']),3),np.uint8) + + lattice = q['meta']['Lattice'] + + for i,q in enumerate(q['data']): + o = Orientation(np.array([q['w'],q['x'],q['y'],q['z']]),lattice).reduced() + colors[i] = np.uint8(o.IPFcolor(d_unit)*255) + + return { + 'data': colors, + 'label': 'IPFcolor_[{} {} {}]'.format(*m), + 'meta' : { + 'Unit': 'RGB (8bit)', + 'Lattice': lattice, + 'Description': 'Inverse Pole Figure (IPF) colors for direction/plane [{} {} {})'.format(*m), + 'Creator': 'result.py:add_IPFcolor v{}'.format(version) + } + } + def add_IPFcolor(self,q,l): + """ + Add RGB color tuple of inverse pole figure (IPF) color. + + Parameters + ---------- + q : str + Label of the dataset containing the crystallographic orientation as quaternions. + l : numpy.array of shape (3) + Lab frame direction for inverse pole figure. + + """ + self._add_generic_pointwise(self._add_IPFcolor,{'q':q},{'l':l}) + + + @staticmethod + def _add_maximum_shear(T_sym): + return { + 'data': mechanics.maximum_shear(T_sym['data']), + 'label': 'max_shear({})'.format(T_sym['label']), + 'meta': { + 'Unit': T_sym['meta']['Unit'], + 'Description': 'Maximum shear component of {} ({})'.format(T_sym['label'],T_sym['meta']['Description']), + 'Creator': 'result.py:add_maximum_shear v{}'.format(version) + } + } + def add_maximum_shear(self,T_sym): + """ + Add maximum shear components of symmetric tensor. + + Parameters + ---------- + T_sym : str + Label of symmetric tensor dataset. + + """ + self._add_generic_pointwise(self._add_maximum_shear,{'T_sym':T_sym}) + + + @staticmethod + def _add_Mises(T_sym): + t = 'strain' if T_sym['meta']['Unit'] == '1' else \ + 'stress' + + return { + 'data': mechanics.Mises_strain(T_sym['data']) if t=='strain' else mechanics.Mises_stress(T_sym['data']), + 'label': '{}_vM'.format(T_sym['label']), + 'meta': { + 'Unit': T_sym['meta']['Unit'], + 'Description': 'Mises equivalent {} of {} ({})'.format(t,T_sym['label'],T_sym['meta']['Description']), + 'Creator': 'result.py:add_Mises v{}'.format(version) + } + } + def add_Mises(self,T_sym): + """ + Add the equivalent Mises stress or strain of a symmetric tensor. + + Parameters + ---------- + T_sym : str + Label of symmetric tensorial stress or strain dataset. + + """ + self._add_generic_pointwise(self._add_Mises,{'T_sym':T_sym}) + + + @staticmethod + def _add_norm(x,ord): + o = ord + if len(x['data'].shape) == 2: + axis = 1 + t = 'vector' + if o is None: o = 2 + elif len(x['data'].shape) == 3: + axis = (1,2) + t = 'tensor' + if o is None: o = 'fro' + else: + raise ValueError + + return { + 'data': np.linalg.norm(x['data'],ord=o,axis=axis,keepdims=True), + 'label': '|{}|_{}'.format(x['label'],o), + 'meta': { + 'Unit': x['meta']['Unit'], + 'Description': '{}-norm of {} {} ({})'.format(o,t,x['label'],x['meta']['Description']), + 'Creator': 'result.py:add_norm v{}'.format(version) + } + } + def add_norm(self,x,ord=None): + """ + Add the norm of vector or tensor. + + Parameters + ---------- + x : str + Label of vector or tensor dataset. + ord : {non-zero int, inf, -inf, ‘fro’, ‘nuc’}, optional + Order of the norm. inf means NumPy’s inf object. For details refer to numpy.linalg.norm. + + """ + self._add_generic_pointwise(self._add_norm,{'x':x},{'ord':ord}) + + + @staticmethod + def _add_PK2(P,F): + return { + 'data': mechanics.PK2(P['data'],F['data']), + 'label': 'S', + 'meta': { + 'Unit': P['meta']['Unit'], + 'Description': '2. Kirchhoff stress calculated from {} ({}) '.format(P['label'], + P['meta']['Description'])+\ + 'and {} ({})'.format(F['label'],F['meta']['Description']), + 'Creator': 'result.py:add_PK2 v{}'.format(version) + } + } + def add_PK2(self,P='P',F='F'): + """ + Add second Piola-Kirchhoff calculated from first Piola-Kirchhoff stress and deformation gradient. + + Parameters + ---------- + P : str, optional + Label first Piola-Kirchhoff stress dataset. Defaults to ‘P’. + F : str, optional + Label of deformation gradient dataset. Defaults to ‘F’. + + """ + self._add_generic_pointwise(self._add_PK2,{'P':P,'F':F}) + + + @staticmethod + def _add_pole(q,p,polar): + pole = np.array(p) + unit_pole = pole/np.linalg.norm(pole) + m = util.scale_to_coprime(pole) + coords = np.empty((len(q['data']),2)) + + for i,q in enumerate(q['data']): + o = Rotation(np.array([q['w'],q['x'],q['y'],q['z']])) + rotatedPole = o*unit_pole # rotate pole according to crystal orientation + (x,y) = rotatedPole[0:2]/(1.+abs(unit_pole[2])) # stereographic projection + coords[i] = [np.sqrt(x*x+y*y),np.arctan2(y,x)] if polar else [x,y] + + return { + 'data': coords, + 'label': 'p^{}_[{} {} {})'.format(u'rφ' if polar else 'xy',*m), + 'meta' : { + 'Unit': '1', + 'Description': '{} coordinates of stereographic projection of pole (direction/plane) in crystal frame'\ + .format('Polar' if polar else 'Cartesian'), + 'Creator' : 'result.py:add_pole v{}'.format(version) + } + } + def add_pole(self,q,p,polar=False): + """ + Add coordinates of stereographic projection of given pole in crystal frame. + + Parameters + ---------- + q : str + Label of the dataset containing the crystallographic orientation as quaternions. + p : numpy.array of shape (3) + Crystallographic direction or plane. + polar : bool, optional + Give pole in polar coordinates. Defaults to False. + + """ + self._add_generic_pointwise(self._add_pole,{'q':q},{'p':p,'polar':polar}) + + + @staticmethod + def _add_rotational_part(F): + if not F['data'].shape[1:] == (3,3): + raise ValueError + return { + 'data': mechanics.rotational_part(F['data']), + 'label': 'R({})'.format(F['label']), + 'meta': { + 'Unit': F['meta']['Unit'], + 'Description': 'Rotational part of {} ({})'.format(F['label'],F['meta']['Description']), + 'Creator': 'result.py:add_rotational_part v{}'.format(version) + } + } + def add_rotational_part(self,F): + """ + Add rotational part of a deformation gradient. + + Parameters + ---------- + F : str, optional + Label of deformation gradient dataset. + + """ + self._add_generic_pointwise(self._add_rotational_part,{'F':F}) + + + @staticmethod + def _add_spherical(T): + if not T['data'].shape[1:] == (3,3): + raise ValueError + + return { + 'data': mechanics.spherical_part(T['data']), + 'label': 'p_{}'.format(T['label']), + 'meta': { + 'Unit': T['meta']['Unit'], + 'Description': 'Spherical component of tensor {} ({})'.format(T['label'],T['meta']['Description']), + 'Creator': 'result.py:add_spherical v{}'.format(version) + } + } + def add_spherical(self,T): + """ + Add the spherical (hydrostatic) part of a tensor. + + Parameters + ---------- + T : str + Label of tensor dataset. + + """ + self._add_generic_pointwise(self._add_spherical,{'T':T}) + + + @staticmethod + def _add_strain_tensor(F,t,m): + if not F['data'].shape[1:] == (3,3): + raise ValueError + + return { + 'data': mechanics.strain_tensor(F['data'],t,m), + 'label': 'epsilon_{}^{}({})'.format(t,m,F['label']), + 'meta': { + 'Unit': F['meta']['Unit'], + 'Description': 'Strain tensor of {} ({})'.format(F['label'],F['meta']['Description']), + 'Creator': 'result.py:add_strain_tensor v{}'.format(version) + } + } + def add_strain_tensor(self,F='F',t='V',m=0.0): + """ + Add strain tensor of a deformation gradient. + + For details refer to damask.mechanics.strain_tensor + + Parameters + ---------- + F : str, optional + Label of deformation gradient dataset. Defaults to ‘F’. + t : {‘V’, ‘U’}, optional + Type of the polar decomposition, ‘V’ for left stretch tensor and ‘U’ for right stretch tensor. + Defaults to ‘V’. + m : float, optional + Order of the strain calculation. Defaults to ‘0.0’. + + """ + self._add_generic_pointwise(self._add_strain_tensor,{'F':F},{'t':t,'m':m}) + + + @staticmethod + def _add_stretch_tensor(F,t): + if not F['data'].shape[1:] == (3,3): + raise ValueError + + return { + 'data': mechanics.left_stretch(F['data']) if t == 'V' else mechanics.right_stretch(F['data']), + 'label': '{}({})'.format(t,F['label']), + 'meta': { + 'Unit': F['meta']['Unit'], + 'Description': '{} stretch tensor of {} ({})'.format('Left' if t == 'V' else 'Right', + F['label'],F['meta']['Description']), + 'Creator': 'result.py:add_stretch_tensor v{}'.format(version) + } + } + def add_stretch_tensor(self,F='F',t='V'): + """ + Add stretch tensor of a deformation gradient. + + Parameters + ---------- + F : str, optional + Label of deformation gradient dataset. Defaults to ‘F’. + t : {‘V’, ‘U’}, optional + Type of the polar decomposition, ‘V’ for left stretch tensor and ‘U’ for right stretch tensor. + Defaults to ‘V’. + + """ + self._add_generic_pointwise(self._add_stretch_tensor,{'F':F},{'t':t}) + + + def _job(self,group,func,datasets,args,lock): + """Execute job for _add_generic_pointwise.""" + try: + datasets_in = {} + lock.acquire() + with h5py.File(self.fname,'r') as f: + for arg,label in datasets.items(): + loc = f[group+'/'+label] + datasets_in[arg]={'data' :loc[()], + 'label':label, + 'meta': {k:v.decode() for k,v in loc.attrs.items()}} + lock.release() + r = func(**datasets_in,**args) + return [group,r] + except Exception as err: + print('Error during calculation: {}.'.format(err)) + return None + + + def _add_generic_pointwise(self,func,datasets,args={}): + """ + General function to add pointwise data. + + Parameters + ---------- + func : function + Callback function that calculates a new dataset from one or more datasets per HDF5 group. + datasets : dictionary + Details of the datasets to be used: label (in HDF5 file) and arg (argument to which the data is parsed in func). + args : dictionary, optional + Arguments parsed to func. + + """ + pool = multiprocessing.Pool(int(Environment().options['DAMASK_NUM_THREADS'])) + lock = multiprocessing.Manager().Lock() + + groups = self.groups_with_datasets(datasets.values()) + default_arg = partial(self._job,func=func,datasets=datasets,args=args,lock=lock) + + util.progressBar(iteration=0,total=len(groups)) + for i,result in enumerate(pool.imap_unordered(default_arg,groups)): + util.progressBar(iteration=i+1,total=len(groups)) + if not result: continue + lock.acquire() + with h5py.File(self.fname, 'a') as f: + try: + dataset = f[result[0]].create_dataset(result[1]['label'],data=result[1]['data']) + for l,v in result[1]['meta'].items(): + dataset.attrs[l]=v.encode() + except OSError as err: + print('Could not add dataset: {}.'.format(err)) + lock.release() + + pool.close() + pool.join() + + + def to_vtk(self,labels,mode='cell'): + """ + Export to vtk cell/point data. + + Parameters + ---------- + labels : str or list of + Labels of the datasets to be exported. + mode : str, either 'cell' or 'point' + Export in cell format or point format. + Defaults to 'cell'. + + """ + if mode.lower()=='cell': + + if self.structured: + coordArray = [vtk.vtkDoubleArray(),vtk.vtkDoubleArray(),vtk.vtkDoubleArray()] + for dim in [0,1,2]: + for c in np.linspace(0,self.size[dim],1+self.grid[dim]): + coordArray[dim].InsertNextValue(c) + + vtk_geom = vtk.vtkRectilinearGrid() + vtk_geom.SetDimensions(*(self.grid+1)) + vtk_geom.SetXCoordinates(coordArray[0]) + vtk_geom.SetYCoordinates(coordArray[1]) + vtk_geom.SetZCoordinates(coordArray[2]) + else: + nodes = vtk.vtkPoints() + with h5py.File(self.fname,'r') as f: + nodes.SetData(numpy_support.numpy_to_vtk(f['/geometry/x_n'][()],deep=True)) + + vtk_geom = vtk.vtkUnstructuredGrid() + vtk_geom.SetPoints(nodes) + vtk_geom.Allocate(f['/geometry/T_c'].shape[0]) + + if self.version_major == 0 and self.version_minor <= 5: + vtk_type = vtk.VTK_HEXAHEDRON + n_nodes = 8 + else: + if f['/geometry/T_c'].attrs['VTK_TYPE'] == b'TRIANGLE': + vtk_type = vtk.VTK_TRIANGLE + n_nodes = 3 + elif f['/geometry/T_c'].attrs['VTK_TYPE'] == b'QUAD': + vtk_type = vtk.VTK_QUAD + n_nodes = 4 + elif f['/geometry/T_c'].attrs['VTK_TYPE'] == b'TETRA': # not tested + vtk_type = vtk.VTK_TETRA + n_nodes = 4 + elif f['/geometry/T_c'].attrs['VTK_TYPE'] == b'HEXAHEDRON': + vtk_type = vtk.VTK_HEXAHEDRON + n_nodes = 8 + + for i in f['/geometry/T_c']: + vtk_geom.InsertNextCell(vtk_type,n_nodes,i-1) + + elif mode.lower()=='point': + Points = vtk.vtkPoints() + Vertices = vtk.vtkCellArray() + for c in self.cell_coordinates(): + pointID = Points.InsertNextPoint(c) + Vertices.InsertNextCell(1) + Vertices.InsertCellPoint(pointID) + + vtk_geom = vtk.vtkPolyData() + vtk_geom.SetPoints(Points) + vtk_geom.SetVerts(Vertices) + vtk_geom.Modified() + + N_digits = int(np.floor(np.log10(int(self.increments[-1][3:]))))+1 + + for i,inc in enumerate(self.iter_selection('increments')): + vtk_data = [] + + materialpoints_backup = self.selection['materialpoints'].copy() + self.pick('materialpoints',False) + for label in (labels if isinstance(labels,list) else [labels]): + for p in self.iter_selection('con_physics'): + if p != 'generic': + for c in self.iter_selection('constituents'): + x = self.get_dataset_location(label) + if len(x) == 0: + continue + array = self.read_dataset(x,0) + shape = [array.shape[0],np.product(array.shape[1:])] + vtk_data.append(numpy_support.numpy_to_vtk(num_array=array.reshape(shape),deep=True)) + vtk_data[-1].SetName('1_'+x[0].split('/',1)[1]) #ToDo: hard coded 1! + vtk_geom.GetCellData().AddArray(vtk_data[-1]) + + else: + x = self.get_dataset_location(label) + if len(x) == 0: + continue + array = self.read_dataset(x,0) + shape = [array.shape[0],np.product(array.shape[1:])] + vtk_data.append(numpy_support.numpy_to_vtk(num_array=array.reshape(shape),deep=True)) + ph_name = re.compile(r'(?<=(constituent\/))(.*?)(?=(generic))') # identify phase name + dset_name = '1_' + re.sub(ph_name,r'',x[0].split('/',1)[1]) # removing phase name + vtk_data[-1].SetName(dset_name) + vtk_geom.GetCellData().AddArray(vtk_data[-1]) + + self.pick('materialpoints',materialpoints_backup) + + constituents_backup = self.selection['constituents'].copy() + self.pick('constituents',False) + for label in (labels if isinstance(labels,list) else [labels]): + for p in self.iter_selection('mat_physics'): + if p != 'generic': + for m in self.iter_selection('materialpoints'): + x = self.get_dataset_location(label) + if len(x) == 0: + continue + array = self.read_dataset(x,0) + shape = [array.shape[0],np.product(array.shape[1:])] + vtk_data.append(numpy_support.numpy_to_vtk(num_array=array.reshape(shape),deep=True)) + vtk_data[-1].SetName('1_'+x[0].split('/',1)[1]) #ToDo: why 1_? + vtk_geom.GetCellData().AddArray(vtk_data[-1]) + else: + x = self.get_dataset_location(label) + if len(x) == 0: + continue + array = self.read_dataset(x,0) + shape = [array.shape[0],np.product(array.shape[1:])] + vtk_data.append(numpy_support.numpy_to_vtk(num_array=array.reshape(shape),deep=True)) + vtk_data[-1].SetName('1_'+x[0].split('/',1)[1]) + vtk_geom.GetCellData().AddArray(vtk_data[-1]) + self.pick('constituents',constituents_backup) + + if mode.lower()=='cell': + writer = vtk.vtkXMLRectilinearGridWriter() if self.structured else \ + vtk.vtkXMLUnstructuredGridWriter() + x = self.get_dataset_location('u_n') + vtk_data.append(numpy_support.numpy_to_vtk(num_array=self.read_dataset(x,0),deep=True)) + vtk_data[-1].SetName('u') + vtk_geom.GetPointData().AddArray(vtk_data[-1]) + elif mode.lower()=='point': + writer = vtk.vtkXMLPolyDataWriter() + + + file_out = '{}_inc{}.{}'.format(os.path.splitext(os.path.basename(self.fname))[0], + inc[3:].zfill(N_digits), + writer.GetDefaultFileExtension()) + + writer.SetCompressorTypeToZLib() + writer.SetDataModeToBinary() + writer.SetFileName(file_out) + writer.SetInputData(vtk_geom) + + writer.Write() + + +################################################################################################### +# BEGIN DEPRECATED + iter_visible = iter_selection + + + def _time_to_inc(self,start,end): + selected = [] + for i,time in enumerate(self.times): + if start <= time <= end: + selected.append(self.increments[i]) + return selected + + + def set_by_time(self,start,end): + """ + Set active increments based on start and end time. + + Parameters + ---------- + start : float + start time (included) + end : float + end time (included) + + """ + self._manage_selection('set','increments',self._time_to_inc(start,end)) + + + def set_by_increment(self,start,end): + """ + Set active time increments based on start and end increment. + + Parameters + ---------- + start : int + start increment (included) + end : int + end increment (included) + + """ + if self.version_minor >= 4: + self._manage_selection('set','increments',[ 'inc{}'.format(i) for i in range(start,end+1)]) + else: + self._manage_selection('set','increments',['inc{:05d}'.format(i) for i in range(start,end+1)]) diff --git a/python/damask/table.py b/python/damask/table.py index 15b501ac9..a4badb3dc 100644 --- a/python/damask/table.py +++ b/python/damask/table.py @@ -32,7 +32,7 @@ class Table(): """Label data individually, e.g. v v v ==> 1_v 2_v 3_v.""" labels = [] for label,shape in self.shapes.items(): - size = np.prod(shape) + size = int(np.prod(shape)) labels += ['{}{}'.format('' if size == 1 else '{}_'.format(i+1),label) for i in range(size)] self.data.columns = labels @@ -41,14 +41,14 @@ class Table(): """Label data condensed, e.g. 1_v 2_v 3_v ==> v v v.""" labels = [] for label,shape in self.shapes.items(): - labels += [label] * np.prod(shape) + labels += [label] * int(np.prod(shape)) self.data.columns = labels def __add_comment(self,label,shape,info): if info is not None: self.comments.append('{}{}: {}'.format(label, - ' '+str(shape) if np.prod(shape) > 1 else '', + ' '+str(shape) if np.prod(shape,dtype=int) > 1 else '', info)) diff --git a/python/damask/util.py b/python/damask/util.py index 553ae0c6e..412142490 100644 --- a/python/damask/util.py +++ b/python/damask/util.py @@ -3,14 +3,16 @@ import time import os import subprocess import shlex +from fractions import Fraction +from functools import reduce from optparse import Option -from queue import Queue -from threading import Thread + +import numpy as np class bcolors: """ ASCII Colors (Blender code). - + https://svn.blender.org/svnroot/bf-blender/trunk/blender/build_files/scons/tools/bcolors.py http://stackoverflow.com/questions/287871/print-in-terminal-with-colors-using-python """ @@ -104,6 +106,7 @@ def strikeout(what): """Formats string as strikeout.""" return bcolors.CROSSOUT+srepr(what)+bcolors.ENDC + def execute(cmd, streamIn = None, wd = './', @@ -207,6 +210,24 @@ def progressBar(iteration, total, prefix='', bar_length=50): sys.stderr.flush() +def scale_to_coprime(v): + """Scale vector to co-prime (relatively prime) integers.""" + MAX_DENOMINATOR = 1000 + + def get_square_denominator(x): + """Denominator of the square of a number.""" + return Fraction(x ** 2).limit_denominator(MAX_DENOMINATOR).denominator + + def lcm(a, b): + """Least common multiple.""" + return a * b // np.gcd(a, b) + + denominators = [int(get_square_denominator(i)) for i in v] + s = reduce(lcm, denominators) ** 0.5 + m = (np.array(v)*s).astype(np.int) + return m//reduce(np.gcd,m) + + class return_message(): """Object with formatted return message.""" @@ -226,56 +247,3 @@ class return_message(): """Return message suitable for interactive shells.""" return srepr(self.message) - -class ThreadPool: - """Pool of threads consuming tasks from a queue.""" - - class Worker(Thread): - """Thread executing tasks from a given tasks queue.""" - - def __init__(self, tasks): - """Worker for tasks.""" - Thread.__init__(self) - self.tasks = tasks - self.daemon = True - self.start() - - def run(self): - while True: - func, args, kargs = self.tasks.get() - try: - func(*args, **kargs) - except Exception as e: - # An exception happened in this thread - print(e) - finally: - # Mark this task as done, whether an exception happened or not - self.tasks.task_done() - - - def __init__(self, num_threads): - """ - Thread pool. - - Parameters - ---------- - num_threads : int - number of threads - - """ - self.tasks = Queue(num_threads) - for _ in range(num_threads): - self.Worker(self.tasks) - - def add_task(self, func, *args, **kargs): - """Add a task to the queue.""" - self.tasks.put((func, args, kargs)) - - def map(self, func, args_list): - """Add a list of tasks to the queue.""" - for args in args_list: - self.add_task(func, args) - - def wait_completion(self): - """Wait for completion of all the tasks in the queue.""" - self.tasks.join() diff --git a/python/tests/reference/DADF5/12grains6x7x8.geom b/python/tests/reference/Result/12grains6x7x8.geom similarity index 100% rename from python/tests/reference/DADF5/12grains6x7x8.geom rename to python/tests/reference/Result/12grains6x7x8.geom diff --git a/python/tests/reference/DADF5/12grains6x7x8_tensionY.hdf5 b/python/tests/reference/Result/12grains6x7x8_tensionY.hdf5 similarity index 100% rename from python/tests/reference/DADF5/12grains6x7x8_tensionY.hdf5 rename to python/tests/reference/Result/12grains6x7x8_tensionY.hdf5 diff --git a/python/tests/reference/DADF5/material.config b/python/tests/reference/Result/material.config similarity index 100% rename from python/tests/reference/DADF5/material.config rename to python/tests/reference/Result/material.config diff --git a/python/tests/reference/DADF5/tensionY.load b/python/tests/reference/Result/tensionY.load similarity index 100% rename from python/tests/reference/DADF5/tensionY.load rename to python/tests/reference/Result/tensionY.load diff --git a/python/tests/test_DADF5.py b/python/tests/test_DADF5.py deleted file mode 100644 index 669ce8446..000000000 --- a/python/tests/test_DADF5.py +++ /dev/null @@ -1,90 +0,0 @@ -import shutil -import os - -import pytest -import numpy as np - -from damask import DADF5 -from damask import mechanics - -@pytest.fixture -def default(tmp_path,reference_dir): - """Small DADF5 file in temp location for modification.""" - fname = '12grains6x7x8_tensionY.hdf5' - shutil.copy(os.path.join(reference_dir,fname),tmp_path) - f = DADF5(os.path.join(tmp_path,fname)) - f.set_by_time(20.0,20.0) - return f - -@pytest.fixture -def reference_dir(reference_dir_base): - """Directory containing reference results.""" - return os.path.join(reference_dir_base,'DADF5') - - -class TestDADF5: - - def test_time_increments(self,default): - shape = default.read_dataset(default.get_dataset_location('F'),0).shape - default.set_by_time(0.0,20.0) - for i in default.iter_visible('increments'): - assert shape == default.read_dataset(default.get_dataset_location('F'),0).shape - - - def test_add_absolute(self,default): - default.add_absolute('Fe') - loc = {'Fe': default.get_dataset_location('Fe'), - '|Fe|': default.get_dataset_location('|Fe|')} - in_memory = np.abs(default.read_dataset(loc['Fe'],0)) - in_file = default.read_dataset(loc['|Fe|'],0) - assert np.allclose(in_memory,in_file) - - def test_add_calculation(self,default): - default.add_calculation('2.0*np.abs(#F#)-1.0','x','-','test') - loc = {'F': default.get_dataset_location('F'), - 'x': default.get_dataset_location('x')} - in_memory = 2.0*np.abs(default.read_dataset(loc['F'],0))-1.0 - in_file = default.read_dataset(loc['x'],0) - assert np.allclose(in_memory,in_file) - - def test_add_Cauchy(self,default): - default.add_Cauchy('P','F') - loc = {'F': default.get_dataset_location('F'), - 'P': default.get_dataset_location('P'), - 'sigma':default.get_dataset_location('sigma')} - in_memory = mechanics.Cauchy(default.read_dataset(loc['F'],0), - default.read_dataset(loc['P'],0)) - in_file = default.read_dataset(loc['sigma'],0) - assert np.allclose(in_memory,in_file) - - def test_add_determinant(self,default): - default.add_determinant('P') - loc = {'P': default.get_dataset_location('P'), - 'det(P)':default.get_dataset_location('det(P)')} - in_memory = np.linalg.det(default.read_dataset(loc['P'],0)).reshape((-1,1)) - in_file = default.read_dataset(loc['det(P)'],0) - assert np.allclose(in_memory,in_file) - - def test_add_deviator(self,default): - default.add_deviator('P') - loc = {'P' :default.get_dataset_location('P'), - 's_P':default.get_dataset_location('s_P')} - in_memory = mechanics.deviatoric_part(default.read_dataset(loc['P'],0)) - in_file = default.read_dataset(loc['s_P'],0) - assert np.allclose(in_memory,in_file) - - def test_add_norm(self,default): - default.add_norm('F',1) - loc = {'F': default.get_dataset_location('F'), - '|F|_1':default.get_dataset_location('|F|_1')} - in_memory = np.linalg.norm(default.read_dataset(loc['F'],0),ord=1,axis=(1,2),keepdims=True) - in_file = default.read_dataset(loc['|F|_1'],0) - assert np.allclose(in_memory,in_file) - - def test_add_spherical(self,default): - default.add_spherical('P') - loc = {'P': default.get_dataset_location('P'), - 'p_P': default.get_dataset_location('p_P')} - in_memory = mechanics.spherical_part(default.read_dataset(loc['P'],0)).reshape(-1,1) - in_file = default.read_dataset(loc['p_P'],0) - assert np.allclose(in_memory,in_file) diff --git a/python/tests/test_Result.py b/python/tests/test_Result.py new file mode 100644 index 000000000..33c1e2842 --- /dev/null +++ b/python/tests/test_Result.py @@ -0,0 +1,183 @@ +import shutil +import os + +import pytest +import numpy as np + +from damask import Result +from damask import mechanics + +@pytest.fixture +def default(tmp_path,reference_dir): + """Small Result file in temp location for modification.""" + fname = '12grains6x7x8_tensionY.hdf5' + shutil.copy(os.path.join(reference_dir,fname),tmp_path) + f = Result(os.path.join(tmp_path,fname)) + f.set_by_time(20.0,20.0) + return f + +@pytest.fixture +def reference_dir(reference_dir_base): + """Directory containing reference results.""" + return os.path.join(reference_dir_base,'Result') + + +class TestResult: + + def test_time_increments(self,default): + shape = default.read_dataset(default.get_dataset_location('F'),0).shape + default.set_by_time(0.0,20.0) + for i in default.iter_visible('increments'): + assert shape == default.read_dataset(default.get_dataset_location('F'),0).shape + + + def test_add_absolute(self,default): + default.add_absolute('Fe') + loc = {'Fe': default.get_dataset_location('Fe'), + '|Fe|': default.get_dataset_location('|Fe|')} + in_memory = np.abs(default.read_dataset(loc['Fe'],0)) + in_file = default.read_dataset(loc['|Fe|'],0) + assert np.allclose(in_memory,in_file) + + def test_add_calculation(self,default): + default.add_calculation('x','2.0*np.abs(#F#)-1.0','-','my notes') + loc = {'F': default.get_dataset_location('F'), + 'x': default.get_dataset_location('x')} + in_memory = 2.0*np.abs(default.read_dataset(loc['F'],0))-1.0 + in_file = default.read_dataset(loc['x'],0) + assert np.allclose(in_memory,in_file) + + def test_add_Cauchy(self,default): + default.add_Cauchy('P','F') + loc = {'F': default.get_dataset_location('F'), + 'P': default.get_dataset_location('P'), + 'sigma':default.get_dataset_location('sigma')} + in_memory = mechanics.Cauchy(default.read_dataset(loc['P'],0), + default.read_dataset(loc['F'],0)) + in_file = default.read_dataset(loc['sigma'],0) + assert np.allclose(in_memory,in_file) + + def test_add_determinant(self,default): + default.add_determinant('P') + loc = {'P': default.get_dataset_location('P'), + 'det(P)':default.get_dataset_location('det(P)')} + in_memory = np.linalg.det(default.read_dataset(loc['P'],0)).reshape((-1,1)) + in_file = default.read_dataset(loc['det(P)'],0) + assert np.allclose(in_memory,in_file) + + def test_add_deviator(self,default): + default.add_deviator('P') + loc = {'P' :default.get_dataset_location('P'), + 's_P':default.get_dataset_location('s_P')} + in_memory = mechanics.deviatoric_part(default.read_dataset(loc['P'],0)) + in_file = default.read_dataset(loc['s_P'],0) + assert np.allclose(in_memory,in_file) + + def test_add_eigenvalues(self,default): + default.add_Cauchy('P','F') + default.add_eigenvalues('sigma') + loc = {'sigma' :default.get_dataset_location('sigma'), + 'lambda(sigma)':default.get_dataset_location('lambda(sigma)')} + in_memory = mechanics.eigenvalues(default.read_dataset(loc['sigma'],0)) + in_file = default.read_dataset(loc['lambda(sigma)'],0) + assert np.allclose(in_memory,in_file) + + def test_add_eigenvectors(self,default): + default.add_Cauchy('P','F') + default.add_eigenvectors('sigma') + loc = {'sigma' :default.get_dataset_location('sigma'), + 'v(sigma)':default.get_dataset_location('v(sigma)')} + in_memory = mechanics.eigenvectors(default.read_dataset(loc['sigma'],0)) + in_file = default.read_dataset(loc['v(sigma)'],0) + assert np.allclose(in_memory,in_file) + + def test_add_maximum_shear(self,default): + default.add_Cauchy('P','F') + default.add_maximum_shear('sigma') + loc = {'sigma' :default.get_dataset_location('sigma'), + 'max_shear(sigma)':default.get_dataset_location('max_shear(sigma)')} + in_memory = mechanics.maximum_shear(default.read_dataset(loc['sigma'],0)).reshape(-1,1) + in_file = default.read_dataset(loc['max_shear(sigma)'],0) + assert np.allclose(in_memory,in_file) + + def test_add_Mises_strain(self,default): + t = ['V','U'][np.random.randint(0,2)] + m = np.random.random()*2.0 - 1.0 + default.add_strain_tensor('F',t,m) + label = 'epsilon_{}^{}(F)'.format(t,m) + default.add_Mises(label) + loc = {label :default.get_dataset_location(label), + label+'_vM':default.get_dataset_location(label+'_vM')} + in_memory = mechanics.Mises_strain(default.read_dataset(loc[label],0)).reshape(-1,1) + in_file = default.read_dataset(loc[label+'_vM'],0) + assert np.allclose(in_memory,in_file) + + def test_add_Mises_stress(self,default): + default.add_Cauchy('P','F') + default.add_Mises('sigma') + loc = {'sigma' :default.get_dataset_location('sigma'), + 'sigma_vM':default.get_dataset_location('sigma_vM')} + in_memory = mechanics.Mises_stress(default.read_dataset(loc['sigma'],0)).reshape(-1,1) + in_file = default.read_dataset(loc['sigma_vM'],0) + assert np.allclose(in_memory,in_file) + + def test_add_norm(self,default): + default.add_norm('F',1) + loc = {'F': default.get_dataset_location('F'), + '|F|_1':default.get_dataset_location('|F|_1')} + in_memory = np.linalg.norm(default.read_dataset(loc['F'],0),ord=1,axis=(1,2),keepdims=True) + in_file = default.read_dataset(loc['|F|_1'],0) + assert np.allclose(in_memory,in_file) + + def test_add_PK2(self,default): + default.add_PK2('P','F') + loc = {'F':default.get_dataset_location('F'), + 'P':default.get_dataset_location('P'), + 'S':default.get_dataset_location('S')} + in_memory = mechanics.PK2(default.read_dataset(loc['P'],0), + default.read_dataset(loc['F'],0)) + in_file = default.read_dataset(loc['S'],0) + assert np.allclose(in_memory,in_file) + + def test_add_rotational_part(self,default): + default.add_rotational_part('F') + loc = {'F': default.get_dataset_location('F'), + 'R(F)': default.get_dataset_location('R(F)')} + in_memory = mechanics.rotational_part(default.read_dataset(loc['F'],0)) + in_file = default.read_dataset(loc['R(F)'],0) + assert np.allclose(in_memory,in_file) + + def test_add_spherical(self,default): + default.add_spherical('P') + loc = {'P': default.get_dataset_location('P'), + 'p_P': default.get_dataset_location('p_P')} + in_memory = mechanics.spherical_part(default.read_dataset(loc['P'],0)).reshape(-1,1) + in_file = default.read_dataset(loc['p_P'],0) + assert np.allclose(in_memory,in_file) + + def test_add_strain(self,default): + t = ['V','U'][np.random.randint(0,2)] + m = np.random.random()*2.0 - 1.0 + default.add_strain_tensor('F',t,m) + label = 'epsilon_{}^{}(F)'.format(t,m) + loc = {'F': default.get_dataset_location('F'), + label: default.get_dataset_location(label)} + in_memory = mechanics.strain_tensor(default.read_dataset(loc['F'],0),t,m) + in_file = default.read_dataset(loc[label],0) + assert np.allclose(in_memory,in_file) + + def test_add_stretch_right(self,default): + default.add_stretch_tensor('F','U') + loc = {'F': default.get_dataset_location('F'), + 'U(F)': default.get_dataset_location('U(F)')} + in_memory = mechanics.right_stretch(default.read_dataset(loc['F'],0)) + in_file = default.read_dataset(loc['U(F)'],0) + assert np.allclose(in_memory,in_file) + + def test_add_stretch_left(self,default): + default.add_stretch_tensor('F','V') + loc = {'F': default.get_dataset_location('F'), + 'V(F)': default.get_dataset_location('V(F)')} + in_memory = mechanics.left_stretch(default.read_dataset(loc['F'],0)) + in_file = default.read_dataset(loc['V(F)'],0) + assert np.allclose(in_memory,in_file) diff --git a/python/tests/test_mechanics.py b/python/tests/test_mechanics.py index 9e1d9bc0c..4248254ab 100644 --- a/python/tests/test_mechanics.py +++ b/python/tests/test_mechanics.py @@ -2,187 +2,224 @@ import numpy as np from damask import mechanics class TestMechanics: - + n = 1000 c = np.random.randint(n) - + def test_vectorize_Cauchy(self): - P = np.random.random((self.n,3,3)) - F = np.random.random((self.n,3,3)) - assert np.allclose(mechanics.Cauchy(F,P)[self.c], - mechanics.Cauchy(F[self.c],P[self.c])) - - - def test_vectorize_strain_tensor(self): - F = np.random.random((self.n,3,3)) - t = ['V','U'][np.random.randint(0,2)] - m = np.random.random()*10. -5.0 - assert np.allclose(mechanics.strain_tensor(F,t,m)[self.c], - mechanics.strain_tensor(F[self.c],t,m)) - + P = np.random.random((self.n,3,3)) + F = np.random.random((self.n,3,3)) + assert np.allclose(mechanics.Cauchy(P,F)[self.c], + mechanics.Cauchy(P[self.c],F[self.c])) def test_vectorize_deviatoric_part(self): - x = np.random.random((self.n,3,3)) - assert np.allclose(mechanics.deviatoric_part(x)[self.c], - mechanics.deviatoric_part(x[self.c])) + x = np.random.random((self.n,3,3)) + assert np.allclose(mechanics.deviatoric_part(x)[self.c], + mechanics.deviatoric_part(x[self.c])) + def test_vectorize_eigenvalues(self): + x = np.random.random((self.n,3,3)) + assert np.allclose(mechanics.eigenvalues(x)[self.c], + mechanics.eigenvalues(x[self.c])) - def test_vectorize_spherical_part(self): - x = np.random.random((self.n,3,3)) - assert np.allclose(mechanics.spherical_part(x,True)[self.c], - mechanics.spherical_part(x[self.c],True)) - - - def test_vectorize_Mises_stress(self): - sigma = np.random.random((self.n,3,3)) - assert np.allclose(mechanics.Mises_stress(sigma)[self.c], - mechanics.Mises_stress(sigma[self.c])) - - - def test_vectorize_Mises_strain(self): - epsilon = np.random.random((self.n,3,3)) - assert np.allclose(mechanics.Mises_strain(epsilon)[self.c], - mechanics.Mises_strain(epsilon[self.c])) - - - def test_vectorize_symmetric(self): - x = np.random.random((self.n,3,3)) - assert np.allclose(mechanics.symmetric(x)[self.c], - mechanics.symmetric(x[self.c])) - - - def test_vectorize_maximum_shear(self): - x = np.random.random((self.n,3,3)) - assert np.allclose(mechanics.maximum_shear(x)[self.c], - mechanics.maximum_shear(x[self.c])) - - - def test_vectorize_principal_components(self): - x = np.random.random((self.n,3,3)) - assert np.allclose(mechanics.principal_components(x)[self.c], - mechanics.principal_components(x[self.c])) - - - def test_vectorize_transpose(self): - x = np.random.random((self.n,3,3)) - assert np.allclose(mechanics.transpose(x)[self.c], - mechanics.transpose(x[self.c])) - - - def test_vectorize_rotational_part(self): - x = np.random.random((self.n,3,3)) - assert np.allclose(mechanics.rotational_part(x)[self.c], - mechanics.rotational_part(x[self.c])) - + def test_vectorize_eigenvectors(self): + x = np.random.random((self.n,3,3)) + assert np.allclose(mechanics.eigenvectors(x)[self.c], + mechanics.eigenvectors(x[self.c])) def test_vectorize_left_stretch(self): - x = np.random.random((self.n,3,3)) - assert np.allclose(mechanics.left_stretch(x)[self.c], - mechanics.left_stretch(x[self.c])) + x = np.random.random((self.n,3,3)) + assert np.allclose(mechanics.left_stretch(x)[self.c], + mechanics.left_stretch(x[self.c])) + def test_vectorize_maximum_shear(self): + x = np.random.random((self.n,3,3)) + assert np.allclose(mechanics.maximum_shear(x)[self.c], + mechanics.maximum_shear(x[self.c])) + + def test_vectorize_Mises_strain(self): + epsilon = np.random.random((self.n,3,3)) + assert np.allclose(mechanics.Mises_strain(epsilon)[self.c], + mechanics.Mises_strain(epsilon[self.c])) + + def test_vectorize_Mises_stress(self): + sigma = np.random.random((self.n,3,3)) + assert np.allclose(mechanics.Mises_stress(sigma)[self.c], + mechanics.Mises_stress(sigma[self.c])) + + def test_vectorize_PK2(self): + F = np.random.random((self.n,3,3)) + P = np.random.random((self.n,3,3)) + assert np.allclose(mechanics.PK2(P,F)[self.c], + mechanics.PK2(P[self.c],F[self.c])) def test_vectorize_right_stretch(self): - x = np.random.random((self.n,3,3)) - assert np.allclose(mechanics.right_stretch(x)[self.c], - mechanics.right_stretch(x[self.c])) + x = np.random.random((self.n,3,3)) + assert np.allclose(mechanics.right_stretch(x)[self.c], + mechanics.right_stretch(x[self.c])) + + def test_vectorize_rotational_part(self): + x = np.random.random((self.n,3,3)) + assert np.allclose(mechanics.rotational_part(x)[self.c], + mechanics.rotational_part(x[self.c])) + + def test_vectorize_spherical_part(self): + x = np.random.random((self.n,3,3)) + assert np.allclose(mechanics.spherical_part(x,True)[self.c], + mechanics.spherical_part(x[self.c],True)) + + def test_vectorize_strain_tensor(self): + F = np.random.random((self.n,3,3)) + t = ['V','U'][np.random.randint(0,2)] + m = np.random.random()*10. -5.0 + assert np.allclose(mechanics.strain_tensor(F,t,m)[self.c], + mechanics.strain_tensor(F[self.c],t,m)) + + def test_vectorize_symmetric(self): + x = np.random.random((self.n,3,3)) + assert np.allclose(mechanics.symmetric(x)[self.c], + mechanics.symmetric(x[self.c])) + + def test_vectorize_transpose(self): + x = np.random.random((self.n,3,3)) + assert np.allclose(mechanics.transpose(x)[self.c], + mechanics.transpose(x[self.c])) def test_Cauchy(self): - """Ensure Cauchy stress is symmetrized 1. Piola-Kirchhoff stress for no deformation.""" - P = np.random.random((self.n,3,3)) - assert np.allclose(mechanics.Cauchy(np.broadcast_to(np.eye(3),(self.n,3,3)),P), - mechanics.symmetric(P)) + """Ensure Cauchy stress is symmetrized 1. Piola-Kirchhoff stress for no deformation.""" + P = np.random.random((self.n,3,3)) + assert np.allclose(mechanics.Cauchy(P,np.broadcast_to(np.eye(3),(self.n,3,3))), + mechanics.symmetric(P)) + def test_polar_decomposition(self): - """F = RU = VR.""" - F = np.broadcast_to(np.eye(3),[self.n,3,3])*np.random.random((self.n,3,3)) - R = mechanics.rotational_part(F) - V = mechanics.left_stretch(F) - U = mechanics.right_stretch(F) - assert np.allclose(np.matmul(R,U), - np.matmul(V,R)) - + """F = RU = VR.""" + F = np.broadcast_to(np.eye(3),[self.n,3,3])*np.random.random((self.n,3,3)) + R = mechanics.rotational_part(F) + V = mechanics.left_stretch(F) + U = mechanics.right_stretch(F) + assert np.allclose(np.matmul(R,U), + np.matmul(V,R)) + + + def test_PK2(self): + """Ensure 2. Piola-Kirchhoff stress is symmetrized 1. Piola-Kirchhoff stress for no deformation.""" + P = np.random.random((self.n,3,3)) + assert np.allclose(mechanics.PK2(P,np.broadcast_to(np.eye(3),(self.n,3,3))), + mechanics.symmetric(P)) + def test_strain_tensor_no_rotation(self): - """Ensure that left and right stretch give same results for no rotation.""" - F = np.broadcast_to(np.eye(3),[self.n,3,3])*np.random.random((self.n,3,3)) - m = np.random.random()*20.0-10.0 - assert np.allclose(mechanics.strain_tensor(F,'U',m), - mechanics.strain_tensor(F,'V',m)) - + """Ensure that left and right stretch give same results for no rotation.""" + F = np.broadcast_to(np.eye(3),[self.n,3,3])*np.random.random((self.n,3,3)) + m = np.random.random()*20.0-10.0 + assert np.allclose(mechanics.strain_tensor(F,'U',m), + mechanics.strain_tensor(F,'V',m)) + def test_strain_tensor_rotation_equivalence(self): - """Ensure that left and right strain differ only by a rotation.""" - F = np.broadcast_to(np.eye(3),[self.n,3,3]) + (np.random.random((self.n,3,3))*0.5 - 0.25) - m = np.random.random()*5.0-2.5 - assert np.allclose(np.linalg.det(mechanics.strain_tensor(F,'U',m)), - np.linalg.det(mechanics.strain_tensor(F,'V',m))) + """Ensure that left and right strain differ only by a rotation.""" + F = np.broadcast_to(np.eye(3),[self.n,3,3]) + (np.random.random((self.n,3,3))*0.5 - 0.25) + m = np.random.random()*5.0-2.5 + assert np.allclose(np.linalg.det(mechanics.strain_tensor(F,'U',m)), + np.linalg.det(mechanics.strain_tensor(F,'V',m))) def test_strain_tensor_rotation(self): - """Ensure that pure rotation results in no strain.""" - F = mechanics.rotational_part(np.random.random((self.n,3,3))) - t = ['V','U'][np.random.randint(0,2)] - m = np.random.random()*2.0 - 1.0 - assert np.allclose(mechanics.strain_tensor(F,t,m), - 0.0) - - def test_rotation_determinant(self): - """ - Ensure that the determinant of the rotational part is +- 1. + """Ensure that pure rotation results in no strain.""" + F = mechanics.rotational_part(np.random.random((self.n,3,3))) + t = ['V','U'][np.random.randint(0,2)] + m = np.random.random()*2.0 - 1.0 + assert np.allclose(mechanics.strain_tensor(F,t,m), + 0.0) - Should be +1, but random F might contain a reflection. - """ - x = np.random.random((self.n,3,3)) - assert np.allclose(np.abs(np.linalg.det(mechanics.rotational_part(x))), - 1.0) + def test_rotation_determinant(self): + """ + Ensure that the determinant of the rotational part is +- 1. + + Should be +1, but random F might contain a reflection. + """ + x = np.random.random((self.n,3,3)) + assert np.allclose(np.abs(np.linalg.det(mechanics.rotational_part(x))), + 1.0) def test_spherical_deviatoric_part(self): - """Ensure that full tensor is sum of spherical and deviatoric part.""" - x = np.random.random((self.n,3,3)) - sph = mechanics.spherical_part(x,True) - assert np.allclose(sph + mechanics.deviatoric_part(x), - x) + """Ensure that full tensor is sum of spherical and deviatoric part.""" + x = np.random.random((self.n,3,3)) + sph = mechanics.spherical_part(x,True) + assert np.allclose(sph + mechanics.deviatoric_part(x), + x) def test_deviatoric_Mises(self): - """Ensure that Mises equivalent stress depends only on deviatoric part.""" - x = np.random.random((self.n,3,3)) - full = mechanics.Mises_stress(x) - dev = mechanics.Mises_stress(mechanics.deviatoric_part(x)) - assert np.allclose(full, - dev) + """Ensure that Mises equivalent stress depends only on deviatoric part.""" + x = np.random.random((self.n,3,3)) + full = mechanics.Mises_stress(x) + dev = mechanics.Mises_stress(mechanics.deviatoric_part(x)) + assert np.allclose(full, + dev) def test_spherical_mapping(self): - """Ensure that mapping to tensor is correct.""" - x = np.random.random((self.n,3,3)) - tensor = mechanics.spherical_part(x,True) - scalar = mechanics.spherical_part(x) - assert np.allclose(np.linalg.det(tensor), - scalar**3.0) + """Ensure that mapping to tensor is correct.""" + x = np.random.random((self.n,3,3)) + tensor = mechanics.spherical_part(x,True) + scalar = mechanics.spherical_part(x) + assert np.allclose(np.linalg.det(tensor), + scalar**3.0) def test_spherical_Mises(self): - """Ensure that Mises equivalent strrain of spherical strain is 0.""" - x = np.random.random((self.n,3,3)) - sph = mechanics.spherical_part(x,True) - assert np.allclose(mechanics.Mises_strain(sph), - 0.0) + """Ensure that Mises equivalent strrain of spherical strain is 0.""" + x = np.random.random((self.n,3,3)) + sph = mechanics.spherical_part(x,True) + assert np.allclose(mechanics.Mises_strain(sph), + 0.0) def test_symmetric(self): - """Ensure that a symmetric tensor is half of the sum of a tensor and its transpose.""" - x = np.random.random((self.n,3,3)) - assert np.allclose(mechanics.symmetric(x)*2.0, - mechanics.transpose(x)+x) + """Ensure that a symmetric tensor is half of the sum of a tensor and its transpose.""" + x = np.random.random((self.n,3,3)) + assert np.allclose(mechanics.symmetric(x)*2.0, + mechanics.transpose(x)+x) def test_transpose(self): - """Ensure that a symmetric tensor equals its transpose.""" - x = mechanics.symmetric(np.random.random((self.n,3,3))) - assert np.allclose(mechanics.transpose(x), - x) + """Ensure that a symmetric tensor equals its transpose.""" + x = mechanics.symmetric(np.random.random((self.n,3,3))) + assert np.allclose(mechanics.transpose(x), + x) def test_Mises(self): - """Ensure that equivalent stress is 3/2 of equivalent strain.""" - x = np.random.random((self.n,3,3)) - assert np.allclose(mechanics.Mises_stress(x)/mechanics.Mises_strain(x), - 1.5) + """Ensure that equivalent stress is 3/2 of equivalent strain.""" + x = np.random.random((self.n,3,3)) + assert np.allclose(mechanics.Mises_stress(x)/mechanics.Mises_strain(x), + 1.5) + + + def test_eigenvalues(self): + """Ensure that the characteristic polynomial can be solved.""" + A = mechanics.symmetric(np.random.random((self.n,3,3))) + lambd = mechanics.eigenvalues(A) + s = np.random.randint(self.n) + for i in range(3): + assert np.allclose(np.linalg.det(A[s]-lambd[s,i]*np.eye(3)),.0) + + def test_eigenvalues_and_vectors(self): + """Ensure that eigenvalues and -vectors are the solution to the characteristic polynomial.""" + A = mechanics.symmetric(np.random.random((self.n,3,3))) + lambd = mechanics.eigenvalues(A) + x = mechanics.eigenvectors(A) + s = np.random.randint(self.n) + for i in range(3): + assert np.allclose(np.dot(A[s]-lambd[s,i]*np.eye(3),x[s,:,i]),.0) + + def test_eigenvectors_RHS(self): + """Ensure that RHS coordinate system does only change sign of determinant.""" + A = mechanics.symmetric(np.random.random((self.n,3,3))) + LRHS = np.linalg.det(mechanics.eigenvectors(A,RHS=False)) + RHS = np.linalg.det(mechanics.eigenvectors(A,RHS=True)) + assert np.allclose(np.abs(LRHS),RHS) + + def test_spherical_no_shear(self): + """Ensure that sherical stress has max shear of 0.0.""" + A = mechanics.spherical_part(mechanics.symmetric(np.random.random((self.n,3,3))),True) + assert np.allclose(mechanics.maximum_shear(A),0.0) diff --git a/src/IO.f90 b/src/IO.f90 index c31ebdfab..6ab87715c 100644 --- a/src/IO.f90 +++ b/src/IO.f90 @@ -400,6 +400,8 @@ subroutine IO_error(error_ID,el,ip,g,instance,ext_msg) msg = 'number of values does not match' case (147) msg = 'not supported anymore' + case (148) + msg = 'Nconstituents mismatch between homogenization and microstructure' !-------------------------------------------------------------------------------------------------- ! material error messages and related messages in mesh diff --git a/src/constitutive_plastic_disloUCLA.f90 b/src/constitutive_plastic_disloUCLA.f90 index 2b353ba26..00a1b8e21 100644 --- a/src/constitutive_plastic_disloUCLA.f90 +++ b/src/constitutive_plastic_disloUCLA.f90 @@ -6,21 +6,10 @@ !> @brief crystal plasticity model for bcc metals, especially Tungsten !-------------------------------------------------------------------------------------------------- submodule(constitutive) plastic_disloUCLA - + real(pReal), parameter :: & kB = 1.38e-23_pReal !< Boltzmann constant in J/Kelvin - - enum, bind(c) - enumerator :: & - undefined_ID, & - rho_mob_ID, & - rho_dip_ID, & - dot_gamma_sl_ID, & - gamma_sl_ID, & - Lambda_sl_ID, & - tau_pass_ID - end enum - + type :: tParameters real(pReal) :: & aTol_rho, & @@ -28,7 +17,7 @@ submodule(constitutive) plastic_disloUCLA mu, & D_0, & !< prefactor for self-diffusion coefficient Q_cl !< activation energy for dislocation climb - real(pReal), dimension(:), allocatable :: & + real(pReal), allocatable, dimension(:) :: & rho_mob_0, & !< initial dislocation density rho_dip_0, & !< initial dipole density b_sl, & !< magnitude of burgers vector [m] @@ -46,30 +35,30 @@ submodule(constitutive) plastic_disloUCLA kink_height, & !< height of the kink pair w, & !< width of the kink pair omega !< attempt frequency for kink pair nucleation - real(pReal), dimension(:,:), allocatable :: & + real(pReal), allocatable, dimension(:,:) :: & h_sl_sl, & !< slip resistance from slip activity forestProjectionEdge - real(pReal), dimension(:,:,:), allocatable :: & + real(pReal), allocatable, dimension(:,:,:) :: & Schmid, & nonSchmid_pos, & nonSchmid_neg integer :: & sum_N_sl !< total number of active slip system - integer, dimension(:), allocatable :: & + integer, allocatable, dimension(:) :: & N_sl !< number of active slip systems for each family - integer(kind(undefined_ID)), dimension(:),allocatable :: & - outputID !< ID of each post result output + character(len=pStringLen), allocatable, dimension(:) :: & + output logical :: & dipoleFormation !< flag indicating consideration of dipole formation end type !< container type for internal constitutive parameters - + type :: tDisloUCLAState real(pReal), dimension(:,:), pointer :: & rho_mob, & rho_dip, & gamma_sl end type tDisloUCLAState - + type :: tDisloUCLAdependentState real(pReal), dimension(:,:), allocatable :: & Lambda_sl, & @@ -88,41 +77,36 @@ contains !-------------------------------------------------------------------------------------------------- -!> @brief module initialization +!> @brief Perform module initialization. !> @details reads in material parameters, allocates arrays, and does sanity checks !-------------------------------------------------------------------------------------------------- module subroutine plastic_disloUCLA_init - + integer :: & Ninstance, & p, i, & NipcMyPhase, & sizeState, sizeDotState, & startIndex, endIndex - - integer(kind(undefined_ID)) :: & - outputID - + character(len=pStringLen) :: & extmsg = '' - character(len=pStringLen), dimension(:), allocatable :: & - outputs - - write(6,'(/,a)') ' <<<+- plastic_'//PLASTICITY_DISLOUCLA_label//' init -+>>>' - - write(6,'(/,a)') ' Cereceda et al., International Journal of Plasticity 78:242–256, 2016' - write(6,'(a)') ' https://dx.doi.org/10.1016/j.ijplas.2015.09.002' - + + write(6,'(/,a)') ' <<<+- plastic_'//PLASTICITY_DISLOUCLA_label//' init -+>>>'; flush(6) + + write(6,'(/,a)') ' Cereceda et al., International Journal of Plasticity 78:242–256, 2016' + write(6,'(a)') ' https://dx.doi.org/10.1016/j.ijplas.2015.09.002' + Ninstance = count(phase_plasticity == PLASTICITY_DISLOUCLA_ID) if (iand(debug_level(debug_constitutive),debug_levelBasic) /= 0) & write(6,'(a16,1x,i5,/)') '# instances:',Ninstance - + allocate(param(Ninstance)) allocate(state(Ninstance)) allocate(dotState(Ninstance)) allocate(dependentState(Ninstance)) - - + + do p = 1, size(phase_plasticity) if (phase_plasticity(p) /= PLASTICITY_DISLOUCLA_ID) cycle associate(prm => param(phase_plasticityInstance(p)), & @@ -134,9 +118,9 @@ module subroutine plastic_disloUCLA_init !-------------------------------------------------------------------------------------------------- ! optional parameters that need to be defined prm%mu = lattice_mu(p) - + prm%aTol_rho = config%getFloat('atol_rho') - + ! sanity checks if (prm%aTol_rho <= 0.0_pReal) extmsg = trim(extmsg)//' atol_rho' @@ -147,7 +131,7 @@ module subroutine plastic_disloUCLA_init slipActive: if (prm%sum_N_sl > 0) then prm%Schmid = lattice_SchmidMatrix_slip(prm%N_sl,config%getString('lattice_structure'),& config%getFloat('c/a',defaultVal=0.0_pReal)) - + if(trim(config%getString('lattice_structure')) == 'bcc') then prm%nonSchmidCoeff = config%getFloats('nonschmid_coefficients',& defaultVal = emptyRealArray) @@ -158,20 +142,20 @@ module subroutine plastic_disloUCLA_init prm%nonSchmid_pos = prm%Schmid prm%nonSchmid_neg = prm%Schmid endif - + prm%h_sl_sl = lattice_interaction_SlipBySlip(prm%N_sl, & config%getFloats('interaction_slipslip'), & config%getString('lattice_structure')) prm%forestProjectionEdge = lattice_forestProjection_edge(prm%N_sl,config%getString('lattice_structure'),& config%getFloat('c/a',defaultVal=0.0_pReal)) prm%forestProjectionEdge = transpose(prm%forestProjectionEdge) - + prm%rho_mob_0 = config%getFloats('rhoedge0', requiredSize=size(prm%N_sl)) prm%rho_dip_0 = config%getFloats('rhoedgedip0', requiredSize=size(prm%N_sl)) prm%v0 = config%getFloats('v0', requiredSize=size(prm%N_sl)) prm%b_sl = config%getFloats('slipburgers', requiredSize=size(prm%N_sl)) prm%delta_F = config%getFloats('qedge', requiredSize=size(prm%N_sl)) - + prm%i_sl = config%getFloats('clambdaslip', requiredSize=size(prm%N_sl)) prm%tau_0 = config%getFloats('tau_peierls', requiredSize=size(prm%N_sl)) prm%p = config%getFloats('p_slip', requiredSize=size(prm%N_sl), & @@ -182,14 +166,14 @@ module subroutine plastic_disloUCLA_init prm%w = config%getFloats('kink_width', requiredSize=size(prm%N_sl)) prm%omega = config%getFloats('omega', requiredSize=size(prm%N_sl)) prm%B = config%getFloats('friction_coeff', requiredSize=size(prm%N_sl)) - + prm%D = config%getFloat('grainsize') prm%D_0 = config%getFloat('d0') prm%Q_cl = config%getFloat('qsd') prm%atomicVolume = config%getFloat('catomicvolume') * prm%b_sl**3.0_pReal prm%D_a = config%getFloat('cedgedipmindistance') * prm%b_sl prm%dipoleformation = config%getFloat('dipoleformationfactor') > 0.0_pReal !should be on by default, ToDo: change to /key/-type key - + ! expand: family => system prm%rho_mob_0 = math_expand(prm%rho_mob_0, prm%N_sl) prm%rho_dip_0 = math_expand(prm%rho_dip_0, prm%N_sl) @@ -206,8 +190,8 @@ module subroutine plastic_disloUCLA_init prm%i_sl = math_expand(prm%i_sl, prm%N_sl) prm%atomicVolume = math_expand(prm%atomicVolume, prm%N_sl) prm%D_a = math_expand(prm%D_a, prm%N_sl) - - + + ! sanity checks if ( prm%D_0 <= 0.0_pReal) extmsg = trim(extmsg)//' D_0' if ( prm%Q_cl <= 0.0_pReal) extmsg = trim(extmsg)//' Q_cl' @@ -219,7 +203,7 @@ module subroutine plastic_disloUCLA_init if (any(prm%tau_0 < 0.0_pReal)) extmsg = trim(extmsg)//' tau_0' if (any(prm%D_a <= 0.0_pReal)) extmsg = trim(extmsg)//' cedgedipmindistance or slipb_sl' if (any(prm%atomicVolume <= 0.0_pReal)) extmsg = trim(extmsg)//' catomicvolume or slipb_sl' - + else slipActive allocate(prm%rho_mob_0(0)) allocate(prm%rho_dip_0(0)) @@ -232,39 +216,14 @@ module subroutine plastic_disloUCLA_init !-------------------------------------------------------------------------------------------------- ! output pararameters - outputs = config%getStrings('(output)',defaultVal=emptyStringArray) - allocate(prm%outputID(0)) - do i=1, size(outputs) - outputID = undefined_ID - select case(trim(outputs(i))) - - case ('edge_density') - outputID = merge(rho_mob_ID,undefined_ID,prm%sum_N_sl>0) - case ('dipole_density') - outputID = merge(rho_dip_ID,undefined_ID,prm%sum_N_sl>0) - case ('shear_rate','shearrate','shear_rate_slip','shearrate_slip') - outputID = merge(dot_gamma_sl_ID,undefined_ID,prm%sum_N_sl>0) - case ('accumulated_shear','accumulatedshear','accumulated_shear_slip') - outputID = merge(gamma_sl_ID,undefined_ID,prm%sum_N_sl>0) - case ('mfp','mfp_slip') - outputID = merge(Lambda_sl_ID,undefined_ID,prm%sum_N_sl>0) - case ('threshold_stress','threshold_stress_slip') - outputID = merge(tau_pass_ID,undefined_ID,prm%sum_N_sl>0) - - end select - - if (outputID /= undefined_ID) then - prm%outputID = [prm%outputID, outputID] - endif - - enddo + prm%output = config%getStrings('(output)',defaultVal=emptyStringArray) !-------------------------------------------------------------------------------------------------- ! allocate state arrays NipcMyPhase = count(material_phaseAt == p) * discretization_nIP sizeDotState = size(['rho_mob ','rho_dip ','gamma_sl']) * prm%sum_N_sl sizeState = sizeDotState - + call material_allocatePlasticState(p,NipcMyPhase,sizeState,sizeDotState,0) !-------------------------------------------------------------------------------------------------- @@ -275,14 +234,14 @@ module subroutine plastic_disloUCLA_init stt%rho_mob= spread(prm%rho_mob_0,2,NipcMyPhase) dot%rho_mob=>plasticState(p)%dotState(startIndex:endIndex,:) plasticState(p)%aTolState(startIndex:endIndex) = prm%aTol_rho - + startIndex = endIndex + 1 endIndex = endIndex + prm%sum_N_sl stt%rho_dip=>plasticState(p)%state(startIndex:endIndex,:) stt%rho_dip= spread(prm%rho_dip_0,2,NipcMyPhase) dot%rho_dip=>plasticState(p)%dotState(startIndex:endIndex,:) plasticState(p)%aTolState(startIndex:endIndex) = prm%aTol_rho - + startIndex = endIndex + 1 endIndex = endIndex + prm%sum_N_sl stt%gamma_sl=>plasticState(p)%state(startIndex:endIndex,:) @@ -290,21 +249,21 @@ module subroutine plastic_disloUCLA_init plasticState(p)%aTolState(startIndex:endIndex) = 1.0e6_pReal ! Don't use for convergence check ! global alias plasticState(p)%slipRate => plasticState(p)%dotState(startIndex:endIndex,:) - + allocate(dst%Lambda_sl(prm%sum_N_sl,NipcMyPhase), source=0.0_pReal) allocate(dst%threshold_stress(prm%sum_N_sl,NipcMyPhase), source=0.0_pReal) - + plasticState(p)%state0 = plasticState(p)%state ! ToDo: this could be done centrally - + end associate - + enddo end subroutine plastic_disloUCLA_init !-------------------------------------------------------------------------------------------------- -!> @brief calculates plastic velocity gradient and its tangent +!> @brief Calculate plastic velocity gradient and its tangent. !-------------------------------------------------------------------------------------------------- pure module subroutine plastic_disloUCLA_LpAndItsTangent(Lp,dLp_dMp, & Mp,T,instance,of) @@ -312,7 +271,7 @@ pure module subroutine plastic_disloUCLA_LpAndItsTangent(Lp,dLp_dMp, & Lp !< plastic velocity gradient real(pReal), dimension(3,3,3,3), intent(out) :: & dLp_dMp !< derivative of Lp with respect to the Mandel stress - + real(pReal), dimension(3,3), intent(in) :: & Mp !< Mandel stress real(pReal), intent(in) :: & @@ -320,18 +279,18 @@ pure module subroutine plastic_disloUCLA_LpAndItsTangent(Lp,dLp_dMp, & integer, intent(in) :: & instance, & of - + integer :: & i,k,l,m,n real(pReal), dimension(param(instance)%sum_N_sl) :: & dot_gamma_pos,dot_gamma_neg, & ddot_gamma_dtau_pos,ddot_gamma_dtau_neg - + Lp = 0.0_pReal dLp_dMp = 0.0_pReal - + associate(prm => param(instance)) - + call kinetics(Mp,T,instance,of,dot_gamma_pos,dot_gamma_neg,ddot_gamma_dtau_pos,ddot_gamma_dtau_neg) do i = 1, prm%sum_N_sl Lp = Lp + (dot_gamma_pos(i)+dot_gamma_neg(i))*prm%Schmid(1:3,1:3,i) @@ -340,17 +299,17 @@ pure module subroutine plastic_disloUCLA_LpAndItsTangent(Lp,dLp_dMp, & + ddot_gamma_dtau_pos(i) * prm%Schmid(k,l,i) * prm%nonSchmid_pos(m,n,i) & + ddot_gamma_dtau_neg(i) * prm%Schmid(k,l,i) * prm%nonSchmid_neg(m,n,i) enddo - + end associate end subroutine plastic_disloUCLA_LpAndItsTangent !-------------------------------------------------------------------------------------------------- -!> @brief calculates the rate of change of microstructure +!> @brief Calculate the rate of change of microstructure. !-------------------------------------------------------------------------------------------------- module subroutine plastic_disloUCLA_dotState(Mp,T,instance,of) - + real(pReal), dimension(3,3), intent(in) :: & Mp !< Mandel stress real(pReal), intent(in) :: & @@ -358,7 +317,7 @@ module subroutine plastic_disloUCLA_dotState(Mp,T,instance,of) integer, intent(in) :: & instance, & of - + real(pReal) :: & VacancyDiffusion real(pReal), dimension(param(instance)%sum_N_sl) :: & @@ -369,16 +328,16 @@ module subroutine plastic_disloUCLA_dotState(Mp,T,instance,of) dot_rho_dip_formation, & dot_rho_dip_climb, & dip_distance - + associate(prm => param(instance), stt => state(instance),dot => dotState(instance), dst => dependentState(instance)) - + call kinetics(Mp,T,instance,of,& gdot_pos,gdot_neg, & tau_pos_out = tau_pos,tau_neg_out = tau_neg) - + dot%gamma_sl(:,of) = (gdot_pos+gdot_neg) ! ToDo: needs to be abs VacancyDiffusion = prm%D_0*exp(-prm%Q_cl/(kB*T)) - + where(dEq0(tau_pos)) ! ToDo: use avg of pos and neg dot_rho_dip_formation = 0.0_pReal dot_rho_dip_climb = 0.0_pReal @@ -393,73 +352,73 @@ module subroutine plastic_disloUCLA_dotState(Mp,T,instance,of) * (1.0_pReal/(dip_distance+prm%D_a)) dot_rho_dip_climb = (4.0_pReal*v_cl*stt%rho_dip(:,of))/(dip_distance-prm%D_a) ! ToDo: Discuss with Franz: Stress dependency? end where - + dot%rho_mob(:,of) = abs(dot%gamma_sl(:,of))/(prm%b_sl*dst%Lambda_sl(:,of)) & ! multiplication - dot_rho_dip_formation & - (2.0_pReal*prm%D_a)/prm%b_sl*stt%rho_mob(:,of)*abs(dot%gamma_sl(:,of)) ! Spontaneous annihilation of 2 single edge dislocations dot%rho_dip(:,of) = dot_rho_dip_formation & - (2.0_pReal*prm%D_a)/prm%b_sl*stt%rho_dip(:,of)*abs(dot%gamma_sl(:,of)) & ! Spontaneous annihilation of a single edge dislocation with a dipole constituent - dot_rho_dip_climb - + end associate end subroutine plastic_disloUCLA_dotState !-------------------------------------------------------------------------------------------------- -!> @brief calculates derived quantities from state +!> @brief Calculate derived quantities from state. !-------------------------------------------------------------------------------------------------- module subroutine plastic_disloUCLA_dependentState(instance,of) - + integer, intent(in) :: & instance, & of - + real(pReal), dimension(param(instance)%sum_N_sl) :: & dislocationSpacing - + associate(prm => param(instance), stt => state(instance),dst => dependentState(instance)) - + dislocationSpacing = sqrt(matmul(prm%forestProjectionEdge, & stt%rho_mob(:,of)+stt%rho_dip(:,of))) dst%threshold_stress(:,of) = prm%mu*prm%b_sl & * sqrt(matmul(prm%h_sl_sl,stt%rho_mob(:,of)+stt%rho_dip(:,of))) - + dst%Lambda_sl(:,of) = prm%D/(1.0_pReal+prm%D*dislocationSpacing/prm%i_sl) - + end associate end subroutine plastic_disloUCLA_dependentState !-------------------------------------------------------------------------------------------------- -!> @brief writes results to HDF5 output file +!> @brief Write results to HDF5 output file. !-------------------------------------------------------------------------------------------------- module subroutine plastic_disloUCLA_results(instance,group) integer, intent(in) :: instance character(len=*), intent(in) :: group - + integer :: o associate(prm => param(instance), stt => state(instance), dst => dependentState(instance)) - outputsLoop: do o = 1,size(prm%outputID) - select case(prm%outputID(o)) - case (rho_mob_ID) - call results_writeDataset(group,stt%rho_mob,'rho_mob',& - 'mobile dislocation density','1/m²') - case (rho_dip_ID) - call results_writeDataset(group,stt%rho_dip,'rho_dip',& - 'dislocation dipole density''1/m²') - case (dot_gamma_sl_ID) - call results_writeDataset(group,stt%gamma_sl,'dot_gamma_sl',& ! this is not dot!! - 'plastic shear','1') - case (Lambda_sl_ID) - call results_writeDataset(group,dst%Lambda_sl,'Lambda_sl',& - 'mean free path for slip','m') - case (tau_pass_ID) - call results_writeDataset(group,dst%threshold_stress,'tau_pass',& - 'threshold stress for slip','Pa') + outputsLoop: do o = 1,size(prm%output) + select case(trim(prm%output(o))) + case('edge_density') ! ToDo: should be rho_mob + if(prm%sum_N_sl>0) call results_writeDataset(group,stt%rho_mob,'rho_mob',& + 'mobile dislocation density','1/m²') + case('dipole_density') ! ToDo: should be rho_dip + if(prm%sum_N_sl>0) call results_writeDataset(group,stt%rho_dip,'rho_dip',& + 'dislocation dipole density''1/m²') + case('shear_rate_slip') ! should be gamma + if(prm%sum_N_sl>0) call results_writeDataset(group,stt%gamma_sl,'dot_gamma_sl',& ! this is not dot!! + 'plastic shear','1') + case('mfp_slip') !ToDo: should be Lambda + if(prm%sum_N_sl>0) call results_writeDataset(group,dst%Lambda_sl,'Lambda_sl',& + 'mean free path for slip','m') + case('threshold_stress_slip') !ToDo: should be tau_pass + if(prm%sum_N_sl>0) call results_writeDataset(group,dst%threshold_stress,'tau_pass',& + 'threshold stress for slip','Pa') end select enddo outputsLoop end associate @@ -468,15 +427,15 @@ end subroutine plastic_disloUCLA_results !-------------------------------------------------------------------------------------------------- -!> @brief Shear rates on slip systems, their derivatives with respect to resolved stress and the -! resolved stresss +!> @brief Calculate shear rates on slip systems, their derivatives with respect to resolved +! stress, and the resolved stress. !> @details Derivatives and resolved stress are calculated only optionally. ! NOTE: Against the common convention, the result (i.e. intent(out)) variables are the last to ! have the optional arguments at the end !-------------------------------------------------------------------------------------------------- pure subroutine kinetics(Mp,T,instance,of, & dot_gamma_pos,dot_gamma_neg,ddot_gamma_dtau_pos,ddot_gamma_dtau_neg,tau_pos_out,tau_neg_out) - + real(pReal), dimension(3,3), intent(in) :: & Mp !< Mandel stress real(pReal), intent(in) :: & @@ -484,7 +443,7 @@ pure subroutine kinetics(Mp,T,instance,of, & integer, intent(in) :: & instance, & of - + real(pReal), intent(out), dimension(param(instance)%sum_N_sl) :: & dot_gamma_pos, & dot_gamma_neg @@ -501,82 +460,82 @@ pure subroutine kinetics(Mp,T,instance,of, & t_n, t_k, dtk,dtn, & needsGoodName ! ToDo: @Karo: any idea? integer :: j - + associate(prm => param(instance), stt => state(instance), dst => dependentState(instance)) - + do j = 1, prm%sum_N_sl tau_pos(j) = math_mul33xx33(Mp,prm%nonSchmid_pos(1:3,1:3,j)) tau_neg(j) = math_mul33xx33(Mp,prm%nonSchmid_neg(1:3,1:3,j)) enddo - - + + if (present(tau_pos_out)) tau_pos_out = tau_pos if (present(tau_neg_out)) tau_neg_out = tau_neg - + associate(BoltzmannRatio => prm%delta_F/(kB*T), & dot_gamma_0 => stt%rho_mob(:,of)*prm%b_sl*prm%v0, & effectiveLength => dst%Lambda_sl(:,of) - prm%w) - + significantPositiveTau: where(abs(tau_pos)-dst%threshold_stress(:,of) > tol_math_check) StressRatio = (abs(tau_pos)-dst%threshold_stress(:,of))/prm%tau_0 StressRatio_p = StressRatio** prm%p StressRatio_pminus1 = StressRatio**(prm%p-1.0_pReal) needsGoodName = exp(-BoltzmannRatio*(1-StressRatio_p) ** prm%q) - + t_n = prm%b_sl/(needsGoodName*prm%omega*effectiveLength) t_k = effectiveLength * prm%B /(2.0_pReal*prm%b_sl*tau_pos) - + vel = prm%kink_height/(t_n + t_k) - + dot_gamma_pos = dot_gamma_0 * sign(vel,tau_pos) * 0.5_pReal else where significantPositiveTau dot_gamma_pos = 0.0_pReal end where significantPositiveTau - + if (present(ddot_gamma_dtau_pos)) then significantPositiveTau2: where(abs(tau_pos)-dst%threshold_stress(:,of) > tol_math_check) dtn = -1.0_pReal * t_n * BoltzmannRatio * prm%p * prm%q * (1.0_pReal-StressRatio_p)**(prm%q - 1.0_pReal) & * (StressRatio)**(prm%p - 1.0_pReal) / prm%tau_0 dtk = -1.0_pReal * t_k / tau_pos - + dvel = -1.0_pReal * prm%kink_height * (dtk + dtn) / (t_n + t_k)**2.0_pReal - + ddot_gamma_dtau_pos = dot_gamma_0 * dvel* 0.5_pReal else where significantPositiveTau2 ddot_gamma_dtau_pos = 0.0_pReal end where significantPositiveTau2 endif - + significantNegativeTau: where(abs(tau_neg)-dst%threshold_stress(:,of) > tol_math_check) StressRatio = (abs(tau_neg)-dst%threshold_stress(:,of))/prm%tau_0 StressRatio_p = StressRatio** prm%p StressRatio_pminus1 = StressRatio**(prm%p-1.0_pReal) needsGoodName = exp(-BoltzmannRatio*(1-StressRatio_p) ** prm%q) - + t_n = prm%b_sl/(needsGoodName*prm%omega*effectiveLength) t_k = effectiveLength * prm%B /(2.0_pReal*prm%b_sl*tau_pos) - + vel = prm%kink_height/(t_n + t_k) - + dot_gamma_neg = dot_gamma_0 * sign(vel,tau_neg) * 0.5_pReal else where significantNegativeTau dot_gamma_neg = 0.0_pReal end where significantNegativeTau - + if (present(ddot_gamma_dtau_neg)) then significantNegativeTau2: where(abs(tau_neg)-dst%threshold_stress(:,of) > tol_math_check) dtn = -1.0_pReal * t_n * BoltzmannRatio * prm%p * prm%q * (1.0_pReal-StressRatio_p)**(prm%q - 1.0_pReal) & * (StressRatio)**(prm%p - 1.0_pReal) / prm%tau_0 dtk = -1.0_pReal * t_k / tau_neg - + dvel = -1.0_pReal * prm%kink_height * (dtk + dtn) / (t_n + t_k)**2.0_pReal - + ddot_gamma_dtau_neg = dot_gamma_0 * dvel * 0.5_pReal else where significantNegativeTau2 ddot_gamma_dtau_neg = 0.0_pReal end where significantNegativeTau2 end if - + end associate end associate diff --git a/src/constitutive_plastic_dislotwin.f90 b/src/constitutive_plastic_dislotwin.f90 index 94e0177a0..152c630ae 100644 --- a/src/constitutive_plastic_dislotwin.f90 +++ b/src/constitutive_plastic_dislotwin.f90 @@ -8,35 +8,17 @@ !> @details to be done !-------------------------------------------------------------------------------------------------- submodule(constitutive) plastic_dislotwin - - real(pReal), parameter :: & + + real(pReal), parameter :: & kB = 1.38e-23_pReal !< Boltzmann constant in J/Kelvin - - enum, bind(c) - enumerator :: & - undefined_ID, & - rho_mob_ID, & - rho_dip_ID, & - dot_gamma_sl_ID, & - gamma_sl_ID, & - Lambda_sl_ID, & - resolved_stress_slip_ID, & - tau_pass_ID, & - edge_dipole_distance_ID, & - f_tw_ID, & - Lambda_tw_ID, & - resolved_stress_twin_ID, & - tau_hat_tw_ID, & - f_tr_ID - end enum - + type :: tParameters real(pReal) :: & mu, & nu, & D0, & !< prefactor for self-diffusion coefficient Qsd, & !< activation energy for dislocation climb - omega, & !< frequency factor for dislocation climb + omega, & !< frequency factor for dislocation climb D, & !< grain size p_sb, & !< p-exponent in shear band velocity q_sb, & !< q-exponent in shear band velocity @@ -58,8 +40,8 @@ submodule(constitutive) plastic_dislotwin aTol_f_tr, & !< absolute tolerance for integration of trans volume fraction gamma_fcc_hex, & !< Free energy difference between austensite and martensite i_tr, & !< - h !< Stack height of hex nucleus - real(pReal), dimension(:), allocatable :: & + h !< Stack height of hex nucleus + real(pReal), allocatable, dimension(:) :: & rho_mob_0, & !< initial unipolar dislocation density per slip system rho_dip_0, & !< initial dipole dislocation density per slip system b_sl, & !< absolute length of burgers vector [m] for each slip system @@ -79,40 +61,39 @@ submodule(constitutive) plastic_dislotwin s, & !< s-exponent in trans nucleation rate gamma_char, & !< characteristic shear for twins B !< drag coefficient - real(pReal), dimension(:,:), allocatable :: & - h_sl_sl, & !< + real(pReal), allocatable, dimension(:,:) :: & + h_sl_sl, & !< h_sl_tw, & !< - h_tw_tw, & !< - h_sl_tr, & !< - h_tr_tr !< - integer, dimension(:,:), allocatable :: & - fcc_twinNucleationSlipPair ! ToDo: Better name? Is also use for trans - real(pReal), dimension(:,:), allocatable :: & + h_tw_tw, & !< + h_sl_tr, & !< + h_tr_tr, & !< n0_sl, & !< slip system normal forestProjection, & C66 - real(pReal), dimension(:,:,:), allocatable :: & + real(pReal), allocatable, dimension(:,:,:) :: & P_tr, & P_sl, & P_tw, & C66_tw, & C66_tr - integer :: & + integer :: & sum_N_sl, & !< total number of active slip system sum_N_tw, & !< total number of active twin system - sum_N_tr !< total number of active transformation system - integer, dimension(:), allocatable :: & + sum_N_tr !< total number of active transformation system + integer, allocatable, dimension(:) :: & N_sl, & !< number of active slip systems for each family N_tw, & !< number of active twin systems for each family N_tr !< number of active transformation systems for each family - integer(kind(undefined_ID)), dimension(:), allocatable :: & - outputID !< ID of each post result output + integer, allocatable, dimension(:,:) :: & + fcc_twinNucleationSlipPair ! ToDo: Better name? Is also use for trans + character(len=pStringLen), allocatable, dimension(:) :: & + output logical :: & ExtendedDislocations, & !< consider split into partials for climb calculation fccTwinTransNucleation, & !< twinning and transformation models are for fcc dipoleFormation !< flag indicating consideration of dipole formation end type !< container type for internal constitutive parameters - + type :: tDislotwinState real(pReal), dimension(:,:), pointer :: & rho_mob, & @@ -121,7 +102,7 @@ submodule(constitutive) plastic_dislotwin f_tw, & f_tr end type tDislotwinState - + type :: tDislotwinMicrostructure real(pReal), dimension(:,:), allocatable :: & Lambda_sl, & !< mean free path between 2 obstacles seen by a moving dislocation @@ -135,7 +116,7 @@ submodule(constitutive) plastic_dislotwin tau_r_tw, & !< stress to bring partials close together (twin) tau_r_tr !< stress to bring partials close together (trans) end type tDislotwinMicrostructure - + !-------------------------------------------------------------------------------------------------- ! containers for parameters and state type(tParameters), allocatable, dimension(:) :: param @@ -148,7 +129,7 @@ contains !-------------------------------------------------------------------------------------------------- -!> @brief module initialization +!> @brief Perform module initialization. !> @details reads in material parameters, allocates arrays, and does sanity checks !-------------------------------------------------------------------------------------------------- module subroutine plastic_dislotwin_init @@ -156,39 +137,34 @@ module subroutine plastic_dislotwin_init integer :: & Ninstance, & p, i, & - NipcMyPhase, outputSize, & + NipcMyPhase, & sizeState, sizeDotState, & startIndex, endIndex - - integer(kind(undefined_ID)) :: & - outputID - + character(len=pStringLen) :: & extmsg = '' - character(len=pStringLen), dimension(:), allocatable :: & - outputs - - write(6,'(/,a)') ' <<<+- constitutive_'//PLASTICITY_DISLOTWIN_label//' init -+>>>' - - write(6,'(/,a)') ' Ma and Roters, Acta Materialia 52(12):3603–3612, 2004' - write(6,'(a)') ' https://doi.org/10.1016/j.actamat.2004.04.012' - - write(6,'(/,a)') ' Roters et al., Computational Materials Science 39:91–95, 2007' - write(6,'(a)') ' https://doi.org/10.1016/j.commatsci.2006.04.014' - - write(6,'(/,a)') ' Wong et al., Acta Materialia 118:140–151, 2016' - write(6,'(a,/)') ' https://doi.org/10.1016/j.actamat.2016.07.032' - + + write(6,'(/,a)') ' <<<+- constitutive_'//PLASTICITY_DISLOTWIN_label//' init -+>>>'; flush(6) + + write(6,'(/,a)') ' Ma and Roters, Acta Materialia 52(12):3603–3612, 2004' + write(6,'(a)') ' https://doi.org/10.1016/j.actamat.2004.04.012' + + write(6,'(/,a)') ' Roters et al., Computational Materials Science 39:91–95, 2007' + write(6,'(a)') ' https://doi.org/10.1016/j.commatsci.2006.04.014' + + write(6,'(/,a)') ' Wong et al., Acta Materialia 118:140–151, 2016' + write(6,'(a,/)') ' https://doi.org/10.1016/j.actamat.2016.07.032' + Ninstance = count(phase_plasticity == PLASTICITY_DISLOTWIN_ID) - + if (iand(debug_level(debug_constitutive),debug_levelBasic) /= 0) & write(6,'(a16,1x,i5,/)') '# instances:',Ninstance - + allocate(param(Ninstance)) allocate(state(Ninstance)) allocate(dotState(Ninstance)) allocate(dependentState(Ninstance)) - + do p = 1, size(phase_plasticity) if (phase_plasticity(p) /= PLASTICITY_DISLOTWIN_ID) cycle associate(prm => param(phase_plasticityInstance(p)), & @@ -196,17 +172,16 @@ module subroutine plastic_dislotwin_init stt => state(phase_plasticityInstance(p)), & dst => dependentState(phase_plasticityInstance(p)), & config => config_phase(p)) - + prm%aTol_rho = config%getFloat('atol_rho', defaultVal=0.0_pReal) prm%aTol_f_tw = config%getFloat('atol_twinfrac', defaultVal=0.0_pReal) prm%aTol_f_tr = config%getFloat('atol_transfrac', defaultVal=0.0_pReal) - + ! This data is read in already in lattice prm%mu = lattice_mu(p) prm%nu = lattice_nu(p) prm%C66 = lattice_C66(1:6,1:6,p) - - + !-------------------------------------------------------------------------------------------------- ! slip related parameters prm%N_sl = config%getInts('nslip',defaultVal=emptyIntArray) @@ -220,14 +195,14 @@ module subroutine plastic_dislotwin_init prm%forestProjection = lattice_forestProjection_edge(prm%N_sl,config%getString('lattice_structure'),& config%getFloat('c/a',defaultVal=0.0_pReal)) prm%forestProjection = transpose(prm%forestProjection) - + prm%n0_sl = lattice_slip_normal(prm%N_sl,config%getString('lattice_structure'),& - config%getFloat('c/a',defaultVal=0.0_pReal)) + config%getFloat('c/a',defaultVal=0.0_pReal)) prm%fccTwinTransNucleation = merge(.true., .false., lattice_structure(p) == LATTICE_FCC_ID) & .and. (prm%N_sl(1) == 12) if(prm%fccTwinTransNucleation) & prm%fcc_twinNucleationSlipPair = lattice_fcc_twinNucleationSlipPair - + prm%rho_mob_0 = config%getFloats('rhoedge0', requiredSize=size(prm%N_sl)) prm%rho_dip_0 = config%getFloats('rhoedgedip0',requiredSize=size(prm%N_sl)) prm%v0 = config%getFloats('v0', requiredSize=size(prm%N_sl)) @@ -238,7 +213,7 @@ module subroutine plastic_dislotwin_init prm%q = config%getFloats('q_slip', requiredSize=size(prm%N_sl)) prm%B = config%getFloats('b', requiredSize=size(prm%N_sl), & defaultVal=[(0.0_pReal, i=1,size(prm%N_sl))]) - + prm%tau_0 = config%getFloat('solidsolutionstrength') prm%CEdgeDipMinDistance = config%getFloat('cedgedipmindistance') prm%D0 = config%getFloat('d0') @@ -249,15 +224,15 @@ module subroutine plastic_dislotwin_init prm%SFE_0K = config%getFloat('sfe_0k') prm%dSFE_dT = config%getFloat('dsfe_dt') endif - + ! multiplication factor according to crystal structure (nearest neighbors bcc vs fcc/hex) !@details: Refer: Argon & Moffat, Acta Metallurgica, Vol. 29, pg 293 to 299, 1981 prm%omega = config%getFloat('omega', defaultVal = 1000.0_pReal) & * merge(12.0_pReal, & 8.0_pReal, & lattice_structure(p) == LATTICE_FCC_ID .or. lattice_structure(p) == LATTICE_HEX_ID) - - + + ! expand: family => system prm%rho_mob_0 = math_expand(prm%rho_mob_0, prm%N_sl) prm%rho_dip_0 = math_expand(prm%rho_dip_0, prm%N_sl) @@ -268,8 +243,8 @@ module subroutine plastic_dislotwin_init prm%p = math_expand(prm%p, prm%N_sl) prm%q = math_expand(prm%q, prm%N_sl) prm%B = math_expand(prm%B, prm%N_sl) - prm%atomicVolume = math_expand(prm%atomicVolume,prm%N_sl) - + prm%atomicVolume = math_expand(prm%atomicVolume,prm%N_sl) + ! sanity checks if ( prm%D0 <= 0.0_pReal) extmsg = trim(extmsg)//' D0' if ( prm%Qsd <= 0.0_pReal) extmsg = trim(extmsg)//' Qsd' @@ -282,11 +257,11 @@ module subroutine plastic_dislotwin_init if (any(prm%B < 0.0_pReal)) extmsg = trim(extmsg)//' B' if (any(prm%p<=0.0_pReal .or. prm%p>1.0_pReal)) extmsg = trim(extmsg)//' p' if (any(prm%q< 1.0_pReal .or. prm%q>2.0_pReal)) extmsg = trim(extmsg)//' q' - + else slipActive allocate(prm%b_sl(0)) endif slipActive - + !-------------------------------------------------------------------------------------------------- ! twin related parameters prm%N_tw = config%getInts('ntwin', defaultVal=emptyIntArray) @@ -297,31 +272,31 @@ module subroutine plastic_dislotwin_init prm%h_tw_tw = lattice_interaction_TwinByTwin(prm%N_tw,& config%getFloats('interaction_twintwin'), & config%getString('lattice_structure')) - + prm%b_tw = config%getFloats('twinburgers', requiredSize=size(prm%N_tw)) prm%t_tw = config%getFloats('twinsize', requiredSize=size(prm%N_tw)) prm%r = config%getFloats('r_twin', requiredSize=size(prm%N_tw)) - + prm%xc_twin = config%getFloat('xc_twin') prm%L_tw = config%getFloat('l0_twin') prm%i_tw = config%getFloat('cmfptwin') - + prm%gamma_char= lattice_characteristicShear_Twin(prm%N_tw,config%getString('lattice_structure'),& config%getFloat('c/a',defaultVal=0.0_pReal)) - + prm%C66_tw = lattice_C66_twin(prm%N_tw,prm%C66,config%getString('lattice_structure'),& config%getFloat('c/a',defaultVal=0.0_pReal)) - + if (.not. prm%fccTwinTransNucleation) then - prm%dot_N_0_tw = config%getFloats('ndot0_twin') + prm%dot_N_0_tw = config%getFloats('ndot0_twin') prm%dot_N_0_tw = math_expand(prm%dot_N_0_tw,prm%N_tw) endif - + ! expand: family => system prm%b_tw = math_expand(prm%b_tw,prm%N_tw) prm%t_tw = math_expand(prm%t_tw,prm%N_tw) prm%r = math_expand(prm%r,prm%N_tw) - + else allocate(prm%gamma_char(0)) allocate(prm%t_tw (0)) @@ -329,7 +304,7 @@ module subroutine plastic_dislotwin_init allocate(prm%r (0)) allocate(prm%h_tw_tw (0,0)) endif - + !-------------------------------------------------------------------------------------------------- ! transformation related parameters prm%N_tr = config%getInts('ntrans', defaultVal=emptyIntArray) @@ -337,29 +312,29 @@ module subroutine plastic_dislotwin_init if (prm%sum_N_tr > 0) then prm%b_tr = config%getFloats('transburgers') prm%b_tr = math_expand(prm%b_tr,prm%N_tr) - + prm%h = config%getFloat('transstackheight', defaultVal=0.0_pReal) ! ToDo: How to handle that??? prm%i_tr = config%getFloat('cmfptrans', defaultVal=0.0_pReal) ! ToDo: How to handle that??? prm%gamma_fcc_hex = config%getFloat('deltag') prm%xc_trans = config%getFloat('xc_trans', defaultVal=0.0_pReal) ! ToDo: How to handle that??? prm%L_tr = config%getFloat('l0_trans') - + prm%h_tr_tr = lattice_interaction_TransByTrans(prm%N_tr,& config%getFloats('interaction_transtrans'), & config%getString('lattice_structure')) - + prm%C66_tr = lattice_C66_trans(prm%N_tr,prm%C66, & config%getString('trans_lattice_structure'), & 0.0_pReal, & config%getFloat('a_bcc', defaultVal=0.0_pReal), & config%getFloat('a_fcc', defaultVal=0.0_pReal)) - + prm%P_tr = lattice_SchmidMatrix_trans(prm%N_tr, & config%getString('trans_lattice_structure'), & 0.0_pReal, & config%getFloat('a_bcc', defaultVal=0.0_pReal), & config%getFloat('a_fcc', defaultVal=0.0_pReal)) - + if (lattice_structure(p) /= LATTICE_fcc_ID) then prm%dot_N_0_tr = config%getFloats('ndot0_trans') prm%dot_N_0_tr = math_expand(prm%dot_N_0_tr,prm%N_tr) @@ -374,59 +349,57 @@ module subroutine plastic_dislotwin_init allocate(prm%s (0)) allocate(prm%h_tr_tr(0,0)) endif - + if (sum(prm%N_tw) > 0 .or. prm%sum_N_tr > 0) then prm%SFE_0K = config%getFloat('sfe_0k') prm%dSFE_dT = config%getFloat('dsfe_dt') prm%V_cs = config%getFloat('vcrossslip') endif - + if (prm%sum_N_sl > 0 .and. prm%sum_N_tw > 0) then prm%h_sl_tw = lattice_interaction_SlipByTwin(prm%N_sl,prm%N_tw,& config%getFloats('interaction_sliptwin'), & config%getString('lattice_structure')) if (prm%fccTwinTransNucleation .and. prm%sum_N_tw > 12) write(6,*) 'mist' ! ToDo: implement better test. The model will fail also if N_tw is [6,6] - endif - - if (prm%sum_N_sl > 0 .and. prm%sum_N_tr > 0) then + endif + + if (prm%sum_N_sl > 0 .and. prm%sum_N_tr > 0) then prm%h_sl_tr = lattice_interaction_SlipByTrans(prm%N_sl,prm%N_tr,& config%getFloats('interaction_sliptrans'), & config%getString('lattice_structure')) if (prm%fccTwinTransNucleation .and. prm%sum_N_tr > 12) write(6,*) 'mist' ! ToDo: implement better test. The model will fail also if N_tr is [6,6] - endif - + endif + !-------------------------------------------------------------------------------------------------- ! shearband related parameters prm%sbVelocity = config%getFloat('shearbandvelocity',defaultVal=0.0_pReal) - if (prm%sbVelocity > 0.0_pReal) then + if (prm%sbVelocity > 0.0_pReal) then prm%sbResistance = config%getFloat('shearbandresistance') prm%sbQedge = config%getFloat('qedgepersbsystem') prm%p_sb = config%getFloat('p_shearband') prm%q_sb = config%getFloat('q_shearband') - + ! sanity checks if (prm%sbResistance < 0.0_pReal) extmsg = trim(extmsg)//' shearbandresistance' if (prm%sbQedge < 0.0_pReal) extmsg = trim(extmsg)//' qedgepersbsystem' if (prm%p_sb <= 0.0_pReal) extmsg = trim(extmsg)//' p_shearband' if (prm%q_sb <= 0.0_pReal) extmsg = trim(extmsg)//' q_shearband' endif - - - + prm%D = config%getFloat('grainsize') - + if (config%keyExists('dipoleformationfactor')) call IO_error(1,ext_msg='use /nodipoleformation/') prm%dipoleformation = .not. config%keyExists('/nodipoleformation/') - - + + !if (Ndot0PerTwinFamily(f,p) < 0.0_pReal) & ! call IO_error(211,el=p,ext_msg='dot_N_0_tw ('//PLASTICITY_DISLOTWIN_label//')') - + if (any(prm%atomicVolume <= 0.0_pReal)) & call IO_error(211,el=p,ext_msg='cAtomicVolume ('//PLASTICITY_DISLOTWIN_label//')') if (prm%sum_N_tw > 0) then if (prm%aTol_rho <= 0.0_pReal) & - call IO_error(211,el=p,ext_msg='aTol_rho ('//PLASTICITY_DISLOTWIN_label//')') + call IO_error(211,el=p,ext_msg='aTol_rho ('//PLASTICITY_DISLOTWIN_label//')') if (prm%aTol_f_tw <= 0.0_pReal) & call IO_error(211,el=p,ext_msg='aTol_f_tw ('//PLASTICITY_DISLOTWIN_label//')') endif @@ -434,50 +407,9 @@ module subroutine plastic_dislotwin_init if (prm%aTol_f_tr <= 0.0_pReal) & call IO_error(211,el=p,ext_msg='aTol_f_tr ('//PLASTICITY_DISLOTWIN_label//')') endif - - outputs = config%getStrings('(output)', defaultVal=emptyStringArray) - allocate(prm%outputID(0)) - do i= 1, size(outputs) - outputID = undefined_ID - select case(outputs(i)) - case ('rho_mob') - outputID = merge(rho_mob_ID,undefined_ID,prm%sum_N_sl > 0) - outputSize = prm%sum_N_sl - case ('rho_dip') - outputID = merge(rho_dip_ID,undefined_ID,prm%sum_N_sl > 0) - outputSize = prm%sum_N_sl - case ('gamma_sl') - outputID = merge(gamma_sl_ID,undefined_ID,prm%sum_N_sl > 0) - outputSize = prm%sum_N_sl - case ('lambda_sl') - outputID = merge(Lambda_sl_ID,undefined_ID,prm%sum_N_sl > 0) - outputSize = prm%sum_N_sl - case ('tau_pass') - outputID= merge(tau_pass_ID,undefined_ID,prm%sum_N_sl > 0) - outputSize = prm%sum_N_sl - - case ('f_tw') - outputID = merge(f_tw_ID,undefined_ID,prm%sum_N_tw >0) - outputSize = prm%sum_N_tw - case ('lambda_tw') - outputID = merge(Lambda_tw_ID,undefined_ID,prm%sum_N_tw >0) - outputSize = prm%sum_N_tw - case ('tau_hat_tw') - outputID = merge(tau_hat_tw_ID,undefined_ID,prm%sum_N_tw >0) - outputSize = prm%sum_N_tw - - case ('f_tr') - outputID = f_tr_ID - outputSize = prm%sum_N_tr - - end select - - if (outputID /= undefined_ID) then - prm%outputID = [prm%outputID, outputID] - endif - - enddo - + + prm%output = config%getStrings('(output)', defaultVal=emptyStringArray) + !-------------------------------------------------------------------------------------------------- ! allocate state arrays NipcMyPhase = count(material_phaseAt == p) * discretization_nIP @@ -485,9 +417,9 @@ module subroutine plastic_dislotwin_init + size(['f_tw']) * prm%sum_N_tw & + size(['f_tr']) * prm%sum_N_tr sizeState = sizeDotState - + call material_allocatePlasticState(p,NipcMyPhase,sizeState,sizeDotState,0) - + !-------------------------------------------------------------------------------------------------- ! locally defined state aliases and initialization of state0 and aTolState startIndex = 1 @@ -496,14 +428,14 @@ module subroutine plastic_dislotwin_init stt%rho_mob= spread(prm%rho_mob_0,2,NipcMyPhase) dot%rho_mob=>plasticState(p)%dotState(startIndex:endIndex,:) plasticState(p)%aTolState(startIndex:endIndex) = prm%aTol_rho - + startIndex = endIndex + 1 endIndex = endIndex + prm%sum_N_sl stt%rho_dip=>plasticState(p)%state(startIndex:endIndex,:) stt%rho_dip= spread(prm%rho_dip_0,2,NipcMyPhase) dot%rho_dip=>plasticState(p)%dotState(startIndex:endIndex,:) plasticState(p)%aTolState(startIndex:endIndex) = prm%aTol_rho - + startIndex = endIndex + 1 endIndex = endIndex + prm%sum_N_sl stt%gamma_sl=>plasticState(p)%state(startIndex:endIndex,:) @@ -511,66 +443,66 @@ module subroutine plastic_dislotwin_init plasticState(p)%aTolState(startIndex:endIndex) = 1.0e6_pReal !ToDo: better make optional parameter ! global alias plasticState(p)%slipRate => plasticState(p)%dotState(startIndex:endIndex,:) - + startIndex = endIndex + 1 endIndex = endIndex + prm%sum_N_tw stt%f_tw=>plasticState(p)%state(startIndex:endIndex,:) dot%f_tw=>plasticState(p)%dotState(startIndex:endIndex,:) plasticState(p)%aTolState(startIndex:endIndex) = prm%aTol_f_tw - + startIndex = endIndex + 1 endIndex = endIndex + prm%sum_N_tr stt%f_tr=>plasticState(p)%state(startIndex:endIndex,:) dot%f_tr=>plasticState(p)%dotState(startIndex:endIndex,:) plasticState(p)%aTolState(startIndex:endIndex) = prm%aTol_f_tr - + allocate(dst%Lambda_sl (prm%sum_N_sl,NipcMyPhase),source=0.0_pReal) allocate(dst%tau_pass (prm%sum_N_sl,NipcMyPhase),source=0.0_pReal) - + allocate(dst%Lambda_tw (prm%sum_N_tw,NipcMyPhase),source=0.0_pReal) allocate(dst%tau_hat_tw (prm%sum_N_tw,NipcMyPhase),source=0.0_pReal) allocate(dst%tau_r_tw (prm%sum_N_tw,NipcMyPhase),source=0.0_pReal) allocate(dst%V_tw (prm%sum_N_tw,NipcMyPhase),source=0.0_pReal) - + allocate(dst%Lambda_tr (prm%sum_N_tr,NipcMyPhase),source=0.0_pReal) allocate(dst%tau_hat_tr (prm%sum_N_tr,NipcMyPhase),source=0.0_pReal) allocate(dst%tau_r_tr (prm%sum_N_tr,NipcMyPhase),source=0.0_pReal) allocate(dst%V_tr (prm%sum_N_tr,NipcMyPhase),source=0.0_pReal) - - + + plasticState(p)%state0 = plasticState(p)%state ! ToDo: this could be done centrally - + end associate - + enddo end subroutine plastic_dislotwin_init !-------------------------------------------------------------------------------------------------- -!> @brief returns the homogenized elasticity matrix +!> @brief Return the homogenized elasticity matrix. !-------------------------------------------------------------------------------------------------- module function plastic_dislotwin_homogenizedC(ipc,ip,el) result(homogenizedC) - + real(pReal), dimension(6,6) :: & homogenizedC integer, intent(in) :: & ipc, & !< component-ID of integration point ip, & !< integration point el !< element - + integer :: i, & of real(pReal) :: f_unrotated - + of = material_phasememberAt(ipc,ip,el) associate(prm => param(phase_plasticityInstance(material_phaseAt(ipc,el))),& stt => state(phase_plasticityInstance(material_phaseAT(ipc,el)))) - + f_unrotated = 1.0_pReal & - sum(stt%f_tw(1:prm%sum_N_tw,of)) & - sum(stt%f_tr(1:prm%sum_N_tr,of)) - + homogenizedC = f_unrotated * prm%C66 do i=1,prm%sum_N_tw homogenizedC = homogenizedC & @@ -580,23 +512,23 @@ module function plastic_dislotwin_homogenizedC(ipc,ip,el) result(homogenizedC) homogenizedC = homogenizedC & + stt%f_tr(i,of)*prm%C66_tr(1:6,1:6,i) enddo - + end associate - + end function plastic_dislotwin_homogenizedC !-------------------------------------------------------------------------------------------------- -!> @brief calculates plastic velocity gradient and its tangent +!> @brief Calculate plastic velocity gradient and its tangent. !-------------------------------------------------------------------------------------------------- module subroutine plastic_dislotwin_LpAndItsTangent(Lp,dLp_dMp,Mp,T,instance,of) - + real(pReal), dimension(3,3), intent(out) :: Lp real(pReal), dimension(3,3,3,3), intent(out) :: dLp_dMp real(pReal), dimension(3,3), intent(in) :: Mp integer, intent(in) :: instance,of real(pReal), intent(in) :: T - + integer :: i,k,l,m,n real(pReal) :: & f_unrotated,StressRatio_p,& @@ -632,16 +564,16 @@ module subroutine plastic_dislotwin_LpAndItsTangent(Lp,dLp_dMp,Mp,T,instance,of) 0, 1,-1, & 0, 1, 1 & ],pReal),[ 3,6]) - + associate(prm => param(instance), stt => state(instance)) - + f_unrotated = 1.0_pReal & - sum(stt%f_tw(1:prm%sum_N_tw,of)) & - sum(stt%f_tr(1:prm%sum_N_tr,of)) - + Lp = 0.0_pReal - dLp_dMp = 0.0_pReal - + dLp_dMp = 0.0_pReal + call kinetics_slip(Mp,T,instance,of,dot_gamma_sl,ddot_gamma_dtau_slip) slipContribution: do i = 1, prm%sum_N_sl Lp = Lp + dot_gamma_sl(i)*prm%P_sl(1:3,1:3,i) @@ -649,37 +581,37 @@ module subroutine plastic_dislotwin_LpAndItsTangent(Lp,dLp_dMp,Mp,T,instance,of) dLp_dMp(k,l,m,n) = dLp_dMp(k,l,m,n) & + ddot_gamma_dtau_slip(i) * prm%P_sl(k,l,i) * prm%P_sl(m,n,i) enddo slipContribution - + !ToDo: Why do this before shear banding? Lp = Lp * f_unrotated dLp_dMp = dLp_dMp * f_unrotated - + shearBandingContribution: if(dNeq0(prm%sbVelocity)) then - + BoltzmannRatio = prm%sbQedge/(kB*T) call math_eigenValuesVectorsSym(Mp,eigValues,eigVectors,error) - + do i = 1,6 P_sb = 0.5_pReal * math_outer(matmul(eigVectors,sb_sComposition(1:3,i)),& matmul(eigVectors,sb_mComposition(1:3,i))) tau = math_mul33xx33(Mp,P_sb) - + significantShearBandStress: if (abs(tau) > tol_math_check) then StressRatio_p = (abs(tau)/prm%sbResistance)**prm%p_sb dot_gamma_sb = sign(prm%sbVelocity*exp(-BoltzmannRatio*(1-StressRatio_p)**prm%q_sb), tau) ddot_gamma_dtau = abs(dot_gamma_sb)*BoltzmannRatio* prm%p_sb*prm%q_sb/ prm%sbResistance & * (abs(tau)/prm%sbResistance)**(prm%p_sb-1.0_pReal) & * (1.0_pReal-StressRatio_p)**(prm%q_sb-1.0_pReal) - + Lp = Lp + dot_gamma_sb * P_sb forall (k=1:3,l=1:3,m=1:3,n=1:3) & dLp_dMp(k,l,m,n) = dLp_dMp(k,l,m,n) & + ddot_gamma_dtau * P_sb(k,l) * P_sb(m,n) endif significantShearBandStress enddo - + endif shearBandingContribution - + call kinetics_twin(Mp,T,dot_gamma_sl,instance,of,dot_gamma_twin,ddot_gamma_dtau_twin) twinContibution: do i = 1, prm%sum_N_tw Lp = Lp + dot_gamma_twin(i)*prm%P_tw(1:3,1:3,i) * f_unrotated @@ -687,7 +619,7 @@ module subroutine plastic_dislotwin_LpAndItsTangent(Lp,dLp_dMp,Mp,T,instance,of) dLp_dMp(k,l,m,n) = dLp_dMp(k,l,m,n) & + ddot_gamma_dtau_twin(i)* prm%P_tw(k,l,i)*prm%P_tw(m,n,i) * f_unrotated enddo twinContibution - + call kinetics_trans(Mp,T,dot_gamma_sl,instance,of,dot_gamma_tr,ddot_gamma_dtau_trans) transContibution: do i = 1, prm%sum_N_tr Lp = Lp + dot_gamma_tr(i)*prm%P_tr(1:3,1:3,i) * f_unrotated @@ -695,15 +627,15 @@ module subroutine plastic_dislotwin_LpAndItsTangent(Lp,dLp_dMp,Mp,T,instance,of) dLp_dMp(k,l,m,n) = dLp_dMp(k,l,m,n) & + ddot_gamma_dtau_trans(i)* prm%P_tr(k,l,i)*prm%P_tr(m,n,i) * f_unrotated enddo transContibution - - + + end associate - + end subroutine plastic_dislotwin_LpAndItsTangent !-------------------------------------------------------------------------------------------------- -!> @brief calculates the rate of change of microstructure +!> @brief Calculate the rate of change of microstructure. !-------------------------------------------------------------------------------------------------- module subroutine plastic_dislotwin_dotState(Mp,T,instance,of) @@ -720,10 +652,10 @@ module subroutine plastic_dislotwin_dotState(Mp,T,instance,of) f_unrotated, & VacancyDiffusion, & rho_dip_distance, & - v_cl, & !< climb velocity + v_cl, & !< climb velocity Gamma, & !< stacking fault energy tau, & - sigma_cl, & !< climb stress + sigma_cl, & !< climb stress b_d !< ratio of burgers vector to stacking fault width real(pReal), dimension(param(instance)%sum_N_sl) :: & dot_rho_dip_formation, & @@ -745,9 +677,9 @@ module subroutine plastic_dislotwin_dotState(Mp,T,instance,of) call kinetics_slip(Mp,T,instance,of,dot_gamma_sl) dot%gamma_sl(:,of) = abs(dot_gamma_sl) - + rho_dip_distance_min = prm%CEdgeDipMinDistance*prm%b_sl - + slipState: do i = 1, prm%sum_N_sl tau = math_mul33xx33(Mp,prm%P_sl(1:3,1:3,i)) @@ -771,15 +703,15 @@ module subroutine plastic_dislotwin_dotState(Mp,T,instance,of) else !@details: Refer: Argon & Moffat, Acta Metallurgica, Vol. 29, pg 293 to 299, 1981 sigma_cl = dot_product(prm%n0_sl(1:3,i),matmul(Mp,prm%n0_sl(1:3,i))) - if (prm%ExtendedDislocations) then + if (prm%ExtendedDislocations) then Gamma = prm%SFE_0K + prm%dSFE_dT * T b_d = 24.0_pReal*PI*(1.0_pReal - prm%nu)/(2.0_pReal + prm%nu)* Gamma/(prm%mu*prm%b_sl(i)) else - b_d = 1.0_pReal + b_d = 1.0_pReal endif v_cl = 2.0_pReal*prm%omega*b_d**2.0_pReal*exp(-prm%Qsd/(kB*T)) & * (exp(abs(sigma_cl)*prm%b_sl(i)**3.0_pReal/(kB*T)) - 1.0_pReal) - + dot_rho_dip_climb(i) = 4.0_pReal*v_cl*stt%rho_dip(i,of) & / (rho_dip_distance-rho_dip_distance_min(i)) endif @@ -801,12 +733,12 @@ module subroutine plastic_dislotwin_dotState(Mp,T,instance,of) dot%f_tr(:,of) = f_unrotated*dot_gamma_tr end associate - + end subroutine plastic_dislotwin_dotState !-------------------------------------------------------------------------------------------------- -!> @brief calculates derived quantities from state +!> @brief Calculate derived quantities from state. !-------------------------------------------------------------------------------------------------- module subroutine plastic_dislotwin_dependentState(T,instance,of) @@ -815,7 +747,7 @@ module subroutine plastic_dislotwin_dependentState(T,instance,of) of real(pReal), intent(in) :: & T - + real(pReal) :: & sumf_twin,Gamma,sumf_trans real(pReal), dimension(param(instance)%sum_N_sl) :: & @@ -830,35 +762,35 @@ module subroutine plastic_dislotwin_dependentState(T,instance,of) f_over_t_tr real(pReal), dimension(:), allocatable :: & x0 - - + + associate(prm => param(instance),& stt => state(instance),& dst => dependentState(instance)) - + sumf_twin = sum(stt%f_tw(1:prm%sum_N_tw,of)) sumf_trans = sum(stt%f_tr(1:prm%sum_N_tr,of)) - + Gamma = prm%SFE_0K + prm%dSFE_dT * T - + !* rescaled volume fraction for topology f_over_t_tw = stt%f_tw(1:prm%sum_N_tw,of)/prm%t_tw ! this is per system ... f_over_t_tr = sumf_trans/prm%t_tr ! but this not ! ToDo ...Physically correct, but naming could be adjusted - + inv_lambda_sl_sl = sqrt(matmul(prm%forestProjection, & stt%rho_mob(:,of)+stt%rho_dip(:,of)))/prm%CLambdaSlip - + if (prm%sum_N_tw > 0 .and. prm%sum_N_sl > 0) & inv_lambda_sl_tw = matmul(prm%h_sl_tw,f_over_t_tw)/(1.0_pReal-sumf_twin) - + inv_lambda_tw_tw = matmul(prm%h_tw_tw,f_over_t_tw)/(1.0_pReal-sumf_twin) - + if (prm%sum_N_tr > 0 .and. prm%sum_N_sl > 0) & inv_lambda_sl_tr = matmul(prm%h_sl_tr,f_over_t_tr)/(1.0_pReal-sumf_trans) - + inv_lambda_tr_tr = matmul(prm%h_tr_tr,f_over_t_tr)/(1.0_pReal-sumf_trans) - + if ((prm%sum_N_tw > 0) .or. (prm%sum_N_tr > 0)) then ! ToDo: better logic needed here dst%Lambda_sl(:,of) = prm%D & / (1.0_pReal+prm%D*(inv_lambda_sl_sl + inv_lambda_sl_tw + inv_lambda_sl_tr)) @@ -866,13 +798,13 @@ module subroutine plastic_dislotwin_dependentState(T,instance,of) dst%Lambda_sl(:,of) = prm%D & / (1.0_pReal+prm%D*inv_lambda_sl_sl) !!!!!! correct? endif - + dst%Lambda_tw(:,of) = prm%i_tw*prm%D/(1.0_pReal+prm%D*inv_lambda_tw_tw) dst%Lambda_tr(:,of) = prm%i_tr*prm%D/(1.0_pReal+prm%D*inv_lambda_tr_tr) - + !* threshold stress for dislocation motion dst%tau_pass(:,of) = prm%mu*prm%b_sl* sqrt(matmul(prm%h_sl_sl,stt%rho_mob(:,of)+stt%rho_dip(:,of))) - + !* threshold stress for growing twin/martensite if(prm%sum_N_tw == prm%sum_N_sl) & dst%tau_hat_tw(:,of) = Gamma/(3.0_pReal*prm%b_tw) & @@ -880,66 +812,67 @@ module subroutine plastic_dislotwin_dependentState(T,instance,of) if(prm%sum_N_tr == prm%sum_N_sl) & dst%tau_hat_tr(:,of) = Gamma/(3.0_pReal*prm%b_tr) & + 3.0_pReal*prm%b_tr*prm%mu/(prm%L_tr*prm%b_sl) & ! slip burgers here correct? - + prm%h*prm%gamma_fcc_hex/ (3.0_pReal*prm%b_tr) - + + prm%h*prm%gamma_fcc_hex/ (3.0_pReal*prm%b_tr) + dst%V_tw(:,of) = (PI/4.0_pReal)*prm%t_tw*dst%Lambda_tw(:,of)**2.0_pReal dst%V_tr(:,of) = (PI/4.0_pReal)*prm%t_tr*dst%Lambda_tr(:,of)**2.0_pReal - - + + x0 = prm%mu*prm%b_tw**2.0_pReal/(Gamma*8.0_pReal*PI)*(2.0_pReal+prm%nu)/(1.0_pReal-prm%nu) ! ToDo: In the paper, this is the burgers vector for slip and is the same for twin and trans dst%tau_r_tw(:,of) = prm%mu*prm%b_tw/(2.0_pReal*PI)*(1.0_pReal/(x0+prm%xc_twin)+cos(pi/3.0_pReal)/x0) - + x0 = prm%mu*prm%b_tr**2.0_pReal/(Gamma*8.0_pReal*PI)*(2.0_pReal+prm%nu)/(1.0_pReal-prm%nu) ! ToDo: In the paper, this is the burgers vector for slip dst%tau_r_tr(:,of) = prm%mu*prm%b_tr/(2.0_pReal*PI)*(1.0_pReal/(x0+prm%xc_trans)+cos(pi/3.0_pReal)/x0) - + end associate end subroutine plastic_dislotwin_dependentState !-------------------------------------------------------------------------------------------------- -!> @brief writes results to HDF5 output file +!> @brief Write results to HDF5 output file. !-------------------------------------------------------------------------------------------------- module subroutine plastic_dislotwin_results(instance,group) integer, intent(in) :: instance character(len=*), intent(in) :: group + integer :: o - associate(prm => param(instance), stt => state(instance), dst => dependentState(instance)) - outputsLoop: do o = 1,size(prm%outputID) - select case(prm%outputID(o)) + associate(prm => param(instance), stt => state(instance), dst => dependentState(instance)) + outputsLoop: do o = 1,size(prm%output) + select case(trim(prm%output(o))) - case (rho_mob_ID) - call results_writeDataset(group,stt%rho_mob,'rho_mob',& - 'mobile dislocation density','1/m²') - case (rho_dip_ID) - call results_writeDataset(group,stt%rho_dip,'rho_dip',& - 'dislocation dipole density''1/m²') - case (gamma_sl_ID) - call results_writeDataset(group,stt%gamma_sl,'gamma_sl',& - 'plastic shear','1') - case (Lambda_sl_ID) - call results_writeDataset(group,dst%Lambda_sl,'Lambda_sl',& - 'mean free path for slip','m') - case (tau_pass_ID) - call results_writeDataset(group,dst%tau_pass,'tau_pass',& - 'passing stress for slip','Pa') + case('rho_mob') + if(prm%sum_N_sl>0) call results_writeDataset(group,stt%rho_mob,'rho_mob',& + 'mobile dislocation density','1/m²') + case('rho_dip') + if(prm%sum_N_sl>0) call results_writeDataset(group,stt%rho_dip,'rho_dip',& + 'dislocation dipole density''1/m²') + case('gamma_sl') + if(prm%sum_N_sl>0) call results_writeDataset(group,stt%gamma_sl,'gamma_sl',& + 'plastic shear','1') + case('lambda_sl') + if(prm%sum_N_sl>0) call results_writeDataset(group,dst%Lambda_sl,'Lambda_sl',& + 'mean free path for slip','m') + case('tau_pass') + if(prm%sum_N_sl>0) call results_writeDataset(group,dst%tau_pass,'tau_pass',& + 'passing stress for slip','Pa') - case (f_tw_ID) - call results_writeDataset(group,stt%f_tw,'f_tw',& - 'twinned volume fraction','m³/m³') - case (Lambda_tw_ID) - call results_writeDataset(group,dst%Lambda_tw,'Lambda_tw',& - 'mean free path for twinning','m') - case (tau_hat_tw_ID) - call results_writeDataset(group,dst%tau_hat_tw,'tau_hat_tw',& - 'threshold stress for twinning','Pa') + case('f_tw') + if(prm%sum_N_tw>0) call results_writeDataset(group,stt%f_tw,'f_tw',& + 'twinned volume fraction','m³/m³') + case('lambda_tw') + if(prm%sum_N_tw>0) call results_writeDataset(group,dst%Lambda_tw,'Lambda_tw',& + 'mean free path for twinning','m') + case('tau_hat_tw') + if(prm%sum_N_tw>0) call results_writeDataset(group,dst%tau_hat_tw,'tau_hat_tw',& + 'threshold stress for twinning','Pa') + + case('f_tr') + if(prm%sum_N_tr>0) call results_writeDataset(group,stt%f_tr,'f_tr',& + 'martensite volume fraction','m³/m³') - case (f_tr_ID) - call results_writeDataset(group,stt%f_tr,'f_tr',& - 'martensite volume fraction','m³/m³') - end select enddo outputsLoop end associate @@ -948,8 +881,8 @@ end subroutine plastic_dislotwin_results !-------------------------------------------------------------------------------------------------- -!> @brief Shear rates on slip systems, their derivatives with respect to resolved stress and the -! resolved stresss +!> @brief Calculate shear rates on slip systems, their derivatives with respect to resolved +! stress, and the resolved stress. !> @details Derivatives and resolved stress are calculated only optionally. ! NOTE: Against the common convention, the result (i.e. intent(out)) variables are the last to ! have the optional arguments at the end @@ -964,7 +897,7 @@ pure subroutine kinetics_slip(Mp,T,instance,of, & integer, intent(in) :: & instance, & of - + real(pReal), dimension(param(instance)%sum_N_sl), intent(out) :: & dot_gamma_sl real(pReal), dimension(param(instance)%sum_N_sl), optional, intent(out) :: & @@ -972,7 +905,7 @@ pure subroutine kinetics_slip(Mp,T,instance,of, & tau_slip real(pReal), dimension(param(instance)%sum_N_sl) :: & ddot_gamma_dtau - + real(pReal), dimension(param(instance)%sum_N_sl) :: & tau, & stressRatio, & @@ -984,25 +917,25 @@ pure subroutine kinetics_slip(Mp,T,instance,of, & dV_run_inverse_dTau, & dV_dTau, & tau_eff !< effective resolved stress - integer :: i - + integer :: i + associate(prm => param(instance), stt => state(instance), dst => dependentState(instance)) - + do i = 1, prm%sum_N_sl tau(i) = math_mul33xx33(Mp,prm%P_sl(1:3,1:3,i)) enddo - + tau_eff = abs(tau)-dst%tau_pass(:,of) - + significantStress: where(tau_eff > tol_math_check) stressRatio = tau_eff/prm%tau_0 StressRatio_p = stressRatio** prm%p BoltzmannRatio = prm%Delta_F/(kB*T) v_wait_inverse = prm%v0**(-1.0_pReal) * exp(BoltzmannRatio*(1.0_pReal-StressRatio_p)** prm%q) v_run_inverse = prm%B/(tau_eff*prm%b_sl) - + dot_gamma_sl = sign(stt%rho_mob(:,of)*prm%b_sl/(v_wait_inverse+v_run_inverse),tau) - + dV_wait_inverse_dTau = -1.0_pReal * v_wait_inverse * prm%p * prm%q * BoltzmannRatio & * (stressRatio**(prm%p-1.0_pReal)) & * (1.0_pReal-StressRatio_p)**(prm%q-1.0_pReal) & @@ -1015,17 +948,21 @@ pure subroutine kinetics_slip(Mp,T,instance,of, & dot_gamma_sl = 0.0_pReal ddot_gamma_dtau = 0.0_pReal end where significantStress - + end associate - + if(present(ddot_gamma_dtau_slip)) ddot_gamma_dtau_slip = ddot_gamma_dtau if(present(tau_slip)) tau_slip = tau - + end subroutine kinetics_slip !-------------------------------------------------------------------------------------------------- -!> @brief calculates shear rates on twin systems +!> @brief Calculate shear rates on twin systems and their derivatives with respect to resolved +! stress. +!> @details Derivatives are calculated only optionally. +! NOTE: Against the common convention, the result (i.e. intent(out)) variables are the last to +! have the optional arguments at the end. !-------------------------------------------------------------------------------------------------- pure subroutine kinetics_twin(Mp,T,dot_gamma_sl,instance,of,& dot_gamma_twin,ddot_gamma_dtau_twin) @@ -1039,22 +976,22 @@ pure subroutine kinetics_twin(Mp,T,dot_gamma_sl,instance,of,& of real(pReal), dimension(param(instance)%sum_N_sl), intent(in) :: & dot_gamma_sl - + real(pReal), dimension(param(instance)%sum_N_tw), intent(out) :: & dot_gamma_twin real(pReal), dimension(param(instance)%sum_N_tw), optional, intent(out) :: & ddot_gamma_dtau_twin - + real, dimension(param(instance)%sum_N_tw) :: & tau, & Ndot0, & stressRatio_r, & ddot_gamma_dtau - + integer :: i,s1,s2 - + associate(prm => param(instance), stt => state(instance), dst => dependentState(instance)) - + do i = 1, prm%sum_N_tw tau(i) = math_mul33xx33(Mp,prm%P_tw(1:3,1:3,i)) isFCC: if (prm%fccTwinTransNucleation) then @@ -1072,7 +1009,7 @@ pure subroutine kinetics_twin(Mp,T,dot_gamma_sl,instance,of,& Ndot0=prm%dot_N_0_tw(i) endif isFCC enddo - + significantStress: where(tau > tol_math_check) StressRatio_r = (dst%tau_hat_tw(:,of)/tau)**prm%r dot_gamma_twin = prm%gamma_char * dst%V_tw(:,of) * Ndot0*exp(-StressRatio_r) @@ -1081,16 +1018,20 @@ pure subroutine kinetics_twin(Mp,T,dot_gamma_sl,instance,of,& dot_gamma_twin = 0.0_pReal ddot_gamma_dtau = 0.0_pReal end where significantStress - + end associate - + if(present(ddot_gamma_dtau_twin)) ddot_gamma_dtau_twin = ddot_gamma_dtau end subroutine kinetics_twin !-------------------------------------------------------------------------------------------------- -!> @brief calculates shear rates on twin systems +!> @brief Calculate shear rates on transformation systems and their derivatives with respect to +! resolved stress. +!> @details Derivatives are calculated only optionally. +! NOTE: Against the common convention, the result (i.e. intent(out)) variables are the last to +! have the optional arguments at the end. !-------------------------------------------------------------------------------------------------- pure subroutine kinetics_trans(Mp,T,dot_gamma_sl,instance,of,& dot_gamma_tr,ddot_gamma_dtau_trans) @@ -1104,21 +1045,21 @@ pure subroutine kinetics_trans(Mp,T,dot_gamma_sl,instance,of,& of real(pReal), dimension(param(instance)%sum_N_sl), intent(in) :: & dot_gamma_sl - + real(pReal), dimension(param(instance)%sum_N_tr), intent(out) :: & dot_gamma_tr real(pReal), dimension(param(instance)%sum_N_tr), optional, intent(out) :: & ddot_gamma_dtau_trans - + real, dimension(param(instance)%sum_N_tr) :: & tau, & Ndot0, & stressRatio_s, & ddot_gamma_dtau - + integer :: i,s1,s2 associate(prm => param(instance), stt => state(instance), dst => dependentState(instance)) - + do i = 1, prm%sum_N_tr tau(i) = math_mul33xx33(Mp,prm%P_tr(1:3,1:3,i)) isFCC: if (prm%fccTwinTransNucleation) then @@ -1136,7 +1077,7 @@ pure subroutine kinetics_trans(Mp,T,dot_gamma_sl,instance,of,& Ndot0=prm%dot_N_0_tr(i) endif isFCC enddo - + significantStress: where(tau > tol_math_check) StressRatio_s = (dst%tau_hat_tr(:,of)/tau)**prm%s dot_gamma_tr = dst%V_tr(:,of) * Ndot0*exp(-StressRatio_s) @@ -1145,9 +1086,9 @@ pure subroutine kinetics_trans(Mp,T,dot_gamma_sl,instance,of,& dot_gamma_tr = 0.0_pReal ddot_gamma_dtau = 0.0_pReal end where significantStress - + end associate - + if(present(ddot_gamma_dtau_trans)) ddot_gamma_dtau_trans = ddot_gamma_dtau end subroutine kinetics_trans diff --git a/src/constitutive_plastic_isotropic.f90 b/src/constitutive_plastic_isotropic.f90 index ef8a823d4..beed2112a 100644 --- a/src/constitutive_plastic_isotropic.f90 +++ b/src/constitutive_plastic_isotropic.f90 @@ -8,14 +8,7 @@ !! untextured polycrystal !-------------------------------------------------------------------------------------------------- submodule(constitutive) plastic_isotropic - - enum, bind(c) - enumerator :: & - undefined_ID, & - xi_ID, & - dot_gamma_ID - end enum - + type :: tParameters real(pReal) :: & M, & !< Taylor factor @@ -34,12 +27,12 @@ submodule(constitutive) plastic_isotropic aTol_gamma integer :: & of_debug = 0 - integer(kind(undefined_ID)), allocatable, dimension(:) :: & - outputID logical :: & dilatation + character(len=pStringLen), allocatable, dimension(:) :: & + output end type tParameters - + type :: tIsotropicState real(pReal), pointer, dimension(:) :: & xi, & @@ -56,50 +49,45 @@ submodule(constitutive) plastic_isotropic contains !-------------------------------------------------------------------------------------------------- -!> @brief module initialization +!> @brief Perform module initialization. !> @details reads in material parameters, allocates arrays, and does sanity checks !-------------------------------------------------------------------------------------------------- module subroutine plastic_isotropic_init integer :: & Ninstance, & - p, i, & + p, & NipcMyPhase, & sizeState, sizeDotState - - integer(kind(undefined_ID)) :: & - outputID - + character(len=pStringLen) :: & extmsg = '' - character(len=pStringLen), dimension(:), allocatable :: & - outputs - - write(6,'(/,a)') ' <<<+- plastic_'//PLASTICITY_ISOTROPIC_label//' init -+>>>' - - write(6,'(/,a)') ' Maiti and Eisenlohr, Scripta Materialia 145:37–40, 2018' - write(6,'(a)') ' https://doi.org/10.1016/j.scriptamat.2017.09.047' - + + write(6,'(/,a)') ' <<<+- plastic_'//PLASTICITY_ISOTROPIC_label//' init -+>>>'; flush(6) + + write(6,'(/,a)') ' Maiti and Eisenlohr, Scripta Materialia 145:37–40, 2018' + write(6,'(a)') ' https://doi.org/10.1016/j.scriptamat.2017.09.047' + Ninstance = count(phase_plasticity == PLASTICITY_ISOTROPIC_ID) if (iand(debug_level(debug_constitutive),debug_levelBasic) /= 0) & write(6,'(a16,1x,i5,/)') '# instances:',Ninstance - + allocate(param(Ninstance)) allocate(state(Ninstance)) allocate(dotState(Ninstance)) - + do p = 1, size(phase_plasticity) if (phase_plasticity(p) /= PLASTICITY_ISOTROPIC_ID) cycle associate(prm => param(phase_plasticityInstance(p)), & dot => dotState(phase_plasticityInstance(p)), & stt => state(phase_plasticityInstance(p)), & config => config_phase(p)) - + #ifdef DEBUG if (p==material_phaseAt(debug_g,debug_e)) & prm%of_debug = material_phasememberAt(debug_g,debug_i,debug_e) #endif - + prm%xi_0 = config%getFloat('tau0') prm%xi_inf = config%getFloat('tausat') prm%dot_gamma_0 = config%getFloat('gdot0') @@ -114,9 +102,9 @@ module subroutine plastic_isotropic_init prm%a = config%getFloat('a') prm%aTol_xi = config%getFloat('atol_flowstress',defaultVal=1.0_pReal) prm%aTol_gamma = config%getFloat('atol_shear', defaultVal=1.0e-6_pReal) - + prm%dilatation = config%keyExists('/dilatation/') - + !-------------------------------------------------------------------------------------------------- ! sanity checks extmsg = '' @@ -128,79 +116,62 @@ module subroutine plastic_isotropic_init if (prm%M <= 0.0_pReal) extmsg = trim(extmsg)//' m' if (prm%aTol_xi <= 0.0_pReal) extmsg = trim(extmsg)//' atol_xi' if (prm%aTol_gamma <= 0.0_pReal) extmsg = trim(extmsg)//' atol_shear' - + !-------------------------------------------------------------------------------------------------- ! exit if any parameter is out of range if (extmsg /= '') & call IO_error(211,ext_msg=trim(extmsg)//'('//PLASTICITY_ISOTROPIC_label//')') - + !-------------------------------------------------------------------------------------------------- ! output pararameters - outputs = config%getStrings('(output)',defaultVal=emptyStringArray) - allocate(prm%outputID(0)) - do i=1, size(outputs) - outputID = undefined_ID - select case(outputs(i)) - - case ('flowstress') - outputID = xi_ID - case ('strainrate') - outputID = dot_gamma_ID - - end select - - if (outputID /= undefined_ID) then - prm%outputID = [prm%outputID, outputID] - endif - - enddo - + prm%output = config%getStrings('(output)',defaultVal=emptyStringArray) + !-------------------------------------------------------------------------------------------------- ! allocate state arrays NipcMyPhase = count(material_phaseAt == p) * discretization_nIP sizeDotState = size(['xi ','accumulated_shear']) sizeState = sizeDotState - + call material_allocatePlasticState(p,NipcMyPhase,sizeState,sizeDotState,0) - + !-------------------------------------------------------------------------------------------------- ! locally defined state aliases and initialization of state0 and aTolState stt%xi => plasticState(p)%state (1,:) stt%xi = prm%xi_0 dot%xi => plasticState(p)%dotState(1,:) plasticState(p)%aTolState(1) = prm%aTol_xi - + stt%gamma => plasticState(p)%state (2,:) dot%gamma => plasticState(p)%dotState(2,:) plasticState(p)%aTolState(2) = prm%aTol_gamma ! global alias plasticState(p)%slipRate => plasticState(p)%dotState(2:2,:) - + plasticState(p)%state0 = plasticState(p)%state ! ToDo: this could be done centrally - + end associate - + enddo end subroutine plastic_isotropic_init !-------------------------------------------------------------------------------------------------- -!> @brief calculates plastic velocity gradient and its tangent +!> @brief Calculate plastic velocity gradient and its tangent. !-------------------------------------------------------------------------------------------------- module subroutine plastic_isotropic_LpAndItsTangent(Lp,dLp_dMp,Mp,instance,of) - + real(pReal), dimension(3,3), intent(out) :: & Lp !< plastic velocity gradient real(pReal), dimension(3,3,3,3), intent(out) :: & dLp_dMp !< derivative of Lp with respect to the Mandel stress - + real(pReal), dimension(3,3), intent(in) :: & Mp !< Mandel stress integer, intent(in) :: & instance, & of - + real(pReal), dimension(3,3) :: & Mp_dev !< deviatoric part of the Mandel stress real(pReal) :: & @@ -209,16 +180,16 @@ module subroutine plastic_isotropic_LpAndItsTangent(Lp,dLp_dMp,Mp,instance,of) squarenorm_Mp_dev !< square of the norm of the deviatoric part of the Mandel stress integer :: & k, l, m, n - + associate(prm => param(instance), stt => state(instance)) - + Mp_dev = math_deviatoric33(Mp) squarenorm_Mp_dev = math_mul33xx33(Mp_dev,Mp_dev) norm_Mp_dev = sqrt(squarenorm_Mp_dev) - + if (norm_Mp_dev > 0.0_pReal) then dot_gamma = prm%dot_gamma_0 * (sqrt(1.5_pReal) * norm_Mp_dev/(prm%M*stt%xi(of))) **prm%n - + Lp = dot_gamma/prm%M * Mp_dev/norm_Mp_dev #ifdef DEBUG if (iand(debug_level(debug_constitutive), debug_levelExtensive) /= 0 & @@ -240,42 +211,42 @@ module subroutine plastic_isotropic_LpAndItsTangent(Lp,dLp_dMp,Mp,instance,of) Lp = 0.0_pReal dLp_dMp = 0.0_pReal end if - + end associate end subroutine plastic_isotropic_LpAndItsTangent !-------------------------------------------------------------------------------------------------- -!> @brief calculates plastic velocity gradient and its tangent +!> @brief Calculate inelastic velocity gradient and its tangent. !-------------------------------------------------------------------------------------------------- module subroutine plastic_isotropic_LiAndItsTangent(Li,dLi_dMi,Mi,instance,of) - + real(pReal), dimension(3,3), intent(out) :: & Li !< inleastic velocity gradient real(pReal), dimension(3,3,3,3), intent(out) :: & dLi_dMi !< derivative of Li with respect to Mandel stress - + real(pReal), dimension(3,3), intent(in) :: & - Mi !< Mandel stress + Mi !< Mandel stress integer, intent(in) :: & instance, & of - + real(pReal) :: & tr !< trace of spherical part of Mandel stress (= 3 x pressure) integer :: & k, l, m, n - + associate(prm => param(instance), stt => state(instance)) - + tr=math_trace33(math_spherical33(Mi)) if (prm%dilatation .and. abs(tr) > 0.0_pReal) then ! no stress or J2 plasticity --> Li and its derivative are zero Li = math_I3 & * prm%dot_gamma_0/prm%M * (3.0_pReal*prm%M*stt%xi(of))**(-prm%n) & * tr * abs(tr)**(prm%n-1.0_pReal) - + #ifdef DEBUG if (iand(debug_level(debug_constitutive), debug_levelExtensive) /= 0 & .and. (of == prm%of_debug .or. .not. iand(debug_level(debug_constitutive),debug_levelSelective) /= 0)) then @@ -292,38 +263,38 @@ module subroutine plastic_isotropic_LiAndItsTangent(Li,dLi_dMi,Mi,instance,of) Li = 0.0_pReal dLi_dMi = 0.0_pReal endif - + end associate end subroutine plastic_isotropic_LiAndItsTangent !-------------------------------------------------------------------------------------------------- -!> @brief calculates the rate of change of microstructure +!> @brief Calculate the rate of change of microstructure. !-------------------------------------------------------------------------------------------------- module subroutine plastic_isotropic_dotState(Mp,instance,of) - + real(pReal), dimension(3,3), intent(in) :: & Mp !< Mandel stress integer, intent(in) :: & instance, & of - + real(pReal) :: & dot_gamma, & !< strainrate xi_inf_star, & !< saturation xi norm_Mp !< norm of the (deviatoric) Mandel stress - + associate(prm => param(instance), stt => state(instance), dot => dotState(instance)) - + if (prm%dilatation) then norm_Mp = sqrt(math_mul33xx33(Mp,Mp)) else norm_Mp = sqrt(math_mul33xx33(math_deviatoric33(Mp),math_deviatoric33(Mp))) endif - + dot_gamma = prm%dot_gamma_0 * (sqrt(1.5_pReal) * norm_Mp /(prm%M*stt%xi(of))) **prm%n - + if (dot_gamma > 1e-12_pReal) then if (dEq0(prm%c_1)) then xi_inf_star = prm%xi_inf @@ -339,28 +310,28 @@ module subroutine plastic_isotropic_dotState(Mp,instance,of) else dot%xi(of) = 0.0_pReal endif - + dot%gamma(of) = dot_gamma ! ToDo: not really used - + end associate end subroutine plastic_isotropic_dotState !-------------------------------------------------------------------------------------------------- -!> @brief writes results to HDF5 output file +!> @brief Write results to HDF5 output file. !-------------------------------------------------------------------------------------------------- module subroutine plastic_isotropic_results(instance,group) - integer, intent(in) :: instance + integer, intent(in) :: instance character(len=*), intent(in) :: group - + integer :: o associate(prm => param(instance), stt => state(instance)) - outputsLoop: do o = 1,size(prm%outputID) - select case(prm%outputID(o)) - case (xi_ID) + outputsLoop: do o = 1,size(prm%output) + select case(trim(prm%output(o))) + case ('flowstress') ! ToDo: should be 'xi' call results_writeDataset(group,stt%xi,'xi','resistance against plastic flow','Pa') end select enddo outputsLoop diff --git a/src/constitutive_plastic_kinehardening.f90 b/src/constitutive_plastic_kinehardening.f90 index 11ce7c494..65751989c 100644 --- a/src/constitutive_plastic_kinehardening.f90 +++ b/src/constitutive_plastic_kinehardening.f90 @@ -7,26 +7,13 @@ !-------------------------------------------------------------------------------------------------- submodule(constitutive) plastic_kinehardening - enum, bind(c) - enumerator :: & - undefined_ID, & - crss_ID, & !< critical resolved stress - crss_back_ID, & !< critical resolved back stress - sense_ID, & !< sense of acting shear stress (-1 or +1) - chi0_ID, & !< backstress at last switch of stress sense (positive?) - gamma0_ID, & !< accumulated shear at last switch of stress sense (at current switch?) - accshear_ID, & - shearrate_ID, & - resolvedstress_ID - end enum - type :: tParameters real(pReal) :: & gdot0, & !< reference shear strain rate for slip n, & !< stress exponent for slip aTolResistance, & aTolShear - real(pReal), allocatable, dimension(:) :: & + real(pReal), allocatable, dimension(:) :: & crss0, & !< initial critical shear stress for slip theta0, & !< initial hardening rate of forward stress for each slip theta1, & !< asymptotic hardening rate of forward stress for each slip @@ -35,21 +22,21 @@ submodule(constitutive) plastic_kinehardening tau1, & tau1_b, & nonSchmidCoeff - real(pReal), allocatable, dimension(:,:) :: & + real(pReal), allocatable, dimension(:,:) :: & interaction_slipslip !< slip resistance from slip activity - real(pReal), allocatable, dimension(:,:,:) :: & + real(pReal), allocatable, dimension(:,:,:) :: & Schmid, & nonSchmid_pos, & nonSchmid_neg integer :: & totalNslip, & !< total number of active slip system of_debug = 0 - integer, allocatable, dimension(:) :: & + integer, allocatable, dimension(:) :: & Nslip !< number of active slip systems for each family - integer(kind(undefined_ID)), allocatable, dimension(:) :: & - outputID !< ID of each post result output + character(len=pStringLen), allocatable, dimension(:) :: & + output end type tParameters - + type :: tKinehardeningState real(pReal), pointer, dimension(:,:) :: & !< vectors along NipcMyInstance crss, & !< critical resolved stress @@ -59,7 +46,7 @@ submodule(constitutive) plastic_kinehardening gamma0, & !< accumulated shear at last switch of stress sense accshear !< accumulated (absolute) shear end type tKinehardeningState - + !-------------------------------------------------------------------------------------------------- ! containers for parameters and state type(tParameters), allocatable, dimension(:) :: param @@ -72,37 +59,32 @@ contains !-------------------------------------------------------------------------------------------------- -!> @brief module initialization +!> @brief Perform module initialization. !> @details reads in material parameters, allocates arrays, and does sanity checks !-------------------------------------------------------------------------------------------------- module subroutine plastic_kinehardening_init integer :: & Ninstance, & - p, i, o, & + p, o, & NipcMyPhase, & sizeState, sizeDeltaState, sizeDotState, & startIndex, endIndex - - integer(kind(undefined_ID)) :: & - outputID - + character(len=pStringLen) :: & extmsg = '' - character(len=pStringLen), dimension(:), allocatable :: & - outputs - - write(6,'(/,a)') ' <<<+- plastic_'//PLASTICITY_KINEHARDENING_label//' init -+>>>' - + + write(6,'(/,a)') ' <<<+- plastic_'//PLASTICITY_KINEHARDENING_label//' init -+>>>'; flush(6) + Ninstance = count(phase_plasticity == PLASTICITY_KINEHARDENING_ID) if (iand(debug_level(debug_constitutive),debug_levelBasic) /= 0) & write(6,'(a16,1x,i5,/)') '# instances:',Ninstance - + allocate(param(Ninstance)) allocate(state(Ninstance)) allocate(dotState(Ninstance)) allocate(deltaState(Ninstance)) - + do p = 1, size(phase_plasticityInstance) if (phase_plasticity(p) /= PLASTICITY_KINEHARDENING_ID) cycle associate(prm => param(phase_plasticityInstance(p)), & @@ -110,22 +92,22 @@ module subroutine plastic_kinehardening_init dlt => deltaState(phase_plasticityInstance(p)), & stt => state(phase_plasticityInstance(p)),& config => config_phase(p)) - + #ifdef DEBUG if (p==material_phaseAt(debug_g,debug_e)) then - prm%of_debug = material_phasememberAt(debug_g,debug_i,debug_e) + prm%of_debug = material_phasememberAt(debug_g,debug_i,debug_e) endif #endif - + !-------------------------------------------------------------------------------------------------- ! optional parameters that need to be defined prm%aTolResistance = config%getFloat('atol_resistance',defaultVal=1.0_pReal) prm%aTolShear = config%getFloat('atol_shear', defaultVal=1.0e-6_pReal) - + ! sanity checks if (prm%aTolResistance <= 0.0_pReal) extmsg = trim(extmsg)//' aTolresistance' if (prm%aTolShear <= 0.0_pReal) extmsg = trim(extmsg)//' aTolShear' - + !-------------------------------------------------------------------------------------------------- ! slip related parameters prm%Nslip = config%getInts('nslip',defaultVal=emptyIntArray) @@ -133,7 +115,7 @@ module subroutine plastic_kinehardening_init slipActive: if (prm%totalNslip > 0) then prm%Schmid = lattice_SchmidMatrix_slip(prm%Nslip,config%getString('lattice_structure'),& config%getFloat('c/a',defaultVal=0.0_pReal)) - + if(trim(config%getString('lattice_structure')) == 'bcc') then prm%nonSchmidCoeff = config%getFloats('nonschmid_coefficients',& defaultVal = emptyRealArray) @@ -146,7 +128,7 @@ module subroutine plastic_kinehardening_init prm%interaction_SlipSlip = lattice_interaction_SlipBySlip(prm%Nslip, & config%getFloats('interaction_slipslip'), & config%getString('lattice_structure')) - + prm%crss0 = config%getFloats('crss0', requiredSize=size(prm%Nslip)) prm%tau1 = config%getFloats('tau1', requiredSize=size(prm%Nslip)) prm%tau1_b = config%getFloats('tau1_b', requiredSize=size(prm%Nslip)) @@ -154,10 +136,10 @@ module subroutine plastic_kinehardening_init prm%theta1 = config%getFloats('theta1', requiredSize=size(prm%Nslip)) prm%theta0_b = config%getFloats('theta0_b', requiredSize=size(prm%Nslip)) prm%theta1_b = config%getFloats('theta1_b', requiredSize=size(prm%Nslip)) - - prm%gdot0 = config%getFloat('gdot0') - prm%n = config%getFloat('n_slip') - + + prm%gdot0 = config%getFloat('gdot0') + prm%n = config%getFloat('n_slip') + ! expand: family => system prm%crss0 = math_expand(prm%crss0, prm%Nslip) prm%tau1 = math_expand(prm%tau1, prm%Nslip) @@ -166,9 +148,9 @@ module subroutine plastic_kinehardening_init prm%theta1 = math_expand(prm%theta1, prm%Nslip) prm%theta0_b = math_expand(prm%theta0_b,prm%Nslip) prm%theta1_b = math_expand(prm%theta1_b,prm%Nslip) - - - + + + !-------------------------------------------------------------------------------------------------- ! sanity checks if ( prm%gdot0 <= 0.0_pReal) extmsg = trim(extmsg)//' gdot0' @@ -176,58 +158,29 @@ module subroutine plastic_kinehardening_init if (any(prm%crss0 <= 0.0_pReal)) extmsg = trim(extmsg)//' crss0' if (any(prm%tau1 <= 0.0_pReal)) extmsg = trim(extmsg)//' tau1' if (any(prm%tau1_b <= 0.0_pReal)) extmsg = trim(extmsg)//' tau1_b' - + !ToDo: Any sensible checks for theta? - + endif slipActive - + !-------------------------------------------------------------------------------------------------- ! exit if any parameter is out of range if (extmsg /= '') & call IO_error(211,ext_msg=trim(extmsg)//'('//PLASTICITY_KINEHARDENING_label//')') - + !-------------------------------------------------------------------------------------------------- ! output pararameters - outputs = config%getStrings('(output)',defaultVal=emptyStringArray) - allocate(prm%outputID(0)) - do i=1, size(outputs) - outputID = undefined_ID - select case(outputs(i)) - - case ('resistance') - outputID = merge(crss_ID,undefined_ID,prm%totalNslip>0) - case ('accumulatedshear') - outputID = merge(accshear_ID,undefined_ID,prm%totalNslip>0) - case ('shearrate') - outputID = merge(shearrate_ID,undefined_ID,prm%totalNslip>0) - case ('resolvedstress') - outputID = merge(resolvedstress_ID,undefined_ID,prm%totalNslip>0) - case ('backstress') - outputID = merge(crss_back_ID,undefined_ID,prm%totalNslip>0) - case ('sense') - outputID = merge(sense_ID,undefined_ID,prm%totalNslip>0) - case ('chi0') - outputID = merge(chi0_ID,undefined_ID,prm%totalNslip>0) - case ('gamma0') - outputID = merge(gamma0_ID,undefined_ID,prm%totalNslip>0) - - end select - - if (outputID /= undefined_ID) then - prm%outputID = [prm%outputID , outputID] - endif - - enddo - + prm%output = config%getStrings('(output)',defaultVal=emptyStringArray) + !-------------------------------------------------------------------------------------------------- ! allocate state arrays NipcMyPhase = count(material_phaseAt == p) * discretization_nIP sizeDotState = size(['crss ','crss_back', 'accshear ']) * prm%totalNslip sizeDeltaState = size(['sense ', 'chi0 ', 'gamma0' ]) * prm%totalNslip sizeState = sizeDotState + sizeDeltaState - + call material_allocatePlasticState(p,NipcMyPhase,sizeState,sizeDotState,sizeDeltaState) - + !-------------------------------------------------------------------------------------------------- ! locally defined state aliases and initialization of state0 and aTolState startIndex = 1 @@ -236,13 +189,13 @@ module subroutine plastic_kinehardening_init stt%crss = spread(prm%crss0, 2, NipcMyPhase) dot%crss => plasticState(p)%dotState(startIndex:endIndex,:) plasticState(p)%aTolState(startIndex:endIndex) = prm%aTolResistance - + startIndex = endIndex + 1 endIndex = endIndex + prm%totalNslip stt%crss_back => plasticState(p)%state (startIndex:endIndex,:) dot%crss_back => plasticState(p)%dotState(startIndex:endIndex,:) plasticState(p)%aTolState(startIndex:endIndex) = prm%aTolResistance - + startIndex = endIndex + 1 endIndex = endIndex + prm%totalNslip stt%accshear => plasticState(p)%state (startIndex:endIndex,:) @@ -250,34 +203,34 @@ module subroutine plastic_kinehardening_init plasticState(p)%aTolState(startIndex:endIndex) = prm%aTolShear ! global alias plasticState(p)%slipRate => plasticState(p)%dotState(startIndex:endIndex,:) - + o = plasticState(p)%offsetDeltaState startIndex = endIndex + 1 endIndex = endIndex + prm%totalNslip stt%sense => plasticState(p)%state (startIndex :endIndex ,:) dlt%sense => plasticState(p)%deltaState(startIndex-o:endIndex-o,:) - + startIndex = endIndex + 1 endIndex = endIndex + prm%totalNslip stt%chi0 => plasticState(p)%state (startIndex :endIndex ,:) dlt%chi0 => plasticState(p)%deltaState(startIndex-o:endIndex-o,:) - + startIndex = endIndex + 1 endIndex = endIndex + prm%totalNslip stt%gamma0 => plasticState(p)%state (startIndex :endIndex ,:) dlt%gamma0 => plasticState(p)%deltaState(startIndex-o:endIndex-o,:) - + plasticState(p)%state0 = plasticState(p)%state ! ToDo: this could be done centrally - + end associate - + enddo end subroutine plastic_kinehardening_init !-------------------------------------------------------------------------------------------------- -!> @brief calculates plastic velocity gradient and its tangent +!> @brief Calculate plastic velocity gradient and its tangent. !-------------------------------------------------------------------------------------------------- pure module subroutine plastic_kinehardening_LpAndItsTangent(Lp,dLp_dMp,Mp,instance,of) @@ -285,26 +238,26 @@ pure module subroutine plastic_kinehardening_LpAndItsTangent(Lp,dLp_dMp,Mp,insta Lp !< plastic velocity gradient real(pReal), dimension(3,3,3,3), intent(out) :: & dLp_dMp !< derivative of Lp with respect to the Mandel stress - + real(pReal), dimension(3,3), intent(in) :: & Mp !< Mandel stress integer, intent(in) :: & instance, & of - + integer :: & i,k,l,m,n real(pReal), dimension(param(instance)%totalNslip) :: & gdot_pos,gdot_neg, & dgdot_dtau_pos,dgdot_dtau_neg - + Lp = 0.0_pReal dLp_dMp = 0.0_pReal - + associate(prm => param(instance)) - + call kinetics(Mp,instance,of,gdot_pos,gdot_neg,dgdot_dtau_pos,dgdot_dtau_neg) - + do i = 1, prm%totalNslip Lp = Lp + (gdot_pos(i)+gdot_neg(i))*prm%Schmid(1:3,1:3,i) forall (k=1:3,l=1:3,m=1:3,n=1:3) & @@ -312,14 +265,14 @@ pure module subroutine plastic_kinehardening_LpAndItsTangent(Lp,dLp_dMp,Mp,insta + dgdot_dtau_pos(i) * prm%Schmid(k,l,i) * prm%nonSchmid_pos(m,n,i) & + dgdot_dtau_neg(i) * prm%Schmid(k,l,i) * prm%nonSchmid_neg(m,n,i) enddo - + end associate end subroutine plastic_kinehardening_LpAndItsTangent !-------------------------------------------------------------------------------------------------- -!> @brief calculates the rate of change of microstructure +!> @brief Calculate the rate of change of microstructure. !-------------------------------------------------------------------------------------------------- module subroutine plastic_kinehardening_dotState(Mp,instance,of) @@ -328,40 +281,40 @@ module subroutine plastic_kinehardening_dotState(Mp,instance,of) integer, intent(in) :: & instance, & of - + real(pReal) :: & sumGamma real(pReal), dimension(param(instance)%totalNslip) :: & gdot_pos,gdot_neg - - + + associate(prm => param(instance), stt => state(instance), dot => dotState(instance)) - + call kinetics(Mp,instance,of,gdot_pos,gdot_neg) dot%accshear(:,of) = abs(gdot_pos+gdot_neg) sumGamma = sum(stt%accshear(:,of)) - - + + dot%crss(:,of) = matmul(prm%interaction_SlipSlip,dot%accshear(:,of)) & * ( prm%theta1 & + (prm%theta0 - prm%theta1 + prm%theta0*prm%theta1*sumGamma/prm%tau1) & * exp(-sumGamma*prm%theta0/prm%tau1) & ) - + dot%crss_back(:,of) = stt%sense(:,of)*dot%accshear(:,of) * & ( prm%theta1_b + & (prm%theta0_b - prm%theta1_b & + prm%theta0_b*prm%theta1_b/(prm%tau1_b+stt%chi0(:,of))*(stt%accshear(:,of)-stt%gamma0(:,of))& ) *exp(-(stt%accshear(:,of)-stt%gamma0(:,of)) *prm%theta0_b/(prm%tau1_b+stt%chi0(:,of))) & ) - + end associate end subroutine plastic_kinehardening_dotState !-------------------------------------------------------------------------------------------------- -!> @brief calculates (instantaneous) incremental change of microstructure +!> @brief Calculate (instantaneous) incremental change of microstructure. !-------------------------------------------------------------------------------------------------- module subroutine plastic_kinehardening_deltaState(Mp,instance,of) @@ -370,18 +323,18 @@ module subroutine plastic_kinehardening_deltaState(Mp,instance,of) integer, intent(in) :: & instance, & of - + real(pReal), dimension(param(instance)%totalNslip) :: & gdot_pos,gdot_neg, & sense - + associate(prm => param(instance), stt => state(instance), dlt => deltaState(instance)) - + call kinetics(Mp,instance,of,gdot_pos,gdot_neg) sense = merge(state(instance)%sense(:,of), & ! keep existing... sign(1.0_pReal,gdot_pos+gdot_neg), & ! ...or have a defined dEq0(gdot_pos+gdot_neg,1e-10_pReal)) ! current sense of shear direction - + #ifdef DEBUG if (iand(debug_level(debug_constitutive), debug_levelExtensive) /= 0 & .and. (of == prm%of_debug & @@ -390,7 +343,7 @@ module subroutine plastic_kinehardening_deltaState(Mp,instance,of) write(6,*) sense,state(instance)%sense(:,of) endif #endif - + !-------------------------------------------------------------------------------------------------- ! switch in sense of shear? where(dNeq(sense,stt%sense(:,of),0.1_pReal)) @@ -402,45 +355,43 @@ module subroutine plastic_kinehardening_deltaState(Mp,instance,of) dlt%chi0 (:,of) = 0.0_pReal dlt%gamma0(:,of) = 0.0_pReal end where - + end associate end subroutine plastic_kinehardening_deltaState !-------------------------------------------------------------------------------------------------- -!> @brief writes results to HDF5 output file +!> @brief Write results to HDF5 output file. !-------------------------------------------------------------------------------------------------- module subroutine plastic_kinehardening_results(instance,group) integer, intent(in) :: instance character(len=*), intent(in) :: group + integer :: o associate(prm => param(instance), stt => state(instance)) - outputsLoop: do o = 1,size(prm%outputID) - select case(prm%outputID(o)) - case (crss_ID) - call results_writeDataset(group,stt%crss,'xi_sl', & - 'resistance against plastic slip','Pa') - - case(crss_back_ID) - call results_writeDataset(group,stt%crss_back,'tau_back', & - 'back stress against plastic slip','Pa') - - case (sense_ID) - call results_writeDataset(group,stt%sense,'sense_of_shear','tbd','1') - - case (chi0_ID) - call results_writeDataset(group,stt%chi0,'chi0','tbd','Pa') - - case (gamma0_ID) - call results_writeDataset(group,stt%gamma0,'gamma0','tbd','1') - - case (accshear_ID) - call results_writeDataset(group,stt%accshear,'gamma_sl', & - 'plastic shear','1') - + outputsLoop: do o = 1,size(prm%output) + select case(trim(prm%output(o))) + case('resistance') + if(prm%totalNslip>0) call results_writeDataset(group,stt%crss,'xi_sl', & + 'resistance against plastic slip','Pa') + case('backstress') ! ToDo: should be 'tau_back' + if(prm%totalNslip>0) call results_writeDataset(group,stt%crss_back,'tau_back', & + 'back stress against plastic slip','Pa') + case ('sense') + if(prm%totalNslip>0) call results_writeDataset(group,stt%sense,'sense_of_shear', & + 'tbd','1') + case ('chi0') + if(prm%totalNslip>0) call results_writeDataset(group,stt%chi0,'chi0', & + 'tbd','Pa') + case ('gamma0') + if(prm%totalNslip>0) call results_writeDataset(group,stt%gamma0,'gamma0', & + 'tbd','1') + case ('accumulatedshear') + if(prm%totalNslip>0) call results_writeDataset(group,stt%accshear,'gamma_sl', & + 'plastic shear','1') end select enddo outputsLoop end associate @@ -449,10 +400,11 @@ end subroutine plastic_kinehardening_results !-------------------------------------------------------------------------------------------------- -!> @brief calculates shear rates on slip systems and derivatives with respect to resolved stress -!> @details: Shear rates are calculated only optionally. +!> @brief Calculate shear rates on slip systems and their derivatives with respect to resolved +! stress. +!> @details: Derivatives are calculated only optionally. ! NOTE: Against the common convention, the result (i.e. intent(out)) variables are the last to -! have the optional arguments at the end +! have the optional arguments at the end. !-------------------------------------------------------------------------------------------------- pure subroutine kinetics(Mp,instance,of, & gdot_pos,gdot_neg,dgdot_dtau_pos,dgdot_dtau_neg) @@ -462,44 +414,44 @@ pure subroutine kinetics(Mp,instance,of, & integer, intent(in) :: & instance, & of - + real(pReal), intent(out), dimension(param(instance)%totalNslip) :: & gdot_pos, & gdot_neg real(pReal), intent(out), optional, dimension(param(instance)%totalNslip) :: & dgdot_dtau_pos, & dgdot_dtau_neg - + real(pReal), dimension(param(instance)%totalNslip) :: & tau_pos, & tau_neg integer :: i logical :: nonSchmidActive - + associate(prm => param(instance), stt => state(instance)) - + nonSchmidActive = size(prm%nonSchmidCoeff) > 0 - + do i = 1, prm%totalNslip tau_pos(i) = math_mul33xx33(Mp,prm%nonSchmid_pos(1:3,1:3,i)) - stt%crss_back(i,of) tau_neg(i) = merge(math_mul33xx33(Mp,prm%nonSchmid_neg(1:3,1:3,i)) - stt%crss_back(i,of), & 0.0_pReal, nonSchmidActive) enddo - + where(dNeq0(tau_pos)) gdot_pos = prm%gdot0 * merge(0.5_pReal,1.0_pReal, nonSchmidActive) & ! 1/2 if non-Schmid active * sign(abs(tau_pos/stt%crss(:,of))**prm%n, tau_pos) else where gdot_pos = 0.0_pReal end where - + where(dNeq0(tau_neg)) gdot_neg = prm%gdot0 * 0.5_pReal & ! only used if non-Schmid active, always 1/2 * sign(abs(tau_neg/stt%crss(:,of))**prm%n, tau_neg) else where gdot_neg = 0.0_pReal end where - + if (present(dgdot_dtau_pos)) then where(dNeq0(gdot_pos)) dgdot_dtau_pos = gdot_pos*prm%n/tau_pos diff --git a/src/constitutive_plastic_none.f90 b/src/constitutive_plastic_none.f90 index 578fb4149..4840c9c09 100644 --- a/src/constitutive_plastic_none.f90 +++ b/src/constitutive_plastic_none.f90 @@ -18,19 +18,19 @@ module subroutine plastic_none_init Ninstance, & p, & NipcMyPhase - - write(6,'(/,a)') ' <<<+- plastic_'//PLASTICITY_NONE_label//' init -+>>>' - + + write(6,'(/,a)') ' <<<+- plastic_'//PLASTICITY_NONE_label//' init -+>>>'; flush(6) + Ninstance = count(phase_plasticity == PLASTICITY_NONE_ID) if (iand(debug_level(debug_constitutive),debug_levelBasic) /= 0) & write(6,'(a16,1x,i5,/)') '# instances:',Ninstance - + do p = 1, size(phase_plasticity) if (phase_plasticity(p) /= PLASTICITY_NONE_ID) cycle - + NipcMyPhase = count(material_phaseAt == p) * discretization_nIP call material_allocatePlasticState(p,NipcMyPhase,0,0,0) - + enddo end subroutine plastic_none_init diff --git a/src/constitutive_plastic_nonlocal.f90 b/src/constitutive_plastic_nonlocal.f90 index 3739ab669..47db19385 100644 --- a/src/constitutive_plastic_nonlocal.f90 +++ b/src/constitutive_plastic_nonlocal.f90 @@ -48,32 +48,6 @@ submodule(constitutive) plastic_nonlocal real(pReal), dimension(:,:,:,:,:,:), allocatable :: & compatibility !< slip system compatibility between me and my neighbors - enum, bind(c) - enumerator :: & - undefined_ID, & - rho_sgl_mob_edg_pos_ID, & - rho_sgl_mob_edg_neg_ID, & - rho_sgl_mob_scr_pos_ID, & - rho_sgl_mob_scr_neg_ID, & - rho_sgl_imm_edg_pos_ID, & - rho_sgl_imm_edg_neg_ID, & - rho_sgl_imm_scr_pos_ID, & - rho_sgl_imm_scr_neg_ID, & - rho_dip_edg_ID, & - rho_dip_scr_ID, & - rho_forest_ID, & - resolvedstress_back_ID, & - tau_pass_ID, & - rho_dot_sgl_ID, & - rho_dot_sgl_mobile_ID, & - rho_dot_dip_ID, & - v_edg_pos_ID, & - v_edg_neg_ID, & - v_scr_pos_ID, & - v_scr_neg_ID, & - gamma_ID - end enum - type :: tParameters !< container type for internal constitutive parameters real(pReal) :: & atomicVolume, & !< atomic volume @@ -135,14 +109,12 @@ submodule(constitutive) plastic_nonlocal integer, dimension(:) ,allocatable :: & Nslip,& colinearSystem !< colinear system to the active slip system (only valid for fcc!) - + character(len=pStringLen), allocatable, dimension(:) :: & + output logical :: & shortRangeStressCorrection, & !< flag indicating the use of the short range stress correction by a excess density gradient term probabilisticMultiplication - integer(kind(undefined_ID)), dimension(:), allocatable :: & - outputID !< ID of each post result output - end type tParameters type :: tNonlocalMicrostructure @@ -198,22 +170,19 @@ module subroutine plastic_nonlocal_init integer :: & sizeState, sizeDotState,sizeDependentState, sizeDeltaState, & maxNinstances, & - p, i, & + p, & l, & s1, s2, & s, & t, & c - integer(kind(undefined_ID)) :: & - outputID character(len=pStringLen) :: & extmsg = '', & structure - character(len=pStringLen), dimension(:), allocatable :: outputs integer :: NofMyPhase - write(6,'(/,a)') ' <<<+- constitutive_'//PLASTICITY_NONLOCAL_label//' init -+>>>' + write(6,'(/,a)') ' <<<+- constitutive_'//PLASTICITY_NONLOCAL_label//' init -+>>>'; flush(6) write(6,'(/,a)') ' Reuber et al., Acta Materialia 71:333–348, 2014' write(6,'(a)') ' https://doi.org/10.1016/j.actamat.2014.03.012' @@ -407,60 +376,7 @@ module subroutine plastic_nonlocal_init endif slipActive - outputs = config%getStrings('(output)',defaultVal=emptyStringArray) - allocate(prm%outputID(0)) - do i=1, size(outputs) - outputID = undefined_ID - select case(trim(outputs(i))) - case ('rho_sgl_mob_edg_pos') - outputID = merge(rho_sgl_mob_edg_pos_ID,undefined_ID,prm%totalNslip>0) - case ('rho_sgl_mob_edg_neg') - outputID = merge(rho_sgl_mob_edg_neg_ID,undefined_ID,prm%totalNslip>0) - case ('rho_sgl_mob_scr_pos') - outputID = merge(rho_sgl_mob_scr_pos_ID,undefined_ID,prm%totalNslip>0) - case ('rho_sgl_mob_scr_neg') - outputID = merge(rho_sgl_mob_scr_neg_ID,undefined_ID,prm%totalNslip>0) - case ('rho_sgl_imm_edg_pos') - outputID = merge(rho_sgl_imm_edg_pos_ID,undefined_ID,prm%totalNslip>0) - case ('rho_sgl_imm_edg_neg') - outputID = merge(rho_sgl_imm_edg_neg_ID,undefined_ID,prm%totalNslip>0) - case ('rho_sgl_imm_scr_pos') - outputID = merge(rho_sgl_imm_scr_pos_ID,undefined_ID,prm%totalNslip>0) - case ('rho_sgl_imm_scr_neg') - outputID = merge(rho_sgl_imm_scr_neg_ID,undefined_ID,prm%totalNslip>0) - case ('rho_dip_edg') - outputID = merge(rho_dip_edg_ID,undefined_ID,prm%totalNslip>0) - case ('rho_dip_scr') - outputID = merge(rho_dip_scr_ID,undefined_ID,prm%totalNslip>0) - case ('rho_forest') - outputID = merge(rho_forest_ID,undefined_ID,prm%totalNslip>0) - case ('resolvedstress_back') - outputID = merge(resolvedstress_back_ID,undefined_ID,prm%totalNslip>0) - case ('tau_pass') - outputID = merge(tau_pass_ID,undefined_ID,prm%totalNslip>0) - case ('rho_dot_sgl') - outputID = merge(rho_dot_sgl_ID,undefined_ID,prm%totalNslip>0) - case ('rho_dot_sgl_mobile') - outputID = merge(rho_dot_sgl_mobile_ID,undefined_ID,prm%totalNslip>0) - case ('rho_dot_dip') - outputID = merge(rho_dot_dip_ID,undefined_ID,prm%totalNslip>0) - case ('v_edg_pos') - outputID = merge(v_edg_pos_ID,undefined_ID,prm%totalNslip>0) - case ('v_edg_neg') - outputID = merge(v_edg_neg_ID,undefined_ID,prm%totalNslip>0) - case ('v_scr_pos') - outputID = merge(v_scr_pos_ID,undefined_ID,prm%totalNslip>0) - case ('v_scr_neg') - outputID = merge(v_scr_neg_ID,undefined_ID,prm%totalNslip>0) - case ('gamma') - outputID = merge(gamma_ID,undefined_ID,prm%totalNslip>0) - end select - - if (outputID /= undefined_ID) then - prm%outputID = [prm%outputID , outputID] - endif - - enddo + prm%output = config%getStrings('(output)',defaultVal=emptyStringArray) !-------------------------------------------------------------------------------------------------- ! allocate state arrays @@ -561,7 +477,7 @@ module subroutine plastic_nonlocal_init stt%v_scr_neg => plasticState(p)%state (15*prm%totalNslip + 1:16*prm%totalNslip ,1:NofMyPhase) allocate(dst%tau_pass(prm%totalNslip,NofMyPhase),source=0.0_pReal) - allocate(dst%tau_Back(prm%totalNslip,NofMyPhase), source=0.0_pReal) + allocate(dst%tau_back(prm%totalNslip,NofMyPhase),source=0.0_pReal) end associate if (NofMyPhase > 0) call stateInit(p,NofMyPhase) @@ -1968,62 +1884,63 @@ module subroutine plastic_nonlocal_results(instance,group) integer, intent(in) :: instance character(len=*),intent(in) :: group + integer :: o associate(prm => param(instance),dst => microstructure(instance),stt=>state(instance)) - outputsLoop: do o = 1,size(prm%outputID) - select case(prm%outputID(o)) - case (rho_sgl_mob_edg_pos_ID) - call results_writeDataset(group,stt%rho_sgl_mob_edg_pos, 'rho_sgl_mob_edg_pos', & - 'positive mobile edge density','1/m²') - case (rho_sgl_imm_edg_pos_ID) - call results_writeDataset(group,stt%rho_sgl_imm_edg_pos, 'rho_sgl_imm_edg_pos',& - 'positive immobile edge density','1/m²') - case (rho_sgl_mob_edg_neg_ID) - call results_writeDataset(group,stt%rho_sgl_mob_edg_neg, 'rho_sgl_mob_edg_neg',& - 'negative mobile edge density','1/m²') - case (rho_sgl_imm_edg_neg_ID) - call results_writeDataset(group,stt%rho_sgl_imm_edg_neg, 'rho_sgl_imm_edg_neg',& - 'negative immobile edge density','1/m²') - case (rho_dip_edg_ID) - call results_writeDataset(group,stt%rho_dip_edg, 'rho_dip_edg',& - 'edge dipole density','1/m²') - case (rho_sgl_mob_scr_pos_ID) - call results_writeDataset(group,stt%rho_sgl_mob_scr_pos, 'rho_sgl_mob_scr_pos',& - 'positive mobile screw density','1/m²') - case (rho_sgl_imm_scr_pos_ID) - call results_writeDataset(group,stt%rho_sgl_imm_scr_pos, 'rho_sgl_imm_scr_pos',& - 'positive immobile screw density','1/m²') - case (rho_sgl_mob_scr_neg_ID) - call results_writeDataset(group,stt%rho_sgl_mob_scr_neg, 'rho_sgl_mob_scr_neg',& - 'negative mobile screw density','1/m²') - case (rho_sgl_imm_scr_neg_ID) - call results_writeDataset(group,stt%rho_sgl_imm_scr_neg, 'rho_sgl_imm_scr_neg',& - 'negative immobile screw density','1/m²') - case (rho_dip_scr_ID) - call results_writeDataset(group,stt%rho_dip_scr, 'rho_dip_scr',& - 'screw dipole density','1/m²') - case (rho_forest_ID) - call results_writeDataset(group,stt%rho_forest, 'rho_forest',& - 'forest density','1/m²') - case (v_edg_pos_ID) - call results_writeDataset(group,stt%v_edg_pos, 'v_edg_pos',& - 'positive edge velocity','m/s') - case (v_edg_neg_ID) - call results_writeDataset(group,stt%v_edg_neg, 'v_edg_neg',& - 'negative edge velocity','m/s') - case (v_scr_pos_ID) - call results_writeDataset(group,stt%v_scr_pos, 'v_scr_pos',& - 'positive srew velocity','m/s') - case (v_scr_neg_ID) - call results_writeDataset(group,stt%v_scr_neg, 'v_scr_neg',& - 'negative screw velocity','m/s') - case(gamma_ID) - call results_writeDataset(group,stt%gamma,'gamma',& - 'plastic shear','1') - case (tau_pass_ID) - call results_writeDataset(group,dst%tau_pass,'tau_pass',& - 'passing stress for slip','Pa') + outputsLoop: do o = 1,size(prm%output) + select case(trim(prm%output(o))) + case('rho_sgl_mob_edg_pos') + if(prm%totalNslip>0) call results_writeDataset(group,stt%rho_sgl_mob_edg_pos, 'rho_sgl_mob_edg_pos', & + 'positive mobile edge density','1/m²') + case('rho_sgl_imm_edg_pos') + if(prm%totalNslip>0) call results_writeDataset(group,stt%rho_sgl_imm_edg_pos, 'rho_sgl_imm_edg_pos',& + 'positive immobile edge density','1/m²') + case('rho_sgl_mob_edg_neg') + if(prm%totalNslip>0) call results_writeDataset(group,stt%rho_sgl_mob_edg_neg, 'rho_sgl_mob_edg_neg',& + 'negative mobile edge density','1/m²') + case('rho_sgl_imm_edg_neg') + if(prm%totalNslip>0) call results_writeDataset(group,stt%rho_sgl_imm_edg_neg, 'rho_sgl_imm_edg_neg',& + 'negative immobile edge density','1/m²') + case('rho_dip_edg') + if(prm%totalNslip>0) call results_writeDataset(group,stt%rho_dip_edg, 'rho_dip_edg',& + 'edge dipole density','1/m²') + case('rho_sgl_mob_scr_pos') + if(prm%totalNslip>0) call results_writeDataset(group,stt%rho_sgl_mob_scr_pos, 'rho_sgl_mob_scr_pos',& + 'positive mobile screw density','1/m²') + case('rho_sgl_imm_scr_pos') + if(prm%totalNslip>0) call results_writeDataset(group,stt%rho_sgl_imm_scr_pos, 'rho_sgl_imm_scr_pos',& + 'positive immobile screw density','1/m²') + case('rho_sgl_mob_scr_neg') + if(prm%totalNslip>0) call results_writeDataset(group,stt%rho_sgl_mob_scr_neg, 'rho_sgl_mob_scr_neg',& + 'negative mobile screw density','1/m²') + case('rho_sgl_imm_scr_neg') + if(prm%totalNslip>0) call results_writeDataset(group,stt%rho_sgl_imm_scr_neg, 'rho_sgl_imm_scr_neg',& + 'negative immobile screw density','1/m²') + case('rho_dip_scr') + if(prm%totalNslip>0) call results_writeDataset(group,stt%rho_dip_scr, 'rho_dip_scr',& + 'screw dipole density','1/m²') + case('rho_forest') + if(prm%totalNslip>0) call results_writeDataset(group,stt%rho_forest, 'rho_forest',& + 'forest density','1/m²') + case('v_edg_pos') + if(prm%totalNslip>0) call results_writeDataset(group,stt%v_edg_pos, 'v_edg_pos',& + 'positive edge velocity','m/s') + case('v_edg_neg') + if(prm%totalNslip>0) call results_writeDataset(group,stt%v_edg_neg, 'v_edg_neg',& + 'negative edge velocity','m/s') + case('v_scr_pos') + if(prm%totalNslip>0) call results_writeDataset(group,stt%v_scr_pos, 'v_scr_pos',& + 'positive srew velocity','m/s') + case('v_scr_neg') + if(prm%totalNslip>0) call results_writeDataset(group,stt%v_scr_neg, 'v_scr_neg',& + 'negative screw velocity','m/s') + case('gamma') + if(prm%totalNslip>0) call results_writeDataset(group,stt%gamma,'gamma',& + 'plastic shear','1') + case('tau_pass') + if(prm%totalNslip>0) call results_writeDataset(group,dst%tau_pass,'tau_pass',& + 'passing stress for slip','Pa') end select enddo outputsLoop end associate diff --git a/src/constitutive_plastic_phenopowerlaw.f90 b/src/constitutive_plastic_phenopowerlaw.f90 index 165ad0081..6dfa3fb16 100644 --- a/src/constitutive_plastic_phenopowerlaw.f90 +++ b/src/constitutive_plastic_phenopowerlaw.f90 @@ -6,19 +6,6 @@ !-------------------------------------------------------------------------------------------------- submodule(constitutive) plastic_phenopowerlaw - enum, bind(c) - enumerator :: & - undefined_ID, & - resistance_slip_ID, & - accumulatedshear_slip_ID, & - shearrate_slip_ID, & - resolvedstress_slip_ID, & - resistance_twin_ID, & - accumulatedshear_twin_ID, & - shearrate_twin_ID, & - resolvedstress_twin_ID - end enum - type :: tParameters real(pReal) :: & gdot0_slip, & !< reference shear strain rate for slip @@ -37,19 +24,19 @@ submodule(constitutive) plastic_phenopowerlaw aTolResistance, & !< absolute tolerance for integration of xi aTolShear, & !< absolute tolerance for integration of gamma aTolTwinfrac !< absolute tolerance for integration of f - real(pReal), allocatable, dimension(:) :: & + real(pReal), allocatable, dimension(:) :: & xi_slip_0, & !< initial critical shear stress for slip xi_twin_0, & !< initial critical shear stress for twin xi_slip_sat, & !< maximum critical shear stress for slip nonSchmidCoeff, & H_int, & !< per family hardening activity (optional) gamma_twin_char !< characteristic shear for twins - real(pReal), allocatable, dimension(:,:) :: & + real(pReal), allocatable, dimension(:,:) :: & interaction_SlipSlip, & !< slip resistance from slip activity interaction_SlipTwin, & !< slip resistance from twin activity interaction_TwinSlip, & !< twin resistance from slip activity interaction_TwinTwin !< twin resistance from twin activity - real(pReal), allocatable, dimension(:,:,:) :: & + real(pReal), allocatable, dimension(:,:,:) :: & Schmid_slip, & Schmid_twin, & nonSchmid_pos, & @@ -57,13 +44,13 @@ submodule(constitutive) plastic_phenopowerlaw integer :: & totalNslip, & !< total number of active slip system totalNtwin !< total number of active twin systems - integer, allocatable, dimension(:) :: & + integer, allocatable, dimension(:) :: & Nslip, & !< number of active slip systems for each family Ntwin !< number of active twin systems for each family - integer(kind(undefined_ID)), allocatable, dimension(:) :: & - outputID !< ID of each post result output + character(len=pStringLen), allocatable, dimension(:) :: & + output end type tParameters - + type :: tPhenopowerlawState real(pReal), pointer, dimension(:,:) :: & xi_slip, & @@ -71,7 +58,7 @@ submodule(constitutive) plastic_phenopowerlaw gamma_slip, & gamma_twin end type tPhenopowerlawState - + !-------------------------------------------------------------------------------------------------- ! containers for parameters and state type(tParameters), allocatable, dimension(:) :: param @@ -83,7 +70,7 @@ contains !-------------------------------------------------------------------------------------------------- -!> @brief module initialization +!> @brief Perform module initialization. !> @details reads in material parameters, allocates arrays, and does sanity checks !-------------------------------------------------------------------------------------------------- module subroutine plastic_phenopowerlaw_init @@ -91,51 +78,46 @@ module subroutine plastic_phenopowerlaw_init integer :: & Ninstance, & p, i, & - NipcMyPhase, outputSize, & + NipcMyPhase, & sizeState, sizeDotState, & startIndex, endIndex - - integer(kind(undefined_ID)) :: & - outputID - + character(len=pStringLen) :: & extmsg = '' - character(len=pStringLen), dimension(:), allocatable :: & - outputs - - write(6,'(/,a)') ' <<<+- plastic_'//PLASTICITY_PHENOPOWERLAW_label//' init -+>>>' - + + write(6,'(/,a)') ' <<<+- plastic_'//PLASTICITY_PHENOPOWERLAW_label//' init -+>>>'; flush(6) + Ninstance = count(phase_plasticity == PLASTICITY_PHENOPOWERLAW_ID) if (iand(debug_level(debug_constitutive),debug_levelBasic) /= 0) & write(6,'(a16,1x,i5,/)') '# instances:',Ninstance - + allocate(param(Ninstance)) allocate(state(Ninstance)) allocate(dotState(Ninstance)) - + do p = 1, size(phase_plasticity) if (phase_plasticity(p) /= PLASTICITY_PHENOPOWERLAW_ID) cycle associate(prm => param(phase_plasticityInstance(p)), & dot => dotState(phase_plasticityInstance(p)), & stt => state(phase_plasticityInstance(p)), & config => config_phase(p)) - + !-------------------------------------------------------------------------------------------------- ! optional parameters that need to be defined prm%c_1 = config%getFloat('twin_c',defaultVal=0.0_pReal) prm%c_2 = config%getFloat('twin_b',defaultVal=1.0_pReal) prm%c_3 = config%getFloat('twin_e',defaultVal=0.0_pReal) prm%c_4 = config%getFloat('twin_d',defaultVal=0.0_pReal) - + prm%aTolResistance = config%getFloat('atol_resistance',defaultVal=1.0_pReal) prm%aTolShear = config%getFloat('atol_shear', defaultVal=1.0e-6_pReal) prm%aTolTwinfrac = config%getFloat('atol_twinfrac', defaultVal=1.0e-6_pReal) - + ! sanity checks if (prm%aTolResistance <= 0.0_pReal) extmsg = trim(extmsg)//' aTolresistance' if (prm%aTolShear <= 0.0_pReal) extmsg = trim(extmsg)//' aTolShear' if (prm%aTolTwinfrac <= 0.0_pReal) extmsg = trim(extmsg)//' atoltwinfrac' - + !-------------------------------------------------------------------------------------------------- ! slip related parameters prm%Nslip = config%getInts('nslip',defaultVal=emptyIntArray) @@ -143,7 +125,7 @@ module subroutine plastic_phenopowerlaw_init slipActive: if (prm%totalNslip > 0) then prm%Schmid_slip = lattice_SchmidMatrix_slip(prm%Nslip,config%getString('lattice_structure'),& config%getFloat('c/a',defaultVal=0.0_pReal)) - + if(trim(config%getString('lattice_structure')) == 'bcc') then prm%nonSchmidCoeff = config%getFloats('nonschmid_coefficients',& defaultVal = emptyRealArray) @@ -157,22 +139,22 @@ module subroutine plastic_phenopowerlaw_init prm%interaction_SlipSlip = lattice_interaction_SlipBySlip(prm%Nslip, & config%getFloats('interaction_slipslip'), & config%getString('lattice_structure')) - + prm%xi_slip_0 = config%getFloats('tau0_slip', requiredSize=size(prm%Nslip)) prm%xi_slip_sat = config%getFloats('tausat_slip', requiredSize=size(prm%Nslip)) prm%H_int = config%getFloats('h_int', requiredSize=size(prm%Nslip), & defaultVal=[(0.0_pReal,i=1,size(prm%Nslip))]) - + prm%gdot0_slip = config%getFloat('gdot0_slip') prm%n_slip = config%getFloat('n_slip') prm%a_slip = config%getFloat('a_slip') prm%h0_SlipSlip = config%getFloat('h0_slipslip') - + ! expand: family => system prm%xi_slip_0 = math_expand(prm%xi_slip_0, prm%Nslip) prm%xi_slip_sat = math_expand(prm%xi_slip_sat,prm%Nslip) prm%H_int = math_expand(prm%H_int, prm%Nslip) - + ! sanity checks if ( prm%gdot0_slip <= 0.0_pReal) extmsg = trim(extmsg)//' gdot0_slip' if ( prm%a_slip <= 0.0_pReal) extmsg = trim(extmsg)//' a_slip' @@ -183,7 +165,7 @@ module subroutine plastic_phenopowerlaw_init allocate(prm%interaction_SlipSlip(0,0)) allocate(prm%xi_slip_0(0)) endif slipActive - + !-------------------------------------------------------------------------------------------------- ! twin related parameters prm%Ntwin = config%getInts('ntwin', defaultVal=emptyIntArray) @@ -196,17 +178,17 @@ module subroutine plastic_phenopowerlaw_init config%getString('lattice_structure')) prm%gamma_twin_char = lattice_characteristicShear_twin(prm%Ntwin,config%getString('lattice_structure'),& config%getFloat('c/a')) - + prm%xi_twin_0 = config%getFloats('tau0_twin',requiredSize=size(prm%Ntwin)) - + prm%gdot0_twin = config%getFloat('gdot0_twin') prm%n_twin = config%getFloat('n_twin') prm%spr = config%getFloat('s_pr') prm%h0_TwinTwin = config%getFloat('h0_twintwin') - + ! expand: family => system prm%xi_twin_0 = math_expand(prm%xi_twin_0, prm%Ntwin) - + ! sanity checks if (prm%gdot0_twin <= 0.0_pReal) extmsg = trim(extmsg)//' gdot0_twin' if (prm%n_twin <= 0.0_pReal) extmsg = trim(extmsg)//' n_twin' @@ -215,7 +197,7 @@ module subroutine plastic_phenopowerlaw_init allocate(prm%xi_twin_0(0)) allocate(prm%gamma_twin_char(0)) endif twinActive - + !-------------------------------------------------------------------------------------------------- ! slip-twin related parameters slipAndTwinActive: if (prm%totalNslip > 0 .and. prm%totalNtwin > 0) then @@ -231,63 +213,25 @@ module subroutine plastic_phenopowerlaw_init allocate(prm%interaction_TwinSlip(prm%TotalNtwin,prm%TotalNslip)) ! at least one dimension is 0 prm%h0_TwinSlip = 0.0_pReal endif slipAndTwinActive - + !-------------------------------------------------------------------------------------------------- ! exit if any parameter is out of range if (extmsg /= '') & call IO_error(211,ext_msg=trim(extmsg)//'('//PLASTICITY_PHENOPOWERLAW_label//')') - + !-------------------------------------------------------------------------------------------------- ! output pararameters - outputs = config%getStrings('(output)',defaultVal=emptyStringArray) - allocate(prm%outputID(0)) - do i=1, size(outputs) - outputID = undefined_ID - select case(outputs(i)) - - case ('resistance_slip') - outputID = merge(resistance_slip_ID,undefined_ID,prm%totalNslip>0) - outputSize = prm%totalNslip - case ('accumulatedshear_slip') - outputID = merge(accumulatedshear_slip_ID,undefined_ID,prm%totalNslip>0) - outputSize = prm%totalNslip - case ('shearrate_slip') - outputID = merge(shearrate_slip_ID,undefined_ID,prm%totalNslip>0) - outputSize = prm%totalNslip - case ('resolvedstress_slip') - outputID = merge(resolvedstress_slip_ID,undefined_ID,prm%totalNslip>0) - outputSize = prm%totalNslip - - case ('resistance_twin') - outputID = merge(resistance_twin_ID,undefined_ID,prm%totalNtwin>0) - outputSize = prm%totalNtwin - case ('accumulatedshear_twin') - outputID = merge(accumulatedshear_twin_ID,undefined_ID,prm%totalNtwin>0) - outputSize = prm%totalNtwin - case ('shearrate_twin') - outputID = merge(shearrate_twin_ID,undefined_ID,prm%totalNtwin>0) - outputSize = prm%totalNtwin - case ('resolvedstress_twin') - outputID = merge(resolvedstress_twin_ID,undefined_ID,prm%totalNtwin>0) - outputSize = prm%totalNtwin - - end select - - if (outputID /= undefined_ID) then - prm%outputID = [prm%outputID, outputID] - endif - - enddo - + prm%output = config%getStrings('(output)',defaultVal=emptyStringArray) + !-------------------------------------------------------------------------------------------------- ! allocate state arrays NipcMyPhase = count(material_phaseAt == p) * discretization_nIP sizeDotState = size(['tau_slip ','gamma_slip']) * prm%totalNslip & + size(['tau_twin ','gamma_twin']) * prm%totalNtwin sizeState = sizeDotState - + call material_allocatePlasticState(p,NipcMyPhase,sizeState,sizeDotState,0) - + !-------------------------------------------------------------------------------------------------- ! locally defined state aliases and initialization of state0 and aTolState startIndex = 1 @@ -296,14 +240,14 @@ module subroutine plastic_phenopowerlaw_init stt%xi_slip = spread(prm%xi_slip_0, 2, NipcMyPhase) dot%xi_slip => plasticState(p)%dotState(startIndex:endIndex,:) plasticState(p)%aTolState(startIndex:endIndex) = prm%aTolResistance - + startIndex = endIndex + 1 endIndex = endIndex + prm%totalNtwin stt%xi_twin => plasticState(p)%state (startIndex:endIndex,:) stt%xi_twin = spread(prm%xi_twin_0, 2, NipcMyPhase) dot%xi_twin => plasticState(p)%dotState(startIndex:endIndex,:) plasticState(p)%aTolState(startIndex:endIndex) = prm%aTolResistance - + startIndex = endIndex + 1 endIndex = endIndex + prm%totalNslip stt%gamma_slip => plasticState(p)%state (startIndex:endIndex,:) @@ -317,18 +261,18 @@ module subroutine plastic_phenopowerlaw_init stt%gamma_twin => plasticState(p)%state (startIndex:endIndex,:) dot%gamma_twin => plasticState(p)%dotState(startIndex:endIndex,:) plasticState(p)%aTolState(startIndex:endIndex) = prm%aTolShear - + plasticState(p)%state0 = plasticState(p)%state ! ToDo: this could be done centrally - + end associate - + enddo end subroutine plastic_phenopowerlaw_init !-------------------------------------------------------------------------------------------------- -!> @brief calculates plastic velocity gradient and its tangent +!> @brief Calculate plastic velocity gradient and its tangent. !> @details asummes that deformation by dislocation glide affects twinned and untwinned volume ! equally (Taylor assumption). Twinning happens only in untwinned volume !-------------------------------------------------------------------------------------------------- @@ -338,13 +282,13 @@ pure module subroutine plastic_phenopowerlaw_LpAndItsTangent(Lp,dLp_dMp,Mp,insta Lp !< plastic velocity gradient real(pReal), dimension(3,3,3,3), intent(out) :: & dLp_dMp !< derivative of Lp with respect to the Mandel stress - + real(pReal), dimension(3,3), intent(in) :: & Mp !< Mandel stress integer, intent(in) :: & instance, & of - + integer :: & i,k,l,m,n real(pReal), dimension(param(instance)%totalNslip) :: & @@ -352,12 +296,12 @@ pure module subroutine plastic_phenopowerlaw_LpAndItsTangent(Lp,dLp_dMp,Mp,insta dgdot_dtauslip_pos,dgdot_dtauslip_neg real(pReal), dimension(param(instance)%totalNtwin) :: & gdot_twin,dgdot_dtautwin - + Lp = 0.0_pReal dLp_dMp = 0.0_pReal - + associate(prm => param(instance)) - + call kinetics_slip(Mp,instance,of,gdot_slip_pos,gdot_slip_neg,dgdot_dtauslip_pos,dgdot_dtauslip_neg) slipSystems: do i = 1, prm%totalNslip Lp = Lp + (gdot_slip_pos(i)+gdot_slip_neg(i))*prm%Schmid_slip(1:3,1:3,i) @@ -366,7 +310,7 @@ pure module subroutine plastic_phenopowerlaw_LpAndItsTangent(Lp,dLp_dMp,Mp,insta + dgdot_dtauslip_pos(i) * prm%Schmid_slip(k,l,i) * prm%nonSchmid_pos(m,n,i) & + dgdot_dtauslip_neg(i) * prm%Schmid_slip(k,l,i) * prm%nonSchmid_neg(m,n,i) enddo slipSystems - + call kinetics_twin(Mp,instance,of,gdot_twin,dgdot_dtautwin) twinSystems: do i = 1, prm%totalNtwin Lp = Lp + gdot_twin(i)*prm%Schmid_twin(1:3,1:3,i) @@ -374,14 +318,14 @@ pure module subroutine plastic_phenopowerlaw_LpAndItsTangent(Lp,dLp_dMp,Mp,insta dLp_dMp(k,l,m,n) = dLp_dMp(k,l,m,n) & + dgdot_dtautwin(i)*prm%Schmid_twin(k,l,i)*prm%Schmid_twin(m,n,i) enddo twinSystems - + end associate end subroutine plastic_phenopowerlaw_LpAndItsTangent !-------------------------------------------------------------------------------------------------- -!> @brief calculates the rate of change of microstructure +!> @brief Calculate the rate of change of microstructure. !-------------------------------------------------------------------------------------------------- module subroutine plastic_phenopowerlaw_dotState(Mp,instance,of) @@ -390,7 +334,7 @@ module subroutine plastic_phenopowerlaw_dotState(Mp,instance,of) integer, intent(in) :: & instance, & of - + real(pReal) :: & c_SlipSlip,c_TwinSlip,c_TwinTwin, & xi_slip_sat_offset,& @@ -398,9 +342,9 @@ module subroutine plastic_phenopowerlaw_dotState(Mp,instance,of) real(pReal), dimension(param(instance)%totalNslip) :: & left_SlipSlip,right_SlipSlip, & gdot_slip_pos,gdot_slip_neg - + associate(prm => param(instance), stt => state(instance), dot => dotState(instance)) - + sumGamma = sum(stt%gamma_slip(:,of)) sumF = sum(stt%gamma_twin(:,of)/prm%gamma_twin_char) @@ -409,26 +353,26 @@ module subroutine plastic_phenopowerlaw_dotState(Mp,instance,of) c_SlipSlip = prm%h0_slipslip * (1.0_pReal + prm%c_1*sumF** prm%c_2) c_TwinSlip = prm%h0_TwinSlip * sumGamma**prm%c_3 c_TwinTwin = prm%h0_TwinTwin * sumF**prm%c_4 - + !-------------------------------------------------------------------------------------------------- ! calculate left and right vectors left_SlipSlip = 1.0_pReal + prm%H_int xi_slip_sat_offset = prm%spr*sqrt(sumF) right_SlipSlip = abs(1.0_pReal-stt%xi_slip(:,of) / (prm%xi_slip_sat+xi_slip_sat_offset)) **prm%a_slip & * sign(1.0_pReal,1.0_pReal-stt%xi_slip(:,of) / (prm%xi_slip_sat+xi_slip_sat_offset)) - + !-------------------------------------------------------------------------------------------------- ! shear rates call kinetics_slip(Mp,instance,of,gdot_slip_pos,gdot_slip_neg) dot%gamma_slip(:,of) = abs(gdot_slip_pos+gdot_slip_neg) call kinetics_twin(Mp,instance,of,dot%gamma_twin(:,of)) - + !-------------------------------------------------------------------------------------------------- ! hardening dot%xi_slip(:,of) = c_SlipSlip * left_SlipSlip * & matmul(prm%interaction_SlipSlip,dot%gamma_slip(:,of)*right_SlipSlip) & + matmul(prm%interaction_SlipTwin,dot%gamma_twin(:,of)) - + dot%xi_twin(:,of) = c_TwinSlip * matmul(prm%interaction_TwinSlip,dot%gamma_slip(:,of)) & + c_TwinTwin * matmul(prm%interaction_TwinTwin,dot%gamma_twin(:,of)) end associate @@ -437,33 +381,33 @@ end subroutine plastic_phenopowerlaw_dotState !-------------------------------------------------------------------------------------------------- -!> @brief writes results to HDF5 output file +!> @brief Write results to HDF5 output file. !-------------------------------------------------------------------------------------------------- module subroutine plastic_phenopowerlaw_results(instance,group) integer, intent(in) :: instance character(len=*), intent(in) :: group - + integer :: o associate(prm => param(instance), stt => state(instance)) - outputsLoop: do o = 1,size(prm%outputID) - select case(prm%outputID(o)) + outputsLoop: do o = 1,size(prm%output) + select case(trim(prm%output(o))) + + case('resistance_slip') + if(prm%totalNslip>0) call results_writeDataset(group,stt%xi_slip, 'xi_sl', & + 'resistance against plastic slip','Pa') + case('accumulatedshear_slip') + if(prm%totalNslip>0) call results_writeDataset(group,stt%gamma_slip,'gamma_sl', & + 'plastic shear','1') + + case('resistance_twin') + if(prm%totalNtwin>0) call results_writeDataset(group,stt%xi_twin, 'xi_tw', & + 'resistance against twinning','Pa') + case('accumulatedshear_twin') + if(prm%totalNtwin>0) call results_writeDataset(group,stt%gamma_twin,'gamma_tw', & + 'twinning shear','1') - case (resistance_slip_ID) - call results_writeDataset(group,stt%xi_slip, 'xi_sl', & - 'resistance against plastic slip','Pa') - case (accumulatedshear_slip_ID) - call results_writeDataset(group,stt%gamma_slip,'gamma_sl', & - 'plastic shear','1') - - case (resistance_twin_ID) - call results_writeDataset(group,stt%xi_twin, 'xi_tw', & - 'resistance against twinning','Pa') - case (accumulatedshear_twin_ID) - call results_writeDataset(group,stt%gamma_twin,'gamma_tw', & - 'twinning shear','1') - end select enddo outputsLoop end associate @@ -472,10 +416,11 @@ end subroutine plastic_phenopowerlaw_results !-------------------------------------------------------------------------------------------------- -!> @brief Shear rates on slip systems and their derivatives with respect to resolved stress +!> @brief Calculate shear rates on slip systems and their derivatives with respect to resolved +! stress. !> @details Derivatives are calculated only optionally. ! NOTE: Against the common convention, the result (i.e. intent(out)) variables are the last to -! have the optional arguments at the end +! have the optional arguments at the end. !-------------------------------------------------------------------------------------------------- pure subroutine kinetics_slip(Mp,instance,of, & gdot_slip_pos,gdot_slip_neg,dgdot_dtau_slip_pos,dgdot_dtau_slip_neg) @@ -485,44 +430,44 @@ pure subroutine kinetics_slip(Mp,instance,of, & integer, intent(in) :: & instance, & of - + real(pReal), intent(out), dimension(param(instance)%totalNslip) :: & gdot_slip_pos, & gdot_slip_neg real(pReal), intent(out), optional, dimension(param(instance)%totalNslip) :: & dgdot_dtau_slip_pos, & dgdot_dtau_slip_neg - + real(pReal), dimension(param(instance)%totalNslip) :: & tau_slip_pos, & tau_slip_neg integer :: i logical :: nonSchmidActive - + associate(prm => param(instance), stt => state(instance)) - + nonSchmidActive = size(prm%nonSchmidCoeff) > 0 - + do i = 1, prm%totalNslip tau_slip_pos(i) = math_mul33xx33(Mp,prm%nonSchmid_pos(1:3,1:3,i)) tau_slip_neg(i) = merge(math_mul33xx33(Mp,prm%nonSchmid_neg(1:3,1:3,i)), & 0.0_pReal, nonSchmidActive) enddo - + where(dNeq0(tau_slip_pos)) gdot_slip_pos = prm%gdot0_slip * merge(0.5_pReal,1.0_pReal, nonSchmidActive) & ! 1/2 if non-Schmid active * sign(abs(tau_slip_pos/stt%xi_slip(:,of))**prm%n_slip, tau_slip_pos) else where gdot_slip_pos = 0.0_pReal end where - + where(dNeq0(tau_slip_neg)) gdot_slip_neg = prm%gdot0_slip * 0.5_pReal & ! only used if non-Schmid active, always 1/2 * sign(abs(tau_slip_neg/stt%xi_slip(:,of))**prm%n_slip, tau_slip_neg) else where gdot_slip_neg = 0.0_pReal end where - + if (present(dgdot_dtau_slip_pos)) then where(dNeq0(gdot_slip_pos)) dgdot_dtau_slip_pos = gdot_slip_pos*prm%n_slip/tau_slip_pos @@ -543,9 +488,9 @@ end subroutine kinetics_slip !-------------------------------------------------------------------------------------------------- -!> @brief Shear rates on twin systems and their derivatives with respect to resolved stress. -! twinning is assumed to take place only in untwinned volume. -!> @details Derivates are calculated only optionally. +!> @brief Calculate shear rates on twin systems and their derivatives with respect to resolved +! stress. Twinning is assumed to take place only in untwinned volume. +!> @details Derivatives are calculated only optionally. ! NOTE: Against the common convention, the result (i.e. intent(out)) variables are the last to ! have the optional arguments at the end. !-------------------------------------------------------------------------------------------------- @@ -557,29 +502,29 @@ pure subroutine kinetics_twin(Mp,instance,of,& integer, intent(in) :: & instance, & of - + real(pReal), dimension(param(instance)%totalNtwin), intent(out) :: & gdot_twin real(pReal), dimension(param(instance)%totalNtwin), intent(out), optional :: & dgdot_dtau_twin - + real(pReal), dimension(param(instance)%totalNtwin) :: & tau_twin integer :: i - + associate(prm => param(instance), stt => state(instance)) - + do i = 1, prm%totalNtwin tau_twin(i) = math_mul33xx33(Mp,prm%Schmid_twin(1:3,1:3,i)) enddo - + where(tau_twin > 0.0_pReal) gdot_twin = (1.0_pReal-sum(stt%gamma_twin(:,of)/prm%gamma_twin_char)) & ! only twin in untwinned volume fraction * prm%gdot0_twin*(abs(tau_twin)/stt%xi_twin(:,of))**prm%n_twin else where gdot_twin = 0.0_pReal end where - + if (present(dgdot_dtau_twin)) then where(dNeq0(gdot_twin)) dgdot_dtau_twin = gdot_twin*prm%n_twin/tau_twin @@ -587,7 +532,7 @@ pure subroutine kinetics_twin(Mp,instance,of,& dgdot_dtau_twin = 0.0_pReal end where endif - + end associate end subroutine kinetics_twin diff --git a/src/crystallite.f90 b/src/crystallite.f90 index c01f9fa67..1a8252cce 100644 --- a/src/crystallite.f90 +++ b/src/crystallite.f90 @@ -57,9 +57,7 @@ module crystallite crystallite_Li0, & !< intermediate velocitiy grad at start of FE inc crystallite_partionedLi0 !< intermediate velocity grad at start of homog inc real(pReal), dimension(:,:,:,:,:), allocatable :: & - crystallite_invFp, & !< inverse of current plastic def grad (end of converged time step) crystallite_subFp0,& !< plastic def grad at start of crystallite inc - crystallite_invFi, & !< inverse of current intermediate def grad (end of converged time step) crystallite_subFi0,& !< intermediate def grad at start of crystallite inc crystallite_subF, & !< def grad to be reached at end of crystallite inc crystallite_subF0, & !< def grad at start of crystallite inc @@ -145,12 +143,10 @@ subroutine crystallite_init allocate(crystallite_partionedFp0(3,3,cMax,iMax,eMax), source=0.0_pReal) allocate(crystallite_subFp0(3,3,cMax,iMax,eMax), source=0.0_pReal) allocate(crystallite_Fp(3,3,cMax,iMax,eMax), source=0.0_pReal) - allocate(crystallite_invFp(3,3,cMax,iMax,eMax), source=0.0_pReal) allocate(crystallite_Fi0(3,3,cMax,iMax,eMax), source=0.0_pReal) allocate(crystallite_partionedFi0(3,3,cMax,iMax,eMax), source=0.0_pReal) allocate(crystallite_subFi0(3,3,cMax,iMax,eMax), source=0.0_pReal) allocate(crystallite_Fi(3,3,cMax,iMax,eMax), source=0.0_pReal) - allocate(crystallite_invFi(3,3,cMax,iMax,eMax), source=0.0_pReal) allocate(crystallite_Fe(3,3,cMax,iMax,eMax), source=0.0_pReal) allocate(crystallite_Lp0(3,3,cMax,iMax,eMax), source=0.0_pReal) allocate(crystallite_partionedLp0(3,3,cMax,iMax,eMax), source=0.0_pReal) @@ -408,9 +404,7 @@ function crystallite_stress(dummyArgumentToPreventInternalCompilerErrorWithGCC) else crystallite_subStep(c,i,e) = num%subStepSizeCryst * crystallite_subStep(c,i,e) crystallite_Fp (1:3,1:3,c,i,e) = crystallite_subFp0(1:3,1:3,c,i,e) - crystallite_invFp(1:3,1:3,c,i,e) = math_inv33(crystallite_Fp (1:3,1:3,c,i,e)) crystallite_Fi (1:3,1:3,c,i,e) = crystallite_subFi0(1:3,1:3,c,i,e) - crystallite_invFi(1:3,1:3,c,i,e) = math_inv33(crystallite_Fi (1:3,1:3,c,i,e)) crystallite_S (1:3,1:3,c,i,e) = crystallite_S0 (1:3,1:3,c,i,e) if (crystallite_subStep(c,i,e) < 1.0_pReal) then ! actual (not initial) cutback crystallite_Lp (1:3,1:3,c,i,e) = crystallite_subLp0(1:3,1:3,c,i,e) @@ -431,11 +425,11 @@ function crystallite_stress(dummyArgumentToPreventInternalCompilerErrorWithGCC) ! prepare for integration if (crystallite_todo(c,i,e)) then crystallite_subF(1:3,1:3,c,i,e) = crystallite_subF0(1:3,1:3,c,i,e) & - + crystallite_subStep(c,i,e) * (crystallite_partionedF (1:3,1:3,c,i,e) & - - crystallite_partionedF0(1:3,1:3,c,i,e)) - crystallite_Fe(1:3,1:3,c,i,e) = matmul(matmul(crystallite_subF (1:3,1:3,c,i,e), & - crystallite_invFp(1:3,1:3,c,i,e)), & - crystallite_invFi(1:3,1:3,c,i,e)) + + crystallite_subStep(c,i,e) *( crystallite_partionedF (1:3,1:3,c,i,e) & + -crystallite_partionedF0(1:3,1:3,c,i,e)) + crystallite_Fe(1:3,1:3,c,i,e) = matmul(matmul(crystallite_subF(1:3,1:3,c,i,e), & + math_inv33(crystallite_Fp(1:3,1:3,c,i,e))), & + math_inv33(crystallite_Fi(1:3,1:3,c,i,e))) crystallite_subdt(c,i,e) = crystallite_subStep(c,i,e) * crystallite_dt(c,i,e) crystallite_converged(c,i,e) = .false. endif @@ -477,7 +471,9 @@ subroutine crystallite_stressTangent o, & p - real(pReal), dimension(3,3) :: temp_33_1, devNull,invSubFi0, temp_33_2, temp_33_3, temp_33_4 + real(pReal), dimension(3,3) :: devNull, & + invSubFp0,invSubFi0,invFp,invFi, & + temp_33_1, temp_33_2, temp_33_3, temp_33_4 real(pReal), dimension(3,3,3,3) :: dSdFe, & dSdF, & dSdFi, & @@ -493,7 +489,8 @@ subroutine crystallite_stressTangent real(pReal), dimension(9,9):: temp_99 logical :: error - !$OMP PARALLEL DO PRIVATE(dSdF,dSdFe,dSdFi,dLpdS,dLpdFi,dFpinvdF,dLidS,dLidFi,dFidS,invSubFi0,o,p, & + !$OMP PARALLEL DO PRIVATE(dSdF,dSdFe,dSdFi,dLpdS,dLpdFi,dFpinvdF,dLidS,dLidFi,dFidS,o,p, & + !$OMP invSubFp0,invSubFi0,invFp,invFi, & !$OMP rhs_3333,lhs_3333,temp_99,temp_33_1,temp_33_2,temp_33_3,temp_33_4,temp_3333,error) elementLooping: do e = FEsolving_execElem(1),FEsolving_execElem(2) do i = FEsolving_execIP(1),FEsolving_execIP(2) @@ -507,16 +504,20 @@ subroutine crystallite_stressTangent crystallite_Fi(1:3,1:3,c,i,e), & c,i,e) + invFp = math_inv33(crystallite_Fp(1:3,1:3,c,i,e)) + invFi = math_inv33(crystallite_Fi(1:3,1:3,c,i,e)) + invSubFp0 = math_inv33(crystallite_subFp0(1:3,1:3,c,i,e)) + invSubFi0 = math_inv33(crystallite_subFi0(1:3,1:3,c,i,e)) + if (sum(abs(dLidS)) < tol_math_check) then dFidS = 0.0_pReal else - invSubFi0 = math_inv33(crystallite_subFi0(1:3,1:3,c,i,e)) lhs_3333 = 0.0_pReal; rhs_3333 = 0.0_pReal do o=1,3; do p=1,3 lhs_3333(1:3,1:3,o,p) = lhs_3333(1:3,1:3,o,p) & + crystallite_subdt(c,i,e)*matmul(invSubFi0,dLidFi(1:3,1:3,o,p)) lhs_3333(1:3,o,1:3,p) = lhs_3333(1:3,o,1:3,p) & - + crystallite_invFi(1:3,1:3,c,i,e)*crystallite_invFi(p,o,c,i,e) + + invFi*invFi(p,o) rhs_3333(1:3,1:3,o,p) = rhs_3333(1:3,1:3,o,p) & - crystallite_subdt(c,i,e)*matmul(invSubFi0,dLidS(1:3,1:3,o,p)) enddo; enddo @@ -538,18 +539,13 @@ subroutine crystallite_stressTangent !-------------------------------------------------------------------------------------------------- ! calculate dSdF - temp_33_1 = transpose(matmul(crystallite_invFp(1:3,1:3,c,i,e), & - crystallite_invFi(1:3,1:3,c,i,e))) - temp_33_2 = matmul(crystallite_subF(1:3,1:3,c,i,e), & - math_inv33(crystallite_subFp0(1:3,1:3,c,i,e))) - temp_33_3 = matmul(matmul(crystallite_subF(1:3,1:3,c,i,e), & - crystallite_invFp (1:3,1:3,c,i,e)), & - math_inv33(crystallite_subFi0(1:3,1:3,c,i,e))) + temp_33_1 = transpose(matmul(invFp,invFi)) + temp_33_2 = matmul(crystallite_subF(1:3,1:3,c,i,e),invSubFp0) + temp_33_3 = matmul(matmul(crystallite_subF(1:3,1:3,c,i,e),invFp), invSubFi0) do o=1,3; do p=1,3 rhs_3333(p,o,1:3,1:3) = matmul(dSdFe(p,o,1:3,1:3),temp_33_1) - temp_3333(1:3,1:3,p,o) = matmul(matmul(temp_33_2,dLpdS(1:3,1:3,p,o)), & - crystallite_invFi(1:3,1:3,c,i,e)) & + temp_3333(1:3,1:3,p,o) = matmul(matmul(temp_33_2,dLpdS(1:3,1:3,p,o)), invFi) & + matmul(temp_33_3,dLidS(1:3,1:3,p,o)) enddo; enddo lhs_3333 = crystallite_subdt(c,i,e)*math_mul3333xx3333(dSdFe,temp_3333) & @@ -569,15 +565,14 @@ subroutine crystallite_stressTangent temp_3333 = math_mul3333xx3333(dLpdS,dSdF) do o=1,3; do p=1,3 dFpinvdF(1:3,1:3,p,o) = -crystallite_subdt(c,i,e) & - * matmul(math_inv33(crystallite_subFp0(1:3,1:3,c,i,e)), & - matmul(temp_3333(1:3,1:3,p,o),crystallite_invFi(1:3,1:3,c,i,e))) + * matmul(invSubFp0, matmul(temp_3333(1:3,1:3,p,o),invFi)) enddo; enddo !-------------------------------------------------------------------------------------------------- ! assemble dPdF - temp_33_1 = matmul(crystallite_S(1:3,1:3,c,i,e),transpose(crystallite_invFp(1:3,1:3,c,i,e))) - temp_33_2 = matmul(crystallite_invFp(1:3,1:3,c,i,e),temp_33_1) - temp_33_3 = matmul(crystallite_subF(1:3,1:3,c,i,e),crystallite_invFp(1:3,1:3,c,i,e)) + temp_33_1 = matmul(crystallite_S(1:3,1:3,c,i,e),transpose(invFp)) + temp_33_2 = matmul(invFp,temp_33_1) + temp_33_3 = matmul(crystallite_subF(1:3,1:3,c,i,e),invFp) temp_33_4 = matmul(temp_33_3,crystallite_S(1:3,1:3,c,i,e)) crystallite_dPdF(1:3,1:3,1:3,1:3,c,i,e) = 0.0_pReal @@ -589,7 +584,7 @@ subroutine crystallite_stressTangent + matmul(matmul(crystallite_subF(1:3,1:3,c,i,e), & dFpinvdF(1:3,1:3,p,o)),temp_33_1) & + matmul(matmul(temp_33_3,dSdF(1:3,1:3,p,o)), & - transpose(crystallite_invFp(1:3,1:3,c,i,e))) & + transpose(invFp)) & + matmul(temp_33_4,transpose(dFpinvdF(1:3,1:3,p,o))) enddo; enddo @@ -734,7 +729,7 @@ subroutine crystallite_results real(pReal), allocatable, dimension(:,:,:) :: select_tensors integer :: e,i,c,j - allocate(select_tensors(3,3,count(material_phaseAt==instance)*homogenization_maxNgrains*discretization_nIP)) + allocate(select_tensors(3,3,count(material_phaseAt==instance)*discretization_nIP)) j=0 do e = 1, size(material_phaseAt,2) @@ -793,28 +788,28 @@ logical function integrateStress(ipc,ip,el,timeFraction) real(pReal), dimension(3,3):: F, & ! deformation gradient at end of timestep Fp_new, & ! plastic deformation gradient at end of timestep - Fe_new, & ! elastic deformation gradient at end of timestep invFp_new, & ! inverse of Fp_new - Fi_new, & ! gradient of intermediate deformation stages - invFi_new, & invFp_current, & ! inverse of Fp_current - invFi_current, & ! inverse of Fp_current Lpguess, & ! current guess for plastic velocity gradient Lpguess_old, & ! known last good guess for plastic velocity gradient Lp_constitutive, & ! plastic velocity gradient resulting from constitutive law residuumLp, & ! current residuum of plastic velocity gradient residuumLp_old, & ! last residuum of plastic velocity gradient deltaLp, & ! direction of next guess + Fi_new, & ! gradient of intermediate deformation stages + invFi_new, & + invFi_current, & ! inverse of Fi_current Liguess, & ! current guess for intermediate velocity gradient Liguess_old, & ! known last good guess for intermediate velocity gradient Li_constitutive, & ! intermediate velocity gradient resulting from constitutive law residuumLi, & ! current residuum of intermediate velocity gradient residuumLi_old, & ! last residuum of intermediate velocity gradient deltaLi, & ! direction of next guess + Fe, & ! elastic deformation gradient + Fe_new, & S, & ! 2nd Piola-Kirchhoff Stress in plastic (lattice) configuration A, & B, & - Fe, & ! elastic deformation gradient temp_33 real(pReal), dimension(9) :: temp_9 ! needed for matrix inversion by LAPACK integer, dimension(9) :: devNull_9 ! needed for matrix inversion by LAPACK @@ -993,16 +988,13 @@ logical function integrateStress(ipc,ip,el,timeFraction) Fe_new = matmul(matmul(F,invFp_new),invFi_new) integrateStress = .true. - crystallite_P (1:3,1:3,ipc,ip,el) = matmul(matmul(F,invFp_new),matmul(S,transpose(invFp_new))) ! ToDo: We propably do not need to store P! + crystallite_P (1:3,1:3,ipc,ip,el) = matmul(matmul(F,invFp_new),matmul(S,transpose(invFp_new))) crystallite_S (1:3,1:3,ipc,ip,el) = S crystallite_Lp (1:3,1:3,ipc,ip,el) = Lpguess crystallite_Li (1:3,1:3,ipc,ip,el) = Liguess crystallite_Fp (1:3,1:3,ipc,ip,el) = Fp_new crystallite_Fi (1:3,1:3,ipc,ip,el) = Fi_new crystallite_Fe (1:3,1:3,ipc,ip,el) = Fe_new - crystallite_invFp(1:3,1:3,ipc,ip,el) = invFp_new - crystallite_invFi(1:3,1:3,ipc,ip,el) = invFi_new - end function integrateStress diff --git a/src/grid/grid_mech_FEM.f90 b/src/grid/grid_mech_FEM.f90 index b51ebb56a..57a27ebe8 100644 --- a/src/grid/grid_mech_FEM.f90 +++ b/src/grid/grid_mech_FEM.f90 @@ -21,10 +21,10 @@ module grid_mech_FEM use discretization use mesh_grid use debug - + implicit none private - + !-------------------------------------------------------------------------------------------------- ! derived types type(tSolutionParams), private :: params @@ -52,20 +52,20 @@ module grid_mech_FEM F_aim_lastIter = math_I3, & F_aim_lastInc = math_I3, & !< previous average deformation gradient P_av = 0.0_pReal !< average 1st Piola--Kirchhoff stress - + character(len=pStringLen), private :: incInfo !< time and increment information - + real(pReal), private, dimension(3,3,3,3) :: & - C_volAvg = 0.0_pReal, & !< current volume average stiffness + C_volAvg = 0.0_pReal, & !< current volume average stiffness C_volAvgLastInc = 0.0_pReal, & !< previous volume average stiffness S = 0.0_pReal !< current compliance (filled up with zeros) - + real(pReal), private :: & err_BC !< deviation from stress BC - + integer, private :: & totalIter = 0 !< total iteration in current increment - + public :: & grid_mech_FEM_init, & grid_mech_FEM_solution, & @@ -79,7 +79,7 @@ contains !> @brief allocates all necessary fields and fills them with data, potentially from restart info !-------------------------------------------------------------------------------------------------- subroutine grid_mech_FEM_init - + real(pReal) :: HGCoeff = 0.0e-2_pReal PetscInt, dimension(0:worldsize-1) :: localK real(pReal), dimension(3,3) :: & @@ -99,9 +99,9 @@ subroutine grid_mech_FEM_init real(pReal), dimension(3,3,3,3) :: devNull PetscScalar, pointer, dimension(:,:,:,:) :: & u_current,u_lastInc - + write(6,'(/,a)') ' <<<+- grid_mech_FEM init -+>>>'; flush(6) - + !-------------------------------------------------------------------------------------------------- ! set default and user defined options for PETSc call PETScOptionsInsertString(PETSC_NULL_OPTIONS,'-mech_snes_type newtonls -mech_ksp_type fgmres & @@ -115,11 +115,11 @@ subroutine grid_mech_FEM_init allocate(F (3,3,grid(1),grid(2),grid3),source = 0.0_pReal) allocate(P_current (3,3,grid(1),grid(2),grid3),source = 0.0_pReal) allocate(F_lastInc (3,3,grid(1),grid(2),grid3),source = 0.0_pReal) - + !-------------------------------------------------------------------------------------------------- ! initialize solver specific parts of PETSc call SNESCreate(PETSC_COMM_WORLD,mech_snes,ierr); CHKERRQ(ierr) - call SNESSetOptionsPrefix(mech_snes,'mech_',ierr);CHKERRQ(ierr) + call SNESSetOptionsPrefix(mech_snes,'mech_',ierr);CHKERRQ(ierr) localK = 0 localK(worldrank) = grid3 call MPI_Allreduce(MPI_IN_PLACE,localK,worldsize,MPI_INTEGER,MPI_SUM,PETSC_COMM_WORLD,ierr) @@ -141,12 +141,12 @@ subroutine grid_mech_FEM_init call DMCreateGlobalVector(mech_grid,solution_lastInc,ierr); CHKERRQ(ierr) call DMCreateGlobalVector(mech_grid,solution_rate ,ierr); CHKERRQ(ierr) call DMSNESSetFunctionLocal(mech_grid,formResidual,PETSC_NULL_SNES,ierr) - CHKERRQ(ierr) + CHKERRQ(ierr) call DMSNESSetJacobianLocal(mech_grid,formJacobian,PETSC_NULL_SNES,ierr) CHKERRQ(ierr) call SNESSetConvergenceTest(mech_snes,converged,PETSC_NULL_SNES,PETSC_NULL_FUNCTION,ierr) ! specify custom convergence check function "_converged" CHKERRQ(ierr) - call SNESSetMaxLinearSolveFailures(mech_snes, huge(1), ierr); CHKERRQ(ierr) ! ignore linear solve failures + call SNESSetMaxLinearSolveFailures(mech_snes, huge(1), ierr); CHKERRQ(ierr) ! ignore linear solve failures call SNESSetFromOptions(mech_snes,ierr); CHKERRQ(ierr) ! pull it all together with additional cli arguments !-------------------------------------------------------------------------------------------------- @@ -156,15 +156,15 @@ subroutine grid_mech_FEM_init call VecSet(solution_rate ,0.0_pReal,ierr);CHKERRQ(ierr) call DMDAVecGetArrayF90(mech_grid,solution_current,u_current,ierr); CHKERRQ(ierr) call DMDAVecGetArrayF90(mech_grid,solution_lastInc,u_lastInc,ierr); CHKERRQ(ierr) - + call DMDAGetCorners(mech_grid,xstart,ystart,zstart,xend,yend,zend,ierr) ! local grid extent - CHKERRQ(ierr) + CHKERRQ(ierr) xend = xstart+xend-1 yend = ystart+yend-1 zend = zstart+zend-1 delta = geomSize/real(grid,pReal) ! grid spacing detJ = product(delta) ! cell volume - + BMat = reshape(real([-1.0_pReal/delta(1),-1.0_pReal/delta(2),-1.0_pReal/delta(3), & 1.0_pReal/delta(1),-1.0_pReal/delta(2),-1.0_pReal/delta(3), & -1.0_pReal/delta(1), 1.0_pReal/delta(2),-1.0_pReal/delta(3), & @@ -173,7 +173,7 @@ subroutine grid_mech_FEM_init 1.0_pReal/delta(1),-1.0_pReal/delta(2), 1.0_pReal/delta(3), & -1.0_pReal/delta(1), 1.0_pReal/delta(2), 1.0_pReal/delta(3), & 1.0_pReal/delta(1), 1.0_pReal/delta(2), 1.0_pReal/delta(3)],pReal), [3,8])/4.0_pReal ! shape function derivative matrix - + HGMat = matmul(transpose(HGcomp),HGcomp) & * HGCoeff*(delta(1)*delta(2) + delta(2)*delta(3) + delta(3)*delta(1))/16.0_pReal ! hourglass stabilization matrix @@ -181,11 +181,11 @@ subroutine grid_mech_FEM_init ! init fields restartRead: if (interface_restartInc > 0) then write(6,'(/,a,i0,a)') ' reading restart data of increment ', interface_restartInc, ' from file' - + write(fileName,'(a,a,i0,a)') trim(getSolverJobName()),'_',worldrank,'.hdf5' fileHandle = HDF5_openFile(fileName) groupHandle = HDF5_openGroup(fileHandle,'solver') - + call HDF5_read(groupHandle,F_aim, 'F_aim') call HDF5_read(groupHandle,F_aim_lastInc,'F_aim_lastInc') call HDF5_read(groupHandle,F_aimDot, 'F_aimDot') @@ -193,7 +193,7 @@ subroutine grid_mech_FEM_init call HDF5_read(groupHandle,F_lastInc, 'F_lastInc') call HDF5_read(groupHandle,u_current, 'u') call HDF5_read(groupHandle,u_lastInc, 'u_lastInc') - + elseif (interface_restartInc == 0) then restartRead F_lastInc = spread(spread(spread(math_I3,3,grid(1)),4,grid(2)),5,grid3) ! initialize to identity F = spread(spread(spread(math_I3,3,grid(1)),4,grid(2)),5,grid3) @@ -207,12 +207,12 @@ subroutine grid_mech_FEM_init CHKERRQ(ierr) call DMDAVecRestoreArrayF90(mech_grid,solution_lastInc,u_lastInc,ierr) CHKERRQ(ierr) - + restartRead2: if (interface_restartInc > 0) then write(6,'(/,a,i0,a)') ' reading more restart data of increment ', interface_restartInc, ' from file' call HDF5_read(groupHandle,C_volAvg, 'C_volAvg') call HDF5_read(groupHandle,C_volAvgLastInc,'C_volAvgLastInc') - + call HDF5_closeGroup(groupHandle) call HDF5_closeFile(fileHandle) @@ -243,14 +243,14 @@ function grid_mech_FEM_solution(incInfoIn,timeinc,timeinc_old,stress_BC,rotation ! PETSc Data PetscErrorCode :: ierr SNESConvergedReason :: reason - + incInfo = incInfoIn !-------------------------------------------------------------------------------------------------- ! update stiffness (and gamma operator) S = utilities_maskedCompliance(rotation_BC,stress_BC%maskLogical,C_volAvg) !-------------------------------------------------------------------------------------------------- -! set module wide available data +! set module wide available data params%stress_mask = stress_BC%maskFloat params%stress_BC = stress_BC%values params%rotation_BC = rotation_BC @@ -258,13 +258,13 @@ function grid_mech_FEM_solution(incInfoIn,timeinc,timeinc_old,stress_BC,rotation params%timeincOld = timeinc_old !-------------------------------------------------------------------------------------------------- -! solve BVP +! solve BVP call SNESsolve(mech_snes,PETSC_NULL_VEC,solution_current,ierr);CHKERRQ(ierr) !-------------------------------------------------------------------------------------------------- ! check convergence call SNESGetConvergedReason(mech_snes,reason,ierr);CHKERRQ(ierr) - + solution%converged = reason > 0 solution%iterationsNeeded = totalIter solution%termIll = terminallyIll @@ -296,15 +296,15 @@ subroutine grid_mech_FEM_forward(cutBack,guess,timeinc,timeinc_old,loadCaseTime, PetscErrorCode :: ierr PetscScalar, pointer, dimension(:,:,:,:) :: & u_current,u_lastInc - + call DMDAVecGetArrayF90(mech_grid,solution_current,u_current,ierr); CHKERRQ(ierr) call DMDAVecGetArrayF90(mech_grid,solution_lastInc,u_lastInc,ierr); CHKERRQ(ierr) - + if (cutBack) then C_volAvg = C_volAvgLastInc else C_volAvgLastInc = C_volAvg - + F_aimDot = merge(stress_BC%maskFloat*(F_aim-F_aim_lastInc)/timeinc_old, 0.0_pReal, guess) F_aim_lastInc = F_aim @@ -329,10 +329,10 @@ subroutine grid_mech_FEM_forward(cutBack,guess,timeinc,timeinc_old,loadCaseTime, call VecSet(solution_rate,0.0_pReal,ierr); CHKERRQ(ierr) endif call VecCopy(solution_current,solution_lastInc,ierr); CHKERRQ(ierr) - + F_lastInc = F - - materialpoint_F0 = reshape(F, [3,3,1,product(grid(1:2))*grid3]) + + materialpoint_F0 = reshape(F, [3,3,1,product(grid(1:2))*grid3]) endif !-------------------------------------------------------------------------------------------------- @@ -365,12 +365,12 @@ subroutine grid_mech_FEM_restartWrite integer(HID_T) :: fileHandle, groupHandle PetscScalar, dimension(:,:,:,:), pointer :: u_current,u_lastInc character(len=pStringLen) :: fileName - + call DMDAVecGetArrayF90(mech_grid,solution_current,u_current,ierr); CHKERRQ(ierr) call DMDAVecGetArrayF90(mech_grid,solution_lastInc,u_lastInc,ierr); CHKERRQ(ierr) write(6,'(a)') ' writing solver data required for restart to file'; flush(6) - + write(fileName,'(a,a,i0,a)') trim(getSolverJobName()),'_',worldrank,'.hdf5' fileHandle = HDF5_openFile(fileName,'w') groupHandle = HDF5_addGroup(fileHandle,'solver') @@ -388,7 +388,7 @@ subroutine grid_mech_FEM_restartWrite call HDF5_closeGroup(groupHandle) call HDF5_closeFile(fileHandle) - + call DMDAVecRestoreArrayF90(mech_grid,solution_current,u_current,ierr);CHKERRQ(ierr) call DMDAVecRestoreArrayF90(mech_grid,solution_lastInc,u_lastInc,ierr);CHKERRQ(ierr) @@ -405,7 +405,7 @@ subroutine converged(snes_local,PETScIter,devNull1,devNull2,fnorm,reason,dummy,i PetscReal, intent(in) :: & devNull1, & devNull2, & - fnorm + fnorm SNESConvergedReason :: reason PetscObject :: dummy PetscErrorCode :: ierr @@ -421,7 +421,7 @@ subroutine converged(snes_local,PETScIter,devNull1,devNull2,fnorm,reason,dummy,i if ((totalIter >= itmin .and. & all([ err_div/divTol, & err_BC /BCTol ] < 1.0_pReal)) & - .or. terminallyIll) then + .or. terminallyIll) then reason = 1 elseif (totalIter >= itmax) then reason = -1 @@ -435,10 +435,10 @@ subroutine converged(snes_local,PETScIter,devNull1,devNull2,fnorm,reason,dummy,i write(6,'(1/,a,f12.2,a,es8.2,a,es9.2,a)') ' error divergence = ', & err_div/divTol, ' (',err_div,' / m, tol = ',divTol,')' write(6,'(a,f12.2,a,es8.2,a,es9.2,a)') ' error stress BC = ', & - err_BC/BCTol, ' (',err_BC, ' Pa, tol = ',BCTol,')' + err_BC/BCTol, ' (',err_BC, ' Pa, tol = ',BCTol,')' write(6,'(/,a)') ' ===========================================================================' flush(6) - + end subroutine converged @@ -475,7 +475,7 @@ subroutine formResidual(da_local,x_local, & write(6,'(1x,a,3(a,i0))') trim(incInfo), ' @ Iteration ', itmin, '≤',totalIter+1, '≤', itmax if (iand(debug_level(debug_spectral),debug_spectralRotation) /= 0) & write(6,'(/,a,/,3(3(f12.7,1x)/))',advance='no') & - ' deformation gradient aim (lab) =', transpose(params%rotation_BC%rotTensor2(F_aim,active=.true.)) + ' deformation gradient aim (lab) =', transpose(params%rotation_BC%rotate(F_aim,active=.true.)) write(6,'(/,a,/,3(3(f12.7,1x)/))',advance='no') & ' deformation gradient aim =', transpose(F_aim) flush(6) @@ -491,7 +491,7 @@ subroutine formResidual(da_local,x_local, & x_elem(ctr,1:3) = x_scal(0:2,i+ii,j+jj,k+kk) enddo; enddo; enddo ii = i-xstart+1; jj = j-ystart+1; kk = k-zstart+1 - F(1:3,1:3,ii,jj,kk) = params%rotation_BC%rotTensor2(F_aim,active=.true.) + transpose(matmul(BMat,x_elem)) + F(1:3,1:3,ii,jj,kk) = params%rotation_BC%rotate(F_aim,active=.true.) + transpose(matmul(BMat,x_elem)) enddo; enddo; enddo call DMDAVecRestoreArrayF90(da_local,x_local,x_scal,ierr);CHKERRQ(ierr) @@ -501,7 +501,7 @@ subroutine formResidual(da_local,x_local, & P_av,C_volAvg,devNull, & F,params%timeinc,params%rotation_BC) call MPI_Allreduce(MPI_IN_PLACE,terminallyIll,1,MPI_LOGICAL,MPI_LOR,PETSC_COMM_WORLD,ierr) - + !-------------------------------------------------------------------------------------------------- ! stress BC handling F_aim_lastIter = F_aim @@ -535,24 +535,24 @@ subroutine formResidual(da_local,x_local, & enddo; enddo; enddo call DMDAVecRestoreArrayF90(da_local,x_local,x_scal,ierr);CHKERRQ(ierr) call DMDAVecRestoreArrayF90(da_local,f_local,f_scal,ierr);CHKERRQ(ierr) - + !-------------------------------------------------------------------------------------------------- ! applying boundary conditions call DMDAVecGetArrayF90(da_local,f_local,f_scal,ierr);CHKERRQ(ierr) if (zstart == 0) then - f_scal(0:2,xstart,ystart,zstart) = 0.0 - f_scal(0:2,xend+1,ystart,zstart) = 0.0 - f_scal(0:2,xstart,yend+1,zstart) = 0.0 - f_scal(0:2,xend+1,yend+1,zstart) = 0.0 + f_scal(0:2,xstart,ystart,zstart) = 0.0 + f_scal(0:2,xend+1,ystart,zstart) = 0.0 + f_scal(0:2,xstart,yend+1,zstart) = 0.0 + f_scal(0:2,xend+1,yend+1,zstart) = 0.0 endif if (zend + 1 == grid(3)) then - f_scal(0:2,xstart,ystart,zend+1) = 0.0 - f_scal(0:2,xend+1,ystart,zend+1) = 0.0 - f_scal(0:2,xstart,yend+1,zend+1) = 0.0 - f_scal(0:2,xend+1,yend+1,zend+1) = 0.0 + f_scal(0:2,xstart,ystart,zend+1) = 0.0 + f_scal(0:2,xend+1,ystart,zend+1) = 0.0 + f_scal(0:2,xstart,yend+1,zend+1) = 0.0 + f_scal(0:2,xend+1,yend+1,zend+1) = 0.0 endif - call DMDAVecRestoreArrayF90(da_local,f_local,f_scal,ierr);CHKERRQ(ierr) - + call DMDAVecRestoreArrayF90(da_local,f_local,f_scal,ierr);CHKERRQ(ierr) + end subroutine formResidual @@ -574,7 +574,7 @@ subroutine formJacobian(da_local,x_local,Jac_pre,Jac,dummy,ierr) PetscObject :: dummy MatNullSpace :: matnull PetscErrorCode :: ierr - + BMatFull = 0.0 BMatFull(1:3,1 :8 ) = BMat BMatFull(4:6,9 :16) = BMat @@ -623,7 +623,7 @@ subroutine formJacobian(da_local,x_local,Jac_pre,Jac,dummy,ierr) call MatAssemblyEnd(Jac,MAT_FINAL_ASSEMBLY,ierr); CHKERRQ(ierr) call MatAssemblyBegin(Jac_pre,MAT_FINAL_ASSEMBLY,ierr); CHKERRQ(ierr) call MatAssemblyEnd(Jac_pre,MAT_FINAL_ASSEMBLY,ierr); CHKERRQ(ierr) - + !-------------------------------------------------------------------------------------------------- ! applying boundary conditions diag = (C_volAvg(1,1,1,1)/delta(1)**2.0_pReal + & diff --git a/src/grid/grid_mech_spectral_basic.f90 b/src/grid/grid_mech_spectral_basic.f90 index af8cbc377..25ac9cb63 100644 --- a/src/grid/grid_mech_spectral_basic.f90 +++ b/src/grid/grid_mech_spectral_basic.f90 @@ -28,11 +28,11 @@ module grid_mech_spectral_basic !-------------------------------------------------------------------------------------------------- ! derived types type(tSolutionParams), private :: params - + type, private :: tNumerics logical :: update_gamma !< update gamma operator with current stiffness end type tNumerics - + type(tNumerics) :: num ! numerics parameters. Better name? !-------------------------------------------------------------------------------------------------- @@ -55,21 +55,21 @@ module grid_mech_spectral_basic F_aim_lastInc = math_I3, & !< previous average deformation gradient P_av = 0.0_pReal !< average 1st Piola--Kirchhoff stress - character(len=pStringLen), private :: incInfo !< time and increment information + character(len=pStringLen), private :: incInfo !< time and increment information real(pReal), private, dimension(3,3,3,3) :: & - C_volAvg = 0.0_pReal, & !< current volume average stiffness + C_volAvg = 0.0_pReal, & !< current volume average stiffness C_volAvgLastInc = 0.0_pReal, & !< previous volume average stiffness C_minMaxAvg = 0.0_pReal, & !< current (min+max)/2 stiffness C_minMaxAvgLastInc = 0.0_pReal, & !< previous (min+max)/2 stiffness S = 0.0_pReal !< current compliance (filled up with zeros) - + real(pReal), private :: & err_BC, & !< deviation from stress BC err_div !< RMS of div of P - + integer, private :: & totalIter = 0 !< total iteration in current increment - + public :: & grid_mech_spectral_basic_init, & grid_mech_spectral_basic_solution, & @@ -83,27 +83,27 @@ contains !> @brief allocates all necessary fields and fills them with data, potentially from restart info !-------------------------------------------------------------------------------------------------- subroutine grid_mech_spectral_basic_init - + real(pReal), dimension(3,3,grid(1),grid(2),grid3) :: P real(pReal), dimension(3,3) :: & temp33_Real = 0.0_pReal - + PetscErrorCode :: ierr PetscScalar, pointer, dimension(:,:,:,:) :: & F ! pointer to solution data - PetscInt, dimension(worldsize) :: localK + PetscInt, dimension(worldsize) :: localK integer(HID_T) :: fileHandle, groupHandle integer :: fileUnit character(len=pStringLen) :: fileName - + write(6,'(/,a)') ' <<<+- grid_mech_spectral_basic init -+>>>'; flush(6) - + write(6,'(/,a)') ' Eisenlohr et al., International Journal of Plasticity 46:37–53, 2013' write(6,'(a)') ' https://doi.org/10.1016/j.ijplas.2012.09.012' - + write(6,'(/,a)') ' Shanthraj et al., International Journal of Plasticity 66:31–45, 2015' write(6,'(a)') ' https://doi.org/10.1016/j.ijplas.2014.02.006' - + num%update_gamma = config_numerics%getInt('update_gamma',defaultVal=0) > 0 !-------------------------------------------------------------------------------------------------- @@ -117,11 +117,11 @@ subroutine grid_mech_spectral_basic_init ! allocate global fields allocate (F_lastInc(3,3,grid(1),grid(2),grid3),source = 0.0_pReal) allocate (Fdot (3,3,grid(1),grid(2),grid3),source = 0.0_pReal) - + !-------------------------------------------------------------------------------------------------- ! initialize solver specific parts of PETSc call SNESCreate(PETSC_COMM_WORLD,snes,ierr); CHKERRQ(ierr) - call SNESSetOptionsPrefix(snes,'mech_',ierr);CHKERRQ(ierr) + call SNESSetOptionsPrefix(snes,'mech_',ierr);CHKERRQ(ierr) localK = 0 localK(worldrank+1) = grid3 call MPI_Allreduce(MPI_IN_PLACE,localK,worldsize,MPI_INTEGER,MPI_SUM,PETSC_COMM_WORLD,ierr) @@ -139,45 +139,45 @@ subroutine grid_mech_spectral_basic_init call DMsetUp(da,ierr); CHKERRQ(ierr) call DMcreateGlobalVector(da,solution_vec,ierr); CHKERRQ(ierr) ! global solution vector (grid x 9, i.e. every def grad tensor) call DMDASNESsetFunctionLocal(da,INSERT_VALUES,formResidual,PETSC_NULL_SNES,ierr) ! residual vector of same shape as solution vector - CHKERRQ(ierr) + CHKERRQ(ierr) call SNESsetConvergenceTest(snes,converged,PETSC_NULL_SNES,PETSC_NULL_FUNCTION,ierr) ! specify custom convergence check function "converged" CHKERRQ(ierr) call SNESsetFromOptions(snes,ierr); CHKERRQ(ierr) ! pull it all together with additional CLI arguments !-------------------------------------------------------------------------------------------------- -! init fields +! init fields call DMDAVecGetArrayF90(da,solution_vec,F,ierr); CHKERRQ(ierr) ! places pointer on PETSc data - + restartRead: if (interface_restartInc > 0) then write(6,'(/,a,i0,a)') ' reading restart data of increment ', interface_restartInc, ' from file' write(fileName,'(a,a,i0,a)') trim(getSolverJobName()),'_',worldrank,'.hdf5' fileHandle = HDF5_openFile(fileName) groupHandle = HDF5_openGroup(fileHandle,'solver') - + call HDF5_read(groupHandle,F_aim, 'F_aim') call HDF5_read(groupHandle,F_aim_lastInc,'F_aim_lastInc') call HDF5_read(groupHandle,F_aimDot, 'F_aimDot') call HDF5_read(groupHandle,F, 'F') call HDF5_read(groupHandle,F_lastInc, 'F_lastInc') - + elseif (interface_restartInc == 0) then restartRead F_lastInc = spread(spread(spread(math_I3,3,grid(1)),4,grid(2)),5,grid3) ! initialize to identity F = reshape(F_lastInc,[9,grid(1),grid(2),grid3]) endif restartRead - + materialpoint_F0 = reshape(F_lastInc, [3,3,1,product(grid(1:2))*grid3]) ! set starting condition for materialpoint_stressAndItsTangent call Utilities_updateCoords(reshape(F,shape(F_lastInc))) call Utilities_constitutiveResponse(P,temp33_Real,C_volAvg,C_minMaxAvg, & ! stress field, stress avg, global average of stiffness and (min+max)/2 reshape(F,shape(F_lastInc)), & ! target F 0.0_pReal) ! time increment call DMDAVecRestoreArrayF90(da,solution_vec,F,ierr); CHKERRQ(ierr) ! deassociate pointer - + restartRead2: if (interface_restartInc > 0) then write(6,'(/,a,i0,a)') ' reading more restart data of increment ', interface_restartInc, ' from file' call HDF5_read(groupHandle,C_volAvg, 'C_volAvg') call HDF5_read(groupHandle,C_volAvgLastInc,'C_volAvgLastInc') - + call HDF5_closeGroup(groupHandle) call HDF5_closeFile(fileHandle) @@ -197,7 +197,7 @@ end subroutine grid_mech_spectral_basic_init !> @brief solution for the basic scheme with internal iterations !-------------------------------------------------------------------------------------------------- function grid_mech_spectral_basic_solution(incInfoIn,timeinc,timeinc_old,stress_BC,rotation_BC) result(solution) - + !-------------------------------------------------------------------------------------------------- ! input data for solution character(len=*), intent(in) :: & @@ -215,7 +215,7 @@ function grid_mech_spectral_basic_solution(incInfoIn,timeinc,timeinc_old,stress_ ! PETSc Data PetscErrorCode :: ierr SNESConvergedReason :: reason - + incInfo = incInfoIn !-------------------------------------------------------------------------------------------------- @@ -224,7 +224,7 @@ function grid_mech_spectral_basic_solution(incInfoIn,timeinc,timeinc_old,stress_ if(num%update_gamma) call utilities_updateGamma(C_minMaxAvg) !-------------------------------------------------------------------------------------------------- -! set module wide available data +! set module wide available data params%stress_mask = stress_BC%maskFloat params%stress_BC = stress_BC%values params%rotation_BC = rotation_BC @@ -232,13 +232,13 @@ function grid_mech_spectral_basic_solution(incInfoIn,timeinc,timeinc_old,stress_ params%timeincOld = timeinc_old !-------------------------------------------------------------------------------------------------- -! solve BVP +! solve BVP call SNESsolve(snes,PETSC_NULL_VEC,solution_vec,ierr); CHKERRQ(ierr) !-------------------------------------------------------------------------------------------------- ! check convergence call SNESGetConvergedReason(snes,reason,ierr); CHKERRQ(ierr) - + solution%converged = reason > 0 solution%iterationsNeeded = totalIter solution%termIll = terminallyIll @@ -271,14 +271,14 @@ subroutine grid_mech_spectral_basic_forward(cutBack,guess,timeinc,timeinc_old,lo PetscScalar, dimension(:,:,:,:), pointer :: F call DMDAVecGetArrayF90(da,solution_vec,F,ierr); CHKERRQ(ierr) - + if (cutBack) then C_volAvg = C_volAvgLastInc C_minMaxAvg = C_minMaxAvgLastInc else C_volAvgLastInc = C_volAvg C_minMaxAvgLastInc = C_minMaxAvg - + F_aimDot = merge(stress_BC%maskFloat*(F_aim-F_aim_lastInc)/timeinc_old, 0.0_pReal, guess) F_aim_lastInc = F_aim @@ -297,9 +297,9 @@ subroutine grid_mech_spectral_basic_forward(cutBack,guess,timeinc,timeinc_old,lo Fdot = utilities_calculateRate(guess, & F_lastInc,reshape(F,[3,3,grid(1),grid(2),grid3]),timeinc_old, & - rotation_BC%rotTensor2(F_aimDot,active=.true.)) + rotation_BC%rotate(F_aimDot,active=.true.)) F_lastInc = reshape(F,[3,3,grid(1),grid(2),grid3]) - + materialpoint_F0 = reshape(F, [3,3,1,product(grid(1:2))*grid3]) endif @@ -307,9 +307,9 @@ subroutine grid_mech_spectral_basic_forward(cutBack,guess,timeinc,timeinc_old,lo ! update average and local deformation gradients F_aim = F_aim_lastInc + F_aimDot * timeinc F = reshape(Utilities_forwardField(timeinc,F_lastInc,Fdot, & ! estimate of F at end of time+timeinc that matches rotated F_aim on average - rotation_BC%rotTensor2(F_aim,active=.true.)),[9,grid(1),grid(2),grid3]) + rotation_BC%rotate(F_aim,active=.true.)),[9,grid(1),grid(2),grid3]) call DMDAVecRestoreArrayF90(da,solution_vec,F,ierr); CHKERRQ(ierr) - + end subroutine grid_mech_spectral_basic_forward @@ -341,11 +341,11 @@ subroutine grid_mech_spectral_basic_restartWrite call DMDAVecGetArrayF90(da,solution_vec,F,ierr); CHKERRQ(ierr) write(6,'(a)') ' writing solver data required for restart to file'; flush(6) - + write(fileName,'(a,a,i0,a)') trim(getSolverJobName()),'_',worldrank,'.hdf5' fileHandle = HDF5_openFile(fileName,'w') groupHandle = HDF5_addGroup(fileHandle,'solver') - + call HDF5_write(groupHandle,F_aim, 'F_aim') call HDF5_write(groupHandle,F_aim_lastInc,'F_aim_lastInc') call HDF5_write(groupHandle,F_aimDot, 'F_aimDot') @@ -358,7 +358,7 @@ subroutine grid_mech_spectral_basic_restartWrite call HDF5_closeGroup(groupHandle) call HDF5_closeFile(fileHandle) - + if (num%update_gamma) call utilities_saveReferenceStiffness call DMDAVecRestoreArrayF90(da,solution_vec,F,ierr); CHKERRQ(ierr) @@ -376,7 +376,7 @@ subroutine converged(snes_local,PETScIter,devNull1,devNull2,devNull3,reason,dumm PetscReal, intent(in) :: & devNull1, & devNull2, & - devNull3 + devNull3 SNESConvergedReason :: reason PetscObject :: dummy PetscErrorCode :: ierr @@ -390,7 +390,7 @@ subroutine converged(snes_local,PETScIter,devNull1,devNull2,devNull3,reason,dumm if ((totalIter >= itmin .and. & all([ err_div/divTol, & err_BC /BCTol ] < 1.0_pReal)) & - .or. terminallyIll) then + .or. terminallyIll) then reason = 1 elseif (totalIter >= itmax) then reason = -1 @@ -404,10 +404,10 @@ subroutine converged(snes_local,PETScIter,devNull1,devNull2,devNull3,reason,dumm write(6,'(1/,a,f12.2,a,es8.2,a,es9.2,a)') ' error divergence = ', & err_div/divTol, ' (',err_div,' / m, tol = ',divTol,')' write(6,'(a,f12.2,a,es8.2,a,es9.2,a)') ' error stress BC = ', & - err_BC/BCTol, ' (',err_BC, ' Pa, tol = ',BCTol,')' + err_BC/BCTol, ' (',err_BC, ' Pa, tol = ',BCTol,')' write(6,'(/,a)') ' ===========================================================================' - flush(6) - + flush(6) + end subroutine converged @@ -441,7 +441,7 @@ subroutine formResidual(in, F, & write(6,'(1x,a,3(a,i0))') trim(incInfo), ' @ Iteration ', itmin, '≤',totalIter, '≤', itmax if (iand(debug_level(debug_spectral),debug_spectralRotation) /= 0) & write(6,'(/,a,/,3(3(f12.7,1x)/))',advance='no') & - ' deformation gradient aim (lab) =', transpose(params%rotation_BC%rotTensor2(F_aim,active=.true.)) + ' deformation gradient aim (lab) =', transpose(params%rotation_BC%rotate(F_aim,active=.true.)) write(6,'(/,a,/,3(3(f12.7,1x)/))',advance='no') & ' deformation gradient aim =', transpose(F_aim) flush(6) @@ -453,7 +453,7 @@ subroutine formResidual(in, F, & P_av,C_volAvg,C_minMaxAvg, & F,params%timeinc,params%rotation_BC) call MPI_Allreduce(MPI_IN_PLACE,terminallyIll,1,MPI_LOGICAL,MPI_LOR,PETSC_COMM_WORLD,ierr) - + !-------------------------------------------------------------------------------------------------- ! stress BC handling deltaF_aim = math_mul3333xx33(S, P_av - params%stress_BC) @@ -466,9 +466,9 @@ subroutine formResidual(in, F, & tensorField_real(1:3,1:3,1:grid(1),1:grid(2),1:grid3) = residuum ! store fPK field for subsequent FFT forward transform call utilities_FFTtensorForward ! FFT forward of global "tensorField_real" err_div = Utilities_divergenceRMS() ! divRMS of tensorField_fourier for later use - call utilities_fourierGammaConvolution(params%rotation_BC%rotTensor2(deltaF_aim,active=.true.)) ! convolution of Gamma and tensorField_fourier + call utilities_fourierGammaConvolution(params%rotation_BC%rotate(deltaF_aim,active=.true.)) ! convolution of Gamma and tensorField_fourier call utilities_FFTtensorBackward ! FFT backward of global tensorField_fourier - + !-------------------------------------------------------------------------------------------------- ! constructing residual residuum = tensorField_real(1:3,1:3,1:grid(1),1:grid(2),1:grid3) ! Gamma*P gives correction towards div(P) = 0, so needs to be zero, too diff --git a/src/grid/grid_mech_spectral_polarisation.f90 b/src/grid/grid_mech_spectral_polarisation.f90 index bdc65a8c5..ab880e2a6 100644 --- a/src/grid/grid_mech_spectral_polarisation.f90 +++ b/src/grid/grid_mech_spectral_polarisation.f90 @@ -22,20 +22,20 @@ module grid_mech_spectral_polarisation use homogenization use mesh_grid use debug - + implicit none private - + !-------------------------------------------------------------------------------------------------- ! derived types type(tSolutionParams), private :: params - + type, private :: tNumerics logical :: update_gamma !< update gamma operator with current stiffness end type tNumerics - + type(tNumerics) :: num ! numerics parameters. Better name? - + !-------------------------------------------------------------------------------------------------- ! PETSc data DM, private :: da @@ -46,7 +46,7 @@ module grid_mech_spectral_polarisation ! common pointwise data real(pReal), private, dimension(:,:,:,:,:), allocatable :: & F_lastInc, & !< field of previous compatible deformation gradients - F_tau_lastInc, & !< field of previous incompatible deformation gradient + F_tau_lastInc, & !< field of previous incompatible deformation gradient Fdot, & !< field of assumed rate of compatible deformation gradient F_tauDot !< field of assumed rate of incopatible deformation gradient @@ -58,25 +58,25 @@ module grid_mech_spectral_polarisation F_aim_lastInc = math_I3, & !< previous average deformation gradient F_av = 0.0_pReal, & !< average incompatible def grad field P_av = 0.0_pReal !< average 1st Piola--Kirchhoff stress - + character(len=pStringLen), private :: incInfo !< time and increment information real(pReal), private, dimension(3,3,3,3) :: & - C_volAvg = 0.0_pReal, & !< current volume average stiffness + C_volAvg = 0.0_pReal, & !< current volume average stiffness C_volAvgLastInc = 0.0_pReal, & !< previous volume average stiffness C_minMaxAvg = 0.0_pReal, & !< current (min+max)/2 stiffness C_minMaxAvgLastInc = 0.0_pReal, & !< previous (min+max)/2 stiffness S = 0.0_pReal, & !< current compliance (filled up with zeros) C_scale = 0.0_pReal, & S_scale = 0.0_pReal - + real(pReal), private :: & err_BC, & !< deviation from stress BC err_curl, & !< RMS of curl of F err_div !< RMS of div of P - + integer, private :: & totalIter = 0 !< total iteration in current increment - + public :: & grid_mech_spectral_polarisation_init, & grid_mech_spectral_polarisation_solution, & @@ -90,26 +90,26 @@ contains !> @brief allocates all necessary fields and fills them with data, potentially from restart info !-------------------------------------------------------------------------------------------------- subroutine grid_mech_spectral_polarisation_init - + real(pReal), dimension(3,3,grid(1),grid(2),grid3) :: P real(pReal), dimension(3,3) :: & temp33_Real = 0.0_pReal - + PetscErrorCode :: ierr PetscScalar, pointer, dimension(:,:,:,:) :: & FandF_tau, & ! overall pointer to solution data F, & ! specific (sub)pointer F_tau ! specific (sub)pointer - PetscInt, dimension(0:worldsize-1) :: localK + PetscInt, dimension(0:worldsize-1) :: localK integer(HID_T) :: fileHandle, groupHandle integer :: fileUnit character(len=pStringLen) :: fileName - + write(6,'(/,a)') ' <<<+- grid_mech_spectral_polarisation init -+>>>'; flush(6) - + write(6,'(/,a)') ' Shanthraj et al., International Journal of Plasticity 66:31–45, 2015' write(6,'(a)') ' https://doi.org/10.1016/j.ijplas.2014.02.006' - + num%update_gamma = config_numerics%getInt('update_gamma',defaultVal=0) > 0 !-------------------------------------------------------------------------------------------------- @@ -125,11 +125,11 @@ subroutine grid_mech_spectral_polarisation_init allocate(Fdot (3,3,grid(1),grid(2),grid3),source = 0.0_pReal) allocate(F_tau_lastInc(3,3,grid(1),grid(2),grid3),source = 0.0_pReal) allocate(F_tauDot (3,3,grid(1),grid(2),grid3),source = 0.0_pReal) - + !-------------------------------------------------------------------------------------------------- ! initialize solver specific parts of PETSc call SNESCreate(PETSC_COMM_WORLD,snes,ierr); CHKERRQ(ierr) - call SNESSetOptionsPrefix(snes,'mech_',ierr);CHKERRQ(ierr) + call SNESSetOptionsPrefix(snes,'mech_',ierr);CHKERRQ(ierr) localK = 0 localK(worldrank) = grid3 call MPI_Allreduce(MPI_IN_PLACE,localK,worldsize,MPI_INTEGER,MPI_SUM,PETSC_COMM_WORLD,ierr) @@ -147,24 +147,24 @@ subroutine grid_mech_spectral_polarisation_init call DMsetUp(da,ierr); CHKERRQ(ierr) call DMcreateGlobalVector(da,solution_vec,ierr); CHKERRQ(ierr) ! global solution vector (grid x 18, i.e. every def grad tensor) call DMDASNESsetFunctionLocal(da,INSERT_VALUES,formResidual,PETSC_NULL_SNES,ierr) ! residual vector of same shape as solution vector - CHKERRQ(ierr) + CHKERRQ(ierr) call SNESsetConvergenceTest(snes,converged,PETSC_NULL_SNES,PETSC_NULL_FUNCTION,ierr) ! specify custom convergence check function "converged" CHKERRQ(ierr) call SNESsetFromOptions(snes,ierr); CHKERRQ(ierr) ! pull it all together with additional CLI arguments !-------------------------------------------------------------------------------------------------- -! init fields +! init fields call DMDAVecGetArrayF90(da,solution_vec,FandF_tau,ierr); CHKERRQ(ierr) ! places pointer on PETSc data F => FandF_tau(0: 8,:,:,:) F_tau => FandF_tau(9:17,:,:,:) - + restartRead: if (interface_restartInc > 0) then write(6,'(/,a,i0,a)') ' reading restart data of increment ', interface_restartInc, ' from file' write(fileName,'(a,a,i0,a)') trim(getSolverJobName()),'_',worldrank,'.hdf5' fileHandle = HDF5_openFile(fileName) groupHandle = HDF5_openGroup(fileHandle,'solver') - + call HDF5_read(groupHandle,F_aim, 'F_aim') call HDF5_read(groupHandle,F_aim_lastInc,'F_aim_lastInc') call HDF5_read(groupHandle,F_aimDot, 'F_aimDot') @@ -172,21 +172,21 @@ subroutine grid_mech_spectral_polarisation_init call HDF5_read(groupHandle,F_lastInc, 'F_lastInc') call HDF5_read(groupHandle,F_tau, 'F_tau') call HDF5_read(groupHandle,F_tau_lastInc,'F_tau_lastInc') - + elseif (interface_restartInc == 0) then restartRead F_lastInc = spread(spread(spread(math_I3,3,grid(1)),4,grid(2)),5,grid3) ! initialize to identity F = reshape(F_lastInc,[9,grid(1),grid(2),grid3]) F_tau = 2.0_pReal*F F_tau_lastInc = 2.0_pReal*F_lastInc endif restartRead - + materialpoint_F0 = reshape(F_lastInc, [3,3,1,product(grid(1:2))*grid3]) ! set starting condition for materialpoint_stressAndItsTangent call Utilities_updateCoords(reshape(F,shape(F_lastInc))) call Utilities_constitutiveResponse(P,temp33_Real,C_volAvg,C_minMaxAvg, & ! stress field, stress avg, global average of stiffness and (min+max)/2 reshape(F,shape(F_lastInc)), & ! target F 0.0_pReal) ! time increment call DMDAVecRestoreArrayF90(da,solution_vec,FandF_tau,ierr); CHKERRQ(ierr) ! deassociate pointer - + restartRead2: if (interface_restartInc > 0) then write(6,'(/,a,i0,a)') ' reading more restart data of increment ', interface_restartInc, ' from file' call HDF5_read(groupHandle,C_volAvg, 'C_volAvg') @@ -200,12 +200,12 @@ subroutine grid_mech_spectral_polarisation_init call MPI_File_read(fileUnit,C_minMaxAvg,81,MPI_DOUBLE,MPI_STATUS_IGNORE,ierr) call MPI_File_close(fileUnit,ierr) endif restartRead2 - + call utilities_updateGamma(C_minMaxAvg) call utilities_saveReferenceStiffness C_scale = C_minMaxAvg S_scale = math_invSym3333(C_minMaxAvg) - + end subroutine grid_mech_spectral_polarisation_init @@ -229,9 +229,9 @@ function grid_mech_spectral_polarisation_solution(incInfoIn,timeinc,timeinc_old, solution !-------------------------------------------------------------------------------------------------- ! PETSc Data - PetscErrorCode :: ierr + PetscErrorCode :: ierr SNESConvergedReason :: reason - + incInfo = incInfoIn !-------------------------------------------------------------------------------------------------- @@ -241,10 +241,10 @@ function grid_mech_spectral_polarisation_solution(incInfoIn,timeinc,timeinc_old, call utilities_updateGamma(C_minMaxAvg) C_scale = C_minMaxAvg S_scale = math_invSym3333(C_minMaxAvg) - endif + endif !-------------------------------------------------------------------------------------------------- -! set module wide available data +! set module wide available data params%stress_mask = stress_BC%maskFloat params%stress_BC = stress_BC%values params%rotation_BC = rotation_BC @@ -252,13 +252,13 @@ function grid_mech_spectral_polarisation_solution(incInfoIn,timeinc,timeinc_old, params%timeincOld = timeinc_old !-------------------------------------------------------------------------------------------------- -! solve BVP +! solve BVP call SNESsolve(snes,PETSC_NULL_VEC,solution_vec,ierr); CHKERRQ(ierr) !-------------------------------------------------------------------------------------------------- ! check convergence call SNESGetConvergedReason(snes,reason,ierr); CHKERRQ(ierr) - + solution%converged = reason > 0 solution%iterationsNeeded = totalIter solution%termIll = terminallyIll @@ -299,7 +299,7 @@ subroutine grid_mech_spectral_polarisation_forward(cutBack,guess,timeinc,timeinc if (cutBack) then C_volAvg = C_volAvgLastInc C_minMaxAvg = C_minMaxAvgLastInc - else + else C_volAvgLastInc = C_volAvg C_minMaxAvgLastInc = C_minMaxAvg @@ -321,13 +321,13 @@ subroutine grid_mech_spectral_polarisation_forward(cutBack,guess,timeinc,timeinc Fdot = utilities_calculateRate(guess, & F_lastInc,reshape(F,[3,3,grid(1),grid(2),grid3]),timeinc_old, & - rotation_BC%rotTensor2(F_aimDot,active=.true.)) + rotation_BC%rotate(F_aimDot,active=.true.)) F_tauDot = utilities_calculateRate(guess, & F_tau_lastInc,reshape(F_tau,[3,3,grid(1),grid(2),grid3]), timeinc_old, & - rotation_BC%rotTensor2(F_aimDot,active=.true.)) + rotation_BC%rotate(F_aimDot,active=.true.)) F_lastInc = reshape(F, [3,3,grid(1),grid(2),grid3]) F_tau_lastInc = reshape(F_tau,[3,3,grid(1),grid(2),grid3]) - + materialpoint_F0 = reshape(F,[3,3,1,product(grid(1:2))*grid3]) endif @@ -335,7 +335,7 @@ subroutine grid_mech_spectral_polarisation_forward(cutBack,guess,timeinc,timeinc ! update average and local deformation gradients F_aim = F_aim_lastInc + F_aimDot * timeinc F = reshape(utilities_forwardField(timeinc,F_lastInc,Fdot, & ! estimate of F at end of time+timeinc that matches rotated F_aim on average - rotation_BC%rotTensor2(F_aim,active=.true.)),& + rotation_BC%rotate(F_aim,active=.true.)),& [9,grid(1),grid(2),grid3]) if (guess) then F_tau = reshape(Utilities_forwardField(timeinc,F_tau_lastInc,F_taudot), & @@ -351,7 +351,7 @@ subroutine grid_mech_spectral_polarisation_forward(cutBack,guess,timeinc,timeinc F_tau(1:9,i,j,k) = reshape(F_lambda33,[9])+F(1:9,i,j,k) enddo; enddo; enddo endif - + call DMDAVecRestoreArrayF90(da,solution_vec,FandF_tau,ierr); CHKERRQ(ierr) end subroutine grid_mech_spectral_polarisation_forward @@ -364,7 +364,7 @@ subroutine grid_mech_spectral_polarisation_updateCoords PetscErrorCode :: ierr PetscScalar, dimension(:,:,:,:), pointer :: FandF_tau - + call DMDAVecGetArrayF90(da,solution_vec,FandF_tau,ierr); CHKERRQ(ierr) call utilities_updateCoords(FandF_tau(0:8,:,:,:)) call DMDAVecRestoreArrayF90(da,solution_vec,FandF_tau,ierr); CHKERRQ(ierr) @@ -391,7 +391,7 @@ subroutine grid_mech_spectral_polarisation_restartWrite write(fileName,'(a,a,i0,a)') trim(getSolverJobName()),'_',worldrank,'.hdf5' fileHandle = HDF5_openFile(fileName,'w') groupHandle = HDF5_addGroup(fileHandle,'solver') - + call HDF5_write(groupHandle,F_aim, 'F_aim') call HDF5_write(groupHandle,F_aim_lastInc,'F_aim_lastInc') call HDF5_write(groupHandle,F_aimDot, 'F_aimDot') @@ -405,9 +405,9 @@ subroutine grid_mech_spectral_polarisation_restartWrite call HDF5_closeGroup(groupHandle) call HDF5_closeFile(fileHandle) - + if(num%update_gamma) call utilities_saveReferenceStiffness - + call DMDAVecRestoreArrayF90(da,solution_vec,FandF_tau,ierr); CHKERRQ(ierr) end subroutine grid_mech_spectral_polarisation_restartWrite @@ -417,13 +417,13 @@ end subroutine grid_mech_spectral_polarisation_restartWrite !> @brief convergence check !-------------------------------------------------------------------------------------------------- subroutine converged(snes_local,PETScIter,devNull1,devNull2,devNull3,reason,dummy,ierr) - + SNES :: snes_local PetscInt, intent(in) :: PETScIter PetscReal, intent(in) :: & devNull1, & devNull2, & - devNull3 + devNull3 SNESConvergedReason :: reason PetscObject :: dummy PetscErrorCode :: ierr @@ -431,11 +431,11 @@ subroutine converged(snes_local,PETScIter,devNull1,devNull2,devNull3,reason,dumm curlTol, & divTol, & BCTol - + curlTol = max(maxval(abs(F_aim-math_I3))*err_curl_tolRel ,err_curl_tolAbs) divTol = max(maxval(abs(P_av)) *err_div_tolRel ,err_div_tolAbs) BCTol = max(maxval(abs(P_av)) *err_stress_tolRel,err_stress_tolAbs) - + if ((totalIter >= itmin .and. & all([ err_div /divTol, & err_curl/curlTol, & @@ -456,9 +456,9 @@ subroutine converged(snes_local,PETScIter,devNull1,devNull2,devNull3,reason,dumm write(6, '(a,f12.2,a,es8.2,a,es9.2,a)') ' error curl = ', & err_curl/curlTol,' (',err_curl,' -, tol = ',curlTol,')' write(6, '(a,f12.2,a,es8.2,a,es9.2,a)') ' error BC = ', & - err_BC/BCTol, ' (',err_BC, ' Pa, tol = ',BCTol,')' + err_BC/BCTol, ' (',err_BC, ' Pa, tol = ',BCTol,')' write(6,'(/,a)') ' ===========================================================================' - flush(6) + flush(6) end subroutine converged @@ -498,7 +498,7 @@ subroutine formResidual(in, FandF_tau, & F_av = sum(sum(sum(F,dim=5),dim=4),dim=3) * wgt call MPI_Allreduce(MPI_IN_PLACE,F_av,9,MPI_DOUBLE,MPI_SUM,PETSC_COMM_WORLD,ierr) - + call SNESGetNumberFunctionEvals(snes,nfuncs,ierr); CHKERRQ(ierr) call SNESGetIterationNumber(snes,PETScIter,ierr); CHKERRQ(ierr) @@ -510,14 +510,14 @@ subroutine formResidual(in, FandF_tau, & write(6,'(1x,a,3(a,i0))') trim(incInfo), ' @ Iteration ', itmin, '≤',totalIter, '≤', itmax if (iand(debug_level(debug_spectral),debug_spectralRotation) /= 0) & write(6,'(/,a,/,3(3(f12.7,1x)/))',advance='no') & - ' deformation gradient aim (lab) =', transpose(params%rotation_BC%rotTensor2(F_aim,active=.true.)) + ' deformation gradient aim (lab) =', transpose(params%rotation_BC%rotate(F_aim,active=.true.)) write(6,'(/,a,/,3(3(f12.7,1x)/))',advance='no') & ' deformation gradient aim =', transpose(F_aim) flush(6) endif newIteration !-------------------------------------------------------------------------------------------------- -! +! tensorField_real = 0.0_pReal do k = 1, grid3; do j = 1, grid(2); do i = 1, grid(1) tensorField_real(1:3,1:3,i,j,k) = & @@ -525,15 +525,15 @@ subroutine formResidual(in, FandF_tau, & polarAlpha*matmul(F(1:3,1:3,i,j,k), & math_mul3333xx33(C_scale,F_tau(1:3,1:3,i,j,k) - F(1:3,1:3,i,j,k) - math_I3)) enddo; enddo; enddo - + !-------------------------------------------------------------------------------------------------- -! doing convolution in Fourier space +! doing convolution in Fourier space call utilities_FFTtensorForward - call utilities_fourierGammaConvolution(params%rotation_BC%rotTensor2(polarBeta*F_aim,active=.true.)) + call utilities_fourierGammaConvolution(params%rotation_BC%rotate(polarBeta*F_aim,active=.true.)) call utilities_FFTtensorBackward !-------------------------------------------------------------------------------------------------- -! constructing residual +! constructing residual residual_F_tau = polarBeta*F - tensorField_real(1:3,1:3,1:grid(1),1:grid(2),1:grid3) !-------------------------------------------------------------------------------------------------- @@ -541,13 +541,13 @@ subroutine formResidual(in, FandF_tau, & call utilities_constitutiveResponse(residual_F, & ! "residuum" gets field of first PK stress (to save memory) P_av,C_volAvg,C_minMaxAvg, & F - residual_F_tau/polarBeta,params%timeinc,params%rotation_BC) - call MPI_Allreduce(MPI_IN_PLACE,terminallyIll,1,MPI_LOGICAL,MPI_LOR,PETSC_COMM_WORLD,ierr) - + call MPI_Allreduce(MPI_IN_PLACE,terminallyIll,1,MPI_LOGICAL,MPI_LOR,PETSC_COMM_WORLD,ierr) + !-------------------------------------------------------------------------------------------------- ! stress BC handling F_aim = F_aim - math_mul3333xx33(S, ((P_av - params%stress_BC))) ! S = 0.0 for no bc err_BC = maxval(abs((1.0_pReal-params%stress_mask) * math_mul3333xx33(C_scale,F_aim & - -params%rotation_BC%rotTensor2(F_av)) + & + -params%rotation_BC%rotate(F_av)) + & params%stress_mask * (P_av-params%stress_BC))) ! mask = 0.0 for no bc ! calculate divergence tensorField_real = 0.0_pReal @@ -566,7 +566,7 @@ subroutine formResidual(in, FandF_tau, & math_mul3333xx33(C_scale,F_tau(1:3,1:3,i,j,k) - F(1:3,1:3,i,j,k) - math_I3))) & + residual_F_tau(1:3,1:3,i,j,k) enddo; enddo; enddo - + !-------------------------------------------------------------------------------------------------- ! calculating curl tensorField_real = 0.0_pReal diff --git a/src/grid/spectral_utilities.f90 b/src/grid/spectral_utilities.f90 index cd064b1f8..87e906727 100644 --- a/src/grid/spectral_utilities.f90 +++ b/src/grid/spectral_utilities.f90 @@ -18,12 +18,12 @@ module spectral_utilities use config use discretization use homogenization - + implicit none private - + include 'fftw3-mpi.f03' - + !-------------------------------------------------------------------------------------------------- ! field labels information enum, bind(c) @@ -109,8 +109,8 @@ module spectral_utilities real(pReal) :: timeinc real(pReal) :: timeincOld end type tSolutionParams - - type, private :: tNumerics + + type, private :: tNumerics real(pReal) :: & FFTW_timelimit !< timelimit for FFTW plan creation, see www.fftw.org integer :: & @@ -122,7 +122,7 @@ module spectral_utilities FFTW_plan_mode, & !< FFTW plan mode, see www.fftw.org PETSc_options end type tNumerics - + type(tNumerics) :: num ! numerics parameters. Better name? enum, bind(c) @@ -189,18 +189,18 @@ subroutine utilities_init scalarSize = 1_C_INTPTR_T, & vecSize = 3_C_INTPTR_T, & tensorSize = 9_C_INTPTR_T - + write(6,'(/,a)') ' <<<+- spectral_utilities init -+>>>' - + write(6,'(/,a)') ' Diehl, Diploma Thesis TU München, 2010' write(6,'(a)') ' https://doi.org/10.13140/2.1.3234.3840' - + write(6,'(/,a)') ' Eisenlohr et al., International Journal of Plasticity 46:37–53, 2013' write(6,'(a)') ' https://doi.org/10.1016/j.ijplas.2012.09.012' - + write(6,'(/,a)') ' Shanthraj et al., International Journal of Plasticity 66:31–45, 2015' write(6,'(a)') ' https://doi.org/10.1016/j.ijplas.2014.02.006' - + write(6,'(/,a)') ' Shanthraj et al., Handbook of Mechanics of Materials, 2019' write(6,'(a)') ' https://doi.org/10.1007/978-981-10-6855-3_80' @@ -209,34 +209,34 @@ subroutine utilities_init debugGeneral = iand(debug_level(debug_SPECTRAL),debug_LEVELBASIC) /= 0 debugRotation = iand(debug_level(debug_SPECTRAL),debug_SPECTRALROTATION) /= 0 debugPETSc = iand(debug_level(debug_SPECTRAL),debug_SPECTRALPETSC) /= 0 - + if(debugPETSc) write(6,'(3(/,a),/)') & ' Initializing PETSc with debug options: ', & trim(PETScDebug), & ' add more using the PETSc_Options keyword in numerics.config '; flush(6) - + call PETScOptionsClear(PETSC_NULL_OPTIONS,ierr) CHKERRQ(ierr) if(debugPETSc) call PETScOptionsInsertString(PETSC_NULL_OPTIONS,trim(PETSCDEBUG),ierr) CHKERRQ(ierr) call PETScOptionsInsertString(PETSC_NULL_OPTIONS,trim(petsc_options),ierr) CHKERRQ(ierr) - + grid1Red = grid(1)/2 + 1 wgt = 1.0/real(product(grid),pReal) - + write(6,'(/,a,3(i12 ))') ' grid a b c: ', grid write(6,'(a,3(es12.5))') ' size x y z: ', geomSize - + num%memory_efficient = config_numerics%getInt ('memory_efficient', defaultVal=1) > 0 num%FFTW_timelimit = config_numerics%getFloat ('fftw_timelimit', defaultVal=-1.0_pReal) num%divergence_correction = config_numerics%getInt ('divergence_correction', defaultVal=2) num%spectral_derivative = config_numerics%getString('spectral_derivative', defaultVal='continuous') num%FFTW_plan_mode = config_numerics%getString('fftw_plan_mode', defaultVal='FFTW_MEASURE') - + if (num%divergence_correction < 0 .or. num%divergence_correction > 2) & call IO_error(301,ext_msg='divergence_correction') - + select case (num%spectral_derivative) case ('continuous') spectral_derivative_ID = DERIVATIVE_CONTINUOUS_ID @@ -265,8 +265,8 @@ subroutine utilities_init else scaledGeomSize = geomSize endif - - + + select case(IO_lc(num%FFTW_plan_mode)) ! setting parameters for the plan creation of FFTW. Basically a translation from fftw3.f case('fftw_estimate') ! ordered from slow execution (but fast plan creation) to fast execution FFTW_planner_flag = FFTW_ESTIMATE @@ -285,7 +285,7 @@ subroutine utilities_init ! general initialization of FFTW (see manual on fftw.org for more details) if (pReal /= C_DOUBLE .or. kind(1) /= C_INT) call IO_error(0,ext_msg='Fortran to C') ! check for correct precision in C call fftw_set_timelimit(num%FFTW_timelimit) ! set timelimit for plan creation - + if (debugGeneral) write(6,'(/,a)') ' FFTW initialized'; flush(6) !-------------------------------------------------------------------------------------------------- @@ -295,19 +295,19 @@ subroutine utilities_init PETSC_COMM_WORLD, local_K, local_K_offset) allocate (xi1st (3,grid1Red,grid(2),grid3),source = cmplx(0.0_pReal,0.0_pReal,pReal)) ! frequencies for first derivatives, only half the size for first dimension allocate (xi2nd (3,grid1Red,grid(2),grid3),source = cmplx(0.0_pReal,0.0_pReal,pReal)) ! frequencies for second derivatives, only half the size for first dimension - + tensorField = fftw_alloc_complex(tensorSize*alloc_local) call c_f_pointer(tensorField, tensorField_real, [3_C_INTPTR_T,3_C_INTPTR_T, & 2_C_INTPTR_T*(gridFFTW(1)/2_C_INTPTR_T + 1_C_INTPTR_T),gridFFTW(2),local_K]) ! place a pointer for a real tensor representation call c_f_pointer(tensorField, tensorField_fourier, [3_C_INTPTR_T,3_C_INTPTR_T, & gridFFTW(1)/2_C_INTPTR_T + 1_C_INTPTR_T , gridFFTW(2),local_K]) ! place a pointer for a fourier tensor representation - + vectorField = fftw_alloc_complex(vecSize*alloc_local) call c_f_pointer(vectorField, vectorField_real, [3_C_INTPTR_T,& 2_C_INTPTR_T*(gridFFTW(1)/2_C_INTPTR_T + 1_C_INTPTR_T),gridFFTW(2),local_K]) ! place a pointer for a real vector representation call c_f_pointer(vectorField, vectorField_fourier,[3_C_INTPTR_T,& gridFFTW(1)/2_C_INTPTR_T + 1_C_INTPTR_T, gridFFTW(2),local_K]) ! place a pointer for a fourier vector representation - + scalarField = fftw_alloc_complex(scalarSize*alloc_local) ! allocate data for real representation (no in place transform) call c_f_pointer(scalarField, scalarField_real, & [2_C_INTPTR_T*(gridFFTW(1)/2_C_INTPTR_T + 1),gridFFTW(2),local_K]) ! place a pointer for a real scalar representation @@ -371,7 +371,7 @@ subroutine utilities_init xi1st(1:3,i,j,k-grid3Offset) = xi2nd(1:3,i,j,k-grid3Offset) endwhere enddo; enddo; enddo - + if(num%memory_efficient) then ! allocate just single fourth order tensor allocate (gamma_hat(3,3,3,3,1,1,1), source = cmplx(0.0_pReal,0.0_pReal,pReal)) else ! precalculation of gamma_hat field @@ -388,7 +388,7 @@ end subroutine utilities_init !> In case of an on-the-fly calculation, only the reference stiffness is updated. !--------------------------------------------------------------------------------------------------- subroutine utilities_updateGamma(C) - + real(pReal), intent(in), dimension(3,3,3,3) :: C !< input stiffness to store as reference stiffness complex(pReal), dimension(3,3) :: temp33_complex, xiDyad_cmplx real(pReal), dimension(6,6) :: A, A_inv @@ -396,9 +396,9 @@ subroutine utilities_updateGamma(C) i, j, k, & l, m, n, o logical :: err - + C_ref = C - + if(.not. num%memory_efficient) then gamma_hat = cmplx(0.0_pReal,0.0_pReal,pReal) ! for the singular point and any non invertible A do k = grid3Offset+1, grid3Offset+grid3; do j = 1, grid(2); do i = 1, grid1Red @@ -419,7 +419,7 @@ subroutine utilities_updateGamma(C) endif enddo; enddo; enddo endif - + end subroutine utilities_updateGamma @@ -501,17 +501,17 @@ end subroutine utilities_FFTvectorBackward !> @brief doing convolution gamma_hat * field_real, ensuring that average value = fieldAim !-------------------------------------------------------------------------------------------------- subroutine utilities_fourierGammaConvolution(fieldAim) - + real(pReal), intent(in), dimension(3,3) :: fieldAim !< desired average value of the field after convolution complex(pReal), dimension(3,3) :: temp33_complex, xiDyad_cmplx real(pReal), dimension(6,6) :: A, A_inv - + integer :: & i, j, k, & l, m, n, o logical :: err - - + + write(6,'(/,a)') ' ... doing gamma convolution ...............................................' flush(6) @@ -531,7 +531,7 @@ subroutine utilities_fourierGammaConvolution(fieldAim) temp33_complex = cmplx(A_inv(1:3,1:3),A_inv(1:3,4:6),pReal) forall(l=1:3, m=1:3, n=1:3, o=1:3) & gamma_hat(l,m,n,o,1,1,1) = temp33_complex(l,n)*conjg(-xi1st(o,i,j,k))*xi1st(m,i,j,k) - else + else gamma_hat(1:3,1:3,1:3,1:3,1,1,1) = cmplx(0.0_pReal,0.0_pReal,pReal) endif forall(l = 1:3, m = 1:3) & @@ -546,7 +546,7 @@ subroutine utilities_fourierGammaConvolution(fieldAim) tensorField_fourier(1:3,1:3,i,j,k) = temp33_Complex enddo; enddo; enddo endif memoryEfficient - + if (grid3Offset == 0) tensorField_fourier(1:3,1:3,1,1,1) = cmplx(fieldAim/wgt,0.0_pReal,pReal) end subroutine utilities_fourierGammaConvolution @@ -561,7 +561,7 @@ subroutine utilities_fourierGreenConvolution(D_ref, mobility_ref, deltaT) real(pReal), intent(in) :: mobility_ref, deltaT complex(pReal) :: GreenOp_hat integer :: i, j, k - + !-------------------------------------------------------------------------------------------------- ! do the actual spectral method calculation do k = 1, grid3; do j = 1, grid(2) ;do i = 1, grid1Red @@ -625,16 +625,16 @@ real(pReal) function utilities_curlRMS() integer :: i, j, k, l, ierr complex(pReal), dimension(3,3) :: curl_fourier complex(pReal), dimension(3) :: rescaledGeom - + write(6,'(/,a)') ' ... calculating curl ......................................................' flush(6) - + rescaledGeom = cmplx(geomSize/scaledGeomSize,0.0_pReal) - + !-------------------------------------------------------------------------------------------------- ! calculating max curl criterion in Fourier space utilities_curlRMS = 0.0_pReal - + do k = 1, grid3; do j = 1, grid(2); do i = 2, grid1Red - 1 do l = 1, 3 @@ -669,7 +669,7 @@ real(pReal) function utilities_curlRMS() utilities_curlRMS = utilities_curlRMS & + sum(real(curl_fourier)**2.0_pReal + aimag(curl_fourier)**2.0_pReal) ! this layer (Nyquist) does not have a conjugate complex counterpart (if grid(1) /= 1) enddo; enddo - + call MPI_Allreduce(MPI_IN_PLACE,utilities_curlRMS,1,MPI_DOUBLE,MPI_SUM,PETSC_COMM_WORLD,ierr) if(ierr /=0) call IO_error(894, ext_msg='utilities_curlRMS') utilities_curlRMS = sqrt(utilities_curlRMS) * wgt @@ -688,9 +688,10 @@ function utilities_maskedCompliance(rot_BC,mask_stress,C) type(rotation), intent(in) :: rot_BC !< rotation of load frame logical, intent(in), dimension(3,3) :: mask_stress !< mask of stress BC - integer :: j, k, m, n - logical, dimension(9) :: mask_stressVector - real(pReal), dimension(9,9) :: temp99_Real + integer :: i, j + logical, dimension(9) :: mask_stressVector + logical, dimension(9,9) :: mask + real(pReal), dimension(9,9) :: temp99_real integer :: size_reduced = 0 real(pReal), dimension(:,:), allocatable :: & s_reduced, & !< reduced compliance matrix (depending on number of stress BC) @@ -698,57 +699,33 @@ function utilities_maskedCompliance(rot_BC,mask_stress,C) sTimesC !< temp variable to check inversion logical :: errmatinv character(len=pStringLen):: formatString - + mask_stressVector = reshape(transpose(mask_stress), [9]) size_reduced = count(mask_stressVector) - if(size_reduced > 0 )then - allocate (c_reduced(size_reduced,size_reduced), source =0.0_pReal) - allocate (s_reduced(size_reduced,size_reduced), source =0.0_pReal) - allocate (sTimesC(size_reduced,size_reduced), source =0.0_pReal) - temp99_Real = math_3333to99(rot_BC%rotTensor4(C)) - + if(size_reduced > 0) then + temp99_real = math_3333to99(rot_BC%rotate(C)) + if(debugGeneral) then write(6,'(/,a)') ' ... updating masked compliance ............................................' write(6,'(/,a,/,9(9(2x,f12.7,1x)/))',advance='no') ' Stiffness C (load) / GPa =',& transpose(temp99_Real)*1.0e-9_pReal flush(6) endif - k = 0 ! calculate reduced stiffness - do n = 1,9 - if(mask_stressVector(n)) then - k = k + 1 - j = 0 - do m = 1,9 - if(mask_stressVector(m)) then - j = j + 1 - c_reduced(k,j) = temp99_Real(n,m) - endif; enddo; endif; enddo - + + do i = 1,9; do j = 1,9 + mask(i,j) = mask_stressVector(i) .and. mask_stressVector(j) + enddo; enddo + c_reduced = reshape(pack(temp99_Real,mask),[size_reduced,size_reduced]) + + allocate(s_reduced,mold = c_reduced) call math_invert(s_reduced, errmatinv, c_reduced) ! invert reduced stiffness if (any(IEEE_is_NaN(s_reduced))) errmatinv = .true. if (errmatinv) call IO_error(error_ID=400,ext_msg='utilities_maskedCompliance') - temp99_Real = 0.0_pReal ! fill up compliance with zeros - k = 0 - do n = 1,9 - if(mask_stressVector(n)) then - k = k + 1 - j = 0 - do m = 1,9 - if(mask_stressVector(m)) then - j = j + 1 - temp99_Real(n,m) = s_reduced(k,j) - endif; enddo; endif; enddo !-------------------------------------------------------------------------------------------------- ! check if inversion was successful sTimesC = matmul(c_reduced,s_reduced) - do m=1, size_reduced - do n=1, size_reduced - errmatinv = errmatinv & - .or. (m==n .and. abs(sTimesC(m,n)-1.0_pReal) > 1.0e-12_pReal) & ! diagonal elements of S*C should be 1 - .or. (m/=n .and. abs(sTimesC(m,n)) > 1.0e-12_pReal) ! off-diagonal elements of S*C should be 0 - enddo - enddo + errmatinv = errmatinv .or. any(dNeq(sTimesC,math_identity2nd(size_reduced),1.0e-12_pReal)) if (debugGeneral .or. errmatinv) then write(formatString, '(i2)') size_reduced formatString = '(/,a,/,'//trim(formatString)//'('//trim(formatString)//'(2x,es9.2,1x)/))' @@ -757,15 +734,18 @@ function utilities_maskedCompliance(rot_BC,mask_stress,C) write(6,trim(formatString),advance='no') ' S (load) ', transpose(s_reduced) if(errmatinv) call IO_error(error_ID=400,ext_msg='utilities_maskedCompliance') endif + temp99_real = reshape(unpack(reshape(s_reduced,[size_reduced**2]),reshape(mask,[81]),0.0_pReal),[9,9]) else temp99_real = 0.0_pReal endif + + utilities_maskedCompliance = math_99to3333(temp99_Real) + if(debugGeneral) then write(6,'(/,a,/,9(9(2x,f10.5,1x)/),/)',advance='no') & ' Masked Compliance (load) * GPa =', transpose(temp99_Real)*1.0e9_pReal flush(6) endif - utilities_maskedCompliance = math_99to3333(temp99_Real) end function utilities_maskedCompliance @@ -774,9 +754,9 @@ end function utilities_maskedCompliance !> @brief calculate scalar gradient in fourier field !-------------------------------------------------------------------------------------------------- subroutine utilities_fourierScalarGradient() - + integer :: i, j, k - + do k = 1, grid3; do j = 1, grid(2); do i = 1,grid1Red vectorField_fourier(1:3,i,j,k) = scalarField_fourier(i,j,k)*xi1st(1:3,i,j,k) ! ToDo: no -conjg? enddo; enddo; enddo @@ -788,9 +768,9 @@ end subroutine utilities_fourierScalarGradient !> @brief calculate vector divergence in fourier field !-------------------------------------------------------------------------------------------------- subroutine utilities_fourierVectorDivergence() - + integer :: i, j, k - + do k = 1, grid3; do j = 1, grid(2); do i = 1,grid1Red scalarField_fourier(i,j,k) = sum(vectorField_fourier(1:3,i,j,k)*conjg(-xi1st(1:3,i,j,k))) enddo; enddo; enddo @@ -802,9 +782,9 @@ end subroutine utilities_fourierVectorDivergence !> @brief calculate vector gradient in fourier field !-------------------------------------------------------------------------------------------------- subroutine utilities_fourierVectorGradient() - + integer :: i, j, k, m, n - + do k = 1, grid3; do j = 1, grid(2); do i = 1,grid1Red do m = 1, 3; do n = 1, 3 tensorField_fourier(m,n,i,j,k) = vectorField_fourier(m,i,j,k)*xi1st(n,i,j,k) @@ -820,7 +800,7 @@ end subroutine utilities_fourierVectorGradient subroutine utilities_fourierTensorDivergence() integer :: i, j, k - + do k = 1, grid3; do j = 1, grid(2); do i = 1,grid1Red vectorField_fourier(:,i,j,k) = matmul(tensorField_fourier(:,:,i,j,k),conjg(-xi1st(:,i,j,k))) enddo; enddo; enddo @@ -833,28 +813,28 @@ end subroutine utilities_fourierTensorDivergence !-------------------------------------------------------------------------------------------------- subroutine utilities_constitutiveResponse(P,P_av,C_volAvg,C_minmaxAvg,& F,timeinc,rotation_BC) - + real(pReal), intent(out), dimension(3,3,3,3) :: C_volAvg, C_minmaxAvg !< average stiffness real(pReal), intent(out), dimension(3,3) :: P_av !< average PK stress real(pReal), intent(out), dimension(3,3,grid(1),grid(2),grid3) :: P !< PK stress real(pReal), intent(in), dimension(3,3,grid(1),grid(2),grid3) :: F !< deformation gradient target real(pReal), intent(in) :: timeinc !< loading time type(rotation), intent(in), optional :: rotation_BC !< rotation of load frame - - + + integer :: & i,ierr real(pReal), dimension(3,3,3,3) :: dPdF_max, dPdF_min real(pReal) :: dPdF_norm_max, dPdF_norm_min real(pReal), dimension(2) :: valueAndRank !< pair of min/max norm of dPdF to synchronize min/max of dPdF - + write(6,'(/,a)') ' ... evaluating constitutive response ......................................' flush(6) - + materialpoint_F = reshape(F,[3,3,1,product(grid(1:2))*grid3]) ! set materialpoint target F to estimated field - + call materialpoint_stressAndItsTangent(.true.,timeinc) ! calculate P field - + P = reshape(materialpoint_P, [3,3,grid(1),grid(2),grid3]) P_av = sum(sum(sum(P,dim=5),dim=4),dim=3) * wgt ! average of P call MPI_Allreduce(MPI_IN_PLACE,P_av,9,MPI_DOUBLE,MPI_SUM,PETSC_COMM_WORLD,ierr) @@ -862,11 +842,11 @@ subroutine utilities_constitutiveResponse(P,P_av,C_volAvg,C_minmaxAvg,& write(6,'(/,a,/,3(3(2x,f12.4,1x)/))',advance='no') ' Piola--Kirchhoff stress (lab) / MPa =',& transpose(P_av)*1.e-6_pReal if(present(rotation_BC)) & - P_av = rotation_BC%rotTensor2(P_av) + P_av = rotation_BC%rotate(P_av) write(6,'(/,a,/,3(3(2x,f12.4,1x)/))',advance='no') ' Piola--Kirchhoff stress / MPa =',& transpose(P_av)*1.e-6_pReal flush(6) - + dPdF_max = 0.0_pReal dPdF_norm_max = 0.0_pReal dPdF_min = huge(1.0_pReal) @@ -881,21 +861,21 @@ subroutine utilities_constitutiveResponse(P,P_av,C_volAvg,C_minmaxAvg,& dPdF_norm_min = sum(materialpoint_dPdF(1:3,1:3,1:3,1:3,1,i)**2.0_pReal) endif end do - + valueAndRank = [dPdF_norm_max,real(worldrank,pReal)] call MPI_Allreduce(MPI_IN_PLACE,valueAndRank,1, MPI_2DOUBLE_PRECISION, MPI_MAXLOC, PETSC_COMM_WORLD, ierr) if (ierr /= 0) call IO_error(894, ext_msg='MPI_Allreduce max') call MPI_Bcast(dPdF_max,81,MPI_DOUBLE,int(valueAndRank(2)),PETSC_COMM_WORLD, ierr) if (ierr /= 0) call IO_error(894, ext_msg='MPI_Bcast max') - + valueAndRank = [dPdF_norm_min,real(worldrank,pReal)] call MPI_Allreduce(MPI_IN_PLACE,valueAndRank,1, MPI_2DOUBLE_PRECISION, MPI_MINLOC, PETSC_COMM_WORLD, ierr) if (ierr /= 0) call IO_error(894, ext_msg='MPI_Allreduce min') call MPI_Bcast(dPdF_min,81,MPI_DOUBLE,int(valueAndRank(2)),PETSC_COMM_WORLD, ierr) if (ierr /= 0) call IO_error(894, ext_msg='MPI_Bcast min') - + C_minmaxAvg = 0.5_pReal*(dPdF_max + dPdF_min) - + C_volAvg = sum(sum(materialpoint_dPdF,dim=6),dim=5) call MPI_Allreduce(MPI_IN_PLACE,C_volAvg,81,MPI_DOUBLE,MPI_SUM,PETSC_COMM_WORLD,ierr) C_volAvg = C_volAvg * wgt @@ -908,7 +888,7 @@ end subroutine utilities_constitutiveResponse !> @brief calculates forward rate, either guessing or just add delta/timeinc !-------------------------------------------------------------------------------------------------- pure function utilities_calculateRate(heterogeneous,field0,field,dt,avRate) - + real(pReal), intent(in), dimension(3,3) :: & avRate !< homogeneous addon real(pReal), intent(in) :: & @@ -920,7 +900,7 @@ pure function utilities_calculateRate(heterogeneous,field0,field,dt,avRate) field !< data of current step real(pReal), dimension(3,3,grid(1),grid(2),grid3) :: & utilities_calculateRate - + if (heterogeneous) then utilities_calculateRate = (field-field0) / dt else @@ -971,14 +951,14 @@ pure function utilities_getFreqDerivative(k_s) complex(pReal), dimension(3) :: utilities_getFreqDerivative select case (spectral_derivative_ID) - case (DERIVATIVE_CONTINUOUS_ID) + case (DERIVATIVE_CONTINUOUS_ID) utilities_getFreqDerivative = cmplx(0.0_pReal, 2.0_pReal*PI*real(k_s,pReal)/geomSize,pReal) - case (DERIVATIVE_CENTRAL_DIFF_ID) + case (DERIVATIVE_CENTRAL_DIFF_ID) utilities_getFreqDerivative = cmplx(0.0_pReal, sin(2.0_pReal*PI*real(k_s,pReal)/real(grid,pReal)), pReal)/ & - cmplx(2.0_pReal*geomSize/real(grid,pReal), 0.0_pReal, pReal) - - case (DERIVATIVE_FWBW_DIFF_ID) + cmplx(2.0_pReal*geomSize/real(grid,pReal), 0.0_pReal, pReal) + + case (DERIVATIVE_FWBW_DIFF_ID) utilities_getFreqDerivative(1) = & cmplx(cos(2.0_pReal*PI*real(k_s(1),pReal)/real(grid(1),pReal)) - 1.0_pReal, & sin(2.0_pReal*PI*real(k_s(1),pReal)/real(grid(1),pReal)), pReal)* & @@ -986,7 +966,7 @@ pure function utilities_getFreqDerivative(k_s) sin(2.0_pReal*PI*real(k_s(2),pReal)/real(grid(2),pReal)), pReal)* & cmplx(cos(2.0_pReal*PI*real(k_s(3),pReal)/real(grid(3),pReal)) + 1.0_pReal, & sin(2.0_pReal*PI*real(k_s(3),pReal)/real(grid(3),pReal)), pReal)/ & - cmplx(4.0_pReal*geomSize(1)/real(grid(1),pReal), 0.0_pReal, pReal) + cmplx(4.0_pReal*geomSize(1)/real(grid(1),pReal), 0.0_pReal, pReal) utilities_getFreqDerivative(2) = & cmplx(cos(2.0_pReal*PI*real(k_s(1),pReal)/real(grid(1),pReal)) + 1.0_pReal, & sin(2.0_pReal*PI*real(k_s(1),pReal)/real(grid(1),pReal)), pReal)* & @@ -994,7 +974,7 @@ pure function utilities_getFreqDerivative(k_s) sin(2.0_pReal*PI*real(k_s(2),pReal)/real(grid(2),pReal)), pReal)* & cmplx(cos(2.0_pReal*PI*real(k_s(3),pReal)/real(grid(3),pReal)) + 1.0_pReal, & sin(2.0_pReal*PI*real(k_s(3),pReal)/real(grid(3),pReal)), pReal)/ & - cmplx(4.0_pReal*geomSize(2)/real(grid(2),pReal), 0.0_pReal, pReal) + cmplx(4.0_pReal*geomSize(2)/real(grid(2),pReal), 0.0_pReal, pReal) utilities_getFreqDerivative(3) = & cmplx(cos(2.0_pReal*PI*real(k_s(1),pReal)/real(grid(1),pReal)) + 1.0_pReal, & sin(2.0_pReal*PI*real(k_s(1),pReal)/real(grid(1),pReal)), pReal)* & @@ -1002,7 +982,7 @@ pure function utilities_getFreqDerivative(k_s) sin(2.0_pReal*PI*real(k_s(2),pReal)/real(grid(2),pReal)), pReal)* & cmplx(cos(2.0_pReal*PI*real(k_s(3),pReal)/real(grid(3),pReal)) - 1.0_pReal, & sin(2.0_pReal*PI*real(k_s(3),pReal)/real(grid(3),pReal)), pReal)/ & - cmplx(4.0_pReal*geomSize(3)/real(grid(3),pReal), 0.0_pReal, pReal) + cmplx(4.0_pReal*geomSize(3)/real(grid(3),pReal), 0.0_pReal, pReal) end select end function utilities_getFreqDerivative @@ -1014,7 +994,7 @@ end function utilities_getFreqDerivative ! convolution !-------------------------------------------------------------------------------------------------- subroutine utilities_updateCoords(F) - + real(pReal), dimension(3,3,grid(1),grid(2),grid3), intent(in) :: F real(pReal), dimension(3, grid(1),grid(2),grid3) :: IPcoords real(pReal), dimension(3, grid(1),grid(2),grid3+2) :: IPfluct_padded ! Fluctuations of cell center displacement (padded along z for MPI) @@ -1040,7 +1020,7 @@ subroutine utilities_updateCoords(F) 1, 0, 1, & 1, 1, 1, & 0, 1, 1 ], [3,8]) - + step = geomSize/real(grid, pReal) !-------------------------------------------------------------------------------------------------- ! integration in Fourier space to get fluctuations of cell center discplacements @@ -1057,27 +1037,27 @@ subroutine utilities_updateCoords(F) enddo; enddo; enddo call fftw_mpi_execute_dft_c2r(planVectorBack,vectorField_fourier,vectorField_real) - + !-------------------------------------------------------------------------------------------------- ! average F if (grid3Offset == 0) Favg = real(tensorField_fourier(1:3,1:3,1,1,1),pReal)*wgt call MPI_Bcast(Favg,9,MPI_DOUBLE,0,PETSC_COMM_WORLD,ierr) if(ierr /=0) call IO_error(894, ext_msg='update_IPcoords/MPI_Bcast') - + !-------------------------------------------------------------------------------------------------- ! pad cell center fluctuations along z-direction (needed when running MPI simulation) IPfluct_padded(1:3,1:grid(1),1:grid(2),2:grid3+1) = vectorField_real(1:3,1:grid(1),1:grid(2),1:grid3) c = product(shape(IPfluct_padded(:,:,:,1))) !< amount of data to transfer rank_t = modulo(worldrank+1,worldsize) rank_b = modulo(worldrank-1,worldsize) - + ! send bottom layer to process below call MPI_Isend(IPfluct_padded(:,:,:,2), c,MPI_DOUBLE,rank_b,0,PETSC_COMM_WORLD,r,ierr) if(ierr /=0) call IO_error(894, ext_msg='update_IPcoords/MPI_Isend') call MPI_Irecv(IPfluct_padded(:,:,:,grid3+2),c,MPI_DOUBLE,rank_t,0,PETSC_COMM_WORLD,r,ierr) if(ierr /=0) call IO_error(894, ext_msg='update_IPcoords/MPI_Irecv') call MPI_Wait(r,s,ierr) - + ! send top layer to process above if(ierr /=0) call IO_error(894, ext_msg='update_IPcoords/MPI_Wait') call MPI_Isend(IPfluct_padded(:,:,:,grid3+1),c,MPI_DOUBLE,rank_t,0,PETSC_COMM_WORLD,r,ierr) @@ -1085,9 +1065,9 @@ subroutine utilities_updateCoords(F) call MPI_Irecv(IPfluct_padded(:,:,:,1), c,MPI_DOUBLE,rank_b,0,PETSC_COMM_WORLD,r,ierr) if(ierr /=0) call IO_error(894, ext_msg='update_IPcoords/MPI_Irecv') call MPI_Wait(r,s,ierr) - + !-------------------------------------------------------------------------------------------------- - ! calculate nodal displacements + ! calculate nodal displacements nodeCoords = 0.0_pReal do k = 0,grid3; do j = 0,grid(2); do i = 0,grid(1) nodeCoords(1:3,i+1,j+1,k+1) = matmul(Favg,step*(real([i,j,k+grid3Offset],pReal))) @@ -1097,17 +1077,17 @@ subroutine utilities_updateCoords(F) + IPfluct_padded(1:3,modulo(me(1)-1,grid(1))+1,modulo(me(2)-1,grid(2))+1,me(3)+1)*0.125_pReal enddo averageFluct enddo; enddo; enddo - + !-------------------------------------------------------------------------------------------------- ! calculate cell center displacements do k = 1,grid3; do j = 1,grid(2); do i = 1,grid(1) IPcoords(1:3,i,j,k) = vectorField_real(1:3,i,j,k) & + matmul(Favg,step*real([i,j,k+grid3Offset]-0.5_pReal,pReal)) enddo; enddo; enddo - + call discretization_setNodeCoords(reshape(NodeCoords,[3,(grid(1)+1)*(grid(2)+1)*(grid3+1)])) call discretization_setIPcoords (reshape(IPcoords, [3,grid(1)*grid(2)*grid3])) - + end subroutine utilities_updateCoords @@ -1115,7 +1095,7 @@ end subroutine utilities_updateCoords !> @brief Write out the current reference stiffness for restart. !--------------------------------------------------------------------------------------------------- subroutine utilities_saveReferenceStiffness - + integer :: & fileUnit @@ -1125,7 +1105,7 @@ subroutine utilities_saveReferenceStiffness write(fileUnit) C_ref close(fileUnit) endif - + end subroutine utilities_saveReferenceStiffness end module spectral_utilities diff --git a/src/homogenization.f90 b/src/homogenization.f90 index 08e02290b..9d935866c 100644 --- a/src/homogenization.f90 +++ b/src/homogenization.f90 @@ -146,9 +146,7 @@ subroutine homogenization_init !-------------------------------------------------------------------------------------------------- ! allocate and initialize global variables allocate(materialpoint_dPdF(3,3,3,3,discretization_nIP,discretization_nElem), source=0.0_pReal) - allocate(materialpoint_F0(3,3,discretization_nIP,discretization_nElem), source=0.0_pReal) materialpoint_F0 = spread(spread(math_I3,3,discretization_nIP),4,discretization_nElem) ! initialize to identity - allocate(materialpoint_F(3,3,discretization_nIP,discretization_nElem), source=0.0_pReal) materialpoint_F = materialpoint_F0 ! initialize to identity allocate(materialpoint_subF0(3,3,discretization_nIP,discretization_nElem), source=0.0_pReal) allocate(materialpoint_subF(3,3,discretization_nIP,discretization_nElem), source=0.0_pReal) @@ -333,12 +331,10 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) !$OMP FLUSH(terminallyIll) if (.not. terminallyIll) then ! so first signals terminally ill... !$OMP CRITICAL (write2out) - write(6,*) 'Integration point ', i,' at element ', e, ' terminally ill' + write(6,*) 'Integration point ', i,' at element ', e, ' terminally ill' !$OMP END CRITICAL (write2out) endif - !$OMP CRITICAL (setTerminallyIll) - terminallyIll = .true. ! ...and kills all others - !$OMP END CRITICAL (setTerminallyIll) + terminallyIll = .true. ! ...and kills all others else ! cutback makes sense materialpoint_subStep(i,e) = subStepSizeHomog * materialpoint_subStep(i,e) ! crystallite had severe trouble, so do a significant cutback diff --git a/src/homogenization_mech_RGC.f90 b/src/homogenization_mech_RGC.f90 index 9cb7efdc8..2152211b7 100644 --- a/src/homogenization_mech_RGC.f90 +++ b/src/homogenization_mech_RGC.f90 @@ -9,17 +9,6 @@ submodule(homogenization) homogenization_mech_RGC use rotations - enum, bind(c) - enumerator :: & - undefined_ID, & - constitutivework_ID, & - penaltyenergy_ID, & - volumediscrepancy_ID, & - averagerelaxrate_ID,& - maximumrelaxrate_ID,& - magnitudemismatch_ID - end enum - type :: tParameters integer, dimension(:), allocatable :: & Nconstituents @@ -31,8 +20,8 @@ submodule(homogenization) homogenization_mech_RGC angles integer :: & of_debug = 0 - integer(kind(undefined_ID)), dimension(:), allocatable :: & - outputID + character(len=pStringLen), allocatable, dimension(:) :: & + output end type tParameters type :: tRGCstate @@ -71,23 +60,18 @@ module subroutine mech_RGC_init integer :: & Ninstance, & - h, i, & + h, & NofMyHomog, & sizeState, nIntFaceTot - integer(kind(undefined_ID)) :: & - outputID - - character(len=pStringLen), dimension(:), allocatable :: & - outputs - write(6,'(/,a)') ' <<<+- homogenization_'//HOMOGENIZATION_RGC_label//' init -+>>>' + write(6,'(/,a)') ' <<<+- homogenization_'//HOMOGENIZATION_RGC_label//' init -+>>>'; flush(6) - write(6,'(/,a)') ' Tjahjanto et al., International Journal of Material Forming 2(1):939–942, 2009' - write(6,'(a)') ' https://doi.org/10.1007/s12289-009-0619-1' + write(6,'(/,a)') ' Tjahjanto et al., International Journal of Material Forming 2(1):939–942, 2009' + write(6,'(a)') ' https://doi.org/10.1007/s12289-009-0619-1' - write(6,'(/,a)') ' Tjahjanto et al., Modelling and Simulation in Materials Science and Engineering 18:015006, 2010' - write(6,'(a)') ' https://doi.org/10.1088/0965-0393/18/1/015006' + write(6,'(/,a)') ' Tjahjanto et al., Modelling and Simulation in Materials Science and Engineering 18:015006, 2010' + write(6,'(a)') ' https://doi.org/10.1088/0965-0393/18/1/015006' Ninstance = count(homogenization_type == HOMOGENIZATION_RGC_ID) if (iand(debug_level(debug_HOMOGENIZATION),debug_levelBasic) /= 0) & @@ -123,34 +107,8 @@ module subroutine mech_RGC_init prm%dAlpha = config%getFloats('grainsize', requiredSize=3) prm%angles = config%getFloats('clusterorientation',requiredSize=3) - outputs = config%getStrings('(output)',defaultVal=emptyStringArray) - allocate(prm%outputID(0)) - - do i=1, size(outputs) - outputID = undefined_ID - select case(outputs(i)) - - case('constitutivework') - outputID = constitutivework_ID - case('penaltyenergy') - outputID = penaltyenergy_ID - case('volumediscrepancy') - outputID = volumediscrepancy_ID - case('averagerelaxrate') - outputID = averagerelaxrate_ID - case('maximumrelaxrate') - outputID = maximumrelaxrate_ID - case('magnitudemismatch') - outputID = magnitudemismatch_ID - - end select - - if (outputID /= undefined_ID) then - prm%outputID = [prm%outputID , outputID] - endif - - enddo - + prm%output = config%getStrings('(output)',defaultVal=emptyStringArray) + NofMyHomog = count(material_homogenizationAt == h) nIntFaceTot = 3*( (prm%Nconstituents(1)-1)*prm%Nconstituents(2)*prm%Nconstituents(3) & + prm%Nconstituents(1)*(prm%Nconstituents(2)-1)*prm%Nconstituents(3) & @@ -711,7 +669,7 @@ module procedure mech_RGC_updateState nDef = 0.0_pReal do i = 1,3; do j = 1,3 do k = 1,3; do l = 1,3 - nDef(i,j) = nDef(i,j) - nVect(k)*gDef(i,l)*math_civita(j,k,l) ! compute the interface mismatch tensor from the jump of deformation gradient + nDef(i,j) = nDef(i,j) - nVect(k)*gDef(i,l)*math_LeviCivita(j,k,l) ! compute the interface mismatch tensor from the jump of deformation gradient enddo; enddo nDefNorm = nDefNorm + nDef(i,j)**2.0_pReal ! compute the norm of the mismatch tensor enddo; enddo @@ -731,7 +689,7 @@ module procedure mech_RGC_updateState rPen(i,j,iGrain) = rPen(i,j,iGrain) + 0.5_pReal*(muGrain*bgGrain + muGNghb*bgGNghb)*prm%xiAlpha & *surfCorr(abs(intFace(1)))/prm%dAlpha(abs(intFace(1))) & *cosh(prm%ciAlpha*nDefNorm) & - *0.5_pReal*nVect(l)*nDef(i,k)/nDefNorm*math_civita(k,l,j) & + *0.5_pReal*nVect(l)*nDef(i,k)/nDefNorm*math_LeviCivita(k,l,j) & *tanh(nDefNorm/xSmoo_RGC) enddo; enddo;enddo; enddo enddo interfaceLoop @@ -934,26 +892,24 @@ module subroutine mech_RGC_results(instance,group) integer :: o associate(stt => state(instance), dst => dependentState(instance), prm => param(instance)) - - outputsLoop: do o = 1,size(prm%outputID) - select case(prm%outputID(o)) - - case (constitutivework_ID) + outputsLoop: do o = 1,size(prm%output) + select case(trim(prm%output(o))) + case('constitutivework') call results_writeDataset(group,stt%work,'W',& 'work density','J/m³') - case (magnitudemismatch_ID) + case('magnitudemismatch') call results_writeDataset(group,dst%mismatch,'N',& 'average mismatch tensor','1') - case (penaltyenergy_ID) + case('penaltyenergy') call results_writeDataset(group,stt%penaltyEnergy,'R',& 'mismatch penalty density','J/m³') - case (volumediscrepancy_ID) + case('volumediscrepancy') call results_writeDataset(group,dst%volumeDiscrepancy,'Delta_V',& 'volume discrepancy','m³') - case (maximumrelaxrate_ID) + case('maximumrelaxrate') call results_writeDataset(group,dst%relaxationrate_max,'max_alpha_dot',& 'maximum relaxation rate','m/s') - case (averagerelaxrate_ID) + case('averagerelaxrate') call results_writeDataset(group,dst%relaxationrate_avg,'avg_alpha_dot',& 'average relaxation rate','m/s') end select diff --git a/src/lattice.f90 b/src/lattice.f90 index 3d624ba48..ab2f43284 100644 --- a/src/lattice.f90 +++ b/src/lattice.f90 @@ -15,14 +15,14 @@ module lattice implicit none private - + ! BEGIN DEPRECATED integer, parameter, public :: & LATTICE_maxNcleavageFamily = 3 !< max # of transformation system families over lattice structures - + integer, allocatable, dimension(:,:), protected, public :: & lattice_NcleavageSystem !< total # of transformation systems in each family - + real(pReal), allocatable, dimension(:,:,:,:,:), protected, public :: & lattice_Scleavage !< Schmid matrices for cleavage systems ! END DEPRECATED @@ -32,16 +32,16 @@ module lattice ! face centered cubic integer, dimension(2), parameter :: & LATTICE_FCC_NSLIPSYSTEM = [12, 6] !< # of slip systems per family for fcc - + integer, dimension(1), parameter :: & LATTICE_FCC_NTWINSYSTEM = [12] !< # of twin systems per family for fcc - + integer, dimension(1), parameter :: & LATTICE_FCC_NTRANSSYSTEM = [12] !< # of transformation systems per family for fcc - + integer, dimension(2), parameter :: & LATTICE_FCC_NCLEAVAGESYSTEM = [3, 4] !< # of cleavage systems per family for fcc - + integer, parameter :: & #ifndef __PGI LATTICE_FCC_NSLIP = sum(LATTICE_FCC_NSLIPSYSTEM), & !< total # of slip systems for fcc @@ -78,7 +78,7 @@ module lattice 0, 1, 1, 0, 1,-1, & 0, 1,-1, 0, 1, 1 & ],pReal),shape(LATTICE_FCC_SYSTEMSLIP)) !< Slip system <110>{111} directions. Sorted according to Eisenlohr & Hantcherli - + real(pReal), dimension(3+3,LATTICE_FCC_NTWIN), parameter :: & LATTICE_FCC_SYSTEMTWIN = reshape(real( [& -2, 1, 1, 1, 1, 1, & @@ -94,7 +94,7 @@ module lattice -1,-2,-1, -1, 1,-1, & -1, 1, 2, -1, 1,-1 & ],pReal),shape(LATTICE_FCC_SYSTEMTWIN)) !< Twin system <112>{111} directions. Sorted according to Eisenlohr & Hantcherli - + integer, dimension(2,LATTICE_FCC_NTWIN), parameter, public :: & LATTICE_FCC_TWINNUCLEATIONSLIPPAIR = reshape( [& 2,3, & @@ -110,7 +110,7 @@ module lattice 10,12, & 10,11 & ],shape(LATTICE_FCC_TWINNUCLEATIONSLIPPAIR)) - + real(pReal), dimension(3+3,LATTICE_FCC_NCLEAVAGE), parameter :: & LATTICE_FCC_SYSTEMCLEAVAGE = reshape(real([& ! Cleavage direction Plane normal @@ -122,18 +122,18 @@ module lattice -1, 0,-1, 1,-1,-1, & 0, 1, 1, -1, 1,-1 & ],pReal),shape(LATTICE_FCC_SYSTEMCLEAVAGE)) - + !-------------------------------------------------------------------------------------------------- ! body centered cubic integer, dimension(2), parameter :: & LATTICE_BCC_NSLIPSYSTEM = [12, 12] !< # of slip systems per family for bcc - + integer, dimension(1), parameter :: & LATTICE_BCC_NTWINSYSTEM = [12] !< # of twin systems per family for bcc - + integer, dimension(2), parameter :: & LATTICE_BCC_NCLEAVAGESYSTEM = [3, 6] !< # of cleavage systems per family for bcc - + integer, parameter :: & #ifndef __PGI LATTICE_BCC_NSLIP = sum(LATTICE_BCC_NSLIPSYSTEM), & !< total # of slip systems for bcc @@ -175,7 +175,7 @@ module lattice -1, 1, 1, 1,-1, 2, & 1, 1, 1, 1, 1,-2 & ],pReal),shape(LATTICE_BCC_SYSTEMSLIP)) - + real(pReal), dimension(3+3,LATTICE_BCC_NTWIN), parameter :: & LATTICE_BCC_SYSTEMTWIN = reshape(real([& ! Twin system <111>{112} @@ -191,8 +191,8 @@ module lattice 1,-1, 1, -1, 1, 2, & -1, 1, 1, 1,-1, 2, & 1, 1, 1, 1, 1,-2 & - ],pReal),shape(LATTICE_BCC_SYSTEMTWIN)) - + ],pReal),shape(LATTICE_BCC_SYSTEMTWIN)) + real(pReal), dimension(3+3,LATTICE_BCC_NCLEAVAGE), parameter :: & LATTICE_BCC_SYSTEMCLEAVAGE = reshape(real([& ! Cleavage direction Plane normal @@ -206,18 +206,18 @@ module lattice -1, 1, 1, 1, 1, 0, & 1, 1, 1, -1, 1, 0 & ],pReal),shape(LATTICE_BCC_SYSTEMCLEAVAGE)) - + !-------------------------------------------------------------------------------------------------- ! hexagonal integer, dimension(6), parameter :: & LATTICE_HEX_NSLIPSYSTEM = [3, 3, 3, 6, 12, 6] !< # of slip systems per family for hex - + integer, dimension(4), parameter :: & LATTICE_HEX_NTWINSYSTEM = [6, 6, 6, 6] !< # of slip systems per family for hex - + integer, dimension(1), parameter :: & LATTICE_HEX_NCLEAVAGESYSTEM = [3] !< # of cleavage systems per family for hex - + integer, parameter :: & #ifndef __PGI LATTICE_HEX_NSLIP = sum(LATTICE_HEX_NSLIPSYSTEM), & !< total # of slip systems for hex @@ -265,14 +265,14 @@ module lattice -1, 2, -1, 3, 1, -1, 0, 1, & -2, 1, 1, 3, 1, -1, 0, 1, & ! pyramidal system: c+a slip <11.3>{-1-1.2} -- as for hexagonal ice (Castelnau et al. 1996, similar to twin system found below) - -1, -1, 2, 3, 1, 1, -2, 2, & ! <11.3>{-1-1.2} shear = 2((c/a)^2-2)/(3 c/a) + -1, -1, 2, 3, 1, 1, -2, 2, & ! <11.3>{-1-1.2} shear = 2((c/a)^2-2)/(3 c/a) 1, -2, 1, 3, -1, 2, -1, 2, & 2, -1, -1, 3, -2, 1, 1, 2, & 1, 1, -2, 3, -1, -1, 2, 2, & -1, 2, -1, 3, 1, -2, 1, 2, & -2, 1, 1, 3, 2, -1, -1, 2 & ],pReal),shape(LATTICE_HEX_SYSTEMSLIP)) !< slip systems for hex, sorted by P. Eisenlohr CCW around starting next to a_1 axis - + real(pReal), dimension(4+4,LATTICE_HEX_NTWIN), parameter :: & LATTICE_HEX_SYSTEMTWIN = reshape(real([& ! Compression or Tension = f(twinning shear=f(c/a)) for each metal ! (according to Yoo 1981) @@ -304,7 +304,7 @@ module lattice 1, -2, 1, -3, 1, -2, 1, 2, & 2, -1, -1, -3, 2, -1, -1, 2 & ],pReal),shape(LATTICE_HEX_SYSTEMTWIN)) !< twin systems for hex, sorted by P. Eisenlohr CCW around starting next to a_1 axis - + real(pReal), dimension(4+4,LATTICE_HEX_NCLEAVAGE), parameter :: & LATTICE_HEX_SYSTEMCLEAVAGE = reshape(real([& ! Cleavage direction Plane normal @@ -312,20 +312,20 @@ module lattice 0, 0, 0, 1, 2,-1,-1, 0, & 0, 0, 0, 1, 0, 1,-1, 0 & ],pReal),shape(LATTICE_HEX_SYSTEMCLEAVAGE)) - - + + !-------------------------------------------------------------------------------------------------- ! body centered tetragonal integer, dimension(13), parameter :: & LATTICE_BCT_NSLIPSYSTEM = [2, 2, 2, 4, 2, 4, 2, 2, 4, 8, 4, 8, 8 ] !< # of slip systems per family for bct (Sn) Bieler J. Electr Mater 2009 - + integer, parameter :: & #ifndef __PGI LATTICE_BCT_NSLIP = sum(LATTICE_BCT_NSLIPSYSTEM) !< total # of slip systems for bct #else LATTICE_BCT_NSLIP = 52 #endif - + real(pReal), dimension(3+3,LATTICE_BCT_NSLIP), parameter :: & LATTICE_BCT_SYSTEMSLIP = reshape(real([& ! Slip direction Plane normal @@ -395,12 +395,12 @@ module lattice -1, 1, 1, -1,-2, 1, & 1, 1, 1, 1,-2, 1 & ],pReal),shape(LATTICE_BCT_SYSTEMSLIP)) !< slip systems for bct sorted by Bieler - + !-------------------------------------------------------------------------------------------------- ! isotropic integer, dimension(1), parameter :: & LATTICE_ISO_NCLEAVAGESYSTEM = [3] !< # of cleavage systems per family for iso - + integer, parameter :: & #ifndef __PGI LATTICE_ISO_NCLEAVAGE = sum(LATTICE_ISO_NCLEAVAGESYSTEM) !< total # of cleavage systems for iso @@ -415,13 +415,13 @@ module lattice 0, 0, 1, 0, 1, 0, & 1, 0, 0, 0, 0, 1 & ],pReal),shape(LATTICE_ISO_SYSTEMCLEAVAGE)) - - + + !-------------------------------------------------------------------------------------------------- ! orthorhombic integer, dimension(3), parameter :: & LATTICE_ORT_NCLEAVAGESYSTEM = [1, 1, 1] !< # of cleavage systems per family for ortho - + integer, parameter :: & #ifndef __PGI LATTICE_ORT_NCLEAVAGE = sum(LATTICE_ORT_NCLEAVAGESYSTEM) !< total # of cleavage systems for ortho @@ -436,23 +436,23 @@ module lattice 0, 0, 1, 0, 1, 0, & 1, 0, 0, 0, 0, 1 & ],pReal),shape(LATTICE_ORT_SYSTEMCLEAVAGE)) - - - + + + ! BEGIN DEPRECATED integer, parameter, public :: & LATTICE_maxNcleavage = max(LATTICE_fcc_Ncleavage,LATTICE_bcc_Ncleavage, & LATTICE_hex_Ncleavage, & LATTICE_iso_Ncleavage,LATTICE_ort_Ncleavage) ! END DEPRECATED - + real(pReal), dimension(:,:,:), allocatable, public, protected :: & lattice_C66 real(pReal), dimension(:,:,:,:,:), allocatable, public, protected :: & lattice_C3333 real(pReal), dimension(:), allocatable, public, protected :: & lattice_mu, lattice_nu - + ! SHOULD NOT BE PART OF LATTICE BEGIN real(pReal), dimension(:,:,:,:), allocatable, public, protected :: & ! with higher-order parameters (e.g. temperature-dependent) lattice_thermalExpansion33 @@ -465,7 +465,7 @@ module lattice lattice_specificHeat, & lattice_referenceTemperature ! SHOULD NOT BE PART OF LATTICE END - + enum, bind(c) enumerator :: LATTICE_undefined_ID, & LATTICE_iso_ID, & @@ -475,18 +475,18 @@ module lattice LATTICE_bct_ID, & LATTICE_ort_ID end enum - + integer(kind(LATTICE_undefined_ID)), dimension(:), allocatable, public, protected :: & lattice_structure - + interface lattice_forestProjection_edge module procedure slipProjection_transverse end interface lattice_forestProjection_edge - + interface lattice_forestProjection_screw module procedure slipProjection_direction end interface lattice_forestProjection_screw - + public :: & lattice_init, & LATTICE_iso_ID, & @@ -516,29 +516,29 @@ module lattice lattice_slip_transverse, & lattice_labels_slip, & lattice_labels_twin - + contains !-------------------------------------------------------------------------------------------------- !> @brief Module initialization !-------------------------------------------------------------------------------------------------- subroutine lattice_init - - integer :: Nphases, p + + integer :: Nphases, p,i character(len=pStringLen) :: & tag = '' real(pReal) :: CoverA real(pReal), dimension(:), allocatable :: & temp - + write(6,'(/,a)') ' <<<+- lattice init -+>>>' - + Nphases = size(config_phase) - + allocate(lattice_structure(Nphases),source = LATTICE_undefined_ID) allocate(lattice_C66(6,6,Nphases), source=0.0_pReal) allocate(lattice_C3333(3,3,3,3,Nphases), source=0.0_pReal) - + allocate(lattice_thermalExpansion33 (3,3,3,Nphases), source=0.0_pReal) ! constant, linear, quadratic coefficients allocate(lattice_thermalConductivity33 (3,3,Nphases), source=0.0_pReal) allocate(lattice_damageDiffusion33 (3,3,Nphases), source=0.0_pReal) @@ -546,32 +546,16 @@ subroutine lattice_init allocate(lattice_massDensity ( Nphases), source=0.0_pReal) allocate(lattice_specificHeat ( Nphases), source=0.0_pReal) allocate(lattice_referenceTemperature ( Nphases), source=300.0_pReal) - + allocate(lattice_mu(Nphases), source=0.0_pReal) allocate(lattice_nu(Nphases), source=0.0_pReal) - - + allocate(lattice_Scleavage(3,3,3,lattice_maxNcleavage,Nphases),source=0.0_pReal) allocate(lattice_NcleavageSystem(lattice_maxNcleavageFamily,Nphases),source=0) - + + do p = 1, size(config_phase) - tag = config_phase(p)%getString('lattice_structure') - select case(trim(tag(1:3))) - case('iso') - lattice_structure(p) = LATTICE_iso_ID - case('fcc') - lattice_structure(p) = LATTICE_fcc_ID - case('bcc') - lattice_structure(p) = LATTICE_bcc_ID - case('hex') - lattice_structure(p) = LATTICE_hex_ID - case('bct') - lattice_structure(p) = LATTICE_bct_ID - case('ort') - lattice_structure(p) = LATTICE_ort_ID - end select - - + lattice_C66(1,1,p) = config_phase(p)%getFloat('c11',defaultVal=0.0_pReal) lattice_C66(1,2,p) = config_phase(p)%getFloat('c12',defaultVal=0.0_pReal) lattice_C66(1,3,p) = config_phase(p)%getFloat('c13',defaultVal=0.0_pReal) @@ -581,21 +565,56 @@ subroutine lattice_init lattice_C66(4,4,p) = config_phase(p)%getFloat('c44',defaultVal=0.0_pReal) lattice_C66(5,5,p) = config_phase(p)%getFloat('c55',defaultVal=0.0_pReal) lattice_C66(6,6,p) = config_phase(p)%getFloat('c66',defaultVal=0.0_pReal) - - + CoverA = config_phase(p)%getFloat('c/a',defaultVal=0.0_pReal) - + + tag = config_phase(p)%getString('lattice_structure') + select case(tag(1:3)) + case('iso') + lattice_structure(p) = LATTICE_iso_ID + case('fcc') + lattice_structure(p) = LATTICE_fcc_ID + case('bcc') + lattice_structure(p) = LATTICE_bcc_ID + case('hex') + if(CoverA < 1.0_pReal .or. CoverA > 2.0_pReal) call IO_error(131,el=p) + lattice_structure(p) = LATTICE_hex_ID + case('bct') + if(CoverA > 2.0_pReal) call IO_error(131,el=p) + lattice_structure(p) = LATTICE_bct_ID + case('ort') + lattice_structure(p) = LATTICE_ort_ID + case default + call IO_error(130,ext_msg='lattice_init') + end select + + lattice_C66(1:6,1:6,p) = lattice_symmetrizeC66(lattice_structure(p),lattice_C66(1:6,1:6,p)) + + lattice_mu(p) = 0.2_pReal *(lattice_C66(1,1,p) -lattice_C66(1,2,p) +3.0_pReal*lattice_C66(4,4,p)) ! (C11iso-C12iso)/2 with C11iso=(3*C11+2*C12+4*C44)/5 and C12iso=(C11+4*C12-2*C44)/5 + lattice_nu(p) = ( lattice_C66(1,1,p) +4.0_pReal*lattice_C66(1,2,p) -2.0_pReal*lattice_C66(4,4,p)) & + / (4.0_pReal*lattice_C66(1,1,p) +6.0_pReal*lattice_C66(1,2,p) +2.0_pReal*lattice_C66(4,4,p))! C12iso/(C11iso+C12iso) with C11iso=(3*C11+2*C12+4*C44)/5 and C12iso=(C11+4*C12-2*C44)/5 + + lattice_C3333(1:3,1:3,1:3,1:3,p) = math_Voigt66to3333(lattice_C66(1:6,1:6,p)) ! Literature data is Voigt + lattice_C66(1:6,1:6,p) = math_sym3333to66(lattice_C3333(1:3,1:3,1:3,1:3,p)) ! DAMASK uses Mandel-weighting + + do i = 1, 6 + if (abs(lattice_C66(i,i,p)) 2.0_pReal) & - .and. lattice_structure(p) == LATTICE_hex_ID) call IO_error(131,el=p) ! checking physical significance of c/a - if ((CoverA > 2.0_pReal) & - .and. lattice_structure(p) == LATTICE_bct_ID) call IO_error(131,el=p) ! checking physical significance of c/a + call lattice_initializeStructure(p, CoverA) enddo - + end subroutine lattice_init - - + + !-------------------------------------------------------------------------------------------------- !> @brief !!!!!!!DEPRECTATED!!!!!! !-------------------------------------------------------------------------------------------------- @@ -622,102 +637,65 @@ subroutine lattice_initializeStructure(myPhase,CoverA) integer, intent(in) :: myPhase real(pReal), intent(in) :: & CoverA - + integer :: & - i, & - myNcleavage - - lattice_C66(1:6,1:6,myPhase) = lattice_symmetrizeC66(lattice_structure(myPhase),& - lattice_C66(1:6,1:6,myPhase)) - - lattice_mu(myPhase) = 0.2_pReal *( lattice_C66(1,1,myPhase) & - - lattice_C66(1,2,myPhase) & - + 3.0_pReal*lattice_C66(4,4,myPhase)) ! (C11iso-C12iso)/2 with C11iso=(3*C11+2*C12+4*C44)/5 and C12iso=(C11+4*C12-2*C44)/5 - lattice_nu(myPhase) = ( lattice_C66(1,1,myPhase) & - + 4.0_pReal*lattice_C66(1,2,myPhase) & - - 2.0_pReal*lattice_C66(4,4,myPhase)) & - /( 4.0_pReal*lattice_C66(1,1,myPhase) & - + 6.0_pReal*lattice_C66(1,2,myPhase) & - + 2.0_pReal*lattice_C66(4,4,myPhase))! C12iso/(C11iso+C12iso) with C11iso=(3*C11+2*C12+4*C44)/5 and C12iso=(C11+4*C12-2*C44)/5 - lattice_C3333(1:3,1:3,1:3,1:3,myPhase) = math_Voigt66to3333(lattice_C66(1:6,1:6,myPhase)) ! Literature data is Voigt - lattice_C66(1:6,1:6,myPhase) = math_sym3333to66(lattice_C3333(1:3,1:3,1:3,1:3,myPhase)) ! DAMASK uses Mandel-weighting - do i = 1, 6 - if (abs(lattice_C66(i,i,myPhase)) @brief Symmetrizes stiffness matrix according to lattice type !> @details J. A. Rayne and B. S. Chandrasekhar Phys. Rev. 120, 1658 Erratum Phys. Rev. 122, 1962 !-------------------------------------------------------------------------------------------------- pure function lattice_symmetrizeC66(struct,C66) - + integer(kind(LATTICE_undefined_ID)), intent(in) :: struct real(pReal), dimension(6,6), intent(in) :: C66 real(pReal), dimension(6,6) :: lattice_symmetrizeC66 integer :: j,k - + lattice_symmetrizeC66 = 0.0_pReal - + select case(struct) case (LATTICE_iso_ID) do k=1,3 @@ -777,22 +755,22 @@ pure function lattice_symmetrizeC66(struct,C66) case default lattice_symmetrizeC66 = C66 end select - + end function lattice_symmetrizeC66 - - + + !-------------------------------------------------------------------------------------------------- !> @brief Symmetrizes 2nd order tensor according to lattice type !-------------------------------------------------------------------------------------------------- pure function lattice_symmetrize33(struct,T33) - + integer(kind(LATTICE_undefined_ID)), intent(in) :: struct real(pReal), dimension(3,3), intent(in) :: T33 real(pReal), dimension(3,3) :: lattice_symmetrize33 integer :: k - + lattice_symmetrize33 = 0.0_pReal - + select case(struct) case (LATTICE_iso_ID,LATTICE_fcc_ID,LATTICE_bcc_ID) do k=1,3 @@ -809,26 +787,26 @@ pure function lattice_symmetrize33(struct,T33) case default lattice_symmetrize33 = T33 end select - + end function lattice_symmetrize33 - - + + !-------------------------------------------------------------------------------------------------- !> @brief Characteristic shear for twinning !-------------------------------------------------------------------------------------------------- function lattice_characteristicShear_Twin(Ntwin,structure,CoverA) result(characteristicShear) - + integer, dimension(:), intent(in) :: Ntwin !< number of active twin systems per family character(len=*), intent(in) :: structure !< lattice structure real(pReal), intent(in) :: cOverA !< c/a ratio real(pReal), dimension(sum(Ntwin)) :: characteristicShear - + integer :: & a, & !< index of active system p, & !< index in potential system list f, & !< index of my family s !< index of my system in current family - + integer, dimension(LATTICE_HEX_NTWIN), parameter :: & HEX_SHEARTWIN = reshape( [& 1, & ! <-10.1>{10.2} @@ -856,10 +834,10 @@ function lattice_characteristicShear_Twin(Ntwin,structure,CoverA) result(charact 4, & 4 & ],[LATTICE_HEX_NTWIN]) ! indicator to formulas below - + if (len_trim(structure) /= 3) & call IO_error(137,ext_msg='lattice_characteristicShear_Twin: '//trim(structure)) - + a = 0 myFamilies: do f = 1,size(Ntwin,1) mySystems: do s = 1,Ntwin(f) @@ -886,28 +864,28 @@ function lattice_characteristicShear_Twin(Ntwin,structure,CoverA) result(charact end select enddo mySystems enddo myFamilies - + end function lattice_characteristicShear_Twin - - + + !-------------------------------------------------------------------------------------------------- !> @brief Rotated elasticity matrices for twinning in 66-vector notation !-------------------------------------------------------------------------------------------------- function lattice_C66_twin(Ntwin,C66,structure,CoverA) - + integer, dimension(:), intent(in) :: Ntwin !< number of active twin systems per family character(len=*), intent(in) :: structure !< lattice structure real(pReal), dimension(6,6), intent(in) :: C66 !< unrotated parent stiffness matrix real(pReal), intent(in) :: cOverA !< c/a ratio real(pReal), dimension(6,6,sum(Ntwin)) :: lattice_C66_twin - + real(pReal), dimension(3,3,sum(Ntwin)):: coordinateSystem type(rotation) :: R integer :: i - + if (len_trim(structure) /= 3) & call IO_error(137,ext_msg='lattice_C66_twin: '//trim(structure)) - + select case(structure(1:3)) case('fcc') coordinateSystem = buildCoordinateSystem(Ntwin,LATTICE_FCC_NSLIPSYSTEM,LATTICE_FCC_SYSTEMTWIN,& @@ -921,35 +899,35 @@ function lattice_C66_twin(Ntwin,C66,structure,CoverA) case default call IO_error(137,ext_msg='lattice_C66_twin: '//trim(structure)) end select - + do i = 1, sum(Ntwin) call R%fromAxisAngle([coordinateSystem(1:3,2,i),PI],P=1) ! ToDo: Why always 180 deg? lattice_C66_twin(1:6,1:6,i) = R%rotTensor4sym(C66) enddo end function lattice_C66_twin - - + + !-------------------------------------------------------------------------------------------------- !> @brief Rotated elasticity matrices for transformation in 66-vector notation !-------------------------------------------------------------------------------------------------- function lattice_C66_trans(Ntrans,C_parent66,structure_target, & cOverA_trans,a_bcc,a_fcc) - + integer, dimension(:), intent(in) :: Ntrans !< number of active twin systems per family character(len=*), intent(in) :: structure_target !< lattice structure real(pReal), dimension(6,6), intent(in) :: C_parent66 real(pReal), dimension(6,6,sum(Ntrans)) :: lattice_C66_trans - + real(pReal), dimension(6,6) :: C_bar66, C_target_unrotated66 real(pReal), dimension(3,3,sum(Ntrans)) :: Q,S type(rotation) :: R real(pReal) :: a_bcc, a_fcc, cOverA_trans integer :: i - + if (len_trim(structure_target) /= 3) & call IO_error(137,ext_msg='lattice_C66_trans (target): '//trim(structure_target)) - + !-------------------------------------------------------------------------------------------------- ! elasticity matrix of the target phase in cube orientation if (structure_target(1:3) == 'hex') then @@ -961,7 +939,7 @@ function lattice_C66_trans(Ntrans,C_parent66,structure_target, & C_bar66(1,3) = (C_parent66(1,1) + 2.0_pReal*C_parent66(1,2) - 2.0_pReal*C_parent66(4,4))/3.0_pReal C_bar66(4,4) = (C_parent66(1,1) - C_parent66(1,2) + C_parent66(4,4))/3.0_pReal C_bar66(1,4) = (C_parent66(1,1) - C_parent66(1,2) - 2.0_pReal*C_parent66(4,4)) /(3.0_pReal*sqrt(2.0_pReal)) - + C_target_unrotated66 = 0.0_pReal C_target_unrotated66(1,1) = C_bar66(1,1) - C_bar66(1,4)**2.0_pReal/C_bar66(4,4) C_target_unrotated66(1,2) = C_bar66(1,2) + C_bar66(1,4)**2.0_pReal/C_bar66(4,4) @@ -976,22 +954,22 @@ function lattice_C66_trans(Ntrans,C_parent66,structure_target, & else call IO_error(137,ext_msg='lattice_C66_trans : '//trim(structure_target)) endif - + do i = 1, 6 if (abs(C_target_unrotated66(i,i)) @brief Non-schmid projections for bcc with up to 6 coefficients ! Koester et al. 2012, Acta Materialia 60 (2012) 3894–3901, eq. (17) @@ -1003,19 +981,19 @@ function lattice_nonSchmidMatrix(Nslip,nonSchmidCoefficients,sense) result(nonSc real(pReal), dimension(:), intent(in) :: nonSchmidCoefficients !< non-Schmid coefficients for projections integer, intent(in) :: sense !< sense (-1,+1) real(pReal), dimension(1:3,1:3,sum(Nslip)) :: nonSchmidMatrix - + real(pReal), dimension(1:3,1:3,sum(Nslip)) :: coordinateSystem !< coordinate system of slip system real(pReal), dimension(3) :: direction, normal, np type(rotation) :: R integer :: i - + if (abs(sense) /= 1) call IO_error(0,ext_msg='lattice_nonSchmidMatrix') - + coordinateSystem = buildCoordinateSystem(Nslip,LATTICE_BCC_NSLIPSYSTEM,LATTICE_BCC_SYSTEMSLIP,& 'bcc',0.0_pReal) coordinateSystem(1:3,1,1:sum(Nslip)) = coordinateSystem(1:3,1,1:sum(Nslip)) *real(sense,pReal) ! convert unidirectional coordinate system nonSchmidMatrix = lattice_SchmidMatrix_slip(Nslip,'bcc',0.0_pReal) ! Schmid contribution - + do i = 1,sum(Nslip) direction = coordinateSystem(1:3,1,i) normal = coordinateSystem(1:3,2,i) @@ -1038,8 +1016,8 @@ function lattice_nonSchmidMatrix(Nslip,nonSchmidCoefficients,sense) result(nonSc enddo end function lattice_nonSchmidMatrix - - + + !-------------------------------------------------------------------------------------------------- !> @brief Slip-slip interaction matrix !> details only active slip systems are considered @@ -1050,10 +1028,10 @@ function lattice_interaction_SlipBySlip(Nslip,interactionValues,structure) resul real(pReal), dimension(:), intent(in) :: interactionValues !< values for slip-slip interaction character(len=*), intent(in) :: structure !< lattice structure real(pReal), dimension(sum(Nslip),sum(Nslip)) :: interactionMatrix - + integer, dimension(:), allocatable :: NslipMax integer, dimension(:,:), allocatable :: interactionTypes - + integer, dimension(LATTICE_FCC_NSLIP,LATTICE_FCC_NSLIP), parameter :: & FCC_INTERACTIONSLIPSLIP = reshape( [& 1, 2, 2, 4, 6, 5, 3, 5, 5, 4, 5, 6, 9,10, 9,10,11,12, & ! -----> acting @@ -1068,7 +1046,7 @@ function lattice_interaction_SlipBySlip(Nslip,interactionValues,structure) resul 4, 5, 6, 3, 5, 5, 4, 6, 5, 1, 2, 2, 10, 9, 9,10,12,11, & 5, 3, 5, 5, 4, 6, 6, 4, 5, 2, 1, 2, 10, 9,11,12,10, 9, & 6, 5, 4, 5, 6, 4, 5, 5, 3, 2, 2, 1, 12,11, 9,10,10, 9, & - + 9, 9,11, 9, 9,11,10,10,12,10,10,12, 1, 7, 8, 8, 8, 8, & 10,10,12,10,10,12, 9, 9,11, 9, 9,11, 7, 1, 8, 8, 8, 8, & 9,11, 9,10,12,10,10,12,10, 9,11, 9, 8, 8, 1, 7, 8, 8, & @@ -1088,7 +1066,7 @@ function lattice_interaction_SlipBySlip(Nslip,interactionValues,structure) resul !<10: similar to glissile junctions in <110>{111} btw one {110} and one {111} plane !<11: crossing btw one {110} and one {111} plane !<12: collinear btw one {110} and one {111} plane - + integer, dimension(LATTICE_BCC_NSLIP,LATTICE_BCC_NSLIP), parameter :: & BCC_INTERACTIONSLIPSLIP = reshape( [& 1,2,6,6,5,4,4,3,4,3,5,4, 6,6,4,3,3,4,6,6,4,3,6,6, & ! -----> acting @@ -1123,7 +1101,7 @@ function lattice_interaction_SlipBySlip(Nslip,interactionValues,structure) resul !< 4: mixed-asymmetrical junction !< 5: mixed-symmetrical junction !< 6: edge junction - + integer, dimension(LATTICE_HEX_NSLIP,LATTICE_HEX_NSLIP), parameter :: & HEX_INTERACTIONSLIPSLIP = reshape( [& 1, 2, 2, 3, 3, 3, 7, 7, 7, 13,13,13,13,13,13, 21,21,21,21,21,21,21,21,21,21,21,21, 31,31,31,31,31,31, & ! -----> acting @@ -1165,7 +1143,7 @@ function lattice_interaction_SlipBySlip(Nslip,interactionValues,structure) resul 42,42,42, 41,41,41, 40,40,40, 39,39,39,39,39,39, 38,38,38,38,38,38,38,38,38,38,38,38, 37,37,37,37,36,37, & 42,42,42, 41,41,41, 40,40,40, 39,39,39,39,39,39, 38,38,38,38,38,38,38,38,38,38,38,38, 37,37,37,37,37,36 & ],shape(HEX_INTERACTIONSLIPSLIP)) !< Slip--slip interaction types for hex (onion peel naming scheme) - + integer, dimension(LATTICE_BCT_NSLIP,LATTICE_BCT_NSLIP), parameter :: & BCT_INTERACTIONSLIPSLIP = reshape( [& 1, 2, 3, 3, 7, 7, 13, 13, 13, 13, 21, 21, 31, 31, 31, 31, 43, 43, 57, 57, 73, 73, 73, 73, 91, 91, 91, 91, 91, 91, 91, 91, 111, 111, 111, 111, 133,133,133,133,133,133,133,133, 157,157,157,157,157,157,157,157, & ! -----> acting @@ -1233,11 +1211,11 @@ function lattice_interaction_SlipBySlip(Nslip,interactionValues,structure) resul 182,182, 181,181, 180,180, 179,179,179,179, 178,178, 177,177,177,177, 176,176, 175,175, 174,174,174,174, 173,173,173,173,173,173,173,173, 172, 172, 172, 172, 171,171,171,171,171,171,171,171, 169,170,170,170,170,170,169,170, & 182,182, 181,181, 180,180, 179,179,179,179, 178,178, 177,177,177,177, 176,176, 175,175, 174,174,174,174, 173,173,173,173,173,173,173,173, 172, 172, 172, 172, 171,171,171,171,171,171,171,171, 169,170,170,170,170,170,170,169 & ],shape(BCT_INTERACTIONSLIPSLIP)) - - + + if (len_trim(structure) /= 3) & call IO_error(137,ext_msg='lattice_interaction_SlipBySlip: '//trim(structure)) - + select case(structure(1:3)) case('fcc') interactionTypes = FCC_INTERACTIONSLIPSLIP @@ -1254,26 +1232,26 @@ function lattice_interaction_SlipBySlip(Nslip,interactionValues,structure) resul case default call IO_error(137,ext_msg='lattice_interaction_SlipBySlip: '//trim(structure)) end select - + interactionMatrix = buildInteraction(Nslip,Nslip,NslipMax,NslipMax,interactionValues,interactionTypes) - + end function lattice_interaction_SlipBySlip - - + + !-------------------------------------------------------------------------------------------------- !> @brief Twin-twin interaction matrix !> details only active twin systems are considered !-------------------------------------------------------------------------------------------------- function lattice_interaction_TwinByTwin(Ntwin,interactionValues,structure) result(interactionMatrix) - + integer, dimension(:), intent(in) :: Ntwin !< number of active twin systems per family real(pReal), dimension(:), intent(in) :: interactionValues !< values for twin-twin interaction character(len=*), intent(in) :: structure !< lattice structure real(pReal), dimension(sum(Ntwin),sum(Ntwin)) :: interactionMatrix - + integer, dimension(:), allocatable :: NtwinMax integer, dimension(:,:), allocatable :: interactionTypes - + integer, dimension(LATTICE_FCC_NTWIN,LATTICE_FCC_NTWIN), parameter :: & FCC_INTERACTIONTWINTWIN = reshape( [& 1,1,1,2,2,2,2,2,2,2,2,2, & ! -----> acting @@ -1289,7 +1267,7 @@ function lattice_interaction_TwinByTwin(Ntwin,interactionValues,structure) resul 2,2,2,2,2,2,2,2,2,1,1,1, & 2,2,2,2,2,2,2,2,2,1,1,1 & ],shape(FCC_INTERACTIONTWINTWIN)) !< Twin-twin interaction types for fcc - + integer, dimension(LATTICE_BCC_NTWIN,LATTICE_BCC_NTWIN), parameter :: & BCC_INTERACTIONTWINTWIN = reshape( [& 1,3,3,3,3,3,3,2,3,3,2,3, & ! -----> acting @@ -1338,10 +1316,10 @@ function lattice_interaction_TwinByTwin(Ntwin,interactionValues,structure) resul 20,20,20,20,20,20, 19,19,19,19,19,19, 18,18,18,18,18,18, 17,17,17,17,16,17, & 20,20,20,20,20,20, 19,19,19,19,19,19, 18,18,18,18,18,18, 17,17,17,17,17,16 & ],shape(HEX_INTERACTIONTWINTWIN)) !< Twin-twin interaction types for hex - + if (len_trim(structure) /= 3) & call IO_error(137,ext_msg='lattice_interaction_TwinByTwin: '//trim(structure)) - + select case(structure(1:3)) case('fcc') interactionTypes = FCC_INTERACTIONTWINTWIN @@ -1355,26 +1333,26 @@ function lattice_interaction_TwinByTwin(Ntwin,interactionValues,structure) resul case default call IO_error(137,ext_msg='lattice_interaction_TwinByTwin: '//trim(structure)) end select - + interactionMatrix = buildInteraction(Ntwin,Ntwin,NtwinMax,NtwinMax,interactionValues,interactionTypes) - + end function lattice_interaction_TwinByTwin - - + + !-------------------------------------------------------------------------------------------------- !> @brief Trans-trans interaction matrix !> details only active trans systems are considered !-------------------------------------------------------------------------------------------------- function lattice_interaction_TransByTrans(Ntrans,interactionValues,structure) result(interactionMatrix) - + integer, dimension(:), intent(in) :: Ntrans !< number of active trans systems per family real(pReal), dimension(:), intent(in) :: interactionValues !< values for trans-trans interaction character(len=*), intent(in) :: structure !< lattice structure (parent crystal) real(pReal), dimension(sum(Ntrans),sum(Ntrans)) :: interactionMatrix - + integer, dimension(:), allocatable :: NtransMax integer, dimension(:,:), allocatable :: interactionTypes - + integer, dimension(LATTICE_FCC_NTRANS,LATTICE_FCC_NTRANS), parameter :: & FCC_INTERACTIONTRANSTRANS = reshape( [& 1,1,1,2,2,2,2,2,2,2,2,2, & ! -----> acting @@ -1390,44 +1368,44 @@ function lattice_interaction_TransByTrans(Ntrans,interactionValues,structure) re 2,2,2,2,2,2,2,2,2,1,1,1, & 2,2,2,2,2,2,2,2,2,1,1,1 & ],shape(FCC_INTERACTIONTRANSTRANS)) !< Trans-trans interaction types for fcc - + if (len_trim(structure) /= 3) & call IO_error(137,ext_msg='lattice_interaction_TransByTrans: '//trim(structure)) - + if(structure(1:3) == 'fcc') then interactionTypes = FCC_INTERACTIONTRANSTRANS NtransMax = LATTICE_FCC_NTRANSSYSTEM else call IO_error(137,ext_msg='lattice_interaction_TransByTrans: '//trim(structure)) end if - + interactionMatrix = buildInteraction(Ntrans,Ntrans,NtransMax,NtransMax,interactionValues,interactionTypes) - + end function lattice_interaction_TransByTrans - - + + !-------------------------------------------------------------------------------------------------- !> @brief Slip-twin interaction matrix !> details only active slip and twin systems are considered !-------------------------------------------------------------------------------------------------- function lattice_interaction_SlipByTwin(Nslip,Ntwin,interactionValues,structure) result(interactionMatrix) - + integer, dimension(:), intent(in) :: Nslip, & !< number of active slip systems per family Ntwin !< number of active twin systems per family real(pReal), dimension(:), intent(in) :: interactionValues !< values for slip-twin interaction character(len=*), intent(in) :: structure !< lattice structure real(pReal), dimension(sum(Nslip),sum(Ntwin)) :: interactionMatrix - + integer, dimension(:), allocatable :: NslipMax, & NtwinMax integer, dimension(:,:), allocatable :: interactionTypes - + integer, dimension(LATTICE_FCC_NTWIN,LATTICE_FCC_NSLIP), parameter :: & FCC_INTERACTIONSLIPTWIN = reshape( [& 1,1,1,3,3,3,2,2,2,3,3,3, & ! -----> twin (acting) 1,1,1,3,3,3,3,3,3,2,2,2, & ! | 1,1,1,2,2,2,3,3,3,3,3,3, & ! | - 3,3,3,1,1,1,3,3,3,2,2,2, & ! v + 3,3,3,1,1,1,3,3,3,2,2,2, & ! v 3,3,3,1,1,1,2,2,2,3,3,3, & ! slip (reacting) 2,2,2,1,1,1,3,3,3,3,3,3, & 2,2,2,3,3,3,1,1,1,3,3,3, & @@ -1436,7 +1414,7 @@ function lattice_interaction_SlipByTwin(Nslip,Ntwin,interactionValues,structure) 3,3,3,2,2,2,3,3,3,1,1,1, & 2,2,2,3,3,3,3,3,3,1,1,1, & 3,3,3,3,3,3,2,2,2,1,1,1, & - + 4,4,4,4,4,4,4,4,4,4,4,4, & 4,4,4,4,4,4,4,4,4,4,4,4, & 4,4,4,4,4,4,4,4,4,4,4,4, & @@ -1520,10 +1498,10 @@ function lattice_interaction_SlipByTwin(Nslip,Ntwin,interactionValues,structure) 21,21,21,21,21,21, 22,22,22,22,22,22, 23,23,23,23,23,23, 24,24,24,24,24,24 & ! ],shape(HEX_INTERACTIONSLIPTWIN)) !< Slip-twin interaction types for hex - + if (len_trim(structure) /= 3) & call IO_error(137,ext_msg='lattice_interaction_SlipByTwin: '//trim(structure)) - + select case(structure(1:3)) case('fcc') interactionTypes = FCC_INTERACTIONSLIPTWIN @@ -1540,28 +1518,28 @@ function lattice_interaction_SlipByTwin(Nslip,Ntwin,interactionValues,structure) case default call IO_error(137,ext_msg='lattice_interaction_SlipByTwin: '//trim(structure)) end select - + interactionMatrix = buildInteraction(Nslip,Ntwin,NslipMax,NtwinMax,interactionValues,interactionTypes) - + end function lattice_interaction_SlipByTwin - - + + !-------------------------------------------------------------------------------------------------- !> @brief Slip-trans interaction matrix !> details only active slip and trans systems are considered !-------------------------------------------------------------------------------------------------- function lattice_interaction_SlipByTrans(Nslip,Ntrans,interactionValues,structure) result(interactionMatrix) - + integer, dimension(:), intent(in) :: Nslip, & !< number of active slip systems per family Ntrans !< number of active trans systems per family real(pReal), dimension(:), intent(in) :: interactionValues !< values for slip-trans interaction character(len=*), intent(in) :: structure !< lattice structure (parent crystal) real(pReal), dimension(sum(Nslip),sum(Ntrans)) :: interactionMatrix - + integer, dimension(:), allocatable :: NslipMax, & NtransMax integer, dimension(:,:), allocatable :: interactionTypes - + integer, dimension(LATTICE_FCC_NTRANS,LATTICE_FCC_NSLIP), parameter :: & FCC_INTERACTIONSLIPTRANS = reshape( [& 1,1,1,3,3,3,2,2,2,3,3,3, & ! -----> trans (acting) @@ -1576,7 +1554,7 @@ function lattice_interaction_SlipByTrans(Nslip,Ntrans,interactionValues,structur 3,3,3,2,2,2,3,3,3,1,1,1, & 2,2,2,3,3,3,3,3,3,1,1,1, & 3,3,3,3,3,3,2,2,2,1,1,1, & - + 4,4,4,4,4,4,4,4,4,4,4,4, & 4,4,4,4,4,4,4,4,4,4,4,4, & 4,4,4,4,4,4,4,4,4,4,4,4, & @@ -1584,10 +1562,10 @@ function lattice_interaction_SlipByTrans(Nslip,Ntrans,interactionValues,structur 4,4,4,4,4,4,4,4,4,4,4,4, & 4,4,4,4,4,4,4,4,4,4,4,4 & ],shape(FCC_INTERACTIONSLIPTRANS)) !< Slip-trans interaction types for fcc - + if (len_trim(structure) /= 3) & call IO_error(137,ext_msg='lattice_interaction_SlipByTrans: '//trim(structure)) - + select case(structure(1:3)) case('fcc') interactionTypes = FCC_INTERACTIONSLIPTRANS @@ -1596,34 +1574,34 @@ function lattice_interaction_SlipByTrans(Nslip,Ntrans,interactionValues,structur case default call IO_error(137,ext_msg='lattice_interaction_SlipByTrans: '//trim(structure)) end select - + interactionMatrix = buildInteraction(Nslip,Ntrans,NslipMax,NtransMax,interactionValues,interactionTypes) - + end function lattice_interaction_SlipByTrans - - + + !-------------------------------------------------------------------------------------------------- !> @brief Twin-slip interaction matrix !> details only active twin and slip systems are considered !-------------------------------------------------------------------------------------------------- function lattice_interaction_TwinBySlip(Ntwin,Nslip,interactionValues,structure) result(interactionMatrix) - + integer, dimension(:), intent(in) :: Ntwin, & !< number of active twin systems per family Nslip !< number of active slip systems per family real(pReal), dimension(:), intent(in) :: interactionValues !< values for twin-twin interaction character(len=*), intent(in) :: structure !< lattice structure real(pReal), dimension(sum(Ntwin),sum(Nslip)) :: interactionMatrix - + integer, dimension(:), allocatable :: NtwinMax, & NslipMax integer, dimension(:,:), allocatable :: interactionTypes - + integer, dimension(LATTICE_FCC_NSLIP,LATTICE_FCC_NTWIN), parameter :: & FCC_INTERACTIONTWINSLIP = 1 !< Twin-slip interaction types for fcc - + integer, dimension(LATTICE_BCC_NSLIP,LATTICE_BCC_NTWIN), parameter :: & BCC_INTERACTIONTWINSLIP = 1 !< Twin-slip interaction types for bcc - + integer, dimension(LATTICE_HEX_NSLIP,LATTICE_HEX_NTWIN), parameter :: & HEX_INTERACTIONTWINSLIP = reshape( [& 1, 1, 1, 5, 5, 5, 9, 9, 9, 13,13,13,13,13,13, 17,17,17,17,17,17,17,17,17,17,17,17, 21,21,21,21,21,21, & ! ----> slip (acting) @@ -1654,10 +1632,10 @@ function lattice_interaction_TwinBySlip(Ntwin,Nslip,interactionValues,structure) 4, 4, 4, 8, 8, 8, 12,12,12, 16,16,16,16,16,16, 20,20,20,20,20,20,20,20,20,20,20,20, 24,24,24,24,24,24, & 4, 4, 4, 8, 8, 8, 12,12,12, 16,16,16,16,16,16, 20,20,20,20,20,20,20,20,20,20,20,20, 24,24,24,24,24,24 & ],shape(HEX_INTERACTIONTWINSLIP)) !< Twin-slip interaction types for hex - + if (len_trim(structure) /= 3) & call IO_error(137,ext_msg='lattice_interaction_TwinBySlip: '//trim(structure)) - + select case(structure(1:3)) case('fcc') interactionTypes = FCC_INTERACTIONTWINSLIP @@ -1674,31 +1652,31 @@ function lattice_interaction_TwinBySlip(Ntwin,Nslip,interactionValues,structure) case default call IO_error(137,ext_msg='lattice_interaction_TwinBySlip: '//trim(structure)) end select - + interactionMatrix = buildInteraction(Ntwin,Nslip,NtwinMax,NslipMax,interactionValues,interactionTypes) - + end function lattice_interaction_TwinBySlip - - + + !-------------------------------------------------------------------------------------------------- !> @brief Schmid matrix for slip !> details only active slip systems are considered !-------------------------------------------------------------------------------------------------- function lattice_SchmidMatrix_slip(Nslip,structure,cOverA) result(SchmidMatrix) - + integer, dimension(:), intent(in) :: Nslip !< number of active slip systems per family character(len=*), intent(in) :: structure !< lattice structure real(pReal), intent(in) :: cOverA real(pReal), dimension(3,3,sum(Nslip)) :: SchmidMatrix - + real(pReal), dimension(3,3,sum(Nslip)) :: coordinateSystem real(pReal), dimension(:,:), allocatable :: slipSystems integer, dimension(:), allocatable :: NslipMax integer :: i - + if (len_trim(structure) /= 3) & call IO_error(137,ext_msg='lattice_SchmidMatrix_slip: '//trim(structure)) - + select case(structure(1:3)) case('fcc') NslipMax = LATTICE_FCC_NSLIPSYSTEM @@ -1715,42 +1693,42 @@ function lattice_SchmidMatrix_slip(Nslip,structure,cOverA) result(SchmidMatrix) case default call IO_error(137,ext_msg='lattice_SchmidMatrix_slip: '//trim(structure)) end select - + if (any(NslipMax(1:size(Nslip)) - Nslip < 0)) & call IO_error(145,ext_msg='Nslip '//trim(structure)) if (any(Nslip < 0)) & call IO_error(144,ext_msg='Nslip '//trim(structure)) - + coordinateSystem = buildCoordinateSystem(Nslip,NslipMax,slipSystems,structure,cOverA) - + do i = 1, sum(Nslip) SchmidMatrix(1:3,1:3,i) = math_outer(coordinateSystem(1:3,1,i),coordinateSystem(1:3,2,i)) if (abs(math_trace33(SchmidMatrix(1:3,1:3,i))) > tol_math_check) & call IO_error(0,i,ext_msg = 'dilatational Schmid matrix for slip') enddo - + end function lattice_SchmidMatrix_slip - - + + !-------------------------------------------------------------------------------------------------- !> @brief Schmid matrix for twinning !> details only active twin systems are considered !-------------------------------------------------------------------------------------------------- function lattice_SchmidMatrix_twin(Ntwin,structure,cOverA) result(SchmidMatrix) - + integer, dimension(:), intent(in) :: Ntwin !< number of active twin systems per family character(len=*), intent(in) :: structure !< lattice structure real(pReal), intent(in) :: cOverA !< c/a ratio real(pReal), dimension(3,3,sum(Ntwin)) :: SchmidMatrix - + real(pReal), dimension(3,3,sum(Ntwin)) :: coordinateSystem real(pReal), dimension(:,:), allocatable :: twinSystems integer, dimension(:), allocatable :: NtwinMax integer :: i - + if (len_trim(structure) /= 3) & call IO_error(137,ext_msg='lattice_SchmidMatrix_twin: '//trim(structure)) - + select case(structure(1:3)) case('fcc') NtwinMax = LATTICE_FCC_NTWINSYSTEM @@ -1764,37 +1742,37 @@ function lattice_SchmidMatrix_twin(Ntwin,structure,cOverA) result(SchmidMatrix) case default call IO_error(137,ext_msg='lattice_SchmidMatrix_twin: '//trim(structure)) end select - + if (any(NtwinMax(1:size(Ntwin)) - Ntwin < 0)) & call IO_error(145,ext_msg='Ntwin '//trim(structure)) if (any(Ntwin < 0)) & call IO_error(144,ext_msg='Ntwin '//trim(structure)) - + coordinateSystem = buildCoordinateSystem(Ntwin,NtwinMax,twinSystems,structure,cOverA) - + do i = 1, sum(Ntwin) SchmidMatrix(1:3,1:3,i) = math_outer(coordinateSystem(1:3,1,i),coordinateSystem(1:3,2,i)) if (abs(math_trace33(SchmidMatrix(1:3,1:3,i))) > tol_math_check) & call IO_error(0,i,ext_msg = 'dilatational Schmid matrix for twin') enddo - + end function lattice_SchmidMatrix_twin - - + + !-------------------------------------------------------------------------------------------------- !> @brief Schmid matrix for twinning !> details only active twin systems are considered !-------------------------------------------------------------------------------------------------- function lattice_SchmidMatrix_trans(Ntrans,structure_target,cOverA,a_bcc,a_fcc) result(SchmidMatrix) - + integer, dimension(:), intent(in) :: Ntrans !< number of active twin systems per family character(len=*), intent(in) :: structure_target !< lattice structure real(pReal), intent(in) :: cOverA !< c/a ratio real(pReal), dimension(3,3,sum(Ntrans)) :: SchmidMatrix - + real(pReal), dimension(3,3,sum(Ntrans)) :: devNull real(pReal) :: a_bcc, a_fcc - + if (len_trim(structure_target) /= 3) & call IO_error(137,ext_msg='lattice_SchmidMatrix_trans: '//trim(structure_target)) if (structure_target(1:3) /= 'bcc' .and. structure_target(1:3) /= 'hex') & @@ -1802,15 +1780,15 @@ function lattice_SchmidMatrix_trans(Ntrans,structure_target,cOverA,a_bcc,a_fcc) if (structure_target(1:3) == 'hex' .and. (cOverA < 1.0_pReal .or. cOverA > 2.0_pReal)) & call IO_error(131,ext_msg='lattice_SchmidMatrix_trans: '//trim(structure_target)) - + if (structure_target(1:3) == 'bcc' .and. (a_bcc <= 0.0_pReal .or. a_fcc <= 0.0_pReal)) & call IO_error(134,ext_msg='lattice_SchmidMatrix_trans: '//trim(structure_target)) - + call buildTransformationSystem(devNull,SchmidMatrix,Ntrans,cOverA,a_fcc,a_bcc) - + end function lattice_SchmidMatrix_trans - - + + !-------------------------------------------------------------------------------------------------- !> @brief Schmid matrix for cleavage !> details only active cleavage systems are considered @@ -1821,15 +1799,15 @@ function lattice_SchmidMatrix_cleavage(Ncleavage,structure,cOverA) result(Schmid character(len=*), intent(in) :: structure !< lattice structure real(pReal), intent(in) :: cOverA !< c/a ratio real(pReal), dimension(3,3,3,sum(Ncleavage)) :: SchmidMatrix - + real(pReal), dimension(3,3,sum(Ncleavage)) :: coordinateSystem real(pReal), dimension(:,:), allocatable :: cleavageSystems integer, dimension(:), allocatable :: NcleavageMax integer :: i - + if (len_trim(structure) /= 3) & call IO_error(137,ext_msg='lattice_SchmidMatrix_cleavage: '//trim(structure)) - + select case(structure(1:3)) case('iso') NcleavageMax = LATTICE_ISO_NCLEAVAGESYSTEM @@ -1849,56 +1827,56 @@ function lattice_SchmidMatrix_cleavage(Ncleavage,structure,cOverA) result(Schmid case default call IO_error(137,ext_msg='lattice_SchmidMatrix_cleavage: '//trim(structure)) end select - + if (any(NcleavageMax(1:size(Ncleavage)) - Ncleavage < 0)) & call IO_error(145,ext_msg='Ncleavage '//trim(structure)) if (any(Ncleavage < 0)) & call IO_error(144,ext_msg='Ncleavage '//trim(structure)) - + coordinateSystem = buildCoordinateSystem(Ncleavage,NcleavageMax,cleavageSystems,structure,cOverA) - + do i = 1, sum(Ncleavage) SchmidMatrix(1:3,1:3,1,i) = math_outer(coordinateSystem(1:3,1,i),coordinateSystem(1:3,2,i)) SchmidMatrix(1:3,1:3,2,i) = math_outer(coordinateSystem(1:3,3,i),coordinateSystem(1:3,2,i)) SchmidMatrix(1:3,1:3,3,i) = math_outer(coordinateSystem(1:3,2,i),coordinateSystem(1:3,2,i)) enddo - + end function lattice_SchmidMatrix_cleavage - - + + !-------------------------------------------------------------------------------------------------- !> @brief Slip direction of slip systems (|| b) !-------------------------------------------------------------------------------------------------- function lattice_slip_direction(Nslip,structure,cOverA) result(d) - + integer, dimension(:), intent(in) :: Nslip !< number of active slip systems per family character(len=*), intent(in) :: structure !< lattice structure real(pReal), intent(in) :: cOverA !< c/a ratio real(pReal), dimension(3,sum(Nslip)) :: d - + real(pReal), dimension(3,3,sum(Nslip)) :: coordinateSystem - + coordinateSystem = coordinateSystem_slip(Nslip,structure,cOverA) d = coordinateSystem(1:3,1,1:sum(Nslip)) - + end function lattice_slip_direction - - + + !-------------------------------------------------------------------------------------------------- !> @brief Normal direction of slip systems (|| n) !-------------------------------------------------------------------------------------------------- function lattice_slip_normal(Nslip,structure,cOverA) result(n) - + integer, dimension(:), intent(in) :: Nslip !< number of active slip systems per family character(len=*), intent(in) :: structure !< lattice structure real(pReal), intent(in) :: cOverA !< c/a ratio real(pReal), dimension(3,sum(Nslip)) :: n - + real(pReal), dimension(3,3,sum(Nslip)) :: coordinateSystem - + coordinateSystem = coordinateSystem_slip(Nslip,structure,cOverA) n = coordinateSystem(1:3,2,1:sum(Nslip)) - + end function lattice_slip_normal @@ -1906,41 +1884,41 @@ end function lattice_slip_normal !> @brief Transverse direction of slip systems ( || t = b x n) !-------------------------------------------------------------------------------------------------- function lattice_slip_transverse(Nslip,structure,cOverA) result(t) - + integer, dimension(:), intent(in) :: Nslip !< number of active slip systems per family character(len=*), intent(in) :: structure !< lattice structure real(pReal), intent(in) :: cOverA !< c/a ratio real(pReal), dimension(3,sum(Nslip)) :: t - + real(pReal), dimension(3,3,sum(Nslip)) :: coordinateSystem - + coordinateSystem = coordinateSystem_slip(Nslip,structure,cOverA) t = coordinateSystem(1:3,3,1:sum(Nslip)) - + end function lattice_slip_transverse - - + + !-------------------------------------------------------------------------------------------------- !> @brief Projection of the transverse direction onto the slip plane !> @details: This projection is used to calculate forest hardening for edge dislocations !-------------------------------------------------------------------------------------------------- function slipProjection_transverse(Nslip,structure,cOverA) result(projection) - + integer, dimension(:), intent(in) :: Nslip !< number of active slip systems per family character(len=*), intent(in) :: structure !< lattice structure real(pReal), intent(in) :: cOverA !< c/a ratio real(pReal), dimension(sum(Nslip),sum(Nslip)) :: projection - + real(pReal), dimension(3,sum(Nslip)) :: n, t integer :: i, j - + n = lattice_slip_normal (Nslip,structure,cOverA) t = lattice_slip_transverse(Nslip,structure,cOverA) - + do i=1, sum(Nslip); do j=1, sum(Nslip) projection(i,j) = abs(math_inner(n(:,i),t(:,j))) enddo; enddo - + end function slipProjection_transverse @@ -1952,15 +1930,15 @@ function lattice_labels_slip(Nslip,structure) result(labels) integer, dimension(:), intent(in) :: Nslip !< number of active slip systems per family character(len=*), intent(in) :: structure !< lattice structure - + character(len=:), dimension(:), allocatable :: labels real(pReal), dimension(:,:), allocatable :: slipSystems integer, dimension(:), allocatable :: NslipMax - + if (len_trim(structure) /= 3) & call IO_error(137,ext_msg='lattice_labels_slip: '//trim(structure)) - + select case(structure(1:3)) case('fcc') NslipMax = LATTICE_FCC_NSLIPSYSTEM @@ -1977,14 +1955,14 @@ function lattice_labels_slip(Nslip,structure) result(labels) case default call IO_error(137,ext_msg='lattice_labels_slip: '//trim(structure)) end select - + if (any(NslipMax(1:size(Nslip)) - Nslip < 0)) & call IO_error(145,ext_msg='Nslip '//trim(structure)) if (any(Nslip < 0)) & call IO_error(144,ext_msg='Nslip '//trim(structure)) - + labels = getLabels(Nslip,NslipMax,slipSystems) - + end function lattice_labels_slip @@ -1996,15 +1974,15 @@ function lattice_labels_twin(Ntwin,structure) result(labels) integer, dimension(:), intent(in) :: Ntwin !< number of active slip systems per family character(len=*), intent(in) :: structure !< lattice structure - + character(len=:), dimension(:), allocatable :: labels real(pReal), dimension(:,:), allocatable :: twinSystems integer, dimension(:), allocatable :: NtwinMax - + if (len_trim(structure) /= 3) & call IO_error(137,ext_msg='lattice_labels_twin: '//trim(structure)) - + select case(structure(1:3)) case('fcc') NtwinMax = LATTICE_FCC_NTWINSYSTEM @@ -2018,14 +1996,14 @@ function lattice_labels_twin(Ntwin,structure) result(labels) case default call IO_error(137,ext_msg='lattice_labels_twin: '//trim(structure)) end select - + if (any(NtwinMax(1:size(Ntwin)) - Ntwin < 0)) & call IO_error(145,ext_msg='Ntwin '//trim(structure)) if (any(Ntwin < 0)) & call IO_error(144,ext_msg='Ntwin '//trim(structure)) - + labels = getLabels(Ntwin,NtwinMax,twinSystems) - + end function lattice_labels_twin @@ -2034,25 +2012,25 @@ end function lattice_labels_twin !> @details: This projection is used to calculate forest hardening for screw dislocations !-------------------------------------------------------------------------------------------------- function slipProjection_direction(Nslip,structure,cOverA) result(projection) - + integer, dimension(:), intent(in) :: Nslip !< number of active slip systems per family character(len=*), intent(in) :: structure !< lattice structure real(pReal), intent(in) :: cOverA !< c/a ratio real(pReal), dimension(sum(Nslip),sum(Nslip)) :: projection - + real(pReal), dimension(3,sum(Nslip)) :: n, d integer :: i, j - + n = lattice_slip_normal (Nslip,structure,cOverA) d = lattice_slip_direction(Nslip,structure,cOverA) - + do i=1, sum(Nslip); do j=1, sum(Nslip) projection(i,j) = abs(math_inner(n(:,i),d(:,j))) enddo; enddo - + end function slipProjection_direction - - + + !-------------------------------------------------------------------------------------------------- !> @brief build a local coordinate system on slip systems !> @details Order: Direction, plane (normal), and common perpendicular @@ -2063,13 +2041,13 @@ function coordinateSystem_slip(Nslip,structure,cOverA) result(coordinateSystem) character(len=*), intent(in) :: structure !< lattice structure real(pReal), intent(in) :: cOverA !< c/a ratio real(pReal), dimension(3,3,sum(Nslip)) :: coordinateSystem - + real(pReal), dimension(:,:), allocatable :: slipSystems integer, dimension(:), allocatable :: NslipMax - + if (len_trim(structure) /= 3) & call IO_error(137,ext_msg='coordinateSystem_slip: '//trim(structure)) - + select case(structure(1:3)) case('fcc') NslipMax = LATTICE_FCC_NSLIPSYSTEM @@ -2086,22 +2064,22 @@ function coordinateSystem_slip(Nslip,structure,cOverA) result(coordinateSystem) case default call IO_error(137,ext_msg='coordinateSystem_slip: '//trim(structure)) end select - + if (any(NslipMax(1:size(Nslip)) - Nslip < 0)) & call IO_error(145,ext_msg='Nslip '//trim(structure)) if (any(Nslip < 0)) & call IO_error(144,ext_msg='Nslip '//trim(structure)) - + coordinateSystem = buildCoordinateSystem(Nslip,NslipMax,slipSystems,structure,cOverA) - + end function coordinateSystem_slip - - + + !-------------------------------------------------------------------------------------------------- !> @brief Populates reduced interaction matrix !-------------------------------------------------------------------------------------------------- function buildInteraction(reacting_used,acting_used,reacting_max,acting_max,values,matrix) - + integer, dimension(:), intent(in) :: & reacting_used, & !< # of reacting systems per family as specified in material.config acting_used, & !< # of acting systems per family as specified in material.config @@ -2110,42 +2088,42 @@ function buildInteraction(reacting_used,acting_used,reacting_max,acting_max,valu real(pReal), dimension(:), intent(in) :: values !< interaction values integer, dimension(:,:), intent(in) :: matrix !< interaction types real(pReal), dimension(sum(reacting_used),sum(acting_used)) :: buildInteraction - + integer :: & acting_family_index, acting_family, acting_system, & reacting_family_index, reacting_family, reacting_system, & i,j,k,l - + do acting_family = 1,size(acting_used,1) acting_family_index = sum(acting_used(1:acting_family-1)) do acting_system = 1,acting_used(acting_family) - + do reacting_family = 1,size(reacting_used,1) reacting_family_index = sum(reacting_used(1:reacting_family-1)) do reacting_system = 1,reacting_used(reacting_family) - + i = sum( acting_max(1: acting_family-1)) + acting_system j = sum(reacting_max(1:reacting_family-1)) + reacting_system - + k = acting_family_index + acting_system l = reacting_family_index + reacting_system - + if (matrix(i,j) > size(values)) call IO_error(138,ext_msg='buildInteraction') - + buildInteraction(l,k) = values(matrix(i,j)) - + enddo; enddo enddo; enddo - + end function buildInteraction - - + + !-------------------------------------------------------------------------------------------------- !> @brief build a local coordinate system on slip, twin, trans, cleavage systems !> @details Order: Direction, plane (normal), and common perpendicular !-------------------------------------------------------------------------------------------------- function buildCoordinateSystem(active,potential,system,structure,cOverA) - + integer, dimension(:), intent(in) :: & active, & !< # of active systems per family potential !< # of potential systems per family @@ -2157,7 +2135,7 @@ function buildCoordinateSystem(active,potential,system,structure,cOverA) cOverA real(pReal), dimension(3,3,sum(active)) :: & buildCoordinateSystem - + real(pReal), dimension(3) :: & direction, normal integer :: & @@ -2165,26 +2143,26 @@ function buildCoordinateSystem(active,potential,system,structure,cOverA) p, & !< index in potential system matrix f, & !< index of my family s !< index of my system in current family - + if (len_trim(structure) /= 3) & call IO_error(137,ext_msg='buildCoordinateSystem: '//trim(structure)) if (trim(structure(1:3)) == 'bct' .and. cOverA > 2.0_pReal) & call IO_error(131,ext_msg='buildCoordinateSystem:'//trim(structure)) if (trim(structure(1:3)) == 'hex' .and. (cOverA < 1.0_pReal .or. cOverA > 2.0_pReal)) & call IO_error(131,ext_msg='buildCoordinateSystem:'//trim(structure)) - + a = 0 activeFamilies: do f = 1,size(active,1) activeSystems: do s = 1,active(f) a = a + 1 p = sum(potential(1:f-1))+s - + select case(trim(structure(1:3))) - + case ('fcc','bcc','iso','ort','bct') direction = system(1:3,p) normal = system(4:6,p) - + case ('hex') direction = [ system(1,p)*1.5_pReal, & (system(1,p)+2.0_pReal*system(2,p))*sqrt(0.75_pReal), & @@ -2192,23 +2170,23 @@ function buildCoordinateSystem(active,potential,system,structure,cOverA) normal = [ system(5,p), & (system(5,p)+2.0_pReal*system(6,p))/sqrt(3.0_pReal), & system(8,p)/cOverA ] ! plane (hkil)->(h (h+2k)/sqrt(3) l/(p/a)) - + case default call IO_error(137,ext_msg='buildCoordinateSystem: '//trim(structure)) - + end select - + buildCoordinateSystem(1:3,1,a) = direction/norm2(direction) buildCoordinateSystem(1:3,2,a) = normal /norm2(normal) buildCoordinateSystem(1:3,3,a) = math_cross(direction/norm2(direction),& normal /norm2(normal)) - + enddo activeSystems enddo activeFamilies - + end function buildCoordinateSystem - - + + !-------------------------------------------------------------------------------------------------- !> @brief Helper function to define transformation systems ! Needed to calculate Schmid matrix and rotated stiffness matrices. @@ -2216,7 +2194,7 @@ end function buildCoordinateSystem ! set a_Xcc = 0.0 for fcc -> hex transformation !-------------------------------------------------------------------------------------------------- subroutine buildTransformationSystem(Q,S,Ntrans,cOverA,a_fcc,a_bcc) - + integer, dimension(:), intent(in) :: & Ntrans real(pReal), dimension(3,3,sum(Ntrans)), intent(out) :: & @@ -2226,10 +2204,10 @@ subroutine buildTransformationSystem(Q,S,Ntrans,cOverA,a_fcc,a_bcc) cOverA, & !< c/a for target hex structure a_bcc, & !< lattice parameter a for target bcc structure a_fcc !< lattice parameter a for parent fcc structure - + type(rotation) :: & R, & !< Pitsch rotation - B !< Rotation of fcc to Bain coordinate system + B !< Rotation of fcc to Bain coordinate system real(pReal), dimension(3,3) :: & U, & !< Bain deformation ss, sd @@ -2267,7 +2245,7 @@ subroutine buildTransformationSystem(Q,S,Ntrans,cOverA,a_fcc,a_bcc) 0.0, 1.0, 0.0, 10.26, & 0.0,-1.0, 0.0, 10.26 & ],shape(LATTICE_FCCTOBCC_SYSTEMTRANS)) - + integer, dimension(9,LATTICE_fcc_Ntrans), parameter :: & LATTICE_FCCTOBCC_BAINVARIANT = reshape( [& 1, 0, 0, 0, 1, 0, 0, 0, 1, & ! Pitsch OR (Ma & Hartmaier 2014, Table 3) @@ -2283,7 +2261,7 @@ subroutine buildTransformationSystem(Q,S,Ntrans,cOverA,a_fcc,a_bcc) 0, 0, 1, 1, 0, 0, 0, 1, 0, & 0, 0, 1, 1, 0, 0, 0, 1, 0 & ],shape(LATTICE_FCCTOBCC_BAINVARIANT)) - + real(pReal), dimension(4,LATTICE_fcc_Ntrans), parameter :: & LATTICE_FCCTOBCC_BAINROT = reshape([& 1.0, 0.0, 0.0, 45.0, & ! Rotate fcc austensite to bain variant @@ -2299,7 +2277,7 @@ subroutine buildTransformationSystem(Q,S,Ntrans,cOverA,a_fcc,a_bcc) 0.0, 0.0, 1.0, 45.0, & 0.0, 0.0, 1.0, 45.0 & ],shape(LATTICE_FCCTOBCC_BAINROT)) - + if (a_bcc > 0.0_pReal .and. a_fcc > 0.0_pReal .and. dEq0(cOverA)) then ! fcc -> bcc transformation do i = 1,sum(Ntrans) call R%fromAxisAngle(LATTICE_FCCTOBCC_SYSTEMTRANS(:,i),degrees=.true.,P=1) @@ -2307,7 +2285,7 @@ subroutine buildTransformationSystem(Q,S,Ntrans,cOverA,a_fcc,a_bcc) x = real(LATTICE_FCCTOBCC_BAINVARIANT(1:3,i),pReal) y = real(LATTICE_FCCTOBCC_BAINVARIANT(4:6,i),pReal) z = real(LATTICE_FCCTOBCC_BAINVARIANT(7:9,i),pReal) - + U = (a_bcc/a_fcc)*math_outer(x,x) & + (a_bcc/a_fcc)*math_outer(y,y) * sqrt(2.0_pReal) & + (a_bcc/a_fcc)*math_outer(z,z) * sqrt(2.0_pReal) @@ -2319,7 +2297,7 @@ subroutine buildTransformationSystem(Q,S,Ntrans,cOverA,a_fcc,a_bcc) sd = MATH_I3 ss(1,3) = sqrt(2.0_pReal)/4.0_pReal sd(3,3) = cOverA/sqrt(8.0_pReal/3.0_pReal) - + do i = 1,sum(Ntrans) x = LATTICE_FCCTOHEX_SYSTEMTRANS(1:3,i)/norm2(LATTICE_FCCTOHEX_SYSTEMTRANS(1:3,i)) z = LATTICE_FCCTOHEX_SYSTEMTRANS(4:6,i)/norm2(LATTICE_FCCTOHEX_SYSTEMTRANS(4:6,i)) @@ -2340,7 +2318,7 @@ end subroutine buildTransformationSystem !> @brief select active systems as strings !-------------------------------------------------------------------------------------------------- function getlabels(active,potential,system) result(labels) - + integer, dimension(:), intent(in) :: & active, & !< # of active systems per family potential !< # of potential systems per family @@ -2350,7 +2328,7 @@ function getlabels(active,potential,system) result(labels) character(len=:), dimension(:), allocatable :: labels character(len=:), allocatable :: label - integer :: i,j + integer :: i,j integer :: & a, & !< index of active system p, & !< index in potential system matrix @@ -2359,13 +2337,13 @@ function getlabels(active,potential,system) result(labels) i = 2*size(system,1) + (size(system,1) - 2) + 4 ! 2 letters per index + spaces + brackets allocate(character(len=i) :: labels(sum(active)), label) - + a = 0 activeFamilies: do f = 1,size(active,1) activeSystems: do s = 1,active(f) a = a + 1 p = sum(potential(1:f-1))+s - + i = 1 label(i:i) = '[' direction: do j = 1, size(system,1)/2 @@ -2374,7 +2352,7 @@ function getlabels(active,potential,system) result(labels) i = i + 3 enddo direction label(i:i) = ']' - + i = i +1 label(i:i) = '(' normal: do j = size(system,1)/2+1, size(system,1) @@ -2383,12 +2361,12 @@ function getlabels(active,potential,system) result(labels) i = i + 3 enddo normal label(i:i) = ')' - + labels(s) = label enddo activeSystems enddo activeFamilies - + end function getlabels diff --git a/src/material.f90 b/src/material.f90 index 7e68049b8..9c5009484 100644 --- a/src/material.f90 +++ b/src/material.f90 @@ -257,7 +257,7 @@ subroutine material_init allocate(damage (material_Nhomogenization)) allocate(temperatureRate (material_Nhomogenization)) - + do m = 1,size(config_microstructure) if(minval(microstructure_phase(1:microstructure_Nconstituents(m),m)) < 1 .or. & maxval(microstructure_phase(1:microstructure_Nconstituents(m),m)) > size(config_phase)) & @@ -268,6 +268,7 @@ subroutine material_init if(microstructure_Nconstituents(m) < 1) & call IO_error(151,m) enddo + if(homogenization_maxNgrains > size(microstructure_phase,1)) call IO_error(148) debugOut: if (iand(myDebug,debug_levelExtensive) /= 0) then write(6,'(/,a,/)') ' MATERIAL configuration' @@ -290,6 +291,7 @@ subroutine material_init !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! new mappings + allocate(material_phaseAt(homogenization_maxNgrains,discretization_nElem), source=0) allocate(material_texture(homogenization_maxNgrains,discretization_nIP,discretization_nElem),source=0) !this is only needed by plasticity nonlocal allocate(material_orientation0(homogenization_maxNgrains,discretization_nIP,discretization_nElem)) @@ -298,15 +300,24 @@ subroutine material_init do i = 1, discretization_nIP myMicro = discretization_microstructureAt(e) do c = 1, homogenization_Ngrains(discretization_homogenizationAt(e)) - material_phaseAt(c,e) = microstructure_phase(c,myMicro) - material_texture(c,i,e) = microstructure_texture(c,myMicro) - material_orientation0(c,i,e) = texture_orientation(material_texture(c,i,e)) + if(microstructure_phase(c,myMicro) > 0) then + material_phaseAt(c,e) = microstructure_phase(c,myMicro) + else + call IO_error(150,ext_msg='phase') + endif + if(microstructure_texture(c,myMicro) > 0) then + material_texture(c,i,e) = microstructure_texture(c,myMicro) + material_orientation0(c,i,e) = texture_orientation(material_texture(c,i,e)) + else + call IO_error(150,ext_msg='texture') + endif enddo enddo enddo deallocate(microstructure_phase) deallocate(microstructure_texture) + deallocate(texture_orientation) allocate(material_homogenizationAt,source=discretization_homogenizationAt) @@ -464,7 +475,7 @@ subroutine material_parseMicrostructure real(pReal), dimension(:,:), allocatable :: & microstructure_fraction !< vol fraction of each constituent in microstructure integer :: & - microstructure_maxNconstituents !< max number of constituents in any phase + maxNconstituents !< max number of constituents in any phase allocate(microstructure_Nconstituents(size(config_microstructure)), source=0) @@ -475,10 +486,10 @@ subroutine material_parseMicrostructure microstructure_Nconstituents(m) = config_microstructure(m)%countKeys('(constituent)') enddo - microstructure_maxNconstituents = maxval(microstructure_Nconstituents) - allocate(microstructure_phase (microstructure_maxNconstituents,size(config_microstructure)),source=0) - allocate(microstructure_texture (microstructure_maxNconstituents,size(config_microstructure)),source=0) - allocate(microstructure_fraction(microstructure_maxNconstituents,size(config_microstructure)),source=0.0_pReal) + maxNconstituents = maxval(microstructure_Nconstituents) + allocate(microstructure_phase (maxNconstituents,size(config_microstructure)),source=0) + allocate(microstructure_texture (maxNconstituents,size(config_microstructure)),source=0) + allocate(microstructure_fraction(maxNconstituents,size(config_microstructure)),source=0.0_pReal) allocate(strings(1)) ! Intel 16.0 Bug do m=1, size(config_microstructure) diff --git a/src/math.f90 b/src/math.f90 index 33e14bdfd..5d02812da 100644 --- a/src/math.f90 +++ b/src/math.f90 @@ -72,7 +72,12 @@ module math 3,2, & 3,3 & ],[2,9]) !< arrangement in Plain notation - + + + interface math_mul33xx33 + module procedure math_tensordot + end interface math_mul33xx33 + !--------------------------------------------------------------------------------------------------- private :: & unitTest @@ -88,7 +93,7 @@ subroutine math_init real(pReal), dimension(4) :: randTest integer :: randSize integer, dimension(:), allocatable :: randInit - + write(6,'(/,a)') ' <<<+- math init -+>>>'; flush(6) call random_seed(size=randSize) @@ -140,7 +145,7 @@ recursive subroutine math_sort(a, istart, iend, sortDim) else e = ubound(a,2) endif - + if(present(sortDim)) then d = sortDim else @@ -160,12 +165,12 @@ recursive subroutine math_sort(a, istart, iend, sortDim) !> @brief Partitioning required for quicksort !------------------------------------------------------------------------------------------------- integer function qsort_partition(a, istart, iend, sort) - + integer, dimension(:,:), intent(inout) :: a integer, intent(in) :: istart,iend,sort integer, dimension(size(a,1)) :: tmp integer :: i,j - + do ! find the first element on the right side less than or equal to the pivot point do j = iend, istart, -1 @@ -187,7 +192,7 @@ recursive subroutine math_sort(a, istart, iend, sortDim) a(:,j) = tmp endif cross enddo - + end function qsort_partition end subroutine math_sort @@ -196,7 +201,7 @@ end subroutine math_sort !-------------------------------------------------------------------------------------------------- !> @brief vector expansion !> @details takes a set of numbers (a,b,c,...) and corresponding multiples (x,y,z,...) -!> to return a vector of x times a, y times b, z times c, ... +!> to return a vector of x times a, y times b, z times c, ... !-------------------------------------------------------------------------------------------------- pure function math_expand(what,how) @@ -204,9 +209,9 @@ pure function math_expand(what,how) integer, dimension(:), intent(in) :: how real(pReal), dimension(sum(how)) :: math_expand integer :: i - + if (sum(how) == 0) return - + do i = 1, size(how) math_expand(sum(how(1:i-1))+1:sum(how(1:i))) = what(mod(i-1,size(what))+1) enddo @@ -266,31 +271,30 @@ end function math_identity4th !-------------------------------------------------------------------------------------------------- -!> @brief permutation tensor e_ijk used for computing cross product of two tensors +!> @brief permutation tensor e_ijk ! e_ijk = 1 if even permutation of ijk ! e_ijk = -1 if odd permutation of ijk ! e_ijk = 0 otherwise !-------------------------------------------------------------------------------------------------- -real(pReal) pure function math_civita(i,j,k) +real(pReal) pure function math_LeviCivita(i,j,k) integer, intent(in) :: i,j,k - math_civita = 0.0_pReal - if (((i == 1).and.(j == 2).and.(k == 3)) .or. & - ((i == 2).and.(j == 3).and.(k == 1)) .or. & - ((i == 3).and.(j == 1).and.(k == 2))) math_civita = 1.0_pReal - if (((i == 1).and.(j == 3).and.(k == 2)) .or. & - ((i == 2).and.(j == 1).and.(k == 3)) .or. & - ((i == 3).and.(j == 2).and.(k == 1))) math_civita = -1.0_pReal + if (all([i,j,k] == [1,2,3]) .or. all([i,j,k] == [2,3,1]) .or. all([i,j,k] == [3,1,2])) then + math_LeviCivita = +1.0_pReal + elseif (all([i,j,k] == [3,2,1]) .or. all([i,j,k] == [2,1,3]) .or. all([i,j,k] == [1,3,2])) then + math_LeviCivita = -1.0_pReal + else + math_LeviCivita = 0.0_pReal + endif -end function math_civita +end function math_LeviCivita !-------------------------------------------------------------------------------------------------- !> @brief kronecker delta function d_ij ! d_ij = 1 if i = j ! d_ij = 0 otherwise -! inspired by http://fortraninacworld.blogspot.de/2012/12/ternary-operator.html !-------------------------------------------------------------------------------------------------- real(pReal) pure function math_delta(i,j) @@ -317,7 +321,7 @@ end function math_cross !-------------------------------------------------------------------------------------------------- -!> @brief outer product A \otimes B of arbitrary sized vectors A and B +!> @brief outer product of arbitrary sized vectors (A ⊗ B / i,j) !-------------------------------------------------------------------------------------------------- pure function math_outer(A,B) @@ -333,7 +337,7 @@ end function math_outer !-------------------------------------------------------------------------------------------------- -!> @brief outer product A \otimes B of arbitrary sized vectors A and B +!> @brief inner product of arbitrary sized vectors (A · B / i,i) !-------------------------------------------------------------------------------------------------- real(pReal) pure function math_inner(A,B) @@ -346,31 +350,26 @@ end function math_inner !-------------------------------------------------------------------------------------------------- -!> @brief matrix multiplication 33xx33 = 1 (double contraction --> ij * ij) +!> @brief double contraction of 3x3 matrices (A : B / ij,ij) !-------------------------------------------------------------------------------------------------- -real(pReal) pure function math_mul33xx33(A,B) +real(pReal) pure function math_tensordot(A,B) real(pReal), dimension(3,3), intent(in) :: A,B - integer :: i,j - real(pReal), dimension(3,3) :: C - do i=1,3; do j=1,3 - C(i,j) = A(i,j) * B(i,j) - enddo; enddo - math_mul33xx33 = sum(C) + math_tensordot = sum(A*B) -end function math_mul33xx33 +end function math_tensordot !-------------------------------------------------------------------------------------------------- -!> @brief matrix multiplication 3333x33 = 33 (double contraction --> ijkl *kl = ij) +!> @brief matrix double contraction 3333x33 = 33 (ijkl,kl) !-------------------------------------------------------------------------------------------------- pure function math_mul3333xx33(A,B) real(pReal), dimension(3,3,3,3), intent(in) :: A real(pReal), dimension(3,3), intent(in) :: B real(pReal), dimension(3,3) :: math_mul3333xx33 - integer :: i,j + integer :: i,j do i=1,3; do j=1,3 math_mul3333xx33(i,j) = sum(A(i,j,1:3,1:3)*B(1:3,1:3)) @@ -380,7 +379,7 @@ end function math_mul3333xx33 !-------------------------------------------------------------------------------------------------- -!> @brief matrix multiplication 3333x3333 = 3333 (ijkl *klmn = ijmn) +!> @brief matrix multiplication 3333x3333 = 3333 (ijkl,klmn) !-------------------------------------------------------------------------------------------------- pure function math_mul3333xx3333(A,B) @@ -402,8 +401,9 @@ end function math_mul3333xx3333 pure function math_exp33(A,n) real(pReal), dimension(3,3), intent(in) :: A - integer, intent(in), optional :: n + integer, intent(in), optional :: n real(pReal), dimension(3,3) :: B, math_exp33 + real(pReal) :: invFac integer :: n_,i @@ -428,15 +428,16 @@ end function math_exp33 !-------------------------------------------------------------------------------------------------- !> @brief Cramer inversion of 33 matrix (function) -!> @details Direct Cramer inversion of matrix A. Returns all zeroes if not possible, i.e. +!> @details Direct Cramer inversion of matrix A. Returns all zeroes if not possible, i.e. ! if determinant is close to zero !-------------------------------------------------------------------------------------------------- pure function math_inv33(A) - real(pReal),dimension(3,3),intent(in) :: A - real(pReal) :: DetA - real(pReal),dimension(3,3) :: math_inv33 - logical :: error + real(pReal), dimension(3,3), intent(in) :: A + real(pReal), dimension(3,3) :: math_inv33 + + real(pReal) :: DetA + logical :: error call math_invert33(math_inv33,DetA,error,A) if(error) math_inv33 = 0.0_pReal @@ -451,10 +452,10 @@ end function math_inv33 !-------------------------------------------------------------------------------------------------- pure subroutine math_invert33(InvA, DetA, error, A) - logical, intent(out) :: error - real(pReal),dimension(3,3),intent(in) :: A - real(pReal),dimension(3,3),intent(out) :: InvA - real(pReal), intent(out) :: DetA + real(pReal), dimension(3,3), intent(out) :: InvA + real(pReal), intent(out) :: DetA + logical, intent(out) :: error + real(pReal), dimension(3,3), intent(in) :: A InvA(1,1) = A(2,2) * A(3,3) - A(2,3) * A(3,2) InvA(2,1) = -A(2,1) * A(3,3) + A(2,3) * A(3,1) @@ -529,12 +530,12 @@ subroutine math_invert(InvA, error, A) dgetrf, & dgetri - invA = A + invA = A call dgetrf(size(A,1),size(A,1),invA,size(A,1),ipiv,ierr) error = (ierr /= 0) call dgetri(size(A,1),InvA,size(A,1),ipiv,work,size(work,1),ierr) error = error .or. (ierr /= 0) - + end subroutine math_invert @@ -621,7 +622,7 @@ end function math_trace33 real(pReal) pure function math_det33(m) real(pReal), dimension(3,3), intent(in) :: m - + math_det33 = m(1,1)* (m(2,2)*m(3,3)-m(2,3)*m(3,2)) & - m(1,2)* (m(2,1)*m(3,3)-m(2,3)*m(3,1)) & + m(1,3)* (m(2,1)*m(3,2)-m(2,2)*m(3,1)) @@ -635,7 +636,7 @@ end function math_det33 real(pReal) pure function math_detSym33(m) real(pReal), dimension(3,3), intent(in) :: m - + math_detSym33 = -(m(1,1)*m(2,3)**2 + m(2,2)*m(1,3)**2 + m(3,3)*m(1,2)**2) & + m(1,1)*m(2,2)*m(3,3) + 2.0_pReal * m(1,2)*m(1,3)*m(2,3) @@ -649,9 +650,9 @@ pure function math_33to9(m33) real(pReal), dimension(9) :: math_33to9 real(pReal), dimension(3,3), intent(in) :: m33 - + integer :: i - + do i = 1, 9 math_33to9(i) = m33(MAPPLAIN(1,i),MAPPLAIN(2,i)) enddo @@ -666,7 +667,7 @@ pure function math_9to33(v9) real(pReal), dimension(3,3) :: math_9to33 real(pReal), dimension(9), intent(in) :: v9 - + integer :: i do i = 1, 9 @@ -687,10 +688,10 @@ pure function math_sym33to6(m33,weighted) real(pReal), dimension(6) :: math_sym33to6 real(pReal), dimension(3,3), intent(in) :: m33 !< symmetric matrix (no internal check) logical, optional, intent(in) :: weighted !< weight according to Mandel (.true. by default) - + real(pReal), dimension(6) :: w integer :: i - + if(present(weighted)) then w = merge(NRMMANDEL,1.0_pReal,weighted) else @@ -699,7 +700,7 @@ pure function math_sym33to6(m33,weighted) do i = 1, 6 math_sym33to6(i) = w(i)*m33(MAPNYE(1,i),MAPNYE(2,i)) - enddo + enddo end function math_sym33to6 @@ -718,7 +719,7 @@ pure function math_6toSym33(v6,weighted) real(pReal), dimension(6) :: w integer :: i - + if(present(weighted)) then w = merge(INVNRMMANDEL,1.0_pReal,weighted) else @@ -781,7 +782,7 @@ pure function math_sym3333to66(m3333,weighted) real(pReal), dimension(6) :: w integer :: i,j - + if(present(weighted)) then w = merge(NRMMANDEL,1.0_pReal,weighted) else @@ -806,10 +807,10 @@ pure function math_66toSym3333(m66,weighted) real(pReal), dimension(3,3,3,3) :: math_66toSym3333 real(pReal), dimension(6,6), intent(in) :: m66 logical, optional, intent(in) :: weighted !< weight according to Mandel (.true. by default) - + real(pReal), dimension(6) :: w integer :: i,j - + if(present(weighted)) then w = merge(INVNRMMANDEL,1.0_pReal,weighted) else @@ -909,48 +910,48 @@ end subroutine math_eigenValuesVectorsSym ! ToDo: has wrong oder of arguments !-------------------------------------------------------------------------------------------------- subroutine math_eigenValuesVectorsSym33(m,values,vectors) - + real(pReal), dimension(3,3),intent(in) :: m real(pReal), dimension(3), intent(out) :: values real(pReal), dimension(3,3),intent(out) :: vectors real(pReal) :: T, U, norm, threshold logical :: error - + values = math_eigenvaluesSym33(m) - + vectors(1:3,2) = [ m(1, 2) * m(2, 3) - m(1, 3) * m(2, 2), & m(1, 3) * m(1, 2) - m(2, 3) * m(1, 1), & m(1, 2)**2] - + T = maxval(abs(values)) U = max(T, T**2) threshold = sqrt(5.68e-14_pReal * U**2) - + ! Calculate first eigenvector by the formula v[0] = (m - lambda[0]).e1 x (m - lambda[0]).e2 vectors(1:3,1) = [ vectors(1,2) + m(1, 3) * values(1), & vectors(2,2) + m(2, 3) * values(1), & (m(1,1) - values(1)) * (m(2,2) - values(1)) - vectors(3,2)] norm = norm2(vectors(1:3, 1)) - + fallback1: if(norm < threshold) then call math_eigenValuesVectorsSym(m,values,vectors,error) return endif fallback1 - - vectors(1:3,1) = vectors(1:3, 1) / norm - + + vectors(1:3,1) = vectors(1:3, 1) / norm + ! Calculate second eigenvector by the formula v[1] = (m - lambda[1]).e1 x (m - lambda[1]).e2 vectors(1:3,2) = [ vectors(1,2) + m(1, 3) * values(2), & vectors(2,2) + m(2, 3) * values(2), & (m(1,1) - values(2)) * (m(2,2) - values(2)) - vectors(3,2)] norm = norm2(vectors(1:3, 2)) - + fallback2: if(norm < threshold) then call math_eigenValuesVectorsSym(m,values,vectors,error) return endif fallback2 vectors(1:3,2) = vectors(1:3, 2) / norm - + ! Calculate third eigenvector according to v[2] = v[0] x v[1] vectors(1:3,3) = math_cross(vectors(1:3,1),vectors(1:3,2)) @@ -968,11 +969,11 @@ function math_eigenvectorBasisSym(m) real(pReal), dimension(size(m,1),size(m,1)) :: math_eigenvectorBasisSym logical :: error integer :: i - + math_eigenvectorBasisSym = 0.0_pReal call math_eigenValuesVectorsSym(m,values,vectors,error) if(error) return - + do i=1, size(m,1) math_eigenvectorBasisSym = math_eigenvectorBasisSym & + sqrt(values(i)) * math_outer(vectors(:,i),vectors(:,i)) @@ -992,13 +993,13 @@ pure function math_eigenvectorBasisSym33(m) real(pReal) :: P, Q, rho, phi real(pReal), parameter :: TOL=1.e-14_pReal real(pReal), dimension(3,3,3) :: N, EB - + invariants = math_invariantsSym33(m) EB = 0.0_pReal - + P = invariants(2)-invariants(1)**2.0_pReal/3.0_pReal Q = -2.0_pReal/27.0_pReal*invariants(1)**3.0_pReal+product(invariants(1:2))/3.0_pReal-invariants(3) - + threeSimilarEigenvalues: if(all(abs([P,Q]) < TOL)) then values = invariants(1)/3.0_pReal ! this is not really correct, but at least the basis is correct @@ -1016,7 +1017,7 @@ pure function math_eigenvectorBasisSym33(m) N(1:3,1:3,1) = m-values(1)*math_I3 N(1:3,1:3,2) = m-values(2)*math_I3 N(1:3,1:3,3) = m-values(3)*math_I3 - twoSimilarEigenvalues: if(abs(values(1)-values(2)) < TOL) then + twoSimilarEigenvalues: if(abs(values(1)-values(2)) < TOL) then EB(1:3,1:3,3)=matmul(N(1:3,1:3,1),N(1:3,1:3,2))/ & ((values(3)-values(1))*(values(3)-values(2))) EB(1:3,1:3,1)=math_I3-EB(1:3,1:3,3) @@ -1024,7 +1025,7 @@ pure function math_eigenvectorBasisSym33(m) EB(1:3,1:3,1)=matmul(N(1:3,1:3,2),N(1:3,1:3,3))/ & ((values(1)-values(2))*(values(1)-values(3))) EB(1:3,1:3,2)=math_I3-EB(1:3,1:3,1) - elseif(abs(values(3)-values(1)) < TOL) then twoSimilarEigenvalues + elseif(abs(values(3)-values(1)) < TOL) then twoSimilarEigenvalues EB(1:3,1:3,2)=matmul(N(1:3,1:3,1),N(1:3,1:3,3))/ & ((values(2)-values(1))*(values(2)-values(3))) EB(1:3,1:3,1)=math_I3-EB(1:3,1:3,2) @@ -1080,7 +1081,7 @@ pure function math_eigenvectorBasisSym33_log(m) N(1:3,1:3,1) = m-values(1)*math_I3 N(1:3,1:3,2) = m-values(2)*math_I3 N(1:3,1:3,3) = m-values(3)*math_I3 - twoSimilarEigenvalues: if(abs(values(1)-values(2)) < TOL) then + twoSimilarEigenvalues: if(abs(values(1)-values(2)) < TOL) then EB(1:3,1:3,3)=matmul(N(1:3,1:3,1),N(1:3,1:3,2))/ & ((values(3)-values(1))*(values(3)-values(2))) EB(1:3,1:3,1)=math_I3-EB(1:3,1:3,3) @@ -1088,7 +1089,7 @@ pure function math_eigenvectorBasisSym33_log(m) EB(1:3,1:3,1)=matmul(N(1:3,1:3,2),N(1:3,1:3,3))/ & ((values(1)-values(2))*(values(1)-values(3))) EB(1:3,1:3,2)=math_I3-EB(1:3,1:3,1) - elseif(abs(values(3)-values(1)) < TOL) then twoSimilarEigenvalues + elseif(abs(values(3)-values(1)) < TOL) then twoSimilarEigenvalues EB(1:3,1:3,2)=matmul(N(1:3,1:3,1),N(1:3,1:3,3))/ & ((values(2)-values(1))*(values(2)-values(3))) EB(1:3,1:3,1)=math_I3-EB(1:3,1:3,2) @@ -1136,7 +1137,7 @@ end function math_rotationalPart33 ! will return NaN on error !-------------------------------------------------------------------------------------------------- function math_eigenvaluesSym(m) - + real(pReal), dimension(:,:), intent(in) :: m real(pReal), dimension(size(m,1)) :: math_eigenvaluesSym real(pReal), dimension(size(m,1),size(m,1)) :: m_ @@ -1221,7 +1222,7 @@ integer pure function math_binomial(n,k) integer, intent(in) :: n, k integer :: i, k_, n_ - + k_ = min(k,n-k) n_ = n math_binomial = merge(1,0,k_>-1) ! handling special cases k < 0 or k > n @@ -1244,7 +1245,7 @@ integer pure function math_multinomial(alpha) math_multinomial = 1 do i = 1, size(alpha) math_multinomial = math_multinomial*math_binomial(sum(alpha(1:i)),alpha(i)) - enddo + enddo end function math_multinomial @@ -1307,6 +1308,7 @@ subroutine unitTest sort_out_ = reshape([-1,-1, +1,+5, +5,+6, +3,-2],[2,4]) integer, dimension(5) :: range_out_ = [1,2,3,4,5] + integer, dimension(3) :: ijk real(pReal) :: det real(pReal), dimension(3) :: v3_1,v3_2,v3_3,v3_4 @@ -1349,7 +1351,7 @@ subroutine unitTest call random_number(v9) if(any(dNeq(math_33to9(math_9to33(v9)),v9))) & call IO_error(0,ext_msg='math_33to9/math_9to33') - + call random_number(t99) if(any(dNeq(math_3333to99(math_99to3333(t99)),t99))) & call IO_error(0,ext_msg='math_3333to99/math_99to3333') @@ -1378,7 +1380,7 @@ subroutine unitTest call random_number(t33) if(dNeq(math_det33(math_symmetric33(t33)),math_detSym33(math_symmetric33(t33)),tol=1.0e-12_pReal)) & call IO_error(0,ext_msg='math_det33/math_detSym33') - + if(any(dNeq0(math_identity2nd(3),math_inv33(math_I3)))) & call IO_error(0,ext_msg='math_inv33(math_I3)') @@ -1410,18 +1412,28 @@ subroutine unitTest if(any(dNeq0(txx_2,txx) .or. e)) & call IO_error(0,ext_msg='math_invert(txx)/math_identity2nd') - call math_invert(t99_2,e,t99) ! not sure how likely it is that we get a singular matrix + call math_invert(t99_2,e,t99) ! not sure how likely it is that we get a singular matrix if(any(dNeq0(matmul(t99_2,t99)-math_identity2nd(9),tol=1.0e-9_pReal)) .or. e) & call IO_error(0,ext_msg='math_invert(t99)') if(any(dNeq(math_clip([4.0_pReal,9.0_pReal],5.0_pReal,6.5_pReal),[5.0_pReal,6.5_pReal]))) & - call IO_error(0,ext_msg='math_clip') + call IO_error(0,ext_msg='math_clip') if(math_factorial(10) /= 3628800) & - call IO_error(0,ext_msg='math_factorial') + call IO_error(0,ext_msg='math_factorial') if(math_binomial(49,6) /= 13983816) & - call IO_error(0,ext_msg='math_binomial') + call IO_error(0,ext_msg='math_binomial') + + ijk = cshift([1,2,3],int(r*1.0e2_pReal)) + if(dNeq(math_LeviCivita(ijk(1),ijk(2),ijk(3)),+1.0_pReal)) & + call IO_error(0,ext_msg='math_LeviCivita(even)') + ijk = cshift([3,2,1],int(r*2.0e2_pReal)) + if(dNeq(math_LeviCivita(ijk(1),ijk(2),ijk(3)),-1.0_pReal)) & + call IO_error(0,ext_msg='math_LeviCivita(odd)') + ijk = cshift([2,2,1],int(r*2.0e2_pReal)) + if(dNeq0(math_LeviCivita(ijk(1),ijk(2),ijk(3))))& + call IO_error(0,ext_msg='math_LeviCivita') end subroutine unitTest diff --git a/src/results.f90 b/src/results.f90 index 8708f7856..cfd495a71 100644 --- a/src/results.f90 +++ b/src/results.f90 @@ -485,15 +485,15 @@ end subroutine results_writeScalarDataset_rotation !-------------------------------------------------------------------------------------------------- !> @brief adds the unique mapping from spatial position and constituent ID to results !-------------------------------------------------------------------------------------------------- -subroutine results_mapping_constituent(phaseAt,memberAt,label) +subroutine results_mapping_constituent(phaseAt,memberAtLocal,label) integer, dimension(:,:), intent(in) :: phaseAt !< phase section at (constituent,element) - integer, dimension(:,:,:), intent(in) :: memberAt !< phase member at (constituent,IP,element) - character(len=*), dimension(:), intent(in) :: label !< label of each phase section + integer, dimension(:,:,:), intent(in) :: memberAtLocal !< phase member at (constituent,IP,element) + character(len=pStringLen), dimension(:), intent(in) :: label !< label of each phase section - integer, dimension(size(memberAt,1),size(memberAt,2),size(memberAt,3)) :: & - phaseAt_perIP, & - memberAt_total + integer, dimension(size(memberAtLocal,1),size(memberAtLocal,2),size(memberAtLocal,3)) :: & + phaseAtMaterialpoint, & + memberAtGlobal integer, dimension(size(label),0:worldsize-1) :: memberOffset !< offset in member counting per process integer, dimension(0:worldsize-1) :: writeSize !< amount of data written per process integer(HSIZE_T), dimension(2) :: & @@ -543,10 +543,10 @@ subroutine results_mapping_constituent(phaseAt,memberAt,label) call h5pcreate_f(H5P_DATASET_XFER_F, plist_id, ierr) memberOffset = 0 do i=1, size(label) - memberOffset(i,worldrank) = count(phaseAt == i)*size(memberAt,2) ! number of points/instance of this process + memberOffset(i,worldrank) = count(phaseAt == i)*size(memberAtLocal,2) ! number of points/instance of this process enddo writeSize = 0 - writeSize(worldrank) = size(memberAt(1,:,:)) ! total number of points by this process + writeSize(worldrank) = size(memberAtLocal(1,:,:)) ! total number of points by this process !-------------------------------------------------------------------------------------------------- ! MPI settings and communication @@ -578,14 +578,14 @@ subroutine results_mapping_constituent(phaseAt,memberAt,label) !--------------------------------------------------------------------------------------------------- ! expand phaseAt to consider IPs (is not stored per IP) - do i = 1, size(phaseAt_perIP,2) - phaseAt_perIP(:,i,:) = phaseAt + do i = 1, size(phaseAtMaterialpoint,2) + phaseAtMaterialpoint(:,i,:) = phaseAt enddo !--------------------------------------------------------------------------------------------------- ! renumber member from my process to all processes do i = 1, size(label) - where(phaseAt_perIP == i) memberAt_total = memberAt + sum(memberOffset(i,0:worldrank-1)) -1 ! convert to 0-based + where(phaseAtMaterialpoint == i) memberAtGlobal = memberAtLocal + sum(memberOffset(i,0:worldrank-1)) -1 ! convert to 0-based enddo !-------------------------------------------------------------------------------------------------- @@ -596,10 +596,10 @@ subroutine results_mapping_constituent(phaseAt,memberAt,label) call h5dcreate_f(loc_id, 'constituent', dtype_id, filespace_id, dset_id, ierr) if (ierr < 0) call IO_error(1,ext_msg='results_mapping_constituent: h5dcreate_f') - call h5dwrite_f(dset_id, name_id, reshape(label(pack(phaseAt_perIP,.true.)),myShape), & + call h5dwrite_f(dset_id, name_id, reshape(label(pack(phaseAtMaterialpoint,.true.)),myShape), & myShape, ierr, file_space_id = filespace_id, mem_space_id = memspace_id, xfer_prp = plist_id) if (ierr < 0) call IO_error(1,ext_msg='results_mapping_constituent: h5dwrite_f/name_id') - call h5dwrite_f(dset_id, position_id, reshape(pack(memberAt_total,.true.),myShape), & + call h5dwrite_f(dset_id, position_id, reshape(pack(memberAtGlobal,.true.),myShape), & myShape, ierr, file_space_id = filespace_id, mem_space_id = memspace_id, xfer_prp = plist_id) if (ierr < 0) call IO_error(1,ext_msg='results_mapping_constituent: h5dwrite_f/position_id') @@ -620,15 +620,15 @@ end subroutine results_mapping_constituent !-------------------------------------------------------------------------------------------------- !> @brief adds the unique mapping from spatial position and constituent ID to results !-------------------------------------------------------------------------------------------------- -subroutine results_mapping_materialpoint(homogenizationAt,memberAt,label) +subroutine results_mapping_materialpoint(homogenizationAt,memberAtLocal,label) integer, dimension(:), intent(in) :: homogenizationAt !< homogenization section at (element) - integer, dimension(:,:), intent(in) :: memberAt !< homogenization member at (IP,element) - character(len=*), dimension(:), intent(in) :: label !< label of each homogenization section + integer, dimension(:,:), intent(in) :: memberAtLocal !< homogenization member at (IP,element) + character(len=pStringLen), dimension(:), intent(in) :: label !< label of each homogenization section - integer, dimension(size(memberAt,1),size(memberAt,2)) :: & - homogenizationAt_perIP, & - memberAt_total + integer, dimension(size(memberAtLocal,1),size(memberAtLocal,2)) :: & + homogenizationAtMaterialpoint, & + memberAtGlobal integer, dimension(size(label),0:worldsize-1) :: memberOffset !< offset in member counting per process integer, dimension(0:worldsize-1) :: writeSize !< amount of data written per process integer(HSIZE_T), dimension(1) :: & @@ -678,10 +678,10 @@ subroutine results_mapping_materialpoint(homogenizationAt,memberAt,label) call h5pcreate_f(H5P_DATASET_XFER_F, plist_id, ierr) memberOffset = 0 do i=1, size(label) - memberOffset(i,worldrank) = count(homogenizationAt == i)*size(memberAt,1) ! number of points/instance of this process + memberOffset(i,worldrank) = count(homogenizationAt == i)*size(memberAtLocal,1) ! number of points/instance of this process enddo writeSize = 0 - writeSize(worldrank) = size(memberAt) ! total number of points by this process + writeSize(worldrank) = size(memberAtLocal) ! total number of points by this process !-------------------------------------------------------------------------------------------------- ! MPI settings and communication @@ -713,14 +713,14 @@ subroutine results_mapping_materialpoint(homogenizationAt,memberAt,label) !--------------------------------------------------------------------------------------------------- ! expand phaseAt to consider IPs (is not stored per IP) - do i = 1, size(homogenizationAt_perIP,1) - homogenizationAt_perIP(i,:) = homogenizationAt + do i = 1, size(homogenizationAtMaterialpoint,1) + homogenizationAtMaterialpoint(i,:) = homogenizationAt enddo !--------------------------------------------------------------------------------------------------- ! renumber member from my process to all processes do i = 1, size(label) - where(homogenizationAt_perIP == i) memberAt_total = memberAt + sum(memberOffset(i,0:worldrank-1)) - 1 ! convert to 0-based + where(homogenizationAtMaterialpoint == i) memberAtGlobal = memberAtLocal + sum(memberOffset(i,0:worldrank-1)) - 1 ! convert to 0-based enddo !-------------------------------------------------------------------------------------------------- @@ -731,10 +731,10 @@ subroutine results_mapping_materialpoint(homogenizationAt,memberAt,label) call h5dcreate_f(loc_id, 'materialpoint', dtype_id, filespace_id, dset_id, ierr) if (ierr < 0) call IO_error(1,ext_msg='results_mapping_materialpoint: h5dcreate_f') - call h5dwrite_f(dset_id, name_id, reshape(label(pack(homogenizationAt_perIP,.true.)),myShape), & + call h5dwrite_f(dset_id, name_id, reshape(label(pack(homogenizationAtMaterialpoint,.true.)),myShape), & myShape, ierr, file_space_id = filespace_id, mem_space_id = memspace_id, xfer_prp = plist_id) if (ierr < 0) call IO_error(1,ext_msg='results_mapping_materialpoint: h5dwrite_f/name_id') - call h5dwrite_f(dset_id, position_id, reshape(pack(memberAt_total,.true.),myShape), & + call h5dwrite_f(dset_id, position_id, reshape(pack(memberAtGlobal,.true.),myShape), & myShape, ierr, file_space_id = filespace_id, mem_space_id = memspace_id, xfer_prp = plist_id) if (ierr < 0) call IO_error(1,ext_msg='results_mapping_materialpoint: h5dwrite_f/position_id') diff --git a/src/thermal_adiabatic.f90 b/src/thermal_adiabatic.f90 index 5fa6b3281..2bef1c3ee 100644 --- a/src/thermal_adiabatic.f90 +++ b/src/thermal_adiabatic.f90 @@ -16,14 +16,9 @@ module thermal_adiabatic implicit none private - enum, bind(c) - enumerator :: undefined_ID, & - temperature_ID - end enum - type :: tParameters - integer(kind(undefined_ID)), dimension(:), allocatable :: & - outputID + character(len=pStringLen), allocatable, dimension(:) :: & + output end type tParameters type(tparameters), dimension(:), allocatable :: & @@ -46,10 +41,9 @@ contains !-------------------------------------------------------------------------------------------------- subroutine thermal_adiabatic_init - integer :: maxNinstance,o,h,NofMyHomog - character(len=pStringLen), dimension(:), allocatable :: outputs + integer :: maxNinstance,h,NofMyHomog - write(6,'(/,a)') ' <<<+- thermal_'//THERMAL_ADIABATIC_label//' init -+>>>'; flush(6) + write(6,'(/,a)') ' <<<+- thermal_'//THERMAL_ADIABATIC_label//' init -+>>>'; flush(6) maxNinstance = count(thermal_type == THERMAL_adiabatic_ID) if (maxNinstance == 0) return @@ -60,15 +54,7 @@ subroutine thermal_adiabatic_init if (thermal_type(h) /= THERMAL_adiabatic_ID) cycle associate(prm => param(thermal_typeInstance(h)),config => config_homogenization(h)) - outputs = config%getStrings('(output)',defaultVal=emptyStringArray) - allocate(prm%outputID(0)) - - do o=1, size(outputs) - select case(outputs(o)) - case('temperature') - prm%outputID = [prm%outputID, temperature_ID] - end select - enddo + prm%output = config%getStrings('(output)',defaultVal=emptyStringArray) NofMyHomog=count(material_homogenizationAt==h) thermalState(h)%sizeState = 1 @@ -76,7 +62,6 @@ subroutine thermal_adiabatic_init allocate(thermalState(h)%subState0(1,NofMyHomog), source=thermal_initialT(h)) allocate(thermalState(h)%state (1,NofMyHomog), source=thermal_initialT(h)) - nullify(thermalMapping(h)%p) thermalMapping(h)%p => material_homogenizationMemberAt deallocate(temperature(h)%p) temperature(h)%p => thermalState(h)%state(1,:) @@ -246,14 +231,13 @@ subroutine thermal_adiabatic_results(homog,group) integer, intent(in) :: homog character(len=*), intent(in) :: group + integer :: o associate(prm => param(damage_typeInstance(homog))) - - outputsLoop: do o = 1,size(prm%outputID) - select case(prm%outputID(o)) - - case (temperature_ID) + outputsLoop: do o = 1,size(prm%output) + select case(trim(prm%output(o))) + case('temperature') ! ToDo: should be 'T' call results_writeDataset(group,temperature(homog)%p,'T',& 'temperature','K') end select diff --git a/src/thermal_conduction.f90 b/src/thermal_conduction.f90 index 9ec8082ec..dad3fecd8 100644 --- a/src/thermal_conduction.f90 +++ b/src/thermal_conduction.f90 @@ -15,15 +15,9 @@ module thermal_conduction implicit none private - enum, bind(c) - enumerator :: & - undefined_ID, & - temperature_ID - end enum - type :: tParameters - integer(kind(undefined_ID)), dimension(:), allocatable :: & - outputID + character(len=pStringLen), allocatable, dimension(:) :: & + output end type tParameters type(tparameters), dimension(:), allocatable :: & @@ -48,10 +42,9 @@ contains subroutine thermal_conduction_init - integer :: maxNinstance,o,NofMyHomog,h - character(len=pStringLen), dimension(:), allocatable :: outputs + integer :: maxNinstance,NofMyHomog,h - write(6,'(/,a)') ' <<<+- thermal_'//THERMAL_CONDUCTION_label//' init -+>>>'; flush(6) + write(6,'(/,a)') ' <<<+- thermal_'//THERMAL_CONDUCTION_label//' init -+>>>'; flush(6) maxNinstance = count(thermal_type == THERMAL_conduction_ID) if (maxNinstance == 0) return @@ -62,15 +55,7 @@ subroutine thermal_conduction_init if (thermal_type(h) /= THERMAL_conduction_ID) cycle associate(prm => param(thermal_typeInstance(h)),config => config_homogenization(h)) - outputs = config%getStrings('(output)',defaultVal=emptyStringArray) - allocate(prm%outputID(0)) - - do o=1, size(outputs) - select case(outputs(o)) - case('temperature') - prm%outputID = [prm%outputID, temperature_ID] - end select - enddo + prm%output = config%getStrings('(output)',defaultVal=emptyStringArray) NofMyHomog=count(material_homogenizationAt==h) thermalState(h)%sizeState = 0 @@ -78,7 +63,6 @@ subroutine thermal_conduction_init allocate(thermalState(h)%subState0(0,NofMyHomog)) allocate(thermalState(h)%state (0,NofMyHomog)) - nullify(thermalMapping(h)%p) thermalMapping(h)%p => material_homogenizationMemberAt deallocate(temperature (h)%p) allocate (temperature (h)%p(NofMyHomog), source=thermal_initialT(h)) @@ -259,14 +243,13 @@ subroutine thermal_conduction_results(homog,group) integer, intent(in) :: homog character(len=*), intent(in) :: group + integer :: o associate(prm => param(damage_typeInstance(homog))) - - outputsLoop: do o = 1,size(prm%outputID) - select case(prm%outputID(o)) - - case (temperature_ID) + outputsLoop: do o = 1,size(prm%output) + select case(trim(prm%output(o))) + case('temperature') ! ToDo: should be 'T' call results_writeDataset(group,temperature(homog)%p,'T',& 'temperature','K') end select