From 78a2941436dfcb0be765a9cca4a0d134882db58b Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 21 Oct 2019 16:59:49 +0200 Subject: [PATCH 001/223] avoid numerical issues --- python/damask/orientation.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python/damask/orientation.py b/python/damask/orientation.py index 85a5c1866..688a3fa3f 100644 --- a/python/damask/orientation.py +++ b/python/damask/orientation.py @@ -702,14 +702,14 @@ class Symmetry: v = np.array(vector,dtype=float) if proper: # check both improper ... theComponents = np.dot(basis['improper'],v) - inSST = np.all(theComponents >= 0.0) + inSST = np.all(np.around(theComponents,12) >= 0.0) if not inSST: # ... and proper SST - theComponents = np.dot(basis['proper'],v) - inSST = np.all(theComponents >= 0.0) + theComponents = np.dot(basis['proper'],v) + inSST = np.all(np.around(theComponents,12) >= 0.0) else: v[2] = abs(v[2]) # z component projects identical theComponents = np.dot(basis['improper'],v) # for positive and negative values - inSST = np.all(theComponents >= 0.0) + inSST = np.all(np.around(theComponents,12) >= 0.0) if color: # have to return color array if inSST: From ecc51e34d3bf19d838f51079fda39270f826ae13 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 21 Oct 2019 22:44:12 +0200 Subject: [PATCH 002/223] rouding should affect color calculation avoid NaN in math.power(x,0.5). math.power(-0.0,0.5) is ok --- python/damask/orientation.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/python/damask/orientation.py b/python/damask/orientation.py index 688a3fa3f..dc851b35c 100644 --- a/python/damask/orientation.py +++ b/python/damask/orientation.py @@ -701,15 +701,15 @@ class Symmetry: v = np.array(vector,dtype=float) if proper: # check both improper ... - theComponents = np.dot(basis['improper'],v) - inSST = np.all(np.around(theComponents,12) >= 0.0) + theComponents = np.around(np.dot(basis['improper'],v),12) + inSST = np.all(theComponents >= 0.0) if not inSST: # ... and proper SST - theComponents = np.dot(basis['proper'],v) - inSST = np.all(np.around(theComponents,12) >= 0.0) + theComponents = np.around(np.dot(basis['proper'],v),12) + inSST = np.all(theComponents >= 0.0) else: v[2] = abs(v[2]) # z component projects identical - theComponents = np.dot(basis['improper'],v) # for positive and negative values - inSST = np.all(np.around(theComponents,12) >= 0.0) + theComponents = np.around(np.dot(basis['improper'],v),12) # for positive and negative values + inSST = np.all(theComponents >= 0.0) if color: # have to return color array if inSST: From 9489c04ccbfe3ec88e6502c14d1e4bb025a388d1 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 21 Oct 2019 23:02:31 +0200 Subject: [PATCH 003/223] less confusing --- python/damask/orientation.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/damask/orientation.py b/python/damask/orientation.py index dc851b35c..97464f9e6 100644 --- a/python/damask/orientation.py +++ b/python/damask/orientation.py @@ -1046,13 +1046,13 @@ class Lattice: for miller in np.hstack((relationship['planes'],relationship['directions'])): myPlane = miller[myPlane_id]/ np.linalg.norm(miller[myPlane_id]) myDir = miller[myDir_id]/ np.linalg.norm(miller[myDir_id]) - myMatrix = np.array([myDir,np.cross(myPlane,myDir),myPlane]).T + myMatrix = np.array([myDir,np.cross(myPlane,myDir),myPlane]) otherPlane = miller[otherPlane_id]/ np.linalg.norm(miller[otherPlane_id]) otherDir = miller[otherDir_id]/ np.linalg.norm(miller[otherDir_id]) - otherMatrix = np.array([otherDir,np.cross(otherPlane,otherDir),otherPlane]).T + otherMatrix = np.array([otherDir,np.cross(otherPlane,otherDir),otherPlane]) - r['rotations'].append(Rotation.fromMatrix(np.dot(otherMatrix,myMatrix.T))) + r['rotations'].append(Rotation.fromMatrix(np.dot(otherMatrix.T,myMatrix))) return r From 5e7f9a223b55f9f8c56ba6fa35a0c46ae5db5b68 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 21 Oct 2019 23:17:58 +0200 Subject: [PATCH 004/223] should be a passive rotation --- python/damask/orientation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/damask/orientation.py b/python/damask/orientation.py index 97464f9e6..fc601b608 100644 --- a/python/damask/orientation.py +++ b/python/damask/orientation.py @@ -1144,7 +1144,7 @@ class Orientation: def relatedOrientations(self,model): """List of orientations related by the given orientation relationship.""" r = self.lattice.relationOperations(model) - return [self.__class__(self.rotation*o,r['lattice']) for o in r['rotations']] + return [self.__class__(o*self.rotation,r['lattice']) for o in r['rotations']] def reduced(self): """Transform orientation to fall into fundamental zone according to symmetry.""" From 23f5e0fa58e8ca0137236bd8499a53534f11a2cb Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 26 Nov 2019 10:25:39 +0100 Subject: [PATCH 005/223] filters for operations on regular grids (in fourier space) --- python/damask/grid_filters.py | 113 ++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 python/damask/grid_filters.py diff --git a/python/damask/grid_filters.py b/python/damask/grid_filters.py new file mode 100644 index 000000000..6588e59bc --- /dev/null +++ b/python/damask/grid_filters.py @@ -0,0 +1,113 @@ +import numpy as np + + +def curl(size,field): + """Calculate curl of a vector or tensor field in Fourier space.""" + shapeFFT = np.array(np.shape(field))[0:3] + grid = np.array(np.shape(field)[2::-1]) + N = grid.prod() # field size + n = np.array(np.shape(field)[3:]).prod() # data size + + field_fourier = np.fft.rfftn(field,axes=(0,1,2),s=shapeFFT) + curl_fourier = np.empty(field_fourier.shape,'c16') + + k_sk = np.where(np.arange(grid[2])>grid[2]//2,np.arange(grid[2])-grid[2],np.arange(grid[2]))/size[0] + if grid[2]%2 == 0: k_sk[grid[2]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) + + k_sj = np.where(np.arange(grid[1])>grid[1]//2,np.arange(grid[1])-grid[1],np.arange(grid[1]))/size[1] + if grid[1]%2 == 0: k_sj[grid[1]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) + + k_si = np.arange(grid[0]//2+1)/size[2] + + kk, kj, ki = np.meshgrid(k_sk,k_sj,k_si,indexing = 'ij') + k_s = np.concatenate((ki[:,:,:,None],kj[:,:,:,None],kk[:,:,:,None]),axis = 3).astype('c16') + + e = np.zeros((3, 3, 3)) + e[0, 1, 2] = e[1, 2, 0] = e[2, 0, 1] = +1.0 # Levi-Civita symbol + e[0, 2, 1] = e[2, 1, 0] = e[1, 0, 2] = -1.0 + + curl_fourier = np.einsum('slm,ijkl,ijkm, ->ijks', e,k_s,field_fourier)*2.0j*np.pi if n == 3 else# vector, 3 -> 3 + np.einsum('slm,ijkl,ijknm,->ijksn',e,k_s,field_fourier)*2.0j*np.pi # tensor, 3x3 -> 3x3 + + return np.fft.irfftn(curl_fourier,axes=(0,1,2),s=shapeFFT).reshape([N,n]) + + +def divergence(size,field): + """Calculate divergence of a vector or tensor field in Fourier space.""" + shapeFFT = np.array(np.shape(field))[0:3] + grid = np.array(np.shape(field)[2::-1]) + N = grid.prod() # field size + n = np.array(np.shape(field)[3:]).prod() # data size + + field_fourier = np.fft.rfftn(field,axes=(0,1,2),s=shapeFFT) + div_fourier = np.empty(field_fourier.shape[0:len(np.shape(field))-1],'c16') + + k_sk = np.where(np.arange(grid[2])>grid[2]//2,np.arange(grid[2])-grid[2],np.arange(grid[2]))/size[0] + if grid[2]%2 == 0: k_sk[grid[2]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) + + k_sj = np.where(np.arange(grid[1])>grid[1]//2,np.arange(grid[1])-grid[1],np.arange(grid[1]))/size[1] + if grid[1]%2 == 0: k_sj[grid[1]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) + + k_si = np.arange(grid[0]//2+1)/size[2] + + kk, kj, ki = np.meshgrid(k_sk,k_sj,k_si,indexing = 'ij') + k_s = np.concatenate((ki[:,:,:,None],kj[:,:,:,None],kk[:,:,:,None]),axis = 3).astype('c16') + + div_fourier = np.einsum('ijkl,ijkl ->ijk', k_s,field_fourier)*2.0j*np.pi if n == 3 else # vector, 3 -> 1 + np.einsum('ijkm,ijklm->ijkl',k_s,field_fourier)*2.0j*np.pi # tensor, 3x3 -> 3 + + return np.fft.irfftn(div_fourier,axes=(0,1,2),s=shapeFFT).reshape([N,n//3]) + + +def gradient(size,field): + """Calculate gradient of a vector or scalar field in Fourier space.""" + shapeFFT = np.array(np.shape(field))[0:3] + grid = np.array(np.shape(field)[2::-1]) + N = grid.prod() # field size + n = np.array(np.shape(field)[3:]).prod() # data size + + field_fourier = np.fft.rfftn(field,axes=(0,1,2),s=shapeFFT) + grad_fourier = np.empty(field_fourier.shape+(3,),'c16') + + k_sk = np.where(np.arange(grid[2])>grid[2]//2,np.arange(grid[2])-grid[2],np.arange(grid[2]))/size[0] + if grid[2]%2 == 0: k_sk[grid[2]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) + + k_sj = np.where(np.arange(grid[1])>grid[1]//2,np.arange(grid[1])-grid[1],np.arange(grid[1]))/size[1] + if grid[1]%2 == 0: k_sj[grid[1]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) + + k_si = np.arange(grid[0]//2+1)/size[2] + + kk, kj, ki = np.meshgrid(k_sk,k_sj,k_si,indexing = 'ij') + k_s = np.concatenate((ki[:,:,:,None],kj[:,:,:,None],kk[:,:,:,None]),axis = 3).astype('c16') + grad_fourier = np.einsum('ijkl,ijkm->ijkm', field_fourier,k_s)*2.0j*np.pi if n == 1 else # scalar, 1 -> 3 + np.einsum('ijkl,ijkm->ijklm',field_fourier,k_s)*2.0j*np.pi # vector, 3 -> 3x3 + + return np.fft.irfftn(grad_fourier,axes=(0,1,2),s=shapeFFT).reshape([N,3*n]) + + +#-------------------------------------------------------------------------------------------------- +def displacementFluctFFT(F,size): + """Calculate displacement field from deformation gradient field.""" + integrator = 0.5j * size / np.pi + + kk, kj, ki = np.meshgrid(np.where(np.arange(grid[2])>grid[2]//2,np.arange(grid[2])-grid[2],np.arange(grid[2])), + np.where(np.arange(grid[1])>grid[1]//2,np.arange(grid[1])-grid[1],np.arange(grid[1])), + np.arange(grid[0]//2+1), + indexing = 'ij') + k_s = np.concatenate((ki[:,:,:,None],kj[:,:,:,None],kk[:,:,:,None]),axis = 3) + k_sSquared = np.einsum('...l,...l',k_s,k_s) + k_sSquared[0,0,0] = 1.0 # ignore global average frequency + +#-------------------------------------------------------------------------------------------------- +# integration in Fourier space + + displacement_fourier = -np.einsum('ijkml,ijkl,l->ijkm', + np.fft.rfftn(F,axes=(0,1,2)), + k_s, + integrator, + ) / k_sSquared[...,np.newaxis] + +#-------------------------------------------------------------------------------------------------- +# backtransformation to real space + + return np.fft.irfftn(displacement_fourier,grid[::-1],axes=(0,1,2)) From b85049cb811b70d71d497001af07b7e73b4fbc25 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 28 Nov 2019 05:41:53 +0100 Subject: [PATCH 006/223] use brackets for line continuation with comments --- python/damask/__init__.py | 1 + python/damask/grid_filters.py | 15 ++++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/python/damask/__init__.py b/python/damask/__init__.py index 2cd26cf1c..77666561c 100644 --- a/python/damask/__init__.py +++ b/python/damask/__init__.py @@ -23,4 +23,5 @@ from .util import extendableOption # noqa # functions in modules from . import mechanics # noqa +from . import grid_filters # noqa diff --git a/python/damask/grid_filters.py b/python/damask/grid_filters.py index 6588e59bc..26ee2cfcc 100644 --- a/python/damask/grid_filters.py +++ b/python/damask/grid_filters.py @@ -26,10 +26,10 @@ def curl(size,field): e[0, 1, 2] = e[1, 2, 0] = e[2, 0, 1] = +1.0 # Levi-Civita symbol e[0, 2, 1] = e[2, 1, 0] = e[1, 0, 2] = -1.0 - curl_fourier = np.einsum('slm,ijkl,ijkm, ->ijks', e,k_s,field_fourier)*2.0j*np.pi if n == 3 else# vector, 3 -> 3 - np.einsum('slm,ijkl,ijknm,->ijksn',e,k_s,field_fourier)*2.0j*np.pi # tensor, 3x3 -> 3x3 + curl = (np.einsum('slm,ijkl,ijkm, ->ijks', e,k_s,field_fourier)*2.0j*np.pi if n == 3 else # vector, 3 -> 3 + np.einsum('slm,ijkl,ijknm,->ijksn',e,k_s,field_fourier)*2.0j*np.pi) # tensor, 3x3 -> 3x3 - return np.fft.irfftn(curl_fourier,axes=(0,1,2),s=shapeFFT).reshape([N,n]) + return np.fft.irfftn(curl,axes=(0,1,2),s=shapeFFT).reshape([N,n]) def divergence(size,field): @@ -53,8 +53,8 @@ def divergence(size,field): kk, kj, ki = np.meshgrid(k_sk,k_sj,k_si,indexing = 'ij') k_s = np.concatenate((ki[:,:,:,None],kj[:,:,:,None],kk[:,:,:,None]),axis = 3).astype('c16') - div_fourier = np.einsum('ijkl,ijkl ->ijk', k_s,field_fourier)*2.0j*np.pi if n == 3 else # vector, 3 -> 1 - np.einsum('ijkm,ijklm->ijkl',k_s,field_fourier)*2.0j*np.pi # tensor, 3x3 -> 3 + divergence = (np.einsum('ijkl,ijkl ->ijk', k_s,field_fourier)*2.0j*np.pi if n == 3 else # vector, 3 -> 1 + np.einsum('ijkm,ijklm->ijkl',k_s,field_fourier)*2.0j*np.pi) # tensor, 3x3 -> 3 return np.fft.irfftn(div_fourier,axes=(0,1,2),s=shapeFFT).reshape([N,n//3]) @@ -79,8 +79,9 @@ def gradient(size,field): kk, kj, ki = np.meshgrid(k_sk,k_sj,k_si,indexing = 'ij') k_s = np.concatenate((ki[:,:,:,None],kj[:,:,:,None],kk[:,:,:,None]),axis = 3).astype('c16') - grad_fourier = np.einsum('ijkl,ijkm->ijkm', field_fourier,k_s)*2.0j*np.pi if n == 1 else # scalar, 1 -> 3 - np.einsum('ijkl,ijkm->ijklm',field_fourier,k_s)*2.0j*np.pi # vector, 3 -> 3x3 + + gradient = (np.einsum('ijkl,ijkm->ijkm', field_fourier,k_s)*2.0j*np.pi if n == 1 else # scalar, 1 -> 3 + np.einsum('ijkl,ijkm->ijklm',field_fourier,k_s)*2.0j*np.pi) # vector, 3 -> 3x3 return np.fft.irfftn(grad_fourier,axes=(0,1,2),s=shapeFFT).reshape([N,3*n]) From 4c4ccfe72e88053822c83886089b96bfda0f5a35 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 28 Nov 2019 06:27:19 +0100 Subject: [PATCH 007/223] not needed --- processing/post/addCurl.py | 1 - processing/post/addDivergence.py | 1 - processing/post/addGradient.py | 1 - 3 files changed, 3 deletions(-) diff --git a/processing/post/addCurl.py b/processing/post/addCurl.py index 484af9677..b4dd465a9 100755 --- a/processing/post/addCurl.py +++ b/processing/post/addCurl.py @@ -27,7 +27,6 @@ def curlFFT(geomdim,field): n = np.array(np.shape(field)[3:]).prod() # data size field_fourier = np.fft.rfftn(field,axes=(0,1,2),s=shapeFFT) - curl_fourier = np.empty(field_fourier.shape,'c16') # differentiation in Fourier space TWOPIIMG = 2.0j*np.pi diff --git a/processing/post/addDivergence.py b/processing/post/addDivergence.py index 31a18f8e1..2d6af2036 100755 --- a/processing/post/addDivergence.py +++ b/processing/post/addDivergence.py @@ -27,7 +27,6 @@ def divFFT(geomdim,field): n = np.array(np.shape(field)[3:]).prod() # data size field_fourier = np.fft.rfftn(field,axes=(0,1,2),s=shapeFFT) - div_fourier = np.empty(field_fourier.shape[0:len(np.shape(field))-1],'c16') # differentiation in Fourier space TWOPIIMG = 2.0j*np.pi diff --git a/processing/post/addGradient.py b/processing/post/addGradient.py index bfadb578e..252b72eb8 100755 --- a/processing/post/addGradient.py +++ b/processing/post/addGradient.py @@ -27,7 +27,6 @@ def gradFFT(geomdim,field): n = np.array(np.shape(field)[3:]).prod() # data size field_fourier = np.fft.rfftn(field,axes=(0,1,2),s=shapeFFT) - grad_fourier = np.empty(field_fourier.shape+(3,),'c16') # differentiation in Fourier space TWOPIIMG = 2.0j*np.pi From 80b50f460e24ad6ef7a49ac2ac88a5d2946aa74e Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 28 Nov 2019 10:09:22 +0100 Subject: [PATCH 008/223] cleaning trying to get rid of strange re-ordering related to ASCII table data layout --- python/damask/grid_filters.py | 47 ++++++++++++++--------------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/python/damask/grid_filters.py b/python/damask/grid_filters.py index 26ee2cfcc..69ee85033 100644 --- a/python/damask/grid_filters.py +++ b/python/damask/grid_filters.py @@ -3,21 +3,18 @@ import numpy as np def curl(size,field): """Calculate curl of a vector or tensor field in Fourier space.""" - shapeFFT = np.array(np.shape(field))[0:3] - grid = np.array(np.shape(field)[2::-1]) - N = grid.prod() # field size + grid = np.array(np.shape(field)[0:3]) n = np.array(np.shape(field)[3:]).prod() # data size - field_fourier = np.fft.rfftn(field,axes=(0,1,2),s=shapeFFT) - curl_fourier = np.empty(field_fourier.shape,'c16') + field_fourier = np.fft.rfftn(field,axes=(0,1,2)) - k_sk = np.where(np.arange(grid[2])>grid[2]//2,np.arange(grid[2])-grid[2],np.arange(grid[2]))/size[0] - if grid[2]%2 == 0: k_sk[grid[2]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) + k_sk = np.where(np.arange(grid[0])>grid[0]//2,np.arange(grid[0])-grid[0],np.arange(grid[0]))/size[0] + if grid[0]%2 == 0: k_sk[grid[0]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) k_sj = np.where(np.arange(grid[1])>grid[1]//2,np.arange(grid[1])-grid[1],np.arange(grid[1]))/size[1] if grid[1]%2 == 0: k_sj[grid[1]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) - k_si = np.arange(grid[0]//2+1)/size[2] + k_si = np.arange(grid[2]//2+1)/size[2] kk, kj, ki = np.meshgrid(k_sk,k_sj,k_si,indexing = 'ij') k_s = np.concatenate((ki[:,:,:,None],kj[:,:,:,None],kk[:,:,:,None]),axis = 3).astype('c16') @@ -26,29 +23,26 @@ def curl(size,field): e[0, 1, 2] = e[1, 2, 0] = e[2, 0, 1] = +1.0 # Levi-Civita symbol e[0, 2, 1] = e[2, 1, 0] = e[1, 0, 2] = -1.0 - curl = (np.einsum('slm,ijkl,ijkm, ->ijks', e,k_s,field_fourier)*2.0j*np.pi if n == 3 else # vector, 3 -> 3 - np.einsum('slm,ijkl,ijknm,->ijksn',e,k_s,field_fourier)*2.0j*np.pi) # tensor, 3x3 -> 3x3 + curl = (np.einsum('slm,ijkl,ijkm ->ijks', e,k_s,field_fourier)*2.0j*np.pi if n == 3 else # vector, 3 -> 3 + np.einsum('slm,ijkl,ijknm->ijksn',e,k_s,field_fourier)*2.0j*np.pi) # tensor, 3x3 -> 3x3 - return np.fft.irfftn(curl,axes=(0,1,2),s=shapeFFT).reshape([N,n]) + return np.fft.irfftn(curl,axes=(0,1,2)) def divergence(size,field): """Calculate divergence of a vector or tensor field in Fourier space.""" - shapeFFT = np.array(np.shape(field))[0:3] - grid = np.array(np.shape(field)[2::-1]) - N = grid.prod() # field size + grid = np.array(np.shape(field)[0:3]) n = np.array(np.shape(field)[3:]).prod() # data size - field_fourier = np.fft.rfftn(field,axes=(0,1,2),s=shapeFFT) - div_fourier = np.empty(field_fourier.shape[0:len(np.shape(field))-1],'c16') + field_fourier = np.fft.rfftn(field,axes=(0,1,2),s=grid) - k_sk = np.where(np.arange(grid[2])>grid[2]//2,np.arange(grid[2])-grid[2],np.arange(grid[2]))/size[0] - if grid[2]%2 == 0: k_sk[grid[2]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) + k_sk = np.where(np.arange(grid[0])>grid[0]//2,np.arange(grid[0])-grid[0],np.arange(grid[0]))/size[0] + if grid[0]%2 == 0: k_sk[grid[0]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) k_sj = np.where(np.arange(grid[1])>grid[1]//2,np.arange(grid[1])-grid[1],np.arange(grid[1]))/size[1] if grid[1]%2 == 0: k_sj[grid[1]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) - k_si = np.arange(grid[0]//2+1)/size[2] + k_si = np.arange(grid[2]//2+1)/size[2] kk, kj, ki = np.meshgrid(k_sk,k_sj,k_si,indexing = 'ij') k_s = np.concatenate((ki[:,:,:,None],kj[:,:,:,None],kk[:,:,:,None]),axis = 3).astype('c16') @@ -56,26 +50,23 @@ def divergence(size,field): divergence = (np.einsum('ijkl,ijkl ->ijk', k_s,field_fourier)*2.0j*np.pi if n == 3 else # vector, 3 -> 1 np.einsum('ijkm,ijklm->ijkl',k_s,field_fourier)*2.0j*np.pi) # tensor, 3x3 -> 3 - return np.fft.irfftn(div_fourier,axes=(0,1,2),s=shapeFFT).reshape([N,n//3]) + return np.fft.irfftn(div_fourier,axes=(0,1,2),s=grid) def gradient(size,field): """Calculate gradient of a vector or scalar field in Fourier space.""" - shapeFFT = np.array(np.shape(field))[0:3] grid = np.array(np.shape(field)[2::-1]) - N = grid.prod() # field size n = np.array(np.shape(field)[3:]).prod() # data size - field_fourier = np.fft.rfftn(field,axes=(0,1,2),s=shapeFFT) - grad_fourier = np.empty(field_fourier.shape+(3,),'c16') + field_fourier = np.fft.rfftn(field,axes=(0,1,2),s=grid) - k_sk = np.where(np.arange(grid[2])>grid[2]//2,np.arange(grid[2])-grid[2],np.arange(grid[2]))/size[0] - if grid[2]%2 == 0: k_sk[grid[2]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) + k_sk = np.where(np.arange(grid[0])>grid[0]//2,np.arange(grid[0])-grid[0],np.arange(grid[0]))/size[0] + if grid[0]%2 == 0: k_sk[grid[0]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) k_sj = np.where(np.arange(grid[1])>grid[1]//2,np.arange(grid[1])-grid[1],np.arange(grid[1]))/size[1] if grid[1]%2 == 0: k_sj[grid[1]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) - k_si = np.arange(grid[0]//2+1)/size[2] + k_si = np.arange(grid[2]//2+1)/size[2] kk, kj, ki = np.meshgrid(k_sk,k_sj,k_si,indexing = 'ij') k_s = np.concatenate((ki[:,:,:,None],kj[:,:,:,None],kk[:,:,:,None]),axis = 3).astype('c16') @@ -83,7 +74,7 @@ def gradient(size,field): gradient = (np.einsum('ijkl,ijkm->ijkm', field_fourier,k_s)*2.0j*np.pi if n == 1 else # scalar, 1 -> 3 np.einsum('ijkl,ijkm->ijklm',field_fourier,k_s)*2.0j*np.pi) # vector, 3 -> 3x3 - return np.fft.irfftn(grad_fourier,axes=(0,1,2),s=shapeFFT).reshape([N,3*n]) + return np.fft.irfftn(grad_fourier,axes=(0,1,2),s=grid) #-------------------------------------------------------------------------------------------------- From 3e65d44e073f925c4bbd8ed0c53b4a5308c1e8ee Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 28 Nov 2019 15:46:22 +0100 Subject: [PATCH 009/223] centralized facilities for differential operations note the need to reverse the grid shape in data from the ASCII table. If x is fastest, z is slowest we require x to be the rightmost index --- processing/post/addCurl.py | 138 ++++-------------------------- processing/post/addDivergence.py | 140 +++++-------------------------- processing/post/addGradient.py | 135 ++++------------------------- python/damask/grid_filters.py | 63 +++++--------- 4 files changed, 74 insertions(+), 402 deletions(-) diff --git a/processing/post/addCurl.py b/processing/post/addCurl.py index b4dd465a9..2fcd107c0 100755 --- a/processing/post/addCurl.py +++ b/processing/post/addCurl.py @@ -2,6 +2,7 @@ import os import sys +from io import StringIO from optparse import OptionParser import numpy as np @@ -12,47 +13,6 @@ import damask scriptName = os.path.splitext(os.path.basename(__file__))[0] scriptID = ' '.join([scriptName,damask.version]) -def merge_dicts(*dict_args): - """Given any number of dicts, shallow copy and merge into a new dict, with precedence going to key value pairs in latter dicts.""" - result = {} - for dictionary in dict_args: - result.update(dictionary) - return result - -def curlFFT(geomdim,field): - """Calculate curl of a vector or tensor field by transforming into Fourier space.""" - shapeFFT = np.array(np.shape(field))[0:3] - grid = np.array(np.shape(field)[2::-1]) - N = grid.prod() # field size - n = np.array(np.shape(field)[3:]).prod() # data size - - field_fourier = np.fft.rfftn(field,axes=(0,1,2),s=shapeFFT) - - # differentiation in Fourier space - TWOPIIMG = 2.0j*np.pi - einsums = { - 3:'slm,ijkl,ijkm->ijks', # vector, 3 -> 3 - 9:'slm,ijkl,ijknm->ijksn', # tensor, 3x3 -> 3x3 - } - k_sk = np.where(np.arange(grid[2])>grid[2]//2,np.arange(grid[2])-grid[2],np.arange(grid[2]))/geomdim[0] - if grid[2]%2 == 0: k_sk[grid[2]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) - - k_sj = np.where(np.arange(grid[1])>grid[1]//2,np.arange(grid[1])-grid[1],np.arange(grid[1]))/geomdim[1] - if grid[1]%2 == 0: k_sj[grid[1]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) - - k_si = np.arange(grid[0]//2+1)/geomdim[2] - - kk, kj, ki = np.meshgrid(k_sk,k_sj,k_si,indexing = 'ij') - k_s = np.concatenate((ki[:,:,:,None],kj[:,:,:,None],kk[:,:,:,None]),axis = 3).astype('c16') - - e = np.zeros((3, 3, 3)) - e[0, 1, 2] = e[1, 2, 0] = e[2, 0, 1] = 1.0 # Levi-Civita symbols - e[0, 2, 1] = e[2, 1, 0] = e[1, 0, 2] = -1.0 - - curl_fourier = np.einsum(einsums[n],e,k_s,field_fourier)*TWOPIIMG - - return np.fft.irfftn(curl_fourier,axes=(0,1,2),s=shapeFFT).reshape([N,n]) - # -------------------------------------------------------------------- # MAIN @@ -60,8 +20,7 @@ def curlFFT(geomdim,field): parser = OptionParser(option_class=damask.extendableOption, usage='%prog options [ASCIItable(s)]', description = """ Add column(s) containing curl of requested column(s). -Operates on periodic ordered three-dimensional data sets -of vector and tensor fields. +Operates on periodic ordered three-dimensional data sets of vector and tensor fields. """, version = scriptID) parser.add_option('-p','--pos','--periodiccellcenter', @@ -69,93 +28,30 @@ parser.add_option('-p','--pos','--periodiccellcenter', type = 'string', metavar = 'string', help = 'label of coordinates [%default]') parser.add_option('-l','--label', - dest = 'data', + dest = 'labels', action = 'extend', metavar = '', help = 'label(s) of field values') parser.set_defaults(pos = 'pos', ) - (options,filenames) = parser.parse_args() - -if options.data is None: parser.error('no data column specified.') - -# --- define possible data types ------------------------------------------------------------------- - -datatypes = { - 3: {'name': 'vector', - 'shape': [3], - }, - 9: {'name': 'tensor', - 'shape': [3,3], - }, - } - -# --- loop over input files ------------------------------------------------------------------------ - if filenames == []: filenames = [None] +if options.labels is None: parser.error('no data column specified.') + for name in filenames: - try: table = damask.ASCIItable(name = name,buffered = False) - except: continue - damask.util.report(scriptName,name) + damask.util.report(scriptName,name) -# --- interpret header ---------------------------------------------------------------------------- + table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) + grid,size = damask.util.coordGridAndSize(table.get_array(options.pos)) - table.head_read() - - remarks = [] - errors = [] - active = [] - - coordDim = table.label_dimension(options.pos) - if coordDim != 3: - errors.append('coordinates "{}" must be three-dimensional.'.format(options.pos)) - else: coordCol = table.label_index(options.pos) - - for me in options.data: - dim = table.label_dimension(me) - if dim in datatypes: - active.append(merge_dicts({'label':me},datatypes[dim])) - remarks.append('differentiating {} "{}"...'.format(datatypes[dim]['name'],me)) - else: - remarks.append('skipping "{}" of dimension {}...'.format(me,dim) if dim != -1 else \ - '"{}" not found...'.format(me) ) - - if remarks != []: damask.util.croak(remarks) - if errors != []: - damask.util.croak(errors) - table.close(dismiss = True) - continue - -# ------------------------------------------ assemble header -------------------------------------- - - table.info_append(scriptID + '\t' + ' '.join(sys.argv[1:])) - for data in active: - table.labels_append(['{}_curlFFT({})'.format(i+1,data['label']) - for i in range(np.prod(np.array(data['shape'])))]) # extend ASCII header with new labels - table.head_write() - -# --------------- figure out size and grid --------------------------------------------------------- - - table.data_readArray() - grid,size = damask.util.coordGridAndSize(table.data[:,table.label_indexrange(options.pos)]) - -# ------------------------------------------ process value field ----------------------------------- - - stack = [table.data] - for data in active: - # we need to reverse order here, because x is fastest,ie rightmost, but leftmost in our x,y,z notation - stack.append(curlFFT(size[::-1], - table.data[:,table.label_indexrange(data['label'])]. - reshape(grid[::-1].tolist()+data['shape']))) - -# ------------------------------------------ output result ----------------------------------------- - - if len(stack) > 1: table.data = np.hstack(tuple(stack)) - table.data_writeArray('%.12g') - -# ------------------------------------------ output finalization ----------------------------------- - - table.close() # close input ASCII table (works for stdin) + for label in options.labels: + field = table.get_array(label) + shape = (3,) if np.prod(field.shape)//np.prod(grid) == 3 else (3,3) # vector or tensor + field = table.get_array(label).reshape(np.append(grid[::-1],shape)) + table.add_array('curlFFT({})'.format(label), + damask.grid_filters.curl(size[::-1],field).reshape((-1,np.prod(shape))), + scriptID+' '+' '.join(sys.argv[1:])) + + table.to_ASCII(sys.stdout if name is None else name) diff --git a/processing/post/addDivergence.py b/processing/post/addDivergence.py index 2d6af2036..562ab7532 100755 --- a/processing/post/addDivergence.py +++ b/processing/post/addDivergence.py @@ -2,6 +2,7 @@ import os import sys +from io import StringIO from optparse import OptionParser import numpy as np @@ -12,52 +13,14 @@ import damask scriptName = os.path.splitext(os.path.basename(__file__))[0] scriptID = ' '.join([scriptName,damask.version]) -def merge_dicts(*dict_args): - """Given any number of dicts, shallow copy and merge into a new dict, with precedence going to key value pairs in latter dicts.""" - result = {} - for dictionary in dict_args: - result.update(dictionary) - return result - -def divFFT(geomdim,field): - """Calculate divergence of a vector or tensor field by transforming into Fourier space.""" - shapeFFT = np.array(np.shape(field))[0:3] - grid = np.array(np.shape(field)[2::-1]) - N = grid.prod() # field size - n = np.array(np.shape(field)[3:]).prod() # data size - - field_fourier = np.fft.rfftn(field,axes=(0,1,2),s=shapeFFT) - - # differentiation in Fourier space - TWOPIIMG = 2.0j*np.pi - einsums = { - 3:'ijkl,ijkl->ijk', # vector, 3 -> 1 - 9:'ijkm,ijklm->ijkl', # tensor, 3x3 -> 3 - } - k_sk = np.where(np.arange(grid[2])>grid[2]//2,np.arange(grid[2])-grid[2],np.arange(grid[2]))/geomdim[0] - if grid[2]%2 == 0: k_sk[grid[2]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) - - k_sj = np.where(np.arange(grid[1])>grid[1]//2,np.arange(grid[1])-grid[1],np.arange(grid[1]))/geomdim[1] - if grid[1]%2 == 0: k_sj[grid[1]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) - - k_si = np.arange(grid[0]//2+1)/geomdim[2] - - kk, kj, ki = np.meshgrid(k_sk,k_sj,k_si,indexing = 'ij') - k_s = np.concatenate((ki[:,:,:,None],kj[:,:,:,None],kk[:,:,:,None]),axis = 3).astype('c16') - - div_fourier = np.einsum(einsums[n],k_s,field_fourier)*TWOPIIMG - - return np.fft.irfftn(div_fourier,axes=(0,1,2),s=shapeFFT).reshape([N,n//3]) - # -------------------------------------------------------------------- # MAIN # -------------------------------------------------------------------- -parser = OptionParser(option_class=damask.extendableOption, usage='%prog option(s) [ASCIItable(s)]', description = """ -Add column(s) containing curl of requested column(s). -Operates on periodic ordered three-dimensional data sets -of vector and tensor fields. +parser = OptionParser(option_class=damask.extendableOption, usage='%prog options [ASCIItable(s)]', description = """ +Add column(s) containing divergence of requested column(s). +Operates on periodic ordered three-dimensional data sets of vector and tensor fields. """, version = scriptID) parser.add_option('-p','--pos','--periodiccellcenter', @@ -65,95 +28,30 @@ parser.add_option('-p','--pos','--periodiccellcenter', type = 'string', metavar = 'string', help = 'label of coordinates [%default]') parser.add_option('-l','--label', - dest = 'data', + dest = 'labels', action = 'extend', metavar = '', help = 'label(s) of field values') parser.set_defaults(pos = 'pos', ) - (options,filenames) = parser.parse_args() - -if options.data is None: parser.error('no data column specified.') - -# --- define possible data types ------------------------------------------------------------------- - -datatypes = { - 3: {'name': 'vector', - 'shape': [3], - }, - 9: {'name': 'tensor', - 'shape': [3,3], - }, - } - -# --- loop over input files ------------------------------------------------------------------------ - if filenames == []: filenames = [None] +if options.labels is None: parser.error('no data column specified.') + for name in filenames: - try: table = damask.ASCIItable(name = name,buffered = False) - except: continue - damask.util.report(scriptName,name) + damask.util.report(scriptName,name) -# --- interpret header ---------------------------------------------------------------------------- + table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) + grid,size = damask.util.coordGridAndSize(table.get_array(options.pos)) - table.head_read() - - remarks = [] - errors = [] - active = [] - - coordDim = table.label_dimension(options.pos) - if coordDim != 3: - errors.append('coordinates "{}" must be three-dimensional.'.format(options.pos)) - else: coordCol = table.label_index(options.pos) - - for me in options.data: - dim = table.label_dimension(me) - if dim in datatypes: - active.append(merge_dicts({'label':me},datatypes[dim])) - remarks.append('differentiating {} "{}"...'.format(datatypes[dim]['name'],me)) - else: - remarks.append('skipping "{}" of dimension {}...'.format(me,dim) if dim != -1 else \ - '"{}" not found...'.format(me) ) - - if remarks != []: damask.util.croak(remarks) - if errors != []: - damask.util.croak(errors) - table.close(dismiss = True) - continue - -# ------------------------------------------ assemble header -------------------------------------- - - table.info_append(scriptID + '\t' + ' '.join(sys.argv[1:])) - for data in active: - table.labels_append(['divFFT({})'.format(data['label']) if data['shape'] == [3] \ - else '{}_divFFT({})'.format(i+1,data['label']) - for i in range(np.prod(np.array(data['shape']))//3)]) # extend ASCII header with new labels - table.head_write() - -# --------------- figure out size and grid --------------------------------------------------------- - - table.data_readArray() - - grid,size = damask.util.coordGridAndSize(table.data[:,table.label_indexrange(options.pos)]) - -# ------------------------------------------ process value field ----------------------------------- - - stack = [table.data] - for data in active: - # we need to reverse order here, because x is fastest,ie rightmost, but leftmost in our x,y,z notation - stack.append(divFFT(size[::-1], - table.data[:,table.label_indexrange(data['label'])]. - reshape(grid[::-1].tolist()+data['shape']))) - -# ------------------------------------------ output result ----------------------------------------- - - if len(stack) > 1: table.data = np.hstack(tuple(stack)) - table.data_writeArray('%.12g') - -# ------------------------------------------ output finalization ----------------------------------- - - table.close() # close input ASCII table (works for stdin) + for label in options.labels: + field = table.get_array(label) + shape = (3,) if np.prod(field.shape)//np.prod(grid) == 3 else (3,3) # vector or tensor + field = table.get_array(label).reshape(np.append(grid[::-1],shape)) + table.add_array('divFFT({})'.format(label), + damask.grid_filters.divergence(size[::-1],field).reshape((-1,np.prod(shape)//3)), + scriptID+' '+' '.join(sys.argv[1:])) + + table.to_ASCII(sys.stdout if name is None else name) diff --git a/processing/post/addGradient.py b/processing/post/addGradient.py index 252b72eb8..d6b537ddd 100755 --- a/processing/post/addGradient.py +++ b/processing/post/addGradient.py @@ -2,6 +2,7 @@ import os import sys +from io import StringIO from optparse import OptionParser import numpy as np @@ -12,43 +13,6 @@ import damask scriptName = os.path.splitext(os.path.basename(__file__))[0] scriptID = ' '.join([scriptName,damask.version]) -def merge_dicts(*dict_args): - """Given any number of dicts, shallow copy and merge into a new dict, with precedence going to key value pairs in latter dicts.""" - result = {} - for dictionary in dict_args: - result.update(dictionary) - return result - -def gradFFT(geomdim,field): - """Calculate gradient of a vector or scalar field by transforming into Fourier space.""" - shapeFFT = np.array(np.shape(field))[0:3] - grid = np.array(np.shape(field)[2::-1]) - N = grid.prod() # field size - n = np.array(np.shape(field)[3:]).prod() # data size - - field_fourier = np.fft.rfftn(field,axes=(0,1,2),s=shapeFFT) - - # differentiation in Fourier space - TWOPIIMG = 2.0j*np.pi - einsums = { - 1:'ijkl,ijkm->ijkm', # scalar, 1 -> 3 - 3:'ijkl,ijkm->ijklm', # vector, 3 -> 3x3 - } - - k_sk = np.where(np.arange(grid[2])>grid[2]//2,np.arange(grid[2])-grid[2],np.arange(grid[2]))/geomdim[0] - if grid[2]%2 == 0: k_sk[grid[2]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) - - k_sj = np.where(np.arange(grid[1])>grid[1]//2,np.arange(grid[1])-grid[1],np.arange(grid[1]))/geomdim[1] - if grid[1]%2 == 0: k_sj[grid[1]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) - - k_si = np.arange(grid[0]//2+1)/geomdim[2] - - kk, kj, ki = np.meshgrid(k_sk,k_sj,k_si,indexing = 'ij') - k_s = np.concatenate((ki[:,:,:,None],kj[:,:,:,None],kk[:,:,:,None]),axis = 3).astype('c16') - grad_fourier = np.einsum(einsums[n],field_fourier,k_s)*TWOPIIMG - - return np.fft.irfftn(grad_fourier,axes=(0,1,2),s=shapeFFT).reshape([N,3*n]) - # -------------------------------------------------------------------- # MAIN @@ -56,9 +20,7 @@ def gradFFT(geomdim,field): parser = OptionParser(option_class=damask.extendableOption, usage='%prog options [ASCIItable(s)]', description = """ Add column(s) containing gradient of requested column(s). -Operates on periodic ordered three-dimensional data sets -of vector and scalar fields. - +Operates on periodic ordered three-dimensional data sets of scalar and vector fields. """, version = scriptID) parser.add_option('-p','--pos','--periodiccellcenter', @@ -66,7 +28,7 @@ parser.add_option('-p','--pos','--periodiccellcenter', type = 'string', metavar = 'string', help = 'label of coordinates [%default]') parser.add_option('-l','--label', - dest = 'data', + dest = 'labels', action = 'extend', metavar = '', help = 'label(s) of field values') @@ -74,85 +36,22 @@ parser.set_defaults(pos = 'pos', ) (options,filenames) = parser.parse_args() - -if options.data is None: parser.error('no data column specified.') - -# --- define possible data types ------------------------------------------------------------------- - -datatypes = { - 1: {'name': 'scalar', - 'shape': [1], - }, - 3: {'name': 'vector', - 'shape': [3], - }, - } - -# --- loop over input files ------------------------------------------------------------------------ - if filenames == []: filenames = [None] +if options.labels is None: parser.error('no data column specified.') + for name in filenames: - try: table = damask.ASCIItable(name = name,buffered = False) - except: continue - damask.util.report(scriptName,name) + damask.util.report(scriptName,name) -# --- interpret header ---------------------------------------------------------------------------- + table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) + grid,size = damask.util.coordGridAndSize(table.get_array(options.pos)) - table.head_read() - - remarks = [] - errors = [] - active = [] - - coordDim = table.label_dimension(options.pos) - if coordDim != 3: - errors.append('coordinates "{}" must be three-dimensional.'.format(options.pos)) - else: coordCol = table.label_index(options.pos) - - for me in options.data: - dim = table.label_dimension(me) - if dim in datatypes: - active.append(merge_dicts({'label':me},datatypes[dim])) - remarks.append('differentiating {} "{}"...'.format(datatypes[dim]['name'],me)) - else: - remarks.append('skipping "{}" of dimension {}...'.format(me,dim) if dim != -1 else \ - '"{}" not found...'.format(me) ) - - if remarks != []: damask.util.croak(remarks) - if errors != []: - damask.util.croak(errors) - table.close(dismiss = True) - continue - -# ------------------------------------------ assemble header -------------------------------------- - - table.info_append(scriptID + '\t' + ' '.join(sys.argv[1:])) - for data in active: - table.labels_append(['{}_gradFFT({})'.format(i+1,data['label']) - for i in range(coordDim*np.prod(np.array(data['shape'])))]) # extend ASCII header with new labels - table.head_write() - -# --------------- figure out size and grid --------------------------------------------------------- - - table.data_readArray() - - grid,size = damask.util.coordGridAndSize(table.data[:,table.label_indexrange(options.pos)]) - -# ------------------------------------------ process value field ----------------------------------- - - stack = [table.data] - for data in active: - # we need to reverse order here, because x is fastest,ie rightmost, but leftmost in our x,y,z notation - stack.append(gradFFT(size[::-1], - table.data[:,table.label_indexrange(data['label'])]. - reshape(grid[::-1].tolist()+data['shape']))) - -# ------------------------------------------ output result ----------------------------------------- - - if len(stack) > 1: table.data = np.hstack(tuple(stack)) - table.data_writeArray('%.12g') - -# ------------------------------------------ output finalization ----------------------------------- - - table.close() # close input ASCII table (works for stdin) + for label in options.labels: + field = table.get_array(label) + shape = (1,) if np.prod(field.shape)//np.prod(grid) == 1 else (3,) # scalar or vector + field = table.get_array(label).reshape(np.append(grid[::-1],shape)) + table.add_array('gradFFT({})'.format(label), + damask.grid_filters.gradient(size[::-1],field).reshape((-1,np.prod(shape)*3)), + scriptID+' '+' '.join(sys.argv[1:])) + + table.to_ASCII(sys.stdout if name is None else name) diff --git a/python/damask/grid_filters.py b/python/damask/grid_filters.py index 69ee85033..c7e96f468 100644 --- a/python/damask/grid_filters.py +++ b/python/damask/grid_filters.py @@ -1,12 +1,8 @@ import numpy as np - -def curl(size,field): - """Calculate curl of a vector or tensor field in Fourier space.""" +def __ks(size,field): + """Get differential operator.""" grid = np.array(np.shape(field)[0:3]) - n = np.array(np.shape(field)[3:]).prod() # data size - - field_fourier = np.fft.rfftn(field,axes=(0,1,2)) k_sk = np.where(np.arange(grid[0])>grid[0]//2,np.arange(grid[0])-grid[0],np.arange(grid[0]))/size[0] if grid[0]%2 == 0: k_sk[grid[0]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) @@ -17,64 +13,47 @@ def curl(size,field): k_si = np.arange(grid[2]//2+1)/size[2] kk, kj, ki = np.meshgrid(k_sk,k_sj,k_si,indexing = 'ij') - k_s = np.concatenate((ki[:,:,:,None],kj[:,:,:,None],kk[:,:,:,None]),axis = 3).astype('c16') + return np.concatenate((ki[:,:,:,None],kj[:,:,:,None],kk[:,:,:,None]),axis = 3) + + +def curl(size,field): + """Calculate curl of a vector or tensor field in Fourier space.""" + n = np.prod(field.shape[3:]) + k_s = __ks(size,field) e = np.zeros((3, 3, 3)) e[0, 1, 2] = e[1, 2, 0] = e[2, 0, 1] = +1.0 # Levi-Civita symbol e[0, 2, 1] = e[2, 1, 0] = e[1, 0, 2] = -1.0 + field_fourier = np.fft.rfftn(field,axes=(0,1,2)) curl = (np.einsum('slm,ijkl,ijkm ->ijks', e,k_s,field_fourier)*2.0j*np.pi if n == 3 else # vector, 3 -> 3 np.einsum('slm,ijkl,ijknm->ijksn',e,k_s,field_fourier)*2.0j*np.pi) # tensor, 3x3 -> 3x3 - return np.fft.irfftn(curl,axes=(0,1,2)) + return np.fft.irfftn(curl,axes=(0,1,2),s=field.shape[0:3]) def divergence(size,field): """Calculate divergence of a vector or tensor field in Fourier space.""" - grid = np.array(np.shape(field)[0:3]) - n = np.array(np.shape(field)[3:]).prod() # data size + n = np.prod(field.shape[3:]) + k_s = __ks(size,field) - field_fourier = np.fft.rfftn(field,axes=(0,1,2),s=grid) + field_fourier = np.fft.rfftn(field,axes=(0,1,2)) + divergence = (np.einsum('ijkl,ijkl ->ijk', k_s,field_fourier)*2.0j*np.pi if n == 3 else # vector, 3 -> 1 + np.einsum('ijkm,ijklm->ijkl',k_s,field_fourier)*2.0j*np.pi) # tensor, 3x3 -> 3 - k_sk = np.where(np.arange(grid[0])>grid[0]//2,np.arange(grid[0])-grid[0],np.arange(grid[0]))/size[0] - if grid[0]%2 == 0: k_sk[grid[0]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) - - k_sj = np.where(np.arange(grid[1])>grid[1]//2,np.arange(grid[1])-grid[1],np.arange(grid[1]))/size[1] - if grid[1]%2 == 0: k_sj[grid[1]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) - - k_si = np.arange(grid[2]//2+1)/size[2] - - kk, kj, ki = np.meshgrid(k_sk,k_sj,k_si,indexing = 'ij') - k_s = np.concatenate((ki[:,:,:,None],kj[:,:,:,None],kk[:,:,:,None]),axis = 3).astype('c16') - - divergence = (np.einsum('ijkl,ijkl ->ijk', k_s,field_fourier)*2.0j*np.pi if n == 3 else # vector, 3 -> 1 - np.einsum('ijkm,ijklm->ijkl',k_s,field_fourier)*2.0j*np.pi) # tensor, 3x3 -> 3 - - return np.fft.irfftn(div_fourier,axes=(0,1,2),s=grid) + return np.fft.irfftn(divergence,axes=(0,1,2),s=field.shape[0:3]) def gradient(size,field): """Calculate gradient of a vector or scalar field in Fourier space.""" - grid = np.array(np.shape(field)[2::-1]) - n = np.array(np.shape(field)[3:]).prod() # data size - - field_fourier = np.fft.rfftn(field,axes=(0,1,2),s=grid) - - k_sk = np.where(np.arange(grid[0])>grid[0]//2,np.arange(grid[0])-grid[0],np.arange(grid[0]))/size[0] - if grid[0]%2 == 0: k_sk[grid[0]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) - - k_sj = np.where(np.arange(grid[1])>grid[1]//2,np.arange(grid[1])-grid[1],np.arange(grid[1]))/size[1] - if grid[1]%2 == 0: k_sj[grid[1]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) - - k_si = np.arange(grid[2]//2+1)/size[2] - - kk, kj, ki = np.meshgrid(k_sk,k_sj,k_si,indexing = 'ij') - k_s = np.concatenate((ki[:,:,:,None],kj[:,:,:,None],kk[:,:,:,None]),axis = 3).astype('c16') + n = np.prod(field.shape[3:]) + k_s = __ks(size,field) + field_fourier = np.fft.rfftn(field,axes=(0,1,2)) gradient = (np.einsum('ijkl,ijkm->ijkm', field_fourier,k_s)*2.0j*np.pi if n == 1 else # scalar, 1 -> 3 np.einsum('ijkl,ijkm->ijklm',field_fourier,k_s)*2.0j*np.pi) # vector, 3 -> 3x3 - return np.fft.irfftn(grad_fourier,axes=(0,1,2),s=grid) + return np.fft.irfftn(gradient,axes=(0,1,2),s=field.shape[0:3]) #-------------------------------------------------------------------------------------------------- From f2e722ed2e92a9b61c9d18d8430619673ec1192c Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 28 Nov 2019 18:22:34 +0100 Subject: [PATCH 010/223] polishing --- python/damask/grid_filters.py | 64 ++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/python/damask/grid_filters.py b/python/damask/grid_filters.py index c7e96f468..e49ff47a9 100644 --- a/python/damask/grid_filters.py +++ b/python/damask/grid_filters.py @@ -1,14 +1,14 @@ import numpy as np -def __ks(size,field): - """Get differential operator.""" +def __ks(size,field,first_order=False): + """Get wave numbers operator.""" grid = np.array(np.shape(field)[0:3]) k_sk = np.where(np.arange(grid[0])>grid[0]//2,np.arange(grid[0])-grid[0],np.arange(grid[0]))/size[0] - if grid[0]%2 == 0: k_sk[grid[0]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) + if grid[0]%2 == 0 and first_order: k_sk[grid[0]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) k_sj = np.where(np.arange(grid[1])>grid[1]//2,np.arange(grid[1])-grid[1],np.arange(grid[1]))/size[1] - if grid[1]%2 == 0: k_sj[grid[1]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) + if grid[1]%2 == 0 and first_order: k_sj[grid[1]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) k_si = np.arange(grid[2]//2+1)/size[2] @@ -19,7 +19,7 @@ def __ks(size,field): def curl(size,field): """Calculate curl of a vector or tensor field in Fourier space.""" n = np.prod(field.shape[3:]) - k_s = __ks(size,field) + k_s = __ks(size,field,True) e = np.zeros((3, 3, 3)) e[0, 1, 2] = e[1, 2, 0] = e[2, 0, 1] = +1.0 # Levi-Civita symbol @@ -35,7 +35,7 @@ def curl(size,field): def divergence(size,field): """Calculate divergence of a vector or tensor field in Fourier space.""" n = np.prod(field.shape[3:]) - k_s = __ks(size,field) + k_s = __ks(size,field,True) field_fourier = np.fft.rfftn(field,axes=(0,1,2)) divergence = (np.einsum('ijkl,ijkl ->ijk', k_s,field_fourier)*2.0j*np.pi if n == 3 else # vector, 3 -> 1 @@ -47,7 +47,7 @@ def divergence(size,field): def gradient(size,field): """Calculate gradient of a vector or scalar field in Fourier space.""" n = np.prod(field.shape[3:]) - k_s = __ks(size,field) + k_s = __ks(size,field,True) field_fourier = np.fft.rfftn(field,axes=(0,1,2)) gradient = (np.einsum('ijkl,ijkm->ijkm', field_fourier,k_s)*2.0j*np.pi if n == 1 else # scalar, 1 -> 3 @@ -56,29 +56,37 @@ def gradient(size,field): return np.fft.irfftn(gradient,axes=(0,1,2),s=field.shape[0:3]) -#-------------------------------------------------------------------------------------------------- -def displacementFluctFFT(F,size): - """Calculate displacement field from deformation gradient field.""" - integrator = 0.5j * size / np.pi +def coord_node(grid,size): + """Positions of nodes (undeformed).""" + x, y, z = np.meshgrid(np.linspace(0,size[2],1+grid[2]), + np.linspace(0,size[1],1+grid[1]), + np.linspace(0,size[0],1+grid[0]), + indexing = 'ij') + + return np.concatenate((z[:,:,:,None],y[:,:,:,None],x[:,:,:,None]),axis = 3) - kk, kj, ki = np.meshgrid(np.where(np.arange(grid[2])>grid[2]//2,np.arange(grid[2])-grid[2],np.arange(grid[2])), - np.where(np.arange(grid[1])>grid[1]//2,np.arange(grid[1])-grid[1],np.arange(grid[1])), - np.arange(grid[0]//2+1), - indexing = 'ij') - k_s = np.concatenate((ki[:,:,:,None],kj[:,:,:,None],kk[:,:,:,None]),axis = 3) - k_sSquared = np.einsum('...l,...l',k_s,k_s) - k_sSquared[0,0,0] = 1.0 # ignore global average frequency -#-------------------------------------------------------------------------------------------------- -# integration in Fourier space +def coord_cell(grid,size): + """Positions of cell centers (undeformed).""" + delta = size/grid*0.5 + x, y, z = np.meshgrid(np.linspace(delta[2],size[2]-delta[2],grid[2]), + np.linspace(delta[1],size[1]-delta[1],grid[1]), + np.linspace(delta[0],size[0]-delta[0],grid[0]), + indexing = 'ij') - displacement_fourier = -np.einsum('ijkml,ijkl,l->ijkm', - np.fft.rfftn(F,axes=(0,1,2)), - k_s, - integrator, - ) / k_sSquared[...,np.newaxis] + return np.concatenate((z[:,:,:,None],y[:,:,:,None],x[:,:,:,None]),axis = 3) -#-------------------------------------------------------------------------------------------------- -# backtransformation to real space - return np.fft.irfftn(displacement_fourier,grid[::-1],axes=(0,1,2)) +def displacement_fluct(size,F): + """Calculate displacement field from deformation gradient field.""" + integrator = 0.5j * size / np.pi + + k_s = __ks(size,F,False) + + displacement = -np.einsum('ijkml,ijkl,l->ijkm', + np.fft.rfftn(F,axes=(0,1,2)), + k_s, + integrator, + ) / k_sSquared[...,np.newaxis] + + return np.fft.irfftn(displacement,axes=(0,1,2)) From 62ca2952fce464016d721cf3ed2be5344d56a541 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 3 Dec 2019 11:27:14 +0100 Subject: [PATCH 011/223] polishing --- processing/post/addCurl.py | 2 +- processing/post/addDivergence.py | 2 +- processing/post/addGradient.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/processing/post/addCurl.py b/processing/post/addCurl.py index 2fcd107c0..b3dfedaf4 100755 --- a/processing/post/addCurl.py +++ b/processing/post/addCurl.py @@ -49,7 +49,7 @@ for name in filenames: for label in options.labels: field = table.get_array(label) shape = (3,) if np.prod(field.shape)//np.prod(grid) == 3 else (3,3) # vector or tensor - field = table.get_array(label).reshape(np.append(grid[::-1],shape)) + field = field.reshape(np.append(grid[::-1],shape)) table.add_array('curlFFT({})'.format(label), damask.grid_filters.curl(size[::-1],field).reshape((-1,np.prod(shape))), scriptID+' '+' '.join(sys.argv[1:])) diff --git a/processing/post/addDivergence.py b/processing/post/addDivergence.py index 562ab7532..7ecaf10f0 100755 --- a/processing/post/addDivergence.py +++ b/processing/post/addDivergence.py @@ -49,7 +49,7 @@ for name in filenames: for label in options.labels: field = table.get_array(label) shape = (3,) if np.prod(field.shape)//np.prod(grid) == 3 else (3,3) # vector or tensor - field = table.get_array(label).reshape(np.append(grid[::-1],shape)) + field = field.reshape(np.append(grid[::-1],shape)) table.add_array('divFFT({})'.format(label), damask.grid_filters.divergence(size[::-1],field).reshape((-1,np.prod(shape)//3)), scriptID+' '+' '.join(sys.argv[1:])) diff --git a/processing/post/addGradient.py b/processing/post/addGradient.py index d6b537ddd..d3081db66 100755 --- a/processing/post/addGradient.py +++ b/processing/post/addGradient.py @@ -49,7 +49,7 @@ for name in filenames: for label in options.labels: field = table.get_array(label) shape = (1,) if np.prod(field.shape)//np.prod(grid) == 1 else (3,) # scalar or vector - field = table.get_array(label).reshape(np.append(grid[::-1],shape)) + field = field.reshape(np.append(grid[::-1],shape)) table.add_array('gradFFT({})'.format(label), damask.grid_filters.gradient(size[::-1],field).reshape((-1,np.prod(shape)*3)), scriptID+' '+' '.join(sys.argv[1:])) From e006e0ebec0a6e7a662f3f453040e2aa83d7f844 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 3 Dec 2019 18:59:59 +0100 Subject: [PATCH 012/223] functions for spatial coordinates on regular grids --- python/damask/grid_filters.py | 67 +++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/python/damask/grid_filters.py b/python/damask/grid_filters.py index e49ff47a9..bf757bdb9 100644 --- a/python/damask/grid_filters.py +++ b/python/damask/grid_filters.py @@ -2,7 +2,7 @@ import numpy as np def __ks(size,field,first_order=False): """Get wave numbers operator.""" - grid = np.array(np.shape(field)[0:3]) + grid = np.array(np.shape(field)[:3]) k_sk = np.where(np.arange(grid[0])>grid[0]//2,np.arange(grid[0])-grid[0],np.arange(grid[0]))/size[0] if grid[0]%2 == 0 and first_order: k_sk[grid[0]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) @@ -29,7 +29,7 @@ def curl(size,field): curl = (np.einsum('slm,ijkl,ijkm ->ijks', e,k_s,field_fourier)*2.0j*np.pi if n == 3 else # vector, 3 -> 3 np.einsum('slm,ijkl,ijknm->ijksn',e,k_s,field_fourier)*2.0j*np.pi) # tensor, 3x3 -> 3x3 - return np.fft.irfftn(curl,axes=(0,1,2),s=field.shape[0:3]) + return np.fft.irfftn(curl,axes=(0,1,2),s=field.shape[:3]) def divergence(size,field): @@ -41,7 +41,7 @@ def divergence(size,field): divergence = (np.einsum('ijkl,ijkl ->ijk', k_s,field_fourier)*2.0j*np.pi if n == 3 else # vector, 3 -> 1 np.einsum('ijkm,ijklm->ijkl',k_s,field_fourier)*2.0j*np.pi) # tensor, 3x3 -> 3 - return np.fft.irfftn(divergence,axes=(0,1,2),s=field.shape[0:3]) + return np.fft.irfftn(divergence,axes=(0,1,2),s=field.shape[:3]) def gradient(size,field): @@ -53,21 +53,11 @@ def gradient(size,field): gradient = (np.einsum('ijkl,ijkm->ijkm', field_fourier,k_s)*2.0j*np.pi if n == 1 else # scalar, 1 -> 3 np.einsum('ijkl,ijkm->ijklm',field_fourier,k_s)*2.0j*np.pi) # vector, 3 -> 3x3 - return np.fft.irfftn(gradient,axes=(0,1,2),s=field.shape[0:3]) + return np.fft.irfftn(gradient,axes=(0,1,2),s=field.shape[:3]) -def coord_node(grid,size): - """Positions of nodes (undeformed).""" - x, y, z = np.meshgrid(np.linspace(0,size[2],1+grid[2]), - np.linspace(0,size[1],1+grid[1]), - np.linspace(0,size[0],1+grid[0]), - indexing = 'ij') - - return np.concatenate((z[:,:,:,None],y[:,:,:,None],x[:,:,:,None]),axis = 3) - - -def coord_cell(grid,size): - """Positions of cell centers (undeformed).""" +def coord0_cell(grid,size): + """Cell center positions (undeformed).""" delta = size/grid*0.5 x, y, z = np.meshgrid(np.linspace(delta[2],size[2]-delta[2],grid[2]), np.linspace(delta[1],size[1]-delta[1],grid[1]), @@ -76,17 +66,50 @@ def coord_cell(grid,size): return np.concatenate((z[:,:,:,None],y[:,:,:,None],x[:,:,:,None]),axis = 3) - -def displacement_fluct(size,F): - """Calculate displacement field from deformation gradient field.""" - integrator = 0.5j * size / np.pi +def displacement_fluct_cell(size,F): + """Cell center displacement field from fluctuation part of the deformation gradient field.""" + integrator = 0.5j*size/np.pi k_s = __ks(size,F,False) + k_s_squared = np.einsum('...l,...l',k_s,k_s) + k_s_squared[0,0,0] = 1.0 displacement = -np.einsum('ijkml,ijkl,l->ijkm', np.fft.rfftn(F,axes=(0,1,2)), k_s, integrator, - ) / k_sSquared[...,np.newaxis] + ) / k_s_squared[...,np.newaxis] - return np.fft.irfftn(displacement,axes=(0,1,2)) + return np.fft.irfftn(displacement,axes=(0,1,2),s=F.shape[:3]) + +def displacement_avg_cell(size,F): + """Cell center displacement field from average part of the deformation gradient field.""" + F_avg = np.average(F,axis=(0,1,2)) + return np.einsum('ml,ijkl->ijkm',F_avg-np.eye(3),coord0_cell(F.shape[:3],size)) + + +def coord0_node(grid,size): + """Nodal positions (undeformed).""" + x, y, z = np.meshgrid(np.linspace(0,size[2],1+grid[2]), + np.linspace(0,size[1],1+grid[1]), + np.linspace(0,size[0],1+grid[0]), + indexing = 'ij') + + return np.concatenate((z[:,:,:,None],y[:,:,:,None],x[:,:,:,None]),axis = 3) + +def displacement_fluct_node(size,F): + return cell_2_node(displacement_fluct_cell(size,F)) + +def displacement_avg_node(size,F): + F_avg = np.average(F,axis=(0,1,2)) + return np.einsum('ml,ijkl->ijkm',F_avg-np.eye(3),coord0_node(F.shape[0:3],size)) + + +def cell_2_node(cell_data): + """Interpolate cell data to nodal data.""" + + n = ( cell_data + np.roll(cell_data,1,(0,1,2)) + + np.roll(cell_data,1,(0,)) + np.roll(cell_data,1,(1,)) + np.roll(cell_data,1,(2,)) + + np.roll(cell_data,1,(0,1)) + np.roll(cell_data,1,(1,2)) + np.roll(cell_data,1,(2,0))) *0.125 + + return np.pad(n,((0,1),(0,1),(0,1))+((0,0),)*len(cell_data.shape[3:]),mode='wrap') From 9ad874339647b8271f5b66c5b9dc21554ee853fd Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 3 Dec 2019 19:30:55 +0100 Subject: [PATCH 013/223] using central functionality --- processing/post/addDisplacement.py | 202 ++++------------------------- python/damask/grid_filters.py | 13 +- 2 files changed, 38 insertions(+), 177 deletions(-) diff --git a/processing/post/addDisplacement.py b/processing/post/addDisplacement.py index 99d07fd18..ab25920b5 100755 --- a/processing/post/addDisplacement.py +++ b/processing/post/addDisplacement.py @@ -2,10 +2,10 @@ import os import sys +from io import StringIO from optparse import OptionParser import numpy as np -import scipy.ndimage import damask @@ -14,79 +14,6 @@ scriptName = os.path.splitext(os.path.basename(__file__))[0] scriptID = ' '.join([scriptName,damask.version]) -#-------------------------------------------------------------------------------------------------- -def cell2node(cellData,grid): - - nodeData = 0.0 - datalen = np.array(cellData.shape[3:]).prod() - - for i in range(datalen): - node = scipy.ndimage.convolve(cellData.reshape(tuple(grid[::-1])+(datalen,))[...,i], - np.ones((2,2,2))/8., # 2x2x2 neighborhood of cells - mode = 'wrap', - origin = -1, # offset to have cell origin as center - ) # now averaged at cell origins - node = np.append(node,node[np.newaxis,0,:,:,...],axis=0) # wrap along z - node = np.append(node,node[:,0,np.newaxis,:,...],axis=1) # wrap along y - node = np.append(node,node[:,:,0,np.newaxis,...],axis=2) # wrap along x - - nodeData = node[...,np.newaxis] if i==0 else np.concatenate((nodeData,node[...,np.newaxis]),axis=-1) - - return nodeData - -#-------------------------------------------------------------------------------------------------- -def displacementAvgFFT(F,grid,size,nodal=False,transformed=False): - """Calculate average cell center (or nodal) displacement for deformation gradient field specified in each grid cell""" - if nodal: - x, y, z = np.meshgrid(np.linspace(0,size[2],1+grid[2]), - np.linspace(0,size[1],1+grid[1]), - np.linspace(0,size[0],1+grid[0]), - indexing = 'ij') - else: - delta = size/grid*0.5 - x, y, z = np.meshgrid(np.linspace(delta[2],size[2]-delta[2],grid[2]), - np.linspace(delta[1],size[1]-delta[1],grid[1]), - np.linspace(delta[0],size[0]-delta[0],grid[0]), - indexing = 'ij') - - origCoords = np.concatenate((z[:,:,:,None],y[:,:,:,None],x[:,:,:,None]),axis = 3) - - F_fourier = F if transformed else np.fft.rfftn(F,axes=(0,1,2)) # transform or use provided data - Favg = np.real(F_fourier[0,0,0,:,:])/grid.prod() # take zero freq for average - avgDisplacement = np.einsum('ml,ijkl->ijkm',Favg-np.eye(3),origCoords) # dX = Favg.X - - return avgDisplacement - -#-------------------------------------------------------------------------------------------------- -def displacementFluctFFT(F,grid,size,nodal=False,transformed=False): - """Calculate cell center (or nodal) displacement for deformation gradient field specified in each grid cell""" - integrator = 0.5j * size / np.pi - - kk, kj, ki = np.meshgrid(np.where(np.arange(grid[2])>grid[2]//2,np.arange(grid[2])-grid[2],np.arange(grid[2])), - np.where(np.arange(grid[1])>grid[1]//2,np.arange(grid[1])-grid[1],np.arange(grid[1])), - np.arange(grid[0]//2+1), - indexing = 'ij') - k_s = np.concatenate((ki[:,:,:,None],kj[:,:,:,None],kk[:,:,:,None]),axis = 3) - k_sSquared = np.einsum('...l,...l',k_s,k_s) - k_sSquared[0,0,0] = 1.0 # ignore global average frequency - -#-------------------------------------------------------------------------------------------------- -# integration in Fourier space - - displacement_fourier = -np.einsum('ijkml,ijkl,l->ijkm', - F if transformed else np.fft.rfftn(F,axes=(0,1,2)), - k_s, - integrator, - ) / k_sSquared[...,np.newaxis] - -#-------------------------------------------------------------------------------------------------- -# backtransformation to real space - - displacement = np.fft.irfftn(displacement_fourier,grid[::-1],axes=(0,1,2)) - - return cell2node(displacement,grid) if nodal else displacement - - # -------------------------------------------------------------------- # MAIN # -------------------------------------------------------------------- @@ -100,7 +27,7 @@ Outputs at cell centers or cell nodes (into separate file). parser.add_option('-f', '--defgrad', - dest = 'defgrad', + dest = 'f', metavar = 'string', help = 'label of deformation gradient [%default]') parser.add_option('-p', @@ -113,108 +40,35 @@ parser.add_option('--nodal', action = 'store_true', help = 'output nodal (instead of cell-centered) displacements') -parser.set_defaults(defgrad = 'f', - pos = 'pos', +parser.set_defaults(f = 'f', + pos = 'pos', ) (options,filenames) = parser.parse_args() -# --- loop over input files ------------------------------------------------------------------------- - -if filenames == []: filenames = [None] - for name in filenames: - outname = (os.path.splitext(name)[0] + - '_nodal' + - os.path.splitext(name)[1]) if (options.nodal and name) else None - try: table = damask.ASCIItable(name = name, - outname = outname, - buffered = False) - except: continue - damask.util.report(scriptName,'{}{}'.format(name if name else '', - ' --> {}'.format(outname) if outname else '')) + damask.util.report(scriptName,name) -# ------------------------------------------ read header ------------------------------------------ - - table.head_read() - -# ------------------------------------------ sanity checks ---------------------------------------- - - errors = [] - remarks = [] - - if table.label_dimension(options.defgrad) != 9: - errors.append('deformation gradient "{}" is not a 3x3 tensor.'.format(options.defgrad)) - - coordDim = table.label_dimension(options.pos) - if not 3 >= coordDim >= 1: - errors.append('coordinates "{}" need to have one, two, or three dimensions.'.format(options.pos)) - elif coordDim < 3: - remarks.append('appending {} dimension{} to coordinates "{}"...'.format(3-coordDim, - 's' if coordDim < 2 else '', - options.pos)) - - if remarks != []: damask.util.croak(remarks) - if errors != []: - damask.util.croak(errors) - table.close(dismiss=True) - continue - -# --------------- figure out size and grid --------------------------------------------------------- - - table.data_readArray([options.defgrad,options.pos]) - table.data_rewind() - - if len(table.data.shape) < 2: table.data.shape += (1,) # expand to 2D shape - if table.data[:,9:].shape[1] < 3: - table.data = np.hstack((table.data, - np.zeros((table.data.shape[0], - 3-table.data[:,9:].shape[1]),dtype='f'))) # fill coords up to 3D with zeros - - grid,size = damask.util.coordGridAndSize(table.data[:,9:12]) - N = grid.prod() - - if N != len(table.data): errors.append('data count {} does not match grid {}x{}x{}.'.format(N,*grid)) - if errors != []: - damask.util.croak(errors) - table.close(dismiss = True) - continue - -# ------------------------------------------ process data ------------------------------------------ - - F_fourier = np.fft.rfftn(table.data[:,:9].reshape(grid[2],grid[1],grid[0],3,3),axes=(0,1,2)) # perform transform only once... - - fluctDisplacement = displacementFluctFFT(F_fourier,grid,size,options.nodal,transformed=True) - avgDisplacement = displacementAvgFFT (F_fourier,grid,size,options.nodal,transformed=True) - -# ------------------------------------------ assemble header --------------------------------------- - - if options.nodal: - table.info_clear() - table.labels_clear() - - table.info_append(scriptID + '\t' + ' '.join(sys.argv[1:])) - table.labels_append((['{}_pos' .format(i+1) for i in range(3)] if options.nodal else []) + - ['{}_avg({}).{}' .format(i+1,options.defgrad,options.pos) for i in range(3)] + - ['{}_fluct({}).{}'.format(i+1,options.defgrad,options.pos) for i in range(3)] ) - table.head_write() - -# ------------------------------------------ output data ------------------------------------------- - - Zrange = np.linspace(0,size[2],1+grid[2]) if options.nodal else range(grid[2]) - Yrange = np.linspace(0,size[1],1+grid[1]) if options.nodal else range(grid[1]) - Xrange = np.linspace(0,size[0],1+grid[0]) if options.nodal else range(grid[0]) - - for i,z in enumerate(Zrange): - for j,y in enumerate(Yrange): - for k,x in enumerate(Xrange): - if options.nodal: table.data_clear() - else: table.data_read() - table.data_append([x,y,z] if options.nodal else []) - table.data_append(list( avgDisplacement[i,j,k,:])) - table.data_append(list(fluctDisplacement[i,j,k,:])) - table.data_write() - -# ------------------------------------------ output finalization ----------------------------------- - - table.close() # close ASCII tables + table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) + grid,size = damask.util.coordGridAndSize(table.get_array(options.pos)) + + F = table.get_array(options.f).reshape(np.append(grid[::-1],(3,3))) + if options.nodal: + table = damask.Table(damask.grid_filters.coord0_node(grid[::-1],size[::-1]).reshape((-1,3)), + {'pos':(3,)}) + table.add_array('avg({}).{}'.format(options.f,options.pos), + damask.grid_filters.displacement_avg_node(size[::-1],F).reshape((-1,3)), + scriptID+' '+' '.join(sys.argv[1:])) + table.add_array('fluct({}).{}'.format(options.f,options.pos), + damask.grid_filters.displacement_fluct_node(size[::-1],F).reshape((-1,3)), + scriptID+' '+' '.join(sys.argv[1:])) + table.to_ASCII(sys.stdout if name is None else os.path.splitext(name)[0]+'_nodal.txt') + else: + table.add_array('avg({}).{}'.format(options.f,options.pos), + damask.grid_filters.displacement_avg_cell(size[::-1],F).reshape((-1,3)), + scriptID+' '+' '.join(sys.argv[1:])) + table.add_array('fluct({}).{}'.format(options.f,options.pos), + damask.grid_filters.displacement_fluct_cell(size[::-1],F).reshape((-1,3)), + scriptID+' '+' '.join(sys.argv[1:])) + + table.to_ASCII(sys.stdout if name is None else name) diff --git a/python/damask/grid_filters.py b/python/damask/grid_filters.py index bf757bdb9..924242c58 100644 --- a/python/damask/grid_filters.py +++ b/python/damask/grid_filters.py @@ -102,14 +102,21 @@ def displacement_fluct_node(size,F): def displacement_avg_node(size,F): F_avg = np.average(F,axis=(0,1,2)) - return np.einsum('ml,ijkl->ijkm',F_avg-np.eye(3),coord0_node(F.shape[0:3],size)) + return np.einsum('ml,ijkl->ijkm',F_avg-np.eye(3),coord0_node(F.shape[:3],size)) def cell_2_node(cell_data): """Interpolate cell data to nodal data.""" - n = ( cell_data + np.roll(cell_data,1,(0,1,2)) + np.roll(cell_data,1,(0,)) + np.roll(cell_data,1,(1,)) + np.roll(cell_data,1,(2,)) - + np.roll(cell_data,1,(0,1)) + np.roll(cell_data,1,(1,2)) + np.roll(cell_data,1,(2,0))) *0.125 + + np.roll(cell_data,1,(0,1)) + np.roll(cell_data,1,(1,2)) + np.roll(cell_data,1,(2,0)))*0.125 return np.pad(n,((0,1),(0,1),(0,1))+((0,0),)*len(cell_data.shape[3:]),mode='wrap') + +def node_2_cell(node_data): + """Interpolate nodal data to cell data.""" + c = ( node_data + np.roll(node_data,1,(0,1,2)) + + np.roll(node_data,1,(0,)) + np.roll(node_data,1,(1,)) + np.roll(node_data,1,(2,)) + + np.roll(node_data,1,(0,1)) + np.roll(node_data,1,(1,2)) + np.roll(node_data,1,(2,0)))*0.125 + + return c[:-1,:-1,:-1] From bc41bbbec52376d2a7b02449def6b0ffe6140c05 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 4 Dec 2019 09:23:08 +0100 Subject: [PATCH 014/223] test coordinates-related functions --- python/tests/test_grid_filters.py | 42 +++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 python/tests/test_grid_filters.py diff --git a/python/tests/test_grid_filters.py b/python/tests/test_grid_filters.py new file mode 100644 index 000000000..89b256dcc --- /dev/null +++ b/python/tests/test_grid_filters.py @@ -0,0 +1,42 @@ +import pytest +import numpy as np + +from damask import grid_filters + +class TestGridFilters: + + def test_coord0_cell(self): + size = np.random.random(3) + grid = np.random.randint(8,32,(3)) + coord = grid_filters.coord0_cell(grid,size) + assert np.allclose(coord[0,0,0],size/grid*.5) and coord.shape == tuple(grid[::-1]) + (3,) + + def test_coord0_node(self): + size = np.random.random(3) + grid = np.random.randint(8,32,(3)) + coord = grid_filters.coord0_node(grid,size) + assert np.allclose(coord[-1,-1,-1],size) and coord.shape == tuple(grid[::-1]+1) + (3,) + + def test_coord0(self): + size = np.random.random(3) + grid = np.random.randint(8,32,(3)) + c = grid_filters.coord0_cell(grid+1,size+size/grid) + n = grid_filters.coord0_node(grid,size) + size/grid*.5 + assert np.allclose(c,n) + + @pytest.mark.parametrize('mode',[('cell'),('node')]) + def test_displacement_avg_vanishes(self,mode): + """Ensure that random fluctuations in F do not result in average displacement.""" + size = np.random.random(3) + grid = np.random.randint(8,32,(3)) + F = np.random.random(tuple(grid)+(3,3)) + F += np.eye(3) - np.average(F,axis=(0,1,2)) + assert np.allclose(eval('grid_filters.displacement_avg_{}(size,F)'.format(mode)),0.0) + + @pytest.mark.parametrize('mode',[('cell'),('node')]) + def test_displacement_fluct_vanishes(self,mode): + """Ensure that constant F does not result in fluctuating displacement.""" + size = np.random.random(3) + grid = np.random.randint(8,32,(3)) + F = np.broadcast_to(np.random.random((3,3)), tuple(grid)+(3,3)) + assert np.allclose(eval('grid_filters.displacement_fluct_{}(size,F)'.format(mode)),0.0) From 381c95bd1e6c65600cb1d793942993c703085a9e Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 4 Dec 2019 10:17:55 +0100 Subject: [PATCH 015/223] WIP: regrid functionality --- python/damask/grid_filters.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/python/damask/grid_filters.py b/python/damask/grid_filters.py index 924242c58..455e93aba 100644 --- a/python/damask/grid_filters.py +++ b/python/damask/grid_filters.py @@ -1,3 +1,4 @@ +from scipy import spatial import numpy as np def __ks(size,field,first_order=False): @@ -120,3 +121,18 @@ def node_2_cell(node_data): + np.roll(node_data,1,(0,1)) + np.roll(node_data,1,(1,2)) + np.roll(node_data,1,(2,0)))*0.125 return c[:-1,:-1,:-1] + +def regrid(size,F,new_grid): + """tbd.""" + c = coord0_cell(F.shape[:3][::-1],size) \ + + displacement_avg_cell(size,F) \ + + displacement_fluct_cell(size,F) + + outer = np.dot(np.average(F,axis=(0,1,2)),size) + for d in range(3): + c[np.where(c[:,:,:,d]<0)] += outer[d] + c[np.where(c[:,:,:,d]>outer[d])] -= outer[d] + + tree = spatial.cKDTree(c.reshape((-1,3)),boxsize=outer) + d,i = tree.query(coord0_cell(new_grid,outer)) + return i From e7358746dca1e8ab5f885374488121050b2ab469 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 5 Dec 2019 10:54:35 +0100 Subject: [PATCH 016/223] taking care of prospector complaints variables in 'eval' are hidden --- python/tests/test_grid_filters.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/tests/test_grid_filters.py b/python/tests/test_grid_filters.py index 89b256dcc..e816e0380 100644 --- a/python/tests/test_grid_filters.py +++ b/python/tests/test_grid_filters.py @@ -27,7 +27,7 @@ class TestGridFilters: @pytest.mark.parametrize('mode',[('cell'),('node')]) def test_displacement_avg_vanishes(self,mode): """Ensure that random fluctuations in F do not result in average displacement.""" - size = np.random.random(3) + size = np.random.random(3) # noqa grid = np.random.randint(8,32,(3)) F = np.random.random(tuple(grid)+(3,3)) F += np.eye(3) - np.average(F,axis=(0,1,2)) @@ -36,7 +36,7 @@ class TestGridFilters: @pytest.mark.parametrize('mode',[('cell'),('node')]) def test_displacement_fluct_vanishes(self,mode): """Ensure that constant F does not result in fluctuating displacement.""" - size = np.random.random(3) + size = np.random.random(3) # noqa grid = np.random.randint(8,32,(3)) - F = np.broadcast_to(np.random.random((3,3)), tuple(grid)+(3,3)) + F = np.broadcast_to(np.random.random((3,3)), tuple(grid)+(3,3)) # noqa assert np.allclose(eval('grid_filters.displacement_fluct_{}(size,F)'.format(mode)),0.0) From f475d1a0d021912c080f652a72f9c32e50da8b95 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 5 Dec 2019 13:35:06 +0100 Subject: [PATCH 017/223] adjusted to changes in table class --- processing/post/addCurl.py | 8 ++++---- processing/post/addDisplacement.py | 27 +++++++++++++-------------- processing/post/addDivergence.py | 8 ++++---- processing/post/addGradient.py | 8 ++++---- 4 files changed, 25 insertions(+), 26 deletions(-) diff --git a/processing/post/addCurl.py b/processing/post/addCurl.py index b3dfedaf4..6d4201cf3 100755 --- a/processing/post/addCurl.py +++ b/processing/post/addCurl.py @@ -47,11 +47,11 @@ for name in filenames: grid,size = damask.util.coordGridAndSize(table.get_array(options.pos)) for label in options.labels: - field = table.get_array(label) + field = table.get(label) shape = (3,) if np.prod(field.shape)//np.prod(grid) == 3 else (3,3) # vector or tensor field = field.reshape(np.append(grid[::-1],shape)) - table.add_array('curlFFT({})'.format(label), - damask.grid_filters.curl(size[::-1],field).reshape((-1,np.prod(shape))), - scriptID+' '+' '.join(sys.argv[1:])) + table.add('curlFFT({})'.format(label), + damask.grid_filters.curl(size[::-1],field).reshape((-1,np.prod(shape))), + scriptID+' '+' '.join(sys.argv[1:])) table.to_ASCII(sys.stdout if name is None else name) diff --git a/processing/post/addDisplacement.py b/processing/post/addDisplacement.py index ab25920b5..9c0e29fc5 100755 --- a/processing/post/addDisplacement.py +++ b/processing/post/addDisplacement.py @@ -52,23 +52,22 @@ for name in filenames: table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) grid,size = damask.util.coordGridAndSize(table.get_array(options.pos)) - F = table.get_array(options.f).reshape(np.append(grid[::-1],(3,3))) + F = table.get(options.f).reshape(np.append(grid[::-1],(3,3))) if options.nodal: table = damask.Table(damask.grid_filters.coord0_node(grid[::-1],size[::-1]).reshape((-1,3)), {'pos':(3,)}) - table.add_array('avg({}).{}'.format(options.f,options.pos), - damask.grid_filters.displacement_avg_node(size[::-1],F).reshape((-1,3)), - scriptID+' '+' '.join(sys.argv[1:])) - table.add_array('fluct({}).{}'.format(options.f,options.pos), - damask.grid_filters.displacement_fluct_node(size[::-1],F).reshape((-1,3)), - scriptID+' '+' '.join(sys.argv[1:])) + table.add('avg({}).{}'.format(options.f,options.pos), + damask.grid_filters.displacement_avg_node(size[::-1],F).reshape((-1,3)), + scriptID+' '+' '.join(sys.argv[1:])) + table.add('fluct({}).{}'.format(options.f,options.pos), + damask.grid_filters.displacement_fluct_node(size[::-1],F).reshape((-1,3)), + scriptID+' '+' '.join(sys.argv[1:])) table.to_ASCII(sys.stdout if name is None else os.path.splitext(name)[0]+'_nodal.txt') else: - table.add_array('avg({}).{}'.format(options.f,options.pos), - damask.grid_filters.displacement_avg_cell(size[::-1],F).reshape((-1,3)), - scriptID+' '+' '.join(sys.argv[1:])) - table.add_array('fluct({}).{}'.format(options.f,options.pos), - damask.grid_filters.displacement_fluct_cell(size[::-1],F).reshape((-1,3)), - scriptID+' '+' '.join(sys.argv[1:])) - + table.add('avg({}).{}'.format(options.f,options.pos), + damask.grid_filters.displacement_avg_cell(size[::-1],F).reshape((-1,3)), + scriptID+' '+' '.join(sys.argv[1:])) + table.add('fluct({}).{}'.format(options.f,options.pos), + damask.grid_filters.displacement_fluct_cell(size[::-1],F).reshape((-1,3)), + scriptID+' '+' '.join(sys.argv[1:])) table.to_ASCII(sys.stdout if name is None else name) diff --git a/processing/post/addDivergence.py b/processing/post/addDivergence.py index 7ecaf10f0..8c6f181f7 100755 --- a/processing/post/addDivergence.py +++ b/processing/post/addDivergence.py @@ -47,11 +47,11 @@ for name in filenames: grid,size = damask.util.coordGridAndSize(table.get_array(options.pos)) for label in options.labels: - field = table.get_array(label) + field = table.get(label) shape = (3,) if np.prod(field.shape)//np.prod(grid) == 3 else (3,3) # vector or tensor field = field.reshape(np.append(grid[::-1],shape)) - table.add_array('divFFT({})'.format(label), - damask.grid_filters.divergence(size[::-1],field).reshape((-1,np.prod(shape)//3)), - scriptID+' '+' '.join(sys.argv[1:])) + table.add('divFFT({})'.format(label), + damask.grid_filters.divergence(size[::-1],field).reshape((-1,np.prod(shape)//3)), + scriptID+' '+' '.join(sys.argv[1:])) table.to_ASCII(sys.stdout if name is None else name) diff --git a/processing/post/addGradient.py b/processing/post/addGradient.py index d3081db66..f7f800c24 100755 --- a/processing/post/addGradient.py +++ b/processing/post/addGradient.py @@ -47,11 +47,11 @@ for name in filenames: grid,size = damask.util.coordGridAndSize(table.get_array(options.pos)) for label in options.labels: - field = table.get_array(label) + field = table.get(label) shape = (1,) if np.prod(field.shape)//np.prod(grid) == 1 else (3,) # scalar or vector field = field.reshape(np.append(grid[::-1],shape)) - table.add_array('gradFFT({})'.format(label), - damask.grid_filters.gradient(size[::-1],field).reshape((-1,np.prod(shape)*3)), - scriptID+' '+' '.join(sys.argv[1:])) + table.add('gradFFT({})'.format(label), + damask.grid_filters.gradient(size[::-1],field).reshape((-1,np.prod(shape)*3)), + scriptID+' '+' '.join(sys.argv[1:])) table.to_ASCII(sys.stdout if name is None else name) From 4ddfd82304678f79d345d4535035a422254b2adf Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 5 Dec 2019 18:32:21 +0100 Subject: [PATCH 018/223] better names --- python/damask/grid_filters.py | 26 +++++++++++++------------- python/tests/test_grid_filters.py | 16 ++++++++-------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/python/damask/grid_filters.py b/python/damask/grid_filters.py index 455e93aba..149d52194 100644 --- a/python/damask/grid_filters.py +++ b/python/damask/grid_filters.py @@ -57,7 +57,7 @@ def gradient(size,field): return np.fft.irfftn(gradient,axes=(0,1,2),s=field.shape[:3]) -def coord0_cell(grid,size): +def cell_coord0(grid,size): """Cell center positions (undeformed).""" delta = size/grid*0.5 x, y, z = np.meshgrid(np.linspace(delta[2],size[2]-delta[2],grid[2]), @@ -67,7 +67,7 @@ def coord0_cell(grid,size): return np.concatenate((z[:,:,:,None],y[:,:,:,None],x[:,:,:,None]),axis = 3) -def displacement_fluct_cell(size,F): +def cell_displacement_fluct(size,F): """Cell center displacement field from fluctuation part of the deformation gradient field.""" integrator = 0.5j*size/np.pi @@ -83,13 +83,13 @@ def displacement_fluct_cell(size,F): return np.fft.irfftn(displacement,axes=(0,1,2),s=F.shape[:3]) -def displacement_avg_cell(size,F): +def cell_displacement_avg(size,F): """Cell center displacement field from average part of the deformation gradient field.""" F_avg = np.average(F,axis=(0,1,2)) - return np.einsum('ml,ijkl->ijkm',F_avg-np.eye(3),coord0_cell(F.shape[:3],size)) + return np.einsum('ml,ijkl->ijkm',F_avg-np.eye(3),cell_coord0(F.shape[:3],size)) -def coord0_node(grid,size): +def node_coord0(grid,size): """Nodal positions (undeformed).""" x, y, z = np.meshgrid(np.linspace(0,size[2],1+grid[2]), np.linspace(0,size[1],1+grid[1]), @@ -98,12 +98,12 @@ def coord0_node(grid,size): return np.concatenate((z[:,:,:,None],y[:,:,:,None],x[:,:,:,None]),axis = 3) -def displacement_fluct_node(size,F): - return cell_2_node(displacement_fluct_cell(size,F)) +def node_displacement_fluct(size,F): + return cell_2_node(cell_displacement_fluct(size,F)) -def displacement_avg_node(size,F): +def node_displacement_avg(size,F): F_avg = np.average(F,axis=(0,1,2)) - return np.einsum('ml,ijkl->ijkm',F_avg-np.eye(3),coord0_node(F.shape[:3],size)) + return np.einsum('ml,ijkl->ijkm',F_avg-np.eye(3),node_coord0(F.shape[:3],size)) def cell_2_node(cell_data): @@ -124,9 +124,9 @@ def node_2_cell(node_data): def regrid(size,F,new_grid): """tbd.""" - c = coord0_cell(F.shape[:3][::-1],size) \ - + displacement_avg_cell(size,F) \ - + displacement_fluct_cell(size,F) + c = cell_coord0(F.shape[:3][::-1],size) \ + + cell_displacement_avg(size,F) \ + + cell_displacement_fluct(size,F) outer = np.dot(np.average(F,axis=(0,1,2)),size) for d in range(3): @@ -134,5 +134,5 @@ def regrid(size,F,new_grid): c[np.where(c[:,:,:,d]>outer[d])] -= outer[d] tree = spatial.cKDTree(c.reshape((-1,3)),boxsize=outer) - d,i = tree.query(coord0_cell(new_grid,outer)) + d,i = tree.query(cell_coord0(new_grid,outer)) return i diff --git a/python/tests/test_grid_filters.py b/python/tests/test_grid_filters.py index e816e0380..4dff41542 100644 --- a/python/tests/test_grid_filters.py +++ b/python/tests/test_grid_filters.py @@ -5,23 +5,23 @@ from damask import grid_filters class TestGridFilters: - def test_coord0_cell(self): + def test_cell_coord0(self): size = np.random.random(3) grid = np.random.randint(8,32,(3)) - coord = grid_filters.coord0_cell(grid,size) + coord = grid_filters.cell_coord0(grid,size) assert np.allclose(coord[0,0,0],size/grid*.5) and coord.shape == tuple(grid[::-1]) + (3,) - def test_coord0_node(self): + def test_node_coord0(self): size = np.random.random(3) grid = np.random.randint(8,32,(3)) - coord = grid_filters.coord0_node(grid,size) + coord = grid_filters.node_coord0(grid,size) assert np.allclose(coord[-1,-1,-1],size) and coord.shape == tuple(grid[::-1]+1) + (3,) def test_coord0(self): size = np.random.random(3) grid = np.random.randint(8,32,(3)) - c = grid_filters.coord0_cell(grid+1,size+size/grid) - n = grid_filters.coord0_node(grid,size) + size/grid*.5 + c = grid_filters.cell_coord0(grid+1,size+size/grid) + n = grid_filters.node_coord0(grid,size) + size/grid*.5 assert np.allclose(c,n) @pytest.mark.parametrize('mode',[('cell'),('node')]) @@ -31,7 +31,7 @@ class TestGridFilters: grid = np.random.randint(8,32,(3)) F = np.random.random(tuple(grid)+(3,3)) F += np.eye(3) - np.average(F,axis=(0,1,2)) - assert np.allclose(eval('grid_filters.displacement_avg_{}(size,F)'.format(mode)),0.0) + assert np.allclose(eval('grid_filters.{}_displacement_avg(size,F)'.format(mode)),0.0) @pytest.mark.parametrize('mode',[('cell'),('node')]) def test_displacement_fluct_vanishes(self,mode): @@ -39,4 +39,4 @@ class TestGridFilters: size = np.random.random(3) # noqa grid = np.random.randint(8,32,(3)) F = np.broadcast_to(np.random.random((3,3)), tuple(grid)+(3,3)) # noqa - assert np.allclose(eval('grid_filters.displacement_fluct_{}(size,F)'.format(mode)),0.0) + assert np.allclose(eval('grid_filters.{}_displacement_fluct(size,F)'.format(mode)),0.0) From f2ac87eb2f60e27881217f1586508c7df6904055 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 6 Dec 2019 04:22:18 +0100 Subject: [PATCH 019/223] follow changes in Table class --- processing/post/addCurl.py | 2 +- processing/post/addDisplacement.py | 2 +- processing/post/addDivergence.py | 2 +- processing/post/addGradient.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/processing/post/addCurl.py b/processing/post/addCurl.py index 6d4201cf3..010d24935 100755 --- a/processing/post/addCurl.py +++ b/processing/post/addCurl.py @@ -44,7 +44,7 @@ for name in filenames: damask.util.report(scriptName,name) table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) - grid,size = damask.util.coordGridAndSize(table.get_array(options.pos)) + grid,size = damask.util.coordGridAndSize(table.get(options.pos)) for label in options.labels: field = table.get(label) diff --git a/processing/post/addDisplacement.py b/processing/post/addDisplacement.py index 9c0e29fc5..f85c2a914 100755 --- a/processing/post/addDisplacement.py +++ b/processing/post/addDisplacement.py @@ -50,7 +50,7 @@ for name in filenames: damask.util.report(scriptName,name) table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) - grid,size = damask.util.coordGridAndSize(table.get_array(options.pos)) + grid,size = damask.util.coordGridAndSize(table.get(options.pos)) F = table.get(options.f).reshape(np.append(grid[::-1],(3,3))) if options.nodal: diff --git a/processing/post/addDivergence.py b/processing/post/addDivergence.py index 8c6f181f7..d2d68ede6 100755 --- a/processing/post/addDivergence.py +++ b/processing/post/addDivergence.py @@ -44,7 +44,7 @@ for name in filenames: damask.util.report(scriptName,name) table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) - grid,size = damask.util.coordGridAndSize(table.get_array(options.pos)) + grid,size = damask.util.coordGridAndSize(table.get(options.pos)) for label in options.labels: field = table.get(label) diff --git a/processing/post/addGradient.py b/processing/post/addGradient.py index f7f800c24..ed9397f02 100755 --- a/processing/post/addGradient.py +++ b/processing/post/addGradient.py @@ -44,7 +44,7 @@ for name in filenames: damask.util.report(scriptName,name) table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) - grid,size = damask.util.coordGridAndSize(table.get_array(options.pos)) + grid,size = damask.util.coordGridAndSize(table.get(options.pos)) for label in options.labels: field = table.get(label) From 3aab154cdbef90ae4ba43657b336290a240b9d03 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 7 Dec 2019 10:55:06 +0100 Subject: [PATCH 020/223] include test for addDisplacement --- PRIVATE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PRIVATE b/PRIVATE index 524e86c11..806e1b32a 160000 --- a/PRIVATE +++ b/PRIVATE @@ -1 +1 @@ -Subproject commit 524e86c117d816e3bd873eed7663e258a6f2e139 +Subproject commit 806e1b32a17bf26c987f417116476a295d3936cd From e283acd6069d7f729f38bc835f2c33a92fd7986e Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 7 Dec 2019 20:07:46 +0100 Subject: [PATCH 021/223] correct way of importing for newer python versions --- processing/post/addCalculation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/processing/post/addCalculation.py b/processing/post/addCalculation.py index db0428753..6effc2489 100755 --- a/processing/post/addCalculation.py +++ b/processing/post/addCalculation.py @@ -4,7 +4,7 @@ import os import sys from optparse import OptionParser import re -import collections +from collections.abc import Iterable import math # noqa import scipy # noqa @@ -18,7 +18,7 @@ scriptName = os.path.splitext(os.path.basename(__file__))[0] scriptID = ' '.join([scriptName,damask.version]) def listify(x): - return x if isinstance(x, collections.Iterable) else [x] + return x if isinstance(x, Iterable) else [x] # -------------------------------------------------------------------- From c6c77b64d2b7d5347e90c5d334fec321cb7bee9f Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 7 Dec 2019 20:08:31 +0100 Subject: [PATCH 022/223] following renames in grid_filter --- processing/post/addDisplacement.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/processing/post/addDisplacement.py b/processing/post/addDisplacement.py index f85c2a914..4f0323504 100755 --- a/processing/post/addDisplacement.py +++ b/processing/post/addDisplacement.py @@ -54,20 +54,20 @@ for name in filenames: F = table.get(options.f).reshape(np.append(grid[::-1],(3,3))) if options.nodal: - table = damask.Table(damask.grid_filters.coord0_node(grid[::-1],size[::-1]).reshape((-1,3)), + table = damask.Table(damask.grid_filters.node_coord0(grid[::-1],size[::-1]).reshape((-1,3)), {'pos':(3,)}) table.add('avg({}).{}'.format(options.f,options.pos), - damask.grid_filters.displacement_avg_node(size[::-1],F).reshape((-1,3)), + damask.grid_filters.node_displacement_avg(size[::-1],F).reshape((-1,3)), scriptID+' '+' '.join(sys.argv[1:])) table.add('fluct({}).{}'.format(options.f,options.pos), - damask.grid_filters.displacement_fluct_node(size[::-1],F).reshape((-1,3)), + damask.grid_filters.node_displacement_fluct(size[::-1],F).reshape((-1,3)), scriptID+' '+' '.join(sys.argv[1:])) table.to_ASCII(sys.stdout if name is None else os.path.splitext(name)[0]+'_nodal.txt') else: table.add('avg({}).{}'.format(options.f,options.pos), - damask.grid_filters.displacement_avg_cell(size[::-1],F).reshape((-1,3)), + damask.grid_filters.cell_displacement_avg(size[::-1],F).reshape((-1,3)), scriptID+' '+' '.join(sys.argv[1:])) table.add('fluct({}).{}'.format(options.f,options.pos), - damask.grid_filters.displacement_fluct_cell(size[::-1],F).reshape((-1,3)), + damask.grid_filters.cell_displacement_fluct(size[::-1],F).reshape((-1,3)), scriptID+' '+' '.join(sys.argv[1:])) table.to_ASCII(sys.stdout if name is None else name) From b92cfbbd5bce653ec0bf0a5a0518f1c87bbfb60d Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 7 Dec 2019 20:15:50 +0100 Subject: [PATCH 023/223] do not use bare except --- processing/post/addCalculation.py | 7 ++++--- processing/post/addCumulative.py | 6 +++--- processing/post/addOrientations.py | 7 ++++--- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/processing/post/addCalculation.py b/processing/post/addCalculation.py index 6effc2489..b1eed3c6d 100755 --- a/processing/post/addCalculation.py +++ b/processing/post/addCalculation.py @@ -65,9 +65,10 @@ for i in range(len(options.formulas)): if filenames == []: filenames = [None] for name in filenames: - try: table = damask.ASCIItable(name = name, - buffered = False) - except: continue + try: + table = damask.ASCIItable(name = name, buffered = False) + except IOError: + continue damask.util.report(scriptName,name) # ------------------------------------------ read header ------------------------------------------- diff --git a/processing/post/addCumulative.py b/processing/post/addCumulative.py index b81a9d14f..c94737b94 100755 --- a/processing/post/addCumulative.py +++ b/processing/post/addCumulative.py @@ -41,9 +41,9 @@ if filenames == []: filenames = [None] for name in filenames: try: - table = damask.ASCIItable(name = name, - buffered = False) - except IOError: continue + table = damask.ASCIItable(name = name, buffered = False) + except IOError: + continue damask.util.report(scriptName,name) # ------------------------------------------ read header ------------------------------------------ diff --git a/processing/post/addOrientations.py b/processing/post/addOrientations.py index 31ce6aeb3..2c46ee5ee 100755 --- a/processing/post/addOrientations.py +++ b/processing/post/addOrientations.py @@ -125,9 +125,10 @@ R = damask.Rotation.fromAxisAngle(np.array(options.labrotation),options.degrees, if filenames == []: filenames = [None] for name in filenames: - try: table = damask.ASCIItable(name = name, - buffered = False) - except Exception: continue + try: + table = damask.ASCIItable(name = name, buffered = False) + except IOError: + continue damask.util.report(scriptName,name) # ------------------------------------------ read header ------------------------------------------ From ba69f5a631735075b372618503b0c31eed069a8e Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 7 Dec 2019 22:33:31 +0100 Subject: [PATCH 024/223] polishing --- python/damask/grid_filters.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/python/damask/grid_filters.py b/python/damask/grid_filters.py index 149d52194..006167ccc 100644 --- a/python/damask/grid_filters.py +++ b/python/damask/grid_filters.py @@ -99,9 +99,11 @@ def node_coord0(grid,size): return np.concatenate((z[:,:,:,None],y[:,:,:,None],x[:,:,:,None]),axis = 3) def node_displacement_fluct(size,F): + """Nodal displacement field from fluctuation part of the deformation gradient field.""" return cell_2_node(cell_displacement_fluct(size,F)) def node_displacement_avg(size,F): + """Nodal displacement field from average part of the deformation gradient field.""" F_avg = np.average(F,axis=(0,1,2)) return np.einsum('ml,ijkl->ijkm',F_avg-np.eye(3),node_coord0(F.shape[:3],size)) @@ -134,5 +136,4 @@ def regrid(size,F,new_grid): c[np.where(c[:,:,:,d]>outer[d])] -= outer[d] tree = spatial.cKDTree(c.reshape((-1,3)),boxsize=outer) - d,i = tree.query(cell_coord0(new_grid,outer)) - return i + return tree.query(cell_coord0(new_grid,outer))[1] From 9dc726ff53bf43e8e33f95d263e2e33bdbfe0524 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 8 Dec 2019 09:17:57 +0100 Subject: [PATCH 025/223] polishing --- python/damask/geom.py | 3 +++ python/damask/table.py | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/python/damask/geom.py b/python/damask/geom.py index 32ea2ed89..63ce20115 100644 --- a/python/damask/geom.py +++ b/python/damask/geom.py @@ -205,6 +205,9 @@ class Geom(): else: self.homogenization = homogenization + @property + def grid(self): + return self.get_grid() def get_microstructure(self): """Return the microstructure representation.""" diff --git a/python/damask/table.py b/python/damask/table.py index 6181fdb1f..d063599ab 100644 --- a/python/damask/table.py +++ b/python/damask/table.py @@ -97,7 +97,6 @@ class Table(): @property def labels(self): - """Return the labels of all columns.""" return list(self.shapes.keys()) From 12564557e65e83e6510ed626119d9a023220f0ee Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 8 Dec 2019 09:18:15 +0100 Subject: [PATCH 026/223] using central functionality --- processing/pre/seeds_fromGeom.py | 106 +++++++++---------------------- 1 file changed, 29 insertions(+), 77 deletions(-) diff --git a/processing/pre/seeds_fromGeom.py b/processing/pre/seeds_fromGeom.py index 889ef6146..c543cb878 100755 --- a/processing/pre/seeds_fromGeom.py +++ b/processing/pre/seeds_fromGeom.py @@ -1,9 +1,12 @@ #!/usr/bin/env python3 -# -*- coding: UTF-8 no BOM -*- -import os,sys -import numpy as np +import os +import sys +from io import StringIO from optparse import OptionParser + +import numpy as np + import damask scriptName = os.path.splitext(os.path.basename(__file__))[0] @@ -29,88 +32,37 @@ parser.add_option('-b', action = 'extend', metavar = '', dest = 'blacklist', help = 'blacklist of grain IDs') -parser.add_option('-p', - '--pos', '--seedposition', - dest = 'pos', - type = 'string', metavar = 'string', - help = 'label of coordinates [%default]') parser.set_defaults(whitelist = [], blacklist = [], - pos = 'pos', ) (options,filenames) = parser.parse_args() - -options.whitelist = list(map(int,options.whitelist)) -options.blacklist = list(map(int,options.blacklist)) - -# --- loop over output files ------------------------------------------------------------------------- - if filenames == []: filenames = [None] +options.whitelist = [int(i) for i in options.whitelist] +options.blacklist = [int(i) for i in options.blacklist] + for name in filenames: - try: table = damask.ASCIItable(name = name, - outname = os.path.splitext(name)[0]+'.seeds' if name else name, - buffered = False, - labeled = False) - except: continue - damask.util.report(scriptName,name) + damask.util.report(scriptName,name) + + geom = damask.Geom.from_file(StringIO(''.join(sys.stdin.read())) if name is None else name) + microstructure = geom.get_microstructure().reshape((-1,1),order='F') -# --- interpret header ---------------------------------------------------------------------------- + mask = np.logical_and(np.in1d(microstructure,options.whitelist,invert=False) if options.whitelist else \ + np.full(geom.grid.prod(),True,dtype=bool), + np.in1d(microstructure,options.blacklist,invert=True) if options.blacklist else \ + np.full(geom.grid.prod(),True,dtype=bool)) + + seeds = np.concatenate((damask.grid_filters.cell_coord0(geom.grid,geom.size).reshape((-1,3)), + microstructure), + axis=1)[mask] + + comments = [scriptID + ' ' + ' '.join(sys.argv[1:]), + "grid\ta {}\tb {}\tc {}".format(*geom.grid), + "size\tx {}\ty {}\tz {}".format(*geom.size), + "origin\tx {}\ty {}\tz {}".format(*geom.origin), + "homogenization\t{}".format(geom.homogenization)] - table.head_read() - info,extra_header = table.head_getGeom() - damask.util.report_geom(info) - - errors = [] - if np.any(info['grid'] < 1): errors.append('invalid grid a b c.') - if np.any(info['size'] <= 0.0): errors.append('invalid size x y z.') - if errors != []: - damask.util.croak(errors) - table.close(dismiss = True) - continue - -# --- read data ------------------------------------------------------------------------------------ - - microstructure = table.microstructure_read(info['grid']) # read (linear) microstructure - -# --- generate grid -------------------------------------------------------------------------------- - - x = (0.5 + np.arange(info['grid'][0],dtype=float))/info['grid'][0]*info['size'][0]+info['origin'][0] - y = (0.5 + np.arange(info['grid'][1],dtype=float))/info['grid'][1]*info['size'][1]+info['origin'][1] - z = (0.5 + np.arange(info['grid'][2],dtype=float))/info['grid'][2]*info['size'][2]+info['origin'][2] - - xx = np.tile( x, info['grid'][1]* info['grid'][2]) - yy = np.tile(np.repeat(y,info['grid'][0] ),info['grid'][2]) - zz = np.repeat(z,info['grid'][0]*info['grid'][1]) - - mask = np.logical_and(np.in1d(microstructure,options.whitelist,invert=False) if options.whitelist != [] - else np.full_like(microstructure,True,dtype=bool), - np.in1d(microstructure,options.blacklist,invert=True ) if options.blacklist != [] - else np.full_like(microstructure,True,dtype=bool)) - -# ------------------------------------------ assemble header --------------------------------------- - - table.info_clear() - table.info_append(extra_header+[ - scriptID + ' ' + ' '.join(sys.argv[1:]), - "grid\ta {}\tb {}\tc {}".format(*info['grid']), - "size\tx {}\ty {}\tz {}".format(*info['size']), - "origin\tx {}\ty {}\tz {}".format(*info['origin']), - "homogenization\t{}".format(info['homogenization']), - "microstructures\t{}".format(info['microstructures']), - ]) - table.labels_clear() - table.labels_append(['{dim}_{label}'.format(dim = 1+i,label = options.pos) for i in range(3)]+['microstructure']) - table.head_write() - table.output_flush() - -# --- write seeds information ------------------------------------------------------------ - - table.data = np.squeeze(np.dstack((xx,yy,zz,microstructure)))[mask] - table.data_writeArray() - -# ------------------------------------------ finalize output --------------------------------------- - - table.close() + table = damask.Table(seeds,{'pos':(3,),'microstructure':(1,)},comments) + table.to_ASCII(sys.stdout if name is None else os.path.splitext(name)[0]+'.seeds') From 871ff4c218363649be2ad307e2144698c6cb5d5b Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 8 Dec 2019 09:31:56 +0100 Subject: [PATCH 027/223] use geom class --- processing/pre/mentat_spectralBox.py | 101 ++++++++++----------------- 1 file changed, 35 insertions(+), 66 deletions(-) diff --git a/processing/pre/mentat_spectralBox.py b/processing/pre/mentat_spectralBox.py index 89f4a7a43..f62622a6b 100755 --- a/processing/pre/mentat_spectralBox.py +++ b/processing/pre/mentat_spectralBox.py @@ -1,9 +1,11 @@ #!/usr/bin/env python3 -# -*- coding: UTF-8 no BOM -*- -import os,sys -import numpy as np +import os +import sys from optparse import OptionParser + +import numpy as np + import damask scriptName = os.path.splitext(os.path.basename(__file__))[0] @@ -191,78 +193,45 @@ parser.add_option('-p', '--port', dest = 'port', type = 'int', metavar = 'int', help = 'Mentat connection port [%default]') -parser.add_option('--homogenization', - dest = 'homogenization', - type = 'int', metavar = 'int', - help = 'homogenization index to be used [auto]') -parser.set_defaults(port = None, - homogenization = None, -) +parser.set_defaults(port = None, + ) (options, filenames) = parser.parse_args() -if options.port: - try: - import py_mentat - except: - parser.error('no valid Mentat release found.') +if options.port is not None: + try: + import py_mentat + except ImportError: + parser.error('no valid Mentat release found.') # --- loop over input files ------------------------------------------------------------------------ if filenames == []: filenames = [None] for name in filenames: - try: - table = damask.ASCIItable(name = name, - outname = os.path.splitext(name)[0]+'.proc' if name else name, - buffered = False, labeled = False) - except: continue - damask.util.report(scriptName,name) - -# --- interpret header ---------------------------------------------------------------------------- - - table.head_read() - info,extra_header = table.head_getGeom() - if options.homogenization: info['homogenization'] = options.homogenization + damask.util.report(scriptName,name) - damask.util.croak(['grid a b c: %s'%(' x '.join(map(str,info['grid']))), - 'size x y z: %s'%(' x '.join(map(str,info['size']))), - 'origin x y z: %s'%(' : '.join(map(str,info['origin']))), - 'homogenization: %i'%info['homogenization'], - 'microstructures: %i'%info['microstructures'], - ]) + geom = damask.Geom.from_file(StringIO(''.join(sys.stdin.read())) if name is None else name) + microstructure = geom.get_microstructure().flatten(order='F') - errors = [] - if np.any(info['grid'] < 1): errors.append('invalid grid a b c.') - if np.any(info['size'] <= 0.0): errors.append('invalid size x y z.') - if errors != []: - damask.util.croak(errors) - table.close(dismiss = True) - continue - -# --- read data ------------------------------------------------------------------------------------ - - microstructure = table.microstructure_read(info['grid']).reshape(info['grid'].prod(),order='F') # read microstructure - - cmds = [\ - init(), - mesh(info['grid'],info['size']), - material(), - geometry(), - initial_conditions(info['homogenization'],microstructure), - '*identify_sets', - '*show_model', - '*redraw', - '*draw_automatic', - ] - - outputLocals = {} - if options.port: - py_mentat.py_connect('',options.port) - output(cmds,outputLocals,'Mentat') - py_mentat.py_disconnect() - else: - output(cmds,outputLocals,table.__IO__['out']) # bad hack into internals of table class... - - table.close() + cmds = [\ + init(), + mesh(geom.grid,geom.size), + material(), + geometry(), + initial_conditions(geom.homogenization,microstructure), + '*identify_sets', + '*show_model', + '*redraw', + '*draw_automatic', + ] + + outputLocals = {} + if options.port: + py_mentat.py_connect('',options.port) + output(cmds,outputLocals,'Mentat') + py_mentat.py_disconnect() + else: + with sys.stdout if name is None else open(os.path.splitext(name)[0]+'.proc','w') as f: + output(cmds,outputLocals,f) From 0292e8fcc78ff7c87036885192ed38a7e61758bf Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 8 Dec 2019 11:00:38 +0100 Subject: [PATCH 028/223] preparing transition to Geom and Table classes --- processing/pre/seeds_fromPokes.py | 100 ++++++++++-------------------- 1 file changed, 33 insertions(+), 67 deletions(-) diff --git a/processing/pre/seeds_fromPokes.py b/processing/pre/seeds_fromPokes.py index 08e600ffe..4a6edc94d 100755 --- a/processing/pre/seeds_fromPokes.py +++ b/processing/pre/seeds_fromPokes.py @@ -1,11 +1,13 @@ #!/usr/bin/env python3 -# -*- coding: UTF-8 no BOM -*- -import os,math,sys -import numpy as np -import damask +import os +import sys from optparse import OptionParser +import numpy as np + +import damask + scriptName = os.path.splitext(os.path.basename(__file__))[0] scriptID = ' '.join([scriptName,damask.version]) @@ -35,109 +37,73 @@ parser.add_option('-y', action = 'store_true', dest = 'y', help = 'poke 45 deg along y') -parser.add_option('-p','--position', - dest = 'position', - type = 'string', metavar = 'string', - help = 'column label for coordinates [%default]') parser.set_defaults(x = False, y = False, box = [0.0,1.0,0.0,1.0,0.0,1.0], N = 16, - position = 'pos', ) (options,filenames) = parser.parse_args() +if filenames == []: filenames = [None] options.box = np.array(options.box).reshape(3,2) -# --- loop over output files ------------------------------------------------------------------------- - -if filenames == []: filenames = [None] - for name in filenames: - try: - table = damask.ASCIItable(name = name, - outname = os.path.splitext(name)[-2]+'_poked_{}.seeds'.format(options.N) if name else name, - buffered = False, labeled = False) - except: continue + table = damask.ASCIItable(name = name, + outname = os.path.splitext(name)[-2]+'_poked_{}.seeds'.format(options.N) if name else name, + buffered = False, labeled = False) damask.util.report(scriptName,name) -# --- interpret header ---------------------------------------------------------------------------- table.head_read() info,extra_header = table.head_getGeom() - - damask.util.croak(['grid a b c: %s'%(' x '.join(map(str,info['grid']))), - 'size x y z: %s'%(' x '.join(map(str,info['size']))), - 'origin x y z: %s'%(' : '.join(map(str,info['origin']))), - 'homogenization: %i'%info['homogenization'], - 'microstructures: %i'%info['microstructures'], - ]) - - errors = [] - if np.any(info['grid'] < 1): errors.append('invalid grid a b c.') - if np.any(info['size'] <= 0.0): errors.append('invalid size x y z.') - if errors != []: - damask.util.croak(errors) - table.close(dismiss = True) - continue - -# --- read data ------------------------------------------------------------------------------------ - - microstructure = table.microstructure_read(info['grid']).reshape(info['grid'],order='F') # read microstructure - -# --- do work ------------------------------------------------------------------------------------ - newInfo = { - 'microstructures': 0, - } - offset = (np.amin(options.box, axis=1)*info['grid']/info['size']).astype(int) - box = np.amax(options.box, axis=1) - np.amin(options.box, axis=1) + grid = info['grid'] + size = info['size'] - Nx = int(options.N/math.sqrt(options.N*info['size'][1]*box[1]/info['size'][0]/box[0])) - Ny = int(options.N/math.sqrt(options.N*info['size'][0]*box[0]/info['size'][1]/box[1])) - Nz = int(box[2]*info['grid'][2]) + microstructure = table.microstructure_read(grid).reshape(grid) # read microstructure + + offset =(np.amin(options.box, axis=1)*grid/size).astype(int) + box = np.amax(options.box, axis=1) \ + - np.amin(options.box, axis=1) + + Nx = int(options.N/np.sqrt(options.N*size[1]*box[1]/size[0]/box[0])) + Ny = int(options.N/np.sqrt(options.N*size[0]*box[0]/size[1]/box[1])) + Nz = int(box[2]*grid[2]) damask.util.croak('poking {} x {} x {} in box {} {} {}...'.format(Nx,Ny,Nz,*box)) seeds = np.zeros((Nx*Ny*Nz,4),'d') - grid = np.zeros(3,'i') + g = np.zeros(3,'i') n = 0 for i in range(Nx): for j in range(Ny): - grid[0] = round((i+0.5)*box[0]*info['grid'][0]/Nx-0.5)+offset[0] - grid[1] = round((j+0.5)*box[1]*info['grid'][1]/Ny-0.5)+offset[1] + g[0] = round((i+0.5)*box[0]*grid[0]/Nx-0.5)+offset[0] + g[1] = round((j+0.5)*box[1]*grid[1]/Ny-0.5)+offset[1] for k in range(Nz): - grid[2] = k + offset[2] - grid %= info['grid'] - seeds[n,0:3] = (0.5+grid)/info['grid'] # normalize coordinates to box - seeds[n, 3] = microstructure[grid[0],grid[1],grid[2]] - if options.x: grid[0] += 1 - if options.y: grid[1] += 1 + g[2] = k + offset[2] + g %= grid + seeds[n,0:3] = (g+0.5)/grid # normalize coordinates to box + seeds[n, 3] = microstructure[g[2],g[1],g[0]] + if options.x: g[0] += 1 + if options.y: g[1] += 1 n += 1 - newInfo['microstructures'] = len(np.unique(seeds[:,3])) - -# --- report --------------------------------------------------------------------------------------- - if (newInfo['microstructures'] != info['microstructures']): - damask.util.croak('--> microstructures: %i'%newInfo['microstructures']) - # ------------------------------------------ assemble header --------------------------------------- table.info_clear() table.info_append(extra_header+[ scriptID + ' ' + ' '.join(sys.argv[1:]), "poking\ta {}\tb {}\tc {}".format(Nx,Ny,Nz), - "grid\ta {}\tb {}\tc {}".format(*info['grid']), - "size\tx {}\ty {}\tz {}".format(*info['size']), + "grid\ta {}\tb {}\tc {}".format(*grid), + "size\tx {}\ty {}\tz {}".format(*size), "origin\tx {}\ty {}\tz {}".format(*info['origin']), "homogenization\t{}".format(info['homogenization']), - "microstructures\t{}".format(newInfo['microstructures']), ]) table.labels_clear() - table.labels_append(['{dim}_{label}'.format(dim = 1+i,label = options.position) for i in range(3)]+['microstructure']) + table.labels_append(['{dim}_{label}'.format(dim = 1+i,label = 'pos') for i in range(3)]+['microstructure']) table.head_write() table.output_flush() From f19694f734c401e4076af1294951d2ef140c45d2 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 8 Dec 2019 11:20:47 +0100 Subject: [PATCH 029/223] starting to use central functionality --- processing/pre/seeds_fromPokes.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/processing/pre/seeds_fromPokes.py b/processing/pre/seeds_fromPokes.py index 4a6edc94d..2c359b0aa 100755 --- a/processing/pre/seeds_fromPokes.py +++ b/processing/pre/seeds_fromPokes.py @@ -50,11 +50,12 @@ if filenames == []: filenames = [None] options.box = np.array(options.box).reshape(3,2) for name in filenames: + damask.util.report(scriptName,name) + geom = damask.Geom.from_file(StringIO(''.join(sys.stdin.read())) if name is None else name) + table = damask.ASCIItable(name = name, outname = os.path.splitext(name)[-2]+'_poked_{}.seeds'.format(options.N) if name else name, buffered = False, labeled = False) - damask.util.report(scriptName,name) - table.head_read() info,extra_header = table.head_getGeom() @@ -62,15 +63,13 @@ for name in filenames: grid = info['grid'] size = info['size'] - microstructure = table.microstructure_read(grid).reshape(grid) # read microstructure - - offset =(np.amin(options.box, axis=1)*grid/size).astype(int) + offset =(np.amin(options.box, axis=1)*geom.grid/geom.size).astype(int) box = np.amax(options.box, axis=1) \ - np.amin(options.box, axis=1) - Nx = int(options.N/np.sqrt(options.N*size[1]*box[1]/size[0]/box[0])) - Ny = int(options.N/np.sqrt(options.N*size[0]*box[0]/size[1]/box[1])) - Nz = int(box[2]*grid[2]) + Nx = int(options.N/np.sqrt(options.N*geom.size[1]*box[1]/geom.size[0]/box[0])) + Ny = int(options.N/np.sqrt(options.N*geom.size[0]*box[0]/geom.size[1]/box[1])) + Nz = int(box[2]*geom.grid[2]) damask.util.croak('poking {} x {} x {} in box {} {} {}...'.format(Nx,Ny,Nz,*box)) @@ -86,7 +85,7 @@ for name in filenames: g[2] = k + offset[2] g %= grid seeds[n,0:3] = (g+0.5)/grid # normalize coordinates to box - seeds[n, 3] = microstructure[g[2],g[1],g[0]] + seeds[n, 3] = geom.microstructure[g[0],g[1],g[2]] if options.x: g[0] += 1 if options.y: g[1] += 1 n += 1 @@ -94,12 +93,12 @@ for name in filenames: # ------------------------------------------ assemble header --------------------------------------- table.info_clear() - table.info_append(extra_header+[ + table.info_append(geom.comments+[ scriptID + ' ' + ' '.join(sys.argv[1:]), "poking\ta {}\tb {}\tc {}".format(Nx,Ny,Nz), - "grid\ta {}\tb {}\tc {}".format(*grid), - "size\tx {}\ty {}\tz {}".format(*size), - "origin\tx {}\ty {}\tz {}".format(*info['origin']), + "grid\ta {}\tb {}\tc {}".format(*geom.grid), + "size\tx {}\ty {}\tz {}".format(*geom.size), + "origin\tx {}\ty {}\tz {}".format(*geom.origin), "homogenization\t{}".format(info['homogenization']), ]) table.labels_clear() From 75e93d9f0c96d88fb2b204fcd14f000cd7954cf2 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 8 Dec 2019 11:25:33 +0100 Subject: [PATCH 030/223] relying on central functionality improves readability --- processing/pre/seeds_fromGeom.py | 6 +- processing/pre/seeds_fromPokes.py | 104 ++++++++++++------------------ 2 files changed, 45 insertions(+), 65 deletions(-) diff --git a/processing/pre/seeds_fromGeom.py b/processing/pre/seeds_fromGeom.py index c543cb878..2118f049d 100755 --- a/processing/pre/seeds_fromGeom.py +++ b/processing/pre/seeds_fromGeom.py @@ -58,11 +58,13 @@ for name in filenames: microstructure), axis=1)[mask] - comments = [scriptID + ' ' + ' '.join(sys.argv[1:]), + comments = geom.comments \ + + [scriptID + ' ' + ' '.join(sys.argv[1:]), "grid\ta {}\tb {}\tc {}".format(*geom.grid), "size\tx {}\ty {}\tz {}".format(*geom.size), "origin\tx {}\ty {}\tz {}".format(*geom.origin), "homogenization\t{}".format(geom.homogenization)] table = damask.Table(seeds,{'pos':(3,),'microstructure':(1,)},comments) - table.to_ASCII(sys.stdout if name is None else os.path.splitext(name)[0]+'.seeds') + table.to_ASCII(sys.stdout if name is None else \ + os.path.splitext(name)[0]+'.seeds') diff --git a/processing/pre/seeds_fromPokes.py b/processing/pre/seeds_fromPokes.py index 2c359b0aa..ef7da63bf 100755 --- a/processing/pre/seeds_fromPokes.py +++ b/processing/pre/seeds_fromPokes.py @@ -50,67 +50,45 @@ if filenames == []: filenames = [None] options.box = np.array(options.box).reshape(3,2) for name in filenames: - damask.util.report(scriptName,name) - geom = damask.Geom.from_file(StringIO(''.join(sys.stdin.read())) if name is None else name) - - table = damask.ASCIItable(name = name, - outname = os.path.splitext(name)[-2]+'_poked_{}.seeds'.format(options.N) if name else name, - buffered = False, labeled = False) - - table.head_read() - info,extra_header = table.head_getGeom() - - grid = info['grid'] - size = info['size'] - - offset =(np.amin(options.box, axis=1)*geom.grid/geom.size).astype(int) - box = np.amax(options.box, axis=1) \ - - np.amin(options.box, axis=1) - - Nx = int(options.N/np.sqrt(options.N*geom.size[1]*box[1]/geom.size[0]/box[0])) - Ny = int(options.N/np.sqrt(options.N*geom.size[0]*box[0]/geom.size[1]/box[1])) - Nz = int(box[2]*geom.grid[2]) - - damask.util.croak('poking {} x {} x {} in box {} {} {}...'.format(Nx,Ny,Nz,*box)) - - seeds = np.zeros((Nx*Ny*Nz,4),'d') - g = np.zeros(3,'i') - - n = 0 - for i in range(Nx): - for j in range(Ny): - g[0] = round((i+0.5)*box[0]*grid[0]/Nx-0.5)+offset[0] - g[1] = round((j+0.5)*box[1]*grid[1]/Ny-0.5)+offset[1] - for k in range(Nz): - g[2] = k + offset[2] - g %= grid - seeds[n,0:3] = (g+0.5)/grid # normalize coordinates to box - seeds[n, 3] = geom.microstructure[g[0],g[1],g[2]] - if options.x: g[0] += 1 - if options.y: g[1] += 1 - n += 1 - - -# ------------------------------------------ assemble header --------------------------------------- - table.info_clear() - table.info_append(geom.comments+[ - scriptID + ' ' + ' '.join(sys.argv[1:]), - "poking\ta {}\tb {}\tc {}".format(Nx,Ny,Nz), - "grid\ta {}\tb {}\tc {}".format(*geom.grid), - "size\tx {}\ty {}\tz {}".format(*geom.size), - "origin\tx {}\ty {}\tz {}".format(*geom.origin), - "homogenization\t{}".format(info['homogenization']), - ]) - table.labels_clear() - table.labels_append(['{dim}_{label}'.format(dim = 1+i,label = 'pos') for i in range(3)]+['microstructure']) - table.head_write() - table.output_flush() - -# --- write seeds information ------------------------------------------------------------ - - table.data = seeds - table.data_writeArray() + damask.util.report(scriptName,name) + geom = damask.Geom.from_file(StringIO(''.join(sys.stdin.read())) if name is None else name) -# --- output finalization -------------------------------------------------------------------------- - - table.close() # close ASCII table + offset =(np.amin(options.box, axis=1)*geom.grid/geom.size).astype(int) + box = np.amax(options.box, axis=1) \ + - np.amin(options.box, axis=1) + + Nx = int(options.N/np.sqrt(options.N*geom.size[1]*box[1]/geom.size[0]/box[0])) + Ny = int(options.N/np.sqrt(options.N*geom.size[0]*box[0]/geom.size[1]/box[1])) + Nz = int(box[2]*geom.grid[2]) + + damask.util.croak('poking {} x {} x {} in box {} {} {}...'.format(Nx,Ny,Nz,*box)) + + seeds = np.zeros((Nx*Ny*Nz,4),'d') + g = np.zeros(3,'i') + + n = 0 + for i in range(Nx): + for j in range(Ny): + g[0] = round((i+0.5)*box[0]*geom.grid[0]/Nx-0.5)+offset[0] + g[1] = round((j+0.5)*box[1]*geom.grid[1]/Ny-0.5)+offset[1] + for k in range(Nz): + g[2] = k + offset[2] + g %= geom.grid + seeds[n,0:3] = (g+0.5)/geom.grid # normalize coordinates to box + seeds[n, 3] = geom.microstructure[g[0],g[1],g[2]] + if options.x: g[0] += 1 + if options.y: g[1] += 1 + n += 1 + + + comments = geom.comments \ + + [scriptID + ' ' + ' '.join(sys.argv[1:]), + "poking\ta {}\tb {}\tc {}".format(Nx,Ny,Nz), + "grid\ta {}\tb {}\tc {}".format(*geom.grid), + "size\tx {}\ty {}\tz {}".format(*geom.size), + "origin\tx {}\ty {}\tz {}".format(*geom.origin), + "homogenization\t{}".format(geom.homogenization)] + + table = damask.Table(seeds,{'pos':(3,),'microstructure':(1,)},comments) + table.to_ASCII(sys.stdout if name is None else \ + os.path.splitext(name)[0]+'_poked_{}.seeds'.format(options.N)) From 8d0c4310cf54acf3182258095ad68527de845498 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 8 Dec 2019 17:57:02 +0100 Subject: [PATCH 031/223] improvements to grid generation - handling of origin - inverse functions: calculate grid,size,origin from regular coordinates (cell or node). should replace corresponding functionality in util --- python/damask/grid_filters.py | 66 ++++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 9 deletions(-) diff --git a/python/damask/grid_filters.py b/python/damask/grid_filters.py index 006167ccc..cb593f449 100644 --- a/python/damask/grid_filters.py +++ b/python/damask/grid_filters.py @@ -57,12 +57,13 @@ def gradient(size,field): return np.fft.irfftn(gradient,axes=(0,1,2),s=field.shape[:3]) -def cell_coord0(grid,size): +def cell_coord0(grid,size,origin=np.zeros(3)): """Cell center positions (undeformed).""" - delta = size/grid*0.5 - x, y, z = np.meshgrid(np.linspace(delta[2],size[2]-delta[2],grid[2]), - np.linspace(delta[1],size[1]-delta[1],grid[1]), - np.linspace(delta[0],size[0]-delta[0],grid[0]), + start = origin + size/grid*.5 + end = origin - size/grid*.5 + size + x, y, z = np.meshgrid(np.linspace(start[2],end[2],grid[2]), + np.linspace(start[1],end[1],grid[1]), + np.linspace(start[0],end[0],grid[0]), indexing = 'ij') return np.concatenate((z[:,:,:,None],y[:,:,:,None],x[:,:,:,None]),axis = 3) @@ -88,12 +89,37 @@ def cell_displacement_avg(size,F): F_avg = np.average(F,axis=(0,1,2)) return np.einsum('ml,ijkl->ijkm',F_avg-np.eye(3),cell_coord0(F.shape[:3],size)) +def cell_coord0_2_DNA(coord0,ordered=False): + coords = [np.unique(coord0[:,i]) for i in range(3)] + mincorner = np.array(list(map(min,coords))) + maxcorner = np.array(list(map(max,coords))) + grid = np.array(list(map(len,coords)),'i') + size = grid/np.maximum(grid-1,1) * (maxcorner-mincorner) + delta = size/grid + origin = mincorner - delta*.5 + + if grid.prod() != len(coord0): + raise ValueError('Data count {} does not match grid {}.'.format(len(coord0),grid)) -def node_coord0(grid,size): + start = origin + delta*.5 + end = origin + size -delta*.5 + + if not np.allclose(coords[0],np.linspace(start[0],end[0],grid[0])) and \ + np.allclose(coords[1],np.linspace(start[1],end[1],grid[1])) and \ + np.allclose(coords[2],np.linspace(start[2],end[2],grid[2])): + raise ValueError('Regular grid spacing violated.') + + if ordered: + if not np.allclose(coord0.reshape(tuple(grid[::-1])+(3,)),cell_coord0(grid,size,origin)): + raise ValueError('Input data is not a regular grid.') + return (grid,size,origin) + + +def node_coord0(grid,size,origin=np.zeros(3)): """Nodal positions (undeformed).""" - x, y, z = np.meshgrid(np.linspace(0,size[2],1+grid[2]), - np.linspace(0,size[1],1+grid[1]), - np.linspace(0,size[0],1+grid[0]), + x, y, z = np.meshgrid(np.linspace(origin[2],size[2]+origin[2],1+grid[2]), + np.linspace(origin[1],size[1]+origin[1],1+grid[1]), + np.linspace(origin[0],size[0]+origin[0],1+grid[0]), indexing = 'ij') return np.concatenate((z[:,:,:,None],y[:,:,:,None],x[:,:,:,None]),axis = 3) @@ -124,6 +150,28 @@ def node_2_cell(node_data): return c[:-1,:-1,:-1] +def node_coord0_2_DNA(coord0,ordered=False): + coords = [np.unique(coord0[:,i]) for i in range(3)] + mincorner = np.array(list(map(min,coords))) + maxcorner = np.array(list(map(max,coords))) + grid = np.array(list(map(len,coords)),'i') - 1 + size = maxcorner-mincorner + origin = mincorner + + if (grid+1).prod() != len(coord0): + raise ValueError('Data count {} does not match grid {}.'.format(len(coord0),grid)) + + if not np.allclose(coords[0],np.linspace(mincorner[0],maxcorner[0],grid[0]+1)) and \ + np.allclose(coords[1],np.linspace(mincorner[1],maxcorner[1],grid[1]+1)) and \ + np.allclose(coords[2],np.linspace(mincorner[2],maxcorner[2],grid[2]+1)): + raise ValueError('Regular grid spacing violated.') + + if ordered: + if not np.allclose(coord0.reshape(tuple((grid+1)[::-1])+(3,)),node_coord0(grid,size,origin)): + raise ValueError('Input data is not a regular grid.') + return (grid,size,origin) + + def regrid(size,F,new_grid): """tbd.""" c = cell_coord0(F.shape[:3][::-1],size) \ From 828e82605ed401be736af10cd033b8ef474cd653 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 8 Dec 2019 18:13:20 +0100 Subject: [PATCH 032/223] ensure that data is correctly ordered --- processing/post/addCurl.py | 2 +- processing/post/addDisplacement.py | 2 +- processing/post/addDivergence.py | 2 +- processing/post/addGradient.py | 2 +- python/damask/grid_filters.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/processing/post/addCurl.py b/processing/post/addCurl.py index 010d24935..5db47ce04 100755 --- a/processing/post/addCurl.py +++ b/processing/post/addCurl.py @@ -44,7 +44,7 @@ for name in filenames: damask.util.report(scriptName,name) table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) - grid,size = damask.util.coordGridAndSize(table.get(options.pos)) + grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.get(options.pos),True) for label in options.labels: field = table.get(label) diff --git a/processing/post/addDisplacement.py b/processing/post/addDisplacement.py index 4f0323504..735e6d875 100755 --- a/processing/post/addDisplacement.py +++ b/processing/post/addDisplacement.py @@ -50,7 +50,7 @@ for name in filenames: damask.util.report(scriptName,name) table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) - grid,size = damask.util.coordGridAndSize(table.get(options.pos)) + grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.get(options.pos),True) F = table.get(options.f).reshape(np.append(grid[::-1],(3,3))) if options.nodal: diff --git a/processing/post/addDivergence.py b/processing/post/addDivergence.py index d2d68ede6..ea4b134a4 100755 --- a/processing/post/addDivergence.py +++ b/processing/post/addDivergence.py @@ -44,7 +44,7 @@ for name in filenames: damask.util.report(scriptName,name) table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) - grid,size = damask.util.coordGridAndSize(table.get(options.pos)) + grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.get(options.pos),True) for label in options.labels: field = table.get(label) diff --git a/processing/post/addGradient.py b/processing/post/addGradient.py index ed9397f02..75109a3e0 100755 --- a/processing/post/addGradient.py +++ b/processing/post/addGradient.py @@ -44,7 +44,7 @@ for name in filenames: damask.util.report(scriptName,name) table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) - grid,size = damask.util.coordGridAndSize(table.get(options.pos)) + grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.get(options.pos),True) for label in options.labels: field = table.get(label) diff --git a/python/damask/grid_filters.py b/python/damask/grid_filters.py index cb593f449..96ae226dd 100644 --- a/python/damask/grid_filters.py +++ b/python/damask/grid_filters.py @@ -102,7 +102,7 @@ def cell_coord0_2_DNA(coord0,ordered=False): raise ValueError('Data count {} does not match grid {}.'.format(len(coord0),grid)) start = origin + delta*.5 - end = origin + size -delta*.5 + end = origin - delta*.5 + size if not np.allclose(coords[0],np.linspace(start[0],end[0],grid[0])) and \ np.allclose(coords[1],np.linspace(start[1],end[1],grid[1])) and \ From b0689340d060567d21b8b2b4ca0cdb3fc7bd0e60 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 8 Dec 2019 18:20:28 +0100 Subject: [PATCH 033/223] updated tests --- PRIVATE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PRIVATE b/PRIVATE index 806e1b32a..b580fd4e9 160000 --- a/PRIVATE +++ b/PRIVATE @@ -1 +1 @@ -Subproject commit 806e1b32a17bf26c987f417116476a295d3936cd +Subproject commit b580fd4e992c2ed457f16d65bcba2e6d099dc29d From 3d09a82f41898d5deb96a8a35967a7f253726f2a Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 8 Dec 2019 18:21:16 +0100 Subject: [PATCH 034/223] fixing prospector complaints --- processing/pre/mentat_spectralBox.py | 3 +-- processing/pre/seeds_fromPokes.py | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/processing/pre/mentat_spectralBox.py b/processing/pre/mentat_spectralBox.py index f62622a6b..a61bef57a 100755 --- a/processing/pre/mentat_spectralBox.py +++ b/processing/pre/mentat_spectralBox.py @@ -2,10 +2,9 @@ import os import sys +from io import StringIO from optparse import OptionParser -import numpy as np - import damask scriptName = os.path.splitext(os.path.basename(__file__))[0] diff --git a/processing/pre/seeds_fromPokes.py b/processing/pre/seeds_fromPokes.py index ef7da63bf..1436841d0 100755 --- a/processing/pre/seeds_fromPokes.py +++ b/processing/pre/seeds_fromPokes.py @@ -2,6 +2,7 @@ import os import sys +from io import StringIO from optparse import OptionParser import numpy as np From 7dc128ad123afb3b2d9e4b608e7855d5d724903b Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 8 Dec 2019 18:33:43 +0100 Subject: [PATCH 035/223] polishing --- processing/post/addCurl.py | 2 +- processing/post/addDisplacement.py | 2 +- processing/post/addDivergence.py | 2 +- processing/post/addGradient.py | 2 +- python/damask/grid_filters.py | 36 ++++++++++++++++++++++++------ 5 files changed, 33 insertions(+), 11 deletions(-) diff --git a/processing/post/addCurl.py b/processing/post/addCurl.py index 5db47ce04..25639dc7c 100755 --- a/processing/post/addCurl.py +++ b/processing/post/addCurl.py @@ -44,7 +44,7 @@ for name in filenames: damask.util.report(scriptName,name) table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) - grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.get(options.pos),True) + grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.get(options.pos)) for label in options.labels: field = table.get(label) diff --git a/processing/post/addDisplacement.py b/processing/post/addDisplacement.py index 735e6d875..59630a6c6 100755 --- a/processing/post/addDisplacement.py +++ b/processing/post/addDisplacement.py @@ -50,7 +50,7 @@ for name in filenames: damask.util.report(scriptName,name) table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) - grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.get(options.pos),True) + grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.get(options.pos)) F = table.get(options.f).reshape(np.append(grid[::-1],(3,3))) if options.nodal: diff --git a/processing/post/addDivergence.py b/processing/post/addDivergence.py index ea4b134a4..585ebb5a5 100755 --- a/processing/post/addDivergence.py +++ b/processing/post/addDivergence.py @@ -44,7 +44,7 @@ for name in filenames: damask.util.report(scriptName,name) table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) - grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.get(options.pos),True) + grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.get(options.pos)) for label in options.labels: field = table.get(label) diff --git a/processing/post/addGradient.py b/processing/post/addGradient.py index 75109a3e0..54b80ed26 100755 --- a/processing/post/addGradient.py +++ b/processing/post/addGradient.py @@ -44,7 +44,7 @@ for name in filenames: damask.util.report(scriptName,name) table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) - grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.get(options.pos),True) + grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.get(options.pos)) for label in options.labels: field = table.get(label) diff --git a/python/damask/grid_filters.py b/python/damask/grid_filters.py index 96ae226dd..b064d9a2d 100644 --- a/python/damask/grid_filters.py +++ b/python/damask/grid_filters.py @@ -89,7 +89,18 @@ def cell_displacement_avg(size,F): F_avg = np.average(F,axis=(0,1,2)) return np.einsum('ml,ijkl->ijkm',F_avg-np.eye(3),cell_coord0(F.shape[:3],size)) -def cell_coord0_2_DNA(coord0,ordered=False): +def cell_coord0_2_DNA(coord0,ordered=True): + """ + Return grid 'DNA', i.e. grid, size, and origin from array of cell positions. + + Parameters + ---------- + coord0 : numpy.ndarray + array of undeformed cell coordinates + ordered : bool, optional + expect coord0 data to be ordered (x fast, z slow). + + """ coords = [np.unique(coord0[:,i]) for i in range(3)] mincorner = np.array(list(map(min,coords))) maxcorner = np.array(list(map(max,coords))) @@ -109,9 +120,9 @@ def cell_coord0_2_DNA(coord0,ordered=False): np.allclose(coords[2],np.linspace(start[2],end[2],grid[2])): raise ValueError('Regular grid spacing violated.') - if ordered: - if not np.allclose(coord0.reshape(tuple(grid[::-1])+(3,)),cell_coord0(grid,size,origin)): - raise ValueError('Input data is not a regular grid.') + if ordered and not np.allclose(coord0.reshape(tuple(grid[::-1])+(3,)),cell_coord0(grid,size,origin)): + raise ValueError('Input data is not a regular grid.') + return (grid,size,origin) @@ -151,6 +162,17 @@ def node_2_cell(node_data): return c[:-1,:-1,:-1] def node_coord0_2_DNA(coord0,ordered=False): + """ + Return grid 'DNA', i.e. grid, size, and origin from array of nodal positions. + + Parameters + ---------- + coord0 : numpy.ndarray + array of undeformed nodal coordinates + ordered : bool, optional + expect coord0 data to be ordered (x fast, z slow). + + """ coords = [np.unique(coord0[:,i]) for i in range(3)] mincorner = np.array(list(map(min,coords))) maxcorner = np.array(list(map(max,coords))) @@ -166,9 +188,9 @@ def node_coord0_2_DNA(coord0,ordered=False): np.allclose(coords[2],np.linspace(mincorner[2],maxcorner[2],grid[2]+1)): raise ValueError('Regular grid spacing violated.') - if ordered: - if not np.allclose(coord0.reshape(tuple((grid+1)[::-1])+(3,)),node_coord0(grid,size,origin)): - raise ValueError('Input data is not a regular grid.') + if ordered and not np.allclose(coord0.reshape(tuple((grid+1)[::-1])+(3,)),node_coord0(grid,size,origin)): + raise ValueError('Input data is not a regular grid.') + return (grid,size,origin) From 4e2e7d02f6c10ad80a2ce9f4fa49257e723ceec8 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 8 Dec 2019 18:54:41 +0100 Subject: [PATCH 036/223] more sensible interface --- python/damask/grid_filters.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/python/damask/grid_filters.py b/python/damask/grid_filters.py index b064d9a2d..9e1170ec4 100644 --- a/python/damask/grid_filters.py +++ b/python/damask/grid_filters.py @@ -1,9 +1,8 @@ from scipy import spatial import numpy as np -def __ks(size,field,first_order=False): +def __ks(size,grid,first_order=False): """Get wave numbers operator.""" - grid = np.array(np.shape(field)[:3]) k_sk = np.where(np.arange(grid[0])>grid[0]//2,np.arange(grid[0])-grid[0],np.arange(grid[0]))/size[0] if grid[0]%2 == 0 and first_order: k_sk[grid[0]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) @@ -20,7 +19,7 @@ def __ks(size,field,first_order=False): def curl(size,field): """Calculate curl of a vector or tensor field in Fourier space.""" n = np.prod(field.shape[3:]) - k_s = __ks(size,field,True) + k_s = __ks(size,field.shape[:3],True) e = np.zeros((3, 3, 3)) e[0, 1, 2] = e[1, 2, 0] = e[2, 0, 1] = +1.0 # Levi-Civita symbol @@ -36,7 +35,7 @@ def curl(size,field): def divergence(size,field): """Calculate divergence of a vector or tensor field in Fourier space.""" n = np.prod(field.shape[3:]) - k_s = __ks(size,field,True) + k_s = __ks(size,field.shape[:3],True) field_fourier = np.fft.rfftn(field,axes=(0,1,2)) divergence = (np.einsum('ijkl,ijkl ->ijk', k_s,field_fourier)*2.0j*np.pi if n == 3 else # vector, 3 -> 1 @@ -48,7 +47,7 @@ def divergence(size,field): def gradient(size,field): """Calculate gradient of a vector or scalar field in Fourier space.""" n = np.prod(field.shape[3:]) - k_s = __ks(size,field,True) + k_s = __ks(size,field.shape[:3],True) field_fourier = np.fft.rfftn(field,axes=(0,1,2)) gradient = (np.einsum('ijkl,ijkm->ijkm', field_fourier,k_s)*2.0j*np.pi if n == 1 else # scalar, 1 -> 3 @@ -72,7 +71,7 @@ def cell_displacement_fluct(size,F): """Cell center displacement field from fluctuation part of the deformation gradient field.""" integrator = 0.5j*size/np.pi - k_s = __ks(size,F,False) + k_s = __ks(size,F.shape[:3],False) k_s_squared = np.einsum('...l,...l',k_s,k_s) k_s_squared[0,0,0] = 1.0 From 21431295fbf8231e3d014f87a13deb7ac1b46a0d Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 8 Dec 2019 20:20:13 +0100 Subject: [PATCH 037/223] documenting --- python/damask/grid_filters.py | 113 ++++++++++++++++++++++++++++++---- 1 file changed, 102 insertions(+), 11 deletions(-) diff --git a/python/damask/grid_filters.py b/python/damask/grid_filters.py index 9e1170ec4..a1e1ff06d 100644 --- a/python/damask/grid_filters.py +++ b/python/damask/grid_filters.py @@ -2,8 +2,15 @@ from scipy import spatial import numpy as np def __ks(size,grid,first_order=False): - """Get wave numbers operator.""" + """ + Get wave numbers operator. + Parameters + ---------- + size : numpy.ndarray + physical size of the periodic field. + + """ k_sk = np.where(np.arange(grid[0])>grid[0]//2,np.arange(grid[0])-grid[0],np.arange(grid[0]))/size[0] if grid[0]%2 == 0 and first_order: k_sk[grid[0]//2] = 0 # Nyquist freq=0 for even grid (Johnson, MIT, 2011) @@ -17,7 +24,15 @@ def __ks(size,grid,first_order=False): def curl(size,field): - """Calculate curl of a vector or tensor field in Fourier space.""" + """ + Calculate curl of a vector or tensor field in Fourier space. + + Parameters + ---------- + size : numpy.ndarray + physical size of the periodic field. + + """ n = np.prod(field.shape[3:]) k_s = __ks(size,field.shape[:3],True) @@ -33,7 +48,15 @@ def curl(size,field): def divergence(size,field): - """Calculate divergence of a vector or tensor field in Fourier space.""" + """ + Calculate divergence of a vector or tensor field in Fourier space. + + Parameters + ---------- + size : numpy.ndarray + physical size of the periodic field. + + """ n = np.prod(field.shape[3:]) k_s = __ks(size,field.shape[:3],True) @@ -45,7 +68,15 @@ def divergence(size,field): def gradient(size,field): - """Calculate gradient of a vector or scalar field in Fourier space.""" + """ + Calculate gradient of a vector or scalar field in Fourier space. + + Parameters + ---------- + size : numpy.ndarray + physical size of the periodic field. + + """ n = np.prod(field.shape[3:]) k_s = __ks(size,field.shape[:3],True) @@ -57,7 +88,17 @@ def gradient(size,field): def cell_coord0(grid,size,origin=np.zeros(3)): - """Cell center positions (undeformed).""" + """ + Cell center positions (undeformed). + + Parameters + ---------- + grid : numpy.ndarray + number of grid points. + size : numpy.ndarray + physical size of the periodic field. + + """ start = origin + size/grid*.5 end = origin - size/grid*.5 + size x, y, z = np.meshgrid(np.linspace(start[2],end[2],grid[2]), @@ -68,7 +109,17 @@ def cell_coord0(grid,size,origin=np.zeros(3)): return np.concatenate((z[:,:,:,None],y[:,:,:,None],x[:,:,:,None]),axis = 3) def cell_displacement_fluct(size,F): - """Cell center displacement field from fluctuation part of the deformation gradient field.""" + """ + Cell center displacement field from fluctuation part of the deformation gradient field. + + Parameters + ---------- + size : numpy.ndarray + physical size of the periodic field. + F : numpy.ndarray + deformation gradient field. + + """ integrator = 0.5j*size/np.pi k_s = __ks(size,F.shape[:3],False) @@ -84,7 +135,17 @@ def cell_displacement_fluct(size,F): return np.fft.irfftn(displacement,axes=(0,1,2),s=F.shape[:3]) def cell_displacement_avg(size,F): - """Cell center displacement field from average part of the deformation gradient field.""" + """ + Cell center displacement field from average part of the deformation gradient field. + + Parameters + ---------- + size : numpy.ndarray + physical size of the periodic field. + F : numpy.ndarray + deformation gradient field. + + """ F_avg = np.average(F,axis=(0,1,2)) return np.einsum('ml,ijkl->ijkm',F_avg-np.eye(3),cell_coord0(F.shape[:3],size)) @@ -95,7 +156,7 @@ def cell_coord0_2_DNA(coord0,ordered=True): Parameters ---------- coord0 : numpy.ndarray - array of undeformed cell coordinates + array of undeformed cell coordinates. ordered : bool, optional expect coord0 data to be ordered (x fast, z slow). @@ -126,7 +187,17 @@ def cell_coord0_2_DNA(coord0,ordered=True): def node_coord0(grid,size,origin=np.zeros(3)): - """Nodal positions (undeformed).""" + """ + Nodal positions (undeformed). + + Parameters + ---------- + grid : numpy.ndarray + number of grid points. + size : numpy.ndarray + physical size of the periodic field. + + """ x, y, z = np.meshgrid(np.linspace(origin[2],size[2]+origin[2],1+grid[2]), np.linspace(origin[1],size[1]+origin[1],1+grid[1]), np.linspace(origin[0],size[0]+origin[0],1+grid[0]), @@ -135,11 +206,31 @@ def node_coord0(grid,size,origin=np.zeros(3)): return np.concatenate((z[:,:,:,None],y[:,:,:,None],x[:,:,:,None]),axis = 3) def node_displacement_fluct(size,F): - """Nodal displacement field from fluctuation part of the deformation gradient field.""" + """ + Nodal displacement field from fluctuation part of the deformation gradient field. + + Parameters + ---------- + size : numpy.ndarray + physical size of the periodic field. + F : numpy.ndarray + deformation gradient field. + + """ return cell_2_node(cell_displacement_fluct(size,F)) def node_displacement_avg(size,F): - """Nodal displacement field from average part of the deformation gradient field.""" + """ + Nodal displacement field from average part of the deformation gradient field. + + Parameters + ---------- + size : numpy.ndarray + physical size of the periodic field. + F : numpy.ndarray + deformation gradient field. + + """ F_avg = np.average(F,axis=(0,1,2)) return np.einsum('ml,ijkl->ijkm',F_avg-np.eye(3),node_coord0(F.shape[:3],size)) From b56864552f92ce306b23fcbf26a839bdf3c547d8 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 8 Dec 2019 21:05:34 +0100 Subject: [PATCH 038/223] testing forward <-> backward conversion --- python/tests/test_grid_filters.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/python/tests/test_grid_filters.py b/python/tests/test_grid_filters.py index 4dff41542..b23fad549 100644 --- a/python/tests/test_grid_filters.py +++ b/python/tests/test_grid_filters.py @@ -24,6 +24,18 @@ class TestGridFilters: n = grid_filters.node_coord0(grid,size) + size/grid*.5 assert np.allclose(c,n) + @pytest.mark.parametrize('mode',[('cell'),('node')]) + def test_grid_DNA(self,mode): + """Ensure that xx_coord0_2_DNA is the inverse of xx_coord0.""" + grid = np.random.randint(8,32,(3)) + size = np.random.random(3) + origin = np.random.random(3) + + coord0 = eval('grid_filters.{}_coord0(grid,size,origin)'.format(mode)) # noqa + _grid,_size,_origin = eval('grid_filters.{}_coord0_2_DNA(coord0.reshape((-1,3)))'.format(mode)) + assert np.allclose(grid,_grid) and np.allclose(size,_size) and np.allclose(origin,_origin) + + @pytest.mark.parametrize('mode',[('cell'),('node')]) def test_displacement_avg_vanishes(self,mode): """Ensure that random fluctuations in F do not result in average displacement.""" From 53cb59fc474134d9aeedde79f0fe041267ef70c6 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 8 Dec 2019 21:29:26 +0100 Subject: [PATCH 039/223] use pytest instead of hand-written test class --- .gitlab-ci.yml | 7 ------- python/damask/orientation.py | 2 +- python/tests/test_Rotation.py | 18 ++++++++++++------ 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6e82561c5..d4c6923d9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -115,13 +115,6 @@ Pytest: - release ################################################################################################### -OrientationRelationship: - stage: preprocessing - script: OrientationRelationship/test.py - except: - - master - - release - Pre_SeedGeneration: stage: preprocessing script: PreProcessing_SeedGeneration/test.py diff --git a/python/damask/orientation.py b/python/damask/orientation.py index fc601b608..65318f169 100644 --- a/python/damask/orientation.py +++ b/python/damask/orientation.py @@ -1025,7 +1025,7 @@ class Lattice: https://doi.org/10.1016/j.actamat.2004.11.021 """ - models={'KS':self.KS, 'GT':self.GT, "GT'":self.GTprime, + models={'KS':self.KS, 'GT':self.GT, 'GT_prime':self.GTprime, 'NW':self.NW, 'Pitsch': self.Pitsch, 'Bain':self.Bain} try: relationship = models[model] diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index 72956c013..79d674bcd 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -2,6 +2,7 @@ import pytest import numpy as np from damask import Rotation +from damask import Orientation n = 1000 @@ -18,38 +19,43 @@ class TestRotation: assert np.allclose(rot.asQuaternion(), Rotation.fromEulers(rot.asEulers()).asQuaternion()) - def test_AxisAngle(self,default): for rot in default: assert np.allclose(rot.asEulers(), Rotation.fromAxisAngle(rot.asAxisAngle()).asEulers()) - def test_Matrix(self,default): for rot in default: assert np.allclose(rot.asAxisAngle(), Rotation.fromMatrix(rot.asMatrix()).asAxisAngle()) - def test_Rodriques(self,default): for rot in default: assert np.allclose(rot.asMatrix(), Rotation.fromRodrigues(rot.asRodrigues()).asMatrix()) - def test_Homochoric(self,default): for rot in default: assert np.allclose(rot.asRodrigues(), Rotation.fromHomochoric(rot.asHomochoric()).asRodrigues()) - def test_Cubochoric(self,default): for rot in default: assert np.allclose(rot.asHomochoric(), Rotation.fromCubochoric(rot.asCubochoric()).asHomochoric()) - def test_Quaternion(self,default): for rot in default: assert np.allclose(rot.asCubochoric(), Rotation.fromQuaternion(rot.asQuaternion()).asCubochoric()) + + + @pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch']) + @pytest.mark.parametrize('lattice',['fcc','bcc']) + def test_relationship_forward_backward(self,model,lattice): + ori = Orientation(Rotation.fromRandom(),lattice) + for i,r in enumerate(ori.relatedOrientations(model)): + ori2 = r.relatedOrientations(model)[i] + misorientation = ori.rotation.misorientation(ori2.rotation) + assert misorientation.asAxisAngle(degrees=True)[3]<1.0e-5 + From fa9ee6b11679f1ecd91027573c0167359bef6062 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 8 Dec 2019 21:48:32 +0100 Subject: [PATCH 040/223] using reference results for orientation relationships --- python/tests/reference/Rotation/bcc_Bain.txt | 5 ++++ python/tests/reference/Rotation/bcc_GT.txt | 26 +++++++++++++++++++ .../tests/reference/Rotation/bcc_GT_prime.txt | 26 +++++++++++++++++++ python/tests/reference/Rotation/bcc_KS.txt | 26 +++++++++++++++++++ python/tests/reference/Rotation/bcc_NW.txt | 14 ++++++++++ .../tests/reference/Rotation/bcc_Pitsch.txt | 14 ++++++++++ python/tests/reference/Rotation/fcc_Bain.txt | 5 ++++ python/tests/reference/Rotation/fcc_GT.txt | 26 +++++++++++++++++++ .../tests/reference/Rotation/fcc_GT_prime.txt | 26 +++++++++++++++++++ python/tests/reference/Rotation/fcc_KS.txt | 26 +++++++++++++++++++ python/tests/reference/Rotation/fcc_NW.txt | 14 ++++++++++ .../tests/reference/Rotation/fcc_Pitsch.txt | 14 ++++++++++ python/tests/test_Rotation.py | 18 +++++++++++++ 13 files changed, 240 insertions(+) create mode 100644 python/tests/reference/Rotation/bcc_Bain.txt create mode 100644 python/tests/reference/Rotation/bcc_GT.txt create mode 100644 python/tests/reference/Rotation/bcc_GT_prime.txt create mode 100644 python/tests/reference/Rotation/bcc_KS.txt create mode 100644 python/tests/reference/Rotation/bcc_NW.txt create mode 100644 python/tests/reference/Rotation/bcc_Pitsch.txt create mode 100644 python/tests/reference/Rotation/fcc_Bain.txt create mode 100644 python/tests/reference/Rotation/fcc_GT.txt create mode 100644 python/tests/reference/Rotation/fcc_GT_prime.txt create mode 100644 python/tests/reference/Rotation/fcc_KS.txt create mode 100644 python/tests/reference/Rotation/fcc_NW.txt create mode 100644 python/tests/reference/Rotation/fcc_Pitsch.txt diff --git a/python/tests/reference/Rotation/bcc_Bain.txt b/python/tests/reference/Rotation/bcc_Bain.txt new file mode 100644 index 000000000..e1675cd32 --- /dev/null +++ b/python/tests/reference/Rotation/bcc_Bain.txt @@ -0,0 +1,5 @@ +1 header +1_Eulers 2_Eulers 3_Eulers +0.0 45.00000000000001 0.0 +90.0 45.00000000000001 270.0 +45.00000000000001 0.0 0.0 diff --git a/python/tests/reference/Rotation/bcc_GT.txt b/python/tests/reference/Rotation/bcc_GT.txt new file mode 100644 index 000000000..b4bfd5d8f --- /dev/null +++ b/python/tests/reference/Rotation/bcc_GT.txt @@ -0,0 +1,26 @@ +1 header +1_Eulers 2_Eulers 3_Eulers +283.60440567265294 9.976439066337804 33.24637065555936 +167.8261034151001 43.397849654402556 183.40022280897963 +262.1156357053931 43.82007387041961 104.07478363123654 +103.604405672653 9.976439066337804 213.24637065555936 +347.8261034151001 43.39784965440255 3.400222808979685 +82.11563570539313 43.82007387041961 284.0747836312365 +76.39559432734703 9.976439066337806 326.75362934444064 +192.17389658489986 43.397849654402556 176.59977719102034 +97.88436429460687 43.82007387041961 255.92521636876344 +256.395594327347 9.976439066337804 146.75362934444064 +12.173896584899929 43.39784965440254 356.59977719102034 +277.8843642946069 43.82007387041961 75.92521636876346 +102.17389658489992 43.39784965440254 266.59977719102034 +346.395594327347 9.976439066337804 56.75362934444064 +7.884364294606862 43.82007387041961 345.9252163687635 +282.17389658489986 43.39784965440254 86.59977719102032 +166.39559432734703 9.976439066337804 236.75362934444058 +187.88436429460683 43.82007387041961 165.92521636876344 +257.8261034151001 43.39784965440255 93.40022280897969 +13.604405672652977 9.976439066337804 303.24637065555936 +352.1156357053931 43.82007387041961 14.074783631236542 +77.82610341510008 43.397849654402556 273.4002228089796 +193.60440567265297 9.976439066337806 123.24637065555939 +153.65751914298576 65.6559553854118 185.90444335627936 diff --git a/python/tests/reference/Rotation/bcc_GT_prime.txt b/python/tests/reference/Rotation/bcc_GT_prime.txt new file mode 100644 index 000000000..73c3bb6b3 --- /dev/null +++ b/python/tests/reference/Rotation/bcc_GT_prime.txt @@ -0,0 +1,26 @@ +1 header +1_Eulers 2_Eulers 3_Eulers +303.24637065555936 9.976439066337804 13.604405672652977 +165.92521636876344 43.82007387041961 187.88436429460683 +266.59977719102034 43.39784965440254 102.17389658489992 +123.24637065555939 9.976439066337804 193.604405672653 +345.9252163687635 43.82007387041961 7.884364294606862 +86.59977719102032 43.39784965440254 282.17389658489986 +56.75362934444064 9.976439066337804 346.395594327347 +194.07478363123653 43.82007387041961 172.11563570539317 +93.40022280897969 43.39784965440255 257.8261034151001 +236.75362934444058 9.976439066337804 166.39559432734697 +14.074783631236542 43.82007387041961 352.1156357053931 +273.4002228089796 43.397849654402556 77.82610341510008 +104.07478363123654 43.82007387041961 262.1156357053931 +326.75362934444064 9.976439066337806 76.39559432734703 +3.400222808979685 43.39784965440255 347.8261034151001 +284.0747836312365 43.82007387041961 82.11563570539313 +146.75362934444064 9.976439066337804 256.395594327347 +183.40022280897963 43.397849654402556 167.8261034151001 +255.92521636876344 43.82007387041961 97.88436429460687 +33.24637065555936 9.976439066337804 283.60440567265294 +26.291675350407385 65.60048732963618 354.34378938496315 +75.92521636876346 43.82007387041961 277.8843642946069 +213.24637065555936 9.976439066337804 103.604405672653 +176.59977719102034 43.397849654402556 192.17389658489986 diff --git a/python/tests/reference/Rotation/bcc_KS.txt b/python/tests/reference/Rotation/bcc_KS.txt new file mode 100644 index 000000000..0add535bb --- /dev/null +++ b/python/tests/reference/Rotation/bcc_KS.txt @@ -0,0 +1,26 @@ +1 header +1_Eulers 2_Eulers 3_Eulers +335.7965716606702 10.528779365509317 65.79657166067024 +228.77270547567446 80.40593177313953 85.64260312151849 +131.22729452432552 80.40593177313954 4.357396878481506 +24.20342833932977 10.52877936550932 24.20342833932976 +221.95489158457983 85.70366403943002 80.37863910890589 +138.04510841542015 85.70366403943004 9.621360891094124 +131.22729452432552 80.40593177313953 94.35739687848151 +24.203428339329765 10.52877936550932 114.20342833932976 +221.95489158457983 85.70366403943004 170.37863910890587 +138.04510841542015 85.70366403943004 99.62136089109411 +335.7965716606702 10.52877936550932 155.79657166067025 +228.77270547567448 80.40593177313954 175.6426031215185 +335.7965716606702 10.52877936550932 335.7965716606702 +228.77270547567448 80.40593177313954 355.6426031215185 +131.2272945243255 80.40593177313954 274.35739687848144 +24.203428339329747 10.52877936550932 294.2034283393298 +221.95489158457985 85.70366403943004 350.3786391089059 +138.04510841542015 85.70366403943004 279.6213608910941 +41.95489158457986 94.29633596056998 9.621360891094133 +318.04510841542015 94.29633596056996 80.37863910890589 +155.79657166067025 169.4712206344907 24.203428339329754 +48.77270547567448 99.59406822686046 4.357396878481504 +311.2272945243255 99.59406822686046 85.64260312151852 +204.20342833932975 169.4712206344907 65.79657166067024 diff --git a/python/tests/reference/Rotation/bcc_NW.txt b/python/tests/reference/Rotation/bcc_NW.txt new file mode 100644 index 000000000..8d3dfbe84 --- /dev/null +++ b/python/tests/reference/Rotation/bcc_NW.txt @@ -0,0 +1,14 @@ +1 header +1_Eulers 2_Eulers 3_Eulers +225.41555594321144 83.13253115922213 83.08266205989301 +134.58444405678856 83.13253115922211 6.917337940107012 +4.702125169424418e-15 9.735610317245317 45.0 +134.58444405678856 83.13253115922213 276.91733794010696 +225.4155559432114 83.13253115922213 353.082662059893 +0.0 9.735610317245317 315.0 +134.58444405678858 83.13253115922213 96.91733794010702 +225.41555594321142 83.13253115922213 173.082662059893 +0.0 9.735610317245317 135.0 +260.40196970123213 45.81931182053556 283.6387072794765 +260.40196970123213 45.81931182053556 283.6387072794765 +180.0 99.73561031724535 225.0 diff --git a/python/tests/reference/Rotation/bcc_Pitsch.txt b/python/tests/reference/Rotation/bcc_Pitsch.txt new file mode 100644 index 000000000..dc9926ca1 --- /dev/null +++ b/python/tests/reference/Rotation/bcc_Pitsch.txt @@ -0,0 +1,14 @@ +1 header +1_Eulers 2_Eulers 3_Eulers +6.9173379401070045 83.13253115922213 44.58444405678856 +45.0 89.99999999999999 279.7356103172453 +166.36129272052352 45.819311820535574 279.59803029876787 +83.08266205989301 83.13253115922213 225.41555594321144 +256.3612927205235 45.819311820535574 189.59803029876787 +315.0 90.0 9.735610317245369 +186.917337940107 83.13253115922213 224.58444405678856 +315.0 90.0 80.26438968275463 +13.638707279476478 45.81931182053557 260.40196970123213 +263.082662059893 83.13253115922213 45.415555943211444 +103.63870727947646 45.819311820535574 170.40196970123213 +224.99999999999997 90.0 170.26438968275465 diff --git a/python/tests/reference/Rotation/fcc_Bain.txt b/python/tests/reference/Rotation/fcc_Bain.txt new file mode 100644 index 000000000..2c12eecc9 --- /dev/null +++ b/python/tests/reference/Rotation/fcc_Bain.txt @@ -0,0 +1,5 @@ +1 header +1_Eulers 2_Eulers 3_Eulers +180.0 45.00000000000001 180.0 +270.0 45.00000000000001 90.0 +315.0 0.0 0.0 diff --git a/python/tests/reference/Rotation/fcc_GT.txt b/python/tests/reference/Rotation/fcc_GT.txt new file mode 100644 index 000000000..e695d0d6f --- /dev/null +++ b/python/tests/reference/Rotation/fcc_GT.txt @@ -0,0 +1,26 @@ +1 header +1_Eulers 2_Eulers 3_Eulers +146.75362934444064 9.976439066337804 256.395594327347 +356.59977719102034 43.39784965440254 12.173896584899929 +75.92521636876346 43.82007387041961 277.8843642946069 +326.75362934444064 9.976439066337806 76.39559432734703 +176.59977719102034 43.397849654402556 192.17389658489986 +255.92521636876344 43.82007387041961 97.88436429460687 +213.24637065555936 9.976439066337804 103.604405672653 +3.400222808979685 43.39784965440255 347.8261034151001 +284.0747836312365 43.82007387041961 82.11563570539313 +33.24637065555936 9.976439066337804 283.60440567265294 +183.40022280897963 43.397849654402556 167.8261034151001 +104.07478363123654 43.82007387041961 262.1156357053931 +273.4002228089796 43.397849654402556 77.82610341510008 +123.24637065555939 9.976439066337806 193.60440567265297 +194.07478363123653 43.82007387041961 172.11563570539317 +93.40022280897969 43.39784965440255 257.8261034151001 +303.24637065555936 9.976439066337804 13.604405672652977 +14.074783631236542 43.82007387041961 352.1156357053931 +86.59977719102032 43.39784965440254 282.17389658489986 +236.75362934444058 9.976439066337804 166.39559432734703 +165.92521636876344 43.82007387041961 187.88436429460683 +266.59977719102034 43.39784965440254 102.17389658489992 +56.75362934444064 9.976439066337804 346.395594327347 +354.0955566437206 65.6559553854118 26.342480857014277 diff --git a/python/tests/reference/Rotation/fcc_GT_prime.txt b/python/tests/reference/Rotation/fcc_GT_prime.txt new file mode 100644 index 000000000..7df7cb6c1 --- /dev/null +++ b/python/tests/reference/Rotation/fcc_GT_prime.txt @@ -0,0 +1,26 @@ +1 header +1_Eulers 2_Eulers 3_Eulers +166.39559432734697 9.976439066337804 236.75362934444058 +352.1156357053931 43.82007387041961 14.074783631236542 +77.82610341510008 43.397849654402556 273.4002228089796 +346.395594327347 9.976439066337804 56.75362934444064 +172.11563570539317 43.82007387041961 194.07478363123653 +257.8261034151001 43.39784965440255 93.40022280897969 +193.604405672653 9.976439066337804 123.24637065555939 +7.884364294606862 43.82007387041961 345.9252163687635 +282.17389658489986 43.39784965440254 86.59977719102032 +13.604405672652977 9.976439066337804 303.24637065555936 +187.88436429460683 43.82007387041961 165.92521636876344 +102.17389658489992 43.39784965440254 266.59977719102034 +277.8843642946069 43.82007387041961 75.92521636876346 +103.604405672653 9.976439066337804 213.24637065555936 +192.17389658489986 43.397849654402556 176.59977719102034 +97.88436429460687 43.82007387041961 255.92521636876344 +283.60440567265294 9.976439066337804 33.24637065555936 +12.173896584899929 43.39784965440254 356.59977719102034 +82.11563570539313 43.82007387041961 284.0747836312365 +256.395594327347 9.976439066337804 146.75362934444064 +185.65621061503683 65.60048732963617 153.70832464959264 +262.1156357053931 43.82007387041961 104.07478363123654 +76.39559432734703 9.976439066337806 326.75362934444064 +347.8261034151001 43.39784965440255 3.400222808979685 diff --git a/python/tests/reference/Rotation/fcc_KS.txt b/python/tests/reference/Rotation/fcc_KS.txt new file mode 100644 index 000000000..3a6e30358 --- /dev/null +++ b/python/tests/reference/Rotation/fcc_KS.txt @@ -0,0 +1,26 @@ +1 header +1_Eulers 2_Eulers 3_Eulers +114.20342833932975 10.52877936550932 204.20342833932972 +94.3573968784815 80.40593177313954 311.22729452432543 +175.6426031215185 80.40593177313954 48.77270547567447 +155.79657166067025 10.52877936550932 155.79657166067025 +99.62136089109411 85.70366403943004 318.04510841542015 +170.37863910890587 85.70366403943002 41.954891584579855 +85.64260312151852 80.40593177313954 48.77270547567448 +65.79657166067024 10.52877936550932 155.79657166067025 +9.621360891094124 85.70366403943004 318.04510841542015 +80.37863910890587 85.70366403943004 41.95489158457987 +24.203428339329758 10.52877936550932 204.20342833932975 +4.357396878481486 80.40593177313954 311.2272945243255 +204.20342833932972 10.52877936550932 204.20342833932972 +184.35739687848147 80.40593177313954 311.2272945243255 +265.64260312151845 80.40593177313953 48.77270547567449 +245.79657166067025 10.528779365509317 155.79657166067025 +189.62136089109413 85.70366403943004 318.04510841542015 +260.3786391089059 85.70366403943002 41.954891584579855 +170.37863910890587 94.29633596056996 138.04510841542015 +99.62136089109411 94.29633596056998 221.95489158457983 +155.79657166067025 169.4712206344907 24.203428339329754 +175.64260312151848 99.59406822686046 131.22729452432552 +94.35739687848151 99.59406822686046 228.77270547567446 +114.20342833932975 169.4712206344907 335.7965716606702 diff --git a/python/tests/reference/Rotation/fcc_NW.txt b/python/tests/reference/Rotation/fcc_NW.txt new file mode 100644 index 000000000..bf3631db0 --- /dev/null +++ b/python/tests/reference/Rotation/fcc_NW.txt @@ -0,0 +1,14 @@ +1 header +1_Eulers 2_Eulers 3_Eulers +96.91733794010702 83.13253115922213 314.5844440567886 +173.082662059893 83.13253115922211 45.41555594321143 +135.0 9.735610317245317 180.0 +263.082662059893 83.13253115922213 45.415555943211444 +186.91733794010702 83.13253115922211 314.5844440567886 +224.99999999999997 9.735610317245317 180.0 +83.082662059893 83.13253115922213 45.415555943211444 +6.917337940106983 83.13253115922211 314.5844440567886 +45.0 9.73561031724532 180.0 +256.36129272052347 45.81931182053556 279.59803029876775 +256.36129272052347 45.81931182053556 279.59803029876775 +315.0 99.73561031724536 0.0 diff --git a/python/tests/reference/Rotation/fcc_Pitsch.txt b/python/tests/reference/Rotation/fcc_Pitsch.txt new file mode 100644 index 000000000..a2369f0a4 --- /dev/null +++ b/python/tests/reference/Rotation/fcc_Pitsch.txt @@ -0,0 +1,14 @@ +1 header +1_Eulers 2_Eulers 3_Eulers +135.41555594321144 83.13253115922213 173.082662059893 +260.26438968275465 90.0 135.0 +260.40196970123213 45.81931182053557 13.638707279476478 +314.5844440567886 83.13253115922213 96.91733794010702 +350.40196970123213 45.81931182053557 283.6387072794765 +170.26438968275465 90.0 224.99999999999997 +315.4155559432114 83.13253115922213 353.08266205989304 +99.73561031724536 90.0 225.0 +279.59803029876787 45.819311820535574 166.36129272052352 +134.58444405678856 83.13253115922213 276.91733794010696 +9.598030298767851 45.819311820535574 76.36129272052355 +9.735610317245369 90.0 315.0 diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index 79d674bcd..a4dc7c61d 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -1,6 +1,9 @@ +import os + import pytest import numpy as np +import damask from damask import Rotation from damask import Orientation @@ -11,6 +14,11 @@ def default(): """A set of n random rotations.""" return [Rotation.fromRandom() for r in range(n)] +@pytest.fixture +def reference_dir(reference_dir_base): + """Directory containing reference results.""" + return os.path.join(reference_dir_base,'Rotation') + class TestRotation: @@ -59,3 +67,13 @@ class TestRotation: misorientation = ori.rotation.misorientation(ori2.rotation) assert misorientation.asAxisAngle(degrees=True)[3]<1.0e-5 + @pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch']) + @pytest.mark.parametrize('lattice',['fcc','bcc']) + def test_relationship_reference(self,update,reference_dir,model,lattice): + reference = os.path.join(reference_dir,'{}_{}.txt'.format(lattice,model)) + ori = Orientation(Rotation(),lattice) + eu = np.array([o.rotation.asEulers(degrees=True) for o in ori.relatedOrientations(model)]) + if update: + table = damask.Table(eu,{'Eulers':(3,)}) + table.to_ASCII(reference) + assert np.allclose(eu,damask.Table.from_ASCII(reference).get('Eulers')) From 5a9173ccf446b80dec3fdc755b8e035a77a347b4 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 8 Dec 2019 21:57:07 +0100 Subject: [PATCH 041/223] might simplify plotting in MTEX --- python/tests/reference/Rotation/bcc_Bain.txt | 8 +-- python/tests/reference/Rotation/bcc_GT.txt | 50 +++++++++---------- .../tests/reference/Rotation/bcc_GT_prime.txt | 50 +++++++++---------- python/tests/reference/Rotation/bcc_KS.txt | 50 +++++++++---------- python/tests/reference/Rotation/bcc_NW.txt | 26 +++++----- .../tests/reference/Rotation/bcc_Pitsch.txt | 26 +++++----- python/tests/reference/Rotation/fcc_Bain.txt | 8 +-- python/tests/reference/Rotation/fcc_GT.txt | 50 +++++++++---------- .../tests/reference/Rotation/fcc_GT_prime.txt | 50 +++++++++---------- python/tests/reference/Rotation/fcc_KS.txt | 50 +++++++++---------- python/tests/reference/Rotation/fcc_NW.txt | 26 +++++----- .../tests/reference/Rotation/fcc_Pitsch.txt | 26 +++++----- python/tests/test_Rotation.py | 2 + 13 files changed, 212 insertions(+), 210 deletions(-) diff --git a/python/tests/reference/Rotation/bcc_Bain.txt b/python/tests/reference/Rotation/bcc_Bain.txt index e1675cd32..e0bc4f6c7 100644 --- a/python/tests/reference/Rotation/bcc_Bain.txt +++ b/python/tests/reference/Rotation/bcc_Bain.txt @@ -1,5 +1,5 @@ 1 header -1_Eulers 2_Eulers 3_Eulers -0.0 45.00000000000001 0.0 -90.0 45.00000000000001 270.0 -45.00000000000001 0.0 0.0 +1_Eulers 2_Eulers 3_Eulers 1_pos 2_pos +0.0 45.00000000000001 0.0 1 1 +90.0 45.00000000000001 270.0 1 2 +45.00000000000001 0.0 0.0 1 3 diff --git a/python/tests/reference/Rotation/bcc_GT.txt b/python/tests/reference/Rotation/bcc_GT.txt index b4bfd5d8f..d1fe2e1c8 100644 --- a/python/tests/reference/Rotation/bcc_GT.txt +++ b/python/tests/reference/Rotation/bcc_GT.txt @@ -1,26 +1,26 @@ 1 header -1_Eulers 2_Eulers 3_Eulers -283.60440567265294 9.976439066337804 33.24637065555936 -167.8261034151001 43.397849654402556 183.40022280897963 -262.1156357053931 43.82007387041961 104.07478363123654 -103.604405672653 9.976439066337804 213.24637065555936 -347.8261034151001 43.39784965440255 3.400222808979685 -82.11563570539313 43.82007387041961 284.0747836312365 -76.39559432734703 9.976439066337806 326.75362934444064 -192.17389658489986 43.397849654402556 176.59977719102034 -97.88436429460687 43.82007387041961 255.92521636876344 -256.395594327347 9.976439066337804 146.75362934444064 -12.173896584899929 43.39784965440254 356.59977719102034 -277.8843642946069 43.82007387041961 75.92521636876346 -102.17389658489992 43.39784965440254 266.59977719102034 -346.395594327347 9.976439066337804 56.75362934444064 -7.884364294606862 43.82007387041961 345.9252163687635 -282.17389658489986 43.39784965440254 86.59977719102032 -166.39559432734703 9.976439066337804 236.75362934444058 -187.88436429460683 43.82007387041961 165.92521636876344 -257.8261034151001 43.39784965440255 93.40022280897969 -13.604405672652977 9.976439066337804 303.24637065555936 -352.1156357053931 43.82007387041961 14.074783631236542 -77.82610341510008 43.397849654402556 273.4002228089796 -193.60440567265297 9.976439066337806 123.24637065555939 -153.65751914298576 65.6559553854118 185.90444335627936 +1_Eulers 2_Eulers 3_Eulers 1_pos 2_pos +283.60440567265294 9.976439066337804 33.24637065555936 1 1 +167.8261034151001 43.397849654402556 183.40022280897963 1 2 +262.1156357053931 43.82007387041961 104.07478363123654 1 3 +103.604405672653 9.976439066337804 213.24637065555936 1 4 +347.8261034151001 43.39784965440255 3.400222808979685 1 5 +82.11563570539313 43.82007387041961 284.0747836312365 1 6 +76.39559432734703 9.976439066337806 326.75362934444064 1 7 +192.17389658489986 43.397849654402556 176.59977719102034 1 8 +97.88436429460687 43.82007387041961 255.92521636876344 1 9 +256.395594327347 9.976439066337804 146.75362934444064 1 10 +12.173896584899929 43.39784965440254 356.59977719102034 1 11 +277.8843642946069 43.82007387041961 75.92521636876346 1 12 +102.17389658489992 43.39784965440254 266.59977719102034 1 13 +346.395594327347 9.976439066337804 56.75362934444064 1 14 +7.884364294606862 43.82007387041961 345.9252163687635 1 15 +282.17389658489986 43.39784965440254 86.59977719102032 1 16 +166.39559432734703 9.976439066337804 236.75362934444058 1 17 +187.88436429460683 43.82007387041961 165.92521636876344 1 18 +257.8261034151001 43.39784965440255 93.40022280897969 1 19 +13.604405672652977 9.976439066337804 303.24637065555936 1 20 +352.1156357053931 43.82007387041961 14.074783631236542 1 21 +77.82610341510008 43.397849654402556 273.4002228089796 1 22 +193.60440567265297 9.976439066337806 123.24637065555939 1 23 +153.65751914298576 65.6559553854118 185.90444335627936 1 24 diff --git a/python/tests/reference/Rotation/bcc_GT_prime.txt b/python/tests/reference/Rotation/bcc_GT_prime.txt index 73c3bb6b3..42f32bcbb 100644 --- a/python/tests/reference/Rotation/bcc_GT_prime.txt +++ b/python/tests/reference/Rotation/bcc_GT_prime.txt @@ -1,26 +1,26 @@ 1 header -1_Eulers 2_Eulers 3_Eulers -303.24637065555936 9.976439066337804 13.604405672652977 -165.92521636876344 43.82007387041961 187.88436429460683 -266.59977719102034 43.39784965440254 102.17389658489992 -123.24637065555939 9.976439066337804 193.604405672653 -345.9252163687635 43.82007387041961 7.884364294606862 -86.59977719102032 43.39784965440254 282.17389658489986 -56.75362934444064 9.976439066337804 346.395594327347 -194.07478363123653 43.82007387041961 172.11563570539317 -93.40022280897969 43.39784965440255 257.8261034151001 -236.75362934444058 9.976439066337804 166.39559432734697 -14.074783631236542 43.82007387041961 352.1156357053931 -273.4002228089796 43.397849654402556 77.82610341510008 -104.07478363123654 43.82007387041961 262.1156357053931 -326.75362934444064 9.976439066337806 76.39559432734703 -3.400222808979685 43.39784965440255 347.8261034151001 -284.0747836312365 43.82007387041961 82.11563570539313 -146.75362934444064 9.976439066337804 256.395594327347 -183.40022280897963 43.397849654402556 167.8261034151001 -255.92521636876344 43.82007387041961 97.88436429460687 -33.24637065555936 9.976439066337804 283.60440567265294 -26.291675350407385 65.60048732963618 354.34378938496315 -75.92521636876346 43.82007387041961 277.8843642946069 -213.24637065555936 9.976439066337804 103.604405672653 -176.59977719102034 43.397849654402556 192.17389658489986 +1_Eulers 2_Eulers 3_Eulers 1_pos 2_pos +303.24637065555936 9.976439066337804 13.604405672652977 1 1 +165.92521636876344 43.82007387041961 187.88436429460683 1 2 +266.59977719102034 43.39784965440254 102.17389658489992 1 3 +123.24637065555939 9.976439066337804 193.604405672653 1 4 +345.9252163687635 43.82007387041961 7.884364294606862 1 5 +86.59977719102032 43.39784965440254 282.17389658489986 1 6 +56.75362934444064 9.976439066337804 346.395594327347 1 7 +194.07478363123653 43.82007387041961 172.11563570539317 1 8 +93.40022280897969 43.39784965440255 257.8261034151001 1 9 +236.75362934444058 9.976439066337804 166.39559432734697 1 10 +14.074783631236542 43.82007387041961 352.1156357053931 1 11 +273.4002228089796 43.397849654402556 77.82610341510008 1 12 +104.07478363123654 43.82007387041961 262.1156357053931 1 13 +326.75362934444064 9.976439066337806 76.39559432734703 1 14 +3.400222808979685 43.39784965440255 347.8261034151001 1 15 +284.0747836312365 43.82007387041961 82.11563570539313 1 16 +146.75362934444064 9.976439066337804 256.395594327347 1 17 +183.40022280897963 43.397849654402556 167.8261034151001 1 18 +255.92521636876344 43.82007387041961 97.88436429460687 1 19 +33.24637065555936 9.976439066337804 283.60440567265294 1 20 +26.291675350407385 65.60048732963618 354.34378938496315 1 21 +75.92521636876346 43.82007387041961 277.8843642946069 1 22 +213.24637065555936 9.976439066337804 103.604405672653 1 23 +176.59977719102034 43.397849654402556 192.17389658489986 1 24 diff --git a/python/tests/reference/Rotation/bcc_KS.txt b/python/tests/reference/Rotation/bcc_KS.txt index 0add535bb..34b393358 100644 --- a/python/tests/reference/Rotation/bcc_KS.txt +++ b/python/tests/reference/Rotation/bcc_KS.txt @@ -1,26 +1,26 @@ 1 header -1_Eulers 2_Eulers 3_Eulers -335.7965716606702 10.528779365509317 65.79657166067024 -228.77270547567446 80.40593177313953 85.64260312151849 -131.22729452432552 80.40593177313954 4.357396878481506 -24.20342833932977 10.52877936550932 24.20342833932976 -221.95489158457983 85.70366403943002 80.37863910890589 -138.04510841542015 85.70366403943004 9.621360891094124 -131.22729452432552 80.40593177313953 94.35739687848151 -24.203428339329765 10.52877936550932 114.20342833932976 -221.95489158457983 85.70366403943004 170.37863910890587 -138.04510841542015 85.70366403943004 99.62136089109411 -335.7965716606702 10.52877936550932 155.79657166067025 -228.77270547567448 80.40593177313954 175.6426031215185 -335.7965716606702 10.52877936550932 335.7965716606702 -228.77270547567448 80.40593177313954 355.6426031215185 -131.2272945243255 80.40593177313954 274.35739687848144 -24.203428339329747 10.52877936550932 294.2034283393298 -221.95489158457985 85.70366403943004 350.3786391089059 -138.04510841542015 85.70366403943004 279.6213608910941 -41.95489158457986 94.29633596056998 9.621360891094133 -318.04510841542015 94.29633596056996 80.37863910890589 -155.79657166067025 169.4712206344907 24.203428339329754 -48.77270547567448 99.59406822686046 4.357396878481504 -311.2272945243255 99.59406822686046 85.64260312151852 -204.20342833932975 169.4712206344907 65.79657166067024 +1_Eulers 2_Eulers 3_Eulers 1_pos 2_pos +335.7965716606702 10.528779365509317 65.79657166067024 1 1 +228.77270547567446 80.40593177313953 85.64260312151849 1 2 +131.22729452432552 80.40593177313954 4.357396878481506 1 3 +24.20342833932977 10.52877936550932 24.20342833932976 1 4 +221.95489158457983 85.70366403943002 80.37863910890589 1 5 +138.04510841542015 85.70366403943004 9.621360891094124 1 6 +131.22729452432552 80.40593177313953 94.35739687848151 1 7 +24.203428339329765 10.52877936550932 114.20342833932976 1 8 +221.95489158457983 85.70366403943004 170.37863910890587 1 9 +138.04510841542015 85.70366403943004 99.62136089109411 1 10 +335.7965716606702 10.52877936550932 155.79657166067025 1 11 +228.77270547567448 80.40593177313954 175.6426031215185 1 12 +335.7965716606702 10.52877936550932 335.7965716606702 1 13 +228.77270547567448 80.40593177313954 355.6426031215185 1 14 +131.2272945243255 80.40593177313954 274.35739687848144 1 15 +24.203428339329747 10.52877936550932 294.2034283393298 1 16 +221.95489158457985 85.70366403943004 350.3786391089059 1 17 +138.04510841542015 85.70366403943004 279.6213608910941 1 18 +41.95489158457986 94.29633596056998 9.621360891094133 1 19 +318.04510841542015 94.29633596056996 80.37863910890589 1 20 +155.79657166067025 169.4712206344907 24.203428339329754 1 21 +48.77270547567448 99.59406822686046 4.357396878481504 1 22 +311.2272945243255 99.59406822686046 85.64260312151852 1 23 +204.20342833932975 169.4712206344907 65.79657166067024 1 24 diff --git a/python/tests/reference/Rotation/bcc_NW.txt b/python/tests/reference/Rotation/bcc_NW.txt index 8d3dfbe84..76e7c6182 100644 --- a/python/tests/reference/Rotation/bcc_NW.txt +++ b/python/tests/reference/Rotation/bcc_NW.txt @@ -1,14 +1,14 @@ 1 header -1_Eulers 2_Eulers 3_Eulers -225.41555594321144 83.13253115922213 83.08266205989301 -134.58444405678856 83.13253115922211 6.917337940107012 -4.702125169424418e-15 9.735610317245317 45.0 -134.58444405678856 83.13253115922213 276.91733794010696 -225.4155559432114 83.13253115922213 353.082662059893 -0.0 9.735610317245317 315.0 -134.58444405678858 83.13253115922213 96.91733794010702 -225.41555594321142 83.13253115922213 173.082662059893 -0.0 9.735610317245317 135.0 -260.40196970123213 45.81931182053556 283.6387072794765 -260.40196970123213 45.81931182053556 283.6387072794765 -180.0 99.73561031724535 225.0 +1_Eulers 2_Eulers 3_Eulers 1_pos 2_pos +225.41555594321144 83.13253115922213 83.08266205989301 1 1 +134.58444405678856 83.13253115922211 6.917337940107012 1 2 +4.702125169424418e-15 9.735610317245317 45.0 1 3 +134.58444405678856 83.13253115922213 276.91733794010696 1 4 +225.4155559432114 83.13253115922213 353.082662059893 1 5 +0.0 9.735610317245317 315.0 1 6 +134.58444405678858 83.13253115922213 96.91733794010702 1 7 +225.41555594321142 83.13253115922213 173.082662059893 1 8 +0.0 9.735610317245317 135.0 1 9 +260.40196970123213 45.81931182053556 283.6387072794765 1 10 +260.40196970123213 45.81931182053556 283.6387072794765 1 11 +180.0 99.73561031724535 225.0 1 12 diff --git a/python/tests/reference/Rotation/bcc_Pitsch.txt b/python/tests/reference/Rotation/bcc_Pitsch.txt index dc9926ca1..ef28bbb4d 100644 --- a/python/tests/reference/Rotation/bcc_Pitsch.txt +++ b/python/tests/reference/Rotation/bcc_Pitsch.txt @@ -1,14 +1,14 @@ 1 header -1_Eulers 2_Eulers 3_Eulers -6.9173379401070045 83.13253115922213 44.58444405678856 -45.0 89.99999999999999 279.7356103172453 -166.36129272052352 45.819311820535574 279.59803029876787 -83.08266205989301 83.13253115922213 225.41555594321144 -256.3612927205235 45.819311820535574 189.59803029876787 -315.0 90.0 9.735610317245369 -186.917337940107 83.13253115922213 224.58444405678856 -315.0 90.0 80.26438968275463 -13.638707279476478 45.81931182053557 260.40196970123213 -263.082662059893 83.13253115922213 45.415555943211444 -103.63870727947646 45.819311820535574 170.40196970123213 -224.99999999999997 90.0 170.26438968275465 +1_Eulers 2_Eulers 3_Eulers 1_pos 2_pos +6.9173379401070045 83.13253115922213 44.58444405678856 1 1 +45.0 89.99999999999999 279.7356103172453 1 2 +166.36129272052352 45.819311820535574 279.59803029876787 1 3 +83.08266205989301 83.13253115922213 225.41555594321144 1 4 +256.3612927205235 45.819311820535574 189.59803029876787 1 5 +315.0 90.0 9.735610317245369 1 6 +186.917337940107 83.13253115922213 224.58444405678856 1 7 +315.0 90.0 80.26438968275463 1 8 +13.638707279476478 45.81931182053557 260.40196970123213 1 9 +263.082662059893 83.13253115922213 45.415555943211444 1 10 +103.63870727947646 45.819311820535574 170.40196970123213 1 11 +224.99999999999997 90.0 170.26438968275465 1 12 diff --git a/python/tests/reference/Rotation/fcc_Bain.txt b/python/tests/reference/Rotation/fcc_Bain.txt index 2c12eecc9..876cf3888 100644 --- a/python/tests/reference/Rotation/fcc_Bain.txt +++ b/python/tests/reference/Rotation/fcc_Bain.txt @@ -1,5 +1,5 @@ 1 header -1_Eulers 2_Eulers 3_Eulers -180.0 45.00000000000001 180.0 -270.0 45.00000000000001 90.0 -315.0 0.0 0.0 +1_Eulers 2_Eulers 3_Eulers 1_pos 2_pos +180.0 45.00000000000001 180.0 1 1 +270.0 45.00000000000001 90.0 1 2 +315.0 0.0 0.0 1 3 diff --git a/python/tests/reference/Rotation/fcc_GT.txt b/python/tests/reference/Rotation/fcc_GT.txt index e695d0d6f..b91a80c46 100644 --- a/python/tests/reference/Rotation/fcc_GT.txt +++ b/python/tests/reference/Rotation/fcc_GT.txt @@ -1,26 +1,26 @@ 1 header -1_Eulers 2_Eulers 3_Eulers -146.75362934444064 9.976439066337804 256.395594327347 -356.59977719102034 43.39784965440254 12.173896584899929 -75.92521636876346 43.82007387041961 277.8843642946069 -326.75362934444064 9.976439066337806 76.39559432734703 -176.59977719102034 43.397849654402556 192.17389658489986 -255.92521636876344 43.82007387041961 97.88436429460687 -213.24637065555936 9.976439066337804 103.604405672653 -3.400222808979685 43.39784965440255 347.8261034151001 -284.0747836312365 43.82007387041961 82.11563570539313 -33.24637065555936 9.976439066337804 283.60440567265294 -183.40022280897963 43.397849654402556 167.8261034151001 -104.07478363123654 43.82007387041961 262.1156357053931 -273.4002228089796 43.397849654402556 77.82610341510008 -123.24637065555939 9.976439066337806 193.60440567265297 -194.07478363123653 43.82007387041961 172.11563570539317 -93.40022280897969 43.39784965440255 257.8261034151001 -303.24637065555936 9.976439066337804 13.604405672652977 -14.074783631236542 43.82007387041961 352.1156357053931 -86.59977719102032 43.39784965440254 282.17389658489986 -236.75362934444058 9.976439066337804 166.39559432734703 -165.92521636876344 43.82007387041961 187.88436429460683 -266.59977719102034 43.39784965440254 102.17389658489992 -56.75362934444064 9.976439066337804 346.395594327347 -354.0955566437206 65.6559553854118 26.342480857014277 +1_Eulers 2_Eulers 3_Eulers 1_pos 2_pos +146.75362934444064 9.976439066337804 256.395594327347 1 1 +356.59977719102034 43.39784965440254 12.173896584899929 1 2 +75.92521636876346 43.82007387041961 277.8843642946069 1 3 +326.75362934444064 9.976439066337806 76.39559432734703 1 4 +176.59977719102034 43.397849654402556 192.17389658489986 1 5 +255.92521636876344 43.82007387041961 97.88436429460687 1 6 +213.24637065555936 9.976439066337804 103.604405672653 1 7 +3.400222808979685 43.39784965440255 347.8261034151001 1 8 +284.0747836312365 43.82007387041961 82.11563570539313 1 9 +33.24637065555936 9.976439066337804 283.60440567265294 1 10 +183.40022280897963 43.397849654402556 167.8261034151001 1 11 +104.07478363123654 43.82007387041961 262.1156357053931 1 12 +273.4002228089796 43.397849654402556 77.82610341510008 1 13 +123.24637065555939 9.976439066337806 193.60440567265297 1 14 +194.07478363123653 43.82007387041961 172.11563570539317 1 15 +93.40022280897969 43.39784965440255 257.8261034151001 1 16 +303.24637065555936 9.976439066337804 13.604405672652977 1 17 +14.074783631236542 43.82007387041961 352.1156357053931 1 18 +86.59977719102032 43.39784965440254 282.17389658489986 1 19 +236.75362934444058 9.976439066337804 166.39559432734703 1 20 +165.92521636876344 43.82007387041961 187.88436429460683 1 21 +266.59977719102034 43.39784965440254 102.17389658489992 1 22 +56.75362934444064 9.976439066337804 346.395594327347 1 23 +354.0955566437206 65.6559553854118 26.342480857014277 1 24 diff --git a/python/tests/reference/Rotation/fcc_GT_prime.txt b/python/tests/reference/Rotation/fcc_GT_prime.txt index 7df7cb6c1..1d6f171c4 100644 --- a/python/tests/reference/Rotation/fcc_GT_prime.txt +++ b/python/tests/reference/Rotation/fcc_GT_prime.txt @@ -1,26 +1,26 @@ 1 header -1_Eulers 2_Eulers 3_Eulers -166.39559432734697 9.976439066337804 236.75362934444058 -352.1156357053931 43.82007387041961 14.074783631236542 -77.82610341510008 43.397849654402556 273.4002228089796 -346.395594327347 9.976439066337804 56.75362934444064 -172.11563570539317 43.82007387041961 194.07478363123653 -257.8261034151001 43.39784965440255 93.40022280897969 -193.604405672653 9.976439066337804 123.24637065555939 -7.884364294606862 43.82007387041961 345.9252163687635 -282.17389658489986 43.39784965440254 86.59977719102032 -13.604405672652977 9.976439066337804 303.24637065555936 -187.88436429460683 43.82007387041961 165.92521636876344 -102.17389658489992 43.39784965440254 266.59977719102034 -277.8843642946069 43.82007387041961 75.92521636876346 -103.604405672653 9.976439066337804 213.24637065555936 -192.17389658489986 43.397849654402556 176.59977719102034 -97.88436429460687 43.82007387041961 255.92521636876344 -283.60440567265294 9.976439066337804 33.24637065555936 -12.173896584899929 43.39784965440254 356.59977719102034 -82.11563570539313 43.82007387041961 284.0747836312365 -256.395594327347 9.976439066337804 146.75362934444064 -185.65621061503683 65.60048732963617 153.70832464959264 -262.1156357053931 43.82007387041961 104.07478363123654 -76.39559432734703 9.976439066337806 326.75362934444064 -347.8261034151001 43.39784965440255 3.400222808979685 +1_Eulers 2_Eulers 3_Eulers 1_pos 2_pos +166.39559432734697 9.976439066337804 236.75362934444058 1 1 +352.1156357053931 43.82007387041961 14.074783631236542 1 2 +77.82610341510008 43.397849654402556 273.4002228089796 1 3 +346.395594327347 9.976439066337804 56.75362934444064 1 4 +172.11563570539317 43.82007387041961 194.07478363123653 1 5 +257.8261034151001 43.39784965440255 93.40022280897969 1 6 +193.604405672653 9.976439066337804 123.24637065555939 1 7 +7.884364294606862 43.82007387041961 345.9252163687635 1 8 +282.17389658489986 43.39784965440254 86.59977719102032 1 9 +13.604405672652977 9.976439066337804 303.24637065555936 1 10 +187.88436429460683 43.82007387041961 165.92521636876344 1 11 +102.17389658489992 43.39784965440254 266.59977719102034 1 12 +277.8843642946069 43.82007387041961 75.92521636876346 1 13 +103.604405672653 9.976439066337804 213.24637065555936 1 14 +192.17389658489986 43.397849654402556 176.59977719102034 1 15 +97.88436429460687 43.82007387041961 255.92521636876344 1 16 +283.60440567265294 9.976439066337804 33.24637065555936 1 17 +12.173896584899929 43.39784965440254 356.59977719102034 1 18 +82.11563570539313 43.82007387041961 284.0747836312365 1 19 +256.395594327347 9.976439066337804 146.75362934444064 1 20 +185.65621061503683 65.60048732963617 153.70832464959264 1 21 +262.1156357053931 43.82007387041961 104.07478363123654 1 22 +76.39559432734703 9.976439066337806 326.75362934444064 1 23 +347.8261034151001 43.39784965440255 3.400222808979685 1 24 diff --git a/python/tests/reference/Rotation/fcc_KS.txt b/python/tests/reference/Rotation/fcc_KS.txt index 3a6e30358..93fdcf07e 100644 --- a/python/tests/reference/Rotation/fcc_KS.txt +++ b/python/tests/reference/Rotation/fcc_KS.txt @@ -1,26 +1,26 @@ 1 header -1_Eulers 2_Eulers 3_Eulers -114.20342833932975 10.52877936550932 204.20342833932972 -94.3573968784815 80.40593177313954 311.22729452432543 -175.6426031215185 80.40593177313954 48.77270547567447 -155.79657166067025 10.52877936550932 155.79657166067025 -99.62136089109411 85.70366403943004 318.04510841542015 -170.37863910890587 85.70366403943002 41.954891584579855 -85.64260312151852 80.40593177313954 48.77270547567448 -65.79657166067024 10.52877936550932 155.79657166067025 -9.621360891094124 85.70366403943004 318.04510841542015 -80.37863910890587 85.70366403943004 41.95489158457987 -24.203428339329758 10.52877936550932 204.20342833932975 -4.357396878481486 80.40593177313954 311.2272945243255 -204.20342833932972 10.52877936550932 204.20342833932972 -184.35739687848147 80.40593177313954 311.2272945243255 -265.64260312151845 80.40593177313953 48.77270547567449 -245.79657166067025 10.528779365509317 155.79657166067025 -189.62136089109413 85.70366403943004 318.04510841542015 -260.3786391089059 85.70366403943002 41.954891584579855 -170.37863910890587 94.29633596056996 138.04510841542015 -99.62136089109411 94.29633596056998 221.95489158457983 -155.79657166067025 169.4712206344907 24.203428339329754 -175.64260312151848 99.59406822686046 131.22729452432552 -94.35739687848151 99.59406822686046 228.77270547567446 -114.20342833932975 169.4712206344907 335.7965716606702 +1_Eulers 2_Eulers 3_Eulers 1_pos 2_pos +114.20342833932975 10.52877936550932 204.20342833932972 1 1 +94.3573968784815 80.40593177313954 311.22729452432543 1 2 +175.6426031215185 80.40593177313954 48.77270547567447 1 3 +155.79657166067025 10.52877936550932 155.79657166067025 1 4 +99.62136089109411 85.70366403943004 318.04510841542015 1 5 +170.37863910890587 85.70366403943002 41.954891584579855 1 6 +85.64260312151852 80.40593177313954 48.77270547567448 1 7 +65.79657166067024 10.52877936550932 155.79657166067025 1 8 +9.621360891094124 85.70366403943004 318.04510841542015 1 9 +80.37863910890587 85.70366403943004 41.95489158457987 1 10 +24.203428339329758 10.52877936550932 204.20342833932975 1 11 +4.357396878481486 80.40593177313954 311.2272945243255 1 12 +204.20342833932972 10.52877936550932 204.20342833932972 1 13 +184.35739687848147 80.40593177313954 311.2272945243255 1 14 +265.64260312151845 80.40593177313953 48.77270547567449 1 15 +245.79657166067025 10.528779365509317 155.79657166067025 1 16 +189.62136089109413 85.70366403943004 318.04510841542015 1 17 +260.3786391089059 85.70366403943002 41.954891584579855 1 18 +170.37863910890587 94.29633596056996 138.04510841542015 1 19 +99.62136089109411 94.29633596056998 221.95489158457983 1 20 +155.79657166067025 169.4712206344907 24.203428339329754 1 21 +175.64260312151848 99.59406822686046 131.22729452432552 1 22 +94.35739687848151 99.59406822686046 228.77270547567446 1 23 +114.20342833932975 169.4712206344907 335.7965716606702 1 24 diff --git a/python/tests/reference/Rotation/fcc_NW.txt b/python/tests/reference/Rotation/fcc_NW.txt index bf3631db0..e041ec0b0 100644 --- a/python/tests/reference/Rotation/fcc_NW.txt +++ b/python/tests/reference/Rotation/fcc_NW.txt @@ -1,14 +1,14 @@ 1 header -1_Eulers 2_Eulers 3_Eulers -96.91733794010702 83.13253115922213 314.5844440567886 -173.082662059893 83.13253115922211 45.41555594321143 -135.0 9.735610317245317 180.0 -263.082662059893 83.13253115922213 45.415555943211444 -186.91733794010702 83.13253115922211 314.5844440567886 -224.99999999999997 9.735610317245317 180.0 -83.082662059893 83.13253115922213 45.415555943211444 -6.917337940106983 83.13253115922211 314.5844440567886 -45.0 9.73561031724532 180.0 -256.36129272052347 45.81931182053556 279.59803029876775 -256.36129272052347 45.81931182053556 279.59803029876775 -315.0 99.73561031724536 0.0 +1_Eulers 2_Eulers 3_Eulers 1_pos 2_pos +96.91733794010702 83.13253115922213 314.5844440567886 1 1 +173.082662059893 83.13253115922211 45.41555594321143 1 2 +135.0 9.735610317245317 180.0 1 3 +263.082662059893 83.13253115922213 45.415555943211444 1 4 +186.91733794010702 83.13253115922211 314.5844440567886 1 5 +224.99999999999997 9.735610317245317 180.0 1 6 +83.082662059893 83.13253115922213 45.415555943211444 1 7 +6.917337940106983 83.13253115922211 314.5844440567886 1 8 +45.0 9.73561031724532 180.0 1 9 +256.36129272052347 45.81931182053556 279.59803029876775 1 10 +256.36129272052347 45.81931182053556 279.59803029876775 1 11 +315.0 99.73561031724536 0.0 1 12 diff --git a/python/tests/reference/Rotation/fcc_Pitsch.txt b/python/tests/reference/Rotation/fcc_Pitsch.txt index a2369f0a4..aa0c32365 100644 --- a/python/tests/reference/Rotation/fcc_Pitsch.txt +++ b/python/tests/reference/Rotation/fcc_Pitsch.txt @@ -1,14 +1,14 @@ 1 header -1_Eulers 2_Eulers 3_Eulers -135.41555594321144 83.13253115922213 173.082662059893 -260.26438968275465 90.0 135.0 -260.40196970123213 45.81931182053557 13.638707279476478 -314.5844440567886 83.13253115922213 96.91733794010702 -350.40196970123213 45.81931182053557 283.6387072794765 -170.26438968275465 90.0 224.99999999999997 -315.4155559432114 83.13253115922213 353.08266205989304 -99.73561031724536 90.0 225.0 -279.59803029876787 45.819311820535574 166.36129272052352 -134.58444405678856 83.13253115922213 276.91733794010696 -9.598030298767851 45.819311820535574 76.36129272052355 -9.735610317245369 90.0 315.0 +1_Eulers 2_Eulers 3_Eulers 1_pos 2_pos +135.41555594321144 83.13253115922213 173.082662059893 1 1 +260.26438968275465 90.0 135.0 1 2 +260.40196970123213 45.81931182053557 13.638707279476478 1 3 +314.5844440567886 83.13253115922213 96.91733794010702 1 4 +350.40196970123213 45.81931182053557 283.6387072794765 1 5 +170.26438968275465 90.0 224.99999999999997 1 6 +315.4155559432114 83.13253115922213 353.08266205989304 1 7 +99.73561031724536 90.0 225.0 1 8 +279.59803029876787 45.819311820535574 166.36129272052352 1 9 +134.58444405678856 83.13253115922213 276.91733794010696 1 10 +9.598030298767851 45.819311820535574 76.36129272052355 1 11 +9.735610317245369 90.0 315.0 1 12 diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index a4dc7c61d..08d543554 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -74,6 +74,8 @@ class TestRotation: ori = Orientation(Rotation(),lattice) eu = np.array([o.rotation.asEulers(degrees=True) for o in ori.relatedOrientations(model)]) if update: + coords = np.array([(1,i+1) for i,x in enumerate(eu)]) table = damask.Table(eu,{'Eulers':(3,)}) + table.add('pos',coords) table.to_ASCII(reference) assert np.allclose(eu,damask.Table.from_ASCII(reference).get('Eulers')) From 0bf22fd03c9e5512d61b4954df9eac0733a0ecd6 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 8 Dec 2019 22:35:39 +0100 Subject: [PATCH 042/223] using central functionality --- processing/pre/geom_toTable.py | 45 ++++++++++++---------------------- 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/processing/pre/geom_toTable.py b/processing/pre/geom_toTable.py index ed33d9c85..3d955ad53 100755 --- a/processing/pre/geom_toTable.py +++ b/processing/pre/geom_toTable.py @@ -2,10 +2,8 @@ import os import sys -from optparse import OptionParser from io import StringIO - -import numpy as np +from optparse import OptionParser import damask @@ -24,38 +22,25 @@ Translate geom description into ASCIItable containing position and microstructur """, version = scriptID) (options, filenames) = parser.parse_args() - - if filenames == []: filenames = [None] for name in filenames: - damask.util.report(scriptName,name) + damask.util.report(scriptName,name) - geom = damask.Geom.from_file(StringIO(''.join(sys.stdin.read())) if name is None else name) + geom = damask.Geom.from_file(StringIO(''.join(sys.stdin.read())) if name is None else name) + damask.util.croak(geom) - damask.util.croak(geom) + coord0 = damask.grid_filters.cell_coord0(geom.grid,geom.size,geom.origin).reshape((-1,3),order='F') -# --- generate grid -------------------------------------------------------------------------------- + comments = geom.comments \ + + [scriptID + ' ' + ' '.join(sys.argv[1:]), + "grid\ta {}\tb {}\tc {}".format(*geom.grid), + "size\tx {}\ty {}\tz {}".format(*geom.size), + "origin\tx {}\ty {}\tz {}".format(*geom.origin), + "homogenization\t{}".format(geom.homogenization)] - grid = geom.get_grid() - size = geom.get_size() - origin = geom.get_origin() + table = damask.Table(coord0,{'pos':(3,)},comments) + table.add('microstructure',geom.microstructure.reshape((-1,1))) - x = (0.5 + np.arange(grid[0],dtype=float))/grid[0]*size[0]+origin[0] - y = (0.5 + np.arange(grid[1],dtype=float))/grid[1]*size[1]+origin[1] - z = (0.5 + np.arange(grid[2],dtype=float))/grid[2]*size[2]+origin[2] - - xx = np.tile( x, grid[1]* grid[2]) - yy = np.tile(np.repeat(y,grid[0] ),grid[2]) - zz = np.repeat(z,grid[0]*grid[1]) - -# --- create ASCII table -------------------------------------------------------------------------- - - table = damask.ASCIItable(outname = os.path.splitext(name)[0]+'.txt' if name else name) - table.info_append(geom.get_comments() + [scriptID + '\t' + ' '.join(sys.argv[1:])]) - table.labels_append(['{}_{}'.format(1+i,'pos') for i in range(3)]+['microstructure']) - table.head_write() - table.output_flush() - table.data = np.squeeze(np.dstack((xx,yy,zz,geom.microstructure.flatten('F'))),axis=0) - table.data_writeArray() - table.close() + table.to_ASCII(sys.stdout if name is None else \ + os.path.splitext(name)[0]+'.txt') From 1955d09d3f92a3f6ddcd3956689567942eb1ef8c Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 8 Dec 2019 22:35:58 +0100 Subject: [PATCH 043/223] modernizing --- processing/pre/geom_fromTable.py | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/processing/pre/geom_fromTable.py b/processing/pre/geom_fromTable.py index f513c4834..40fd17437 100755 --- a/processing/pre/geom_fromTable.py +++ b/processing/pre/geom_fromTable.py @@ -78,36 +78,15 @@ for name in filenames: table = damask.ASCIItable(name = name,readonly=True) table.head_read() # read ASCII header info -# ------------------------------------------ sanity checks --------------------------------------- - - coordDim = table.label_dimension(options.pos) - - errors = [] - if not 3 >= coordDim >= 2: - errors.append('coordinates "{}" need to have two or three dimensions.'.format(options.pos)) - if not np.all(table.label_dimension(label) == dim): - errors.append('input "{}" needs to have dimension {}.'.format(label,dim)) - if options.phase and table.label_dimension(options.phase) != 1: - errors.append('phase column "{}" is not scalar.'.format(options.phase)) - - if errors != []: - damask.util.croak(errors) - continue table.data_readArray([options.pos] \ + (label if isinstance(label, list) else [label]) \ + ([options.phase] if options.phase else [])) - if coordDim == 2: - table.data = np.insert(table.data,2,np.zeros(len(table.data)),axis=1) # add zero z coordinate for two-dimensional input if options.phase is None: table.data = np.column_stack((table.data,np.ones(len(table.data)))) # add single phase if no phase column given - grid,size = damask.util.coordGridAndSize(table.data[:,0:3]) - coords = [np.unique(table.data[:,i]) for i in range(3)] - mincorner = np.array(list(map(min,coords))) - origin = mincorner - 0.5*size/grid # shift from cell center to corner - + grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.data[:,0:3]) indices = np.lexsort((table.data[:,0],table.data[:,1],table.data[:,2])) # indices of position when sorting x fast, z slow microstructure = np.empty(grid,dtype = int) # initialize empty microstructure @@ -142,7 +121,6 @@ for name in filenames: config_header += [''] for i,data in enumerate(unique): config_header += ['[Grain{}]'.format(i+1), - 'crystallite 1', '(constituent)\tphase {}\ttexture {}\tfraction 1.0'.format(int(data[4]),i+1), ] From 1fcbc356114e833653eb430b718d55032693d21c Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 9 Dec 2019 05:25:22 +0100 Subject: [PATCH 044/223] untested python2.7 code complicated code. Easier to re-implement from scratch if really needed --- processing/misc/yieldSurface.py | 1431 ------------------------- processing/misc/yieldSurfaceFast.py | 1513 --------------------------- 2 files changed, 2944 deletions(-) delete mode 100755 processing/misc/yieldSurface.py delete mode 100755 processing/misc/yieldSurfaceFast.py diff --git a/processing/misc/yieldSurface.py b/processing/misc/yieldSurface.py deleted file mode 100755 index 28f52062f..000000000 --- a/processing/misc/yieldSurface.py +++ /dev/null @@ -1,1431 +0,0 @@ -#!/usr/bin/env python2.7 -# -*- coding: UTF-8 no BOM -*- - -import threading,time,os -import numpy as np -from optparse import OptionParser -import damask -from damask.util import leastsqBound - -scriptName = os.path.splitext(os.path.basename(__file__))[0] -scriptID = ' '.join([scriptName,damask.version]) - -def runFit(exponent, eqStress, dimension, criterion): - global threads, myFit, myLoad - global fitResidual - global Guess, dDim - - dDim = dimension - 3 - nParas = len(fitCriteria[criterion]['bound'][dDim]) - nExpo = fitCriteria[criterion]['nExpo'] - - if exponent > 0.0: # User defined exponents - nParas = nParas-nExpo - fitCriteria[criterion]['bound'][dDim] = fitCriteria[criterion]['bound'][dDim][:nParas] - - for i in range(nParas): - temp = fitCriteria[criterion]['bound'][dDim][i] - if fitCriteria[criterion]['bound'][dDim][i] == (None,None): - Guess.append(1.0) - else: - g = (temp[0]+temp[1])/2.0 - if g == 0: g = temp[1]*0.5 - Guess.append(g) - - myLoad = Loadcase(options.load[0],options.load[1],options.load[2], - nSet = 10, dimension = dimension, vegter = options.criterion=='vegter') - - - myFit = Criterion(exponent,eqStress, dimension, criterion) - for t in range(options.threads): - threads.append(myThread(t)) - threads[t].start() - - for t in range(options.threads): - threads[t].join() - damask.util.croak('Residuals') - damask.util.croak(fitResidual) - -def principalStresses(sigmas): - """ - Computes principal stresses (i.e. eigenvalues) for a set of Cauchy stresses. - - sorted in descending order. - """ - lambdas=np.zeros(0,'d') - for i in range(np.shape(sigmas)[1]): - eigenvalues = np.linalg.eigvalsh(sym6toT33(sigmas[:,i])) - lambdas = np.append(lambdas,np.sort(eigenvalues)[::-1]) #append eigenvalues in descending order - lambdas = np.transpose(lambdas.reshape(np.shape(sigmas)[1],3)) - return lambdas - -def principalStress(p): - I = invariant(p) - - I1s3I2= (I[0]**2 - 3.0*I[1])**0.5 - numer = 2.0*I[0]**3 - 9.0*I[0]*I[1] + 27.0*I[2] - denom = 2.0*I1s3I2**3 - cs = numer/denom - - phi = np.arccos(cs)/3.0 - t1 = I[0]/3.0; t2 = 2.0/3.0*I1s3I2 - return np.array( [t1 + t2*np.cos(phi), - t1 + t2*np.cos(phi+np.pi*2.0/3.0), - t1 + t2*np.cos(phi+np.pi*4.0/3.0)]) - -def principalStrs_Der(p, s, dim, Karafillis=False): - """Derivative of principal stress with respect to stress""" - third = 1.0/3.0 - third2 = 2.0*third - - I = invariant(p) - I1s3I2= np.sqrt(I[0]**2 - 3.0*I[1]) - numer = 2.0*I[0]**3 - 9.0*I[0]*I[1] + 27.0*I[2] - denom = 2.0*I1s3I2**3 - cs = numer/denom - phi = np.arccos(cs)/3.0 - - dphidcs = -third/np.sqrt(1.0 - cs**2) - dcsddenom = 0.5*numer*(-1.5)*I1s3I2**(-5.0) - dcsdI1 = (6.0*I[0]**2 - 9.0*I[1])*denom + dcsddenom*(2.0*I[0]) - dcsdI2 = ( - 9.0*I[0])*denom + dcsddenom*(-3.0) - dcsdI3 = 27.0*denom - dphidI1, dphidI2, dphidI3 = dphidcs*dcsdI1, dphidcs*dcsdI2, dphidcs*dcsdI3 - - dI1s3I2dI1 = I[0]/I1s3I2 - dI1s3I2dI2 = -1.5/I1s3I2 - tcoeff = third2*I1s3I2 - - dSidIj = lambda theta : ( tcoeff*(-np.sin(theta))*dphidI1 + third2*dI1s3I2dI1*np.cos(theta) + third, - tcoeff*(-np.sin(theta))*dphidI2 + third2*dI1s3I2dI2*np.cos(theta), - tcoeff*(-np.sin(theta))*dphidI3) - dSdI = np.array([dSidIj(phi),dSidIj(phi+np.pi*2.0/3.0),dSidIj(phi+np.pi*4.0/3.0)]) # i=1,2,3; j=1,2,3 - -# calculate the derivation of principal stress with regards to the anisotropic coefficients - one = np.ones_like(s); zero = np.zeros_like(s); num = len(s) - dIdp = np.array([[one, one, one, zero, zero, zero], - [p[1]+p[2], p[2]+p[0], p[0]+p[1], -2.0*p[3], -2.0*p[4], -2.0*p[5]], - [p[1]*p[2]-p[4]**2, p[2]*p[0]-p[5]**2, p[0]*p[1]-p[3]**2, - -2.0*p[3]*p[2]+2.0*p[4]*p[5], -2.0*p[4]*p[0]+2.0*p[5]*p[3], -2.0*p[5]*p[1]+2.0*p[3]*p[4]] ]) - if Karafillis: - dpdc = np.array([[zero,s[0]-s[2],s[0]-s[1]], [s[1]-s[2],zero,s[1]-s[0]], [s[2]-s[1],s[2]-s[0],zero]])/3.0 - dSdp = np.array([np.dot(dSdI[:,:,i],dIdp[:,:,i]).T for i in range(num)]).T - if dim == 2: - temp = np.vstack([dSdp[:,3]*s[3]]).T.reshape(num,1,3).T - else: - temp = np.vstack([dSdp[:,3]*s[3],dSdp[:,4]*s[4],dSdp[:,5]*s[5]]).T.reshape(num,3,3).T - - return np.concatenate((np.array([np.dot(dSdp[:,0:3,i], dpdc[:,:,i]).T for i in range(num)]).T, - temp), axis=1) - else: - if dim == 2: - dIdc=np.array([[-dIdp[i,0]*s[1], -dIdp[i,1]*s[0], -dIdp[i,1]*s[2], - -dIdp[i,2]*s[1], -dIdp[i,2]*s[0], -dIdp[i,0]*s[2], - dIdp[i,3]*s[3] ] for i in range(3)]) - else: - dIdc=np.array([[-dIdp[i,0]*s[1], -dIdp[i,1]*s[0], -dIdp[i,1]*s[2], - -dIdp[i,2]*s[1], -dIdp[i,2]*s[0], -dIdp[i,0]*s[2], - dIdp[i,3]*s[3], dIdp[i,4]*s[4], dIdp[i,5]*s[5] ] for i in range(3)]) - return np.array([np.dot(dSdI[:,:,i],dIdc[:,:,i]).T for i in range(num)]).T - -def invariant(sigmas): - I = np.zeros(3) - s11,s22,s33,s12,s23,s31 = sigmas - I[0] = s11 + s22 + s33 - I[1] = s11*s22 + s22*s33 + s33*s11 - s12**2 - s23**2 - s31**2 - I[2] = s11*s22*s33 + 2.0*s12*s23*s31 - s12**2*s33 - s23**2*s11 - s31**2*s22 - return I - -def math_ln(x): - return np.log(x + 1.0e-32) - -def sym6toT33(sym6): - """Shape the symmetric stress tensor(6) into (3,3)""" - return np.array([[sym6[0],sym6[3],sym6[5]], - [sym6[3],sym6[1],sym6[4]], - [sym6[5],sym6[4],sym6[2]]]) - -def t33toSym6(t33): - """Shape the stress tensor(3,3) into symmetric (6)""" - return np.array([ t33[0,0], - t33[1,1], - t33[2,2], - (t33[0,1] + t33[1,0])/2.0, # 0 3 5 - (t33[1,2] + t33[2,1])/2.0, # * 1 4 - (t33[2,0] + t33[0,2])/2.0,]) # * * 2 - -class Criteria(object): - def __init__(self, criterion, uniaxialStress,exponent, dimension): - self.stress0 = uniaxialStress - if exponent < 0.0: # Fitting exponent m - self.mFix = [False, exponent] - else: # fixed exponent m - self.mFix = [True, exponent] - self.func = fitCriteria[criterion]['func'] - self.criteria = criterion - self.dim = dimension - def fun(self, paras, ydata, sigmas): - return self.func(self.stress0, paras, sigmas,self.mFix,self.criteria,self.dim) - def jac(self, paras, ydata, sigmas): - return self.func(self.stress0, paras, sigmas,self.mFix,self.criteria,self.dim,Jac=True) - -class Vegter(object): - """Vegter yield criterion""" - - def __init__(self, refPts, refNormals,nspace=11): - self.refPts, self.refNormals = self._getRefPointsNormals(refPts, refNormals) - self.hingePts = self._getHingePoints() - self.nspace = nspace - def _getRefPointsNormals(self,refPtsQtr,refNormalsQtr): - if len(refPtsQtr) == 12: - refPts = refPtsQtr - refNormals = refNormalsQtr - else: - refPts = np.empty([13,2]) - refNormals = np.empty([13,2]) - refPts[12] = refPtsQtr[0] - refNormals[12] = refNormalsQtr[0] - for i in range(3): - refPts[i] = refPtsQtr[i] - refPts[i+3] = refPtsQtr[3-i][::-1] - refPts[i+6] =-refPtsQtr[i] - refPts[i+9] =-refPtsQtr[3-i][::-1] - refNormals[i] = refNormalsQtr[i] - refNormals[i+3] = refNormalsQtr[3-i][::-1] - refNormals[i+6] =-refNormalsQtr[i] - refNormals[i+9] =-refNormalsQtr[3-i][::-1] - return refPts,refNormals - - def _getHingePoints(self): - """ - Calculate the hinge point B according to the reference points A,C and the normals n,m - - refPoints = np.array([[p1_x, p1_y], [p2_x, p2_y]]); - refNormals = np.array([[n1_x, n1_y], [n2_x, n2_y]]) - """ - def hingPoint(points, normals): - A1 = points[0][0]; A2 = points[0][1] - C1 = points[1][0]; C2 = points[1][1] - n1 = normals[0][0]; n2 = normals[0][1] - m1 = normals[1][0]; m2 = normals[1][1] - B1 = (m2*(n1*A1 + n2*A2) - n2*(m1*C1 + m2*C2))/(n1*m2-m1*n2) - B2 = (n1*(m1*C1 + m2*C2) - m1*(n1*A1 + n2*A2))/(n1*m2-m1*n2) - return np.array([B1,B2]) - return np.array([hingPoint(self.refPts[i:i+2],self.refNormals[i:i+2]) for i in range(len(self.refPts)-1)]) - - def getBezier(self): - def bezier(R,H): - b = [] - for mu in np.linspace(0.0,1.0,self.nspace): - b.append(np.array(R[0]*np.ones_like(mu) + 2.0*mu*(H - R[0]) + mu**2*(R[0]+R[1] - 2.0*H))) - return b - return np.array([bezier(self.refPts[i:i+2],self.hingePts[i]) for i in range(len(self.refPts)-1)]) - -def VetgerCriterion(stress,lankford, rhoBi0, theta=0.0): - """0-pure shear; 1-uniaxial; 2-plane strain; 3-equi-biaxial""" - def getFourierParas(r): - # get the value after Fourier transformation - nset = len(r) - lmatrix = np.empty([nset,nset]) - theta = np.linspace(0.0,np.pi/2,nset) - for i,th in enumerate(theta): - lmatrix[i] = np.array([np.cos(2*j*th) for j in range(nset)]) - return np.linalg.solve(lmatrix, r) - - nps = len(stress) - if nps%4 != 0: - damask.util.croak('Warning: the number of stress points is uncorrect, stress points of %s are missing in set %i'%( - ['eq-biaxial, plane strain & uniaxial', 'eq-biaxial & plane strain','eq-biaxial'][nps%4-1],nps/4+1)) - else: - nset = nps/4 - strsSet = stress.reshape(nset,4,2) - refPts = np.empty([4,2]) - - fouriercoeffs = np.array([np.cos(2.0*i*theta) for i in range(nset)]) - for i in range(2): - refPts[3,i] = sum(strsSet[:,3,i])/nset - for j in range(3): - refPts[j,i] = np.dot(getFourierParas(strsSet[:,j,i]), fouriercoeffs) - - -def Tresca(eqStress=None, #not needed/supported - paras=None, - sigmas=None, - mFix=None, #not needed/supported - criteria=None, #not needed/supported - dim=3, - Jac=False): - """ - Tresca yield criterion - - the fitted parameter is paras(sigma0) - """ - if not Jac: - lambdas = principalStresses(sigmas) - r = np.amax(np.array([abs(lambdas[2,:]-lambdas[1,:]),\ - abs(lambdas[1,:]-lambdas[0,:]),\ - abs(lambdas[0,:]-lambdas[2,:])]),0) - paras - return r.ravel() - else: - return -np.ones(len(sigmas)) - -def Cazacu_Barlat(eqStress=None, - paras=None, - sigmas=None, - mFix=None,#not needed/supported - criteria=None, - dim=3, #2D also possible - Jac=False): - """ - Cazacu-Barlat (CB) yield criterion - - the fitted parameters are: - a1,a2,a3,a6; b1,b2,b3,b4,b5,b10; c for plane stress - a1,a2,a3,a4,a5,a6; b1,b2,b3,b4,b5,b6,b7,b8,b9,b10,b11; c: for general case - mFix is ignored - """ - s11,s22,s33,s12,s23,s31 = sigmas - if dim == 2: - (a1,a2,a3,a4), (b1,b2,b3,b4,b5,b10), c = paras[0:4],paras[4:10],paras[10] - a5 = a6 = b6 = b7 = b8 = b9 = b11 = 0.0 - s33 = s23 = s31 = np.zeros_like(s11) - else: - (a1,a2,a3,a4,a5,a6), (b1,b2,b3,b4,b5,b6,b7,b8,b9,b10,b11), c = paras[0:6],paras[6:17],paras[17] - - s1_2, s2_2, s3_2, s12_2, s23_2, s31_2 = np.array([s11,s22,s33,s12,s23,s31])**2 - s1_3, s2_3, s3_3, s123, s321 = s11*s1_2, s22*s2_2, s33*s3_2,s11*s22*s33, s12*s23*s31 - d12_2,d23_2,d31_2 = (s11-s22)**2, (s22-s33)**2, (s33-s11)**2 - - J20 = ( a1*d12_2 + a2*d23_2 + a3*d31_2 )/6.0 + a4*s12_2 + a5*s23_2 + a6*s31_2 - J30 = ( (b1 +b2 )*s1_3 + (b3 +b4 )*s2_3 + ( b1+b4-b2 + b1+b4-b3 )*s3_3 )/27.0- \ - ( (b1*s22+b2*s33)*s1_2 + (b3*s33+b4*s11)*s2_2 + ((b1+b4-b2)*s11 + (b1+b4-b3)*s22)*s3_2 )/9.0 + \ - ( (b1+b4)*s123/9.0 + b11*s321 )*2.0 - \ - ( ( 2.0*b9 *s22 - b8*s33 - (2.0*b9 -b8)*s11 )*s31_2 + - ( 2.0*b10*s33 - b5*s22 - (2.0*b10-b5)*s11 )*s12_2 + - ( (b6+b7)*s11 - b6*s22 - b7*s33 )*s23_2 - )/3.0 - f0 = J20**3 - c*J30**2 - r = f0**(1.0/6.0)*np.sqrt(3.0)/eqStress - - if not Jac: - return (r - 1.0).ravel() - else: - drdf = r/f0/6.0 - dj2, dj3 = drdf*3.0*J20**2, -drdf*2.0*J30*c - jc = -drdf*J30**2 - - ja1,ja2,ja3 = dj2*d12_2/6.0, dj2*d23_2/6.0, dj2*d31_2/6.0 - ja4,ja5,ja6 = dj2*s12_2, dj2*s23_2, dj2*s31_2 - jb1 = dj3*( (s1_3 + 2.0*s3_3)/27.0 - s22*s1_2/9.0 - (s11+s22)*s3_2/9.0 + s123/4.5 ) - jb2 = dj3*( (s1_3 - s3_3)/27.0 - s33*s1_2/9.0 + s11 *s3_2/9.0 ) - jb3 = dj3*( (s2_3 - s3_3)/27.0 - s33*s2_2/9.0 + s22 *s3_2/9.0 ) - jb4 = dj3*( (s2_3 + 2.0*s3_3)/27.0 - s11*s2_2/9.0 - (s11+s22)*s3_2/9.0 + s123/4.5 ) - - jb5, jb10 = dj3*(s22 - s11)*s12_2/3.0, dj3*(s11 - s33)*s12_2/1.5 - jb6, jb7 = dj3*(s22 - s11)*s23_2/3.0, dj3*(s33 - s11)*s23_2/3.0 - jb8, jb9 = dj3*(s33 - s11)*s31_2/3.0, dj3*(s11 - s22)*s31_2/1.5 - jb11 = dj3*s321*2.0 - if dim == 2: - return np.vstack((ja1,ja2,ja3,ja4,jb1,jb2,jb3,jb4,jb5,jb10,jc)).T - else: - return np.vstack((ja1,ja2,ja3,ja4,ja5,ja6,jb1,jb2,jb3,jb4,jb5,jb6,jb7,jb8,jb9,jb10,jb11,jc)).T - -def Drucker(eqStress=None,#not needed/supported - paras=None, - sigmas=None, - mFix=None, #not needed/supported - criteria=None, - dim=3, - Jac=False): - """ - Drucker yield criterion - - the fitted parameters are - sigma0, C_D for Drucker(p=1); - sigma0, C_D, p for general Drucker - eqStress, mFix are invalid inputs - """ - if criteria == 'drucker': - sigma0, C_D= paras - p = 1.0 - else: - sigma0, C_D = paras[0:2] - if mFix[0]: p = mFix[1] - else: p = paras[-1] - I = invariant(sigmas) - J = np.zeros([3]) - J[1] = I[0]**2/3.0 - I[1] - J[2] = I[0]**3/13.5 - I[0]*I[1]/3.0 + I[2] - J2_3p = J[1]**(3.0*p) - J3_2p = J[2]**(2.0*p) - left = J2_3p - C_D*J3_2p - r = left**(1.0/(6.0*p))*3.0**0.5/sigma0 - - if not Jac: - return (r - 1.0).ravel() - else: - drdl = r/left/(6.0*p) - if criteria == 'drucker': - return np.vstack((-r/sigma0, -drdl*J3_2p)).T - else: - dldp = 3.0*J2_3p*math_ln(J[1]) - 2.0*C_D*J3_2p*math_ln(J[2]) - jp = drdl*dldp + r*math_ln(left)/(-6.0*p*p) - - if mFix[0]: return np.vstack((-r/sigma0, -drdl*J3_2p)).T - else: return np.vstack((-r/sigma0, -drdl*J3_2p, jp)).T - -def Hill1948(eqStress=None,#not needed/supported - paras=None, - sigmas=None, - mFix=None, #not needed/supported - criteria=None,#not needed/supported - dim=3, - Jac=False): - """ - Hill 1948 yield criterion - - the fitted parameters are: - F, G, H, L, M, N for 3D - F, G, H, N for 2D - """ - s11,s22,s33,s12,s23,s31 = sigmas - if dim == 2: # plane stress - jac = np.array([ s22**2, s11**2, (s11-s22)**2, 2.0*s12**2]) - else: # general case - jac = np.array([(s22-s33)**2,(s33-s11)**2,(s11-s22)**2, 2.0*s23**2,2.0*s31**2,2.0*s12**2]) - - if not Jac: - return (np.dot(paras,jac)/2.0-0.5).ravel() - else: - return jac.T - -def Hill1979(eqStress=None,#not needed/supported - paras=None, - sigmas=None, - mFix=None, - criteria=None,#not needed/supported - dim=3, - Jac=False): - """ - Hill 1979 yield criterion - - the fitted parameters are: f,g,h,a,b,c,m - """ - if mFix[0]: - m = mFix[1] - else: - m = paras[-1] - - coeff = paras[0:6] - s = principalStresses(sigmas) - diffs = np.array([s[1]-s[2], s[2]-s[0], s[0]-s[1],\ - 2.0*s[0]-s[1]-s[2], 2.0*s[1]-s[2]-s[0], 2.0*s[2]-s[0]-s[1]])**2 - - diffsm = diffs**(m/2.0) - left = np.dot(coeff,diffsm) - r = (0.5*left)**(1.0/m)/eqStress #left = base**mi - - if not Jac: - return (r-1.0).ravel() - else: - drdl, dldm = r/left/m, np.dot(coeff,diffsm*math_ln(diffs))*0.5 - jm = drdl*dldm + r*math_ln(0.5*left)*(-1.0/m/m) #/(-m**2) - - if mFix[0]: return np.vstack((drdl*diffsm)).T - else: return np.vstack((drdl*diffsm, jm)).T - -def Hosford(eqStress=None, - paras=None, - sigmas=None, - mFix=None, - criteria=None, - dim=3, - Jac=False): - """ - Hosford family criteria - - the fitted parameters are: - von Mises: sigma0 - Hershey: (1) sigma0, a, when a is not fixed; (2) sigma0, when a is fixed - general Hosford: (1) F,G,H, a, when a is not fixed; (2) F,G,H, when a is fixed - """ - if criteria == 'vonmises': - sigma0 = paras - coeff = np.ones(3) - a = 2.0 - elif criteria == 'hershey': - sigma0 = paras[0] - coeff = np.ones(3) - if mFix[0]: a = mFix[1] - else: a = paras[1] - else: - sigma0 = eqStress - coeff = paras[0:3] - if mFix[0]: a = mFix[1] - else: a = paras[3] - - s = principalStresses(sigmas) - diffs = np.array([s[1]-s[2], s[2]-s[0], s[0]-s[1]])**2 - diffsm = diffs**(a/2.0) - left = np.dot(coeff,diffsm) - r = (0.5*left)**(1.0/a)/sigma0 - - if not Jac: - return (r-1.0).ravel() - else: - if criteria == 'vonmises': # von Mises - return -r/sigma0 - else: - drdl, dlda = r/left/a, np.dot(coeff,diffsm*math_ln(diffs))*0.5 - ja = drdl*dlda + r*math_ln(0.5*left)*(-1.0/a/a) - if criteria == 'hershey': # Hershey - if mFix[0]: return -r/sigma0 - else: return np.vstack((-r/sigma0, ja)).T - else: # Anisotropic Hosford - if mFix[0]: return np.vstack((drdl*diffsm)).T - else: return np.vstack((drdl*diffsm, ja)).T - -def Barlat1989(eqStress=None, - paras=None, - sigmas=None, - mFix=None, - criteria=None, - dim=3, - Jac=False): - """ - Barlat-Lian 1989 yield criteria - - the fitted parameters are: - Anisotropic: a, h, p, m; m is optional - """ - a, h, p = paras[0:3] - if mFix[0]: m = mFix[1] - else: m = paras[-1] - - c = 2.0-a - s11,s22,s12 = sigmas[0], sigmas[1], sigmas[3] - k1,k2 = 0.5*(s11 + h*s22), (0.25*(s11 - h*s22)**2 + (p*s12)**2)**0.5 - fs = np.array([ (k1+k2)**2, (k1-k2)**2, 4.0*k2**2 ]); fm = fs**(m/2.0) - left = np.dot(np.array([a,a,c]),fm) - r = (0.5*left)**(1.0/m)/eqStress - - if not Jac: - return (r-1.0).ravel() - else: - dk1dh = 0.5*s22 - dk2dh, dk2dp = 0.25*(s11-h*s22)*(-s22)/k2, p*s12**2/k2 - dlda, dldc = fm[0]+fm[1], fm[2] - fm1 = fs**(m/2.0-1.0)*m - dldk1, dldk2 = a*fm1[0]*(k1+k2)+a*fm1[1]*(k1-k2), a*fm1[0]*(k1+k2)-a*fm1[1]*(k1-k2)+c*fm1[2]*k2*4.0 - drdl, drdm = r/m/left, r*math_ln(0.5*left)*(-1.0/m/m) - dldm = np.dot(np.array([a,a,c]),fm*math_ln(fs))*0.5 - - ja,jc = drdl*dlda, drdl*dldc - jh,jp = drdl*(dldk1*dk1dh + dldk2*dk2dh), drdl*dldk2*dk2dp - jm = drdl*dldm + drdm - - if mFix[0]: return np.vstack((ja,jc,jh,jp)).T - else: return np.vstack((ja,jc,jh,jp,jm)).T - -def Barlat1991(eqStress, paras, sigmas, mFix, criteria, dim, Jac=False): - """ - Barlat 1991 criteria - - the fitted parameters are: - Anisotropic: a, b, c, f, g, h, m for 3D - a, b, c, h, m for plane stress - m is optional - """ - if dim == 2: coeff = paras[0:4] # plane stress - else: coeff = paras[0:6] # general case - if mFix[0]: m = mFix[1] - else: m = paras[-1] - - s11,s22,s33,s12,s23,s31 = sigmas - if dim == 2: - dXdx = np.array([s22,-s11,s11-s22,s12]) - A,B,C,H = np.array(coeff)[:,None]*dXdx; F=G=0.0 - else: - dXdx = np.array([s22-s33,s33-s11,s11-s22,s23,s31,s12]) - A,B,C,F,G,H = np.array(coeff)[:,None]*dXdx - - I2 = (F*F + G*G + H*H)/3.0+ ((A-C)**2+(C-B)**2+(B-A)**2)/54.0 - I3 = (C-B)*(A-C)*(B-A)/54.0 + F*G*H - ((C-B)*F*F + (A-C)*G*G + (B-A)*H*H)/6.0 - phi1 = np.arccos(I3/I2**1.5)/3.0 + np.pi/6.0; absc1 = 2.0*np.abs(np.cos(phi1)) - phi2 = phi1 + np.pi/3.0; absc2 = 2.0*np.abs(np.cos(phi2)) - phi3 = phi2 + np.pi/3.0; absc3 = 2.0*np.abs(np.cos(phi3)) - left = ( absc1**m + absc2**m + absc3**m ) - r = (0.5*left)**(1.0/m)*np.sqrt(3.0*I2)/eqStress - - if not Jac: - return (r - 1.0).ravel() - else: - dfdl = r/left/m - jm = r*math_ln(0.5*left)*(-1.0/m/m) + dfdl*0.5*( - absc1**m*math_ln(absc1) + absc2**m*math_ln(absc2) + absc3**m*math_ln(absc3) ) - - da,db,dc = (2.0*A-B-C)/18.0, (2.0*B-C-A)/18.0, (2.0*C-A-B)/18.0 - if dim == 2: - dI2dx = np.array([da, db, dc, H])/1.5*dXdx - dI3dx = np.array([ da*(B-C) + (H**2-G**2)/2.0, - db*(C-A) + (F**2-H**2)/2.0, - dc*(A-B) + (G**2-F**2)/2.0, - (G*F + (A-B))*H ])/3.0*dXdx - else: - dI2dx = np.array([da, db, dc, F,G,H])/1.5*dXdx - dI3dx = np.array([ da*(B-C) + (H**2-G**2)/2.0, - db*(C-A) + (F**2-H**2)/2.0, - dc*(A-B) + (G**2-F**2)/2.0, - (H*G*3.0 + (B-C))*F, - (F*H*3.0 + (C-A))*G, - (G*F*3.0 + (A-B))*H ])/3.0*dXdx - darccos = -1.0/np.sqrt(1.0 - I3**2/I2**3) - - dfdcos = lambda phi : dfdl*m*(2.0*abs(np.cos(phi)))**(m-1.0)*np.sign(np.cos(phi))*(-np.sin(phi)/1.5) - - dfdthe= (dfdcos(phi1) + dfdcos(phi2) + dfdcos(phi3)) - dfdI2, dfdI3 = dfdthe*darccos*I3*(-1.5)*I2**(-2.5)+r/2.0/I2, dfdthe*darccos*I2**(-1.5) - - if mFix[0]: return np.vstack((dfdI2*dI2dx + dfdI3*dI3dx)).T - else: return np.vstack((dfdI2*dI2dx + dfdI3*dI3dx, jm)).T - -def BBC2000(eqStress, paras, sigmas, mFix, criteria, dim, Jac=False): - """ - BBC2000 yield criterion - - the fitted parameters are - d,e,f,g, b,c,a, k; k is optional - criteria are invalid input - """ - d,e,f,g, b,c,a= paras[0:7] - if mFix[0]: k = mFix[1] - else: k = paras[-1] - - s11,s22,s12 = sigmas[0], sigmas[1], sigmas[3] - k2 = 2.0*k; k1 = k - 1.0 - M,N,P,Q,R = d+e, e+f, (d-e)/2.0, (e-f)/2.0, g**2 - Gamma = M*s11 + N*s22 - Psi = ( (P*s11 + Q*s22)**2 + s12**2*R )**0.5 - - l1, l2, l3 = b*Gamma + c*Psi, b*Gamma - c*Psi, 2.0*c*Psi - l1s,l2s,l3s = l1**2, l2**2, l3**2 - - left = a*l1s**k + a*l2s**k + (1-a)*l3s**k - r = left**(1.0/k2)/eqStress - if not Jac: - return (r - 1.0).ravel() - else: - drdl,drdk = r/left/k2, r*math_ln(left)*(-1.0/k2/k) - dldl1,dldl2,dldl3 = a*k2*(l1s**k1)*l1, a*k2*(l2s**k1)*l2, (1-a)*k2*(l3s**k1)*l3 - dldGama, dldPsi = (dldl1 + dldl2)*b, (dldl1 - dldl2 + 2.0*dldl3)*c - temp = (P*s11 + Q*s22)/Psi - dPsidP, dPsidQ, dPsidR = temp*s11, temp*s22, 0.5*s12**2/Psi - dlda = l1s**k + l2s**k - l3s**k - dldb = dldl1*Gamma + dldl2*Gamma - dldc = dldl1*Psi - dldl2*Psi + dldl3*2.0*Psi - dldk = a*math_ln(l1s)*l1s**k + a*math_ln(l2s)*l2s**k + (1-a)*math_ln(l3s)*l3s**k - - J = drdl*np.array([dldGama*s11+dldPsi*dPsidP*0.5, dldGama*(s11+s22)+dldPsi*(-dPsidP+dPsidQ)*0.5, #jd,je - dldGama*s22-dldPsi*dPsidQ*0.5, dldPsi*dPsidR*2.0*g, #jf,jg - dldb, dldc, dlda]) #jb,jc,ja - if mFix[0]: return np.vstack(J).T - else: return np.vstack((J, drdl*dldk + drdk)).T - - -def BBC2003(eqStress, paras, sigmas, mFix, criteria, dim, Jac=False): - """ - BBC2003 yield criterion - - the fitted parameters are - M,N,P,Q,R,S,T,a, k; k is optional - criteria are invalid input - """ - M,N,P,Q,R,S,T,a = paras[0:8] - if mFix[0]: k = mFix[1] - else: k = paras[-1] - - s11,s22,s12 = sigmas[0], sigmas[1], sigmas[3] - k2 = 2.0*k; k1 = k - 1.0 - Gamma = 0.5 * (s11 + M*s22) - Psi = ( 0.25*(N*s11 - P*s22)**2 + Q*Q*s12**2 )**0.5 - Lambda = ( 0.25*(R*s11 - S*s22)**2 + T*T*s12**2 )**0.5 - - l1, l2, l3 = Gamma + Psi, Gamma - Psi, 2.0*Lambda - l1s,l2s,l3s = l1**2, l2**2, l3**2 - left = a*l1s**k + a*l2s**k + (1-a)*l3s**k - r = left**(1.0/k2)/eqStress - if not Jac: - return (r - 1.0).ravel() - else: - drdl,drdk = r/left/k2, r*math_ln(left)*(-1.0/k2/k) - dldl1,dldl2,dldl3 = a*k2*(l1s**k1)*l1, a*k2*(l2s**k1)*l2, (1-a)*k2*(l3s**k1)*l3 - - dldGamma, dldPsi, dldLambda = dldl1+dldl2, dldl1-dldl2, 2.0*dldl3 - temp = 0.25/Psi*(N*s11 - P*s22) - dPsidN, dPsidP, dPsidQ = s11*temp, -s22*temp, Q*s12**2/Psi - temp = 0.25/Lambda*(R*s11 - S*s22) - dLambdadR, dLambdadS, dLambdadT = s11*temp, -s22*temp, T*s12**2/Psi - dldk = a*math_ln(l1s)*l1s**k + a*math_ln(l2s)*l2s**k + (1-a)*math_ln(l3s)*l3s**k - - J = drdl * np.array([dldGamma*s22*0.5, #jM - dldPsi*dPsidN, dldPsi*dPsidP, dldPsi*dPsidQ, #jN, jP, jQ - dldLambda*dLambdadR, dldLambda*dLambdadS, dldLambda*dLambdadT, #jR, jS, jT - l1s**k + l2s**k - l3s**k ]) #ja - - if mFix[0]: return np.vstack(J).T - else : return np.vstack((J, drdl*dldk+drdk)).T - -def BBC2005(eqStress, paras, sigmas, mFix, criteria, dim, Jac=False): - """ - BBC2005 yield criterion - - the fitted parameters are - a, b, L ,M, N, P, Q, R, k k are optional - criteria is invalid input - """ - a,b,L, M, N, P, Q, R = paras[0:8] - if mFix[0]: k = mFix[1] - else: k = paras[-1] - - s11 = sigmas[0]; s22 = sigmas[1]; s12 = sigmas[3] - k2 = 2.0*k - Gamma = L*s11 + M*s22 - Lambda = ( (N*s11 - P*s22)**2 + s12**2 )**0.5 - Psi = ( (Q*s11 - R*s22)**2 + s12**2 )**0.5 - - l1 = Lambda + Gamma; l2 = Lambda - Gamma; l3 = Lambda + Psi; l4 = Lambda - Psi - l1s = l1**2; l2s = l2**2; l3s = l3**2; l4s = l4**2 - left = a*l1s**k + a*l2s**k + b*l3s**k + b*l4s**k - sBar = left**(1.0/k2); r = sBar/eqStress - 1.0 - if not Jac: - return r.ravel() - else: - ln = lambda x : np.log(x + 1.0e-32) - expo = 0.5/k; k1 = k-1.0 - - dsBardl = expo*sBar/left/eqStress - dsBarde = sBar*ln(left); dedk = expo/(-k) - dldl1 = a*k*(l1s**k1)*(2.0*l1) - dldl2 = a*k*(l2s**k1)*(2.0*l2) - dldl3 = b*k*(l3s**k1)*(2.0*l3) - dldl4 = b*k*(l4s**k1)*(2.0*l4) - - dldLambda = dldl1 + dldl2 + dldl3 + dldl4 - dldGama = dldl1 - dldl2 - dldPsi = dldl3 - dldl4 - temp = (N*s11 - P*s22)/Lambda - dLambdadN = s11*temp; dLambdadP = -s22*temp - temp = (Q*s11 - R*s22)/Psi - dPsidQ = s11*temp; dPsidR = -s22*temp - dldk = a*ln(l1s)*l1s**k + a*ln(l2s)*l2s**k + b*ln(l3s)*l3s**k + b*ln(l4s)*l4s**k - - J = dsBardl * np.array( [ - l1s**k+l2s**k, l3s**k+l4s**k,dldGama*s11,dldGama*s22,dldLambda*dLambdadN, - dldLambda*dLambdadP, dldPsi*dPsidQ, dldPsi*dPsidR]) - - if mFix[0]: return np.vstack(J).T - else : return np.vstack(J, dldk+dsBarde*dedk).T - -def Yld2000(eqStress, paras, sigmas, mFix, criteria, dim, Jac=False): - """ - Yld2000 yield criterion - - C: c11,c22,c66 c12=c21=1.0 JAC NOT PASS - D: d11,d12,d21,d22,d66 - """ - C,D = paras[0:3], paras[3:8] - if mFix[0]: m = mFix[1] - else: m = paras[-1] - - s11, s22, s12 = sigmas[0],sigmas[1],sigmas[3] - X = np.array([ 2.0*C[0]*s11-C[0]*s22, 2.0*C[1]*s22-C[1]*s11, 3.0*C[2]*s12 ])/3.0 # a1,a2,a7 - Y = np.array([ (8.0*D[2]-2.0*D[0]-2.0*D[3]+2.0*D[1])*s11 + (4.0*D[3]-4.0*D[1]-4.0*D[2]+ D[0])*s22, - (4.0*D[0]-4.0*D[2]-4.0*D[1]+ D[3])*s11 + (8.0*D[1]-2.0*D[3]-2.0*D[0]+2.0*D[2])*s22, - 9.0*D[4]*s12 ])/9.0 - - def priStrs(s): - temp = np.sqrt( (s[0]-s[1])**2 + 4.0*s[2]**2 ) - return 0.5*(s[0]+s[1] + temp), 0.5*(s[0]+s[1] - temp) - m2 = m/2.0; m21 = m2 - 1.0 - (X1,X2), (Y1,Y2) = priStrs(X), priStrs(Y) # Principal values of X, Y - phi1s, phi21s, phi22s = (X1-X2)**2, (2.0*Y2+Y1)**2, (2.0*Y1+Y2)**2 - phi1, phi21, phi22 = phi1s**m2, phi21s**m2, phi22s**m2 - left = phi1 + phi21 + phi22 - r = (0.5*left)**(1.0/m)/eqStress - - if not Jac: - return (r-1.0).ravel() - else: - drdl, drdm = r/m/left, r*math_ln(0.5*left)*(-1.0/m/m) #/(-m*m) - dldm = ( phi1*math_ln(phi1s) + phi21*math_ln(phi21s) + phi22*math_ln(phi22s) )*0.5 - zero = np.zeros_like(s11); num = len(s11) - def dPrincipalds(X): - """Derivative of principla with respect to stress""" - temp = 1.0/np.sqrt( (X[0]-X[1])**2 + 4.0*X[2]**2 ) - dP1dsi = 0.5*np.array([ 1.0+temp*(X[0]-X[1]), 1.0-temp*(X[0]-X[1]), temp*4.0*X[2]]) - dP2dsi = 0.5*np.array([ 1.0-temp*(X[0]-X[1]), 1.0+temp*(X[0]-X[1]), -temp*4.0*X[2]]) - return np.array([dP1dsi, dP2dsi]) - - dXdXi, dYdYi = dPrincipalds(X), dPrincipalds(Y) - dXidC = np.array([ [ 2.0*s11-s22, zero, zero ], #dX11dC - [ zero, 2.0*s22-s11, zero ], #dX22dC - [ zero, zero, 3.0*s12 ] ])/3.0 #dX12dC - dYidD = np.array([ [ -2.0*s11+ s22, 2.0*s11-4.0*s22, 8.0*s11-4.0*s22, -2.0*s11+4.0*s22, zero ], #dY11dD - [ 4.0*s11-2.0*s22, -4.0*s11+8.0*s22, -4.0*s11+2.0*s22, s11-2.0*s22, zero ], #dY22dD - [ zero, zero, zero, zero, 9.0*s12 ] ])/9.0 #dY12dD - - dXdC=np.array([np.dot(dXdXi[:,:,i], dXidC[:,:,i]).T for i in range(num)]).T - dYdD=np.array([np.dot(dYdYi[:,:,i], dYidD[:,:,i]).T for i in range(num)]).T - - dldX = m*np.array([ phi1s**m21*(X1-X2), phi1s**m21*(X2-X1)]) - dldY = m*np.array([phi21s**m21*(2.0*Y2+Y1) + 2.0*phi22s**m21*(2.0*Y1+Y2), \ - phi22s**m21*(2.0*Y1+Y2) + 2.0*phi21s**m21*(2.0*Y2+Y1) ]) - jC = drdl*np.array([np.dot(dldX[:,i], dXdC[:,:,i]) for i in range(num)]).T - jD = drdl*np.array([np.dot(dldY[:,i], dYdD[:,:,i]) for i in range(num)]).T - - jm = drdl*dldm + drdm - if mFix[0]: return np.vstack((jC,jD)).T - else: return np.vstack((jC,jD,jm)).T - -def Yld200418p(eqStress, paras, sigmas, mFix, criteria, dim, Jac=False): - """ - Yld2004-18p yield criterion - - the fitted parameters are - C: c12,c21,c23,c32,c31,c13,c44,c55,c66; D: d12,d21,d23,d32,d31,d13,d44,d55,d66 for 3D - C: c12,c21,c23,c32,c31,c13,c44; D: d12,d21,d23,d32,d31,d13,d44 for 2D - and m, m are optional - criteria is ignored - """ - if dim == 2: C,D = np.append(paras[0:7],[0.0,0.0]), np.append(paras[7:14],[0.0,0.0]) - else: C,D = paras[0:9], paras[9:18] - if mFix[0]: m = mFix[1] - else: m = paras[-1] - - sv = (sigmas[0] + sigmas[1] + sigmas[2])/3.0 - sdev = np.vstack((sigmas[0:3]-sv,sigmas[3:6])) - ys = lambda sdev, C: np.array([-C[0]*sdev[1]-C[5]*sdev[2], -C[1]*sdev[0]-C[2]*sdev[2], - -C[4]*sdev[0]-C[3]*sdev[1], C[6]*sdev[3], C[7]*sdev[4], C[8]*sdev[5]]) - p,q = ys(sdev, C), ys(sdev, D) - pLambdas, qLambdas = principalStress(p), principalStress(q) # no sort - - m2 = m/2.0; x3 = range(3); num = len(sv) - PiQj = np.array([(pLambdas[i,:]-qLambdas[j,:]) for i in x3 for j in x3]) - QiPj = np.array([(qLambdas[i,:]-pLambdas[j,:]) for i in x3 for j in x3]).reshape(3,3,num) - PiQjs = PiQj**2 - left = np.sum(PiQjs**m2,axis=0) - r = (0.25*left)**(1.0/m)/eqStress - - if not Jac: - return (r - 1.0).ravel() - else: - drdl, drdm = r/m/left, r*math_ln(0.25*left)*(-1.0/m/m) - dldm = np.sum(PiQjs**m2*math_ln(PiQjs),axis=0)*0.5 - dPdc, dQdd = principalStrs_Der(p, sdev, dim), principalStrs_Der(q, sdev, dim) - PiQjs3d = ( PiQjs**(m2-1.0) ).reshape(3,3,num) - dldP = -m*np.array([np.diag(np.dot(PiQjs3d[:,:,i], QiPj [:,:,i])) for i in range(num)]).T - dldQ = m*np.array([np.diag(np.dot(QiPj [:,:,i], PiQjs3d[:,:,i])) for i in range(num)]).T - - jm = drdl*dldm + drdm - jc = drdl*np.sum([dldP[i]*dPdc[i] for i in x3],axis=0) - jd = drdl*np.sum([dldQ[i]*dQdd[i] for i in x3],axis=0) - - if mFix[0]: return np.vstack((jc,jd)).T - else: return np.vstack((jc,jd,jm)).T - -def KarafillisBoyce(eqStress, paras, sigmas, mFix, criteria, dim, Jac=False): - """ - Karafillis-Boyce - - the fitted parameters are - c11,c12,c13,c14,c15,c16,c,m for 3D - c11,c12,c13,c14,c,m for plane stress - 0 1 and self.dimen == 2: - return fitCriteria[self.name]['labels'][1] - else: - return fitCriteria[self.name]['labels'][0] - - def report_name(self): - return fitCriteria[self.name]['name'] - - def fit(self,stress): - global fitResults; fitErrors; fitResidual - if options.exponent > 0.0: nExponent = options.exponent - else: nExponent = 0 - nameCriterion = self.name.lower() - criteria = Criteria(nameCriterion,self.uniaxial,self.expo, self.dimen) - bounds = fitCriteria[nameCriterion]['bound'][dDim] # Default bounds, no bound - guess0 = Guess # Default initial guess, depends on bounds - - if fitResults == []: - initialguess = guess0 - else: - initialguess = np.array(fitResults[-1]) - - ydata = np.zeros(np.shape(stress)[1]) - try: - popt, pcov, infodict, errmsg, ierr = \ - leastsqBound (criteria.fun, initialguess, args=(ydata,stress), - bounds=bounds, Dfun=criteria.jac, full_output=True) - if ierr not in [1, 2, 3, 4]: - raise RuntimeError("Optimal parameters not found: "+errmsg) - else: - residual = criteria.fun(popt, ydata, stress) - fitResidual.append(np.linalg.norm(residual)/np.sqrt(len(residual))) - if (len(ydata) > len(initialguess)) and pcov is not None: - s_sq = (criteria.fun(popt, *(ydata,stress))**2).sum()/(len(ydata)-len(initialguess)) - pcov = pcov * s_sq - perr = np.sqrt(np.diag(pcov)) - fitResults.append(popt.tolist()) - fitErrors .append(perr.tolist()) - - popt = np.concatenate((np.array(popt), np.repeat(options.exponent,nExponent))) - perr = np.concatenate((np.array(perr), np.repeat(0.0,nExponent))) - - damask.util.croak('Needed {} function calls for fitting'.format(infodict['nfev'])) - except Exception as detail: - damask.util.croak(detail) - pass - return popt - -#--------------------------------------------------------------------------------------------------- -class myThread (threading.Thread): - """Runner""" - - def __init__(self, threadID): - threading.Thread.__init__(self) - self.threadID = threadID - def run(self): - semaphore.acquire() - conv=converged() - semaphore.release() - while not conv: - doSim(self.name) - semaphore.acquire() - conv=converged() - semaphore.release() - -def doSim(thread): - semaphore.acquire() - global myLoad - loadNo=loadcaseNo() - if not os.path.isfile('%s.load'%loadNo): - damask.util.croak('Generating load case for simulation %s (%s)'%(loadNo,thread)) - f=open('%s.load'%loadNo,'w') - f.write(myLoad.getLoadcase(loadNo)) - f.close() - semaphore.release() - else: semaphore.release() - -# if spectralOut does not exist, run simulation - semaphore.acquire() - if not os.path.isfile('%s_%i.spectralOut'%(options.geometry,loadNo)): - damask.util.croak('Starting simulation %i (%s)'%(loadNo,thread)) - semaphore.release() - damask.util.execute('DAMASK_spectral -g %s -l %i'%(options.geometry,loadNo)) - else: semaphore.release() - -# if ASCII tables do not exist, run postprocessing - semaphore.acquire() - if not os.path.isfile('./postProc/%s_%i.txt'%(options.geometry,loadNo)): - damask.util.croak('Starting post processing for simulation %i (%s)'%(loadNo,thread)) - semaphore.release() - try: - damask.util.execute('postResults --cr f,p --co totalshear %s_%i.spectralOut'%(options.geometry,loadNo)) - except: - damask.util.execute('postResults --cr f,p %s_%i.spectralOut'%(options.geometry,loadNo)) - damask.util.execute('addCauchy ./postProc/%s_%i.txt'%(options.geometry,loadNo)) - damask.util.execute('addStrainTensors -0 -v ./postProc/%s_%i.txt'%(options.geometry,loadNo)) - damask.util.execute('addMises -s Cauchy -e ln(V) ./postProc/%s_%i.txt'%(options.geometry,loadNo)) - else: semaphore.release() - -# reading values from ASCII table (including linear interpolation between points) - semaphore.acquire() - damask.util.croak('Reading values from simulation %i (%s)'%(loadNo,thread)) - refFile = './postProc/%s_%i.txt'%(options.geometry,loadNo) - table = damask.ASCIItable(refFile,readonly=True) - table.head_read() - - thresholdKey = {'equivalentStrain':'Mises(ln(V))', - 'totalshear': 'totalshear', - }[options.fitting] - - for l in [thresholdKey,'1_Cauchy']: - if l not in table.labels(raw = True): damask.util.croak('%s not found'%l) - semaphore.release() - - table.data_readArray(['%i_Cauchy'%(i+1) for i in range(9)]+[thresholdKey]+['%i_ln(V)'%(i+1) for i in range(9)]) - - validity = np.zeros((int(options.yieldValue[2])), dtype=bool) # found data for desired threshold - yieldStress = np.empty((int(options.yieldValue[2]),6),'d') - deformationRate = np.empty((int(options.yieldValue[2]),6),'d') - - line = 0 - for i,threshold in enumerate(np.linspace(options.yieldValue[0],options.yieldValue[1],options.yieldValue[2])): - while line < np.shape(table.data)[0]: - if abs(table.data[line,9])>= threshold: - upper,lower = abs(table.data[line,9]),abs(table.data[line-1,9]) # values for linear interpolation - stress = np.array(table.data[line-1,0:9] * (upper-threshold)/(upper-lower) + \ - table.data[line ,0:9] * (threshold-lower)/(upper-lower)).reshape(3,3) # linear interpolation of stress values - yieldStress[i,:] = t33toSym6(stress) - - dstrain= np.array(table.data[line,10:] - table.data[line-1,10:]).reshape(3,3) - deformationRate[i,:] = t33toSym6(dstrain) - - validity[i] = True - break - else: - line+=1 - if not validity[i]: - semaphore.acquire() - damask.util.croak('The data of result %i at the threshold %f is invalid,'%(loadNo,threshold)\ - +'the fitting at this point is skipped') - semaphore.release() - -# do the actual fitting procedure and write results to file - semaphore.acquire() - global stressAll, strainAll - f=open(options.geometry+'_'+options.criterion+'_'+str(time.time())+'.txt','w') - f.write(' '.join([options.fitting]+myFit.report_labels())+'\n') - try: - for i,threshold in enumerate(np.linspace(options.yieldValue[0],options.yieldValue[1],options.yieldValue[2])): - if validity[i]: - stressAll[i]=np.append(stressAll[i], yieldStress[i]/stressUnit) - strainAll[i]=np.append(strainAll[i], deformationRate[i]) - f.write( str(threshold)+' '+ - ' '.join(map(str,myFit.fit(stressAll[i].reshape(len(stressAll[i])//6,6).transpose())))+'\n') - except Exception: - damask.util.croak('Could not fit results of simulation (%s)'%thread) - semaphore.release() - return - damask.util.croak('\n') - semaphore.release() - -def loadcaseNo(): - global N_simulations - N_simulations+=1 - return N_simulations - -def converged(): - global N_simulations; fitResidual - - if N_simulations < options.max: - if len(fitResidual) > 5 and N_simulations >= options.min: - residualList = np.array(fitResidual[len(fitResidual)-5:]) - if np.std(residualList)/np.max(residualList) < 0.05: - return True - return False - else: - return True - -# -------------------------------------------------------------------- -# MAIN -# -------------------------------------------------------------------- - -parser = OptionParser(option_class=damask.extendableOption, usage='%prog options [file[s]]', description = """ -Performs calculations with various loads on given geometry file and fits yield surface. - -""", version = scriptID) - -# maybe make an option to specifiy if 2D/3D fitting should be done? - -parser.add_option('-l','--load' , dest='load', type='float', nargs=3, - help='load: final strain; increments; time %default', metavar='float int float') -parser.add_option('-g','--geometry', dest='geometry', type='string', - help='name of the geometry file [%default]', metavar='string') -parser.add_option('-c','--criterion', dest='criterion', choices=fitCriteria.keys(), - help='criterion for stopping simulations [%default]', metavar='string') -parser.add_option('-f','--fitting', dest='fitting', choices=thresholdParameter, - help='yield criterion [%default]', metavar='string') -parser.add_option('-y','--yieldvalue', dest='yieldValue', type='float', nargs=3, - help='yield points: start; end; count %default', metavar='float float int') -parser.add_option('--min', dest='min', type='int', - help='minimum number of simulations [%default]', metavar='int') -parser.add_option('--max', dest='max', type='int', - help='maximum number of iterations [%default]', metavar='int') -parser.add_option('-t','--threads', dest='threads', type='int', - help='number of parallel executions [%default]', metavar='int') -parser.add_option('-b','--bound', dest='bounds', type='float', nargs=2, - help='yield points: start; end; count %default', metavar='float float') -parser.add_option('-d','--dimension', dest='dimension', type='choice', choices=['2','3'], - help='dimension of the virtual test [%default]', metavar='int') -parser.add_option('-e', '--exponent', dest='exponent', type='float', - help='exponent of non-quadratic criteria', metavar='int') -parser.add_option('-u', '--uniaxial', dest='eqStress', type='float', - help='Equivalent stress', metavar='float') - -parser.set_defaults(min = 12, - max = 30, - threads = 4, - yieldValue = (0.002,0.004,2), - load = (0.010,100,100.0), - criterion = 'vonmises', - fitting = 'totalshear', - geometry = '20grains16x16x16', - bounds = None, - dimension = '3', - exponent = -1.0, - ) - -options = parser.parse_args()[0] - -if options.threads < 1: - parser.error('invalid number of threads {}'.format(options.threads)) -if options.min < 0: - parser.error('invalid minimum number of simulations {}'.format(options.min)) -if options.max < options.min: - parser.error('invalid maximum number of simulations (below minimum)') -if options.yieldValue[0] > options.yieldValue[1]: - parser.error('invalid yield start (below yield end)') -if options.yieldValue[2] != int(options.yieldValue[2]): - parser.error('count must be an integer') - -for check in [options.geometry+'.geom','numerics.config','material.config']: - if not os.path.isfile(check): - damask.util.croak('"{}" file not found'.format(check)) - -options.dimension = int(options.dimension) - -stressUnit = 1.0e9 if options.criterion == 'hill1948' else 1.0e6 - - -if options.dimension not in fitCriteria[options.criterion]['dimen']: - parser.error('invalid dimension for selected criterion') - -if options.criterion not in ['vonmises','tresca','drucker','hill1948'] and options.eqStress is None: - parser.error('please specify an equivalent stress (e.g. fitting to von Mises)') - -# global variables -fitResults = [] -fitErrors = [] -fitResidual = [] -stressAll= [np.zeros(0,'d').reshape(0,0) for i in range(int(options.yieldValue[2]))] -strainAll= [np.zeros(0,'d').reshape(0,0) for i in range(int(options.yieldValue[2]))] -N_simulations=0 -Guess = [] -threads=[] -semaphore=threading.Semaphore(1) -dDim = None -myLoad = None -myFit = None - -run = runFit(options.exponent, options.eqStress, options.dimension, options.criterion) - -damask.util.croak('Finished fitting to yield criteria') diff --git a/processing/misc/yieldSurfaceFast.py b/processing/misc/yieldSurfaceFast.py deleted file mode 100755 index c58dca733..000000000 --- a/processing/misc/yieldSurfaceFast.py +++ /dev/null @@ -1,1513 +0,0 @@ -#!/usr/bin/env python2.7 -# -*- coding: UTF-8 no BOM -*- - -import threading,time,os -import numpy as np -from optparse import OptionParser -import damask -from damask.util import leastsqBound -from scipy.optimize import nnls - -scriptName = os.path.splitext(os.path.basename(__file__))[0] -scriptID = ' '.join([scriptName,damask.version]) - -def runFit(exponent, eqStress, dimension, criterion): - global threads, myFit, myLoad - global fitResidual - global Guess, dDim - - if options.criterion!='facet': - dDim = dimension - 3 - nParas = len(fitCriteria[criterion]['bound'][dDim]) - nExpo = fitCriteria[criterion]['nExpo'] - - if exponent > 0.0: # User defined exponents - nParas = nParas-nExpo - fitCriteria[criterion]['bound'][dDim] = fitCriteria[criterion]['bound'][dDim][:nParas] - - for i in range(nParas): - temp = fitCriteria[criterion]['bound'][dDim][i] - if fitCriteria[criterion]['bound'][dDim][i] == (None,None): - Guess.append(1.0) - else: - g = (temp[0]+temp[1])/2.0 - if g == 0: g = temp[1]*0.5 - Guess.append(g) - - myLoad = Loadcase(options.load[0],options.load[1],options.load[2],options.flag,options.yieldValue, - nSet = 10, dimension = dimension, vegter = options.criterion=='vegter') - - - myFit = Criterion(exponent,eqStress, dimension, criterion) - for t in range(options.threads): - threads.append(myThread(t)) - threads[t].start() - - for t in range(options.threads): - threads[t].join() - - if options.criterion=='facet': - doFacetFit() - - damask.util.croak('Residuals') - damask.util.croak(fitResidual) - -def doFacetFit(): - n = options.order - Data = np.zeros((options.numpoints, 10)) - for i in range(options.numpoints): - fileName = options.geometry + '_' + str(i+1) + '.yield' - data_i = np.loadtxt(fileName) - - sv = (data_i[0,0] + data_i[1,1] + data_i[2,2])/3.0 - - #convert stress and strain form the 6D to 5D space - S1 = np.sqrt(2.0)*(data_i[0,0] - data_i[1,1])/2.0 - S2 = np.sqrt(6.0)*(data_i[0,0] + data_i[1,1] - 2.0*sv)/2.0 - S3 = np.sqrt(2.0)*data_i[1,2] - S4 = np.sqrt(2.0)*data_i[2,0] - S5 = np.sqrt(2.0)*data_i[0,1] - - E1 = np.sqrt(2.0)*(data_i[3,0]-data_i[4,1])/2.0 - E2 = np.sqrt(6.0)*(data_i[3,0]+data_i[4,1])/2.0 - E3 = np.sqrt(2.0)*data_i[4,2] - E4 = np.sqrt(2.0)*data_i[5,0] - E5 = np.sqrt(2.0)*data_i[3,1] - - Data[i,:] = [E1,E2,E3,E4,E5,S1,S2,S3,S4,S5] - - Data[:,5:] = Data[:,5:] / 100000000.0 - - path=os.path.join(os.getcwd(),'final.mmm') - np.savetxt(path, Data, header='', comments='', fmt='% 15.10f') - - if options.dimension == 2: - reducedIndices = [0,1,4,5,6,9] - elif options.dimension == 3: - reducedIndices = [i for i in range(10)] - - numDirections = Data.shape[0] - Indices = np.arange(numDirections) - sdPairs = Data[:,reducedIndices][Indices,:] - numPairs = sdPairs.shape[0] - dimensionality = sdPairs.shape[1] / 2 - ds = sdPairs[:,0:dimensionality] - s = sdPairs[:,dimensionality::] - - A = np.zeros((numPairs, numPairs)) - B = np.ones((numPairs,)) - for i in range(numPairs): - for j in range(numPairs): - lamb = 1.0 - s_i = s[i,:] - ds_j = ds[j,:] - A[i,j] = lamb * (np.dot(s_i.ravel(), ds_j.ravel()) ** n) - - lambdas, residuals = nnls(A, B) - nonZeroTerms = np.logical_not(np.isclose(lambdas, 0.)) - numNonZeroTerms = np.sum(nonZeroTerms) - dataOut = np.zeros((numNonZeroTerms, 6)) - - if options.dimension == 2: - dataOut[:,0] = lambdas[nonZeroTerms] - dataOut[:,1] = ds[nonZeroTerms,:][:,0] - dataOut[:,2] = ds[nonZeroTerms,:][:,1] - dataOut[:,5] = ds[nonZeroTerms,:][:,2] - elif options.dimension == 3: - dataOut[:,0] = lambdas[nonZeroTerms] - dataOut[:,1] = ds[nonZeroTerms,:][:,0] - dataOut[:,2] = ds[nonZeroTerms,:][:,1] - dataOut[:,3] = ds[nonZeroTerms,:][:,2] - dataOut[:,4] = ds[nonZeroTerms,:][:,3] - dataOut[:,5] = ds[nonZeroTerms,:][:,4] - - headerText = 'facet\n 1 \n F \n {0:<3d} \n {1:<3d} '.format(n, numNonZeroTerms) - path=os.path.join(os.getcwd(),'facet_o{0}.fac'.format(n)) - np.savetxt(path, dataOut, header=headerText, comments='', fmt='% 15.10f') - -def principalStresses(sigmas): - """ - Computes principal stresses (i.e. eigenvalues) for a set of Cauchy stresses. - - sorted in descending order. - """ - lambdas=np.zeros(0,'d') - for i in range(np.shape(sigmas)[1]): - eigenvalues = np.linalg.eigvalsh(sym6toT33(sigmas[:,i])) - lambdas = np.append(lambdas,np.sort(eigenvalues)[::-1]) #append eigenvalues in descending order - lambdas = np.transpose(lambdas.reshape(np.shape(sigmas)[1],3)) - return lambdas - -def principalStress(p): - I = invariant(p) - - I1s3I2= (I[0]**2 - 3.0*I[1])**0.5 - numer = 2.0*I[0]**3 - 9.0*I[0]*I[1] + 27.0*I[2] - denom = 2.0*I1s3I2**3 - cs = numer/denom - - phi = np.arccos(cs)/3.0 - t1 = I[0]/3.0; t2 = 2.0/3.0*I1s3I2 - return np.array( [t1 + t2*np.cos(phi), - t1 + t2*np.cos(phi+np.pi*2.0/3.0), - t1 + t2*np.cos(phi+np.pi*4.0/3.0)]) - -def principalStrs_Der(p, s, dim, Karafillis=False): - """Derivative of principal stress with respect to stress""" - third = 1.0/3.0 - third2 = 2.0*third - - I = invariant(p) - I1s3I2= np.sqrt(I[0]**2 - 3.0*I[1]) - numer = 2.0*I[0]**3 - 9.0*I[0]*I[1] + 27.0*I[2] - denom = 2.0*I1s3I2**3 - cs = numer/denom - phi = np.arccos(cs)/3.0 - - dphidcs = -third/np.sqrt(1.0 - cs**2) - dcsddenom = 0.5*numer*(-1.5)*I1s3I2**(-5.0) - dcsdI1 = (6.0*I[0]**2 - 9.0*I[1])*denom + dcsddenom*(2.0*I[0]) - dcsdI2 = ( - 9.0*I[0])*denom + dcsddenom*(-3.0) - dcsdI3 = 27.0*denom - dphidI1, dphidI2, dphidI3 = dphidcs*dcsdI1, dphidcs*dcsdI2, dphidcs*dcsdI3 - - dI1s3I2dI1 = I[0]/I1s3I2 - dI1s3I2dI2 = -1.5/I1s3I2 - tcoeff = third2*I1s3I2 - - dSidIj = lambda theta : ( tcoeff*(-np.sin(theta))*dphidI1 + third2*dI1s3I2dI1*np.cos(theta) + third, - tcoeff*(-np.sin(theta))*dphidI2 + third2*dI1s3I2dI2*np.cos(theta), - tcoeff*(-np.sin(theta))*dphidI3) - dSdI = np.array([dSidIj(phi),dSidIj(phi+np.pi*2.0/3.0),dSidIj(phi+np.pi*4.0/3.0)]) # i=1,2,3; j=1,2,3 - -# calculate the derivation of principal stress with regards to the anisotropic coefficients - one = np.ones_like(s); zero = np.zeros_like(s); num = len(s) - dIdp = np.array([[one, one, one, zero, zero, zero], - [p[1]+p[2], p[2]+p[0], p[0]+p[1], -2.0*p[3], -2.0*p[4], -2.0*p[5]], - [p[1]*p[2]-p[4]**2, p[2]*p[0]-p[5]**2, p[0]*p[1]-p[3]**2, - -2.0*p[3]*p[2]+2.0*p[4]*p[5], -2.0*p[4]*p[0]+2.0*p[5]*p[3], -2.0*p[5]*p[1]+2.0*p[3]*p[4]] ]) - if Karafillis: - dpdc = np.array([[zero,s[0]-s[2],s[0]-s[1]], [s[1]-s[2],zero,s[1]-s[0]], [s[2]-s[1],s[2]-s[0],zero]])/3.0 - dSdp = np.array([np.dot(dSdI[:,:,i],dIdp[:,:,i]).T for i in range(num)]).T - if dim == 2: - temp = np.vstack([dSdp[:,3]*s[3]]).T.reshape(num,1,3).T - else: - temp = np.vstack([dSdp[:,3]*s[3],dSdp[:,4]*s[4],dSdp[:,5]*s[5]]).T.reshape(num,3,3).T - - return np.concatenate((np.array([np.dot(dSdp[:,0:3,i], dpdc[:,:,i]).T for i in range(num)]).T, - temp), axis=1) - else: - if dim == 2: - dIdc=np.array([[-dIdp[i,0]*s[1], -dIdp[i,1]*s[0], -dIdp[i,1]*s[2], - -dIdp[i,2]*s[1], -dIdp[i,2]*s[0], -dIdp[i,0]*s[2], - dIdp[i,3]*s[3] ] for i in range(3)]) - else: - dIdc=np.array([[-dIdp[i,0]*s[1], -dIdp[i,1]*s[0], -dIdp[i,1]*s[2], - -dIdp[i,2]*s[1], -dIdp[i,2]*s[0], -dIdp[i,0]*s[2], - dIdp[i,3]*s[3], dIdp[i,4]*s[4], dIdp[i,5]*s[5] ] for i in range(3)]) - return np.array([np.dot(dSdI[:,:,i],dIdc[:,:,i]).T for i in range(num)]).T - -def invariant(sigmas): - I = np.zeros(3) - s11,s22,s33,s12,s23,s31 = sigmas - I[0] = s11 + s22 + s33 - I[1] = s11*s22 + s22*s33 + s33*s11 - s12**2 - s23**2 - s31**2 - I[2] = s11*s22*s33 + 2.0*s12*s23*s31 - s12**2*s33 - s23**2*s11 - s31**2*s22 - return I - -def math_ln(x): - return np.log(x + 1.0e-32) - -def sym6toT33(sym6): - """Shape the symmetric stress tensor(6) into (3,3)""" - return np.array([[sym6[0],sym6[3],sym6[5]], - [sym6[3],sym6[1],sym6[4]], - [sym6[5],sym6[4],sym6[2]]]) - -def t33toSym6(t33): - """Shape the stress tensor(3,3) into symmetric (6)""" - return np.array([ t33[0,0], - t33[1,1], - t33[2,2], - (t33[0,1] + t33[1,0])/2.0, # 0 3 5 - (t33[1,2] + t33[2,1])/2.0, # * 1 4 - (t33[2,0] + t33[0,2])/2.0,]) # * * 2 - -class Criteria(object): - def __init__(self, criterion, uniaxialStress,exponent, dimension): - self.stress0 = uniaxialStress - if exponent < 0.0: # Fitting exponent m - self.mFix = [False, exponent] - else: # fixed exponent m - self.mFix = [True, exponent] - self.func = fitCriteria[criterion]['func'] - self.criteria = criterion - self.dim = dimension - def fun(self, paras, ydata, sigmas): - return self.func(self.stress0, paras, sigmas,self.mFix,self.criteria,self.dim) - def jac(self, paras, ydata, sigmas): - return self.func(self.stress0, paras, sigmas,self.mFix,self.criteria,self.dim,Jac=True) - -class Vegter(object): - """Vegter yield criterion""" - - def __init__(self, refPts, refNormals,nspace=11): - self.refPts, self.refNormals = self._getRefPointsNormals(refPts, refNormals) - self.hingePts = self._getHingePoints() - self.nspace = nspace - def _getRefPointsNormals(self,refPtsQtr,refNormalsQtr): - if len(refPtsQtr) == 12: - refPts = refPtsQtr - refNormals = refNormalsQtr - else: - refPts = np.empty([13,2]) - refNormals = np.empty([13,2]) - refPts[12] = refPtsQtr[0] - refNormals[12] = refNormalsQtr[0] - for i in range(3): - refPts[i] = refPtsQtr[i] - refPts[i+3] = refPtsQtr[3-i][::-1] - refPts[i+6] =-refPtsQtr[i] - refPts[i+9] =-refPtsQtr[3-i][::-1] - refNormals[i] = refNormalsQtr[i] - refNormals[i+3] = refNormalsQtr[3-i][::-1] - refNormals[i+6] =-refNormalsQtr[i] - refNormals[i+9] =-refNormalsQtr[3-i][::-1] - return refPts,refNormals - - def _getHingePoints(self): - """ - Calculate the hinge point B according to the reference points A,C and the normals n,m - - refPoints = np.array([[p1_x, p1_y], [p2_x, p2_y]]); - refNormals = np.array([[n1_x, n1_y], [n2_x, n2_y]]) - """ - def hingPoint(points, normals): - A1 = points[0][0]; A2 = points[0][1] - C1 = points[1][0]; C2 = points[1][1] - n1 = normals[0][0]; n2 = normals[0][1] - m1 = normals[1][0]; m2 = normals[1][1] - B1 = (m2*(n1*A1 + n2*A2) - n2*(m1*C1 + m2*C2))/(n1*m2-m1*n2) - B2 = (n1*(m1*C1 + m2*C2) - m1*(n1*A1 + n2*A2))/(n1*m2-m1*n2) - return np.array([B1,B2]) - return np.array([hingPoint(self.refPts[i:i+2],self.refNormals[i:i+2]) for i in range(len(self.refPts)-1)]) - - def getBezier(self): - def bezier(R,H): - b = [] - for mu in np.linspace(0.0,1.0,self.nspace): - b.append(np.array(R[0]*np.ones_like(mu) + 2.0*mu*(H - R[0]) + mu**2*(R[0]+R[1] - 2.0*H))) - return b - return np.array([bezier(self.refPts[i:i+2],self.hingePts[i]) for i in range(len(self.refPts)-1)]) - -def VetgerCriterion(stress,lankford, rhoBi0, theta=0.0): - """0-pure shear; 1-uniaxial; 2-plane strain; 3-equi-biaxial""" - def getFourierParas(r): - # get the value after Fourier transformation - nset = len(r) - lmatrix = np.empty([nset,nset]) - theta = np.linspace(0.0,np.pi/2,nset) - for i,th in enumerate(theta): - lmatrix[i] = np.array([np.cos(2*j*th) for j in range(nset)]) - return np.linalg.solve(lmatrix, r) - - nps = len(stress) - if nps%4 != 0: - damask.util.croak('Warning: the number of stress points is uncorrect, stress points of %s are missing in set %i'%( - ['eq-biaxial, plane strain & uniaxial', 'eq-biaxial & plane strain','eq-biaxial'][nps%4-1],nps/4+1)) - else: - nset = nps/4 - strsSet = stress.reshape(nset,4,2) - refPts = np.empty([4,2]) - - fouriercoeffs = np.array([np.cos(2.0*i*theta) for i in range(nset)]) - for i in range(2): - refPts[3,i] = sum(strsSet[:,3,i])/nset - for j in range(3): - refPts[j,i] = np.dot(getFourierParas(strsSet[:,j,i]), fouriercoeffs) - - -def Tresca(eqStress=None, #not needed/supported - paras=None, - sigmas=None, - mFix=None, #not needed/supported - criteria=None, #not needed/supported - dim=3, - Jac=False): - """ - Tresca yield criterion - - the fitted parameter is paras(sigma0) - """ - if not Jac: - lambdas = principalStresses(sigmas) - r = np.amax(np.array([abs(lambdas[2,:]-lambdas[1,:]),\ - abs(lambdas[1,:]-lambdas[0,:]),\ - abs(lambdas[0,:]-lambdas[2,:])]),0) - paras - return r.ravel() - else: - return -np.ones(len(sigmas)) - -def Cazacu_Barlat(eqStress=None, - paras=None, - sigmas=None, - mFix=None,#not needed/supported - criteria=None, - dim=3, #2D also possible - Jac=False): - """ - Cazacu-Barlat (CB) yield criterion - - the fitted parameters are: - a1,a2,a3,a6; b1,b2,b3,b4,b5,b10; c for plane stress - a1,a2,a3,a4,a5,a6; b1,b2,b3,b4,b5,b6,b7,b8,b9,b10,b11; c: for general case - mFix is ignored - """ - s11,s22,s33,s12,s23,s31 = sigmas - if dim == 2: - (a1,a2,a3,a4), (b1,b2,b3,b4,b5,b10), c = paras[0:4],paras[4:10],paras[10] - a5 = a6 = b6 = b7 = b8 = b9 = b11 = 0.0 - s33 = s23 = s31 = np.zeros_like(s11) - else: - (a1,a2,a3,a4,a5,a6), (b1,b2,b3,b4,b5,b6,b7,b8,b9,b10,b11), c = paras[0:6],paras[6:17],paras[17] - - s1_2, s2_2, s3_2, s12_2, s23_2, s31_2 = np.array([s11,s22,s33,s12,s23,s31])**2 - s1_3, s2_3, s3_3, s123, s321 = s11*s1_2, s22*s2_2, s33*s3_2,s11*s22*s33, s12*s23*s31 - d12_2,d23_2,d31_2 = (s11-s22)**2, (s22-s33)**2, (s33-s11)**2 - - J20 = ( a1*d12_2 + a2*d23_2 + a3*d31_2 )/6.0 + a4*s12_2 + a5*s23_2 + a6*s31_2 - J30 = ( (b1 +b2 )*s1_3 + (b3 +b4 )*s2_3 + ( b1+b4-b2 + b1+b4-b3 )*s3_3 )/27.0- \ - ( (b1*s22+b2*s33)*s1_2 + (b3*s33+b4*s11)*s2_2 + ((b1+b4-b2)*s11 + (b1+b4-b3)*s22)*s3_2 )/9.0 + \ - ( (b1+b4)*s123/9.0 + b11*s321 )*2.0 - \ - ( ( 2.0*b9 *s22 - b8*s33 - (2.0*b9 -b8)*s11 )*s31_2 + - ( 2.0*b10*s33 - b5*s22 - (2.0*b10-b5)*s11 )*s12_2 + - ( (b6+b7)*s11 - b6*s22 - b7*s33 )*s23_2 - )/3.0 - f0 = J20**3 - c*J30**2 - r = f0**(1.0/6.0)*np.sqrt(3.0)/eqStress - - if not Jac: - return (r - 1.0).ravel() - else: - drdf = r/f0/6.0 - dj2, dj3 = drdf*3.0*J20**2, -drdf*2.0*J30*c - jc = -drdf*J30**2 - - ja1,ja2,ja3 = dj2*d12_2/6.0, dj2*d23_2/6.0, dj2*d31_2/6.0 - ja4,ja5,ja6 = dj2*s12_2, dj2*s23_2, dj2*s31_2 - jb1 = dj3*( (s1_3 + 2.0*s3_3)/27.0 - s22*s1_2/9.0 - (s11+s22)*s3_2/9.0 + s123/4.5 ) - jb2 = dj3*( (s1_3 - s3_3)/27.0 - s33*s1_2/9.0 + s11 *s3_2/9.0 ) - jb3 = dj3*( (s2_3 - s3_3)/27.0 - s33*s2_2/9.0 + s22 *s3_2/9.0 ) - jb4 = dj3*( (s2_3 + 2.0*s3_3)/27.0 - s11*s2_2/9.0 - (s11+s22)*s3_2/9.0 + s123/4.5 ) - - jb5, jb10 = dj3*(s22 - s11)*s12_2/3.0, dj3*(s11 - s33)*s12_2/1.5 - jb6, jb7 = dj3*(s22 - s11)*s23_2/3.0, dj3*(s33 - s11)*s23_2/3.0 - jb8, jb9 = dj3*(s33 - s11)*s31_2/3.0, dj3*(s11 - s22)*s31_2/1.5 - jb11 = dj3*s321*2.0 - if dim == 2: - return np.vstack((ja1,ja2,ja3,ja4,jb1,jb2,jb3,jb4,jb5,jb10,jc)).T - else: - return np.vstack((ja1,ja2,ja3,ja4,ja5,ja6,jb1,jb2,jb3,jb4,jb5,jb6,jb7,jb8,jb9,jb10,jb11,jc)).T - -def Drucker(eqStress=None,#not needed/supported - paras=None, - sigmas=None, - mFix=None, #not needed/supported - criteria=None, - dim=3, - Jac=False): - """ - Drucker yield criterion - - the fitted parameters are - sigma0, C_D for Drucker(p=1); - sigma0, C_D, p for general Drucker - eqStress, mFix are invalid inputs - """ - if criteria == 'drucker': - sigma0, C_D= paras - p = 1.0 - else: - sigma0, C_D = paras[0:2] - if mFix[0]: p = mFix[1] - else: p = paras[-1] - I = invariant(sigmas) - J = np.zeros([3]) - J[1] = I[0]**2/3.0 - I[1] - J[2] = I[0]**3/13.5 - I[0]*I[1]/3.0 + I[2] - J2_3p = J[1]**(3.0*p) - J3_2p = J[2]**(2.0*p) - left = J2_3p - C_D*J3_2p - r = left**(1.0/(6.0*p))*3.0**0.5/sigma0 - - if not Jac: - return (r - 1.0).ravel() - else: - drdl = r/left/(6.0*p) - if criteria == 'drucker': - return np.vstack((-r/sigma0, -drdl*J3_2p)).T - else: - dldp = 3.0*J2_3p*math_ln(J[1]) - 2.0*C_D*J3_2p*math_ln(J[2]) - jp = drdl*dldp + r*math_ln(left)/(-6.0*p*p) - - if mFix[0]: return np.vstack((-r/sigma0, -drdl*J3_2p)).T - else: return np.vstack((-r/sigma0, -drdl*J3_2p, jp)).T - -def Hill1948(eqStress=None,#not needed/supported - paras=None, - sigmas=None, - mFix=None, #not needed/supported - criteria=None,#not needed/supported - dim=3, - Jac=False): - """ - Hill 1948 yield criterion - - the fitted parameters are: - F, G, H, L, M, N for 3D - F, G, H, N for 2D - """ - s11,s22,s33,s12,s23,s31 = sigmas - if dim == 2: # plane stress - jac = np.array([ s22**2, s11**2, (s11-s22)**2, 2.0*s12**2]) - else: # general case - jac = np.array([(s22-s33)**2,(s33-s11)**2,(s11-s22)**2, 2.0*s23**2,2.0*s31**2,2.0*s12**2]) - - if not Jac: - return (np.dot(paras,jac)/2.0-0.5).ravel() - else: - return jac.T - -def Hill1979(eqStress=None,#not needed/supported - paras=None, - sigmas=None, - mFix=None, - criteria=None,#not needed/supported - dim=3, - Jac=False): - """ - Hill 1979 yield criterion - - the fitted parameters are: f,g,h,a,b,c,m - """ - if mFix[0]: - m = mFix[1] - else: - m = paras[-1] - - coeff = paras[0:6] - s = principalStresses(sigmas) - diffs = np.array([s[1]-s[2], s[2]-s[0], s[0]-s[1],\ - 2.0*s[0]-s[1]-s[2], 2.0*s[1]-s[2]-s[0], 2.0*s[2]-s[0]-s[1]])**2 - - diffsm = diffs**(m/2.0) - left = np.dot(coeff,diffsm) - r = (0.5*left)**(1.0/m)/eqStress #left = base**mi - - if not Jac: - return (r-1.0).ravel() - else: - drdl, dldm = r/left/m, np.dot(coeff,diffsm*math_ln(diffs))*0.5 - jm = drdl*dldm + r*math_ln(0.5*left)*(-1.0/m/m) #/(-m**2) - - if mFix[0]: return np.vstack((drdl*diffsm)).T - else: return np.vstack((drdl*diffsm, jm)).T - -def Hosford(eqStress=None, - paras=None, - sigmas=None, - mFix=None, - criteria=None, - dim=3, - Jac=False): - """ - Hosford family criteria - - the fitted parameters are: - von Mises: sigma0 - Hershey: (1) sigma0, a, when a is not fixed; (2) sigma0, when a is fixed - general Hosford: (1) F,G,H, a, when a is not fixed; (2) F,G,H, when a is fixed - """ - if criteria == 'vonmises': - sigma0 = paras - coeff = np.ones(3) - a = 2.0 - elif criteria == 'hershey': - sigma0 = paras[0] - coeff = np.ones(3) - if mFix[0]: a = mFix[1] - else: a = paras[1] - else: - sigma0 = eqStress - coeff = paras[0:3] - if mFix[0]: a = mFix[1] - else: a = paras[3] - - s = principalStresses(sigmas) - diffs = np.array([s[1]-s[2], s[2]-s[0], s[0]-s[1]])**2 - diffsm = diffs**(a/2.0) - left = np.dot(coeff,diffsm) - r = (0.5*left)**(1.0/a)/sigma0 - - if not Jac: - return (r-1.0).ravel() - else: - if criteria == 'vonmises': # von Mises - return -r/sigma0 - else: - drdl, dlda = r/left/a, np.dot(coeff,diffsm*math_ln(diffs))*0.5 - ja = drdl*dlda + r*math_ln(0.5*left)*(-1.0/a/a) - if criteria == 'hershey': # Hershey - if mFix[0]: return -r/sigma0 - else: return np.vstack((-r/sigma0, ja)).T - else: # Anisotropic Hosford - if mFix[0]: return np.vstack((drdl*diffsm)).T - else: return np.vstack((drdl*diffsm, ja)).T - -def Barlat1989(eqStress=None, - paras=None, - sigmas=None, - mFix=None, - criteria=None, - dim=3, - Jac=False): - """ - Barlat-Lian 1989 yield criteria - - the fitted parameters are: - Anisotropic: a, h, p, m; m is optional - """ - a, h, p = paras[0:3] - if mFix[0]: m = mFix[1] - else: m = paras[-1] - - c = 2.0-a - s11,s22,s12 = sigmas[0], sigmas[1], sigmas[3] - k1,k2 = 0.5*(s11 + h*s22), (0.25*(s11 - h*s22)**2 + (p*s12)**2)**0.5 - fs = np.array([ (k1+k2)**2, (k1-k2)**2, 4.0*k2**2 ]); fm = fs**(m/2.0) - left = np.dot(np.array([a,a,c]),fm) - r = (0.5*left)**(1.0/m)/eqStress - - if not Jac: - return (r-1.0).ravel() - else: - dk1dh = 0.5*s22 - dk2dh, dk2dp = 0.25*(s11-h*s22)*(-s22)/k2, p*s12**2/k2 - dlda, dldc = fm[0]+fm[1], fm[2] - fm1 = fs**(m/2.0-1.0)*m - dldk1, dldk2 = a*fm1[0]*(k1+k2)+a*fm1[1]*(k1-k2), a*fm1[0]*(k1+k2)-a*fm1[1]*(k1-k2)+c*fm1[2]*k2*4.0 - drdl, drdm = r/m/left, r*math_ln(0.5*left)*(-1.0/m/m) - dldm = np.dot(np.array([a,a,c]),fm*math_ln(fs))*0.5 - - ja,jc = drdl*dlda, drdl*dldc - jh,jp = drdl*(dldk1*dk1dh + dldk2*dk2dh), drdl*dldk2*dk2dp - jm = drdl*dldm + drdm - - if mFix[0]: return np.vstack((ja,jc,jh,jp)).T - else: return np.vstack((ja,jc,jh,jp,jm)).T - -def Barlat1991(eqStress, paras, sigmas, mFix, criteria, dim, Jac=False): - """ - Barlat 1991 criteria - - the fitted parameters are: - Anisotropic: a, b, c, f, g, h, m for 3D - a, b, c, h, m for plane stress - m is optional - """ - if dim == 2: coeff = paras[0:4] # plane stress - else: coeff = paras[0:6] # general case - if mFix[0]: m = mFix[1] - else: m = paras[-1] - - s11,s22,s33,s12,s23,s31 = sigmas - if dim == 2: - dXdx = np.array([s22,-s11,s11-s22,s12]) - A,B,C,H = np.array(coeff)[:,None]*dXdx; F=G=0.0 - else: - dXdx = np.array([s22-s33,s33-s11,s11-s22,s23,s31,s12]) - A,B,C,F,G,H = np.array(coeff)[:,None]*dXdx - - I2 = (F*F + G*G + H*H)/3.0+ ((A-C)**2+(C-B)**2+(B-A)**2)/54.0 - I3 = (C-B)*(A-C)*(B-A)/54.0 + F*G*H - ((C-B)*F*F + (A-C)*G*G + (B-A)*H*H)/6.0 - phi1 = np.arccos(I3/I2**1.5)/3.0 + np.pi/6.0; absc1 = 2.0*np.abs(np.cos(phi1)) - phi2 = phi1 + np.pi/3.0; absc2 = 2.0*np.abs(np.cos(phi2)) - phi3 = phi2 + np.pi/3.0; absc3 = 2.0*np.abs(np.cos(phi3)) - left = ( absc1**m + absc2**m + absc3**m ) - r = (0.5*left)**(1.0/m)*np.sqrt(3.0*I2)/eqStress - - if not Jac: - return (r - 1.0).ravel() - else: - dfdl = r/left/m - jm = r*math_ln(0.5*left)*(-1.0/m/m) + dfdl*0.5*( - absc1**m*math_ln(absc1) + absc2**m*math_ln(absc2) + absc3**m*math_ln(absc3) ) - - da,db,dc = (2.0*A-B-C)/18.0, (2.0*B-C-A)/18.0, (2.0*C-A-B)/18.0 - if dim == 2: - dI2dx = np.array([da, db, dc, H])/1.5*dXdx - dI3dx = np.array([ da*(B-C) + (H**2-G**2)/2.0, - db*(C-A) + (F**2-H**2)/2.0, - dc*(A-B) + (G**2-F**2)/2.0, - (G*F + (A-B))*H ])/3.0*dXdx - else: - dI2dx = np.array([da, db, dc, F,G,H])/1.5*dXdx - dI3dx = np.array([ da*(B-C) + (H**2-G**2)/2.0, - db*(C-A) + (F**2-H**2)/2.0, - dc*(A-B) + (G**2-F**2)/2.0, - (H*G*3.0 + (B-C))*F, - (F*H*3.0 + (C-A))*G, - (G*F*3.0 + (A-B))*H ])/3.0*dXdx - darccos = -1.0/np.sqrt(1.0 - I3**2/I2**3) - - dfdcos = lambda phi : dfdl*m*(2.0*abs(np.cos(phi)))**(m-1.0)*np.sign(np.cos(phi))*(-np.sin(phi)/1.5) - - dfdthe= (dfdcos(phi1) + dfdcos(phi2) + dfdcos(phi3)) - dfdI2, dfdI3 = dfdthe*darccos*I3*(-1.5)*I2**(-2.5)+r/2.0/I2, dfdthe*darccos*I2**(-1.5) - - if mFix[0]: return np.vstack((dfdI2*dI2dx + dfdI3*dI3dx)).T - else: return np.vstack((dfdI2*dI2dx + dfdI3*dI3dx, jm)).T - -def BBC2000(eqStress, paras, sigmas, mFix, criteria, dim, Jac=False): - """ - BBC2000 yield criterion - - the fitted parameters are - d,e,f,g, b,c,a, k; k is optional - criteria are invalid input - """ - d,e,f,g, b,c,a= paras[0:7] - if mFix[0]: k = mFix[1] - else: k = paras[-1] - - s11,s22,s12 = sigmas[0], sigmas[1], sigmas[3] - k2 = 2.0*k; k1 = k - 1.0 - M,N,P,Q,R = d+e, e+f, (d-e)/2.0, (e-f)/2.0, g**2 - Gamma = M*s11 + N*s22 - Psi = ( (P*s11 + Q*s22)**2 + s12**2*R )**0.5 - - l1, l2, l3 = b*Gamma + c*Psi, b*Gamma - c*Psi, 2.0*c*Psi - l1s,l2s,l3s = l1**2, l2**2, l3**2 - - left = a*l1s**k + a*l2s**k + (1-a)*l3s**k - r = left**(1.0/k2)/eqStress - if not Jac: - return (r - 1.0).ravel() - else: - drdl,drdk = r/left/k2, r*math_ln(left)*(-1.0/k2/k) - dldl1,dldl2,dldl3 = a*k2*(l1s**k1)*l1, a*k2*(l2s**k1)*l2, (1-a)*k2*(l3s**k1)*l3 - dldGama, dldPsi = (dldl1 + dldl2)*b, (dldl1 - dldl2 + 2.0*dldl3)*c - temp = (P*s11 + Q*s22)/Psi - dPsidP, dPsidQ, dPsidR = temp*s11, temp*s22, 0.5*s12**2/Psi - dlda = l1s**k + l2s**k - l3s**k - dldb = dldl1*Gamma + dldl2*Gamma - dldc = dldl1*Psi - dldl2*Psi + dldl3*2.0*Psi - dldk = a*math_ln(l1s)*l1s**k + a*math_ln(l2s)*l2s**k + (1-a)*math_ln(l3s)*l3s**k - - J = drdl*np.array([dldGama*s11+dldPsi*dPsidP*0.5, dldGama*(s11+s22)+dldPsi*(-dPsidP+dPsidQ)*0.5, #jd,je - dldGama*s22-dldPsi*dPsidQ*0.5, dldPsi*dPsidR*2.0*g, #jf,jg - dldb, dldc, dlda]) #jb,jc,ja - if mFix[0]: return np.vstack(J).T - else: return np.vstack((J, drdl*dldk + drdk)).T - - -def BBC2003(eqStress, paras, sigmas, mFix, criteria, dim, Jac=False): - """ - BBC2003 yield criterion - - the fitted parameters are - M,N,P,Q,R,S,T,a, k; k is optional - criteria are invalid input - """ - M,N,P,Q,R,S,T,a = paras[0:8] - if mFix[0]: k = mFix[1] - else: k = paras[-1] - - s11,s22,s12 = sigmas[0], sigmas[1], sigmas[3] - k2 = 2.0*k; k1 = k - 1.0 - Gamma = 0.5 * (s11 + M*s22) - Psi = ( 0.25*(N*s11 - P*s22)**2 + Q*Q*s12**2 )**0.5 - Lambda = ( 0.25*(R*s11 - S*s22)**2 + T*T*s12**2 )**0.5 - - l1, l2, l3 = Gamma + Psi, Gamma - Psi, 2.0*Lambda - l1s,l2s,l3s = l1**2, l2**2, l3**2 - left = a*l1s**k + a*l2s**k + (1-a)*l3s**k - r = left**(1.0/k2)/eqStress - if not Jac: - return (r - 1.0).ravel() - else: - drdl,drdk = r/left/k2, r*math_ln(left)*(-1.0/k2/k) - dldl1,dldl2,dldl3 = a*k2*(l1s**k1)*l1, a*k2*(l2s**k1)*l2, (1-a)*k2*(l3s**k1)*l3 - - dldGamma, dldPsi, dldLambda = dldl1+dldl2, dldl1-dldl2, 2.0*dldl3 - temp = 0.25/Psi*(N*s11 - P*s22) - dPsidN, dPsidP, dPsidQ = s11*temp, -s22*temp, Q*s12**2/Psi - temp = 0.25/Lambda*(R*s11 - S*s22) - dLambdadR, dLambdadS, dLambdadT = s11*temp, -s22*temp, T*s12**2/Psi - dldk = a*math_ln(l1s)*l1s**k + a*math_ln(l2s)*l2s**k + (1-a)*math_ln(l3s)*l3s**k - - J = drdl * np.array([dldGamma*s22*0.5, #jM - dldPsi*dPsidN, dldPsi*dPsidP, dldPsi*dPsidQ, #jN, jP, jQ - dldLambda*dLambdadR, dldLambda*dLambdadS, dldLambda*dLambdadT, #jR, jS, jT - l1s**k + l2s**k - l3s**k ]) #ja - - if mFix[0]: return np.vstack(J).T - else : return np.vstack((J, drdl*dldk+drdk)).T - -def BBC2005(eqStress, paras, sigmas, mFix, criteria, dim, Jac=False): - """ - BBC2005 yield criterion - - the fitted parameters are - a, b, L ,M, N, P, Q, R, k k are optional - criteria is invalid input - """ - a,b,L, M, N, P, Q, R = paras[0:8] - if mFix[0]: k = mFix[1] - else: k = paras[-1] - - s11 = sigmas[0]; s22 = sigmas[1]; s12 = sigmas[3] - k2 = 2.0*k - Gamma = L*s11 + M*s22 - Lambda = ( (N*s11 - P*s22)**2 + s12**2 )**0.5 - Psi = ( (Q*s11 - R*s22)**2 + s12**2 )**0.5 - - l1 = Lambda + Gamma; l2 = Lambda - Gamma; l3 = Lambda + Psi; l4 = Lambda - Psi - l1s = l1**2; l2s = l2**2; l3s = l3**2; l4s = l4**2 - left = a*l1s**k + a*l2s**k + b*l3s**k + b*l4s**k - sBar = left**(1.0/k2); r = sBar/eqStress - 1.0 - if not Jac: - return r.ravel() - else: - ln = lambda x : np.log(x + 1.0e-32) - expo = 0.5/k; k1 = k-1.0 - - dsBardl = expo*sBar/left/eqStress - dsBarde = sBar*ln(left); dedk = expo/(-k) - dldl1 = a*k*(l1s**k1)*(2.0*l1) - dldl2 = a*k*(l2s**k1)*(2.0*l2) - dldl3 = b*k*(l3s**k1)*(2.0*l3) - dldl4 = b*k*(l4s**k1)*(2.0*l4) - - dldLambda = dldl1 + dldl2 + dldl3 + dldl4 - dldGama = dldl1 - dldl2 - dldPsi = dldl3 - dldl4 - temp = (N*s11 - P*s22)/Lambda - dLambdadN = s11*temp; dLambdadP = -s22*temp - temp = (Q*s11 - R*s22)/Psi - dPsidQ = s11*temp; dPsidR = -s22*temp - dldk = a*ln(l1s)*l1s**k + a*ln(l2s)*l2s**k + b*ln(l3s)*l3s**k + b*ln(l4s)*l4s**k - - J = dsBardl * np.array( [ - l1s**k+l2s**k, l3s**k+l4s**k,dldGama*s11,dldGama*s22,dldLambda*dLambdadN, - dldLambda*dLambdadP, dldPsi*dPsidQ, dldPsi*dPsidR]) - - if mFix[0]: return np.vstack(J).T - else : return np.vstack(J, dldk+dsBarde*dedk).T - -def Yld2000(eqStress, paras, sigmas, mFix, criteria, dim, Jac=False): - """ - Yld2000 yield criterion - - C: c11,c22,c66 c12=c21=1.0 JAC NOT PASS - D: d11,d12,d21,d22,d66 - """ - C,D = paras[0:3], paras[3:8] - if mFix[0]: m = mFix[1] - else: m = paras[-1] - - s11, s22, s12 = sigmas[0],sigmas[1],sigmas[3] - X = np.array([ 2.0*C[0]*s11-C[0]*s22, 2.0*C[1]*s22-C[1]*s11, 3.0*C[2]*s12 ])/3.0 # a1,a2,a7 - Y = np.array([ (8.0*D[2]-2.0*D[0]-2.0*D[3]+2.0*D[1])*s11 + (4.0*D[3]-4.0*D[1]-4.0*D[2]+ D[0])*s22, - (4.0*D[0]-4.0*D[2]-4.0*D[1]+ D[3])*s11 + (8.0*D[1]-2.0*D[3]-2.0*D[0]+2.0*D[2])*s22, - 9.0*D[4]*s12 ])/9.0 - - def priStrs(s): - temp = np.sqrt( (s[0]-s[1])**2 + 4.0*s[2]**2 ) - return 0.5*(s[0]+s[1] + temp), 0.5*(s[0]+s[1] - temp) - m2 = m/2.0; m21 = m2 - 1.0 - (X1,X2), (Y1,Y2) = priStrs(X), priStrs(Y) # Principal values of X, Y - phi1s, phi21s, phi22s = (X1-X2)**2, (2.0*Y2+Y1)**2, (2.0*Y1+Y2)**2 - phi1, phi21, phi22 = phi1s**m2, phi21s**m2, phi22s**m2 - left = phi1 + phi21 + phi22 - r = (0.5*left)**(1.0/m)/eqStress - - if not Jac: - return (r-1.0).ravel() - else: - drdl, drdm = r/m/left, r*math_ln(0.5*left)*(-1.0/m/m) #/(-m*m) - dldm = ( phi1*math_ln(phi1s) + phi21*math_ln(phi21s) + phi22*math_ln(phi22s) )*0.5 - zero = np.zeros_like(s11); num = len(s11) - def dPrincipalds(X): - """Derivative of principla with respect to stress""" - temp = 1.0/np.sqrt( (X[0]-X[1])**2 + 4.0*X[2]**2 ) - dP1dsi = 0.5*np.array([ 1.0+temp*(X[0]-X[1]), 1.0-temp*(X[0]-X[1]), temp*4.0*X[2]]) - dP2dsi = 0.5*np.array([ 1.0-temp*(X[0]-X[1]), 1.0+temp*(X[0]-X[1]), -temp*4.0*X[2]]) - return np.array([dP1dsi, dP2dsi]) - - dXdXi, dYdYi = dPrincipalds(X), dPrincipalds(Y) - dXidC = np.array([ [ 2.0*s11-s22, zero, zero ], #dX11dC - [ zero, 2.0*s22-s11, zero ], #dX22dC - [ zero, zero, 3.0*s12 ] ])/3.0 #dX12dC - dYidD = np.array([ [ -2.0*s11+ s22, 2.0*s11-4.0*s22, 8.0*s11-4.0*s22, -2.0*s11+4.0*s22, zero ], #dY11dD - [ 4.0*s11-2.0*s22, -4.0*s11+8.0*s22, -4.0*s11+2.0*s22, s11-2.0*s22, zero ], #dY22dD - [ zero, zero, zero, zero, 9.0*s12 ] ])/9.0 #dY12dD - - dXdC=np.array([np.dot(dXdXi[:,:,i], dXidC[:,:,i]).T for i in range(num)]).T - dYdD=np.array([np.dot(dYdYi[:,:,i], dYidD[:,:,i]).T for i in range(num)]).T - - dldX = m*np.array([ phi1s**m21*(X1-X2), phi1s**m21*(X2-X1)]) - dldY = m*np.array([phi21s**m21*(2.0*Y2+Y1) + 2.0*phi22s**m21*(2.0*Y1+Y2), \ - phi22s**m21*(2.0*Y1+Y2) + 2.0*phi21s**m21*(2.0*Y2+Y1) ]) - jC = drdl*np.array([np.dot(dldX[:,i], dXdC[:,:,i]) for i in range(num)]).T - jD = drdl*np.array([np.dot(dldY[:,i], dYdD[:,:,i]) for i in range(num)]).T - - jm = drdl*dldm + drdm - if mFix[0]: return np.vstack((jC,jD)).T - else: return np.vstack((jC,jD,jm)).T - -def Yld200418p(eqStress, paras, sigmas, mFix, criteria, dim, Jac=False): - """ - Yld2004-18p yield criterion - - the fitted parameters are - C: c12,c21,c23,c32,c31,c13,c44,c55,c66; D: d12,d21,d23,d32,d31,d13,d44,d55,d66 for 3D - C: c12,c21,c23,c32,c31,c13,c44; D: d12,d21,d23,d32,d31,d13,d44 for 2D - and m, m are optional - criteria is ignored - """ - if dim == 2: C,D = np.append(paras[0:7],[0.0,0.0]), np.append(paras[7:14],[0.0,0.0]) - else: C,D = paras[0:9], paras[9:18] - if mFix[0]: m = mFix[1] - else: m = paras[-1] - - sv = (sigmas[0] + sigmas[1] + sigmas[2])/3.0 - sdev = np.vstack((sigmas[0:3]-sv,sigmas[3:6])) - ys = lambda sdev, C: np.array([-C[0]*sdev[1]-C[5]*sdev[2], -C[1]*sdev[0]-C[2]*sdev[2], - -C[4]*sdev[0]-C[3]*sdev[1], C[6]*sdev[3], C[7]*sdev[4], C[8]*sdev[5]]) - p,q = ys(sdev, C), ys(sdev, D) - pLambdas, qLambdas = principalStress(p), principalStress(q) # no sort - - m2 = m/2.0; x3 = range(3); num = len(sv) - PiQj = np.array([(pLambdas[i,:]-qLambdas[j,:]) for i in x3 for j in x3]) - QiPj = np.array([(qLambdas[i,:]-pLambdas[j,:]) for i in x3 for j in x3]).reshape(3,3,num) - PiQjs = PiQj**2 - left = np.sum(PiQjs**m2,axis=0) - r = (0.25*left)**(1.0/m)/eqStress - - if not Jac: - return (r - 1.0).ravel() - else: - drdl, drdm = r/m/left, r*math_ln(0.25*left)*(-1.0/m/m) - dldm = np.sum(PiQjs**m2*math_ln(PiQjs),axis=0)*0.5 - dPdc, dQdd = principalStrs_Der(p, sdev, dim), principalStrs_Der(q, sdev, dim) - PiQjs3d = ( PiQjs**(m2-1.0) ).reshape(3,3,num) - dldP = -m*np.array([np.diag(np.dot(PiQjs3d[:,:,i], QiPj [:,:,i])) for i in range(num)]).T - dldQ = m*np.array([np.diag(np.dot(QiPj [:,:,i], PiQjs3d[:,:,i])) for i in range(num)]).T - - jm = drdl*dldm + drdm - jc = drdl*np.sum([dldP[i]*dPdc[i] for i in x3],axis=0) - jd = drdl*np.sum([dldQ[i]*dQdd[i] for i in x3],axis=0) - - if mFix[0]: return np.vstack((jc,jd)).T - else: return np.vstack((jc,jd,jm)).T - -def KarafillisBoyce(eqStress, paras, sigmas, mFix, criteria, dim, Jac=False): - """ - Karafillis-Boyce - - the fitted parameters are - c11,c12,c13,c14,c15,c16,c,m for 3D - c11,c12,c13,c14,c,m for plane stress - 0 1 and self.dimen == 2: - return fitCriteria[self.name]['labels'][1] - else: - return fitCriteria[self.name]['labels'][0] - - def report_name(self): - return fitCriteria[self.name]['name'] - - def fit(self,stress): - global fitResults; fitErrors; fitResidual - if options.exponent > 0.0: nExponent = options.exponent - else: nExponent = 0 - nameCriterion = self.name.lower() - criteria = Criteria(nameCriterion,self.uniaxial,self.expo, self.dimen) - bounds = fitCriteria[nameCriterion]['bound'][dDim] # Default bounds, no bound - guess0 = Guess # Default initial guess, depends on bounds - - if fitResults == []: - initialguess = guess0 - else: - initialguess = np.array(fitResults[-1]) - - ydata = np.zeros(np.shape(stress)[1]) - try: - popt, pcov, infodict, errmsg, ierr = \ - leastsqBound (criteria.fun, initialguess, args=(ydata,stress), - bounds=bounds, Dfun=criteria.jac, full_output=True) - if ierr not in [1, 2, 3, 4]: - raise RuntimeError("Optimal parameters not found: "+errmsg) - else: - residual = criteria.fun(popt, ydata, stress) - fitResidual.append(np.linalg.norm(residual)/np.sqrt(len(residual))) - if (len(ydata) > len(initialguess)) and pcov is not None: - s_sq = (criteria.fun(popt, *(ydata,stress))**2).sum()/(len(ydata)-len(initialguess)) - pcov = pcov * s_sq - perr = np.sqrt(np.diag(pcov)) - fitResults.append(popt.tolist()) - fitErrors .append(perr.tolist()) - - popt = np.concatenate((np.array(popt), np.repeat(options.exponent,nExponent))) - perr = np.concatenate((np.array(perr), np.repeat(0.0,nExponent))) - - damask.util.croak('Needed {} function calls for fitting'.format(infodict['nfev'])) - except Exception as detail: - damask.util.croak(detail) - pass - return popt - -#--------------------------------------------------------------------------------------------------- -class myThread (threading.Thread): - """Runner""" - - def __init__(self, threadID): - threading.Thread.__init__(self) - self.threadID = threadID - def run(self): - semaphore.acquire() - conv=converged() - semaphore.release() - while not conv: - if options.criterion=='facet': - doSimForFacet(self.name) - else: - doSim(self.name) - semaphore.acquire() - conv=converged() - semaphore.release() - -def doSim(thread): - semaphore.acquire() - global myLoad - loadNo=loadcaseNo() - if not os.path.isfile('%s.load'%loadNo): - damask.util.croak('Generating load case for simulation %s (%s)'%(loadNo,thread)) - f=open('%s.load'%loadNo,'w') - f.write(myLoad.getLoadcase(loadNo)) - f.close() - semaphore.release() - else: semaphore.release() - -# if spectralOut does not exist, run simulation - semaphore.acquire() - if not os.path.isfile('%s_%i.spectralOut'%(options.geometry,loadNo)): - damask.util.croak('Starting simulation %i (%s)'%(loadNo,thread)) - semaphore.release() - damask.util.execute('DAMASK_spectral -g %s -l %i'%(options.geometry,loadNo)) - else: semaphore.release() - -# reading values from ASCII file - semaphore.acquire() - damask.util.croak('Reading values from simulation %i (%s)'%(loadNo,thread)) - semaphore.release() - refFile = '%s_%i.yield'%(options.geometry,loadNo) - yieldStress = np.empty((6),'d') - if not os.path.isfile(refFile): - validity = False - else: - validity = True - yieldData = np.loadtxt(refFile) - stress = yieldData[:3] - yieldStress = t33toSym6(stress) -# do the actual fitting procedure and write results to file - semaphore.acquire() - global stressAll - f=open(options.geometry+'_'+options.criterion+'_'+str(time.time())+'.txt','w') - f.write(' '.join([options.fitting]+myFit.report_labels())+'\n') - try: - if validity: - stressAll=np.append(stressAll, yieldStress/stressUnit) - f.write(' '.join(map(str,myFit.fit(stressAll.reshape(len(stressAll)//6,6).transpose())))+'\n') - except Exception: - damask.util.croak('Could not fit results of simulation (%s)'%thread) - semaphore.release() - return - damask.util.croak('\n') - semaphore.release() - -def doSimForFacet(thread): - semaphore.acquire() - global myLoad - loadNo=loadcaseNo() - if not os.path.isfile('%s.load'%loadNo): - damask.util.croak('Generating load case for simulation %s (%s)'%(loadNo,thread)) - f=open('%s.load'%loadNo,'w') - f.write(myLoad.getLoadcase(loadNo)) - f.close() - semaphore.release() - else: semaphore.release() - -# if spectralOut does not exist, run simulation - semaphore.acquire() - if not os.path.isfile('%s_%i.spectralOut'%(options.geometry,loadNo)): - damask.util.croak('Starting simulation %i (%s)'%(loadNo,thread)) - semaphore.release() - damask.util.execute('DAMASK_spectral -g %s -l %i'%(options.geometry,loadNo)) - else: semaphore.release() - -def loadcaseNo(): - global N_simulations - N_simulations+=1 - return N_simulations - -def converged(): - global N_simulations; fitResidual - - if options.criterion=='facet': - if N_simulations == options.numpoints: - return True - else: - return False - else: - if N_simulations < options.max: - if len(fitResidual) > 5 and N_simulations >= options.min: - residualList = np.array(fitResidual[len(fitResidual)-5:]) - if np.std(residualList)/np.max(residualList) < 0.05: - return True - return False - else: - return True - -# -------------------------------------------------------------------- -# MAIN -# -------------------------------------------------------------------- - -parser = OptionParser(option_class=damask.extendableOption, usage='%prog options [file[s]]', description = """ -Performs calculations with various loads on given geometry file and fits yield surface. - -""", version = scriptID) - -# maybe make an option to specifiy if 2D/3D fitting should be done? - -parser.add_option('-l','--load' , dest='load', type='float', nargs=3, - help='load: final strain; increments; time %default', metavar='float int float') -parser.add_option('-g','--geometry', dest='geometry', type='string', - help='name of the geometry file [%default]', metavar='string') -parser.add_option('-c','--criterion', dest='criterion', choices=fitCriteria.keys(), - help='criterion for stopping simulations [%default]', metavar='string') -parser.add_option('-f','--fitting', dest='fitting', choices=thresholdParameter, - help='yield criterion [%default]', metavar='string') -parser.add_option('-y','--yieldvalue', dest='yieldValue', type='float', - help='yield points %default', metavar='float') -parser.add_option('--min', dest='min', type='int', - help='minimum number of simulations [%default]', metavar='int') -parser.add_option('--max', dest='max', type='int', - help='maximum number of iterations [%default]', metavar='int') -parser.add_option('-t','--threads', dest='threads', type='int', - help='number of parallel executions [%default]', metavar='int') -parser.add_option('-b','--bound', dest='bounds', type='float', nargs=2, - help='yield points: start; end; count %default', metavar='float float') -parser.add_option('-d','--dimension', dest='dimension', type='choice', choices=['2','3'], - help='dimension of the virtual test [%default]', metavar='int') -parser.add_option('-e', '--exponent', dest='exponent', type='float', - help='exponent of non-quadratic criteria', metavar='int') -parser.add_option('-u', '--uniaxial', dest='eqStress', type='float', - help='Equivalent stress', metavar='float') -parser.add_option('--flag', dest='flag', type='string', - help='yield stop flag, totalStrain, plasticStrain or plasticWork', metavar='string') -parser.add_option('--numpoints', dest='numpoints', type='int', - help='number of yield points to fit facet potential [%default]', metavar='int') -parser.add_option('--order', dest='order', type='int', - help='order of facet potential [%default]', metavar='int') - -parser.set_defaults(min = 12, - max = 20, - threads = 4, - yieldValue = 0.002, - load = (0.010,100,100.0), - criterion = 'vonmises', - fitting = 'totalshear', - geometry = '20grains16x16x16', - bounds = None, - dimension = '3', - exponent = -1.0, - flag = 'totalStrain', - numpoints = 100, - order = 8 - ) - -options = parser.parse_args()[0] - -if options.threads < 1: - parser.error('invalid number of threads {}'.format(options.threads)) -if options.min < 0: - parser.error('invalid minimum number of simulations {}'.format(options.min)) -if options.max < options.min: - parser.error('invalid maximum number of simulations (below minimum)') - -for check in [options.geometry+'.geom','numerics.config','material.config']: - if not os.path.isfile(check): - damask.util.croak('"{}" file not found'.format(check)) - -options.dimension = int(options.dimension) - -stressUnit = 1.0e9 if options.criterion == 'hill1948' else 1.0e6 - - -if options.dimension not in fitCriteria[options.criterion]['dimen']: - parser.error('invalid dimension for selected criterion') - -if options.criterion not in ['vonmises','tresca','drucker','hill1948'] and options.eqStress is None: - parser.error('please specify an equivalent stress (e.g. fitting to von Mises)') - -# global variables -fitResults = [] -fitErrors = [] -fitResidual = [] -stressAll= np.zeros(0,'d').reshape(0,0) -N_simulations=0 -Guess = [] -threads=[] -semaphore=threading.Semaphore(1) -dDim = None -myLoad = None -myFit = None - -if options.criterion == 'facet': - run = runFit(options.exponent, options.eqStress, options.dimension, options.criterion) -else: - run = runFit(options.exponent, options.eqStress, options.dimension, options.criterion) - -damask.util.croak('Finished fitting to yield criteria') From d1fa2a14dc6fe63032c088378f66bdad2b1482ea Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 9 Dec 2019 05:28:00 +0100 Subject: [PATCH 045/223] was used only for yield surface fitting --- python/damask/util.py | 257 ------------------------------------------ 1 file changed, 257 deletions(-) diff --git a/python/damask/util.py b/python/damask/util.py index 63b9aed65..cf041f946 100644 --- a/python/damask/util.py +++ b/python/damask/util.py @@ -221,263 +221,6 @@ class return_message(): return srepr(self.message) -def leastsqBound(func, x0, args=(), bounds=None, Dfun=None, full_output=0, - col_deriv=0, ftol=1.49012e-8, xtol=1.49012e-8, - gtol=0.0, maxfev=0, epsfcn=None, factor=100, diag=None): - from scipy.optimize import _minpack - """ - Non-linear least square fitting (Levenberg-Marquardt method) with - bounded parameters. - the codes of transformation between int <-> ext refers to the work of - Jonathan J. Helmus: https://github.com/jjhelmus/leastsqbound-scipy - other codes refer to the source code of minpack.py: - - An internal parameter list is used to enforce contraints on the fitting - parameters. The transfomation is based on that of MINUIT package. - please see: F. James and M. Winkler. MINUIT User's Guide, 2004. - - bounds : list - (min, max) pairs for each parameter, use None for 'min' or 'max' - when there is no bound in that direction. - For example: if there are two parameters needed to be fitting, then - bounds is [(min1,max1), (min2,max2)] - - This function is based on 'leastsq' of minpack.py, the annotation of - other parameters can be found in 'least_squares.py'. - """ - - def _check_func(checker, argname, thefunc, x0, args, numinputs, - output_shape=None): - from numpy import shape - """The same as that of minpack.py""" - res = np.atleast_1d(thefunc(*((x0[:numinputs],) + args))) - if (output_shape is not None) and (shape(res) != output_shape): - if (output_shape[0] != 1): - if len(output_shape) > 1: - if output_shape[1] == 1: - return shape(res) - msg = "%s: there is a mismatch between the input and output " \ - "shape of the '%s' argument" % (checker, argname) - func_name = getattr(thefunc, '__name__', None) - if func_name: - msg += " '%s'." % func_name - else: - msg += "." - raise TypeError(msg) - if np.issubdtype(res.dtype, np.inexact): - dt = res.dtype - else: - dt = dtype(float) - return shape(res), dt - - def _int2extGrad(p_int, bounds): - """Calculate the gradients of transforming the internal (unconstrained) to external (constrained) parameter.""" - grad = np.empty_like(p_int) - for i, (x, bound) in enumerate(zip(p_int, bounds)): - lower, upper = bound - if lower is None and upper is None: # No constraints - grad[i] = 1.0 - elif upper is None: # only lower bound - grad[i] = x/np.sqrt(x*x + 1.0) - elif lower is None: # only upper bound - grad[i] = -x/np.sqrt(x*x + 1.0) - else: # lower and upper bounds - grad[i] = (upper - lower)*np.cos(x)/2.0 - return grad - - def _int2extFunc(bounds): - """Transform internal parameters into external parameters.""" - local = [_int2extLocal(b) for b in bounds] - - def _transform_i2e(p_int): - p_ext = np.empty_like(p_int) - p_ext[:] = [i(j) for i, j in zip(local, p_int)] - return p_ext - return _transform_i2e - - def _ext2intFunc(bounds): - """Transform external parameters into internal parameters.""" - local = [_ext2intLocal(b) for b in bounds] - - def _transform_e2i(p_ext): - p_int = np.empty_like(p_ext) - p_int[:] = [i(j) for i, j in zip(local, p_ext)] - return p_int - return _transform_e2i - - def _int2extLocal(bound): - """Transform a single internal parameter to an external parameter.""" - lower, upper = bound - if lower is None and upper is None: # no constraints - return lambda x: x - elif upper is None: # only lower bound - return lambda x: lower - 1.0 + np.sqrt(x*x + 1.0) - elif lower is None: # only upper bound - return lambda x: upper + 1.0 - np.sqrt(x*x + 1.0) - else: - return lambda x: lower + ((upper - lower)/2.0)*(np.sin(x) + 1.0) - - def _ext2intLocal(bound): - """Transform a single external parameter to an internal parameter.""" - lower, upper = bound - if lower is None and upper is None: # no constraints - return lambda x: x - elif upper is None: # only lower bound - return lambda x: np.sqrt((x - lower + 1.0)**2 - 1.0) - elif lower is None: # only upper bound - return lambda x: np.sqrt((x - upper - 1.0)**2 - 1.0) - else: - return lambda x: np.arcsin((2.0*(x - lower)/(upper - lower)) - 1.0) - - i2e = _int2extFunc(bounds) - e2i = _ext2intFunc(bounds) - - x0 = np.asarray(x0).flatten() - n = len(x0) - - if len(bounds) != n: - raise ValueError('the length of bounds is inconsistent with the number of parameters ') - - if not isinstance(args, tuple): - args = (args,) - - shape, dtype = _check_func('leastsq', 'func', func, x0, args, n) - m = shape[0] - - if n > m: - raise TypeError('Improper input: N=%s must not exceed M=%s' % (n, m)) - if epsfcn is None: - epsfcn = np.finfo(dtype).eps - - def funcWarp(x, *args): - return func(i2e(x), *args) - - xi0 = e2i(x0) - - if Dfun is None: - if maxfev == 0: - maxfev = 200*(n + 1) - retval = _minpack._lmdif(funcWarp, xi0, args, full_output, ftol, xtol, - gtol, maxfev, epsfcn, factor, diag) - else: - if col_deriv: - _check_func('leastsq', 'Dfun', Dfun, x0, args, n, (n, m)) - else: - _check_func('leastsq', 'Dfun', Dfun, x0, args, n, (m, n)) - if maxfev == 0: - maxfev = 100*(n + 1) - - def DfunWarp(x, *args): - return Dfun(i2e(x), *args) - - retval = _minpack._lmder(funcWarp, DfunWarp, xi0, args, full_output, col_deriv, - ftol, xtol, gtol, maxfev, factor, diag) - - errors = {0: ["Improper input parameters.", TypeError], - 1: ["Both actual and predicted relative reductions " - "in the sum of squares\n are at most %f" % ftol, None], - 2: ["The relative error between two consecutive " - "iterates is at most %f" % xtol, None], - 3: ["Both actual and predicted relative reductions in " - "the sum of squares\n are at most %f and the " - "relative error between two consecutive " - "iterates is at \n most %f" % (ftol, xtol), None], - 4: ["The cosine of the angle between func(x) and any " - "column of the\n Jacobian is at most %f in " - "absolute value" % gtol, None], - 5: ["Number of calls to function has reached " - "maxfev = %d." % maxfev, ValueError], - 6: ["ftol=%f is too small, no further reduction " - "in the sum of squares\n is possible.""" % ftol, - ValueError], - 7: ["xtol=%f is too small, no further improvement in " - "the approximate\n solution is possible." % xtol, - ValueError], - 8: ["gtol=%f is too small, func(x) is orthogonal to the " - "columns of\n the Jacobian to machine " - "precision." % gtol, ValueError], - 'unknown': ["Unknown error.", TypeError]} - - info = retval[-1] # The FORTRAN return value - - if info not in [1, 2, 3, 4] and not full_output: - if info in [5, 6, 7, 8]: - np.warnings.warn(errors[info][0], RuntimeWarning) - else: - try: - raise errors[info][1](errors[info][0]) - except KeyError: - raise errors['unknown'][1](errors['unknown'][0]) - - mesg = errors[info][0] - x = i2e(retval[0]) - - if full_output: - grad = _int2extGrad(retval[0], bounds) - retval[1]['fjac'] = (retval[1]['fjac'].T / np.take(grad, - retval[1]['ipvt'] - 1)).T - cov_x = None - if info in [1, 2, 3, 4]: - from numpy.dual import inv - from numpy.linalg import LinAlgError - perm = np.take(np.eye(n), retval[1]['ipvt'] - 1, 0) - r = np.triu(np.transpose(retval[1]['fjac'])[:n, :]) - R = np.dot(r, perm) - try: - cov_x = inv(np.dot(np.transpose(R), R)) - except LinAlgError as inverror: - print(inverror) - pass - return (x, cov_x) + retval[1:-1] + (mesg, info) - else: - return (x, info) - -def _general_function(params, ydata, xdata, function): - return function(xdata, *params) - ydata -def _weighted_general_function(params, ydata, xdata, function, weights): - return (function(xdata, *params) - ydata)*weights - -def curve_fit_bound(f, xdata, ydata, p0=None, sigma=None, bounds=None, **kw): - """Similar as 'curve_fit' in minpack.py.""" - if p0 is None: - # determine number of parameters by inspecting the function - import inspect - args, varargs, varkw, defaults = inspect.getargspec(f) - if len(args) < 2: - msg = "Unable to determine number of fit parameters." - raise ValueError(msg) - if 'self' in args: - p0 = [1.0] * (len(args)-2) - else: - p0 = [1.0] * (len(args)-1) - - if np.isscalar(p0): - p0 = np.array([p0]) - - args = (ydata, xdata, f) - if sigma is None: - func = _general_function - else: - func = _weighted_general_function - args += (1.0/np.asarray(sigma),) - - return_full = kw.pop('full_output', False) - res = leastsqBound(func, p0, args=args, bounds = bounds, full_output=True, **kw) - (popt, pcov, infodict, errmsg, ier) = res - - if ier not in [1, 2, 3, 4]: - msg = "Optimal parameters not found: " + errmsg - raise RuntimeError(msg) - - if (len(ydata) > len(p0)) and pcov is not None: - s_sq = (func(popt, *args)**2).sum()/(len(ydata)-len(p0)) - pcov = pcov * s_sq - else: - pcov = np.inf - - return (popt, pcov, infodict, errmsg, ier) if return_full else (popt, pcov) - - class ThreadPool: """Pool of threads consuming tasks from a queue.""" From 82c741d6bbfd5ebf2d0d82fb611c3d80a20110f5 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 9 Dec 2019 05:32:38 +0100 Subject: [PATCH 046/223] crystallite not needed anymore --- processing/pre/geom_fromDREAM3D.py | 1 - processing/pre/geom_fromOsteonGeometry.py | 3 --- processing/pre/geom_fromVoronoiTessellation.py | 1 - processing/pre/hybridIA_linODFsampling.py | 6 ------ processing/pre/patchFromReconstructedBoundaries.py | 4 ---- 5 files changed, 15 deletions(-) diff --git a/processing/pre/geom_fromDREAM3D.py b/processing/pre/geom_fromDREAM3D.py index 159793cd8..7d5b1442d 100755 --- a/processing/pre/geom_fromDREAM3D.py +++ b/processing/pre/geom_fromDREAM3D.py @@ -145,7 +145,6 @@ for name in filenames: config_header += [''] for i in range(np.nanmax(microstructure)): config_header += ['[{}{}]'.format(label,i+1), - 'crystallite 1', '(constituent)\tphase {}\ttexture {}\tfraction 1.0'.format(phase[i],i+1), ] diff --git a/processing/pre/geom_fromOsteonGeometry.py b/processing/pre/geom_fromOsteonGeometry.py index 499a8867f..627d92728 100755 --- a/processing/pre/geom_fromOsteonGeometry.py +++ b/processing/pre/geom_fromOsteonGeometry.py @@ -126,15 +126,12 @@ for i in range(3,np.max(microstructure)): config_header = ['', '[canal]', - 'crystallite 1', '(constituent)\tphase 1\ttexture 1\tfraction 1.0', '[interstitial]', - 'crystallite 1', '(constituent)\tphase 2\ttexture 2\tfraction 1.0' ] for i in range(3,np.max(microstructure)): config_header += ['[Point{}]'.format(i-2), - 'crystallite 1', '(constituent)\tphase 3\ttexture {}\tfraction 1.0'.format(i) ] diff --git a/processing/pre/geom_fromVoronoiTessellation.py b/processing/pre/geom_fromVoronoiTessellation.py index 28e215f85..d5ec43701 100755 --- a/processing/pre/geom_fromVoronoiTessellation.py +++ b/processing/pre/geom_fromVoronoiTessellation.py @@ -290,7 +290,6 @@ for name in filenames: config_header += [''] for ID in grainIDs: config_header += ['[Grain{}]'.format(ID), - 'crystallite 1', '(constituent)\tphase {}\ttexture {}\tfraction 1.0'.format(options.phase,ID) ] diff --git a/processing/pre/hybridIA_linODFsampling.py b/processing/pre/hybridIA_linODFsampling.py index cf1a473cf..caa747337 100755 --- a/processing/pre/hybridIA_linODFsampling.py +++ b/processing/pre/hybridIA_linODFsampling.py @@ -211,10 +211,6 @@ parser.add_option('-p','--phase', dest = 'phase', type = 'int', metavar = 'int', help = 'phase index to be used [%default]') -parser.add_option('--crystallite', - dest = 'crystallite', - type = 'int', metavar = 'int', - help = 'crystallite index to be used [%default]') parser.add_option('-r', '--rnd', dest = 'randomSeed', type = 'int', metavar = 'int', \ @@ -223,7 +219,6 @@ parser.set_defaults(randomSeed = None, number = 500, algorithm = 'IA', phase = 1, - crystallite = 1, ang = True, ) @@ -351,7 +346,6 @@ for name in filenames: for i,ID in enumerate(range(nSamples)): materialConfig += ['[Grain%s]'%(str(ID+1).zfill(formatwidth)), - 'crystallite %i'%options.crystallite, '(constituent) phase %i texture %s fraction 1.0'%(options.phase,str(ID+1).rjust(formatwidth)), ] diff --git a/processing/pre/patchFromReconstructedBoundaries.py b/processing/pre/patchFromReconstructedBoundaries.py index fabec0fdf..e9196916e 100755 --- a/processing/pre/patchFromReconstructedBoundaries.py +++ b/processing/pre/patchFromReconstructedBoundaries.py @@ -941,19 +941,15 @@ if any(output in options.output for output in ['spectral','mentat']): for i,grain in enumerate(rcData['grainMapping']): config+=['[grain{}]'.format(grain), - 'crystallite\t1', '(constituent)\tphase 1\ttexture {}\tfraction 1.0'.format(i+1)] if (options.xmargin > 0.0): config+=['[x-margin]', - 'crystallite\t1', '(constituent)\tphase 2\ttexture {}\tfraction 1.0\n'.format(len(rcData['grainMapping'])+1)] if (options.ymargin > 0.0): config+=['[y-margin]', - 'crystallite\t1', '(constituent)\tphase 2\ttexture {}\tfraction 1.0\n'.format(len(rcData['grainMapping'])+1)] if (options.xmargin > 0.0 and options.ymargin > 0.0): config+=['[xy-margin]', - 'crystallite\t1', '(constituent)\tphase 2\ttexture {}\tfraction 1.0\n'.format(len(rcData['grainMapping'])+1)] if (options.xmargin > 0.0 or options.ymargin > 0.0): From acc252ea5b8ebf316c75bf10cd291c67d12f9e44 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 9 Dec 2019 05:38:15 +0100 Subject: [PATCH 047/223] thermal/damage constitutive (i.e. source) results are not tested --- src/constitutive.f90 | 89 +------------------------------------------- src/crystallite.f90 | 16 +------- 2 files changed, 3 insertions(+), 102 deletions(-) diff --git a/src/constitutive.f90 b/src/constitutive.f90 index 4067c026a..6ad5c76cc 100644 --- a/src/constitutive.f90 +++ b/src/constitutive.f90 @@ -50,7 +50,6 @@ module constitutive constitutive_SandItsTangents, & constitutive_collectDotState, & constitutive_collectDeltaState, & - constitutive_postResults, & constitutive_results contains @@ -101,51 +100,14 @@ subroutine constitutive_init if (any(phase_kinematics == KINEMATICS_slipplane_opening_ID)) call kinematics_slipplane_opening_init if (any(phase_kinematics == KINEMATICS_thermal_expansion_ID)) call kinematics_thermal_expansion_init - write(6,'(/,a)') ' <<<+- constitutive init -+>>>' + write(6,'(/,a)') ' <<<+- constitutive init -+>>>'; flush(6) mainProcess: if (worldrank == 0) then !-------------------------------------------------------------------------------------------------- ! write description file for constitutive output call IO_write_jobFile(FILEUNIT,'outputConstitutive') PhaseLoop: do ph = 1,material_Nphase - activePhase: if (any(material_phaseAt == ph)) then - write(FILEUNIT,'(/,a,/)') '['//trim(config_name_phase(ph))//']' - - SourceLoop: do s = 1, phase_Nsources(ph) - knownSource = .true. ! assume valid - sourceType: select case (phase_source(s,ph)) - case (SOURCE_damage_isoBrittle_ID) sourceType - ins = source_damage_isoBrittle_instance(ph) - outputName = SOURCE_damage_isoBrittle_label - thisOutput => source_damage_isoBrittle_output - thisSize => source_damage_isoBrittle_sizePostResult - case (SOURCE_damage_isoDuctile_ID) sourceType - ins = source_damage_isoDuctile_instance(ph) - outputName = SOURCE_damage_isoDuctile_label - thisOutput => source_damage_isoDuctile_output - thisSize => source_damage_isoDuctile_sizePostResult - case (SOURCE_damage_anisoBrittle_ID) sourceType - ins = source_damage_anisoBrittle_instance(ph) - outputName = SOURCE_damage_anisoBrittle_label - thisOutput => source_damage_anisoBrittle_output - thisSize => source_damage_anisoBrittle_sizePostResult - case (SOURCE_damage_anisoDuctile_ID) sourceType - ins = source_damage_anisoDuctile_instance(ph) - outputName = SOURCE_damage_anisoDuctile_label - thisOutput => source_damage_anisoDuctile_output - thisSize => source_damage_anisoDuctile_sizePostResult - case default sourceType - knownSource = .false. - end select sourceType - if (knownSource) then - write(FILEUNIT,'(a)') '(source)'//char(9)//trim(outputName) - OutputSourceLoop: do o = 1,size(thisOutput(:,ins)) - if(len_trim(thisOutput(o,ins)) > 0) & - write(FILEUNIT,'(a,i4)') trim(thisOutput(o,ins))//char(9),thisSize(o,ins) - enddo OutputSourceLoop - endif - enddo SourceLoop - endif activePhase + if (any(material_phaseAt == ph)) write(FILEUNIT,'(/,a,/)') '['//trim(config_name_phase(ph))//']' enddo PhaseLoop close(FILEUNIT) endif mainProcess @@ -169,8 +131,6 @@ subroutine constitutive_init plasticState(ph)%sizeDotState) constitutive_source_maxSizeDotState = max(constitutive_source_maxSizeDotState, & maxval(sourceState(ph)%p(:)%sizeDotState)) - constitutive_source_maxSizePostResults = max(constitutive_source_maxSizePostResults, & - maxval(sourceState(ph)%p(:)%sizePostResults)) enddo PhaseLoop2 @@ -639,51 +599,6 @@ subroutine constitutive_collectDeltaState(S, Fe, Fi, ipc, ip, el) end subroutine constitutive_collectDeltaState -!-------------------------------------------------------------------------------------------------- -!> @brief returns array of constitutive results -!-------------------------------------------------------------------------------------------------- -function constitutive_postResults(S, Fi, ipc, ip, el) - - integer, intent(in) :: & - ipc, & !< component-ID of integration point - ip, & !< integration point - el !< element - real(pReal), dimension(sum(sourceState(material_phaseAt(ipc,el))%p(:)%sizePostResults)) :: & - constitutive_postResults - real(pReal), intent(in), dimension(3,3) :: & - Fi !< intermediate deformation gradient - real(pReal), intent(in), dimension(3,3) :: & - S !< 2nd Piola Kirchhoff stress - integer :: & - startPos, endPos - integer :: & - i, of, instance !< counter in source loop - - constitutive_postResults = 0.0_pReal - - - endPos = 0 - - SourceLoop: do i = 1, phase_Nsources(material_phaseAt(ipc,el)) - startPos = endPos + 1 - endPos = endPos + sourceState(material_phaseAt(ipc,el))%p(i)%sizePostResults - of = material_phasememberAt(ipc,ip,el) - sourceType: select case (phase_source(i,material_phaseAt(ipc,el))) - case (SOURCE_damage_isoBrittle_ID) sourceType - constitutive_postResults(startPos:endPos) = source_damage_isoBrittle_postResults(material_phaseAt(ipc,el),of) - case (SOURCE_damage_isoDuctile_ID) sourceType - constitutive_postResults(startPos:endPos) = source_damage_isoDuctile_postResults(material_phaseAt(ipc,el),of) - case (SOURCE_damage_anisoBrittle_ID) sourceType - constitutive_postResults(startPos:endPos) = source_damage_anisoBrittle_postResults(material_phaseAt(ipc,el),of) - case (SOURCE_damage_anisoDuctile_ID) sourceType - constitutive_postResults(startPos:endPos) = source_damage_anisoDuctile_postResults(material_phaseAt(ipc,el),of) - end select sourceType - - enddo SourceLoop - -end function constitutive_postResults - - !-------------------------------------------------------------------------------------------------- !> @brief writes constitutive results to HDF5 output file !-------------------------------------------------------------------------------------------------- diff --git a/src/crystallite.f90 b/src/crystallite.f90 index 292241001..dffd4079a 100644 --- a/src/crystallite.f90 +++ b/src/crystallite.f90 @@ -742,23 +742,9 @@ function crystallite_postResults(ipc, ip, el) ip, & !< integration point index ipc !< grain index - real(pReal), dimension(1+ & - 1+sum(sourceState(material_phaseAt(ipc,el))%p(:)%sizePostResults)) :: & - crystallite_postResults - integer :: & - c - + real(pReal), dimension(2) :: crystallite_postResults crystallite_postResults = 0.0_pReal - crystallite_postResults(1) = 0.0_pReal ! header-like information (length) - c = 1 - - crystallite_postResults(c+1) = real(sum(sourceState(material_phaseAt(ipc,el))%p(:)%sizePostResults),pReal) ! size of constitutive results - c = c + 1 - if (size(crystallite_postResults)-c > 0) & - crystallite_postResults(c+1:size(crystallite_postResults)) = & - constitutive_postResults(crystallite_S(1:3,1:3,ipc,ip,el), crystallite_Fi(1:3,1:3,ipc,ip,el), & - ipc, ip, el) end function crystallite_postResults From 07ebd8d1b3e92efe14e76d7b67c475a503539360 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 9 Dec 2019 05:48:37 +0100 Subject: [PATCH 048/223] only damage/thermal 'homogenization' postResults is currently needed --- src/crystallite.f90 | 18 ------------------ src/damage_nonlocal.f90 | 2 +- src/homogenization.f90 | 10 +++------- 3 files changed, 4 insertions(+), 26 deletions(-) diff --git a/src/crystallite.f90 b/src/crystallite.f90 index dffd4079a..4ae87b82e 100644 --- a/src/crystallite.f90 +++ b/src/crystallite.f90 @@ -108,7 +108,6 @@ module crystallite crystallite_stressTangent, & crystallite_orientations, & crystallite_push33ToRef, & - crystallite_postResults, & crystallite_results contains @@ -732,23 +731,6 @@ function crystallite_push33ToRef(ipc,ip,el, tensor33) end function crystallite_push33ToRef -!-------------------------------------------------------------------------------------------------- -!> @brief return results of particular grain -!-------------------------------------------------------------------------------------------------- -function crystallite_postResults(ipc, ip, el) - - integer, intent(in):: & - el, & !< element index - ip, & !< integration point index - ipc !< grain index - - real(pReal), dimension(2) :: crystallite_postResults - - crystallite_postResults = 0.0_pReal - -end function crystallite_postResults - - !-------------------------------------------------------------------------------------------------- !> @brief writes crystallite results to HDF5 output file !-------------------------------------------------------------------------------------------------- diff --git a/src/damage_nonlocal.f90 b/src/damage_nonlocal.f90 index 0a8a3c867..d37f68486 100644 --- a/src/damage_nonlocal.f90 +++ b/src/damage_nonlocal.f90 @@ -55,7 +55,7 @@ contains !-------------------------------------------------------------------------------------------------- subroutine damage_nonlocal_init - integer :: maxNinstance,homog,instance,o,i + integer :: maxNinstance,homog,instance,i integer :: sizeState integer :: NofMyHomog, h integer(kind(undefined_ID)) :: & diff --git a/src/homogenization.f90 b/src/homogenization.f90 index 0112f9cf5..c5250f9d2 100644 --- a/src/homogenization.f90 +++ b/src/homogenization.f90 @@ -583,7 +583,7 @@ end subroutine materialpoint_stressAndItsTangent !-------------------------------------------------------------------------------------------------- -!> @brief parallelized calculation of result array at material points +!> @brief calculation of result array at material points !-------------------------------------------------------------------------------------------------- subroutine materialpoint_postResults @@ -595,7 +595,6 @@ subroutine materialpoint_postResults i, & !< integration point number e !< element number - !$OMP PARALLEL DO PRIVATE(myNgrains,thePos,theSize) elementLooping: do e = FEsolving_execElem(1),FEsolving_execElem(2) myNgrains = homogenization_Ngrains(material_homogenizationAt(e)) IpLooping: do i = FEsolving_execIP(1,e),FEsolving_execIP(2,e) @@ -615,15 +614,12 @@ subroutine materialpoint_postResults thePos = thePos + 1 grainLooping :do g = 1,myNgrains - theSize = 1 + & - 1 + & - sum(sourceState(material_phaseAt(g,e))%p(:)%sizePostResults) - materialpoint_results(thePos+1:thePos+theSize,i,e) = crystallite_postResults(g,i,e) ! tell crystallite results + theSize = 2 + materialpoint_results(thePos+1:thePos+theSize,i,e) = 0.0_pReal thePos = thePos + theSize enddo grainLooping enddo IpLooping enddo elementLooping - !$OMP END PARALLEL DO end subroutine materialpoint_postResults From 1c180864022c5484813ced9b85725938441efc02 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 9 Dec 2019 05:58:27 +0100 Subject: [PATCH 049/223] not needed anymore --- src/constitutive.f90 | 18 ++++-------------- src/homogenization.f90 | 3 +-- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/src/constitutive.f90 b/src/constitutive.f90 index 6ad5c76cc..2d0688467 100644 --- a/src/constitutive.f90 +++ b/src/constitutive.f90 @@ -37,7 +37,6 @@ module constitutive integer, public, protected :: & constitutive_plasticity_maxSizeDotState, & - constitutive_source_maxSizePostResults, & constitutive_source_maxSizeDotState public :: & @@ -60,17 +59,9 @@ contains !-------------------------------------------------------------------------------------------------- subroutine constitutive_init - integer, parameter :: FILEUNIT = 204 integer :: & - o, & !< counter in output loop ph, & !< counter in phase loop - s, & !< counter in source loop - ins !< instance of plasticity/source - - integer, dimension(:,:), pointer :: thisSize - character(len=64), dimension(:,:), pointer :: thisOutput - character(len=32) :: outputName !< name of output, intermediate fix until HDF5 output is ready - logical :: knownSource + s !< counter in source loop !-------------------------------------------------------------------------------------------------- ! initialized plasticity @@ -105,16 +96,15 @@ subroutine constitutive_init mainProcess: if (worldrank == 0) then !-------------------------------------------------------------------------------------------------- ! write description file for constitutive output - call IO_write_jobFile(FILEUNIT,'outputConstitutive') + call IO_write_jobFile(204,'outputConstitutive') PhaseLoop: do ph = 1,material_Nphase - if (any(material_phaseAt == ph)) write(FILEUNIT,'(/,a,/)') '['//trim(config_name_phase(ph))//']' + if (any(material_phaseAt == ph)) write(204,'(/,a,/)') '['//trim(config_name_phase(ph))//']' enddo PhaseLoop - close(FILEUNIT) + close(204) endif mainProcess constitutive_plasticity_maxSizeDotState = 0 constitutive_source_maxSizeDotState = 0 - constitutive_source_maxSizePostResults = 0 PhaseLoop2:do ph = 1,material_Nphase !-------------------------------------------------------------------------------------------------- diff --git a/src/homogenization.f90 b/src/homogenization.f90 index c5250f9d2..5be988ec3 100644 --- a/src/homogenization.f90 +++ b/src/homogenization.f90 @@ -262,8 +262,7 @@ subroutine homogenization_init materialpoint_sizeResults = 1 & ! grain count + 1 + thermal_maxSizePostResults & + damage_maxSizePostResults & - + homogenization_maxNgrains * ( 1 & ! crystallite size - + 1 + constitutive_source_maxSizePostResults) + + homogenization_maxNgrains * 2 ! obsolete header information allocate(materialpoint_results(materialpoint_sizeResults,discretization_nIP,discretization_nElem)) write(6,'(/,a)') ' <<<+- homogenization init -+>>>' From eef6ae5733a662da968bb05318c994b9e05221e7 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 9 Dec 2019 06:12:14 +0100 Subject: [PATCH 050/223] shell scripts are deprecated --- .gitlab-ci.yml | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6e82561c5..a2d4fb6a2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -506,18 +506,6 @@ GridSolver: - master - release -Processing: - stage: createDocumentation - script: - - cd $DAMASKROOT/processing/pre - - $DAMASKROOT/PRIVATE/documenting/scriptHelpToWiki.py --debug *.py - - cd $DAMASKROOT/processing/post - - rm vtk2ang.py DAD*.py - - $DAMASKROOT/PRIVATE/documenting/scriptHelpToWiki.py --debug *.py - except: - - master - - release - ################################################################################################## backupData: stage: saveDocumentation @@ -528,7 +516,6 @@ backupData: - mv $TESTROOT/performance/time.png $BACKUP/${CI_PIPELINE_ID}_${CI_COMMIT_SHA}/ - mv $TESTROOT/performance/memory.png $BACKUP/${CI_PIPELINE_ID}_${CI_COMMIT_SHA}/ - mv $DAMASKROOT/PRIVATE/documenting/DAMASK_* $BACKUP/${CI_PIPELINE_ID}_${CI_COMMIT_SHA}/ - - mv $DAMASKROOT/processing $BACKUP/${CI_PIPELINE_ID}_${CI_COMMIT_SHA}/ only: - development From 5abe27ab60e81d47d01c35c141e8cc913b29e1ce Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 9 Dec 2019 06:24:37 +0100 Subject: [PATCH 051/223] only locally used --- src/source_damage_anisoBrittle.f90 | 4 ++-- src/source_damage_isoBrittle.f90 | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/source_damage_anisoBrittle.f90 b/src/source_damage_anisoBrittle.f90 index 9997f81e5..a68f7b315 100644 --- a/src/source_damage_anisoBrittle.f90 +++ b/src/source_damage_anisoBrittle.f90 @@ -21,10 +21,10 @@ module source_damage_anisoBrittle source_damage_anisoBrittle_offset, & !< which source is my current source mechanism? source_damage_anisoBrittle_instance !< instance of source mechanism - integer, dimension(:,:), allocatable, target, public :: & + integer, dimension(:,:), allocatable :: & source_damage_anisoBrittle_sizePostResult !< size of each post result output - character(len=64), dimension(:,:), allocatable, target, public :: & + character(len=64), dimension(:,:), allocatable :: & source_damage_anisoBrittle_output !< name of each post result output integer, dimension(:,:), allocatable :: & diff --git a/src/source_damage_isoBrittle.f90 b/src/source_damage_isoBrittle.f90 index 89f5a038c..f6b99555f 100644 --- a/src/source_damage_isoBrittle.f90 +++ b/src/source_damage_isoBrittle.f90 @@ -18,9 +18,9 @@ module source_damage_isoBrittle integer, dimension(:), allocatable, public, protected :: & source_damage_isoBrittle_offset, & source_damage_isoBrittle_instance - integer, dimension(:,:), allocatable, target, public :: & + integer, dimension(:,:), allocatable :: & source_damage_isoBrittle_sizePostResult - character(len=64), dimension(:,:), allocatable, target, public :: & + character(len=64), dimension(:,:), allocatable :: & source_damage_isoBrittle_output enum, bind(c) From 4be7aa990c0c4afbe507e65fe3817febbd003e45 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 10 Dec 2019 06:45:00 +0100 Subject: [PATCH 052/223] HDF5 results output for constitutive damage models --- src/source_damage_anisoBrittle.f90 | 52 +++++++++++---------------- src/source_damage_anisoDuctile.f90 | 56 ++++++++++++------------------ src/source_damage_isoBrittle.f90 | 54 ++++++++++++---------------- src/source_damage_isoDuctile.f90 | 55 ++++++++++++----------------- 4 files changed, 88 insertions(+), 129 deletions(-) diff --git a/src/source_damage_anisoBrittle.f90 b/src/source_damage_anisoBrittle.f90 index a68f7b315..5dc8b96af 100644 --- a/src/source_damage_anisoBrittle.f90 +++ b/src/source_damage_anisoBrittle.f90 @@ -13,6 +13,7 @@ module source_damage_anisoBrittle use discretization use config use lattice + use results implicit none private @@ -21,9 +22,6 @@ module source_damage_anisoBrittle source_damage_anisoBrittle_offset, & !< which source is my current source mechanism? source_damage_anisoBrittle_instance !< instance of source mechanism - integer, dimension(:,:), allocatable :: & - source_damage_anisoBrittle_sizePostResult !< size of each post result output - character(len=64), dimension(:,:), allocatable :: & source_damage_anisoBrittle_output !< name of each post result output @@ -61,7 +59,7 @@ module source_damage_anisoBrittle source_damage_anisoBrittle_init, & source_damage_anisoBrittle_dotState, & source_damage_anisobrittle_getRateAndItsTangent, & - source_damage_anisoBrittle_postResults + source_damage_anisoBrittle_results contains @@ -102,7 +100,6 @@ subroutine source_damage_anisoBrittle_init enddo enddo - allocate(source_damage_anisoBrittle_sizePostResult(maxval(phase_Noutput),Ninstance), source=0) allocate(source_damage_anisoBrittle_output(maxval(phase_Noutput),Ninstance)) source_damage_anisoBrittle_output = '' @@ -154,7 +151,6 @@ subroutine source_damage_anisoBrittle_init select case(outputs(i)) case ('anisobrittle_drivingforce') - source_damage_anisoBrittle_sizePostResult(i,source_damage_anisoBrittle_instance(p)) = 1 source_damage_anisoBrittle_output(i,source_damage_anisoBrittle_instance(p)) = outputs(i) prm%outputID = [prm%outputID, damage_drivingforce_ID] @@ -171,7 +167,6 @@ subroutine source_damage_anisoBrittle_init call material_allocateSourceState(phase,sourceOffset,NofMyPhase,1,1,0) - sourceState(phase)%p(sourceOffset)%sizePostResults = sum(source_damage_anisoBrittle_sizePostResult(:,instance)) sourceState(phase)%p(sourceOffset)%aTolState=param(instance)%aTol @@ -262,39 +257,32 @@ subroutine source_damage_anisobrittle_getRateAndItsTangent(localphiDot, dLocalph dLocalphiDot_dPhi = -sourceState(phase)%p(sourceOffset)%state(1,constituent) -end subroutine source_damage_anisobrittle_getRateAndItsTangent +end subroutine source_damage_anisoBrittle_getRateAndItsTangent !-------------------------------------------------------------------------------------------------- -!> @brief return array of local damage results +!> @brief writes results to HDF5 output file !-------------------------------------------------------------------------------------------------- -function source_damage_anisoBrittle_postResults(phase, constituent) +subroutine source_damage_anisoBrittle_results(phase,group) - integer, intent(in) :: & - phase, & - constituent - - real(pReal), dimension(sum(source_damage_anisoBrittle_sizePostResult(:, & - source_damage_anisoBrittle_instance(phase)))) :: & - source_damage_anisoBrittle_postResults - - integer :: & - instance, sourceOffset, o, c - - instance = source_damage_anisoBrittle_instance(phase) + integer, intent(in) :: phase + character(len=*), intent(in) :: group +#if defined(PETSc) || defined(DAMASK_HDF5) + integer :: sourceOffset, o, instance + + instance = source_damage_anisoBrittle_instance(phase) sourceOffset = source_damage_anisoBrittle_offset(phase) - c = 0 - - do o = 1,size(param(instance)%outputID) - select case(param(instance)%outputID(o)) + associate(prm => param(instance), stt => sourceState(phase)%p(sourceOffset)%state) + outputsLoop: do o = 1,size(prm%outputID) + select case(prm%outputID(o)) case (damage_drivingforce_ID) - source_damage_anisoBrittle_postResults(c+1) = & - sourceState(phase)%p(sourceOffset)%state(1,constituent) - c = c + 1 - + call results_writeDataset(group,stt,'tbd','driving force','tbd') end select - enddo -end function source_damage_anisoBrittle_postResults + enddo outputsLoop + end associate +#endif + +end subroutine source_damage_anisoBrittle_results end module source_damage_anisoBrittle diff --git a/src/source_damage_anisoDuctile.f90 b/src/source_damage_anisoDuctile.f90 index 409466e48..caba26ef4 100644 --- a/src/source_damage_anisoDuctile.f90 +++ b/src/source_damage_anisoDuctile.f90 @@ -12,6 +12,7 @@ module source_damage_anisoDuctile use discretization use material use config + use results implicit none private @@ -20,9 +21,6 @@ module source_damage_anisoDuctile source_damage_anisoDuctile_offset, & !< which source is my current damage mechanism? source_damage_anisoDuctile_instance !< instance of damage source mechanism - integer, dimension(:,:), allocatable, target, public :: & - source_damage_anisoDuctile_sizePostResult !< size of each post result output - character(len=64), dimension(:,:), allocatable, target, public :: & source_damage_anisoDuctile_output !< name of each post result output @@ -54,7 +52,7 @@ module source_damage_anisoDuctile source_damage_anisoDuctile_init, & source_damage_anisoDuctile_dotState, & source_damage_anisoDuctile_getRateAndItsTangent, & - source_damage_anisoDuctile_postResults + source_damage_anisoDuctile_results contains @@ -96,7 +94,6 @@ subroutine source_damage_anisoDuctile_init enddo enddo - allocate(source_damage_anisoDuctile_sizePostResult(maxval(phase_Noutput),Ninstance),source=0) allocate(source_damage_anisoDuctile_output(maxval(phase_Noutput),Ninstance)) source_damage_anisoDuctile_output = '' @@ -139,7 +136,6 @@ subroutine source_damage_anisoDuctile_init select case(outputs(i)) case ('anisoductile_drivingforce') - source_damage_anisoDuctile_sizePostResult(i,source_damage_anisoDuctile_instance(p)) = 1 source_damage_anisoDuctile_output(i,source_damage_anisoDuctile_instance(p)) = outputs(i) prm%outputID = [prm%outputID, damage_drivingforce_ID] @@ -156,8 +152,7 @@ subroutine source_damage_anisoDuctile_init sourceOffset = source_damage_anisoDuctile_offset(phase) call material_allocateSourceState(phase,sourceOffset,NofMyPhase,1,1,0) - sourceState(phase)%p(sourceOffset)%sizePostResults = sum(source_damage_anisoDuctile_sizePostResult(:,instance)) - sourceState(phase)%p(sourceOffset)%aTolState=param(instance)%aTol + sourceState(phase)%p(sourceOffset)%aTolState=param(instance)%aTol enddo @@ -226,35 +221,28 @@ end subroutine source_damage_anisoDuctile_getRateAndItsTangent !-------------------------------------------------------------------------------------------------- -!> @brief return array of local damage results +!> @brief writes results to HDF5 output file !-------------------------------------------------------------------------------------------------- -function source_damage_anisoDuctile_postResults(phase, constituent) +subroutine source_damage_anisoDuctile_results(phase,group) - integer, intent(in) :: & - phase, & - constituent - real(pReal), dimension(sum(source_damage_anisoDuctile_sizePostResult(:, & - source_damage_anisoDuctile_instance(phase)))) :: & - source_damage_anisoDuctile_postResults - - integer :: & - instance, sourceOffset, o, c - - instance = source_damage_anisoDuctile_instance(phase) + integer, intent(in) :: phase + character(len=*), intent(in) :: group +#if defined(PETSc) || defined(DAMASK_HDF5) + integer :: sourceOffset, o, instance + + instance = source_damage_anisoDuctile_instance(phase) sourceOffset = source_damage_anisoDuctile_offset(phase) - - c = 0 - - do o = 1,size(param(instance)%outputID) - select case(param(instance)%outputID(o)) - case (damage_drivingforce_ID) - source_damage_anisoDuctile_postResults(c+1) = & - sourceState(phase)%p(sourceOffset)%state(1,constituent) - c = c + 1 - - end select - enddo -end function source_damage_anisoDuctile_postResults + associate(prm => param(instance), stt => sourceState(phase)%p(sourceOffset)%state) + outputsLoop: do o = 1,size(prm%outputID) + select case(prm%outputID(o)) + case (damage_drivingforce_ID) + call results_writeDataset(group,stt,'tbd','driving force','tbd') + end select + enddo outputsLoop + end associate +#endif + +end subroutine source_damage_anisoDuctile_results end module source_damage_anisoDuctile diff --git a/src/source_damage_isoBrittle.f90 b/src/source_damage_isoBrittle.f90 index f6b99555f..e38c15682 100644 --- a/src/source_damage_isoBrittle.f90 +++ b/src/source_damage_isoBrittle.f90 @@ -12,14 +12,13 @@ module source_damage_isoBrittle use discretization use material use config + use results implicit none private integer, dimension(:), allocatable, public, protected :: & source_damage_isoBrittle_offset, & source_damage_isoBrittle_instance - integer, dimension(:,:), allocatable :: & - source_damage_isoBrittle_sizePostResult character(len=64), dimension(:,:), allocatable :: & source_damage_isoBrittle_output @@ -46,7 +45,7 @@ module source_damage_isoBrittle source_damage_isoBrittle_init, & source_damage_isoBrittle_deltaState, & source_damage_isoBrittle_getRateAndItsTangent, & - source_damage_isoBrittle_postResults + source_damage_isoBrittle_Results contains @@ -85,8 +84,7 @@ subroutine source_damage_isoBrittle_init source_damage_isoBrittle_offset(phase) = source enddo enddo - - allocate(source_damage_isoBrittle_sizePostResult(maxval(phase_Noutput),Ninstance),source=0) + allocate(source_damage_isoBrittle_output(maxval(phase_Noutput),Ninstance)) source_damage_isoBrittle_output = '' @@ -122,7 +120,6 @@ subroutine source_damage_isoBrittle_init select case(outputs(i)) case ('isobrittle_drivingforce') - source_damage_isoBrittle_sizePostResult(i,source_damage_isoBrittle_instance(p)) = 1 source_damage_isoBrittle_output(i,source_damage_isoBrittle_instance(p)) = outputs(i) prm%outputID = [prm%outputID, damage_drivingforce_ID] @@ -139,7 +136,6 @@ subroutine source_damage_isoBrittle_init sourceOffset = source_damage_isoBrittle_offset(phase) call material_allocateSourceState(phase,sourceOffset,NofMyPhase,1,1,1) - sourceState(phase)%p(sourceOffset)%sizePostResults = sum(source_damage_isoBrittle_sizePostResult(:,instance)) sourceState(phase)%p(sourceOffset)%aTolState=param(instance)%aTol enddo @@ -214,35 +210,31 @@ subroutine source_damage_isoBrittle_getRateAndItsTangent(localphiDot, dLocalphiD - sourceState(phase)%p(sourceOffset)%state(1,constituent) end subroutine source_damage_isoBrittle_getRateAndItsTangent - -!-------------------------------------------------------------------------------------------------- -!> @brief return array of local damage results -!-------------------------------------------------------------------------------------------------- -function source_damage_isoBrittle_postResults(phase, constituent) - integer, intent(in) :: & - phase, & - constituent - real(pReal), dimension(sum(source_damage_isoBrittle_sizePostResult(:, & - source_damage_isoBrittle_instance(phase)))) :: & - source_damage_isoBrittle_postResults - integer :: & - instance, sourceOffset, o, c - - instance = source_damage_isoBrittle_instance(phase) +!-------------------------------------------------------------------------------------------------- +!> @brief writes results to HDF5 output file +!-------------------------------------------------------------------------------------------------- +subroutine source_damage_isoBrittle_results(phase,group) + + integer, intent(in) :: phase + character(len=*), intent(in) :: group +#if defined(PETSc) || defined(DAMASK_HDF5) + integer :: sourceOffset, o, instance + + instance = source_damage_isoBrittle_instance(phase) sourceOffset = source_damage_isoBrittle_offset(phase) - c = 0 - - do o = 1,size(param(instance)%outputID) - select case(param(instance)%outputID(o)) + associate(prm => param(instance), stt => sourceState(phase)%p(sourceOffset)%state) + outputsLoop: do o = 1,size(prm%outputID) + select case(prm%outputID(o)) case (damage_drivingforce_ID) - source_damage_isoBrittle_postResults(c+1) = sourceState(phase)%p(sourceOffset)%state(1,constituent) - c = c + 1 - + call results_writeDataset(group,stt,'tbd','driving force','tbd') end select - enddo -end function source_damage_isoBrittle_postResults + enddo outputsLoop + end associate +#endif + +end subroutine source_damage_isoBrittle_results end module source_damage_isoBrittle diff --git a/src/source_damage_isoDuctile.f90 b/src/source_damage_isoDuctile.f90 index 65930cd07..69b8e82bf 100644 --- a/src/source_damage_isoDuctile.f90 +++ b/src/source_damage_isoDuctile.f90 @@ -11,6 +11,7 @@ module source_damage_isoDuctile use discretization use material use config + use results implicit none private @@ -18,9 +19,6 @@ module source_damage_isoDuctile source_damage_isoDuctile_offset, & !< which source is my current damage mechanism? source_damage_isoDuctile_instance !< instance of damage source mechanism - integer, dimension(:,:), allocatable, target, public :: & - source_damage_isoDuctile_sizePostResult !< size of each post result output - character(len=64), dimension(:,:), allocatable, target, public :: & source_damage_isoDuctile_output !< name of each post result output @@ -46,7 +44,7 @@ module source_damage_isoDuctile source_damage_isoDuctile_init, & source_damage_isoDuctile_dotState, & source_damage_isoDuctile_getRateAndItsTangent, & - source_damage_isoDuctile_postResults + source_damage_isoDuctile_Results contains @@ -86,7 +84,6 @@ subroutine source_damage_isoDuctile_init enddo enddo - allocate(source_damage_isoDuctile_sizePostResult(maxval(phase_Noutput),Ninstance),source=0) allocate(source_damage_isoDuctile_output(maxval(phase_Noutput),Ninstance)) source_damage_isoDuctile_output = '' @@ -122,7 +119,6 @@ subroutine source_damage_isoDuctile_init select case(outputs(i)) case ('isoductile_drivingforce') - source_damage_isoDuctile_sizePostResult(i,source_damage_isoDuctile_instance(p)) = 1 source_damage_isoDuctile_output(i,source_damage_isoDuctile_instance(p)) = outputs(i) prm%outputID = [prm%outputID, damage_drivingforce_ID] @@ -138,9 +134,7 @@ subroutine source_damage_isoDuctile_init sourceOffset = source_damage_isoDuctile_offset(phase) call material_allocateSourceState(phase,sourceOffset,NofMyPhase,1,1,0) - sourceState(phase)%p(sourceOffset)%sizePostResults = sum(source_damage_isoDuctile_sizePostResult(:,instance)) sourceState(phase)%p(sourceOffset)%aTolState=param(instance)%aTol - enddo @@ -196,35 +190,32 @@ subroutine source_damage_isoDuctile_getRateAndItsTangent(localphiDot, dLocalphiD dLocalphiDot_dPhi = -sourceState(phase)%p(sourceOffset)%state(1,constituent) end subroutine source_damage_isoDuctile_getRateAndItsTangent - -!-------------------------------------------------------------------------------------------------- -!> @brief return array of local damage results -!-------------------------------------------------------------------------------------------------- -function source_damage_isoDuctile_postResults(phase, constituent) - integer, intent(in) :: & - phase, & - constituent - real(pReal), dimension(sum(source_damage_isoDuctile_sizePostResult(:, & - source_damage_isoDuctile_instance(phase)))) :: & - source_damage_isoDuctile_postResults - integer :: & - instance, sourceOffset, o, c +!-------------------------------------------------------------------------------------------------- +!> @brief writes results to HDF5 output file +!-------------------------------------------------------------------------------------------------- +subroutine source_damage_isoDuctile_results(phase,group) + + integer, intent(in) :: phase + character(len=*), intent(in) :: group +#if defined(PETSc) || defined(DAMASK_HDF5) + integer :: sourceOffset, o, instance - instance = source_damage_isoDuctile_instance(phase) - sourceOffset = source_damage_isoDuctile_offset(phase) + instance = source_damage_isoDuctile_instance(phase) + sourceOffset = source_damage_isoDuctile_offset(phase) - c = 0 + associate(prm => param(instance), stt => sourceState(phase)%p(sourceOffset)%state) + outputsLoop: do o = 1,size(prm%outputID) + select case(prm%outputID(o)) + case (damage_drivingforce_ID) + call results_writeDataset(group,stt,'tbd','driving force','tbd') + end select + enddo outputsLoop + end associate +#endif - do o = 1,size(param(instance)%outputID) - select case(param(instance)%outputID(o)) - case (damage_drivingforce_ID) - source_damage_isoDuctile_postResults(c+1) = sourceState(phase)%p(sourceOffset)%state(1,constituent) - c = c + 1 +end subroutine source_damage_isoDuctile_results - end select - enddo -end function source_damage_isoDuctile_postResults end module source_damage_isoDuctile From 5681e661e2472f2692e067635c87913ac80bf937 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 10 Dec 2019 07:14:39 +0100 Subject: [PATCH 053/223] DADF5-results replaces postResults --- src/damage_local.f90 | 31 +++++++++++++++++++++++++++++- src/damage_nonlocal.f90 | 32 ++++++++++++++++++++++++++++++- src/homogenization.f90 | 42 +++++++++++++++++++++++++---------------- 3 files changed, 87 insertions(+), 18 deletions(-) diff --git a/src/damage_local.f90 b/src/damage_local.f90 index 74ad47c9b..5ce27c339 100644 --- a/src/damage_local.f90 +++ b/src/damage_local.f90 @@ -11,6 +11,7 @@ module damage_local use source_damage_isoDuctile use source_damage_anisoBrittle use source_damage_anisoDuctile + use results implicit none private @@ -42,7 +43,8 @@ module damage_local public :: & damage_local_init, & damage_local_updateState, & - damage_local_postResults + damage_local_postResults, & + damage_local_Results contains @@ -210,6 +212,33 @@ subroutine damage_local_getSourceAndItsTangent(phiDot, dPhiDot_dPhi, phi, ip, el end subroutine damage_local_getSourceAndItsTangent +!-------------------------------------------------------------------------------------------------- +!> @brief writes results to HDF5 output file +!-------------------------------------------------------------------------------------------------- +module subroutine damage_local_results(homog,group) + + integer, intent(in) :: homog + character(len=*), intent(in) :: group +#if defined(PETSc) || defined(DAMASK_HDF5) + integer :: o, instance + + instance = damage_typeInstance(homog) + associate(prm => param(instance)) + + outputsLoop: do o = 1,size(prm%outputID) + select case(prm%outputID(o)) + + case (damage_ID) + call results_writeDataset(group,damage(homog)%p,'phi',& + 'damage indicator','-') + end select + enddo outputsLoop + end associate +#endif + +end subroutine damage_local_results + + !-------------------------------------------------------------------------------------------------- !> @brief return array of damage results !-------------------------------------------------------------------------------------------------- diff --git a/src/damage_nonlocal.f90 b/src/damage_nonlocal.f90 index d37f68486..d7e1aa074 100644 --- a/src/damage_nonlocal.f90 +++ b/src/damage_nonlocal.f90 @@ -14,6 +14,7 @@ module damage_nonlocal use source_damage_isoDuctile use source_damage_anisoBrittle use source_damage_anisoDuctile + use results implicit none private @@ -45,7 +46,8 @@ module damage_nonlocal damage_nonlocal_getDiffusion33, & damage_nonlocal_getMobility, & damage_nonlocal_putNonLocalDamage, & - damage_nonlocal_postResults + damage_nonlocal_postResults, & + damage_nonlocal_Results contains @@ -246,6 +248,34 @@ subroutine damage_nonlocal_putNonLocalDamage(phi,ip,el) end subroutine damage_nonlocal_putNonLocalDamage + +!-------------------------------------------------------------------------------------------------- +!> @brief writes results to HDF5 output file +!-------------------------------------------------------------------------------------------------- +module subroutine damage_nonlocal_results(homog,group) + + integer, intent(in) :: homog + character(len=*), intent(in) :: group +#if defined(PETSc) || defined(DAMASK_HDF5) + integer :: o, instance + + instance = damage_typeInstance(homog) + associate(prm => param(instance)) + + outputsLoop: do o = 1,size(prm%outputID) + select case(prm%outputID(o)) + + case (damage_ID) + call results_writeDataset(group,damage(homog)%p,'phi',& + 'damage indicator','-') + end select + enddo outputsLoop + end associate +#endif + +end subroutine damage_nonlocal_results + + !-------------------------------------------------------------------------------------------------- !> @brief return array of damage results !-------------------------------------------------------------------------------------------------- diff --git a/src/homogenization.f90 b/src/homogenization.f90 index 5be988ec3..842c5f4b6 100644 --- a/src/homogenization.f90 +++ b/src/homogenization.f90 @@ -23,7 +23,6 @@ module homogenization use damage_local use damage_nonlocal use results - use HDF5_utilities implicit none private @@ -790,33 +789,44 @@ subroutine homogenization_results material_homogenization_type => homogenization_type integer :: p - character(len=256) :: group + character(len=pStringLen) :: group_base,group !real(pReal), dimension(:,:,:), allocatable :: temp do p=1,size(config_name_homogenization) - group = trim('current/materialpoint')//'/'//trim(config_name_homogenization(p)) - call HDF5_closeGroup(results_addGroup(group)) - - group = trim(group)//'/mech' - - call HDF5_closeGroup(results_addGroup(group)) - select case(material_homogenization_type(p)) - case(HOMOGENIZATION_rgc_ID) - call mech_RGC_results(homogenization_typeInstance(p),group) - end select - - group = trim('current/materialpoint')//'/'//trim(config_name_homogenization(p))//'/generic' - call HDF5_closeGroup(results_addGroup(group)) + group_base = 'current/materialpoint/'//trim(config_name_homogenization(p)) + call results_closeGroup(results_addGroup(group_base)) + group = trim(group_base)//'/generic' + call results_closeGroup(results_addGroup(group)) !temp = reshape(materialpoint_F,[3,3,discretization_nIP*discretization_nElem]) !call results_writeDataset(group,temp,'F',& ! 'deformation gradient','1') !temp = reshape(materialpoint_P,[3,3,discretization_nIP*discretization_nElem]) !call results_writeDataset(group,temp,'P',& ! '1st Piola-Kirchoff stress','Pa') + + group = trim(group_base)//'/mech' + call results_closeGroup(results_addGroup(group)) + select case(material_homogenization_type(p)) + case(HOMOGENIZATION_rgc_ID) + call mech_RGC_results(homogenization_typeInstance(p),group) + end select + + group = trim(group_base)//'/damage' + call results_closeGroup(results_addGroup(group)) + select case(damage_type(p)) + case(DAMAGE_LOCAL_ID) + call damage_local_results(p,group) + case(DAMAGE_NONLOCAL_ID) + call damage_nonlocal_results(p,group) + end select + + group = trim(group_base)//'/thermal' + call results_closeGroup(results_addGroup(group)) + + enddo - enddo #endif end subroutine homogenization_results From ca06c1d3a294228d70235f3cfd7bd9e15360da60 Mon Sep 17 00:00:00 2001 From: "f.basile" Date: Tue, 10 Dec 2019 13:49:33 +0100 Subject: [PATCH 054/223] Fix bugs in relatedOperations for NW,GT and GTprime. --- python/damask/orientation.py | 6 +- python/tests/reference/Rotation/1_BCC.pdf | Bin 0 -> 44551 bytes python/tests/reference/Rotation/1_FCC.pdf | Bin 0 -> 44391 bytes python/tests/reference/Rotation/2_BCC.pdf | Bin 0 -> 43118 bytes python/tests/reference/Rotation/2_FCC.pdf | Bin 0 -> 43107 bytes .../tests/reference/Rotation/PoleFigures_OR.m | 99 ++++++++++++++++++ python/tests/reference/Rotation/bcc_GT.txt | 2 +- .../tests/reference/Rotation/bcc_GT_prime.txt | 2 +- python/tests/reference/Rotation/bcc_NW.txt | 2 +- python/tests/reference/Rotation/fcc_GT.txt | 2 +- .../tests/reference/Rotation/fcc_GT_prime.txt | 2 +- python/tests/reference/Rotation/fcc_NW.txt | 2 +- 12 files changed, 108 insertions(+), 9 deletions(-) create mode 100644 python/tests/reference/Rotation/1_BCC.pdf create mode 100644 python/tests/reference/Rotation/1_FCC.pdf create mode 100644 python/tests/reference/Rotation/2_BCC.pdf create mode 100644 python/tests/reference/Rotation/2_FCC.pdf create mode 100644 python/tests/reference/Rotation/PoleFigures_OR.m diff --git a/python/damask/orientation.py b/python/damask/orientation.py index 65318f169..1b08d2937 100644 --- a/python/damask/orientation.py +++ b/python/damask/orientation.py @@ -875,7 +875,7 @@ class Lattice: [[ 17, 12, 5],[ 17, 7, 17]], [[ 5, 17, 12],[ 17, 17, 7]], [[ 12, -5,-17],[ 7,-17,-17]], - [[-17,-12, 5],[-17, 7, 17]]],dtype='float')} + [[-17,-12, 5],[-17,-7, 17]]],dtype='float')} # Greninger--Troiano' orientation relationship for fcc <-> bcc transformation # from Y. He et al., Journal of Applied Crystallography 39:72-81, 2006 @@ -901,7 +901,7 @@ class Lattice: [[-17,-17, 7],[-17, -5, 12]], [[ 7,-17,-17],[ 12,-17, -5]], [[ 17, -7,-17],[ 5, -12,-17]], - [[ 17,-17, 7],[ 17, -5,-12]], + [[ 17,-17, -7],[ 17, -5,-12]], [[ -7, 17,-17],[-12, 17, -5]], [[-17, 7,-17],[ -5, 12,-17]], [[-17, 17, -7],[-17, 5,-12]]],dtype='float'), @@ -957,7 +957,7 @@ class Lattice: [[ 2, 1, -1],[ 0, -1, 1]], [[ -1, -2, -1],[ 0, -1, 1]], [[ -1, 1, 2],[ 0, -1, 1]], - [[ -1, 2, 1],[ 0, -1, 1]], + [[ 2, -1, 1],[ 0, -1, 1]], #It is wrong in the paper, but matrix is correct [[ -1, 2, 1],[ 0, -1, 1]], [[ -1, -1, -2],[ 0, -1, 1]]],dtype='float')} diff --git a/python/tests/reference/Rotation/1_BCC.pdf b/python/tests/reference/Rotation/1_BCC.pdf new file mode 100644 index 0000000000000000000000000000000000000000..fe22f6a2e36d180c423a1902b1a7409f866ff324 GIT binary patch literal 44551 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%0ALAhtr$ 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 BBRK#7 literal 0 HcmV?d00001 diff --git a/python/tests/reference/Rotation/PoleFigures_OR.m b/python/tests/reference/Rotation/PoleFigures_OR.m new file mode 100644 index 000000000..fb2baa6a1 --- /dev/null +++ b/python/tests/reference/Rotation/PoleFigures_OR.m @@ -0,0 +1,99 @@ +%% Import Script for EBSD Data +% +% Use MTEX +clear ; clear all + +%% Specify Crystal and Specimen Symmetries + +% crystal symmetry +CS_bcc = {... + crystalSymmetry('m-3m', [2.8665 2.8665 2.8665], 'mineral', 'Iron-alpha', 'color', 'light blue'),... + crystalSymmetry('m-3m', [1 1 1], 'color', 'light blue')}; + +CS_fcc = {... + crystalSymmetry('m-3m', [3.662 3.662 3.662], 'mineral', 'Iron', 'color', 'light blue'),... + crystalSymmetry('m-3m', [1 1 1], 'color', 'light blue')}; + +% plotting convention +setMTEXpref('xAxisDirection','north'); +setMTEXpref('zAxisDirection','outOfPlane'); + +%% path to files +pname = 'L:\f.gallardo\DAMASK\python\tests\reference\Rotation'; % has to be changed + +% which files to be imported +fname1 = [pname '\bcc_Bain.txt']; fname2 = [pname '\bcc_GT.txt']; fname3 = [pname '\bcc_GT_prime.txt']; +fname4 = [pname '\bcc_KS.txt']; fname5 = [pname '\bcc_NW.txt']; fname6 = [pname '\bcc_Pitsch.txt']; +fname7 = [pname '\fcc_Bain.txt']; fname8 = [pname '\fcc_GT.txt']; fname9 = [pname '\fcc_GT_prime.txt']; +fname10 = [pname '\fcc_KS.txt']; fname11 = [pname '\fcc_NW.txt']; fname12 = [pname '\fcc_Pitsch.txt']; + + +%% Import the Data + +% create an EBSD variable containing the data +ebsd1 = loadEBSD(fname1,CS_bcc,'interface','generic',... + 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Passive Rotation'); +ebsd2 = loadEBSD(fname2,CS_bcc,'interface','generic',... + 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Passive Rotation'); +ebsd3 = loadEBSD(fname3,CS_bcc,'interface','generic',... + 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Passive Rotation'); +ebsd4 = loadEBSD(fname4,CS_bcc,'interface','generic',... + 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Passive Rotation'); +ebsd5 = loadEBSD(fname5,CS_bcc,'interface','generic',... + 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Passive Rotation'); +ebsd6 = loadEBSD(fname6,CS_bcc,'interface','generic',... + 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Passive Rotation'); + +ebsd7 = loadEBSD(fname7,CS_fcc,'interface','generic',... + 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Passive Rotation'); +ebsd8 = loadEBSD(fname8,CS_fcc,'interface','generic',... + 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Passive Rotation'); +ebsd9 = loadEBSD(fname9,CS_fcc,'interface','generic',... + 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Passive Rotation'); +ebsd10 = loadEBSD(fname10,CS_fcc,'interface','generic',... + 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Passive Rotation'); +ebsd11 = loadEBSD(fname11,CS_fcc,'interface','generic',... + 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Passive Rotation'); +ebsd12 = loadEBSD(fname12,CS_fcc,'interface','generic',... + 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Passive Rotation'); + +%% Plot Data 1stpart_bcc +h1 = [Miller(1,0,0,ebsd1.CS),Miller(1,1,0,ebsd1.CS),Miller(1,1,1,ebsd1.CS)]; % 3 pole figures +plotPDF(ebsd1.orientations,h1,'MarkerSize',5,'MarkerColor','r','MarkerEdgeColor','r','DisplayName','BCC-Bain') +hold on +plotPDF(ebsd2.orientations,h1,'MarkerSize',5,'MarkerColor','w','MarkerEdgeColor','b','DisplayName','BCC-GT') +plotPDF(ebsd3.orientations,h1,'MarkerSize',5,'MarkerColor','w','MarkerEdgeColor','g','DisplayName','BCC-GT_Prime') +legend('show','location','southoutside') +cd 'L:\f.gallardo\DAMASK\python\tests\reference\Rotation'; % has to be changed +orient('landscape') +print('-bestfit','1_BCC.pdf','-dpdf') + +%% Plot Data 2nd part_bcc +close +plotPDF(ebsd4.orientations,h1,'MarkerSize',5,'MarkerColor','r','MarkerEdgeColor','w','DisplayName','BCC-KS') +hold on +plotPDF(ebsd5.orientations,h1,'MarkerSize',5,'MarkerColor','w','MarkerEdgeColor','m','DisplayName','BCC-NW') +plotPDF(ebsd6.orientations,h1,'MarkerSize',5,'MarkerColor','y','MarkerEdgeColor','w','DisplayName','BCC-Pitsch') +legend('show','location','southoutside') +print('-bestfit','2_BCC.pdf','-dpdf') + +%% Plot Data 1stpart_fcc +close +h2 = [Miller(1,0,0,ebsd7.CS),Miller(1,1,0,ebsd7.CS),Miller(1,1,1,ebsd7.CS)]; % 3 pole figures +plotPDF(ebsd7.orientations,h2,'MarkerSize',5,'MarkerColor','r','MarkerEdgeColor','r','DisplayName','FCC-Bain') +hold on +plotPDF(ebsd8.orientations,h2,'MarkerSize',5,'MarkerColor','w','MarkerEdgeColor','b','DisplayName','FCC-GT') +plotPDF(ebsd9.orientations,h2,'MarkerSize',5,'MarkerColor','w','MarkerEdgeColor','g','DisplayName','FCC-GT_Prime') +legend('show','location','southoutside') +print('-bestfit','1_FCC.pdf','-dpdf') + +%% Plot Data 2nd part_bcc +close +plotPDF(ebsd10.orientations,h2,'MarkerSize',5,'MarkerColor','r','MarkerEdgeColor','w','DisplayName','FCC-KS') +hold on +plotPDF(ebsd11.orientations,h2,'MarkerSize',5,'MarkerColor','w','MarkerEdgeColor','m','DisplayName','FCC-NW') +plotPDF(ebsd12.orientations,h2,'MarkerSize',5,'MarkerColor','y','MarkerEdgeColor','w','DisplayName','FCC-Pitsch') +legend('show','location','southoutside') +print('-bestfit','2_FCC.pdf','-dpdf') +close + diff --git a/python/tests/reference/Rotation/bcc_GT.txt b/python/tests/reference/Rotation/bcc_GT.txt index d1fe2e1c8..5d5102698 100644 --- a/python/tests/reference/Rotation/bcc_GT.txt +++ b/python/tests/reference/Rotation/bcc_GT.txt @@ -23,4 +23,4 @@ 352.1156357053931 43.82007387041961 14.074783631236542 1 21 77.82610341510008 43.397849654402556 273.4002228089796 1 22 193.60440567265297 9.976439066337806 123.24637065555939 1 23 -153.65751914298576 65.6559553854118 185.90444335627936 1 24 +172.11563570539317 43.82007387041961 194.07478363123653 1 24 diff --git a/python/tests/reference/Rotation/bcc_GT_prime.txt b/python/tests/reference/Rotation/bcc_GT_prime.txt index 42f32bcbb..e398d3139 100644 --- a/python/tests/reference/Rotation/bcc_GT_prime.txt +++ b/python/tests/reference/Rotation/bcc_GT_prime.txt @@ -20,7 +20,7 @@ 183.40022280897963 43.397849654402556 167.8261034151001 1 18 255.92521636876344 43.82007387041961 97.88436429460687 1 19 33.24637065555936 9.976439066337804 283.60440567265294 1 20 -26.291675350407385 65.60048732963618 354.34378938496315 1 21 +356.59977719102034 43.39784965440254 12.173896584899929 1 21 75.92521636876346 43.82007387041961 277.8843642946069 1 22 213.24637065555936 9.976439066337804 103.604405672653 1 23 176.59977719102034 43.397849654402556 192.17389658489986 1 24 diff --git a/python/tests/reference/Rotation/bcc_NW.txt b/python/tests/reference/Rotation/bcc_NW.txt index 76e7c6182..754c69bba 100644 --- a/python/tests/reference/Rotation/bcc_NW.txt +++ b/python/tests/reference/Rotation/bcc_NW.txt @@ -9,6 +9,6 @@ 134.58444405678858 83.13253115922213 96.91733794010702 1 7 225.41555594321142 83.13253115922213 173.082662059893 1 8 0.0 9.735610317245317 135.0 1 9 -260.40196970123213 45.81931182053556 283.6387072794765 1 10 +99.59803029876785 45.81931182053557 166.36129272052355 1 10 260.40196970123213 45.81931182053556 283.6387072794765 1 11 180.0 99.73561031724535 225.0 1 12 diff --git a/python/tests/reference/Rotation/fcc_GT.txt b/python/tests/reference/Rotation/fcc_GT.txt index b91a80c46..cefae431a 100644 --- a/python/tests/reference/Rotation/fcc_GT.txt +++ b/python/tests/reference/Rotation/fcc_GT.txt @@ -23,4 +23,4 @@ 165.92521636876344 43.82007387041961 187.88436429460683 1 21 266.59977719102034 43.39784965440254 102.17389658489992 1 22 56.75362934444064 9.976439066337804 346.395594327347 1 23 -354.0955566437206 65.6559553854118 26.342480857014277 1 24 +345.9252163687635 43.82007387041961 7.884364294606862 1 24 diff --git a/python/tests/reference/Rotation/fcc_GT_prime.txt b/python/tests/reference/Rotation/fcc_GT_prime.txt index 1d6f171c4..44a9b25ec 100644 --- a/python/tests/reference/Rotation/fcc_GT_prime.txt +++ b/python/tests/reference/Rotation/fcc_GT_prime.txt @@ -20,7 +20,7 @@ 12.173896584899929 43.39784965440254 356.59977719102034 1 18 82.11563570539313 43.82007387041961 284.0747836312365 1 19 256.395594327347 9.976439066337804 146.75362934444064 1 20 -185.65621061503683 65.60048732963617 153.70832464959264 1 21 +167.8261034151001 43.397849654402556 183.40022280897963 1 21 262.1156357053931 43.82007387041961 104.07478363123654 1 22 76.39559432734703 9.976439066337806 326.75362934444064 1 23 347.8261034151001 43.39784965440255 3.400222808979685 1 24 diff --git a/python/tests/reference/Rotation/fcc_NW.txt b/python/tests/reference/Rotation/fcc_NW.txt index e041ec0b0..cc9c95a05 100644 --- a/python/tests/reference/Rotation/fcc_NW.txt +++ b/python/tests/reference/Rotation/fcc_NW.txt @@ -9,6 +9,6 @@ 83.082662059893 83.13253115922213 45.415555943211444 1 7 6.917337940106983 83.13253115922211 314.5844440567886 1 8 45.0 9.73561031724532 180.0 1 9 -256.36129272052347 45.81931182053556 279.59803029876775 1 10 +13.638707279476469 45.81931182053557 80.40196970123216 1 10 256.36129272052347 45.81931182053556 279.59803029876775 1 11 315.0 99.73561031724536 0.0 1 12 From a162840ab6c0f2fecc6798b1c07d75c43f25d7d7 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 10 Dec 2019 16:18:44 +0100 Subject: [PATCH 055/223] include some cleaning --- PRIVATE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PRIVATE b/PRIVATE index 524e86c11..a535b399a 160000 --- a/PRIVATE +++ b/PRIVATE @@ -1 +1 @@ -Subproject commit 524e86c117d816e3bd873eed7663e258a6f2e139 +Subproject commit a535b399a91ef1b117b88332b77bbba90dac83f2 From c46c18de6ffd59497c93c4d55830dbe5606920e3 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 10 Dec 2019 16:20:50 +0100 Subject: [PATCH 056/223] taking care of prospector complaints --- processing/pre/hybridIA_linODFsampling.py | 20 +++++++++---------- .../pre/patchFromReconstructedBoundaries.py | 12 ++++------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/processing/pre/hybridIA_linODFsampling.py b/processing/pre/hybridIA_linODFsampling.py index caa747337..80d82a458 100755 --- a/processing/pre/hybridIA_linODFsampling.py +++ b/processing/pre/hybridIA_linODFsampling.py @@ -19,7 +19,7 @@ def integerFactorization(i): return j def binAsBins(bin,intervals): - """Explode compound bin into 3D bins list""" + """Explode compound bin into 3D bins list.""" bins = [0]*3 bins[0] = (bin//(intervals[1] * intervals[2])) % intervals[0] bins[1] = (bin//intervals[2]) % intervals[1] @@ -27,17 +27,17 @@ def binAsBins(bin,intervals): return bins def binsAsBin(bins,intervals): - """Implode 3D bins into compound bin""" + """Implode 3D bins into compound bin.""" return (bins[0]*intervals[1] + bins[1])*intervals[2] + bins[2] def EulersAsBins(Eulers,intervals,deltas,center): - """Return list of Eulers translated into 3D bins list""" + """Return list of Eulers translated into 3D bins list.""" return [int((euler+(0.5-center)*delta)//delta)%interval \ for euler,delta,interval in zip(Eulers,deltas,intervals) \ ] def binAsEulers(bin,intervals,deltas,center): - """Compound bin number translated into list of Eulers""" + """Compound bin number translated into list of Eulers.""" Eulers = [0.0]*3 Eulers[2] = (bin%intervals[2] + center)*deltas[2] Eulers[1] = (bin//intervals[2]%intervals[1] + center)*deltas[1] @@ -45,7 +45,7 @@ def binAsEulers(bin,intervals,deltas,center): return Eulers def directInvRepetitions(probability,scale): - """Calculate number of samples drawn by direct inversion""" + """Calculate number of samples drawn by direct inversion.""" nDirectInv = 0 for bin in range(len(probability)): # loop over bins nDirectInv += int(round(probability[bin]*scale)) # calc repetition @@ -56,7 +56,7 @@ def directInvRepetitions(probability,scale): # ----- efficient algorithm --------- def directInversion (ODF,nSamples): - """ODF contains 'dV_V' (normalized to 1), 'center', 'intervals', 'limits' (in radians)""" + """ODF contains 'dV_V' (normalized to 1), 'center', 'intervals', 'limits' (in radians).""" nOptSamples = max(ODF['nNonZero'],nSamples) # random subsampling if too little samples requested nInvSamples = 0 @@ -118,7 +118,7 @@ def directInversion (ODF,nSamples): # ----- trial and error algorithms --------- def MonteCarloEulers (ODF,nSamples): - """ODF contains 'dV_V' (normalized to 1), 'center', 'intervals', 'limits' (in radians)""" + """ODF contains 'dV_V' (normalized to 1), 'center', 'intervals', 'limits' (in radians).""" countMC = 0 maxdV_V = max(ODF['dV_V']) orientations = np.zeros((nSamples,3),'f') @@ -141,7 +141,7 @@ def MonteCarloEulers (ODF,nSamples): def MonteCarloBins (ODF,nSamples): - """ODF contains 'dV_V' (normalized to 1), 'center', 'intervals', 'limits' (in radians)""" + """ODF contains 'dV_V' (normalized to 1), 'center', 'intervals', 'limits' (in radians).""" countMC = 0 maxdV_V = max(ODF['dV_V']) orientations = np.zeros((nSamples,3),'f') @@ -163,7 +163,7 @@ def MonteCarloBins (ODF,nSamples): def TothVanHoutteSTAT (ODF,nSamples): - """ODF contains 'dV_V' (normalized to 1), 'center', 'intervals', 'limits' (in radians)""" + """ODF contains 'dV_V' (normalized to 1), 'center', 'intervals', 'limits' (in radians).""" orientations = np.zeros((nSamples,3),'f') reconstructedODF = np.zeros(ODF['nBins'],'f') unitInc = 1.0/nSamples @@ -235,7 +235,7 @@ if filenames == []: filenames = [None] for name in filenames: try: table = damask.ASCIItable(name = name, buffered = False, readonly=True) - except: + except IOError: continue damask.util.report(scriptName,name) diff --git a/processing/pre/patchFromReconstructedBoundaries.py b/processing/pre/patchFromReconstructedBoundaries.py index e9196916e..b710fb2cb 100755 --- a/processing/pre/patchFromReconstructedBoundaries.py +++ b/processing/pre/patchFromReconstructedBoundaries.py @@ -78,13 +78,11 @@ def rcbOrientationParser(content,idcolumn): damask.util.croak('You might not have chosen the correct column for the grain IDs! '+ 'Please check the "--id" option.') raise - except: - raise return grains def rcbParser(content,M,size,tolerance,idcolumn,segmentcolumn): - """Parser for TSL-OIM reconstructed boundary files""" + """Parser for TSL-OIM reconstructed boundary files.""" # find bounding box boxX = [1.*sys.maxint,-1.*sys.maxint] boxY = [1.*sys.maxint,-1.*sys.maxint] @@ -99,8 +97,6 @@ def rcbParser(content,M,size,tolerance,idcolumn,segmentcolumn): damask.util.croak('You might not have chosen the correct column for the segment end points! '+ 'Please check the "--segment" option.') raise - except: - raise (x[0],y[0]) = (M[0]*x[0]+M[1]*y[0],M[2]*x[0]+M[3]*y[0]) # apply transformation to coordinates (x[1],y[1]) = (M[0]*x[1]+M[1]*y[1],M[2]*x[1]+M[3]*y[1]) # to get rcb --> Euler system boxX[0] = min(boxX[0],x[0],x[1]) @@ -728,7 +724,7 @@ def image(name,imgsize,marginX,marginY,rcData): # ------------------------- def inside(x,y,points): - """Tests whether point(x,y) is within polygon described by points""" + """Tests whether point(x,y) is within polygon described by points.""" inside = False npoints=len(points) (x1,y1) = points[npoints-1] # start with last point of points @@ -750,7 +746,7 @@ def inside(x,y,points): # ------------------------- def fftbuild(rcData,height,xframe,yframe,grid,extrusion): - """Build array of grain numbers""" + """Build array of grain numbers.""" maxX = -1.*sys.maxint maxY = -1.*sys.maxint for line in rcData['point']: # find data range @@ -883,7 +879,7 @@ try: boundaryFile = open(args[0]) boundarySegments = boundaryFile.readlines() boundaryFile.close() -except: +except IOError: damask.util.croak('unable to read boundary file "{}".'.format(args[0])) raise From ac35759c2fab4710d1831e72c49f7d27df773bac Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 10 Dec 2019 17:25:51 +0100 Subject: [PATCH 057/223] not a module subroutine --- src/damage_local.f90 | 2 +- src/damage_nonlocal.f90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/damage_local.f90 b/src/damage_local.f90 index 5ce27c339..a57bb5c65 100644 --- a/src/damage_local.f90 +++ b/src/damage_local.f90 @@ -215,7 +215,7 @@ end subroutine damage_local_getSourceAndItsTangent !-------------------------------------------------------------------------------------------------- !> @brief writes results to HDF5 output file !-------------------------------------------------------------------------------------------------- -module subroutine damage_local_results(homog,group) +subroutine damage_local_results(homog,group) integer, intent(in) :: homog character(len=*), intent(in) :: group diff --git a/src/damage_nonlocal.f90 b/src/damage_nonlocal.f90 index d7e1aa074..a5e211783 100644 --- a/src/damage_nonlocal.f90 +++ b/src/damage_nonlocal.f90 @@ -252,7 +252,7 @@ end subroutine damage_nonlocal_putNonLocalDamage !-------------------------------------------------------------------------------------------------- !> @brief writes results to HDF5 output file !-------------------------------------------------------------------------------------------------- -module subroutine damage_nonlocal_results(homog,group) +subroutine damage_nonlocal_results(homog,group) integer, intent(in) :: homog character(len=*), intent(in) :: group From 74707b1b00be08413175f0573d4075b11da551bd Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 10 Dec 2019 17:36:25 +0100 Subject: [PATCH 058/223] hybridIA test needs update (no crystallite) --- PRIVATE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PRIVATE b/PRIVATE index a535b399a..be7872952 160000 --- a/PRIVATE +++ b/PRIVATE @@ -1 +1 @@ -Subproject commit a535b399a91ef1b117b88332b77bbba90dac83f2 +Subproject commit be78729525144accdbcda97e9abc625558af89cb From db91803b807ef016c18cf43460adb85b868f3369 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 10 Dec 2019 17:52:37 +0100 Subject: [PATCH 059/223] cleaning --- PRIVATE | 2 +- src/damage_local.f90 | 38 -------------------------------- src/damage_nonlocal.f90 | 40 --------------------------------- src/homogenization.f90 | 49 ++--------------------------------------- 4 files changed, 3 insertions(+), 126 deletions(-) diff --git a/PRIVATE b/PRIVATE index be7872952..952238b95 160000 --- a/PRIVATE +++ b/PRIVATE @@ -1 +1 @@ -Subproject commit be78729525144accdbcda97e9abc625558af89cb +Subproject commit 952238b951a3d0c1c79df52530681724d3dead2e diff --git a/src/damage_local.f90 b/src/damage_local.f90 index a57bb5c65..aa9292f49 100644 --- a/src/damage_local.f90 +++ b/src/damage_local.f90 @@ -16,8 +16,6 @@ module damage_local implicit none private - integer, dimension(:,:), allocatable, target, public :: & - damage_local_sizePostResult character(len=64), dimension(:,:), allocatable, target, public :: & damage_local_output integer, dimension(:), allocatable, target, public :: & @@ -43,7 +41,6 @@ module damage_local public :: & damage_local_init, & damage_local_updateState, & - damage_local_postResults, & damage_local_Results contains @@ -68,7 +65,6 @@ subroutine damage_local_init maxNinstance = count(damage_type == DAMAGE_local_ID) if (maxNinstance == 0) return - allocate(damage_local_sizePostResult (maxval(homogenization_Noutput),maxNinstance),source=0) allocate(damage_local_output (maxval(homogenization_Noutput),maxNinstance)) damage_local_output = '' allocate(damage_local_outputID (maxval(homogenization_Noutput),maxNinstance),source=undefined_ID) @@ -92,7 +88,6 @@ subroutine damage_local_init case ('damage') damage_local_output(i,damage_typeInstance(h)) = outputs(i) damage_local_Noutput(instance) = damage_local_Noutput(instance) + 1 - damage_local_sizePostResult(i,damage_typeInstance(h)) = 1 prm%outputID = [prm%outputID , damage_ID] end select @@ -108,7 +103,6 @@ subroutine damage_local_init ! allocate state arrays sizeState = 1 damageState(homog)%sizeState = sizeState - damageState(homog)%sizePostResults = sum(damage_local_sizePostResult(:,instance)) allocate(damageState(homog)%state0 (sizeState,NofMyHomog), source=damage_initialPhi(homog)) allocate(damageState(homog)%subState0(sizeState,NofMyHomog), source=damage_initialPhi(homog)) allocate(damageState(homog)%state (sizeState,NofMyHomog), source=damage_initialPhi(homog)) @@ -239,36 +233,4 @@ subroutine damage_local_results(homog,group) end subroutine damage_local_results -!-------------------------------------------------------------------------------------------------- -!> @brief return array of damage results -!-------------------------------------------------------------------------------------------------- -function damage_local_postResults(ip,el) - - integer, intent(in) :: & - ip, & !< integration point - el !< element - real(pReal), dimension(sum(damage_local_sizePostResult(:,damage_typeInstance(material_homogenizationAt(el))))) :: & - damage_local_postResults - - integer :: instance, homog, offset, o, c - - homog = material_homogenizationAt(el) - offset = damageMapping(homog)%p(ip,el) - instance = damage_typeInstance(homog) - associate(prm => param(instance)) - c = 0 - - outputsLoop: do o = 1,size(prm%outputID) - select case(prm%outputID(o)) - - case (damage_ID) - damage_local_postResults(c+1) = damage(homog)%p(offset) - c = c + 1 - end select - enddo outputsLoop - - end associate - -end function damage_local_postResults - end module damage_local diff --git a/src/damage_nonlocal.f90 b/src/damage_nonlocal.f90 index a5e211783..855fa0ea5 100644 --- a/src/damage_nonlocal.f90 +++ b/src/damage_nonlocal.f90 @@ -19,8 +19,6 @@ module damage_nonlocal implicit none private - integer, dimension(:,:), allocatable, target, public :: & - damage_nonlocal_sizePostResult character(len=64), dimension(:,:), allocatable, target, public :: & damage_nonlocal_output integer, dimension(:), allocatable, target, public :: & @@ -46,7 +44,6 @@ module damage_nonlocal damage_nonlocal_getDiffusion33, & damage_nonlocal_getMobility, & damage_nonlocal_putNonLocalDamage, & - damage_nonlocal_postResults, & damage_nonlocal_Results contains @@ -71,7 +68,6 @@ subroutine damage_nonlocal_init maxNinstance = count(damage_type == DAMAGE_nonlocal_ID) if (maxNinstance == 0) return - allocate(damage_nonlocal_sizePostResult (maxval(homogenization_Noutput),maxNinstance),source=0) allocate(damage_nonlocal_output (maxval(homogenization_Noutput),maxNinstance)) damage_nonlocal_output = '' allocate(damage_nonlocal_Noutput (maxNinstance), source=0) @@ -94,7 +90,6 @@ subroutine damage_nonlocal_init case ('damage') damage_nonlocal_output(i,damage_typeInstance(h)) = outputs(i) damage_nonlocal_Noutput(instance) = damage_nonlocal_Noutput(instance) + 1 - damage_nonlocal_sizePostResult(i,damage_typeInstance(h)) = 1 prm%outputID = [prm%outputID , damage_ID] end select @@ -109,7 +104,6 @@ subroutine damage_nonlocal_init ! allocate state arrays sizeState = 1 damageState(homog)%sizeState = sizeState - damageState(homog)%sizePostResults = sum(damage_nonlocal_sizePostResult(:,instance)) allocate(damageState(homog)%state0 (sizeState,NofMyHomog), source=damage_initialPhi(homog)) allocate(damageState(homog)%subState0(sizeState,NofMyHomog), source=damage_initialPhi(homog)) allocate(damageState(homog)%state (sizeState,NofMyHomog), source=damage_initialPhi(homog)) @@ -248,7 +242,6 @@ subroutine damage_nonlocal_putNonLocalDamage(phi,ip,el) end subroutine damage_nonlocal_putNonLocalDamage - !-------------------------------------------------------------------------------------------------- !> @brief writes results to HDF5 output file !-------------------------------------------------------------------------------------------------- @@ -275,37 +268,4 @@ subroutine damage_nonlocal_results(homog,group) end subroutine damage_nonlocal_results - -!-------------------------------------------------------------------------------------------------- -!> @brief return array of damage results -!-------------------------------------------------------------------------------------------------- -function damage_nonlocal_postResults(ip,el) - - integer, intent(in) :: & - ip, & !< integration point - el !< element - real(pReal), dimension(sum(damage_nonlocal_sizePostResult(:,damage_typeInstance(material_homogenizationAt(el))))) :: & - damage_nonlocal_postResults - - integer :: & - instance, homog, offset, o, c - - homog = material_homogenizationAt(el) - offset = damageMapping(homog)%p(ip,el) - instance = damage_typeInstance(homog) - associate(prm => param(instance)) - c = 0 - - outputsLoop: do o = 1,size(prm%outputID) - select case(prm%outputID(o)) - - case (damage_ID) - damage_nonlocal_postResults(c+1) = damage(homog)%p(offset) - c = c + 1 - end select - enddo outputsLoop - - end associate -end function damage_nonlocal_postResults - end module damage_nonlocal diff --git a/src/homogenization.f90 b/src/homogenization.f90 index 842c5f4b6..5fa723eda 100644 --- a/src/homogenization.f90 +++ b/src/homogenization.f90 @@ -39,8 +39,7 @@ module homogenization materialpoint_results !< results array of material point integer, public, protected :: & materialpoint_sizeResults, & - thermal_maxSizePostResults, & - damage_maxSizePostResults + thermal_maxSizePostResults real(pReal), dimension(:,:,:,:), allocatable :: & materialpoint_subF0, & !< def grad of IP at beginning of homogenization increment @@ -196,35 +195,6 @@ subroutine homogenization_init endif endif - i = damage_typeInstance(p) ! which instance of this damage type - valid = .true. ! assume valid - select case(damage_type(p)) ! split per damage type - case (DAMAGE_none_ID) - outputName = DAMAGE_none_label - thisNoutput => null() - thisOutput => null() - thisSize => null() - case (DAMAGE_local_ID) - outputName = DAMAGE_local_label - thisNoutput => damage_local_Noutput - thisOutput => damage_local_output - thisSize => damage_local_sizePostResult - case (DAMAGE_nonlocal_ID) - outputName = DAMAGE_nonlocal_label - thisNoutput => damage_nonlocal_Noutput - thisOutput => damage_nonlocal_output - thisSize => damage_nonlocal_sizePostResult - case default - valid = .false. - end select - if (valid) then - write(FILEUNIT,'(a)') '(damage)'//char(9)//trim(outputName) - if (damage_type(p) /= DAMAGE_none_ID) then - do e = 1,thisNoutput(i) - write(FILEUNIT,'(a,i4)') trim(thisOutput(e,i))//char(9),thisSize(e,i) - enddo - endif - endif endif enddo close(FILEUNIT) @@ -252,15 +222,12 @@ subroutine homogenization_init !-------------------------------------------------------------------------------------------------- ! allocate and initialize global state and postresutls variables thermal_maxSizePostResults = 0 - damage_maxSizePostResults = 0 do p = 1,size(config_homogenization) thermal_maxSizePostResults = max(thermal_maxSizePostResults, thermalState(p)%sizePostResults) - damage_maxSizePostResults = max(damage_maxSizePostResults, damageState (p)%sizePostResults) enddo materialpoint_sizeResults = 1 & ! grain count + 1 + thermal_maxSizePostResults & - + damage_maxSizePostResults & + homogenization_maxNgrains * 2 ! obsolete header information allocate(materialpoint_results(materialpoint_sizeResults,discretization_nIP,discretization_nElem)) @@ -742,8 +709,7 @@ function postResults(ip,el) integer, intent(in) :: & ip, & !< integration point el !< element number - real(pReal), dimension( thermalState (material_homogenizationAt(el))%sizePostResults & - + damageState (material_homogenizationAt(el))%sizePostResults) :: & + real(pReal), dimension( thermalState (material_homogenizationAt(el))%sizePostResults) :: & postResults integer :: & startPos, endPos ,& @@ -766,17 +732,6 @@ function postResults(ip,el) end select chosenThermal - startPos = endPos + 1 - endPos = endPos + damageState(material_homogenizationAt(el))%sizePostResults - chosenDamage: select case (damage_type(material_homogenizationAt(el))) - - case (DAMAGE_local_ID) chosenDamage - postResults(startPos:endPos) = damage_local_postResults(ip, el) - case (DAMAGE_nonlocal_ID) chosenDamage - postResults(startPos:endPos) = damage_nonlocal_postResults(ip, el) - - end select chosenDamage - end function postResults From 4c1281b4fc417e3042c501e2e0c36a25ec1fe742 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 10 Dec 2019 20:04:03 +0100 Subject: [PATCH 060/223] rename changed order in shapes dict. This resulted in wrong column names when writing to ASCII file two fixes (one would be enough): 1) keep order (build new directory) 2) write in order of labels in pandas dataframe, not in order in shapes dict --- python/damask/table.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/python/damask/table.py b/python/damask/table.py index 6181fdb1f..56af8b622 100644 --- a/python/damask/table.py +++ b/python/damask/table.py @@ -203,7 +203,7 @@ class Table(): '' if info is None else ': {}'.format(info), )) - self.shapes[label_new] = self.shapes.pop(label_old) + self.shapes = {(label if label is not label_old else label_new):self.shapes[label] for label in self.shapes} def sort_by(self,labels,ascending=True): @@ -234,8 +234,9 @@ class Table(): Filename or file for reading. """ + seen = set() labels = [] - for l in self.shapes: + for l in [x for x in self.data.columns if not (x in seen or seen.add(x))]: if(self.shapes[l] == (1,)): labels.append('{}'.format(l)) elif(len(self.shapes[l]) == 1): From e142d00d02a4229ceee3203e270ecdc16360902f Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 10 Dec 2019 20:05:24 +0100 Subject: [PATCH 061/223] test now sensible to wrong rename --- python/tests/test_Table.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/python/tests/test_Table.py b/python/tests/test_Table.py index a0dc31975..2046d3803 100644 --- a/python/tests/test_Table.py +++ b/python/tests/test_Table.py @@ -65,11 +65,13 @@ class TestTable: default.add('nine',d,'random data') assert np.allclose(d,default.get('nine')) - def test_rename_equivalent(self,default): - v = default.get('v') - default.rename('v','u') - u = default.get('u') - assert np.all(v == u) + def test_rename_equivalent(self): + x = np.random.random((5,13)) + t = Table(x,{'F':(3,3),'v':(3,),'s':(1,)},['random test data']) + s = t.get('s') + t.rename('s','u') + u = t.get('u') + assert np.all(s == u) def test_rename_gone(self,default): default.rename('v','V') From 8fb8e9be6ea2a3fff9c2d037df9a11f831943d86 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 10 Dec 2019 20:25:19 +0100 Subject: [PATCH 062/223] write temperature to DADF5 --- src/homogenization.f90 | 7 +++++++ src/thermal_adiabatic.f90 | 26 ++++++++++++++++++++++++++ src/thermal_conduction.f90 | 27 +++++++++++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/src/homogenization.f90 b/src/homogenization.f90 index 5fa723eda..b014bebb5 100644 --- a/src/homogenization.f90 +++ b/src/homogenization.f90 @@ -779,6 +779,13 @@ subroutine homogenization_results group = trim(group_base)//'/thermal' call results_closeGroup(results_addGroup(group)) + select case(thermal_type(p)) + case(THERMAL_ADIABATIC_ID) + call thermal_adiabatic_results(p,group) + case(THERMAL_CONDUCTION_ID) + call thermal_conduction_results(p,group) + end select + enddo diff --git a/src/thermal_adiabatic.f90 b/src/thermal_adiabatic.f90 index 2aa69bec5..5c30f280b 100644 --- a/src/thermal_adiabatic.f90 +++ b/src/thermal_adiabatic.f90 @@ -7,6 +7,7 @@ module thermal_adiabatic use config use numerics use material + use results use source_thermal_dissipation use source_thermal_externalheat use crystallite @@ -37,6 +38,7 @@ module thermal_adiabatic thermal_adiabatic_getSourceAndItsTangent, & thermal_adiabatic_getSpecificHeat, & thermal_adiabatic_getMassDensity, & + thermal_adiabatic_results, & thermal_adiabatic_postResults contains @@ -251,6 +253,30 @@ function thermal_adiabatic_getMassDensity(ip,el) end function thermal_adiabatic_getMassDensity +!-------------------------------------------------------------------------------------------------- +!> @brief writes results to HDF5 output file +!-------------------------------------------------------------------------------------------------- +subroutine thermal_adiabatic_results(homog,group) + + integer, intent(in) :: homog + character(len=*), intent(in) :: group +#if defined(PETSc) || defined(DAMASK_HDF5) + integer :: o, instance + + instance = thermal_typeInstance(homog) + + outputsLoop: do o = 1,thermal_adiabatic_Noutput(instance) + select case(thermal_adiabatic_outputID(o,instance)) + + case (temperature_ID) + call results_writeDataset(group,temperature(homog)%p,'T',& + 'temperature','K') + end select + enddo outputsLoop +#endif + +end subroutine thermal_adiabatic_results + !-------------------------------------------------------------------------------------------------- !> @brief return array of thermal results !-------------------------------------------------------------------------------------------------- diff --git a/src/thermal_conduction.f90 b/src/thermal_conduction.f90 index e513d709f..5507d8a7d 100644 --- a/src/thermal_conduction.f90 +++ b/src/thermal_conduction.f90 @@ -7,6 +7,7 @@ module thermal_conduction use material use config use lattice + use results use crystallite use source_thermal_dissipation use source_thermal_externalheat @@ -37,6 +38,7 @@ module thermal_conduction thermal_conduction_getSpecificHeat, & thermal_conduction_getMassDensity, & thermal_conduction_putTemperatureAndItsRate, & + thermal_conduction_results, & thermal_conduction_postResults contains @@ -263,6 +265,31 @@ subroutine thermal_conduction_putTemperatureAndItsRate(T,Tdot,ip,el) end subroutine thermal_conduction_putTemperatureAndItsRate + +!-------------------------------------------------------------------------------------------------- +!> @brief writes results to HDF5 output file +!-------------------------------------------------------------------------------------------------- +subroutine thermal_conduction_results(homog,group) + + integer, intent(in) :: homog + character(len=*), intent(in) :: group +#if defined(PETSc) || defined(DAMASK_HDF5) + integer :: o, instance + + instance = thermal_typeInstance(homog) + + outputsLoop: do o = 1,thermal_conduction_Noutput(instance) + select case(thermal_conduction_outputID(o,instance)) + + case (temperature_ID) + call results_writeDataset(group,temperature(homog)%p,'T',& + 'temperature','K') + end select + enddo outputsLoop +#endif + +end subroutine thermal_conduction_results + !-------------------------------------------------------------------------------------------------- !> @brief return array of thermal results From e3b316bcaeee886f644bae31c19d4dc30d89ca64 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 10 Dec 2019 20:33:44 +0100 Subject: [PATCH 063/223] all tests without spectral Out --- PRIVATE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PRIVATE b/PRIVATE index 952238b95..4c20d48ec 160000 --- a/PRIVATE +++ b/PRIVATE @@ -1 +1 @@ -Subproject commit 952238b951a3d0c1c79df52530681724d3dead2e +Subproject commit 4c20d48ec1df8230dde85afbc3e7b5efca8cd314 From 9b67ead62f4b7795499d6ae6f7b64ddd9e9c56ad Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 11 Dec 2019 00:10:02 +0100 Subject: [PATCH 064/223] removed postResults completely --- src/CPFEM2.f90 | 1 - src/DAMASK_abaqus.f | 5 +- src/constitutive.f90 | 11 --- src/crystallite.f90 | 8 -- src/grid/DAMASK_grid.f90 | 78 +------------------ src/homogenization.f90 | 150 +------------------------------------ src/prec.f90 | 3 +- src/thermal_adiabatic.f90 | 37 +-------- src/thermal_conduction.f90 | 37 +-------- 9 files changed, 8 insertions(+), 322 deletions(-) diff --git a/src/CPFEM2.f90 b/src/CPFEM2.f90 index 7123602f8..ace3e51f4 100644 --- a/src/CPFEM2.f90 +++ b/src/CPFEM2.f90 @@ -65,7 +65,6 @@ subroutine CPFEM_initAll call constitutive_init call crystallite_init call homogenization_init - call materialpoint_postResults call CPFEM_init end subroutine CPFEM_initAll diff --git a/src/DAMASK_abaqus.f b/src/DAMASK_abaqus.f index e2c56a06e..0f663dde3 100644 --- a/src/DAMASK_abaqus.f +++ b/src/DAMASK_abaqus.f @@ -143,9 +143,6 @@ subroutine UMAT(STRESS,STATEV,DDSDDE,SSE,SPD,SCD,& outdatedByNewInc, & outdatedFFN1, & lastStep - use homogenization, only: & - materialpoint_sizeResults, & - materialpoint_results implicit none integer(pInt), intent(in) :: & @@ -332,7 +329,7 @@ subroutine UMAT(STRESS,STATEV,DDSDDE,SSE,SPD,SCD,& ddsdde(6,:) = ddsdde_h(5,:) end if - statev = materialpoint_results(1:min(nstatv,materialpoint_sizeResults),npt,mesh_FEasCP('elem', noel)) + statev = 0 if (terminallyIll) pnewdt = 0.5_pReal ! force cutback directly ? !$ call omp_set_num_threads(defaultNumThreadsInt) ! reset number of threads to stored default value diff --git a/src/constitutive.f90 b/src/constitutive.f90 index 2d0688467..1fd833f29 100644 --- a/src/constitutive.f90 +++ b/src/constitutive.f90 @@ -93,16 +93,6 @@ subroutine constitutive_init write(6,'(/,a)') ' <<<+- constitutive init -+>>>'; flush(6) - mainProcess: if (worldrank == 0) then -!-------------------------------------------------------------------------------------------------- -! write description file for constitutive output - call IO_write_jobFile(204,'outputConstitutive') - PhaseLoop: do ph = 1,material_Nphase - if (any(material_phaseAt == ph)) write(204,'(/,a,/)') '['//trim(config_name_phase(ph))//']' - enddo PhaseLoop - close(204) - endif mainProcess - constitutive_plasticity_maxSizeDotState = 0 constitutive_source_maxSizeDotState = 0 @@ -123,7 +113,6 @@ subroutine constitutive_init maxval(sourceState(ph)%p(:)%sizeDotState)) enddo PhaseLoop2 - end subroutine constitutive_init diff --git a/src/crystallite.f90 b/src/crystallite.f90 index 4ae87b82e..9b7768e4a 100644 --- a/src/crystallite.f90 +++ b/src/crystallite.f90 @@ -118,7 +118,6 @@ contains !-------------------------------------------------------------------------------------------------- subroutine crystallite_init - integer, parameter :: FILEUNIT=434 logical, dimension(:,:), allocatable :: devNull integer :: & c, & !< counter in integration point component loop @@ -232,13 +231,6 @@ subroutine crystallite_init #endif enddo -!-------------------------------------------------------------------------------------------------- -! write description file for crystallite output - if (worldrank == 0) then - call IO_write_jobFile(FILEUNIT,'outputCrystallite') - write(FILEUNIT,'(/,a,/)') '[not supported anymore]' - close(FILEUNIT) - endif call config_deallocate('material.config/phase') !-------------------------------------------------------------------------------------------------- diff --git a/src/grid/DAMASK_grid.f90 b/src/grid/DAMASK_grid.f90 index 51c97456b..d2a86d769 100644 --- a/src/grid/DAMASK_grid.f90 +++ b/src/grid/DAMASK_grid.f90 @@ -15,11 +15,7 @@ program DAMASK_spectral use config use debug use math - use mesh_grid use CPFEM2 - use FEsolving - use numerics - use homogenization use material use spectral_utilities use grid_mech_spectral_basic @@ -80,12 +76,6 @@ program DAMASK_spectral type(tLoadCase), allocatable, dimension(:) :: loadCases !< array of all load cases type(tLoadCase) :: newLoadCase type(tSolutionState), allocatable, dimension(:) :: solres - integer(MPI_OFFSET_KIND) :: fileOffset - integer(MPI_OFFSET_KIND), dimension(:), allocatable :: outputSize - integer, parameter :: maxByteOut = 2147483647-4096 !< limit of one file output write https://trac.mpich.org/projects/mpich/ticket/1742 - integer, parameter :: maxRealOut = maxByteOut/pReal - integer(pLongInt), dimension(2) :: outputIndex - PetscErrorCode :: ierr procedure(grid_mech_spectral_basic_init), pointer :: & mech_init procedure(grid_mech_spectral_basic_forward), pointer :: & @@ -280,10 +270,8 @@ program DAMASK_spectral enddo if (any(newLoadCase%stress%maskLogical .eqv. & newLoadCase%deformation%maskLogical)) errorID = 831 ! exclusive or masking only - if (any(newLoadCase%stress%maskLogical .and. & - transpose(newLoadCase%stress%maskLogical) .and. & - reshape([ .false.,.true.,.true.,.true.,.false.,.true.,.true.,.true.,.false.],[ 3,3]))) & - errorID = 838 ! no rotation is allowed by stress BC + if (any(newLoadCase%stress%maskLogical .and. transpose(newLoadCase%stress%maskLogical) & + .and. (math_I3<1))) errorID = 838 ! no rotation is allowed by stress BC write(6,'(2x,a)') 'stress / GPa:' do i = 1, 3; do j = 1, 3 if(newLoadCase%stress%maskLogical(i,j)) then @@ -335,26 +323,10 @@ program DAMASK_spectral ! write header of output file if (worldrank == 0) then writeHeader: if (interface_restartInc < 1) then - open(newunit=fileUnit,file=trim(getSolverJobName())//& - '.spectralOut',form='UNFORMATTED',status='REPLACE') - write(fileUnit) 'load:', trim(loadCaseFile) ! ... and write header - write(fileUnit) 'workingdir:', 'n/a' - write(fileUnit) 'geometry:', trim(geometryFile) - write(fileUnit) 'grid:', grid - write(fileUnit) 'size:', geomSize - write(fileUnit) 'materialpoint_sizeResults:', materialpoint_sizeResults - write(fileUnit) 'loadcases:', size(loadCases) - write(fileUnit) 'frequencies:', loadCases%outputfrequency ! one entry per LoadCase - write(fileUnit) 'times:', loadCases%time ! one entry per LoadCase - write(fileUnit) 'logscales:', loadCases%logscale - write(fileUnit) 'increments:', loadCases%incs ! one entry per LoadCase - write(fileUnit) 'startingIncrement:', interface_restartInc ! start with writing out the previous inc - write(fileUnit) 'eoh' - close(fileUnit) ! end of header open(newunit=statUnit,file=trim(getSolverJobName())//'.sta',form='FORMATTED',status='REPLACE') write(statUnit,'(a)') 'Increment Time CutbackLevel Converged IterationsNeeded' ! statistics file if (iand(debug_level(debug_spectral),debug_levelBasic) /= 0) & - write(6,'(/,a)') ' header of result and statistics file written out' + write(6,'(/,a)') ' header of statistics file written out' flush(6) else writeHeader open(newunit=statUnit,file=trim(getSolverJobName())//& @@ -362,40 +334,11 @@ program DAMASK_spectral endif writeHeader endif -!-------------------------------------------------------------------------------------------------- -! prepare MPI parallel out (including opening of file) - allocate(outputSize(worldsize), source = 0_MPI_OFFSET_KIND) - outputSize(worldrank+1) = size(materialpoint_results,kind=MPI_OFFSET_KIND)*int(pReal,MPI_OFFSET_KIND) - call MPI_allreduce(MPI_IN_PLACE,outputSize,worldsize,MPI_LONG,MPI_SUM,PETSC_COMM_WORLD,ierr) ! get total output size over each process - if (ierr /= 0) call IO_error(error_ID=894, ext_msg='MPI_allreduce') - call MPI_file_open(PETSC_COMM_WORLD, trim(getSolverJobName())//'.spectralOut', & - MPI_MODE_WRONLY + MPI_MODE_APPEND, & - MPI_INFO_NULL, & - fileUnit, & - ierr) - if (ierr /= 0) call IO_error(error_ID=894, ext_msg='MPI_file_open') - call MPI_file_get_position(fileUnit,fileOffset,ierr) ! get offset from header - if (ierr /= 0) call IO_error(error_ID=894, ext_msg='MPI_file_get_position') - fileOffset = fileOffset + sum(outputSize(1:worldrank)) ! offset of my process in file (header + processes before me) - call MPI_file_seek (fileUnit,fileOffset,MPI_SEEK_SET,ierr) - if (ierr /= 0) call IO_error(error_ID=894, ext_msg='MPI_file_seek') - writeUndeformed: if (interface_restartInc < 1) then write(6,'(1/,a)') ' ... writing initial configuration to file ........................' call CPFEM_results(0,0.0_pReal) - do i = 1, size(materialpoint_results,3)/(maxByteOut/(materialpoint_sizeResults*pReal))+1 ! slice the output of my process in chunks not exceeding the limit for one output - outputIndex = int([(i-1)*((maxRealOut)/materialpoint_sizeResults)+1, & - min(i*((maxRealOut)/materialpoint_sizeResults),size(materialpoint_results,3))],pLongInt) - call MPI_file_write(fileUnit,reshape(materialpoint_results(:,:,outputIndex(1):outputIndex(2)), & - [(outputIndex(2)-outputIndex(1)+1)*int(materialpoint_sizeResults,pLongInt)]), & - int((outputIndex(2)-outputIndex(1)+1)*int(materialpoint_sizeResults,pLongInt)), & - MPI_DOUBLE, MPI_STATUS_IGNORE, ierr) - if (ierr /= 0) call IO_error(error_ID=894, ext_msg='MPI_file_write') - enddo - fileOffset = fileOffset + sum(outputSize) ! forward to current file position endif writeUndeformed - loadCaseLooping: do currentLoadCase = 1, size(loadCases) time0 = time ! load case start time guess = loadCases(currentLoadCase)%followFormerTrajectory ! change of load case? homogeneous guess for the first inc @@ -526,7 +469,6 @@ program DAMASK_spectral write(6,'(/,a)') ' cutting back ' else ! no more options to continue call IO_warning(850) - call MPI_File_close(fileUnit,ierr) close(statUnit) call quit(0) ! quit endif @@ -546,19 +488,6 @@ program DAMASK_spectral if (mod(inc,loadCases(currentLoadCase)%outputFrequency) == 0) then ! at output frequency write(6,'(1/,a)') ' ... writing results to file ......................................' flush(6) - call materialpoint_postResults() - call MPI_File_seek (fileUnit,fileOffset,MPI_SEEK_SET,ierr) - if (ierr /= 0) call IO_error(894, ext_msg='MPI_file_seek') - do i=1, size(materialpoint_results,3)/(maxByteOut/(materialpoint_sizeResults*pReal))+1 ! slice the output of my process in chunks not exceeding the limit for one output - outputIndex=int([(i-1)*((maxRealOut)/materialpoint_sizeResults)+1, & - min(i*((maxRealOut)/materialpoint_sizeResults),size(materialpoint_results,3))],pLongInt) - call MPI_file_write(fileUnit,reshape(materialpoint_results(:,:,outputIndex(1):outputIndex(2)),& - [(outputIndex(2)-outputIndex(1)+1)*int(materialpoint_sizeResults,pLongInt)]), & - int((outputIndex(2)-outputIndex(1)+1)*int(materialpoint_sizeResults,pLongInt)),& - MPI_DOUBLE, MPI_STATUS_IGNORE, ierr) - if(ierr /=0) call IO_error(894, ext_msg='MPI_file_write') - enddo - fileOffset = fileOffset + sum(outputSize) ! forward to current file position call CPFEM_results(totalIncsCounter,time) endif if (mod(inc,loadCases(currentLoadCase)%restartFrequency) == 0) then @@ -575,7 +504,6 @@ program DAMASK_spectral !-------------------------------------------------------------------------------------------------- ! report summary of whole calculation write(6,'(/,a)') ' ###########################################################################' - call MPI_file_close(fileUnit,ierr) close(statUnit) call quit(0) ! no complains ;) diff --git a/src/homogenization.f90 b/src/homogenization.f90 index b014bebb5..a3a9be2f0 100644 --- a/src/homogenization.f90 +++ b/src/homogenization.f90 @@ -35,11 +35,6 @@ module homogenization materialpoint_P !< first P--K stress of IP real(pReal), dimension(:,:,:,:,:,:), allocatable, public :: & materialpoint_dPdF !< tangent of first P--K stress at IP - real(pReal), dimension(:,:,:), allocatable, public :: & - materialpoint_results !< results array of material point - integer, public, protected :: & - materialpoint_sizeResults, & - thermal_maxSizePostResults real(pReal), dimension(:,:,:,:), allocatable :: & materialpoint_subF0, & !< def grad of IP at beginning of homogenization increment @@ -124,7 +119,6 @@ module homogenization public :: & homogenization_init, & materialpoint_stressAndItsTangent, & - materialpoint_postResults, & homogenization_results contains @@ -135,14 +129,6 @@ contains !-------------------------------------------------------------------------------------------------- subroutine homogenization_init - integer, parameter :: FILEUNIT = 200 - integer :: e,i,p - integer, dimension(:,:), pointer :: thisSize - integer, dimension(:) , pointer :: thisNoutput - character(len=64), dimension(:,:), pointer :: thisOutput - character(len=32) :: outputName !< name of output, intermediate fix until HDF5 output is ready - logical :: valid - if (any(homogenization_type == HOMOGENIZATION_NONE_ID)) call mech_none_init if (any(homogenization_type == HOMOGENIZATION_ISOSTRAIN_ID)) call mech_isostrain_init if (any(homogenization_type == HOMOGENIZATION_RGC_ID)) call mech_RGC_init @@ -155,51 +141,6 @@ subroutine homogenization_init if (any(damage_type == DAMAGE_local_ID)) call damage_local_init if (any(damage_type == DAMAGE_nonlocal_ID)) call damage_nonlocal_init -!-------------------------------------------------------------------------------------------------- -! write description file for homogenization output - mainProcess: if (worldrank == 0) then - call IO_write_jobFile(FILEUNIT,'outputHomogenization') - do p = 1,size(config_homogenization) - if (any(material_homogenizationAt == p)) then - write(FILEUNIT,'(/,a,/)') '['//trim(config_name_homogenization(p))//']' - write(FILEUNIT,'(a)') '(type) n/a' - write(FILEUNIT,'(a,i4)') '(ngrains)'//char(9),homogenization_Ngrains(p) - - i = thermal_typeInstance(p) ! which instance of this thermal type - valid = .true. ! assume valid - select case(thermal_type(p)) ! split per thermal type - case (THERMAL_isothermal_ID) - outputName = THERMAL_isothermal_label - thisNoutput => null() - thisOutput => null() - thisSize => null() - case (THERMAL_adiabatic_ID) - outputName = THERMAL_adiabatic_label - thisNoutput => thermal_adiabatic_Noutput - thisOutput => thermal_adiabatic_output - thisSize => thermal_adiabatic_sizePostResult - case (THERMAL_conduction_ID) - outputName = THERMAL_conduction_label - thisNoutput => thermal_conduction_Noutput - thisOutput => thermal_conduction_output - thisSize => thermal_conduction_sizePostResult - case default - valid = .false. - end select - if (valid) then - write(FILEUNIT,'(a)') '(thermal)'//char(9)//trim(outputName) - if (thermal_type(p) /= THERMAL_isothermal_ID) then - do e = 1,thisNoutput(i) - write(FILEUNIT,'(a,i4)') trim(thisOutput(e,i))//char(9),thisSize(e,i) - enddo - endif - endif - - endif - enddo - close(FILEUNIT) - endif mainProcess - call config_deallocate('material.config/homogenization') !-------------------------------------------------------------------------------------------------- @@ -219,19 +160,7 @@ subroutine homogenization_init allocate(materialpoint_converged(discretization_nIP,discretization_nElem), source=.true.) allocate(materialpoint_doneAndHappy(2,discretization_nIP,discretization_nElem), source=.true.) -!-------------------------------------------------------------------------------------------------- -! allocate and initialize global state and postresutls variables - thermal_maxSizePostResults = 0 - do p = 1,size(config_homogenization) - thermal_maxSizePostResults = max(thermal_maxSizePostResults, thermalState(p)%sizePostResults) - enddo - - materialpoint_sizeResults = 1 & ! grain count - + 1 + thermal_maxSizePostResults & - + homogenization_maxNgrains * 2 ! obsolete header information - allocate(materialpoint_results(materialpoint_sizeResults,discretization_nIP,discretization_nElem)) - - write(6,'(/,a)') ' <<<+- homogenization init -+>>>' + write(6,'(/,a)') ' <<<+- homogenization init -+>>>'; flush(6) if (iand(debug_level(debug_homogenization), debug_levelBasic) /= 0) then write(6,'(a32,1x,7(i8,1x))') 'materialpoint_dPdF: ', shape(materialpoint_dPdF) @@ -547,48 +476,6 @@ subroutine materialpoint_stressAndItsTangent(updateJaco,dt) end subroutine materialpoint_stressAndItsTangent -!-------------------------------------------------------------------------------------------------- -!> @brief calculation of result array at material points -!-------------------------------------------------------------------------------------------------- -subroutine materialpoint_postResults - - integer :: & - thePos, & - theSize, & - myNgrains, & - g, & !< grain number - i, & !< integration point number - e !< element number - - elementLooping: do e = FEsolving_execElem(1),FEsolving_execElem(2) - myNgrains = homogenization_Ngrains(material_homogenizationAt(e)) - IpLooping: do i = FEsolving_execIP(1,e),FEsolving_execIP(2,e) - thePos = 0 - - theSize = thermalState (material_homogenizationAt(e))%sizePostResults & - + damageState (material_homogenizationAt(e))%sizePostResults - materialpoint_results(thePos+1,i,e) = real(theSize,pReal) ! tell size of homogenization results - thePos = thePos + 1 - - if (theSize > 0) then ! any homogenization results to mention? - materialpoint_results(thePos+1:thePos+theSize,i,e) = postResults(i,e) - thePos = thePos + theSize - endif - - materialpoint_results(thePos+1,i,e) = real(myNgrains,pReal) ! tell number of grains at materialpoint - thePos = thePos + 1 - - grainLooping :do g = 1,myNgrains - theSize = 2 - materialpoint_results(thePos+1:thePos+theSize,i,e) = 0.0_pReal - thePos = thePos + theSize - enddo grainLooping - enddo IpLooping - enddo elementLooping - -end subroutine materialpoint_postResults - - !-------------------------------------------------------------------------------------------------- !> @brief partition material point def grad onto constituents !-------------------------------------------------------------------------------------------------- @@ -700,41 +587,6 @@ subroutine averageStressAndItsTangent(ip,el) end subroutine averageStressAndItsTangent -!-------------------------------------------------------------------------------------------------- -!> @brief return array of homogenization results for post file inclusion. call only, -!> if homogenization_sizePostResults(i,e) > 0 !! -!-------------------------------------------------------------------------------------------------- -function postResults(ip,el) - - integer, intent(in) :: & - ip, & !< integration point - el !< element number - real(pReal), dimension( thermalState (material_homogenizationAt(el))%sizePostResults) :: & - postResults - integer :: & - startPos, endPos ,& - homog - - - postResults = 0.0_pReal - startPos = 1 - endPos = thermalState(material_homogenizationAt(el))%sizePostResults - chosenThermal: select case (thermal_type(material_homogenizationAt(el))) - - case (THERMAL_adiabatic_ID) chosenThermal - homog = material_homogenizationAt(el) - postResults(startPos:endPos) = & - thermal_adiabatic_postResults(homog,thermal_typeInstance(homog),thermalMapping(homog)%p(ip,el)) - case (THERMAL_conduction_ID) chosenThermal - homog = material_homogenizationAt(el) - postResults(startPos:endPos) = & - thermal_conduction_postResults(homog,thermal_typeInstance(homog),thermalMapping(homog)%p(ip,el)) - - end select chosenThermal - -end function postResults - - !-------------------------------------------------------------------------------------------------- !> @brief writes homogenization results to HDF5 output file !-------------------------------------------------------------------------------------------------- diff --git a/src/prec.f90 b/src/prec.f90 index b6d5d4fdf..8fd2495ce 100644 --- a/src/prec.f90 +++ b/src/prec.f90 @@ -42,8 +42,7 @@ module prec sizeState = 0, & !< size of state sizeDotState = 0, & !< size of dot state, i.e. state(1:sizeDot) follows time evolution by dotState rates offsetDeltaState = 0, & !< index offset of delta state - sizeDeltaState = 0, & !< size of delta state, i.e. state(offset+1:offset+sizeDelta) follows time evolution by deltaState increments - sizePostResults = 0 !< size of output data + sizeDeltaState = 0 !< size of delta state, i.e. state(offset+1:offset+sizeDelta) follows time evolution by deltaState increments real(pReal), pointer, dimension(:), contiguous :: & atolState real(pReal), pointer, dimension(:,:), contiguous :: & ! a pointer is needed here because we might point to state/doState. However, they will never point to something, but are rather allocated and, hence, contiguous diff --git a/src/thermal_adiabatic.f90 b/src/thermal_adiabatic.f90 index 5c30f280b..36dd2316b 100644 --- a/src/thermal_adiabatic.f90 +++ b/src/thermal_adiabatic.f90 @@ -16,8 +16,6 @@ module thermal_adiabatic implicit none private - integer, dimension(:,:), allocatable, target, public :: & - thermal_adiabatic_sizePostResult !< size of each post result output character(len=64), dimension(:,:), allocatable, target, public :: & thermal_adiabatic_output !< name of each post result output @@ -38,8 +36,7 @@ module thermal_adiabatic thermal_adiabatic_getSourceAndItsTangent, & thermal_adiabatic_getSpecificHeat, & thermal_adiabatic_getMassDensity, & - thermal_adiabatic_results, & - thermal_adiabatic_postResults + thermal_adiabatic_results contains @@ -59,7 +56,6 @@ subroutine thermal_adiabatic_init maxNinstance = count(thermal_type == THERMAL_adiabatic_ID) if (maxNinstance == 0) return - allocate(thermal_adiabatic_sizePostResult (maxval(homogenization_Noutput),maxNinstance),source=0) allocate(thermal_adiabatic_output (maxval(homogenization_Noutput),maxNinstance)) thermal_adiabatic_output = '' allocate(thermal_adiabatic_outputID (maxval(homogenization_Noutput),maxNinstance),source=undefined_ID) @@ -77,14 +73,12 @@ subroutine thermal_adiabatic_init thermal_adiabatic_Noutput(instance) = thermal_adiabatic_Noutput(instance) + 1 thermal_adiabatic_outputID(thermal_adiabatic_Noutput(instance),instance) = temperature_ID thermal_adiabatic_output(thermal_adiabatic_Noutput(instance),instance) = outputs(i) - thermal_adiabatic_sizePostResult(thermal_adiabatic_Noutput(instance),instance) = 1 end select enddo ! allocate state arrays sizeState = 1 thermalState(section)%sizeState = sizeState - thermalState(section)%sizePostResults = sum(thermal_adiabatic_sizePostResult(:,instance)) allocate(thermalState(section)%state0 (sizeState,NofMyHomog), source=thermal_initialT(section)) allocate(thermalState(section)%subState0(sizeState,NofMyHomog), source=thermal_initialT(section)) allocate(thermalState(section)%state (sizeState,NofMyHomog), source=thermal_initialT(section)) @@ -277,33 +271,4 @@ subroutine thermal_adiabatic_results(homog,group) end subroutine thermal_adiabatic_results -!-------------------------------------------------------------------------------------------------- -!> @brief return array of thermal results -!-------------------------------------------------------------------------------------------------- -function thermal_adiabatic_postResults(homog,instance,of) result(postResults) - - integer, intent(in) :: & - homog, & - instance, & - of - - real(pReal), dimension(sum(thermal_adiabatic_sizePostResult(:,instance))) :: & - postResults - - integer :: & - o, c - - c = 0 - - do o = 1,thermal_adiabatic_Noutput(instance) - select case(thermal_adiabatic_outputID(o,instance)) - - case (temperature_ID) - postResults(c+1) = temperature(homog)%p(of) - c = c + 1 - end select - enddo - -end function thermal_adiabatic_postResults - end module thermal_adiabatic diff --git a/src/thermal_conduction.f90 b/src/thermal_conduction.f90 index 5507d8a7d..ed25fccde 100644 --- a/src/thermal_conduction.f90 +++ b/src/thermal_conduction.f90 @@ -15,8 +15,6 @@ module thermal_conduction implicit none private - integer, dimension(:,:), allocatable, target, public :: & - thermal_conduction_sizePostResult !< size of each post result output character(len=64), dimension(:,:), allocatable, target, public :: & thermal_conduction_output !< name of each post result output @@ -38,8 +36,7 @@ module thermal_conduction thermal_conduction_getSpecificHeat, & thermal_conduction_getMassDensity, & thermal_conduction_putTemperatureAndItsRate, & - thermal_conduction_results, & - thermal_conduction_postResults + thermal_conduction_results contains @@ -62,7 +59,6 @@ subroutine thermal_conduction_init maxNinstance = count(thermal_type == THERMAL_conduction_ID) if (maxNinstance == 0) return - allocate(thermal_conduction_sizePostResult (maxval(homogenization_Noutput),maxNinstance),source=0) allocate(thermal_conduction_output (maxval(homogenization_Noutput),maxNinstance)) thermal_conduction_output = '' allocate(thermal_conduction_outputID (maxval(homogenization_Noutput),maxNinstance),source=undefined_ID) @@ -80,7 +76,6 @@ subroutine thermal_conduction_init thermal_conduction_Noutput(instance) = thermal_conduction_Noutput(instance) + 1 thermal_conduction_outputID(thermal_conduction_Noutput(instance),instance) = temperature_ID thermal_conduction_output(thermal_conduction_Noutput(instance),instance) = outputs(i) - thermal_conduction_sizePostResult(thermal_conduction_Noutput(instance),instance) = 1 end select enddo @@ -88,7 +83,6 @@ subroutine thermal_conduction_init ! allocate state arrays sizeState = 0 thermalState(section)%sizeState = sizeState - thermalState(section)%sizePostResults = sum(thermal_conduction_sizePostResult(:,instance)) allocate(thermalState(section)%state0 (sizeState,NofMyHomog)) allocate(thermalState(section)%subState0(sizeState,NofMyHomog)) allocate(thermalState(section)%state (sizeState,NofMyHomog)) @@ -290,33 +284,4 @@ subroutine thermal_conduction_results(homog,group) end subroutine thermal_conduction_results - -!-------------------------------------------------------------------------------------------------- -!> @brief return array of thermal results -!-------------------------------------------------------------------------------------------------- -function thermal_conduction_postResults(homog,instance,of) result(postResults) - - integer, intent(in) :: & - homog, & - instance, & - of - - real(pReal), dimension(sum(thermal_conduction_sizePostResult(:,instance))) :: & - postResults - - integer :: & - o, c - - c = 0 - do o = 1,thermal_conduction_Noutput(instance) - select case(thermal_conduction_outputID(o,instance)) - - case (temperature_ID) - postResults(c+1) = temperature(homog)%p(of) - c = c + 1 - end select - enddo - -end function thermal_conduction_postResults - end module thermal_conduction From b3639387f0b89a87159d2d09de6db326c350e6e6 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 11 Dec 2019 08:10:45 +0100 Subject: [PATCH 065/223] including fixed tests for restart --- PRIVATE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PRIVATE b/PRIVATE index 4c20d48ec..84eb17697 160000 --- a/PRIVATE +++ b/PRIVATE @@ -1 +1 @@ -Subproject commit 4c20d48ec1df8230dde85afbc3e7b5efca8cd314 +Subproject commit 84eb176974a97026f518fc3f2f4d63a1336280b3 From 0b04843c91efb68d59196f8fcee36b4c512236f8 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 11 Dec 2019 08:11:00 +0100 Subject: [PATCH 066/223] pandas is needed for Table class --- python/setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/setup.py b/python/setup.py index 515401c59..def343ec1 100644 --- a/python/setup.py +++ b/python/setup.py @@ -15,6 +15,7 @@ setuptools.setup( packages=setuptools.find_packages(), include_package_data=True, install_requires = [ + "pandas", "scipy", "h5py", "vtk" From 5e4e53b9f9cfce4d0b4775d51588891079de0d5d Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 11 Dec 2019 08:17:23 +0100 Subject: [PATCH 067/223] including one more test that works without spectralOut --- PRIVATE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PRIVATE b/PRIVATE index 84eb17697..283c7567f 160000 --- a/PRIVATE +++ b/PRIVATE @@ -1 +1 @@ -Subproject commit 84eb176974a97026f518fc3f2f4d63a1336280b3 +Subproject commit 283c7567f1e4f6f1893cb5b5efdbbda1682f7533 From 562d216fa934f185a8205f7791e546b52d1303bb Mon Sep 17 00:00:00 2001 From: "f.basile" Date: Wed, 11 Dec 2019 09:23:12 +0100 Subject: [PATCH 068/223] bcc is passive rotation and fcc is active rotation --- .../tests/reference/Rotation/PoleFigures_OR.m | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/python/tests/reference/Rotation/PoleFigures_OR.m b/python/tests/reference/Rotation/PoleFigures_OR.m index fb2baa6a1..7a6d750ad 100644 --- a/python/tests/reference/Rotation/PoleFigures_OR.m +++ b/python/tests/reference/Rotation/PoleFigures_OR.m @@ -1,7 +1,7 @@ %% Import Script for EBSD Data -% -% Use MTEX -clear ; clear all +% +% Start MTEX first in Matlab +clear ; clear all ; close; %% Specify Crystal and Specimen Symmetries @@ -19,7 +19,7 @@ setMTEXpref('xAxisDirection','north'); setMTEXpref('zAxisDirection','outOfPlane'); %% path to files -pname = 'L:\f.gallardo\DAMASK\python\tests\reference\Rotation'; % has to be changed +pname = 'L:\f.gallardo\DAMASK\python\tests\reference\Rotation'; % has to be changed to hwere you have DAMASK % which files to be imported fname1 = [pname '\bcc_Bain.txt']; fname2 = [pname '\bcc_GT.txt']; fname3 = [pname '\bcc_GT_prime.txt']; @@ -45,17 +45,17 @@ ebsd6 = loadEBSD(fname6,CS_bcc,'interface','generic',... 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Passive Rotation'); ebsd7 = loadEBSD(fname7,CS_fcc,'interface','generic',... - 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Passive Rotation'); + 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Active Rotation'); ebsd8 = loadEBSD(fname8,CS_fcc,'interface','generic',... - 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Passive Rotation'); + 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Active Rotation'); ebsd9 = loadEBSD(fname9,CS_fcc,'interface','generic',... - 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Passive Rotation'); + 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Active Rotation'); ebsd10 = loadEBSD(fname10,CS_fcc,'interface','generic',... - 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Passive Rotation'); + 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Active Rotation'); ebsd11 = loadEBSD(fname11,CS_fcc,'interface','generic',... - 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Passive Rotation'); + 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Active Rotation'); ebsd12 = loadEBSD(fname12,CS_fcc,'interface','generic',... - 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Passive Rotation'); + 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Active Rotation'); %% Plot Data 1stpart_bcc h1 = [Miller(1,0,0,ebsd1.CS),Miller(1,1,0,ebsd1.CS),Miller(1,1,1,ebsd1.CS)]; % 3 pole figures From 37e52fd81f16861b45b96d50349d3aa1f1d29a3d Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 22 Oct 2019 23:31:27 +0200 Subject: [PATCH 069/223] polishing --- python/damask/orientation.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/python/damask/orientation.py b/python/damask/orientation.py index 1b08d2937..a63444155 100644 --- a/python/damask/orientation.py +++ b/python/damask/orientation.py @@ -1126,10 +1126,9 @@ class Orientation: return (Orientation(r,self.lattice), i,j, k == 1) if symmetries else r # disorientation ... # ... own sym, other sym, # self-->other: True, self<--other: False - - def inFZ(self): return self.lattice.symmetry.inFZ(self.rotation.asRodrigues(vector=True)) + def equivalentOrientations(self,members=[]): """List of orientations which are symmetrically equivalent.""" @@ -1145,6 +1144,7 @@ class Orientation: """List of orientations related by the given orientation relationship.""" r = self.lattice.relationOperations(model) return [self.__class__(o*self.rotation,r['lattice']) for o in r['rotations']] + def reduced(self): """Transform orientation to fall into fundamental zone according to symmetry.""" @@ -1152,7 +1152,8 @@ class Orientation: if self.lattice.symmetry.inFZ(me.rotation.asRodrigues(vector=True)): break return self.__class__(me.rotation,self.lattice) - + + def inversePole(self, axis, proper = False, From bd12ef83b92a6cc8722099d366945da7f6ce6f1e Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 11 Dec 2019 17:38:31 +0100 Subject: [PATCH 070/223] parametrizing --- .../tests/reference/Rotation/PoleFigures_OR.m | 116 +++++------------- 1 file changed, 29 insertions(+), 87 deletions(-) diff --git a/python/tests/reference/Rotation/PoleFigures_OR.m b/python/tests/reference/Rotation/PoleFigures_OR.m index 7a6d750ad..a17bdd831 100644 --- a/python/tests/reference/Rotation/PoleFigures_OR.m +++ b/python/tests/reference/Rotation/PoleFigures_OR.m @@ -1,99 +1,41 @@ -%% Import Script for EBSD Data -% % Start MTEX first in Matlab -clear ; clear all ; close; -%% Specify Crystal and Specimen Symmetries +tmp = matlab.desktop.editor.getActive; +cd(fileparts(tmp.Filename)); -% crystal symmetry -CS_bcc = {... - crystalSymmetry('m-3m', [2.8665 2.8665 2.8665], 'mineral', 'Iron-alpha', 'color', 'light blue'),... - crystalSymmetry('m-3m', [1 1 1], 'color', 'light blue')}; -CS_fcc = {... - crystalSymmetry('m-3m', [3.662 3.662 3.662], 'mineral', 'Iron', 'color', 'light blue'),... - crystalSymmetry('m-3m', [1 1 1], 'color', 'light blue')}; +%% Specify Crystal +symmetry = {crystalSymmetry('m-3m', [1 1 1], 'mineral', 'Iron', 'color', 'light blue')} % plotting convention setMTEXpref('xAxisDirection','north'); setMTEXpref('zAxisDirection','outOfPlane'); -%% path to files -pname = 'L:\f.gallardo\DAMASK\python\tests\reference\Rotation'; % has to be changed to hwere you have DAMASK -% which files to be imported -fname1 = [pname '\bcc_Bain.txt']; fname2 = [pname '\bcc_GT.txt']; fname3 = [pname '\bcc_GT_prime.txt']; -fname4 = [pname '\bcc_KS.txt']; fname5 = [pname '\bcc_NW.txt']; fname6 = [pname '\bcc_Pitsch.txt']; -fname7 = [pname '\fcc_Bain.txt']; fname8 = [pname '\fcc_GT.txt']; fname9 = [pname '\fcc_GT_prime.txt']; -fname10 = [pname '\fcc_KS.txt']; fname11 = [pname '\fcc_NW.txt']; fname12 = [pname '\fcc_Pitsch.txt']; +lattice_types = {'BCC','FCC'}; +models = {'Bain','GT','GT_prime','KS','NW','Pitsch'}; +rotation = containers.Map; +rotation('BCC') = 'Passive Rotation'; +rotation('FCC') = 'Active Rotation'; -%% Import the Data - -% create an EBSD variable containing the data -ebsd1 = loadEBSD(fname1,CS_bcc,'interface','generic',... - 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Passive Rotation'); -ebsd2 = loadEBSD(fname2,CS_bcc,'interface','generic',... - 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Passive Rotation'); -ebsd3 = loadEBSD(fname3,CS_bcc,'interface','generic',... - 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Passive Rotation'); -ebsd4 = loadEBSD(fname4,CS_bcc,'interface','generic',... - 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Passive Rotation'); -ebsd5 = loadEBSD(fname5,CS_bcc,'interface','generic',... - 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Passive Rotation'); -ebsd6 = loadEBSD(fname6,CS_bcc,'interface','generic',... - 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Passive Rotation'); - -ebsd7 = loadEBSD(fname7,CS_fcc,'interface','generic',... - 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Active Rotation'); -ebsd8 = loadEBSD(fname8,CS_fcc,'interface','generic',... - 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Active Rotation'); -ebsd9 = loadEBSD(fname9,CS_fcc,'interface','generic',... - 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Active Rotation'); -ebsd10 = loadEBSD(fname10,CS_fcc,'interface','generic',... - 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Active Rotation'); -ebsd11 = loadEBSD(fname11,CS_fcc,'interface','generic',... - 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Active Rotation'); -ebsd12 = loadEBSD(fname12,CS_fcc,'interface','generic',... - 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', 'Active Rotation'); - -%% Plot Data 1stpart_bcc -h1 = [Miller(1,0,0,ebsd1.CS),Miller(1,1,0,ebsd1.CS),Miller(1,1,1,ebsd1.CS)]; % 3 pole figures -plotPDF(ebsd1.orientations,h1,'MarkerSize',5,'MarkerColor','r','MarkerEdgeColor','r','DisplayName','BCC-Bain') -hold on -plotPDF(ebsd2.orientations,h1,'MarkerSize',5,'MarkerColor','w','MarkerEdgeColor','b','DisplayName','BCC-GT') -plotPDF(ebsd3.orientations,h1,'MarkerSize',5,'MarkerColor','w','MarkerEdgeColor','g','DisplayName','BCC-GT_Prime') -legend('show','location','southoutside') -cd 'L:\f.gallardo\DAMASK\python\tests\reference\Rotation'; % has to be changed -orient('landscape') -print('-bestfit','1_BCC.pdf','-dpdf') - -%% Plot Data 2nd part_bcc -close -plotPDF(ebsd4.orientations,h1,'MarkerSize',5,'MarkerColor','r','MarkerEdgeColor','w','DisplayName','BCC-KS') -hold on -plotPDF(ebsd5.orientations,h1,'MarkerSize',5,'MarkerColor','w','MarkerEdgeColor','m','DisplayName','BCC-NW') -plotPDF(ebsd6.orientations,h1,'MarkerSize',5,'MarkerColor','y','MarkerEdgeColor','w','DisplayName','BCC-Pitsch') -legend('show','location','southoutside') -print('-bestfit','2_BCC.pdf','-dpdf') - -%% Plot Data 1stpart_fcc -close -h2 = [Miller(1,0,0,ebsd7.CS),Miller(1,1,0,ebsd7.CS),Miller(1,1,1,ebsd7.CS)]; % 3 pole figures -plotPDF(ebsd7.orientations,h2,'MarkerSize',5,'MarkerColor','r','MarkerEdgeColor','r','DisplayName','FCC-Bain') -hold on -plotPDF(ebsd8.orientations,h2,'MarkerSize',5,'MarkerColor','w','MarkerEdgeColor','b','DisplayName','FCC-GT') -plotPDF(ebsd9.orientations,h2,'MarkerSize',5,'MarkerColor','w','MarkerEdgeColor','g','DisplayName','FCC-GT_Prime') -legend('show','location','southoutside') -print('-bestfit','1_FCC.pdf','-dpdf') - -%% Plot Data 2nd part_bcc -close -plotPDF(ebsd10.orientations,h2,'MarkerSize',5,'MarkerColor','r','MarkerEdgeColor','w','DisplayName','FCC-KS') -hold on -plotPDF(ebsd11.orientations,h2,'MarkerSize',5,'MarkerColor','w','MarkerEdgeColor','m','DisplayName','FCC-NW') -plotPDF(ebsd12.orientations,h2,'MarkerSize',5,'MarkerColor','y','MarkerEdgeColor','w','DisplayName','FCC-Pitsch') -legend('show','location','southoutside') -print('-bestfit','2_FCC.pdf','-dpdf') -close - +for lattice = lattice_types + for p = 0:length(models)/3-1 + p + EBSD_data = {loadEBSD(strcat(lattice,'_',models{p*3+1},'.txt'),symmetry,'interface','generic',... + 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', rotation(char(lattice))), + loadEBSD(strcat(lattice,'_',models{p*3+2},'.txt'),symmetry,'interface','generic',... + 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', rotation(char(lattice))), + loadEBSD(strcat(lattice,'_',models{p*3+3},'.txt'),symmetry,'interface','generic',... + 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', rotation(char(lattice)))} + h = [Miller(1,0,0,symmetry{1}),Miller(1,1,0,symmetry{1}),Miller(1,1,1,symmetry{1})]; % 3 pole figures + plotPDF(EBSD_data{1}.orientations,h,'MarkerSize',5,'MarkerColor','r','DisplayName',models{p*3+1}) + hold on + plotPDF(EBSD_data{2}.orientations,h,'MarkerSize',5,'MarkerColor','b','DisplayName',models{p*3+2}) + plotPDF(EBSD_data{3}.orientations,h,'MarkerSize',5,'MarkerColor','g','DisplayName',models{p*3+3}) + legend('show','location','southoutside') + orient('landscape') + print('-bestfit',strcat(int2str(p+1),'_',char(lattice),'.pdf'),'-dpdf') + close + end +end \ No newline at end of file From fcbe4ee5a2950cbafd7e851db81d833b6647b82f Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 11 Dec 2019 18:08:51 +0100 Subject: [PATCH 071/223] [skip ci] polishing --- python/tests/reference/Rotation/PoleFigures_OR.m | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/python/tests/reference/Rotation/PoleFigures_OR.m b/python/tests/reference/Rotation/PoleFigures_OR.m index a17bdd831..74e1365b7 100644 --- a/python/tests/reference/Rotation/PoleFigures_OR.m +++ b/python/tests/reference/Rotation/PoleFigures_OR.m @@ -3,8 +3,6 @@ tmp = matlab.desktop.editor.getActive; cd(fileparts(tmp.Filename)); - -%% Specify Crystal symmetry = {crystalSymmetry('m-3m', [1 1 1], 'mineral', 'Iron', 'color', 'light blue')} % plotting convention @@ -21,14 +19,13 @@ rotation('FCC') = 'Active Rotation'; for lattice = lattice_types for p = 0:length(models)/3-1 - p EBSD_data = {loadEBSD(strcat(lattice,'_',models{p*3+1},'.txt'),symmetry,'interface','generic',... 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', rotation(char(lattice))), loadEBSD(strcat(lattice,'_',models{p*3+2},'.txt'),symmetry,'interface','generic',... 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', rotation(char(lattice))), loadEBSD(strcat(lattice,'_',models{p*3+3},'.txt'),symmetry,'interface','generic',... 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', rotation(char(lattice)))} - h = [Miller(1,0,0,symmetry{1}),Miller(1,1,0,symmetry{1}),Miller(1,1,1,symmetry{1})]; % 3 pole figures + h = [Miller(1,0,0,symmetry{1}),Miller(1,1,0,symmetry{1}),Miller(1,1,1,symmetry{1})]; % 3 pole figures plotPDF(EBSD_data{1}.orientations,h,'MarkerSize',5,'MarkerColor','r','DisplayName',models{p*3+1}) hold on plotPDF(EBSD_data{2}.orientations,h,'MarkerSize',5,'MarkerColor','b','DisplayName',models{p*3+2}) @@ -38,4 +35,4 @@ for lattice = lattice_types print('-bestfit',strcat(int2str(p+1),'_',char(lattice),'.pdf'),'-dpdf') close end -end \ No newline at end of file +end From a0a99c526b285965e2647047bcd29f413802fdf2 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 11 Dec 2019 19:24:29 +0100 Subject: [PATCH 072/223] some remaining IntOut format strings --- PRIVATE | 2 +- src/IO.f90 | 23 +------------------- src/grid/grid_mech_FEM.f90 | 3 +-- src/grid/grid_mech_spectral_basic.f90 | 3 +-- src/grid/grid_mech_spectral_polarisation.f90 | 3 +-- 5 files changed, 5 insertions(+), 29 deletions(-) diff --git a/PRIVATE b/PRIVATE index 283c7567f..f60209558 160000 --- a/PRIVATE +++ b/PRIVATE @@ -1 +1 @@ -Subproject commit 283c7567f1e4f6f1893cb5b5efdbbda1682f7533 +Subproject commit f602095584e1d8c4f96e5746d6a199bd3bc029c3 diff --git a/src/IO.f90 b/src/IO.f90 index 8973860c2..c121cc65e 100644 --- a/src/IO.f90 +++ b/src/IO.f90 @@ -32,8 +32,7 @@ module IO IO_intValue, & IO_lc, & IO_error, & - IO_warning, & - IO_intOut + IO_warning #if defined(Marc4DAMASK) || defined(Abaqus) public :: & IO_open_inputFile, & @@ -542,26 +541,6 @@ pure function IO_lc(string) end function IO_lc -!-------------------------------------------------------------------------------------------------- -!> @brief returns format string for integer values without leading zeros -!> @details deprecated, use '(i0)' format specifier -!-------------------------------------------------------------------------------------------------- -pure function IO_intOut(intToPrint) - - integer, intent(in) :: intToPrint - character(len=41) :: IO_intOut - integer :: N_digits - character(len=19) :: width ! maximum digits for 64 bit integer - character(len=20) :: min_width ! longer for negative values - - N_digits = 1 + int(log10(real(max(abs(intToPrint),1)))) - write(width, '(I19.19)') N_digits - write(min_width, '(I20.20)') N_digits + merge(1,0,intToPrint < 0) - IO_intOut = 'I'//trim(min_width)//'.'//trim(width) - -end function IO_intOut - - !-------------------------------------------------------------------------------------------------- !> @brief write error statements to standard out and terminate the Marc/spectral run with exit #9xxx !> in ABAQUS either time step is reduced or execution terminated diff --git a/src/grid/grid_mech_FEM.f90 b/src/grid/grid_mech_FEM.f90 index f6074fee9..a34d880f7 100644 --- a/src/grid/grid_mech_FEM.f90 +++ b/src/grid/grid_mech_FEM.f90 @@ -476,8 +476,7 @@ subroutine formResidual(da_local,x_local, & ! begin of new iteration newIteration: if (totalIter <= PETScIter) then totalIter = totalIter + 1 - write(6,'(1x,a,3(a,'//IO_intOut(itmax)//'))') & - trim(incInfo), ' @ Iteration ', itmin, '≤',totalIter+1, '≤', itmax + 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.)) diff --git a/src/grid/grid_mech_spectral_basic.f90 b/src/grid/grid_mech_spectral_basic.f90 index fb69427e3..f05f9bc93 100644 --- a/src/grid/grid_mech_spectral_basic.f90 +++ b/src/grid/grid_mech_spectral_basic.f90 @@ -440,8 +440,7 @@ subroutine formResidual(in, F, & ! begin of new iteration newIteration: if (totalIter <= PETScIter) then totalIter = totalIter + 1 - write(6,'(1x,a,3(a,'//IO_intOut(itmax)//'))') & - trim(incInfo), ' @ Iteration ', itmin, '≤',totalIter, '≤', itmax + 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.)) diff --git a/src/grid/grid_mech_spectral_polarisation.f90 b/src/grid/grid_mech_spectral_polarisation.f90 index ed2e0e1a9..33c3e4e72 100644 --- a/src/grid/grid_mech_spectral_polarisation.f90 +++ b/src/grid/grid_mech_spectral_polarisation.f90 @@ -509,8 +509,7 @@ subroutine formResidual(in, FandF_tau, & ! begin of new iteration newIteration: if (totalIter <= PETScIter) then totalIter = totalIter + 1 - write(6,'(1x,a,3(a,'//IO_intOut(itmax)//'))') & - trim(incInfo), ' @ Iteration ', itmin, '≤',totalIter, '≤', itmax + 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.)) From 1d7010778e446d2d95aa441560da31a0e48eedc9 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 11 Dec 2019 19:43:23 +0100 Subject: [PATCH 073/223] [skip ci] silence annoying warnings --- python/damask/dadf5.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/damask/dadf5.py b/python/damask/dadf5.py index beced188d..278a4d89d 100644 --- a/python/damask/dadf5.py +++ b/python/damask/dadf5.py @@ -360,7 +360,7 @@ class DADF5(): f[k] path.append(k) except KeyError as e: - print('unable to locate geometry dataset: {}'.format(str(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): @@ -369,7 +369,7 @@ class DADF5(): f[k] path.append(k) except KeyError as e: - print('unable to locate {} dataset: {}'.format(o,str(e))) + pass return path From 674d800ceafff78f10d361034128d9e0c9524967 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 11 Dec 2019 20:52:45 +0100 Subject: [PATCH 074/223] fix for test needed --- PRIVATE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PRIVATE b/PRIVATE index f60209558..a4a216604 160000 --- a/PRIVATE +++ b/PRIVATE @@ -1 +1 @@ -Subproject commit f602095584e1d8c4f96e5746d6a199bd3bc029c3 +Subproject commit a4a216604ca07f6391c209fa75b593c8e8a887e5 From 4276ef4764e07d01094896a51ce6e5ba7b382c06 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 12 Dec 2019 23:11:26 +0100 Subject: [PATCH 075/223] only stack size matters (for Intel Compiler) --- env/DAMASK.csh | 1 - env/DAMASK.sh | 3 --- env/DAMASK.zsh | 3 --- 3 files changed, 7 deletions(-) diff --git a/env/DAMASK.csh b/env/DAMASK.csh index d223d885a..d3b4474b2 100644 --- a/env/DAMASK.csh +++ b/env/DAMASK.csh @@ -26,7 +26,6 @@ endif # more info https://jblevins.org/log/segfault # https://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap # http://superuser.com/questions/220059/what-parameters-has-ulimit -limit datasize unlimited # maximum heap size (kB) limit stacksize unlimited # maximum stack size (kB) endif if ( `limit | grep memoryuse` != "" ) then diff --git a/env/DAMASK.sh b/env/DAMASK.sh index 1b4bea86a..a6d7b2667 100644 --- a/env/DAMASK.sh +++ b/env/DAMASK.sh @@ -48,10 +48,7 @@ PROCESSING=$(type -p postResults || true 2>/dev/null) # more info https://jblevins.org/log/segfault # https://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap # http://superuser.com/questions/220059/what-parameters-has-ulimit -ulimit -d unlimited 2>/dev/null # maximum heap size (kB) ulimit -s unlimited 2>/dev/null # maximum stack size (kB) -ulimit -v unlimited 2>/dev/null # maximum virtual memory size -ulimit -m unlimited 2>/dev/null # maximum physical memory size # disable output in case of scp if [ ! -z "$PS1" ]; then diff --git a/env/DAMASK.zsh b/env/DAMASK.zsh index 5449007f9..42021191f 100644 --- a/env/DAMASK.zsh +++ b/env/DAMASK.zsh @@ -40,10 +40,7 @@ PROCESSING=$(which postResults || true 2>/dev/null) # more info https://jblevins.org/log/segfault # https://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap # http://superuser.com/questions/220059/what-parameters-has-ulimit -ulimit -d unlimited 2>/dev/null # maximum heap size (kB) ulimit -s unlimited 2>/dev/null # maximum stack size (kB) -ulimit -v unlimited 2>/dev/null # maximum virtual memory size -ulimit -m unlimited 2>/dev/null # maximum physical memory size # disable output in case of scp if [ ! -z "$PS1" ]; then From ee56b82c8a97de781d01a428c27efbe6cc421b7c Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 12 Dec 2019 23:22:37 +0100 Subject: [PATCH 076/223] avoid spaces --- src/grid/DAMASK_grid.f90 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/grid/DAMASK_grid.f90 b/src/grid/DAMASK_grid.f90 index 7fe8d82c4..60caf8947 100644 --- a/src/grid/DAMASK_grid.f90 +++ b/src/grid/DAMASK_grid.f90 @@ -257,7 +257,7 @@ program DAMASK_spectral reportAndCheck: if (worldrank == 0) then write (loadcase_string, '(i6)' ) currentLoadCase - write(6,'(/,1x,a,i6)') 'load case: ', currentLoadCase + write(6,'(/,1x,a,i0)') 'load case: ', currentLoadCase if (.not. newLoadCase%followFormerTrajectory) write(6,'(2x,a)') 'drop guessing along trajectory' if (newLoadCase%deformation%myType == 'l') then do j = 1, 3 @@ -300,14 +300,14 @@ program DAMASK_spectral write(6,'(2x,a,/,3(3(3x,f12.7,1x)/))',advance='no') 'rotation of loadframe:',& transpose(newLoadCase%rot%asMatrix()) if (newLoadCase%time < 0.0_pReal) errorID = 834 ! negative time increment - write(6,'(2x,a,f12.6)') 'time: ', newLoadCase%time + write(6,'(2x,a,f0.3)') 'time: ', newLoadCase%time if (newLoadCase%incs < 1) errorID = 835 ! non-positive incs count - write(6,'(2x,a,i5)') 'increments: ', newLoadCase%incs + write(6,'(2x,a,i0)') 'increments: ', newLoadCase%incs if (newLoadCase%outputfrequency < 1) errorID = 836 ! non-positive result frequency - write(6,'(2x,a,i5)') 'output frequency: ', newLoadCase%outputfrequency + write(6,'(2x,a,i0)') 'output frequency: ', newLoadCase%outputfrequency if (newLoadCase%restartfrequency < 1) errorID = 839 ! non-positive restart frequency if (newLoadCase%restartfrequency < huge(0)) & - write(6,'(2x,a,i5)') 'restart frequency: ', newLoadCase%restartfrequency + write(6,'(2x,a,i0)') 'restart frequency: ', newLoadCase%restartfrequency if (errorID > 0) call IO_error(error_ID = errorID, ext_msg = loadcase_string) ! exit with error message endif reportAndCheck loadCases = [loadCases,newLoadCase] ! load case is ok, append it From 63fc1253982e36a6e92688082f8493d9ff7c2c8f Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 13 Dec 2019 09:08:48 +0100 Subject: [PATCH 077/223] include origin do DADF5 requires updat of file version --- python/damask/dadf5.py | 7 +++++-- src/CPFEM2.f90 | 2 +- src/mesh_grid.f90 | 40 ++++++++++++++++++++++++++++++---------- src/results.f90 | 2 +- 4 files changed, 37 insertions(+), 14 deletions(-) diff --git a/python/damask/dadf5.py b/python/damask/dadf5.py index beced188d..959d222df 100644 --- a/python/damask/dadf5.py +++ b/python/damask/dadf5.py @@ -37,7 +37,7 @@ class DADF5(): 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 <= 4: + if self.version_major != 0 or not 2 <= self.version_minor <= 5: raise TypeError('Unsupported DADF5 version {} '.format(f.attrs['DADF5-version'])) self.structured = 'grid' in f['geometry'].attrs.keys() @@ -45,6 +45,9 @@ class DADF5(): 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)} @@ -830,7 +833,7 @@ class DADF5(): N_not_calculated = len(todo) while N_not_calculated > 0: result = results.get() - with h5py.File(self.fname,'a') as f: # write to file + 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() diff --git a/src/CPFEM2.f90 b/src/CPFEM2.f90 index 9edb61d33..6406e9b30 100644 --- a/src/CPFEM2.f90 +++ b/src/CPFEM2.f90 @@ -14,10 +14,10 @@ module CPFEM2 use material use lattice use IO - use HDF5 use DAMASK_interface use results use discretization + use HDF5 use HDF5_utilities use homogenization use constitutive diff --git a/src/mesh_grid.f90 b/src/mesh_grid.f90 index 2b337f047..d10ffef8a 100644 --- a/src/mesh_grid.f90 +++ b/src/mesh_grid.f90 @@ -27,9 +27,8 @@ module mesh_grid integer, public, protected :: & grid3, & !< (local) grid in 3rd direction grid3Offset !< (local) grid offset in 3rd direction - real(pReal), dimension(3), public, protected :: & - geomSize + geomSize !< (global) physical size real(pReal), public, protected :: & size3, & !< (local) size in 3rd direction size3offset !< (local) size offset in 3rd direction @@ -49,7 +48,8 @@ subroutine mesh_init(ip,el) include 'fftw3-mpi.f03' real(pReal), dimension(3) :: & - mySize !< domain size of this process + mySize, & !< domain size of this process + origin !< (global) distance to origin integer, dimension(3) :: & myGrid !< domain grid of this process @@ -61,9 +61,9 @@ subroutine mesh_init(ip,el) integer(C_INTPTR_T) :: & devNull, z, z_offset - write(6,'(/,a)') ' <<<+- mesh_grid init -+>>>' + write(6,'(/,a)') ' <<<+- mesh_grid init -+>>>'; flush(6) - call readGeom(grid,geomSize,microstructureAt,homogenizationAt) + call readGeom(grid,geomSize,origin,microstructureAt,homogenizationAt) !-------------------------------------------------------------------------------------------------- ! grid solver specific quantities @@ -104,8 +104,9 @@ subroutine mesh_init(ip,el) ! store geometry information for post processing call results_openJobFile call results_closeGroup(results_addGroup('geometry')) - call results_addAttribute('grid',grid,'geometry') - call results_addAttribute('size',geomSize,'geometry') + call results_addAttribute('grid', grid, 'geometry') + call results_addAttribute('size', geomSize,'geometry') + call results_addAttribute('origin',origin, 'geometry') call results_closeJobFile !-------------------------------------------------------------------------------------------------- @@ -129,10 +130,13 @@ end subroutine mesh_init !> @details important variables have an implicit "save" attribute. Therefore, this function is ! supposed to be called only once! !-------------------------------------------------------------------------------------------------- -subroutine readGeom(grid,geomSize,microstructure,homogenization) +subroutine readGeom(grid,geomSize,origin,microstructure,homogenization) - integer, dimension(3), intent(out) :: grid ! grid (for all processes!) - real(pReal), dimension(3), intent(out) :: geomSize ! size (for all processes!) + integer, dimension(3), intent(out) :: & + grid ! grid (for all processes!) + real(pReal), dimension(3), intent(out) :: & + geomSize, & ! size (for all processes!) + origin ! origin (for all processes!) integer, dimension(:), intent(out), allocatable :: & microstructure, & homogenization @@ -181,6 +185,7 @@ subroutine readGeom(grid,geomSize,microstructure,homogenization) !-------------------------------------------------------------------------------------------------- ! read and interprete header + origin = 0.0_pReal l = 0 do while (l < headerLength .and. startPos < len(rawData)) endPos = startPos + index(rawData(startPos:),new_line('')) - 1 @@ -221,8 +226,23 @@ subroutine readGeom(grid,geomSize,microstructure,homogenization) enddo endif + case ('origin') + if (chunkPos(1) > 6) then + do j = 2,6,2 + select case (IO_lc(IO_stringValue(line,chunkPos,j))) + case('x') + origin(1) = IO_floatValue(line,chunkPos,j+1) + case('y') + origin(2) = IO_floatValue(line,chunkPos,j+1) + case('z') + origin(3) = IO_floatValue(line,chunkPos,j+1) + end select + enddo + endif + case ('homogenization') if (chunkPos(1) > 1) h = IO_intValue(line,chunkPos,2) + end select enddo diff --git a/src/results.f90 b/src/results.f90 index 8fb7e134d..a7037a454 100644 --- a/src/results.f90 +++ b/src/results.f90 @@ -70,7 +70,7 @@ subroutine results_init resultsFile = HDF5_openFile(trim(getSolverJobName())//'.hdf5','w',.true.) call HDF5_addAttribute(resultsFile,'DADF5_version_major',0) - call HDF5_addAttribute(resultsFile,'DADF5_version_minor',4) + call HDF5_addAttribute(resultsFile,'DADF5_version_minor',5) call HDF5_addAttribute(resultsFile,'DAMASK_version',DAMASKVERSION) call get_command(commandLine) call HDF5_addAttribute(resultsFile,'call',trim(commandLine)) From 186605610d69e13fd735f3646fde5764cb7a79bd Mon Sep 17 00:00:00 2001 From: Vitesh Shah Date: Fri, 13 Dec 2019 09:53:47 +0100 Subject: [PATCH 078/223] No phase name for generic datasets --- processing/post/DADF5_vtk_cells.py | 5 ++++- processing/post/DADF5_vtk_points.py | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/processing/post/DADF5_vtk_cells.py b/processing/post/DADF5_vtk_cells.py index b8875f4e9..9e8585773 100755 --- a/processing/post/DADF5_vtk_cells.py +++ b/processing/post/DADF5_vtk_cells.py @@ -2,6 +2,7 @@ import os import argparse +import re import h5py import numpy as np @@ -89,10 +90,12 @@ for filename in options.filenames: x = results.get_dataset_location(label) if len(x) == 0: continue + ph_name = re.compile(r'(\/[1-9])_([A-Z][a-z]*)_(([a-z]*)|([A-Z]*))') #looking for phase name in dataset name array = results.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]) + dset_name = '1_' + re.sub(ph_name,r'',x[0].split('/',1)[1]) #removing phase name from generic dataset + vtk_data[-1].SetName(dset_name) grid.GetCellData().AddArray(vtk_data[-1]) results.set_visible('constituents', False) diff --git a/processing/post/DADF5_vtk_points.py b/processing/post/DADF5_vtk_points.py index 9265cc3a0..908474336 100755 --- a/processing/post/DADF5_vtk_points.py +++ b/processing/post/DADF5_vtk_points.py @@ -2,6 +2,7 @@ import os import argparse +import re import numpy as np import vtk @@ -76,10 +77,12 @@ for filename in options.filenames: x = results.get_dataset_location(label) if len(x) == 0: continue + ph_name = re.compile(r'(\/[1-9])_([A-Z][a-z]*)_(([a-z]*)|([A-Z]*))') #looking for phase name in dataset name array = results.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]) + dset_name = '1_' + re.sub(ph_name,r'',x[0].split('/',1)[1]) #removing phase name from generic dataset + vtk_data[-1].SetName(dset_name) Polydata.GetCellData().AddArray(vtk_data[-1]) results.set_visible('constituents', False) From b14c15fd9ee6720d79677ed30d4558e3928269a5 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 13 Dec 2019 12:15:45 +0100 Subject: [PATCH 079/223] directly output DADF5 to vtk from python --- python/damask/dadf5.py | 120 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/python/damask/dadf5.py b/python/damask/dadf5.py index beced188d..1142d02b4 100644 --- a/python/damask/dadf5.py +++ b/python/damask/dadf5.py @@ -1,7 +1,10 @@ from queue import Queue import re import glob +import os +import vtk +from vtk.util import numpy_support import h5py import numpy as np @@ -841,3 +844,120 @@ class DADF5(): N_added +=1 pool.wait_completion() + + + def to_vtk(self,labels): + """ + Export to vtk cell data. + + Parameters + ---------- + labels : list of str + Labels of the datasets to be exported. + + """ + 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) + + grid = vtk.vtkRectilinearGrid() + grid.SetDimensions(*(self.grid+1)) + grid.SetXCoordinates(coordArray[0]) + grid.SetYCoordinates(coordArray[1]) + grid.SetZCoordinates(coordArray[2]) + + else: + + nodes = vtk.vtkPoints() + with h5py.File(self.fname) as f: + nodes.SetData(numpy_support.numpy_to_vtk(f['/geometry/x_n'][()],deep=True)) + + grid = vtk.vtkUnstructuredGrid() + grid.SetPoints(nodes) + grid.Allocate(f['/geometry/T_c'].shape[0]) + for i in f['/geometry/T_c']: + grid.InsertNextCell(vtk.VTK_HEXAHEDRON,8,i-1) # not for all elements! + + 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: + 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! + grid.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'(\/[1-9])_([A-Z][a-z]*)_(([a-z]*)|([A-Z]*))') #looking for phase name in dataset name + dset_name = '1_' + re.sub(ph_name,r'',x[0].split('/',1)[1]) #removing phase name from generic dataset + vtk_data[-1].SetName(dset_name) + grid.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: + 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_? + grid.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]) + grid.GetCellData().AddArray(vtk_data[-1]) + self.set_visible('constituents',constituents_backup) + + 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') + grid.GetPointData().AddArray(vtk_data[-1]) + + 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(grid) + + writer.Write() From 0c30f6b1dfc52cc399a08bf479e239f29abf36ff Mon Sep 17 00:00:00 2001 From: "f.basile" Date: Fri, 13 Dec 2019 12:52:33 +0100 Subject: [PATCH 080/223] Polishing Mtex script for plotting pole figures. --- python/tests/reference/Rotation/1_BCC.pdf | Bin 44551 -> 43263 bytes python/tests/reference/Rotation/1_FCC.pdf | Bin 44391 -> 43263 bytes python/tests/reference/Rotation/2_BCC.pdf | Bin 43118 -> 43017 bytes python/tests/reference/Rotation/2_FCC.pdf | Bin 43107 -> 43017 bytes .../tests/reference/Rotation/PoleFigures_OR.m | 10 +++++----- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/python/tests/reference/Rotation/1_BCC.pdf b/python/tests/reference/Rotation/1_BCC.pdf index fe22f6a2e36d180c423a1902b1a7409f866ff324..599b892105907e96d19f52b7d927dafda0500d7c 100644 GIT binary patch literal 43263 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 AKL7v# literal 44391 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 literal 43107 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%0ALAhtr$ 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 BBRK#7 diff --git a/python/tests/reference/Rotation/PoleFigures_OR.m b/python/tests/reference/Rotation/PoleFigures_OR.m index 74e1365b7..ede2a0e0c 100644 --- a/python/tests/reference/Rotation/PoleFigures_OR.m +++ b/python/tests/reference/Rotation/PoleFigures_OR.m @@ -19,7 +19,7 @@ rotation('FCC') = 'Active Rotation'; for lattice = lattice_types for p = 0:length(models)/3-1 - EBSD_data = {loadEBSD(strcat(lattice,'_',models{p*3+1},'.txt'),symmetry,'interface','generic',... + EBSD_data = {loadEBSD(strcat (lattice,'_',models{p*3+1},'.txt'),symmetry,'interface','generic',... 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', rotation(char(lattice))), loadEBSD(strcat(lattice,'_',models{p*3+2},'.txt'),symmetry,'interface','generic',... 'ColumnNames', { 'phi1' 'Phi' 'phi2' 'x' 'y'}, 'Bunge', rotation(char(lattice))), @@ -28,11 +28,11 @@ for lattice = lattice_types h = [Miller(1,0,0,symmetry{1}),Miller(1,1,0,symmetry{1}),Miller(1,1,1,symmetry{1})]; % 3 pole figures plotPDF(EBSD_data{1}.orientations,h,'MarkerSize',5,'MarkerColor','r','DisplayName',models{p*3+1}) hold on - plotPDF(EBSD_data{2}.orientations,h,'MarkerSize',5,'MarkerColor','b','DisplayName',models{p*3+2}) - plotPDF(EBSD_data{3}.orientations,h,'MarkerSize',5,'MarkerColor','g','DisplayName',models{p*3+3}) - legend('show','location','southoutside') + plotPDF(EBSD_data{2}.orientations,h,'MarkerSize',4,'MarkerColor','b','DisplayName',models{p*3+2}) + plotPDF(EBSD_data{3}.orientations,h,'MarkerSize',3,'MarkerColor','g','DisplayName',models{p*3+3}) + legend('show','location','southoutside','Interpreter', 'none') orient('landscape') print('-bestfit',strcat(int2str(p+1),'_',char(lattice),'.pdf'),'-dpdf') close end -end +end \ No newline at end of file From b662ca0acf72d4a6eac8c8587ff518d1edbe0704 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 13 Dec 2019 13:23:13 +0100 Subject: [PATCH 081/223] do not modify pdf files --- .gitattributes | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitattributes b/.gitattributes index 8d1f26a78..7a5c5bde5 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,8 +3,8 @@ # always use LF, even if the files are edited on windows, they need to be compiled/used on unix * text eol=lf -# Denote all files that are truly binary and should not be modified. +# Denote all files that are binary and should not be modified. *.png binary *.jpg binary -*.cae binary *.hdf5 binary +*.pdf binary From 57a69ad4c93ce54740097bd421e574e806d442c8 Mon Sep 17 00:00:00 2001 From: "f.basile" Date: Fri, 13 Dec 2019 13:32:16 +0100 Subject: [PATCH 082/223] Reupload PDF pole figures. --- python/tests/reference/Rotation/1_BCC.pdf | Bin 43263 -> 43264 bytes python/tests/reference/Rotation/1_FCC.pdf | Bin 43263 -> 43264 bytes python/tests/reference/Rotation/2_BCC.pdf | Bin 43017 -> 43017 bytes python/tests/reference/Rotation/2_FCC.pdf | Bin 43017 -> 43017 bytes .../tests/reference/Rotation/PoleFigures_OR.m | 2 +- 5 files changed, 1 insertion(+), 1 deletion(-) diff --git a/python/tests/reference/Rotation/1_BCC.pdf b/python/tests/reference/Rotation/1_BCC.pdf index 599b892105907e96d19f52b7d927dafda0500d7c..445f1d250fea234be7680d874b149e52bed42851 100644 GIT binary patch delta 97 zcmex=k*VPl(}YS^V`BqjlZ|y8{EWPt+xYosIvct;S-Kgx8oC;oTNoIb7`d4lyBQc5 X7?~TInmL)78`>$@5K=Pv#tL-+B;Xlw delta 95 zcmZp;#Pt6n(}YS^BNJmIIU1W=nwuD!n3qFW+rYHj&6=Ft|rE& Q29|aTHiVQ+ezaU20EpWdssI20 delta 90 zcmeCYz|?txX+kBdk%_U9*~U7><<3Tq#^#nr&gO~2CmK)&d!$3j!v!yrp~6OE^bB^rY6Q_KtVGD PCp!fjLP{n-TCNTNkZl;Z delta 90 zcmeCYz|?txX+kBdk%_Uf(Z)K(<<2ISrk0MT2Bwxy#x72dhDMgAj*f Date: Fri, 13 Dec 2019 14:20:18 +0100 Subject: [PATCH 083/223] Point based vtk file in DADF5 class --- python/damask/dadf5.py | 115 +++++++++++++++++++++++++++-------------- 1 file changed, 75 insertions(+), 40 deletions(-) diff --git a/python/damask/dadf5.py b/python/damask/dadf5.py index 1142d02b4..1f4af8de3 100644 --- a/python/damask/dadf5.py +++ b/python/damask/dadf5.py @@ -846,41 +846,58 @@ class DADF5(): pool.wait_completion() - def to_vtk(self,labels): + def to_vtk(self,labels,mode='Cell'): """ - Export to vtk cell data. + Export to vtk cell/point data. Parameters ---------- labels : list of str Labels of the datasets to be exported. + mode : str + Export in cell format or point format. + Default value is 'Cell' """ - if self.structured: + if mode=='Cell': - 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) - - grid = vtk.vtkRectilinearGrid() - grid.SetDimensions(*(self.grid+1)) - grid.SetXCoordinates(coordArray[0]) - grid.SetYCoordinates(coordArray[1]) - grid.SetZCoordinates(coordArray[2]) - - else: - - nodes = vtk.vtkPoints() - with h5py.File(self.fname) as f: - nodes.SetData(numpy_support.numpy_to_vtk(f['/geometry/x_n'][()],deep=True)) + 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) + + grid = vtk.vtkRectilinearGrid() + grid.SetDimensions(*(self.grid+1)) + grid.SetXCoordinates(coordArray[0]) + grid.SetYCoordinates(coordArray[1]) + grid.SetZCoordinates(coordArray[2]) + + else: - grid = vtk.vtkUnstructuredGrid() - grid.SetPoints(nodes) - grid.Allocate(f['/geometry/T_c'].shape[0]) - for i in f['/geometry/T_c']: - grid.InsertNextCell(vtk.VTK_HEXAHEDRON,8,i-1) # not for all elements! - + nodes = vtk.vtkPoints() + with h5py.File(self.fname) as f: + nodes.SetData(numpy_support.numpy_to_vtk(f['/geometry/x_n'][()],deep=True)) + + grid = vtk.vtkUnstructuredGrid() + grid.SetPoints(nodes) + grid.Allocate(f['/geometry/T_c'].shape[0]) + for i in f['/geometry/T_c']: + grid.InsertNextCell(vtk.VTK_HEXAHEDRON,8,i-1) # not for all elements! + else: + Points = vtk.vtkPoints() + Vertices = vtk.vtkCellArray() + for c in self.cell_coordinates(): + pointID = Points.InsertNextPoint(c) + Vertices.InsertNextCell(1) + Vertices.InsertCellPoint(pointID) + + Polydata = vtk.vtkPolyData() + Polydata.SetPoints(Points) + Polydata.SetVerts(Vertices) + Polydata.Modified() + N_digits = int(np.floor(np.log10(int(self.increments[-1][3:]))))+1 for i,inc in enumerate(self.iter_visible('increments')): @@ -900,7 +917,10 @@ class DADF5(): 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! - grid.GetCellData().AddArray(vtk_data[-1]) + if mode=='Cell': + grid.GetCellData().AddArray(vtk_data[-1]) + else: + Polydata.GetCellData().AddArray(vtk_data[-1]) else: x = self.get_dataset_location(label) if len(x) == 0: @@ -912,9 +932,12 @@ class DADF5(): ph_name = re.compile(r'(\/[1-9])_([A-Z][a-z]*)_(([a-z]*)|([A-Z]*))') #looking for phase name in dataset name dset_name = '1_' + re.sub(ph_name,r'',x[0].split('/',1)[1]) #removing phase name from generic dataset vtk_data[-1].SetName(dset_name) - grid.GetCellData().AddArray(vtk_data[-1]) + if mode=='Cell': + grid.GetCellData().AddArray(vtk_data[-1]) + else: + Polydata.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: @@ -929,7 +952,10 @@ class DADF5(): 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_? - grid.GetCellData().AddArray(vtk_data[-1]) + if mode=='Cell': + grid.GetCellData().AddArray(vtk_data[-1]) + else: + Polydata.GetCellData().AddArray(vtk_data[-1]) else: x = self.get_dataset_location(label) if len(x) == 0: @@ -939,17 +965,23 @@ class DADF5(): 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]) - grid.GetCellData().AddArray(vtk_data[-1]) + if mode=='Cell': + grid.GetCellData().AddArray(vtk_data[-1]) + else: + Polydata.GetCellData().AddArray(vtk_data[-1]) self.set_visible('constituents',constituents_backup) - - writer = vtk.vtkXMLRectilinearGridWriter() if self.structured else \ - vtk.vtkXMLUnstructuredGridWriter() + + 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') + grid.GetPointData().AddArray(vtk_data[-1]) + else: + writer = vtk.vtkXMLPolyDataWriter() - 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') - grid.GetPointData().AddArray(vtk_data[-1]) file_out = '{}_inc{}.{}'.format(os.path.splitext(os.path.basename(self.fname))[0], inc[3:].zfill(N_digits), @@ -958,6 +990,9 @@ class DADF5(): writer.SetCompressorTypeToZLib() writer.SetDataModeToBinary() writer.SetFileName(file_out) - writer.SetInputData(grid) - + if mode=='Cell': + writer.SetInputData(grid) + else: + writer.SetInputData(Polydata) + writer.Write() From e5448fc381b8524cfeaf7ea89e8a6423d6b62f80 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 13 Dec 2019 14:36:52 +0100 Subject: [PATCH 084/223] avoid code duplication --- python/damask/dadf5.py | 63 +++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 38 deletions(-) diff --git a/python/damask/dadf5.py b/python/damask/dadf5.py index 1f4af8de3..0f32b4b5e 100644 --- a/python/damask/dadf5.py +++ b/python/damask/dadf5.py @@ -854,9 +854,9 @@ class DADF5(): ---------- labels : list of str Labels of the datasets to be exported. - mode : str + mode : str, either 'Cell' or 'Point' Export in cell format or point format. - Default value is 'Cell' + Default value is 'Cell'. """ if mode=='Cell': @@ -868,11 +868,11 @@ class DADF5(): for c in np.linspace(0,self.size[dim],1+self.grid[dim]): coordArray[dim].InsertNextValue(c) - grid = vtk.vtkRectilinearGrid() - grid.SetDimensions(*(self.grid+1)) - grid.SetXCoordinates(coordArray[0]) - grid.SetYCoordinates(coordArray[1]) - grid.SetZCoordinates(coordArray[2]) + 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: @@ -880,12 +880,12 @@ class DADF5(): with h5py.File(self.fname) as f: nodes.SetData(numpy_support.numpy_to_vtk(f['/geometry/x_n'][()],deep=True)) - grid = vtk.vtkUnstructuredGrid() - grid.SetPoints(nodes) - grid.Allocate(f['/geometry/T_c'].shape[0]) + vtk_geom = vtk.vtkUnstructuredGrid() + vtk_geom.SetPoints(nodes) + vtk_geom.Allocate(f['/geometry/T_c'].shape[0]) for i in f['/geometry/T_c']: - grid.InsertNextCell(vtk.VTK_HEXAHEDRON,8,i-1) # not for all elements! - else: + vtk_geom.InsertNextCell(vtk.VTK_HEXAHEDRON,8,i-1) # not for all elements! + elif mode == 'Point': Points = vtk.vtkPoints() Vertices = vtk.vtkCellArray() for c in self.cell_coordinates(): @@ -893,10 +893,10 @@ class DADF5(): Vertices.InsertNextCell(1) Vertices.InsertCellPoint(pointID) - Polydata = vtk.vtkPolyData() - Polydata.SetPoints(Points) - Polydata.SetVerts(Vertices) - Polydata.Modified() + 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 @@ -917,10 +917,8 @@ class DADF5(): 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! - if mode=='Cell': - grid.GetCellData().AddArray(vtk_data[-1]) - else: - Polydata.GetCellData().AddArray(vtk_data[-1]) + vtk_geom.GetCellData().AddArray(vtk_data[-1]) + else: x = self.get_dataset_location(label) if len(x) == 0: @@ -932,10 +930,8 @@ class DADF5(): ph_name = re.compile(r'(\/[1-9])_([A-Z][a-z]*)_(([a-z]*)|([A-Z]*))') #looking for phase name in dataset name dset_name = '1_' + re.sub(ph_name,r'',x[0].split('/',1)[1]) #removing phase name from generic dataset vtk_data[-1].SetName(dset_name) - if mode=='Cell': - grid.GetCellData().AddArray(vtk_data[-1]) - else: - Polydata.GetCellData().AddArray(vtk_data[-1]) + vtk_geom.GetCellData().AddArray(vtk_data[-1]) + self.set_visible('materialpoints',materialpoints_backup) constituents_backup = self.visible['constituents'].copy() @@ -952,10 +948,7 @@ class DADF5(): 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_? - if mode=='Cell': - grid.GetCellData().AddArray(vtk_data[-1]) - else: - Polydata.GetCellData().AddArray(vtk_data[-1]) + vtk_geom.GetCellData().AddArray(vtk_data[-1]) else: x = self.get_dataset_location(label) if len(x) == 0: @@ -965,10 +958,7 @@ class DADF5(): 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]) - if mode=='Cell': - grid.GetCellData().AddArray(vtk_data[-1]) - else: - Polydata.GetCellData().AddArray(vtk_data[-1]) + vtk_geom.GetCellData().AddArray(vtk_data[-1]) self.set_visible('constituents',constituents_backup) if mode=='Cell': @@ -978,8 +968,8 @@ class DADF5(): 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') - grid.GetPointData().AddArray(vtk_data[-1]) - else: + vtk_geom.GetPointData().AddArray(vtk_data[-1]) + elif mode == 'Point': writer = vtk.vtkXMLPolyDataWriter() @@ -990,9 +980,6 @@ class DADF5(): writer.SetCompressorTypeToZLib() writer.SetDataModeToBinary() writer.SetFileName(file_out) - if mode=='Cell': - writer.SetInputData(grid) - else: - writer.SetInputData(Polydata) + writer.SetInputData(vtk_geom) writer.Write() From 5b376712ef80091a618b384cf723a8b02a927e8b Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 13 Dec 2019 14:39:10 +0100 Subject: [PATCH 085/223] bugfix: wrong coordinates --- python/damask/dadf5.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/damask/dadf5.py b/python/damask/dadf5.py index 0f32b4b5e..367d15f36 100644 --- a/python/damask/dadf5.py +++ b/python/damask/dadf5.py @@ -436,7 +436,7 @@ class DADF5(): 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],y[:,:,:,None]),axis = 3).reshape([np.product(self.grid),3]) + 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'][()] From 2fb5ac652b0d9fa5d319a78c773bc052341be4d5 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 14 Dec 2019 14:02:16 +0100 Subject: [PATCH 086/223] use new keyword keywords should follow the symbols in formulas, not their description: "T" for temperature, not "temperature" "F" for deformation gradient, not "defgrad" --- PRIVATE | 2 +- examples/ConfigFiles/Homogenization_Thermal_Conduction.config | 2 +- examples/SpectralMethod/EshelbyInclusion/material.config | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PRIVATE b/PRIVATE index a4a216604..62842dec1 160000 --- a/PRIVATE +++ b/PRIVATE @@ -1 +1 @@ -Subproject commit a4a216604ca07f6391c209fa75b593c8e8a887e5 +Subproject commit 62842dec152d30213cc586852b0825ad264fb56b diff --git a/examples/ConfigFiles/Homogenization_Thermal_Conduction.config b/examples/ConfigFiles/Homogenization_Thermal_Conduction.config index 48ad9ddc6..36fc7ea6e 100644 --- a/examples/ConfigFiles/Homogenization_Thermal_Conduction.config +++ b/examples/ConfigFiles/Homogenization_Thermal_Conduction.config @@ -1,3 +1,3 @@ thermal conduction -initialT 300.0 +t0 270.0 (output) temperature diff --git a/examples/SpectralMethod/EshelbyInclusion/material.config b/examples/SpectralMethod/EshelbyInclusion/material.config index e002584b0..d1ea80964 100644 --- a/examples/SpectralMethod/EshelbyInclusion/material.config +++ b/examples/SpectralMethod/EshelbyInclusion/material.config @@ -6,7 +6,7 @@ mech none # isostrain 1 grain thermal adiabatic # thermal strain (stress) induced mass transport -initialT 300.0 +t0 330.0 (output) temperature #-------------------# From 898352e2220b4edcea864cc40a34095ad821d8f9 Mon Sep 17 00:00:00 2001 From: Test User Date: Sat, 14 Dec 2019 16:57:23 +0100 Subject: [PATCH 087/223] [skip ci] updated version information after successful test of v2.0.3-1255-ga6da8fdd --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 1439ed27c..dd4016408 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.0.3-1237-g5a2053cd +v2.0.3-1255-ga6da8fdd From 4469f2f8f0712202d8175a6e1ea3ceb3b05073e4 Mon Sep 17 00:00:00 2001 From: Vitesh Shah Date: Wed, 18 Dec 2019 10:14:19 +0100 Subject: [PATCH 088/223] More general regex --- processing/post/DADF5_vtk_cells.py | 2 +- processing/post/DADF5_vtk_points.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/processing/post/DADF5_vtk_cells.py b/processing/post/DADF5_vtk_cells.py index 9e8585773..b3afe939d 100755 --- a/processing/post/DADF5_vtk_cells.py +++ b/processing/post/DADF5_vtk_cells.py @@ -90,7 +90,7 @@ for filename in options.filenames: x = results.get_dataset_location(label) if len(x) == 0: continue - ph_name = re.compile(r'(\/[1-9])_([A-Z][a-z]*)_(([a-z]*)|([A-Z]*))') #looking for phase name in dataset name + ph_name = re.compile(r'(?<=(constituent\/))(.*?)(?=(generic))') #looking for phase name in dataset name array = results.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)) diff --git a/processing/post/DADF5_vtk_points.py b/processing/post/DADF5_vtk_points.py index 908474336..925a73a5c 100755 --- a/processing/post/DADF5_vtk_points.py +++ b/processing/post/DADF5_vtk_points.py @@ -77,7 +77,7 @@ for filename in options.filenames: x = results.get_dataset_location(label) if len(x) == 0: continue - ph_name = re.compile(r'(\/[1-9])_([A-Z][a-z]*)_(([a-z]*)|([A-Z]*))') #looking for phase name in dataset name + ph_name = re.compile(r'(?<=(constituent\/))(.*?)(?=(generic))') #looking for phase name in dataset name array = results.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)) From 3c42368f6681313a6efcd55afe9b145f1a5166a8 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 18 Dec 2019 11:29:13 +0100 Subject: [PATCH 089/223] also use newer regex --- python/damask/dadf5.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/damask/dadf5.py b/python/damask/dadf5.py index 367d15f36..2b86cb5f0 100644 --- a/python/damask/dadf5.py +++ b/python/damask/dadf5.py @@ -833,7 +833,7 @@ class DADF5(): N_not_calculated = len(todo) while N_not_calculated > 0: result = results.get() - with h5py.File(self.fname,'a') as f: # write to file + 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() @@ -927,8 +927,8 @@ class DADF5(): 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'(\/[1-9])_([A-Z][a-z]*)_(([a-z]*)|([A-Z]*))') #looking for phase name in dataset name - dset_name = '1_' + re.sub(ph_name,r'',x[0].split('/',1)[1]) #removing phase name from generic dataset + 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]) From 4b6388fbb29e4257860ff4595ef3aaf821f8106c Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 18 Dec 2019 20:05:51 +0100 Subject: [PATCH 090/223] always use HDF5 output --- src/CPFEM.f90 | 4 ---- src/HDF5_utilities.f90 | 4 ---- src/constitutive.f90 | 6 +++--- src/crystallite.f90 | 4 ++-- src/discretization.f90 | 4 ++-- src/homogenization.f90 | 4 ++-- src/homogenization_mech_RGC.f90 | 6 ------ src/material.f90 | 2 -- src/mesh_marc.f90 | 2 -- src/results.f90 | 2 -- 10 files changed, 9 insertions(+), 29 deletions(-) diff --git a/src/CPFEM.f90 b/src/CPFEM.f90 index 9bf8c547c..3a7f35633 100644 --- a/src/CPFEM.f90 +++ b/src/CPFEM.f90 @@ -87,10 +87,8 @@ subroutine CPFEM_initAll(el,ip) call math_init call rotations_init call FE_init -#ifdef DAMASK_HDF5 call HDF5_utilities_init call results_init -#endif call mesh_init(ip, el) call lattice_init call material_init @@ -374,7 +372,6 @@ subroutine CPFEM_results(inc,time) integer(pInt), intent(in) :: inc real(pReal), intent(in) :: time -#ifdef DAMASK_HDF5 call results_openJobFile call results_addIncrement(inc,time) call constitutive_results @@ -382,7 +379,6 @@ subroutine CPFEM_results(inc,time) call homogenization_results call results_removeLink('current') ! ToDo: put this into closeJobFile call results_closeJobFile -#endif end subroutine CPFEM_results diff --git a/src/HDF5_utilities.f90 b/src/HDF5_utilities.f90 index e4819431e..c88afbf7d 100644 --- a/src/HDF5_utilities.f90 +++ b/src/HDF5_utilities.f90 @@ -5,9 +5,7 @@ !> @author Martin Diehl, Max-Planck-Institut für Eisenforschung GmbH !-------------------------------------------------------------------------------------------------- module HDF5_utilities -#if defined(PETSc) || defined(DAMASK_HDF5) use HDF5 -#endif #ifdef PETSc use PETSC #endif @@ -20,7 +18,6 @@ module HDF5_utilities implicit none public -#if defined(PETSc) || defined(DAMASK_HDF5) !-------------------------------------------------------------------------------------------------- !> @brief reads integer or float data of defined shape from file ! ToDo: order of arguments wrong !> @details for parallel IO, all dimension except for the last need to match @@ -1928,6 +1925,5 @@ subroutine finalize_write(plist_id, dset_id, filespace_id, memspace_id) if (hdferr < 0) call IO_error(1,ext_msg='finalize_write: h5sclose_f/memspace_id') end subroutine finalize_write -#endif end module HDF5_Utilities diff --git a/src/constitutive.f90 b/src/constitutive.f90 index 4067c026a..f6e0054af 100644 --- a/src/constitutive.f90 +++ b/src/constitutive.f90 @@ -688,7 +688,7 @@ end function constitutive_postResults !> @brief writes constitutive results to HDF5 output file !-------------------------------------------------------------------------------------------------- subroutine constitutive_results -#if defined(PETSc) || defined(DAMASK_HDF5) + integer :: p character(len=256) :: group do p=1,size(config_name_phase) @@ -719,8 +719,8 @@ subroutine constitutive_results call plastic_nonlocal_results(phase_plasticityInstance(p),group) end select - enddo -#endif + enddo + end subroutine constitutive_results end module constitutive diff --git a/src/crystallite.f90 b/src/crystallite.f90 index 292241001..e6e1473c4 100644 --- a/src/crystallite.f90 +++ b/src/crystallite.f90 @@ -767,7 +767,7 @@ end function crystallite_postResults !> @brief writes crystallite results to HDF5 output file !-------------------------------------------------------------------------------------------------- subroutine crystallite_results -#if defined(PETSc) || defined(DAMASK_HDF5) + integer :: p,o real(pReal), allocatable, dimension(:,:,:) :: selected_tensors type(rotation), allocatable, dimension(:) :: selected_rotations @@ -888,7 +888,7 @@ subroutine crystallite_results enddo end function select_rotations -#endif + end subroutine crystallite_results diff --git a/src/discretization.f90 b/src/discretization.f90 index 873148666..5f9d3f521 100644 --- a/src/discretization.f90 +++ b/src/discretization.f90 @@ -78,7 +78,7 @@ end subroutine discretization_init !> @brief write the displacements !-------------------------------------------------------------------------------------------------- subroutine discretization_results -#if defined(PETSc) || defined(DAMASK_HDF5) + real(pReal), dimension(:,:), allocatable :: u call results_closeGroup(results_addGroup(trim('current/geometry'))) @@ -90,7 +90,7 @@ subroutine discretization_results u = discretization_IPcoords & - discretization_IPcoords0 call results_writeDataset('current/geometry',u,'u_c','cell center displacements','m') -#endif + end subroutine discretization_results diff --git a/src/homogenization.f90 b/src/homogenization.f90 index 0112f9cf5..6c64f0fb5 100644 --- a/src/homogenization.f90 +++ b/src/homogenization.f90 @@ -790,7 +790,7 @@ end function postResults !> @brief writes homogenization results to HDF5 output file !-------------------------------------------------------------------------------------------------- subroutine homogenization_results -#if defined(PETSc) || defined(DAMASK_HDF5) + use material, only: & material_homogenization_type => homogenization_type @@ -822,7 +822,7 @@ subroutine homogenization_results ! '1st Piola-Kirchoff stress','Pa') enddo -#endif + end subroutine homogenization_results end module homogenization diff --git a/src/homogenization_mech_RGC.f90 b/src/homogenization_mech_RGC.f90 index 61a1997cd..23e99c8c5 100644 --- a/src/homogenization_mech_RGC.f90 +++ b/src/homogenization_mech_RGC.f90 @@ -928,7 +928,6 @@ end subroutine mech_RGC_averageStressAndItsTangent !> @brief writes results to HDF5 output file !-------------------------------------------------------------------------------------------------- module subroutine mech_RGC_results(instance,group) -#if defined(PETSc) || defined(DAMASK_HDF5) integer, intent(in) :: instance character(len=*), intent(in) :: group @@ -962,11 +961,6 @@ module subroutine mech_RGC_results(instance,group) enddo outputsLoop end associate -#else - integer, intent(in) :: instance - character(len=*), intent(in) :: group -#endif - end subroutine mech_RGC_results diff --git a/src/material.f90 b/src/material.f90 index 8aeab5dec..9ebd00397 100644 --- a/src/material.f90 +++ b/src/material.f90 @@ -354,12 +354,10 @@ subroutine material_init call config_deallocate('material.config/microstructure') call config_deallocate('material.config/texture') -#if defined(PETSc) || defined(DAMASK_HDF5) call results_openJobFile call results_mapping_constituent(material_phaseAt,material_phaseMemberAt,config_name_phase) call results_mapping_materialpoint(material_homogenizationAt,material_homogenizationMemberAt,config_name_homogenization) call results_closeJobFile -#endif !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! diff --git a/src/mesh_marc.f90 b/src/mesh_marc.f90 index f640baa72..aef15e6bf 100644 --- a/src/mesh_marc.f90 +++ b/src/mesh_marc.f90 @@ -150,7 +150,6 @@ subroutine writeGeometry(elemType, & real(pReal), dimension(:,:), allocatable :: & coordinates_temp -#if defined(DAMASK_HDF5) call results_openJobFile call HDF5_closeGroup(results_addGroup('geometry')) @@ -171,7 +170,6 @@ subroutine writeGeometry(elemType, & 'coordinates of the material points','m') call results_closeJobFile -#endif end subroutine writeGeometry diff --git a/src/results.f90 b/src/results.f90 index a7037a454..d38e629ec 100644 --- a/src/results.f90 +++ b/src/results.f90 @@ -16,7 +16,6 @@ module results implicit none private -#if defined(PETSc) || defined(DAMASK_HDF5) integer(HID_T) :: resultsFile interface results_writeDataset @@ -978,5 +977,4 @@ end subroutine results_mapping_materialpoint !end subroutine HDF5_mappingCells -#endif end module results From 7ccf83637938077cc655d00686ad81496e048a7d Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 18 Dec 2019 20:06:44 +0100 Subject: [PATCH 091/223] not needed for python-based workflow --- processing/post/DADF5_vtk_cells.py | 147 ---------------------------- processing/post/DADF5_vtk_points.py | 127 ------------------------ 2 files changed, 274 deletions(-) delete mode 100755 processing/post/DADF5_vtk_cells.py delete mode 100755 processing/post/DADF5_vtk_points.py diff --git a/processing/post/DADF5_vtk_cells.py b/processing/post/DADF5_vtk_cells.py deleted file mode 100755 index b3afe939d..000000000 --- a/processing/post/DADF5_vtk_cells.py +++ /dev/null @@ -1,147 +0,0 @@ -#!/usr/bin/env python3 - -import os -import argparse -import re - -import h5py -import numpy as np -import vtk -from vtk.util import numpy_support - -import damask - -scriptName = os.path.splitext(os.path.basename(__file__))[0] -scriptID = ' '.join([scriptName,damask.version]) - -# -------------------------------------------------------------------- -# MAIN -# -------------------------------------------------------------------- -parser = argparse.ArgumentParser() - -#ToDo: We need to decide on a way of handling arguments of variable lentght -#https://stackoverflow.com/questions/15459997/passing-integer-lists-to-python - -#parser.add_argument('--version', action='version', version='%(prog)s {}'.format(scriptID)) -parser.add_argument('filenames', nargs='+', - help='DADF5 files') -parser.add_argument('-d','--dir', dest='dir',default='postProc',metavar='string', - help='name of subdirectory relative to the location of the DADF5 file to hold output') -parser.add_argument('--mat', nargs='+', - help='labels for materialpoint',dest='mat') -parser.add_argument('--con', nargs='+', - help='labels for constituent',dest='con') - -options = parser.parse_args() - -if options.mat is None: options.mat=[] -if options.con is None: options.con=[] - -# --- loop over input files ------------------------------------------------------------------------ - -for filename in options.filenames: - results = damask.DADF5(filename) - - if results.structured: # for grid solvers use rectilinear grid - grid = vtk.vtkRectilinearGrid() - coordArray = [vtk.vtkDoubleArray(), - vtk.vtkDoubleArray(), - vtk.vtkDoubleArray(), - ] - - grid.SetDimensions(*(results.grid+1)) - for dim in [0,1,2]: - for c in np.linspace(0,results.size[dim],1+results.grid[dim]): - coordArray[dim].InsertNextValue(c) - - grid.SetXCoordinates(coordArray[0]) - grid.SetYCoordinates(coordArray[1]) - grid.SetZCoordinates(coordArray[2]) - else: - nodes = vtk.vtkPoints() - with h5py.File(filename) as f: - nodes.SetData(numpy_support.numpy_to_vtk(f['/geometry/x_n'][()],deep=True)) - grid = vtk.vtkUnstructuredGrid() - grid.SetPoints(nodes) - grid.Allocate(f['/geometry/T_c'].shape[0]) - for i in f['/geometry/T_c']: - grid.InsertNextCell(vtk.VTK_HEXAHEDRON,8,i-1) - - N_digits = int(np.floor(np.log10(int(results.increments[-1][3:]))))+1 - for i,inc in enumerate(results.iter_visible('increments')): - print('Output step {}/{}'.format(i+1,len(results.increments))) - vtk_data = [] - - results.set_visible('materialpoints',False) - results.set_visible('constituents', True) - for label in options.con: - for p in results.iter_visible('con_physics'): - if p != 'generic': - for c in results.iter_visible('constituents'): - x = results.get_dataset_location(label) - if len(x) == 0: - continue - array = results.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]) - grid.GetCellData().AddArray(vtk_data[-1]) - else: - x = results.get_dataset_location(label) - if len(x) == 0: - continue - ph_name = re.compile(r'(?<=(constituent\/))(.*?)(?=(generic))') #looking for phase name in dataset name - array = results.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)) - dset_name = '1_' + re.sub(ph_name,r'',x[0].split('/',1)[1]) #removing phase name from generic dataset - vtk_data[-1].SetName(dset_name) - grid.GetCellData().AddArray(vtk_data[-1]) - - results.set_visible('constituents', False) - results.set_visible('materialpoints',True) - for label in options.mat: - for p in results.iter_visible('mat_physics'): - if p != 'generic': - for m in results.iter_visible('materialpoints'): - x = results.get_dataset_location(label) - if len(x) == 0: - continue - array = results.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]) - grid.GetCellData().AddArray(vtk_data[-1]) - else: - x = results.get_dataset_location(label) - if len(x) == 0: - continue - array = results.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]) - grid.GetCellData().AddArray(vtk_data[-1]) - - writer = vtk.vtkXMLRectilinearGridWriter() if results.structured else \ - vtk.vtkXMLUnstructuredGridWriter() - - results.set_visible('constituents', False) - results.set_visible('materialpoints',False) - x = results.get_dataset_location('u_n') - vtk_data.append(numpy_support.numpy_to_vtk(num_array=results.read_dataset(x,0),deep=True,array_type=vtk.VTK_DOUBLE)) - vtk_data[-1].SetName('u') - grid.GetPointData().AddArray(vtk_data[-1]) - - dirname = os.path.abspath(os.path.join(os.path.dirname(filename),options.dir)) - if not os.path.isdir(dirname): - os.mkdir(dirname,0o755) - file_out = '{}_inc{}.{}'.format(os.path.splitext(os.path.split(filename)[-1])[0], - inc[3:].zfill(N_digits), - writer.GetDefaultFileExtension()) - - writer.SetCompressorTypeToZLib() - writer.SetDataModeToBinary() - writer.SetFileName(os.path.join(dirname,file_out)) - writer.SetInputData(grid) - - writer.Write() diff --git a/processing/post/DADF5_vtk_points.py b/processing/post/DADF5_vtk_points.py deleted file mode 100755 index 925a73a5c..000000000 --- a/processing/post/DADF5_vtk_points.py +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/env python3 - -import os -import argparse -import re - -import numpy as np -import vtk -from vtk.util import numpy_support - -import damask - -scriptName = os.path.splitext(os.path.basename(__file__))[0] -scriptID = ' '.join([scriptName,damask.version]) - -# -------------------------------------------------------------------- -# MAIN -# -------------------------------------------------------------------- -parser = argparse.ArgumentParser() - -#ToDo: We need to decide on a way of handling arguments of variable lentght -#https://stackoverflow.com/questions/15459997/passing-integer-lists-to-python - -#parser.add_argument('--version', action='version', version='%(prog)s {}'.format(scriptID)) -parser.add_argument('filenames', nargs='+', - help='DADF5 files') -parser.add_argument('-d','--dir', dest='dir',default='postProc',metavar='string', - help='name of subdirectory relative to the location of the DADF5 file to hold output') -parser.add_argument('--mat', nargs='+', - help='labels for materialpoint',dest='mat') -parser.add_argument('--con', nargs='+', - help='labels for constituent',dest='con') - -options = parser.parse_args() - -if options.mat is None: options.mat=[] -if options.con is None: options.con=[] - -# --- loop over input files ------------------------------------------------------------------------ - -for filename in options.filenames: - results = damask.DADF5(filename) - - Points = vtk.vtkPoints() - Vertices = vtk.vtkCellArray() - for c in results.cell_coordinates(): - pointID = Points.InsertNextPoint(c) - Vertices.InsertNextCell(1) - Vertices.InsertCellPoint(pointID) - - Polydata = vtk.vtkPolyData() - Polydata.SetPoints(Points) - Polydata.SetVerts(Vertices) - Polydata.Modified() - - N_digits = int(np.floor(np.log10(int(results.increments[-1][3:]))))+1 - for i,inc in enumerate(results.iter_visible('increments')): - print('Output step {}/{}'.format(i+1,len(results.increments))) - vtk_data = [] - - results.set_visible('materialpoints',False) - results.set_visible('constituents', True) - for label in options.con: - - for p in results.iter_visible('con_physics'): - if p != 'generic': - for c in results.iter_visible('constituents'): - x = results.get_dataset_location(label) - if len(x) == 0: - continue - array = results.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]) - Polydata.GetCellData().AddArray(vtk_data[-1]) - else: - x = results.get_dataset_location(label) - if len(x) == 0: - continue - ph_name = re.compile(r'(?<=(constituent\/))(.*?)(?=(generic))') #looking for phase name in dataset name - array = results.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)) - dset_name = '1_' + re.sub(ph_name,r'',x[0].split('/',1)[1]) #removing phase name from generic dataset - vtk_data[-1].SetName(dset_name) - Polydata.GetCellData().AddArray(vtk_data[-1]) - - results.set_visible('constituents', False) - results.set_visible('materialpoints',True) - for label in options.mat: - for p in results.iter_visible('mat_physics'): - if p != 'generic': - for m in results.iter_visible('materialpoints'): - x = results.get_dataset_location(label) - if len(x) == 0: - continue - array = results.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]) - Polydata.GetCellData().AddArray(vtk_data[-1]) - else: - x = results.get_dataset_location(label) - if len(x) == 0: - continue - array = results.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]) - Polydata.GetCellData().AddArray(vtk_data[-1]) - - writer = vtk.vtkXMLPolyDataWriter() - - - dirname = os.path.abspath(os.path.join(os.path.dirname(filename),options.dir)) - if not os.path.isdir(dirname): - os.mkdir(dirname,0o755) - file_out = '{}_inc{}.{}'.format(os.path.splitext(os.path.split(filename)[-1])[0], - inc[3:].zfill(N_digits), - writer.GetDefaultFileExtension()) - - writer.SetCompressorTypeToZLib() - writer.SetDataModeToBinary() - writer.SetFileName(os.path.join(dirname,file_out)) - writer.SetInputData(Polydata) - - writer.Write() From 9d7248f8c49c62727194c33d66b7637a858ebcff Mon Sep 17 00:00:00 2001 From: Test User Date: Wed, 18 Dec 2019 22:10:38 +0100 Subject: [PATCH 092/223] [skip ci] updated version information after successful test of v2.0.3-1264-g80b559dc --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index dd4016408..005c93723 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.0.3-1255-ga6da8fdd +v2.0.3-1264-g80b559dc From 98e606d6f0c4e061548c49c894fdc09b02db71b7 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 19 Dec 2019 00:00:00 +0100 Subject: [PATCH 093/223] correct type --- src/mesh_grid.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh_grid.f90 b/src/mesh_grid.f90 index d10ffef8a..3d839e1c9 100644 --- a/src/mesh_grid.f90 +++ b/src/mesh_grid.f90 @@ -367,7 +367,7 @@ pure function cellEdgeNormal(nElems) integer, intent(in) :: nElems - real, dimension(3,6,1,nElems) :: cellEdgeNormal + real(pReal), dimension(3,6,1,nElems) :: cellEdgeNormal cellEdgeNormal(1:3,1,1,:) = spread([+1.0_pReal, 0.0_pReal, 0.0_pReal],2,nElems) cellEdgeNormal(1:3,2,1,:) = spread([-1.0_pReal, 0.0_pReal, 0.0_pReal],2,nElems) From 101cc83cee9b536a094be710101bb5a179b13058 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 19 Dec 2019 00:00:09 +0100 Subject: [PATCH 094/223] copy and paste error --- cmake/Compiler-PGI.cmake | 1 - 1 file changed, 1 deletion(-) diff --git a/cmake/Compiler-PGI.cmake b/cmake/Compiler-PGI.cmake index bca76f648..c18679417 100644 --- a/cmake/Compiler-PGI.cmake +++ b/cmake/Compiler-PGI.cmake @@ -1,7 +1,6 @@ ################################################################################################### # PGI Compiler ################################################################################################### -elseif(CMAKE_Fortran_COMPILER_ID STREQUAL "PGI") if (OPTIMIZATION STREQUAL "OFF") set (OPTIMIZATION_FLAGS "-O0" ) From f0ad07580290dbac7a43f8e0f33205aafd2fbe7d Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 19 Dec 2019 00:01:44 +0100 Subject: [PATCH 095/223] fixed indentation --- src/homogenization.f90 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/homogenization.f90 b/src/homogenization.f90 index 6c64f0fb5..4380ee5aa 100644 --- a/src/homogenization.f90 +++ b/src/homogenization.f90 @@ -107,12 +107,12 @@ module homogenization P,& !< partitioned stresses F,& !< partitioned deformation gradients F0 !< partitioned initial deformation gradients - real(pReal), dimension(:,:,:,:,:), intent(in) :: dPdF !< partitioned stiffnesses - real(pReal), dimension(3,3), intent(in) :: avgF !< average F - real(pReal), intent(in) :: dt !< time increment - integer, intent(in) :: & - ip, & !< integration point number - el !< element number + real(pReal), dimension(:,:,:,:,:), intent(in) :: dPdF !< partitioned stiffnesses + real(pReal), dimension(3,3), intent(in) :: avgF !< average F + real(pReal), intent(in) :: dt !< time increment + integer, intent(in) :: & + ip, & !< integration point number + el !< element number end function mech_RGC_updateState From befd4e2adf4dad2aa193b4ac60e411f485600cc5 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 19 Dec 2019 07:49:53 +0100 Subject: [PATCH 096/223] avoid imports --- src/homogenization.f90 | 7 +++---- src/mesh_marc.f90 | 3 +-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/homogenization.f90 b/src/homogenization.f90 index 4380ee5aa..9580336d3 100644 --- a/src/homogenization.f90 +++ b/src/homogenization.f90 @@ -23,7 +23,6 @@ module homogenization use damage_local use damage_nonlocal use results - use HDF5_utilities implicit none private @@ -801,18 +800,18 @@ subroutine homogenization_results do p=1,size(config_name_homogenization) group = trim('current/materialpoint')//'/'//trim(config_name_homogenization(p)) - call HDF5_closeGroup(results_addGroup(group)) + call results_closeGroup(results_addGroup(group)) group = trim(group)//'/mech' - call HDF5_closeGroup(results_addGroup(group)) + call results_closeGroup(results_addGroup(group)) select case(material_homogenization_type(p)) case(HOMOGENIZATION_rgc_ID) call mech_RGC_results(homogenization_typeInstance(p),group) end select group = trim('current/materialpoint')//'/'//trim(config_name_homogenization(p))//'/generic' - call HDF5_closeGroup(results_addGroup(group)) + call results_closeGroup(results_addGroup(group)) !temp = reshape(materialpoint_F,[3,3,discretization_nIP*discretization_nElem]) !call results_writeDataset(group,temp,'F',& diff --git a/src/mesh_marc.f90 b/src/mesh_marc.f90 index aef15e6bf..cebe844e7 100644 --- a/src/mesh_marc.f90 +++ b/src/mesh_marc.f90 @@ -18,7 +18,6 @@ module mesh use element use discretization use geometry_plastic_nonlocal - use HDF5_utilities use results implicit none @@ -151,7 +150,7 @@ subroutine writeGeometry(elemType, & coordinates_temp call results_openJobFile - call HDF5_closeGroup(results_addGroup('geometry')) + call results_closeGroup(results_addGroup('geometry')) connectivity_temp = connectivity_elem call results_writeDataset('geometry',connectivity_temp,'T_e',& From 6114f5c325d593bbea0001fc38d1cf34a039a2ff Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 19 Dec 2019 18:04:24 +0100 Subject: [PATCH 097/223] inplace option does not exist anymore --- processing/pre/seeds_check.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/processing/pre/seeds_check.sh b/processing/pre/seeds_check.sh index 025c9eb90..502a19024 100755 --- a/processing/pre/seeds_check.sh +++ b/processing/pre/seeds_check.sh @@ -6,7 +6,6 @@ do vtk_addPointCloudData $seeds \ --data microstructure,weight \ - --inplace \ --vtk ${seeds%.*}.vtp \ done From 0fdc880e2cd51f693984f3195b4040762ac71229 Mon Sep 17 00:00:00 2001 From: Test User Date: Fri, 20 Dec 2019 14:10:21 +0100 Subject: [PATCH 098/223] [skip ci] updated version information after successful test of v2.0.3-1294-g034367fa --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 005c93723..287da9b11 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.0.3-1264-g80b559dc +v2.0.3-1294-g034367fa From d12842a4418c1c52c4dfb9f14a384171cb8df618 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 07:13:13 +0100 Subject: [PATCH 099/223] only stack size matters Intel compiler requires large stack size, otherwise DAMASK crashes with segmentation fault. Other limits are not important (checked runtime) --- env/DAMASK.csh | 12 ++---------- env/DAMASK.sh | 6 +++--- env/DAMASK.zsh | 7 +++---- 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/env/DAMASK.csh b/env/DAMASK.csh index d3b4474b2..b6ccca869 100644 --- a/env/DAMASK.csh +++ b/env/DAMASK.csh @@ -12,7 +12,6 @@ cd $DAMASK_ROOT >/dev/null set BRANCH = `git branch 2>/dev/null| grep -E '^\* ')` cd - >/dev/null -# if DAMASK_BIN is present set path = ($DAMASK_ROOT/bin $path) set SOLVER=`which DAMASK_spectral` @@ -21,19 +20,12 @@ if ( "x$DAMASK_NUM_THREADS" == "x" ) then set DAMASK_NUM_THREADS=1 endif -# currently, there is no information that unlimited causes problems +# currently, there is no information that unlimited stack size causes problems # still, http://software.intel.com/en-us/forums/topic/501500 suggest to fix it # more info https://jblevins.org/log/segfault # https://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap # http://superuser.com/questions/220059/what-parameters-has-ulimit limit stacksize unlimited # maximum stack size (kB) -endif -if ( `limit | grep memoryuse` != "" ) then - limit memoryuse unlimited # maximum physical memory size -endif -if ( `limit | grep vmemoryuse` != "" ) then - limit vmemoryuse unlimited # maximum virtual memory size -endif # disable output in case of scp if ( $?prompt ) then @@ -44,7 +36,7 @@ if ( $?prompt ) then echo echo Using environment with ... echo "DAMASK $DAMASK_ROOT $BRANCH" - echo "Spectral Solver $SOLVER" + echo "Grid Solver $SOLVER" echo "Post Processing $PROCESSING" if ( $?PETSC_DIR) then echo "PETSc location $PETSC_DIR" diff --git a/env/DAMASK.sh b/env/DAMASK.sh index a6d7b2667..56696a0e8 100644 --- a/env/DAMASK.sh +++ b/env/DAMASK.sh @@ -43,7 +43,7 @@ PROCESSING=$(type -p postResults || true 2>/dev/null) [ "x$DAMASK_NUM_THREADS" == "x" ] && DAMASK_NUM_THREADS=1 -# currently, there is no information that unlimited causes problems +# currently, there is no information that unlimited stack size causes problems # still, http://software.intel.com/en-us/forums/topic/501500 suggest to fix it # more info https://jblevins.org/log/segfault # https://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap @@ -59,7 +59,7 @@ if [ ! -z "$PS1" ]; then echo echo Using environment with ... echo "DAMASK $DAMASK_ROOT $BRANCH" - echo "Spectral Solver $SOLVER" + echo "Grid Solver $SOLVER" echo "Post Processing $PROCESSING" if [ "x$PETSC_DIR" != "x" ]; then echo -n "PETSc location " @@ -93,7 +93,7 @@ fi export DAMASK_NUM_THREADS export PYTHONPATH=$DAMASK_ROOT/python:$PYTHONPATH -for var in BASE STAT SOLVER PROCESSING FREE DAMASK_BIN BRANCH; do +for var in BASE STAT SOLVER PROCESSING BRANCH; do unset "${var}" done for var in DAMASK MSC; do diff --git a/env/DAMASK.zsh b/env/DAMASK.zsh index 42021191f..8ac97fe18 100644 --- a/env/DAMASK.zsh +++ b/env/DAMASK.zsh @@ -24,7 +24,6 @@ unset -f set # add BRANCH if DAMASK_ROOT is a git repository cd $DAMASK_ROOT >/dev/null; BRANCH=$(git branch 2>/dev/null| grep -E '^\* '); cd - >/dev/null -# add DAMASK_BIN if present PATH=${DAMASK_ROOT}/bin:$PATH SOLVER=$(which DAMASK_spectral || true 2>/dev/null) @@ -35,7 +34,7 @@ PROCESSING=$(which postResults || true 2>/dev/null) [[ "x$DAMASK_NUM_THREADS" == "x" ]] && DAMASK_NUM_THREADS=1 -# currently, there is no information that unlimited causes problems +# currently, there is no information that unlimited stack size causes problems # still, http://software.intel.com/en-us/forums/topic/501500 suggest to fix it # more info https://jblevins.org/log/segfault # https://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap @@ -51,7 +50,7 @@ if [ ! -z "$PS1" ]; then echo echo "Using environment with ..." echo "DAMASK $DAMASK_ROOT $BRANCH" - echo "Spectral Solver $SOLVER" + echo "Grid Solver $SOLVER" echo "Post Processing $PROCESSING" if [ "x$PETSC_DIR" != "x" ]; then echo -n "PETSc location " @@ -87,7 +86,7 @@ fi export DAMASK_NUM_THREADS export PYTHONPATH=$DAMASK_ROOT/python:$PYTHONPATH -for var in BASE STAT SOLVER PROCESSING FREE DAMASK_BIN BRANCH; do +for var in SOLVER PROCESSING BRANCH; do unset "${var}" done for var in DAMASK MSC; do From 089e71deba23882900d95b065818dfbf443fa234 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 07:29:01 +0100 Subject: [PATCH 100/223] does not work (even after removing the extra bracket) --- env/DAMASK.csh | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/env/DAMASK.csh b/env/DAMASK.csh index b6ccca869..1b16e444b 100644 --- a/env/DAMASK.csh +++ b/env/DAMASK.csh @@ -7,11 +7,6 @@ set DAMASK_ROOT=`python -c "import os,sys; print(os.path.realpath(os.path.expand source $DAMASK_ROOT/CONFIG -# add BRANCH if DAMASK_ROOT is a git repository -cd $DAMASK_ROOT >/dev/null -set BRANCH = `git branch 2>/dev/null| grep -E '^\* ')` -cd - >/dev/null - set path = ($DAMASK_ROOT/bin $path) set SOLVER=`which DAMASK_spectral` @@ -35,7 +30,7 @@ if ( $?prompt ) then echo https://damask.mpie.de echo echo Using environment with ... - echo "DAMASK $DAMASK_ROOT $BRANCH" + echo "DAMASK $DAMASK_ROOT" echo "Grid Solver $SOLVER" echo "Post Processing $PROCESSING" if ( $?PETSC_DIR) then From b10e862be82978b5002ac825fd2592ced8a0ef6c Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 07:34:40 +0100 Subject: [PATCH 101/223] the compiler can do the counting --- src/IO.f90 | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/IO.f90 b/src/IO.f90 index c121cc65e..cffcb6471 100644 --- a/src/IO.f90 +++ b/src/IO.f90 @@ -11,9 +11,9 @@ module IO implicit none private - character(len=5), parameter, public :: & + character(len=*), parameter, public :: & IO_EOF = '#EOF#' !< end of file string - character(len=207), parameter, private :: & + character(len=*), parameter, private :: & IO_DIVIDER = '───────────────────'//& '───────────────────'//& '───────────────────'//& @@ -401,7 +401,7 @@ function IO_stringValue(string,chunkPos,myChunk,silent) character(len=:), allocatable :: IO_stringValue logical, optional,intent(in) :: silent !< switch to trigger verbosity - character(len=16), parameter :: MYNAME = 'IO_stringValue: ' + character(len=*), parameter :: MYNAME = 'IO_stringValue: ' logical :: warn @@ -429,8 +429,8 @@ real(pReal) function IO_floatValue (string,chunkPos,myChunk) integer, dimension(:), intent(in) :: chunkPos !< positions of start and end of each tag/chunk in given string integer, intent(in) :: myChunk !< position number of desired chunk character(len=*), intent(in) :: string !< raw input with known start and end of each chunk - character(len=15), parameter :: MYNAME = 'IO_floatValue: ' - character(len=17), parameter :: VALIDCHARACTERS = '0123456789eEdD.+-' + character(len=*), parameter :: MYNAME = 'IO_floatValue: ' + character(len=*), parameter :: VALIDCHARACTERS = '0123456789eEdD.+-' IO_floatValue = 0.0_pReal @@ -453,8 +453,8 @@ integer function IO_intValue(string,chunkPos,myChunk) character(len=*), intent(in) :: string !< raw input with known start and end of each chunk integer, intent(in) :: myChunk !< position number of desired chunk integer, dimension(:), intent(in) :: chunkPos !< positions of start and end of each tag/chunk in given string - character(len=13), parameter :: MYNAME = 'IO_intValue: ' - character(len=12), parameter :: VALIDCHARACTERS = '0123456789+-' + character(len=*), parameter :: MYNAME = 'IO_intValue: ' + character(len=*), parameter :: VALIDCHARACTERS = '0123456789+-' IO_intValue = 0 @@ -477,9 +477,9 @@ real(pReal) function IO_fixedNoEFloatValue (string,ends,myChunk) character(len=*), intent(in) :: string !< raw input with known ends of each chunk integer, intent(in) :: myChunk !< position number of desired chunk integer, dimension(:), intent(in) :: ends !< positions of end of each tag/chunk in given string - character(len=22), parameter :: MYNAME = 'IO_fixedNoEFloatValue ' - character(len=13), parameter :: VALIDBASE = '0123456789.+-' - character(len=12), parameter :: VALIDEXP = '0123456789+-' + character(len=*), parameter :: MYNAME = 'IO_fixedNoEFloatValue ' + character(len=*), parameter :: VALIDBASE = '0123456789.+-' + character(len=*), parameter :: VALIDEXP = '0123456789+-' real(pReal) :: base integer :: expon @@ -509,8 +509,8 @@ integer function IO_fixedIntValue(string,ends,myChunk) character(len=*), intent(in) :: string !< raw input with known ends of each chunk integer, intent(in) :: myChunk !< position number of desired chunk integer, dimension(:), intent(in) :: ends !< positions of end of each tag/chunk in given string - character(len=20), parameter :: MYNAME = 'IO_fixedIntValue: ' - character(len=12), parameter :: VALIDCHARACTERS = '0123456789+-' + character(len=*), parameter :: MYNAME = 'IO_fixedIntValue: ' + character(len=*), parameter :: VALIDCHARACTERS = '0123456789+-' IO_fixedIntValue = IO_verifyIntValue(trim(adjustl(string(ends(myChunk)+1:ends(myChunk+1)))),& VALIDCHARACTERS,MYNAME) From 41650cdd118d0fd4523f0f22b64b8f28aba6a516 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 07:38:17 +0100 Subject: [PATCH 102/223] use standard string length --- src/HDF5_utilities.f90 | 32 ++++++++++++++++---------------- src/constitutive.f90 | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/HDF5_utilities.f90 b/src/HDF5_utilities.f90 index c88afbf7d..02e575d98 100644 --- a/src/HDF5_utilities.f90 +++ b/src/HDF5_utilities.f90 @@ -276,8 +276,8 @@ logical function HDF5_objectExists(loc_id,path) integer(HID_T), intent(in) :: loc_id character(len=*), intent(in), optional :: path - integer :: hdferr - character(len=256) :: p + integer :: hdferr + character(len=pStringLen) :: p if (present(path)) then p = trim(path) @@ -305,10 +305,10 @@ subroutine HDF5_addAttribute_str(loc_id,attrLabel,attrValue,path) character(len=*), intent(in) :: attrLabel, attrValue character(len=*), intent(in), optional :: path - integer :: hdferr - integer(HID_T) :: attr_id, space_id, type_id - logical :: attrExists - character(len=256) :: p + integer :: hdferr + integer(HID_T) :: attr_id, space_id, type_id + logical :: attrExists + character(len=pStringLen) :: p if (present(path)) then p = trim(path) @@ -352,10 +352,10 @@ subroutine HDF5_addAttribute_int(loc_id,attrLabel,attrValue,path) integer, intent(in) :: attrValue character(len=*), intent(in), optional :: path - integer :: hdferr - integer(HID_T) :: attr_id, space_id - logical :: attrExists - character(len=256) :: p + integer :: hdferr + integer(HID_T) :: attr_id, space_id + logical :: attrExists + character(len=pStringLen) :: p if (present(path)) then p = trim(path) @@ -393,10 +393,10 @@ subroutine HDF5_addAttribute_real(loc_id,attrLabel,attrValue,path) real(pReal), intent(in) :: attrValue character(len=*), intent(in), optional :: path - integer :: hdferr - integer(HID_T) :: attr_id, space_id - logical :: attrExists - character(len=256) :: p + integer :: hdferr + integer(HID_T) :: attr_id, space_id + logical :: attrExists + character(len=pStringLen) :: p if (present(path)) then p = trim(path) @@ -438,7 +438,7 @@ subroutine HDF5_addAttribute_int_array(loc_id,attrLabel,attrValue,path) integer(HID_T) :: attr_id, space_id integer(HSIZE_T),dimension(1) :: array_size logical :: attrExists - character(len=256) :: p + character(len=pStringLen) :: p if (present(path)) then p = trim(path) @@ -482,7 +482,7 @@ subroutine HDF5_addAttribute_real_array(loc_id,attrLabel,attrValue,path) integer(HID_T) :: attr_id, space_id integer(HSIZE_T),dimension(1) :: array_size logical :: attrExists - character(len=256) :: p + character(len=pStringLen) :: p if (present(path)) then p = trim(path) diff --git a/src/constitutive.f90 b/src/constitutive.f90 index de262efe1..977c80337 100644 --- a/src/constitutive.f90 +++ b/src/constitutive.f90 @@ -584,7 +584,7 @@ end subroutine constitutive_collectDeltaState subroutine constitutive_results integer :: p - character(len=256) :: group + character(len=pStringLen) :: group do p=1,size(config_name_phase) group = trim('current/constituent')//'/'//trim(config_name_phase(p)) call HDF5_closeGroup(results_addGroup(group)) From 19a45d9c2b68a6a658cec16c890098cea4b552b8 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 07:55:42 +0100 Subject: [PATCH 103/223] cleaning of source modules: - offset and instance are only used locally - output names are needed only temporarly - HDF5 is always enabled --- src/source_damage_anisoBrittle.f90 | 15 +++------------ src/source_damage_anisoDuctile.f90 | 15 ++------------- src/source_damage_isoBrittle.f90 | 14 +++----------- src/source_damage_isoDuctile.f90 | 16 +++------------- src/source_thermal_dissipation.f90 | 7 +++---- src/source_thermal_externalheat.f90 | 6 +++--- 6 files changed, 17 insertions(+), 56 deletions(-) diff --git a/src/source_damage_anisoBrittle.f90 b/src/source_damage_anisoBrittle.f90 index 5dc8b96af..2211ffdd2 100644 --- a/src/source_damage_anisoBrittle.f90 +++ b/src/source_damage_anisoBrittle.f90 @@ -18,12 +18,9 @@ module source_damage_anisoBrittle implicit none private - integer, dimension(:), allocatable, public, protected :: & + integer, dimension(:), allocatable :: & source_damage_anisoBrittle_offset, & !< which source is my current source mechanism? source_damage_anisoBrittle_instance !< instance of source mechanism - - character(len=64), dimension(:,:), allocatable :: & - source_damage_anisoBrittle_output !< name of each post result output integer, dimension(:,:), allocatable :: & source_damage_anisoBrittle_Ncleavage !< number of cleavage systems per family @@ -82,7 +79,7 @@ subroutine source_damage_anisoBrittle_init character(len=65536), dimension(:), allocatable :: & outputs - write(6,'(/,a)') ' <<<+- source_'//SOURCE_DAMAGE_ANISOBRITTLE_LABEL//' init -+>>>' + write(6,'(/,a)') ' <<<+- source_'//SOURCE_DAMAGE_ANISOBRITTLE_LABEL//' init -+>>>'; flush(6) Ninstance = count(phase_source == SOURCE_damage_anisoBrittle_ID) if (Ninstance == 0) return @@ -100,9 +97,6 @@ subroutine source_damage_anisoBrittle_init enddo enddo - allocate(source_damage_anisoBrittle_output(maxval(phase_Noutput),Ninstance)) - source_damage_anisoBrittle_output = '' - allocate(source_damage_anisoBrittle_Ncleavage(lattice_maxNcleavageFamily,Ninstance), source=0) allocate(param(Ninstance)) @@ -151,7 +145,6 @@ subroutine source_damage_anisoBrittle_init select case(outputs(i)) case ('anisobrittle_drivingforce') - source_damage_anisoBrittle_output(i,source_damage_anisoBrittle_instance(p)) = outputs(i) prm%outputID = [prm%outputID, damage_drivingforce_ID] end select @@ -266,8 +259,7 @@ end subroutine source_damage_anisoBrittle_getRateAndItsTangent subroutine source_damage_anisoBrittle_results(phase,group) integer, intent(in) :: phase - character(len=*), intent(in) :: group -#if defined(PETSc) || defined(DAMASK_HDF5) + character(len=*), intent(in) :: group integer :: sourceOffset, o, instance instance = source_damage_anisoBrittle_instance(phase) @@ -281,7 +273,6 @@ subroutine source_damage_anisoBrittle_results(phase,group) end select enddo outputsLoop end associate -#endif end subroutine source_damage_anisoBrittle_results diff --git a/src/source_damage_anisoDuctile.f90 b/src/source_damage_anisoDuctile.f90 index caba26ef4..b6a4942c1 100644 --- a/src/source_damage_anisoDuctile.f90 +++ b/src/source_damage_anisoDuctile.f90 @@ -17,14 +17,10 @@ module source_damage_anisoDuctile implicit none private - integer, dimension(:), allocatable, public, protected :: & + integer, dimension(:), allocatable :: & source_damage_anisoDuctile_offset, & !< which source is my current damage mechanism? source_damage_anisoDuctile_instance !< instance of damage source mechanism - character(len=64), dimension(:,:), allocatable, target, public :: & - source_damage_anisoDuctile_output !< name of each post result output - - enum, bind(c) enumerator :: undefined_ID, & damage_drivingforce_ID @@ -76,7 +72,7 @@ subroutine source_damage_anisoDuctile_init character(len=65536), dimension(:), allocatable :: & outputs - write(6,'(/,a)') ' <<<+- source_'//SOURCE_DAMAGE_ANISODUCTILE_LABEL//' init -+>>>' + write(6,'(/,a)') ' <<<+- source_'//SOURCE_DAMAGE_ANISODUCTILE_LABEL//' init -+>>>'; flush(6) Ninstance = count(phase_source == SOURCE_damage_anisoDuctile_ID) if (Ninstance == 0) return @@ -93,10 +89,6 @@ subroutine source_damage_anisoDuctile_init source_damage_anisoDuctile_offset(phase) = source enddo enddo - - allocate(source_damage_anisoDuctile_output(maxval(phase_Noutput),Ninstance)) - source_damage_anisoDuctile_output = '' - allocate(param(Ninstance)) @@ -136,7 +128,6 @@ subroutine source_damage_anisoDuctile_init select case(outputs(i)) case ('anisoductile_drivingforce') - source_damage_anisoDuctile_output(i,source_damage_anisoDuctile_instance(p)) = outputs(i) prm%outputID = [prm%outputID, damage_drivingforce_ID] end select @@ -227,7 +218,6 @@ subroutine source_damage_anisoDuctile_results(phase,group) integer, intent(in) :: phase character(len=*), intent(in) :: group -#if defined(PETSc) || defined(DAMASK_HDF5) integer :: sourceOffset, o, instance instance = source_damage_anisoDuctile_instance(phase) @@ -241,7 +231,6 @@ subroutine source_damage_anisoDuctile_results(phase,group) end select enddo outputsLoop end associate -#endif end subroutine source_damage_anisoDuctile_results diff --git a/src/source_damage_isoBrittle.f90 b/src/source_damage_isoBrittle.f90 index e38c15682..e10177502 100644 --- a/src/source_damage_isoBrittle.f90 +++ b/src/source_damage_isoBrittle.f90 @@ -16,11 +16,9 @@ module source_damage_isoBrittle implicit none private - integer, dimension(:), allocatable, public, protected :: & + integer, dimension(:), allocatable :: & source_damage_isoBrittle_offset, & source_damage_isoBrittle_instance - character(len=64), dimension(:,:), allocatable :: & - source_damage_isoBrittle_output enum, bind(c) enumerator :: & @@ -67,7 +65,7 @@ subroutine source_damage_isoBrittle_init character(len=65536), dimension(:), allocatable :: & outputs - write(6,'(/,a)') ' <<<+- source_'//SOURCE_DAMAGE_ISOBRITTLE_LABEL//' init -+>>>' + write(6,'(/,a)') ' <<<+- source_'//SOURCE_DAMAGE_ISOBRITTLE_LABEL//' init -+>>>'; flush(6) Ninstance = count(phase_source == SOURCE_damage_isoBrittle_ID) if (Ninstance == 0) return @@ -84,9 +82,6 @@ subroutine source_damage_isoBrittle_init source_damage_isoBrittle_offset(phase) = source enddo enddo - - allocate(source_damage_isoBrittle_output(maxval(phase_Noutput),Ninstance)) - source_damage_isoBrittle_output = '' allocate(param(Ninstance)) @@ -120,7 +115,6 @@ subroutine source_damage_isoBrittle_init select case(outputs(i)) case ('isobrittle_drivingforce') - source_damage_isoBrittle_output(i,source_damage_isoBrittle_instance(p)) = outputs(i) prm%outputID = [prm%outputID, damage_drivingforce_ID] end select @@ -218,8 +212,7 @@ end subroutine source_damage_isoBrittle_getRateAndItsTangent subroutine source_damage_isoBrittle_results(phase,group) integer, intent(in) :: phase - character(len=*), intent(in) :: group -#if defined(PETSc) || defined(DAMASK_HDF5) + character(len=*), intent(in) :: group integer :: sourceOffset, o, instance instance = source_damage_isoBrittle_instance(phase) @@ -233,7 +226,6 @@ subroutine source_damage_isoBrittle_results(phase,group) end select enddo outputsLoop end associate -#endif end subroutine source_damage_isoBrittle_results diff --git a/src/source_damage_isoDuctile.f90 b/src/source_damage_isoDuctile.f90 index 69b8e82bf..fca804c84 100644 --- a/src/source_damage_isoDuctile.f90 +++ b/src/source_damage_isoDuctile.f90 @@ -15,18 +15,14 @@ module source_damage_isoDuctile implicit none private - integer, dimension(:), allocatable, public, protected :: & + integer, dimension(:), allocatable :: & source_damage_isoDuctile_offset, & !< which source is my current damage mechanism? source_damage_isoDuctile_instance !< instance of damage source mechanism - character(len=64), dimension(:,:), allocatable, target, public :: & - source_damage_isoDuctile_output !< name of each post result output - - enum, bind(c) enumerator :: undefined_ID, & damage_drivingforce_ID - end enum !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!11 ToDo + end enum !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ToDo type, private :: tParameters !< container type for internal constitutive parameters real(pReal) :: & @@ -83,9 +79,6 @@ subroutine source_damage_isoDuctile_init source_damage_isoDuctile_offset(phase) = source enddo enddo - - allocate(source_damage_isoDuctile_output(maxval(phase_Noutput),Ninstance)) - source_damage_isoDuctile_output = '' allocate(param(Ninstance)) @@ -119,7 +112,6 @@ subroutine source_damage_isoDuctile_init select case(outputs(i)) case ('isoductile_drivingforce') - source_damage_isoDuctile_output(i,source_damage_isoDuctile_instance(p)) = outputs(i) prm%outputID = [prm%outputID, damage_drivingforce_ID] end select @@ -198,8 +190,7 @@ end subroutine source_damage_isoDuctile_getRateAndItsTangent subroutine source_damage_isoDuctile_results(phase,group) integer, intent(in) :: phase - character(len=*), intent(in) :: group -#if defined(PETSc) || defined(DAMASK_HDF5) + character(len=*), intent(in) :: group integer :: sourceOffset, o, instance instance = source_damage_isoDuctile_instance(phase) @@ -213,7 +204,6 @@ subroutine source_damage_isoDuctile_results(phase,group) end select enddo outputsLoop end associate -#endif end subroutine source_damage_isoDuctile_results diff --git a/src/source_thermal_dissipation.f90 b/src/source_thermal_dissipation.f90 index 0d16a9171..e13742a90 100644 --- a/src/source_thermal_dissipation.f90 +++ b/src/source_thermal_dissipation.f90 @@ -14,7 +14,7 @@ module source_thermal_dissipation implicit none private - integer, dimension(:), allocatable, public, protected :: & + integer, dimension(:), allocatable :: & source_thermal_dissipation_offset, & !< which source is my current thermal dissipation mechanism? source_thermal_dissipation_instance !< instance of thermal dissipation source mechanism @@ -39,10 +39,9 @@ contains !-------------------------------------------------------------------------------------------------- subroutine source_thermal_dissipation_init - integer :: Ninstance,instance,source,sourceOffset - integer :: NofMyPhase,p + integer :: Ninstance,instance,source,sourceOffset,NofMyPhase,p - write(6,'(/,a)') ' <<<+- source_'//SOURCE_thermal_dissipation_label//' init -+>>>' + write(6,'(/,a)') ' <<<+- source_'//SOURCE_thermal_dissipation_label//' init -+>>>'; flush(6) Ninstance = count(phase_source == SOURCE_thermal_dissipation_ID) diff --git a/src/source_thermal_externalheat.f90 b/src/source_thermal_externalheat.f90 index 00e6da6bf..7ae37f037 100644 --- a/src/source_thermal_externalheat.f90 +++ b/src/source_thermal_externalheat.f90 @@ -14,7 +14,7 @@ module source_thermal_externalheat implicit none private - integer, dimension(:), allocatable, public, protected :: & + integer, dimension(:), allocatable :: & source_thermal_externalheat_offset, & !< which source is my current thermal dissipation mechanism? source_thermal_externalheat_instance !< instance of thermal dissipation source mechanism @@ -43,9 +43,9 @@ contains !-------------------------------------------------------------------------------------------------- subroutine source_thermal_externalheat_init - integer :: maxNinstance,instance,source,sourceOffset,NofMyPhase,p + integer :: maxNinstance,instance,source,sourceOffset,NofMyPhase,p - write(6,'(/,a)') ' <<<+- source_'//SOURCE_thermal_externalheat_label//' init -+>>>' + write(6,'(/,a)') ' <<<+- source_'//SOURCE_thermal_externalheat_label//' init -+>>>'; flush(6) maxNinstance = count(phase_source == SOURCE_thermal_externalheat_ID) From e8e3af000ada1439b8d77150ecdf81989495c121 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 07:59:09 +0100 Subject: [PATCH 104/223] indentation was not corrected after split per compiler --- cmake/Compiler-GNU.cmake | 210 ++++++++++++++++++------------------- cmake/Compiler-Intel.cmake | 180 +++++++++++++++---------------- cmake/Compiler-PGI.cmake | 24 ++--- 3 files changed, 207 insertions(+), 207 deletions(-) diff --git a/cmake/Compiler-GNU.cmake b/cmake/Compiler-GNU.cmake index 008c0c90e..6a9973bc6 100644 --- a/cmake/Compiler-GNU.cmake +++ b/cmake/Compiler-GNU.cmake @@ -2,129 +2,129 @@ # GNU Compiler ################################################################################################### - if (OPENMP) - set (OPENMP_FLAGS "-fopenmp") - endif () +if (OPENMP) + set (OPENMP_FLAGS "-fopenmp") +endif () - if (OPTIMIZATION STREQUAL "OFF") - set (OPTIMIZATION_FLAGS "-O0" ) - elseif (OPTIMIZATION STREQUAL "DEFENSIVE") - set (OPTIMIZATION_FLAGS "-O2") - elseif (OPTIMIZATION STREQUAL "AGGRESSIVE") - set (OPTIMIZATION_FLAGS "-O3 -ffast-math -funroll-loops -ftree-vectorize") - endif () +if (OPTIMIZATION STREQUAL "OFF") + set (OPTIMIZATION_FLAGS "-O0" ) +elseif (OPTIMIZATION STREQUAL "DEFENSIVE") + set (OPTIMIZATION_FLAGS "-O2") +elseif (OPTIMIZATION STREQUAL "AGGRESSIVE") + set (OPTIMIZATION_FLAGS "-O3 -ffast-math -funroll-loops -ftree-vectorize") +endif () - set (STANDARD_CHECK "-std=f2008ts -pedantic-errors" ) - set (LINKER_FLAGS "${LINKER_FLAGS} -Wl") - # options parsed directly to the linker - set (LINKER_FLAGS "${LINKER_FLAGS},-undefined,dynamic_lookup" ) - # ensure to link against dynamic libraries +set (STANDARD_CHECK "-std=f2008ts -pedantic-errors" ) +set (LINKER_FLAGS "${LINKER_FLAGS} -Wl") +# options parsed directly to the linker +set (LINKER_FLAGS "${LINKER_FLAGS},-undefined,dynamic_lookup" ) +# ensure to link against dynamic libraries #------------------------------------------------------------------------------------------------ # Fine tuning compilation options - set (COMPILE_FLAGS "${COMPILE_FLAGS} -xf95-cpp-input") - # preprocessor +set (COMPILE_FLAGS "${COMPILE_FLAGS} -xf95-cpp-input") +# preprocessor - set (COMPILE_FLAGS "${COMPILE_FLAGS} -ffree-line-length-132") - # restrict line length to the standard 132 characters (lattice.f90 require more characters) +set (COMPILE_FLAGS "${COMPILE_FLAGS} -ffree-line-length-132") +# restrict line length to the standard 132 characters (lattice.f90 require more characters) - set (COMPILE_FLAGS "${COMPILE_FLAGS} -fimplicit-none") - # assume "implicit none" even if not present in source +set (COMPILE_FLAGS "${COMPILE_FLAGS} -fimplicit-none") +# assume "implicit none" even if not present in source - set (COMPILE_FLAGS "${COMPILE_FLAGS} -Wall") - # sets the following Fortran options: - # -Waliasing: warn about possible aliasing of dummy arguments. Specifically, it warns if the same actual argument is associated with a dummy argument with "INTENT(IN)" and a dummy argument with "INTENT(OUT)" in a call with an explicit interface. - # -Wampersand: checks if a character expression is continued proberly by an ampersand at the end of the line and at the beginning of the new line - # -Warray-bounds: checks if array reference is out of bounds at compile time. use -fcheck-bounds to also check during runtime - # -Wconversion: warn about implicit conversions between different type - # -Wsurprising: warn when "suspicious" code constructs are encountered. While technically legal these usually indicate that an error has been made. - # -Wc-binding-type: - # -Wintrinsics-std: only standard intrisics are available, e.g. "call flush(6)" will cause an error - # -Wno-tabs: do not allow tabs in source - # -Wintrinsic-shadow: warn if a user-defined procedure or module procedure has the same name as an intrinsic - # -Wline-truncation: - # -Wtarget-lifetime: - # -Wreal-q-constant: warn about real-literal-constants with 'q' exponent-letter - # -Wunused: a number of unused-xxx warnings - # and sets the general (non-Fortran options) options: - # -Waddress - # -Warray-bounds (only with -O2) - # -Wc++11-compat - # -Wchar-subscripts - # -Wcomment - # -Wformat - # -Wmaybe-uninitialized - # -Wnonnull - # -Wparentheses - # -Wpointer-sign - # -Wreorder - # -Wreturn-type - # -Wsequence-point - # -Wstrict-aliasing - # -Wstrict-overflow=1 - # -Wswitch - # -Wtrigraphs - # -Wuninitialized - # -Wunknown-pragmas - # -Wunused-function - # -Wunused-label - # -Wunused-value - # -Wunused-variable - # -Wvolatile-register-var +set (COMPILE_FLAGS "${COMPILE_FLAGS} -Wall") +# sets the following Fortran options: +# -Waliasing: warn about possible aliasing of dummy arguments. Specifically, it warns if the same actual argument is associated with a dummy argument with "INTENT(IN)" and a dummy argument with "INTENT(OUT)" in a call with an explicit interface. +# -Wampersand: checks if a character expression is continued proberly by an ampersand at the end of the line and at the beginning of the new line +# -Warray-bounds: checks if array reference is out of bounds at compile time. use -fcheck-bounds to also check during runtime +# -Wconversion: warn about implicit conversions between different type +# -Wsurprising: warn when "suspicious" code constructs are encountered. While technically legal these usually indicate that an error has been made. +# -Wc-binding-type: +# -Wintrinsics-std: only standard intrisics are available, e.g. "call flush(6)" will cause an error +# -Wno-tabs: do not allow tabs in source +# -Wintrinsic-shadow: warn if a user-defined procedure or module procedure has the same name as an intrinsic +# -Wline-truncation: +# -Wtarget-lifetime: +# -Wreal-q-constant: warn about real-literal-constants with 'q' exponent-letter +# -Wunused: a number of unused-xxx warnings +# and sets the general (non-Fortran options) options: +# -Waddress +# -Warray-bounds (only with -O2) +# -Wc++11-compat +# -Wchar-subscripts +# -Wcomment +# -Wformat +# -Wmaybe-uninitialized +# -Wnonnull +# -Wparentheses +# -Wpointer-sign +# -Wreorder +# -Wreturn-type +# -Wsequence-point +# -Wstrict-aliasing +# -Wstrict-overflow=1 +# -Wswitch +# -Wtrigraphs +# -Wuninitialized +# -Wunknown-pragmas +# -Wunused-function +# -Wunused-label +# -Wunused-value +# -Wunused-variable +# -Wvolatile-register-var - set (COMPILE_FLAGS "${COMPILE_FLAGS} -Wextra") - # sets the following Fortran options: - # -Wunuses-parameter: - # -Wcompare-reals: - # and sets the general (non-Fortran options) options: - # -Wclobbered - # -Wempty-body - # -Wignored-qualifiers - # -Wmissing-field-initializers - # -Woverride-init - # -Wsign-compare - # -Wtype-limits - # -Wuninitialized - # -Wunused-but-set-parameter (only with -Wunused or -Wall) - # -Wno-globals +set (COMPILE_FLAGS "${COMPILE_FLAGS} -Wextra") +# sets the following Fortran options: +# -Wunuses-parameter: +# -Wcompare-reals: +# and sets the general (non-Fortran options) options: +# -Wclobbered +# -Wempty-body +# -Wignored-qualifiers +# -Wmissing-field-initializers +# -Woverride-init +# -Wsign-compare +# -Wtype-limits +# -Wuninitialized +# -Wunused-but-set-parameter (only with -Wunused or -Wall) +# -Wno-globals - set (COMPILE_FLAGS "${COMPILE_FLAGS} -Wcharacter-truncation") - # warn if character expressions (strings) are truncated +set (COMPILE_FLAGS "${COMPILE_FLAGS} -Wcharacter-truncation") +# warn if character expressions (strings) are truncated - set (COMPILE_FLAGS "${COMPILE_FLAGS} -Wunderflow") - # produce a warning when numerical constant expressions are encountered, which yield an UNDERFLOW during compilation +set (COMPILE_FLAGS "${COMPILE_FLAGS} -Wunderflow") +# produce a warning when numerical constant expressions are encountered, which yield an UNDERFLOW during compilation - set (COMPILE_FLAGS "${COMPILE_FLAGS} -Wsuggest-attribute=pure") - set (COMPILE_FLAGS "${COMPILE_FLAGS} -Wsuggest-attribute=noreturn") - set (COMPILE_FLAGS "${COMPILE_FLAGS} -Wconversion-extra") - set (COMPILE_FLAGS "${COMPILE_FLAGS} -Wimplicit-procedure") - set (COMPILE_FLAGS "${COMPILE_FLAGS} -Wno-unused-parameter") - set (COMPILE_FLAGS "${COMPILE_FLAGS} -ffpe-summary=all") - # print summary of floating point exeptions (invalid,zero,overflow,underflow,inexact,denormal) +set (COMPILE_FLAGS "${COMPILE_FLAGS} -Wsuggest-attribute=pure") +set (COMPILE_FLAGS "${COMPILE_FLAGS} -Wsuggest-attribute=noreturn") +set (COMPILE_FLAGS "${COMPILE_FLAGS} -Wconversion-extra") +set (COMPILE_FLAGS "${COMPILE_FLAGS} -Wimplicit-procedure") +set (COMPILE_FLAGS "${COMPILE_FLAGS} -Wno-unused-parameter") +set (COMPILE_FLAGS "${COMPILE_FLAGS} -ffpe-summary=all") +# print summary of floating point exeptions (invalid,zero,overflow,underflow,inexact,denormal) - # Additional options - # -Warray-temporarieswarnings: because we have many temporary arrays (performance issue?): - # -Wimplicit-interface: no interfaces for lapack/MPI routines - # -Wunsafe-loop-optimizations: warn if the loop cannot be optimized due to nontrivial assumptions. +# Additional options +# -Warray-temporarieswarnings: because we have many temporary arrays (performance issue?): +# -Wimplicit-interface: no interfaces for lapack/MPI routines +# -Wunsafe-loop-optimizations: warn if the loop cannot be optimized due to nontrivial assumptions. #------------------------------------------------------------------------------------------------ # Runtime debugging - set (DEBUG_FLAGS "${DEBUG_FLAGS} -ffpe-trap=invalid,zero,overflow") - # stop execution if floating point exception is detected (NaN is silent) +set (DEBUG_FLAGS "${DEBUG_FLAGS} -ffpe-trap=invalid,zero,overflow") +# stop execution if floating point exception is detected (NaN is silent) - set (DEBUG_FLAGS "${DEBUG_FLAGS} -g") - # Generate symbolic debugging information in the object file +set (DEBUG_FLAGS "${DEBUG_FLAGS} -g") +# Generate symbolic debugging information in the object file - set (DEBUG_FLAGS "${DEBUG_FLAGS} -fbacktrace") - set (DEBUG_FLAGS "${DEBUG_FLAGS} -fdump-core") - set (DEBUG_FLAGS "${DEBUG_FLAGS} -fcheck=all") - # checks for (array-temps,bounds,do,mem,pointer,recursion) +set (DEBUG_FLAGS "${DEBUG_FLAGS} -fbacktrace") +set (DEBUG_FLAGS "${DEBUG_FLAGS} -fdump-core") +set (DEBUG_FLAGS "${DEBUG_FLAGS} -fcheck=all") +# checks for (array-temps,bounds,do,mem,pointer,recursion) - # Additional options - # -ffpe-trap=precision,denormal,underflow +# Additional options +# -ffpe-trap=precision,denormal,underflow #------------------------------------------------------------------------------------------------ # precision settings - set (PRECISION_FLAGS "${PRECISION_FLAGS} -fdefault-real-8") - # set precision to 8 bytes for standard real (=8 for pReal). Will set size of double to 16 bytes as long as -fdefault-double-8 is not set - set (PRECISION_FLAGS "${PRECISION_FLAGS} -fdefault-double-8") - # set precision to 8 bytes for double real, would be 16 bytes if -fdefault-real-8 is used +set (PRECISION_FLAGS "${PRECISION_FLAGS} -fdefault-real-8") +# set precision to 8 bytes for standard real (=8 for pReal). Will set size of double to 16 bytes as long as -fdefault-double-8 is not set +set (PRECISION_FLAGS "${PRECISION_FLAGS} -fdefault-double-8") +# set precision to 8 bytes for double real, would be 16 bytes if -fdefault-real-8 is used diff --git a/cmake/Compiler-Intel.cmake b/cmake/Compiler-Intel.cmake index 60ed46cbc..1a2c2c455 100644 --- a/cmake/Compiler-Intel.cmake +++ b/cmake/Compiler-Intel.cmake @@ -1,116 +1,116 @@ ################################################################################################### # Intel Compiler ################################################################################################### - if (OPENMP) - set (OPENMP_FLAGS "-qopenmp -parallel") - endif () +if (OPENMP) + set (OPENMP_FLAGS "-qopenmp -parallel") +endif () - if (OPTIMIZATION STREQUAL "OFF") - set (OPTIMIZATION_FLAGS "-O0 -no-ip") - elseif (OPTIMIZATION STREQUAL "DEFENSIVE") - set (OPTIMIZATION_FLAGS "-O2") - elseif (OPTIMIZATION STREQUAL "AGGRESSIVE") - set (OPTIMIZATION_FLAGS "-ipo -O3 -no-prec-div -fp-model fast=2 -xHost") - # -fast = -ipo, -O3, -no-prec-div, -static, -fp-model fast=2, and -xHost" - endif () +if (OPTIMIZATION STREQUAL "OFF") + set (OPTIMIZATION_FLAGS "-O0 -no-ip") +elseif (OPTIMIZATION STREQUAL "DEFENSIVE") + set (OPTIMIZATION_FLAGS "-O2") +elseif (OPTIMIZATION STREQUAL "AGGRESSIVE") + set (OPTIMIZATION_FLAGS "-ipo -O3 -no-prec-div -fp-model fast=2 -xHost") + # -fast = -ipo, -O3, -no-prec-div, -static, -fp-model fast=2, and -xHost" +endif () - # -assume std_mod_proc_name (included in -standard-semantics) causes problems if other modules - # (PETSc, HDF5) are not compiled with this option (https://software.intel.com/en-us/forums/intel-fortran-compiler-for-linux-and-mac-os-x/topic/62172) - set (STANDARD_CHECK "-stand f15 -standard-semantics -assume nostd_mod_proc_name") - set (LINKER_FLAGS "${LINKER_FLAGS} -shared-intel") - # Link against shared Intel libraries instead of static ones +# -assume std_mod_proc_name (included in -standard-semantics) causes problems if other modules +# (PETSc, HDF5) are not compiled with this option (https://software.intel.com/en-us/forums/intel-fortran-compiler-for-linux-and-mac-os-x/topic/62172) +set (STANDARD_CHECK "-stand f15 -standard-semantics -assume nostd_mod_proc_name") +set (LINKER_FLAGS "${LINKER_FLAGS} -shared-intel") +# Link against shared Intel libraries instead of static ones #------------------------------------------------------------------------------------------------ # Fine tuning compilation options - set (COMPILE_FLAGS "${COMPILE_FLAGS} -fpp") - # preprocessor +set (COMPILE_FLAGS "${COMPILE_FLAGS} -fpp") +# preprocessor - set (COMPILE_FLAGS "${COMPILE_FLAGS} -ftz") - # flush underflow to zero, automatically set if -O[1,2,3] +set (COMPILE_FLAGS "${COMPILE_FLAGS} -ftz") +# flush underflow to zero, automatically set if -O[1,2,3] - set (COMPILE_FLAGS "${COMPILE_FLAGS} -diag-disable") - # disables warnings ... - set (COMPILE_FLAGS "${COMPILE_FLAGS} 5268") - # ... the text exceeds right hand column allowed on the line (we have only comments there) - set (COMPILE_FLAGS "${COMPILE_FLAGS},7624") - # ... about deprecated forall (has nice syntax and most likely a performance advantage) +set (COMPILE_FLAGS "${COMPILE_FLAGS} -diag-disable") +# disables warnings ... +set (COMPILE_FLAGS "${COMPILE_FLAGS} 5268") +# ... the text exceeds right hand column allowed on the line (we have only comments there) +set (COMPILE_FLAGS "${COMPILE_FLAGS},7624") +# ... about deprecated forall (has nice syntax and most likely a performance advantage) - set (COMPILE_FLAGS "${COMPILE_FLAGS} -warn") - # enables warnings ... - set (COMPILE_FLAGS "${COMPILE_FLAGS} declarations") - # ... any undeclared names (alternative name: -implicitnone) - set (COMPILE_FLAGS "${COMPILE_FLAGS},general") - # ... warning messages and informational messages are issued by the compiler - set (COMPILE_FLAGS "${COMPILE_FLAGS},usage") - # ... questionable programming practices - set (COMPILE_FLAGS "${COMPILE_FLAGS},interfaces") - # ... checks the interfaces of all SUBROUTINEs called and FUNCTIONs invoked in your compilation against an external set of interface blocks - set (COMPILE_FLAGS "${COMPILE_FLAGS},ignore_loc") - # ... %LOC is stripped from an actual argument - set (COMPILE_FLAGS "${COMPILE_FLAGS},alignments") - # ... data that is not naturally aligned - set (COMPILE_FLAGS "${COMPILE_FLAGS},unused") - # ... declared variables that are never used +set (COMPILE_FLAGS "${COMPILE_FLAGS} -warn") +# enables warnings ... +set (COMPILE_FLAGS "${COMPILE_FLAGS} declarations") +# ... any undeclared names (alternative name: -implicitnone) +set (COMPILE_FLAGS "${COMPILE_FLAGS},general") +# ... warning messages and informational messages are issued by the compiler +set (COMPILE_FLAGS "${COMPILE_FLAGS},usage") +# ... questionable programming practices +set (COMPILE_FLAGS "${COMPILE_FLAGS},interfaces") +# ... checks the interfaces of all SUBROUTINEs called and FUNCTIONs invoked in your compilation against an external set of interface blocks +set (COMPILE_FLAGS "${COMPILE_FLAGS},ignore_loc") +# ... %LOC is stripped from an actual argument +set (COMPILE_FLAGS "${COMPILE_FLAGS},alignments") +# ... data that is not naturally aligned +set (COMPILE_FLAGS "${COMPILE_FLAGS},unused") +# ... declared variables that are never used - # Additional options - # -warn: enables warnings, where - # truncated_source: Determines whether warnings occur when source exceeds the maximum column width in fixed-format files. - # (too many warnings because we have comments beyond character 132) - # uncalled: Determines whether warnings occur when a statement function is never called - # all: - # -name as_is: case sensitive Fortran! +# Additional options +# -warn: enables warnings, where +# truncated_source: Determines whether warnings occur when source exceeds the maximum column width in fixed-format files. +# (too many warnings because we have comments beyond character 132) +# uncalled: Determines whether warnings occur when a statement function is never called +# all: +# -name as_is: case sensitive Fortran! #------------------------------------------------------------------------------------------------ # Runtime debugging - set (DEBUG_FLAGS "${DEBUG_FLAGS} -g") - # Generate symbolic debugging information in the object file +set (DEBUG_FLAGS "${DEBUG_FLAGS} -g") +# Generate symbolic debugging information in the object file - set (DEBUG_FLAGS "${DEBUG_FLAGS} -traceback") - # Generate extra information in the object file to provide source file traceback information when a severe error occurs at run time +set (DEBUG_FLAGS "${DEBUG_FLAGS} -traceback") +# Generate extra information in the object file to provide source file traceback information when a severe error occurs at run time - set (DEBUG_FLAGS "${DEBUG_FLAGS} -gen-interfaces") - # Generate an interface block for each routine. http://software.intel.com/en-us/blogs/2012/01/05/doctor-fortran-gets-explicit-again/ +set (DEBUG_FLAGS "${DEBUG_FLAGS} -gen-interfaces") +# Generate an interface block for each routine. http://software.intel.com/en-us/blogs/2012/01/05/doctor-fortran-gets-explicit-again/ - set (DEBUG_FLAGS "${DEBUG_FLAGS} -fp-stack-check") - # Generate extra code after every function call to ensure that the floating-point (FP) stack is in the expected state +set (DEBUG_FLAGS "${DEBUG_FLAGS} -fp-stack-check") +# Generate extra code after every function call to ensure that the floating-point (FP) stack is in the expected state - set (DEBUG_FLAGS "${DEBUG_FLAGS} -fp-model strict") - # Trap uninitalized variables +set (DEBUG_FLAGS "${DEBUG_FLAGS} -fp-model strict") +# Trap uninitalized variables - set (DEBUG_FLAGS "${DEBUG_FLAGS} -check" ) - # Checks at runtime ... - set (DEBUG_FLAGS "${DEBUG_FLAGS} bounds") - # ... if an array index is too small (<1) or too large! - set (DEBUG_FLAGS "${DEBUG_FLAGS},format") - # ... for the data type of an item being formatted for output. - set (DEBUG_FLAGS "${DEBUG_FLAGS},output_conversion") - # ... for the fit of data items within a designated format descriptor field. - set (DEBUG_FLAGS "${DEBUG_FLAGS},pointers") - # ... for certain disassociated or uninitialized pointers or unallocated allocatable objects. - set (DEBUG_FLAGS "${DEBUG_FLAGS},uninit") - # ... for uninitialized variables. - set (DEBUG_FLAGS "${DEBUG_FLAGS} -ftrapuv") - # ... initializes stack local variables to an unusual value to aid error detection - set (DEBUG_FLAGS "${DEBUG_FLAGS} -fpe-all=0") - # ... capture all floating-point exceptions, sets -ftz automatically +set (DEBUG_FLAGS "${DEBUG_FLAGS} -check" ) +# Checks at runtime ... +set (DEBUG_FLAGS "${DEBUG_FLAGS} bounds") +# ... if an array index is too small (<1) or too large! +set (DEBUG_FLAGS "${DEBUG_FLAGS},format") +# ... for the data type of an item being formatted for output. +set (DEBUG_FLAGS "${DEBUG_FLAGS},output_conversion") +# ... for the fit of data items within a designated format descriptor field. +set (DEBUG_FLAGS "${DEBUG_FLAGS},pointers") +# ... for certain disassociated or uninitialized pointers or unallocated allocatable objects. +set (DEBUG_FLAGS "${DEBUG_FLAGS},uninit") +# ... for uninitialized variables. +set (DEBUG_FLAGS "${DEBUG_FLAGS} -ftrapuv") +# ... initializes stack local variables to an unusual value to aid error detection +set (DEBUG_FLAGS "${DEBUG_FLAGS} -fpe-all=0") +# ... capture all floating-point exceptions, sets -ftz automatically - set (DEBUG_FLAGS "${DEBUG_FLAGS} -warn") - # enables warnings ... - set (DEBUG_FLAGS "${DEBUG_FLAGS} errors") - # ... warnings are changed to errors - set (DEBUG_FLAGS "${DEBUG_FLAGS},stderrors") - # ... warnings about Fortran standard violations are changed to errors +set (DEBUG_FLAGS "${DEBUG_FLAGS} -warn") +# enables warnings ... +set (DEBUG_FLAGS "${DEBUG_FLAGS} errors") +# ... warnings are changed to errors +set (DEBUG_FLAGS "${DEBUG_FLAGS},stderrors") +# ... warnings about Fortran standard violations are changed to errors - set (DEBUG_FLAGS "${DEBUG_FLAGS} -debug-parameters all") - # generate debug information for parameters +set (DEBUG_FLAGS "${DEBUG_FLAGS} -debug-parameters all") +# generate debug information for parameters - # Additional options - # -heap-arrays: Should not be done for OpenMP, but set "ulimit -s unlimited" on shell. Probably it helps also to unlimit other limits - # -check: Checks at runtime, where - # arg_temp_created: will cause a lot of warnings because we create a bunch of temporary arrays (performance?) - # stack: +# Additional options +# -heap-arrays: Should not be done for OpenMP, but set "ulimit -s unlimited" on shell. Probably it helps also to unlimit other limits +# -check: Checks at runtime, where +# arg_temp_created: will cause a lot of warnings because we create a bunch of temporary arrays (performance?) +# stack: #------------------------------------------------------------------------------------------------ # precision settings - set (PRECISION_FLAGS "${PRECISION_FLAGS} -real-size 64") - # set precision for standard real to 32 | 64 | 128 (= 4 | 8 | 16 bytes, type pReal is always 8 bytes) +set (PRECISION_FLAGS "${PRECISION_FLAGS} -real-size 64") +# set precision for standard real to 32 | 64 | 128 (= 4 | 8 | 16 bytes, type pReal is always 8 bytes) diff --git a/cmake/Compiler-PGI.cmake b/cmake/Compiler-PGI.cmake index c18679417..39d9b092f 100644 --- a/cmake/Compiler-PGI.cmake +++ b/cmake/Compiler-PGI.cmake @@ -2,23 +2,23 @@ # PGI Compiler ################################################################################################### - if (OPTIMIZATION STREQUAL "OFF") - set (OPTIMIZATION_FLAGS "-O0" ) - elseif (OPTIMIZATION STREQUAL "DEFENSIVE") - set (OPTIMIZATION_FLAGS "-O2") - elseif (OPTIMIZATION STREQUAL "AGGRESSIVE") - set (OPTIMIZATION_FLAGS "-O3") - endif () +if (OPTIMIZATION STREQUAL "OFF") + set (OPTIMIZATION_FLAGS "-O0" ) +elseif (OPTIMIZATION STREQUAL "DEFENSIVE") + set (OPTIMIZATION_FLAGS "-O2") +elseif (OPTIMIZATION STREQUAL "AGGRESSIVE") + set (OPTIMIZATION_FLAGS "-O3") +endif () #------------------------------------------------------------------------------------------------ # Fine tuning compilation options - set (COMPILE_FLAGS "${COMPILE_FLAGS} -Mpreprocess") - # preprocessor +set (COMPILE_FLAGS "${COMPILE_FLAGS} -Mpreprocess") +# preprocessor - set (STANDARD_CHECK "-Mallocatable=03") +set (STANDARD_CHECK "-Mallocatable=03") #------------------------------------------------------------------------------------------------ # Runtime debugging - set (DEBUG_FLAGS "${DEBUG_FLAGS} -g") - # Includes debugging information in the object module; sets the optimization level to zero unless a -⁠O option is present on the command line +set (DEBUG_FLAGS "${DEBUG_FLAGS} -g") +# Includes debugging information in the object module; sets the optimization level to zero unless a -⁠O option is present on the command line From 6425c37f2d8e4f24078819f18a2a3287866e8fc0 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 10:07:37 +0100 Subject: [PATCH 105/223] use default string length 256 characters are more than enough for string values and keys --- src/list.f90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/list.f90 b/src/list.f90 index 79eafc964..689227545 100644 --- a/src/list.f90 +++ b/src/list.f90 @@ -261,7 +261,7 @@ end function getInt !! error unless default is given. If raw is true, the the complete string is returned, otherwise !! the individual chunks are returned !-------------------------------------------------------------------------------------------------- -character(len=65536) function getString(this,key,defaultVal,raw) +character(len=pStringLen) function getString(this,key,defaultVal,raw) class(tPartitionedStringList), target, intent(in) :: this character(len=*), intent(in) :: key @@ -400,13 +400,13 @@ end function getInts !-------------------------------------------------------------------------------------------------- function getStrings(this,key,defaultVal,raw) - character(len=65536),dimension(:), allocatable :: getStrings + character(len=pStringLen),dimension(:), allocatable :: getStrings class(tPartitionedStringList),target, intent(in) :: this character(len=*), intent(in) :: key character(len=*), dimension(:), intent(in), optional :: defaultVal logical, intent(in), optional :: raw type(tPartitionedStringList), pointer :: item - character(len=65536) :: str + character(len=pStringLen) :: str integer :: i logical :: found, & whole, & From af6973adf628cca3f1396c2a11a4f39af837b111 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 10:10:23 +0100 Subject: [PATCH 106/223] HDF5 out is always on --- src/damage_local.f90 | 2 -- src/damage_nonlocal.f90 | 2 -- src/geometry_plastic_nonlocal.f90 | 2 -- src/plastic_disloUCLA.f90 | 6 ------ src/plastic_dislotwin.f90 | 6 ------ src/plastic_isotropic.f90 | 5 ----- src/plastic_kinematichardening.f90 | 5 ----- src/plastic_nonlocal.f90 | 8 +------- src/plastic_phenopowerlaw.f90 | 6 ------ src/thermal_adiabatic.f90 | 2 -- src/thermal_conduction.f90 | 5 ++--- 11 files changed, 3 insertions(+), 46 deletions(-) diff --git a/src/damage_local.f90 b/src/damage_local.f90 index aa9292f49..fdb6c2206 100644 --- a/src/damage_local.f90 +++ b/src/damage_local.f90 @@ -213,7 +213,6 @@ subroutine damage_local_results(homog,group) integer, intent(in) :: homog character(len=*), intent(in) :: group -#if defined(PETSc) || defined(DAMASK_HDF5) integer :: o, instance instance = damage_typeInstance(homog) @@ -228,7 +227,6 @@ subroutine damage_local_results(homog,group) end select enddo outputsLoop end associate -#endif end subroutine damage_local_results diff --git a/src/damage_nonlocal.f90 b/src/damage_nonlocal.f90 index 855fa0ea5..bd5a0b2e6 100644 --- a/src/damage_nonlocal.f90 +++ b/src/damage_nonlocal.f90 @@ -249,7 +249,6 @@ subroutine damage_nonlocal_results(homog,group) integer, intent(in) :: homog character(len=*), intent(in) :: group -#if defined(PETSc) || defined(DAMASK_HDF5) integer :: o, instance instance = damage_typeInstance(homog) @@ -264,7 +263,6 @@ subroutine damage_nonlocal_results(homog,group) end select enddo outputsLoop end associate -#endif end subroutine damage_nonlocal_results diff --git a/src/geometry_plastic_nonlocal.f90 b/src/geometry_plastic_nonlocal.f90 index 408306b2b..b69ab2eff 100644 --- a/src/geometry_plastic_nonlocal.f90 +++ b/src/geometry_plastic_nonlocal.f90 @@ -122,7 +122,6 @@ subroutine geometry_plastic_nonlocal_results integer, dimension(:), allocatable :: shp -#if defined(PETSc) || defined(DAMASK_HDF5) call results_openJobFile writeVolume: block @@ -151,7 +150,6 @@ subroutine geometry_plastic_nonlocal_results call results_closeJobFile -#endif end subroutine geometry_plastic_nonlocal_results diff --git a/src/plastic_disloUCLA.f90 b/src/plastic_disloUCLA.f90 index 29ef0fcc9..e64c7ae58 100644 --- a/src/plastic_disloUCLA.f90 +++ b/src/plastic_disloUCLA.f90 @@ -463,7 +463,6 @@ end subroutine plastic_disloUCLA_dependentState !> @brief writes results to HDF5 output file !-------------------------------------------------------------------------------------------------- subroutine plastic_disloUCLA_results(instance,group) -#if defined(PETSc) || defined(DAMASK_HDF5) integer, intent(in) :: instance character(len=*), intent(in) :: group @@ -491,11 +490,6 @@ subroutine plastic_disloUCLA_results(instance,group) end select enddo outputsLoop end associate - -#else - integer, intent(in) :: instance - character(len=*), intent(in) :: group -#endif end subroutine plastic_disloUCLA_results diff --git a/src/plastic_dislotwin.f90 b/src/plastic_dislotwin.f90 index 1f731a891..454ed42b1 100644 --- a/src/plastic_dislotwin.f90 +++ b/src/plastic_dislotwin.f90 @@ -926,7 +926,6 @@ end subroutine plastic_dislotwin_dependentState !> @brief writes results to HDF5 output file !-------------------------------------------------------------------------------------------------- subroutine plastic_dislotwin_results(instance,group) -#if defined(PETSc) || defined(DAMASK_HDF5) integer, intent(in) :: instance character(len=*) :: group @@ -969,11 +968,6 @@ subroutine plastic_dislotwin_results(instance,group) end select enddo outputsLoop end associate - -#else - integer, intent(in) :: instance - character(len=*) :: group -#endif end subroutine plastic_dislotwin_results diff --git a/src/plastic_isotropic.f90 b/src/plastic_isotropic.f90 index 9beb2262b..b86fff04a 100644 --- a/src/plastic_isotropic.f90 +++ b/src/plastic_isotropic.f90 @@ -373,7 +373,6 @@ end subroutine plastic_isotropic_dotState !> @brief writes results to HDF5 output file !-------------------------------------------------------------------------------------------------- subroutine plastic_isotropic_results(instance,group) -#if defined(PETSc) || defined(DAMASK_HDF5) integer, intent(in) :: instance character(len=*), intent(in) :: group @@ -388,10 +387,6 @@ subroutine plastic_isotropic_results(instance,group) end select enddo outputsLoop end associate -#else - integer, intent(in) :: instance - character(len=*) :: group -#endif end subroutine plastic_isotropic_results diff --git a/src/plastic_kinematichardening.f90 b/src/plastic_kinematichardening.f90 index 2a3dc4640..569051602 100644 --- a/src/plastic_kinematichardening.f90 +++ b/src/plastic_kinematichardening.f90 @@ -437,7 +437,6 @@ end subroutine plastic_kinehardening_deltaState !> @brief writes results to HDF5 output file !-------------------------------------------------------------------------------------------------- subroutine plastic_kinehardening_results(instance,group) -#if defined(PETSc) || defined(DAMASK_HDF5) integer, intent(in) :: instance character(len=*) :: group @@ -470,10 +469,6 @@ subroutine plastic_kinehardening_results(instance,group) end select enddo outputsLoop end associate -#else - integer, intent(in) :: instance - character(len=*) :: group -#endif end subroutine plastic_kinehardening_results diff --git a/src/plastic_nonlocal.f90 b/src/plastic_nonlocal.f90 index 5375aba49..f65fb2193 100644 --- a/src/plastic_nonlocal.f90 +++ b/src/plastic_nonlocal.f90 @@ -12,6 +12,7 @@ module plastic_nonlocal use material use lattice use rotations + use results use config use lattice use discretization @@ -1974,9 +1975,6 @@ end function getRho !> @brief writes results to HDF5 output file !-------------------------------------------------------------------------------------------------- subroutine plastic_nonlocal_results(instance,group) -#if defined(PETSc) || defined(DAMASK_HDF5) - use results, only: & - results_writeDataset integer, intent(in) :: instance character(len=*) :: group @@ -2039,10 +2037,6 @@ subroutine plastic_nonlocal_results(instance,group) end select enddo outputsLoop end associate -#else - integer, intent(in) :: instance - character(len=*) :: group -#endif end subroutine plastic_nonlocal_results diff --git a/src/plastic_phenopowerlaw.f90 b/src/plastic_phenopowerlaw.f90 index b8f5c8306..ef5bd36ef 100644 --- a/src/plastic_phenopowerlaw.f90 +++ b/src/plastic_phenopowerlaw.f90 @@ -464,7 +464,6 @@ end subroutine plastic_phenopowerlaw_dotState !> @brief writes results to HDF5 output file !-------------------------------------------------------------------------------------------------- subroutine plastic_phenopowerlaw_results(instance,group) -#if defined(PETSc) || defined(DAMASK_HDF5) integer, intent(in) :: instance character(len=*), intent(in) :: group @@ -492,11 +491,6 @@ subroutine plastic_phenopowerlaw_results(instance,group) end select enddo outputsLoop end associate - -#else - integer, intent(in) :: instance - character(len=*), intent(in) :: group -#endif end subroutine plastic_phenopowerlaw_results diff --git a/src/thermal_adiabatic.f90 b/src/thermal_adiabatic.f90 index 36dd2316b..7f2007195 100644 --- a/src/thermal_adiabatic.f90 +++ b/src/thermal_adiabatic.f90 @@ -254,7 +254,6 @@ subroutine thermal_adiabatic_results(homog,group) integer, intent(in) :: homog character(len=*), intent(in) :: group -#if defined(PETSc) || defined(DAMASK_HDF5) integer :: o, instance instance = thermal_typeInstance(homog) @@ -267,7 +266,6 @@ subroutine thermal_adiabatic_results(homog,group) 'temperature','K') end select enddo outputsLoop -#endif end subroutine thermal_adiabatic_results diff --git a/src/thermal_conduction.f90 b/src/thermal_conduction.f90 index ed25fccde..153ca12eb 100644 --- a/src/thermal_conduction.f90 +++ b/src/thermal_conduction.f90 @@ -209,7 +209,8 @@ function thermal_conduction_getSpecificHeat(ip,el) thermal_conduction_getSpecificHeat/real(homogenization_Ngrains(material_homogenizationAt(el)),pReal) end function thermal_conduction_getSpecificHeat - + + !-------------------------------------------------------------------------------------------------- !> @brief returns homogenized mass density !-------------------------------------------------------------------------------------------------- @@ -267,7 +268,6 @@ subroutine thermal_conduction_results(homog,group) integer, intent(in) :: homog character(len=*), intent(in) :: group -#if defined(PETSc) || defined(DAMASK_HDF5) integer :: o, instance instance = thermal_typeInstance(homog) @@ -280,7 +280,6 @@ subroutine thermal_conduction_results(homog,group) 'temperature','K') end select enddo outputsLoop -#endif end subroutine thermal_conduction_results From 4dc5dac831a02bcc39481d3ebd26af376a3e1216 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 10:20:50 +0100 Subject: [PATCH 107/223] was only needed for postResults --- src/damage_local.f90 | 14 ++------------ src/damage_nonlocal.f90 | 16 ++-------------- 2 files changed, 4 insertions(+), 26 deletions(-) diff --git a/src/damage_local.f90 b/src/damage_local.f90 index fdb6c2206..93ec5ec7f 100644 --- a/src/damage_local.f90 +++ b/src/damage_local.f90 @@ -15,11 +15,6 @@ module damage_local implicit none private - - character(len=64), dimension(:,:), allocatable, target, public :: & - damage_local_output - integer, dimension(:), allocatable, target, public :: & - damage_local_Noutput enum, bind(c) enumerator :: & @@ -60,15 +55,12 @@ subroutine damage_local_init character(len=65536), dimension(:), allocatable :: & outputs - write(6,'(/,a)') ' <<<+- damage_'//DAMAGE_local_label//' init -+>>>' + write(6,'(/,a)') ' <<<+- damage_'//DAMAGE_local_label//' init -+>>>'; flush(6) maxNinstance = count(damage_type == DAMAGE_local_ID) if (maxNinstance == 0) return - allocate(damage_local_output (maxval(homogenization_Noutput),maxNinstance)) - damage_local_output = '' allocate(damage_local_outputID (maxval(homogenization_Noutput),maxNinstance),source=undefined_ID) - allocate(damage_local_Noutput (maxNinstance), source=0) allocate(param(maxNinstance)) @@ -86,9 +78,7 @@ subroutine damage_local_init select case(outputs(i)) case ('damage') - damage_local_output(i,damage_typeInstance(h)) = outputs(i) - damage_local_Noutput(instance) = damage_local_Noutput(instance) + 1 - prm%outputID = [prm%outputID , damage_ID] + prm%outputID = [prm%outputID , damage_ID] end select enddo diff --git a/src/damage_nonlocal.f90 b/src/damage_nonlocal.f90 index bd5a0b2e6..8365cbca5 100644 --- a/src/damage_nonlocal.f90 +++ b/src/damage_nonlocal.f90 @@ -18,11 +18,6 @@ module damage_nonlocal implicit none private - - character(len=64), dimension(:,:), allocatable, target, public :: & - damage_nonlocal_output - integer, dimension(:), allocatable, target, public :: & - damage_nonlocal_Noutput enum, bind(c) enumerator :: & @@ -63,14 +58,10 @@ subroutine damage_nonlocal_init character(len=65536), dimension(:), allocatable :: & outputs - write(6,'(/,a)') ' <<<+- damage_'//DAMAGE_nonlocal_label//' init -+>>>' + write(6,'(/,a)') ' <<<+- damage_'//DAMAGE_nonlocal_label//' init -+>>>'; flush(6) maxNinstance = count(damage_type == DAMAGE_nonlocal_ID) if (maxNinstance == 0) return - - allocate(damage_nonlocal_output (maxval(homogenization_Noutput),maxNinstance)) - damage_nonlocal_output = '' - allocate(damage_nonlocal_Noutput (maxNinstance), source=0) allocate(param(maxNinstance)) @@ -86,11 +77,8 @@ subroutine damage_nonlocal_init do i=1, size(outputs) outputID = undefined_ID select case(outputs(i)) - case ('damage') - damage_nonlocal_output(i,damage_typeInstance(h)) = outputs(i) - damage_nonlocal_Noutput(instance) = damage_nonlocal_Noutput(instance) + 1 - prm%outputID = [prm%outputID , damage_ID] + prm%outputID = [prm%outputID , damage_ID] end select enddo From 91ad5092f47d20b2d133ea5c6462bcd640a44003 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 10:31:19 +0100 Subject: [PATCH 108/223] preparing removal of old output data --- src/thermal_adiabatic.f90 | 19 +++++++++++++++++-- src/thermal_conduction.f90 | 19 +++++++++++++++++-- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/thermal_adiabatic.f90 b/src/thermal_adiabatic.f90 index 7f2007195..8d1af8fd0 100644 --- a/src/thermal_adiabatic.f90 +++ b/src/thermal_adiabatic.f90 @@ -26,6 +26,15 @@ module thermal_adiabatic enumerator :: undefined_ID, & temperature_ID end enum + + type :: tParameters + integer(kind(undefined_ID)), dimension(:), allocatable :: & + outputID + end type tParameters + + type(tparameters), dimension(:), allocatable :: & + param + integer(kind(undefined_ID)), dimension(:,:), allocatable :: & thermal_adiabatic_outputID !< ID of each post result output @@ -51,11 +60,13 @@ subroutine thermal_adiabatic_init character(len=65536), dimension(0), parameter :: emptyStringArray = [character(len=65536)::] character(len=65536), dimension(:), allocatable :: outputs - write(6,'(/,a)') ' <<<+- thermal_'//THERMAL_ADIABATIC_label//' init -+>>>' + write(6,'(/,a)') ' <<<+- thermal_'//THERMAL_ADIABATIC_label//' init -+>>>'; flush(6) maxNinstance = count(thermal_type == THERMAL_adiabatic_ID) if (maxNinstance == 0) return + allocate(param(maxNinstance)) + allocate(thermal_adiabatic_output (maxval(homogenization_Noutput),maxNinstance)) thermal_adiabatic_output = '' allocate(thermal_adiabatic_outputID (maxval(homogenization_Noutput),maxNinstance),source=undefined_ID) @@ -64,6 +75,9 @@ subroutine thermal_adiabatic_init initializeInstances: do section = 1, size(thermal_type) if (thermal_type(section) /= THERMAL_adiabatic_ID) cycle + associate(prm => param(thermal_typeInstance(section)), & + config => config_homogenization(section)) + NofMyHomog=count(material_homogenizationAt==section) instance = thermal_typeInstance(section) outputs = config_homogenization(section)%getStrings('(output)',defaultVal=emptyStringArray) @@ -89,7 +103,8 @@ subroutine thermal_adiabatic_init temperature(section)%p => thermalState(section)%state(1,:) deallocate(temperatureRate(section)%p) allocate (temperatureRate(section)%p(NofMyHomog), source=0.0_pReal) - + + end associate enddo initializeInstances end subroutine thermal_adiabatic_init diff --git a/src/thermal_conduction.f90 b/src/thermal_conduction.f90 index 153ca12eb..095a9a85c 100644 --- a/src/thermal_conduction.f90 +++ b/src/thermal_conduction.f90 @@ -25,6 +25,15 @@ module thermal_conduction enumerator :: undefined_ID, & temperature_ID end enum + + type :: tParameters + integer(kind(undefined_ID)), dimension(:), allocatable :: & + outputID + end type tParameters + + type(tparameters), dimension(:), allocatable :: & + param + integer(kind(undefined_ID)), dimension(:,:), allocatable, private :: & thermal_conduction_outputID !< ID of each post result output @@ -54,11 +63,13 @@ subroutine thermal_conduction_init character(len=65536), dimension(0), parameter :: emptyStringArray = [character(len=65536)::] character(len=65536), dimension(:), allocatable :: outputs - write(6,'(/,a)') ' <<<+- thermal_'//THERMAL_CONDUCTION_label//' init -+>>>' + write(6,'(/,a)') ' <<<+- thermal_'//THERMAL_CONDUCTION_label//' init -+>>>'; flush(6) maxNinstance = count(thermal_type == THERMAL_conduction_ID) if (maxNinstance == 0) return + allocate(param(maxNinstance)) + allocate(thermal_conduction_output (maxval(homogenization_Noutput),maxNinstance)) thermal_conduction_output = '' allocate(thermal_conduction_outputID (maxval(homogenization_Noutput),maxNinstance),source=undefined_ID) @@ -67,6 +78,9 @@ subroutine thermal_conduction_init initializeInstances: do section = 1, size(thermal_type) if (thermal_type(section) /= THERMAL_conduction_ID) cycle + associate(prm => param(thermal_typeInstance(section)), & + config => config_homogenization(section)) + NofMyHomog=count(material_homogenizationAt==section) instance = thermal_typeInstance(section) outputs = config_homogenization(section)%getStrings('(output)',defaultVal=emptyStringArray) @@ -93,7 +107,8 @@ subroutine thermal_conduction_init allocate (temperature (section)%p(NofMyHomog), source=thermal_initialT(section)) deallocate(temperatureRate(section)%p) allocate (temperatureRate(section)%p(NofMyHomog), source=0.0_pReal) - + + end associate enddo initializeInstances end subroutine thermal_conduction_init From 7baf4e7f53ca8906caaf08ccd2efd227a2a610b9 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 10:36:42 +0100 Subject: [PATCH 109/223] cleaning --- src/damage_local.f90 | 8 +++----- src/damage_nonlocal.f90 | 9 +++------ 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/damage_local.f90 b/src/damage_local.f90 index 93ec5ec7f..b96ea8ca8 100644 --- a/src/damage_local.f90 +++ b/src/damage_local.f90 @@ -46,7 +46,7 @@ contains !-------------------------------------------------------------------------------------------------- subroutine damage_local_init - integer :: maxNinstance,homog,instance,i + integer :: maxNinstance,homog,i integer :: sizeState integer :: NofMyHomog, h integer(kind(undefined_ID)) :: & @@ -87,7 +87,6 @@ subroutine damage_local_init homog = h NofMyHomog = count(material_homogenizationAt == homog) - instance = damage_typeInstance(homog) ! allocate state arrays @@ -203,10 +202,9 @@ subroutine damage_local_results(homog,group) integer, intent(in) :: homog character(len=*), intent(in) :: group - integer :: o, instance + integer :: o - instance = damage_typeInstance(homog) - associate(prm => param(instance)) + associate(prm => param(damage_typeInstance(homog))) outputsLoop: do o = 1,size(prm%outputID) select case(prm%outputID(o)) diff --git a/src/damage_nonlocal.f90 b/src/damage_nonlocal.f90 index 8365cbca5..db9242235 100644 --- a/src/damage_nonlocal.f90 +++ b/src/damage_nonlocal.f90 @@ -49,7 +49,7 @@ contains !-------------------------------------------------------------------------------------------------- subroutine damage_nonlocal_init - integer :: maxNinstance,homog,instance,i + integer :: maxNinstance,homog,i integer :: sizeState integer :: NofMyHomog, h integer(kind(undefined_ID)) :: & @@ -70,7 +70,6 @@ subroutine damage_nonlocal_init associate(prm => param(damage_typeInstance(h)), & config => config_homogenization(h)) - instance = damage_typeInstance(h) outputs = config%getStrings('(output)',defaultVal=emptyStringArray) allocate(prm%outputID(0)) @@ -86,7 +85,6 @@ subroutine damage_nonlocal_init homog = h NofMyHomog = count(material_homogenizationAt == homog) - instance = damage_typeInstance(homog) ! allocate state arrays @@ -237,10 +235,9 @@ subroutine damage_nonlocal_results(homog,group) integer, intent(in) :: homog character(len=*), intent(in) :: group - integer :: o, instance + integer :: o - instance = damage_typeInstance(homog) - associate(prm => param(instance)) + associate(prm => param(damage_typeInstance(homog))) outputsLoop: do o = 1,size(prm%outputID) select case(prm%outputID(o)) From 6678770c43ac10b29611e464d2dccd999a8aa7ad Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 10:43:36 +0100 Subject: [PATCH 110/223] public variables not needed anymore --- src/thermal_adiabatic.f90 | 34 +++++++++------------------------- src/thermal_conduction.f90 | 36 ++++++++++-------------------------- 2 files changed, 19 insertions(+), 51 deletions(-) diff --git a/src/thermal_adiabatic.f90 b/src/thermal_adiabatic.f90 index 8d1af8fd0..a32d6941d 100644 --- a/src/thermal_adiabatic.f90 +++ b/src/thermal_adiabatic.f90 @@ -15,13 +15,7 @@ module thermal_adiabatic implicit none private - - character(len=64), dimension(:,:), allocatable, target, public :: & - thermal_adiabatic_output !< name of each post result output - - integer, dimension(:), allocatable, target, public :: & - thermal_adiabatic_Noutput !< number of outputs per instance of this thermal model - + enum, bind(c) enumerator :: undefined_ID, & temperature_ID @@ -34,10 +28,6 @@ module thermal_adiabatic type(tparameters), dimension(:), allocatable :: & param - - integer(kind(undefined_ID)), dimension(:,:), allocatable :: & - thermal_adiabatic_outputID !< ID of each post result output - public :: & thermal_adiabatic_init, & @@ -67,12 +57,6 @@ subroutine thermal_adiabatic_init allocate(param(maxNinstance)) - allocate(thermal_adiabatic_output (maxval(homogenization_Noutput),maxNinstance)) - thermal_adiabatic_output = '' - allocate(thermal_adiabatic_outputID (maxval(homogenization_Noutput),maxNinstance),source=undefined_ID) - allocate(thermal_adiabatic_Noutput (maxNinstance), source=0) - - initializeInstances: do section = 1, size(thermal_type) if (thermal_type(section) /= THERMAL_adiabatic_ID) cycle associate(prm => param(thermal_typeInstance(section)), & @@ -80,13 +64,12 @@ subroutine thermal_adiabatic_init NofMyHomog=count(material_homogenizationAt==section) instance = thermal_typeInstance(section) - outputs = config_homogenization(section)%getStrings('(output)',defaultVal=emptyStringArray) + outputs = config%getStrings('(output)',defaultVal=emptyStringArray) + allocate(prm%outputID(0)) do i=1, size(outputs) select case(outputs(i)) case('temperature') - thermal_adiabatic_Noutput(instance) = thermal_adiabatic_Noutput(instance) + 1 - thermal_adiabatic_outputID(thermal_adiabatic_Noutput(instance),instance) = temperature_ID - thermal_adiabatic_output(thermal_adiabatic_Noutput(instance),instance) = outputs(i) + prm%outputID = [prm%outputID, temperature_ID] end select enddo @@ -269,18 +252,19 @@ subroutine thermal_adiabatic_results(homog,group) integer, intent(in) :: homog character(len=*), intent(in) :: group - integer :: o, instance + integer :: o - instance = thermal_typeInstance(homog) + associate(prm => param(damage_typeInstance(homog))) - outputsLoop: do o = 1,thermal_adiabatic_Noutput(instance) - select case(thermal_adiabatic_outputID(o,instance)) + outputsLoop: do o = 1,size(prm%outputID) + select case(prm%outputID(o)) case (temperature_ID) call results_writeDataset(group,temperature(homog)%p,'T',& 'temperature','K') end select enddo outputsLoop + end associate end subroutine thermal_adiabatic_results diff --git a/src/thermal_conduction.f90 b/src/thermal_conduction.f90 index 095a9a85c..57703c325 100644 --- a/src/thermal_conduction.f90 +++ b/src/thermal_conduction.f90 @@ -14,13 +14,7 @@ module thermal_conduction implicit none private - - character(len=64), dimension(:,:), allocatable, target, public :: & - thermal_conduction_output !< name of each post result output - - integer, dimension(:), allocatable, target, public :: & - thermal_conduction_Noutput !< number of outputs per instance of this damage - + enum, bind(c) enumerator :: undefined_ID, & temperature_ID @@ -32,11 +26,7 @@ module thermal_conduction end type tParameters type(tparameters), dimension(:), allocatable :: & - param - - integer(kind(undefined_ID)), dimension(:,:), allocatable, private :: & - thermal_conduction_outputID !< ID of each post result output - + param public :: & thermal_conduction_init, & @@ -70,12 +60,6 @@ subroutine thermal_conduction_init allocate(param(maxNinstance)) - allocate(thermal_conduction_output (maxval(homogenization_Noutput),maxNinstance)) - thermal_conduction_output = '' - allocate(thermal_conduction_outputID (maxval(homogenization_Noutput),maxNinstance),source=undefined_ID) - allocate(thermal_conduction_Noutput (maxNinstance), source=0) - - initializeInstances: do section = 1, size(thermal_type) if (thermal_type(section) /= THERMAL_conduction_ID) cycle associate(prm => param(thermal_typeInstance(section)), & @@ -83,13 +67,12 @@ subroutine thermal_conduction_init NofMyHomog=count(material_homogenizationAt==section) instance = thermal_typeInstance(section) - outputs = config_homogenization(section)%getStrings('(output)',defaultVal=emptyStringArray) + outputs = config%getStrings('(output)',defaultVal=emptyStringArray) + allocate(prm%outputID(0)) do i=1, size(outputs) select case(outputs(i)) case('temperature') - thermal_conduction_Noutput(instance) = thermal_conduction_Noutput(instance) + 1 - thermal_conduction_outputID(thermal_conduction_Noutput(instance),instance) = temperature_ID - thermal_conduction_output(thermal_conduction_Noutput(instance),instance) = outputs(i) + prm%outputID = [prm%outputID, temperature_ID] end select enddo @@ -283,18 +266,19 @@ subroutine thermal_conduction_results(homog,group) integer, intent(in) :: homog character(len=*), intent(in) :: group - integer :: o, instance + integer :: o - instance = thermal_typeInstance(homog) + associate(prm => param(damage_typeInstance(homog))) - outputsLoop: do o = 1,thermal_conduction_Noutput(instance) - select case(thermal_conduction_outputID(o,instance)) + outputsLoop: do o = 1,size(prm%outputID) + select case(prm%outputID(o)) case (temperature_ID) call results_writeDataset(group,temperature(homog)%p,'T',& 'temperature','K') end select enddo outputsLoop + end associate end subroutine thermal_conduction_results From ac182ef536fa834071c9bef720ea17932fbb1333 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 10:55:11 +0100 Subject: [PATCH 111/223] less variable, same style --- src/damage_local.f90 | 60 ++++++++++++-------------------------- src/damage_none.f90 | 29 +++++++++--------- src/damage_nonlocal.f90 | 56 +++++++++++++---------------------- src/thermal_adiabatic.f90 | 47 ++++++++++++++--------------- src/thermal_conduction.f90 | 55 ++++++++++++++++------------------ src/thermal_isothermal.f90 | 33 ++++++++++----------- 6 files changed, 115 insertions(+), 165 deletions(-) diff --git a/src/damage_local.f90 b/src/damage_local.f90 index b96ea8ca8..34aef6c9d 100644 --- a/src/damage_local.f90 +++ b/src/damage_local.f90 @@ -5,8 +5,8 @@ module damage_local use prec use material - use numerics use config + use numerics use source_damage_isoBrittle use source_damage_isoDuctile use source_damage_anisoBrittle @@ -22,9 +22,6 @@ module damage_local damage_ID end enum - integer(kind(undefined_ID)), dimension(:,:), allocatable :: & - damage_local_outputID !< ID of each post result output - type :: tParameters integer(kind(undefined_ID)), dimension(:), allocatable :: & outputID @@ -46,61 +43,42 @@ contains !-------------------------------------------------------------------------------------------------- subroutine damage_local_init - integer :: maxNinstance,homog,i - integer :: sizeState - integer :: NofMyHomog, h - integer(kind(undefined_ID)) :: & - outputID + integer :: maxNinstance,o,NofMyHomog,h character(len=65536), dimension(0), parameter :: emptyStringArray = [character(len=65536)::] - character(len=65536), dimension(:), allocatable :: & - outputs + character(len=65536), dimension(:), allocatable :: outputs write(6,'(/,a)') ' <<<+- damage_'//DAMAGE_local_label//' init -+>>>'; flush(6) maxNinstance = count(damage_type == DAMAGE_local_ID) if (maxNinstance == 0) return - allocate(damage_local_outputID (maxval(homogenization_Noutput),maxNinstance),source=undefined_ID) - allocate(param(maxNinstance)) do h = 1, size(damage_type) if (damage_type(h) /= DAMAGE_LOCAL_ID) cycle - associate(prm => param(damage_typeInstance(h)), & - config => config_homogenization(h)) + associate(prm => param(damage_typeInstance(h)),config => config_homogenization(h)) - outputs = config%getStrings('(output)',defaultVal=emptyStringArray) allocate(prm%outputID(0)) - do i=1, size(outputs) - outputID = undefined_ID - select case(outputs(i)) - - case ('damage') - prm%outputID = [prm%outputID , damage_ID] - end select - + do o=1, size(outputs) + select case(outputs(o)) + case ('damage') + prm%outputID = [prm%outputID , damage_ID] + end select enddo - - homog = h - - NofMyHomog = count(material_homogenizationAt == homog) - - -! allocate state arrays - sizeState = 1 - damageState(homog)%sizeState = sizeState - allocate(damageState(homog)%state0 (sizeState,NofMyHomog), source=damage_initialPhi(homog)) - allocate(damageState(homog)%subState0(sizeState,NofMyHomog), source=damage_initialPhi(homog)) - allocate(damageState(homog)%state (sizeState,NofMyHomog), source=damage_initialPhi(homog)) + NofMyHomog = count(material_homogenizationAt == h) + damageState(h)%sizeState = 1 + allocate(damageState(h)%state0 (1,NofMyHomog), source=damage_initialPhi(h)) + allocate(damageState(h)%subState0(1,NofMyHomog), source=damage_initialPhi(h)) + allocate(damageState(h)%state (1,NofMyHomog), source=damage_initialPhi(h)) - nullify(damageMapping(homog)%p) - damageMapping(homog)%p => mappingHomogenization(1,:,:) - deallocate(damage(homog)%p) - damage(homog)%p => damageState(homog)%state(1,:) - + nullify(damageMapping(h)%p) + damageMapping(h)%p => mappingHomogenization(1,:,:) + deallocate(damage(h)%p) + damage(h)%p => damageState(h)%state(1,:) + end associate enddo diff --git a/src/damage_none.f90 b/src/damage_none.f90 index 62d2cc0eb..d3b1b73c5 100644 --- a/src/damage_none.f90 +++ b/src/damage_none.f90 @@ -19,26 +19,23 @@ contains !-------------------------------------------------------------------------------------------------- subroutine damage_none_init - integer :: & - homog, & - NofMyHomog + integer :: h,NofMyHomog - write(6,'(/,a)') ' <<<+- damage_'//DAMAGE_NONE_LABEL//' init -+>>>' + write(6,'(/,a)') ' <<<+- damage_'//DAMAGE_NONE_LABEL//' init -+>>>'; flush(6) - initializeInstances: do homog = 1, size(config_homogenization) + do h = 1, size(config_homogenization) + if (damage_type(h) /= DAMAGE_NONE_ID) cycle + + NofMyHomog = count(material_homogenizationAt == h) + damageState(h)%sizeState = 0 + allocate(damageState(h)%state0 (0,NofMyHomog)) + allocate(damageState(h)%subState0(0,NofMyHomog)) + allocate(damageState(h)%state (0,NofMyHomog)) - myhomog: if (damage_type(homog) == DAMAGE_NONE_ID) then - NofMyHomog = count(material_homogenizationAt == homog) - damageState(homog)%sizeState = 0 - allocate(damageState(homog)%state0 (0,NofMyHomog)) - allocate(damageState(homog)%subState0(0,NofMyHomog)) - allocate(damageState(homog)%state (0,NofMyHomog)) + deallocate(damage(h)%p) + allocate (damage(h)%p(1), source=damage_initialPhi(h)) - deallocate(damage(homog)%p) - allocate (damage(homog)%p(1), source=damage_initialPhi(homog)) - - endif myhomog - enddo initializeInstances + enddo end subroutine damage_none_init diff --git a/src/damage_nonlocal.f90 b/src/damage_nonlocal.f90 index db9242235..18a456f34 100644 --- a/src/damage_nonlocal.f90 +++ b/src/damage_nonlocal.f90 @@ -1,13 +1,12 @@ !-------------------------------------------------------------------------------------------------- !> @author Pratheek Shanthraj, Max-Planck-Institut für Eisenforschung GmbH !> @brief material subroutine for non-locally evolving damage field -!> @details to be done !-------------------------------------------------------------------------------------------------- module damage_nonlocal use prec use material - use numerics use config + use numerics use crystallite use lattice use source_damage_isoBrittle @@ -49,14 +48,9 @@ contains !-------------------------------------------------------------------------------------------------- subroutine damage_nonlocal_init - integer :: maxNinstance,homog,i - integer :: sizeState - integer :: NofMyHomog, h - integer(kind(undefined_ID)) :: & - outputID - character(len=65536), dimension(0), parameter :: emptyStringArray = [character(len=65536)::] - character(len=65536), dimension(:), allocatable :: & - outputs + integer :: maxNinstance,o,NofMyHomog,h + character(len=65536), dimension(0), parameter :: emptyStringArray = [character(len=65536)::] + character(len=65536), dimension(:), allocatable :: outputs write(6,'(/,a)') ' <<<+- damage_'//DAMAGE_nonlocal_label//' init -+>>>'; flush(6) @@ -67,40 +61,32 @@ subroutine damage_nonlocal_init do h = 1, size(damage_type) if (damage_type(h) /= DAMAGE_NONLOCAL_ID) cycle - associate(prm => param(damage_typeInstance(h)), & - config => config_homogenization(h)) + associate(prm => param(damage_typeInstance(h)),config => config_homogenization(h)) outputs = config%getStrings('(output)',defaultVal=emptyStringArray) allocate(prm%outputID(0)) - do i=1, size(outputs) - outputID = undefined_ID - select case(outputs(i)) - case ('damage') - prm%outputID = [prm%outputID , damage_ID] - end select - + do o=1, size(outputs) + select case(outputs(o)) + case ('damage') + prm%outputID = [prm%outputID, damage_ID] + end select enddo - homog = h + NofMyHomog = count(material_homogenizationAt == h) + damageState(h)%sizeState = 1 + allocate(damageState(h)%state0 (1,NofMyHomog), source=damage_initialPhi(h)) + allocate(damageState(h)%subState0(1,NofMyHomog), source=damage_initialPhi(h)) + allocate(damageState(h)%state (1,NofMyHomog), source=damage_initialPhi(h)) - NofMyHomog = count(material_homogenizationAt == homog) - - -! allocate state arrays - sizeState = 1 - damageState(homog)%sizeState = sizeState - allocate(damageState(homog)%state0 (sizeState,NofMyHomog), source=damage_initialPhi(homog)) - allocate(damageState(homog)%subState0(sizeState,NofMyHomog), source=damage_initialPhi(homog)) - allocate(damageState(homog)%state (sizeState,NofMyHomog), source=damage_initialPhi(homog)) - - nullify(damageMapping(homog)%p) - damageMapping(homog)%p => mappingHomogenization(1,:,:) - deallocate(damage(homog)%p) - damage(homog)%p => damageState(homog)%state(1,:) - + nullify(damageMapping(h)%p) + damageMapping(h)%p => mappingHomogenization(1,:,:) + deallocate(damage(h)%p) + damage(h)%p => damageState(h)%state(1,:) + end associate enddo + end subroutine damage_nonlocal_init diff --git a/src/thermal_adiabatic.f90 b/src/thermal_adiabatic.f90 index a32d6941d..3bbc5b613 100644 --- a/src/thermal_adiabatic.f90 +++ b/src/thermal_adiabatic.f90 @@ -46,8 +46,8 @@ contains !-------------------------------------------------------------------------------------------------- subroutine thermal_adiabatic_init - integer :: maxNinstance,section,instance,i,sizeState,NofMyHomog - character(len=65536), dimension(0), parameter :: emptyStringArray = [character(len=65536)::] + integer :: maxNinstance,o,h,NofMyHomog + character(len=65536), dimension(0), parameter :: emptyStringArray = [character(len=65536)::] character(len=65536), dimension(:), allocatable :: outputs write(6,'(/,a)') ' <<<+- thermal_'//THERMAL_ADIABATIC_label//' init -+>>>'; flush(6) @@ -57,38 +57,35 @@ subroutine thermal_adiabatic_init allocate(param(maxNinstance)) - initializeInstances: do section = 1, size(thermal_type) - if (thermal_type(section) /= THERMAL_adiabatic_ID) cycle - associate(prm => param(thermal_typeInstance(section)), & - config => config_homogenization(section)) + do h = 1, size(thermal_type) + if (thermal_type(h) /= THERMAL_adiabatic_ID) cycle + associate(prm => param(thermal_typeInstance(h)),config => config_homogenization(h)) - NofMyHomog=count(material_homogenizationAt==section) - instance = thermal_typeInstance(section) outputs = config%getStrings('(output)',defaultVal=emptyStringArray) allocate(prm%outputID(0)) - do i=1, size(outputs) - select case(outputs(i)) + + do o=1, size(outputs) + select case(outputs(o)) case('temperature') - prm%outputID = [prm%outputID, temperature_ID] + prm%outputID = [prm%outputID, temperature_ID] end select enddo + + NofMyHomog=count(material_homogenizationAt==h) + thermalState(h)%sizeState = 1 + allocate(thermalState(h)%state0 (1,NofMyHomog), source=thermal_initialT(h)) + allocate(thermalState(h)%subState0(1,NofMyHomog), source=thermal_initialT(h)) + allocate(thermalState(h)%state (1,NofMyHomog), source=thermal_initialT(h)) - ! allocate state arrays - sizeState = 1 - thermalState(section)%sizeState = sizeState - allocate(thermalState(section)%state0 (sizeState,NofMyHomog), source=thermal_initialT(section)) - allocate(thermalState(section)%subState0(sizeState,NofMyHomog), source=thermal_initialT(section)) - allocate(thermalState(section)%state (sizeState,NofMyHomog), source=thermal_initialT(section)) - - nullify(thermalMapping(section)%p) - thermalMapping(section)%p => mappingHomogenization(1,:,:) - deallocate(temperature(section)%p) - temperature(section)%p => thermalState(section)%state(1,:) - deallocate(temperatureRate(section)%p) - allocate (temperatureRate(section)%p(NofMyHomog), source=0.0_pReal) + nullify(thermalMapping(h)%p) + thermalMapping(h)%p => mappingHomogenization(1,:,:) + deallocate(temperature(h)%p) + temperature(h)%p => thermalState(h)%state(1,:) + deallocate(temperatureRate(h)%p) + allocate (temperatureRate(h)%p(NofMyHomog), source=0.0_pReal) end associate - enddo initializeInstances + enddo end subroutine thermal_adiabatic_init diff --git a/src/thermal_conduction.f90 b/src/thermal_conduction.f90 index 57703c325..27adc39aa 100644 --- a/src/thermal_conduction.f90 +++ b/src/thermal_conduction.f90 @@ -16,8 +16,9 @@ module thermal_conduction private enum, bind(c) - enumerator :: undefined_ID, & - temperature_ID + enumerator :: & + undefined_ID, & + temperature_ID end enum type :: tParameters @@ -47,10 +48,8 @@ contains subroutine thermal_conduction_init - integer :: maxNinstance,section,instance,i - integer :: sizeState - integer :: NofMyHomog - character(len=65536), dimension(0), parameter :: emptyStringArray = [character(len=65536)::] + integer :: maxNinstance,o,NofMyHomog,h + character(len=65536), dimension(0), parameter :: emptyStringArray = [character(len=65536)::] character(len=65536), dimension(:), allocatable :: outputs write(6,'(/,a)') ' <<<+- thermal_'//THERMAL_CONDUCTION_label//' init -+>>>'; flush(6) @@ -60,39 +59,35 @@ subroutine thermal_conduction_init allocate(param(maxNinstance)) - initializeInstances: do section = 1, size(thermal_type) - if (thermal_type(section) /= THERMAL_conduction_ID) cycle - associate(prm => param(thermal_typeInstance(section)), & - config => config_homogenization(section)) + do h = 1, size(thermal_type) + if (thermal_type(h) /= THERMAL_conduction_ID) cycle + associate(prm => param(thermal_typeInstance(h)),config => config_homogenization(h)) - NofMyHomog=count(material_homogenizationAt==section) - instance = thermal_typeInstance(section) outputs = config%getStrings('(output)',defaultVal=emptyStringArray) allocate(prm%outputID(0)) - do i=1, size(outputs) - select case(outputs(i)) + + do o=1, size(outputs) + select case(outputs(o)) case('temperature') - prm%outputID = [prm%outputID, temperature_ID] + prm%outputID = [prm%outputID, temperature_ID] end select enddo + + NofMyHomog=count(material_homogenizationAt==h) + thermalState(h)%sizeState = 0 + allocate(thermalState(h)%state0 (0,NofMyHomog)) + allocate(thermalState(h)%subState0(0,NofMyHomog)) + allocate(thermalState(h)%state (0,NofMyHomog)) - - ! allocate state arrays - sizeState = 0 - thermalState(section)%sizeState = sizeState - allocate(thermalState(section)%state0 (sizeState,NofMyHomog)) - allocate(thermalState(section)%subState0(sizeState,NofMyHomog)) - allocate(thermalState(section)%state (sizeState,NofMyHomog)) - - nullify(thermalMapping(section)%p) - thermalMapping(section)%p => mappingHomogenization(1,:,:) - deallocate(temperature (section)%p) - allocate (temperature (section)%p(NofMyHomog), source=thermal_initialT(section)) - deallocate(temperatureRate(section)%p) - allocate (temperatureRate(section)%p(NofMyHomog), source=0.0_pReal) + nullify(thermalMapping(h)%p) + thermalMapping(h)%p => mappingHomogenization(1,:,:) + deallocate(temperature (h)%p) + allocate (temperature (h)%p(NofMyHomog), source=thermal_initialT(h)) + deallocate(temperatureRate(h)%p) + allocate (temperatureRate(h)%p(NofMyHomog), source=0.0_pReal) end associate - enddo initializeInstances + enddo end subroutine thermal_conduction_init diff --git a/src/thermal_isothermal.f90 b/src/thermal_isothermal.f90 index f06239944..3cafc6402 100644 --- a/src/thermal_isothermal.f90 +++ b/src/thermal_isothermal.f90 @@ -3,7 +3,6 @@ !> @brief material subroutine for isothermal temperature field !-------------------------------------------------------------------------------------------------- module thermal_isothermal - use prec use config use material @@ -20,27 +19,25 @@ contains !-------------------------------------------------------------------------------------------------- subroutine thermal_isothermal_init - integer :: & - homog, & - NofMyHomog + integer :: h,NofMyHomog - write(6,'(/,a)') ' <<<+- thermal_'//THERMAL_isothermal_label//' init -+>>>' + write(6,'(/,a)') ' <<<+- thermal_'//THERMAL_isothermal_label//' init -+>>>'; flush(6) - initializeInstances: do homog = 1, material_Nhomogenization - - if (thermal_type(homog) /= THERMAL_isothermal_ID) cycle - NofMyHomog = count(material_homogenizationAt == homog) - thermalState(homog)%sizeState = 0 - allocate(thermalState(homog)%state0 (0,NofMyHomog), source=0.0_pReal) - allocate(thermalState(homog)%subState0(0,NofMyHomog), source=0.0_pReal) - allocate(thermalState(homog)%state (0,NofMyHomog), source=0.0_pReal) + do h = 1, size(config_homogenization) + if (thermal_type(h) /= THERMAL_isothermal_ID) cycle + + NofMyHomog = count(material_homogenizationAt == h) + thermalState(h)%sizeState = 0 + allocate(thermalState(h)%state0 (0,NofMyHomog)) + allocate(thermalState(h)%subState0(0,NofMyHomog)) + allocate(thermalState(h)%state (0,NofMyHomog)) - deallocate(temperature (homog)%p) - allocate (temperature (homog)%p(1), source=thermal_initialT(homog)) - deallocate(temperatureRate(homog)%p) - allocate (temperatureRate(homog)%p(1), source=0.0_pReal) + deallocate(temperature (h)%p) + allocate (temperature (h)%p(1), source=thermal_initialT(h)) + deallocate(temperatureRate(h)%p) + allocate (temperatureRate(h)%p(1)) - enddo initializeInstances + enddo end subroutine thermal_isothermal_init From 83cf0623185eb2b3f77872bcef25a7cd34de3922 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 11:19:33 +0100 Subject: [PATCH 112/223] better define only once --- src/damage_local.f90 | 1 - src/damage_nonlocal.f90 | 1 - src/homogenization_mech_RGC.f90 | 2 -- src/plastic_disloUCLA.f90 | 4 ---- src/plastic_dislotwin.f90 | 4 ---- src/plastic_isotropic.f90 | 2 -- src/plastic_kinematichardening.f90 | 4 ---- src/plastic_nonlocal.f90 | 4 ---- src/plastic_phenopowerlaw.f90 | 4 ---- src/prec.f90 | 4 ++++ src/source_damage_anisoBrittle.f90 | 2 -- src/source_damage_anisoDuctile.f90 | 2 -- src/source_damage_isoBrittle.f90 | 1 - src/source_damage_isoDuctile.f90 | 1 - src/thermal_adiabatic.f90 | 1 - src/thermal_conduction.f90 | 1 - 16 files changed, 4 insertions(+), 34 deletions(-) diff --git a/src/damage_local.f90 b/src/damage_local.f90 index 34aef6c9d..0874b5aee 100644 --- a/src/damage_local.f90 +++ b/src/damage_local.f90 @@ -44,7 +44,6 @@ contains subroutine damage_local_init integer :: maxNinstance,o,NofMyHomog,h - character(len=65536), dimension(0), parameter :: emptyStringArray = [character(len=65536)::] character(len=65536), dimension(:), allocatable :: outputs write(6,'(/,a)') ' <<<+- damage_'//DAMAGE_local_label//' init -+>>>'; flush(6) diff --git a/src/damage_nonlocal.f90 b/src/damage_nonlocal.f90 index 18a456f34..47355a479 100644 --- a/src/damage_nonlocal.f90 +++ b/src/damage_nonlocal.f90 @@ -49,7 +49,6 @@ contains subroutine damage_nonlocal_init integer :: maxNinstance,o,NofMyHomog,h - character(len=65536), dimension(0), parameter :: emptyStringArray = [character(len=65536)::] character(len=65536), dimension(:), allocatable :: outputs write(6,'(/,a)') ' <<<+- damage_'//DAMAGE_nonlocal_label//' init -+>>>'; flush(6) diff --git a/src/homogenization_mech_RGC.f90 b/src/homogenization_mech_RGC.f90 index 23e99c8c5..d2e12c072 100644 --- a/src/homogenization_mech_RGC.f90 +++ b/src/homogenization_mech_RGC.f90 @@ -74,8 +74,6 @@ module subroutine mech_RGC_init NofMyHomog, & sizeState, nIntFaceTot - character(len=65536), dimension(0), parameter :: emptyStringArray = [character(len=65536)::] - integer(kind(undefined_ID)) :: & outputID diff --git a/src/plastic_disloUCLA.f90 b/src/plastic_disloUCLA.f90 index e64c7ae58..8610b6bc2 100644 --- a/src/plastic_disloUCLA.f90 +++ b/src/plastic_disloUCLA.f90 @@ -121,10 +121,6 @@ subroutine plastic_disloUCLA_init() sizeState, sizeDotState, & startIndex, endIndex - integer, dimension(0), parameter :: emptyIntArray = [integer::] - real(pReal), dimension(0), parameter :: emptyRealArray = [real(pReal)::] - character(len=65536), dimension(0), parameter :: emptyStringArray = [character(len=65536)::] - integer(kind(undefined_ID)) :: & outputID diff --git a/src/plastic_dislotwin.f90 b/src/plastic_dislotwin.f90 index 454ed42b1..8c19c7d83 100644 --- a/src/plastic_dislotwin.f90 +++ b/src/plastic_dislotwin.f90 @@ -180,10 +180,6 @@ subroutine plastic_dislotwin_init sizeState, sizeDotState, & startIndex, endIndex - integer, dimension(0), parameter :: emptyIntArray = [integer::] - real(pReal), dimension(0), parameter :: emptyRealArray = [real(pReal)::] - character(len=65536), dimension(0), parameter :: emptyStringArray = [character(len=65536)::] - integer(kind(undefined_ID)) :: & outputID diff --git a/src/plastic_isotropic.f90 b/src/plastic_isotropic.f90 index b86fff04a..38166df4a 100644 --- a/src/plastic_isotropic.f90 +++ b/src/plastic_isotropic.f90 @@ -85,8 +85,6 @@ subroutine plastic_isotropic_init NipcMyPhase, & sizeState, sizeDotState - character(len=65536), dimension(0), parameter :: emptyStringArray = [character(len=65536)::] - integer(kind(undefined_ID)) :: & outputID diff --git a/src/plastic_kinematichardening.f90 b/src/plastic_kinematichardening.f90 index 569051602..9b0c41041 100644 --- a/src/plastic_kinematichardening.f90 +++ b/src/plastic_kinematichardening.f90 @@ -103,10 +103,6 @@ subroutine plastic_kinehardening_init sizeState, sizeDeltaState, sizeDotState, & startIndex, endIndex - integer, dimension(0), parameter :: emptyIntArray = [integer::] - real(pReal), dimension(0), parameter :: emptyRealArray = [real(pReal)::] - character(len=65536), dimension(0), parameter :: emptyStringArray = [character(len=65536)::] - integer(kind(undefined_ID)) :: & outputID diff --git a/src/plastic_nonlocal.f90 b/src/plastic_nonlocal.f90 index f65fb2193..ac914104c 100644 --- a/src/plastic_nonlocal.f90 +++ b/src/plastic_nonlocal.f90 @@ -225,10 +225,6 @@ contains !-------------------------------------------------------------------------------------------------- subroutine plastic_nonlocal_init - character(len=65536), dimension(0), parameter :: emptyStringArray = [character(len=65536)::] - integer, dimension(0), parameter :: emptyIntArray = [integer::] - real(pReal), dimension(0), parameter :: emptyRealArray = [real(pReal)::] - integer :: & sizeState, sizeDotState,sizeDependentState, sizeDeltaState, & maxNinstances, & diff --git a/src/plastic_phenopowerlaw.f90 b/src/plastic_phenopowerlaw.f90 index ef5bd36ef..f5c430558 100644 --- a/src/plastic_phenopowerlaw.f90 +++ b/src/plastic_phenopowerlaw.f90 @@ -113,10 +113,6 @@ subroutine plastic_phenopowerlaw_init sizeState, sizeDotState, & startIndex, endIndex - integer, dimension(0), parameter :: emptyIntArray = [integer::] - real(pReal), dimension(0), parameter :: emptyRealArray = [real(pReal)::] - character(len=65536), dimension(0), parameter :: emptyStringArray = [character(len=65536)::] - integer(kind(undefined_ID)) :: & outputID diff --git a/src/prec.f90 b/src/prec.f90 index 8fd2495ce..710400a82 100644 --- a/src/prec.f90 +++ b/src/prec.f90 @@ -79,6 +79,10 @@ module prec real(pReal), private, parameter :: PREAL_EPSILON = epsilon(0.0_pReal) !< minimum positive number such that 1.0 + EPSILON /= 1.0. real(pReal), private, parameter :: PREAL_MIN = tiny(0.0_pReal) !< smallest normalized floating point number + integer, dimension(0), parameter, public :: emptyIntArray = [integer::] + real(pReal), dimension(0), parameter, public :: emptyRealArray = [real(pReal)::] + character(len=65536), dimension(0), parameter, public :: emptyStringArray = [character(len=65536)::] + private :: & unitTest diff --git a/src/source_damage_anisoBrittle.f90 b/src/source_damage_anisoBrittle.f90 index 2211ffdd2..240e3ae48 100644 --- a/src/source_damage_anisoBrittle.f90 +++ b/src/source_damage_anisoBrittle.f90 @@ -69,8 +69,6 @@ subroutine source_damage_anisoBrittle_init integer :: Ninstance,phase,instance,source,sourceOffset integer :: NofMyPhase,p ,i - integer, dimension(0), parameter :: emptyIntArray = [integer::] - character(len=65536), dimension(0), parameter :: emptyStringArray = [character(len=65536)::] integer(kind(undefined_ID)) :: & outputID diff --git a/src/source_damage_anisoDuctile.f90 b/src/source_damage_anisoDuctile.f90 index b6a4942c1..6101eb214 100644 --- a/src/source_damage_anisoDuctile.f90 +++ b/src/source_damage_anisoDuctile.f90 @@ -62,8 +62,6 @@ subroutine source_damage_anisoDuctile_init integer :: Ninstance,phase,instance,source,sourceOffset integer :: NofMyPhase,p ,i - integer, dimension(0), parameter :: emptyIntArray = [integer::] - character(len=65536), dimension(0), parameter :: emptyStringArray = [character(len=65536)::] integer(kind(undefined_ID)) :: & outputID diff --git a/src/source_damage_isoBrittle.f90 b/src/source_damage_isoBrittle.f90 index e10177502..609b7a7e0 100644 --- a/src/source_damage_isoBrittle.f90 +++ b/src/source_damage_isoBrittle.f90 @@ -56,7 +56,6 @@ subroutine source_damage_isoBrittle_init integer :: Ninstance,phase,instance,source,sourceOffset integer :: NofMyPhase,p,i - character(len=65536), dimension(0), parameter :: emptyStringArray = [character(len=65536)::] integer(kind(undefined_ID)) :: & outputID diff --git a/src/source_damage_isoDuctile.f90 b/src/source_damage_isoDuctile.f90 index fca804c84..9212e771f 100644 --- a/src/source_damage_isoDuctile.f90 +++ b/src/source_damage_isoDuctile.f90 @@ -53,7 +53,6 @@ subroutine source_damage_isoDuctile_init integer :: Ninstance,phase,instance,source,sourceOffset integer :: NofMyPhase,p,i - character(len=65536), dimension(0), parameter :: emptyStringArray = [character(len=65536)::] integer(kind(undefined_ID)) :: & outputID diff --git a/src/thermal_adiabatic.f90 b/src/thermal_adiabatic.f90 index 3bbc5b613..985c0fffb 100644 --- a/src/thermal_adiabatic.f90 +++ b/src/thermal_adiabatic.f90 @@ -47,7 +47,6 @@ contains subroutine thermal_adiabatic_init integer :: maxNinstance,o,h,NofMyHomog - character(len=65536), dimension(0), parameter :: emptyStringArray = [character(len=65536)::] character(len=65536), dimension(:), allocatable :: outputs write(6,'(/,a)') ' <<<+- thermal_'//THERMAL_ADIABATIC_label//' init -+>>>'; flush(6) diff --git a/src/thermal_conduction.f90 b/src/thermal_conduction.f90 index 27adc39aa..7b09864cf 100644 --- a/src/thermal_conduction.f90 +++ b/src/thermal_conduction.f90 @@ -49,7 +49,6 @@ subroutine thermal_conduction_init integer :: maxNinstance,o,NofMyHomog,h - character(len=65536), dimension(0), parameter :: emptyStringArray = [character(len=65536)::] character(len=65536), dimension(:), allocatable :: outputs write(6,'(/,a)') ' <<<+- thermal_'//THERMAL_CONDUCTION_label//' init -+>>>'; flush(6) From 747a340599d7f0b12dbc57ee5f28c92fe9d00aad Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 11:46:12 +0100 Subject: [PATCH 113/223] unified string length --- src/material.f90 | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/src/material.f90 b/src/material.f90 index 9ebd00397..b8f7e9baf 100644 --- a/src/material.f90 +++ b/src/material.f90 @@ -391,8 +391,8 @@ end subroutine material_init !-------------------------------------------------------------------------------------------------- subroutine material_parseHomogenization - integer :: h - character(len=65536) :: tag + integer :: h + character(len=pStringLen) :: tag allocate(homogenization_type(size(config_homogenization)), source=HOMOGENIZATION_undefined_ID) allocate(thermal_type(size(config_homogenization)), source=THERMAL_isothermal_ID) @@ -482,11 +482,11 @@ end subroutine material_parseHomogenization !-------------------------------------------------------------------------------------------------- subroutine material_parseMicrostructure - character(len=65536), dimension(:), allocatable :: & + character(len=pStringLen), dimension(:), allocatable :: & strings integer, allocatable, dimension(:) :: chunkPos integer :: e, m, c, i - character(len=65536) :: & + character(len=pStringLen) :: & tag allocate(microstructure_Nconstituents(size(config_microstructure)), source=0) @@ -540,7 +540,7 @@ end subroutine material_parseMicrostructure subroutine material_parsePhase integer :: sourceCtr, kinematicsCtr, stiffDegradationCtr, p - character(len=65536), dimension(:), allocatable :: str + character(len=pStringLen), dimension(:), allocatable :: str allocate(phase_elasticity(size(config_phase)),source=ELASTICITY_undefined_ID) @@ -594,9 +594,9 @@ subroutine material_parsePhase #if defined(__GFORTRAN__) || defined(__PGI) str = ['GfortranBug86277'] str = config_phase(p)%getStrings('(source)',defaultVal=str) - if (str(1) == 'GfortranBug86277') str = [character(len=65536)::] + if (str(1) == 'GfortranBug86277') str = [character(len=pStringLen)::] #else - str = config_phase(p)%getStrings('(source)',defaultVal=[character(len=65536)::]) + str = config_phase(p)%getStrings('(source)',defaultVal=[character(len=pStringLen)::]) #endif do sourceCtr = 1, size(str) select case (trim(str(sourceCtr))) @@ -618,9 +618,9 @@ subroutine material_parsePhase #if defined(__GFORTRAN__) || defined(__PGI) str = ['GfortranBug86277'] str = config_phase(p)%getStrings('(kinematics)',defaultVal=str) - if (str(1) == 'GfortranBug86277') str = [character(len=65536)::] + if (str(1) == 'GfortranBug86277') str = [character(len=pStringLen)::] #else - str = config_phase(p)%getStrings('(kinematics)',defaultVal=[character(len=65536)::]) + str = config_phase(p)%getStrings('(kinematics)',defaultVal=[character(len=pStringLen)::]) #endif do kinematicsCtr = 1, size(str) select case (trim(str(kinematicsCtr))) @@ -635,9 +635,9 @@ subroutine material_parsePhase #if defined(__GFORTRAN__) || defined(__PGI) str = ['GfortranBug86277'] str = config_phase(p)%getStrings('(stiffness_degradation)',defaultVal=str) - if (str(1) == 'GfortranBug86277') str = [character(len=65536)::] + if (str(1) == 'GfortranBug86277') str = [character(len=pStringLen)::] #else - str = config_phase(p)%getStrings('(stiffness_degradation)',defaultVal=[character(len=65536)::]) + str = config_phase(p)%getStrings('(stiffness_degradation)',defaultVal=[character(len=pStringLen)::]) #endif do stiffDegradationCtr = 1, size(str) select case (trim(str(stiffDegradationCtr))) @@ -663,8 +663,8 @@ end subroutine material_parsePhase !-------------------------------------------------------------------------------------------------- subroutine material_parseTexture - integer :: j, t - character(len=65536), dimension(:), allocatable :: strings ! Values for given key in material config + integer :: j,t + character(len=pStringLen), dimension(:), allocatable :: strings ! Values for given key in material config integer, dimension(:), allocatable :: chunkPos real(pReal), dimension(3,3) :: transformation ! maps texture to microstructure coordinate system real(pReal), dimension(3) :: Eulers ! Euler angles in degrees from file @@ -700,29 +700,27 @@ subroutine material_parseTexture do j = 1, 3 ! look for "x", "y", and "z" entries select case (strings(j)) case('x', '+x') - transformation(j,1:3) = [ 1.0_pReal, 0.0_pReal, 0.0_pReal] ! original axis is now +x-axis + transformation(j,1:3) = [ 1.0_pReal, 0.0_pReal, 0.0_pReal] ! original axis is now +x-axis case('-x') - transformation(j,1:3) = [-1.0_pReal, 0.0_pReal, 0.0_pReal] ! original axis is now -x-axis + transformation(j,1:3) = [-1.0_pReal, 0.0_pReal, 0.0_pReal] ! original axis is now -x-axis case('y', '+y') - transformation(j,1:3) = [ 0.0_pReal, 1.0_pReal, 0.0_pReal] ! original axis is now +y-axis + transformation(j,1:3) = [ 0.0_pReal, 1.0_pReal, 0.0_pReal] ! original axis is now +y-axis case('-y') - transformation(j,1:3) = [ 0.0_pReal,-1.0_pReal, 0.0_pReal] ! original axis is now -y-axis + transformation(j,1:3) = [ 0.0_pReal,-1.0_pReal, 0.0_pReal] ! original axis is now -y-axis case('z', '+z') - transformation(j,1:3) = [ 0.0_pReal, 0.0_pReal, 1.0_pReal] ! original axis is now +z-axis + transformation(j,1:3) = [ 0.0_pReal, 0.0_pReal, 1.0_pReal] ! original axis is now +z-axis case('-z') - transformation(j,1:3) = [ 0.0_pReal, 0.0_pReal,-1.0_pReal] ! original axis is now -z-axis + transformation(j,1:3) = [ 0.0_pReal, 0.0_pReal,-1.0_pReal] ! original axis is now -z-axis case default call IO_error(157,t) end select enddo - if(dNeq(math_det33(transformation),1.0_pReal)) call IO_error(157,t) call transformation_%fromMatrix(transformation) texture_orientation(t) = texture_orientation(t) * transformation_ endif enddo - end subroutine material_parseTexture From 27483bafbc2daf993e8a61912856502a1a083799 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 11:52:33 +0100 Subject: [PATCH 114/223] non-existing value evaluates to 0 or 1 --- src/future.f90 | 2 +- src/rotations.f90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/future.f90 b/src/future.f90 index 354a522e4..b7eb3fec9 100644 --- a/src/future.f90 +++ b/src/future.f90 @@ -10,7 +10,7 @@ module future contains -#if defined(__GFORTRAN__) && __GNUC__<9 || __INTEL_COMPILER<1800 +#if defined(__GFORTRAN__) && __GNUC__<9 || defined(__INTEL_COMPILER) && INTEL_COMPILER<1800 !-------------------------------------------------------------------------------------------------- !> @brief substitute for the findloc intrinsic (only for integer, dimension(:) at the moment) !-------------------------------------------------------------------------------------------------- diff --git a/src/rotations.f90 b/src/rotations.f90 index a4a0bac88..5deb02a20 100644 --- a/src/rotations.f90 +++ b/src/rotations.f90 @@ -596,7 +596,7 @@ function om2ax(om) result(ax) else call dgeev('N','V',3,om_,3,Wr,Wi,devNull,3,VR,3,work,size(work,1),ierr) if (ierr /= 0) call IO_error(0,ext_msg='Error in om2ax: DGEEV return not zero') -#if defined(__GFORTRAN__) && __GNUC__ < 9 || __INTEL_COMPILER < 1800 +#if defined(__GFORTRAN__) && __GNUC__<9 || defined(__INTEL_COMPILER) && INTEL_COMPILER<1800 i = maxloc(merge(1,0,cEq(cmplx(Wr,Wi,pReal),cmplx(1.0_pReal,0.0_pReal,pReal),tol=1.0e-14_pReal)),dim=1) #else i = findloc(cEq(cmplx(Wr,Wi,pReal),cmplx(1.0_pReal,0.0_pReal,pReal),tol=1.0e-14_pReal),.true.,dim=1) !find eigenvalue (1,0) From 127678e2e140f0e6f0e01fa9684ef10fb4fc28ae Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 12:28:24 +0100 Subject: [PATCH 115/223] use default string length --- src/plastic_nonlocal.f90 | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/plastic_nonlocal.f90 b/src/plastic_nonlocal.f90 index ac914104c..3977f1fa7 100644 --- a/src/plastic_nonlocal.f90 +++ b/src/plastic_nonlocal.f90 @@ -28,9 +28,6 @@ module plastic_nonlocal real(pReal), parameter :: & KB = 1.38e-23_pReal !< Physical parameter, Boltzmann constant in J/Kelvin - character(len=64), dimension(:,:), allocatable :: & - plastic_nonlocal_output !< name of each post result output - ! storage order of dislocation types integer, dimension(8), parameter :: & sgl = [1,2,3,4,5,6,7,8] !< signed (single) @@ -202,9 +199,6 @@ module plastic_nonlocal type(tNonlocalMicrostructure), dimension(:), allocatable :: microstructure - integer(kind(undefined_ID)), dimension(:,:), allocatable :: & - plastic_nonlocal_outputID !< ID of each post result output - public :: & plastic_nonlocal_init, & plastic_nonlocal_dependentState, & @@ -237,10 +231,10 @@ subroutine plastic_nonlocal_init integer(kind(undefined_ID)) :: & outputID - character(len=512) :: & + character(len=pStringLen) :: & extmsg = '', & structure - character(len=65536), dimension(:), allocatable :: outputs + character(len=pStringLen), dimension(:), allocatable :: outputs integer :: NofMyPhase write(6,'(/,a)') ' <<<+- constitutive_'//PLASTICITY_NONLOCAL_label//' init -+>>>' @@ -261,9 +255,6 @@ subroutine plastic_nonlocal_init allocate(deltaState(maxNinstances)) allocate(microstructure(maxNinstances)) - allocate(plastic_nonlocal_output(maxval(phase_Noutput), maxNinstances)) - plastic_nonlocal_output = '' - allocate(plastic_nonlocal_outputID(maxval(phase_Noutput), maxNinstances), source=undefined_ID) allocate(totalNslip(maxNinstances), source=0) @@ -489,7 +480,6 @@ subroutine plastic_nonlocal_init end select if (outputID /= undefined_ID) then - plastic_nonlocal_output(i,phase_plasticityInstance(p)) = outputs(i) prm%outputID = [prm%outputID , outputID] endif @@ -511,8 +501,8 @@ subroutine plastic_nonlocal_init 'maxDipoleHeightEdge ','maxDipoleHeightScrew' ]) * prm%totalNslip !< other dependent state variables that are not updated by microstructure sizeDeltaState = sizeDotState - call material_allocatePlasticState(p,NofMyPhase,sizeState,sizeDotState,sizeDeltaState, & - prm%totalNslip,0,0) + call material_allocatePlasticState(p,NofMyPhase,sizeState,sizeDotState,sizeDeltaState) + plasticState(p)%nonlocal = .true. plasticState(p)%offsetDeltaState = 0 ! ToDo: state structure does not follow convention From 1037aa98d3358ab2787b26c7e019c7cbdb171e24 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 12:28:41 +0100 Subject: [PATCH 116/223] DADF5 is now the only output --- .gitlab-ci.yml | 5 ----- CONFIG | 4 ---- .../mods_MarcMentat/2018.1/Marc_tools/include_linux64 | 11 +++-------- .../mods_MarcMentat/2018/Marc_tools/include_linux64 | 11 +++-------- .../mods_MarcMentat/2019/Marc_tools/include_linux64 | 8 +++----- 5 files changed, 9 insertions(+), 30 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 36d723db4..7958db9b8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -391,7 +391,6 @@ Marc_compileIfort: stage: compileMarc script: - module load $IntelMarc $HDF5Marc $MSC - - export DAMASK_HDF5=ON - Marc_compileIfort/test.py except: - master @@ -402,7 +401,6 @@ Hex_elastic: stage: marc script: - module load $IntelMarc $HDF5Marc $MSC - - export DAMASK_HDF5=ON - Hex_elastic/test.py except: - master @@ -412,7 +410,6 @@ CubicFCC_elastic: stage: marc script: - module load $IntelMarc $HDF5Marc $MSC - - export DAMASK_HDF5=ON - CubicFCC_elastic/test.py except: - master @@ -422,7 +419,6 @@ CubicBCC_elastic: stage: marc script: - module load $IntelMarc $HDF5Marc $MSC - - export DAMASK_HDF5=ON - CubicBCC_elastic/test.py except: - master @@ -432,7 +428,6 @@ J2_plasticBehavior: stage: marc script: - module load $IntelMarc $HDF5Marc $MSC - - export DAMASK_HDF5=ON - J2_plasticBehavior/test.py except: - master diff --git a/CONFIG b/CONFIG index 53e87b647..8da4d5b96 100644 --- a/CONFIG +++ b/CONFIG @@ -1,11 +1,7 @@ # "set"-syntax needed only for tcsh (but works with bash and zsh) -# DAMASK_ROOT will be expanded - set DAMASK_NUM_THREADS = 4 set MSC_ROOT = /opt/msc set MARC_VERSION = 2019 set ABAQUS_VERSION = 2019 - -set DAMASK_HDF5 = ON diff --git a/installation/mods_MarcMentat/2018.1/Marc_tools/include_linux64 b/installation/mods_MarcMentat/2018.1/Marc_tools/include_linux64 index 10a796e47..8adabaff1 100644 --- a/installation/mods_MarcMentat/2018.1/Marc_tools/include_linux64 +++ b/installation/mods_MarcMentat/2018.1/Marc_tools/include_linux64 @@ -99,14 +99,9 @@ else fi # DAMASK uses the HDF5 compiler wrapper around the Intel compiler -if test "$DAMASK_HDF5" = "ON";then - H5FC="$(h5fc -shlib -show)" - HDF5_LIB=${H5FC//ifort/} - FCOMP="$H5FC -DDAMASK_HDF5" - echo $FCOMP -else - FCOMP=ifort -fi +H5FC="$(h5fc -shlib -show)" +HDF5_LIB=${H5FC//ifort/} +FCOMP="$H5FC -DDAMASK_HDF5" # AEM if test "$MARCDLLOUTDIR" = ""; then diff --git a/installation/mods_MarcMentat/2018/Marc_tools/include_linux64 b/installation/mods_MarcMentat/2018/Marc_tools/include_linux64 index 694dccee3..c99313a30 100644 --- a/installation/mods_MarcMentat/2018/Marc_tools/include_linux64 +++ b/installation/mods_MarcMentat/2018/Marc_tools/include_linux64 @@ -99,14 +99,9 @@ else fi # DAMASK uses the HDF5 compiler wrapper around the Intel compiler -if test "$DAMASK_HDF5" = "ON";then - H5FC="$(h5fc -shlib -show)" - HDF5_LIB=${H5FC//ifort/} - FCOMP="$H5FC -DDAMASK_HDF5" - echo $FCOMP -else - FCOMP=ifort -fi +H5FC="$(h5fc -shlib -show)" +HDF5_LIB=${H5FC//ifort/} +FCOMP="$H5FC -DDAMASK_HDF5" # AEM if test "$MARCDLLOUTDIR" = ""; then diff --git a/installation/mods_MarcMentat/2019/Marc_tools/include_linux64 b/installation/mods_MarcMentat/2019/Marc_tools/include_linux64 index 6d630bd1d..2dba03961 100644 --- a/installation/mods_MarcMentat/2019/Marc_tools/include_linux64 +++ b/installation/mods_MarcMentat/2019/Marc_tools/include_linux64 @@ -100,11 +100,9 @@ else fi # DAMASK uses the HDF5 compiler wrapper around the Intel compiler -if test "$DAMASK_HDF5" = "ON";then - H5FC="$(h5fc -shlib -show)" - HDF5_LIB=${H5FC//ifort/} - FCOMP="$H5FC -DDAMASK_HDF5" -fi +H5FC="$(h5fc -shlib -show)" +HDF5_LIB=${H5FC//ifort/} +FCOMP="$H5FC -DDAMASK_HDF5" # AEM if test "$MARCDLLOUTDIR" = ""; then From 34af10fac1eacf1a8075f5fdcedc2c77b2348266 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 12:37:02 +0100 Subject: [PATCH 117/223] using default string length --- src/IO.f90 | 24 ++++++++++++------------ src/crystallite.f90 | 2 +- src/damage_local.f90 | 2 +- src/damage_nonlocal.f90 | 2 +- src/grid/DAMASK_grid.f90 | 2 +- src/homogenization_mech_RGC.f90 | 2 +- src/homogenization_mech_isostrain.f90 | 2 +- src/lattice.f90 | 2 +- src/mesh/DAMASK_FEM.f90 | 2 +- src/prec.f90 | 6 +++--- src/source_damage_anisoBrittle.f90 | 2 +- src/source_damage_anisoDuctile.f90 | 2 +- src/source_damage_isoBrittle.f90 | 2 +- src/source_damage_isoDuctile.f90 | 2 +- 14 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/IO.f90 b/src/IO.f90 index cffcb6471..c07809d51 100644 --- a/src/IO.f90 +++ b/src/IO.f90 @@ -243,12 +243,12 @@ subroutine IO_open_inputFile(fileUnit) integer, allocatable, dimension(:) :: chunkPos - character(len=65536) :: line,fname + character(len=pStringLen :: line,fname logical :: createSuccess,fexist do - read(unit2,'(A65536)',END=220) line + read(unit2,'(A256)',END=220) line chunkPos = IO_stringPos(line) if (IO_lc(IO_StringValue(line,chunkPos,1))=='*include') then @@ -884,7 +884,7 @@ end subroutine IO_warning !-------------------------------------------------------------------------------------------------- function IO_read(fileUnit) result(line) - integer, intent(in) :: fileUnit !< file unit + integer, intent(in) :: fileUnit !< file unit character(len=pStringLen) :: line @@ -924,7 +924,7 @@ integer function IO_countDataLines(fileUnit) integer, allocatable, dimension(:) :: chunkPos - character(len=65536) :: line, & + character(len=pStringLen) :: line, & tmp IO_countDataLines = 0 @@ -956,7 +956,7 @@ integer function IO_countNumericalDataLines(fileUnit) integer, allocatable, dimension(:) :: chunkPos - character(len=65536) :: line, & + character(len=pStringLen) :: line, & tmp IO_countNumericalDataLines = 0 @@ -991,7 +991,7 @@ integer function IO_countContinuousIntValues(fileUnit) integer :: l,c #endif integer, allocatable, dimension(:) :: chunkPos - character(len=65536) :: line + character(len=pString) :: line IO_countContinuousIntValues = 0 line = '' @@ -1048,21 +1048,21 @@ function IO_continuousIntValues(fileUnit,maxN,lookupName,lookupMap,lookupMaxN) integer, intent(in) :: fileUnit, & lookupMaxN integer, dimension(:,:), intent(in) :: lookupMap - character(len=64), dimension(:), intent(in) :: lookupName + character(len=*), dimension(:), intent(in) :: lookupName integer :: i,first,last #ifdef Abaqus integer :: j,l,c #endif integer, allocatable, dimension(:) :: chunkPos - character(len=65536) line - logical rangeGeneration + character(len=pStringLen) :: line + logical :: rangeGeneration IO_continuousIntValues = 0 rangeGeneration = .false. #if defined(Marc4DAMASK) do - read(fileUnit,'(A65536)',end=100) line + read(fileUnit,'(A256)',end=100) line chunkPos = IO_stringPos(line) if (chunkPos(1) < 1) then ! empty line exit @@ -1103,14 +1103,14 @@ function IO_continuousIntValues(fileUnit,maxN,lookupName,lookupMap,lookupMaxN) !-------------------------------------------------------------------------------------------------- ! check if the element values in the elset are auto generated backspace(fileUnit) - read(fileUnit,'(A65536)',end=100) line + read(fileUnit,'(A256)',end=100) line chunkPos = IO_stringPos(line) do i = 1,chunkPos(1) if (IO_lc(IO_stringValue(line,chunkPos,i)) == 'generate') rangeGeneration = .true. enddo do l = 1,c - read(fileUnit,'(A65536)',end=100) line + read(fileUnit,'(A256)',end=100) line chunkPos = IO_stringPos(line) if (verify(IO_stringValue(line,chunkPos,1),'0123456789') > 0) then ! a non-int, i.e. set names follow on this line do i = 1,chunkPos(1) ! loop over set names in line diff --git a/src/crystallite.f90 b/src/crystallite.f90 index 84d5dd17d..d33e774e9 100644 --- a/src/crystallite.f90 +++ b/src/crystallite.f90 @@ -77,7 +77,7 @@ module crystallite crystallite_localPlasticity !< indicates this grain to have purely local constitutive law type :: tOutput !< new requested output (per phase) - character(len=65536), allocatable, dimension(:) :: & + character(len=pStringLen), allocatable, dimension(:) :: & label end type tOutput type(tOutput), allocatable, dimension(:) :: output_constituent diff --git a/src/damage_local.f90 b/src/damage_local.f90 index 0874b5aee..6cb45a391 100644 --- a/src/damage_local.f90 +++ b/src/damage_local.f90 @@ -44,7 +44,7 @@ contains subroutine damage_local_init integer :: maxNinstance,o,NofMyHomog,h - character(len=65536), dimension(:), allocatable :: outputs + character(len=pStringLen), dimension(:), allocatable :: outputs write(6,'(/,a)') ' <<<+- damage_'//DAMAGE_local_label//' init -+>>>'; flush(6) diff --git a/src/damage_nonlocal.f90 b/src/damage_nonlocal.f90 index 47355a479..17bdecaca 100644 --- a/src/damage_nonlocal.f90 +++ b/src/damage_nonlocal.f90 @@ -49,7 +49,7 @@ contains subroutine damage_nonlocal_init integer :: maxNinstance,o,NofMyHomog,h - character(len=65536), dimension(:), allocatable :: outputs + character(len=pStringLen), dimension(:), allocatable :: outputs write(6,'(/,a)') ' <<<+- damage_'//DAMAGE_nonlocal_label//' init -+>>>'; flush(6) diff --git a/src/grid/DAMASK_grid.f90 b/src/grid/DAMASK_grid.f90 index b17e490ea..b324a5afc 100644 --- a/src/grid/DAMASK_grid.f90 +++ b/src/grid/DAMASK_grid.f90 @@ -36,7 +36,7 @@ program DAMASK_spectral N_t = 0, & !< # of time indicators found in load case file N_n = 0, & !< # of increment specifiers found in load case file N_def = 0 !< # of rate of deformation specifiers found in load case file - character(len=65536) :: & + character(len=pStringLen) :: & line !-------------------------------------------------------------------------------------------------- diff --git a/src/homogenization_mech_RGC.f90 b/src/homogenization_mech_RGC.f90 index d2e12c072..c493c4190 100644 --- a/src/homogenization_mech_RGC.f90 +++ b/src/homogenization_mech_RGC.f90 @@ -77,7 +77,7 @@ module subroutine mech_RGC_init integer(kind(undefined_ID)) :: & outputID - character(len=65536), dimension(:), allocatable :: & + character(len=pStringLen), dimension(:), allocatable :: & outputs write(6,'(/,a)') ' <<<+- homogenization_'//HOMOGENIZATION_RGC_label//' init -+>>>' diff --git a/src/homogenization_mech_isostrain.f90 b/src/homogenization_mech_isostrain.f90 index cdc078925..9345d1eda 100644 --- a/src/homogenization_mech_isostrain.f90 +++ b/src/homogenization_mech_isostrain.f90 @@ -33,7 +33,7 @@ module subroutine mech_isostrain_init Ninstance, & h, & NofMyHomog - character(len=65536) :: & + character(len=pStringLen) :: & tag = '' write(6,'(/,a)') ' <<<+- homogenization_'//HOMOGENIZATION_ISOSTRAIN_label//' init -+>>>' diff --git a/src/lattice.f90 b/src/lattice.f90 index fada61392..025a1f8a5 100644 --- a/src/lattice.f90 +++ b/src/lattice.f90 @@ -492,7 +492,7 @@ contains subroutine lattice_init integer :: Nphases - character(len=65536) :: & + character(len=pStringLen) :: & tag = '' integer :: i,p real(pReal), dimension(:), allocatable :: & diff --git a/src/mesh/DAMASK_FEM.f90 b/src/mesh/DAMASK_FEM.f90 index cd0bcacb1..9b9b95b91 100644 --- a/src/mesh/DAMASK_FEM.f90 +++ b/src/mesh/DAMASK_FEM.f90 @@ -27,7 +27,7 @@ program DAMASK_FEM integer, allocatable, dimension(:) :: chunkPos ! this is longer than needed for geometry parsing integer :: & N_def = 0 !< # of rate of deformation specifiers found in load case file - character(len=65536) :: & + character(len=pStringLen) :: & line !-------------------------------------------------------------------------------------------------- diff --git a/src/prec.f90 b/src/prec.f90 index 710400a82..2f0f20a00 100644 --- a/src/prec.f90 +++ b/src/prec.f90 @@ -79,9 +79,9 @@ module prec real(pReal), private, parameter :: PREAL_EPSILON = epsilon(0.0_pReal) !< minimum positive number such that 1.0 + EPSILON /= 1.0. real(pReal), private, parameter :: PREAL_MIN = tiny(0.0_pReal) !< smallest normalized floating point number - integer, dimension(0), parameter, public :: emptyIntArray = [integer::] - real(pReal), dimension(0), parameter, public :: emptyRealArray = [real(pReal)::] - character(len=65536), dimension(0), parameter, public :: emptyStringArray = [character(len=65536)::] + integer, dimension(0), parameter, public :: emptyIntArray = [integer::] + real(pReal), dimension(0), parameter, public :: emptyRealArray = [real(pReal)::] + character(len=pStringLen), dimension(0), parameter, public :: emptyStringArray = [character(len=pStringLen)::] private :: & unitTest diff --git a/src/source_damage_anisoBrittle.f90 b/src/source_damage_anisoBrittle.f90 index 240e3ae48..e5ed05799 100644 --- a/src/source_damage_anisoBrittle.f90 +++ b/src/source_damage_anisoBrittle.f90 @@ -74,7 +74,7 @@ subroutine source_damage_anisoBrittle_init character(len=pStringLen) :: & extmsg = '' - character(len=65536), dimension(:), allocatable :: & + character(len=pStringLen), dimension(:), allocatable :: & outputs write(6,'(/,a)') ' <<<+- source_'//SOURCE_DAMAGE_ANISOBRITTLE_LABEL//' init -+>>>'; flush(6) diff --git a/src/source_damage_anisoDuctile.f90 b/src/source_damage_anisoDuctile.f90 index 6101eb214..fef897914 100644 --- a/src/source_damage_anisoDuctile.f90 +++ b/src/source_damage_anisoDuctile.f90 @@ -67,7 +67,7 @@ subroutine source_damage_anisoDuctile_init character(len=pStringLen) :: & extmsg = '' - character(len=65536), dimension(:), allocatable :: & + character(len=pStringLen), dimension(:), allocatable :: & outputs write(6,'(/,a)') ' <<<+- source_'//SOURCE_DAMAGE_ANISODUCTILE_LABEL//' init -+>>>'; flush(6) diff --git a/src/source_damage_isoBrittle.f90 b/src/source_damage_isoBrittle.f90 index 609b7a7e0..53c0b77a7 100644 --- a/src/source_damage_isoBrittle.f90 +++ b/src/source_damage_isoBrittle.f90 @@ -61,7 +61,7 @@ subroutine source_damage_isoBrittle_init character(len=pStringLen) :: & extmsg = '' - character(len=65536), dimension(:), allocatable :: & + character(len=pStringLen), dimension(:), allocatable :: & outputs write(6,'(/,a)') ' <<<+- source_'//SOURCE_DAMAGE_ISOBRITTLE_LABEL//' init -+>>>'; flush(6) diff --git a/src/source_damage_isoDuctile.f90 b/src/source_damage_isoDuctile.f90 index 9212e771f..6ee588d0c 100644 --- a/src/source_damage_isoDuctile.f90 +++ b/src/source_damage_isoDuctile.f90 @@ -58,7 +58,7 @@ subroutine source_damage_isoDuctile_init character(len=pStringLen) :: & extmsg = '' - character(len=65536), dimension(:), allocatable :: & + character(len=pStringLen), dimension(:), allocatable :: & outputs write(6,'(/,a)') ' <<<+- source_'//SOURCE_DAMAGE_ISODUCTILE_LABEL//' init -+>>>' From 0d975e70233640d6d79971265628e999614171e2 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 12:40:02 +0100 Subject: [PATCH 118/223] polishing - default string length - Nslip/Ntwin/Ntrans not stored in state anymore --- src/IO.f90 | 2 +- src/material.f90 | 8 ++------ src/plastic_disloUCLA.f90 | 5 ++--- src/plastic_dislotwin.f90 | 5 ++--- src/plastic_isotropic.f90 | 5 ++--- src/plastic_kinematichardening.f90 | 5 ++--- src/plastic_none.f90 | 4 ++-- src/plastic_phenopowerlaw.f90 | 5 ++--- src/thermal_adiabatic.f90 | 2 +- src/thermal_conduction.f90 | 2 +- 10 files changed, 17 insertions(+), 26 deletions(-) diff --git a/src/IO.f90 b/src/IO.f90 index c07809d51..8926e1e21 100644 --- a/src/IO.f90 +++ b/src/IO.f90 @@ -991,7 +991,7 @@ integer function IO_countContinuousIntValues(fileUnit) integer :: l,c #endif integer, allocatable, dimension(:) :: chunkPos - character(len=pString) :: line + character(len=pStringLen) :: line IO_countContinuousIntValues = 0 line = '' diff --git a/src/material.f90 b/src/material.f90 index b8f7e9baf..a4494ed6e 100644 --- a/src/material.f90 +++ b/src/material.f90 @@ -728,18 +728,14 @@ end subroutine material_parseTexture !> @brief allocates the plastic state of a phase !-------------------------------------------------------------------------------------------------- subroutine material_allocatePlasticState(phase,NofMyPhase,& - sizeState,sizeDotState,sizeDeltaState,& - Nslip,Ntwin,Ntrans) + sizeState,sizeDotState,sizeDeltaState) integer, intent(in) :: & phase, & NofMyPhase, & sizeState, & sizeDotState, & - sizeDeltaState, & - Nslip, & - Ntwin, & - Ntrans + sizeDeltaState plasticState(phase)%sizeState = sizeState plasticState(phase)%sizeDotState = sizeDotState diff --git a/src/plastic_disloUCLA.f90 b/src/plastic_disloUCLA.f90 index 8610b6bc2..d1291d853 100644 --- a/src/plastic_disloUCLA.f90 +++ b/src/plastic_disloUCLA.f90 @@ -126,7 +126,7 @@ subroutine plastic_disloUCLA_init() character(len=pStringLen) :: & extmsg = '' - character(len=65536), dimension(:), allocatable :: & + character(len=pStringLen), dimension(:), allocatable :: & outputs write(6,'(/,a)') ' <<<+- plastic_'//PLASTICITY_DISLOUCLA_label//' init -+>>>' @@ -286,8 +286,7 @@ subroutine plastic_disloUCLA_init() sizeDotState = size(['rho_mob ','rho_dip ','gamma_sl']) * prm%sum_N_sl sizeState = sizeDotState - call material_allocatePlasticState(p,NipcMyPhase,sizeState,sizeDotState,0, & - prm%sum_N_sl,0,0) + call material_allocatePlasticState(p,NipcMyPhase,sizeState,sizeDotState,0) !-------------------------------------------------------------------------------------------------- ! locally defined state aliases and initialization of state0 and aTolState diff --git a/src/plastic_dislotwin.f90 b/src/plastic_dislotwin.f90 index 8c19c7d83..ae89dcc1c 100644 --- a/src/plastic_dislotwin.f90 +++ b/src/plastic_dislotwin.f90 @@ -185,7 +185,7 @@ subroutine plastic_dislotwin_init character(len=pStringLen) :: & extmsg = '' - character(len=65536), dimension(:), allocatable :: & + character(len=pStringLen), dimension(:), allocatable :: & outputs write(6,'(/,a)') ' <<<+- constitutive_'//PLASTICITY_DISLOTWIN_label//' init -+>>>' @@ -506,8 +506,7 @@ subroutine plastic_dislotwin_init + size(['f_tr']) * prm%sum_N_tr sizeState = sizeDotState - call material_allocatePlasticState(p,NipcMyPhase,sizeState,sizeDotState,0, & - prm%sum_N_sl,prm%sum_N_tw,prm%sum_N_tr) + call material_allocatePlasticState(p,NipcMyPhase,sizeState,sizeDotState,0) !-------------------------------------------------------------------------------------------------- ! locally defined state aliases and initialization of state0 and aTolState diff --git a/src/plastic_isotropic.f90 b/src/plastic_isotropic.f90 index 38166df4a..96d70be4a 100644 --- a/src/plastic_isotropic.f90 +++ b/src/plastic_isotropic.f90 @@ -90,7 +90,7 @@ subroutine plastic_isotropic_init character(len=pStringLen) :: & extmsg = '' - character(len=65536), dimension(:), allocatable :: & + character(len=pStringLen), dimension(:), allocatable :: & outputs write(6,'(/,a)') ' <<<+- plastic_'//PLASTICITY_ISOTROPIC_label//' init -+>>>' @@ -179,8 +179,7 @@ subroutine plastic_isotropic_init sizeDotState = size(['xi ','accumulated_shear']) sizeState = sizeDotState - call material_allocatePlasticState(p,NipcMyPhase,sizeState,sizeDotState,0, & - 1,0,0) + call material_allocatePlasticState(p,NipcMyPhase,sizeState,sizeDotState,0) !-------------------------------------------------------------------------------------------------- ! locally defined state aliases and initialization of state0 and aTolState diff --git a/src/plastic_kinematichardening.f90 b/src/plastic_kinematichardening.f90 index 9b0c41041..721073746 100644 --- a/src/plastic_kinematichardening.f90 +++ b/src/plastic_kinematichardening.f90 @@ -108,7 +108,7 @@ subroutine plastic_kinehardening_init character(len=pStringLen) :: & extmsg = '' - character(len=65536), dimension(:), allocatable :: & + character(len=pStringLen), dimension(:), allocatable :: & outputs write(6,'(/,a)') ' <<<+- plastic_'//PLASTICITY_KINEHARDENING_label//' init -+>>>' @@ -245,8 +245,7 @@ subroutine plastic_kinehardening_init sizeDeltaState = size(['sense ', 'chi0 ', 'gamma0' ]) * prm%totalNslip sizeState = sizeDotState + sizeDeltaState - call material_allocatePlasticState(p,NipcMyPhase,sizeState,sizeDotState,sizeDeltaState, & - prm%totalNslip,0,0) + call material_allocatePlasticState(p,NipcMyPhase,sizeState,sizeDotState,sizeDeltaState) !-------------------------------------------------------------------------------------------------- ! locally defined state aliases and initialization of state0 and aTolState diff --git a/src/plastic_none.f90 b/src/plastic_none.f90 index a4979bb2c..f77b19c09 100644 --- a/src/plastic_none.f90 +++ b/src/plastic_none.f90 @@ -38,8 +38,8 @@ subroutine plastic_none_init if (phase_plasticity(p) /= PLASTICITY_NONE_ID) cycle NipcMyPhase = count(material_phaseAt == p) * discretization_nIP - call material_allocatePlasticState(p,NipcMyPhase,0,0,0, & - 0,0,0) + call material_allocatePlasticState(p,NipcMyPhase,0,0,0) + enddo end subroutine plastic_none_init diff --git a/src/plastic_phenopowerlaw.f90 b/src/plastic_phenopowerlaw.f90 index f5c430558..a8e459f63 100644 --- a/src/plastic_phenopowerlaw.f90 +++ b/src/plastic_phenopowerlaw.f90 @@ -118,7 +118,7 @@ subroutine plastic_phenopowerlaw_init character(len=pStringLen) :: & extmsg = '' - character(len=65536), dimension(:), allocatable :: & + character(len=pStringLen), dimension(:), allocatable :: & outputs write(6,'(/,a)') ' <<<+- plastic_'//PLASTICITY_PHENOPOWERLAW_label//' init -+>>>' @@ -304,8 +304,7 @@ subroutine plastic_phenopowerlaw_init + size(['tau_twin ','gamma_twin']) * prm%totalNtwin sizeState = sizeDotState - call material_allocatePlasticState(p,NipcMyPhase,sizeState,sizeDotState,0, & - prm%totalNslip,prm%totalNtwin,0) + call material_allocatePlasticState(p,NipcMyPhase,sizeState,sizeDotState,0) !-------------------------------------------------------------------------------------------------- ! locally defined state aliases and initialization of state0 and aTolState diff --git a/src/thermal_adiabatic.f90 b/src/thermal_adiabatic.f90 index 985c0fffb..d96604e59 100644 --- a/src/thermal_adiabatic.f90 +++ b/src/thermal_adiabatic.f90 @@ -47,7 +47,7 @@ contains subroutine thermal_adiabatic_init integer :: maxNinstance,o,h,NofMyHomog - character(len=65536), dimension(:), allocatable :: outputs + character(len=pStringLen), dimension(:), allocatable :: outputs write(6,'(/,a)') ' <<<+- thermal_'//THERMAL_ADIABATIC_label//' init -+>>>'; flush(6) diff --git a/src/thermal_conduction.f90 b/src/thermal_conduction.f90 index 7b09864cf..537b0366b 100644 --- a/src/thermal_conduction.f90 +++ b/src/thermal_conduction.f90 @@ -49,7 +49,7 @@ subroutine thermal_conduction_init integer :: maxNinstance,o,NofMyHomog,h - character(len=65536), dimension(:), allocatable :: outputs + character(len=pStringLen), dimension(:), allocatable :: outputs write(6,'(/,a)') ' <<<+- thermal_'//THERMAL_CONDUCTION_label//' init -+>>>'; flush(6) From dd318c8d1d3af3af6fd5a4c8c6d313499045d0c0 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 15:12:01 +0100 Subject: [PATCH 119/223] Table class replaces ASCIItable class --- processing/post/addCompatibilityMismatch.py | 111 +++----------------- processing/post/addIPFcolor.py | 55 +++------- processing/post/addNorm.py | 71 +++---------- processing/post/addPole.py | 57 +++------- 4 files changed, 60 insertions(+), 234 deletions(-) diff --git a/processing/post/addCompatibilityMismatch.py b/processing/post/addCompatibilityMismatch.py index 7556cb863..e4b6d940d 100755 --- a/processing/post/addCompatibilityMismatch.py +++ b/processing/post/addCompatibilityMismatch.py @@ -2,10 +2,10 @@ import os import math +import sys from optparse import OptionParser import numpy as np -import scipy.ndimage import damask @@ -13,26 +13,6 @@ import damask scriptName = os.path.splitext(os.path.basename(__file__))[0] scriptID = ' '.join([scriptName,damask.version]) -#-------------------------------------------------------------------------------------------------- -def cell2node(cellData,grid): - - nodeData = 0.0 - datalen = np.array(cellData.shape[3:]).prod() - - for i in range(datalen): - node = scipy.ndimage.convolve(cellData.reshape(tuple(grid[::-1])+(datalen,))[...,i], - np.ones((2,2,2))/8., # 2x2x2 neighborhood of cells - mode = 'wrap', - origin = -1, # offset to have cell origin as center - ) # now averaged at cell origins - node = np.append(node,node[np.newaxis,0,:,:,...],axis=0) # wrap along z - node = np.append(node,node[:,0,np.newaxis,:,...],axis=1) # wrap along y - node = np.append(node,node[:,:,0,np.newaxis,...],axis=2) # wrap along x - - nodeData = node[...,np.newaxis] if i==0 else np.concatenate((nodeData,node[...,np.newaxis]),axis=-1) - - return nodeData - #-------------------------------------------------------------------------------------------------- def deformationAvgFFT(F,grid,size,nodal=False,transformed=False): """Calculate average cell center (or nodal) deformation for deformation gradient field specified in each grid cell""" @@ -82,7 +62,7 @@ def displacementFluctFFT(F,grid,size,nodal=False,transformed=False): displacement = np.fft.irfftn(displacement_fourier,grid[::-1],axes=(0,1,2)) - return cell2node(displacement,grid) if nodal else displacement + return damask.grid_filters.cell_2_node(displacement) if nodal else displacement def volTetrahedron(coords): @@ -241,92 +221,33 @@ parser.set_defaults(pos = 'pos', ) (options,filenames) = parser.parse_args() - -# --- 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) - -# ------------------------------------------ read header ------------------------------------------ - - table.head_read() - -# ------------------------------------------ sanity checks ---------------------------------------- - - errors = [] - remarks = [] - if table.label_dimension(options.defgrad) != 9: - errors.append('deformation gradient "{}" is not a 3x3 tensor.'.format(options.defgrad)) + table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) + grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.get(options.pos)) - coordDim = table.label_dimension(options.pos) - if not 3 >= coordDim >= 1: - errors.append('coordinates "{}" need to have one, two, or three dimensions.'.format(options.pos)) - elif coordDim < 3: - remarks.append('appending {} dimension{} to coordinates "{}"...'.format(3-coordDim, - 's' if coordDim < 2 else '', - options.pos)) - - if remarks != []: damask.util.croak(remarks) - if errors != []: - damask.util.croak(errors) - table.close(dismiss=True) - continue - -# --------------- figure out size and grid --------------------------------------------------------- - - table.data_readArray([options.defgrad,options.pos]) - table.data_rewind() - - if table.data[:,9:].shape[1] < 3: - table.data = np.hstack((table.data, - np.zeros((table.data.shape[0], - 3-table.data[:,9:].shape[1]),dtype='f'))) # fill coords up to 3D with zeros - - grid,size = damask.util.coordGridAndSize(table.data[:,9:12]) N = grid.prod() - if N != len(table.data): errors.append('data count {} does not match grid {}x{}x{}.'.format(N,*grid)) - if errors != []: - damask.util.croak(errors) - table.close(dismiss = True) - continue - -# -----------------------------process data and assemble header ------------------------------------- - - F_fourier = np.fft.rfftn(table.data[:,:9].reshape(grid[2],grid[1],grid[0],3,3),axes=(0,1,2)) # perform transform only once... + F_fourier = np.fft.rfftn(table.get(options.defgrad).reshape(grid[2],grid[1],grid[0],3,3),axes=(0,1,2)) # perform transform only once... nodes = displacementFluctFFT(F_fourier,grid,size,True,transformed=True)\ + deformationAvgFFT (F_fourier,grid,size,True,transformed=True) if options.shape: - table.labels_append(['shapeMismatch({})'.format(options.defgrad)]) centres = displacementFluctFFT(F_fourier,grid,size,False,transformed=True)\ + deformationAvgFFT (F_fourier,grid,size,False,transformed=True) - + shapeMismatch = shapeMismatch( size,table.get(options.defgrad).reshape(grid[2],grid[1],grid[0],3,3),nodes,centres) + table.add('shapeMismatch(({}))'.format(options.defgrad), + shapeMismatch.reshape((-1,1)), + scriptID+' '+' '.join(sys.argv[1:])) + if options.volume: - table.labels_append(['volMismatch({})'.format(options.defgrad)]) + volumeMismatch = volumeMismatch(size,table.get(options.defgrad).reshape(grid[2],grid[1],grid[0],3,3),nodes) + table.add('volMismatch(({}))'.format(options.defgrad), + volumeMismatch.reshape((-1,1)), + scriptID+' '+' '.join(sys.argv[1:])) - table.head_write() - if options.shape: - shapeMismatch = shapeMismatch( size,table.data[:,:9].reshape(grid[2],grid[1],grid[0],3,3),nodes,centres) - if options.volume: - volumeMismatch = volumeMismatch(size,table.data[:,:9].reshape(grid[2],grid[1],grid[0],3,3),nodes) - -# ------------------------------------------ output data ------------------------------------------- - for i in range(grid[2]): - for j in range(grid[1]): - for k in range(grid[0]): - table.data_read() - if options.shape: table.data_append(shapeMismatch[i,j,k]) - if options.volume: table.data_append(volumeMismatch[i,j,k]) - table.data_write() - -# ------------------------------------------ output finalization ----------------------------------- - - table.close() # close ASCII tables + table.to_ASCII(sys.stdout if name is None else name) diff --git a/processing/post/addIPFcolor.py b/processing/post/addIPFcolor.py index 0149dd078..5114b6a91 100755 --- a/processing/post/addIPFcolor.py +++ b/processing/post/addIPFcolor.py @@ -43,54 +43,25 @@ parser.set_defaults(pole = (0.0,0.0,1.0), ) (options, filenames) = parser.parse_args() +if filenames == []: filenames = [None] # damask.Orientation requires Bravais lattice, but we are only interested in symmetry -symmetry2lattice={'cubic':'bcc','hexagonal':'hex','tetragonal':'bct'} +symmetry2lattice={'cubic':'fcc','hexagonal':'hex','tetragonal':'bct'} lattice = symmetry2lattice[options.symmetry] pole = np.array(options.pole) pole /= np.linalg.norm(pole) -# --- 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.head_read() - -# ------------------------------------------ sanity checks ---------------------------------------- - - if not table.label_dimension(options.quaternion) == 4: - damask.util.croak('input {} does not have dimension 4.'.format(options.quaternion)) - table.close(dismiss = True) # close ASCIItable and remove empty file - continue - - column = table.label_index(options.quaternion) - -# ------------------------------------------ assemble header --------------------------------------- - - table.info_append(scriptID + '\t' + ' '.join(sys.argv[1:])) - table.labels_append(['{}_IPF_{:g}{:g}{:g}_{sym}'.format(i+1,*options.pole,sym = options.symmetry.lower()) for i in range(3)]) - table.head_write() - -# ------------------------------------------ process data ------------------------------------------ - - outputAlive = True - while outputAlive and table.data_read(): # read next data line of ASCII table - o = damask.Orientation(np.array(list(map(float,table.data[column:column+4]))), - lattice = lattice).reduced() - - table.data_append(o.IPFcolor(pole)) - outputAlive = table.data_write() # output processed line - -# ------------------------------------------ output finalization ----------------------------------- - - table.close() # close ASCII tables + table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) + orientation = table.get(options.quaternion) + color = np.empty((orientation.shape[0],3)) + for i,o in enumerate(orientation): + color[i] = damask.Orientation(o,lattice = lattice).IPFcolor(pole) + + table.add('IPF_{:g}{:g}{:g}_{sym}'.format(*options.pole,sym = options.symmetry.lower()), + color, + scriptID+' '+' '.join(sys.argv[1:])) + table.to_ASCII(sys.stdout if name is None else name) diff --git a/processing/post/addNorm.py b/processing/post/addNorm.py index c8c0b05bf..e58947de0 100755 --- a/processing/post/addNorm.py +++ b/processing/post/addNorm.py @@ -42,7 +42,7 @@ parser.add_option('-n','--norm', type = 'choice', choices = normChoices, metavar='string', help = 'type of element-wise p-norm [frobenius] {%s}'%(','.join(map(str,normChoices)))) parser.add_option('-l','--label', - dest = 'label', + dest = 'labels', action = 'extend', metavar = '', help = 'heading of column(s) to calculate norm of') @@ -50,62 +50,25 @@ parser.set_defaults(norm = 'frobenius', ) (options,filenames) = parser.parse_args() - -if options.norm.lower() not in normChoices: - parser.error('invalid norm ({}) specified.'.format(options.norm)) -if options.label is None: - parser.error('no data column specified.') - -# --- loop over input files ------------------------------------------------------------------------- - if filenames == []: filenames = [None] +if options.norm.lower() not in normChoices: + parser.error('invalid norm ({}) specified.'.format(options.norm)) +if options.labels is None: + parser.error('no data column specified.') + 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) + for label in options.labels: + data = table.get(label) + data_norm = np.empty((data.shape[0],1)) + for i,d in enumerate(data): + data_norm[i] = norm(options.norm.capitalize(),d) - table.head_read() + table.add('norm{}({})'.format(options.norm.capitalize(),label), + data_norm, + scriptID+' '+' '.join(sys.argv[1:])) -# ------------------------------------------ sanity checks ---------------------------------------- - - errors = [] - remarks = [] - columns = [] - dims = [] - - for what in options.label: - dim = table.label_dimension(what) - if dim < 0: remarks.append('column {} not found...'.format(what)) - else: - dims.append(dim) - columns.append(table.label_index(what)) - table.labels_append('norm{}({})'.format(options.norm.capitalize(),what)) # 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 -------------------------------------- - - 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 column,dim in zip(columns,dims): - table.data_append(norm(options.norm.capitalize(), - map(float,table.data[column:column+dim]))) - outputAlive = table.data_write() # output processed line - -# ------------------------------------------ output finalization ----------------------------------- - - table.close() # close input ASCII table (works for stdin) + table.to_ASCII(sys.stdout if name is None else name) diff --git a/processing/post/addPole.py b/processing/post/addPole.py index c8b83b106..9f4982ff5 100755 --- a/processing/post/addPole.py +++ b/processing/post/addPole.py @@ -42,52 +42,23 @@ parser.set_defaults(pole = (1.0,0.0,0.0), ) (options, filenames) = parser.parse_args() +if filenames == []: filenames = [None] pole = np.array(options.pole) pole /= np.linalg.norm(pole) -# --- 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.head_read() - -# ------------------------------------------ sanity checks ---------------------------------------- - - if not table.label_dimension(options.quaternion) == 4: - damask.util.croak('input {} does not have dimension 4.'.format(options.quaternion)) - table.close(dismiss = True) # close ASCIItable and remove empty file - continue - - column = table.label_index(options.quaternion) - -# ------------------------------------------ assemble header --------------------------------------- - - table.info_append(scriptID + '\t' + ' '.join(sys.argv[1:])) - table.labels_append(['{}_pole_{}{}{}'.format(i+1,*options.pole) for i in range(2)]) - table.head_write() - -# ------------------------------------------ process data ------------------------------------------ - outputAlive = True - while outputAlive and table.data_read(): # read next data line of ASCII table - o = damask.Rotation(np.array(list(map(float,table.data[column:column+4])))) - - rotatedPole = o*pole # rotate pole according to crystal orientation - (x,y) = rotatedPole[0:2]/(1.+abs(pole[2])) # stereographic projection - - table.data_append([np.sqrt(x*x+y*y),np.arctan2(y,x)] if options.polar else [x,y]) # cartesian coordinates - - outputAlive = table.data_write() # output processed line - -# ------------------------------------------ output finalization ----------------------------------- - - table.close() # close ASCII tables + table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) + orientation = table.get(options.quaternion) + poles = np.empty((orientation.shape[0],2)) + for i,o in enumerate(orientation): + rotatedPole = damask.Rotation(o)*pole # rotate pole according to crystal orientation + (x,y) = rotatedPole[0:2]/(1.+abs(pole[2])) # stereographic projection + poles[i] = [np.sqrt(x*x+y*y),np.arctan2(y,x)] if options.polar else [x,y] # cartesian coordinates + + table.add('pole_{}{}{}'.format(*options.pole), + poles, + scriptID+' '+' '.join(sys.argv[1:])) + table.to_ASCII(sys.stdout if name is None else name) From 503626473a5ed13e5aa5cc929737128ba1d6c861 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 17:38:16 +0100 Subject: [PATCH 120/223] bugfix: wrong grid order for x fast, z slow, the shape of the array needs to be reversed --- python/damask/grid_filters.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/damask/grid_filters.py b/python/damask/grid_filters.py index a1e1ff06d..664198630 100644 --- a/python/damask/grid_filters.py +++ b/python/damask/grid_filters.py @@ -147,7 +147,7 @@ def cell_displacement_avg(size,F): """ F_avg = np.average(F,axis=(0,1,2)) - return np.einsum('ml,ijkl->ijkm',F_avg-np.eye(3),cell_coord0(F.shape[:3],size)) + return np.einsum('ml,ijkl->ijkm',F_avg-np.eye(3),cell_coord0(F.shape[:3][::-1],size)) def cell_coord0_2_DNA(coord0,ordered=True): """ @@ -232,7 +232,7 @@ def node_displacement_avg(size,F): """ F_avg = np.average(F,axis=(0,1,2)) - return np.einsum('ml,ijkl->ijkm',F_avg-np.eye(3),node_coord0(F.shape[:3],size)) + return np.einsum('ml,ijkl->ijkm',F_avg-np.eye(3),node_coord0(F.shape[:3][::-1],size)) def cell_2_node(cell_data): From 2cd2d6f5065ade2eacab31a1bee880afb14cbed9 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 18:07:04 +0100 Subject: [PATCH 121/223] cell_2_node/node_2_cell work only for periodic data hence, coordinates and displacements cannot be converted easily --- python/damask/grid_filters.py | 67 ++++++++++++++++++++++++++++++- python/tests/test_grid_filters.py | 7 ++++ 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/python/damask/grid_filters.py b/python/damask/grid_filters.py index 664198630..69e7dd6b1 100644 --- a/python/damask/grid_filters.py +++ b/python/damask/grid_filters.py @@ -97,6 +97,8 @@ def cell_coord0(grid,size,origin=np.zeros(3)): number of grid points. size : numpy.ndarray physical size of the periodic field. + origin : numpy.ndarray, optional + physical origin of the periodic field. Default is [0.0,0.0,0.0]. """ start = origin + size/grid*.5 @@ -149,6 +151,36 @@ def cell_displacement_avg(size,F): F_avg = np.average(F,axis=(0,1,2)) return np.einsum('ml,ijkl->ijkm',F_avg-np.eye(3),cell_coord0(F.shape[:3][::-1],size)) +def cell_displacement(size,F): + """ + Cell center displacement field from deformation gradient field. + + Parameters + ---------- + size : numpy.ndarray + physical size of the periodic field. + F : numpy.ndarray + deformation gradient field. + + """ + return cell_displacement_avg(size,F) + cell_displacement_fluct(size,F) + +def cell_coord(size,F,origin=np.zeros(3)): + """ + Cell center positions. + + Parameters + ---------- + size : numpy.ndarray + physical size of the periodic field. + F : numpy.ndarray + deformation gradient field. + origin : numpy.ndarray, optional + physical origin of the periodic field. Default is [0.0,0.0,0.0]. + + """ + return cell_coord0(F.shape[:3][::-1],size,origin) + cell_displacement(size,F) + def cell_coord0_2_DNA(coord0,ordered=True): """ Return grid 'DNA', i.e. grid, size, and origin from array of cell positions. @@ -196,6 +228,8 @@ def node_coord0(grid,size,origin=np.zeros(3)): number of grid points. size : numpy.ndarray physical size of the periodic field. + origin : numpy.ndarray, optional + physical origin of the periodic field. Default is [0.0,0.0,0.0]. """ x, y, z = np.meshgrid(np.linspace(origin[2],size[2]+origin[2],1+grid[2]), @@ -234,9 +268,38 @@ def node_displacement_avg(size,F): F_avg = np.average(F,axis=(0,1,2)) return np.einsum('ml,ijkl->ijkm',F_avg-np.eye(3),node_coord0(F.shape[:3][::-1],size)) +def node_displacement(size,F): + """ + Nodal displacement field from deformation gradient field. + + Parameters + ---------- + size : numpy.ndarray + physical size of the periodic field. + F : numpy.ndarray + deformation gradient field. + + """ + return node_displacement_avg(size,F) + node_displacement_fluct(size,F) + +def node_coord(size,F,origin=np.zeros(3)): + """ + Nodal positions. + + Parameters + ---------- + size : numpy.ndarray + physical size of the periodic field. + F : numpy.ndarray + deformation gradient field. + origin : numpy.ndarray, optional + physical origin of the periodic field. Default is [0.0,0.0,0.0]. + + """ + return node_coord0(F.shape[:3][::-1],size,origin) + node_displacement(size,F) def cell_2_node(cell_data): - """Interpolate cell data to nodal data.""" + """Interpolate periodic cell data to nodal data.""" n = ( cell_data + np.roll(cell_data,1,(0,1,2)) + np.roll(cell_data,1,(0,)) + np.roll(cell_data,1,(1,)) + np.roll(cell_data,1,(2,)) + np.roll(cell_data,1,(0,1)) + np.roll(cell_data,1,(1,2)) + np.roll(cell_data,1,(2,0)))*0.125 @@ -244,7 +307,7 @@ def cell_2_node(cell_data): return np.pad(n,((0,1),(0,1),(0,1))+((0,0),)*len(cell_data.shape[3:]),mode='wrap') def node_2_cell(node_data): - """Interpolate nodal data to cell data.""" + """Interpolate periodic nodal data to cell data.""" c = ( node_data + np.roll(node_data,1,(0,1,2)) + np.roll(node_data,1,(0,)) + np.roll(node_data,1,(1,)) + np.roll(node_data,1,(2,)) + np.roll(node_data,1,(0,1)) + np.roll(node_data,1,(1,2)) + np.roll(node_data,1,(2,0)))*0.125 diff --git a/python/tests/test_grid_filters.py b/python/tests/test_grid_filters.py index b23fad549..c8442eee4 100644 --- a/python/tests/test_grid_filters.py +++ b/python/tests/test_grid_filters.py @@ -35,6 +35,13 @@ class TestGridFilters: _grid,_size,_origin = eval('grid_filters.{}_coord0_2_DNA(coord0.reshape((-1,3)))'.format(mode)) assert np.allclose(grid,_grid) and np.allclose(size,_size) and np.allclose(origin,_origin) + def test_displacement_fluct_equivalence(self): + size = np.random.random(3) + grid = np.random.randint(8,32,(3)) + F = np.random.random(tuple(grid)+(3,3)) + assert np.allclose(grid_filters.node_displacement_fluct(size,F), + grid_filters.cell_2_node(grid_filters.cell_displacement_fluct(size,F))) + @pytest.mark.parametrize('mode',[('cell'),('node')]) def test_displacement_avg_vanishes(self,mode): From da33ba17bcd17e738731e5ba6379875e1a4b53da Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 18:17:04 +0100 Subject: [PATCH 122/223] using central (and tested) functionality --- processing/post/addCompatibilityMismatch.py | 64 ++------------------- 1 file changed, 4 insertions(+), 60 deletions(-) diff --git a/processing/post/addCompatibilityMismatch.py b/processing/post/addCompatibilityMismatch.py index e4b6d940d..553fa9390 100755 --- a/processing/post/addCompatibilityMismatch.py +++ b/processing/post/addCompatibilityMismatch.py @@ -13,58 +13,6 @@ import damask scriptName = os.path.splitext(os.path.basename(__file__))[0] scriptID = ' '.join([scriptName,damask.version]) -#-------------------------------------------------------------------------------------------------- -def deformationAvgFFT(F,grid,size,nodal=False,transformed=False): - """Calculate average cell center (or nodal) deformation for deformation gradient field specified in each grid cell""" - if nodal: - x, y, z = np.meshgrid(np.linspace(0,size[2],1+grid[2]), - np.linspace(0,size[1],1+grid[1]), - np.linspace(0,size[0],1+grid[0]), - indexing = 'ij') - else: - x, y, z = np.meshgrid(np.linspace(size[2]/grid[2]/2.,size[2]-size[2]/grid[2]/2.,grid[2]), - np.linspace(size[1]/grid[1]/2.,size[1]-size[1]/grid[1]/2.,grid[1]), - np.linspace(size[0]/grid[0]/2.,size[0]-size[0]/grid[0]/2.,grid[0]), - indexing = 'ij') - - origCoords = np.concatenate((z[:,:,:,None],y[:,:,:,None],x[:,:,:,None]),axis = 3) - - F_fourier = F if transformed else np.fft.rfftn(F,axes=(0,1,2)) # transform or use provided data - Favg = np.real(F_fourier[0,0,0,:,:])/grid.prod() # take zero freq for average - avgDeformation = np.einsum('ml,ijkl->ijkm',Favg,origCoords) # dX = Favg.X - - return avgDeformation - -#-------------------------------------------------------------------------------------------------- -def displacementFluctFFT(F,grid,size,nodal=False,transformed=False): - """Calculate cell center (or nodal) displacement for deformation gradient field specified in each grid cell""" - integrator = 0.5j * size / math.pi - - kk, kj, ki = np.meshgrid(np.where(np.arange(grid[2])>grid[2]//2,np.arange(grid[2])-grid[2],np.arange(grid[2])), - np.where(np.arange(grid[1])>grid[1]//2,np.arange(grid[1])-grid[1],np.arange(grid[1])), - np.arange(grid[0]//2+1), - indexing = 'ij') - k_s = np.concatenate((ki[:,:,:,None],kj[:,:,:,None],kk[:,:,:,None]),axis = 3) - k_sSquared = np.einsum('...l,...l',k_s,k_s) - k_sSquared[0,0,0] = 1.0 # ignore global average frequency - -#-------------------------------------------------------------------------------------------------- -# integration in Fourier space - - displacement_fourier = -np.einsum('ijkml,ijkl,l->ijkm', - F if transformed else np.fft.rfftn(F,axes=(0,1,2)), - k_s, - integrator, - ) / k_sSquared[...,np.newaxis] - -#-------------------------------------------------------------------------------------------------- -# backtransformation to real space - - displacement = np.fft.irfftn(displacement_fourier,grid[::-1],axes=(0,1,2)) - - return damask.grid_filters.cell_2_node(displacement) if nodal else displacement - - def volTetrahedron(coords): """ Return the volume of the tetrahedron with given vertices or sides. @@ -230,15 +178,11 @@ for name in filenames: table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.get(options.pos)) - N = grid.prod() - - F_fourier = np.fft.rfftn(table.get(options.defgrad).reshape(grid[2],grid[1],grid[0],3,3),axes=(0,1,2)) # perform transform only once... - nodes = displacementFluctFFT(F_fourier,grid,size,True,transformed=True)\ - + deformationAvgFFT (F_fourier,grid,size,True,transformed=True) - + F = table.get(options.defgrad).reshape(grid[2],grid[1],grid[0],3,3) + nodes = damask.grid_filters.node_coord(size,F) + if options.shape: - centres = displacementFluctFFT(F_fourier,grid,size,False,transformed=True)\ - + deformationAvgFFT (F_fourier,grid,size,False,transformed=True) + centres = damask.grid_filters.cell_coord(size,F) shapeMismatch = shapeMismatch( size,table.get(options.defgrad).reshape(grid[2],grid[1],grid[0],3,3),nodes,centres) table.add('shapeMismatch(({}))'.format(options.defgrad), shapeMismatch.reshape((-1,1)), From b2934988649c267cea3820534e4e88eab8393fa4 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 18:19:54 +0100 Subject: [PATCH 123/223] fixing prospector complaints --- processing/post/addCompatibilityMismatch.py | 10 +++++----- processing/post/addIPFcolor.py | 1 + processing/post/addNorm.py | 1 + processing/post/addPole.py | 1 + 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/processing/post/addCompatibilityMismatch.py b/processing/post/addCompatibilityMismatch.py index 553fa9390..8aff13c53 100755 --- a/processing/post/addCompatibilityMismatch.py +++ b/processing/post/addCompatibilityMismatch.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 import os -import math import sys +from io import StringIO from optparse import OptionParser import numpy as np @@ -61,10 +61,10 @@ def volTetrahedron(coords): def volumeMismatch(size,F,nodes): """ - Calculates the volume mismatch + Calculates the volume mismatch. volume mismatch is defined as the difference between volume of reconstructed - (compatible) cube and determinant of defgrad at the FP + (compatible) cube and determinant of deformation gradient at Fourier point. """ coords = np.empty([8,3]) vMismatch = np.empty(grid[::-1]) @@ -97,11 +97,11 @@ def volumeMismatch(size,F,nodes): def shapeMismatch(size,F,nodes,centres): """ - Routine to calculate the shape mismatch + Routine to calculate the shape mismatch. shape mismatch is defined as difference between the vectors from the central point to the corners of reconstructed (combatible) volume element and the vectors calculated by deforming - the initial volume element with the current deformation gradient + the initial volume element with the current deformation gradient. """ coordsInitial = np.empty([8,3]) sMismatch = np.empty(grid[::-1]) diff --git a/processing/post/addIPFcolor.py b/processing/post/addIPFcolor.py index 5114b6a91..014b0147d 100755 --- a/processing/post/addIPFcolor.py +++ b/processing/post/addIPFcolor.py @@ -2,6 +2,7 @@ import os import sys +from io import StringIO from optparse import OptionParser import numpy as np diff --git a/processing/post/addNorm.py b/processing/post/addNorm.py index e58947de0..4ac2bf899 100755 --- a/processing/post/addNorm.py +++ b/processing/post/addNorm.py @@ -2,6 +2,7 @@ import os import sys +from io import StringIO from optparse import OptionParser import numpy as np diff --git a/processing/post/addPole.py b/processing/post/addPole.py index 9f4982ff5..58f9235dc 100755 --- a/processing/post/addPole.py +++ b/processing/post/addPole.py @@ -2,6 +2,7 @@ import os import sys +from io import StringIO from optparse import OptionParser import numpy as np From 6989679d3bea0e8abafa681f122219dc06bb105d Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 19:03:36 +0100 Subject: [PATCH 124/223] using central functionality - Table class for table data - grid_filters for grid related functions --- processing/post/addEuclideanDistance.py | 124 +++++++----------------- processing/post/addGaussian.py | 83 +++------------- python/damask/grid_filters.py | 13 +++ 3 files changed, 61 insertions(+), 159 deletions(-) diff --git a/processing/post/addEuclideanDistance.py b/processing/post/addEuclideanDistance.py index 1ca2169f6..d2604b4fc 100755 --- a/processing/post/addEuclideanDistance.py +++ b/processing/post/addEuclideanDistance.py @@ -121,13 +121,14 @@ parser.set_defaults(pos = 'pos', ) (options,filenames) = parser.parse_args() +if filenames == []: filenames = [None] if options.type is None: - parser.error('no feature type selected.') + parser.error('no feature type selected.') if not set(options.type).issubset(set(list(itertools.chain(*map(lambda x: x['names'],features))))): - parser.error('type must be chosen from (%s).'%(', '.join(map(lambda x:'|'.join(x['names']),features))) ) + parser.error('type must be chosen from (%s).'%(', '.join(map(lambda x:'|'.join(x['names']),features))) ) if 'biplane' in options.type and 'boundary' in options.type: - parser.error('only one from aliases "biplane" and "boundary" possible.') + parser.error('only one from aliases "biplane" and "boundary" possible.') feature_list = [] for i,feature in enumerate(features): @@ -137,104 +138,49 @@ for i,feature in enumerate(features): feature_list.append(i) # remember valid features break -# --- 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) + grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.get(options.pos)) - table.head_read() + neighborhood = neighborhoods[options.neighborhood] + diffToNeighbor = np.empty(list(grid+2)+[len(neighborhood)],'i') + microstructure = periodic_3Dpad(table.get(options.id).astype('i').reshape(grid,order='F')) -# ------------------------------------------ sanity checks ---------------------------------------- - - errors = [] - remarks = [] - - if not 3 >= table.label_dimension(options.pos) >= 1: - errors.append('coordinates "{}" need to have one, two, or three dimensions.'.format(options.pos)) - - if table.label_dimension(options.id) != 1: errors.append('grain identifier {} not found.'.format(options.id)) - else: idCol = table.label_index(options.id) - - if remarks != []: - damask.util.croak(remarks) - remarks = [] - if errors != []: - damask.util.croak(errors) - table.close(dismiss = True) - continue - -# ------------------------------------------ assemble header --------------------------------------- - - table.info_append(scriptID + '\t' + ' '.join(sys.argv[1:])) - for feature in feature_list: - table.labels_append('ED_{}({})'.format(features[feature]['names'][0],options.id)) # extend ASCII header with new labels - table.head_write() - -# --------------- figure out size and grid --------------------------------------------------------- - - table.data_readArray() - - grid,size = damask.util.coordGridAndSize(table.data[:,table.label_indexrange(options.pos)]) - N = grid.prod() - - if N != len(table.data): errors.append('data count {} does not match grid {}.'.format(N,'x'.join(map(str,grid)))) - else: remarks.append('grid: {}x{}x{}'.format(*grid)) - if remarks != []: damask.util.croak(remarks) - if errors != []: - damask.util.croak(errors) - table.close(dismiss = True) - continue - -# ------------------------------------------ process value field ----------------------------------- - - stack = [table.data] - - neighborhood = neighborhoods[options.neighborhood] - diffToNeighbor = np.empty(list(grid+2)+[len(neighborhood)],'i') - microstructure = periodic_3Dpad(table.data[:,idCol].astype('i').reshape(grid,order='F')) - - for i,p in enumerate(neighborhood): - stencil = np.zeros((3,3,3),'i') - stencil[1,1,1] = -1 - stencil[p[0]+1, - p[1]+1, - p[2]+1] = 1 - diffToNeighbor[:,:,:,i] = ndimage.convolve(microstructure,stencil) # compare ID at each point... + for i,p in enumerate(neighborhood): + stencil = np.zeros((3,3,3),'i') + stencil[1,1,1] = -1 + stencil[p[0]+1, + p[1]+1, + p[2]+1] = 1 + diffToNeighbor[:,:,:,i] = ndimage.convolve(microstructure,stencil) # compare ID at each point... # ...to every one in the specified neighborhood # for same IDs at both locations ==> 0 - diffToNeighbor = np.sort(diffToNeighbor) # sort diff such that number of changes in diff (steps)... + diffToNeighbor = np.sort(diffToNeighbor) # sort diff such that number of changes in diff (steps)... # ...reflects number of unique neighbors - uniques = np.where(diffToNeighbor[1:-1,1:-1,1:-1,0] != 0, 1,0) # initialize unique value counter (exclude myself [= 0]) + uniques = np.where(diffToNeighbor[1:-1,1:-1,1:-1,0] != 0, 1,0) # initialize unique value counter (exclude myself [= 0]) - for i in range(1,len(neighborhood)): # check remaining points in neighborhood - uniques += np.where(np.logical_and( - diffToNeighbor[1:-1,1:-1,1:-1,i] != 0, # not myself? - diffToNeighbor[1:-1,1:-1,1:-1,i] != diffToNeighbor[1:-1,1:-1,1:-1,i-1], - ), # flip of ID difference detected? - 1,0) # count that flip + for i in range(1,len(neighborhood)): # check remaining points in neighborhood + uniques += np.where(np.logical_and( + diffToNeighbor[1:-1,1:-1,1:-1,i] != 0, # not myself? + diffToNeighbor[1:-1,1:-1,1:-1,i] != diffToNeighbor[1:-1,1:-1,1:-1,i-1], + ), # flip of ID difference detected? + 1,0) # count that flip - distance = np.ones((len(feature_list),grid[0],grid[1],grid[2]),'d') + distance = np.ones((len(feature_list),grid[0],grid[1],grid[2]),'d') - for i,feature_id in enumerate(feature_list): - distance[i,:,:,:] = np.where(uniques >= features[feature_id]['aliens'],0.0,1.0) # seed with 0.0 when enough unique neighbor IDs are present - distance[i,:,:,:] = ndimage.morphology.distance_transform_edt(distance[i,:,:,:])*[options.scale]*3 + for i,feature_id in enumerate(feature_list): + distance[i,:,:,:] = np.where(uniques >= features[feature_id]['aliens'],0.0,1.0) # seed with 0.0 when enough unique neighbor IDs are present + distance[i,:,:,:] = ndimage.morphology.distance_transform_edt(distance[i,:,:,:])*[options.scale]*3 - distance = distance.reshape([len(feature_list),grid.prod(),1],order='F') - for i in range(len(feature_list)): - stack.append(distance[i,:]) + distance = distance.reshape([len(feature_list),grid.prod(),1],order='F') -# ------------------------------------------ output result ----------------------------------------- - if len(stack) > 1: table.data = np.hstack(tuple(stack)) - table.data_writeArray('%.12g') + for i,feature in enumerate(feature_list): + table.add('ED_{}({})'.format(features[feature]['names'][0],options.id), + distance[i,:], + scriptID+' '+' '.join(sys.argv[1:])) -# ------------------------------------------ output finalization ----------------------------------- - - table.close() # close input ASCII table (works for stdin) + table.to_ASCII(sys.stdout if name is None else name) diff --git a/processing/post/addGaussian.py b/processing/post/addGaussian.py index 9b601a1dc..e43b162da 100755 --- a/processing/post/addGaussian.py +++ b/processing/post/addGaussian.py @@ -30,7 +30,7 @@ parser.add_option('-p','--pos','--periodiccellcenter', type = 'string', metavar = 'string', help = 'label of coordinates [%default]') parser.add_option('-s','--scalar', - dest = 'scalar', + dest = 'labels', action = 'extend', metavar = '', help = 'label(s) of scalar field values') parser.add_option('-o','--order', @@ -56,78 +56,21 @@ parser.set_defaults(pos = 'pos', ) (options,filenames) = parser.parse_args() - -if options.scalar is None: - parser.error('no data column specified.') - -# --- loop over input files ------------------------------------------------------------------------ - if filenames == []: filenames = [None] +if options.labels is None: parser.error('no data column specified.') + 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) + damask.grid_filters.coord0_check(table.get(options.pos)) - table.head_read() + for label in options.labels: + table.add('Gauss{}({})'.format(options.sigma,label), + ndimage.filters.gaussian_filter(table.get(label).reshape((-1)), + options.sigma,options.order, + mode = 'wrap' if options.periodic else 'nearest'), + scriptID+' '+' '.join(sys.argv[1:])) -# ------------------------------------------ sanity checks ---------------------------------------- - - items = { - 'scalar': {'dim': 1, 'shape': [1], 'labels':options.scalar, 'active':[], 'column': []}, - } - errors = [] - remarks = [] - column = {} - - if table.label_dimension(options.pos) != 3: errors.append('coordinates {} are not a vector.'.format(options.pos)) - else: colCoord = table.label_index(options.pos) - - for type, data in items.items(): - for what in (data['labels'] if data['labels'] is not None else []): - dim = table.label_dimension(what) - if dim != data['dim']: remarks.append('column {} is not a {}.'.format(what,type)) - else: - items[type]['active'].append(what) - items[type]['column'].append(table.label_index(what)) - - if remarks != []: damask.util.croak(remarks) - if errors != []: - damask.util.croak(errors) - table.close(dismiss = True) - continue - -# ------------------------------------------ assemble header -------------------------------------- - - table.info_append(scriptID + '\t' + ' '.join(sys.argv[1:])) - for type, data in items.items(): - for label in data['active']: - table.labels_append(['Gauss{}({})'.format(options.sigma,label)]) # extend ASCII header with new labels - table.head_write() - -# --------------- figure out size and grid --------------------------------------------------------- - - table.data_readArray() - - grid,size = damask.util.coordGridAndSize(table.data[:,table.label_indexrange(options.pos)]) - -# ------------------------------------------ process value field ----------------------------------- - - stack = [table.data] - for type, data in items.items(): - for i,label in enumerate(data['active']): - stack.append(ndimage.filters.gaussian_filter(table.data[:,data['column'][i]], - options.sigma,options.order, - mode = 'wrap' if options.periodic else 'nearest' - ).reshape([table.data.shape[0],1]) - ) - -# ------------------------------------------ output result ----------------------------------------- - if len(stack) > 1: table.data = np.hstack(tuple(stack)) - table.data_writeArray('%.12g') - -# ------------------------------------------ output finalization ----------------------------------- - - table.close() # close input ASCII table (works for stdin) + table.to_ASCII(sys.stdout if name is None else name) diff --git a/python/damask/grid_filters.py b/python/damask/grid_filters.py index 69e7dd6b1..cd19932ca 100644 --- a/python/damask/grid_filters.py +++ b/python/damask/grid_filters.py @@ -217,6 +217,19 @@ def cell_coord0_2_DNA(coord0,ordered=True): return (grid,size,origin) +def coord0_check(coord0): + """ + Check whether coordinates lie on a regular grid + + Parameters + ---------- + coord0 : numpy.ndarray + array of undeformed cell coordinates. + + """ + cell_coord0_2_DNA(coord0,ordered=True) + + def node_coord0(grid,size,origin=np.zeros(3)): """ From 5b7139dc220ecd53ec8f928eb8a27ff54942f43a Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 19:04:29 +0100 Subject: [PATCH 125/223] using specialized class --- processing/post/addEuclideanDistance.py | 1 + processing/post/addGaussian.py | 2 +- processing/post/averageDown.py | 5 +++-- processing/post/blowUp.py | 5 +++-- python/damask/grid_filters.py | 2 +- python/damask/util.py | 24 ------------------------ 6 files changed, 9 insertions(+), 30 deletions(-) diff --git a/processing/post/addEuclideanDistance.py b/processing/post/addEuclideanDistance.py index d2604b4fc..eaf91b894 100755 --- a/processing/post/addEuclideanDistance.py +++ b/processing/post/addEuclideanDistance.py @@ -2,6 +2,7 @@ import os import sys +from io import StringIO from optparse import OptionParser import itertools diff --git a/processing/post/addGaussian.py b/processing/post/addGaussian.py index e43b162da..5f3ec5d60 100755 --- a/processing/post/addGaussian.py +++ b/processing/post/addGaussian.py @@ -2,9 +2,9 @@ import os import sys +from io import StringIO from optparse import OptionParser -import numpy as np from scipy import ndimage import damask diff --git a/processing/post/averageDown.py b/processing/post/averageDown.py index d94bc8dbd..d4e2a3529 100755 --- a/processing/post/averageDown.py +++ b/processing/post/averageDown.py @@ -65,7 +65,8 @@ for name in filenames: outname = os.path.join(os.path.dirname(name), prefix+os.path.basename(name)) if name else name, buffered = False) - except: continue + except IOError: + continue damask.util.report(scriptName,name) # ------------------------------------------ read header ------------------------------------------ @@ -95,7 +96,7 @@ for name in filenames: table.data_readArray() if (options.grid is None or options.size is None): - grid,size = damask.util.coordGridAndSize(table.data[:,table.label_indexrange(options.pos)]) + grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.data[:,table.label_indexrange(options.pos)]) else: grid = np.array(options.grid,'i') size = np.array(options.size,'d') diff --git a/processing/post/blowUp.py b/processing/post/blowUp.py index 3dccb1aaf..32cc1909d 100755 --- a/processing/post/blowUp.py +++ b/processing/post/blowUp.py @@ -55,7 +55,8 @@ for name in filenames: outname = os.path.join(os.path.dirname(name), prefix+os.path.basename(name)) if name else name, buffered = False) - except: continue + except IOError: + continue damask.util.report(scriptName,name) # ------------------------------------------ read header ------------------------------------------ @@ -82,7 +83,7 @@ for name in filenames: table.data_readArray(options.pos) table.data_rewind() - grid,size = damask.util.coordGridAndSize(table.data) + grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.data) packing = np.array(options.packing,'i') outSize = grid*packing diff --git a/python/damask/grid_filters.py b/python/damask/grid_filters.py index cd19932ca..93e61f5d8 100644 --- a/python/damask/grid_filters.py +++ b/python/damask/grid_filters.py @@ -219,7 +219,7 @@ def cell_coord0_2_DNA(coord0,ordered=True): def coord0_check(coord0): """ - Check whether coordinates lie on a regular grid + Check whether coordinates lie on a regular grid. Parameters ---------- diff --git a/python/damask/util.py b/python/damask/util.py index cf041f946..558439a4c 100644 --- a/python/damask/util.py +++ b/python/damask/util.py @@ -119,30 +119,6 @@ def execute(cmd, if process.returncode != 0: raise RuntimeError('{} failed with returncode {}'.format(cmd,process.returncode)) return out,error -def coordGridAndSize(coordinates): - """Determines grid count and overall physical size along each dimension of an ordered array of coordinates.""" - dim = coordinates.shape[1] - coords = [np.unique(coordinates[:,i]) for i in range(dim)] - mincorner = np.array(list(map(min,coords))) - maxcorner = np.array(list(map(max,coords))) - grid = np.array(list(map(len,coords)),'i') - size = grid/np.maximum(np.ones(dim,'d'), grid-1.0) * (maxcorner-mincorner) # size from edge to edge = dim * n/(n-1) - size = np.where(grid > 1, size, min(size[grid > 1]/grid[grid > 1])) # spacing for grid==1 equal to smallest among other ones - delta = size/grid - - N = grid.prod() - - if N != len(coordinates): - raise ValueError('Data count {} does not match grid {}.'.format(len(coordinates),' x '.join(map(repr,grid)))) - - if np.any(np.abs(np.log10((coords[0][1:]-coords[0][:-1])/delta[0])) > 0.01) \ - or np.any(np.abs(np.log10((coords[1][1:]-coords[1][:-1])/delta[1])) > 0.01): - raise ValueError('regular grid spacing {} violated.'.format(' x '.join(map(repr,delta)))) - if dim==3 and np.any(np.abs(np.log10((coords[2][1:]-coords[2][:-1])/delta[2])) > 0.01): - raise ValueError('regular grid spacing {} violated.'.format(' x '.join(map(repr,delta)))) - - return grid,size - # ----------------------------- class extendableOption(Option): """ From a7d60dc52a3b016fdaf5bb3f8a7d8c7042e61de6 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 19:10:41 +0100 Subject: [PATCH 126/223] not used anymore geom class has own report function --- python/damask/util.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/python/damask/util.py b/python/damask/util.py index 558439a4c..0065daba5 100644 --- a/python/damask/util.py +++ b/python/damask/util.py @@ -7,9 +7,6 @@ from optparse import Option from queue import Queue from threading import Thread - -import numpy as np - class bcolors: """ ASCII Colors (Blender code). @@ -64,19 +61,6 @@ def report(who = None, croak( (emph(who)+': ' if who is not None else '') + (what if what is not None else '') + '\n' ) -# ----------------------------- -def report_geom(info, - what = ['grid','size','origin','homogenization','microstructures']): - """Reports (selected) geometry information.""" - output = { - 'grid' : 'grid a b c: {}'.format(' x '.join(list(map(str,info['grid' ])))), - 'size' : 'size x y z: {}'.format(' x '.join(list(map(str,info['size' ])))), - 'origin' : 'origin x y z: {}'.format(' : '.join(list(map(str,info['origin'])))), - 'homogenization' : 'homogenization: {}'.format(info['homogenization']), - 'microstructures' : 'microstructures: {}'.format(info['microstructures']), - } - for item in what: croak(output[item.lower()]) - # ----------------------------- def emph(what): """Formats string with emphasis.""" From 343da2e54d04b5271939302748a20e260d181f45 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 20:18:33 +0100 Subject: [PATCH 127/223] Table class instead of ASCIItable --- processing/post/addCumulative.py | 76 ++++-------------------- processing/post/addDerivative.py | 81 +++++--------------------- processing/post/vtk_pointCloud.py | 35 +---------- processing/post/vtk_rectilinearGrid.py | 47 +-------------- 4 files changed, 31 insertions(+), 208 deletions(-) diff --git a/processing/post/addCumulative.py b/processing/post/addCumulative.py index c94737b94..958c6a70a 100755 --- a/processing/post/addCumulative.py +++ b/processing/post/addCumulative.py @@ -2,6 +2,7 @@ import os import sys +from io import StringIO from optparse import OptionParser import numpy as np @@ -22,79 +23,26 @@ Add cumulative (sum of first to current row) values for given label(s). """, version = scriptID) parser.add_option('-l','--label', - dest='label', + dest='labels', action = 'extend', metavar = '', help = 'columns to cumulate') - parser.add_option('-p','--product', dest='product', action = 'store_true', help = 'product of values instead of sum') (options,filenames) = parser.parse_args() - -if options.label is None: - parser.error('no data column(s) specified.') - -# --- loop over input files ------------------------------------------------------------------------- - if filenames == []: filenames = [None] +if options.labels is None: + parser.error('no data column(s) specified.') + for name in filenames: - try: - table = damask.ASCIItable(name = name, buffered = False) - except IOError: - 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) + for label in options.labels: + table.add('cum_{}({})'.format('prod' if options.product else 'sum',label), + np.cumprod(table.get(label),0) if options.product else np.cumsum(table.get(label),0), + scriptID+' '+' '.join(sys.argv[1:])) - table.head_read() - -# ------------------------------------------ sanity checks ---------------------------------------- - - errors = [] - remarks = [] - columns = [] - dims = [] - how = 'prod' if options.product else 'sum' - - for what in options.label: - dim = table.label_dimension(what) - if dim < 0: remarks.append('column {} not found...'.format(what)) - else: - dims.append(dim) - columns.append(table.label_index(what)) - table.labels_append('cum_{}({})'.format(how,what) if dim == 1 else - ['{}_cum_{}({})'.format(i+1,how,what) for i in range(dim)] ) # 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 --------------------------------------- - - table.info_append(scriptID + '\t' + ' '.join(sys.argv[1:])) - table.head_write() - -# ------------------------------------------ process data ------------------------------------------ - mask = [] - for col,dim in zip(columns,dims): mask += range(col,col+dim) # isolate data columns to cumulate - cumulated = np.ones(len(mask)) if options.product else np.zeros(len(mask)) # prepare output field - - outputAlive = True - while outputAlive and table.data_read(): # read next data line of ASCII table - if options.product: - for i,col in enumerate(mask): - cumulated[i] *= float(table.data[col]) # cumulate values (multiplication) - else: - for i,col in enumerate(mask): - cumulated[i] += float(table.data[col]) # cumulate values (addition) - table.data_append(cumulated) - - outputAlive = table.data_write() # output processed line - -# ------------------------------------------ output finalization ----------------------------------- - - table.close() # close ASCII tables + table.to_ASCII(sys.stdout if name is None else name) diff --git a/processing/post/addDerivative.py b/processing/post/addDerivative.py index 8ebfdf2da..4e9410794 100755 --- a/processing/post/addDerivative.py +++ b/processing/post/addDerivative.py @@ -2,6 +2,7 @@ import os import sys +from io import StringIO from optparse import OptionParser import numpy as np @@ -30,7 +31,7 @@ def derivative(coordinates,what): (coordinates[0] - coordinates[1]) result[-1,:] = (what[-1,:] - what[-2,:]) / \ (coordinates[-1] - coordinates[-2]) - + return result @@ -48,78 +49,26 @@ parser.add_option('-c','--coordinates', type = 'string', metavar='string', help = 'heading of coordinate column') parser.add_option('-l','--label', - dest = 'label', + dest = 'labels', action = 'extend', metavar = '', help = 'heading of column(s) to differentiate') (options,filenames) = parser.parse_args() - -if options.coordinates is None: - parser.error('no coordinate column specified.') -if options.label is None: - parser.error('no data column specified.') - -# --- loop over input files ------------------------------------------------------------------------- - if filenames == []: filenames = [None] +if options.coordinates is None: + parser.error('no coordinate column specified.') +if options.labels is None: + parser.error('no data column specified.') + 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) + for label in options.labels: + table.add('d({})/d({})'.format(label,options.coordinates), + derivative(table.get(options.coordinates),table.get(label)), + scriptID+' '+' '.join(sys.argv[1:])) - table.head_read() - -# ------------------------------------------ sanity checks ---------------------------------------- - - errors = [] - remarks = [] - columns = [] - dims = [] - - if table.label_dimension(options.coordinates) != 1: - errors.append('coordinate column {} is not scalar.'.format(options.coordinates)) - - for what in options.label: - dim = table.label_dimension(what) - if dim < 0: remarks.append('column {} not found...'.format(what)) - else: - dims.append(dim) - columns.append(table.label_index(what)) - table.labels_append('d({})/d({})'.format(what,options.coordinates) if dim == 1 else - ['{}_d({})/d({})'.format(i+1,what,options.coordinates) for i in range(dim)] ) # 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 -------------------------------------- - - table.info_append(scriptID + '\t' + ' '.join(sys.argv[1:])) - table.head_write() - -# ------------------------------------------ process data ------------------------------------------ - - table.data_readArray() - - mask = [] - for col,dim in zip(columns,dims): mask += range(col,col+dim) # isolate data columns to differentiate - - differentiated = derivative(table.data[:,table.label_index(options.coordinates)].reshape((len(table.data),1)), - table.data[:,mask]) # calculate numerical derivative - - table.data = np.hstack((table.data,differentiated)) - -# ------------------------------------------ output result ----------------------------------------- - - table.data_writeArray() - -# ------------------------------------------ output finalization ----------------------------------- - - table.close() # close ASCII tables + table.to_ASCII(sys.stdout if name is None else name) diff --git a/processing/post/vtk_pointCloud.py b/processing/post/vtk_pointCloud.py index 06aad0aca..3977358ec 100755 --- a/processing/post/vtk_pointCloud.py +++ b/processing/post/vtk_pointCloud.py @@ -33,49 +33,20 @@ parser.set_defaults(pos = 'pos', ) (options, filenames) = parser.parse_args() - -# --- loop over input files ------------------------------------------------------------------------- - if filenames == []: filenames = [None] for name in filenames: - try: table = damask.ASCIItable(name = name, - buffered = False, - readonly = True) - except: continue damask.util.report(scriptName,name) -# --- interpret header ---------------------------------------------------------------------------- - - table.head_read() - - errors = [] - remarks = [] - coordDim = table.label_dimension(options.pos) - if not 3 >= coordDim >= 1: errors.append('coordinates "{}" need to have one, two, or three dimensions.'.format(options.pos)) - elif coordDim < 3: remarks.append('appending {} dimension{} to coordinates "{}"...'.format(3-coordDim, - 's' if coordDim < 2 else '', - options.pos)) - - if remarks != []: damask.util.croak(remarks) - if errors != []: - damask.util.croak(errors) - table.close(dismiss=True) - continue + table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) # ------------------------------------------ process data --------------------------------------- - table.data_readArray(options.pos) - if table.data.shape[1] < 3: - table.data = np.hstack((table.data, - np.zeros((table.data.shape[0], - 3-table.data.shape[1]),dtype='f'))) # fill coords up to 3D with zeros - Polydata = vtk.vtkPolyData() Points = vtk.vtkPoints() Vertices = vtk.vtkCellArray() - for p in table.data: + for p in table.get(options.pos): pointID = Points.InsertNextPoint(p) Vertices.InsertNextCell(1) Vertices.InsertCellPoint(pointID) @@ -104,5 +75,3 @@ for name in filenames: writer.Write() if name is None: sys.stdout.write(writer.GetOutputString()) - - table.close() diff --git a/processing/post/vtk_rectilinearGrid.py b/processing/post/vtk_rectilinearGrid.py index bb29a5d4c..f502f3962 100755 --- a/processing/post/vtk_rectilinearGrid.py +++ b/processing/post/vtk_rectilinearGrid.py @@ -40,48 +40,14 @@ parser.set_defaults(mode = 'cell', ) (options, filenames) = parser.parse_args() - -# --- loop over input files ------------------------------------------------------------------------- - if filenames == []: filenames = [None] for name in filenames: - try: table = damask.ASCIItable(name = name, - buffered = False, - labeled = True, - readonly = True, - ) - except: continue damask.util.report(scriptName,name) -# --- interpret header ---------------------------------------------------------------------------- - - table.head_read() - - remarks = [] - errors = [] - coordDim = table.label_dimension(options.pos) - if not 3 >= coordDim >= 1: errors.append('coordinates "{}" need to have one, two, or three dimensions.'.format(options.pos)) - elif coordDim < 3: remarks.append('appending {} dimension{} to coordinates "{}"...'.format(3-coordDim, - 's' if coordDim < 2 else '', - options.pos)) - - if remarks != []: damask.util.croak(remarks) - if errors != []: - damask.util.croak(errors) - table.close(dismiss=True) - continue - -# --------------- figure out size and grid --------------------------------------------------------- - - table.data_readArray(options.pos) - if table.data.shape[1] < 3: - table.data = np.hstack((table.data, - np.zeros((table.data.shape[0], - 3-table.data.shape[1]),dtype='f'))) # fill coords up to 3D with zeros - - coords = [np.unique(table.data[:,i]) for i in range(3)] + table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) + coords = [np.unique(table.get(options.pos)[:,i]) for i in range(3)] if options.mode == 'cell': coords = [0.5 * np.array([3.0 * coords[i][0] - coords[i][0 + int(len(coords[i]) > 1)]] + \ [coords[i][j-1] + coords[i][j] for j in range(1,len(coords[i]))] + \ @@ -90,13 +56,6 @@ for name in filenames: grid = np.array(list(map(len,coords)),'i') N = grid.prod() if options.mode == 'point' else (grid-1).prod() - if N != len(table.data): - errors.append('data count {} does not match grid {}x{}x{}.'.format(N,*(grid - (options.mode == 'cell')) )) - if errors != []: - damask.util.croak(errors) - table.close(dismiss = True) - continue - # ------------------------------------------ process data --------------------------------------- rGrid = vtk.vtkRectilinearGrid() @@ -135,5 +94,3 @@ for name in filenames: writer.Write() if name is None: sys.stdout.write(writer.GetOutputString()) - - table.close() From e08b096f082deaac6cef62d702c469c8f2559557 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 21 Dec 2019 23:43:56 +0100 Subject: [PATCH 128/223] just to make sure ... --- python/tests/test_grid_filters.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/python/tests/test_grid_filters.py b/python/tests/test_grid_filters.py index c8442eee4..fdddaf3a1 100644 --- a/python/tests/test_grid_filters.py +++ b/python/tests/test_grid_filters.py @@ -30,18 +30,36 @@ class TestGridFilters: grid = np.random.randint(8,32,(3)) size = np.random.random(3) origin = np.random.random(3) - coord0 = eval('grid_filters.{}_coord0(grid,size,origin)'.format(mode)) # noqa _grid,_size,_origin = eval('grid_filters.{}_coord0_2_DNA(coord0.reshape((-1,3)))'.format(mode)) assert np.allclose(grid,_grid) and np.allclose(size,_size) and np.allclose(origin,_origin) def test_displacement_fluct_equivalence(self): + """Ensure that fluctuations are periodic.""" size = np.random.random(3) grid = np.random.randint(8,32,(3)) F = np.random.random(tuple(grid)+(3,3)) assert np.allclose(grid_filters.node_displacement_fluct(size,F), grid_filters.cell_2_node(grid_filters.cell_displacement_fluct(size,F))) + def test_interpolation_nonperiodic(self): + size = np.random.random(3) + grid = np.random.randint(8,32,(3)) + F = np.random.random(tuple(grid)+(3,3)) + assert np.allclose(grid_filters.node_coord(size,F) [1:-1,1:-1,1:-1],grid_filters.cell_2_node( + grid_filters.cell_coord(size,F))[1:-1,1:-1,1:-1]) + + @pytest.mark.parametrize('mode',[('cell'),('node')]) + def test_coord0_origin(self,mode): + origin= np.random.random(3) + size = np.random.random(3) # noqa + grid = np.random.randint(8,32,(3)) + shifted = eval('grid_filters.{}_coord0(grid,size,origin)'.format(mode)) + unshifted = eval('grid_filters.{}_coord0(grid,size)'.format(mode)) + if mode == 'cell': + assert np.allclose(shifted,unshifted+np.broadcast_to(origin,tuple(grid[::-1]) +(3,))) + elif mode == 'node': + assert np.allclose(shifted,unshifted+np.broadcast_to(origin,tuple(grid[::-1]+1)+(3,))) @pytest.mark.parametrize('mode',[('cell'),('node')]) def test_displacement_avg_vanishes(self,mode): From fe463515d01452e6bc7d864f18dc7e7ba3e2e946 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 22 Dec 2019 07:07:34 +0100 Subject: [PATCH 129/223] following prospector advice --- processing/post/vtk_pointCloud.py | 2 +- processing/post/vtk_rectilinearGrid.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/processing/post/vtk_pointCloud.py b/processing/post/vtk_pointCloud.py index 3977358ec..44f719267 100755 --- a/processing/post/vtk_pointCloud.py +++ b/processing/post/vtk_pointCloud.py @@ -2,10 +2,10 @@ import os import sys +from io import StringIO from optparse import OptionParser import vtk -import numpy as np import damask diff --git a/processing/post/vtk_rectilinearGrid.py b/processing/post/vtk_rectilinearGrid.py index f502f3962..2ccad6319 100755 --- a/processing/post/vtk_rectilinearGrid.py +++ b/processing/post/vtk_rectilinearGrid.py @@ -2,6 +2,7 @@ import os import sys +from io import StringIO from optparse import OptionParser import vtk From cde6853a20073c8ce465d8b488a194a7fb561ae3 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 22 Dec 2019 07:20:23 +0100 Subject: [PATCH 130/223] do not repeat code --- processing/post/DADF5_postResults.py | 47 +++++++--------------------- 1 file changed, 11 insertions(+), 36 deletions(-) diff --git a/processing/post/DADF5_postResults.py b/processing/post/DADF5_postResults.py index efbf84b98..a6dc0b34a 100755 --- a/processing/post/DADF5_postResults.py +++ b/processing/post/DADF5_postResults.py @@ -39,61 +39,36 @@ for filename in options.filenames: results = damask.DADF5(filename) if not results.structured: continue - delta = results.size/results.grid*0.5 - x, y, z = np.meshgrid(np.linspace(delta[2],results.size[2]-delta[2],results.grid[2]), - np.linspace(delta[1],results.size[1]-delta[1],results.grid[1]), - np.linspace(delta[0],results.size[0]-delta[0],results.grid[0]), - indexing = 'ij') - - coords = np.concatenate((z[:,:,:,None],y[:,:,:,None],x[:,:,:,None]),axis = 3) + if results.version_major == 0 and results.version_minor >= 5: + coords = damask.grid_filters.cell_coord0(results.grid,results.size,results.origin) + else: + coords = damask.grid_filters.cell_coord0(results.grid,results.size) N_digits = int(np.floor(np.log10(int(results.increments[-1][3:]))))+1 N_digits = 5 # hack to keep test intact for i,inc in enumerate(results.iter_visible('increments')): print('Output step {}/{}'.format(i+1,len(results.increments))) - header = '1 header\n' - - data = np.array([int(inc[3:]) for j in range(np.product(results.grid))]).reshape([np.product(results.grid),1]) - header+= 'inc' - - coords = coords.reshape([np.product(results.grid),3]) - data = np.concatenate((data,coords),1) - header+=' 1_pos 2_pos 3_pos' + 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) for label in options.con: x = results.get_dataset_location(label) - if len(x) == 0: - continue - array = results.read_dataset(x,0,plain=True) - d = np.product(np.shape(array)[1:]) - data = np.concatenate((data,np.reshape(array,[np.product(results.grid),d])),1) - - if d>1: - header+= ''.join([' {}_{}'.format(j+1,label) for j in range(d)]) - else: - header+=' '+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) for label in options.mat: x = results.get_dataset_location(label) - if len(x) == 0: - continue - array = results.read_dataset(x,0,plain=True) - d = np.product(np.shape(array)[1:]) - data = np.concatenate((data,np.reshape(array,[np.product(results.grid),d])),1) - - if d>1: - header+= ''.join([' {}_{}'.format(j+1,label) for j in range(d)]) - else: - header+=' '+label + if len(x) != 0: + table.add(label,results.read_dataset(x,0,plain=True).reshape((results.grid.prod(),-1))) dirname = os.path.abspath(os.path.join(os.path.dirname(filename),options.dir)) if not os.path.isdir(dirname): os.mkdir(dirname,0o755) file_out = '{}_inc{}.txt'.format(os.path.splitext(os.path.split(filename)[-1])[0], inc[3:].zfill(N_digits)) - np.savetxt(os.path.join(dirname,file_out),data,header=header,comments='') + table.to_ASCII(os.path.join(dirname,file_out)) From b4ae91f81770904682811ac6da62fc4ddc83fd93 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 22 Dec 2019 07:43:07 +0100 Subject: [PATCH 131/223] proper alignment --- env/DAMASK.csh | 2 +- env/DAMASK.sh | 2 +- env/DAMASK.zsh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/env/DAMASK.csh b/env/DAMASK.csh index 1b16e444b..b1b9dfb98 100644 --- a/env/DAMASK.csh +++ b/env/DAMASK.csh @@ -19,7 +19,7 @@ endif # still, http://software.intel.com/en-us/forums/topic/501500 suggest to fix it # more info https://jblevins.org/log/segfault # https://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap -# http://superuser.com/questions/220059/what-parameters-has-ulimit +# http://superuser.com/questions/220059/what-parameters-has-ulimit limit stacksize unlimited # maximum stack size (kB) # disable output in case of scp diff --git a/env/DAMASK.sh b/env/DAMASK.sh index 56696a0e8..50760b76d 100644 --- a/env/DAMASK.sh +++ b/env/DAMASK.sh @@ -47,7 +47,7 @@ PROCESSING=$(type -p postResults || true 2>/dev/null) # still, http://software.intel.com/en-us/forums/topic/501500 suggest to fix it # more info https://jblevins.org/log/segfault # https://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap -# http://superuser.com/questions/220059/what-parameters-has-ulimit +# http://superuser.com/questions/220059/what-parameters-has-ulimit ulimit -s unlimited 2>/dev/null # maximum stack size (kB) # disable output in case of scp diff --git a/env/DAMASK.zsh b/env/DAMASK.zsh index 8ac97fe18..066d56dd6 100644 --- a/env/DAMASK.zsh +++ b/env/DAMASK.zsh @@ -38,7 +38,7 @@ PROCESSING=$(which postResults || true 2>/dev/null) # still, http://software.intel.com/en-us/forums/topic/501500 suggest to fix it # more info https://jblevins.org/log/segfault # https://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap -# http://superuser.com/questions/220059/what-parameters-has-ulimit +# http://superuser.com/questions/220059/what-parameters-has-ulimit ulimit -s unlimited 2>/dev/null # maximum stack size (kB) # disable output in case of scp From 48c21045d7e7335b90b1c7633c7a749db0c5fe40 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 22 Dec 2019 09:04:50 +0100 Subject: [PATCH 132/223] centralized functionality for ang import --- processing/misc/ang_toTable.py | 49 ++------- python/damask/table.py | 50 ++++++++- python/tests/reference/Table/simple.ang | 138 ++++++++++++++++++++++++ python/tests/test_Table.py | 11 ++ 4 files changed, 204 insertions(+), 44 deletions(-) create mode 100644 python/tests/reference/Table/simple.ang diff --git a/processing/misc/ang_toTable.py b/processing/misc/ang_toTable.py index 19fdcd55b..5579f2466 100755 --- a/processing/misc/ang_toTable.py +++ b/processing/misc/ang_toTable.py @@ -1,8 +1,10 @@ -#!/usr/bin/env python2.7 -# -*- coding: UTF-8 no BOM -*- +#!/usr/bin/env python3 import os +import sys +from io import StringIO from optparse import OptionParser + import damask scriptName = os.path.splitext(os.path.basename(__file__))[0] @@ -19,47 +21,10 @@ Convert TSL/EDAX *.ang file to ASCIItable """, version = scriptID) (options, filenames) = parser.parse_args() - -# --- loop over input files ------------------------------------------------------------------------- - if filenames == []: filenames = [None] for name in filenames: - try: - table = damask.ASCIItable(name = name, - outname = os.path.splitext(name)[0]+'.txt' if name else name, - buffered = False, labeled = False) - except: continue - damask.util.report(scriptName,name) + damask.util.report(scriptName,name) -# --- interpret header ----------------------------------------------------------------------------- - - table.head_read() - -# --- read comments -------------------------------------------------------------------------------- - - table.info_clear() - while table.data_read(advance = False) and table.line.startswith('#'): # cautiously (non-progressing) read header - table.info_append(table.line) # add comment to info part - table.data_read() # wind forward - - table.labels_clear() - table.labels_append(['1_Euler','2_Euler','3_Euler', - '1_pos','2_pos', - 'IQ','CI','PhaseID','Intensity','Fit', - ], # OIM Analysis 7.2 Manual, p 403 (of 517) - reset = True) - -# ------------------------------------------ assemble header --------------------------------------- - - table.head_write() - -#--- write remainder of data file ------------------------------------------------------------------ - - outputAlive = True - while outputAlive and table.data_read(): - outputAlive = table.data_write() - -# ------------------------------------------ finalize output --------------------------------------- - - table.close() + table = damask.Table.from_ang(StringIO(''.join(sys.stdin.read())) if name is None else name) + table.to_ASCII(sys.stdout if name is None else os.path.splitext(name)[0]+'.txt') diff --git a/python/damask/table.py b/python/damask/table.py index 5aa74106c..a5ce50237 100644 --- a/python/damask/table.py +++ b/python/damask/table.py @@ -3,6 +3,8 @@ import re import pandas as pd import numpy as np +from . import version + class Table(): """Store spreadsheet-like data.""" @@ -20,7 +22,7 @@ class Table(): Additional, human-readable information. """ - self.comments = [] if comments is None else [c for c in comments] + self.comments = ['table.py v {}'.format(version)] if not comments else [c for c in comments] self.data = pd.DataFrame(data=data) self.shapes = shapes self.__label_condensed() @@ -69,13 +71,16 @@ class Table(): f = open(fname) except TypeError: f = fname + f.seek(0) header,keyword = f.readline().split() if keyword == 'header': header = int(header) else: raise Exception - comments = [f.readline()[:-1] for i in range(1,header)] + + comments = ['table.py:from_ASCII v {}'.format(version)] + comments+= [f.readline()[:-1] for i in range(1,header)] labels = f.readline().split() shapes = {} @@ -95,6 +100,47 @@ class Table(): return Table(data,shapes,comments) + @staticmethod + def from_ang(fname): + """ + Create table from TSL ang file. + + A valid TSL ang file needs to contains the following columns: + * Euler angles (Bunge notation) in radians, 3 floats, label 'eu'. + * Spatial position in meters, 2 floats, label 'pos'. + * Image quality, 1 float, label 'IQ'. + * Confidence index, 1 float, label 'CI'. + * Phase ID, 1 int, label 'ID'. + * SEM signal, 1 float, label 'intensity'. + * Fit, 1 float, label 'fit'. + + Parameters + ---------- + fname : file, str, or pathlib.Path + Filename or file for reading. + + """ + shapes = {'eu':(3,), 'pos':(2,), + 'IQ':(1,), 'CI':(1,), 'ID':(1,), 'intensity':(1,), 'fit':(1,)} + try: + f = open(fname) + except TypeError: + f = fname + f.seek(0) + + content = f.readlines() + + comments = ['table.py:from_ang v {}'.format(version)] + for line in content: + if line.startswith('#'): + comments.append(line.strip()) + else: + break + + data = np.loadtxt(content) + + return Table(data,shapes,comments) + @property def labels(self): return list(self.shapes.keys()) diff --git a/python/tests/reference/Table/simple.ang b/python/tests/reference/Table/simple.ang new file mode 100644 index 000000000..8e009e2dc --- /dev/null +++ b/python/tests/reference/Table/simple.ang @@ -0,0 +1,138 @@ +# TEM_PIXperUM 1.000000 +# x-star 240.000000 +# y-star 240.000000 +# z-star 240.000000 +# WorkingDistance 20.000000 +# +# Phase 1 +# MaterialName Iron(Alpha) +# Formula +# Info +# Symmetry 43 +# LatticeConstants 2.870 2.870 2.870 90.000 90.000 90.000 +# NumberFamilies 100 +# hklFamilies 9223440 0 2 32763 0.000000 32763 +# hklFamilies 0 0 0 9218712 0.000000 9218712 +# hklFamilies 0 0 3801155 0 0.000000 0 +# hklFamilies 5570652 6619251 7536754 -1203738484 0.000000 -1203738484 +# hklFamilies 7143516 5111900 7864421 32763 0.000000 32763 +# hklFamilies 6488180 7274604 6553717 9220480 0.000000 9220480 +# hklFamilies 3145820 2949169 3145777 0 0.000000 0 +# hklFamilies 3014704 7209057 103 9220488 0.000000 9220488 +# hklFamilies 0 0 0 0 0.000000 0 +# hklFamilies 0 0 0 9220032 0.000000 9220032 +# hklFamilies 0 0 0 0 0.000000 0 +# hklFamilies 0 0 0 -1203728363 0.000000 -1203728363 +# hklFamilies 0 0 0 32763 0.000000 32763 +# hklFamilies 0 0 0 9218628 0.000000 9218628 +# hklFamilies 0 0 0 0 0.000000 0 +# hklFamilies 0 0 0 9218504 0.000000 9218504 +# hklFamilies 0 0 0 0 0.000000 0 +# hklFamilies 0 0 0 9219904 0.000000 9219904 +# hklFamilies 0 0 0 0 0.000000 0 +# hklFamilies 0 0 0 0 -0.000046 0 +# hklFamilies 0 0 0 0 0.000000 0 +# hklFamilies 0 0 0 256 0.000000 256 +# hklFamilies 0 0 0 0 0.000000 0 +# hklFamilies 0 0 0 -1203753636 0.000000 -1203753636 +# hklFamilies 0 0 0 32763 0.000000 32763 +# hklFamilies 0 0 0 9220576 0.000000 9220576 +# hklFamilies 0 0 0 0 0.000000 0 +# hklFamilies 0 0 0 9218736 0.000000 9218736 +# hklFamilies 0 0 0 0 0.000000 0 +# hklFamilies 0 0 0 103219574 0.000000 103219574 +# hklFamilies 0 0 0 0 0.000000 0 +# hklFamilies 0 0 0 9220576 0.000000 9220576 +# hklFamilies 0 0 0 0 0.000000 0 +# hklFamilies 0 0 0 9220692 0.000000 9220692 +# hklFamilies 1434293657 0 0 0 0.000000 0 +# hklFamilies 0 0 0 9218584 0.000000 9218584 +# hklFamilies 0 0 0 0 0.000000 0 +# hklFamilies 0 0 0 9219976 0.000000 9219976 +# hklFamilies 0 0 0 0 0.000000 0 +# hklFamilies 0 0 0 0 0.000000 0 +# hklFamilies 0 0 0 0 0.000000 0 +# hklFamilies 0 0 0 256 0.000000 256 +# hklFamilies 0 0 69473872 0 0.000000 0 +# hklFamilies 0 1889785611 -1546188227 -1203753636 -0.000046 -1203753636 +# hklFamilies 9224144 0 1434294456 32763 0.000000 32763 +# hklFamilies 0 9224160 0 9220672 0.000000 9220672 +# hklFamilies -1168390977 32763 851982 0 0.000000 0 +# hklFamilies 0 304 0 9218816 0.000000 9218816 +# hklFamilies 27030208 0 1434297593 0 0.000000 0 +# hklFamilies 0 9224160 0 101654020 0.000000 101654020 +# hklFamilies 9224064 0 0 0 0.000000 0 +# hklFamilies 0 25563456 0 9220672 0.000000 9220672 +# hklFamilies 9224544 0 25559040 0 0.000000 0 +# hklFamilies 0 25559788 0 9220788 0.000000 9220788 +# hklFamilies 176 0 304 24 0.000000 24 +# hklFamilies 0 25562304 0 4 0.000000 4 +# hklFamilies 9224208 0 0 0 0.000000 0 +# hklFamilies 0 281 0 9220032 0.000000 9220032 +# hklFamilies 0 0 0 0 0.000000 0 +# hklFamilies 0 -1168390977 32763 9220660 0.000000 9220660 +# hklFamilies 21 0 -1168390977 8 0.000000 8 +# hklFamilies 32763 2490388 0 24 0.000000 24 +# hklFamilies 48 0 69650048 25 0.000000 25 +# hklFamilies 0 -1216995621 32763 65535 -0.000046 65535 +# hklFamilies 0 0 25562688 1 0.000000 1 +# hklFamilies 0 0 21776 0 -0.000058 0 +# hklFamilies 25562688 0 25559724 0 0.000000 0 +# hklFamilies 0 25559040 0 1179652 0.000000 1179652 +# hklFamilies 25559724 0 25562304 32763 0.000000 32763 +# hklFamilies 0 48 0 9219904 0.000000 9219904 +# hklFamilies 25562304 0 28 0 0.000000 0 +# hklFamilies 0 0 0 8781958 0.000000 8781958 +# hklFamilies 31 0 0 0 0.000000 0 +# hklFamilies 0 0 0 103304392 0.000000 103304392 +# hklFamilies 3 0 48 0 0.000000 0 +# hklFamilies 0 9224505 0 103219694 -0.000046 103219694 +# hklFamilies 27000832 0 -1168393705 0 0.000000 0 +# hklFamilies 32763 25559040 0 9220192 0.000000 9220192 +# hklFamilies 0 32763 31 0 0.000000 0 +# hklFamilies 0 0 0 9219872 0.000000 9219872 +# hklFamilies 69729712 0 9224640 0 0.000000 0 +# hklFamilies 0 69729904 0 1397706823 0.000000 1397706823 +# hklFamilies 69911504 0 0 59 0.000000 59 +# hklFamilies 0 27007968 0 103219200 0.000000 103219200 +# hklFamilies 0 0 -1216843775 0 0.000000 0 +# hklFamilies 32763 69911504 0 0 0.000000 0 +# hklFamilies -1168296496 32763 9225328 0 0.000000 0 +# hklFamilies 0 1434343267 0 9632160 0.000000 9632160 +# hklFamilies 69908840 0 -1216995621 0 0.000000 0 +# hklFamilies 32763 256 0 9632112 0.000000 9632112 +# hklFamilies 0 0 399376220 0 0.000000 0 +# hklFamilies 21776 1966087 4456474 262148 0.000000 262148 +# hklFamilies 9224704 0 1434198234 0 0.000000 0 +# hklFamilies 0 0 0 9704044 0.000000 9704044 +# hklFamilies -1168373699 32763 1 0 0.000000 0 +# hklFamilies 0 69911504 0 94961568 -0.000046 94961568 +# hklFamilies 1 0 69911504 0 0.000000 0 +# hklFamilies 0 10 0 9220016 0.000000 9220016 +# hklFamilies -1 0 27030208 0 0.000000 0 +# hklFamilies 0 1434488087 18 9219992 -0.000046 9219992 +# ElasticConstants 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 +# ElasticConstants 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 +# ElasticConstants 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 +# ElasticConstants 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 +# ElasticConstants 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 +# ElasticConstants 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 +# Categories1 1 1 1 1 +# +# GRID: SqrGrid +# XSTEP: 0.050000 +# YSTEP: 0.050000 +# NCOLS_ODD: 2 +# NCOLS_EVEN: 2 +# NROWS: 2 +# +# OPERATOR: +# +# SAMPLEID: +# +# SCANID: +# +0.0 0.0 0.0 0.00 0.00 60.0 20.0 1 2.0 1.5 +0.0 2.0 0.0 0.05 0.00 60.0 20.0 1 2.0 1.5 +0.0 2.0 0.0 0.00 0.05 60.0 20.0 1 2.0 1.5 +0.0 0.0 1.0 0.05 0.05 60.0 20.0 1 2.0 1.5 diff --git a/python/tests/test_Table.py b/python/tests/test_Table.py index 2046d3803..818a55f40 100644 --- a/python/tests/test_Table.py +++ b/python/tests/test_Table.py @@ -47,6 +47,17 @@ class TestTable: new = Table.from_ASCII(f) assert all(default.data==new.data) + def test_read_ang_str(self,reference_dir): + new = Table.from_ang(os.path.join(reference_dir,'simple.ang')) + assert new.data.shape == (4,10) and \ + new.labels == ['eu', 'pos', 'IQ', 'CI', 'ID', 'intensity', 'fit'] + + def test_read_ang_file(self,reference_dir): + f = open(os.path.join(reference_dir,'simple.ang')) + new = Table.from_ang(f) + assert new.data.shape == (4,10) and \ + new.labels == ['eu', 'pos', 'IQ', 'CI', 'ID', 'intensity', 'fit'] + @pytest.mark.parametrize('fname',['datatype-mix.txt','whitespace-mix.txt']) def test_read_strange(self,reference_dir,fname): with open(os.path.join(reference_dir,fname)) as f: From 70cb1f07642e64726a13c4468552ea88ccc8567b Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 22 Dec 2019 16:01:39 +0100 Subject: [PATCH 133/223] copy of addGridData W/O functionality for meshes --- processing/post/vtk_addRectilinearGridData.py | 199 ------------------ 1 file changed, 199 deletions(-) delete mode 100755 processing/post/vtk_addRectilinearGridData.py diff --git a/processing/post/vtk_addRectilinearGridData.py b/processing/post/vtk_addRectilinearGridData.py deleted file mode 100755 index 6f5e44e35..000000000 --- a/processing/post/vtk_addRectilinearGridData.py +++ /dev/null @@ -1,199 +0,0 @@ -#!/usr/bin/env python3 - -import os -from optparse import OptionParser - -from collections import defaultdict - -import vtk -from vtk.util import numpy_support - -import damask - - -scriptName = os.path.splitext(os.path.basename(__file__))[0] -scriptID = ' '.join([scriptName,damask.version]) - - -# -------------------------------------------------------------------- -# MAIN -# -------------------------------------------------------------------- - -msg = "Add scalars, vectors, and/or an RGB tuple from" -msg += "an ASCIItable to existing VTK rectilinear grid (.vtr/.vtk)." -parser = OptionParser(option_class=damask.extendableOption, - usage='%prog options [file[s]]', - description = msg, - version = scriptID) - -parser.add_option( '--vtk', - dest = 'vtk', - type = 'string', metavar = 'string', - help = 'VTK file name') -parser.add_option('-r', '--render', - dest = 'render', - action = 'store_true', - help = 'open output in VTK render window') -parser.add_option('-d', '--data', - dest = 'data', - action = 'extend', metavar = '', - help = 'scalar/vector value(s) label(s)') -parser.add_option('-t', '--tensor', - dest = 'tensor', - action = 'extend', metavar = '', - help = 'tensor (3x3) value label(s)') -parser.add_option('-c', '--color', - dest = 'color', - action = 'extend', metavar = '', - help = 'RGB color tuple label') - -parser.set_defaults(data = [], - tensor = [], - color = [], - render = False, -) - -(options, filenames) = parser.parse_args() - -if not options.vtk: parser.error('no VTK file specified.') -if not os.path.exists(options.vtk): parser.error('VTK file does not exist.') - -vtk_file,vtk_ext = os.path.splitext(options.vtk) -if vtk_ext == '.vtr': - reader = vtk.vtkXMLRectilinearGridReader() - reader.SetFileName(options.vtk) - reader.Update() - rGrid = reader.GetOutput() -elif vtk_ext == '.vtk': - reader = vtk.vtkGenericDataObjectReader() - reader.SetFileName(options.vtk) - reader.Update() - rGrid = reader.GetRectilinearGridOutput() -else: - parser.error('unsupported VTK file type extension.') - -Npoints = rGrid.GetNumberOfPoints() -Ncells = rGrid.GetNumberOfCells() - -damask.util.croak('{}: {} points and {} cells...'.format(options.vtk,Npoints,Ncells)) - -# --- loop over input files ------------------------------------------------------------------------- - -if filenames == []: filenames = [None] - -for name in filenames: - try: table = damask.ASCIItable(name = name, - buffered = False, - readonly = True) - except: continue - damask.util.report(scriptName, name) - -# --- interpret header ---------------------------------------------------------------------------- - - table.head_read() - - remarks = [] - errors = [] - VTKarray = {} - active = defaultdict(list) - - for datatype,dimension,label in [['data',0,options.data], - ['tensor',9,options.tensor], - ['color' ,3,options.color], - ]: - for i,dim in enumerate(table.label_dimension(label)): - me = label[i] - if dim == -1: remarks.append('{} "{}" not found...'.format(datatype,me)) - elif dimension > 0 \ - and dim != dimension: remarks.append('"{}" not of dimension {}...'.format(me,dimension)) - else: - remarks.append('adding {}{} "{}"...'.format(datatype if dim > 1 else 'scalar', - '' if dimension > 0 or dim == 1 else '[{}]'.format(dim), - me)) - active[datatype].append(me) - - if remarks != []: damask.util.croak(remarks) - if errors != []: - damask.util.croak(errors) - table.close(dismiss = True) - continue - -# ------------------------------------------ process data --------------------------------------- - - table.data_readArray([item for sublist in active.values() for item in sublist]) # read all requested data - - for datatype,labels in active.items(): # loop over scalar,color - for me in labels: # loop over all requested items - VTKtype = vtk.VTK_DOUBLE - VTKdata = table.data[:, table.label_indexrange(me)].copy() # copy to force contiguous layout - - if datatype == 'color': - VTKtype = vtk.VTK_UNSIGNED_CHAR - VTKdata = (VTKdata*255).astype(int) # translate to 0..255 UCHAR - elif datatype == 'tensor': - VTKdata[:,1] = VTKdata[:,3] = 0.5*(VTKdata[:,1]+VTKdata[:,3]) - VTKdata[:,2] = VTKdata[:,6] = 0.5*(VTKdata[:,2]+VTKdata[:,6]) - VTKdata[:,5] = VTKdata[:,7] = 0.5*(VTKdata[:,5]+VTKdata[:,7]) - - VTKarray[me] = numpy_support.numpy_to_vtk(num_array=VTKdata,deep=True,array_type=VTKtype) - VTKarray[me].SetName(me) - - table.close() # close input ASCII table - -# ------------------------------------------ add data --------------------------------------- - - if len(table.data) == Npoints: mode = 'point' - elif len(table.data) == Ncells: mode = 'cell' - else: - damask.util.croak('data count is incompatible with grid...') - continue - - damask.util.croak('{} mode...'.format(mode)) - - for datatype,labels in active.items(): # loop over scalar,color - if datatype == 'color': - if mode == 'cell': rGrid.GetCellData().SetScalars(VTKarray[active['color'][0]]) - elif mode == 'point': rGrid.GetPointData().SetScalars(VTKarray[active['color'][0]]) - for me in labels: # loop over all requested items - if mode == 'cell': rGrid.GetCellData().AddArray(VTKarray[me]) - elif mode == 'point': rGrid.GetPointData().AddArray(VTKarray[me]) - - rGrid.Modified() - -# ------------------------------------------ output result --------------------------------------- - - writer = vtk.vtkXMLRectilinearGridWriter() - writer.SetDataModeToBinary() - writer.SetCompressorTypeToZLib() - writer.SetFileName(vtk_file+'.'+writer.GetDefaultFileExtension()) - writer.SetInputData(rGrid) - writer.Write() - -# ------------------------------------------ render result --------------------------------------- - -if options.render: - mapper = vtk.vtkDataSetMapper() - mapper.SetInputData(rGrid) - actor = vtk.vtkActor() - actor.SetMapper(mapper) - -# Create the graphics structure. The renderer renders into the -# render window. The render window interactor captures mouse events -# and will perform appropriate camera or actor manipulation -# depending on the nature of the events. - - ren = vtk.vtkRenderer() - - renWin = vtk.vtkRenderWindow() - renWin.AddRenderer(ren) - - ren.AddActor(actor) - ren.SetBackground(1, 1, 1) - renWin.SetSize(200, 200) - - iren = vtk.vtkRenderWindowInteractor() - iren.SetRenderWindow(renWin) - - iren.Initialize() - renWin.Render() - iren.Start() From c3c08b5b53cde18091bc93d73b8bfee5095fa277 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 22 Dec 2019 16:16:04 +0100 Subject: [PATCH 134/223] using new class/unifying style --- processing/post/vtk_addGridData.py | 82 ++++++-------------- processing/post/vtk_addPointCloudData.py | 96 +++++++----------------- 2 files changed, 48 insertions(+), 130 deletions(-) diff --git a/processing/post/vtk_addGridData.py b/processing/post/vtk_addGridData.py index b9beb6abe..1596cd7fe 100755 --- a/processing/post/vtk_addGridData.py +++ b/processing/post/vtk_addGridData.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import os +import sys from optparse import OptionParser from collections import defaultdict @@ -18,11 +19,10 @@ scriptID = ' '.join([scriptName,damask.version]) # MAIN # -------------------------------------------------------------------- -msg = "Add scalars, vectors, and/or an RGB tuple from" -msg += "an ASCIItable to existing VTK grid (.vtr/.vtk/.vtu)." parser = OptionParser(option_class=damask.extendableOption, usage='%prog options [ASCIItable(s)]', - description = msg, + description = "Add scalars, vectors, tensors, and/or an RGB tuple from ASCIItable " + + "to existing VTK grid (.vtr/.vtk/.vtu).", version = scriptID) parser.add_option( '--vtk', @@ -49,10 +49,10 @@ parser.add_option('-c', '--color', parser.set_defaults(data = [], tensor = [], color = [], - render = False, ) (options, filenames) = parser.parse_args() +if filenames == []: filenames = [None] if not options.vtk: parser.error('No VTK file specified.') if not os.path.exists(options.vtk): parser.error('VTK file does not exist.') @@ -87,65 +87,28 @@ Ncells = rGrid.GetNumberOfCells() damask.util.croak('{}: {} points and {} cells...'.format(options.vtk,Npoints,Ncells)) -# --- loop over input files ------------------------------------------------------------------------- - -if filenames == []: filenames = [None] - for name in filenames: - try: table = damask.ASCIItable(name = name, - buffered = False, - readonly = True) - except: continue - damask.util.report(scriptName, name) + damask.util.report(scriptName,name) -# --- interpret header ---------------------------------------------------------------------------- - - table.head_read() - - remarks = [] - errors = [] + table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) + VTKarray = {} - active = defaultdict(list) + for data in options.data: + VTKarray[data] = numpy_support.numpy_to_vtk(table.get(data).copy(), + deep=True,array_type=vtk.VTK_DOUBLE) + VTKarray[data].SetName(data) + + for color in options.color: + VTKarray[color] = numpy_support.numpy_to_vtk((table.get(color)*255).astype(int).copy(), + deep=True,array_type=vtk.VTK_UNSIGNED_CHAR) + VTKarray[color].SetName(color) - for datatype,dimension,label in [['data',99,options.data], - ['tensor',9,options.tensor], - ['color' ,3,options.color], - ]: - for i,dim in enumerate(table.label_dimension(label)): - me = label[i] - if dim == -1: remarks.append('{} "{}" not found...'.format(datatype,me)) - elif dim > dimension: remarks.append('"{}" not of dimension {}...'.format(me,dimension)) - else: - remarks.append('adding {} "{}"...'.format(datatype,me)) - active[datatype].append(me) + for tensor in options.tensor: + data = damask.mechanics.symmetric(table.get(tensor).reshape((-1,3,3))).reshape((-1,9)) + VTKarray[tensor] = numpy_support.numpy_to_vtk(data.copy(), + deep=True,array_type=vtk.VTK_DOUBLE) + VTKarray[tensor].SetName(tensor) - if remarks != []: damask.util.croak(remarks) - if errors != []: - damask.util.croak(errors) - table.close(dismiss = True) - continue - -# ------------------------------------------ process data --------------------------------------- - - table.data_readArray([item for sublist in active.values() for item in sublist]) # read all requested data - - for datatype,labels in active.items(): # loop over scalar,color - for me in labels: # loop over all requested items - VTKtype = vtk.VTK_DOUBLE - VTKdata = table.data[:, table.label_indexrange(me)].copy() # copy to force contiguous layout - - if datatype == 'color': - VTKtype = vtk.VTK_UNSIGNED_CHAR - VTKdata = (VTKdata*255).astype(int) # translate to 0..255 UCHAR - elif datatype == 'tensor': - VTKdata[:,1] = VTKdata[:,3] = 0.5*(VTKdata[:,1]+VTKdata[:,3]) - VTKdata[:,2] = VTKdata[:,6] = 0.5*(VTKdata[:,2]+VTKdata[:,6]) - VTKdata[:,5] = VTKdata[:,7] = 0.5*(VTKdata[:,5]+VTKdata[:,7]) - - VTKarray[me] = numpy_support.numpy_to_vtk(num_array=VTKdata,deep=True,array_type=VTKtype) - VTKarray[me].SetName(me) - - table.close() # close input ASCII table # ------------------------------------------ add data --------------------------------------- @@ -166,7 +129,6 @@ for name in filenames: elif mode == 'point': rGrid.GetPointData().AddArray(VTKarray[me]) rGrid.Modified() - if vtk.VTK_MAJOR_VERSION <= 5: rGrid.Update() # ------------------------------------------ output result --------------------------------------- @@ -184,7 +146,7 @@ if options.render: actor.SetMapper(mapper) # Create the graphics structure. The renderer renders into the -# render window. The render window interactor captures mouse events +# render window. The render window interactively captures mouse events # and will perform appropriate camera or actor manipulation # depending on the nature of the events. diff --git a/processing/post/vtk_addPointCloudData.py b/processing/post/vtk_addPointCloudData.py index 96bacae8a..5a40d967a 100755 --- a/processing/post/vtk_addPointCloudData.py +++ b/processing/post/vtk_addPointCloudData.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import os +import sys from optparse import OptionParser from collections import defaultdict @@ -20,7 +21,8 @@ scriptID = ' '.join([scriptName,damask.version]) parser = OptionParser(option_class=damask.extendableOption, usage='%prog options [ASCIItable(s)]', - description = """Add scalar and RGB tuples from ASCIItable to existing VTK point cloud (.vtp).""", + description = "Add scalars, vectors, tensors, and/or an RGB tuple from ASCIItable " + + "VTK point cloud (.vtp).", version = scriptID) parser.add_option( '--vtk', @@ -39,9 +41,10 @@ parser.add_option('-t', '--tensor', dest = 'tensor', action = 'extend', metavar = '', help = 'tensor (3x3) value label(s)') -parser.add_option('-c', '--color', dest='color', action='extend', - metavar ='', - help = 'RGB color tuples') +parser.add_option('-c', '--color', + dest = 'color', + action = 'extend', metavar = '', + help = 'RGB color tuple label') parser.set_defaults(data = [], tensor = [], @@ -49,8 +52,9 @@ parser.set_defaults(data = [], ) (options, filenames) = parser.parse_args() +if filenames == []: filenames = [None] -if not options.vtk: parser.error('no VTK file specified.') +if not options.vtk: parser.error('No VTK file specified.') if not os.path.exists(options.vtk): parser.error('VTK file does not exist.') vtk_file,vtk_ext = os.path.splitext(options.vtk) @@ -77,76 +81,28 @@ if Npoints != Ncells or Npoints != Nvertices: damask.util.croak('{}: {} points/vertices/cells...'.format(options.vtk,Npoints)) -# --- loop over input files ------------------------------------------------------------------------- - -if filenames == []: filenames = [None] - for name in filenames: - try: table = damask.ASCIItable(name = name, - buffered = False, - readonly = True) - except: continue - damask.util.report(scriptName, name) + damask.util.report(scriptName,name) -# --- interpret header ---------------------------------------------------------------------------- - - table.head_read() - - remarks = [] - errors = [] + table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) + VTKarray = {} - active = defaultdict(list) + for data in options.data: + VTKarray[data] = numpy_support.numpy_to_vtk(table.get(data).copy(), + deep=True,array_type=vtk.VTK_DOUBLE) + VTKarray[data].SetName(data) + + for color in options.color: + VTKarray[color] = numpy_support.numpy_to_vtk((table.get(color)*255).astype(int).copy(), + deep=True,array_type=vtk.VTK_UNSIGNED_CHAR) + VTKarray[color].SetName(color) - for datatype,dimension,label in [['data',0,options.data], - ['tensor',9,options.tensor], - ['color' ,3,options.color], - ]: - for i,dim in enumerate(table.label_dimension(label)): - me = label[i] - if dim == -1: remarks.append('{} "{}" not found...'.format(datatype,me)) - elif dimension > 0 \ - and dim != dimension: remarks.append('"{}" not of dimension {}...'.format(me,dimension)) - else: - remarks.append('adding {}{} "{}"...'.format(datatype if dim > 1 else 'scalar', - '' if dimension > 0 or dim == 1 else '[{}]'.format(dim), - me)) - active[datatype].append(me) + for tensor in options.tensor: + data = damask.mechanics.symmetric(table.get(tensor).reshape((-1,3,3))).reshape((-1,9)) + VTKarray[tensor] = numpy_support.numpy_to_vtk(data.copy(), + deep=True,array_type=vtk.VTK_DOUBLE) + VTKarray[tensor].SetName(tensor) - if remarks != []: damask.util.croak(remarks) - if errors != []: - damask.util.croak(errors) - table.close(dismiss = True) - continue - -# --------------------------------------- process and add data ----------------------------------- - - table.data_readArray([item for sublist in active.values() for item in sublist]) # read all requested data - - for datatype,labels in active.items(): # loop over scalar,color - for me in labels: # loop over all requested items - VTKtype = vtk.VTK_DOUBLE - VTKdata = table.data[:, table.label_indexrange(me)].copy() # copy to force contiguous layout - - if datatype == 'color': - VTKtype = vtk.VTK_UNSIGNED_CHAR - VTKdata = (VTKdata*255).astype(int) # translate to 0..255 UCHAR - elif datatype == 'tensor': - VTKdata[:,1] = VTKdata[:,3] = 0.5*(VTKdata[:,1]+VTKdata[:,3]) - VTKdata[:,2] = VTKdata[:,6] = 0.5*(VTKdata[:,2]+VTKdata[:,6]) - VTKdata[:,5] = VTKdata[:,7] = 0.5*(VTKdata[:,5]+VTKdata[:,7]) - - VTKarray[me] = numpy_support.numpy_to_vtk(num_array=VTKdata,deep=True,array_type=VTKtype) - VTKarray[me].SetName(me) - - if datatype == 'color': - Polydata.GetPointData().SetScalars(VTKarray[me]) - Polydata.GetCellData().SetScalars(VTKarray[me]) - else: - Polydata.GetPointData().AddArray(VTKarray[me]) - Polydata.GetCellData().AddArray(VTKarray[me]) - - - table.input_close() # close input ASCII table # ------------------------------------------ output result --------------------------------------- From 2cb3213e37967e708abc52cb2f03908795adb0b2 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 22 Dec 2019 18:11:01 +0100 Subject: [PATCH 135/223] improvements to Table class - string comparison should be '!=' not 'is not', latter compares object, not value - functions for common operations: append (vstack, growTable) and join (hstack, addTable) --- python/damask/table.py | 41 +++++++++++++++++++++++++++++++++++--- python/tests/test_Table.py | 28 ++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/python/damask/table.py b/python/damask/table.py index a5ce50237..28ce3efe0 100644 --- a/python/damask/table.py +++ b/python/damask/table.py @@ -248,17 +248,17 @@ class Table(): '' if info is None else ': {}'.format(info), )) - self.shapes = {(label if label is not label_old else label_new):self.shapes[label] for label in self.shapes} + self.shapes = {(label if label != label_old else label_new):self.shapes[label] for label in self.shapes} def sort_by(self,labels,ascending=True): """ - Get column data. + Sort table by values of given labels. Parameters ---------- label : str or list - Column labels. + Column labels for sorting. ascending : bool or list, optional Set sort order. @@ -269,6 +269,41 @@ class Table(): self.comments.append('sorted by [{}]'.format(', '.join(labels))) + def append(self,other): + """ + Append other table vertically (similar to numpy.vstack). Requires matching shapes and order. + + Parameters + ---------- + other : Table + Table to append + + """ + if self.shapes != other.shapes or not self.data.columns.equals(other.data.columns): + raise KeyError('Labels or shapes or order do not match') + else: + self.data = self.data.append(other.data,ignore_index=True) + + + def join(self,other): + """ + Append other table horizontally (similar to numpy.hstack). Requires matching number of rows + and no common lables + + Parameters + ---------- + other : Table + Table to join + + """ + if set(self.shapes) & set(other.shapes) or self.data.shape[0] != other.data.shape[0]: + raise KeyError('Dublicated keys or row count mismatch') + else: + self.data = self.data.join(other.data) + for key in other.shapes: + self.shapes[key] = other.shapes[key] + + def to_ASCII(self,fname): """ Store as plain text file. diff --git a/python/tests/test_Table.py b/python/tests/test_Table.py index 818a55f40..d5f505e76 100644 --- a/python/tests/test_Table.py +++ b/python/tests/test_Table.py @@ -86,14 +86,42 @@ class TestTable: def test_rename_gone(self,default): default.rename('v','V') + assert 'v' not in default.shapes and 'v' not in default.data.columns with pytest.raises(KeyError): default.get('v') def test_delete(self,default): default.delete('v') + assert 'v' not in default.shapes and 'v' not in default.data.columns with pytest.raises(KeyError): default.get('v') + def test_join(self): + x = np.random.random((5,13)) + a = Table(x,{'F':(3,3),'v':(3,),'s':(1,)},['random test data']) + y = np.random.random((5,3)) + b = Table(y,{'u':(3,)},['random test data']) + a.join(b) + assert np.array_equal(a.get('u'), b.get('u')) + + def test_join_invalid(self): + x = np.random.random((5,13)) + a = Table(x,{'F':(3,3),'v':(3,),'s':(1,)},['random test data']) + with pytest.raises(KeyError): + a.join(a) + + def test_append(self): + x = np.random.random((5,13)) + a = Table(x,{'F':(3,3),'v':(3,),'s':(1,)},['random test data']) + a.append(a) + assert np.array_equal(a.data[:5].to_numpy(),a.data[5:].to_numpy()) + + def test_append_invalid(self): + x = np.random.random((5,13)) + a = Table(x,{'F':(3,3),'v':(3,),'s':(1,)},['random test data']) + b = Table(x,{'F':(3,3),'u':(3,),'s':(1,)},['random test data']) + with pytest.raises(KeyError): + a.append(b) def test_invalid_initialization(self): x = np.random.random((5,10)) From 644967329f9165c1e19125aea75ffac3765d0288 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 22 Dec 2019 22:23:33 +0100 Subject: [PATCH 136/223] improved tests --- PRIVATE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PRIVATE b/PRIVATE index 036faecca..e6d5dfb2f 160000 --- a/PRIVATE +++ b/PRIVATE @@ -1 +1 @@ -Subproject commit 036faecca39b46fd2328597ca858cbb04e37f79a +Subproject commit e6d5dfb2fa8544de93378d60aaf8423409cfd387 From db14baec4123588a25179172ef7cbb9e6ac274e3 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 22 Dec 2019 22:23:48 +0100 Subject: [PATCH 137/223] using central functionality --- processing/post/addTable.py | 57 +++++++----------------------------- processing/post/growTable.py | 53 ++++++--------------------------- 2 files changed, 19 insertions(+), 91 deletions(-) diff --git a/processing/post/addTable.py b/processing/post/addTable.py index 7af1dcf35..c7d34f1e8 100755 --- a/processing/post/addTable.py +++ b/processing/post/addTable.py @@ -25,56 +25,19 @@ parser.add_option('-a', '--add','--table', help = 'tables to add') (options,filenames) = parser.parse_args() - -if options.table is None: - parser.error('no table specified.') - - -# --- loop over input files ------------------------------------------------------------------------- - if filenames == []: filenames = [None] +if options.table is None: + parser.error('no table specified.') + for name in filenames: - try: table = damask.ASCIItable(name = name, - buffered = False) - except: continue + damask.util.report(scriptName,name) - damask.util.report(scriptName,name) + table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) - tables = [] - for addTable in options.table: - try: tables.append(damask.ASCIItable(name = addTable, - buffered = False, - readonly = True) - ) - except: continue + for addTable in options.table: + table2 = damask.Table.from_ASCII(addTable) + table2.data = table2.data[:table.data.shape[0]] + table.join(table2) -# ------------------------------------------ read headers ------------------------------------------ - - table.head_read() - for addTable in tables: addTable.head_read() - -# ------------------------------------------ assemble header -------------------------------------- - - table.info_append(scriptID + '\t' + ' '.join(sys.argv[1:])) - - for addTable in tables: table.labels_append(addTable.labels(raw = True)) # extend ASCII header with new labels - - table.head_write() - -# ------------------------------------------ process data ------------------------------------------ - - outputAlive = True - while outputAlive and table.data_read(): - for addTable in tables: - outputAlive = addTable.data_read() # read next table's data - if not outputAlive: break - table.data_append(addTable.data) # append to master table - if outputAlive: - outputAlive = table.data_write() # output processed line - -# ------------------------------------------ output finalization ----------------------------------- - - table.close() # close ASCII tables - for addTable in tables: - addTable.close() + table.to_ASCII(sys.stdout if name is None else name) diff --git a/processing/post/growTable.py b/processing/post/growTable.py index 361ea5764..4fd647b1e 100755 --- a/processing/post/growTable.py +++ b/processing/post/growTable.py @@ -27,53 +27,18 @@ parser.add_option('-a', '--add','--table', help = 'tables to add') (options,filenames) = parser.parse_args() - -if options.table is None: - parser.error('no table specified.') - - -# --- loop over input files ------------------------------------------------------------------------- - if filenames == []: filenames = [None] +if options.table is None: + parser.error('no table specified.') + for name in filenames: - try: table = damask.ASCIItable(name = name, - buffered = False) - except: continue + damask.util.report(scriptName,name) - damask.util.report(scriptName,name) + table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) - tables = [] - for addTable in options.table: - try: tables.append(damask.ASCIItable(name = addTable, - buffered = False, - readonly = True) - ) - except: continue + for growTable in options.table: + table2 = damask.Table.from_ASCII(growTable) + table.append(table2) -# ------------------------------------------ read headers ------------------------------------------ - - table.head_read() - for addTable in tables: addTable.head_read() - -# ------------------------------------------ assemble header -------------------------------------- - - table.info_append(scriptID + '\t' + ' '.join(sys.argv[1:])) - - table.head_write() - -# ------------------------------------------ process data ------------------------------------------ - - table.data_readArray() - data = table.data - for addTable in tables: - addTable.data_readArray(table.labels(raw = True)) - data = np.vstack((data,addTable.data)) - table.data = data - table.data_writeArray() - -# ------------------------------------------ output finalization ----------------------------------- - - table.close() # close ASCII tables - for addTable in tables: - addTable.close() + table.to_ASCII(sys.stdout if name is None else name) From 149e4d6d73cdd2ffc08795c0881cde7698f88eef Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 23 Dec 2019 05:51:41 +0100 Subject: [PATCH 138/223] not needed --- processing/post/addStrainTensors.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/processing/post/addStrainTensors.py b/processing/post/addStrainTensors.py index 2aa206952..c4d0779e2 100755 --- a/processing/post/addStrainTensors.py +++ b/processing/post/addStrainTensors.py @@ -143,9 +143,6 @@ for name in filenames: for theStretch in stretches: stretch[theStretch] = np.where(abs(stretch[theStretch]) < 1e-12, 0, stretch[theStretch]) # kill nasty noisy data (D,V) = np.linalg.eigh((stretch[theStretch]+stretch[theStretch].T)*0.5) # eigen decomposition (of symmetric(ed) matrix) - neg = np.where(D < 0.0) # find negative eigenvalues ... - D[neg] *= -1. # ... flip value ... - V[:,neg] *= -1. # ... and vector for theStrain in strains: d = operator(theStretch,theStrain,D) # operate on eigenvalues of U or V eps = np.dot(V,np.dot(np.diag(d),V.T)).reshape(9) # build tensor back from eigenvalue/vector basis From 3ed9126845d42e4fd186df885df777f5739b14f4 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 23 Dec 2019 05:52:14 +0100 Subject: [PATCH 139/223] also not needed --- processing/post/addStrainTensors.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/processing/post/addStrainTensors.py b/processing/post/addStrainTensors.py index c4d0779e2..881b53fec 100755 --- a/processing/post/addStrainTensors.py +++ b/processing/post/addStrainTensors.py @@ -19,8 +19,8 @@ def operator(stretch,strain,eigenvalues): 'U#ln': np.log(eigenvalues) , 'V#Biot': ( np.ones(3,'d') - 1.0/eigenvalues ) , 'U#Biot': ( eigenvalues - np.ones(3,'d') ) , - 'V#Green': ( np.ones(3,'d') - 1.0/eigenvalues/eigenvalues) *0.5, - 'U#Green': ( eigenvalues*eigenvalues - np.ones(3,'d')) *0.5, + 'V#Green': ( np.ones(3,'d') - 1.0/eigenvalues**2.0) *0.5, + 'U#Green': ( eigenvalues**2.0 - np.ones(3,'d')) *0.5, }[stretch+'#'+strain] @@ -141,7 +141,6 @@ for name in filenames: stretch['V'] = np.dot(F,R_inv) # F = VR for theStretch in stretches: - stretch[theStretch] = np.where(abs(stretch[theStretch]) < 1e-12, 0, stretch[theStretch]) # kill nasty noisy data (D,V) = np.linalg.eigh((stretch[theStretch]+stretch[theStretch].T)*0.5) # eigen decomposition (of symmetric(ed) matrix) for theStrain in strains: d = operator(theStretch,theStrain,D) # operate on eigenvalues of U or V From 3c4c364107c29dbf88e80e8deecbea2001d4b453 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 23 Dec 2019 06:48:42 +0100 Subject: [PATCH 140/223] bugfix: wrong results when calculating multiple strain tensors. Introduced in cd6a4d1cfd655581fbb022f0d52a868bf2144db8 --- processing/post/addStrainTensors.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/processing/post/addStrainTensors.py b/processing/post/addStrainTensors.py index 881b53fec..8516d9be8 100755 --- a/processing/post/addStrainTensors.py +++ b/processing/post/addStrainTensors.py @@ -15,12 +15,12 @@ scriptID = ' '.join([scriptName,damask.version]) def operator(stretch,strain,eigenvalues): """Albrecht Bertram: Elasticity and Plasticity of Large Deformations An Introduction (3rd Edition, 2012), p. 102.""" return { - 'V#ln': np.log(eigenvalues) , - 'U#ln': np.log(eigenvalues) , - 'V#Biot': ( np.ones(3,'d') - 1.0/eigenvalues ) , - 'U#Biot': ( eigenvalues - np.ones(3,'d') ) , - 'V#Green': ( np.ones(3,'d') - 1.0/eigenvalues**2.0) *0.5, - 'U#Green': ( eigenvalues**2.0 - np.ones(3,'d')) *0.5, + 'V#ln': np.log(eigenvalues) , + 'U#ln': np.log(eigenvalues) , + 'V#Biot': ( np.ones(3,'d') - eigenvalues**-1.0) , + 'U#Biot': ( eigenvalues**1.0 - np.ones(3,'d')) , + 'V#Green': ( np.ones(3,'d') - eigenvalues**-2.0)*0.5, + 'U#Green': ( eigenvalues**2.0 - np.ones(3,'d')) *0.5, }[stretch+'#'+strain] @@ -142,7 +142,7 @@ for name in filenames: for theStretch in stretches: (D,V) = np.linalg.eigh((stretch[theStretch]+stretch[theStretch].T)*0.5) # eigen decomposition (of symmetric(ed) matrix) - for theStrain in strains: + for theStrain in strains: d = operator(theStretch,theStrain,D) # operate on eigenvalues of U or V eps = np.dot(V,np.dot(np.diag(d),V.T)).reshape(9) # build tensor back from eigenvalue/vector basis From 60580cc45adb20dbf1284a3229213ba790ae5bac Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 23 Dec 2019 07:16:50 +0100 Subject: [PATCH 141/223] mixed up left/right stretch tensor --- python/damask/mechanics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/damask/mechanics.py b/python/damask/mechanics.py index 5503d7048..08f5ca125 100644 --- a/python/damask/mechanics.py +++ b/python/damask/mechanics.py @@ -58,10 +58,10 @@ def strain_tensor(F,t,m): """ F_ = F.reshape((1,3,3)) if F.shape == (3,3) else F - if t == 'U': + if t == 'V': B = np.matmul(F_,transpose(F_)) w,n = np.linalg.eigh(B) - elif t == 'V': + elif t == 'U': C = np.matmul(transpose(F_),F_) w,n = np.linalg.eigh(C) From 93bc66d19fbe68adff3c61bcf3455f55fc068b51 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 23 Dec 2019 09:33:14 +0100 Subject: [PATCH 142/223] same order as in mechanics --- processing/post/addStrainTensors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/processing/post/addStrainTensors.py b/processing/post/addStrainTensors.py index 8516d9be8..f35c965ba 100755 --- a/processing/post/addStrainTensors.py +++ b/processing/post/addStrainTensors.py @@ -137,8 +137,8 @@ for name in filenames: F = np.array(list(map(float,table.data[column:column+items['tensor']['dim']])),'d').reshape(items['tensor']['shape']) (U,S,Vh) = np.linalg.svd(F) # singular value decomposition R_inv = np.dot(U,Vh).T # rotation of polar decomposition - stretch['U'] = np.dot(R_inv,F) # F = RU stretch['V'] = np.dot(F,R_inv) # F = VR + stretch['U'] = np.dot(R_inv,F) # F = RU for theStretch in stretches: (D,V) = np.linalg.eigh((stretch[theStretch]+stretch[theStretch].T)*0.5) # eigen decomposition (of symmetric(ed) matrix) From d26005960c2adf04f45c9b2f57d01b2cf9e5839e Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 23 Dec 2019 09:52:42 +0100 Subject: [PATCH 143/223] do not rely on singular value decomposition --- processing/post/addStrainTensors.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/processing/post/addStrainTensors.py b/processing/post/addStrainTensors.py index f35c965ba..7799dcd88 100755 --- a/processing/post/addStrainTensors.py +++ b/processing/post/addStrainTensors.py @@ -15,12 +15,12 @@ scriptID = ' '.join([scriptName,damask.version]) def operator(stretch,strain,eigenvalues): """Albrecht Bertram: Elasticity and Plasticity of Large Deformations An Introduction (3rd Edition, 2012), p. 102.""" return { - 'V#ln': np.log(eigenvalues) , - 'U#ln': np.log(eigenvalues) , - 'V#Biot': ( np.ones(3,'d') - eigenvalues**-1.0) , - 'U#Biot': ( eigenvalues**1.0 - np.ones(3,'d')) , - 'V#Green': ( np.ones(3,'d') - eigenvalues**-2.0)*0.5, - 'U#Green': ( eigenvalues**2.0 - np.ones(3,'d')) *0.5, + 'V#ln': np.log(eigenvalues)*.5 , + 'U#ln': np.log(eigenvalues)*.5 , + 'V#Biot': ( np.ones(3,'d') - eigenvalues**-0.5) , + 'U#Biot': ( eigenvalues**0.5 - np.ones(3,'d')) , + 'V#Green': ( np.ones(3,'d') - eigenvalues**-1.0)*0.5, + 'U#Green': ( eigenvalues**1.0 - np.ones(3,'d')) *0.5, }[stretch+'#'+strain] @@ -135,10 +135,8 @@ for name in filenames: while outputAlive and table.data_read(): # read next data line of ASCII table for column in items['tensor']['column']: # loop over all requested defgrads F = np.array(list(map(float,table.data[column:column+items['tensor']['dim']])),'d').reshape(items['tensor']['shape']) - (U,S,Vh) = np.linalg.svd(F) # singular value decomposition - R_inv = np.dot(U,Vh).T # rotation of polar decomposition - stretch['V'] = np.dot(F,R_inv) # F = VR - stretch['U'] = np.dot(R_inv,F) # F = RU + stretch['V'] = np.dot(F,F.T) # F = VR + stretch['U'] = np.dot(F.T,F) # F = RU for theStretch in stretches: (D,V) = np.linalg.eigh((stretch[theStretch]+stretch[theStretch].T)*0.5) # eigen decomposition (of symmetric(ed) matrix) From 27d6b91f188c38206050e2016be1206422340b1f Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 23 Dec 2019 10:19:38 +0100 Subject: [PATCH 144/223] using central funtionality --- PRIVATE | 2 +- processing/post/addStrainTensors.py | 100 ++++++---------------------- 2 files changed, 23 insertions(+), 79 deletions(-) diff --git a/PRIVATE b/PRIVATE index e6d5dfb2f..cda69f9a5 160000 --- a/PRIVATE +++ b/PRIVATE @@ -1 +1 @@ -Subproject commit e6d5dfb2fa8544de93378d60aaf8423409cfd387 +Subproject commit cda69f9a59fe64223439a2c725e1a78cf22b28aa diff --git a/processing/post/addStrainTensors.py b/processing/post/addStrainTensors.py index 7799dcd88..756761e46 100755 --- a/processing/post/addStrainTensors.py +++ b/processing/post/addStrainTensors.py @@ -12,15 +12,15 @@ import damask scriptName = os.path.splitext(os.path.basename(__file__))[0] scriptID = ' '.join([scriptName,damask.version]) -def operator(stretch,strain,eigenvalues): +def parameters(stretch,strain): """Albrecht Bertram: Elasticity and Plasticity of Large Deformations An Introduction (3rd Edition, 2012), p. 102.""" return { - 'V#ln': np.log(eigenvalues)*.5 , - 'U#ln': np.log(eigenvalues)*.5 , - 'V#Biot': ( np.ones(3,'d') - eigenvalues**-0.5) , - 'U#Biot': ( eigenvalues**0.5 - np.ones(3,'d')) , - 'V#Green': ( np.ones(3,'d') - eigenvalues**-1.0)*0.5, - 'U#Green': ( eigenvalues**1.0 - np.ones(3,'d')) *0.5, + 'V#ln': ('V',0.0), + 'U#ln': ('U',0.0), + 'V#Biot': ('V',-.5), + 'U#Biot': ('U',+.5), + 'V#Green': ('V',-1.), + 'U#Green': ('U',+1.), }[stretch+'#'+strain] @@ -64,9 +64,10 @@ parser.set_defaults( ) (options,filenames) = parser.parse_args() +if filenames == []: filenames = [None] if len(options.defgrad) > 1: - options.defgrad = options.defgrad[1:] + options.defgrad = options.defgrad[1:] stretches = [] strains = [] @@ -78,78 +79,21 @@ if options.biot: strains.append('Biot') if options.green: strains.append('Green') if options.defgrad is None: - parser.error('no data column specified.') - -# --- loop over input files ------------------------------------------------------------------------- - -if filenames == []: filenames = [None] + parser.error('no data column specified.') for name in filenames: - try: - table = damask.ASCIItable(name = name, - buffered = False) - except IOError: continue - damask.util.report(scriptName,name) + damask.util.report(scriptName,name) + + table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) -# ------------------------------------------ read header ------------------------------------------ - - table.head_read() - -# ------------------------------------------ sanity checks ---------------------------------------- - - items = { - 'tensor': {'dim': 9, 'shape': [3,3], 'labels':options.defgrad, '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 defgrad in options.defgrad: + F = table.get(defgrad).reshape((-1,3,3)) for theStretch in stretches: - for theStrain in strains: - table.labels_append(['{}_{}({}){}'.format(i+1, # extend ASCII header with new labels - theStrain, - theStretch, - what if what != 'f' else '') for i in range(9)]) + for theStrain in strains: + (t,m) = parameters(theStretch,theStrain) + label = '{}({}){}'.format(theStrain,theStretch,defgrad if defgrad != 'f' else '') + table.add(label, + damask.mechanics.strain_tensor(F,t,m).reshape((-1,9)), + scriptID+' '+' '.join(sys.argv[1:])) - if remarks != []: damask.util.croak(remarks) - if errors != []: - damask.util.croak(errors) - table.close(dismiss = True) - continue - -# ------------------------------------------ assemble header -------------------------------------- - - table.info_append(scriptID + '\t' + ' '.join(sys.argv[1:])) - table.head_write() - -# ------------------------------------------ process data ------------------------------------------ - - stretch = {} - outputAlive = True - - while outputAlive and table.data_read(): # read next data line of ASCII table - for column in items['tensor']['column']: # loop over all requested defgrads - F = np.array(list(map(float,table.data[column:column+items['tensor']['dim']])),'d').reshape(items['tensor']['shape']) - stretch['V'] = np.dot(F,F.T) # F = VR - stretch['U'] = np.dot(F.T,F) # F = RU - - for theStretch in stretches: - (D,V) = np.linalg.eigh((stretch[theStretch]+stretch[theStretch].T)*0.5) # eigen decomposition (of symmetric(ed) matrix) - for theStrain in strains: - d = operator(theStretch,theStrain,D) # operate on eigenvalues of U or V - eps = np.dot(V,np.dot(np.diag(d),V.T)).reshape(9) # build tensor back from eigenvalue/vector basis - - table.data_append(list(eps)) - -# ------------------------------------------ output result ----------------------------------------- - - outputAlive = table.data_write() # output processed line - -# ------------------------------------------ output finalization ----------------------------------- - - table.close() # close ASCII tables + table.to_ASCII(sys.stdout if name is None else name) From aec9c601d6aa17d8baa7a09796e8d312aba7b8a7 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 23 Dec 2019 11:25:07 +0100 Subject: [PATCH 145/223] some insights from continuum mechanics formulated as test --- python/damask/mechanics.py | 18 ++++++++---- python/tests/test_mechanics.py | 54 +++++++++++++++++++++++++++++++--- 2 files changed, 62 insertions(+), 10 deletions(-) diff --git a/python/damask/mechanics.py b/python/damask/mechanics.py index 08f5ca125..307f1d83d 100644 --- a/python/damask/mechanics.py +++ b/python/damask/mechanics.py @@ -92,21 +92,27 @@ def deviatoric_part(x): x - np.einsum('ijk,i->ijk',np.broadcast_to(np.eye(3),[x.shape[0],3,3]),spherical_part(x)) -def spherical_part(x): +def spherical_part(x,tensor=False): """ Return spherical (hydrostatic) part of a tensor. - A single scalar is returned, i.e. the hydrostatic part is not mapped on the 3rd order identity - matrix. - 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 """ - return np.trace(x)/3.0 if np.shape(x) == (3,3) else \ - np.trace(x,axis1=1,axis2=2)/3.0 + 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): diff --git a/python/tests/test_mechanics.py b/python/tests/test_mechanics.py index aab92bef3..1ea1f2bba 100644 --- a/python/tests/test_mechanics.py +++ b/python/tests/test_mechanics.py @@ -30,8 +30,8 @@ class TestMechanics: def test_vectorize_spherical_part(self): x = np.random.random((self.n,3,3)) - assert np.allclose(mechanics.spherical_part(x)[self.c], - mechanics.spherical_part(x[self.c])) + assert np.allclose(mechanics.spherical_part(x,True)[self.c], + mechanics.spherical_part(x[self.c],True)) def test_vectorize_Mises_stress(self): @@ -94,6 +94,15 @@ class TestMechanics: assert np.allclose(mechanics.Cauchy(np.broadcast_to(np.eye(3),(self.n,3,3)),P), 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)) + def test_strain_tensor_no_rotation(self): """Ensure that left and right stretch give same results for no rotation.""" @@ -102,6 +111,12 @@ class TestMechanics: 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.random.random((self.n,3,3)) + 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.""" @@ -111,15 +126,46 @@ class TestMechanics: 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. + + 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 = np.broadcast_to(np.eye(3),(self.n,3,3))\ - * np.repeat(mechanics.spherical_part(x),9).reshape(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) + + 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) + + 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) def test_symmetric(self): """Ensure that a symmetric tensor is half of the sum of a tensor and its transpose.""" From 264afe00f9f2f23370aad41c63ce5704c5c9a9ac Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 23 Dec 2019 17:52:35 +0100 Subject: [PATCH 146/223] using central functionality --- processing/post/averageDown.py | 18 ++++------------- processing/post/blowUp.py | 36 ++++++++++------------------------ 2 files changed, 14 insertions(+), 40 deletions(-) diff --git a/processing/post/averageDown.py b/processing/post/averageDown.py index d4e2a3529..c7586ed94 100755 --- a/processing/post/averageDown.py +++ b/processing/post/averageDown.py @@ -122,20 +122,10 @@ for name in filenames: #--- generate grid -------------------------------------------------------------------------------- - x = (0.5 + shift[0] + np.arange(packedGrid[0],dtype=float))/packedGrid[0]*size[0] - y = (0.5 + shift[1] + np.arange(packedGrid[1],dtype=float))/packedGrid[1]*size[1] - z = (0.5 + shift[2] + np.arange(packedGrid[2],dtype=float))/packedGrid[2]*size[2] + coords = damask.grid_filters.cell_coord0(packedGrid,size,shift/packedGrid*size+origin) + table.data[:,table.label_indexrange(options.pos)] = coords.reshape((-1,3)) - xx = np.tile( x, packedGrid[1]* packedGrid[2]) - yy = np.tile(np.repeat(y,packedGrid[0] ),packedGrid[2]) - zz = np.repeat(z,packedGrid[0]*packedGrid[1]) - table.data[:,table.label_indexrange(options.pos)] = np.squeeze(np.dstack((xx,yy,zz))) - -# ------------------------------------------ output result ----------------------------------------- - - table.data_writeArray() - # ------------------------------------------ output finalization ----------------------------------- - - table.close() # close ASCII tables + table.data_writeArray() + table.close() diff --git a/processing/post/blowUp.py b/processing/post/blowUp.py index 32cc1909d..47abc187a 100755 --- a/processing/post/blowUp.py +++ b/processing/post/blowUp.py @@ -4,6 +4,7 @@ import os import sys from optparse import OptionParser +from scipy import ndimage import numpy as np import damask @@ -94,30 +95,13 @@ for name in filenames: table.head_write() # ------------------------------------------ process data ------------------------------------------- + table.data_readArray() + data = table.data.reshape(tuple(grid)+(-1,)) + table.data = ndimage.interpolation.zoom(data,tuple(packing)+(1,),order=0,mode='nearest').reshape((outSize.prod(),-1)) + coords = damask.grid_filters.cell_coord0(outSize,size,origin) + table.data[:,table.label_indexrange(options.pos)] = coords.reshape((-1,3)) + table.data[:,table.label_index('elem')] = np.arange(1,outSize.prod()+1) - data = np.zeros(outSize.tolist()+[len(table.labels(raw = True))]) - p = np.zeros(3,'i') - - for p[2] in range(grid[2]): - for p[1] in range(grid[1]): - for p[0] in range(grid[0]): - d = p*packing - table.data_read() - data[d[0]:d[0]+packing[0], - d[1]:d[1]+packing[1], - d[2]:d[2]+packing[2], - : ] = np.tile(np.array(table.data_asFloat(),'d'),packing.tolist()+[1]) # tile to match blowUp voxel size - elementSize = size/grid/packing - elem = 1 - for c in range(outSize[2]): - for b in range(outSize[1]): - for a in range(outSize[0]): - data[a,b,c,table.label_indexrange(options.pos)] = [a+0.5,b+0.5,c+0.5]*elementSize - if colElem != -1: data[a,b,c,colElem] = elem - table.data = data[a,b,c,:].tolist() - outputAlive = table.data_write() # output processed line - elem += 1 - -# ------------------------------------------ output finalization ----------------------------------- - - table.close() # close input ASCII table (works for stdin) +# ------------------------------------------ output finalization ----------------------------------- + table.data_writeArray() + table.close() From 9e949556265a80d8c3d041ff85d0d7e0eea0d7b6 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 24 Dec 2019 15:58:37 +0100 Subject: [PATCH 147/223] wrong coordinates for non-cubical grids --- processing/pre/geom_toTable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/processing/pre/geom_toTable.py b/processing/pre/geom_toTable.py index 3d955ad53..53196e808 100755 --- a/processing/pre/geom_toTable.py +++ b/processing/pre/geom_toTable.py @@ -30,7 +30,7 @@ for name in filenames: geom = damask.Geom.from_file(StringIO(''.join(sys.stdin.read())) if name is None else name) damask.util.croak(geom) - coord0 = damask.grid_filters.cell_coord0(geom.grid,geom.size,geom.origin).reshape((-1,3),order='F') + coord0 = damask.grid_filters.cell_coord0(geom.grid,geom.size,geom.origin).reshape((-1,3)) comments = geom.comments \ + [scriptID + ' ' + ' '.join(sys.argv[1:]), From f5c58517a7d43d573130c9ffd2e3a913c6ad4ab2 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 24 Dec 2019 15:59:09 +0100 Subject: [PATCH 148/223] enable one grid point along all directions --- python/damask/grid_filters.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/python/damask/grid_filters.py b/python/damask/grid_filters.py index 93e61f5d8..9c8b1b88e 100644 --- a/python/damask/grid_filters.py +++ b/python/damask/grid_filters.py @@ -200,6 +200,10 @@ def cell_coord0_2_DNA(coord0,ordered=True): size = grid/np.maximum(grid-1,1) * (maxcorner-mincorner) delta = size/grid origin = mincorner - delta*.5 + + # 1D/2D: size/origin combination undefined, set origin to 0.0 + size [np.where(grid==1)] = origin[np.where(grid==1)]*2. + origin[np.where(grid==1)] = 0.0 if grid.prod() != len(coord0): raise ValueError('Data count {} does not match grid {}.'.format(len(coord0),grid)) From be319c5a83bcffc1184466ef683838181fec298b Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 25 Dec 2019 09:24:42 +0100 Subject: [PATCH 149/223] single source of truth --- src/lattice.f90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lattice.f90 b/src/lattice.f90 index 025a1f8a5..4aab12fc9 100644 --- a/src/lattice.f90 +++ b/src/lattice.f90 @@ -371,7 +371,7 @@ module lattice 1,-1, 1, -2,-1, 1, & -1, 1, 1, -1,-2, 1, & 1, 1, 1, 1,-2, 1 & - ],pReal),[ 3 + 3,LATTICE_BCT_NSLIP]) !< slip systems for bct sorted by Bieler + ],pReal),shape(LATTICE_BCT_SYSTEMSLIP)) !< slip systems for bct sorted by Bieler !-------------------------------------------------------------------------------------------------- ! isotropic @@ -387,7 +387,7 @@ module lattice 0, 1, 0, 1, 0, 0, & 0, 0, 1, 0, 1, 0, & 1, 0, 0, 0, 0, 1 & - ],pReal),[ 3 + 3,LATTICE_ISO_NCLEAVAGE]) + ],pReal),shape(LATTICE_ISO_SYSTEMCLEAVAGE)) !-------------------------------------------------------------------------------------------------- @@ -404,7 +404,7 @@ module lattice 0, 1, 0, 1, 0, 0, & 0, 0, 1, 0, 1, 0, & 1, 0, 0, 0, 0, 1 & - ],pReal),[ 3 + 3,LATTICE_ORT_NCLEAVAGE]) + ],pReal),shape(LATTICE_ORT_SYSTEMCLEAVAGE)) From 05b2c80430cbf0a20b72275d29344bf672970484 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 26 Dec 2019 14:34:22 +0100 Subject: [PATCH 150/223] migrating ASCIItable to Table class --- processing/post/averageDown.py | 54 ++++++------------------------- processing/post/blowUp.py | 58 +++++++--------------------------- 2 files changed, 21 insertions(+), 91 deletions(-) diff --git a/processing/post/averageDown.py b/processing/post/averageDown.py index c7586ed94..817000dfa 100755 --- a/processing/post/averageDown.py +++ b/processing/post/averageDown.py @@ -49,6 +49,7 @@ parser.set_defaults(pos = 'pos', ) (options,filenames) = parser.parse_args() +if filenames == []: filenames = [None] packing = np.array(options.packing,dtype = int) shift = np.array(options.shift, dtype = int) @@ -56,47 +57,14 @@ shift = np.array(options.shift, dtype = int) prefix = 'averagedDown{}x{}x{}_'.format(*packing) if any(shift != 0): prefix += 'shift{:+}{:+}{:+}_'.format(*shift) -# --- loop over input files ------------------------------------------------------------------------ - -if filenames == []: filenames = [None] for name in filenames: - try: table = damask.ASCIItable(name = name, - outname = os.path.join(os.path.dirname(name), - prefix+os.path.basename(name)) if name else name, - buffered = False) - except IOError: - continue damask.util.report(scriptName,name) - -# ------------------------------------------ read header ------------------------------------------ - - table.head_read() - -# ------------------------------------------ sanity checks ---------------------------------------- - - errors = [] - remarks = [] - if table.label_dimension(options.pos) != 3: errors.append('coordinates {} are not a vector.'.format(options.pos)) - - if remarks != []: damask.util.croak(remarks) - if errors != []: - damask.util.croak(errors) - table.close(dismiss = True) - continue - -# ------------------------------------------ assemble header --------------------------------------- - - table.info_append(scriptID + '\t' + ' '.join(sys.argv[1:])) - table.head_write() - -# --------------- figure out size and grid --------------------------------------------------------- - - table.data_readArray() + table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) if (options.grid is None or options.size is None): - grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.data[:,table.label_indexrange(options.pos)]) + grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.get(options.pos)) else: grid = np.array(options.grid,'i') size = np.array(options.size,'d') @@ -105,27 +73,25 @@ for name in filenames: shift = np.where(grid == 1,0,shift) # reset shift to 0 where grid==1 packedGrid = np.maximum(np.ones(3,'i'),grid//packing) + data = table.data.values.reshape(tuple(grid)+(-1,),order = 'F') averagedDown = scipy.ndimage.filters.uniform_filter( \ np.roll( np.roll( - np.roll(table.data.reshape(list(grid)+[table.data.shape[1]],order = 'F'), + np.roll(data, -shift[0],axis = 0), -shift[1],axis = 1), -shift[2],axis = 2), size = list(packing) + [1], mode = 'wrap', origin = list(-(packing//2)) + [0])\ - [::packing[0],::packing[1],::packing[2],:].reshape((packedGrid.prod(),table.data.shape[1]),order = 'F') + [::packing[0],::packing[1],::packing[2],:].reshape((packedGrid.prod(),-1),order = 'F') - table.data = averagedDown - -#--- generate grid -------------------------------------------------------------------------------- + table = damask.Table(averagedDown,table.shapes,table.comments) coords = damask.grid_filters.cell_coord0(packedGrid,size,shift/packedGrid*size+origin) - table.data[:,table.label_indexrange(options.pos)] = coords.reshape((-1,3)) + table.set(options.pos, coords.reshape((-1,3))) -# ------------------------------------------ output finalization ----------------------------------- - table.data_writeArray() - table.close() + outname = os.path.join(os.path.dirname(name),prefix+os.path.basename(name)) + table.to_ASCII(sys.stdout if name is None else outname) diff --git a/processing/post/blowUp.py b/processing/post/blowUp.py index 47abc187a..ec42e2365 100755 --- a/processing/post/blowUp.py +++ b/processing/post/blowUp.py @@ -43,65 +43,29 @@ parser.set_defaults(pos = 'pos', ) (options,filenames) = parser.parse_args() +if filenames == []: filenames = [None] options.packing = np.array(options.packing) prefix = 'blowUp{}x{}x{}_'.format(*options.packing) -# --- loop over input files ------------------------------------------------------------------------- - -if filenames == []: filenames = [None] for name in filenames: - try: table = damask.ASCIItable(name = name, - outname = os.path.join(os.path.dirname(name), - prefix+os.path.basename(name)) if name else name, - buffered = False) - except IOError: - continue damask.util.report(scriptName,name) - -# ------------------------------------------ read header ------------------------------------------ - - table.head_read() - -# ------------------------------------------ sanity checks ---------------------------------------- - errors = [] - remarks = [] - - if table.label_dimension(options.pos) != 3: errors.append('coordinates "{}" are not a vector.'.format(options.pos)) - - colElem = table.label_index('elem') - - if remarks != []: damask.util.croak(remarks) - if errors != []: - damask.util.croak(errors) - table.close(dismiss = True) - continue - -# --------------- figure out size and grid --------------------------------------------------------- - - table.data_readArray(options.pos) - table.data_rewind() - - grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.data) + table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) + grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.get(options.pos)) packing = np.array(options.packing,'i') outSize = grid*packing -# ------------------------------------------ assemble header -------------------------------------- + data = table.data.values.reshape(tuple(grid)+(-1,)) + blownUp = ndimage.interpolation.zoom(data,tuple(packing)+(1,),order=0,mode='nearest').reshape((outSize.prod(),-1)) - table.info_append(scriptID + '\t' + ' '.join(sys.argv[1:])) - table.head_write() + table = damask.Table(blownUp,table.shapes,table.comments) -# ------------------------------------------ process data ------------------------------------------- - table.data_readArray() - data = table.data.reshape(tuple(grid)+(-1,)) - table.data = ndimage.interpolation.zoom(data,tuple(packing)+(1,),order=0,mode='nearest').reshape((outSize.prod(),-1)) coords = damask.grid_filters.cell_coord0(outSize,size,origin) - table.data[:,table.label_indexrange(options.pos)] = coords.reshape((-1,3)) - table.data[:,table.label_index('elem')] = np.arange(1,outSize.prod()+1) - -# ------------------------------------------ output finalization ----------------------------------- - table.data_writeArray() - table.close() + table.set(options.pos,coords.reshape((-1,3))) + table.set('elem',np.arange(1,outSize.prod()+1)) + + outname = os.path.join(os.path.dirname(name),prefix+os.path.basename(name)) + table.to_ASCII(sys.stdout if name is None else outname) From af0a4c5d268bb88f4dbf8816a597c05a63fd31fe Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 26 Dec 2019 15:24:37 +0100 Subject: [PATCH 151/223] unused --- src/IO.f90 | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/IO.f90 b/src/IO.f90 index 8926e1e21..9a0c94661 100644 --- a/src/IO.f90 +++ b/src/IO.f90 @@ -23,7 +23,6 @@ module IO IO_read_ASCII, & IO_open_file, & IO_open_jobFile_binary, & - IO_write_jobFile, & IO_isBlank, & IO_getTag, & IO_stringPos, & @@ -289,25 +288,6 @@ end subroutine IO_open_inputFile #endif -!-------------------------------------------------------------------------------------------------- -!> @brief opens ASCII file to given unit for writing. File is named after solver job name plus -!! given extension and located in current working directory -!-------------------------------------------------------------------------------------------------- -subroutine IO_write_jobFile(fileUnit,ext) - - integer, intent(in) :: fileUnit !< file unit - character(len=*), intent(in) :: ext !< extension of file - - integer :: myStat - character(len=1024) :: path - - path = trim(getSolverJobName())//'.'//ext - open(fileUnit,status='replace',iostat=myStat,file=path) - if (myStat /= 0) call IO_error(100,el=myStat,ext_msg=path) - -end subroutine IO_write_jobFile - - !-------------------------------------------------------------------------------------------------- !> @brief identifies strings without content !-------------------------------------------------------------------------------------------------- From f9fcaca60b6c3640dcf6c50a562736259cce81d4 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 26 Dec 2019 15:24:51 +0100 Subject: [PATCH 152/223] aliases are just confusing --- src/mesh_grid.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh_grid.f90 b/src/mesh_grid.f90 index 3d839e1c9..8411ae773 100644 --- a/src/mesh_grid.f90 +++ b/src/mesh_grid.f90 @@ -173,7 +173,7 @@ subroutine readGeom(grid,geomSize,origin,microstructure,homogenization) !-------------------------------------------------------------------------------------------------- ! get header length endPos = index(rawData,new_line('')) - if(endPos <= index(rawData,'head')) then + if(endPos <= index(rawData,'head')) then ! ToDo: Should be 'header' startPos = len(rawData) call IO_error(error_ID=841, ext_msg='readGeom') else From d4d419b713b34e9d31879d8d985c1f805d3ab77a Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 30 Dec 2019 06:48:55 +0100 Subject: [PATCH 153/223] better readable --- src/mesh_grid.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mesh_grid.f90 b/src/mesh_grid.f90 index 8411ae773..3ce633dbe 100644 --- a/src/mesh_grid.f90 +++ b/src/mesh_grid.f90 @@ -277,13 +277,13 @@ subroutine readGeom(grid,geomSize,origin,microstructure,homogenization) compression: if (IO_lc(IO_stringValue(line,chunkPos,2)) == 'of') then c = IO_intValue(line,chunkPos,1) microstructure(e:e+c-1) = [(IO_intValue(line,chunkPos,3),i = 1,IO_intValue(line,chunkPos,1))] - else if (IO_lc(IO_stringValue(line,chunkPos,2)) == 'to') then compression + else if (IO_lc(IO_stringValue(line,chunkPos,2)) == 'to') then compression c = abs(IO_intValue(line,chunkPos,3) - IO_intValue(line,chunkPos,1)) + 1 o = merge(+1, -1, IO_intValue(line,chunkPos,3) > IO_intValue(line,chunkPos,1)) microstructure(e:e+c-1) = [(i, i = IO_intValue(line,chunkPos,1),IO_intValue(line,chunkPos,3),o)] else compression c = chunkPos(1) - microstructure(e:e+c-1) = [(IO_intValue(line,chunkPos,i+1), i=0, c-1)] + microstructure(e:e+c-1) = [(IO_intValue(line,chunkPos,i+1), i=0, c-1)] endif compression endif noCompression From a87e396b83a9b5a438812fb90fa3d87361f1626a Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 2 Jan 2020 11:31:33 +0100 Subject: [PATCH 154/223] bugfix: need to addData to vtk not really sure why 'SetScalars' was used for color. 'AddArray' seems to work. Also, there seems to be no difference between 'data' and 'tensor' --- processing/post/vtk_addGridData.py | 13 ++++--------- processing/post/vtk_addPointCloudData.py | 8 +++++--- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/processing/post/vtk_addGridData.py b/processing/post/vtk_addGridData.py index 1596cd7fe..8e76cfca8 100755 --- a/processing/post/vtk_addGridData.py +++ b/processing/post/vtk_addGridData.py @@ -2,8 +2,8 @@ import os import sys +from io import StringIO from optparse import OptionParser -from collections import defaultdict import vtk from vtk.util import numpy_support @@ -120,14 +120,9 @@ for name in filenames: damask.util.croak('{} mode...'.format(mode)) - for datatype,labels in active.items(): # loop over scalar,color - if datatype == 'color': - if mode == 'cell': rGrid.GetCellData().SetScalars(VTKarray[active['color'][0]]) - elif mode == 'point': rGrid.GetPointData().SetScalars(VTKarray[active['color'][0]]) - for me in labels: # loop over all requested items - if mode == 'cell': rGrid.GetCellData().AddArray(VTKarray[me]) - elif mode == 'point': rGrid.GetPointData().AddArray(VTKarray[me]) - + for data in VTKarray: + if mode == 'cell': rGrid.GetCellData().AddArray(VTKarray[data]) + elif mode == 'point': rGrid.GetPointData().AddArray(VTKarray[data]) rGrid.Modified() # ------------------------------------------ output result --------------------------------------- diff --git a/processing/post/vtk_addPointCloudData.py b/processing/post/vtk_addPointCloudData.py index 5a40d967a..833bfc88e 100755 --- a/processing/post/vtk_addPointCloudData.py +++ b/processing/post/vtk_addPointCloudData.py @@ -2,8 +2,8 @@ import os import sys +from io import StringIO from optparse import OptionParser -from collections import defaultdict import vtk from vtk.util import numpy_support @@ -104,10 +104,12 @@ for name in filenames: VTKarray[tensor].SetName(tensor) -# ------------------------------------------ output result --------------------------------------- - + for data in VTKarray: + Polydata.GetPointData().AddArray(VTKarray[data]) Polydata.Modified() +# ------------------------------------------ output result --------------------------------------- + writer = vtk.vtkXMLPolyDataWriter() writer.SetDataModeToBinary() writer.SetCompressorTypeToZLib() From b9a82ef5236adf8e1de2abd174900f13e7b68e6a Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 2 Jan 2020 12:10:32 +0100 Subject: [PATCH 155/223] not needed anymore --- examples/ConfigFiles/Texture_Gauss_001.config | 2 +- examples/ConfigFiles/Texture_Gauss_101.config | 2 +- examples/ConfigFiles/Texture_Gauss_111.config | 2 +- examples/ConfigFiles/Texture_Gauss_123.config | 2 +- examples/FEM/polyXtal/material.config | 299 ++++++----------- examples/MSC.Marc/material.config | 303 ++++++------------ .../EshelbyInclusion/material.config | 4 +- .../Polycrystal/material.config | 60 ++-- 8 files changed, 227 insertions(+), 447 deletions(-) diff --git a/examples/ConfigFiles/Texture_Gauss_001.config b/examples/ConfigFiles/Texture_Gauss_001.config index 4711c4135..4fb519f08 100644 --- a/examples/ConfigFiles/Texture_Gauss_001.config +++ b/examples/ConfigFiles/Texture_Gauss_001.config @@ -1,2 +1,2 @@ [001] -(gauss) phi1 0.000 Phi 0.000 phi2 0.000 scatter 0.000 fraction 1.000 +(gauss) phi1 0.000 Phi 0.000 phi2 0.000 diff --git a/examples/ConfigFiles/Texture_Gauss_101.config b/examples/ConfigFiles/Texture_Gauss_101.config index 79457aeac..c6c1b5dbe 100644 --- a/examples/ConfigFiles/Texture_Gauss_101.config +++ b/examples/ConfigFiles/Texture_Gauss_101.config @@ -1,2 +1,2 @@ [101] -(gauss) phi1 0.000 Phi 45.000 phi2 90.000 scatter 0.000 fraction 1.000 +(gauss) phi1 0.000 Phi 45.000 phi2 90.000 diff --git a/examples/ConfigFiles/Texture_Gauss_111.config b/examples/ConfigFiles/Texture_Gauss_111.config index 8204bfb69..0d685a66e 100644 --- a/examples/ConfigFiles/Texture_Gauss_111.config +++ b/examples/ConfigFiles/Texture_Gauss_111.config @@ -1,2 +1,2 @@ [111] -(gauss) phi1 0.000 Phi 54.7356 phi2 45.000 scatter 0.000 fraction 1.000 +(gauss) phi1 0.000 Phi 54.7356 phi2 45.000 diff --git a/examples/ConfigFiles/Texture_Gauss_123.config b/examples/ConfigFiles/Texture_Gauss_123.config index 32d28442f..da4fa30ab 100644 --- a/examples/ConfigFiles/Texture_Gauss_123.config +++ b/examples/ConfigFiles/Texture_Gauss_123.config @@ -1,2 +1,2 @@ [123] -(gauss) phi1 209.805 Phi 29.206 phi2 63.435 scatter 0.000 fraction 1.000 +(gauss) phi1 209.805 Phi 29.206 phi2 63.435 diff --git a/examples/FEM/polyXtal/material.config b/examples/FEM/polyXtal/material.config index 51cf8c163..197890821 100644 --- a/examples/FEM/polyXtal/material.config +++ b/examples/FEM/polyXtal/material.config @@ -459,301 +459,202 @@ crystallite 1 #-------------------# [Grain001] -(gauss) phi1 172.344 Phi 114.046 phi2 294.669 scatter 0.0 fraction 1.0 - +(gauss) phi1 172.344 Phi 114.046 phi2 294.669 [Grain002] -(gauss) phi1 186.013 Phi 94.7338 phi2 329.683 scatter 0.0 fraction 1.0 - +(gauss) phi1 186.013 Phi 94.7338 phi2 329.683 [Grain003] -(gauss) phi1 162.41 Phi 98.9455 phi2 130.322 scatter 0.0 fraction 1.0 - +(gauss) phi1 162.41 Phi 98.9455 phi2 130.322 [Grain004] -(gauss) phi1 355.272 Phi 140.621 phi2 125.567 scatter 0.0 fraction 1.0 - +(gauss) phi1 355.272 Phi 140.621 phi2 125.567 [Grain005] -(gauss) phi1 21.7641 Phi 143.388 phi2 240.373 scatter 0.0 fraction 1.0 - +(gauss) phi1 21.7641 Phi 143.388 phi2 240.373 [Grain006] -(gauss) phi1 88.1966 Phi 92.3358 phi2 194.78 scatter 0.0 fraction 1.0 - +(gauss) phi1 88.1966 Phi 92.3358 phi2 194.78 [Grain007] -(gauss) phi1 161.137 Phi 78.0062 phi2 111.948 scatter 0.0 fraction 1.0 - +(gauss) phi1 161.137 Phi 78.0062 phi2 111.948 [Grain008] -(gauss) phi1 169.792 Phi 89.5333 phi2 159.265 scatter 0.0 fraction 1.0 - +(gauss) phi1 169.792 Phi 89.5333 phi2 159.265 [Grain009] -(gauss) phi1 264.847 Phi 130.291 phi2 180.604 scatter 0.0 fraction 1.0 - +(gauss) phi1 264.847 Phi 130.291 phi2 180.604 [Grain010] -(gauss) phi1 70.6323 Phi 84.1754 phi2 341.162 scatter 0.0 fraction 1.0 - +(gauss) phi1 70.6323 Phi 84.1754 phi2 341.162 [Grain011] -(gauss) phi1 67.7751 Phi 36.1662 phi2 139.898 scatter 0.0 fraction 1.0 - +(gauss) phi1 67.7751 Phi 36.1662 phi2 139.898 [Grain012] -(gauss) phi1 111.621 Phi 19.1089 phi2 228.338 scatter 0.0 fraction 1.0 - +(gauss) phi1 111.621 Phi 19.1089 phi2 228.338 [Grain013] -(gauss) phi1 129.9 Phi 139.011 phi2 238.735 scatter 0.0 fraction 1.0 - +(gauss) phi1 129.9 Phi 139.011 phi2 238.735 [Grain014] -(gauss) phi1 221.405 Phi 129.743 phi2 99.6471 scatter 0.0 fraction 1.0 - +(gauss) phi1 221.405 Phi 129.743 phi2 99.6471 [Grain015] -(gauss) phi1 241.783 Phi 98.3729 phi2 260.615 scatter 0.0 fraction 1.0 - +(gauss) phi1 241.783 Phi 98.3729 phi2 260.615 [Grain016] -(gauss) phi1 72.5592 Phi 122.403 phi2 165.046 scatter 0.0 fraction 1.0 - +(gauss) phi1 72.5592 Phi 122.403 phi2 165.046 [Grain017] -(gauss) phi1 64.8818 Phi 82.6384 phi2 236.305 scatter 0.0 fraction 1.0 - +(gauss) phi1 64.8818 Phi 82.6384 phi2 236.305 [Grain018] -(gauss) phi1 201.096 Phi 65.9312 phi2 330.745 scatter 0.0 fraction 1.0 - +(gauss) phi1 201.096 Phi 65.9312 phi2 330.745 [Grain019] -(gauss) phi1 192.994 Phi 81.9371 phi2 239.326 scatter 0.0 fraction 1.0 - +(gauss) phi1 192.994 Phi 81.9371 phi2 239.326 [Grain020] -(gauss) phi1 125.335 Phi 90.4527 phi2 207.982 scatter 0.0 fraction 1.0 - +(gauss) phi1 125.335 Phi 90.4527 phi2 207.982 [Grain021] -(gauss) phi1 55.8848 Phi 26.4455 phi2 100.921 scatter 0.0 fraction 1.0 - +(gauss) phi1 55.8848 Phi 26.4455 phi2 100.921 [Grain022] -(gauss) phi1 40.722 Phi 95.6415 phi2 269.174 scatter 0.0 fraction 1.0 - +(gauss) phi1 40.722 Phi 95.6415 phi2 269.174 [Grain023] -(gauss) phi1 250.487 Phi 69.6035 phi2 201.732 scatter 0.0 fraction 1.0 - +(gauss) phi1 250.487 Phi 69.6035 phi2 201.732 [Grain024] -(gauss) phi1 204.199 Phi 84.983 phi2 20.3469 scatter 0.0 fraction 1.0 - +(gauss) phi1 204.199 Phi 84.983 phi2 20.3469 [Grain025] -(gauss) phi1 12.7416 Phi 128.589 phi2 271.553 scatter 0.0 fraction 1.0 - +(gauss) phi1 12.7416 Phi 128.589 phi2 271.553 [Grain026] -(gauss) phi1 299.704 Phi 85.3961 phi2 217.359 scatter 0.0 fraction 1.0 - +(gauss) phi1 299.704 Phi 85.3961 phi2 217.359 [Grain027] -(gauss) phi1 48.8232 Phi 83.6209 phi2 200.361 scatter 0.0 fraction 1.0 - +(gauss) phi1 48.8232 Phi 83.6209 phi2 200.361 [Grain028] -(gauss) phi1 336.395 Phi 97.3059 phi2 187.071 scatter 0.0 fraction 1.0 - +(gauss) phi1 336.395 Phi 97.3059 phi2 187.071 [Grain029] -(gauss) phi1 274.354 Phi 78.2424 phi2 320.308 scatter 0.0 fraction 1.0 - +(gauss) phi1 274.354 Phi 78.2424 phi2 320.308 [Grain030] -(gauss) phi1 320.776 Phi 149.72 phi2 163.862 scatter 0.0 fraction 1.0 - +(gauss) phi1 320.776 Phi 149.72 phi2 163.862 [Grain031] -(gauss) phi1 179.549 Phi 106.548 phi2 345.498 scatter 0.0 fraction 1.0 - +(gauss) phi1 179.549 Phi 106.548 phi2 345.498 [Grain032] -(gauss) phi1 163.508 Phi 24.4238 phi2 127.809 scatter 0.0 fraction 1.0 - +(gauss) phi1 163.508 Phi 24.4238 phi2 127.809 [Grain033] -(gauss) phi1 193.405 Phi 157.012 phi2 321.342 scatter 0.0 fraction 1.0 - +(gauss) phi1 193.405 Phi 157.012 phi2 321.342 [Grain034] -(gauss) phi1 9.09886 Phi 95.9453 phi2 102.32 scatter 0.0 fraction 1.0 - +(gauss) phi1 9.09886 Phi 95.9453 phi2 102.32 [Grain035] -(gauss) phi1 353.876 Phi 150.824 phi2 174.886 scatter 0.0 fraction 1.0 - +(gauss) phi1 353.876 Phi 150.824 phi2 174.886 [Grain036] -(gauss) phi1 138.914 Phi 76.5811 phi2 167.927 scatter 0.0 fraction 1.0 - +(gauss) phi1 138.914 Phi 76.5811 phi2 167.927 [Grain037] -(gauss) phi1 262.655 Phi 76.2738 phi2 12.4459 scatter 0.0 fraction 1.0 - +(gauss) phi1 262.655 Phi 76.2738 phi2 12.4459 [Grain038] -(gauss) phi1 121.849 Phi 65.5254 phi2 192.601 scatter 0.0 fraction 1.0 - +(gauss) phi1 121.849 Phi 65.5254 phi2 192.601 [Grain039] -(gauss) phi1 275.824 Phi 81.6788 phi2 164.228 scatter 0.0 fraction 1.0 - +(gauss) phi1 275.824 Phi 81.6788 phi2 164.228 [Grain040] -(gauss) phi1 68.9202 Phi 160.5 phi2 210.862 scatter 0.0 fraction 1.0 - +(gauss) phi1 68.9202 Phi 160.5 phi2 210.862 [Grain041] -(gauss) phi1 51.0398 Phi 82.7291 phi2 74.016 scatter 0.0 fraction 1.0 - +(gauss) phi1 51.0398 Phi 82.7291 phi2 74.016 [Grain042] -(gauss) phi1 338.746 Phi 62.7854 phi2 129.362 scatter 0.0 fraction 1.0 - +(gauss) phi1 338.746 Phi 62.7854 phi2 129.362 [Grain043] -(gauss) phi1 204.51 Phi 151.256 phi2 178.89 scatter 0.0 fraction 1.0 - +(gauss) phi1 204.51 Phi 151.256 phi2 178.89 [Grain044] -(gauss) phi1 122.098 Phi 104.003 phi2 323.04 scatter 0.0 fraction 1.0 - +(gauss) phi1 122.098 Phi 104.003 phi2 323.04 [Grain045] -(gauss) phi1 106.693 Phi 108.61 phi2 336.935 scatter 0.0 fraction 1.0 - +(gauss) phi1 106.693 Phi 108.61 phi2 336.935 [Grain046] -(gauss) phi1 118.856 Phi 160.992 phi2 316.152 scatter 0.0 fraction 1.0 - +(gauss) phi1 118.856 Phi 160.992 phi2 316.152 [Grain047] -(gauss) phi1 177.962 Phi 114.868 phi2 13.6918 scatter 0.0 fraction 1.0 - +(gauss) phi1 177.962 Phi 114.868 phi2 13.6918 [Grain048] -(gauss) phi1 330.273 Phi 174.495 phi2 231.249 scatter 0.0 fraction 1.0 - +(gauss) phi1 330.273 Phi 174.495 phi2 231.249 [Grain049] -(gauss) phi1 7.31937 Phi 94.7313 phi2 17.8461 scatter 0.0 fraction 1.0 - +(gauss) phi1 7.31937 Phi 94.7313 phi2 17.8461 [Grain050] -(gauss) phi1 74.3385 Phi 49.9546 phi2 286.482 scatter 0.0 fraction 1.0 - +(gauss) phi1 74.3385 Phi 49.9546 phi2 286.482 [Grain051] -(gauss) phi1 326.388 Phi 76.9547 phi2 214.758 scatter 0.0 fraction 1.0 - +(gauss) phi1 326.388 Phi 76.9547 phi2 214.758 [Grain052] -(gauss) phi1 276.024 Phi 72.1242 phi2 275.884 scatter 0.0 fraction 1.0 - +(gauss) phi1 276.024 Phi 72.1242 phi2 275.884 [Grain053] -(gauss) phi1 137.681 Phi 116.99 phi2 6.87047 scatter 0.0 fraction 1.0 - +(gauss) phi1 137.681 Phi 116.99 phi2 6.87047 [Grain054] -(gauss) phi1 200.213 Phi 123.618 phi2 268.84 scatter 0.0 fraction 1.0 - +(gauss) phi1 200.213 Phi 123.618 phi2 268.84 [Grain055] -(gauss) phi1 7.13702 Phi 56.2015 phi2 119.65 scatter 0.0 fraction 1.0 - +(gauss) phi1 7.13702 Phi 56.2015 phi2 119.65 [Grain056] -(gauss) phi1 72.1783 Phi 81.0906 phi2 6.06213 scatter 0.0 fraction 1.0 - +(gauss) phi1 72.1783 Phi 81.0906 phi2 6.06213 [Grain057] -(gauss) phi1 184.565 Phi 110.01 phi2 239.546 scatter 0.0 fraction 1.0 - +(gauss) phi1 184.565 Phi 110.01 phi2 239.546 [Grain058] -(gauss) phi1 210.124 Phi 128.631 phi2 8.61611 scatter 0.0 fraction 1.0 - +(gauss) phi1 210.124 Phi 128.631 phi2 8.61611 [Grain059] -(gauss) phi1 290.326 Phi 170.412 phi2 144.269 scatter 0.0 fraction 1.0 - +(gauss) phi1 290.326 Phi 170.412 phi2 144.269 [Grain060] -(gauss) phi1 204.748 Phi 76.7343 phi2 200.385 scatter 0.0 fraction 1.0 - +(gauss) phi1 204.748 Phi 76.7343 phi2 200.385 [Grain061] -(gauss) phi1 54.3015 Phi 65.9143 phi2 117.373 scatter 0.0 fraction 1.0 - +(gauss) phi1 54.3015 Phi 65.9143 phi2 117.373 [Grain062] -(gauss) phi1 261.263 Phi 52.255 phi2 95.9146 scatter 0.0 fraction 1.0 - +(gauss) phi1 261.263 Phi 52.255 phi2 95.9146 [Grain063] -(gauss) phi1 328.054 Phi 51.0778 phi2 24.2782 scatter 0.0 fraction 1.0 - +(gauss) phi1 328.054 Phi 51.0778 phi2 24.2782 [Grain064] -(gauss) phi1 163.03 Phi 154.894 phi2 64.126 scatter 0.0 fraction 1.0 - +(gauss) phi1 163.03 Phi 154.894 phi2 64.126 [Grain065] -(gauss) phi1 183.87 Phi 80.1848 phi2 18.7438 scatter 0.0 fraction 1.0 - +(gauss) phi1 183.87 Phi 80.1848 phi2 18.7438 [Grain066] -(gauss) phi1 219.91 Phi 113.727 phi2 126.67 scatter 0.0 fraction 1.0 - +(gauss) phi1 219.91 Phi 113.727 phi2 126.67 [Grain067] -(gauss) phi1 1.43844 Phi 87.6365 phi2 217.342 scatter 0.0 fraction 1.0 - +(gauss) phi1 1.43844 Phi 87.6365 phi2 217.342 [Grain068] -(gauss) phi1 16.6245 Phi 162.07 phi2 43.7899 scatter 0.0 fraction 1.0 - +(gauss) phi1 16.6245 Phi 162.07 phi2 43.7899 [Grain069] -(gauss) phi1 16.86 Phi 53.8682 phi2 256.917 scatter 0.0 fraction 1.0 - +(gauss) phi1 16.86 Phi 53.8682 phi2 256.917 [Grain070] -(gauss) phi1 1.01921 Phi 118.449 phi2 307.223 scatter 0.0 fraction 1.0 - +(gauss) phi1 1.01921 Phi 118.449 phi2 307.223 [Grain071] -(gauss) phi1 19.0397 Phi 83.8885 phi2 262.687 scatter 0.0 fraction 1.0 - +(gauss) phi1 19.0397 Phi 83.8885 phi2 262.687 [Grain072] -(gauss) phi1 99.799 Phi 77.2307 phi2 84.9727 scatter 0.0 fraction 1.0 - +(gauss) phi1 99.799 Phi 77.2307 phi2 84.9727 [Grain073] -(gauss) phi1 234.292 Phi 63.5029 phi2 250.315 scatter 0.0 fraction 1.0 - +(gauss) phi1 234.292 Phi 63.5029 phi2 250.315 [Grain074] -(gauss) phi1 315.529 Phi 106.015 phi2 103.711 scatter 0.0 fraction 1.0 - +(gauss) phi1 315.529 Phi 106.015 phi2 103.711 [Grain075] -(gauss) phi1 235.595 Phi 110.152 phi2 210.277 scatter 0.0 fraction 1.0 - +(gauss) phi1 235.595 Phi 110.152 phi2 210.277 [Grain076] -(gauss) phi1 341.907 Phi 17.1839 phi2 332.75 scatter 0.0 fraction 1.0 - +(gauss) phi1 341.907 Phi 17.1839 phi2 332.75 [Grain077] -(gauss) phi1 352.166 Phi 88.6049 phi2 114.964 scatter 0.0 fraction 1.0 - +(gauss) phi1 352.166 Phi 88.6049 phi2 114.964 [Grain078] -(gauss) phi1 342.33 Phi 117.777 phi2 180.346 scatter 0.0 fraction 1.0 - +(gauss) phi1 342.33 Phi 117.777 phi2 180.346 [Grain079] -(gauss) phi1 224.952 Phi 70.5702 phi2 148.486 scatter 0.0 fraction 1.0 - +(gauss) phi1 224.952 Phi 70.5702 phi2 148.486 [Grain080] -(gauss) phi1 7.71702 Phi 23.6124 phi2 131.591 scatter 0.0 fraction 1.0 - +(gauss) phi1 7.71702 Phi 23.6124 phi2 131.591 [Grain081] -(gauss) phi1 65.1024 Phi 138.774 phi2 247.344 scatter 0.0 fraction 1.0 - +(gauss) phi1 65.1024 Phi 138.774 phi2 247.344 [Grain082] -(gauss) phi1 37.6181 Phi 51.5209 phi2 8.4169 scatter 0.0 fraction 1.0 - +(gauss) phi1 37.6181 Phi 51.5209 phi2 8.4169 [Grain083] -(gauss) phi1 245.335 Phi 53.4543 phi2 52.5205 scatter 0.0 fraction 1.0 - +(gauss) phi1 245.335 Phi 53.4543 phi2 52.5205 [Grain084] -(gauss) phi1 259.572 Phi 87.7026 phi2 272.065 scatter 0.0 fraction 1.0 - +(gauss) phi1 259.572 Phi 87.7026 phi2 272.065 [Grain085] -(gauss) phi1 269.39 Phi 103.379 phi2 132.506 scatter 0.0 fraction 1.0 - +(gauss) phi1 269.39 Phi 103.379 phi2 132.506 [Grain086] -(gauss) phi1 175.156 Phi 119.338 phi2 355.51 scatter 0.0 fraction 1.0 - +(gauss) phi1 175.156 Phi 119.338 phi2 355.51 [Grain087] -(gauss) phi1 248.11 Phi 39.4772 phi2 310.371 scatter 0.0 fraction 1.0 - +(gauss) phi1 248.11 Phi 39.4772 phi2 310.371 [Grain088] -(gauss) phi1 121.809 Phi 141.465 phi2 10.0736 scatter 0.0 fraction 1.0 - +(gauss) phi1 121.809 Phi 141.465 phi2 10.0736 [Grain089] -(gauss) phi1 2.4357 Phi 47.118 phi2 274.654 scatter 0.0 fraction 1.0 - +(gauss) phi1 2.4357 Phi 47.118 phi2 274.654 [Grain090] -(gauss) phi1 314.188 Phi 134.146 phi2 250.673 scatter 0.0 fraction 1.0 - +(gauss) phi1 314.188 Phi 134.146 phi2 250.673 [Grain091] -(gauss) phi1 114.815 Phi 121.132 phi2 275.124 scatter 0.0 fraction 1.0 - +(gauss) phi1 114.815 Phi 121.132 phi2 275.124 [Grain092] -(gauss) phi1 126.699 Phi 99.0325 phi2 320.537 scatter 0.0 fraction 1.0 - +(gauss) phi1 126.699 Phi 99.0325 phi2 320.537 [Grain093] -(gauss) phi1 184.138 Phi 20.1663 phi2 159.314 scatter 0.0 fraction 1.0 - +(gauss) phi1 184.138 Phi 20.1663 phi2 159.314 [Grain094] -(gauss) phi1 296.502 Phi 15.2389 phi2 39.382 scatter 0.0 fraction 1.0 - +(gauss) phi1 296.502 Phi 15.2389 phi2 39.382 [Grain095] -(gauss) phi1 167.8 Phi 151.764 phi2 192.568 scatter 0.0 fraction 1.0 - +(gauss) phi1 167.8 Phi 151.764 phi2 192.568 [Grain096] -(gauss) phi1 257.822 Phi 133.446 phi2 257.108 scatter 0.0 fraction 1.0 - +(gauss) phi1 257.822 Phi 133.446 phi2 257.108 [Grain097] -(gauss) phi1 71.6923 Phi 74.5726 phi2 342.575 scatter 0.0 fraction 1.0 - +(gauss) phi1 71.6923 Phi 74.5726 phi2 342.575 [Grain098] -(gauss) phi1 176.748 Phi 28.39 phi2 327.375 scatter 0.0 fraction 1.0 - +(gauss) phi1 176.748 Phi 28.39 phi2 327.375 [Grain099] -(gauss) phi1 121.822 Phi 141.836 phi2 22.6349 scatter 0.0 fraction 1.0 - +(gauss) phi1 121.822 Phi 141.836 phi2 22.6349 [Grain100] -(gauss) phi1 180.151 Phi 109.246 phi2 146.177 scatter 0.0 fraction 1.0 +(gauss) phi1 180.151 Phi 109.246 phi2 146.177 diff --git a/examples/MSC.Marc/material.config b/examples/MSC.Marc/material.config index 92a3ed38e..69b8412f4 100644 --- a/examples/MSC.Marc/material.config +++ b/examples/MSC.Marc/material.config @@ -9,307 +9,206 @@ #-------------------# [Grain001] -crystallite 1 (constituent) phase 1 texture 1 fraction 1.0 [Grain002] -crystallite 1 (constituent) phase 1 texture 2 fraction 1.0 [Grain003] -crystallite 1 (constituent) phase 1 texture 3 fraction 1.0 [Grain004] -crystallite 1 (constituent) phase 1 texture 4 fraction 1.0 [Grain005] -crystallite 1 (constituent) phase 1 texture 5 fraction 1.0 [Grain006] -crystallite 1 (constituent) phase 1 texture 6 fraction 1.0 [Grain007] -crystallite 1 (constituent) phase 1 texture 7 fraction 1.0 [Grain008] -crystallite 1 (constituent) phase 1 texture 8 fraction 1.0 [Grain009] -crystallite 1 (constituent) phase 1 texture 9 fraction 1.0 [Grain010] -crystallite 1 (constituent) phase 1 texture 10 fraction 1.0 [Grain011] -crystallite 1 (constituent) phase 1 texture 11 fraction 1.0 [Grain012] -crystallite 1 (constituent) phase 1 texture 12 fraction 1.0 [Grain013] -crystallite 1 (constituent) phase 1 texture 13 fraction 1.0 [Grain014] -crystallite 1 (constituent) phase 1 texture 14 fraction 1.0 [Grain015] -crystallite 1 (constituent) phase 1 texture 15 fraction 1.0 [Grain016] -crystallite 1 (constituent) phase 1 texture 16 fraction 1.0 [Grain017] -crystallite 1 (constituent) phase 1 texture 17 fraction 1.0 [Grain018] -crystallite 1 (constituent) phase 1 texture 18 fraction 1.0 [Grain019] -crystallite 1 (constituent) phase 1 texture 19 fraction 1.0 [Grain020] -crystallite 1 (constituent) phase 1 texture 20 fraction 1.0 [Grain021] -crystallite 1 (constituent) phase 1 texture 21 fraction 1.0 [Grain022] -crystallite 1 (constituent) phase 1 texture 22 fraction 1.0 [Grain023] -crystallite 1 (constituent) phase 1 texture 23 fraction 1.0 [Grain024] -crystallite 1 (constituent) phase 1 texture 24 fraction 1.0 [Grain025] -crystallite 1 (constituent) phase 1 texture 25 fraction 1.0 [Grain026] -crystallite 1 (constituent) phase 1 texture 26 fraction 1.0 [Grain027] -crystallite 1 (constituent) phase 1 texture 27 fraction 1.0 [Grain028] -crystallite 1 (constituent) phase 1 texture 28 fraction 1.0 [Grain029] -crystallite 1 (constituent) phase 1 texture 29 fraction 1.0 [Grain030] -crystallite 1 (constituent) phase 1 texture 30 fraction 1.0 [Grain031] -crystallite 1 (constituent) phase 1 texture 31 fraction 1.0 [Grain032] -crystallite 1 (constituent) phase 1 texture 32 fraction 1.0 [Grain033] -crystallite 1 (constituent) phase 1 texture 33 fraction 1.0 [Grain034] -crystallite 1 (constituent) phase 1 texture 34 fraction 1.0 [Grain035] -crystallite 1 (constituent) phase 1 texture 35 fraction 1.0 [Grain036] -crystallite 1 (constituent) phase 1 texture 36 fraction 1.0 [Grain037] -crystallite 1 (constituent) phase 1 texture 37 fraction 1.0 [Grain038] -crystallite 1 (constituent) phase 1 texture 38 fraction 1.0 [Grain039] -crystallite 1 (constituent) phase 1 texture 39 fraction 1.0 [Grain040] -crystallite 1 (constituent) phase 1 texture 40 fraction 1.0 [Grain041] -crystallite 1 (constituent) phase 1 texture 41 fraction 1.0 [Grain042] -crystallite 1 (constituent) phase 1 texture 42 fraction 1.0 [Grain043] -crystallite 1 (constituent) phase 1 texture 43 fraction 1.0 [Grain044] -crystallite 1 (constituent) phase 1 texture 44 fraction 1.0 [Grain045] -crystallite 1 (constituent) phase 1 texture 45 fraction 1.0 [Grain046] -crystallite 1 (constituent) phase 1 texture 46 fraction 1.0 [Grain047] -crystallite 1 (constituent) phase 1 texture 47 fraction 1.0 [Grain048] -crystallite 1 (constituent) phase 1 texture 48 fraction 1.0 [Grain049] -crystallite 1 (constituent) phase 1 texture 49 fraction 1.0 [Grain050] -crystallite 1 (constituent) phase 1 texture 50 fraction 1.0 [Grain051] -crystallite 1 (constituent) phase 1 texture 51 fraction 1.0 [Grain052] -crystallite 1 (constituent) phase 1 texture 52 fraction 1.0 [Grain053] -crystallite 1 (constituent) phase 1 texture 53 fraction 1.0 [Grain054] -crystallite 1 (constituent) phase 1 texture 54 fraction 1.0 [Grain055] -crystallite 1 (constituent) phase 1 texture 55 fraction 1.0 [Grain056] -crystallite 1 (constituent) phase 1 texture 56 fraction 1.0 [Grain057] -crystallite 1 (constituent) phase 1 texture 57 fraction 1.0 [Grain058] -crystallite 1 (constituent) phase 1 texture 58 fraction 1.0 [Grain059] -crystallite 1 (constituent) phase 1 texture 59 fraction 1.0 [Grain060] -crystallite 1 (constituent) phase 1 texture 60 fraction 1.0 [Grain061] -crystallite 1 (constituent) phase 1 texture 61 fraction 1.0 [Grain062] -crystallite 1 (constituent) phase 1 texture 62 fraction 1.0 [Grain063] -crystallite 1 (constituent) phase 1 texture 63 fraction 1.0 [Grain064] -crystallite 1 (constituent) phase 1 texture 64 fraction 1.0 [Grain065] -crystallite 1 (constituent) phase 1 texture 65 fraction 1.0 [Grain066] -crystallite 1 (constituent) phase 1 texture 66 fraction 1.0 [Grain067] -crystallite 1 (constituent) phase 1 texture 67 fraction 1.0 [Grain068] -crystallite 1 (constituent) phase 1 texture 68 fraction 1.0 [Grain069] -crystallite 1 (constituent) phase 1 texture 69 fraction 1.0 [Grain070] -crystallite 1 (constituent) phase 1 texture 70 fraction 1.0 [Grain071] -crystallite 1 (constituent) phase 1 texture 71 fraction 1.0 [Grain072] -crystallite 1 (constituent) phase 1 texture 72 fraction 1.0 [Grain073] -crystallite 1 (constituent) phase 1 texture 73 fraction 1.0 [Grain074] -crystallite 1 (constituent) phase 1 texture 74 fraction 1.0 [Grain075] -crystallite 1 (constituent) phase 1 texture 75 fraction 1.0 [Grain076] -crystallite 1 (constituent) phase 1 texture 76 fraction 1.0 [Grain077] -crystallite 1 (constituent) phase 1 texture 77 fraction 1.0 [Grain078] -crystallite 1 (constituent) phase 1 texture 78 fraction 1.0 [Grain079] -crystallite 1 (constituent) phase 1 texture 79 fraction 1.0 [Grain080] -crystallite 1 (constituent) phase 1 texture 80 fraction 1.0 [Grain081] -crystallite 1 (constituent) phase 1 texture 81 fraction 1.0 [Grain082] -crystallite 1 (constituent) phase 1 texture 82 fraction 1.0 [Grain083] -crystallite 1 (constituent) phase 1 texture 83 fraction 1.0 [Grain084] -crystallite 1 (constituent) phase 1 texture 84 fraction 1.0 [Grain085] -crystallite 1 (constituent) phase 1 texture 85 fraction 1.0 [Grain086] -crystallite 1 (constituent) phase 1 texture 86 fraction 1.0 [Grain087] -crystallite 1 (constituent) phase 1 texture 87 fraction 1.0 [Grain088] -crystallite 1 (constituent) phase 1 texture 88 fraction 1.0 [Grain089] -crystallite 1 (constituent) phase 1 texture 89 fraction 1.0 [Grain090] -crystallite 1 (constituent) phase 1 texture 90 fraction 1.0 [Grain091] -crystallite 1 (constituent) phase 1 texture 91 fraction 1.0 [Grain092] -crystallite 1 (constituent) phase 1 texture 92 fraction 1.0 [Grain093] -crystallite 1 (constituent) phase 1 texture 93 fraction 1.0 [Grain094] -crystallite 1 (constituent) phase 1 texture 94 fraction 1.0 [Grain095] -crystallite 1 (constituent) phase 1 texture 95 fraction 1.0 [Grain096] -crystallite 1 (constituent) phase 1 texture 96 fraction 1.0 [Grain097] -crystallite 1 (constituent) phase 1 texture 97 fraction 1.0 [Grain098] -crystallite 1 (constituent) phase 1 texture 98 fraction 1.0 [Grain099] -crystallite 1 (constituent) phase 1 texture 99 fraction 1.0 [Grain100] -crystallite 1 (constituent) phase 1 texture 100 fraction 1.0 [cubeGrain] -crystallite 1 (constituent) phase 1 texture 101 fraction 1.0 #-------------------# @@ -317,208 +216,208 @@ crystallite 1 #-------------------# [Grain001] -(gauss) phi1 359.121452 Phi 82.319471 Phi2 347.729535 scatter 0 fraction 1 +(gauss) phi1 359.121452 Phi 82.319471 Phi2 347.729535 [Grain002] -(gauss) phi1 269.253967 Phi 105.379919 Phi2 173.029284 scatter 0 fraction 1 +(gauss) phi1 269.253967 Phi 105.379919 Phi2 173.029284 [Grain003] -(gauss) phi1 26.551535 Phi 171.606752 Phi2 124.949264 scatter 0 fraction 1 +(gauss) phi1 26.551535 Phi 171.606752 Phi2 124.949264 [Grain004] -(gauss) phi1 123.207774 Phi 124.339577 Phi2 47.937748 scatter 0 fraction 1 +(gauss) phi1 123.207774 Phi 124.339577 Phi2 47.937748 [Grain005] -(gauss) phi1 324.188825 Phi 103.089216 Phi2 160.373624 scatter 0 fraction 1 +(gauss) phi1 324.188825 Phi 103.089216 Phi2 160.373624 [Grain006] -(gauss) phi1 238.295585 Phi 165.416882 Phi2 234.307741 scatter 0 fraction 1 +(gauss) phi1 238.295585 Phi 165.416882 Phi2 234.307741 [Grain007] -(gauss) phi1 232.707177 Phi 110.733726 Phi2 308.049265 scatter 0 fraction 1 +(gauss) phi1 232.707177 Phi 110.733726 Phi2 308.049265 [Grain008] -(gauss) phi1 144.463291 Phi 125.891441 Phi2 348.674207 scatter 0 fraction 1 +(gauss) phi1 144.463291 Phi 125.891441 Phi2 348.674207 [Grain009] -(gauss) phi1 215.423832 Phi 69.759502 Phi2 164.477632 scatter 0 fraction 1 +(gauss) phi1 215.423832 Phi 69.759502 Phi2 164.477632 [Grain010] -(gauss) phi1 118.805444 Phi 143.057031 Phi2 271.963190 scatter 0 fraction 1 +(gauss) phi1 118.805444 Phi 143.057031 Phi2 271.963190 [Grain011] -(gauss) phi1 218.049576 Phi 64.017550 Phi2 323.040457 scatter 0 fraction 1 +(gauss) phi1 218.049576 Phi 64.017550 Phi2 323.040457 [Grain012] -(gauss) phi1 236.962483 Phi 134.312093 Phi2 220.433366 scatter 0 fraction 1 +(gauss) phi1 236.962483 Phi 134.312093 Phi2 220.433366 [Grain013] -(gauss) phi1 352.317686 Phi 3.356527 Phi2 92.447275 scatter 0 fraction 1 +(gauss) phi1 352.317686 Phi 3.356527 Phi2 92.447275 [Grain014] -(gauss) phi1 198.311545 Phi 71.452240 Phi2 199.441849 scatter 0 fraction 1 +(gauss) phi1 198.311545 Phi 71.452240 Phi2 199.441849 [Grain015] -(gauss) phi1 351.993635 Phi 36.500987 Phi2 236.852886 scatter 0 fraction 1 +(gauss) phi1 351.993635 Phi 36.500987 Phi2 236.852886 [Grain016] -(gauss) phi1 262.389063 Phi 101.249950 Phi2 334.305959 scatter 0 fraction 1 +(gauss) phi1 262.389063 Phi 101.249950 Phi2 334.305959 [Grain017] -(gauss) phi1 53.220668 Phi 69.570254 Phi2 277.061151 scatter 0 fraction 1 +(gauss) phi1 53.220668 Phi 69.570254 Phi2 277.061151 [Grain018] -(gauss) phi1 122.156119 Phi 140.207051 Phi2 221.172906 scatter 0 fraction 1 +(gauss) phi1 122.156119 Phi 140.207051 Phi2 221.172906 [Grain019] -(gauss) phi1 295.422170 Phi 26.595511 Phi2 263.206315 scatter 0 fraction 1 +(gauss) phi1 295.422170 Phi 26.595511 Phi2 263.206315 [Grain020] -(gauss) phi1 179.137406 Phi 104.500977 Phi2 151.742108 scatter 0 fraction 1 +(gauss) phi1 179.137406 Phi 104.500977 Phi2 151.742108 [Grain021] -(gauss) phi1 199.045094 Phi 5.228899 Phi2 356.542109 scatter 0 fraction 1 +(gauss) phi1 199.045094 Phi 5.228899 Phi2 356.542109 [Grain022] -(gauss) phi1 268.671476 Phi 24.835403 Phi2 33.578889 scatter 0 fraction 1 +(gauss) phi1 268.671476 Phi 24.835403 Phi2 33.578889 [Grain023] -(gauss) phi1 264.248527 Phi 59.766630 Phi2 340.865462 scatter 0 fraction 1 +(gauss) phi1 264.248527 Phi 59.766630 Phi2 340.865462 [Grain024] -(gauss) phi1 254.223491 Phi 51.125301 Phi2 201.094027 scatter 0 fraction 1 +(gauss) phi1 254.223491 Phi 51.125301 Phi2 201.094027 [Grain025] -(gauss) phi1 22.214008 Phi 92.248774 Phi2 215.168318 scatter 0 fraction 1 +(gauss) phi1 22.214008 Phi 92.248774 Phi2 215.168318 [Grain026] -(gauss) phi1 49.511491 Phi 79.933539 Phi2 187.188575 scatter 0 fraction 1 +(gauss) phi1 49.511491 Phi 79.933539 Phi2 187.188575 [Grain027] -(gauss) phi1 318.916204 Phi 113.102650 Phi2 241.076629 scatter 0 fraction 1 +(gauss) phi1 318.916204 Phi 113.102650 Phi2 241.076629 [Grain028] -(gauss) phi1 239.378433 Phi 89.578655 Phi2 94.167043 scatter 0 fraction 1 +(gauss) phi1 239.378433 Phi 89.578655 Phi2 94.167043 [Grain029] -(gauss) phi1 27.561421 Phi 142.892093 Phi2 197.735666 scatter 0 fraction 1 +(gauss) phi1 27.561421 Phi 142.892093 Phi2 197.735666 [Grain030] -(gauss) phi1 135.210581 Phi 165.859834 Phi2 285.449561 scatter 0 fraction 1 +(gauss) phi1 135.210581 Phi 165.859834 Phi2 285.449561 [Grain031] -(gauss) phi1 223.515916 Phi 56.824378 Phi2 343.289074 scatter 0 fraction 1 +(gauss) phi1 223.515916 Phi 56.824378 Phi2 343.289074 [Grain032] -(gauss) phi1 41.127974 Phi 111.289145 Phi2 214.855145 scatter 0 fraction 1 +(gauss) phi1 41.127974 Phi 111.289145 Phi2 214.855145 [Grain033] -(gauss) phi1 17.335045 Phi 140.496745 Phi2 77.747371 scatter 0 fraction 1 +(gauss) phi1 17.335045 Phi 140.496745 Phi2 77.747371 [Grain034] -(gauss) phi1 36.206421 Phi 148.574232 Phi2 88.870226 scatter 0 fraction 1 +(gauss) phi1 36.206421 Phi 148.574232 Phi2 88.870226 [Grain035] -(gauss) phi1 159.618336 Phi 125.680504 Phi2 204.119403 scatter 0 fraction 1 +(gauss) phi1 159.618336 Phi 125.680504 Phi2 204.119403 [Grain036] -(gauss) phi1 8.752464 Phi 99.173166 Phi2 143.227089 scatter 0 fraction 1 +(gauss) phi1 8.752464 Phi 99.173166 Phi2 143.227089 [Grain037] -(gauss) phi1 351.570753 Phi 67.343218 Phi2 1.779612 scatter 0 fraction 1 +(gauss) phi1 351.570753 Phi 67.343218 Phi2 1.779612 [Grain038] -(gauss) phi1 46.771572 Phi 155.018674 Phi2 302.319987 scatter 0 fraction 1 +(gauss) phi1 46.771572 Phi 155.018674 Phi2 302.319987 [Grain039] -(gauss) phi1 244.255976 Phi 80.566566 Phi2 264.069331 scatter 0 fraction 1 +(gauss) phi1 244.255976 Phi 80.566566 Phi2 264.069331 [Grain040] -(gauss) phi1 41.775388 Phi 47.109507 Phi2 300.598550 scatter 0 fraction 1 +(gauss) phi1 41.775388 Phi 47.109507 Phi2 300.598550 [Grain041] -(gauss) phi1 268.753103 Phi 46.654050 Phi2 190.382041 scatter 0 fraction 1 +(gauss) phi1 268.753103 Phi 46.654050 Phi2 190.382041 [Grain042] -(gauss) phi1 239.574480 Phi 62.517793 Phi2 147.817535 scatter 0 fraction 1 +(gauss) phi1 239.574480 Phi 62.517793 Phi2 147.817535 [Grain043] -(gauss) phi1 128.059775 Phi 61.916743 Phi2 169.674359 scatter 0 fraction 1 +(gauss) phi1 128.059775 Phi 61.916743 Phi2 169.674359 [Grain044] -(gauss) phi1 166.545156 Phi 58.709099 Phi2 252.885391 scatter 0 fraction 1 +(gauss) phi1 166.545156 Phi 58.709099 Phi2 252.885391 [Grain045] -(gauss) phi1 92.867691 Phi 28.906456 Phi2 164.197290 scatter 0 fraction 1 +(gauss) phi1 92.867691 Phi 28.906456 Phi2 164.197290 [Grain046] -(gauss) phi1 291.056147 Phi 35.145174 Phi2 250.155599 scatter 0 fraction 1 +(gauss) phi1 291.056147 Phi 35.145174 Phi2 250.155599 [Grain047] -(gauss) phi1 79.015862 Phi 44.772479 Phi2 267.982808 scatter 0 fraction 1 +(gauss) phi1 79.015862 Phi 44.772479 Phi2 267.982808 [Grain048] -(gauss) phi1 108.400702 Phi 69.883075 Phi2 222.737053 scatter 0 fraction 1 +(gauss) phi1 108.400702 Phi 69.883075 Phi2 222.737053 [Grain049] -(gauss) phi1 348.326500 Phi 11.339714 Phi2 121.682346 scatter 0 fraction 1 +(gauss) phi1 348.326500 Phi 11.339714 Phi2 121.682346 [Grain050] -(gauss) phi1 331.476209 Phi 108.775043 Phi2 335.139671 scatter 0 fraction 1 +(gauss) phi1 331.476209 Phi 108.775043 Phi2 335.139671 [Grain051] -(gauss) phi1 196.750278 Phi 93.955106 Phi2 63.689075 scatter 0 fraction 1 +(gauss) phi1 196.750278 Phi 93.955106 Phi2 63.689075 [Grain052] -(gauss) phi1 136.077875 Phi 130.508342 Phi2 128.468976 scatter 0 fraction 1 +(gauss) phi1 136.077875 Phi 130.508342 Phi2 128.468976 [Grain053] -(gauss) phi1 239.643513 Phi 76.284643 Phi2 168.821008 scatter 0 fraction 1 +(gauss) phi1 239.643513 Phi 76.284643 Phi2 168.821008 [Grain054] -(gauss) phi1 113.850670 Phi 117.531757 Phi2 71.971648 scatter 0 fraction 1 +(gauss) phi1 113.850670 Phi 117.531757 Phi2 71.971648 [Grain055] -(gauss) phi1 149.554071 Phi 16.543098 Phi2 195.556172 scatter 0 fraction 1 +(gauss) phi1 149.554071 Phi 16.543098 Phi2 195.556172 [Grain056] -(gauss) phi1 46.626579 Phi 52.447846 Phi2 304.495569 scatter 0 fraction 1 +(gauss) phi1 46.626579 Phi 52.447846 Phi2 304.495569 [Grain057] -(gauss) phi1 255.251821 Phi 86.678048 Phi2 238.982712 scatter 0 fraction 1 +(gauss) phi1 255.251821 Phi 86.678048 Phi2 238.982712 [Grain058] -(gauss) phi1 324.266133 Phi 28.075458 Phi2 41.191295 scatter 0 fraction 1 +(gauss) phi1 324.266133 Phi 28.075458 Phi2 41.191295 [Grain059] -(gauss) phi1 312.000332 Phi 74.648725 Phi2 87.403581 scatter 0 fraction 1 +(gauss) phi1 312.000332 Phi 74.648725 Phi2 87.403581 [Grain060] -(gauss) phi1 57.742481 Phi 163.241519 Phi2 68.491438 scatter 0 fraction 1 +(gauss) phi1 57.742481 Phi 163.241519 Phi2 68.491438 [Grain061] -(gauss) phi1 112.442447 Phi 51.735320 Phi2 206.538656 scatter 0 fraction 1 +(gauss) phi1 112.442447 Phi 51.735320 Phi2 206.538656 [Grain062] -(gauss) phi1 297.453842 Phi 115.283041 Phi2 57.785319 scatter 0 fraction 1 +(gauss) phi1 297.453842 Phi 115.283041 Phi2 57.785319 [Grain063] -(gauss) phi1 119.132681 Phi 117.923565 Phi2 196.121206 scatter 0 fraction 1 +(gauss) phi1 119.132681 Phi 117.923565 Phi2 196.121206 [Grain064] -(gauss) phi1 199.267314 Phi 163.091476 Phi2 53.549301 scatter 0 fraction 1 +(gauss) phi1 199.267314 Phi 163.091476 Phi2 53.549301 [Grain065] -(gauss) phi1 37.765215 Phi 76.795488 Phi2 146.264753 scatter 0 fraction 1 +(gauss) phi1 37.765215 Phi 76.795488 Phi2 146.264753 [Grain066] -(gauss) phi1 324.550183 Phi 27.665150 Phi2 56.383148 scatter 0 fraction 1 +(gauss) phi1 324.550183 Phi 27.665150 Phi2 56.383148 [Grain067] -(gauss) phi1 337.305377 Phi 136.807151 Phi2 133.661586 scatter 0 fraction 1 +(gauss) phi1 337.305377 Phi 136.807151 Phi2 133.661586 [Grain068] -(gauss) phi1 115.744041 Phi 64.536978 Phi2 262.694800 scatter 0 fraction 1 +(gauss) phi1 115.744041 Phi 64.536978 Phi2 262.694800 [Grain069] -(gauss) phi1 136.293403 Phi 48.862462 Phi2 343.319175 scatter 0 fraction 1 +(gauss) phi1 136.293403 Phi 48.862462 Phi2 343.319175 [Grain070] -(gauss) phi1 111.030931 Phi 80.823213 Phi2 84.041594 scatter 0 fraction 1 +(gauss) phi1 111.030931 Phi 80.823213 Phi2 84.041594 [Grain071] -(gauss) phi1 303.985249 Phi 118.929631 Phi2 302.307709 scatter 0 fraction 1 +(gauss) phi1 303.985249 Phi 118.929631 Phi2 302.307709 [Grain072] -(gauss) phi1 193.556259 Phi 75.928015 Phi2 176.696899 scatter 0 fraction 1 +(gauss) phi1 193.556259 Phi 75.928015 Phi2 176.696899 [Grain073] -(gauss) phi1 102.543259 Phi 121.929923 Phi2 234.496773 scatter 0 fraction 1 +(gauss) phi1 102.543259 Phi 121.929923 Phi2 234.496773 [Grain074] -(gauss) phi1 218.581323 Phi 101.753894 Phi2 305.566089 scatter 0 fraction 1 +(gauss) phi1 218.581323 Phi 101.753894 Phi2 305.566089 [Grain075] -(gauss) phi1 229.542114 Phi 118.839215 Phi2 129.179156 scatter 0 fraction 1 +(gauss) phi1 229.542114 Phi 118.839215 Phi2 129.179156 [Grain076] -(gauss) phi1 202.258840 Phi 139.205956 Phi2 352.248979 scatter 0 fraction 1 +(gauss) phi1 202.258840 Phi 139.205956 Phi2 352.248979 [Grain077] -(gauss) phi1 137.954289 Phi 63.806918 Phi2 128.975049 scatter 0 fraction 1 +(gauss) phi1 137.954289 Phi 63.806918 Phi2 128.975049 [Grain078] -(gauss) phi1 327.557366 Phi 84.987420 Phi2 345.483143 scatter 0 fraction 1 +(gauss) phi1 327.557366 Phi 84.987420 Phi2 345.483143 [Grain079] -(gauss) phi1 334.610243 Phi 74.535474 Phi2 106.419231 scatter 0 fraction 1 +(gauss) phi1 334.610243 Phi 74.535474 Phi2 106.419231 [Grain080] -(gauss) phi1 62.906243 Phi 46.752029 Phi2 222.692276 scatter 0 fraction 1 +(gauss) phi1 62.906243 Phi 46.752029 Phi2 222.692276 [Grain081] -(gauss) phi1 254.121439 Phi 121.005485 Phi2 287.265977 scatter 0 fraction 1 +(gauss) phi1 254.121439 Phi 121.005485 Phi2 287.265977 [Grain082] -(gauss) phi1 140.765045 Phi 141.268031 Phi2 271.327656 scatter 0 fraction 1 +(gauss) phi1 140.765045 Phi 141.268031 Phi2 271.327656 [Grain083] -(gauss) phi1 10.726984 Phi 66.339177 Phi2 189.073212 scatter 0 fraction 1 +(gauss) phi1 10.726984 Phi 66.339177 Phi2 189.073212 [Grain084] -(gauss) phi1 270.921536 Phi 72.821127 Phi2 313.590515 scatter 0 fraction 1 +(gauss) phi1 270.921536 Phi 72.821127 Phi2 313.590515 [Grain085] -(gauss) phi1 299.059668 Phi 23.884874 Phi2 80.016277 scatter 0 fraction 1 +(gauss) phi1 299.059668 Phi 23.884874 Phi2 80.016277 [Grain086] -(gauss) phi1 208.617406 Phi 11.031834 Phi2 302.388247 scatter 0 fraction 1 +(gauss) phi1 208.617406 Phi 11.031834 Phi2 302.388247 [Grain087] -(gauss) phi1 62.929967 Phi 65.223261 Phi2 108.558265 scatter 0 fraction 1 +(gauss) phi1 62.929967 Phi 65.223261 Phi2 108.558265 [Grain088] -(gauss) phi1 9.014959 Phi 33.542169 Phi2 247.970366 scatter 0 fraction 1 +(gauss) phi1 9.014959 Phi 33.542169 Phi2 247.970366 [Grain089] -(gauss) phi1 272.432808 Phi 30.065174 Phi2 19.803570 scatter 0 fraction 1 +(gauss) phi1 272.432808 Phi 30.065174 Phi2 19.803570 [Grain090] -(gauss) phi1 179.621980 Phi 151.763475 Phi2 61.871794 scatter 0 fraction 1 +(gauss) phi1 179.621980 Phi 151.763475 Phi2 61.871794 [Grain091] -(gauss) phi1 247.810321 Phi 112.752980 Phi2 264.668469 scatter 0 fraction 1 +(gauss) phi1 247.810321 Phi 112.752980 Phi2 264.668469 [Grain092] -(gauss) phi1 270.780630 Phi 102.037858 Phi2 31.602610 scatter 0 fraction 1 +(gauss) phi1 270.780630 Phi 102.037858 Phi2 31.602610 [Grain093] -(gauss) phi1 17.626672 Phi 56.032415 Phi2 245.079600 scatter 0 fraction 1 +(gauss) phi1 17.626672 Phi 56.032415 Phi2 245.079600 [Grain094] -(gauss) phi1 112.165186 Phi 87.390459 Phi2 182.086729 scatter 0 fraction 1 +(gauss) phi1 112.165186 Phi 87.390459 Phi2 182.086729 [Grain095] -(gauss) phi1 157.869381 Phi 79.905131 Phi2 107.037081 scatter 0 fraction 1 +(gauss) phi1 157.869381 Phi 79.905131 Phi2 107.037081 [Grain096] -(gauss) phi1 106.163846 Phi 148.477084 Phi2 350.980466 scatter 0 fraction 1 +(gauss) phi1 106.163846 Phi 148.477084 Phi2 350.980466 [Grain097] -(gauss) phi1 262.138550 Phi 58.923588 Phi2 111.303439 scatter 0 fraction 1 +(gauss) phi1 262.138550 Phi 58.923588 Phi2 111.303439 [Grain098] -(gauss) phi1 88.739397 Phi 119.092789 Phi2 222.502594 scatter 0 fraction 1 +(gauss) phi1 88.739397 Phi 119.092789 Phi2 222.502594 [Grain099] -(gauss) phi1 337.603765 Phi 10.145102 Phi2 80.934916 scatter 0 fraction 1 +(gauss) phi1 337.603765 Phi 10.145102 Phi2 80.934916 [Grain100] -(gauss) phi1 341.022242 Phi 45.927285 Phi2 252.045476 scatter 0 fraction 1 +(gauss) phi1 341.022242 Phi 45.927285 Phi2 252.045476 [cube] -(gauss) phi1 0 Phi 0 phi2 0 scatter 0 fraction 1 +(gauss) phi1 0 Phi 0 phi2 0 #-------------------# diff --git a/examples/SpectralMethod/EshelbyInclusion/material.config b/examples/SpectralMethod/EshelbyInclusion/material.config index d1ea80964..6dda5c2dd 100644 --- a/examples/SpectralMethod/EshelbyInclusion/material.config +++ b/examples/SpectralMethod/EshelbyInclusion/material.config @@ -104,8 +104,8 @@ crystallite 1 #--------------------------# [cube] -(gauss) phi1 0.0 Phi 0.0 phi2 0.0 scatter 0.0 fraction 1.0 +(gauss) phi1 0.0 Phi 0.0 phi2 0.0 [rotated] -(gauss) phi1 0.0 Phi 45.0 phi2 0.0 scatter 0.0 fraction 1.0 +(gauss) phi1 0.0 Phi 45.0 phi2 0.0 diff --git a/examples/SpectralMethod/Polycrystal/material.config b/examples/SpectralMethod/Polycrystal/material.config index e47c2142c..44e8b8c1d 100644 --- a/examples/SpectralMethod/Polycrystal/material.config +++ b/examples/SpectralMethod/Polycrystal/material.config @@ -51,64 +51,44 @@ atol_resistance 1 #-------------------# [Grain01] -crystallite 1 (constituent) phase 1 texture 01 fraction 1.0 [Grain02] -crystallite 1 (constituent) phase 1 texture 02 fraction 1.0 [Grain03] -crystallite 1 (constituent) phase 1 texture 03 fraction 1.0 [Grain04] -crystallite 1 (constituent) phase 1 texture 04 fraction 1.0 [Grain05] -crystallite 1 (constituent) phase 1 texture 05 fraction 1.0 [Grain06] -crystallite 1 (constituent) phase 1 texture 06 fraction 1.0 [Grain07] -crystallite 1 (constituent) phase 1 texture 07 fraction 1.0 [Grain08] -crystallite 1 (constituent) phase 1 texture 08 fraction 1.0 [Grain09] -crystallite 1 (constituent) phase 1 texture 09 fraction 1.0 [Grain10] -crystallite 1 (constituent) phase 1 texture 10 fraction 1.0 [Grain11] -crystallite 1 (constituent) phase 1 texture 11 fraction 1.0 [Grain12] -crystallite 1 (constituent) phase 1 texture 12 fraction 1.0 [Grain13] -crystallite 1 (constituent) phase 1 texture 13 fraction 1.0 [Grain14] -crystallite 1 (constituent) phase 1 texture 14 fraction 1.0 [Grain15] -crystallite 1 (constituent) phase 1 texture 15 fraction 1.0 [Grain16] -crystallite 1 (constituent) phase 1 texture 16 fraction 1.0 [Grain17] -crystallite 1 (constituent) phase 1 texture 17 fraction 1.0 [Grain18] -crystallite 1 (constituent) phase 1 texture 18 fraction 1.0 [Grain19] -crystallite 1 (constituent) phase 1 texture 19 fraction 1.0 [Grain20] -crystallite 1 (constituent) phase 1 texture 20 fraction 1.0 @@ -116,42 +96,42 @@ crystallite 1 #-------------------# [Grain01] -(gauss) phi1 0.0 Phi 0.0 phi2 0.0 scatter 0.0 fraction 1.0 +(gauss) phi1 0.0 Phi 0.0 phi2 0.0 [Grain02] -(gauss) phi1 257.468172 Phi 53.250534 phi2 157.331503 scatter 0.0 fraction 1.0 +(gauss) phi1 257.468172 Phi 53.250534 phi2 157.331503 [Grain03] -(gauss) phi1 216.994815 Phi 94.418518 phi2 251.147231 scatter 0.0 fraction 1.0 +(gauss) phi1 216.994815 Phi 94.418518 phi2 251.147231 [Grain04] -(gauss) phi1 196.157946 Phi 55.870978 phi2 21.68117 scatter 0.0 fraction 1.0 +(gauss) phi1 196.157946 Phi 55.870978 phi2 21.68117 [Grain05] -(gauss) phi1 152.515728 Phi 139.769395 phi2 240.036018 scatter 0.0 fraction 1.0 +(gauss) phi1 152.515728 Phi 139.769395 phi2 240.036018 [Grain06] -(gauss) phi1 232.521881 Phi 73.749222 phi2 241.429633 scatter 0.0 fraction 1.0 +(gauss) phi1 232.521881 Phi 73.749222 phi2 241.429633 [Grain07] -(gauss) phi1 157.531396 Phi 135.503513 phi2 75.737722 scatter 0.0 fraction 1.0 +(gauss) phi1 157.531396 Phi 135.503513 phi2 75.737722 [Grain08] -(gauss) phi1 321.03828 Phi 27.209843 phi2 46.413467 scatter 0.0 fraction 1.0 +(gauss) phi1 321.03828 Phi 27.209843 phi2 46.413467 [Grain09] -(gauss) phi1 346.918594 Phi 87.495569 phi2 113.554206 scatter 0.0 fraction 1.0 +(gauss) phi1 346.918594 Phi 87.495569 phi2 113.554206 [Grain10] -(gauss) phi1 138.038947 Phi 99.827132 phi2 130.935878 scatter 0.0 fraction 1.0 +(gauss) phi1 138.038947 Phi 99.827132 phi2 130.935878 [Grain11] -(gauss) phi1 285.021014 Phi 118.092004 phi2 205.270837 scatter 0.0 fraction 1.0 +(gauss) phi1 285.021014 Phi 118.092004 phi2 205.270837 [Grain12] -(gauss) phi1 190.402171 Phi 56.738068 phi2 157.896545 scatter 0.0 fraction 1.0 +(gauss) phi1 190.402171 Phi 56.738068 phi2 157.896545 [Grain13] -(gauss) phi1 204.496042 Phi 95.031265 phi2 355.814582 scatter 0.0 fraction 1.0 +(gauss) phi1 204.496042 Phi 95.031265 phi2 355.814582 [Grain14] -(gauss) phi1 333.21479 Phi 82.133355 phi2 36.736132 scatter 0.0 fraction 1.0 +(gauss) phi1 333.21479 Phi 82.133355 phi2 36.736132 [Grain15] -(gauss) phi1 25.572981 Phi 164.242648 phi2 75.195632 scatter 0.0 fraction 1.0 +(gauss) phi1 25.572981 Phi 164.242648 phi2 75.195632 [Grain16] -(gauss) phi1 31.366548 Phi 76.392403 phi2 58.071426 scatter 0.0 fraction 1.0 +(gauss) phi1 31.366548 Phi 76.392403 phi2 58.071426 [Grain17] -(gauss) phi1 7.278623 Phi 77.044663 phi2 235.118997 scatter 0.0 fraction 1.0 +(gauss) phi1 7.278623 Phi 77.044663 phi2 235.118997 [Grain18] -(gauss) phi1 299.743144 Phi 76.475096 phi2 91.184977 scatter 0.0 fraction 1.0 +(gauss) phi1 299.743144 Phi 76.475096 phi2 91.184977 [Grain19] -(gauss) phi1 280.13643 Phi 27.439718 phi2 167.871878 scatter 0.0 fraction 1.0 +(gauss) phi1 280.13643 Phi 27.439718 phi2 167.871878 [Grain20] -(gauss) phi1 313.204373 Phi 68.676053 phi2 87.993213 scatter 0.0 fraction 1.0 +(gauss) phi1 313.204373 Phi 68.676053 phi2 87.993213 From e47119e7f75d0f7fcae2dc4308114287f7da34d2 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 2 Jan 2020 12:11:40 +0100 Subject: [PATCH 156/223] outdated/not needed --- .../reference_postProc/rotation_90deg.txt | 94 ------------------- .../rotation_90deg_inc90.txt | 3 - .../SpectralMethod/EshelbyInclusion/runAll.sh | 54 ----------- 3 files changed, 151 deletions(-) delete mode 100644 examples/MSC.Marc/reference_postProc/rotation_90deg.txt delete mode 100644 examples/MSC.Marc/reference_postProc/rotation_90deg_inc90.txt delete mode 100755 examples/SpectralMethod/EshelbyInclusion/runAll.sh diff --git a/examples/MSC.Marc/reference_postProc/rotation_90deg.txt b/examples/MSC.Marc/reference_postProc/rotation_90deg.txt deleted file mode 100644 index 27c80fbdb..000000000 --- a/examples/MSC.Marc/reference_postProc/rotation_90deg.txt +++ /dev/null @@ -1,94 +0,0 @@ -2 header -$Id: postResults 861 2011-05-06 10:00:27Z MPIE\c.kords $ -inc time elem node ip grain ip.x ip.y ip.z CauchyStress.intensity CauchyStress.t11 CauchyStress.t22 CauchyStress.t33 CauchyStress.t12 CauchyStress.t23 CauchyStress.t13 1_1_f 1_2_f 1_3_f 1_4_f 1_5_f 1_6_f 1_7_f 1_8_f 1_9_f 1_1_grainrotation 1_2_grainrotation 1_3_grainrotation 1_4_grainrotation 1_1_resistance_slip 1_2_resistance_slip 1_3_resistance_slip 1_4_resistance_slip 1_5_resistance_slip 1_6_resistance_slip 1_7_resistance_slip 1_8_resistance_slip 1_9_resistance_slip 1_10_resistance_slip 1_11_resistance_slip 1_12_resistance_slip -0 0.0 1 5 1 1 0.5 0.5 0.5 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -1 1.0 1 5 1 1 0.5 0.5 0.5 0.024172998067 0.056046936661 0.0577092021704 0.0580734722316 0.0075496127829 0.00882737897336 0.00766104180366 1.0 1.1259596093e-13 1.12595994811e-13 1.55780499177e-13 0.999847710133 -0.0174524057657 1.55782193243e-13 0.0174524057657 0.999847710133 1.0 -1.23725617425e-12 1.23720879461e-12 1.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -2 2.0 1 5 1 1 0.5 0.5 0.5 0.0241432830571 0.0377263836563 0.0324090756476 0.033376660198 0.00727691268548 0.00865175202489 0.00764666078612 1.0 1.10501743118e-13 1.10501777e-13 1.50193822223e-13 0.99939084053 -0.034899495542 1.55527744546e-13 0.034899495542 0.99939084053 1.0 -6.45080505939e-13 5.68662738426e-13 2.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -3 3.0 1 5 1 1 0.5 0.5 0.5 0.0257848323757 0.0201567672193 0.00817993376404 0.00972554087639 0.00701188668609 0.00847246591002 0.00763081293553 1.0 1.08486407791e-13 1.08486434896e-13 1.447601533e-13 0.998629510403 -0.0523359552026 1.55243941075e-13 0.0523359552026 0.998629510403 1.0 -4.46705416797e-13 3.46547009838e-13 3.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -4 4.0 1 5 1 1 0.5 0.5 0.5 0.0286209738301 0.00342932180502 -0.0149000706151 -0.0128218811005 0.00675505120307 0.00828998535872 0.0076136472635 1.0 1.06547922069e-13 1.06547949174e-13 1.39501515266e-13 0.997564077377 -0.0697564706206 1.54932937682e-13 0.0697564706206 0.997564077377 1.0 -3.46813588048e-13 2.36204529243e-13 4.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -5 5.0 1 5 1 1 0.5 0.5 0.5 0.032148640163 -0.0124530605972 -0.0368128865957 -0.0341981202364 0.00650494545698 0.00810338370502 0.00759505899623 1.0 1.04684232745e-13 1.04684253074e-13 1.34370257434e-13 0.996194720268 -0.0871557444334 1.54588120733e-13 0.0871557444334 0.996194720268 1.0 -2.8629133569e-13 1.70304512138e-13 5.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -6 6.0 1 5 1 1 0.5 0.5 0.5 0.0360145416329 -0.027449907735 -0.0575108379126 -0.0543776340783 0.00626290449873 0.00791467912495 0.0075753852725 1.0 1.02893266281e-13 1.0289328661e-13 1.29417364412e-13 0.994521915913 -0.104528464377 1.54218746606e-13 0.104528464377 0.994521915913 1.0 -2.45509477548e-13 1.26874964528e-13 6.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -7 7.0 1 5 1 1 0.5 0.5 0.5 0.0399740663918 -0.0415176264942 -0.0769297853112 -0.0733039304614 0.00602708896622 0.00772315822542 0.00755509966984 1.0 1.01173003352e-13 1.01173016904e-13 1.24581768512e-13 0.992546141148 -0.121869340539 1.5383908611e-13 0.121869340539 0.992546141148 1.0 -2.1607593644e-13 9.60403970436e-14 7.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -8 8.0 1 5 1 1 0.5 0.5 0.5 0.0438656010832 -0.0547002702951 -0.0950862541795 -0.0910048484802 0.00579795939848 0.0075320713222 0.00753418169916 1.0 9.95214110774e-14 9.952142463e-14 1.19883541028e-13 0.990268051624 -0.139173105359 1.5344615769e-13 0.139173105359 0.990268051624 1.0 -1.93732616754e-13 7.31539937134e-14 8.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -9 9.0 1 5 1 1 0.5 0.5 0.5 0.0476265064906 -0.06693007797 -0.111963532865 -0.107425913215 0.00557443965226 0.00733647309244 0.00751292472705 1.0 9.79365107892e-14 9.79365175654e-14 1.15288543144e-13 0.987688362598 -0.156434461474 1.53048269045e-13 0.156434461474 0.987688362598 1.0 -1.7614966289e-13 5.54610577028e-14 9.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -10 10.0 1 5 1 1 0.5 0.5 0.5 0.0511619284052 -0.0781974568963 -0.127493560314 -0.122531078756 0.00535882124677 0.00714056473225 0.00749061629176 1.0 9.64163509231e-14 9.64163509231e-14 1.10881335851e-13 0.984807729721 -0.173648178577 1.52618409989e-13 0.173648178577 0.984807729721 1.0 -1.61827379296e-13 4.16502735892e-14 10.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -11 11.0 1 5 1 1 0.5 0.5 0.5 0.0544590124719 -0.088471762836 -0.141687169671 -0.136302277446 0.0051483400166 0.00694213900715 0.00746928341687 1.0 9.49590273492e-14 9.49590341255e-14 1.06568040796e-13 0.981627166271 -0.190808996558 1.52226335378e-13 0.190808996558 0.981627166271 1.0 -1.50064490456e-13 3.04205106743e-14 11.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -12 12.0 1 5 1 1 0.5 0.5 0.5 0.0574517639017 -0.0978478044271 -0.154580652714 -0.148810505867 0.00494291307405 0.00674560666084 0.00744635425508 1.0 9.35626969238e-14 9.35626969238e-14 1.02349369485e-13 0.978147625923 -0.207911685109 1.51779779608e-13 0.207911685109 0.978147625923 1.0 -1.40004354681e-13 2.11307767752e-14 12.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -13 13.0 1 5 1 1 0.5 0.5 0.5 0.0601600304963 -0.10622742027 -0.166131272912 -0.159981891513 0.0047429674305 0.00654464075342 0.00742361694574 1.0 9.22255571608e-14 9.22255503845e-14 9.82438888813e-14 0.974370062351 -0.224951043725 1.5134031181e-13 0.224951043725 0.974370062351 1.0 -1.31394718782e-13 1.33769905892e-14 13.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -14 14.0 1 5 1 1 0.5 0.5 0.5 0.0625560237246 -0.113614186645 -0.176321923733 -0.169804736972 0.00454916572198 0.00634495634586 0.00740135088563 1.0 9.09458597841e-14 9.09458530078e-14 9.42784397643e-14 0.970295727253 -0.241921886802 1.50916524286e-13 0.241921886802 0.970295727253 1.0 -1.23946358277e-13 6.88772167895e-15 14.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -15 15.0 1 5 1 1 0.5 0.5 0.5 0.064623152588 -0.120048590004 -0.185179919004 -0.17832493782 0.00435922853649 0.00614238297567 0.00737902149558 1.0 8.97219175041e-14 8.97219107278e-14 9.03759421358e-14 0.965925812721 -0.258819043636 1.50488345743e-13 0.258819043636 0.965925812721 1.0 -1.17391732728e-13 1.26347923687e-15 15.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -16 16.0 1 5 1 1 0.5 0.5 0.5 0.0663746195376 -0.125523671508 -0.192728817463 -0.185538485646 0.00417540827766 0.00594279170036 0.00735600618646 1.0 8.85521107938e-14 8.85521040175e-14 8.66191951607e-14 0.961261689663 -0.275637358427 1.50032669123e-13 0.275637358427 0.961261689663 1.0 -1.11524369384e-13 -3.50626472131e-15 16.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -17 17.0 1 5 1 1 0.5 0.5 0.5 0.0677872073143 -0.130049750209 -0.198957800865 -0.191445931792 0.00399514567107 0.00573630817235 0.00733454944566 1.0 8.74348675601e-14 8.74348607838e-14 8.29210628655e-14 0.956304728985 -0.292371690273 1.49627706059e-13 0.292371690273 0.956304728985 1.0 -1.06359209905e-13 -7.7192983925e-15 17.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -18 18.0 1 5 1 1 0.5 0.5 0.5 0.0688726440667 -0.13366368413 -0.20391356945 -0.196102648973 0.00382082024589 0.00553295295686 0.00731354439631 1.0 8.63686902487e-14 8.63686766962e-14 7.93686337898e-14 0.951056540012 -0.309017002583 1.49233476596e-13 0.309017002583 0.951056540012 1.0 -1.01717394815e-13 -1.1326327212e-14 18.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -19 19.0 1 5 1 1 0.5 0.5 0.5 0.0696297180331 -0.136321663857 -0.207558274269 -0.199463963509 0.00364940264262 0.00533262779936 0.00729088904336 1.0 8.53521355156e-14 8.53521151868e-14 7.58580481416e-14 0.945518553257 -0.325568139553 1.48775008155e-13 0.325568139553 0.945518553257 1.0 -9.7403408581e-14 -1.45807929668e-14 19.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -20 20.0 1 5 1 1 0.5 0.5 0.5 0.0700644066665 -0.138097688556 -0.209973961115 -0.201613843441 0.00348059250973 0.00512766698375 0.00727100577205 1.0 8.43838074503e-14 8.43837938977e-14 7.23816555195e-14 0.939692616463 -0.342020124197 1.48407572039e-13 0.342020124197 0.939692616463 1.0 -9.35965172554e-14 -1.75459757942e-14 20.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -21 21.0 1 5 1 1 0.5 0.5 0.5 0.0701877711751 -0.138971403241 -0.211157605052 -0.20253777504 0.00331580708735 0.00492311827838 0.00725055858493 1.0 8.34624050102e-14 8.34623846814e-14 6.89923311081e-14 0.933580458164 -0.358367949724 1.4801277337e-13 0.358367949724 0.933580458164 1.0 -9.0061604821e-14 -2.01888507268e-14 21.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -22 22.0 1 5 1 1 0.5 0.5 0.5 0.0699866553715 -0.138959825039 -0.211107447743 -0.202245801687 0.00315649039112 0.00472341617569 0.00723067810759 1.0 8.25866610342e-14 8.25866339291e-14 6.57438174539e-14 0.927183866501 -0.374606579542 1.4762987382e-13 0.374606579542 0.927183866501 1.0 -8.68153950963e-14 -2.24807118179e-14 22.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -23 23.0 1 5 1 1 0.5 0.5 0.5 0.0694881714825 -0.138143435121 -0.209938883781 -0.200845211744 0.00299837184139 0.00451778201386 0.00721126794815 1.0 8.17553761235e-14 8.17553490184e-14 6.24881345127e-14 0.920504868031 -0.39073112607 1.47254902499e-13 0.39073112607 0.920504868031 1.0 -8.38166477413e-14 -2.46553706542e-14 23.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -24 24.0 1 5 1 1 0.5 0.5 0.5 0.0687132541533 -0.136471450329 -0.20762142539 -0.198303565383 0.0028445108328 0.00431606685743 0.00719203986228 1.0 8.09674050896e-14 8.09673779845e-14 5.93358776826e-14 0.913545489311 -0.406736642122 1.46877193567e-13 0.406736642122 0.913545489311 1.0 -8.10227265054e-14 -2.65915626107e-14 24.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -25 25.0 1 5 1 1 0.5 0.5 0.5 0.0676169983635 -0.13403198123 -0.204196736217 -0.194667950273 0.00269203982316 0.00410861568525 0.00717331608757 1.0 8.02216705066e-14 8.02216434015e-14 5.61884252234e-14 0.906307816505 -0.422618240118 1.46507656809e-13 0.422618240118 0.906307816505 1.0 -7.84230400888e-14 -2.84337508508e-14 25.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -26 26.0 1 5 1 1 0.5 0.5 0.5 0.0662631331079 -0.130787238479 -0.199683398008 -0.189970225096 0.00254649785347 0.00390863511711 0.00715752178803 1.0 7.95171491586e-14 7.95171220536e-14 5.32437219585e-14 0.898794054985 -0.438371151686 1.46231442753e-13 0.438371151686 0.898794054985 1.0 -7.6093421657e-14 -2.99671074901e-14 26.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -27 27.0 1 5 1 1 0.5 0.5 0.5 0.0646436064666 -0.126818150282 -0.194168791175 -0.184269443154 0.00239934097044 0.00370743637905 0.00713936518878 1.0 7.88528788163e-14 7.8852844935e-14 5.02027178238e-14 0.891006529331 -0.453990489244 1.45861472314e-13 0.453990489244 0.891006529331 1.0 -7.37995886732e-14 -3.15536984562e-14 27.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -28 28.0 1 5 1 1 0.5 0.5 0.5 0.0627565607148 -0.122118026018 -0.18763422966 -0.177565723658 0.00225654477254 0.00350114423782 0.00712363282219 1.0 7.82279311316e-14 7.82279040265e-14 4.72786279527e-14 0.88294762373 -0.469471544027 1.45566271168e-13 0.469471544027 0.88294762373 1.0 -7.17171954418e-14 -3.29618534615e-14 28.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -29 29.0 1 5 1 1 0.5 0.5 0.5 0.0606526623375 -0.11671321094 -0.180158898234 -0.169930398464 0.00211710762233 0.003298870055 0.00710933981463 1.0 7.76414658477e-14 7.76414319664e-14 4.44384214485e-14 0.874619722366 -0.484809607267 1.45310806031e-13 0.484809607267 0.874619722366 1.0 -6.9789633057e-14 -3.42433838175e-14 29.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -30 30.0 1 5 1 1 0.5 0.5 0.5 0.0582944850272 -0.1107018888 -0.171804904938 -0.161417961121 0.00197660620324 0.00309796072543 0.00709319859743 1.0 7.7092669813e-14 7.70926291555e-14 4.15282669635e-14 0.866025388241 -0.5 1.44978782668e-13 0.5 0.866025388241 1.0 -6.78861535128e-14 -3.55643994614e-14 30.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -31 31.0 1 5 1 1 0.5 0.5 0.5 0.0557503201598 -0.104059666395 -0.162606656551 -0.152081504464 0.00184033811092 0.00289721833542 0.00708150491118 1.0 7.65807840861e-14 7.65807502048e-14 3.87372832379e-14 0.857167303562 -0.515038073063 1.44792137264e-13 0.515038073063 0.857167303562 1.0 -6.62197551025e-14 -3.67385463685e-14 31.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -32 32.0 1 5 1 1 0.5 0.5 0.5 0.0529884126408 -0.0968004092574 -0.152546048164 -0.14190004766 0.00170668761712 0.0026978047099 0.00706525752321 1.0 7.6105117488e-14 7.61050768304e-14 3.60106096418e-14 0.848048090935 -0.529919266701 1.44433035952e-13 0.529919266701 0.848048090935 1.0 -6.44701645043e-14 -3.78307716371e-14 32.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -33 33.0 1 5 1 1 0.5 0.5 0.5 0.0500567272075 -0.0890314355493 -0.141778171062 -0.131015405059 0.00157438474707 0.00249168649316 0.00705360202119 1.0 7.56650127211e-14 7.56649788398e-14 3.33055252214e-14 0.838670551777 -0.544639050961 1.44223988221e-13 0.544639050961 0.838670551777 1.0 -6.29398605716e-14 -3.88876756317e-14 33.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -34 34.0 1 5 1 1 0.5 0.5 0.5 0.0469640629868 -0.0807560905814 -0.130312100053 -0.119427792728 0.00144245161209 0.00229033012874 0.00704180356115 1.0 7.52598734743e-14 7.52598328167e-14 3.05895784506e-14 0.829037606716 -0.559192895889 1.43997755885e-13 0.559192895889 0.829037606716 1.0 -6.14617270923e-14 -3.99417607007e-14 34.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -35 35.0 1 5 1 1 0.5 0.5 0.5 0.0437172172209 -0.071987785399 -0.118148125708 -0.107174038887 0.00131240475457 0.00208335206844 0.00703246705234 1.0 7.48891440939e-14 7.48891034364e-14 2.79183008092e-14 0.819152057171 -0.573576450348 1.43846116659e-13 0.573576450348 0.819152057171 1.0 -6.01114481542e-14 -4.09455862228e-14 35.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -36 36.0 1 5 1 1 0.5 0.5 0.5 0.0403788372113 -0.0627495497465 -0.105374902487 -0.0943066850305 0.00118379341438 0.00188469397835 0.00702259968966 1.0 7.45523163603e-14 7.45522757027e-14 2.5277750135e-14 0.809017002583 -0.587785243988 1.43663347277e-13 0.587785243988 0.809017002583 1.0 -5.87893923538e-14 -4.19154524999e-14 36.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -37 37.0 1 5 1 1 0.5 0.5 0.5 0.0369407713034 -0.053146019578 -0.0920672789216 -0.0809241756797 0.00105845439248 0.00168191338889 0.0070105525665 1.0 7.42489362636e-14 7.42488888298e-14 2.27346861768e-14 0.798635542393 -0.601814985275 1.43391131217e-13 0.601814985275 0.798635542393 1.0 -5.74447715958e-14 -4.27990704942e-14 37.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -38 38.0 1 5 1 1 0.5 0.5 0.5 0.0334600072977 -0.0431970842183 -0.0782782137394 -0.0670673400164 0.000933687435463 0.00148375425488 0.00700649619102 1.0 7.39785836756e-14 7.39785362418e-14 2.01957371047e-14 0.788010776043 -0.61566144228 1.43388285186e-13 0.61566144228 0.788010776043 1.0 -5.63700595804e-14 -4.36789107216e-14 38.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -39 39.0 1 5 1 1 0.5 0.5 0.5 0.0299732687575 -0.032885748893 -0.0640020221472 -0.0527336075902 0.000808531127404 0.00128437299281 0.00699805375189 1.0 7.37408926781e-14 7.37408452443e-14 1.76288596622e-14 0.777145981789 -0.629320383072 1.4321798413e-13 0.629320383072 0.777145981789 1.0 -5.52001275093e-14 -4.45814548201e-14 39.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -40 40.0 1 5 1 1 0.5 0.5 0.5 0.0265501119175 -0.0222893729806 -0.0493332147598 -0.0380037464201 0.000683732156176 0.00107720762026 0.00699263811111 1.0 7.35355447866e-14 7.35354973528e-14 1.50622549643e-14 0.766044437885 -0.642787575722 1.43141751165e-13 0.642787575722 0.766044437885 1.0 -5.41440536069e-14 -4.54841479964e-14 40.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -41 41.0 1 5 1 1 0.5 0.5 0.5 0.0232912298862 -0.0114479679614 -0.0343343839049 -0.0229646526277 0.000558626372367 0.000879483588506 0.00698518194258 1.0 7.33622553982e-14 7.33622079643e-14 1.24743245179e-14 0.754709601402 -0.656059026718 1.42980692932e-13 0.656059026718 0.754709601402 1.0 -5.30580997703e-14 -4.64043103802e-14 41.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -42 42.0 1 5 1 1 0.5 0.5 0.5 0.0203406476883 -0.000415417132899 -0.0190748237073 -0.00765737332404 0.000437716866145 0.000681487843394 0.00698295794427 1.0 7.32207941197e-14 7.32207466859e-14 1.00257809663e-14 0.7431448102 -0.669130623341 1.42991467191e-13 0.669130623341 0.7431448102 1.0 -5.21353590182e-14 -4.72217344437e-14 42.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -43 43.0 1 5 1 1 0.5 0.5 0.5 0.017873190849 0.0107409814373 -0.00360602373257 0.00782212708145 0.000314239208819 0.000479505921248 0.00697580305859 1.0 7.31109712159e-14 7.31109170058e-14 7.47973206403e-15 0.731353700161 -0.681998372078 1.42815406311e-13 0.681998372078 0.731353700161 1.0 -5.1103120623e-14 -4.81168585336e-14 43.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -44 44.0 1 5 1 1 0.5 0.5 0.5 0.0162081584588 0.0220755711198 0.0120714716613 0.0235084760934 0.000193880769075 0.000282061198959 0.00697334948927 1.0 7.30326240564e-14 7.30325698463e-14 5.03865299026e-15 0.71933978796 -0.694658339024 1.42792353462e-13 0.694658339024 0.71933978796 1.0 -5.02115777884e-14 -4.89405845223e-14 44.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -45 45.0 1 5 1 1 0.5 0.5 0.5 0.0156273554774 0.0334772281349 0.0278379991651 0.0392913781106 7.09175001248e-05 8.74613033375e-05 0.00697097880766 1.0 7.29856645498e-14 7.29856103397e-14 2.50234157871e-15 0.707106769085 -0.707106769085 1.42759393716e-13 0.707106769085 0.707106769085 1.0 -4.93375177139e-14 -4.98392356573e-14 45.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -46 46.0 1 5 1 1 0.5 0.5 0.5 0.0162480691786 0.0449191257358 0.0436743237078 0.0551158338785 -4.99888519698e-05 -0.000117406474601 0.00696983095258 1.0 7.29700181572e-14 7.29699571708e-14 3.70651989336e-17 0.694658398628 -0.71933978796 1.42756737421e-13 0.71933978796 0.694658398628 1.0 -4.85075202933e-14 -5.06943662396e-14 46.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -47 47.0 1 5 1 1 0.5 0.5 0.5 0.017954318699 0.0563476122916 0.0594997182488 0.0709141045809 -0.000170402156073 -0.000321725587128 0.00697033200413 1.0 7.29856645498e-14 7.29856035634e-14 -2.41059012283e-15 0.681998372078 -0.731353700161 1.42799441434e-13 0.731353700161 0.681998372078 1.0 -4.77291918824e-14 -5.15456851735e-14 47.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -48 48.0 1 5 1 1 0.5 0.5 0.5 0.0204742447658 0.0677090287209 0.075221426785 0.0866304710507 -0.00029246028862 -0.000511626130901 0.00697235297412 1.0 7.30326240564e-14 7.303256307e-14 -4.91429161294e-15 0.669130623341 -0.7431448102 1.42883006316e-13 0.7431448102 0.669130623341 1.0 -4.69965219356e-14 -5.2443960226e-14 48.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -49 49.0 1 5 1 1 0.5 0.5 0.5 0.023524710328 0.0790234953165 0.0908864960074 0.102273575962 -0.000416969822254 -0.000719646865036 0.0069726947695 1.0 7.31109644396e-14 7.31109034533e-14 -7.50072916376e-15 0.656059026718 -0.754709541798 1.42894593727e-13 0.754709541798 0.656059026718 1.0 -4.62321492396e-14 -5.34057729138e-14 49.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -50 50.0 1 5 1 1 0.5 0.5 0.5 0.0268598238159 0.0902177020907 0.106357127428 0.1177008003 -0.000538720283657 -0.0009086750797 0.00697285402566 1.0 7.32207941197e-14 7.32207331333e-14 -9.98427042269e-15 0.642787635326 -0.766044437885 1.42887085627e-13 0.766044437885 0.642787635326 1.0 -4.54714797716e-14 -5.43082492497e-14 50.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -51 51.0 1 5 1 1 0.5 0.5 0.5 0.0303826202004 0.101213820279 0.121579430997 0.132867023349 -0.000660901481751 -0.00111284002196 0.00697690108791 1.0 7.33622553982e-14 7.33621876355e-14 -1.24756806177e-14 0.629320383072 -0.777145922184 1.43004138804e-13 0.777145922184 0.629320383072 1.0 -4.48062200948e-14 -5.52263923069e-14 51.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -52 52.0 1 5 1 1 0.5 0.5 0.5 0.0339949033569 0.11201544106 0.136535584927 0.147761180997 -0.000784714065958 -0.00131459208205 0.00697894394398 1.0 7.35355380104e-14 7.3535477024e-14 -1.50155309329e-14 0.615661501884 -0.788010716438 1.43037965912e-13 0.788010716438 0.615661501884 1.0 -4.40999640234e-14 -5.61864601069e-14 52.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -53 53.0 1 5 1 1 0.5 0.5 0.5 0.0376308280896 0.122612737119 0.15118843317 0.1623544842 -0.00091004971182 -0.00151794939302 0.00698458682746 1.0 7.37408859018e-14 7.37408249155e-14 -1.75981801289e-14 0.60181504488 -0.798635482788 1.43186406742e-13 0.798635482788 0.60181504488 1.0 -4.34776455289e-14 -5.71844546542e-14 53.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -54 54.0 1 5 1 1 0.5 0.5 0.5 0.0412246747871 0.132881388068 0.165393546224 0.176486164331 -0.00103311298881 -0.00171284656972 0.00699377711862 1.0 7.39785768994e-14 7.39785091367e-14 -2.00878776232e-14 0.587785243988 -0.809017002583 1.4344772657e-13 0.809017002583 0.587785243988 1.0 -4.29343382678e-14 -5.81362689651e-14 54.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -55 55.0 1 5 1 1 0.5 0.5 0.5 0.0447415113323 0.142851829529 0.179176211357 0.190176308155 -0.00116179022007 -0.00190604047384 0.00699571380392 1.0 7.42489294873e-14 7.42488617247e-14 -2.27610458421e-14 0.573576450348 -0.819152057171 1.43440909649e-13 0.819152057171 0.573576450348 1.0 -4.22339470525e-14 -5.92136542165e-14 55.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -56 56.0 1 5 1 1 0.5 0.5 0.5 0.0481736416751 0.152466788888 0.192468911409 0.203372821212 -0.00128960516304 -0.00210840022191 0.00700309127569 1.0 7.4552309584e-14 7.45522418214e-14 -2.53872833536e-14 0.559192895889 -0.829037547112 1.43614029631e-13 0.829037547112 0.559192895889 1.0 -4.1651784696e-14 -6.02744647271e-14 56.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -57 57.0 1 5 1 1 0.5 0.5 0.5 0.0514957040887 0.16164894402 0.205184012651 0.215988025069 -0.00141807645559 -0.00230375886895 0.00701119331643 1.0 7.48891373177e-14 7.48890627788e-14 -2.80182744139e-14 0.544639050961 -0.838670551777 1.43800837665e-13 0.838670551777 0.544639050961 1.0 -4.10839371963e-14 -6.13515111652e-14 57.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -58 58.0 1 5 1 1 0.5 0.5 0.5 0.0546568840541 0.170429125428 0.217311233282 0.228001371026 -0.00154578115325 -0.00251251878217 0.00702094798908 1.0 7.5259866698e-14 7.52597921591e-14 -3.06018773689e-14 0.529919266701 -0.848048090935 1.44034266393e-13 0.848048090935 0.529919266701 1.0 -4.05486903006e-14 -6.24149440898e-14 58.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -59 59.0 1 5 1 1 0.5 0.5 0.5 0.057665178562 0.178753256798 0.228810757399 0.239380404353 -0.00167655316181 -0.0027051072102 0.00703151477501 1.0 7.56650059448e-14 7.56649314059e-14 -3.3271399958e-14 0.515038073063 -0.857167303562 1.44284839068e-13 0.857167303562 0.515038073063 1.0 -4.00271416218e-14 -6.35444252555e-14 59.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -60 60.0 1 5 1 1 0.5 0.5 0.5 0.0604909854062 0.186547547579 0.239586278796 0.250017344952 -0.00180981971789 -0.00291019980796 0.00704179238528 1.0 7.61051039354e-14 7.61050361728e-14 -3.6004626201e-14 0.5 -0.866025388241 1.44513903882e-13 0.866025388241 0.5 1.0 -3.94958825573e-14 -6.47265850943e-14 60.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -61 61.0 1 5 1 1 0.5 0.5 0.5 0.0631217688617 0.193791866302 0.249607756734 0.259886533022 -0.00194548477884 -0.00310831447132 0.00705294730142 1.0 7.65807705336e-14 7.65806959947e-14 -3.87961283108e-14 0.48480963707 -0.874619722366 1.44762877358e-13 0.874619722366 0.48480963707 1.0 -3.89781861843e-14 -6.59583268537e-14 61.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -62 62.0 1 5 1 1 0.5 0.5 0.5 0.0655460742438 0.20045940578 0.258834958076 0.268953949213 -0.00208330713212 -0.00330717721954 0.00706291478127 1.0 7.70926562605e-14 7.70925749453e-14 -4.16352980467e-14 0.46947157383 -0.882947564125 1.44959158609e-13 0.882947564125 0.46947157383 1.0 -3.84318262164e-14 -6.72338636046e-14 62.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -63 63.0 1 5 1 1 0.5 0.5 0.5 0.0677412166974 0.206590846181 0.267280697823 0.277241885662 -0.00222166883759 -0.00350732635707 0.00707424012944 1.0 7.76414522952e-14 7.764137098e-14 -4.44629108651e-14 0.453990519047 -0.891006529331 1.45192844835e-13 0.891006529331 0.453990519047 1.0 -3.79073942375e-14 -6.85204659939e-14 63.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -64 64.0 1 5 1 1 0.5 0.5 0.5 0.0697129410434 0.212010905147 0.274793446064 0.284571915865 -0.00236255140044 -0.00370562658645 0.00708921952173 1.0 7.82279175791e-14 7.82278430402e-14 -4.73467022966e-14 0.438371151686 -0.898794054985 1.45545264751e-13 0.898794054985 0.438371151686 1.0 -3.74487496135e-14 -6.98572872725e-14 64.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -65 65.0 1 5 1 1 0.5 0.5 0.5 0.0714181561332 0.216807678342 0.281412482262 0.290993452072 -0.00250276038423 -0.00390887679532 0.00710331602022 1.0 7.88528652638e-14 7.88527839486e-14 -5.01717130298e-14 0.42261826992 -0.906307756901 1.45856701825e-13 0.906307756901 0.42261826992 1.0 -3.69653241935e-14 -7.11814369383e-14 65.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -66 66.0 1 5 1 1 0.5 0.5 0.5 0.0728654392243 0.220906943083 0.287071973085 0.296444416046 -0.00265323766507 -0.00410525733605 0.00711599038914 1.0 7.95171356061e-14 7.95170542909e-14 -5.33217404692e-14 0.406736671925 -0.913545429707 1.46108494227e-13 0.913545429707 0.406736671925 1.0 -3.64467062487e-14 -7.27051272377e-14 66.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -67 67.0 1 5 1 1 0.5 0.5 0.5 0.0740421446261 0.224261745811 0.291689932346 0.300876945257 -0.00280131306499 -0.00430715922266 0.00713248644024 1.0 8.0221656954e-14 8.02215688626e-14 -5.63473692618e-14 0.390731155872 -0.920504868031 1.46486108291e-13 0.920504868031 0.390731155872 1.0 -3.59935876677e-14 -7.41815937562e-14 67.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -68 68.0 1 5 1 1 0.5 0.5 0.5 0.0749262839347 0.22694632411 0.295370638371 0.30431458354 -0.00294701498933 -0.00449755322188 0.00714947842062 1.0 8.09673915371e-14 8.09673034456e-14 -5.92471696161e-14 0.374606609344 -0.927183866501 1.46872639918e-13 0.927183866501 0.374606609344 1.0 -3.55405978356e-14 -7.56131336471e-14 68.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -69 69.0 1 5 1 1 0.5 0.5 0.5 0.0755447188712 0.228792086244 0.297914654016 0.306645393372 -0.00310259847902 -0.00470214849338 0.00716820033267 1.0 8.17553557947e-14 8.17552677033e-14 -6.24511022323e-14 0.358367979527 -0.93358039856 1.47312107716e-13 0.93358039856 0.358367979527 1.0 -3.51104440118e-14 -7.7233013008e-14 69.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -70 70.0 1 5 1 1 0.5 0.5 0.5 0.0758345472717 0.229867011309 0.299378037453 0.307846367359 -0.00326239760034 -0.00490199634805 0.00718151498586 1.0 8.25866474817e-14 8.2586552614e-14 -6.57566788021e-14 0.342020153999 -0.939692616463 1.47553288489e-13 0.939692616463 0.342020153999 1.0 -3.45680752631e-14 -7.89318358395e-14 70.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -71 71.0 1 5 1 1 0.5 0.5 0.5 0.0758360864293 0.230157241225 0.299764603376 0.307952821255 -0.00342002091929 -0.00510233594105 0.00720483297482 1.0 8.34623914577e-14 8.34623033663e-14 -6.89357628598e-14 0.325568169355 -0.945518553257 1.48140492386e-13 0.945518553257 0.325568169355 1.0 -3.42024992312e-14 -8.05897232628e-14 71.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -72 72.0 1 5 1 1 0.5 0.5 0.5 0.075525141316 0.229602411389 0.298972666264 0.306899368763 -0.00358790112659 -0.0052975253202 0.00721790129319 1.0 8.43837938977e-14 8.438369903e-14 -7.24243798614e-14 0.309017002583 -0.951056480408 1.48359772276e-13 0.951056480408 0.309017002583 1.0 -3.36342078863e-14 -8.24389384882e-14 72.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -73 73.0 1 5 1 1 0.5 0.5 0.5 0.0749000000883 0.228239625692 0.29704400897 0.304708212614 -0.00375670660287 -0.00549271516502 0.00723527790979 1.0 8.5352121963e-14 8.53520203191e-14 -7.58904522341e-14 0.292371720076 -0.956304728985 1.48725527878e-13 0.956304728985 0.292371720076 1.0 -3.3134577032e-14 -8.43050130574e-14 73.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -74 74.0 1 5 1 1 0.5 0.5 0.5 0.0739484623657 0.225991547108 0.293922573328 0.301260143518 -0.00392613513395 -0.00569652067497 0.00725413672626 1.0 8.63686766962e-14 8.63685818285e-14 -7.93205019896e-14 0.275637388229 -0.961261689663 1.49138825747e-13 0.961261689663 0.275637388229 1.0 -3.26499251084e-14 -8.6183183257e-14 74.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -75 75.0 1 5 1 1 0.5 0.5 0.5 0.072654064954 0.22285503149 0.289548963308 0.296567112207 -0.00410118186846 -0.00588755588979 0.00727464165539 1.0 8.74348607838e-14 8.74347591399e-14 -8.2887859174e-14 0.258819073439 -0.965925812721 1.49606293066e-13 0.965925812721 0.258819073439 1.0 -3.21823527571e-14 -8.81655249567e-14 75.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -76 76.0 1 5 1 1 0.5 0.5 0.5 0.0710590306271 0.218880146742 0.284022808075 0.290706813335 -0.00428106123582 -0.00608264096081 0.00729729793966 1.0 8.85520972413e-14 8.85519955973e-14 -8.65618002082e-14 0.241921916604 -0.970295727253 1.50146700086e-13 0.970295727253 0.241921916604 1.0 -3.17401744536e-14 -9.02373878744e-14 76.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -77 77.0 1 5 1 1 0.5 0.5 0.5 0.0691430442849 0.214006558061 0.277257710695 0.283598601818 -0.00446529500186 -0.00628326507285 0.00731486734003 1.0 8.97219039515e-14 8.97218023076e-14 -9.03223757722e-14 0.224951073527 -0.974370062351 1.5050570653e-13 0.974370062351 0.224951073527 1.0 -3.11913851952e-14 -9.23900916167e-14 77.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -78 78.0 1 5 1 1 0.5 0.5 0.5 0.0668996797354 0.208257958293 0.269263744354 0.275259256363 -0.00465713022277 -0.00647377641872 0.00733389845118 1.0 9.09458462315e-14 9.09457378113e-14 -9.42809808631e-14 0.207911714911 -0.978147566319 1.50915440084e-13 0.978147566319 0.207911714911 1.0 -3.06547254486e-14 -9.46824541513e-14 78.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -79 79.0 1 5 1 1 0.5 0.5 0.5 0.0643428410009 0.201603919268 0.260032474995 0.265651851892 -0.00485096639022 -0.00666582910344 0.00735527323559 1.0 9.22255503845e-14 9.2225435188e-14 -9.82367805808e-14 0.190809011459 -0.981627166271 1.51408264181e-13 0.981627166271 0.190809011459 1.0 -3.0145265624e-14 -9.70135769136e-14 79.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -80 80.0 1 5 1 1 0.5 0.5 0.5 0.0614645414137 0.194048047066 0.249545291066 0.254766076803 -0.00505235884339 -0.00686362525448 0.00737208453938 1.0 9.35626901476e-14 9.35625817274e-14 -1.02382444428e-13 0.17364820838 -0.984807729721 1.51741778322e-13 0.984807729721 0.17364820838 1.0 -2.95383529648e-14 -9.94839518924e-14 80.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -81 81.0 1 5 1 1 0.5 0.5 0.5 0.0582718432416 0.185640856624 0.237853914499 0.242651328444 -0.00525587564334 -0.00704948464409 0.00739240786061 1.0 9.49590273492e-14 9.49589053765e-14 -1.06522917657e-13 0.156434491277 -0.987688362598 1.52202360958e-13 0.987688362598 0.156434491277 1.0 -2.89785014561e-14 -1.01996719176e-13 81.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -82 82.0 1 5 1 1 0.5 0.5 0.5 0.0548411210712 0.176334485412 0.224956199527 0.229332342744 -0.00546766957268 -0.00723874382675 0.00740870647132 1.0 9.64163441469e-14 9.64162289504e-14 -1.10871842306e-13 0.13917312026 -0.990268051624 1.52525670045e-13 0.990268051624 0.13917312026 1.0 -2.833043316e-14 -1.04662663898e-13 82.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -83 83.0 1 5 1 1 0.5 0.5 0.5 0.0511313149972 0.166156679392 0.210826560855 0.214756399393 -0.00568466819823 -0.00742362486199 0.00743296789005 1.0 9.79365040129e-14 9.79363888164e-14 -1.15317233844e-13 0.121869370341 -0.992546141148 1.53136387577e-13 0.992546141148 0.121869370341 1.0 -2.78072717305e-14 -1.07427616607e-13 83.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -84 84.0 1 5 1 1 0.5 0.5 0.5 0.0472034591648 0.155100286007 0.195493191481 0.198953047395 -0.00591072347015 -0.00760667771101 0.00744895776734 1.0 9.95214110774e-14 9.95212891047e-14 -1.19991419145e-13 0.104528486729 -0.994521915913 1.53463518477e-13 0.994521915913 0.104528486729 1.0 -2.7119679183e-14 -1.10360986571e-13 84.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -85 85.0 1 5 1 1 0.5 0.5 0.5 0.0431223359335 0.143299892545 0.179094478488 0.182078793645 -0.0061384961009 -0.00779581116512 0.00746519910172 1.0 1.01173003352e-13 1.01172881379e-13 -1.24631763785e-13 0.0871557667851 -0.996194720268 1.53809826204e-13 0.996194720268 0.0871557667851 1.0 -2.64190067528e-14 -1.13333658864e-13 85.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -86 86.0 1 5 1 1 0.5 0.5 0.5 0.0389558933784 0.130603894591 0.161492869258 0.163967981935 -0.00637407042086 -0.00797392893583 0.00748812500387 1.0 1.02893273058e-13 1.02893144309e-13 -1.29449253508e-13 0.069756500423 -0.997564077377 1.54404497543e-13 0.997564077377 0.069756500423 1.0 -2.58185688134e-14 -1.16454941393e-13 86.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -87 87.0 1 5 1 1 0.5 0.5 0.5 0.0348200893229 0.117140188813 0.142814710736 0.144770666957 -0.00661494443193 -0.00814818497747 0.0075002736412 1.0 1.04684239521e-13 1.04684103996e-13 -1.34351825997e-13 0.0523359812796 -0.998629510403 1.5463373854e-13 0.998629510403 0.0523359812796 1.0 -2.50090916129e-14 -1.19682055607e-13 87.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -88 88.0 1 5 1 1 0.5 0.5 0.5 0.0309165569603 0.102922193706 0.123093500733 0.124490439892 -0.00686429999769 -0.0083291567862 0.00751314265653 1.0 1.06547935622e-13 1.06547800097e-13 -1.39447996336e-13 0.0348995216191 -0.99939084053 1.54905873285e-13 0.99939084053 0.0348995216191 1.0 -2.41937732733e-14 -1.23072938559e-13 88.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -89 89.0 1 5 1 1 0.5 0.5 0.5 0.0275482477894 0.0879731550813 0.102374792099 0.103184834123 -0.00712391687557 -0.00850248057395 0.00752882473171 1.0 1.08486421344e-13 1.08486279042e-13 -1.44796799334e-13 0.0174524337053 -0.999847710133 1.55297460004e-13 0.999847710133 0.0174524337053 1.0 -2.34091547958e-14 -1.26660902361e-13 89.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 -90 90.0 1 5 1 1 0.5 0.5 0.5 0.0251206236511 0.0723270624876 0.0806358680129 0.0808931067586 -0.00738708348945 -0.00866825506091 0.00754283368587 1.0 1.10501756671e-13 1.10501614369e-13 -1.50157664081e-13 2.67917759089e-08 -1.0 1.55653268052e-13 1.0 2.67920814423e-08 1.0 -2.25758302298e-14 -1.30329706988e-13 90.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 31000000.0 diff --git a/examples/MSC.Marc/reference_postProc/rotation_90deg_inc90.txt b/examples/MSC.Marc/reference_postProc/rotation_90deg_inc90.txt deleted file mode 100644 index ebee6aed8..000000000 --- a/examples/MSC.Marc/reference_postProc/rotation_90deg_inc90.txt +++ /dev/null @@ -1,3 +0,0 @@ -1 header -inc elem node ip grain ip.x ip.y ip.z 1_1_p 1_2_p 1_3_p 1_4_p 1_5_p 1_6_p 1_7_p 1_8_p 1_9_p 1_1_div(p) 1_2_div(p) 1_3_div(p) 1_norm(div(p)) -90 1 5 1 1 0.5 0.5 0.5 0.0723270624876 0.00754283322021 0.00738708395511 -0.00738708348945 -0.00866825319827 -0.0806358680129 0.00754283368587 0.0808931067586 0.00866825692356 0.000000 0.000000 0.000000 0.000000 diff --git a/examples/SpectralMethod/EshelbyInclusion/runAll.sh b/examples/SpectralMethod/EshelbyInclusion/runAll.sh deleted file mode 100755 index cef1128ef..000000000 --- a/examples/SpectralMethod/EshelbyInclusion/runAll.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env bash - -for geom in $(ls geom/*.geom) -do - base=${geom%.geom} - base=${base#geom/} - name=${base}_thermal - vtr=${base}.vtr - - [[ -f ${name}.spectralOut ]] || \ - DAMASK_spectral \ - --workingdir ./ \ - --load thermal.load \ - --geom $geom \ - > ${name}.out - - if [ ! -f postProc/${name}_inc10.txt ] - then - postResults ${name}.spectralOut \ - --ho temperature \ - --cr f,fe,fi,fp,p \ - --split \ - --separation x,y,z \ - - addCauchy postProc/${name}_inc*.txt \ - - addDeviator postProc/${name}_inc*.txt \ - --spherical \ - --tensor p,Cauchy \ - - addDisplacement postProc/${name}_inc*.txt \ - --nodal \ - - fi - - geom_check ${geom} - - for inc in {00..10} - do - echo "generating postProc/${name}_inc${inc}.vtr" - cp geom/${vtr} postProc/${name}_inc${inc}.vtr - vtk_addRectilinearGridData \ - postProc/${name}_inc${inc}.txt \ - --vtk postProc/${name}_inc${inc}.vtr \ - --data 'sph(p)','sph(Cauchy)',temperature \ - --tensor f,fe,fi,fp,p,Cauchy \ - - vtk_addRectilinearGridData \ - postProc/${name}_inc${inc}_nodal.txt \ - --vtk postProc/${name}_inc${inc}.vtr \ - --data 'avg(f).pos','fluct(f).pos' \ - - done -done From ce08571cd8f01b3db600f87827fb476209a40fdf Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 2 Jan 2020 13:39:52 +0100 Subject: [PATCH 157/223] documentation was outdated --- src/quit.f90 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/quit.f90 b/src/quit.f90 index 146071600..bc4987c2d 100644 --- a/src/quit.f90 +++ b/src/quit.f90 @@ -2,8 +2,7 @@ !> @author Martin Diehl, Max-Planck-Institut für Eisenforschung GmbH !> @brief quit subroutine !> @details exits the program and reports current time and duration. Exit code 0 signals -!> everything is fine. Exit code 1 signals an error, message according to IO_error. Exit code -!> 2 signals no severe problems, but some increments did not converge +!> everything is fine. Exit code 1 signals an error, message according to IO_error. !-------------------------------------------------------------------------------------------------- subroutine quit(stop_id) #include From 27a6d1d682432ab35fef76ff4e18e13547c5f222 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 2 Jan 2020 15:14:12 +0100 Subject: [PATCH 158/223] private functions do not need a prefix --- src/IO.f90 | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/src/IO.f90 b/src/IO.f90 index 9a0c94661..6d8506506 100644 --- a/src/IO.f90 +++ b/src/IO.f90 @@ -13,6 +13,8 @@ module IO private character(len=*), parameter, public :: & IO_EOF = '#EOF#' !< end of file string + character, parameter, public :: & + IO_EOL = new_line(' ') !< end of line str character(len=*), parameter, private :: & IO_DIVIDER = '───────────────────'//& '───────────────────'//& @@ -47,10 +49,6 @@ module IO #endif #endif - private :: & - IO_verifyFloatValue, & - IO_verifyIntValue - contains @@ -102,7 +100,7 @@ function IO_read_ASCII(fileName) result(fileContent) ! count lines to allocate string array myTotalLines = 1 do l=1, len(rawData) - if (rawData(l:l) == new_line('')) myTotalLines = myTotalLines+1 + if (rawData(l:l) == IO_EOL) myTotalLines = myTotalLines+1 enddo allocate(fileContent(myTotalLines)) @@ -112,7 +110,7 @@ function IO_read_ASCII(fileName) result(fileContent) startPos = 1 l = 1 do while (l <= myTotalLines) - endPos = merge(startPos + scan(rawData(startPos:),new_line('')) - 2,len(rawData),l /= myTotalLines) + endPos = merge(startPos + scan(rawData(startPos:),IO_EOL) - 2,len(rawData),l /= myTotalLines) if (endPos - startPos > pStringLen-1) then line = rawData(startPos:startPos+pStringLen-1) if (.not. warned) then @@ -418,7 +416,7 @@ real(pReal) function IO_floatValue (string,chunkPos,myChunk) call IO_warning(201,el=myChunk,ext_msg=MYNAME//trim(string)) else valuePresent IO_floatValue = & - IO_verifyFloatValue(trim(adjustl(string(chunkPos(myChunk*2):chunkPos(myChunk*2+1)))),& + verifyFloatValue(trim(adjustl(string(chunkPos(myChunk*2):chunkPos(myChunk*2+1)))),& VALIDCHARACTERS,MYNAME) endif valuePresent @@ -441,7 +439,7 @@ integer function IO_intValue(string,chunkPos,myChunk) valuePresent: if (myChunk > chunkPos(1) .or. myChunk < 1) then call IO_warning(201,el=myChunk,ext_msg=MYNAME//trim(string)) else valuePresent - IO_intValue = IO_verifyIntValue(trim(adjustl(string(chunkPos(myChunk*2):chunkPos(myChunk*2+1)))),& + IO_intValue = verifyIntValue(trim(adjustl(string(chunkPos(myChunk*2):chunkPos(myChunk*2+1)))),& VALIDCHARACTERS,MYNAME) endif valuePresent @@ -467,12 +465,12 @@ real(pReal) function IO_fixedNoEFloatValue (string,ends,myChunk) pos_exp = scan(string(ends(myChunk)+1:ends(myChunk+1)),'+-',back=.true.) hasExponent: if (pos_exp > 1) then - base = IO_verifyFloatValue(trim(adjustl(string(ends(myChunk)+1:ends(myChunk)+pos_exp-1))),& + base = verifyFloatValue(trim(adjustl(string(ends(myChunk)+1:ends(myChunk)+pos_exp-1))),& VALIDBASE,MYNAME//'(base): ') - expon = IO_verifyIntValue(trim(adjustl(string(ends(myChunk)+pos_exp:ends(myChunk+1)))),& + expon = verifyIntValue(trim(adjustl(string(ends(myChunk)+pos_exp:ends(myChunk+1)))),& VALIDEXP,MYNAME//'(exp): ') else hasExponent - base = IO_verifyFloatValue(trim(adjustl(string(ends(myChunk)+1:ends(myChunk+1)))),& + base = verifyFloatValue(trim(adjustl(string(ends(myChunk)+1:ends(myChunk+1)))),& VALIDBASE,MYNAME//'(base): ') expon = 0 endif hasExponent @@ -492,7 +490,7 @@ integer function IO_fixedIntValue(string,ends,myChunk) character(len=*), parameter :: MYNAME = 'IO_fixedIntValue: ' character(len=*), parameter :: VALIDCHARACTERS = '0123456789+-' - IO_fixedIntValue = IO_verifyIntValue(trim(adjustl(string(ends(myChunk)+1:ends(myChunk+1)))),& + IO_fixedIntValue = verifyIntValue(trim(adjustl(string(ends(myChunk)+1:ends(myChunk+1)))),& VALIDCHARACTERS,MYNAME) end function IO_fixedIntValue @@ -1128,34 +1126,34 @@ function IO_continuousIntValues(fileUnit,maxN,lookupName,lookupMap,lookupMaxN) !-------------------------------------------------------------------------------------------------- !> @brief returns verified integer value in given string !-------------------------------------------------------------------------------------------------- -integer function IO_verifyIntValue (string,validChars,myName) +integer function verifyIntValue (string,validChars,myName) character(len=*), intent(in) :: string, & !< string for conversion to int value. Must not contain spaces! validChars, & !< valid characters in string myName !< name of caller function (for debugging) integer :: readStatus, invalidWhere - IO_verifyIntValue = 0 + verifyIntValue = 0 invalidWhere = verify(string,validChars) if (invalidWhere == 0) then - read(UNIT=string,iostat=readStatus,FMT=*) IO_verifyIntValue ! no offending chars found + read(UNIT=string,iostat=readStatus,FMT=*) verifyIntValue ! no offending chars found if (readStatus /= 0) & ! error during string to integer conversion call IO_warning(203,ext_msg=myName//'"'//string//'"') else call IO_warning(202,ext_msg=myName//'"'//string//'"') ! complain about offending characters - read(UNIT=string(1:invalidWhere-1),iostat=readStatus,FMT=*) IO_verifyIntValue ! interpret remaining string + read(UNIT=string(1:invalidWhere-1),iostat=readStatus,FMT=*) verifyIntValue ! interpret remaining string if (readStatus /= 0) & ! error during string to integer conversion call IO_warning(203,ext_msg=myName//'"'//string(1:invalidWhere-1)//'"') endif -end function IO_verifyIntValue +end function verifyIntValue !-------------------------------------------------------------------------------------------------- !> @brief returns verified float value in given string !-------------------------------------------------------------------------------------------------- -real(pReal) function IO_verifyFloatValue (string,validChars,myName) +real(pReal) function verifyFloatValue (string,validChars,myName) character(len=*), intent(in) :: string, & !< string for conversion to int value. Must not contain spaces! validChars, & !< valid characters in string @@ -1163,20 +1161,20 @@ real(pReal) function IO_verifyFloatValue (string,validChars,myName) integer :: readStatus, invalidWhere - IO_verifyFloatValue = 0.0_pReal + verifyFloatValue = 0.0_pReal invalidWhere = verify(string,validChars) if (invalidWhere == 0) then - read(UNIT=string,iostat=readStatus,FMT=*) IO_verifyFloatValue ! no offending chars found + read(UNIT=string,iostat=readStatus,FMT=*) verifyFloatValue ! no offending chars found if (readStatus /= 0) & ! error during string to float conversion call IO_warning(203,ext_msg=myName//'"'//string//'"') else call IO_warning(202,ext_msg=myName//'"'//string//'"') ! complain about offending characters - read(UNIT=string(1:invalidWhere-1),iostat=readStatus,FMT=*) IO_verifyFloatValue ! interpret remaining string + read(UNIT=string(1:invalidWhere-1),iostat=readStatus,FMT=*) verifyFloatValue ! interpret remaining string if (readStatus /= 0) & ! error during string to float conversion call IO_warning(203,ext_msg=myName//'"'//string(1:invalidWhere-1)//'"') endif -end function IO_verifyFloatValue +end function verifyFloatValue end module IO From 97e7f510e2160d366c622738bdc1c6203a8e1750 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 2 Jan 2020 16:27:17 +0100 Subject: [PATCH 159/223] wrong time was reported in case of trailing inactive increments --- python/damask/dadf5.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/damask/dadf5.py b/python/damask/dadf5.py index edba4515c..f6ea959bd 100644 --- a/python/damask/dadf5.py +++ b/python/damask/dadf5.py @@ -102,7 +102,7 @@ class DADF5(): 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]) @@ -339,8 +339,8 @@ class DADF5(): """Return information on all active datasets in the file.""" message = '' with h5py.File(self.fname,'r') as f: - for s,i in enumerate(self.iter_visible('increments')): - message+='\n{} ({}s)\n'.format(i,self.times[s]) + 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) From a3664bbde183f82988ad675cdfbcceed2a9b2381 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 2 Jan 2020 17:46:14 +0100 Subject: [PATCH 160/223] need to reverse F->C flip --- processing/pre/geom_toTable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/processing/pre/geom_toTable.py b/processing/pre/geom_toTable.py index 53196e808..494bbaffa 100755 --- a/processing/pre/geom_toTable.py +++ b/processing/pre/geom_toTable.py @@ -40,7 +40,7 @@ for name in filenames: "homogenization\t{}".format(geom.homogenization)] table = damask.Table(coord0,{'pos':(3,)},comments) - table.add('microstructure',geom.microstructure.reshape((-1,1))) + table.add('microstructure',geom.microstructure.reshape((-1,1),order='F')) table.to_ASCII(sys.stdout if name is None else \ os.path.splitext(name)[0]+'.txt') From 49f3de44b9cf6e3448a5047dd3b9187312aa78dc Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 2 Jan 2020 18:02:13 +0100 Subject: [PATCH 161/223] limit import statements (numpy already in use) --- python/damask/colormaps.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/python/damask/colormaps.py b/python/damask/colormaps.py index b38d47070..ad924325f 100644 --- a/python/damask/colormaps.py +++ b/python/damask/colormaps.py @@ -1,5 +1,3 @@ -import math - import numpy as np class Color(): @@ -328,11 +326,11 @@ class Color(): if self.model != 'CIELAB': return Msh = np.zeros(3,'d') - Msh[0] = math.sqrt(np.dot(self.color,self.color)) + Msh[0] = np.sqrt(np.dot(self.color,self.color)) if (Msh[0] > 0.001): - Msh[1] = math.acos(self.color[0]/Msh[0]) + Msh[1] = np.acos(self.color[0]/Msh[0]) if (self.color[1] != 0.0): - Msh[2] = math.atan2(self.color[2],self.color[1]) + Msh[2] = np.atan2(self.color[2],self.color[1]) converted = Color('MSH', Msh) self.model = converted.model @@ -349,9 +347,9 @@ class Color(): if self.model != 'MSH': return Lab = np.zeros(3,'d') - Lab[0] = self.color[0] * math.cos(self.color[1]) - Lab[1] = self.color[0] * math.sin(self.color[1]) * math.cos(self.color[2]) - Lab[2] = self.color[0] * math.sin(self.color[1]) * math.sin(self.color[2]) + Lab[0] = self.color[0] * np.cos(self.color[1]) + Lab[1] = self.color[0] * np.sin(self.color[1]) * np.cos(self.color[2]) + Lab[2] = self.color[0] * np.sin(self.color[1]) * np.sin(self.color[2]) converted = Color('CIELAB', Lab) self.model = converted.model @@ -476,14 +474,14 @@ class Colormap(): if Msh_sat[0] >= Msh_unsat[0]: return Msh_sat[2] else: - hSpin = Msh_sat[1]/math.sin(Msh_sat[1])*math.sqrt(Msh_unsat[0]**2.0-Msh_sat[0]**2)/Msh_sat[0] - if Msh_sat[2] < - math.pi/3.0: hSpin *= -1.0 + hSpin = Msh_sat[1]/np.sin(Msh_sat[1])*np.sqrt(Msh_unsat[0]**2.0-Msh_sat[0]**2)/Msh_sat[0] + if Msh_sat[2] < - np.pi/3.0: hSpin *= -1.0 return Msh_sat[2] + hSpin Msh1 = np.array(lo[:]) Msh2 = np.array(hi[:]) - if (Msh1[1] > 0.05 and Msh2[1] > 0.05 and rad_diff(Msh1,Msh2) > math.pi/3.0): + if (Msh1[1] > 0.05 and Msh2[1] > 0.05 and rad_diff(Msh1,Msh2) > np.pi/3.0): M_mid = max(Msh1[0],Msh2[0],88.0) if frac < 0.5: Msh2 = np.array([M_mid,0.0,0.0],'d') From e0110c676ec4ab36ec699a7205ecf83c23039000 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 2 Jan 2020 18:44:51 +0100 Subject: [PATCH 162/223] avoid long lines --- src/prec.f90 | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/prec.f90 b/src/prec.f90 index 2f0f20a00..4c4449551 100644 --- a/src/prec.f90 +++ b/src/prec.f90 @@ -79,9 +79,12 @@ module prec real(pReal), private, parameter :: PREAL_EPSILON = epsilon(0.0_pReal) !< minimum positive number such that 1.0 + EPSILON /= 1.0. real(pReal), private, parameter :: PREAL_MIN = tiny(0.0_pReal) !< smallest normalized floating point number - integer, dimension(0), parameter, public :: emptyIntArray = [integer::] - real(pReal), dimension(0), parameter, public :: emptyRealArray = [real(pReal)::] - character(len=pStringLen), dimension(0), parameter, public :: emptyStringArray = [character(len=pStringLen)::] + integer, dimension(0), parameter, public :: & + emptyIntArray = [integer::] + real(pReal), dimension(0), parameter, public :: & + emptyRealArray = [real(pReal)::] + character(len=pStringLen), dimension(0), parameter, public :: & + emptyStringArray = [character(len=pStringLen)::] private :: & unitTest From 6a0593bf9e27e709e3efd3f55aab63a3c4dab2e6 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 2 Jan 2020 19:47:48 +0100 Subject: [PATCH 163/223] everything is anyway public --- src/geometry_plastic_nonlocal.f90 | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/src/geometry_plastic_nonlocal.f90 b/src/geometry_plastic_nonlocal.f90 index b69ab2eff..7890af5aa 100644 --- a/src/geometry_plastic_nonlocal.f90 +++ b/src/geometry_plastic_nonlocal.f90 @@ -10,32 +10,24 @@ module geometry_plastic_nonlocal use results implicit none - private + public - integer, public, protected :: & + integer, protected :: & geometry_plastic_nonlocal_nIPneighbors - integer, dimension(:,:,:,:), allocatable, public, protected :: & + integer, dimension(:,:,:,:), allocatable, protected :: & geometry_plastic_nonlocal_IPneighborhood !< 6 or less neighboring IPs as [element ID, IP ID, face ID that point to me] - real(pReal), dimension(:,:), allocatable, public, protected :: & + real(pReal), dimension(:,:), allocatable, protected :: & geometry_plastic_nonlocal_IPvolume0 !< volume associated with IP (initially!) - real(pReal), dimension(:,:,:), allocatable, public, protected :: & + real(pReal), dimension(:,:,:), allocatable, protected :: & geometry_plastic_nonlocal_IParea0 !< area of interface to neighboring IP (initially!) - real(pReal), dimension(:,:,:,:), allocatable, public, protected :: & + real(pReal), dimension(:,:,:,:), allocatable, protected :: & geometry_plastic_nonlocal_IPareaNormal0 !< area normal of interface to neighboring IP (initially!) - public :: & - geometry_plastic_nonlocal_setIPneighborhood, & - geometry_plastic_nonlocal_setIPvolume, & - geometry_plastic_nonlocal_setIParea, & - geometry_plastic_nonlocal_setIPareaNormal, & - geometry_plastic_nonlocal_results, & - geometry_plastic_nonlocal_disable - contains !--------------------------------------------------------------------------------------------------- @@ -96,7 +88,7 @@ end subroutine geometry_plastic_nonlocal_setIPareaNormal !--------------------------------------------------------------------------------------------------- -!> @brief Frees memory used by variables only needed by plastic_nonlocal +!> @brief Free memory used by variables only needed by plastic_nonlocal !--------------------------------------------------------------------------------------------------- subroutine geometry_plastic_nonlocal_disable @@ -116,7 +108,7 @@ end subroutine geometry_plastic_nonlocal_disable !--------------------------------------------------------------------------------------------------- -!> @brief Writes geometry data to results file +!> @brief Write geometry data to results file !--------------------------------------------------------------------------------------------------- subroutine geometry_plastic_nonlocal_results From 1ad846482106044ec91095e96cb8471cf6c73303 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 2 Jan 2020 20:38:40 +0100 Subject: [PATCH 164/223] not needed object-oriented mesh did not work out nicely --- src/commercialFEM_fileList.f90 | 3 +- src/mesh_FEM.f90 | 33 --------------- src/mesh_abaqus.f90 | 1 - src/mesh_base.f90 | 74 ---------------------------------- src/mesh_marc.f90 | 1 - 5 files changed, 1 insertion(+), 111 deletions(-) delete mode 100644 src/mesh_base.f90 diff --git a/src/commercialFEM_fileList.f90 b/src/commercialFEM_fileList.f90 index 79a385818..31bddd141 100644 --- a/src/commercialFEM_fileList.f90 +++ b/src/commercialFEM_fileList.f90 @@ -1,7 +1,7 @@ !-------------------------------------------------------------------------------------------------- !> @author Martin Diehl, Max-Planck-Institut für Eisenforschung GmbH !> @brief all DAMASK files without solver -!> @details List of files needed by MSC.Marc, Abaqus/Explicit, and Abaqus/Standard +!> @details List of files needed by MSC.Marc and Abaqus/Standard !-------------------------------------------------------------------------------------------------- #include "IO.f90" #include "numerics.f90" @@ -15,7 +15,6 @@ #include "rotations.f90" #include "FEsolving.f90" #include "element.f90" -#include "mesh_base.f90" #include "HDF5_utilities.f90" #include "results.f90" #include "geometry_plastic_nonlocal.f90" diff --git a/src/mesh_FEM.f90 b/src/mesh_FEM.f90 index 4b4ff18aa..28d09a9f5 100644 --- a/src/mesh_FEM.f90 +++ b/src/mesh_FEM.f90 @@ -20,7 +20,6 @@ module mesh use FEsolving use FEM_Zoo use prec - use mesh_base implicit none private @@ -53,18 +52,6 @@ module mesh PetscInt, dimension(:), allocatable, public, protected :: & mesh_boundaries - - type, public, extends(tMesh) :: tMesh_FEM - - - contains - procedure, pass(self) :: tMesh_FEM_init - generic, public :: init => tMesh_FEM_init - end type tMesh_FEM - - type(tMesh_FEM), public, protected :: theMesh - - public :: & mesh_init, & mesh_FEM_build_ipVolumes, & @@ -72,24 +59,6 @@ module mesh contains -subroutine tMesh_FEM_init(self,dimen,order,nodes) - - integer, intent(in) :: dimen - integer, intent(in) :: order - real(pReal), intent(in), dimension(:,:) :: nodes - class(tMesh_FEM) :: self - - if (dimen == 2) then - if (order == 1) call self%tMesh%init('mesh',1,nodes) - if (order == 2) call self%tMesh%init('mesh',2,nodes) - elseif(dimen == 3) then - if (order == 1) call self%tMesh%init('mesh',6,nodes) - if (order == 2) call self%tMesh%init('mesh',8,nodes) - endif - - end subroutine tMesh_FEM_init - - !-------------------------------------------------------------------------------------------------- !> @brief initializes the mesh by calling all necessary private routines the mesh module @@ -217,8 +186,6 @@ subroutine mesh_init forall (j = 1:mesh_NcpElems) FEsolving_execIP(2,j) = FE_Nips(FE_geomtype(mesh_element(2,j))) ! ...up to own IP count for each element allocate(mesh_node0(3,mesh_Nnodes),source=0.0_pReal) - call theMesh%init(dimplex,integrationOrder,mesh_node0) - call theMesh%setNelems(mesh_NcpElems) call discretization_init(mesh_element(3,:),mesh_element(4,:),& reshape(mesh_ipCoordinates,[3,mesh_maxNips*mesh_NcpElems]), & diff --git a/src/mesh_abaqus.f90 b/src/mesh_abaqus.f90 index 15332b3fb..f4f5113e8 100644 --- a/src/mesh_abaqus.f90 +++ b/src/mesh_abaqus.f90 @@ -7,7 +7,6 @@ !-------------------------------------------------------------------------------------------------- module mesh use prec - use mesh_base use geometry_plastic_nonlocal use discretization use math diff --git a/src/mesh_base.f90 b/src/mesh_base.f90 deleted file mode 100644 index dab7059ee..000000000 --- a/src/mesh_base.f90 +++ /dev/null @@ -1,74 +0,0 @@ -!-------------------------------------------------------------------------------------------------- -!> @author Franz Roters, Max-Planck-Institut für Eisenforschung GmbH -!> @author Philip Eisenlohr, Max-Planck-Institut für Eisenforschung GmbH -!> @author Christoph Koords, Max-Planck-Institut für Eisenforschung GmbH -!> @author Martin Diehl, Max-Planck-Institut für Eisenforschung GmbH -!> @brief Sets up the mesh for the solvers MSC.Marc,FEM, Abaqus and the spectral solver -!-------------------------------------------------------------------------------------------------- -module mesh_base - - use prec - use element - - implicit none - -!--------------------------------------------------------------------------------------------------- -!> Properties of a whole mesh (consisting of one type of elements) -!--------------------------------------------------------------------------------------------------- - type, public :: tMesh - type(tElement) :: & - elem - real(pReal), dimension(:,:), allocatable, public :: & - ipVolume, & !< volume associated with each IP (initially!) - node_0, & !< node x,y,z coordinates (initially) - node !< node x,y,z coordinates (deformed) - integer(pInt), dimension(:,:), allocatable, public :: & - cellnodeParent !< cellnode's parent element ID, cellnode's intra-element ID - character(pStringLen) :: type = "n/a" - integer(pInt) :: & - Nnodes, & !< total number of nodes in mesh - Nelems = -1_pInt, & - elemType, & - Ncells, & - nIPneighbors, & - NcellNodes - integer(pInt), dimension(:,:), allocatable, public :: & - connectivity - contains - procedure, pass(self) :: tMesh_base_init - procedure :: setNelems => tMesh_base_setNelems ! not needed once we compute the cells from the connectivity - generic, public :: init => tMesh_base_init - end type tMesh - -contains - -subroutine tMesh_base_init(self,meshType,elemType,nodes) - - class(tMesh) :: self - character(len=*), intent(in) :: meshType - integer(pInt), intent(in) :: elemType - real(pReal), dimension(:,:), intent(in) :: nodes - - write(6,'(/,a)') ' <<<+- mesh_base_init -+>>>' - - write(6,*)' mesh type ',meshType - write(6,*)' # node ',size(nodes,2) - - self%type = meshType - call self%elem%init(elemType) - self%node_0 = nodes - self%nNodes = size(nodes,2) - -end subroutine tMesh_base_init - - -subroutine tMesh_base_setNelems(self,Nelems) - - class(tMesh) :: self - integer(pInt), intent(in) :: Nelems - - self%Nelems = Nelems - -end subroutine tMesh_base_setNelems - -end module mesh_base diff --git a/src/mesh_marc.f90 b/src/mesh_marc.f90 index cebe844e7..8de8ee96c 100644 --- a/src/mesh_marc.f90 +++ b/src/mesh_marc.f90 @@ -9,7 +9,6 @@ module mesh use IO use prec use math - use mesh_base use DAMASK_interface use IO use debug From f633c67fe4647d12c26edb0241aec6ddd6692def Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 2 Jan 2020 20:49:02 +0100 Subject: [PATCH 165/223] polishing --- src/math.f90 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/math.f90 b/src/math.f90 index 0b06c9186..d6d87d30a 100644 --- a/src/math.f90 +++ b/src/math.f90 @@ -411,9 +411,9 @@ pure function math_exp33(A,n) endif do i = 1, order - invFac = invFac/real(i,pReal) ! invfac = 1/i! + invFac = invFac/real(i,pReal) ! invfac = 1/(i!) B = matmul(B,A) - math_exp33 = math_exp33 + invFac*B ! exp = SUM (A^i)/i! + math_exp33 = math_exp33 + invFac*B ! exp = SUM (A^i)/(i!) enddo end function math_exp33 @@ -489,8 +489,8 @@ function math_invSym3333(A) real(pReal), dimension(6*(64+2)) :: work logical :: error external :: & - dgetrf, & - dgetri + dgetrf, & + dgetri temp66 = math_sym3333to66(A) call dgetrf(6,6,temp66,6,ipiv6,ierr) @@ -519,8 +519,8 @@ subroutine math_invert(InvA, error, A) real(pReal), dimension(size(A,1)*(64+2)) :: work integer :: ierr external :: & - dgetrf, & - dgetri + dgetrf, & + dgetri invA = A call dgetrf(size(A,1),size(A,1),invA,size(A,1),ipiv,ierr) From c919237998866a09d8fdaf1584a7b6ffba6b4d85 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 2 Jan 2020 21:00:23 +0100 Subject: [PATCH 166/223] one loop is enough --- src/lattice.f90 | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/lattice.f90 b/src/lattice.f90 index 4aab12fc9..050c7ed87 100644 --- a/src/lattice.f90 +++ b/src/lattice.f90 @@ -491,13 +491,12 @@ contains !-------------------------------------------------------------------------------------------------- subroutine lattice_init - integer :: Nphases + integer :: Nphases, p character(len=pStringLen) :: & tag = '' - integer :: i,p + real(pReal) :: CoverA real(pReal), dimension(:), allocatable :: & - temp, & - CoverA !< c/a ratio for low symmetry type lattice + temp write(6,'(/,a)') ' <<<+- lattice init -+>>>' @@ -515,15 +514,13 @@ subroutine lattice_init 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_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) - allocate(CoverA(Nphases),source=0.0_pReal) - do p = 1, size(config_phase) tag = config_phase(p)%getString('lattice_structure') select case(trim(tag(1:3))) @@ -553,7 +550,7 @@ subroutine lattice_init lattice_C66(6,6,p) = config_phase(p)%getFloat('c66',defaultVal=0.0_pReal) - CoverA(p) = config_phase(p)%getFloat('c/a',defaultVal=0.0_pReal) + CoverA = config_phase(p)%getFloat('c/a',defaultVal=0.0_pReal) lattice_thermalConductivity33(1,1,p) = config_phase(p)%getFloat('thermal_conductivity11',defaultVal=0.0_pReal) lattice_thermalConductivity33(2,2,p) = config_phase(p)%getFloat('thermal_conductivity22',defaultVal=0.0_pReal) @@ -573,14 +570,12 @@ subroutine lattice_init lattice_DamageDiffusion33(2,2,p) = config_phase(p)%getFloat( 'damage_diffusion22',defaultVal=0.0_pReal) lattice_DamageDiffusion33(3,3,p) = config_phase(p)%getFloat( 'damage_diffusion33',defaultVal=0.0_pReal) lattice_DamageMobility(p) = config_phase(p)%getFloat( 'damage_mobility',defaultVal=0.0_pReal) - enddo - do i = 1,Nphases - if ((CoverA(i) < 1.0_pReal .or. CoverA(i) > 2.0_pReal) & - .and. lattice_structure(i) == LATTICE_hex_ID) call IO_error(131,el=i) ! checking physical significance of c/a - if ((CoverA(i) > 2.0_pReal) & - .and. lattice_structure(i) == LATTICE_bct_ID) call IO_error(131,el=i) ! checking physical significance of c/a - call lattice_initializeStructure(i, CoverA(i)) + if ((CoverA < 1.0_pReal .or. CoverA > 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 From e1168c09b9336a748df5e3b677b9cd4c5356c03e Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 2 Jan 2020 21:18:56 +0100 Subject: [PATCH 167/223] use specialized functions --- src/CPFEM2.f90 | 1 - src/constitutive.f90 | 5 ++- src/quit.f90 | 2 +- src/results.f90 | 75 ++++++++++++++++++++++++++++---------------- 4 files changed, 51 insertions(+), 32 deletions(-) diff --git a/src/CPFEM2.f90 b/src/CPFEM2.f90 index 52b96cf70..6b4479b89 100644 --- a/src/CPFEM2.f90 +++ b/src/CPFEM2.f90 @@ -17,7 +17,6 @@ module CPFEM2 use DAMASK_interface use results use discretization - use HDF5 use HDF5_utilities use homogenization use constitutive diff --git a/src/constitutive.f90 b/src/constitutive.f90 index 977c80337..697275367 100644 --- a/src/constitutive.f90 +++ b/src/constitutive.f90 @@ -11,7 +11,6 @@ module constitutive use config use material use results - use HDF5_utilities use lattice use discretization use plastic_none @@ -587,11 +586,11 @@ subroutine constitutive_results character(len=pStringLen) :: group do p=1,size(config_name_phase) group = trim('current/constituent')//'/'//trim(config_name_phase(p)) - call HDF5_closeGroup(results_addGroup(group)) + call results_closeGroup(results_addGroup(group)) group = trim(group)//'/plastic' - call HDF5_closeGroup(results_addGroup(group)) + call results_closeGroup(results_addGroup(group)) select case(phase_plasticity(p)) case(PLASTICITY_ISOTROPIC_ID) diff --git a/src/quit.f90 b/src/quit.f90 index bc4987c2d..5c421c86a 100644 --- a/src/quit.f90 +++ b/src/quit.f90 @@ -10,7 +10,7 @@ subroutine quit(stop_id) #ifdef _OPENMP use MPI #endif - use hdf5 + use HDF5 implicit none integer, intent(in) :: stop_id diff --git a/src/results.f90 b/src/results.f90 index d38e629ec..73c5d7dbb 100644 --- a/src/results.f90 +++ b/src/results.f90 @@ -68,14 +68,14 @@ subroutine results_init write(6,'(a)') ' https://doi.org/10.1007/s40192-017-0084-5' resultsFile = HDF5_openFile(trim(getSolverJobName())//'.hdf5','w',.true.) - call HDF5_addAttribute(resultsFile,'DADF5_version_major',0) - call HDF5_addAttribute(resultsFile,'DADF5_version_minor',5) - call HDF5_addAttribute(resultsFile,'DAMASK_version',DAMASKVERSION) + call results_addAttribute('DADF5_version_major',0) + call results_addAttribute('DADF5_version_minor',5) + call results_addAttribute('DAMASK_version',DAMASKVERSION) call get_command(commandLine) - call HDF5_addAttribute(resultsFile,'call',trim(commandLine)) - call HDF5_closeGroup(results_addGroup('mapping')) - call HDF5_closeGroup(results_addGroup('mapping/cellResults')) - call HDF5_closeFile(resultsFile) + call results_addAttribute('call',trim(commandLine)) + call results_closeGroup(results_addGroup('mapping')) + call results_closeGroup(results_addGroup('mapping/cellResults')) + call results_closeJobFile end subroutine results_init @@ -110,12 +110,11 @@ subroutine results_addIncrement(inc,time) character(len=pStringLen) :: incChar write(incChar,'(i10)') inc - call HDF5_closeGroup(results_addGroup(trim('inc'//trim(adjustl(incChar))))) + call results_closeGroup(results_addGroup(trim('inc'//trim(adjustl(incChar))))) call results_setLink(trim('inc'//trim(adjustl(incChar))),'current') - call HDF5_addAttribute(resultsFile,'time/s',time,trim('inc'//trim(adjustl(incChar)))) - - call HDF5_closeGroup(results_addGroup('current/constituent')) - call HDF5_closeGroup(results_addGroup('current/materialpoint')) + call results_addAttribute('time/s',time,trim('inc'//trim(adjustl(incChar)))) + call results_closeGroup(results_addGroup('current/constituent')) + call results_closeGroup(results_addGroup('current/materialpoint')) end subroutine results_addIncrement @@ -173,9 +172,14 @@ end subroutine results_setLink !-------------------------------------------------------------------------------------------------- subroutine results_addAttribute_str(attrLabel,attrValue,path) - character(len=*), intent(in) :: attrLabel, attrValue, path + character(len=*), intent(in) :: attrLabel, attrValue + character(len=*), intent(in), optional :: path - call HDF5_addAttribute(resultsFile,attrLabel, attrValue, path) + if (present(path)) then + call HDF5_addAttribute(resultsFile,attrLabel, attrValue, path) + else + call HDF5_addAttribute(resultsFile,attrLabel, attrValue) + endif end subroutine results_addAttribute_str @@ -185,10 +189,15 @@ end subroutine results_addAttribute_str !-------------------------------------------------------------------------------------------------- subroutine results_addAttribute_int(attrLabel,attrValue,path) - character(len=*), intent(in) :: attrLabel, path - integer, intent(in) :: attrValue + character(len=*), intent(in) :: attrLabel + integer, intent(in) :: attrValue + character(len=*), intent(in), optional :: path - call HDF5_addAttribute(resultsFile,attrLabel, attrValue, path) + if (present(path)) then + call HDF5_addAttribute(resultsFile,attrLabel, attrValue, path) + else + call HDF5_addAttribute(resultsFile,attrLabel, attrValue) + endif end subroutine results_addAttribute_int @@ -198,10 +207,15 @@ end subroutine results_addAttribute_int !-------------------------------------------------------------------------------------------------- subroutine results_addAttribute_real(attrLabel,attrValue,path) - character(len=*), intent(in) :: attrLabel, path - real(pReal), intent(in) :: attrValue + character(len=*), intent(in) :: attrLabel + real(pReal), intent(in) :: attrValue + character(len=*), intent(in), optional :: path - call HDF5_addAttribute(resultsFile,attrLabel, attrValue, path) + if (present(path)) then + call HDF5_addAttribute(resultsFile,attrLabel, attrValue, path) + else + call HDF5_addAttribute(resultsFile,attrLabel, attrValue) + endif end subroutine results_addAttribute_real @@ -211,10 +225,15 @@ end subroutine results_addAttribute_real !-------------------------------------------------------------------------------------------------- subroutine results_addAttribute_int_array(attrLabel,attrValue,path) - character(len=*), intent(in) :: attrLabel, path + character(len=*), intent(in) :: attrLabel integer, intent(in), dimension(:) :: attrValue + character(len=*), intent(in), optional :: path - call HDF5_addAttribute(resultsFile,attrLabel, attrValue, path) + if (present(path)) then + call HDF5_addAttribute(resultsFile,attrLabel, attrValue, path) + else + call HDF5_addAttribute(resultsFile,attrLabel, attrValue) + endif end subroutine results_addAttribute_int_array @@ -224,10 +243,15 @@ end subroutine results_addAttribute_int_array !-------------------------------------------------------------------------------------------------- subroutine results_addAttribute_real_array(attrLabel,attrValue,path) - character(len=*), intent(in) :: attrLabel, path + character(len=*), intent(in) :: attrLabel real(pReal), intent(in), dimension(:) :: attrValue + character(len=*), intent(in), optional :: path - call HDF5_addAttribute(resultsFile,attrLabel, attrValue, path) + if (present(path)) then + call HDF5_addAttribute(resultsFile,attrLabel, attrValue, path) + else + call HDF5_addAttribute(resultsFile,attrLabel, attrValue) + endif end subroutine results_addAttribute_real_array @@ -720,7 +744,6 @@ end subroutine results_mapping_materialpoint !!> @brief adds the backward mapping from spatial position and constituent ID to results !!-------------------------------------------------------------------------------------------------- !subroutine HDF5_backwardMappingPhase(material_phase,phasememberat,phase_name,dataspace_size,mpiOffset,mpiOffset_phase) -! use hdf5 ! integer(pInt), intent(in), dimension(:,:,:) :: material_phase, phasememberat ! character(len=*), intent(in), dimension(:) :: phase_name @@ -834,7 +857,6 @@ end subroutine results_mapping_materialpoint !!> @brief adds the backward mapping from spatial position and constituent ID to results !!-------------------------------------------------------------------------------------------------- !subroutine HDF5_backwardMappingHomog(material_homog,homogmemberat,homogenization_name,dataspace_size,mpiOffset,mpiOffset_homog) -! use hdf5 ! integer(pInt), intent(in), dimension(:,:) :: material_homog, homogmemberat ! character(len=*), intent(in), dimension(:) :: homogenization_name @@ -941,7 +963,6 @@ end subroutine results_mapping_materialpoint !!> @brief adds the unique cell to node mapping !!-------------------------------------------------------------------------------------------------- !subroutine HDF5_mappingCells(mapping) -! use hdf5 ! integer(pInt), intent(in), dimension(:) :: mapping From b2d5fc4dc552ad26b35d84801679b0b398d59e1c Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 2 Jan 2020 21:35:05 +0100 Subject: [PATCH 168/223] 2020! --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 1ab20178c..3ffc3b9e3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright 2011-19 Max-Planck-Institut für Eisenforschung GmbH +Copyright 2011-20 Max-Planck-Institut für Eisenforschung GmbH DAMASK is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by From 18fc41f33f34767eda1fa7b5f67d475ca7012f9a Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 2 Jan 2020 21:36:43 +0100 Subject: [PATCH 169/223] not needed anymore (only python3 and HDF5) --- installation/patch/disable_HDF5 | 57 --------- installation/patch/disable_old_output | 178 -------------------------- installation/patch/python2to3.sh | 8 -- 3 files changed, 243 deletions(-) delete mode 100644 installation/patch/disable_HDF5 delete mode 100644 installation/patch/disable_old_output delete mode 100755 installation/patch/python2to3.sh diff --git a/installation/patch/disable_HDF5 b/installation/patch/disable_HDF5 deleted file mode 100644 index bbba30c4a..000000000 --- a/installation/patch/disable_HDF5 +++ /dev/null @@ -1,57 +0,0 @@ -diff --git a/src/DAMASK_grid.f90 b/src/DAMASK_grid.f90 -index 496bfd0d..7b0f499c 100644 ---- a/src/DAMASK_grid.f90 -+++ b/src/DAMASK_grid.f90 -@@ -75,7 +75,6 @@ program DAMASK_spectral - use grid_mech_spectral_polarisation - use grid_damage_spectral - use grid_thermal_spectral -- use results - - implicit none - -@@ -153,8 +152,6 @@ program DAMASK_spectral - 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' - -- call results_openJobFile() -- call results_closeJobFile() - !-------------------------------------------------------------------------------------------------- - ! initialize field solver information - nActiveFields = 1 -@@ -595,7 +592,6 @@ program DAMASK_spectral - if(ierr /=0_pInt) call IO_error(894_pInt, ext_msg='MPI_file_write') - enddo - fileOffset = fileOffset + sum(outputSize) ! forward to current file position -- call CPFEM_results(totalIncsCounter,time) - endif - if ( loadCases(currentLoadCase)%restartFrequency > 0_pInt & ! writing of restart info requested ... - .and. mod(inc,loadCases(currentLoadCase)%restartFrequency) == 0_pInt) then ! ... and at frequency of writing restart information -diff --git a/src/HDF5_utilities.f90 b/src/HDF5_utilities.f90 -index a81aaee0..3d3cdee3 100644 ---- a/src/HDF5_utilities.f90 -+++ b/src/HDF5_utilities.f90 -@@ -197,7 +197,6 @@ integer(HID_T) function HDF5_addGroup(fileHandle,groupName) - !------------------------------------------------------------------------------------------------- - ! setting I/O mode to collective - #ifdef PETSc -- call h5pset_all_coll_metadata_ops_f(aplist_id, .true., hdferr) - if (hdferr < 0) call IO_error(1_pInt,ext_msg = 'HDF5_addGroup: h5pset_all_coll_metadata_ops_f ('//trim(groupName)//')') - #endif - -@@ -232,7 +231,6 @@ integer(HID_T) function HDF5_openGroup(fileHandle,groupName) - !------------------------------------------------------------------------------------------------- - ! setting I/O mode to collective - #ifdef PETSc -- call h5pget_all_coll_metadata_ops_f(aplist_id, is_collective, hdferr) - if (hdferr < 0) call IO_error(1_pInt,ext_msg = 'HDF5_openGroup: h5pset_all_coll_metadata_ops_f ('//trim(groupName)//')') - #endif - -@@ -1646,7 +1644,6 @@ subroutine initialize_read(dset_id, filespace_id, memspace_id, plist_id, aplist_ - call h5pcreate_f(H5P_DATASET_ACCESS_F, aplist_id, hdferr) - if (hdferr < 0) call IO_error(1_pInt,ext_msg='initialize_read: h5pcreate_f') - #ifdef PETSc -- call h5pset_all_coll_metadata_ops_f(aplist_id, .true., hdferr) - if (hdferr < 0) call IO_error(1_pInt,ext_msg='initialize_read: h5pset_all_coll_metadata_ops_f') - #endif - diff --git a/installation/patch/disable_old_output b/installation/patch/disable_old_output deleted file mode 100644 index 732dfc83e..000000000 --- a/installation/patch/disable_old_output +++ /dev/null @@ -1,178 +0,0 @@ -From 6dbd904a4cfc28add3c39bb2a4ec9e2dbb2442b6 Mon Sep 17 00:00:00 2001 -From: Martin Diehl -Date: Thu, 18 Apr 2019 18:25:32 +0200 -Subject: [PATCH] to create patch - ---- - src/DAMASK_grid.f90 | 81 +----------------------------------------- - src/homogenization.f90 | 2 ++ - 2 files changed, 3 insertions(+), 80 deletions(-) - -diff --git a/src/DAMASK_grid.f90 b/src/DAMASK_grid.f90 -index f2f52bb2..a7543f4d 100644 ---- a/src/DAMASK_grid.f90 -+++ b/src/DAMASK_grid.f90 -@@ -18,7 +18,6 @@ program DAMASK_spectral - use DAMASK_interface, only: & - DAMASK_interface_init, & - loadCaseFile, & -- geometryFile, & - getSolverJobName, & - interface_restartInc - use IO, only: & -@@ -49,14 +48,9 @@ program DAMASK_spectral - restartInc - use numerics, only: & - worldrank, & -- worldsize, & - stagItMax, & - maxCutBack, & - continueCalculation -- use homogenization, only: & -- materialpoint_sizeResults, & -- materialpoint_results, & -- materialpoint_postResults - use material, only: & - thermal_type, & - damage_type, & -@@ -131,12 +125,6 @@ program DAMASK_spectral - type(tLoadCase), allocatable, dimension(:) :: loadCases !< array of all load cases - type(tLoadCase) :: newLoadCase - type(tSolutionState), allocatable, dimension(:) :: solres -- integer(MPI_OFFSET_KIND) :: fileOffset -- integer(MPI_OFFSET_KIND), dimension(:), allocatable :: outputSize -- integer(pInt), parameter :: maxByteOut = 2147483647-4096 !< limit of one file output write https://trac.mpich.org/projects/mpich/ticket/1742 -- integer(pInt), parameter :: maxRealOut = maxByteOut/pReal -- integer(pLongInt), dimension(2) :: outputIndex -- PetscErrorCode :: ierr - procedure(grid_mech_spectral_basic_init), pointer :: & - mech_init - procedure(grid_mech_spectral_basic_forward), pointer :: & -@@ -384,22 +372,6 @@ program DAMASK_spectral - ! write header of output file - if (worldrank == 0) then - writeHeader: if (interface_restartInc < 1_pInt) then -- open(newunit=fileUnit,file=trim(getSolverJobName())//& -- '.spectralOut',form='UNFORMATTED',status='REPLACE') -- write(fileUnit) 'load:', trim(loadCaseFile) ! ... and write header -- write(fileUnit) 'workingdir:', 'n/a' -- write(fileUnit) 'geometry:', trim(geometryFile) -- write(fileUnit) 'grid:', grid -- write(fileUnit) 'size:', geomSize -- write(fileUnit) 'materialpoint_sizeResults:', materialpoint_sizeResults -- write(fileUnit) 'loadcases:', size(loadCases) -- write(fileUnit) 'frequencies:', loadCases%outputfrequency ! one entry per LoadCase -- write(fileUnit) 'times:', loadCases%time ! one entry per LoadCase -- write(fileUnit) 'logscales:', loadCases%logscale -- write(fileUnit) 'increments:', loadCases%incs ! one entry per LoadCase -- write(fileUnit) 'startingIncrement:', restartInc ! start with writing out the previous inc -- write(fileUnit) 'eoh' -- close(fileUnit) ! end of header - open(newunit=statUnit,file=trim(getSolverJobName())//& - '.sta',form='FORMATTED',status='REPLACE') - write(statUnit,'(a)') 'Increment Time CutbackLevel Converged IterationsNeeded' ! statistics file -@@ -412,39 +384,6 @@ program DAMASK_spectral - endif writeHeader - endif - --!-------------------------------------------------------------------------------------------------- --! prepare MPI parallel out (including opening of file) -- allocate(outputSize(worldsize), source = 0_MPI_OFFSET_KIND) -- outputSize(worldrank+1) = size(materialpoint_results,kind=MPI_OFFSET_KIND)*int(pReal,MPI_OFFSET_KIND) -- call MPI_allreduce(MPI_IN_PLACE,outputSize,worldsize,MPI_LONG,MPI_SUM,PETSC_COMM_WORLD,ierr) ! get total output size over each process -- if (ierr /= 0_pInt) call IO_error(error_ID=894_pInt, ext_msg='MPI_allreduce') -- call MPI_file_open(PETSC_COMM_WORLD, trim(getSolverJobName())//'.spectralOut', & -- MPI_MODE_WRONLY + MPI_MODE_APPEND, & -- MPI_INFO_NULL, & -- fileUnit, & -- ierr) -- if (ierr /= 0_pInt) call IO_error(error_ID=894_pInt, ext_msg='MPI_file_open') -- call MPI_file_get_position(fileUnit,fileOffset,ierr) ! get offset from header -- if (ierr /= 0_pInt) call IO_error(error_ID=894_pInt, ext_msg='MPI_file_get_position') -- fileOffset = fileOffset + sum(outputSize(1:worldrank)) ! offset of my process in file (header + processes before me) -- call MPI_file_seek (fileUnit,fileOffset,MPI_SEEK_SET,ierr) -- if (ierr /= 0_pInt) call IO_error(error_ID=894_pInt, ext_msg='MPI_file_seek') -- -- writeUndeformed: if (interface_restartInc < 1_pInt) then -- write(6,'(1/,a)') ' ... writing initial configuration to file ........................' -- call CPFEM_results(0_pInt,0.0_pReal) -- do i = 1, size(materialpoint_results,3)/(maxByteOut/(materialpoint_sizeResults*pReal))+1 ! slice the output of my process in chunks not exceeding the limit for one output -- outputIndex = int([(i-1_pInt)*((maxRealOut)/materialpoint_sizeResults)+1_pInt, & ! QUESTION: why not starting i at 0 instead of murky 1? -- min(i*((maxRealOut)/materialpoint_sizeResults),size(materialpoint_results,3))],pLongInt) -- call MPI_file_write(fileUnit,reshape(materialpoint_results(:,:,outputIndex(1):outputIndex(2)), & -- [(outputIndex(2)-outputIndex(1)+1)*int(materialpoint_sizeResults,pLongInt)]), & -- int((outputIndex(2)-outputIndex(1)+1)*int(materialpoint_sizeResults,pLongInt)), & -- MPI_DOUBLE, MPI_STATUS_IGNORE, ierr) -- if (ierr /= 0_pInt) call IO_error(error_ID=894_pInt, ext_msg='MPI_file_write') -- enddo -- fileOffset = fileOffset + sum(outputSize) ! forward to current file position -- endif writeUndeformed -- - - loadCaseLooping: do currentLoadCase = 1_pInt, size(loadCases) - time0 = time ! load case start time -@@ -574,7 +513,6 @@ program DAMASK_spectral - write(6,'(/,a)') ' cutting back ' - else ! no more options to continue - call IO_warning(850_pInt) -- call MPI_file_close(fileUnit,ierr) - close(statUnit) - call quit(-1_pInt*(lastRestartWritten+1_pInt)) ! quit and provide information about last restart inc written - endif -@@ -593,24 +531,8 @@ program DAMASK_spectral - ' increment ', totalIncsCounter, ' NOT converged' - endif; flush(6) - -- if (mod(inc,loadCases(currentLoadCase)%outputFrequency) == 0_pInt) then ! at output frequency -- write(6,'(1/,a)') ' ... writing results to file ......................................' -- flush(6) -- call materialpoint_postResults() -- call MPI_file_seek (fileUnit,fileOffset,MPI_SEEK_SET,ierr) -- if (ierr /= 0_pInt) call IO_error(894_pInt, ext_msg='MPI_file_seek') -- do i=1, size(materialpoint_results,3)/(maxByteOut/(materialpoint_sizeResults*pReal))+1 ! slice the output of my process in chunks not exceeding the limit for one output -- outputIndex=int([(i-1_pInt)*((maxRealOut)/materialpoint_sizeResults)+1_pInt, & -- min(i*((maxRealOut)/materialpoint_sizeResults),size(materialpoint_results,3))],pLongInt) -- call MPI_file_write(fileUnit,reshape(materialpoint_results(:,:,outputIndex(1):outputIndex(2)),& -- [(outputIndex(2)-outputIndex(1)+1)*int(materialpoint_sizeResults,pLongInt)]), & -- int((outputIndex(2)-outputIndex(1)+1)*int(materialpoint_sizeResults,pLongInt)),& -- MPI_DOUBLE, MPI_STATUS_IGNORE, ierr) -- if(ierr /=0_pInt) call IO_error(894_pInt, ext_msg='MPI_file_write') -- enddo -- fileOffset = fileOffset + sum(outputSize) ! forward to current file position -+ if (mod(inc,loadCases(currentLoadCase)%outputFrequency) == 0_pInt) & ! at output frequency - call CPFEM_results(totalIncsCounter,time) -- endif - if ( loadCases(currentLoadCase)%restartFrequency > 0_pInt & ! writing of restart info requested ... - .and. mod(inc,loadCases(currentLoadCase)%restartFrequency) == 0_pInt) then ! ... and at frequency of writing restart information - restartWrite = .true. ! set restart parameter for FEsolving -@@ -633,7 +555,6 @@ program DAMASK_spectral - real(convergedCounter, pReal)/& - real(notConvergedCounter + convergedCounter,pReal)*100.0_pReal, ' %) increments converged!' - flush(6) -- call MPI_file_close(fileUnit,ierr) - close(statUnit) - - if (notConvergedCounter > 0_pInt) call quit(2_pInt) ! error if some are not converged -diff --git a/src/homogenization.f90 b/src/homogenization.f90 -index 06da6ab2..0743d545 100644 ---- a/src/homogenization.f90 -+++ b/src/homogenization.f90 -@@ -269,6 +269,7 @@ subroutine homogenization_init - + homogenization_maxNgrains * (1 + crystallite_maxSizePostResults & ! crystallite size & crystallite results - + 1 + constitutive_plasticity_maxSizePostResults & ! constitutive size & constitutive results - + constitutive_source_maxSizePostResults) -+ materialpoint_sizeResults = 0 - allocate(materialpoint_results(materialpoint_sizeResults,theMesh%elem%nIPs,theMesh%nElems)) - - write(6,'(/,a)') ' <<<+- homogenization init -+>>>' -@@ -682,6 +683,7 @@ subroutine materialpoint_postResults - i, & !< integration point number - e !< element number - -+ return - !$OMP PARALLEL DO PRIVATE(myNgrains,myCrystallite,thePos,theSize) - elementLooping: do e = FEsolving_execElem(1),FEsolving_execElem(2) - myNgrains = homogenization_Ngrains(mesh_element(3,e)) --- -2.21.0 - diff --git a/installation/patch/python2to3.sh b/installation/patch/python2to3.sh deleted file mode 100755 index 255e62781..000000000 --- a/installation/patch/python2to3.sh +++ /dev/null @@ -1,8 +0,0 @@ -#! /usr/bin/env bash -if [ $1x != 3to2x ]; then - echo 'python2.7 to python3' - find . -name '*.py' -type f | xargs sed -i 's/usr\/bin\/env python2.7/usr\/bin\/env python3/g' -else - echo 'python3 to python2.7' - find . -name '*.py' -type f | xargs sed -i 's/usr\/bin\/env python3/usr\/bin\/env python2.7/g' -fi From cba7114dff26afbc38839e4cc25439e9c2a4a699 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Thu, 2 Jan 2020 21:42:28 +0100 Subject: [PATCH 170/223] there is also an FEM solver that operates on regular grids --- CMakeLists.txt | 2 +- Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d53000d2..24079457a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -113,7 +113,7 @@ if (DAMASK_SOLVER STREQUAL "grid") elseif (DAMASK_SOLVER STREQUAL "fem" OR DAMASK_SOLVER STREQUAL "mesh") project (damask-mesh Fortran C) add_definitions (-DFEM) - message ("Building FEM Solver\n") + message ("Building Mesh Solver\n") else () message (FATAL_ERROR "Build target (DAMASK_SOLVER) is not defined") endif () diff --git a/Makefile b/Makefile index b24e3d36b..0d9374fad 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,7 @@ build/grid: .PHONY: build/mesh build/mesh: @mkdir -p build/mesh - @(cd build/mesh; cmake -Wno-dev -DDAMASK_SOLVER=FEM -DCMAKE_INSTALL_PREFIX=${DAMASK_ROOT} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DBUILDCMD_POST=${BUILDCMD_POST} -DBUILDCMD_PRE=${BUILDCMD_PRE} -DOPTIMIZATION=${OPTIMIZATION} -DOPENMP=${OPENMP} ../../;) + @(cd build/mesh; cmake -Wno-dev -DDAMASK_SOLVER=MESH -DCMAKE_INSTALL_PREFIX=${DAMASK_ROOT} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DBUILDCMD_POST=${BUILDCMD_POST} -DBUILDCMD_PRE=${BUILDCMD_PRE} -DOPTIMIZATION=${OPTIMIZATION} -DOPENMP=${OPENMP} ../../;) .PHONY: clean clean: From d81dc01ce0dbd3408e98066be1453e577d06b3b0 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 3 Jan 2020 12:00:19 +0100 Subject: [PATCH 171/223] we consider indvidual systems, not families --- src/lattice.f90 | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/lattice.f90 b/src/lattice.f90 index 050c7ed87..d351079fb 100644 --- a/src/lattice.f90 +++ b/src/lattice.f90 @@ -2114,8 +2114,8 @@ end function buildInteraction function buildCoordinateSystem(active,potential,system,structure,cOverA) integer, dimension(:), intent(in) :: & - active, & - potential + active, & !< # of active systems per family + potential !< # of potential systems per family real(pReal), dimension(:,:), intent(in) :: & system character(len=*), intent(in) :: & @@ -2308,12 +2308,12 @@ end subroutine buildTransformationSystem !-------------------------------------------------------------------------------------------------- function getlabels(active,potential,system,structure) result(labels) - integer, dimension(:), intent(in) :: & - active, & - potential - real(pReal), dimension(:,:), intent(in) :: & + integer, dimension(:), intent(in) :: & + active, & !< # of active systems per family + potential !< # of potential systems per family + real(pReal), dimension(:,:), intent(in) :: & system - character(len=*), intent(in) :: structure !< lattice structure + character(len=*), intent(in) :: structure !< lattice structure character(len=:), dimension(:), allocatable :: labels character(len=:), allocatable :: label @@ -2335,15 +2335,16 @@ function getlabels(active,potential,system,structure) result(labels) p = sum(potential(1:f-1))+s i = 1 - label(i:i) = merge('[','<',structure(1:3) /= 'bct') + label(i:i) = '[' direction: do j = 1, size(system,1)/2 write(label(i+1:i+2),"(I2.1)") int(system(j,p)) label(i+3:i+3) = ' ' i = i + 3 enddo direction label(i:i) = ']' + i = i +1 - label(i:i) = merge('(','{',structure(1:3) /= 'bct') + label(i:i) = '(' normal: do j = size(system,1)/2+1, size(system,1) write(label(i+1:i+2),"(I2.1)") int(system(j,p)) label(i+3:i+3) = ' ' From a6ddbbd70c1c6c4bf1033e7e1cbc4650010615e2 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 3 Jan 2020 12:40:25 +0100 Subject: [PATCH 172/223] cleaning --- src/DAMASK_interface.f90 | 2 +- src/lattice.f90 | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/DAMASK_interface.f90 b/src/DAMASK_interface.f90 index ee8f220ae..4cf155ac1 100644 --- a/src/DAMASK_interface.f90 +++ b/src/DAMASK_interface.f90 @@ -10,7 +10,7 @@ !> and working directory. !-------------------------------------------------------------------------------------------------- #define GCC_MIN 6 -#define INTEL_MIN 1600 +#define INTEL_MIN 1700 #define PETSC_MAJOR 3 #define PETSC_MINOR_MIN 10 #define PETSC_MINOR_MAX 12 diff --git a/src/lattice.f90 b/src/lattice.f90 index d351079fb..a6c88dd34 100644 --- a/src/lattice.f90 +++ b/src/lattice.f90 @@ -483,9 +483,11 @@ module lattice lattice_slip_normal, & lattice_slip_direction, & lattice_slip_transverse, & - lattice_labels_slip + lattice_labels_slip, & + lattice_labels_twin contains + !-------------------------------------------------------------------------------------------------- !> @brief Module initialization !-------------------------------------------------------------------------------------------------- @@ -2306,14 +2308,13 @@ end subroutine buildTransformationSystem !-------------------------------------------------------------------------------------------------- !> @brief select active systems as strings !-------------------------------------------------------------------------------------------------- -function getlabels(active,potential,system,structure) result(labels) +function getlabels(active,potential,system) result(labels) integer, dimension(:), intent(in) :: & active, & !< # of active systems per family potential !< # of potential systems per family real(pReal), dimension(:,:), intent(in) :: & system - character(len=*), intent(in) :: structure !< lattice structure character(len=:), dimension(:), allocatable :: labels character(len=:), allocatable :: label From 4ebd89c04058df0d20b2b2695e300573bf003359 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 3 Jan 2020 13:33:32 +0100 Subject: [PATCH 173/223] shape is known (no need for automatic allocation) --- src/crystallite.f90 | 2 +- src/lattice.f90 | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/crystallite.f90 b/src/crystallite.f90 index d33e774e9..f2eadd776 100644 --- a/src/crystallite.f90 +++ b/src/crystallite.f90 @@ -118,7 +118,7 @@ contains !-------------------------------------------------------------------------------------------------- subroutine crystallite_init - logical, dimension(:,:), allocatable :: devNull + logical, dimension(discretization_nIP,discretization_nElem) :: devNull integer :: & c, & !< counter in integration point component loop i, & !< counter in integration point loop diff --git a/src/lattice.f90 b/src/lattice.f90 index a6c88dd34..36a682ab3 100644 --- a/src/lattice.f90 +++ b/src/lattice.f90 @@ -1952,7 +1952,7 @@ function lattice_labels_slip(Nslip,structure) result(labels) if (any(Nslip < 0)) & call IO_error(144,ext_msg='Nslip '//trim(structure)) - labels = getLabels(Nslip,NslipMax,slipSystems,structure) + labels = getLabels(Nslip,NslipMax,slipSystems) end function lattice_labels_slip @@ -1993,7 +1993,7 @@ function lattice_labels_twin(Ntwin,structure) result(labels) if (any(Ntwin < 0)) & call IO_error(144,ext_msg='Ntwin '//trim(structure)) - labels = getLabels(Ntwin,NtwinMax,twinSystems,structure) + labels = getLabels(Ntwin,NtwinMax,twinSystems) end function lattice_labels_twin From 2975e46ca1c38c42e4dd581a9c51b82492cbaf4c Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 3 Jan 2020 13:52:06 +0100 Subject: [PATCH 174/223] not needed --- src/grid/grid_mech_FEM.f90 | 2 -- src/grid/grid_mech_spectral_basic.f90 | 2 -- src/grid/grid_mech_spectral_polarisation.f90 | 2 -- 3 files changed, 6 deletions(-) diff --git a/src/grid/grid_mech_FEM.f90 b/src/grid/grid_mech_FEM.f90 index a34d880f7..5133d577a 100644 --- a/src/grid/grid_mech_FEM.f90 +++ b/src/grid/grid_mech_FEM.f90 @@ -12,8 +12,6 @@ module grid_mech_FEM use PETScdmda use PETScsnes use prec - use CPFEM2 - use IO use debug use FEsolving use numerics diff --git a/src/grid/grid_mech_spectral_basic.f90 b/src/grid/grid_mech_spectral_basic.f90 index f05f9bc93..b34034fa3 100644 --- a/src/grid/grid_mech_spectral_basic.f90 +++ b/src/grid/grid_mech_spectral_basic.f90 @@ -15,13 +15,11 @@ module grid_mech_spectral_basic use HDF5_utilities use math use spectral_utilities - use IO use FEsolving use config use numerics use homogenization use mesh_grid - use CPFEM2 use debug implicit none diff --git a/src/grid/grid_mech_spectral_polarisation.f90 b/src/grid/grid_mech_spectral_polarisation.f90 index 33c3e4e72..4986a3457 100644 --- a/src/grid/grid_mech_spectral_polarisation.f90 +++ b/src/grid/grid_mech_spectral_polarisation.f90 @@ -16,13 +16,11 @@ module grid_mech_spectral_polarisation use math use rotations use spectral_utilities - use IO use FEsolving use config use numerics use homogenization use mesh_grid - use CPFEM2 use debug implicit none From 886e1110644d23e11b3bae76e8225dfe294dff48 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 3 Jan 2020 13:53:23 +0100 Subject: [PATCH 175/223] not needed --- src/grid/grid_damage_spectral.f90 | 1 - src/grid/grid_thermal_spectral.f90 | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/grid/grid_damage_spectral.f90 b/src/grid/grid_damage_spectral.f90 index 1fb91b49b..e2ce28c0e 100644 --- a/src/grid/grid_damage_spectral.f90 +++ b/src/grid/grid_damage_spectral.f90 @@ -15,7 +15,6 @@ module grid_damage_spectral use mesh_grid use damage_nonlocal use numerics - use damage_nonlocal implicit none private diff --git a/src/grid/grid_thermal_spectral.f90 b/src/grid/grid_thermal_spectral.f90 index c7f886f13..c381d837d 100644 --- a/src/grid/grid_thermal_spectral.f90 +++ b/src/grid/grid_thermal_spectral.f90 @@ -14,8 +14,8 @@ module grid_thermal_spectral use spectral_utilities use mesh_grid use thermal_conduction - use material use numerics + use material implicit none private From 326e3d082443d017481ae806d9691d3bbc394201 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 3 Jan 2020 14:02:14 +0100 Subject: [PATCH 176/223] polishing --- src/grid/grid_mech_FEM.f90 | 25 ++++++++++++------------- src/grid/grid_mech_spectral_basic.f90 | 4 ++-- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/grid/grid_mech_FEM.f90 b/src/grid/grid_mech_FEM.f90 index 5133d577a..b2ea20f16 100644 --- a/src/grid/grid_mech_FEM.f90 +++ b/src/grid/grid_mech_FEM.f90 @@ -7,20 +7,20 @@ module grid_mech_FEM #include #include - use DAMASK_interface - use HDF5_utilities use PETScdmda use PETScsnes + use prec - use debug + use DAMASK_interface + use HDF5_utilities + use math + use spectral_utilities use FEsolving use numerics use homogenization - use DAMASK_interface - use spectral_utilities use discretization use mesh_grid - use math + use debug implicit none private @@ -50,7 +50,7 @@ module grid_mech_FEM F_aimDot = 0.0_pReal, & !< assumed rate of average deformation gradient F_aim = math_I3, & !< current prescribed deformation gradient F_aim_lastIter = math_I3, & - F_aim_lastInc = math_I3, & !< previous average deformation gradient + F_aim_lastInc = math_I3, & !< previous average deformation gradient P_av = 0.0_pReal !< average 1st Piola--Kirchhoff stress character(len=1024), private :: incInfo !< time and increment information @@ -80,8 +80,8 @@ contains !-------------------------------------------------------------------------------------------------- subroutine grid_mech_FEM_init - real(pReal) :: HGCoeff = 0e-2_pReal - PetscInt, dimension(:), allocatable :: localK + real(pReal) :: HGCoeff = 0.0e-2_pReal + PetscInt, dimension(worldsize) :: localK real(pReal), dimension(3,3) :: & temp33_Real = 0.0_pReal real(pReal), dimension(4,8) :: & @@ -121,10 +121,9 @@ subroutine grid_mech_FEM_init ! initialize solver specific parts of PETSc call SNESCreate(PETSC_COMM_WORLD,mech_snes,ierr); CHKERRQ(ierr) call SNESSetOptionsPrefix(mech_snes,'mech_',ierr);CHKERRQ(ierr) - allocate(localK(worldsize), source = 0); localK(worldrank+1) = grid3 - do rank = 1, worldsize - call MPI_Bcast(localK(rank),1,MPI_INTEGER,rank-1,PETSC_COMM_WORLD,ierr) - enddo + localK = 0 + localK(worldrank+1) = grid3 + call MPI_Allreduce(MPI_IN_PLACE,localK,worldsize,MPI_INTEGER,MPI_SUM,PETSC_COMM_WORLD,ierr) call DMDACreate3d(PETSC_COMM_WORLD, & DM_BOUNDARY_PERIODIC, DM_BOUNDARY_PERIODIC, DM_BOUNDARY_PERIODIC, & DMDA_STENCIL_BOX, & diff --git a/src/grid/grid_mech_spectral_basic.f90 b/src/grid/grid_mech_spectral_basic.f90 index b34034fa3..9fcd3d563 100644 --- a/src/grid/grid_mech_spectral_basic.f90 +++ b/src/grid/grid_mech_spectral_basic.f90 @@ -282,7 +282,7 @@ subroutine grid_mech_spectral_basic_forward(cutBack,guess,timeinc,timeinc_old,lo F_aimDot = merge(stress_BC%maskFloat*(F_aim-F_aim_lastInc)/timeinc_old, 0.0_pReal, guess) F_aim_lastInc = F_aim - !-------------------------------------------------------------------------------------------------- + !----------------------------------------------------------------------------------------------- ! calculate rate for aim if (deformation_BC%myType=='l') then ! calculate F_aimDot from given L and current F F_aimDot = & @@ -362,7 +362,7 @@ subroutine grid_mech_spectral_basic_restartWrite if (num%update_gamma) call utilities_saveReferenceStiffness call DMDAVecRestoreArrayF90(da,solution_vec,F,ierr); CHKERRQ(ierr) - + end subroutine grid_mech_spectral_basic_restartWrite From b5e4e42505095c70af3186e53159e20d159661e1 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 3 Jan 2020 14:44:31 +0100 Subject: [PATCH 177/223] splitext includes leading dot --- python/damask/geom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/damask/geom.py b/python/damask/geom.py index 63ce20115..bfe475730 100644 --- a/python/damask/geom.py +++ b/python/damask/geom.py @@ -422,7 +422,7 @@ class Geom(): ext = os.path.splitext(fname)[1] if ext == '': name = fname + '.' + writer.GetDefaultFileExtension() - elif ext == writer.GetDefaultFileExtension(): + elif ext[1:] == writer.GetDefaultFileExtension(): name = fname else: raise ValueError("unknown extension {}".format(ext)) From e692118ec5c412347dc40e34036f1fce814f11f8 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 3 Jan 2020 15:41:15 +0100 Subject: [PATCH 178/223] easier to use for single output label --- python/damask/dadf5.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/damask/dadf5.py b/python/damask/dadf5.py index f6ea959bd..73e785b10 100644 --- a/python/damask/dadf5.py +++ b/python/damask/dadf5.py @@ -855,7 +855,7 @@ class DADF5(): Parameters ---------- - labels : list of str + 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. @@ -908,7 +908,7 @@ class DADF5(): materialpoints_backup = self.visible['materialpoints'].copy() self.set_visible('materialpoints',False) - for label in labels: + 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'): @@ -939,7 +939,7 @@ class DADF5(): constituents_backup = self.visible['constituents'].copy() self.set_visible('constituents',False) - for label in labels: + 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'): From e1b35be23a0182785d43c64c16ad3f2daabddcb2 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 3 Jan 2020 15:41:46 +0100 Subject: [PATCH 179/223] not used anymore --- src/mesh_marc.f90 | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/mesh_marc.f90 b/src/mesh_marc.f90 index 8de8ee96c..1d7fd09c3 100644 --- a/src/mesh_marc.f90 +++ b/src/mesh_marc.f90 @@ -36,12 +36,6 @@ module mesh mesh_mapFEtoCPelem, & !< [sorted FEid, corresponding CPid] mesh_mapFEtoCPnode !< [sorted FEid, corresponding CPid] -!-------------------------------------------------------------------------------------------------- -! DEPRECATED - real(pReal), dimension(:,:,:), allocatable, public :: & - mesh_ipCoordinates !< IP x,y,z coordinates (after deformation!) -!-------------------------------------------------------------------------------------------------- - public :: & mesh_init, & mesh_FEasCP @@ -96,8 +90,6 @@ subroutine mesh_init(ip,el) calcMode(ip,mesh_FEasCP('elem',el)) = .true. ! first ip,el needs to be already pingponged to "calc" - allocate(mesh_ipCoordinates(3,elem%nIPs,nElems),source=0.0_pReal) ! deprecated - allocate(cellNodeDefinition(elem%nNodes-1)) allocate(connectivity_cell(elem%NcellNodesPerCell,elem%nIPs,nElems)) call buildCells(connectivity_cell,cellNodeDefinition,& From 385085de7333b9ab5e0fec47b620c5487600ba55 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 4 Jan 2020 01:05:40 +0100 Subject: [PATCH 180/223] correct names for numpy (differ from math) --- python/damask/colormaps.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/damask/colormaps.py b/python/damask/colormaps.py index ad924325f..e4183e830 100644 --- a/python/damask/colormaps.py +++ b/python/damask/colormaps.py @@ -328,9 +328,9 @@ class Color(): Msh = np.zeros(3,'d') Msh[0] = np.sqrt(np.dot(self.color,self.color)) if (Msh[0] > 0.001): - Msh[1] = np.acos(self.color[0]/Msh[0]) + Msh[1] = np.arccos(self.color[0]/Msh[0]) if (self.color[1] != 0.0): - Msh[2] = np.atan2(self.color[2],self.color[1]) + Msh[2] = np.arctan2(self.color[2],self.color[1]) converted = Color('MSH', Msh) self.model = converted.model From bd5f96326092c33402c3b07f1132c468108312c0 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 4 Jan 2020 14:37:09 +0100 Subject: [PATCH 181/223] polishing --- CMakeLists.txt | 11 +++-------- src/IO.f90 | 17 ++++++++--------- src/grid/grid_mech_FEM.f90 | 1 - 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 24079457a..e44f5eab2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -117,9 +117,7 @@ elseif (DAMASK_SOLVER STREQUAL "fem" OR DAMASK_SOLVER STREQUAL "mesh") else () message (FATAL_ERROR "Build target (DAMASK_SOLVER) is not defined") endif () - -# set linker commands (needs to be done after defining the project) -set (CMAKE_LINKER "${PETSC_LINKER}") +list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) if (CMAKE_BUILD_TYPE STREQUAL "") set (CMAKE_BUILD_TYPE "RELEASE") @@ -168,9 +166,6 @@ add_definitions (-DDAMASKVERSION="${DAMASK_V}") # definition of other macros add_definitions (-DPETSc) -set (DAMASK_INCLUDE_FLAGS "${DAMASK_INCLUDE_FLAGS} ${PETSC_INCLUDES}") -list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) - if (CMAKE_Fortran_COMPILER_ID STREQUAL "Intel") include(Compiler-Intel) elseif(CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") @@ -183,14 +178,14 @@ endif () set (CMAKE_Fortran_FLAGS_${CMAKE_BUILD_TYPE} "${BUILDCMD_PRE} ${OPENMP_FLAGS} ${STANDARD_CHECK} ${OPTIMIZATION_FLAGS} ${COMPILE_FLAGS} ${PRECISION_FLAGS}") -set (CMAKE_Fortran_LINK_EXECUTABLE "${BUILDCMD_PRE} ${CMAKE_LINKER} ${OPENMP_FLAGS} ${OPTIMIZATION_FLAGS} ${LINKER_FLAGS}") +set (CMAKE_Fortran_LINK_EXECUTABLE "${BUILDCMD_PRE} ${PETSC_LINKER} ${OPENMP_FLAGS} ${OPTIMIZATION_FLAGS} ${LINKER_FLAGS}") if (CMAKE_BUILD_TYPE STREQUAL "DEBUG") set (CMAKE_Fortran_FLAGS_${CMAKE_BUILD_TYPE} "${CMAKE_Fortran_FLAGS_${CMAKE_BUILD_TYPE}} ${DEBUG_FLAGS}") set (CMAKE_Fortran_LINK_EXECUTABLE "${CMAKE_Fortran_LINK_EXECUTABLE} ${DEBUG_FLAGS}") endif () -set (CMAKE_Fortran_FLAGS_${CMAKE_BUILD_TYPE} "${CMAKE_Fortran_FLAGS_${CMAKE_BUILD_TYPE}} ${DAMASK_INCLUDE_FLAGS} ${BUILDCMD_POST}") +set (CMAKE_Fortran_FLAGS_${CMAKE_BUILD_TYPE} "${CMAKE_Fortran_FLAGS_${CMAKE_BUILD_TYPE}} ${PETSC_INCLUDES} ${BUILDCMD_POST}") set (CMAKE_Fortran_LINK_EXECUTABLE "${CMAKE_Fortran_LINK_EXECUTABLE} -o ${PETSC_EXTERNAL_LIB} ${BUILDCMD_POST}") message ("Fortran Compiler Flags:\n${CMAKE_Fortran_FLAGS_${CMAKE_BUILD_TYPE}}\n") diff --git a/src/IO.f90 b/src/IO.f90 index 6d8506506..6c6c3b7c7 100644 --- a/src/IO.f90 +++ b/src/IO.f90 @@ -384,7 +384,7 @@ function IO_stringValue(string,chunkPos,myChunk,silent) logical :: warn if (present(silent)) then - warn = silent + warn = .not. silent else warn = .false. endif @@ -414,11 +414,10 @@ real(pReal) function IO_floatValue (string,chunkPos,myChunk) valuePresent: if (myChunk > chunkPos(1) .or. myChunk < 1) then call IO_warning(201,el=myChunk,ext_msg=MYNAME//trim(string)) - else valuePresent - IO_floatValue = & - verifyFloatValue(trim(adjustl(string(chunkPos(myChunk*2):chunkPos(myChunk*2+1)))),& - VALIDCHARACTERS,MYNAME) - endif valuePresent + else valuePresent + IO_floatValue = verifyFloatValue(trim(adjustl(string(chunkPos(myChunk*2):chunkPos(myChunk*2+1)))),& + VALIDCHARACTERS,MYNAME) + endif valuePresent end function IO_floatValue @@ -466,12 +465,12 @@ real(pReal) function IO_fixedNoEFloatValue (string,ends,myChunk) pos_exp = scan(string(ends(myChunk)+1:ends(myChunk+1)),'+-',back=.true.) hasExponent: if (pos_exp > 1) then base = verifyFloatValue(trim(adjustl(string(ends(myChunk)+1:ends(myChunk)+pos_exp-1))),& - VALIDBASE,MYNAME//'(base): ') + VALIDBASE,MYNAME//'(base): ') expon = verifyIntValue(trim(adjustl(string(ends(myChunk)+pos_exp:ends(myChunk+1)))),& - VALIDEXP,MYNAME//'(exp): ') + VALIDEXP,MYNAME//'(exp): ') else hasExponent base = verifyFloatValue(trim(adjustl(string(ends(myChunk)+1:ends(myChunk+1)))),& - VALIDBASE,MYNAME//'(base): ') + VALIDBASE,MYNAME//'(base): ') expon = 0 endif hasExponent IO_fixedNoEFloatValue = base*10.0_pReal**real(expon,pReal) diff --git a/src/grid/grid_mech_FEM.f90 b/src/grid/grid_mech_FEM.f90 index b2ea20f16..713e83029 100644 --- a/src/grid/grid_mech_FEM.f90 +++ b/src/grid/grid_mech_FEM.f90 @@ -94,7 +94,6 @@ subroutine grid_mech_FEM_init 1.0_pReal,-1.0_pReal,-1.0_pReal,-1.0_pReal, & 1.0_pReal, 1.0_pReal, 1.0_pReal, 1.0_pReal], [4,8]) PetscErrorCode :: ierr - integer :: rank integer(HID_T) :: fileHandle, groupHandle character(len=pStringLen) :: fileName real(pReal), dimension(3,3,3,3) :: devNull From 3999c0b63079bed881902f47a9f269269a612dda Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 4 Jan 2020 17:15:12 +0100 Subject: [PATCH 182/223] is not used anymore (and IO_fixedXXXvalue seem to be superfluous) --- src/IO.f90 | 43 ++----------------------------------------- src/mesh_marc.f90 | 7 +++---- 2 files changed, 5 insertions(+), 45 deletions(-) diff --git a/src/IO.f90 b/src/IO.f90 index 6c6c3b7c7..2d45a8add 100644 --- a/src/IO.f90 +++ b/src/IO.f90 @@ -23,7 +23,7 @@ module IO public :: & IO_init, & IO_read_ASCII, & - IO_open_file, & + IO_open_file, & ! deprecated, use IO_read_ASCII IO_open_jobFile_binary, & IO_isBlank, & IO_getTag, & @@ -44,8 +44,7 @@ module IO IO_countDataLines #elif defined(Marc4DAMASK) IO_fixedNoEFloatValue, & - IO_fixedIntValue, & - IO_countNumericalDataLines + IO_fixedIntValue #endif #endif @@ -549,14 +548,8 @@ subroutine IO_error(error_ID,el,ip,g,instance,ext_msg) msg = 'could not read file:' case (103) msg = 'could not assemble input files' - case (104) - msg = '{input} recursion limit reached' - case (105) - msg = 'unknown output:' case (106) msg = 'working directory does not exist:' - case (107) - msg = 'line length exceeds limit of 256' !-------------------------------------------------------------------------------------------------- ! lattice error messages @@ -923,38 +916,6 @@ end function IO_countDataLines #endif -#ifdef Marc4DAMASK -!-------------------------------------------------------------------------------------------------- -!> @brief count lines containig data up to next *keyword -!-------------------------------------------------------------------------------------------------- -integer function IO_countNumericalDataLines(fileUnit) - - integer, intent(in) :: fileUnit !< file handle - - - integer, allocatable, dimension(:) :: chunkPos - character(len=pStringLen) :: line, & - tmp - - IO_countNumericalDataLines = 0 - line = '' - - do while (trim(line) /= IO_EOF) - line = IO_read(fileUnit) - chunkPos = IO_stringPos(line) - tmp = IO_lc(IO_stringValue(line,chunkPos,1)) - if (verify(trim(tmp),'0123456789') == 0) then ! numerical values - IO_countNumericalDataLines = IO_countNumericalDataLines + 1 - else - exit - endif - enddo - backspace(fileUnit) - -end function IO_countNumericalDataLines -#endif - - !-------------------------------------------------------------------------------------------------- !> @brief count items in consecutive lines depending on lines !> @details Marc: ints concatenated by "c" as last char or range of values a "to" b diff --git a/src/mesh_marc.f90 b/src/mesh_marc.f90 index 1d7fd09c3..00fd76326 100644 --- a/src/mesh_marc.f90 +++ b/src/mesh_marc.f90 @@ -488,8 +488,7 @@ subroutine inputRead_mapNodes(fileContent) chunkPos = IO_stringPos(fileContent(l)) if( IO_lc(IO_stringValue(fileContent(l),chunkPos,1)) == 'coordinates' ) then do i = 1,size(mesh_mapFEtoCPnode,2) - mesh_mapFEtoCPnode(1,i) = IO_fixedIntValue (fileContent(l+1+i),[0,10],1) - mesh_mapFEtoCPnode(2,i) = i + mesh_mapFEtoCPnode(1:2,i) = [IO_fixedIntValue (fileContent(l+1+i),[0,10],1),i] ! ToDo: use IO_intValue enddo exit endif @@ -520,9 +519,9 @@ subroutine inputRead_elemNodes(nodes, & chunkPos = IO_stringPos(fileContent(l)) if( IO_lc(IO_stringValue(fileContent(l),chunkPos,1)) == 'coordinates' ) then do i=1,nNode - m = mesh_FEasCP('node',IO_fixedIntValue(fileContent(l+1+i),node_ends,1)) + m = mesh_FEasCP('node',IO_fixedIntValue(fileContent(l+1+i),node_ends,1)) !ToDo: use IO_intValue do j = 1,3 - nodes(j,m) = mesh_unitlength * IO_fixedNoEFloatValue(fileContent(l+1+i),node_ends,j+1) + nodes(j,m) = mesh_unitlength * IO_fixedNoEFloatValue(fileContent(l+1+i),node_ends,j+1) !ToDo: use IO_floatValue enddo enddo exit From 6b6ad5235535d8dbf9dcb4154f854269d49c51d8 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 4 Jan 2020 18:25:59 +0100 Subject: [PATCH 183/223] use variable string as return (no need for trim) --- src/DAMASK_interface.f90 | 37 ++++++++++++++++++++----------------- src/system_routines.f90 | 14 ++++++++++---- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/src/DAMASK_interface.f90 b/src/DAMASK_interface.f90 index 4cf155ac1..27a0084f5 100644 --- a/src/DAMASK_interface.f90 +++ b/src/DAMASK_interface.f90 @@ -269,10 +269,10 @@ subroutine DAMASK_interface_init write(6,'(a,a)') ' Working dir argument: ', trim(workingDirArg) write(6,'(a,a)') ' Geometry argument: ', trim(geometryArg) write(6,'(a,a)') ' Load case argument: ', trim(loadcaseArg) - write(6,'(a,a)') ' Working directory: ', trim(getCWD()) + write(6,'(a,a)') ' Working directory: ', getCWD() write(6,'(a,a)') ' Geometry file: ', trim(geometryFile) write(6,'(a,a)') ' Loadcase file: ', trim(loadCaseFile) - write(6,'(a,a)') ' Solver job name: ', trim(getSolverJobName()) + write(6,'(a,a)') ' Solver job name: ', getSolverJobName() if (interface_restartInc > 0) & write(6,'(a,i6.6)') ' Restart from increment: ', interface_restartInc @@ -308,7 +308,7 @@ subroutine setWorkingDirectory(workingDirectoryArg) workingDirectory = trim(rectifyPath(workingDirectory)) error = setCWD(trim(workingDirectory)) if(error) then - write(6,'(/,a)') ' ERROR: Working directory "'//trim(workingDirectory)//'" does not exist' + write(6,'(/,a)') ' ERROR: Invalid Working directory: '//trim(workingDirectory) call quit(1) endif @@ -318,8 +318,9 @@ end subroutine setWorkingDirectory !-------------------------------------------------------------------------------------------------- !> @brief solver job name (no extension) as combination of geometry and load case name !-------------------------------------------------------------------------------------------------- -character(len=1024) function getSolverJobName() +function getSolverJobName() + character(len=:), allocatable :: getSolverJobName integer :: posExt,posSep posExt = scan(geometryFile,'.',back=.true.) @@ -330,7 +331,7 @@ character(len=1024) function getSolverJobName() posExt = scan(loadCaseFile,'.',back=.true.) posSep = scan(loadCaseFile,'/',back=.true.) - getSolverJobName = trim(getSolverJobName)//'_'//loadCaseFile(posSep+1:posExt-1) + getSolverJobName = getSolverJobName//'_'//loadCaseFile(posSep+1:posExt-1) end function getSolverJobName @@ -338,15 +339,16 @@ end function getSolverJobName !-------------------------------------------------------------------------------------------------- !> @brief basename of geometry file with extension from command line arguments !-------------------------------------------------------------------------------------------------- -character(len=1024) function getGeometryFile(geometryParameter) +function getGeometryFile(geometryParameter) - character(len=1024), intent(in) :: geometryParameter - logical :: file_exists - external :: quit + character(len=:), allocatable :: getGeometryFile + character(len=*), intent(in) :: geometryParameter + logical :: file_exists + external :: quit getGeometryFile = trim(geometryParameter) - if (scan(getGeometryFile,'/') /= 1) getGeometryFile = trim(getCWD())//'/'//trim(getGeometryFile) - getGeometryFile = makeRelativePath(trim(getCWD()), getGeometryFile) + if (scan(getGeometryFile,'/') /= 1) getGeometryFile = getCWD()//'/'//trim(getGeometryFile) + getGeometryFile = makeRelativePath(getCWD(), getGeometryFile) inquire(file=trim(getGeometryFile), exist=file_exists) if (.not. file_exists) then @@ -360,15 +362,16 @@ end function getGeometryFile !-------------------------------------------------------------------------------------------------- !> @brief relative path of loadcase from command line arguments !-------------------------------------------------------------------------------------------------- -character(len=1024) function getLoadCaseFile(loadCaseParameter) +function getLoadCaseFile(loadCaseParameter) - character(len=1024), intent(in) :: loadCaseParameter - logical :: file_exists - external :: quit + character(len=:), allocatable :: getLoadCaseFile + character(len=*), intent(in) :: loadCaseParameter + logical :: file_exists + external :: quit getLoadCaseFile = trim(loadCaseParameter) - if (scan(getLoadCaseFile,'/') /= 1) getLoadCaseFile = trim(getCWD())//'/'//trim(getLoadCaseFile) - getLoadCaseFile = makeRelativePath(trim(getCWD()), getLoadCaseFile) + if (scan(getLoadCaseFile,'/') /= 1) getLoadCaseFile = getCWD()//'/'//trim(getLoadCaseFile) + getLoadCaseFile = makeRelativePath(getCWD(), getLoadCaseFile) inquire(file=trim(getLoadCaseFile), exist=file_exists) if (.not. file_exists) then diff --git a/src/system_routines.f90 b/src/system_routines.f90 index 0611c96db..932eefeb6 100644 --- a/src/system_routines.f90 +++ b/src/system_routines.f90 @@ -93,21 +93,24 @@ end function isDirectory !-------------------------------------------------------------------------------------------------- !> @brief gets the current working directory !-------------------------------------------------------------------------------------------------- -character(len=1024) function getCWD() +function getCWD() character(kind=C_CHAR), dimension(1024) :: charArray ! C string is an array + character(len=:), allocatable :: getCWD integer(C_INT) :: stat integer :: i call getCurrentWorkDir_C(charArray,stat) + if (stat /= 0_C_INT) then getCWD = 'Error occured when getting currend working directory' else - getCWD = repeat('',len(getCWD)) + allocate(character(len=1024)::getCWD) arrayToString: do i=1,len(getCWD) if (charArray(i) /= C_NULL_CHAR) then getCWD(i:i)=charArray(i) else + getCWD = getCWD(:i-1) exit endif enddo arrayToString @@ -119,21 +122,24 @@ end function getCWD !-------------------------------------------------------------------------------------------------- !> @brief gets the current host name !-------------------------------------------------------------------------------------------------- -character(len=1024) function getHostName() +function getHostName() character(kind=C_CHAR), dimension(1024) :: charArray ! C string is an array + character(len=:), allocatable :: getHostName integer(C_INT) :: stat integer :: i call getHostName_C(charArray,stat) + if (stat /= 0_C_INT) then getHostName = 'Error occured when getting host name' else - getHostName = repeat('',len(getHostName)) + allocate(character(len=1024)::getHostName) arrayToString: do i=1,len(getHostName) if (charArray(i) /= C_NULL_CHAR) then getHostName(i:i)=charArray(i) else + getHostName = getHostName(:i-1) exit endif enddo arrayToString From bd6f2a6b5c0b26adbce7129df25d854160f46834 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 4 Jan 2020 19:01:36 +0100 Subject: [PATCH 184/223] consistent string length --- src/IO.f90 | 8 ++++---- src/grid/grid_mech_FEM.f90 | 2 +- src/grid/grid_mech_spectral_basic.f90 | 2 +- src/grid/grid_mech_spectral_polarisation.f90 | 2 +- src/grid/spectral_utilities.f90 | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/IO.f90 b/src/IO.f90 index 2d45a8add..1e39daa1e 100644 --- a/src/IO.f90 +++ b/src/IO.f90 @@ -528,8 +528,8 @@ subroutine IO_error(error_ID,el,ip,g,instance,ext_msg) character(len=*), optional, intent(in) :: ext_msg external :: quit - character(len=1024) :: msg - character(len=1024) :: formatString + character(len=pStringLen) :: msg + character(len=pStringLen) :: formatString select case (error_ID) @@ -769,8 +769,8 @@ subroutine IO_warning(warning_ID,el,ip,g,ext_msg) integer, optional, intent(in) :: el,ip,g character(len=*), optional, intent(in) :: ext_msg - character(len=1024) :: msg - character(len=1024) :: formatString + character(len=pStringLen) :: msg + character(len=pStringLen) :: formatString select case (warning_ID) case (1) diff --git a/src/grid/grid_mech_FEM.f90 b/src/grid/grid_mech_FEM.f90 index 713e83029..05fc3520a 100644 --- a/src/grid/grid_mech_FEM.f90 +++ b/src/grid/grid_mech_FEM.f90 @@ -53,7 +53,7 @@ module grid_mech_FEM F_aim_lastInc = math_I3, & !< previous average deformation gradient P_av = 0.0_pReal !< average 1st Piola--Kirchhoff stress - character(len=1024), 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 diff --git a/src/grid/grid_mech_spectral_basic.f90 b/src/grid/grid_mech_spectral_basic.f90 index 9fcd3d563..af8cbc377 100644 --- a/src/grid/grid_mech_spectral_basic.f90 +++ b/src/grid/grid_mech_spectral_basic.f90 @@ -55,7 +55,7 @@ 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=1024), 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_volAvgLastInc = 0.0_pReal, & !< previous volume average stiffness diff --git a/src/grid/grid_mech_spectral_polarisation.f90 b/src/grid/grid_mech_spectral_polarisation.f90 index 4986a3457..59ab84869 100644 --- a/src/grid/grid_mech_spectral_polarisation.f90 +++ b/src/grid/grid_mech_spectral_polarisation.f90 @@ -59,7 +59,7 @@ module grid_mech_spectral_polarisation F_av = 0.0_pReal, & !< average incompatible def grad field P_av = 0.0_pReal !< average 1st Piola--Kirchhoff stress - character(len=1024), 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_volAvgLastInc = 0.0_pReal, & !< previous volume average stiffness diff --git a/src/grid/spectral_utilities.f90 b/src/grid/spectral_utilities.f90 index 04a24e877..68c372b26 100644 --- a/src/grid/spectral_utilities.f90 +++ b/src/grid/spectral_utilities.f90 @@ -700,7 +700,7 @@ function utilities_maskedCompliance(rot_BC,mask_stress,C) c_reduced, & !< reduced stiffness (depending on number of stress BC) sTimesC !< temp variable to check inversion logical :: errmatinv - character(len=1024):: formatString + character(len=pStringLen):: formatString mask_stressVector = reshape(transpose(mask_stress), [9]) size_reduced = count(mask_stressVector) From 09f42a399136df4747c807544fc624bd5dfcd539 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Wed, 8 Jan 2020 15:34:21 +0100 Subject: [PATCH 185/223] ang files might have more columns --- python/damask/table.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python/damask/table.py b/python/damask/table.py index 28ce3efe0..ef8a84276 100644 --- a/python/damask/table.py +++ b/python/damask/table.py @@ -138,9 +138,12 @@ class Table(): break data = np.loadtxt(content) + for c in range(data.shape[1]-10): + shapes['user_defined{}'.format(c+1)] = (1,) return Table(data,shapes,comments) + @property def labels(self): return list(self.shapes.keys()) From 70e23fea9365c74efefcc58c0199f984286595c6 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 10 Jan 2020 01:28:32 +0100 Subject: [PATCH 186/223] polishing --- src/kinematics_cleavage_opening.f90 | 2 +- src/kinematics_slipplane_opening.f90 | 29 ++++++++++------------------ src/kinematics_thermal_expansion.f90 | 2 +- 3 files changed, 12 insertions(+), 21 deletions(-) diff --git a/src/kinematics_cleavage_opening.f90 b/src/kinematics_cleavage_opening.f90 index eec8a1986..6b060a8d9 100644 --- a/src/kinematics_cleavage_opening.f90 +++ b/src/kinematics_cleavage_opening.f90 @@ -65,7 +65,7 @@ subroutine kinematics_cleavage_opening_init integer :: maxNinstance,p,instance - write(6,'(/,a)') ' <<<+- kinematics_'//KINEMATICS_cleavage_opening_LABEL//' init -+>>>' + write(6,'(/,a)') ' <<<+- kinematics_'//KINEMATICS_cleavage_opening_LABEL//' init -+>>>'; flush(6) maxNinstance = count(phase_kinematics == KINEMATICS_cleavage_opening_ID) if (maxNinstance == 0) return diff --git a/src/kinematics_slipplane_opening.f90 b/src/kinematics_slipplane_opening.f90 index c0f198985..018a5b4b5 100644 --- a/src/kinematics_slipplane_opening.f90 +++ b/src/kinematics_slipplane_opening.f90 @@ -51,7 +51,7 @@ subroutine kinematics_slipplane_opening_init integer :: maxNinstance,p,instance - write(6,'(/,a)') ' <<<+- kinematics_'//KINEMATICS_slipplane_opening_LABEL//' init -+>>>' + write(6,'(/,a)') ' <<<+- kinematics_'//KINEMATICS_slipplane_opening_LABEL//' init -+>>>'; flush(6) maxNinstance = count(phase_kinematics == KINEMATICS_slipplane_opening_ID) if (maxNinstance == 0) return @@ -144,40 +144,31 @@ subroutine kinematics_slipplane_opening_LiAndItsTangent(Ld, dLd_dTstar, S, ipc, traction_crit = prm%critLoad(i)* damage(homog)%p(damageOffset) ! degrading critical load carrying capacity by damage - udotd = sign(1.0_pReal,traction_d)* & - prm%sdot0* & - (abs(traction_d)/traction_crit - & - abs(traction_d)/prm%critLoad(i))**prm%n + udotd = sign(1.0_pReal,traction_d)* prm%sdot0* ( abs(traction_d)/traction_crit & + - abs(traction_d)/prm%critLoad(i))**prm%n if (abs(udotd) > tol_math_check) then Ld = Ld + udotd*projection_d dudotd_dt = udotd*prm%n/traction_d forall (k=1:3,l=1:3,m=1:3,n=1:3) & - dLd_dTstar(k,l,m,n) = dLd_dTstar(k,l,m,n) + & - dudotd_dt*projection_d(k,l)*projection_d(m,n) + dLd_dTstar(k,l,m,n) = dLd_dTstar(k,l,m,n) + dudotd_dt*projection_d(k,l)*projection_d(m,n) endif - udott = sign(1.0_pReal,traction_t)* & - prm%sdot0* & - (abs(traction_t)/traction_crit - & - abs(traction_t)/prm%critLoad(i))**prm%n + udott = sign(1.0_pReal,traction_t)* prm%sdot0* ( abs(traction_t)/traction_crit & + - abs(traction_t)/prm%critLoad(i))**prm%n if (abs(udott) > tol_math_check) then Ld = Ld + udott*projection_t dudott_dt = udott*prm%n/traction_t forall (k=1:3,l=1:3,m=1:3,n=1:3) & - dLd_dTstar(k,l,m,n) = dLd_dTstar(k,l,m,n) + & - dudott_dt*projection_t(k,l)*projection_t(m,n) + dLd_dTstar(k,l,m,n) = dLd_dTstar(k,l,m,n) + dudott_dt*projection_t(k,l)*projection_t(m,n) endif - udotn = & - prm%sdot0* & - (max(0.0_pReal,traction_n)/traction_crit - & - max(0.0_pReal,traction_n)/prm%critLoad(i))**prm%n + udotn = prm%sdot0* ( max(0.0_pReal,traction_n)/traction_crit & + - max(0.0_pReal,traction_n)/prm%critLoad(i))**prm%n if (abs(udotn) > tol_math_check) then Ld = Ld + udotn*projection_n dudotn_dt = udotn*prm%n/traction_n forall (k=1:3,l=1:3,m=1:3,n=1:3) & - dLd_dTstar(k,l,m,n) = dLd_dTstar(k,l,m,n) + & - dudotn_dt*projection_n(k,l)*projection_n(m,n) + dLd_dTstar(k,l,m,n) = dLd_dTstar(k,l,m,n) + dudotn_dt*projection_n(k,l)*projection_n(m,n) endif enddo diff --git a/src/kinematics_thermal_expansion.f90 b/src/kinematics_thermal_expansion.f90 index 814d604ed..7f7994959 100644 --- a/src/kinematics_thermal_expansion.f90 +++ b/src/kinematics_thermal_expansion.f90 @@ -42,7 +42,7 @@ subroutine kinematics_thermal_expansion_init real(pReal), dimension(:), allocatable :: & temp - write(6,'(/,a)') ' <<<+- kinematics_'//KINEMATICS_thermal_expansion_LABEL//' init -+>>>' + write(6,'(/,a)') ' <<<+- kinematics_'//KINEMATICS_thermal_expansion_LABEL//' init -+>>>'; flush(6) Ninstance = count(phase_kinematics == KINEMATICS_thermal_expansion_ID) From 7d2012f492021243e3d8c2bd8f1f51369150c075 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 10 Jan 2020 01:29:35 +0100 Subject: [PATCH 187/223] no need to exclude small values no danger of division by zero --- src/kinematics_slipplane_opening.f90 | 30 +++++++++++----------------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/src/kinematics_slipplane_opening.f90 b/src/kinematics_slipplane_opening.f90 index 018a5b4b5..a736338f9 100644 --- a/src/kinematics_slipplane_opening.f90 +++ b/src/kinematics_slipplane_opening.f90 @@ -146,30 +146,24 @@ subroutine kinematics_slipplane_opening_LiAndItsTangent(Ld, dLd_dTstar, S, ipc, udotd = sign(1.0_pReal,traction_d)* prm%sdot0* ( abs(traction_d)/traction_crit & - abs(traction_d)/prm%critLoad(i))**prm%n - if (abs(udotd) > tol_math_check) then - Ld = Ld + udotd*projection_d - dudotd_dt = udotd*prm%n/traction_d - forall (k=1:3,l=1:3,m=1:3,n=1:3) & - dLd_dTstar(k,l,m,n) = dLd_dTstar(k,l,m,n) + dudotd_dt*projection_d(k,l)*projection_d(m,n) - endif + Ld = Ld + udotd*projection_d + dudotd_dt = udotd*prm%n/traction_d + forall (k=1:3,l=1:3,m=1:3,n=1:3) & + dLd_dTstar(k,l,m,n) = dLd_dTstar(k,l,m,n) + dudotd_dt*projection_d(k,l)*projection_d(m,n) udott = sign(1.0_pReal,traction_t)* prm%sdot0* ( abs(traction_t)/traction_crit & - abs(traction_t)/prm%critLoad(i))**prm%n - if (abs(udott) > tol_math_check) then - Ld = Ld + udott*projection_t - dudott_dt = udott*prm%n/traction_t - forall (k=1:3,l=1:3,m=1:3,n=1:3) & - dLd_dTstar(k,l,m,n) = dLd_dTstar(k,l,m,n) + dudott_dt*projection_t(k,l)*projection_t(m,n) - endif + Ld = Ld + udott*projection_t + dudott_dt = udott*prm%n/traction_t + forall (k=1:3,l=1:3,m=1:3,n=1:3) & + dLd_dTstar(k,l,m,n) = dLd_dTstar(k,l,m,n) + dudott_dt*projection_t(k,l)*projection_t(m,n) udotn = prm%sdot0* ( max(0.0_pReal,traction_n)/traction_crit & - max(0.0_pReal,traction_n)/prm%critLoad(i))**prm%n - if (abs(udotn) > tol_math_check) then - Ld = Ld + udotn*projection_n - dudotn_dt = udotn*prm%n/traction_n - forall (k=1:3,l=1:3,m=1:3,n=1:3) & - dLd_dTstar(k,l,m,n) = dLd_dTstar(k,l,m,n) + dudotn_dt*projection_n(k,l)*projection_n(m,n) - endif + Ld = Ld + udotn*projection_n + dudotn_dt = udotn*prm%n/traction_n + forall (k=1:3,l=1:3,m=1:3,n=1:3) & + dLd_dTstar(k,l,m,n) = dLd_dTstar(k,l,m,n) + dudotn_dt*projection_n(k,l)*projection_n(m,n) enddo end associate From 0771411cd82a66ff7504748a731f1035bf4bf8f3 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 10 Jan 2020 01:33:03 +0100 Subject: [PATCH 188/223] group similar operations --- src/kinematics_slipplane_opening.f90 | 29 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/kinematics_slipplane_opening.f90 b/src/kinematics_slipplane_opening.f90 index a736338f9..2c94448bd 100644 --- a/src/kinematics_slipplane_opening.f90 +++ b/src/kinematics_slipplane_opening.f90 @@ -134,36 +134,35 @@ subroutine kinematics_slipplane_opening_LiAndItsTangent(Ld, dLd_dTstar, S, ipc, dLd_dTstar = 0.0_pReal do i = 1, prm%totalNslip - projection_d = math_outer(prm%slip_direction(1:3,i),prm%slip_normal(1:3,i)) + projection_d = math_outer(prm%slip_direction(1:3,i), prm%slip_normal(1:3,i)) projection_t = math_outer(prm%slip_transverse(1:3,i),prm%slip_normal(1:3,i)) - projection_n = math_outer(prm%slip_normal(1:3,i),prm%slip_normal(1:3,i)) + projection_n = math_outer(prm%slip_normal(1:3,i), prm%slip_normal(1:3,i)) traction_d = math_mul33xx33(S,projection_d) traction_t = math_mul33xx33(S,projection_t) traction_n = math_mul33xx33(S,projection_n) - traction_crit = prm%critLoad(i)* damage(homog)%p(damageOffset) ! degrading critical load carrying capacity by damage + traction_crit = prm%critLoad(i)* damage(homog)%p(damageOffset) ! degrading critical load carrying capacity by damage udotd = sign(1.0_pReal,traction_d)* prm%sdot0* ( abs(traction_d)/traction_crit & - abs(traction_d)/prm%critLoad(i))**prm%n - Ld = Ld + udotd*projection_d - dudotd_dt = udotd*prm%n/traction_d - forall (k=1:3,l=1:3,m=1:3,n=1:3) & - dLd_dTstar(k,l,m,n) = dLd_dTstar(k,l,m,n) + dudotd_dt*projection_d(k,l)*projection_d(m,n) - udott = sign(1.0_pReal,traction_t)* prm%sdot0* ( abs(traction_t)/traction_crit & - abs(traction_t)/prm%critLoad(i))**prm%n - Ld = Ld + udott*projection_t - dudott_dt = udott*prm%n/traction_t - forall (k=1:3,l=1:3,m=1:3,n=1:3) & - dLd_dTstar(k,l,m,n) = dLd_dTstar(k,l,m,n) + dudott_dt*projection_t(k,l)*projection_t(m,n) - udotn = prm%sdot0* ( max(0.0_pReal,traction_n)/traction_crit & - max(0.0_pReal,traction_n)/prm%critLoad(i))**prm%n - Ld = Ld + udotn*projection_n + + dudotd_dt = udotd*prm%n/traction_d + dudott_dt = udott*prm%n/traction_t dudotn_dt = udotn*prm%n/traction_n + forall (k=1:3,l=1:3,m=1:3,n=1:3) & - dLd_dTstar(k,l,m,n) = dLd_dTstar(k,l,m,n) + dudotn_dt*projection_n(k,l)*projection_n(m,n) + dLd_dTstar(k,l,m,n) = dLd_dTstar(k,l,m,n) + dudotd_dt*projection_d(k,l)*projection_d(m,n) & + + dudott_dt*projection_t(k,l)*projection_t(m,n) & + + dudotn_dt*projection_n(k,l)*projection_n(m,n) + + Ld = Ld + udotd*projection_d & + + udott*projection_t & + + udotn*projection_n enddo end associate From 5d2802f6c98eb952b6d5af69d610fc11234d7acd Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 10 Jan 2020 01:36:48 +0100 Subject: [PATCH 189/223] no patches available at the moment --- installation/patch/README.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/installation/patch/README.md b/installation/patch/README.md index 0b8251510..95f377691 100644 --- a/installation/patch/README.md +++ b/installation/patch/README.md @@ -9,14 +9,6 @@ cd DAMASK_ROOT patch -p1 < installation/patch/nameOfPatch ``` -## Available patches - - * **disable_HDF5** disables all HDF5 output. - HDF5 output is an experimental feature. Also, some routines not present in HDF5 1.8.x are removed to allow compilation of DAMASK with HDF5 < 1.10.x - - * **disable_old_output** disables all non-HDF5 output. - Saves some memory when using only HDF5 output - ## Create patch commit your changes From 8f43f054375b4689fbf19389baf343c8efc6a262 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 10 Jan 2020 01:45:00 +0100 Subject: [PATCH 190/223] stronger encapsulation --- src/CPFEM.f90 | 2 +- src/CPFEM2.f90 | 2 +- src/results.f90 | 12 ++++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/CPFEM.f90 b/src/CPFEM.f90 index 3a7f35633..d74155703 100644 --- a/src/CPFEM.f90 +++ b/src/CPFEM.f90 @@ -377,7 +377,7 @@ subroutine CPFEM_results(inc,time) call constitutive_results call crystallite_results call homogenization_results - call results_removeLink('current') ! ToDo: put this into closeJobFile + call results_finalizeIncrement call results_closeJobFile end subroutine CPFEM_results diff --git a/src/CPFEM2.f90 b/src/CPFEM2.f90 index 6b4479b89..bc3a424e3 100644 --- a/src/CPFEM2.f90 +++ b/src/CPFEM2.f90 @@ -201,7 +201,7 @@ subroutine CPFEM_results(inc,time) call crystallite_results call homogenization_results call discretization_results - call results_removeLink('current') ! ToDo: put this into closeJobFile? + call results_finalizeIncrement call results_closeJobFile end subroutine CPFEM_results diff --git a/src/results.f90 b/src/results.f90 index 73c5d7dbb..ca2a868fb 100644 --- a/src/results.f90 +++ b/src/results.f90 @@ -47,6 +47,7 @@ module results results_openJobFile, & results_closeJobFile, & results_addIncrement, & + results_finalizeIncrement, & results_addGroup, & results_openGroup, & results_closeGroup, & @@ -119,6 +120,17 @@ subroutine results_addIncrement(inc,time) end subroutine results_addIncrement +!-------------------------------------------------------------------------------------------------- +!> @brief finalize increment +!> @details remove soft link +!-------------------------------------------------------------------------------------------------- +subroutine results_finalizeIncrement + + call results_removeLink('current') + +end subroutine results_finalizeIncrement + + !-------------------------------------------------------------------------------------------------- !> @brief open a group from the results file !-------------------------------------------------------------------------------------------------- From 87c7a5d5a32fbd810f8d3800f0ed7f43cb12ba80 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 10 Jan 2020 02:11:19 +0100 Subject: [PATCH 191/223] polishing --- src/math.f90 | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/math.f90 b/src/math.f90 index d6d87d30a..62c22b6d8 100644 --- a/src/math.f90 +++ b/src/math.f90 @@ -398,19 +398,19 @@ pure function math_exp33(A,n) real(pReal), dimension(3,3), intent(in) :: A real(pReal), dimension(3,3) :: B, math_exp33 real(pReal) :: invFac - integer :: order - - B = math_I3 ! init - invFac = 1.0_pReal ! 0! - math_exp33 = B ! A^0 = eye2 + integer :: n_ if (present(n)) then - order = n + n_ = n else - order = 5 + n_ = 5 endif - - do i = 1, order + + invFac = 1.0_pReal ! 0! + B = math_I3 + math_exp33 = math_I3 ! A^0 = I + + do i = 1, n_ invFac = invFac/real(i,pReal) ! invfac = 1/(i!) B = matmul(B,A) math_exp33 = math_exp33 + invFac*B ! exp = SUM (A^i)/(i!) @@ -882,16 +882,20 @@ real(pReal) function math_sampleGaussVar(meanvalue, stddev, width) real(pReal), intent(in), optional :: width ! width of considered values as multiples of standard deviation real(pReal), dimension(2) :: rnd ! random numbers real(pReal) :: scatter, & ! normalized scatter around meanvalue - myWidth + width_ if (abs(stddev) < tol_math_check) then math_sampleGaussVar = meanvalue else - myWidth = merge(width,3.0_pReal,present(width)) ! use +-3*sigma as default value for scatter if not given - + if (present(width)) then + width_ = width + else + width_ = 3.0_pReal ! use +-3*sigma as default scatter + endif + do call random_number(rnd) - scatter = myWidth * (2.0_pReal * rnd(1) - 1.0_pReal) + scatter = width_ * (2.0_pReal * rnd(1) - 1.0_pReal) if (rnd(2) <= exp(-0.5_pReal * scatter ** 2.0_pReal)) exit ! test if scattered value is drawn enddo From 115a2552f8f0b2fd3b5eda8b983ae880bacdfc44 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Fri, 10 Jan 2020 03:19:39 +0100 Subject: [PATCH 192/223] 4 newer versions are out --- src/element.f90 | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/element.f90 b/src/element.f90 index 02f5fb762..3a1e3f5a3 100644 --- a/src/element.f90 +++ b/src/element.f90 @@ -39,7 +39,7 @@ module element integer, parameter, private :: & NELEMTYPE = 13 - integer, dimension(NelemType), parameter, private :: NNODE = & + integer, dimension(NELEMTYPE), parameter, private :: NNODE = & [ & 3, & ! 2D 3node 1ip 6, & ! 2D 6node 3ip @@ -57,7 +57,7 @@ module element 20 & ! 3D 20node 27ip ] !< number of nodes that constitute a specific type of element - integer, dimension(NelemType), parameter, public :: GEOMTYPE = & + integer, dimension(NELEMTYPE), parameter, public :: GEOMTYPE = & [ & 1, & 2, & @@ -74,8 +74,7 @@ module element 10 & ] !< geometry type of particular element type - !integer, dimension(maxval(geomType)), parameter, private :: NCELLNODE = & ! Intel 16.0 complains - integer, dimension(10), parameter, private :: NCELLNODE = & + integer, dimension(maxval(GEOMTYPE)), parameter, private :: NCELLNODE = & [ & 3, & 7, & @@ -89,8 +88,7 @@ module element 64 & ] !< number of cell nodes in a specific geometry type - !integer, dimension(maxval(geomType)), parameter, private :: NIP = & ! Intel 16.0 complains - integer, dimension(10), parameter, private :: NIP = & + integer, dimension(maxval(GEOMTYPE)), parameter, private :: NIP = & [ & 1, & 3, & @@ -104,8 +102,7 @@ module element 27 & ] !< number of IPs in a specific geometry type - !integer, dimension(maxval(geomType)), parameter, private :: CELLTYPE = & ! Intel 16.0 complains - integer, dimension(10), parameter, private :: CELLTYPE = & + integer, dimension(maxval(GEOMTYPE)), parameter, private :: CELLTYPE = & [ & 1, & ! 2D 3node 2, & ! 2D 4node @@ -119,8 +116,7 @@ module element 4 & ! 3D 8node ] !< cell type that is used by each geometry type - !integer, dimension(maxval(cellType)), parameter, private :: nIPNeighbor = & ! Intel 16.0 complains - integer, dimension(4), parameter, private :: NIPNEIGHBOR = & + integer, dimension(maxval(CELLTYPE)), parameter, private :: NIPNEIGHBOR = & [ & 3, & ! 2D 3node 4, & ! 2D 4node @@ -128,8 +124,7 @@ module element 6 & ! 3D 8node ] !< number of ip neighbors / cell faces in a specific cell type - !integer, dimension(maxval(cellType)), parameter, private :: NCELLNODESPERCELLFACE = & ! Intel 16.0 complains - integer, dimension(4), parameter, private :: NCELLNODEPERCELLFACE = & + integer, dimension(maxval(CELLTYPE)), parameter, private :: NCELLNODEPERCELLFACE = & [ & 2, & ! 2D 3node 2, & ! 2D 4node @@ -137,8 +132,7 @@ module element 4 & ! 3D 8node ] !< number of cell nodes in a specific cell type - !integer, dimension(maxval(CELLTYPE)), parameter, private :: NCELLNODEPERCELL = & ! Intel 16.0 complains - integer, dimension(4), parameter, private :: NCELLNODEPERCELL = & + integer, dimension(maxval(CELLTYPE)), parameter, private :: NCELLNODEPERCELL = & [ & 3, & ! 2D 3node 4, & ! 2D 4node From 3a08a8bbe27ec3b6b74bbda5516ec97747f5b6ed Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Fri, 10 Jan 2020 12:07:30 -0500 Subject: [PATCH 193/223] always using intrinsic init when assigning quaternions as output variables --- src/quaternions.f90 | 68 +++++++++++++++++---------------------------- 1 file changed, 26 insertions(+), 42 deletions(-) diff --git a/src/quaternions.f90 b/src/quaternions.f90 index 8efb985ed..e55d3804e 100644 --- a/src/quaternions.f90 +++ b/src/quaternions.f90 @@ -142,32 +142,29 @@ type(quaternion) pure function init__(array) real(pReal), intent(in), dimension(4) :: array - init__%w=array(1) - init__%x=array(2) - init__%y=array(3) - init__%z=array(4) + init__%w = array(1) + init__%x = array(2) + init__%y = array(3) + init__%z = array(4) end function init__ !--------------------------------------------------------------------------------------------------- -!> assing a quaternion +!> assigning a quaternion !--------------------------------------------------------------------------------------------------- elemental pure subroutine assign_quat__(self,other) type(quaternion), intent(out) :: self type(quaternion), intent(in) :: other - self%w = other%w - self%x = other%x - self%y = other%y - self%z = other%z - + self = [other%w,other%x,other%y,other%z] + end subroutine assign_quat__ !--------------------------------------------------------------------------------------------------- -!> assing a 4-vector +!> assigning a 4-vector !--------------------------------------------------------------------------------------------------- pure subroutine assign_vec__(self,other) @@ -189,11 +186,8 @@ type(quaternion) elemental pure function add__(self,other) class(quaternion), intent(in) :: self,other - add__%w = self%w + other%w - add__%x = self%x + other%x - add__%y = self%y + other%y - add__%z = self%z + other%z - + add__ = [self%w + other%w,self%x + other%x,self%y + other%y,self%z + other%z] + end function add__ @@ -204,11 +198,8 @@ type(quaternion) elemental pure function pos__(self) class(quaternion), intent(in) :: self - pos__%w = self%w - pos__%x = self%x - pos__%y = self%y - pos__%z = self%z - + pos__ = [self%w,self%x,self%y,self%z] + end function pos__ @@ -219,26 +210,20 @@ type(quaternion) elemental pure function sub__(self,other) class(quaternion), intent(in) :: self,other - sub__%w = self%w - other%w - sub__%x = self%x - other%x - sub__%y = self%y - other%y - sub__%z = self%z - other%z - + sub__ = [self%w - other%w,self%x - other%x,self%y - other%y,self%z - other%z] + end function sub__ !--------------------------------------------------------------------------------------------------- -!> unary positive operator +!> unary negative operator !--------------------------------------------------------------------------------------------------- type(quaternion) elemental pure function neg__(self) class(quaternion), intent(in) :: self - neg__%w = -self%w - neg__%x = -self%x - neg__%y = -self%y - neg__%z = -self%z - + neg__ = [-self%w,-self%x,-self%y,-self%z] + end function neg__ @@ -258,18 +243,15 @@ end function mul_quat__ !--------------------------------------------------------------------------------------------------- -!> multiplication of quaternions with scalar +!> multiplication of quaternion with scalar !--------------------------------------------------------------------------------------------------- type(quaternion) elemental pure function mul_scal__(self,scal) class(quaternion), intent(in) :: self real(pReal), intent(in) :: scal - mul_scal__%w = self%w*scal - mul_scal__%x = self%x*scal - mul_scal__%y = self%y*scal - mul_scal__%z = self%z*scal - + mul_scal__ = [self%w,self%x,self%y,self%z]*scal + end function mul_scal__ @@ -418,7 +400,7 @@ type(quaternion) elemental pure function conjg__(a) class(quaternion), intent(in) :: a - conjg__ = quaternion([a%w, -a%x, -a%y, -a%z]) + conjg__ = [a%w, -a%x, -a%y, -a%z] end function conjg__ @@ -430,7 +412,7 @@ type(quaternion) elemental pure function quat_homomorphed(self) class(quaternion), intent(in) :: self - quat_homomorphed = quaternion(-[self%w,self%x,self%y,self%z]) + quat_homomorphed = -[self%w,self%x,self%y,self%z] end function quat_homomorphed @@ -444,6 +426,7 @@ pure function asArray(self) class(quaternion), intent(in) :: self asArray = [self%w,self%x,self%y,self%z] + if (self%w < 0) asArray = -asArray end function asArray @@ -484,6 +467,7 @@ subroutine unitTest type(quaternion) :: q, q_2 call random_number(qu) + if (qu(1) < 0.0_pReal) qu = -qu q = qu q_2 = q + q @@ -492,10 +476,10 @@ subroutine unitTest q_2 = q - q if(any(dNeq0(q_2%asArray()))) call IO_error(401,ext_msg='sub__') - q_2 = q * 5.0_preal + q_2 = q * 5.0_pReal if(any(dNeq(q_2%asArray(),5.0_pReal*qu))) call IO_error(401,ext_msg='mul__') - q_2 = q / 0.5_preal + q_2 = q / 0.5_pReal if(any(dNeq(q_2%asArray(),2.0_pReal*qu))) call IO_error(401,ext_msg='div__') q_2 = q From 79cafebffe5ae92c029ab8af987c8401ec3ec8f1 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 11 Jan 2020 03:08:39 +0100 Subject: [PATCH 194/223] following https://www.python.org/dev/peps/pep-0257/ --- src/quaternions.f90 | 106 ++++++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 49 deletions(-) diff --git a/src/quaternions.f90 b/src/quaternions.f90 index e55d3804e..3df230e31 100644 --- a/src/quaternions.f90 +++ b/src/quaternions.f90 @@ -31,7 +31,8 @@ !> @author Marc De Graef, Carnegie Mellon University !> @author Martin Diehl, Max-Planck-Institut für Eisenforschung GmbH !> @brief general quaternion math, not limited to unit quaternions -!> @details w is the real part, (x, y, z) are the imaginary parts. +!> @details w is the real part, (x, y, z) are the imaginary parts. +!> @details https://users.aalto.fi/~ssarkka/pub/quat.pdf !--------------------------------------------------------------------------------------------------- module quaternions use prec @@ -117,6 +118,14 @@ module quaternions interface log module procedure log__ end interface log + + interface real + module procedure real__ + end interface real + + interface aimag + module procedure aimag__ + end interface aimag private :: & unitTest @@ -125,18 +134,18 @@ contains !-------------------------------------------------------------------------------------------------- -!> @brief doing self test +!> @brief do self test !-------------------------------------------------------------------------------------------------- subroutine quaternions_init - write(6,'(/,a)') ' <<<+- quaternions init -+>>>' + write(6,'(/,a)') ' <<<+- quaternions init -+>>>'; flush(6) call unitTest end subroutine quaternions_init !--------------------------------------------------------------------------------------------------- -!> constructor for a quaternion from a 4-vector +!> construct a quaternion from a 4-vector !--------------------------------------------------------------------------------------------------- type(quaternion) pure function init__(array) @@ -151,7 +160,7 @@ end function init__ !--------------------------------------------------------------------------------------------------- -!> assigning a quaternion +!> assign a quaternion !--------------------------------------------------------------------------------------------------- elemental pure subroutine assign_quat__(self,other) @@ -164,7 +173,7 @@ end subroutine assign_quat__ !--------------------------------------------------------------------------------------------------- -!> assigning a 4-vector +!> assign a 4-vector !--------------------------------------------------------------------------------------------------- pure subroutine assign_vec__(self,other) @@ -180,7 +189,7 @@ end subroutine assign_vec__ !--------------------------------------------------------------------------------------------------- -!> addition of two quaternions +!> add a quaternion !--------------------------------------------------------------------------------------------------- type(quaternion) elemental pure function add__(self,other) @@ -192,7 +201,7 @@ end function add__ !--------------------------------------------------------------------------------------------------- -!> unary positive operator +!> return (unary positive operator) !--------------------------------------------------------------------------------------------------- type(quaternion) elemental pure function pos__(self) @@ -204,7 +213,7 @@ end function pos__ !--------------------------------------------------------------------------------------------------- -!> subtraction of two quaternions +!> subtract a quaternion !--------------------------------------------------------------------------------------------------- type(quaternion) elemental pure function sub__(self,other) @@ -216,7 +225,7 @@ end function sub__ !--------------------------------------------------------------------------------------------------- -!> unary negative operator +!> negate (unary negative operator) !--------------------------------------------------------------------------------------------------- type(quaternion) elemental pure function neg__(self) @@ -228,7 +237,7 @@ end function neg__ !--------------------------------------------------------------------------------------------------- -!> multiplication of two quaternions +!> multiply with a quaternion !--------------------------------------------------------------------------------------------------- type(quaternion) elemental pure function mul_quat__(self,other) @@ -243,7 +252,7 @@ end function mul_quat__ !--------------------------------------------------------------------------------------------------- -!> multiplication of quaternion with scalar +!> multiply with a scalar !--------------------------------------------------------------------------------------------------- type(quaternion) elemental pure function mul_scal__(self,scal) @@ -256,7 +265,7 @@ end function mul_scal__ !--------------------------------------------------------------------------------------------------- -!> division of two quaternions +!> divide by a quaternion !--------------------------------------------------------------------------------------------------- type(quaternion) elemental pure function div_quat__(self,other) @@ -268,7 +277,7 @@ end function div_quat__ !--------------------------------------------------------------------------------------------------- -!> divisiont of quaternions by scalar +!> divide by a scalar !--------------------------------------------------------------------------------------------------- type(quaternion) elemental pure function div_scal__(self,scal) @@ -281,7 +290,7 @@ end function div_scal__ !--------------------------------------------------------------------------------------------------- -!> equality of two quaternions +!> test equality !--------------------------------------------------------------------------------------------------- logical elemental pure function eq__(self,other) @@ -294,7 +303,7 @@ end function eq__ !--------------------------------------------------------------------------------------------------- -!> inequality of two quaternions +!> test inequality !--------------------------------------------------------------------------------------------------- logical elemental pure function neq__(self,other) @@ -306,20 +315,7 @@ end function neq__ !--------------------------------------------------------------------------------------------------- -!> quaternion to the power of a scalar -!--------------------------------------------------------------------------------------------------- -type(quaternion) elemental pure function pow_scal__(self,expon) - - class(quaternion), intent(in) :: self - real(pReal), intent(in) :: expon - - pow_scal__ = exp(log(self)*expon) - -end function pow_scal__ - - -!--------------------------------------------------------------------------------------------------- -!> quaternion to the power of a quaternion +!> raise to the power of a quaternion !--------------------------------------------------------------------------------------------------- type(quaternion) elemental pure function pow_quat__(self,expon) @@ -332,7 +328,20 @@ end function pow_quat__ !--------------------------------------------------------------------------------------------------- -!> exponential of a quaternion +!> raise to the power of a scalar +!--------------------------------------------------------------------------------------------------- +type(quaternion) elemental pure function pow_scal__(self,expon) + + class(quaternion), intent(in) :: self + real(pReal), intent(in) :: expon + + pow_scal__ = exp(log(self)*expon) + +end function pow_scal__ + + +!--------------------------------------------------------------------------------------------------- +!> take exponential !> ToDo: Lacks any check for invalid operations !--------------------------------------------------------------------------------------------------- type(quaternion) elemental pure function exp__(self) @@ -340,7 +349,7 @@ type(quaternion) elemental pure function exp__(self) class(quaternion), intent(in) :: self real(pReal) :: absImag - absImag = norm2([self%x, self%y, self%z]) + absImag = norm2(aimag(self)) exp__ = exp(self%w) * [ cos(absImag), & self%x/absImag * sin(absImag), & @@ -351,7 +360,7 @@ end function exp__ !--------------------------------------------------------------------------------------------------- -!> logarithm of a quaternion +!> take logarithm !> ToDo: Lacks any check for invalid operations !--------------------------------------------------------------------------------------------------- type(quaternion) elemental pure function log__(self) @@ -359,7 +368,7 @@ type(quaternion) elemental pure function log__(self) class(quaternion), intent(in) :: self real(pReal) :: absImag - absImag = norm2([self%x, self%y, self%z]) + absImag = norm2(aimag(self)) log__ = [log(abs(self)), & self%x/absImag * acos(self%w/abs(self)), & @@ -370,7 +379,7 @@ end function log__ !--------------------------------------------------------------------------------------------------- -!> norm of a quaternion +!> return norm !--------------------------------------------------------------------------------------------------- real(pReal) elemental pure function abs__(a) @@ -382,7 +391,7 @@ end function abs__ !--------------------------------------------------------------------------------------------------- -!> dot product of two quaternions +!> calculate dot product !--------------------------------------------------------------------------------------------------- real(pReal) elemental pure function dot_product__(a,b) @@ -394,7 +403,7 @@ end function dot_product__ !--------------------------------------------------------------------------------------------------- -!> conjugate complex of a quaternion +!> take conjugate complex !--------------------------------------------------------------------------------------------------- type(quaternion) elemental pure function conjg__(a) @@ -406,7 +415,7 @@ end function conjg__ !--------------------------------------------------------------------------------------------------- -!> homomorphed quaternion of a quaternion +!> homomorph !--------------------------------------------------------------------------------------------------- type(quaternion) elemental pure function quat_homomorphed(self) @@ -418,7 +427,7 @@ end function quat_homomorphed !--------------------------------------------------------------------------------------------------- -!> quaternion as plain array +!> return as plain array !--------------------------------------------------------------------------------------------------- pure function asArray(self) @@ -432,7 +441,7 @@ end function asArray !--------------------------------------------------------------------------------------------------- -!> real part of a quaternion +!> real part (scalar) !--------------------------------------------------------------------------------------------------- pure function real__(self) @@ -445,7 +454,7 @@ end function real__ !--------------------------------------------------------------------------------------------------- -!> imaginary part of a quaternion +!> imaginary part (3-vector) !--------------------------------------------------------------------------------------------------- pure function aimag__(self) @@ -463,37 +472,36 @@ end function aimag__ subroutine unitTest real(pReal), dimension(4) :: qu - type(quaternion) :: q, q_2 call random_number(qu) if (qu(1) < 0.0_pReal) qu = -qu q = qu - + q_2 = q + q if(any(dNeq(q_2%asArray(),2.0_pReal*qu))) call IO_error(401,ext_msg='add__') - + q_2 = q - q if(any(dNeq0(q_2%asArray()))) call IO_error(401,ext_msg='sub__') - + q_2 = q * 5.0_pReal if(any(dNeq(q_2%asArray(),5.0_pReal*qu))) call IO_error(401,ext_msg='mul__') - + q_2 = q / 0.5_pReal if(any(dNeq(q_2%asArray(),2.0_pReal*qu))) call IO_error(401,ext_msg='div__') - + q_2 = q if(q_2 /= q) call IO_error(401,ext_msg='eq__') if(any(dNeq(q%asArray(),qu))) call IO_error(401,ext_msg='eq__') if(dNeq(q%real(), qu(1))) call IO_error(401,ext_msg='real()') if(any(dNeq(q%aimag(), qu(2:4)))) call IO_error(401,ext_msg='aimag()') - + q_2 = q%homomorphed() if(q /= q_2* (-1.0_pReal)) call IO_error(401,ext_msg='homomorphed') if(dNeq(q_2%real(), qu(1)* (-1.0_pReal))) call IO_error(401,ext_msg='homomorphed/real') if(any(dNeq(q_2%aimag(),qu(2:4)*(-1.0_pReal)))) call IO_error(401,ext_msg='homomorphed/aimag') - + q_2 = conjg(q) if(dNeq(q_2%real(), q%real())) call IO_error(401,ext_msg='conjg/real') if(any(dNeq(q_2%aimag(),q%aimag()*(-1.0_pReal)))) call IO_error(401,ext_msg='conjg/aimag') From aefd401e8cdda0ad4164f09728595f4c2467d7b1 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 11 Jan 2020 03:11:45 +0100 Subject: [PATCH 195/223] this is a quaternion class it is meant to represent any quaternion, not only unit quaternions/rotations that follow a specific convention. Need to check in rotations.f90 where the homomorph should happen --- src/quaternions.f90 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/quaternions.f90 b/src/quaternions.f90 index 3df230e31..9a14fa211 100644 --- a/src/quaternions.f90 +++ b/src/quaternions.f90 @@ -435,7 +435,6 @@ pure function asArray(self) class(quaternion), intent(in) :: self asArray = [self%w,self%x,self%y,self%z] - if (self%w < 0) asArray = -asArray end function asArray @@ -475,7 +474,7 @@ subroutine unitTest type(quaternion) :: q, q_2 call random_number(qu) - if (qu(1) < 0.0_pReal) qu = -qu + qu = (qu-0.5_pReal) * 2.0_pReal q = qu q_2 = q + q From c7180c329571a952b37bca68953910c82588ef79 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 11 Jan 2020 03:50:17 +0100 Subject: [PATCH 196/223] some more tests for quaternion operations --- src/quaternions.f90 | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/quaternions.f90 b/src/quaternions.f90 index 9a14fa211..252135eec 100644 --- a/src/quaternions.f90 +++ b/src/quaternions.f90 @@ -475,7 +475,10 @@ subroutine unitTest call random_number(qu) qu = (qu-0.5_pReal) * 2.0_pReal - q = qu + q = quaternion(qu) + + q_2= qu + if(any(dNeq(q%asArray(),q_2%asArray()))) call IO_error(401,ext_msg='assign_vec__') q_2 = q + q if(any(dNeq(q_2%asArray(),2.0_pReal*qu))) call IO_error(401,ext_msg='add__') @@ -489,8 +492,13 @@ subroutine unitTest q_2 = q / 0.5_pReal if(any(dNeq(q_2%asArray(),2.0_pReal*qu))) call IO_error(401,ext_msg='div__') + q_2 = q * 0.3_pReal + if(dNeq0(abs(q)) .and. q_2 == q) call IO_error(401,ext_msg='eq__') + q_2 = q - if(q_2 /= q) call IO_error(401,ext_msg='eq__') + if(q_2 /= q) call IO_error(401,ext_msg='neq__') + + if(dNeq(abs(q),norm2(qu))) call IO_error(401,ext_msg='abs__') if(any(dNeq(q%asArray(),qu))) call IO_error(401,ext_msg='eq__') if(dNeq(q%real(), qu(1))) call IO_error(401,ext_msg='real()') @@ -504,6 +512,11 @@ subroutine unitTest q_2 = conjg(q) if(dNeq(q_2%real(), q%real())) call IO_error(401,ext_msg='conjg/real') if(any(dNeq(q_2%aimag(),q%aimag()*(-1.0_pReal)))) call IO_error(401,ext_msg='conjg/aimag') + + if (norm2(aimag(q)) * abs(real(q)) > 0.0_pReal) then + if (dNeq0(abs(q-exp(log(q))),1.0e-12_pReal)) call IO_error(401,ext_msg='exp/log') + if (dNeq0(abs(q-log(exp(q))),1.0e-12_pReal)) call IO_error(401,ext_msg='log/exp') + endif end subroutine unitTest From 115716b8c21df19868bc5f52c53c0ba2ce38724f Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 11 Jan 2020 03:58:12 +0100 Subject: [PATCH 197/223] polishing/use existing functions --- src/quaternions.f90 | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/quaternions.f90 b/src/quaternions.f90 index 252135eec..a49f53007 100644 --- a/src/quaternions.f90 +++ b/src/quaternions.f90 @@ -195,7 +195,8 @@ type(quaternion) elemental pure function add__(self,other) class(quaternion), intent(in) :: self,other - add__ = [self%w + other%w,self%x + other%x,self%y + other%y,self%z + other%z] + add__ = [ self%w, self%x, self%y ,self%z] & + + [other%w, other%x, other%y,other%z] end function add__ @@ -207,7 +208,7 @@ type(quaternion) elemental pure function pos__(self) class(quaternion), intent(in) :: self - pos__ = [self%w,self%x,self%y,self%z] + pos__ = self * (+1.0_pReal) end function pos__ @@ -219,7 +220,8 @@ type(quaternion) elemental pure function sub__(self,other) class(quaternion), intent(in) :: self,other - sub__ = [self%w - other%w,self%x - other%x,self%y - other%y,self%z - other%z] + sub__ = [ self%w, self%x, self%y ,self%z] & + - [other%w, other%x, other%y,other%z] end function sub__ @@ -231,7 +233,7 @@ type(quaternion) elemental pure function neg__(self) class(quaternion), intent(in) :: self - neg__ = [-self%w,-self%x,-self%y,-self%z] + neg__ = self * (-1.0_pReal) end function neg__ @@ -333,7 +335,7 @@ end function pow_quat__ type(quaternion) elemental pure function pow_scal__(self,expon) class(quaternion), intent(in) :: self - real(pReal), intent(in) :: expon + real(pReal), intent(in) :: expon pow_scal__ = exp(log(self)*expon) @@ -421,7 +423,7 @@ type(quaternion) elemental pure function quat_homomorphed(self) class(quaternion), intent(in) :: self - quat_homomorphed = -[self%w,self%x,self%y,self%z] + quat_homomorphed = - self end function quat_homomorphed From de95ca590608c150f081e8fa4d9f247feb188118 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 11 Jan 2020 04:15:51 +0100 Subject: [PATCH 198/223] inverse of a quaternion --- src/quaternions.f90 | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/src/quaternions.f90 b/src/quaternions.f90 index a49f53007..0ce7765e6 100644 --- a/src/quaternions.f90 +++ b/src/quaternions.f90 @@ -82,12 +82,13 @@ module quaternions procedure, public :: conjg__ procedure, public :: exp__ procedure, public :: log__ - - procedure, public :: homomorphed => quat_homomorphed - procedure, public :: asArray procedure, public :: real => real__ procedure, public :: aimag => aimag__ + procedure, public :: homomorphed + procedure, public :: asArray + procedure, public :: inverse + end type interface assignment (=) @@ -137,7 +138,6 @@ contains !> @brief do self test !-------------------------------------------------------------------------------------------------- subroutine quaternions_init - write(6,'(/,a)') ' <<<+- quaternions init -+>>>'; flush(6) call unitTest @@ -419,13 +419,13 @@ end function conjg__ !--------------------------------------------------------------------------------------------------- !> homomorph !--------------------------------------------------------------------------------------------------- -type(quaternion) elemental pure function quat_homomorphed(self) +type(quaternion) elemental pure function homomorphed(self) class(quaternion), intent(in) :: self - quat_homomorphed = - self + homomorphed = - self -end function quat_homomorphed +end function homomorphed !--------------------------------------------------------------------------------------------------- @@ -467,6 +467,18 @@ pure function aimag__(self) end function aimag__ +!--------------------------------------------------------------------------------------------------- +!> inverse +!--------------------------------------------------------------------------------------------------- +type(quaternion) elemental pure function inverse(self) + + class(quaternion), intent(in) :: self + + inverse = conjg(self)/abs(self)**2.0_pReal + +end function inverse + + !-------------------------------------------------------------------------------------------------- !> @brief check correctness of (some) quaternions functions !-------------------------------------------------------------------------------------------------- @@ -478,7 +490,7 @@ subroutine unitTest call random_number(qu) qu = (qu-0.5_pReal) * 2.0_pReal q = quaternion(qu) - + q_2= qu if(any(dNeq(q%asArray(),q_2%asArray()))) call IO_error(401,ext_msg='assign_vec__') @@ -515,9 +527,15 @@ subroutine unitTest if(dNeq(q_2%real(), q%real())) call IO_error(401,ext_msg='conjg/real') if(any(dNeq(q_2%aimag(),q%aimag()*(-1.0_pReal)))) call IO_error(401,ext_msg='conjg/aimag') + if(abs(q) > 0.0_pReal) then + q_2 = q * q%inverse() + if( dNeq(real(q_2), 1.0_pReal,1.0e-15_pReal)) call IO_error(401,ext_msg='inverse/real') + if(any(dNeq0(aimag(q_2), 1.0e-15_pReal))) call IO_error(401,ext_msg='inverse/aimag') + endif + if (norm2(aimag(q)) * abs(real(q)) > 0.0_pReal) then - if (dNeq0(abs(q-exp(log(q))),1.0e-12_pReal)) call IO_error(401,ext_msg='exp/log') - if (dNeq0(abs(q-log(exp(q))),1.0e-12_pReal)) call IO_error(401,ext_msg='log/exp') + if (dNeq0(abs(q-exp(log(q))),1.0e-13_pReal)) call IO_error(401,ext_msg='exp/log') + if (dNeq0(abs(q-log(exp(q))),1.0e-13_pReal)) call IO_error(401,ext_msg='log/exp') endif end subroutine unitTest From f02851959765844654bd43265cfd34b1c5cc6a6a Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 11 Jan 2020 04:44:30 +0100 Subject: [PATCH 199/223] some facts from wikipedia as tests --- src/quaternions.f90 | 50 ++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/src/quaternions.f90 b/src/quaternions.f90 index 0ce7765e6..d474f3994 100644 --- a/src/quaternions.f90 +++ b/src/quaternions.f90 @@ -79,9 +79,9 @@ module quaternions procedure, public :: abs__ procedure, public :: dot_product__ - procedure, public :: conjg__ procedure, public :: exp__ procedure, public :: log__ + procedure, public :: conjg => conjg__ procedure, public :: real => real__ procedure, public :: aimag => aimag__ @@ -138,6 +138,7 @@ contains !> @brief do self test !-------------------------------------------------------------------------------------------------- subroutine quaternions_init + write(6,'(/,a)') ' <<<+- quaternions init -+>>>'; flush(6) call unitTest @@ -259,7 +260,7 @@ end function mul_quat__ type(quaternion) elemental pure function mul_scal__(self,scal) class(quaternion), intent(in) :: self - real(pReal), intent(in) :: scal + real(pReal), intent(in) :: scal mul_scal__ = [self%w,self%x,self%y,self%z]*scal @@ -284,7 +285,7 @@ end function div_quat__ type(quaternion) elemental pure function div_scal__(self,scal) class(quaternion), intent(in) :: self - real(pReal), intent(in) :: scal + real(pReal), intent(in) :: scal div_scal__ = [self%w,self%x,self%y,self%z]/scal @@ -492,50 +493,57 @@ subroutine unitTest q = quaternion(qu) q_2= qu - if(any(dNeq(q%asArray(),q_2%asArray()))) call IO_error(401,ext_msg='assign_vec__') + if(any(dNeq(q%asArray(),q_2%asArray()))) call IO_error(401,ext_msg='assign_vec__') q_2 = q + q - if(any(dNeq(q_2%asArray(),2.0_pReal*qu))) call IO_error(401,ext_msg='add__') + if(any(dNeq(q_2%asArray(),2.0_pReal*qu))) call IO_error(401,ext_msg='add__') q_2 = q - q - if(any(dNeq0(q_2%asArray()))) call IO_error(401,ext_msg='sub__') + if(any(dNeq0(q_2%asArray()))) call IO_error(401,ext_msg='sub__') q_2 = q * 5.0_pReal - if(any(dNeq(q_2%asArray(),5.0_pReal*qu))) call IO_error(401,ext_msg='mul__') + if(any(dNeq(q_2%asArray(),5.0_pReal*qu))) call IO_error(401,ext_msg='mul__') q_2 = q / 0.5_pReal - if(any(dNeq(q_2%asArray(),2.0_pReal*qu))) call IO_error(401,ext_msg='div__') + if(any(dNeq(q_2%asArray(),2.0_pReal*qu))) call IO_error(401,ext_msg='div__') q_2 = q * 0.3_pReal - if(dNeq0(abs(q)) .and. q_2 == q) call IO_error(401,ext_msg='eq__') + if(dNeq0(abs(q)) .and. q_2 == q) call IO_error(401,ext_msg='eq__') q_2 = q - if(q_2 /= q) call IO_error(401,ext_msg='neq__') + if(q_2 /= q) call IO_error(401,ext_msg='neq__') - if(dNeq(abs(q),norm2(qu))) call IO_error(401,ext_msg='abs__') + if(dNeq(abs(q),norm2(qu))) call IO_error(401,ext_msg='abs__') + if(dNeq(abs(q)**2.0_pReal, real(q*q%conjg()))) call IO_error(401,ext_msg='abs__/*conjg') - if(any(dNeq(q%asArray(),qu))) call IO_error(401,ext_msg='eq__') - if(dNeq(q%real(), qu(1))) call IO_error(401,ext_msg='real()') - if(any(dNeq(q%aimag(), qu(2:4)))) call IO_error(401,ext_msg='aimag()') + if(any(dNeq(q%asArray(),qu))) call IO_error(401,ext_msg='eq__') + if(dNeq(q%real(), qu(1))) call IO_error(401,ext_msg='real()') + if(any(dNeq(q%aimag(), qu(2:4)))) call IO_error(401,ext_msg='aimag()') q_2 = q%homomorphed() - if(q /= q_2* (-1.0_pReal)) call IO_error(401,ext_msg='homomorphed') - if(dNeq(q_2%real(), qu(1)* (-1.0_pReal))) call IO_error(401,ext_msg='homomorphed/real') - if(any(dNeq(q_2%aimag(),qu(2:4)*(-1.0_pReal)))) call IO_error(401,ext_msg='homomorphed/aimag') + if(q /= q_2* (-1.0_pReal)) call IO_error(401,ext_msg='homomorphed') + if(dNeq(q_2%real(), qu(1)* (-1.0_pReal))) call IO_error(401,ext_msg='homomorphed/real') + if(any(dNeq(q_2%aimag(),qu(2:4)*(-1.0_pReal)))) call IO_error(401,ext_msg='homomorphed/aimag') q_2 = conjg(q) - if(dNeq(q_2%real(), q%real())) call IO_error(401,ext_msg='conjg/real') - if(any(dNeq(q_2%aimag(),q%aimag()*(-1.0_pReal)))) call IO_error(401,ext_msg='conjg/aimag') + if(dNeq(abs(q),abs(q_2))) call IO_error(401,ext_msg='conjg/abs') + if(q /= conjg(q_2)) call IO_error(401,ext_msg='conjg/involution') + if(dNeq(q_2%real(), q%real())) call IO_error(401,ext_msg='conjg/real') + if(any(dNeq(q_2%aimag(),q%aimag()*(-1.0_pReal)))) call IO_error(401,ext_msg='conjg/aimag') if(abs(q) > 0.0_pReal) then q_2 = q * q%inverse() if( dNeq(real(q_2), 1.0_pReal,1.0e-15_pReal)) call IO_error(401,ext_msg='inverse/real') if(any(dNeq0(aimag(q_2), 1.0e-15_pReal))) call IO_error(401,ext_msg='inverse/aimag') + + q_2 = q/abs(q) + q_2 = conjg(q_2) - inverse(q_2) + if(any(dNeq0(q_2%asArray(),1.0e-15_pReal))) call IO_error(401,ext_msg='inverse/conjg') endif if (norm2(aimag(q)) * abs(real(q)) > 0.0_pReal) then - if (dNeq0(abs(q-exp(log(q))),1.0e-13_pReal)) call IO_error(401,ext_msg='exp/log') - if (dNeq0(abs(q-log(exp(q))),1.0e-13_pReal)) call IO_error(401,ext_msg='log/exp') + if (dNeq0(abs(q-exp(log(q))),1.0e-13_pReal)) call IO_error(401,ext_msg='exp/log') + if (dNeq0(abs(q-log(exp(q))),1.0e-13_pReal)) call IO_error(401,ext_msg='log/exp') endif end subroutine unitTest From 3a6819f548897d20a04a1df5498b764ee79d5413 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 11 Jan 2020 05:14:17 +0100 Subject: [PATCH 200/223] check for invalid operations --- src/quaternions.f90 | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/quaternions.f90 b/src/quaternions.f90 index d474f3994..bdd44b93b 100644 --- a/src/quaternions.f90 +++ b/src/quaternions.f90 @@ -345,7 +345,6 @@ end function pow_scal__ !--------------------------------------------------------------------------------------------------- !> take exponential -!> ToDo: Lacks any check for invalid operations !--------------------------------------------------------------------------------------------------- type(quaternion) elemental pure function exp__(self) @@ -354,17 +353,18 @@ type(quaternion) elemental pure function exp__(self) absImag = norm2(aimag(self)) - exp__ = exp(self%w) * [ cos(absImag), & - self%x/absImag * sin(absImag), & - self%y/absImag * sin(absImag), & - self%z/absImag * sin(absImag)] + exp__ = merge(exp(self%w) * [ cos(absImag), & + self%x/absImag * sin(absImag), & + self%y/absImag * sin(absImag), & + self%z/absImag * sin(absImag)], & + IEEE_value(1.0_pReal,IEEE_SIGNALING_NAN), & + dNeq0(absImag)) end function exp__ !--------------------------------------------------------------------------------------------------- !> take logarithm -!> ToDo: Lacks any check for invalid operations !--------------------------------------------------------------------------------------------------- type(quaternion) elemental pure function log__(self) @@ -373,10 +373,12 @@ type(quaternion) elemental pure function log__(self) absImag = norm2(aimag(self)) - log__ = [log(abs(self)), & - self%x/absImag * acos(self%w/abs(self)), & - self%y/absImag * acos(self%w/abs(self)), & - self%z/absImag * acos(self%w/abs(self))] + log__ = merge([log(abs(self)), & + self%x/absImag * acos(self%w/abs(self)), & + self%y/absImag * acos(self%w/abs(self)), & + self%z/absImag * acos(self%w/abs(self))], & + IEEE_value(1.0_pReal,IEEE_SIGNALING_NAN), & + dNeq0(absImag)) end function log__ @@ -541,7 +543,7 @@ subroutine unitTest if(any(dNeq0(q_2%asArray(),1.0e-15_pReal))) call IO_error(401,ext_msg='inverse/conjg') endif - if (norm2(aimag(q)) * abs(real(q)) > 0.0_pReal) then + if (norm2(aimag(q)) > 0.0_pReal) then if (dNeq0(abs(q-exp(log(q))),1.0e-13_pReal)) call IO_error(401,ext_msg='exp/log') if (dNeq0(abs(q-log(exp(q))),1.0e-13_pReal)) call IO_error(401,ext_msg='log/exp') endif From 842666cc20a4aafd1608f22e70ab1b152c1f9396 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 11 Jan 2020 11:23:53 +0100 Subject: [PATCH 201/223] no overlap with Marc's code --- src/quaternions.f90 | 33 ++------------------------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/src/quaternions.f90 b/src/quaternions.f90 index bdd44b93b..886c7d071 100644 --- a/src/quaternions.f90 +++ b/src/quaternions.f90 @@ -1,38 +1,9 @@ -! ################################################################### -! Copyright (c) 2013-2015, Marc De Graef/Carnegie Mellon University -! Modified 2017-2019, Martin Diehl/Max-Planck-Institut für Eisenforschung GmbH -! All rights reserved. -! -! Redistribution and use in source and binary forms, with or without modification, are -! permitted provided that the following conditions are met: -! -! - Redistributions of source code must retain the above copyright notice, this list -! of conditions and the following disclaimer. -! - Redistributions in binary form must reproduce the above copyright notice, this -! list of conditions and the following disclaimer in the documentation and/or -! other materials provided with the distribution. -! - Neither the names of Marc De Graef, Carnegie Mellon University nor the names -! of its contributors may be used to endorse or promote products derived from -! this software without specific prior written permission. -! -! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -! ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -! LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -! DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -! SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -! OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -! USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -! ################################################################### - !--------------------------------------------------------------------------------------------------- -!> @author Marc De Graef, Carnegie Mellon University !> @author Martin Diehl, Max-Planck-Institut für Eisenforschung GmbH +!> @author Philip Eisenlohr, Michigan State University !> @brief general quaternion math, not limited to unit quaternions !> @details w is the real part, (x, y, z) are the imaginary parts. -!> @details https://users.aalto.fi/~ssarkka/pub/quat.pdf +!> @details https://en.wikipedia.org/wiki/Quaternion !--------------------------------------------------------------------------------------------------- module quaternions use prec From e762cb4dfd5352ac18b2d7f53c926dacac1ad779 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 11 Jan 2020 12:36:35 +0100 Subject: [PATCH 202/223] issue with gfortran < 9 the false branch of merge seems to be evaluated which results in a signaling NaN --- src/quaternions.f90 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/quaternions.f90 b/src/quaternions.f90 index 886c7d071..a67f345a0 100644 --- a/src/quaternions.f90 +++ b/src/quaternions.f90 @@ -513,12 +513,14 @@ subroutine unitTest q_2 = conjg(q_2) - inverse(q_2) if(any(dNeq0(q_2%asArray(),1.0e-15_pReal))) call IO_error(401,ext_msg='inverse/conjg') endif - + +#if !(defined(__GFORTRAN__) && __GNUC__ < 9) if (norm2(aimag(q)) > 0.0_pReal) then if (dNeq0(abs(q-exp(log(q))),1.0e-13_pReal)) call IO_error(401,ext_msg='exp/log') if (dNeq0(abs(q-log(exp(q))),1.0e-13_pReal)) call IO_error(401,ext_msg='log/exp') endif - +#endif + end subroutine unitTest From ac112d2d36f783b942186c5d6fa003bb62f194d3 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 11 Jan 2020 13:55:56 +0100 Subject: [PATCH 203/223] tolerance needed for optimized code --- src/quaternions.f90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/quaternions.f90 b/src/quaternions.f90 index a67f345a0..0ca404880 100644 --- a/src/quaternions.f90 +++ b/src/quaternions.f90 @@ -487,7 +487,8 @@ subroutine unitTest if(q_2 /= q) call IO_error(401,ext_msg='neq__') if(dNeq(abs(q),norm2(qu))) call IO_error(401,ext_msg='abs__') - if(dNeq(abs(q)**2.0_pReal, real(q*q%conjg()))) call IO_error(401,ext_msg='abs__/*conjg') + if(dNeq(abs(q)**2.0_pReal, real(q*q%conjg()),1.0e-14_pReal)) & + call IO_error(401,ext_msg='abs__/*conjg') if(any(dNeq(q%asArray(),qu))) call IO_error(401,ext_msg='eq__') if(dNeq(q%real(), qu(1))) call IO_error(401,ext_msg='real()') From 300f1b7015cf644e4c175aea43483d651493bff8 Mon Sep 17 00:00:00 2001 From: Philip Eisenlohr Date: Sat, 11 Jan 2020 11:36:22 -0500 Subject: [PATCH 204/223] added options to return "natural" versions of asQ, asRodrig, and asAxisAngle --- python/damask/orientation.py | 40 +++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/python/damask/orientation.py b/python/damask/orientation.py index a63444155..a86ba9331 100644 --- a/python/damask/orientation.py +++ b/python/damask/orientation.py @@ -170,9 +170,18 @@ class Rotation: ################################################################################################ # convert to different orientation representations (numpy arrays) - def asQuaternion(self): - """Unit quaternion: (q, p_1, p_2, p_3).""" - return self.quaternion.asArray() + def asQuaternion(self, + quaternion = False): + """ + Unit quaternion [q, p_1, p_2, p_3] unless quaternion == True: damask.quaternion object. + + Parameters + ---------- + quaternion : bool, optional + return quaternion as DAMASK object. + + """ + return self.quaternion if quaternion else self.quaternion.asArray() def asEulers(self, degrees = False): @@ -190,33 +199,36 @@ class Rotation: return eu def asAxisAngle(self, - degrees = False): + degrees = False, + pair = False): """ - Axis angle pair: ([n_1, n_2, n_3], ω). + Axis angle representation [n_1, n_2, n_3, ω] unless pair == True: ([n_1, n_2, n_3], ω). Parameters ---------- degrees : bool, optional return rotation angle in degrees. + pair : bool, optional + return tuple of axis and angle. """ ax = qu2ax(self.quaternion.asArray()) if degrees: ax[3] = np.degrees(ax[3]) - return ax + return (ax[:3],np.degrees(ax[3])) if pair else ax def asMatrix(self): """Rotation matrix.""" return qu2om(self.quaternion.asArray()) def asRodrigues(self, - vector=False): + vector = False): """ - Rodrigues-Frank vector: ([n_1, n_2, n_3], tan(ω/2)). + Rodrigues-Frank vector representation [n_1, n_2, n_3, tan(ω/2)] unless vector == True: [n_1, n_2, n_3] * tan(ω/2). Parameters ---------- vector : bool, optional - return as array of length 3, i.e. scale the unit vector giving the rotation axis. + return as actual Rodrigues--Frank vector, i.e. rotation axis scaled by tan(ω/2). """ ro = qu2ro(self.quaternion.asArray()) @@ -252,8 +264,8 @@ class Rotation: acceptHomomorph = False, P = -1): - qu = quaternion if isinstance(quaternion, np.ndarray) and quaternion.dtype == np.dtype(float) \ - else np.array(quaternion,dtype=float) + qu = quaternion if isinstance(quaternion, np.ndarray) and quaternion.dtype == np.dtype(float) \ + else np.array(quaternion,dtype=float) if P > 0: qu[1:4] *= -1 # convert from P=1 to P=-1 if qu[0] < 0.0: if acceptHomomorph: @@ -1193,9 +1205,9 @@ class Orientation: ref = orientations[0] for o in orientations: closest.append(o.equivalentOrientations( - ref.disorientation(o, - SST = False, # select (o[ther]'s) sym orientation - symmetries = True)[2]).rotation) # with lowest misorientation + ref.disorientation(o, + SST = False, # select (o[ther]'s) sym orientation + symmetries = True)[2]).rotation) # with lowest misorientation return Orientation(Rotation.fromAverage(closest,weights),ref.lattice) From d4535dadb4ea4cf6973fc5f9964bd49fbf33dde3 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 11 Jan 2020 20:33:29 +0100 Subject: [PATCH 205/223] use American english --- processing/post/addCompatibilityMismatch.py | 4 ++-- src/mesh_grid.f90 | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/processing/post/addCompatibilityMismatch.py b/processing/post/addCompatibilityMismatch.py index 8aff13c53..29e874614 100755 --- a/processing/post/addCompatibilityMismatch.py +++ b/processing/post/addCompatibilityMismatch.py @@ -182,8 +182,8 @@ for name in filenames: nodes = damask.grid_filters.node_coord(size,F) if options.shape: - centres = damask.grid_filters.cell_coord(size,F) - shapeMismatch = shapeMismatch( size,table.get(options.defgrad).reshape(grid[2],grid[1],grid[0],3,3),nodes,centres) + centers = damask.grid_filters.cell_coord(size,F) + shapeMismatch = shapeMismatch( size,table.get(options.defgrad).reshape(grid[2],grid[1],grid[0],3,3),nodes,centers) table.add('shapeMismatch(({}))'.format(options.defgrad), shapeMismatch.reshape((-1,1)), scriptID+' '+' '.join(sys.argv[1:])) diff --git a/src/mesh_grid.f90 b/src/mesh_grid.f90 index 3d839e1c9..62ce67397 100644 --- a/src/mesh_grid.f90 +++ b/src/mesh_grid.f90 @@ -296,7 +296,7 @@ end subroutine readGeom !--------------------------------------------------------------------------------------------------- -!> @brief Calculate undeformed position of IPs/cell centres (pretend to be an element) +!> @brief Calculate undeformed position of IPs/cell centers (pretend to be an element) !--------------------------------------------------------------------------------------------------- function IPcoordinates0(grid,geomSize,grid3Offset) From 8f99dd85ac5ec48ba6152e152d234228e491e212 Mon Sep 17 00:00:00 2001 From: Test User Date: Sat, 11 Jan 2020 22:16:08 +0100 Subject: [PATCH 206/223] [skip ci] updated version information after successful test of v2.0.3-1308-g612e8e34 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 287da9b11..cf29053c3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.0.3-1294-g034367fa +v2.0.3-1308-g612e8e34 From d9afdaed165984d5a03d376ae37494c4bd61d900 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 11 Jan 2020 23:43:03 +0100 Subject: [PATCH 207/223] using updated tests (no crystallite anymore) --- PRIVATE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PRIVATE b/PRIVATE index cda69f9a5..3eeffba15 160000 --- a/PRIVATE +++ b/PRIVATE @@ -1 +1 @@ -Subproject commit cda69f9a59fe64223439a2c725e1a78cf22b28aa +Subproject commit 3eeffba1513c0495032d0f33522e3aaa1ed77465 From 97ddd56540d82bdad9c12a1ba6c166423928b5fb Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sat, 11 Jan 2020 23:43:31 +0100 Subject: [PATCH 208/223] avoid strange deformation gradients otherwise, the test fails in a few cases (determinants were in the range 1e-5 to 1e16) --- python/tests/test_mechanics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/tests/test_mechanics.py b/python/tests/test_mechanics.py index 1ea1f2bba..9e1d9bc0c 100644 --- a/python/tests/test_mechanics.py +++ b/python/tests/test_mechanics.py @@ -113,7 +113,7 @@ class TestMechanics: def test_strain_tensor_rotation_equivalence(self): """Ensure that left and right strain differ only by a rotation.""" - F = np.random.random((self.n,3,3)) + 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))) From ddd8027b8a354c0ad7535ac10bdaa96e41599cf3 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 12 Jan 2020 00:10:42 +0100 Subject: [PATCH 209/223] autodetect string length --- src/FEsolving.f90 | 4 ++-- src/IO.f90 | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/FEsolving.f90 b/src/FEsolving.f90 index 38788a065..8c9683266 100644 --- a/src/FEsolving.f90 +++ b/src/FEsolving.f90 @@ -46,10 +46,10 @@ subroutine FE_init call IO_open_inputFile(FILEUNIT) rewind(FILEUNIT) do - read (FILEUNIT,'(a256)',END=100) line + read (FILEUNIT,'(A)',END=100) line chunkPos = IO_stringPos(line) if(IO_lc(IO_stringValue(line,chunkPos,1)) == 'solver') then - read (FILEUNIT,'(a256)',END=100) line ! next line + read (FILEUNIT,'(A)',END=100) line ! next line chunkPos = IO_stringPos(line) symmetricSolver = (IO_intValue(line,chunkPos,2) /= 1) endif diff --git a/src/IO.f90 b/src/IO.f90 index 1e39daa1e..0436e9b19 100644 --- a/src/IO.f90 +++ b/src/IO.f90 @@ -244,7 +244,7 @@ subroutine IO_open_inputFile(fileUnit) do - read(unit2,'(A256)',END=220) line + read(unit2,'(A)',END=220) line chunkPos = IO_stringPos(line) if (IO_lc(IO_StringValue(line,chunkPos,1))=='*include') then @@ -1000,7 +1000,7 @@ function IO_continuousIntValues(fileUnit,maxN,lookupName,lookupMap,lookupMaxN) #if defined(Marc4DAMASK) do - read(fileUnit,'(A256)',end=100) line + read(fileUnit,'(A)',end=100) line chunkPos = IO_stringPos(line) if (chunkPos(1) < 1) then ! empty line exit @@ -1041,14 +1041,14 @@ function IO_continuousIntValues(fileUnit,maxN,lookupName,lookupMap,lookupMaxN) !-------------------------------------------------------------------------------------------------- ! check if the element values in the elset are auto generated backspace(fileUnit) - read(fileUnit,'(A256)',end=100) line + read(fileUnit,'(A)',end=100) line chunkPos = IO_stringPos(line) do i = 1,chunkPos(1) if (IO_lc(IO_stringValue(line,chunkPos,i)) == 'generate') rangeGeneration = .true. enddo do l = 1,c - read(fileUnit,'(A256)',end=100) line + read(fileUnit,'(A)',end=100) line chunkPos = IO_stringPos(line) if (verify(IO_stringValue(line,chunkPos,1),'0123456789') > 0) then ! a non-int, i.e. set names follow on this line do i = 1,chunkPos(1) ! loop over set names in line From 1bb94f03b8f15387918439deae6c79ee57ebda1b Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 12 Jan 2020 00:14:35 +0100 Subject: [PATCH 210/223] polishing (prospector was complaining) --- processing/post/addStrainTensors.py | 3 +-- processing/post/addTable.py | 1 + processing/post/averageDown.py | 1 + processing/post/blowUp.py | 1 + processing/post/growTable.py | 3 +-- python/damask/table.py | 9 ++++++--- 6 files changed, 11 insertions(+), 7 deletions(-) diff --git a/processing/post/addStrainTensors.py b/processing/post/addStrainTensors.py index 756761e46..77015a91f 100755 --- a/processing/post/addStrainTensors.py +++ b/processing/post/addStrainTensors.py @@ -2,10 +2,9 @@ import os import sys +from io import StringIO from optparse import OptionParser -import numpy as np - import damask diff --git a/processing/post/addTable.py b/processing/post/addTable.py index c7d34f1e8..944214e69 100755 --- a/processing/post/addTable.py +++ b/processing/post/addTable.py @@ -2,6 +2,7 @@ import os import sys +from io import StringIO from optparse import OptionParser import damask diff --git a/processing/post/averageDown.py b/processing/post/averageDown.py index 817000dfa..cbd1f9637 100755 --- a/processing/post/averageDown.py +++ b/processing/post/averageDown.py @@ -2,6 +2,7 @@ import os import sys +from io import StringIO from optparse import OptionParser import numpy as np diff --git a/processing/post/blowUp.py b/processing/post/blowUp.py index ec42e2365..9cb6347ab 100755 --- a/processing/post/blowUp.py +++ b/processing/post/blowUp.py @@ -2,6 +2,7 @@ import os import sys +from io import StringIO from optparse import OptionParser from scipy import ndimage diff --git a/processing/post/growTable.py b/processing/post/growTable.py index 4fd647b1e..1dbfa8423 100755 --- a/processing/post/growTable.py +++ b/processing/post/growTable.py @@ -2,10 +2,9 @@ import os import sys +from io import StringIO from optparse import OptionParser -import numpy as np - import damask scriptName = os.path.splitext(os.path.basename(__file__))[0] diff --git a/python/damask/table.py b/python/damask/table.py index ef8a84276..343ba638d 100644 --- a/python/damask/table.py +++ b/python/damask/table.py @@ -274,7 +274,9 @@ class Table(): def append(self,other): """ - Append other table vertically (similar to numpy.vstack). Requires matching shapes and order. + Append other table vertically (similar to numpy.vstack). + + Requires matching labels/shapes and order. Parameters ---------- @@ -290,8 +292,9 @@ class Table(): def join(self,other): """ - Append other table horizontally (similar to numpy.hstack). Requires matching number of rows - and no common lables + Append other table horizontally (similar to numpy.hstack). + + Requires matching number of rows and no common labels. Parameters ---------- From 5bc1c98da7f520b218da6014f22d64442ad3e2e6 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 12 Jan 2020 00:49:03 +0100 Subject: [PATCH 211/223] use 0-based indexing for worldrank --- src/grid/grid_damage_spectral.f90 | 6 +++--- src/grid/grid_mech_FEM.f90 | 6 +++--- src/grid/grid_mech_spectral_polarisation.f90 | 6 +++--- src/grid/grid_thermal_spectral.f90 | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/grid/grid_damage_spectral.f90 b/src/grid/grid_damage_spectral.f90 index e2ce28c0e..97b20a6db 100644 --- a/src/grid/grid_damage_spectral.f90 +++ b/src/grid/grid_damage_spectral.f90 @@ -54,7 +54,7 @@ contains !-------------------------------------------------------------------------------------------------- subroutine grid_damage_spectral_init - PetscInt, dimension(worldsize) :: localK + PetscInt, dimension(0:worldsize-1) :: localK integer :: i, j, k, cell DM :: damage_grid Vec :: uBound, lBound @@ -78,8 +78,8 @@ subroutine grid_damage_spectral_init ! initialize solver specific parts of PETSc call SNESCreate(PETSC_COMM_WORLD,damage_snes,ierr); CHKERRQ(ierr) call SNESSetOptionsPrefix(damage_snes,'damage_',ierr);CHKERRQ(ierr) - localK = 0 - localK(worldrank+1) = grid3 + localK = 0 + localK(worldrank) = grid3 call MPI_Allreduce(MPI_IN_PLACE,localK,worldsize,MPI_INTEGER,MPI_SUM,PETSC_COMM_WORLD,ierr) call DMDACreate3D(PETSC_COMM_WORLD, & DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, & ! cut off stencil at boundary diff --git a/src/grid/grid_mech_FEM.f90 b/src/grid/grid_mech_FEM.f90 index 05fc3520a..b51ebb56a 100644 --- a/src/grid/grid_mech_FEM.f90 +++ b/src/grid/grid_mech_FEM.f90 @@ -81,7 +81,7 @@ contains subroutine grid_mech_FEM_init real(pReal) :: HGCoeff = 0.0e-2_pReal - PetscInt, dimension(worldsize) :: localK + PetscInt, dimension(0:worldsize-1) :: localK real(pReal), dimension(3,3) :: & temp33_Real = 0.0_pReal real(pReal), dimension(4,8) :: & @@ -120,8 +120,8 @@ subroutine grid_mech_FEM_init ! initialize solver specific parts of PETSc call SNESCreate(PETSC_COMM_WORLD,mech_snes,ierr); CHKERRQ(ierr) call SNESSetOptionsPrefix(mech_snes,'mech_',ierr);CHKERRQ(ierr) - localK = 0 - localK(worldrank+1) = grid3 + localK = 0 + localK(worldrank) = grid3 call MPI_Allreduce(MPI_IN_PLACE,localK,worldsize,MPI_INTEGER,MPI_SUM,PETSC_COMM_WORLD,ierr) call DMDACreate3d(PETSC_COMM_WORLD, & DM_BOUNDARY_PERIODIC, DM_BOUNDARY_PERIODIC, DM_BOUNDARY_PERIODIC, & diff --git a/src/grid/grid_mech_spectral_polarisation.f90 b/src/grid/grid_mech_spectral_polarisation.f90 index 59ab84869..bdc65a8c5 100644 --- a/src/grid/grid_mech_spectral_polarisation.f90 +++ b/src/grid/grid_mech_spectral_polarisation.f90 @@ -100,7 +100,7 @@ subroutine grid_mech_spectral_polarisation_init FandF_tau, & ! overall pointer to solution data F, & ! specific (sub)pointer F_tau ! specific (sub)pointer - PetscInt, dimension(worldsize) :: localK + PetscInt, dimension(0:worldsize-1) :: localK integer(HID_T) :: fileHandle, groupHandle integer :: fileUnit character(len=pStringLen) :: fileName @@ -130,8 +130,8 @@ subroutine grid_mech_spectral_polarisation_init ! initialize solver specific parts of PETSc call SNESCreate(PETSC_COMM_WORLD,snes,ierr); CHKERRQ(ierr) call SNESSetOptionsPrefix(snes,'mech_',ierr);CHKERRQ(ierr) - localK = 0 - localK(worldrank+1) = grid3 + localK = 0 + localK(worldrank) = grid3 call MPI_Allreduce(MPI_IN_PLACE,localK,worldsize,MPI_INTEGER,MPI_SUM,PETSC_COMM_WORLD,ierr) call DMDACreate3d(PETSC_COMM_WORLD, & DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, & ! cut off stencil at boundary diff --git a/src/grid/grid_thermal_spectral.f90 b/src/grid/grid_thermal_spectral.f90 index c381d837d..2d69f075e 100644 --- a/src/grid/grid_thermal_spectral.f90 +++ b/src/grid/grid_thermal_spectral.f90 @@ -55,7 +55,7 @@ contains !-------------------------------------------------------------------------------------------------- subroutine grid_thermal_spectral_init - PetscInt, dimension(worldsize) :: localK + PetscInt, dimension(0:worldsize-1) :: localK integer :: i, j, k, cell DM :: thermal_grid PetscScalar, dimension(:,:,:), pointer :: x_scal @@ -77,8 +77,8 @@ subroutine grid_thermal_spectral_init ! initialize solver specific parts of PETSc call SNESCreate(PETSC_COMM_WORLD,thermal_snes,ierr); CHKERRQ(ierr) call SNESSetOptionsPrefix(thermal_snes,'thermal_',ierr);CHKERRQ(ierr) - localK = 0 - localK(worldrank+1) = grid3 + localK = 0 + localK(worldrank) = grid3 call MPI_Allreduce(MPI_IN_PLACE,localK,worldsize,MPI_INTEGER,MPI_SUM,PETSC_COMM_WORLD,ierr) call DMDACreate3D(PETSC_COMM_WORLD, & DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, DM_BOUNDARY_NONE, & ! cut off stencil at boundary From 13150291966c96d8f02aeef7bbb267e5a2513688 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 12 Jan 2020 02:06:53 +0100 Subject: [PATCH 212/223] do not clutter comments --- python/damask/table.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/python/damask/table.py b/python/damask/table.py index 343ba638d..c40baa23c 100644 --- a/python/damask/table.py +++ b/python/damask/table.py @@ -22,7 +22,7 @@ class Table(): Additional, human-readable information. """ - self.comments = ['table.py v {}'.format(version)] if not comments else [c for c in comments] + self.comments = [c for c in comments] self.data = pd.DataFrame(data=data) self.shapes = shapes self.__label_condensed() @@ -79,8 +79,7 @@ class Table(): else: raise Exception - comments = ['table.py:from_ASCII v {}'.format(version)] - comments+= [f.readline()[:-1] for i in range(1,header)] + comments = [f.readline()[:-1] for i in range(1,header)] labels = f.readline().split() shapes = {} From 19e88df57131af9a82bce7d8da995e36bc21cb74 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 12 Jan 2020 07:53:41 +0100 Subject: [PATCH 213/223] polishing --- python/damask/table.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/python/damask/table.py b/python/damask/table.py index c40baa23c..a3c0a8af0 100644 --- a/python/damask/table.py +++ b/python/damask/table.py @@ -3,7 +3,6 @@ import re import pandas as pd import numpy as np -from . import version class Table(): """Store spreadsheet-like data.""" @@ -22,7 +21,7 @@ class Table(): Additional, human-readable information. """ - self.comments = [c for c in comments] + self.comments = [] if comments is None else [c for c in comments] self.data = pd.DataFrame(data=data) self.shapes = shapes self.__label_condensed() @@ -77,8 +76,8 @@ class Table(): if keyword == 'header': header = int(header) else: - raise Exception - + raise TypeError + comments = [f.readline()[:-1] for i in range(1,header)] labels = f.readline().split() @@ -138,7 +137,7 @@ class Table(): data = np.loadtxt(content) for c in range(data.shape[1]-10): - shapes['user_defined{}'.format(c+1)] = (1,) + shapes['n/a_{}'.format(c+1)] = (1,) return Table(data,shapes,comments) From 70b73762edc4b3c2e7a6831724c86e8fbe169a2b Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 12 Jan 2020 19:21:16 +0100 Subject: [PATCH 214/223] avoid warning due to change in default parameter --- python/damask/dadf5.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/damask/dadf5.py b/python/damask/dadf5.py index 73e785b10..5c9ebe08e 100644 --- a/python/damask/dadf5.py +++ b/python/damask/dadf5.py @@ -880,7 +880,7 @@ class DADF5(): else: nodes = vtk.vtkPoints() - with h5py.File(self.fname) as f: + 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() From 26442ee7838b801caf2238a607c02e0315720868 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Sun, 12 Jan 2020 19:35:45 +0100 Subject: [PATCH 215/223] bugfixes missing import/outdated test --- PRIVATE | 2 +- python/damask/table.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/PRIVATE b/PRIVATE index 3eeffba15..99b076706 160000 --- a/PRIVATE +++ b/PRIVATE @@ -1 +1 @@ -Subproject commit 3eeffba1513c0495032d0f33522e3aaa1ed77465 +Subproject commit 99b076706a186ec7deb051ae181c2d9697c799fc diff --git a/python/damask/table.py b/python/damask/table.py index a3c0a8af0..b3dfc2433 100644 --- a/python/damask/table.py +++ b/python/damask/table.py @@ -3,6 +3,7 @@ import re import pandas as pd import numpy as np +from . import version class Table(): """Store spreadsheet-like data.""" From 96b2b6f602053f0e1d2fc0e9669f291ed9ebcd28 Mon Sep 17 00:00:00 2001 From: Test User Date: Sun, 12 Jan 2020 20:41:01 +0100 Subject: [PATCH 216/223] [skip ci] updated version information after successful test of v2.0.3-1354-gef5a8a3a --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index cf29053c3..7712dccc1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.0.3-1308-g612e8e34 +v2.0.3-1354-gef5a8a3a From ce1afbeabae1dd6263fcccbad54caaba1aed1f7a Mon Sep 17 00:00:00 2001 From: Test User Date: Mon, 13 Jan 2020 01:11:24 +0100 Subject: [PATCH 217/223] [skip ci] updated version information after successful test of v2.0.3-1406-g5fc1abae --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 7712dccc1..a7308c0ee 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.0.3-1354-gef5a8a3a +v2.0.3-1406-g5fc1abae From f9772a3df85bb7dbb2a5b3308c1443206e1650ca Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 13 Jan 2020 02:51:49 +0100 Subject: [PATCH 218/223] more descriptive name --- processing/post/addCompatibilityMismatch.py | 2 +- processing/post/addCurl.py | 2 +- processing/post/addDisplacement.py | 2 +- processing/post/addDivergence.py | 2 +- processing/post/addEuclideanDistance.py | 2 +- processing/post/addGradient.py | 2 +- processing/post/averageDown.py | 2 +- processing/post/blowUp.py | 2 +- processing/pre/geom_fromTable.py | 2 +- python/damask/grid_filters.py | 6 +++--- python/tests/test_grid_filters.py | 4 ++-- 11 files changed, 14 insertions(+), 14 deletions(-) diff --git a/processing/post/addCompatibilityMismatch.py b/processing/post/addCompatibilityMismatch.py index 29e874614..c7c5086ca 100755 --- a/processing/post/addCompatibilityMismatch.py +++ b/processing/post/addCompatibilityMismatch.py @@ -176,7 +176,7 @@ for name in filenames: damask.util.report(scriptName,name) table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) - grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.get(options.pos)) + grid,size,origin = damask.grid_filters.cell_coord0_gridSizeOrigin(table.get(options.pos)) F = table.get(options.defgrad).reshape(grid[2],grid[1],grid[0],3,3) nodes = damask.grid_filters.node_coord(size,F) diff --git a/processing/post/addCurl.py b/processing/post/addCurl.py index 25639dc7c..f106054b3 100755 --- a/processing/post/addCurl.py +++ b/processing/post/addCurl.py @@ -44,7 +44,7 @@ for name in filenames: damask.util.report(scriptName,name) table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) - grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.get(options.pos)) + grid,size,origin = damask.grid_filters.cell_coord0_gridSizeOrigin(table.get(options.pos)) for label in options.labels: field = table.get(label) diff --git a/processing/post/addDisplacement.py b/processing/post/addDisplacement.py index 59630a6c6..faabc795f 100755 --- a/processing/post/addDisplacement.py +++ b/processing/post/addDisplacement.py @@ -50,7 +50,7 @@ for name in filenames: damask.util.report(scriptName,name) table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) - grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.get(options.pos)) + grid,size,origin = damask.grid_filters.cell_coord0_gridSizeOrigin(table.get(options.pos)) F = table.get(options.f).reshape(np.append(grid[::-1],(3,3))) if options.nodal: diff --git a/processing/post/addDivergence.py b/processing/post/addDivergence.py index 585ebb5a5..cb9486990 100755 --- a/processing/post/addDivergence.py +++ b/processing/post/addDivergence.py @@ -44,7 +44,7 @@ for name in filenames: damask.util.report(scriptName,name) table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) - grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.get(options.pos)) + grid,size,origin = damask.grid_filters.cell_coord0_gridSizeOrigin(table.get(options.pos)) for label in options.labels: field = table.get(label) diff --git a/processing/post/addEuclideanDistance.py b/processing/post/addEuclideanDistance.py index eaf91b894..be820220a 100755 --- a/processing/post/addEuclideanDistance.py +++ b/processing/post/addEuclideanDistance.py @@ -143,7 +143,7 @@ for name in filenames: damask.util.report(scriptName,name) table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) - grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.get(options.pos)) + grid,size,origin = damask.grid_filters.cell_coord0_gridSizeOrigin(table.get(options.pos)) neighborhood = neighborhoods[options.neighborhood] diffToNeighbor = np.empty(list(grid+2)+[len(neighborhood)],'i') diff --git a/processing/post/addGradient.py b/processing/post/addGradient.py index 54b80ed26..8620c123b 100755 --- a/processing/post/addGradient.py +++ b/processing/post/addGradient.py @@ -44,7 +44,7 @@ for name in filenames: damask.util.report(scriptName,name) table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) - grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.get(options.pos)) + grid,size,origin = damask.grid_filters.cell_coord0_gridSizeOrigin(table.get(options.pos)) for label in options.labels: field = table.get(label) diff --git a/processing/post/averageDown.py b/processing/post/averageDown.py index cbd1f9637..0d3948251 100755 --- a/processing/post/averageDown.py +++ b/processing/post/averageDown.py @@ -65,7 +65,7 @@ for name in filenames: table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) if (options.grid is None or options.size is None): - grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.get(options.pos)) + grid,size,origin = damask.grid_filters.cell_coord0_gridSizeOrigin(table.get(options.pos)) else: grid = np.array(options.grid,'i') size = np.array(options.size,'d') diff --git a/processing/post/blowUp.py b/processing/post/blowUp.py index 9cb6347ab..718858e1c 100755 --- a/processing/post/blowUp.py +++ b/processing/post/blowUp.py @@ -54,7 +54,7 @@ for name in filenames: damask.util.report(scriptName,name) table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) - grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.get(options.pos)) + grid,size,origin = damask.grid_filters.cell_coord0_gridSizeOrigin(table.get(options.pos)) packing = np.array(options.packing,'i') outSize = grid*packing diff --git a/processing/pre/geom_fromTable.py b/processing/pre/geom_fromTable.py index 40fd17437..0879f8812 100755 --- a/processing/pre/geom_fromTable.py +++ b/processing/pre/geom_fromTable.py @@ -86,7 +86,7 @@ for name in filenames: if options.phase is None: table.data = np.column_stack((table.data,np.ones(len(table.data)))) # add single phase if no phase column given - grid,size,origin = damask.grid_filters.cell_coord0_2_DNA(table.data[:,0:3]) + grid,size,origin = damask.grid_filters.cell_coord0_gridSizeOrigin(table.data[:,0:3]) indices = np.lexsort((table.data[:,0],table.data[:,1],table.data[:,2])) # indices of position when sorting x fast, z slow microstructure = np.empty(grid,dtype = int) # initialize empty microstructure diff --git a/python/damask/grid_filters.py b/python/damask/grid_filters.py index 9c8b1b88e..36ce3b8e2 100644 --- a/python/damask/grid_filters.py +++ b/python/damask/grid_filters.py @@ -181,7 +181,7 @@ def cell_coord(size,F,origin=np.zeros(3)): """ return cell_coord0(F.shape[:3][::-1],size,origin) + cell_displacement(size,F) -def cell_coord0_2_DNA(coord0,ordered=True): +def cell_coord0_gridSizeOrigin(coord0,ordered=True): """ Return grid 'DNA', i.e. grid, size, and origin from array of cell positions. @@ -231,7 +231,7 @@ def coord0_check(coord0): array of undeformed cell coordinates. """ - cell_coord0_2_DNA(coord0,ordered=True) + cell_coord0_gridSizeOrigin(coord0,ordered=True) @@ -331,7 +331,7 @@ def node_2_cell(node_data): return c[:-1,:-1,:-1] -def node_coord0_2_DNA(coord0,ordered=False): +def node_coord0_gridSizeOrigin(coord0,ordered=False): """ Return grid 'DNA', i.e. grid, size, and origin from array of nodal positions. diff --git a/python/tests/test_grid_filters.py b/python/tests/test_grid_filters.py index fdddaf3a1..a5455e1ae 100644 --- a/python/tests/test_grid_filters.py +++ b/python/tests/test_grid_filters.py @@ -26,12 +26,12 @@ class TestGridFilters: @pytest.mark.parametrize('mode',[('cell'),('node')]) def test_grid_DNA(self,mode): - """Ensure that xx_coord0_2_DNA is the inverse of xx_coord0.""" + """Ensure that xx_coord0_gridSizeOrigin is the inverse of xx_coord0.""" grid = np.random.randint(8,32,(3)) size = np.random.random(3) origin = np.random.random(3) coord0 = eval('grid_filters.{}_coord0(grid,size,origin)'.format(mode)) # noqa - _grid,_size,_origin = eval('grid_filters.{}_coord0_2_DNA(coord0.reshape((-1,3)))'.format(mode)) + _grid,_size,_origin = eval('grid_filters.{}_coord0_gridSizeOrigin(coord0.reshape((-1,3)))'.format(mode)) assert np.allclose(grid,_grid) and np.allclose(size,_size) and np.allclose(origin,_origin) def test_displacement_fluct_equivalence(self): From ff60cf0b6730e2ceffcc15a14af84e658561752c Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 13 Jan 2020 03:40:37 +0100 Subject: [PATCH 219/223] use new table class --- processing/post/addAPS34IDEstrainCoords.py | 59 ++++++---------------- 1 file changed, 16 insertions(+), 43 deletions(-) diff --git a/processing/post/addAPS34IDEstrainCoords.py b/processing/post/addAPS34IDEstrainCoords.py index fe834cf38..c14983799 100755 --- a/processing/post/addAPS34IDEstrainCoords.py +++ b/processing/post/addAPS34IDEstrainCoords.py @@ -2,6 +2,7 @@ import os import sys +from io import StringIO from optparse import OptionParser import numpy as np @@ -24,61 +25,33 @@ Transform X,Y,Z,F APS BeamLine 34 coordinates to x,y,z APS strain coordinates. parser.add_option('-f','--frame',dest='frame', metavar='string', help='label of APS X,Y,Z coords') -parser.add_option('--depth', dest='depth', metavar='string', +parser.add_option('--depth', dest='depth', metavar='string', help='depth') (options,filenames) = parser.parse_args() +if filenames == []: filenames = [None] if options.frame is None: parser.error('frame not specified') if options.depth is None: parser.error('depth not specified') -# --- loop over input files ------------------------------------------------------------------------ -if filenames == []: filenames = [None] +theta=-0.75*np.pi +RotMat2TSL=np.array([[1., 0., 0.], + [0., np.cos(theta), np.sin(theta)], # Orientation to account for -135 deg + [0., -np.sin(theta), np.cos(theta)]]) # rotation for TSL convention 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) + + coord = - table.get(options.frame) + coord[:,2] += table.get(options.depth)[:,0] - table.head_read() + table.add('coord', + np.einsum('ijk,ik->ij',np.broadcast_to(RotMat2TSL,(coord.shape[0],3,3)),coord), + scriptID+' '+' '.join(sys.argv[1:])) -# ------------------------------------------ sanity checks ----------------------------------------- - errors = [] - if table.label_dimension(options.frame) != 3: - errors.append('input {} does not have dimension 3.'.format(options.frame)) - if table.label_dimension(options.depth) != 1: - errors.append('input {} does not have dimension 1.'.format(options.depth)) - if errors != []: - damask.util.croak(errors) - table.close(dismiss = True) - continue - - table.info_append(scriptID + '\t' + ' '.join(sys.argv[1:])) - -# ------------------------------------------ assemble header --------------------------------------- - table.labels_append(['%i_coord'%(i+1) for i in range(3)]) # extend ASCII header with new labels - table.head_write() - -# ------------------------------------------ process data ------------------------------------------ - theta=-0.75*np.pi - RotMat2TSL=np.array([[1., 0., 0.], - [0., np.cos(theta), np.sin(theta)], # Orientation to account for -135 deg - [0., -np.sin(theta), np.cos(theta)]]) # rotation for TSL convention - outputAlive = True - while outputAlive and table.data_read(): # read next data line of ASCII table - coord = list(map(float,table.data[table.label_index(options.frame):table.label_index(options.frame)+3])) - depth = float(table.data[table.label_index(options.depth)]) - - table.data_append(np.dot(RotMat2TSL,np.array([-coord[0],-coord[1],-coord[2]+depth]))) - - outputAlive = table.data_write() # output processed line - -# ------------------------------------------ output finalization ----------------------------------- - - table.close() # close ASCII tables + table.to_ASCII(sys.stdout if name is None else name) From a0a99afa97e44e3eace5f53d637cb92ee797b48c Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 13 Jan 2020 09:56:43 +0100 Subject: [PATCH 220/223] [skip ci] obsolete material.config features --- PRIVATE | 2 +- examples/FEM/polyXtal/material.config | 210 +----------------- examples/MSC.Marc/material.config | 5 - .../EshelbyInclusion/material.config | 53 +++-- 4 files changed, 36 insertions(+), 234 deletions(-) diff --git a/PRIVATE b/PRIVATE index 99b076706..66d562c75 160000 --- a/PRIVATE +++ b/PRIVATE @@ -1 +1 @@ -Subproject commit 99b076706a186ec7deb051ae181c2d9697c799fc +Subproject commit 66d562c755cd9aa4bbb8280c509383014acd52db diff --git a/examples/FEM/polyXtal/material.config b/examples/FEM/polyXtal/material.config index 197890821..71e5350dc 100644 --- a/examples/FEM/polyXtal/material.config +++ b/examples/FEM/polyXtal/material.config @@ -4,14 +4,6 @@ [SX] mech none -#-------------------# - -#-------------------# - -[aLittleSomething] -(output) f -(output) p - #-------------------# #-------------------# @@ -50,408 +42,212 @@ interaction_twinslip 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 interaction_twintwin 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 atol_resistance 1 +(output) f +(output) p + #-------------------# #-------------------# [Grain001] -crystallite 1 (constituent) phase 1 texture 1 fraction 1.0 - [Grain002] -crystallite 1 (constituent) phase 1 texture 2 fraction 1.0 - [Grain003] -crystallite 1 (constituent) phase 1 texture 3 fraction 1.0 - [Grain004] -crystallite 1 (constituent) phase 1 texture 4 fraction 1.0 - [Grain005] -crystallite 1 (constituent) phase 1 texture 5 fraction 1.0 - [Grain006] -crystallite 1 (constituent) phase 1 texture 6 fraction 1.0 - [Grain007] -crystallite 1 (constituent) phase 1 texture 7 fraction 1.0 - [Grain008] -crystallite 1 (constituent) phase 1 texture 8 fraction 1.0 - [Grain009] -crystallite 1 (constituent) phase 1 texture 9 fraction 1.0 - [Grain010] -crystallite 1 (constituent) phase 1 texture 10 fraction 1.0 - [Grain011] -crystallite 1 (constituent) phase 1 texture 11 fraction 1.0 - [Grain012] -crystallite 1 (constituent) phase 1 texture 12 fraction 1.0 - [Grain013] -crystallite 1 (constituent) phase 1 texture 13 fraction 1.0 - [Grain014] -crystallite 1 (constituent) phase 1 texture 14 fraction 1.0 - [Grain015] -crystallite 1 (constituent) phase 1 texture 15 fraction 1.0 - [Grain016] -crystallite 1 (constituent) phase 1 texture 16 fraction 1.0 - [Grain017] -crystallite 1 (constituent) phase 1 texture 17 fraction 1.0 - [Grain018] -crystallite 1 (constituent) phase 1 texture 18 fraction 1.0 - [Grain019] -crystallite 1 (constituent) phase 1 texture 19 fraction 1.0 - [Grain020] -crystallite 1 (constituent) phase 1 texture 20 fraction 1.0 - [Grain021] -crystallite 1 (constituent) phase 1 texture 21 fraction 1.0 - [Grain022] -crystallite 1 (constituent) phase 1 texture 22 fraction 1.0 - [Grain023] -crystallite 1 (constituent) phase 1 texture 23 fraction 1.0 - [Grain024] -crystallite 1 (constituent) phase 1 texture 24 fraction 1.0 - [Grain025] -crystallite 1 (constituent) phase 1 texture 25 fraction 1.0 - [Grain026] -crystallite 1 (constituent) phase 1 texture 26 fraction 1.0 - [Grain027] -crystallite 1 (constituent) phase 1 texture 27 fraction 1.0 - [Grain028] -crystallite 1 (constituent) phase 1 texture 28 fraction 1.0 - [Grain029] -crystallite 1 (constituent) phase 1 texture 29 fraction 1.0 - [Grain030] -crystallite 1 (constituent) phase 1 texture 30 fraction 1.0 - [Grain031] -crystallite 1 (constituent) phase 1 texture 31 fraction 1.0 - [Grain032] -crystallite 1 (constituent) phase 1 texture 32 fraction 1.0 - [Grain033] -crystallite 1 (constituent) phase 1 texture 33 fraction 1.0 - [Grain034] -crystallite 1 (constituent) phase 1 texture 34 fraction 1.0 - [Grain035] -crystallite 1 (constituent) phase 1 texture 35 fraction 1.0 - [Grain036] -crystallite 1 (constituent) phase 1 texture 36 fraction 1.0 - [Grain037] -crystallite 1 (constituent) phase 1 texture 37 fraction 1.0 - [Grain038] -crystallite 1 (constituent) phase 1 texture 38 fraction 1.0 - [Grain039] -crystallite 1 (constituent) phase 1 texture 39 fraction 1.0 - [Grain040] -crystallite 1 (constituent) phase 1 texture 40 fraction 1.0 - [Grain041] -crystallite 1 (constituent) phase 1 texture 41 fraction 1.0 - [Grain042] -crystallite 1 (constituent) phase 1 texture 42 fraction 1.0 - [Grain043] -crystallite 1 (constituent) phase 1 texture 43 fraction 1.0 - [Grain044] -crystallite 1 (constituent) phase 1 texture 44 fraction 1.0 - [Grain045] -crystallite 1 (constituent) phase 1 texture 45 fraction 1.0 - [Grain046] -crystallite 1 (constituent) phase 1 texture 46 fraction 1.0 - [Grain047] -crystallite 1 (constituent) phase 1 texture 47 fraction 1.0 - [Grain048] -crystallite 1 (constituent) phase 1 texture 48 fraction 1.0 - [Grain049] -crystallite 1 (constituent) phase 1 texture 49 fraction 1.0 - [Grain050] -crystallite 1 (constituent) phase 1 texture 50 fraction 1.0 - [Grain051] -crystallite 1 (constituent) phase 1 texture 51 fraction 1.0 - [Grain052] -crystallite 1 (constituent) phase 1 texture 52 fraction 1.0 - [Grain053] -crystallite 1 (constituent) phase 1 texture 53 fraction 1.0 - [Grain054] -crystallite 1 (constituent) phase 1 texture 54 fraction 1.0 - [Grain055] -crystallite 1 (constituent) phase 1 texture 55 fraction 1.0 - [Grain056] -crystallite 1 (constituent) phase 1 texture 56 fraction 1.0 - [Grain057] -crystallite 1 (constituent) phase 1 texture 57 fraction 1.0 - [Grain058] -crystallite 1 (constituent) phase 1 texture 58 fraction 1.0 - [Grain059] -crystallite 1 (constituent) phase 1 texture 59 fraction 1.0 - [Grain060] -crystallite 1 (constituent) phase 1 texture 60 fraction 1.0 - [Grain061] -crystallite 1 (constituent) phase 1 texture 61 fraction 1.0 - [Grain062] -crystallite 1 (constituent) phase 1 texture 62 fraction 1.0 - [Grain063] -crystallite 1 (constituent) phase 1 texture 63 fraction 1.0 - [Grain064] -crystallite 1 (constituent) phase 1 texture 64 fraction 1.0 - [Grain065] -crystallite 1 (constituent) phase 1 texture 65 fraction 1.0 - [Grain066] -crystallite 1 (constituent) phase 1 texture 66 fraction 1.0 - [Grain067] -crystallite 1 (constituent) phase 1 texture 67 fraction 1.0 - [Grain068] -crystallite 1 (constituent) phase 1 texture 68 fraction 1.0 - [Grain069] -crystallite 1 (constituent) phase 1 texture 69 fraction 1.0 - [Grain070] -crystallite 1 (constituent) phase 1 texture 70 fraction 1.0 - [Grain071] -crystallite 1 (constituent) phase 1 texture 71 fraction 1.0 - [Grain072] -crystallite 1 (constituent) phase 1 texture 72 fraction 1.0 - [Grain073] -crystallite 1 (constituent) phase 1 texture 73 fraction 1.0 - [Grain074] -crystallite 1 (constituent) phase 1 texture 74 fraction 1.0 - [Grain075] -crystallite 1 (constituent) phase 1 texture 75 fraction 1.0 - [Grain076] -crystallite 1 (constituent) phase 1 texture 76 fraction 1.0 - [Grain077] -crystallite 1 (constituent) phase 1 texture 77 fraction 1.0 - [Grain078] -crystallite 1 (constituent) phase 1 texture 78 fraction 1.0 - [Grain079] -crystallite 1 (constituent) phase 1 texture 79 fraction 1.0 - [Grain080] -crystallite 1 (constituent) phase 1 texture 80 fraction 1.0 - [Grain081] -crystallite 1 (constituent) phase 1 texture 81 fraction 1.0 - [Grain082] -crystallite 1 (constituent) phase 1 texture 82 fraction 1.0 - [Grain083] -crystallite 1 (constituent) phase 1 texture 83 fraction 1.0 - [Grain084] -crystallite 1 (constituent) phase 1 texture 84 fraction 1.0 - [Grain085] -crystallite 1 (constituent) phase 1 texture 85 fraction 1.0 - [Grain086] -crystallite 1 (constituent) phase 1 texture 86 fraction 1.0 - [Grain087] -crystallite 1 (constituent) phase 1 texture 87 fraction 1.0 - [Grain088] -crystallite 1 (constituent) phase 1 texture 88 fraction 1.0 - [Grain089] -crystallite 1 (constituent) phase 1 texture 89 fraction 1.0 - [Grain090] -crystallite 1 (constituent) phase 1 texture 90 fraction 1.0 - [Grain091] -crystallite 1 (constituent) phase 1 texture 91 fraction 1.0 - [Grain092] -crystallite 1 (constituent) phase 1 texture 92 fraction 1.0 - [Grain093] -crystallite 1 (constituent) phase 1 texture 93 fraction 1.0 - [Grain094] -crystallite 1 (constituent) phase 1 texture 94 fraction 1.0 - [Grain095] -crystallite 1 (constituent) phase 1 texture 95 fraction 1.0 - [Grain096] -crystallite 1 (constituent) phase 1 texture 96 fraction 1.0 - [Grain097] -crystallite 1 (constituent) phase 1 texture 97 fraction 1.0 - [Grain098] -crystallite 1 (constituent) phase 1 texture 98 fraction 1.0 - [Grain099] -crystallite 1 (constituent) phase 1 texture 99 fraction 1.0 - [Grain100] -crystallite 1 (constituent) phase 1 texture 100 fraction 1.0 #-------------------# diff --git a/examples/MSC.Marc/material.config b/examples/MSC.Marc/material.config index 69b8412f4..46ea44367 100644 --- a/examples/MSC.Marc/material.config +++ b/examples/MSC.Marc/material.config @@ -419,11 +419,6 @@ [cube] (gauss) phi1 0 Phi 0 phi2 0 -#-------------------# - -#-------------------# - -{../ConfigFiles/Crystallite_All.config} #-------------------# diff --git a/examples/SpectralMethod/EshelbyInclusion/material.config b/examples/SpectralMethod/EshelbyInclusion/material.config index 6dda5c2dd..008c44f4b 100644 --- a/examples/SpectralMethod/EshelbyInclusion/material.config +++ b/examples/SpectralMethod/EshelbyInclusion/material.config @@ -3,25 +3,12 @@ #-------------------# [direct] -mech none # isostrain 1 grain +mech none -thermal adiabatic # thermal strain (stress) induced mass transport +thermal adiabatic t0 330.0 (output) temperature -#-------------------# - -#-------------------# - -[aLittleSomething] - -(output) texture -(output) f -(output) p -(output) fe -(output) fi -(output) fp - #-------------------# #-------------------# @@ -34,6 +21,12 @@ plasticity none {config/elastic_isotropic.config} {config/thermal.config} +(output) f +(output) p +(output) fe +(output) fi +(output) fp + #................. [Ti matrix] @@ -43,6 +36,12 @@ plasticity none {config/elastic_Ti.config} {config/thermal.config} +(output) f +(output) p +(output) fe +(output) fi +(output) fp + #................. [isotropic inclusion] @@ -52,6 +51,12 @@ plasticity none {config/thermal.config} {config/thermalExpansion_isotropic.config} +(output) f +(output) p +(output) fe +(output) fi +(output) fp + #................. [anisotropic inclusion] @@ -61,6 +66,12 @@ plasticity none {config/thermal.config} {config/thermalExpansion_fullyAnisotropic.config} +(output) f +(output) p +(output) fe +(output) fi +(output) fp + #................. [Ti inclusion] @@ -71,32 +82,32 @@ plasticity none {config/thermal.config} {config/thermalExpansion_Ti.config} +(output) f +(output) p +(output) fe +(output) fi +(output) fp + #--------------------------# #--------------------------# [isotropic matrix] -crystallite 1 (constituent) phase 1 texture 1 fraction 1.0 [Ti matrix] -crystallite 1 (constituent) phase 2 texture 1 fraction 1.0 [isotropic inclusion] -crystallite 1 (constituent) phase 3 texture 1 fraction 1.0 [anisotropic inclusion] -crystallite 1 (constituent) phase 4 texture 1 fraction 1.0 [rotated inclusion] -crystallite 1 (constituent) phase 4 texture 2 fraction 1.0 [Ti inclusion] -crystallite 1 (constituent) phase 5 texture 1 fraction 1.0 #--------------------------# From 7b3015c6bd9464ed83c714684cf4364db9e9f76a Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 13 Jan 2020 18:44:50 +0100 Subject: [PATCH 221/223] relaxed tolerances. one out of multiple thousand tests failed ... --- python/tests/test_Rotation.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index 08d543554..e352e1c26 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -45,17 +45,17 @@ class TestRotation: def test_Homochoric(self,default): for rot in default: assert np.allclose(rot.asRodrigues(), - Rotation.fromHomochoric(rot.asHomochoric()).asRodrigues()) + Rotation.fromHomochoric(rot.asHomochoric()).asRodrigues(),rtol=5.e-5) def test_Cubochoric(self,default): for rot in default: assert np.allclose(rot.asHomochoric(), - Rotation.fromCubochoric(rot.asCubochoric()).asHomochoric()) + Rotation.fromCubochoric(rot.asCubochoric()).asHomochoric(),rtol=5.e-5) def test_Quaternion(self,default): for rot in default: assert np.allclose(rot.asCubochoric(), - Rotation.fromQuaternion(rot.asQuaternion()).asCubochoric()) + Rotation.fromQuaternion(rot.asQuaternion()).asCubochoric(),rtol=5.e-5) @pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch']) From 93fca511e98fb2b5342333f5d68aaa9c81af08f1 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Mon, 13 Jan 2020 21:40:22 +0100 Subject: [PATCH 222/223] used wrong PRIVATE (causing tests to fail) --- PRIVATE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PRIVATE b/PRIVATE index 66d562c75..99b076706 160000 --- a/PRIVATE +++ b/PRIVATE @@ -1 +1 @@ -Subproject commit 66d562c755cd9aa4bbb8280c509383014acd52db +Subproject commit 99b076706a186ec7deb051ae181c2d9697c799fc From 31788301e99165ec7afcdb84212fdaf67ffdce16 Mon Sep 17 00:00:00 2001 From: Test User Date: Mon, 13 Jan 2020 23:55:20 +0100 Subject: [PATCH 223/223] [skip ci] updated version information after successful test of v2.0.3-1484-g93fca511 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index a7308c0ee..c0a399148 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.0.3-1406-g5fc1abae +v2.0.3-1484-g93fca511