Merge branch 'PythonProspectorRules' into even-more-HDF5-postprocessing
This commit is contained in:
commit
f415a8cad2
2
PRIVATE
2
PRIVATE
|
@ -1 +1 @@
|
||||||
Subproject commit 18a976753be06aca6e15f580998e713daa08bb41
|
Subproject commit e8c935312c9ff22873f792282ff13e5a991752d3
|
|
@ -1,8 +1,9 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: UTF-8 no BOM -*-
|
|
||||||
|
|
||||||
# Makes postprocessing routines acessible from everywhere.
|
# Makes postprocessing routines accessible from everywhere.
|
||||||
import os,sys
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
import damask
|
import damask
|
||||||
|
|
||||||
damaskEnv = damask.Environment()
|
damaskEnv = damask.Environment()
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# Code below available according to the followin conditions on https://github.com/MarDiehl/3Drotations
|
# Code below available according to the following conditions on
|
||||||
|
# https://github.com/MarDiehl/3Drotations
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# Copyright (c) 2017-2019, Martin Diehl/Max-Planck-Institut für Eisenforschung GmbH
|
# Copyright (c) 2017-2019, Martin Diehl/Max-Planck-Institut für Eisenforschung GmbH
|
||||||
# Copyright (c) 2013-2014, Marc De Graef/Carnegie Mellon University
|
# Copyright (c) 2013-2014, Marc De Graef/Carnegie Mellon University
|
||||||
|
@ -36,7 +37,20 @@ beta = np.pi**(5./6.)/6.**(1./6.)/2.
|
||||||
R1 = (3.*np.pi/4.)**(1./3.)
|
R1 = (3.*np.pi/4.)**(1./3.)
|
||||||
|
|
||||||
def CubeToBall(cube):
|
def CubeToBall(cube):
|
||||||
|
"""
|
||||||
|
Map a point in a uniform refinable cubical grid to a point on a uniform refinable grid on a ball.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
cube : numpy.ndarray
|
||||||
|
coordinates of a point in a uniform refinable cubical grid.
|
||||||
|
|
||||||
|
References
|
||||||
|
----------
|
||||||
|
D. Roşca et al., Modelling and Simulation in Materials Science and Engineering 22:075013, 2014
|
||||||
|
https://doi.org/10.1088/0965-0393/22/7/075013
|
||||||
|
|
||||||
|
"""
|
||||||
if np.abs(np.max(cube))>np.pi**(2./3.) * 0.5:
|
if np.abs(np.max(cube))>np.pi**(2./3.) * 0.5:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
|
@ -45,7 +59,7 @@ def CubeToBall(cube):
|
||||||
ball = np.zeros(3)
|
ball = np.zeros(3)
|
||||||
else:
|
else:
|
||||||
# get pyramide and scale by grid parameter ratio
|
# get pyramide and scale by grid parameter ratio
|
||||||
p = GetPyramidOrder(cube)
|
p = get_order(cube)
|
||||||
XYZ = cube[p] * sc
|
XYZ = cube[p] * sc
|
||||||
|
|
||||||
# intercept all the points along the z-axis
|
# intercept all the points along the z-axis
|
||||||
|
@ -74,7 +88,20 @@ def CubeToBall(cube):
|
||||||
|
|
||||||
|
|
||||||
def BallToCube(ball):
|
def BallToCube(ball):
|
||||||
|
"""
|
||||||
|
Map a point on a uniform refinable grid on a ball to a point in a uniform refinable cubical grid.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
ball : numpy.ndarray
|
||||||
|
coordinates of a point on a uniform refinable grid on a ball.
|
||||||
|
|
||||||
|
References
|
||||||
|
----------
|
||||||
|
D. Roşca et al., Modelling and Simulation in Materials Science and Engineering 22:075013, 2014
|
||||||
|
https://doi.org/10.1088/0965-0393/22/7/075013
|
||||||
|
|
||||||
|
"""
|
||||||
rs = np.linalg.norm(ball)
|
rs = np.linalg.norm(ball)
|
||||||
if rs > R1:
|
if rs > R1:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
@ -82,7 +109,7 @@ def BallToCube(ball):
|
||||||
if np.allclose(ball,0.0,rtol=0.0,atol=1.0e-300):
|
if np.allclose(ball,0.0,rtol=0.0,atol=1.0e-300):
|
||||||
cube = np.zeros(3)
|
cube = np.zeros(3)
|
||||||
else:
|
else:
|
||||||
p = GetPyramidOrder(ball)
|
p = get_order(ball)
|
||||||
xyz3 = ball[p]
|
xyz3 = ball[p]
|
||||||
|
|
||||||
# inverse M_3
|
# inverse M_3
|
||||||
|
@ -110,8 +137,24 @@ def BallToCube(ball):
|
||||||
|
|
||||||
return cube
|
return cube
|
||||||
|
|
||||||
def GetPyramidOrder(xyz):
|
def get_order(xyz):
|
||||||
|
"""
|
||||||
|
Get order of the coordinates.
|
||||||
|
|
||||||
|
Depending on the pyramid in which the point is located, the order need to be adjusted.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
xyz : numpy.ndarray
|
||||||
|
coordinates of a point on a uniform refinable grid on a ball or
|
||||||
|
in a uniform refinable cubical grid.
|
||||||
|
|
||||||
|
References
|
||||||
|
----------
|
||||||
|
D. Roşca et al., Modelling and Simulation in Materials Science and Engineering 22:075013, 2014
|
||||||
|
https://doi.org/10.1088/0965-0393/22/7/075013
|
||||||
|
|
||||||
|
"""
|
||||||
if (abs(xyz[0])<= xyz[2]) and (abs(xyz[1])<= xyz[2]) or \
|
if (abs(xyz[0])<= xyz[2]) and (abs(xyz[1])<= xyz[2]) or \
|
||||||
(abs(xyz[0])<=-xyz[2]) and (abs(xyz[1])<=-xyz[2]):
|
(abs(xyz[0])<=-xyz[2]) and (abs(xyz[1])<=-xyz[2]):
|
||||||
return [0,1,2]
|
return [0,1,2]
|
||||||
|
|
|
@ -3,13 +3,7 @@ import math
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
class Color():
|
class Color():
|
||||||
"""
|
"""Color representation in and conversion between different color-spaces."""
|
||||||
Conversion of colors between different color-spaces.
|
|
||||||
|
|
||||||
Colors should be given in the form Color('model',[vector]).
|
|
||||||
To convert or copy color from one space to other, use the methods
|
|
||||||
convertTo('model') or expressAs('model'), respectively.
|
|
||||||
"""
|
|
||||||
|
|
||||||
__slots__ = [
|
__slots__ = [
|
||||||
'model',
|
'model',
|
||||||
|
@ -22,7 +16,17 @@ class Color():
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
model = 'RGB',
|
model = 'RGB',
|
||||||
color = np.zeros(3,'d')):
|
color = np.zeros(3,'d')):
|
||||||
|
"""
|
||||||
|
Create a Color object.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
model : string
|
||||||
|
color model
|
||||||
|
color : numpy.ndarray
|
||||||
|
vector representing the color according to the selected model
|
||||||
|
|
||||||
|
"""
|
||||||
self.__transforms__ = \
|
self.__transforms__ = \
|
||||||
{'HSV': {'index': 0, 'next': self._HSV2HSL},
|
{'HSV': {'index': 0, 'next': self._HSV2HSL},
|
||||||
'HSL': {'index': 1, 'next': self._HSL2RGB, 'prev': self._HSL2HSV},
|
'HSL': {'index': 1, 'next': self._HSL2RGB, 'prev': self._HSL2HSV},
|
||||||
|
@ -49,18 +53,27 @@ class Color():
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
"""Color model and values"""
|
"""Color model and values."""
|
||||||
return 'Model: %s Color: %s'%(self.model,str(self.color))
|
return 'Model: %s Color: %s'%(self.model,str(self.color))
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""Color model and values"""
|
"""Color model and values."""
|
||||||
return self.__repr__()
|
return self.__repr__()
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
def convertTo(self,toModel = 'RGB'):
|
def convertTo(self,toModel = 'RGB'):
|
||||||
|
"""
|
||||||
|
Change the color model permanently.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
toModel : string
|
||||||
|
color model
|
||||||
|
|
||||||
|
"""
|
||||||
toModel = toModel.upper()
|
toModel = toModel.upper()
|
||||||
if toModel not in list(self.__transforms__.keys()): return
|
if toModel not in list(self.__transforms__.keys()): return
|
||||||
|
|
||||||
|
@ -79,16 +92,25 @@ class Color():
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
def expressAs(self,asModel = 'RGB'):
|
def expressAs(self,asModel = 'RGB'):
|
||||||
|
"""
|
||||||
|
Return the color in a different model.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
asModel : string
|
||||||
|
color model
|
||||||
|
|
||||||
|
"""
|
||||||
return self.__class__(self.model,self.color).convertTo(asModel)
|
return self.__class__(self.model,self.color).convertTo(asModel)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _HSV2HSL(self):
|
def _HSV2HSL(self):
|
||||||
"""
|
"""
|
||||||
Convert H(ue) S(aturation) V(alue or brightness) to H(ue) S(aturation) L(uminance)
|
Convert H(ue) S(aturation) V(alue or brightness) to H(ue) S(aturation) L(uminance).
|
||||||
|
|
||||||
with all values in the range of 0 to 1
|
All values are in the range [0,1]
|
||||||
http://codeitdown.com/hsl-hsb-hsv-color/
|
http://codeitdown.com/hsl-hsb-hsv-color
|
||||||
"""
|
"""
|
||||||
if self.model != 'HSV': return
|
if self.model != 'HSV': return
|
||||||
|
|
||||||
|
@ -105,10 +127,10 @@ class Color():
|
||||||
|
|
||||||
def _HSL2HSV(self):
|
def _HSL2HSV(self):
|
||||||
"""
|
"""
|
||||||
Convert H(ue) S(aturation) L(uminance) to H(ue) S(aturation) V(alue or brightness)
|
Convert H(ue) S(aturation) L(uminance) to H(ue) S(aturation) V(alue or brightness).
|
||||||
|
|
||||||
with all values in the range of 0 to 1
|
All values are in the range [0,1]
|
||||||
http://codeitdown.com/hsl-hsb-hsv-color/
|
http://codeitdown.com/hsl-hsb-hsv-color
|
||||||
"""
|
"""
|
||||||
if self.model != 'HSL': return
|
if self.model != 'HSL': return
|
||||||
|
|
||||||
|
@ -124,9 +146,9 @@ class Color():
|
||||||
|
|
||||||
def _HSL2RGB(self):
|
def _HSL2RGB(self):
|
||||||
"""
|
"""
|
||||||
Convert H(ue) S(aturation) L(uminance) to R(red) G(reen) B(lue)
|
Convert H(ue) S(aturation) L(uminance) to R(red) G(reen) B(lue).
|
||||||
|
|
||||||
with all values in the range of 0 to 1
|
All values are in the range [0,1]
|
||||||
from http://en.wikipedia.org/wiki/HSL_and_HSV
|
from http://en.wikipedia.org/wiki/HSL_and_HSV
|
||||||
"""
|
"""
|
||||||
if self.model != 'HSL': return
|
if self.model != 'HSL': return
|
||||||
|
@ -150,9 +172,9 @@ class Color():
|
||||||
|
|
||||||
def _RGB2HSL(self):
|
def _RGB2HSL(self):
|
||||||
"""
|
"""
|
||||||
Convert R(ed) G(reen) B(lue) to H(ue) S(aturation) L(uminance)
|
Convert R(ed) G(reen) B(lue) to H(ue) S(aturation) L(uminance).
|
||||||
|
|
||||||
with all values in the range of 0 to 1
|
All values are in the range [0,1]
|
||||||
from http://130.113.54.154/~monger/hsl-rgb.html
|
from http://130.113.54.154/~monger/hsl-rgb.html
|
||||||
"""
|
"""
|
||||||
if self.model != 'RGB': return
|
if self.model != 'RGB': return
|
||||||
|
@ -190,9 +212,9 @@ class Color():
|
||||||
|
|
||||||
def _RGB2XYZ(self):
|
def _RGB2XYZ(self):
|
||||||
"""
|
"""
|
||||||
Convert R(ed) G(reen) B(lue) to CIE XYZ
|
Convert R(ed) G(reen) B(lue) to CIE XYZ.
|
||||||
|
|
||||||
with all values in the range of 0 to 1
|
All values are in the range [0,1]
|
||||||
from http://www.cs.rit.edu/~ncs/color/t_convert.html
|
from http://www.cs.rit.edu/~ncs/color/t_convert.html
|
||||||
"""
|
"""
|
||||||
if self.model != 'RGB': return
|
if self.model != 'RGB': return
|
||||||
|
@ -219,9 +241,9 @@ class Color():
|
||||||
|
|
||||||
def _XYZ2RGB(self):
|
def _XYZ2RGB(self):
|
||||||
"""
|
"""
|
||||||
Convert CIE XYZ to R(ed) G(reen) B(lue)
|
Convert CIE XYZ to R(ed) G(reen) B(lue).
|
||||||
|
|
||||||
with all values in the range of 0 to 1
|
All values are in the range [0,1]
|
||||||
from http://www.cs.rit.edu/~ncs/color/t_convert.html
|
from http://www.cs.rit.edu/~ncs/color/t_convert.html
|
||||||
"""
|
"""
|
||||||
if self.model != 'XYZ':
|
if self.model != 'XYZ':
|
||||||
|
@ -251,9 +273,9 @@ class Color():
|
||||||
|
|
||||||
def _CIELAB2XYZ(self):
|
def _CIELAB2XYZ(self):
|
||||||
"""
|
"""
|
||||||
Convert CIE Lab to CIE XYZ
|
Convert CIE Lab to CIE XYZ.
|
||||||
|
|
||||||
with XYZ in the range of 0 to 1
|
All values are in the range [0,1]
|
||||||
from http://www.easyrgb.com/index.php?X=MATH&H=07#text7
|
from http://www.easyrgb.com/index.php?X=MATH&H=07#text7
|
||||||
"""
|
"""
|
||||||
if self.model != 'CIELAB': return
|
if self.model != 'CIELAB': return
|
||||||
|
@ -275,11 +297,11 @@ class Color():
|
||||||
|
|
||||||
def _XYZ2CIELAB(self):
|
def _XYZ2CIELAB(self):
|
||||||
"""
|
"""
|
||||||
Convert CIE XYZ to CIE Lab
|
Convert CIE XYZ to CIE Lab.
|
||||||
|
|
||||||
with XYZ in the range of 0 to 1
|
All values are in the range [0,1]
|
||||||
from http://en.wikipedia.org/wiki/Lab_color_space,
|
from http://en.wikipedia.org/wiki/Lab_color_space,
|
||||||
http://www.cs.rit.edu/~ncs/color/t_convert.html
|
http://www.cs.rit.edu/~ncs/color/t_convert.html
|
||||||
"""
|
"""
|
||||||
if self.model != 'XYZ': return
|
if self.model != 'XYZ': return
|
||||||
|
|
||||||
|
@ -299,7 +321,7 @@ class Color():
|
||||||
|
|
||||||
def _CIELAB2MSH(self):
|
def _CIELAB2MSH(self):
|
||||||
"""
|
"""
|
||||||
Convert CIE Lab to Msh colorspace
|
Convert CIE Lab to Msh colorspace.
|
||||||
|
|
||||||
from http://www.cs.unm.edu/~kmorel/documents/ColorMaps/DivergingColorMapWorkshop.xls
|
from http://www.cs.unm.edu/~kmorel/documents/ColorMaps/DivergingColorMapWorkshop.xls
|
||||||
"""
|
"""
|
||||||
|
@ -319,7 +341,7 @@ class Color():
|
||||||
|
|
||||||
def _MSH2CIELAB(self):
|
def _MSH2CIELAB(self):
|
||||||
"""
|
"""
|
||||||
Convert Msh colorspace to CIE Lab
|
Convert Msh colorspace to CIE Lab.
|
||||||
|
|
||||||
with s,h in radians
|
with s,h in radians
|
||||||
from http://www.cs.unm.edu/~kmorel/documents/ColorMaps/DivergingColorMapWorkshop.xls
|
from http://www.cs.unm.edu/~kmorel/documents/ColorMaps/DivergingColorMapWorkshop.xls
|
||||||
|
@ -337,7 +359,7 @@ class Color():
|
||||||
|
|
||||||
|
|
||||||
class Colormap():
|
class Colormap():
|
||||||
"""Perceptually uniform diverging or sequential colormaps."""
|
"""Perceptually uniform diverging or sequential colormap."""
|
||||||
|
|
||||||
__slots__ = [
|
__slots__ = [
|
||||||
'left',
|
'left',
|
||||||
|
@ -394,7 +416,21 @@ class Colormap():
|
||||||
interpolate = 'perceptualuniform',
|
interpolate = 'perceptualuniform',
|
||||||
predefined = None
|
predefined = None
|
||||||
):
|
):
|
||||||
|
"""
|
||||||
|
Create a Colormap object.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
left : Color
|
||||||
|
left color (minimum value)
|
||||||
|
right : Color
|
||||||
|
right color (maximum value)
|
||||||
|
interpolate : str
|
||||||
|
interpolation scheme (either 'perceptualuniform' or 'linear')
|
||||||
|
predefined : bool
|
||||||
|
ignore other arguments and use predefined definition
|
||||||
|
|
||||||
|
"""
|
||||||
if predefined is not None:
|
if predefined is not None:
|
||||||
left = self.__predefined__[predefined.lower()]['left']
|
left = self.__predefined__[predefined.lower()]['left']
|
||||||
right= self.__predefined__[predefined.lower()]['right']
|
right= self.__predefined__[predefined.lower()]['right']
|
||||||
|
@ -412,16 +448,22 @@ class Colormap():
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
"""Left and right value of colormap"""
|
"""Left and right value of colormap."""
|
||||||
return 'Left: %s Right: %s'%(self.left,self.right)
|
return 'Left: %s Right: %s'%(self.left,self.right)
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
def invert(self):
|
def invert(self):
|
||||||
|
"""Switch left/minimum with right/maximum."""
|
||||||
(self.left, self.right) = (self.right, self.left)
|
(self.left, self.right) = (self.right, self.left)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
def show_predefined(self):
|
||||||
|
"""Show the labels of the predefined colormaps."""
|
||||||
|
print('\n'.join(self.__predefined__.keys()))
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
def color(self,fraction = 0.5):
|
def color(self,fraction = 0.5):
|
||||||
|
|
||||||
|
@ -490,21 +532,22 @@ class Colormap():
|
||||||
frac = 0.5*(np.array(crop) + 1.0) # rescale crop range to fractions
|
frac = 0.5*(np.array(crop) + 1.0) # rescale crop range to fractions
|
||||||
colors = [self.color(float(i)/(steps-1)*(frac[1]-frac[0])+frac[0]).expressAs(model).color for i in range(steps)]
|
colors = [self.color(float(i)/(steps-1)*(frac[1]-frac[0])+frac[0]).expressAs(model).color for i in range(steps)]
|
||||||
if format == 'paraview':
|
if format == 'paraview':
|
||||||
colormap = ['[\n {{\n "ColorSpace" : "RGB", "Name" : "{}",\n "RGBPoints" : ['.format(name)] \
|
colormap = ['[\n {{\n "ColorSpace": "RGB", "Name": "{}", "DefaultMap": true,\n "RGBPoints" : ['.format(name)] \
|
||||||
+ [' {:4d},{:8.6f},{:8.6f},{:8.6f},'.format(i,color[0],color[1],color[2],)
|
+ [' {:4d},{:8.6f},{:8.6f},{:8.6f},'.format(i,color[0],color[1],color[2],) \
|
||||||
for i,color in enumerate(colors[:-1])]\
|
for i,color in enumerate(colors[:-1])] \
|
||||||
+ [' {:4d},{:8.6f},{:8.6f},{:8.6f} '.format(len(colors),colors[-1][0],colors[-1][1],colors[-1][2],)]\
|
+ [' {:4d},{:8.6f},{:8.6f},{:8.6f} '.format(len(colors),colors[-1][0],colors[-1][1],colors[-1][2],)] \
|
||||||
+ [' ]\n }\n]']
|
+ [' ]\n }\n]']
|
||||||
|
|
||||||
elif format == 'gmsh':
|
elif format == 'gmsh':
|
||||||
colormap = ['View.ColorTable = {'] \
|
colormap = ['View.ColorTable = {'] \
|
||||||
+ [',\n'.join(['{%s}'%(','.join([str(x*255.0) for x in color])) for color in colors])] \
|
+ [',\n'.join(['{%s}'%(','.join([str(x*255.0) for x in color])) for color in colors])] \
|
||||||
+ ['}']
|
+ ['}']
|
||||||
|
|
||||||
elif format == 'gom':
|
elif format == 'gom':
|
||||||
colormap = ['1 1 ' + str(name) \
|
colormap = ['1 1 ' + str(name)
|
||||||
+ ' 9 ' + str(name) \
|
+ ' 9 ' + str(name)
|
||||||
+ ' 0 1 0 3 0 0 -1 9 \ 0 0 0 255 255 255 0 0 255 ' \
|
+ ' 0 1 0 3 0 0 -1 9 \\ 0 0 0 255 255 255 0 0 255 '
|
||||||
+ '30 NO_UNIT 1 1 64 64 64 255 1 0 0 0 0 0 0 3 0 ' + str(len(colors)) \
|
+ '30 NO_UNIT 1 1 64 64 64 255 1 0 0 0 0 0 0 3 0 ' + str(len(colors))
|
||||||
+ ' '.join([' 0 %s 255 1'%(' '.join([str(int(x*255.0)) for x in color])) for color in reversed(colors)])]
|
+ ' '.join([' 0 %s 255 1'%(' '.join([str(int(x*255.0)) for x in color])) for color in reversed(colors)])]
|
||||||
|
|
||||||
elif format == 'raw':
|
elif format == 'raw':
|
||||||
|
|
|
@ -10,10 +10,27 @@ from . import version
|
||||||
|
|
||||||
|
|
||||||
class Geom():
|
class Geom():
|
||||||
"""Geometry definition for grid solvers"""
|
"""Geometry definition for grid solvers."""
|
||||||
|
|
||||||
def __init__(self,microstructure,size,origin=[0.0,0.0,0.0],homogenization=1,comments=[]):
|
def __init__(self,microstructure,size,origin=[0.0,0.0,0.0],homogenization=1,comments=[]):
|
||||||
"""New geometry definition from array of microstructures and size"""
|
"""
|
||||||
|
New geometry definition from array of microstructures and size.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
microstructure : numpy.ndarray
|
||||||
|
microstructure array (3D)
|
||||||
|
size : list or numpy.ndarray
|
||||||
|
physical size of the microstructure in meter.
|
||||||
|
origin : list or numpy.ndarray, optional
|
||||||
|
physical origin of the microstructure in meter.
|
||||||
|
homogenization : integer, optional
|
||||||
|
homogenization index.
|
||||||
|
comments : list of str, optional
|
||||||
|
comments lines.
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.__transforms__ = \
|
||||||
self.set_microstructure(microstructure)
|
self.set_microstructure(microstructure)
|
||||||
self.set_size(size)
|
self.set_size(size)
|
||||||
self.set_origin(origin)
|
self.set_origin(origin)
|
||||||
|
@ -22,7 +39,7 @@ class Geom():
|
||||||
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
"""Basic information on geometry definition"""
|
"""Basic information on geometry definition."""
|
||||||
return util.srepr([
|
return util.srepr([
|
||||||
'grid a b c: {}'.format(' x '.join(map(str,self.get_grid ()))),
|
'grid a b c: {}'.format(' x '.join(map(str,self.get_grid ()))),
|
||||||
'size x y z: {}'.format(' x '.join(map(str,self.get_size ()))),
|
'size x y z: {}'.format(' x '.join(map(str,self.get_size ()))),
|
||||||
|
@ -33,7 +50,21 @@ class Geom():
|
||||||
])
|
])
|
||||||
|
|
||||||
def update(self,microstructure=None,size=None,origin=None,rescale=False):
|
def update(self,microstructure=None,size=None,origin=None,rescale=False):
|
||||||
"""Updates microstructure and size"""
|
"""
|
||||||
|
Updates microstructure and size.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
microstructure : numpy.ndarray, optional
|
||||||
|
microstructure array (3D).
|
||||||
|
size : list or numpy.ndarray, optional
|
||||||
|
physical size of the microstructure in meter.
|
||||||
|
origin : list or numpy.ndarray, optional
|
||||||
|
physical origin of the microstructure in meter.
|
||||||
|
rescale : bool, optional
|
||||||
|
ignore size parameter and rescale according to change of grid points.
|
||||||
|
|
||||||
|
"""
|
||||||
grid_old = self.get_grid()
|
grid_old = self.get_grid()
|
||||||
size_old = self.get_size()
|
size_old = self.get_size()
|
||||||
origin_old = self.get_origin()
|
origin_old = self.get_origin()
|
||||||
|
@ -42,48 +73,78 @@ class Geom():
|
||||||
|
|
||||||
if size is not None and rescale:
|
if size is not None and rescale:
|
||||||
raise ValueError('Either set size explicitly or rescale automatically')
|
raise ValueError('Either set size explicitly or rescale automatically')
|
||||||
|
elif size is not None:
|
||||||
|
self.set_size(size)
|
||||||
|
elif rescale:
|
||||||
|
self.set_size(self.get_grid()/grid_old*self.size)
|
||||||
|
|
||||||
self.set_microstructure(microstructure)
|
self.set_microstructure(microstructure)
|
||||||
self.set_size(self.get_grid()/grid_old*self.size if rescale else size)
|
|
||||||
self.set_origin(origin)
|
self.set_origin(origin)
|
||||||
|
|
||||||
message = ['grid a b c: {}'.format(' x '.join(map(str,grid_old)))]
|
message = ['grid a b c: {}'.format(' x '.join(map(str,grid_old)))]
|
||||||
if np.any(grid_old != self.get_grid()):
|
if np.any(grid_old != self.get_grid()):
|
||||||
message[-1] = util.delete(message[-1])
|
message[-1] = util.delete(message[-1])
|
||||||
message.append('grid a b c: {}'.format(' x '.join(map(str,self.get_grid()))))
|
message.append(util.emph('grid a b c: {}'.format(' x '.join(map(str,self.get_grid())))))
|
||||||
|
|
||||||
message.append('size x y z: {}'.format(' x '.join(map(str,size_old))))
|
message.append('size x y z: {}'.format(' x '.join(map(str,size_old))))
|
||||||
if np.any(size_old != self.get_size()):
|
if np.any(size_old != self.get_size()):
|
||||||
message[-1] = util.delete(message[-1])
|
message[-1] = util.delete(message[-1])
|
||||||
message.append('size x y z: {}'.format(' x '.join(map(str,self.get_size()))))
|
message.append(util.emph('size x y z: {}'.format(' x '.join(map(str,self.get_size())))))
|
||||||
|
|
||||||
message.append('origin x y z: {}'.format(' '.join(map(str,origin_old))))
|
message.append('origin x y z: {}'.format(' '.join(map(str,origin_old))))
|
||||||
if np.any(origin_old != self.get_origin()):
|
if np.any(origin_old != self.get_origin()):
|
||||||
message[-1] = util.delete(message[-1])
|
message[-1] = util.delete(message[-1])
|
||||||
message.append('origin x y z: {}'.format(' '.join(map(str,self.get_origin()))))
|
message.append(util.emph('origin x y z: {}'.format(' '.join(map(str,self.get_origin())))))
|
||||||
|
|
||||||
message.append('homogenization: {}'.format(self.get_homogenization()))
|
message.append('homogenization: {}'.format(self.get_homogenization()))
|
||||||
|
|
||||||
message.append('# microstructures: {}'.format(unique_old))
|
message.append('# microstructures: {}'.format(unique_old))
|
||||||
if unique_old != len(np.unique(self.microstructure)):
|
if unique_old != len(np.unique(self.microstructure)):
|
||||||
message[-1] = util.delete(message[-1])
|
message[-1] = util.delete(message[-1])
|
||||||
message.append('# microstructures: {}'.format(len(np.unique(self.microstructure))))
|
message.append(util.emph('# microstructures: {}'.format(len(np.unique(self.microstructure)))))
|
||||||
|
|
||||||
message.append('max microstructure: {}'.format(max_old))
|
message.append('max microstructure: {}'.format(max_old))
|
||||||
if max_old != np.nanmax(self.microstructure):
|
if max_old != np.nanmax(self.microstructure):
|
||||||
message[-1] = util.delete(message[-1])
|
message[-1] = util.delete(message[-1])
|
||||||
message.append('max microstructure: {}'.format(np.nanmax(self.microstructure)))
|
message.append(util.emph('max microstructure: {}'.format(np.nanmax(self.microstructure))))
|
||||||
|
|
||||||
return util.return_message(message)
|
return util.return_message(message)
|
||||||
|
|
||||||
def set_comments(self,comments):
|
def set_comments(self,comments):
|
||||||
|
"""
|
||||||
|
Replaces all existing comments.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
comments : list of str
|
||||||
|
new comments.
|
||||||
|
|
||||||
|
"""
|
||||||
self.comments = []
|
self.comments = []
|
||||||
self.add_comments(comments)
|
self.add_comments(comments)
|
||||||
|
|
||||||
def add_comments(self,comments):
|
def add_comments(self,comments):
|
||||||
|
"""
|
||||||
|
Appends comments to existing comments.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
comments : list of str
|
||||||
|
new comments.
|
||||||
|
|
||||||
|
"""
|
||||||
self.comments += [str(c) for c in comments] if isinstance(comments,list) else [str(comments)]
|
self.comments += [str(c) for c in comments] if isinstance(comments,list) else [str(comments)]
|
||||||
|
|
||||||
def set_microstructure(self,microstructure):
|
def set_microstructure(self,microstructure):
|
||||||
|
"""
|
||||||
|
Replaces the existing microstructure representation.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
microstructure : numpy.ndarray
|
||||||
|
microstructure array (3D).
|
||||||
|
|
||||||
|
"""
|
||||||
if microstructure is not None:
|
if microstructure is not None:
|
||||||
if len(microstructure.shape) != 3:
|
if len(microstructure.shape) != 3:
|
||||||
raise ValueError('Invalid microstructure shape {}'.format(*microstructure.shape))
|
raise ValueError('Invalid microstructure shape {}'.format(*microstructure.shape))
|
||||||
|
@ -93,6 +154,15 @@ class Geom():
|
||||||
self.microstructure = np.copy(microstructure)
|
self.microstructure = np.copy(microstructure)
|
||||||
|
|
||||||
def set_size(self,size):
|
def set_size(self,size):
|
||||||
|
"""
|
||||||
|
Replaces the existing size information.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
size : list or numpy.ndarray
|
||||||
|
physical size of the microstructure in meter.
|
||||||
|
|
||||||
|
"""
|
||||||
if size is None:
|
if size is None:
|
||||||
grid = np.asarray(self.microstructure.shape)
|
grid = np.asarray(self.microstructure.shape)
|
||||||
self.size = grid/np.max(grid)
|
self.size = grid/np.max(grid)
|
||||||
|
@ -103,6 +173,15 @@ class Geom():
|
||||||
self.size = np.array(size)
|
self.size = np.array(size)
|
||||||
|
|
||||||
def set_origin(self,origin):
|
def set_origin(self,origin):
|
||||||
|
"""
|
||||||
|
Replaces the existing origin information.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
origin : list or numpy.ndarray
|
||||||
|
physical origin of the microstructure in meter
|
||||||
|
|
||||||
|
"""
|
||||||
if origin is not None:
|
if origin is not None:
|
||||||
if len(origin) != 3:
|
if len(origin) != 3:
|
||||||
raise ValueError('Invalid origin {}'.format(*origin))
|
raise ValueError('Invalid origin {}'.format(*origin))
|
||||||
|
@ -110,6 +189,15 @@ class Geom():
|
||||||
self.origin = np.array(origin)
|
self.origin = np.array(origin)
|
||||||
|
|
||||||
def set_homogenization(self,homogenization):
|
def set_homogenization(self,homogenization):
|
||||||
|
"""
|
||||||
|
Replaces the existing homogenization index.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
homogenization : integer
|
||||||
|
homogenization index
|
||||||
|
|
||||||
|
"""
|
||||||
if homogenization is not None:
|
if homogenization is not None:
|
||||||
if not isinstance(homogenization,int) or homogenization < 1:
|
if not isinstance(homogenization,int) or homogenization < 1:
|
||||||
raise TypeError('Invalid homogenization {}'.format(homogenization))
|
raise TypeError('Invalid homogenization {}'.format(homogenization))
|
||||||
|
@ -118,24 +206,31 @@ class Geom():
|
||||||
|
|
||||||
|
|
||||||
def get_microstructure(self):
|
def get_microstructure(self):
|
||||||
|
"""Return the microstructure representation."""
|
||||||
return np.copy(self.microstructure)
|
return np.copy(self.microstructure)
|
||||||
|
|
||||||
def get_size(self):
|
def get_size(self):
|
||||||
|
"""Return the physical size in meter."""
|
||||||
return np.copy(self.size)
|
return np.copy(self.size)
|
||||||
|
|
||||||
def get_origin(self):
|
def get_origin(self):
|
||||||
|
"""Return the origin in meter."""
|
||||||
return np.copy(self.origin)
|
return np.copy(self.origin)
|
||||||
|
|
||||||
def get_grid(self):
|
def get_grid(self):
|
||||||
|
"""Return the grid discretization."""
|
||||||
return np.array(self.microstructure.shape)
|
return np.array(self.microstructure.shape)
|
||||||
|
|
||||||
def get_homogenization(self):
|
def get_homogenization(self):
|
||||||
|
"""Return the homogenization index."""
|
||||||
return self.homogenization
|
return self.homogenization
|
||||||
|
|
||||||
def get_comments(self):
|
def get_comments(self):
|
||||||
|
"""Return the comments."""
|
||||||
return self.comments[:]
|
return self.comments[:]
|
||||||
|
|
||||||
def get_header(self):
|
def get_header(self):
|
||||||
|
"""Return the full header (grid, size, origin, homogenization, comments)."""
|
||||||
header = ['{} header'.format(len(self.comments)+4)] + self.comments
|
header = ['{} header'.format(len(self.comments)+4)] + self.comments
|
||||||
header.append('grid a {} b {} c {}'.format(*self.get_grid()))
|
header.append('grid a {} b {} c {}'.format(*self.get_grid()))
|
||||||
header.append('size x {} y {} z {}'.format(*self.get_size()))
|
header.append('size x {} y {} z {}'.format(*self.get_size()))
|
||||||
|
@ -145,7 +240,15 @@ class Geom():
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_file(cls,fname):
|
def from_file(cls,fname):
|
||||||
"""Reads a geom file"""
|
"""
|
||||||
|
Reads a geom file.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
fname : str or file handle
|
||||||
|
geometry file to read.
|
||||||
|
|
||||||
|
"""
|
||||||
with (open(fname) if isinstance(fname,str) else fname) as f:
|
with (open(fname) if isinstance(fname,str) else fname) as f:
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
header_length,keyword = f.readline().split()[:2]
|
header_length,keyword = f.readline().split()[:2]
|
||||||
|
@ -190,13 +293,21 @@ class Geom():
|
||||||
raise TypeError('Invalid file: expected {} entries,found {}'.format(grid.prod(),i))
|
raise TypeError('Invalid file: expected {} entries,found {}'.format(grid.prod(),i))
|
||||||
|
|
||||||
microstructure = microstructure.reshape(grid,order='F')
|
microstructure = microstructure.reshape(grid,order='F')
|
||||||
if not np.any(np.mod(microstructure.flatten(),1) != 0.0): # no float present
|
if not np.any(np.mod(microstructure.flatten(),1) != 0.0): # no float present
|
||||||
microstructure = microstructure.astype('int')
|
microstructure = microstructure.astype('int')
|
||||||
|
|
||||||
return cls(microstructure.reshape(grid),size,origin,homogenization,comments)
|
return cls(microstructure.reshape(grid),size,origin,homogenization,comments)
|
||||||
|
|
||||||
def to_file(self,fname):
|
def to_file(self,fname):
|
||||||
"""Writes to file"""
|
"""
|
||||||
|
Writes a geom file.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
fname : str or file handle
|
||||||
|
geometry file to write.
|
||||||
|
|
||||||
|
"""
|
||||||
header = self.get_header()
|
header = self.get_header()
|
||||||
grid = self.get_grid()
|
grid = self.get_grid()
|
||||||
format_string = '%{}i'.format(1+int(np.floor(np.log10(np.nanmax(self.microstructure))))) if self.microstructure.dtype == int \
|
format_string = '%{}i'.format(1+int(np.floor(np.log10(np.nanmax(self.microstructure))))) if self.microstructure.dtype == int \
|
||||||
|
@ -208,7 +319,15 @@ class Geom():
|
||||||
|
|
||||||
|
|
||||||
def to_vtk(self,fname=None):
|
def to_vtk(self,fname=None):
|
||||||
"""Generates vtk file. If file name is given, stored in file otherwise returned as string"""
|
"""
|
||||||
|
Generates vtk file.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
fname : str, optional
|
||||||
|
vtk file to write. If no file is given, a string is returned.
|
||||||
|
|
||||||
|
"""
|
||||||
grid = self.get_grid() + np.ones(3,dtype=int)
|
grid = self.get_grid() + np.ones(3,dtype=int)
|
||||||
size = self.get_size()
|
size = self.get_size()
|
||||||
origin = self.get_origin()
|
origin = self.get_origin()
|
||||||
|
@ -262,7 +381,7 @@ class Geom():
|
||||||
|
|
||||||
|
|
||||||
def show(self):
|
def show(self):
|
||||||
"""Show raw content (as in file)"""
|
"""Show raw content (as in file)."""
|
||||||
f=StringIO()
|
f=StringIO()
|
||||||
self.to_file(f)
|
self.to_file(f)
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
|
|
|
@ -10,35 +10,44 @@ from .quaternion import P
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
class Rotation:
|
class Rotation:
|
||||||
u"""
|
u"""
|
||||||
Orientation stored as unit quaternion.
|
Orientation stored with functionality for conversion to different representations.
|
||||||
|
|
||||||
Following: D Rowenhorst et al. Consistent representations of and conversions between 3D rotations
|
|
||||||
10.1088/0965-0393/23/8/083501
|
|
||||||
Convention 1: coordinate frames are right-handed
|
|
||||||
Convention 2: a rotation angle ω is taken to be positive for a counterclockwise rotation
|
|
||||||
when viewing from the end point of the rotation axis towards the origin
|
|
||||||
Convention 3: rotations will be interpreted in the passive sense
|
|
||||||
Convention 4: Euler angle triplets are implemented using the Bunge convention,
|
|
||||||
with the angular ranges as [0, 2π],[0, π],[0, 2π]
|
|
||||||
Convention 5: the rotation angle ω is limited to the interval [0, π]
|
|
||||||
Convention 6: P = -1 (as default)
|
|
||||||
|
|
||||||
q is the real part, p = (x, y, z) are the imaginary parts.
|
|
||||||
|
|
||||||
|
References
|
||||||
|
----------
|
||||||
|
D. Rowenhorst et al., Modelling and Simulation in Materials Science and Engineering 23:083501, 2015
|
||||||
|
https://doi.org/10.1088/0965-0393/23/8/083501
|
||||||
|
|
||||||
|
Conventions
|
||||||
|
-----------
|
||||||
|
Convention 1: Coordinate frames are right-handed.
|
||||||
|
Convention 2: A rotation angle ω is taken to be positive for a counterclockwise rotation
|
||||||
|
when viewing from the end point of the rotation axis towards the origin.
|
||||||
|
Convention 3: Rotations will be interpreted in the passive sense.
|
||||||
|
Convention 4: Euler angle triplets are implemented using the Bunge convention,
|
||||||
|
with the angular ranges as [0, 2π],[0, π],[0, 2π].
|
||||||
|
Convention 5: The rotation angle ω is limited to the interval [0, π].
|
||||||
|
Convention 6: P = -1 (as default).
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
Vector "a" (defined in coordinate system "A") is passively rotated
|
Vector "a" (defined in coordinate system "A") is passively rotated
|
||||||
resulting in new coordinates "b" when expressed in system "B".
|
resulting in new coordinates "b" when expressed in system "B".
|
||||||
b = Q * a
|
b = Q * a
|
||||||
b = np.dot(Q.asMatrix(),a)
|
b = np.dot(Q.asMatrix(),a)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ['quaternion']
|
__slots__ = ['quaternion']
|
||||||
|
|
||||||
def __init__(self,quaternion = np.array([1.0,0.0,0.0,0.0])):
|
def __init__(self,quaternion = np.array([1.0,0.0,0.0,0.0])):
|
||||||
"""
|
"""
|
||||||
Initializes to identity unless specified
|
Initializes to identity unless specified.
|
||||||
|
|
||||||
If a quaternion is given, it needs to comply with the convection. Use .fromQuaternion
|
Parameters
|
||||||
to check the input.
|
----------
|
||||||
|
quaternion : numpy.ndarray, optional
|
||||||
|
Unit quaternion that follows the conventions. Use .fromQuaternion to perform a sanity check.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if isinstance(quaternion,Quaternion):
|
if isinstance(quaternion,Quaternion):
|
||||||
self.quaternion = quaternion.copy()
|
self.quaternion = quaternion.copy()
|
||||||
|
@ -46,14 +55,14 @@ class Rotation:
|
||||||
self.quaternion = Quaternion(q=quaternion[0],p=quaternion[1:4])
|
self.quaternion = Quaternion(q=quaternion[0],p=quaternion[1:4])
|
||||||
|
|
||||||
def __copy__(self):
|
def __copy__(self):
|
||||||
"""Copy"""
|
"""Copy."""
|
||||||
return self.__class__(self.quaternion)
|
return self.__class__(self.quaternion)
|
||||||
|
|
||||||
copy = __copy__
|
copy = __copy__
|
||||||
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
"""Value in selected representation"""
|
"""Orientation displayed as unit quaternion, rotation matrix, and Bunge-Euler angles."""
|
||||||
return '\n'.join([
|
return '\n'.join([
|
||||||
'{}'.format(self.quaternion),
|
'{}'.format(self.quaternion),
|
||||||
'Matrix:\n{}'.format( '\n'.join(['\t'.join(list(map(str,self.asMatrix()[i,:]))) for i in range(3)]) ),
|
'Matrix:\n{}'.format( '\n'.join(['\t'.join(list(map(str,self.asMatrix()[i,:]))) for i in range(3)]) ),
|
||||||
|
@ -62,9 +71,18 @@ class Rotation:
|
||||||
|
|
||||||
def __mul__(self, other):
|
def __mul__(self, other):
|
||||||
"""
|
"""
|
||||||
Multiplication
|
Multiplication.
|
||||||
|
|
||||||
Rotation: Details needed (active/passive), rotation of (3,3,3,3)-matrix should be considered
|
Parameters
|
||||||
|
----------
|
||||||
|
other : numpy.ndarray or Rotation
|
||||||
|
Vector, second or fourth order tensor, or rotation object that is rotated.
|
||||||
|
|
||||||
|
Todo
|
||||||
|
----
|
||||||
|
Document details active/passive)
|
||||||
|
considere rotation of (3,3,3,3)-matrix
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if isinstance(other, Rotation): # rotate a rotation
|
if isinstance(other, Rotation): # rotate a rotation
|
||||||
return self.__class__(self.quaternion * other.quaternion).standardize()
|
return self.__class__(self.quaternion * other.quaternion).standardize()
|
||||||
|
@ -104,32 +122,48 @@ class Rotation:
|
||||||
|
|
||||||
|
|
||||||
def inverse(self):
|
def inverse(self):
|
||||||
"""In-place inverse rotation/backward rotation"""
|
"""In-place inverse rotation/backward rotation."""
|
||||||
self.quaternion.conjugate()
|
self.quaternion.conjugate()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def inversed(self):
|
def inversed(self):
|
||||||
"""Inverse rotation/backward rotation"""
|
"""Inverse rotation/backward rotation."""
|
||||||
return self.copy().inverse()
|
return self.copy().inverse()
|
||||||
|
|
||||||
|
|
||||||
def standardize(self):
|
def standardize(self):
|
||||||
"""In-place quaternion representation with positive q"""
|
"""In-place quaternion representation with positive q."""
|
||||||
if self.quaternion.q < 0.0: self.quaternion.homomorph()
|
if self.quaternion.q < 0.0: self.quaternion.homomorph()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def standardized(self):
|
def standardized(self):
|
||||||
"""Quaternion representation with positive q"""
|
"""Quaternion representation with positive q."""
|
||||||
return self.copy().standardize()
|
return self.copy().standardize()
|
||||||
|
|
||||||
|
|
||||||
def misorientation(self,other):
|
def misorientation(self,other):
|
||||||
"""Misorientation"""
|
"""
|
||||||
|
Get Misorientation.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
other : Rotation
|
||||||
|
Rotation to which the misorientation is computed.
|
||||||
|
|
||||||
|
"""
|
||||||
return other*self.inversed()
|
return other*self.inversed()
|
||||||
|
|
||||||
|
|
||||||
def average(self,other):
|
def average(self,other):
|
||||||
"""Calculate the average rotation"""
|
"""
|
||||||
|
Calculate the average rotation.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
other : Rotation
|
||||||
|
Rotation from which the average is rotated.
|
||||||
|
|
||||||
|
"""
|
||||||
return Rotation.fromAverage([self,other])
|
return Rotation.fromAverage([self,other])
|
||||||
|
|
||||||
|
|
||||||
|
@ -137,41 +171,75 @@ class Rotation:
|
||||||
# convert to different orientation representations (numpy arrays)
|
# convert to different orientation representations (numpy arrays)
|
||||||
|
|
||||||
def asQuaternion(self):
|
def asQuaternion(self):
|
||||||
"""Unit quaternion: (q, [p_1, p_2, p_3])"""
|
"""Unit quaternion: (q, p_1, p_2, p_3)."""
|
||||||
return self.quaternion.asArray()
|
return self.quaternion.asArray()
|
||||||
|
|
||||||
def asEulers(self,
|
def asEulers(self,
|
||||||
degrees = False):
|
degrees = False):
|
||||||
"""Bunge-Euler angles: (φ_1, ϕ, φ_2)"""
|
"""
|
||||||
|
Bunge-Euler angles: (φ_1, ϕ, φ_2).
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
degrees : bool, optional
|
||||||
|
return angles in degrees.
|
||||||
|
|
||||||
|
"""
|
||||||
eu = qu2eu(self.quaternion.asArray())
|
eu = qu2eu(self.quaternion.asArray())
|
||||||
if degrees: eu = np.degrees(eu)
|
if degrees: eu = np.degrees(eu)
|
||||||
return eu
|
return eu
|
||||||
|
|
||||||
def asAxisAngle(self,
|
def asAxisAngle(self,
|
||||||
degrees = False):
|
degrees = False):
|
||||||
"""Axis-angle pair: ([n_1, n_2, n_3], ω)"""
|
"""
|
||||||
|
Axis angle pair: ([n_1, n_2, n_3], ω).
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
degrees : bool, optional
|
||||||
|
return rotation angle in degrees.
|
||||||
|
|
||||||
|
"""
|
||||||
ax = qu2ax(self.quaternion.asArray())
|
ax = qu2ax(self.quaternion.asArray())
|
||||||
if degrees: ax[3] = np.degrees(ax[3])
|
if degrees: ax[3] = np.degrees(ax[3])
|
||||||
return ax
|
return ax
|
||||||
|
|
||||||
def asMatrix(self):
|
def asMatrix(self):
|
||||||
"""Rotation matrix"""
|
"""Rotation matrix."""
|
||||||
return qu2om(self.quaternion.asArray())
|
return qu2om(self.quaternion.asArray())
|
||||||
|
|
||||||
def asRodrigues(self,vector=False):
|
def asRodrigues(self,
|
||||||
"""Rodrigues-Frank vector: ([n_1, n_2, n_3], tan(ω/2))"""
|
vector=False):
|
||||||
|
"""
|
||||||
|
Rodrigues-Frank vector: ([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.
|
||||||
|
|
||||||
|
"""
|
||||||
ro = qu2ro(self.quaternion.asArray())
|
ro = qu2ro(self.quaternion.asArray())
|
||||||
return ro[:3]*ro[3] if vector else ro
|
return ro[:3]*ro[3] if vector else ro
|
||||||
|
|
||||||
def asHomochoric(self):
|
def asHomochoric(self):
|
||||||
"""Homochoric vector: (h_1, h_2, h_3)"""
|
"""Homochoric vector: (h_1, h_2, h_3)."""
|
||||||
return qu2ho(self.quaternion.asArray())
|
return qu2ho(self.quaternion.asArray())
|
||||||
|
|
||||||
def asCubochoric(self):
|
def asCubochoric(self):
|
||||||
|
"""Cubochoric vector: (c_1, c_2, c_3)."""
|
||||||
return qu2cu(self.quaternion.asArray())
|
return qu2cu(self.quaternion.asArray())
|
||||||
|
|
||||||
def asM(self):
|
def asM(self):
|
||||||
"""Intermediate representation supporting quaternion averaging (see F. Landis Markley et al.)"""
|
"""
|
||||||
|
Intermediate representation supporting quaternion averaging.
|
||||||
|
|
||||||
|
References
|
||||||
|
----------
|
||||||
|
F. Landis Markley et al., Journal of Guidance, Control, and Dynamics 30(4):1193-1197, 2007
|
||||||
|
https://doi.org/10.2514/1.28949
|
||||||
|
|
||||||
|
"""
|
||||||
return self.quaternion.asM()
|
return self.quaternion.asM()
|
||||||
|
|
||||||
|
|
||||||
|
@ -299,14 +367,20 @@ class Rotation:
|
||||||
rotations,
|
rotations,
|
||||||
weights = []):
|
weights = []):
|
||||||
"""
|
"""
|
||||||
Average rotation
|
Average rotation.
|
||||||
|
|
||||||
ref: F. Landis Markley, Yang Cheng, John Lucas Crassidis, and Yaakov Oshman.
|
References
|
||||||
Averaging Quaternions,
|
----------
|
||||||
Journal of Guidance, Control, and Dynamics, Vol. 30, No. 4 (2007), pp. 1193-1197.
|
F. Landis Markley et al., Journal of Guidance, Control, and Dynamics 30(4):1193-1197, 2007
|
||||||
doi: 10.2514/1.28949
|
https://doi.org/10.2514/1.28949
|
||||||
|
|
||||||
usage: input a list of rotations and optional weights
|
Parameters
|
||||||
|
----------
|
||||||
|
rotations : list of Rotations
|
||||||
|
Rotations to average from
|
||||||
|
weights : list of floats, optional
|
||||||
|
Weights for each rotation used for averaging
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not all(isinstance(item, Rotation) for item in rotations):
|
if not all(isinstance(item, Rotation) for item in rotations):
|
||||||
raise TypeError("Only instances of Rotation can be averaged.")
|
raise TypeError("Only instances of Rotation can be averaged.")
|
||||||
|
@ -339,42 +413,78 @@ class Rotation:
|
||||||
# ******************************************************************************************
|
# ******************************************************************************************
|
||||||
class Symmetry:
|
class Symmetry:
|
||||||
"""
|
"""
|
||||||
Symmetry operations for lattice systems
|
Symmetry operations for lattice systems.
|
||||||
|
|
||||||
|
References
|
||||||
|
----------
|
||||||
https://en.wikipedia.org/wiki/Crystal_system
|
https://en.wikipedia.org/wiki/Crystal_system
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
lattices = [None,'orthorhombic','tetragonal','hexagonal','cubic',]
|
lattices = [None,'orthorhombic','tetragonal','hexagonal','cubic',]
|
||||||
|
|
||||||
def __init__(self, symmetry = None):
|
def __init__(self, symmetry = None):
|
||||||
if isinstance(symmetry, str) and symmetry.lower() in Symmetry.lattices:
|
"""
|
||||||
self.lattice = symmetry.lower()
|
Symmetry Definition.
|
||||||
else:
|
|
||||||
self.lattice = None
|
Parameters
|
||||||
|
----------
|
||||||
|
symmetry : str, optional
|
||||||
|
label of the crystal system
|
||||||
|
|
||||||
|
"""
|
||||||
|
if symmetry is not None and symmetry.lower() not in Symmetry.lattices:
|
||||||
|
raise KeyError('Symmetry/crystal system "{}" is unknown'.format(symmetry))
|
||||||
|
|
||||||
|
self.lattice = symmetry.lower() if isinstance(symmetry,str) else symmetry
|
||||||
|
|
||||||
|
|
||||||
def __copy__(self):
|
def __copy__(self):
|
||||||
"""Copy"""
|
"""Copy."""
|
||||||
return self.__class__(self.lattice)
|
return self.__class__(self.lattice)
|
||||||
|
|
||||||
copy = __copy__
|
copy = __copy__
|
||||||
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
"""Readable string"""
|
"""Readable string."""
|
||||||
return '{}'.format(self.lattice)
|
return '{}'.format(self.lattice)
|
||||||
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
"""Equal to other"""
|
"""
|
||||||
|
Equal to other.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
other : Symmetry
|
||||||
|
Symmetry to check for equality.
|
||||||
|
|
||||||
|
"""
|
||||||
return self.lattice == other.lattice
|
return self.lattice == other.lattice
|
||||||
|
|
||||||
def __neq__(self, other):
|
def __neq__(self, other):
|
||||||
"""Not equal to other"""
|
"""
|
||||||
|
Not Equal to other.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
other : Symmetry
|
||||||
|
Symmetry to check for inequality.
|
||||||
|
|
||||||
|
"""
|
||||||
return not self.__eq__(other)
|
return not self.__eq__(other)
|
||||||
|
|
||||||
def __cmp__(self,other):
|
def __cmp__(self,other):
|
||||||
"""Linear ordering"""
|
"""
|
||||||
|
Linear ordering.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
other : Symmetry
|
||||||
|
Symmetry to check for for order.
|
||||||
|
|
||||||
|
"""
|
||||||
myOrder = Symmetry.lattices.index(self.lattice)
|
myOrder = Symmetry.lattices.index(self.lattice)
|
||||||
otherOrder = Symmetry.lattices.index(other.lattice)
|
otherOrder = Symmetry.lattices.index(other.lattice)
|
||||||
return (myOrder > otherOrder) - (myOrder < otherOrder)
|
return (myOrder > otherOrder) - (myOrder < otherOrder)
|
||||||
|
@ -458,7 +568,7 @@ class Symmetry:
|
||||||
|
|
||||||
def inFZ(self,rodrigues):
|
def inFZ(self,rodrigues):
|
||||||
"""
|
"""
|
||||||
Check whether given Rodrigues vector falls into fundamental zone of own symmetry.
|
Check whether given Rodriques-Frank vector falls into fundamental zone of own symmetry.
|
||||||
|
|
||||||
Fundamental zone in Rodrigues space is point symmetric around origin.
|
Fundamental zone in Rodrigues space is point symmetric around origin.
|
||||||
"""
|
"""
|
||||||
|
@ -491,11 +601,13 @@ class Symmetry:
|
||||||
|
|
||||||
def inDisorientationSST(self,rodrigues):
|
def inDisorientationSST(self,rodrigues):
|
||||||
"""
|
"""
|
||||||
Check whether given Rodrigues vector (of misorientation) falls into standard stereographic triangle of own symmetry.
|
Check whether given Rodriques-Frank vector (of misorientation) falls into standard stereographic triangle of own symmetry.
|
||||||
|
|
||||||
|
References
|
||||||
|
----------
|
||||||
|
A. Heinz and P. Neumann, Acta Crystallographica Section A 47:780-789, 1991
|
||||||
|
https://doi.org/10.1107/S0108767391006864
|
||||||
|
|
||||||
Determination of disorientations follow the work of A. Heinz and P. Neumann:
|
|
||||||
Representation of Orientation and Disorientation Data for Cubic, Hexagonal, Tetragonal and Orthorhombic Crystals
|
|
||||||
Acta Cryst. (1991). A47, 780-789
|
|
||||||
"""
|
"""
|
||||||
if (len(rodrigues) != 3):
|
if (len(rodrigues) != 3):
|
||||||
raise ValueError('Input is not a Rodriques-Frank vector.\n')
|
raise ValueError('Input is not a Rodriques-Frank vector.\n')
|
||||||
|
@ -611,11 +723,15 @@ class Symmetry:
|
||||||
# ******************************************************************************************
|
# ******************************************************************************************
|
||||||
class Lattice:
|
class Lattice:
|
||||||
"""
|
"""
|
||||||
Lattice system
|
Lattice system.
|
||||||
|
|
||||||
Currently, this contains only a mapping from Bravais lattice to symmetry
|
Currently, this contains only a mapping from Bravais lattice to symmetry
|
||||||
and orientation relationships. It could include twin and slip systems.
|
and orientation relationships. It could include twin and slip systems.
|
||||||
|
|
||||||
|
References
|
||||||
|
----------
|
||||||
https://en.wikipedia.org/wiki/Bravais_lattice
|
https://en.wikipedia.org/wiki/Bravais_lattice
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
lattices = {
|
lattices = {
|
||||||
|
@ -628,18 +744,27 @@ class Lattice:
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, lattice):
|
def __init__(self, lattice):
|
||||||
|
"""
|
||||||
|
New lattice of given type.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
lattice : str
|
||||||
|
Bravais lattice.
|
||||||
|
|
||||||
|
"""
|
||||||
self.lattice = lattice
|
self.lattice = lattice
|
||||||
self.symmetry = Symmetry(self.lattices[lattice]['symmetry'])
|
self.symmetry = Symmetry(self.lattices[lattice]['symmetry'])
|
||||||
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
"""Report basic lattice information"""
|
"""Report basic lattice information."""
|
||||||
return 'Bravais lattice {} ({} symmetry)'.format(self.lattice,self.symmetry)
|
return 'Bravais lattice {} ({} symmetry)'.format(self.lattice,self.symmetry)
|
||||||
|
|
||||||
|
|
||||||
# Kurdjomov--Sachs orientation relationship for fcc <-> bcc transformation
|
# Kurdjomov--Sachs orientation relationship for fcc <-> bcc transformation
|
||||||
# from S. Morito et al. Journal of Alloys and Compounds 577 (2013) 587-S592
|
# from S. Morito et al., Journal of Alloys and Compounds 577:s587-s592, 2013
|
||||||
# also see K. Kitahara et al. Acta Materialia 54 (2006) 1279-1288
|
# also see K. Kitahara et al., Acta Materialia 54:1279-1288, 2006
|
||||||
KS = {'mapping':{'fcc':0,'bcc':1},
|
KS = {'mapping':{'fcc':0,'bcc':1},
|
||||||
'planes': np.array([
|
'planes': np.array([
|
||||||
[[ 1, 1, 1],[ 0, 1, 1]],
|
[[ 1, 1, 1],[ 0, 1, 1]],
|
||||||
|
@ -693,7 +818,7 @@ class Lattice:
|
||||||
[[ 1, 0, 1],[ -1, 1, -1]]],dtype='float')}
|
[[ 1, 0, 1],[ -1, 1, -1]]],dtype='float')}
|
||||||
|
|
||||||
# Greninger--Troiano orientation relationship for fcc <-> bcc transformation
|
# Greninger--Troiano orientation relationship for fcc <-> bcc transformation
|
||||||
# from Y. He et al. Journal of Applied Crystallography 39 (2006) 72-81
|
# from Y. He et al., Journal of Applied Crystallography 39:72-81, 2006
|
||||||
GT = {'mapping':{'fcc':0,'bcc':1},
|
GT = {'mapping':{'fcc':0,'bcc':1},
|
||||||
'planes': np.array([
|
'planes': np.array([
|
||||||
[[ 1, 1, 1],[ 1, 0, 1]],
|
[[ 1, 1, 1],[ 1, 0, 1]],
|
||||||
|
@ -747,7 +872,7 @@ class Lattice:
|
||||||
[[-17,-12, 5],[-17, 7, 17]]],dtype='float')}
|
[[-17,-12, 5],[-17, 7, 17]]],dtype='float')}
|
||||||
|
|
||||||
# Greninger--Troiano' orientation relationship for fcc <-> bcc transformation
|
# Greninger--Troiano' orientation relationship for fcc <-> bcc transformation
|
||||||
# from Y. He et al. Journal of Applied Crystallography 39 (2006) 72-81
|
# from Y. He et al., Journal of Applied Crystallography 39:72-81, 2006
|
||||||
GTprime = {'mapping':{'fcc':0,'bcc':1},
|
GTprime = {'mapping':{'fcc':0,'bcc':1},
|
||||||
'planes': np.array([
|
'planes': np.array([
|
||||||
[[ 7, 17, 17],[ 12, 5, 17]],
|
[[ 7, 17, 17],[ 12, 5, 17]],
|
||||||
|
@ -801,7 +926,7 @@ class Lattice:
|
||||||
[[ 1, 1, 0],[ 1, 1, -1]]],dtype='float')}
|
[[ 1, 1, 0],[ 1, 1, -1]]],dtype='float')}
|
||||||
|
|
||||||
# Nishiyama--Wassermann orientation relationship for fcc <-> bcc transformation
|
# Nishiyama--Wassermann orientation relationship for fcc <-> bcc transformation
|
||||||
# from H. Kitahara et al. Materials Characterization 54 (2005) 378-386
|
# from H. Kitahara et al., Materials Characterization 54:378-386, 2005
|
||||||
NW = {'mapping':{'fcc':0,'bcc':1},
|
NW = {'mapping':{'fcc':0,'bcc':1},
|
||||||
'planes': np.array([
|
'planes': np.array([
|
||||||
[[ 1, 1, 1],[ 0, 1, 1]],
|
[[ 1, 1, 1],[ 0, 1, 1]],
|
||||||
|
@ -831,7 +956,7 @@ class Lattice:
|
||||||
[[ -1, -1, -2],[ 0, -1, 1]]],dtype='float')}
|
[[ -1, -1, -2],[ 0, -1, 1]]],dtype='float')}
|
||||||
|
|
||||||
# Pitsch orientation relationship for fcc <-> bcc transformation
|
# Pitsch orientation relationship for fcc <-> bcc transformation
|
||||||
# from Y. He et al. Acta Materialia 53 (2005) 1179-1190
|
# from Y. He et al., Acta Materialia 53:1179-1190, 2005
|
||||||
Pitsch = {'mapping':{'fcc':0,'bcc':1},
|
Pitsch = {'mapping':{'fcc':0,'bcc':1},
|
||||||
'planes': np.array([
|
'planes': np.array([
|
||||||
[[ 0, 1, 0],[ -1, 0, 1]],
|
[[ 0, 1, 0],[ -1, 0, 1]],
|
||||||
|
@ -861,7 +986,7 @@ class Lattice:
|
||||||
[[ 1, 1, 0],[ 1, 1, -1]]],dtype='float')}
|
[[ 1, 1, 0],[ 1, 1, -1]]],dtype='float')}
|
||||||
|
|
||||||
# Bain orientation relationship for fcc <-> bcc transformation
|
# Bain orientation relationship for fcc <-> bcc transformation
|
||||||
# from Y. He et al./Journal of Applied Crystallography (2006). 39, 72-81
|
# from Y. He et al., Journal of Applied Crystallography 39:72-81, 2006
|
||||||
Bain = {'mapping':{'fcc':0,'bcc':1},
|
Bain = {'mapping':{'fcc':0,'bcc':1},
|
||||||
'planes': np.array([
|
'planes': np.array([
|
||||||
[[ 1, 0, 0],[ 1, 0, 0]],
|
[[ 1, 0, 0],[ 1, 0, 0]],
|
||||||
|
@ -873,12 +998,32 @@ class Lattice:
|
||||||
[[ 1, 0, 0],[ 1, 1, 0]]],dtype='float')}
|
[[ 1, 0, 0],[ 1, 1, 0]]],dtype='float')}
|
||||||
|
|
||||||
def relationOperations(self,model):
|
def relationOperations(self,model):
|
||||||
|
"""
|
||||||
|
Crystallographic orientation relationships for phase transformations.
|
||||||
|
|
||||||
|
References
|
||||||
|
----------
|
||||||
|
S. Morito et al., Journal of Alloys and Compounds 577:s587-s592, 2013
|
||||||
|
https://doi.org/10.1016/j.jallcom.2012.02.004
|
||||||
|
|
||||||
|
K. Kitahara et al., Acta Materialia 54(5):1279-1288, 2006
|
||||||
|
https://doi.org/10.1016/j.actamat.2005.11.001
|
||||||
|
|
||||||
|
Y. He et al., Journal of Applied Crystallography 39:72-81, 2006
|
||||||
|
https://doi.org/10.1107/S0021889805038276
|
||||||
|
|
||||||
|
H. Kitahara et al., Materials Characterization 54(4-5):378-386, 2005
|
||||||
|
https://doi.org/10.1016/j.matchar.2004.12.015
|
||||||
|
|
||||||
|
Y. He et al., Acta Materialia 53(4):1179-1190, 2005
|
||||||
|
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'":self.GTprime,
|
||||||
'NW':self.NW, 'Pitsch': self.Pitsch, 'Bain':self.Bain}
|
'NW':self.NW, 'Pitsch': self.Pitsch, 'Bain':self.Bain}
|
||||||
try:
|
try:
|
||||||
relationship = models[model]
|
relationship = models[model]
|
||||||
except:
|
except KeyError :
|
||||||
raise KeyError('Orientation relationship "{}" is unknown'.format(model))
|
raise KeyError('Orientation relationship "{}" is unknown'.format(model))
|
||||||
|
|
||||||
if self.lattice not in relationship['mapping']:
|
if self.lattice not in relationship['mapping']:
|
||||||
|
@ -909,19 +1054,29 @@ class Lattice:
|
||||||
|
|
||||||
class Orientation:
|
class Orientation:
|
||||||
"""
|
"""
|
||||||
Crystallographic orientation
|
Crystallographic orientation.
|
||||||
|
|
||||||
A crystallographic orientation contains a rotation and a lattice
|
A crystallographic orientation contains a rotation and a lattice.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ['rotation','lattice']
|
__slots__ = ['rotation','lattice']
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
"""Report lattice type and orientation"""
|
"""Report lattice type and orientation."""
|
||||||
return self.lattice.__repr__()+'\n'+self.rotation.__repr__()
|
return self.lattice.__repr__()+'\n'+self.rotation.__repr__()
|
||||||
|
|
||||||
def __init__(self, rotation, lattice):
|
def __init__(self, rotation, lattice):
|
||||||
|
"""
|
||||||
|
New orientation from rotation and lattice.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
rotation : Rotation
|
||||||
|
Rotation specifying the lattice orientation.
|
||||||
|
lattice : Lattice
|
||||||
|
Lattice type of the crystal.
|
||||||
|
|
||||||
|
"""
|
||||||
if isinstance(lattice, Lattice):
|
if isinstance(lattice, Lattice):
|
||||||
self.lattice = lattice
|
self.lattice = lattice
|
||||||
else:
|
else:
|
||||||
|
@ -971,7 +1126,7 @@ class Orientation:
|
||||||
return self.lattice.symmetry.inFZ(self.rotation.asRodrigues(vector=True))
|
return self.lattice.symmetry.inFZ(self.rotation.asRodrigues(vector=True))
|
||||||
|
|
||||||
def equivalentOrientations(self,members=[]):
|
def equivalentOrientations(self,members=[]):
|
||||||
"""List of orientations which are symmetrically equivalent"""
|
"""List of orientations which are symmetrically equivalent."""
|
||||||
try:
|
try:
|
||||||
iter(members) # asking for (even empty) list of members?
|
iter(members) # asking for (even empty) list of members?
|
||||||
except TypeError:
|
except TypeError:
|
||||||
|
@ -981,12 +1136,12 @@ class Orientation:
|
||||||
for q in self.lattice.symmetry.symmetryOperations(members)] # yes, return list of rotations
|
for q in self.lattice.symmetry.symmetryOperations(members)] # yes, return list of rotations
|
||||||
|
|
||||||
def relatedOrientations(self,model):
|
def relatedOrientations(self,model):
|
||||||
"""List of orientations related by the given orientation relationship"""
|
"""List of orientations related by the given orientation relationship."""
|
||||||
r = self.lattice.relationOperations(model)
|
r = self.lattice.relationOperations(model)
|
||||||
return [self.__class__(self.rotation*o,r['lattice']) for o in r['rotations']]
|
return [self.__class__(self.rotation*o,r['lattice']) for o in r['rotations']]
|
||||||
|
|
||||||
def reduced(self):
|
def reduced(self):
|
||||||
"""Transform orientation to fall into fundamental zone according to symmetry"""
|
"""Transform orientation to fall into fundamental zone according to symmetry."""
|
||||||
for me in self.equivalentOrientations():
|
for me in self.equivalentOrientations():
|
||||||
if self.lattice.symmetry.inFZ(me.rotation.asRodrigues(vector=True)): break
|
if self.lattice.symmetry.inFZ(me.rotation.asRodrigues(vector=True)): break
|
||||||
|
|
||||||
|
@ -996,7 +1151,7 @@ class Orientation:
|
||||||
axis,
|
axis,
|
||||||
proper = False,
|
proper = False,
|
||||||
SST = True):
|
SST = True):
|
||||||
"""Axis rotated according to orientation (using crystal symmetry to ensure location falls into SST)"""
|
"""Axis rotated according to orientation (using crystal symmetry to ensure location falls into SST)."""
|
||||||
if SST: # pole requested to be within SST
|
if SST: # pole requested to be within SST
|
||||||
for i,o in enumerate(self.equivalentOrientations()): # test all symmetric equivalent quaternions
|
for i,o in enumerate(self.equivalentOrientations()): # test all symmetric equivalent quaternions
|
||||||
pole = o.rotation*axis # align crystal direction to axis
|
pole = o.rotation*axis # align crystal direction to axis
|
||||||
|
@ -1008,7 +1163,7 @@ class Orientation:
|
||||||
|
|
||||||
|
|
||||||
def IPFcolor(self,axis):
|
def IPFcolor(self,axis):
|
||||||
"""TSL color of inverse pole figure for given axis"""
|
"""TSL color of inverse pole figure for given axis."""
|
||||||
color = np.zeros(3,'d')
|
color = np.zeros(3,'d')
|
||||||
|
|
||||||
for o in self.equivalentOrientations():
|
for o in self.equivalentOrientations():
|
||||||
|
@ -1023,7 +1178,7 @@ class Orientation:
|
||||||
def fromAverage(cls,
|
def fromAverage(cls,
|
||||||
orientations,
|
orientations,
|
||||||
weights = []):
|
weights = []):
|
||||||
"""Create orientation from average of list of orientations"""
|
"""Create orientation from average of list of orientations."""
|
||||||
if not all(isinstance(item, Orientation) for item in orientations):
|
if not all(isinstance(item, Orientation) for item in orientations):
|
||||||
raise TypeError("Only instances of Orientation can be averaged.")
|
raise TypeError("Only instances of Orientation can be averaged.")
|
||||||
|
|
||||||
|
@ -1039,7 +1194,7 @@ class Orientation:
|
||||||
|
|
||||||
|
|
||||||
def average(self,other):
|
def average(self,other):
|
||||||
"""Calculate the average rotation"""
|
"""Calculate the average rotation."""
|
||||||
return Orientation.fromAverage([self,other])
|
return Orientation.fromAverage([self,other])
|
||||||
|
|
||||||
|
|
||||||
|
@ -1080,10 +1235,10 @@ def isone(a):
|
||||||
def iszero(a):
|
def iszero(a):
|
||||||
return np.isclose(a,0.0,atol=1.0e-12,rtol=0.0)
|
return np.isclose(a,0.0,atol=1.0e-12,rtol=0.0)
|
||||||
|
|
||||||
#---------- quaternion ----------
|
#---------- Quaternion ----------
|
||||||
|
|
||||||
def qu2om(qu):
|
def qu2om(qu):
|
||||||
"""Quaternion to orientation matrix"""
|
"""Quaternion to rotation matrix."""
|
||||||
qq = qu[0]**2-(qu[1]**2 + qu[2]**2 + qu[3]**2)
|
qq = qu[0]**2-(qu[1]**2 + qu[2]**2 + qu[3]**2)
|
||||||
om = np.diag(qq + 2.0*np.array([qu[1],qu[2],qu[3]])**2)
|
om = np.diag(qq + 2.0*np.array([qu[1],qu[2],qu[3]])**2)
|
||||||
|
|
||||||
|
@ -1097,7 +1252,7 @@ def qu2om(qu):
|
||||||
|
|
||||||
|
|
||||||
def qu2eu(qu):
|
def qu2eu(qu):
|
||||||
"""Quaternion to Euler angles"""
|
"""Quaternion to Bunge-Euler angles."""
|
||||||
q03 = qu[0]**2+qu[3]**2
|
q03 = qu[0]**2+qu[3]**2
|
||||||
q12 = qu[1]**2+qu[2]**2
|
q12 = qu[1]**2+qu[2]**2
|
||||||
chi = np.sqrt(q03*q12)
|
chi = np.sqrt(q03*q12)
|
||||||
|
@ -1117,7 +1272,7 @@ def qu2eu(qu):
|
||||||
|
|
||||||
def qu2ax(qu):
|
def qu2ax(qu):
|
||||||
"""
|
"""
|
||||||
Quaternion to axis angle
|
Quaternion to axis angle pair.
|
||||||
|
|
||||||
Modified version of the original formulation, should be numerically more stable
|
Modified version of the original formulation, should be numerically more stable
|
||||||
"""
|
"""
|
||||||
|
@ -1134,7 +1289,7 @@ def qu2ax(qu):
|
||||||
|
|
||||||
|
|
||||||
def qu2ro(qu):
|
def qu2ro(qu):
|
||||||
"""Quaternion to Rodrigues vector"""
|
"""Quaternion to Rodriques-Frank vector."""
|
||||||
if iszero(qu[0]):
|
if iszero(qu[0]):
|
||||||
ro = [qu[1], qu[2], qu[3], np.inf]
|
ro = [qu[1], qu[2], qu[3], np.inf]
|
||||||
else:
|
else:
|
||||||
|
@ -1146,7 +1301,7 @@ def qu2ro(qu):
|
||||||
|
|
||||||
|
|
||||||
def qu2ho(qu):
|
def qu2ho(qu):
|
||||||
"""Quaternion to homochoric"""
|
"""Quaternion to homochoric vector."""
|
||||||
omega = 2.0 * np.arccos(np.clip(qu[0],-1.0,1.0)) # avoid numerical difficulties
|
omega = 2.0 * np.arccos(np.clip(qu[0],-1.0,1.0)) # avoid numerical difficulties
|
||||||
|
|
||||||
if iszero(omega):
|
if iszero(omega):
|
||||||
|
@ -1160,15 +1315,15 @@ def qu2ho(qu):
|
||||||
|
|
||||||
|
|
||||||
def qu2cu(qu):
|
def qu2cu(qu):
|
||||||
"""Quaternion to cubochoric"""
|
"""Quaternion to cubochoric vector."""
|
||||||
return ho2cu(qu2ho(qu))
|
return ho2cu(qu2ho(qu))
|
||||||
|
|
||||||
|
|
||||||
#---------- orientation matrix ----------
|
#---------- Rotation matrix ----------
|
||||||
|
|
||||||
def om2qu(om):
|
def om2qu(om):
|
||||||
"""
|
"""
|
||||||
Orientation matrix to quaternion
|
Rotation matrix to quaternion.
|
||||||
|
|
||||||
The original formulation (direct conversion) had (numerical?) issues
|
The original formulation (direct conversion) had (numerical?) issues
|
||||||
"""
|
"""
|
||||||
|
@ -1176,7 +1331,7 @@ def om2qu(om):
|
||||||
|
|
||||||
|
|
||||||
def om2eu(om):
|
def om2eu(om):
|
||||||
"""Orientation matrix to Euler angles"""
|
"""Rotation matrix to Bunge-Euler angles."""
|
||||||
if abs(om[2,2]) < 1.0:
|
if abs(om[2,2]) < 1.0:
|
||||||
zeta = 1.0/np.sqrt(1.0-om[2,2]**2)
|
zeta = 1.0/np.sqrt(1.0-om[2,2]**2)
|
||||||
eu = np.array([np.arctan2(om[2,0]*zeta,-om[2,1]*zeta),
|
eu = np.array([np.arctan2(om[2,0]*zeta,-om[2,1]*zeta),
|
||||||
|
@ -1191,7 +1346,7 @@ def om2eu(om):
|
||||||
|
|
||||||
|
|
||||||
def om2ax(om):
|
def om2ax(om):
|
||||||
"""Orientation matrix to axis angle"""
|
"""Rotation matrix to axis angle pair."""
|
||||||
ax=np.empty(4)
|
ax=np.empty(4)
|
||||||
|
|
||||||
# first get the rotation angle
|
# first get the rotation angle
|
||||||
|
@ -1212,24 +1367,24 @@ def om2ax(om):
|
||||||
|
|
||||||
|
|
||||||
def om2ro(om):
|
def om2ro(om):
|
||||||
"""Orientation matrix to Rodriques vector"""
|
"""Rotation matrix to Rodriques-Frank vector."""
|
||||||
return eu2ro(om2eu(om))
|
return eu2ro(om2eu(om))
|
||||||
|
|
||||||
|
|
||||||
def om2ho(om):
|
def om2ho(om):
|
||||||
"""Orientation matrix to homochoric"""
|
"""Rotation matrix to homochoric vector."""
|
||||||
return ax2ho(om2ax(om))
|
return ax2ho(om2ax(om))
|
||||||
|
|
||||||
|
|
||||||
def om2cu(om):
|
def om2cu(om):
|
||||||
"""Orientation matrix to cubochoric"""
|
"""Rotation matrix to cubochoric vector."""
|
||||||
return ho2cu(om2ho(om))
|
return ho2cu(om2ho(om))
|
||||||
|
|
||||||
|
|
||||||
#---------- Euler angles ----------
|
#---------- Bunge-Euler angles ----------
|
||||||
|
|
||||||
def eu2qu(eu):
|
def eu2qu(eu):
|
||||||
"""Euler angles to quaternion"""
|
"""Bunge-Euler angles to quaternion."""
|
||||||
ee = 0.5*eu
|
ee = 0.5*eu
|
||||||
cPhi = np.cos(ee[1])
|
cPhi = np.cos(ee[1])
|
||||||
sPhi = np.sin(ee[1])
|
sPhi = np.sin(ee[1])
|
||||||
|
@ -1242,7 +1397,7 @@ def eu2qu(eu):
|
||||||
|
|
||||||
|
|
||||||
def eu2om(eu):
|
def eu2om(eu):
|
||||||
"""Euler angles to orientation matrix"""
|
"""Bunge-Euler angles to rotation matrix."""
|
||||||
c = np.cos(eu)
|
c = np.cos(eu)
|
||||||
s = np.sin(eu)
|
s = np.sin(eu)
|
||||||
|
|
||||||
|
@ -1255,7 +1410,7 @@ def eu2om(eu):
|
||||||
|
|
||||||
|
|
||||||
def eu2ax(eu):
|
def eu2ax(eu):
|
||||||
"""Euler angles to axis angle"""
|
"""Bunge-Euler angles to axis angle pair."""
|
||||||
t = np.tan(eu[1]*0.5)
|
t = np.tan(eu[1]*0.5)
|
||||||
sigma = 0.5*(eu[0]+eu[2])
|
sigma = 0.5*(eu[0]+eu[2])
|
||||||
delta = 0.5*(eu[0]-eu[2])
|
delta = 0.5*(eu[0]-eu[2])
|
||||||
|
@ -1266,7 +1421,7 @@ def eu2ax(eu):
|
||||||
if iszero(alpha):
|
if iszero(alpha):
|
||||||
ax = np.array([ 0.0, 0.0, 1.0, 0.0 ])
|
ax = np.array([ 0.0, 0.0, 1.0, 0.0 ])
|
||||||
else:
|
else:
|
||||||
ax = -P/tau * np.array([ t*np.cos(delta), t*np.sin(delta), np.sin(sigma) ]) # passive axis-angle pair so a minus sign in front
|
ax = -P/tau * np.array([ t*np.cos(delta), t*np.sin(delta), np.sin(sigma) ]) # passive axis angle pair so a minus sign in front
|
||||||
ax = np.append(ax,alpha)
|
ax = np.append(ax,alpha)
|
||||||
if alpha < 0.0: ax *= -1.0 # ensure alpha is positive
|
if alpha < 0.0: ax *= -1.0 # ensure alpha is positive
|
||||||
|
|
||||||
|
@ -1274,8 +1429,8 @@ def eu2ax(eu):
|
||||||
|
|
||||||
|
|
||||||
def eu2ro(eu):
|
def eu2ro(eu):
|
||||||
"""Euler angles to Rodrigues vector"""
|
"""Bunge-Euler angles to Rodriques-Frank vector."""
|
||||||
ro = eu2ax(eu) # convert to axis angle representation
|
ro = eu2ax(eu) # convert to axis angle pair representation
|
||||||
if ro[3] >= np.pi: # Differs from original implementation. check convention 5
|
if ro[3] >= np.pi: # Differs from original implementation. check convention 5
|
||||||
ro[3] = np.inf
|
ro[3] = np.inf
|
||||||
elif iszero(ro[3]):
|
elif iszero(ro[3]):
|
||||||
|
@ -1287,19 +1442,19 @@ def eu2ro(eu):
|
||||||
|
|
||||||
|
|
||||||
def eu2ho(eu):
|
def eu2ho(eu):
|
||||||
"""Euler angles to homochoric"""
|
"""Bunge-Euler angles to homochoric vector."""
|
||||||
return ax2ho(eu2ax(eu))
|
return ax2ho(eu2ax(eu))
|
||||||
|
|
||||||
|
|
||||||
def eu2cu(eu):
|
def eu2cu(eu):
|
||||||
"""Euler angles to cubochoric"""
|
"""Bunge-Euler angles to cubochoric vector."""
|
||||||
return ho2cu(eu2ho(eu))
|
return ho2cu(eu2ho(eu))
|
||||||
|
|
||||||
|
|
||||||
#---------- axis angle ----------
|
#---------- Axis angle pair ----------
|
||||||
|
|
||||||
def ax2qu(ax):
|
def ax2qu(ax):
|
||||||
"""Axis angle to quaternion"""
|
"""Axis angle pair to quaternion."""
|
||||||
if iszero(ax[3]):
|
if iszero(ax[3]):
|
||||||
qu = np.array([ 1.0, 0.0, 0.0, 0.0 ])
|
qu = np.array([ 1.0, 0.0, 0.0, 0.0 ])
|
||||||
else:
|
else:
|
||||||
|
@ -1311,7 +1466,7 @@ def ax2qu(ax):
|
||||||
|
|
||||||
|
|
||||||
def ax2om(ax):
|
def ax2om(ax):
|
||||||
"""Axis angle to orientation matrix"""
|
"""Axis angle pair to rotation matrix."""
|
||||||
c = np.cos(ax[3])
|
c = np.cos(ax[3])
|
||||||
s = np.sin(ax[3])
|
s = np.sin(ax[3])
|
||||||
omc = 1.0-c
|
omc = 1.0-c
|
||||||
|
@ -1326,12 +1481,12 @@ def ax2om(ax):
|
||||||
|
|
||||||
|
|
||||||
def ax2eu(ax):
|
def ax2eu(ax):
|
||||||
"""Orientation matrix to Euler angles"""
|
"""Rotation matrix to Bunge Euler angles."""
|
||||||
return om2eu(ax2om(ax))
|
return om2eu(ax2om(ax))
|
||||||
|
|
||||||
|
|
||||||
def ax2ro(ax):
|
def ax2ro(ax):
|
||||||
"""Axis angle to Rodrigues vector"""
|
"""Axis angle pair to Rodriques-Frank vector."""
|
||||||
if iszero(ax[3]):
|
if iszero(ax[3]):
|
||||||
ro = [ 0.0, 0.0, P, 0.0 ]
|
ro = [ 0.0, 0.0, P, 0.0 ]
|
||||||
else:
|
else:
|
||||||
|
@ -1344,36 +1499,36 @@ def ax2ro(ax):
|
||||||
|
|
||||||
|
|
||||||
def ax2ho(ax):
|
def ax2ho(ax):
|
||||||
"""Axis angle to homochoric"""
|
"""Axis angle pair to homochoric vector."""
|
||||||
f = (0.75 * ( ax[3] - np.sin(ax[3]) ))**(1.0/3.0)
|
f = (0.75 * ( ax[3] - np.sin(ax[3]) ))**(1.0/3.0)
|
||||||
ho = ax[0:3] * f
|
ho = ax[0:3] * f
|
||||||
return ho
|
return ho
|
||||||
|
|
||||||
|
|
||||||
def ax2cu(ax):
|
def ax2cu(ax):
|
||||||
"""Axis angle to cubochoric"""
|
"""Axis angle pair to cubochoric vector."""
|
||||||
return ho2cu(ax2ho(ax))
|
return ho2cu(ax2ho(ax))
|
||||||
|
|
||||||
|
|
||||||
#---------- Rodrigues--Frank ----------
|
#---------- Rodrigues-Frank vector ----------
|
||||||
|
|
||||||
def ro2qu(ro):
|
def ro2qu(ro):
|
||||||
"""Rodrigues vector to quaternion"""
|
"""Rodriques-Frank vector to quaternion."""
|
||||||
return ax2qu(ro2ax(ro))
|
return ax2qu(ro2ax(ro))
|
||||||
|
|
||||||
|
|
||||||
def ro2om(ro):
|
def ro2om(ro):
|
||||||
"""Rodgrigues vector to orientation matrix"""
|
"""Rodgrigues-Frank vector to rotation matrix."""
|
||||||
return ax2om(ro2ax(ro))
|
return ax2om(ro2ax(ro))
|
||||||
|
|
||||||
|
|
||||||
def ro2eu(ro):
|
def ro2eu(ro):
|
||||||
"""Rodrigues vector to orientation matrix"""
|
"""Rodriques-Frank vector to Bunge-Euler angles."""
|
||||||
return om2eu(ro2om(ro))
|
return om2eu(ro2om(ro))
|
||||||
|
|
||||||
|
|
||||||
def ro2ax(ro):
|
def ro2ax(ro):
|
||||||
"""Rodrigues vector to axis angle"""
|
"""Rodriques-Frank vector to axis angle pair."""
|
||||||
ta = ro[3]
|
ta = ro[3]
|
||||||
|
|
||||||
if iszero(ta):
|
if iszero(ta):
|
||||||
|
@ -1389,7 +1544,7 @@ def ro2ax(ro):
|
||||||
|
|
||||||
|
|
||||||
def ro2ho(ro):
|
def ro2ho(ro):
|
||||||
"""Rodrigues vector to homochoric"""
|
"""Rodriques-Frank vector to homochoric vector."""
|
||||||
if iszero(np.sum(ro[0:3]**2.0)):
|
if iszero(np.sum(ro[0:3]**2.0)):
|
||||||
ho = [ 0.0, 0.0, 0.0 ]
|
ho = [ 0.0, 0.0, 0.0 ]
|
||||||
else:
|
else:
|
||||||
|
@ -1400,29 +1555,29 @@ def ro2ho(ro):
|
||||||
|
|
||||||
|
|
||||||
def ro2cu(ro):
|
def ro2cu(ro):
|
||||||
"""Rodrigues vector to cubochoric"""
|
"""Rodriques-Frank vector to cubochoric vector."""
|
||||||
return ho2cu(ro2ho(ro))
|
return ho2cu(ro2ho(ro))
|
||||||
|
|
||||||
|
|
||||||
#---------- homochoric ----------
|
#---------- Homochoric vector----------
|
||||||
|
|
||||||
def ho2qu(ho):
|
def ho2qu(ho):
|
||||||
"""Homochoric to quaternion"""
|
"""Homochoric vector to quaternion."""
|
||||||
return ax2qu(ho2ax(ho))
|
return ax2qu(ho2ax(ho))
|
||||||
|
|
||||||
|
|
||||||
def ho2om(ho):
|
def ho2om(ho):
|
||||||
"""Homochoric to orientation matrix"""
|
"""Homochoric vector to rotation matrix."""
|
||||||
return ax2om(ho2ax(ho))
|
return ax2om(ho2ax(ho))
|
||||||
|
|
||||||
|
|
||||||
def ho2eu(ho):
|
def ho2eu(ho):
|
||||||
"""Homochoric to Euler angles"""
|
"""Homochoric vector to Bunge-Euler angles."""
|
||||||
return ax2eu(ho2ax(ho))
|
return ax2eu(ho2ax(ho))
|
||||||
|
|
||||||
|
|
||||||
def ho2ax(ho):
|
def ho2ax(ho):
|
||||||
"""Homochoric to axis angle"""
|
"""Homochoric vector to axis angle pair."""
|
||||||
tfit = np.array([+1.0000000000018852, -0.5000000002194847,
|
tfit = np.array([+1.0000000000018852, -0.5000000002194847,
|
||||||
-0.024999992127593126, -0.003928701544781374,
|
-0.024999992127593126, -0.003928701544781374,
|
||||||
-0.0008152701535450438, -0.0002009500426119712,
|
-0.0008152701535450438, -0.0002009500426119712,
|
||||||
|
@ -1448,42 +1603,42 @@ def ho2ax(ho):
|
||||||
|
|
||||||
|
|
||||||
def ho2ro(ho):
|
def ho2ro(ho):
|
||||||
"""Axis angle to Rodriques vector"""
|
"""Axis angle pair to Rodriques-Frank vector."""
|
||||||
return ax2ro(ho2ax(ho))
|
return ax2ro(ho2ax(ho))
|
||||||
|
|
||||||
|
|
||||||
def ho2cu(ho):
|
def ho2cu(ho):
|
||||||
"""Homochoric to cubochoric"""
|
"""Homochoric vector to cubochoric vector."""
|
||||||
return Lambert.BallToCube(ho)
|
return Lambert.BallToCube(ho)
|
||||||
|
|
||||||
|
|
||||||
#---------- cubochoric ----------
|
#---------- Cubochoric ----------
|
||||||
|
|
||||||
def cu2qu(cu):
|
def cu2qu(cu):
|
||||||
"""Cubochoric to quaternion"""
|
"""Cubochoric vector to quaternion."""
|
||||||
return ho2qu(cu2ho(cu))
|
return ho2qu(cu2ho(cu))
|
||||||
|
|
||||||
|
|
||||||
def cu2om(cu):
|
def cu2om(cu):
|
||||||
"""Cubochoric to orientation matrix"""
|
"""Cubochoric vector to rotation matrix."""
|
||||||
return ho2om(cu2ho(cu))
|
return ho2om(cu2ho(cu))
|
||||||
|
|
||||||
|
|
||||||
def cu2eu(cu):
|
def cu2eu(cu):
|
||||||
"""Cubochoric to Euler angles"""
|
"""Cubochoric vector to Bunge-Euler angles."""
|
||||||
return ho2eu(cu2ho(cu))
|
return ho2eu(cu2ho(cu))
|
||||||
|
|
||||||
|
|
||||||
def cu2ax(cu):
|
def cu2ax(cu):
|
||||||
"""Cubochoric to axis angle"""
|
"""Cubochoric vector to axis angle pair."""
|
||||||
return ho2ax(cu2ho(cu))
|
return ho2ax(cu2ho(cu))
|
||||||
|
|
||||||
|
|
||||||
def cu2ro(cu):
|
def cu2ro(cu):
|
||||||
"""Cubochoric to Rodrigues vector"""
|
"""Cubochoric vector to Rodriques-Frank vector."""
|
||||||
return ho2ro(cu2ho(cu))
|
return ho2ro(cu2ho(cu))
|
||||||
|
|
||||||
|
|
||||||
def cu2ho(cu):
|
def cu2ho(cu):
|
||||||
"""Cubochoric to homochoric"""
|
"""Cubochoric vector to homochoric vector."""
|
||||||
return Lambert.CubeToBall(cu)
|
return Lambert.CubeToBall(cu)
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
# -*- coding: UTF-8 no BOM -*-
|
"""Tools to control the various solvers."""
|
||||||
"""Tools to control the various BVP solvers"""
|
|
||||||
|
|
||||||
from .solver import Solver # noqa
|
from .solver import Solver # noqa
|
||||||
from .spectral import Spectral # noqa
|
|
||||||
from .marc import Marc # noqa
|
from .marc import Marc # noqa
|
||||||
from .abaqus import Abaqus # noqa
|
from .abaqus import Abaqus # noqa
|
||||||
|
|
|
@ -1,17 +1,24 @@
|
||||||
# -*- coding: UTF-8 no BOM -*-
|
import subprocess
|
||||||
|
|
||||||
from .solver import Solver
|
from .solver import Solver
|
||||||
import damask
|
import damask
|
||||||
import subprocess
|
|
||||||
|
|
||||||
class Abaqus(Solver):
|
class Abaqus(Solver):
|
||||||
|
"""Wrapper to run DAMASK with Abaqus."""
|
||||||
|
|
||||||
def __init__(self,version=''): # example version string: 2017
|
def __init__(self,version=int(damask.Environment().options['ABAQUS_VERSION'])):
|
||||||
self.solver='Abaqus'
|
"""
|
||||||
if version =='':
|
Create a Abaqus solver object.
|
||||||
version = damask.Environment().options['ABAQUS_VERSION']
|
|
||||||
else:
|
Parameters
|
||||||
self.version = version
|
----------
|
||||||
|
version : integer
|
||||||
|
Abaqus version
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.solver ='Abaqus'
|
||||||
|
self.version = int(version)
|
||||||
|
|
||||||
def return_run_command(self,model):
|
def return_run_command(self,model):
|
||||||
env=damask.Environment()
|
env=damask.Environment()
|
||||||
|
@ -21,7 +28,7 @@ class Abaqus(Solver):
|
||||||
except OSError: # link to abqXXX not existing
|
except OSError: # link to abqXXX not existing
|
||||||
cmd='abaqus'
|
cmd='abaqus'
|
||||||
process = subprocess.Popen(['abaqus','information=release'],stdout = subprocess.PIPE,stderr = subprocess.PIPE)
|
process = subprocess.Popen(['abaqus','information=release'],stdout = subprocess.PIPE,stderr = subprocess.PIPE)
|
||||||
detectedVersion = process.stdout.readlines()[1].split()[1].decode('utf-8')
|
detectedVersion = int(process.stdout.readlines()[1].split()[1].decode('utf-8'))
|
||||||
if self.version != detectedVersion:
|
if self.version != detectedVersion:
|
||||||
raise Exception('found Abaqus version {}, but requested {}'.format(detectedVersion,self.version))
|
raise Exception('found Abaqus version {}, but requested {}'.format(detectedVersion,self.version))
|
||||||
return '{} -job {} -user {}/src/DAMASK_abaqus interactive'.format(cmd,model,env.rootDir())
|
return '{} -job {} -user {}/src/DAMASK_abaqus interactive'.format(cmd,model,env.rootDir())
|
||||||
|
|
|
@ -1,49 +1,47 @@
|
||||||
# -*- coding: UTF-8 no BOM -*-
|
import os
|
||||||
|
import subprocess
|
||||||
|
import shlex
|
||||||
|
|
||||||
from .solver import Solver
|
from .solver import Solver
|
||||||
|
import damask
|
||||||
|
|
||||||
class Marc(Solver):
|
class Marc(Solver):
|
||||||
|
"""Wrapper to run DAMASK with MSCMarc."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self,version=float(damask.Environment().options['MARC_VERSION'])):
|
||||||
self.solver = 'Marc'
|
"""
|
||||||
|
Create a Marc solver object.
|
||||||
|
|
||||||
#--------------------------
|
|
||||||
def version(self):
|
|
||||||
import damask.environment
|
|
||||||
|
|
||||||
return damask.environment.Environment().options['MARC_VERSION']
|
|
||||||
|
|
||||||
|
|
||||||
#--------------------------
|
Parameters
|
||||||
def libraryPath(self,release = ''):
|
----------
|
||||||
import os,damask.environment
|
version : float
|
||||||
|
Marc version
|
||||||
|
|
||||||
MSCpath = damask.environment.Environment().options['MSC_ROOT']
|
"""
|
||||||
if len(release) == 0: release = self.version()
|
self.solver ='Marc'
|
||||||
|
self.version = float(damask.environment.Environment().options['MARC_VERSION'])
|
||||||
path = '{}/mentat{}/shlib/linux64'.format(MSCpath,release)
|
|
||||||
|
|
||||||
return path if os.path.exists(path) else ''
|
|
||||||
|
|
||||||
|
|
||||||
#--------------------------
|
#--------------------------
|
||||||
def toolsPath(self,release = ''):
|
def libraryPath(self):
|
||||||
import os,damask.environment
|
|
||||||
|
|
||||||
MSCpath = damask.environment.Environment().options['MSC_ROOT']
|
path_MSC = damask.environment.Environment().options['MSC_ROOT']
|
||||||
if len(release) == 0: release = self.version()
|
path_lib = '{}/mentat{}/shlib/linux64'.format(path_MSC,self.version)
|
||||||
|
|
||||||
path = '%s/marc%s/tools'%(MSCpath,release)
|
return path_lib if os.path.exists(path_lib) else ''
|
||||||
|
|
||||||
|
|
||||||
|
#--------------------------
|
||||||
|
def toolsPath(self):
|
||||||
|
|
||||||
|
path_MSC = damask.environment.Environment().options['MSC_ROOT']
|
||||||
|
path_tools = '{}/marc{}/tools'.format(path_MSC,self.version)
|
||||||
|
|
||||||
|
return path_tools if os.path.exists(path_tools) else ''
|
||||||
|
|
||||||
return path if os.path.exists(path) else ''
|
|
||||||
|
|
||||||
|
|
||||||
#--------------------------
|
#--------------------------
|
||||||
def submit_job(self,
|
def submit_job(self,
|
||||||
release = '',
|
|
||||||
model = 'model',
|
model = 'model',
|
||||||
job = 'job1',
|
job = 'job1',
|
||||||
logfile = None,
|
logfile = None,
|
||||||
|
@ -51,25 +49,22 @@ class Marc(Solver):
|
||||||
optimization ='',
|
optimization ='',
|
||||||
):
|
):
|
||||||
|
|
||||||
import os,damask.environment
|
|
||||||
import subprocess,shlex
|
|
||||||
|
|
||||||
if len(release) == 0: release = self.version()
|
|
||||||
damaskEnv = damask.environment.Environment()
|
damaskEnv = damask.environment.Environment()
|
||||||
|
|
||||||
user = 'not found'
|
user = 'not found'
|
||||||
|
|
||||||
if compile:
|
if compile:
|
||||||
if os.path.isfile(os.path.join(damaskEnv.relPath('src/'),'DAMASK_marc{}.f90'.format(release))):
|
if os.path.isfile(os.path.join(damaskEnv.relPath('src/'),'DAMASK_marc{}.f90'.format(self.version))):
|
||||||
user = os.path.join(damaskEnv.relPath('src/'),'DAMASK_marc{}'.format(release))
|
user = os.path.join(damaskEnv.relPath('src/'),'DAMASK_marc{}'.format(self.version))
|
||||||
else:
|
else:
|
||||||
if os.path.isfile(os.path.join(damaskEnv.relPath('src/'),'DAMASK_marc{}.marc'.format(release))):
|
if os.path.isfile(os.path.join(damaskEnv.relPath('src/'),'DAMASK_marc{}.marc'.format(self.version))):
|
||||||
user = os.path.join(damaskEnv.relPath('src/'),'DAMASK_marc{}'.format(release))
|
user = os.path.join(damaskEnv.relPath('src/'),'DAMASK_marc{}'.format(self.version))
|
||||||
|
|
||||||
# Define options [see Marc Installation and Operation Guide, pp 23]
|
# Define options [see Marc Installation and Operation Guide, pp 23]
|
||||||
script = 'run_damask_{}mp'.format(optimization)
|
script = 'run_damask_{}mp'.format(optimization)
|
||||||
|
|
||||||
cmd = os.path.join(self.toolsPath(release),script) + \
|
cmd = os.path.join(self.toolsPath(),script) + \
|
||||||
' -jid ' + model + '_' + job + \
|
' -jid ' + model + '_' + job + \
|
||||||
' -nprocd 1 -autorst 0 -ci n -cr n -dcoup 0 -b no -v no'
|
' -nprocd 1 -autorst 0 -ci n -cr n -dcoup 0 -b no -v no'
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,6 @@
|
||||||
# -*- coding: UTF-8 no BOM -*-
|
|
||||||
|
|
||||||
|
|
||||||
import damask.solver
|
|
||||||
|
|
||||||
class Solver():
|
class Solver():
|
||||||
"""
|
"""
|
||||||
General class for solver specific functionality.
|
General class for solver specific functionality.
|
||||||
|
|
||||||
Sub-classed by the individual solvers.
|
Sub-classed by the individual solvers.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self,solver=''):
|
|
||||||
solverClass = {
|
|
||||||
'spectral': damask.solver.Spectral,
|
|
||||||
'marc': damask.solver.Marc,
|
|
||||||
}
|
|
||||||
if solver.lower() in list(solverClass.keys()):
|
|
||||||
self.__class__=solverClass[solver.lower()]
|
|
||||||
self.__init__()
|
|
||||||
|
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
# -*- coding: UTF-8 no BOM -*-
|
|
||||||
|
|
||||||
|
|
||||||
from .solver import Solver
|
|
||||||
|
|
||||||
class Spectral(Solver):
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.solver='Spectral'
|
|
Loading…
Reference in New Issue