Merge branch '125-python-typehints' into 'development'

first few type hints for Python

See merge request damask/DAMASK!450
This commit is contained in:
Daniel Otto de Mentock 2021-11-05 07:45:29 +00:00
commit 8e08af31e5
9 changed files with 108 additions and 65 deletions

View File

@ -81,7 +81,7 @@ checkout:
- release
###################################################################################################
processing:
pytest:
stage: python
script:
- cd $DAMASKROOT/python
@ -91,6 +91,15 @@ processing:
- master
- release
mypy:
stage: python
script:
- cd $DAMASKROOT/python
- mypy -m damask
except:
- master
- release
###################################################################################################
compile_grid_Intel:

View File

@ -14,8 +14,8 @@ from . import tensor # noqa
from . import mechanics # noqa
from . import solver # noqa
from . import grid_filters # noqa
#Modules that contain only one class (of the same name), are prefixed by a '_'.
#For example, '_colormap' containsa class called 'Colormap' which is imported as 'damask.Colormap'.
# Modules that contain only one class (of the same name), are prefixed by a '_'.
# For example, '_colormap' contains a class called 'Colormap' which is imported as 'damask.Colormap'.
from ._rotation import Rotation # noqa
from ._crystal import Crystal # noqa
from ._orientation import Orientation # noqa

View File

@ -125,7 +125,7 @@ class Orientation(Rotation,Crystal):
"""Create deep copy."""
dup = copy.deepcopy(self)
if rotation is not None:
dup.quaternion = Orientation(rotation,family='cubic').quaternion
dup.quaternion = Rotation(rotation).quaternion
return dup
copy = __copy__

View File

@ -1,3 +1,5 @@
import copy
import numpy as np
from . import tensor
@ -85,9 +87,12 @@ class Rotation:
+ str(self.quaternion)
def __copy__(self,**kwargs):
def __copy__(self,rotation=None):
"""Create deep copy."""
return self.__class__(rotation=kwargs['rotation'] if 'rotation' in kwargs else self.quaternion)
dup = copy.deepcopy(self)
if rotation is not None:
dup.quaternion = Rotation(rotation).quaternion
return dup
copy = __copy__

View File

@ -11,10 +11,14 @@ the following operations are required for tensorial data:
- D1 = D3.reshape(cells+(-1,)).reshape(-1,9,order='F')
"""
from typing import Sequence, Tuple, Union
from scipy import spatial as _spatial
import numpy as _np
def _ks(size,cells,first_order=False):
def _ks(size: _np.ndarray, cells: Union[_np.ndarray,Sequence[int]], first_order: bool = False) -> _np.ndarray:
"""
Get wave numbers operator.
@ -41,7 +45,7 @@ def _ks(size,cells,first_order=False):
return _np.stack(_np.meshgrid(k_sk,k_sj,k_si,indexing = 'ij'), axis=-1)
def curl(size,f):
def curl(size: _np.ndarray, f: _np.ndarray) -> _np.ndarray:
u"""
Calculate curl of a vector or tensor field in Fourier space.
@ -72,7 +76,7 @@ def curl(size,f):
return _np.fft.irfftn(curl_,axes=(0,1,2),s=f.shape[:3])
def divergence(size,f):
def divergence(size: _np.ndarray, f: _np.ndarray) -> _np.ndarray:
u"""
Calculate divergence of a vector or tensor field in Fourier space.
@ -99,7 +103,7 @@ def divergence(size,f):
return _np.fft.irfftn(div_,axes=(0,1,2),s=f.shape[:3])
def gradient(size,f):
def gradient(size: _np.ndarray, f: _np.ndarray) -> _np.ndarray:
u"""
Calculate gradient of a scalar or vector fieldin Fourier space.
@ -126,7 +130,9 @@ def gradient(size,f):
return _np.fft.irfftn(grad_,axes=(0,1,2),s=f.shape[:3])
def coordinates0_point(cells,size,origin=_np.zeros(3)):
def coordinates0_point(cells: Union[ _np.ndarray,Sequence[int]],
size: _np.ndarray,
origin: _np.ndarray = _np.zeros(3)) -> _np.ndarray:
"""
Cell center positions (undeformed).
@ -145,8 +151,8 @@ def coordinates0_point(cells,size,origin=_np.zeros(3)):
Undeformed cell center coordinates.
"""
start = origin + size/cells*.5
end = origin + size - size/cells*.5
start = origin + size/_np.array(cells)*.5
end = origin + size - size/_np.array(cells)*.5
return _np.stack(_np.meshgrid(_np.linspace(start[0],end[0],cells[0]),
_np.linspace(start[1],end[1],cells[1]),
@ -154,7 +160,7 @@ def coordinates0_point(cells,size,origin=_np.zeros(3)):
axis = -1)
def displacement_fluct_point(size,F):
def displacement_fluct_point(size: _np.ndarray, F: _np.ndarray) -> _np.ndarray:
"""
Cell center displacement field from fluctuation part of the deformation gradient field.
@ -186,7 +192,7 @@ def displacement_fluct_point(size,F):
return _np.fft.irfftn(displacement,axes=(0,1,2),s=F.shape[:3])
def displacement_avg_point(size,F):
def displacement_avg_point(size: _np.ndarray, F: _np.ndarray) -> _np.ndarray:
"""
Cell center displacement field from average part of the deformation gradient field.
@ -207,7 +213,7 @@ def displacement_avg_point(size,F):
return _np.einsum('ml,ijkl->ijkm',F_avg - _np.eye(3),coordinates0_point(F.shape[:3],size))
def displacement_point(size,F):
def displacement_point(size: _np.ndarray, F: _np.ndarray) -> _np.ndarray:
"""
Cell center displacement field from deformation gradient field.
@ -227,7 +233,7 @@ def displacement_point(size,F):
return displacement_avg_point(size,F) + displacement_fluct_point(size,F)
def coordinates_point(size,F,origin=_np.zeros(3)):
def coordinates_point(size: _np.ndarray, F: _np.ndarray, origin: _np.ndarray = _np.zeros(3)) -> _np.ndarray:
"""
Cell center positions.
@ -249,7 +255,8 @@ def coordinates_point(size,F,origin=_np.zeros(3)):
return coordinates0_point(F.shape[:3],size,origin) + displacement_point(size,F)
def cellsSizeOrigin_coordinates0_point(coordinates0,ordered=True):
def cellsSizeOrigin_coordinates0_point(coordinates0: _np.ndarray,
ordered: bool = True) -> Tuple[_np.ndarray,_np.ndarray,_np.ndarray]:
"""
Return grid 'DNA', i.e. cells, size, and origin from 1D array of point positions.
@ -292,13 +299,15 @@ def cellsSizeOrigin_coordinates0_point(coordinates0,ordered=True):
raise ValueError('Regular cell spacing violated.')
if ordered and not _np.allclose(coordinates0.reshape(tuple(cells)+(3,),order='F'),
coordinates0_point(cells,size,origin),atol=atol):
coordinates0_point(list(cells),size,origin),atol=atol):
raise ValueError('Input data is not ordered (x fast, z slow).')
return (cells,size,origin)
def coordinates0_node(cells,size,origin=_np.zeros(3)):
def coordinates0_node(cells: Union[_np.ndarray,Sequence[int]],
size: _np.ndarray,
origin: _np.ndarray = _np.zeros(3)) -> _np.ndarray:
"""
Nodal positions (undeformed).
@ -323,7 +332,7 @@ def coordinates0_node(cells,size,origin=_np.zeros(3)):
axis = -1)
def displacement_fluct_node(size,F):
def displacement_fluct_node(size: _np.ndarray, F: _np.ndarray) -> _np.ndarray:
"""
Nodal displacement field from fluctuation part of the deformation gradient field.
@ -343,7 +352,7 @@ def displacement_fluct_node(size,F):
return point_to_node(displacement_fluct_point(size,F))
def displacement_avg_node(size,F):
def displacement_avg_node(size: _np.ndarray, F: _np.ndarray) -> _np.ndarray:
"""
Nodal displacement field from average part of the deformation gradient field.
@ -364,7 +373,7 @@ def displacement_avg_node(size,F):
return _np.einsum('ml,ijkl->ijkm',F_avg - _np.eye(3),coordinates0_node(F.shape[:3],size))
def displacement_node(size,F):
def displacement_node(size: _np.ndarray, F: _np.ndarray) -> _np.ndarray:
"""
Nodal displacement field from deformation gradient field.
@ -384,7 +393,7 @@ def displacement_node(size,F):
return displacement_avg_node(size,F) + displacement_fluct_node(size,F)
def coordinates_node(size,F,origin=_np.zeros(3)):
def coordinates_node(size: _np.ndarray, F: _np.ndarray, origin: _np.ndarray = _np.zeros(3)) -> _np.ndarray:
"""
Nodal positions.
@ -406,7 +415,8 @@ def coordinates_node(size,F,origin=_np.zeros(3)):
return coordinates0_node(F.shape[:3],size,origin) + displacement_node(size,F)
def cellsSizeOrigin_coordinates0_node(coordinates0,ordered=True):
def cellsSizeOrigin_coordinates0_node(coordinates0: _np.ndarray,
ordered: bool = True) -> Tuple[_np.ndarray,_np.ndarray,_np.ndarray]:
"""
Return grid 'DNA', i.e. cells, size, and origin from 1D array of nodal positions.
@ -441,13 +451,13 @@ def cellsSizeOrigin_coordinates0_node(coordinates0,ordered=True):
raise ValueError('Regular cell spacing violated.')
if ordered and not _np.allclose(coordinates0.reshape(tuple(cells+1)+(3,),order='F'),
coordinates0_node(cells,size,origin),atol=atol):
coordinates0_node(list(cells),size,origin),atol=atol):
raise ValueError('Input data is not ordered (x fast, z slow).')
return (cells,size,origin)
def point_to_node(cell_data):
def point_to_node(cell_data: _np.ndarray) -> _np.ndarray:
"""
Interpolate periodic point data to nodal data.
@ -469,7 +479,7 @@ def point_to_node(cell_data):
return _np.pad(n,((0,1),(0,1),(0,1))+((0,0),)*len(cell_data.shape[3:]),mode='wrap')
def node_to_point(node_data):
def node_to_point(node_data: _np.ndarray) -> _np.ndarray:
"""
Interpolate periodic nodal data to point data.
@ -491,7 +501,7 @@ def node_to_point(node_data):
return c[1:,1:,1:]
def coordinates0_valid(coordinates0):
def coordinates0_valid(coordinates0: _np.ndarray) -> bool:
"""
Check whether coordinates form a regular grid.
@ -513,7 +523,7 @@ def coordinates0_valid(coordinates0):
return False
def regrid(size,F,cells):
def regrid(size: _np.ndarray, F: _np.ndarray, cells: Union[_np.ndarray,Sequence[int]]) -> _np.ndarray:
"""
Return mapping from coordinates in deformed configuration to a regular grid.

View File

@ -5,13 +5,15 @@ All routines operate on numpy.ndarrays of shape (...,3,3).
"""
from . import tensor as _tensor
from . import _rotation
from typing import Sequence
import numpy as _np
from . import tensor as _tensor
from . import _rotation
def deformation_Cauchy_Green_left(F):
def deformation_Cauchy_Green_left(F: _np.ndarray) -> _np.ndarray:
"""
Calculate left Cauchy-Green deformation tensor (Finger deformation tensor).
@ -29,7 +31,7 @@ def deformation_Cauchy_Green_left(F):
return _np.matmul(F,_tensor.transpose(F))
def deformation_Cauchy_Green_right(F):
def deformation_Cauchy_Green_right(F: _np.ndarray) -> _np.ndarray:
"""
Calculate right Cauchy-Green deformation tensor.
@ -47,7 +49,7 @@ def deformation_Cauchy_Green_right(F):
return _np.matmul(_tensor.transpose(F),F)
def equivalent_strain_Mises(epsilon):
def equivalent_strain_Mises(epsilon: _np.ndarray) -> _np.ndarray:
"""
Calculate the Mises equivalent of a strain tensor.
@ -65,7 +67,7 @@ def equivalent_strain_Mises(epsilon):
return _equivalent_Mises(epsilon,2.0/3.0)
def equivalent_stress_Mises(sigma):
def equivalent_stress_Mises(sigma: _np.ndarray) -> _np.ndarray:
"""
Calculate the Mises equivalent of a stress tensor.
@ -83,7 +85,7 @@ def equivalent_stress_Mises(sigma):
return _equivalent_Mises(sigma,3.0/2.0)
def maximum_shear(T_sym):
def maximum_shear(T_sym: _np.ndarray) -> _np.ndarray:
"""
Calculate the maximum shear component of a symmetric tensor.
@ -102,7 +104,7 @@ def maximum_shear(T_sym):
return (w[...,0] - w[...,2])*0.5
def rotation(T):
def rotation(T: _np.ndarray) -> _rotation.Rotation:
"""
Calculate the rotational part of a tensor.
@ -120,7 +122,7 @@ def rotation(T):
return _rotation.Rotation.from_matrix(_polar_decomposition(T,'R')[0])
def strain(F,t,m):
def strain(F: _np.ndarray, t: str, m: float) -> _np.ndarray:
"""
Calculate strain tensor (SethHill family).
@ -160,7 +162,7 @@ def strain(F,t,m):
return eps
def stress_Cauchy(P,F):
def stress_Cauchy(P: _np.ndarray, F: _np.ndarray) -> _np.ndarray:
"""
Calculate the Cauchy stress (true stress).
@ -182,7 +184,7 @@ def stress_Cauchy(P,F):
return _tensor.symmetric(_np.einsum('...,...ij,...kj',1.0/_np.linalg.det(F),P,F))
def stress_second_Piola_Kirchhoff(P,F):
def stress_second_Piola_Kirchhoff(P: _np.ndarray, F: _np.ndarray) -> _np.ndarray:
"""
Calculate the second Piola-Kirchhoff stress.
@ -205,7 +207,7 @@ def stress_second_Piola_Kirchhoff(P,F):
return _tensor.symmetric(_np.einsum('...ij,...jk',_np.linalg.inv(F),P))
def stretch_left(T):
def stretch_left(T: _np.ndarray) -> _np.ndarray:
"""
Calculate left stretch of a tensor.
@ -223,7 +225,7 @@ def stretch_left(T):
return _polar_decomposition(T,'V')[0]
def stretch_right(T):
def stretch_right(T: _np.ndarray) -> _np.ndarray:
"""
Calculate right stretch of a tensor.
@ -241,7 +243,7 @@ def stretch_right(T):
return _polar_decomposition(T,'U')[0]
def _polar_decomposition(T,requested):
def _polar_decomposition(T: _np.ndarray, requested: Sequence[str]) -> tuple:
"""
Perform singular value decomposition.
@ -257,21 +259,21 @@ def _polar_decomposition(T,requested):
u, _, vh = _np.linalg.svd(T)
R = _np.einsum('...ij,...jk',u,vh)
output = ()
output = []
if 'R' in requested:
output+=(R,)
output+=[R]
if 'V' in requested:
output+=(_np.einsum('...ij,...kj',T,R),)
output+=[_np.einsum('...ij,...kj',T,R)]
if 'U' in requested:
output+=(_np.einsum('...ji,...jk',R,T),)
output+=[_np.einsum('...ji,...jk',R,T)]
if len(output) == 0:
raise ValueError('output needs to be out of V, R, U')
return output
return tuple(output)
def _equivalent_Mises(T_sym,s):
def _equivalent_Mises(T_sym: _np.ndarray, s: float) -> _np.ndarray:
"""
Base equation for Mises equivalent of a stress or strain tensor.

View File

@ -1,5 +1,7 @@
"""Functionality for generation of seed points for Voronoi or Laguerre tessellation."""
from typing import Sequence,Tuple
from scipy import spatial as _spatial
import numpy as _np
@ -7,7 +9,7 @@ from . import util as _util
from . import grid_filters as _grid_filters
def from_random(size,N_seeds,cells=None,rng_seed=None):
def from_random(size: _np.ndarray, N_seeds: int, cells: _np.ndarray = None, rng_seed=None) -> _np.ndarray:
"""
Place seeds randomly in space.
@ -41,7 +43,8 @@ def from_random(size,N_seeds,cells=None,rng_seed=None):
return coords
def from_Poisson_disc(size,N_seeds,N_candidates,distance,periodic=True,rng_seed=None):
def from_Poisson_disc(size: _np.ndarray, N_seeds: int, N_candidates: int, distance: float,
periodic: bool = True, rng_seed=None) -> _np.ndarray:
"""
Place seeds according to a Poisson disc distribution.
@ -75,18 +78,17 @@ def from_Poisson_disc(size,N_seeds,N_candidates,distance,periodic=True,rng_seed=
i = 0
progress = _util._ProgressBar(N_seeds+1,'',50)
while s < N_seeds:
i += 1
candidates = rng.random((N_candidates,3))*_np.broadcast_to(size,(N_candidates,3))
tree = _spatial.cKDTree(coords[:s],boxsize=size) if periodic else \
_spatial.cKDTree(coords[:s])
distances = tree.query(candidates)[0]
best = distances.argmax()
if distances[best] > distance: # require minimum separation
i = 0
coords[s] = candidates[best] # maximum separation to existing point cloud
s += 1
progress.update(s)
i = 0
else:
i += 1
if i == 100:
raise ValueError('Seeding not possible')
@ -94,22 +96,23 @@ def from_Poisson_disc(size,N_seeds,N_candidates,distance,periodic=True,rng_seed=
return coords
def from_grid(grid,selection=None,invert=False,average=False,periodic=True):
def from_grid(grid, selection: Sequence[int] = None,
invert: bool = False, average: bool = False, periodic: bool = True) -> Tuple[_np.ndarray, _np.ndarray]:
"""
Create seeds from grid description.
Parameters
----------
grid : damask.Grid
Grid, from which the material IDs are used as seeds.
Grid from which the material IDs are used as seeds.
selection : iterable of integers, optional
Material IDs to consider.
invert : boolean, false
Do not consider the material IDs given in selection. Defaults to False.
Consider all material IDs except those in selection. Defaults to False.
average : boolean, optional
Seed corresponds to center of gravity of material ID cloud.
periodic : boolean, optional
Center of gravity with periodic boundaries.
Center of gravity accounts for periodic boundaries.
Returns
-------

View File

@ -8,7 +8,7 @@ All routines operate on numpy.ndarrays of shape (...,3,3).
import numpy as _np
def deviatoric(T):
def deviatoric(T: _np.ndarray) -> _np.ndarray:
"""
Calculate deviatoric part of a tensor.
@ -26,7 +26,7 @@ def deviatoric(T):
return T - spherical(T,tensor=True)
def eigenvalues(T_sym):
def eigenvalues(T_sym: _np.ndarray) -> _np.ndarray:
"""
Eigenvalues, i.e. principal components, of a symmetric tensor.
@ -45,7 +45,7 @@ def eigenvalues(T_sym):
return _np.linalg.eigvalsh(symmetric(T_sym))
def eigenvectors(T_sym,RHS=False):
def eigenvectors(T_sym: _np.ndarray, RHS: bool = False) -> _np.ndarray:
"""
Eigenvectors of a symmetric tensor.
@ -70,7 +70,7 @@ def eigenvectors(T_sym,RHS=False):
return v
def spherical(T,tensor=True):
def spherical(T: _np.ndarray, tensor: bool = True) -> _np.ndarray:
"""
Calculate spherical part of a tensor.
@ -92,7 +92,7 @@ def spherical(T,tensor=True):
return _np.einsum('...jk,...',_np.eye(3),sph) if tensor else sph
def symmetric(T):
def symmetric(T: _np.ndarray) -> _np.ndarray:
"""
Symmetrize tensor.
@ -110,7 +110,7 @@ def symmetric(T):
return (T+transpose(T))*0.5
def transpose(T):
def transpose(T: _np.ndarray) -> _np.ndarray:
"""
Transpose tensor.

14
python/mypy.ini Normal file
View File

@ -0,0 +1,14 @@
[mypy-scipy.*]
ignore_missing_imports = True
[mypy-h5py.*]
ignore_missing_imports = True
[mypy-vtk.*]
ignore_missing_imports = True
[mypy-PIL.*]
ignore_missing_imports = True
[mypy-matplotlib.*]
ignore_missing_imports = True
[mypy-pandas.*]
ignore_missing_imports = True
[mypy-wx.*]
ignore_missing_imports = True